mirror of
https://github.com/EDeev/vkrepost_to_tg.git
synced 2026-10-07 20:49:50 +03:00
Зависимости, тесты, CI, Docker, лицензия MIT и README на русском и английском
requirements.txt раньше не устанавливался (sqlite3 и бэкпорт asyncio с PyPI).
This commit is contained in:
parent
a38819dee7
commit
b7a1f6ea22
13 changed files with 378 additions and 146 deletions
4
.env.example
Normal file
4
.env.example
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Токен бота от @BotFather
|
||||
BOT_TOKEN=123456:your-token
|
||||
# Сервисный ключ приложения VK (vk.com/apps?act=manage → приложение → Настройки)
|
||||
VK_SERVICE_TOKEN=your-vk-service-key
|
||||
23
.github/workflows/ci.yml
vendored
Normal file
23
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- run: pip install -r requirements-dev.txt
|
||||
- run: ruff check --select E9,F code tests
|
||||
- run: pytest -q
|
||||
- name: Бот импортируется
|
||||
working-directory: code
|
||||
env:
|
||||
BOT_TOKEN: "123456:TEST"
|
||||
run: python -c "import bot"
|
||||
42
.github/workflows/docker.yml
vendored
Normal file
42
.github/workflows/docker.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
image:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: dcr.deev.su
|
||||
username: ${{ secrets.ZOT_USERNAME }}
|
||||
password: ${{ secrets.ZOT_PASSWORD }}
|
||||
- id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/edeev/vkrepost_to_tg
|
||||
dcr.deev.su/edeev/vkrepost_to_tg
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.env
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.pytest_cache/
|
||||
db/*.db
|
||||
*.log
|
||||
19
Dockerfile
Normal file
19
Dockerfile
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
FROM python:3.12-slim
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1
|
||||
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY code/ code/
|
||||
RUN useradd --create-home --uid 1000 app \
|
||||
&& mkdir -p db \
|
||||
&& chown -R app:app /app
|
||||
USER app
|
||||
VOLUME ["/app/db"]
|
||||
|
||||
# пути к базам в коде — относительно папки code/ (../db)
|
||||
WORKDIR /app/code
|
||||
CMD ["python", "bot.py"]
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Egor Deev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
86
README.en.md
Normal file
86
README.en.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# Portal in VK
|
||||
|
||||
[Русский](README.md) · **English**
|
||||
|
||||
[](https://github.com/EDeev/vkrepost_to_tg/actions/workflows/ci.yml)
|
||||
[](https://github.com/EDeev/vkrepost_to_tg/actions/workflows/docker.yml)
|
||||
[](LICENSE)
|
||||
|
||||
A Telegram bot that forwards new posts from VK (VKontakte) pages and communities to Telegram: subscribe
|
||||
with `/add <short name>` and every new post arrives as a message, with photos, video covers, documents,
|
||||
audio, polls and reposts. The bot speaks Russian.
|
||||
|
||||
**Status:** personal project, completed · bot [@vkportalbot](https://t.me/vkportalbot)
|
||||
|
||||
**Stack:** Python 3.12 · aiogram 3 · vk_api · SQLite · Docker
|
||||
|
||||
## Features
|
||||
|
||||
- Up to 10 subscriptions per user: personal pages, groups and public pages
|
||||
- A post is converted to Telegram format:
|
||||
- photos, video covers and image documents — as an album;
|
||||
- audio — as a separate message;
|
||||
- polls, links and documents — in the text;
|
||||
- `[id1|Name]` mentions — as links;
|
||||
- reposts show both authors and the comment.
|
||||
- Telegram limits are handled: a single photo, albums up to 10 items, long text as separate messages
|
||||
- `/last_post` — the latest post of any page; `/update` — resend a post with fresh data
|
||||
- Your own VK token (optional) gives posts from closed pages you follow and likes from Telegram (`/like`
|
||||
as a reply to a post)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you send the bot your VK token, it is stored on the bot's server. Delete it with `/logout`; revoke
|
||||
> access completely in VK settings, "Apps and websites".
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
git clone https://github.com/EDeev/vkrepost_to_tg.git && cd vkrepost_to_tg
|
||||
cp .env.example .env # BOT_TOKEN and the VK app service key
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Prebuilt image: `docker pull ghcr.io/edeev/vkrepost_to_tg` or `docker pull dcr.deev.su/edeev/vkrepost_to_tg`.
|
||||
The SQLite databases are created on first start.
|
||||
|
||||
Without Docker: Python 3.12, `pip install -r requirements.txt`, then
|
||||
`cd code && BOT_TOKEN=… VK_SERVICE_TOKEN=… python bot.py`.
|
||||
|
||||
## How it works
|
||||
|
||||
```
|
||||
code/bot.py entry point and background polling of subscriptions every minute
|
||||
code/handlers.py commands: subscriptions, latest post, likes, VK token
|
||||
code/vk_scripts.py VK API requests and post parsing
|
||||
code/scripts.py post → HTML text and Telegram media, sending within Telegram limits
|
||||
code/sql.py users, subscriptions and each page's latest post (SQLite)
|
||||
```
|
||||
|
||||
For a closed page the bot uses the token of one of its subscribers; for open pages, the service key. VK
|
||||
requests run in a separate thread so the bot never freezes. Text from VK is escaped.
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
pip install -r requirements-dev.txt
|
||||
ruff check --select E9,F code tests && pytest
|
||||
```
|
||||
|
||||
The tests cover post parsing (escaping, mentions, links, reposts, album limit) and sending (single photo,
|
||||
long text, audio). The Docker image is built on `v*` tags and published to GitHub Packages and
|
||||
`dcr.deev.su`.
|
||||
|
||||
## License
|
||||
|
||||
MIT — see [LICENSE](LICENSE).
|
||||
|
||||
## Author
|
||||
|
||||
**Egor Deev** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space)
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<sub>⭐ If you find this project useful, give it a star on GitHub!</sub>
|
||||
<p><sub>Made with ❤️ — <a href="https://deev.space">deev.space</a></sub></p>
|
||||
</div>
|
||||
188
README.md
188
README.md
|
|
@ -1,155 +1,87 @@
|
|||
# Repost from Vk to Tg 🔄
|
||||
# Portal in VK
|
||||
|
||||
[](https://python.org)
|
||||
[](https://aiogram.dev)
|
||||
[](https://dev.vk.com)
|
||||
[](LICENSE)
|
||||
**Русский** · [English](README.en.md)
|
||||
|
||||
**Автоматизированная система репостинга контента из VKontakte в Telegram**
|
||||
[](https://github.com/EDeev/vkrepost_to_tg/actions/workflows/ci.yml)
|
||||
[](https://github.com/EDeev/vkrepost_to_tg/actions/workflows/docker.yml)
|
||||
[](LICENSE)
|
||||
|
||||
Telegram-бот для автоматического мониторинга и репостинга публикаций из социальной сети ВКонтакте с поддержкой персонализированных подписок и расширенным функционалом взаимодействия.
|
||||
Telegram-бот, который пересылает новые посты со страниц и сообществ ВКонтакте в Telegram: подписываешься
|
||||
командой `/add <короткое имя>`, и каждый новый пост приходит сообщением — с фото, обложками видео,
|
||||
документами, аудио, опросами и репостами.
|
||||
|
||||
## 🎯 Основная функциональность
|
||||
**Статус:** личный проект, завершён · бот [@vkportalbot](https://t.me/vkportalbot)
|
||||
|
||||
### Ключевые возможности
|
||||
- **Автоматический мониторинг** публикаций из VK страниц и сообществ
|
||||
- **Персонализированные подписки** до 10 источников на пользователя
|
||||
- **Интеграция с VK API** через пользовательские токены
|
||||
- **Интерактивное взаимодействие** с лайками постов через Telegram
|
||||
- **Многоформатная поддержка** медиа-контента (фото, видео, аудио, документы)
|
||||
**Стек:** Python 3.12 · aiogram 3 · vk_api · SQLite · Docker
|
||||
|
||||
### Архитектурные особенности
|
||||
- Асинхронная обработка запросов с использованием `asyncio`
|
||||
- Двухуровневая система баз данных (пользователи/группы)
|
||||
- Токен-ротация для обхода ограничений API
|
||||
- Централизованное управление подписками
|
||||
## Возможности
|
||||
|
||||
## 🚀 Быстрый старт
|
||||
- До 10 подписок на пользователя: личные страницы, группы и паблики
|
||||
- Пост переводится в формат Telegram:
|
||||
- фото, обложки видео и картинки-документы — альбомом;
|
||||
- аудио — отдельным сообщением;
|
||||
- опросы, ссылки и документы — в тексте;
|
||||
- упоминания `[id1|Имя]` — ссылками;
|
||||
- у репостов — оба автора и комментарий.
|
||||
- Ограничения Telegram учтены: одиночное фото, альбомы до 10 элементов, длинный текст отдельными
|
||||
сообщениями
|
||||
- `/last_post` — последний пост любой страницы, `/update` — прислать пост заново с актуальными данными
|
||||
- Свой токен VK (необязательно) даёт посты закрытых страниц, на которые вы подписаны, и лайки из Telegram
|
||||
(`/like` ответом на пост)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Если вы присылаете боту свой токен VK, он хранится на сервере бота. Удалить его можно командой
|
||||
> `/logout`, полностью отозвать доступ — в настройках VK, раздел «Приложения и сайты».
|
||||
|
||||
## Запуск
|
||||
|
||||
### Предварительные требования
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
git clone https://github.com/EDeev/vkrepost_to_tg.git && cd vkrepost_to_tg
|
||||
cp .env.example .env # BOT_TOKEN и сервисный ключ приложения VK
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Конфигурация
|
||||
1. Создайте Telegram-бота через [@BotFather](https://t.me/botfather)
|
||||
2. Получите служебный токен VK API
|
||||
3. Настройте файл `config.py`:
|
||||
Готовый образ: `docker pull ghcr.io/edeev/vkrepost_to_tg` или `docker pull dcr.deev.su/edeev/vkrepost_to_tg`.
|
||||
Базы SQLite создаются при первом запуске.
|
||||
|
||||
```python
|
||||
# TOKENS
|
||||
botToken = 'YOUR_TELEGRAM_BOT_TOKEN'
|
||||
serviceToken = "YOUR_VK_SERVICE_TOKEN"
|
||||
Без Docker: Python 3.12, `pip install -r requirements.txt`, затем
|
||||
`cd code && BOT_TOKEN=… VK_SERVICE_TOKEN=… python bot.py`.
|
||||
|
||||
# URL
|
||||
loginUrl = "https://oauth.vk.com/authorize?client_id=YOUR_APP_ID&display=page&redirect_uri=https://oauth.vk.com/blank.html&scope=wall,likes&response_type=token&v=5.131"
|
||||
## Как устроено
|
||||
|
||||
```
|
||||
code/bot.py запуск и фоновый опрос подписок раз в минуту
|
||||
code/handlers.py команды: подписки, последний пост, лайки, токен VK
|
||||
code/vk_scripts.py запросы к VK API и разбор поста
|
||||
code/scripts.py пост → текст в HTML и медиа Telegram, отправка с учётом ограничений
|
||||
code/sql.py пользователи, подписки и последний пост каждой страницы (SQLite)
|
||||
```
|
||||
|
||||
### Запуск системы
|
||||
Для закрытой страницы бот берёт токен одного из её подписчиков, для открытых — сервисный ключ. Запросы
|
||||
к VK идут в отдельном потоке, чтобы бот не замирал. Текст из ВК экранируется.
|
||||
|
||||
## Разработка
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
pip install -r requirements-dev.txt
|
||||
ruff check --select E9,F code tests && pytest
|
||||
```
|
||||
|
||||
## 📋 Структура команд
|
||||
Тесты проверяют разбор постов (экранирование, упоминания, ссылки, репосты, лимит альбома) и отправку
|
||||
(одиночное фото, длинный текст, аудио). Docker-образ собирается по тегу `v*` и публикуется в GitHub
|
||||
Packages и `dcr.deev.su`.
|
||||
|
||||
| Команда | Описание | Синтаксис |
|
||||
|---------|----------|-----------|
|
||||
| `/start` | Инициализация пользователя | `/start` |
|
||||
| `/add` | Добавление подписки | `/add domain_name` |
|
||||
| `/del` | Удаление подписки | `/del domain_name` |
|
||||
| `/list` | Просмотр активных подписок | `/list` |
|
||||
| `/like` | Лайк поста (ответ на сообщение) | `/like` |
|
||||
| `/notif` | Переключение уведомлений | `/notif` |
|
||||
| `/last_post` | Получение последней публикации | `/last_post domain_name` |
|
||||
## Лицензия
|
||||
|
||||
## 🏗️ Архитектура системы
|
||||
MIT — см. [LICENSE](LICENSE).
|
||||
|
||||
### Компоненты системы
|
||||
```
|
||||
├── bot.py # Основной модуль бота
|
||||
├── vk_scripts.py # VK API интеграция
|
||||
├── sql.py # Управление базами данных
|
||||
├── scripts.py # Вспомогательные функции
|
||||
├── config.py # Конфигурация проекта
|
||||
└── db/
|
||||
├── users.db # База пользователей
|
||||
└── base.db # Основная база данных
|
||||
```
|
||||
## Автор
|
||||
|
||||
### Технологический стек
|
||||
- **Backend**: Python 3.8+
|
||||
- **Telegram Framework**: Aiogram 2.x
|
||||
- **VK Integration**: vk_api
|
||||
- **Database**: SQLite
|
||||
- **Async Processing**: asyncio
|
||||
- **Media Processing**: Built-in handlers
|
||||
|
||||
### Схема базы данных
|
||||
|
||||
#### Таблица `user` (users.db)
|
||||
```sql
|
||||
user_id INTEGER PRIMARY KEY -- Telegram ID пользователя
|
||||
id INTEGER AUTOINCREMENT -- Внутренний ID
|
||||
```
|
||||
|
||||
#### Таблица `user` (base.db)
|
||||
```sql
|
||||
user_id INTEGER -- Ссылка на users.db
|
||||
status BOOLEAN -- Статус уведомлений
|
||||
groups TEXT -- Список подписок (разделитель ;)
|
||||
token TEXT -- VK access token
|
||||
count INTEGER -- Количество подписок
|
||||
```
|
||||
|
||||
## ⚡ Алгоритм работы
|
||||
|
||||
### Цикл мониторинга
|
||||
1. **Сканирование источников** (интервал: 60 секунд)
|
||||
2. **Проверка новых публикаций** через VK API
|
||||
3. **Форматирование контента** под Telegram
|
||||
4. **Массовая рассылка** подписчикам
|
||||
5. **Обновление метаданных** в базе данных
|
||||
|
||||
### Обработка медиа-контента
|
||||
- **Фотографии**: Группировка в медиа-альбомы
|
||||
- **Видео**: Информационные заглушки с ссылками
|
||||
- **Аудио**: Отдельные медиа-сообщения
|
||||
- **Документы**: Прямые ссылки с метаданными
|
||||
- **Опросы**: Текстовое представление с результатами
|
||||
|
||||
## 🔧 Расширенные возможности
|
||||
|
||||
### Система токенов
|
||||
- **Служебный токен**: Базовый доступ к публичному контенту
|
||||
- **Пользовательские токены**: Доступ к закрытым страницам и функции лайков
|
||||
- **Автоматическая ротация**: Распределение нагрузки между токенами
|
||||
|
||||
### Обработка ошибок
|
||||
- Graceful handling VK API лимитов
|
||||
- Автоматический фallback на служебный токен
|
||||
- Логирование критических ошибок
|
||||
|
||||
## 📊 Метрики производительности
|
||||
|
||||
- **Пропускная способность**: До 1000 пользователей
|
||||
- **Частота обновлений**: 60 секунд
|
||||
- **Лимит подписок**: 10 на пользователя
|
||||
- **Поддерживаемые форматы**: 6 типов медиа
|
||||
|
||||
## 📄 Лицензия
|
||||
|
||||
Проект распространяется под лицензией MIT.
|
||||
|
||||
## 👨💻 Автор
|
||||
|
||||
**Деев Егор Викторович**
|
||||
- GitHub: [@EDeev](https://github.com/EDeev)
|
||||
- Email: egor@deev.space
|
||||
- Telegram: [@Egor_Deev](https://t.me/Egor_Deev)
|
||||
**Деев Егор Викторович** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space)
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<sub>Проект носит некоммерческий характер и предназначен для образовательных целей и личного использования.</sub>
|
||||
<p><sub>Создано с ❤️ от вашего дорогого - deev.space ©</sub></p>
|
||||
<sub>⭐ Если проект оказался полезным, поставьте звёздочку на GitHub!</sub>
|
||||
<p><sub>Сделано с ❤️ — <a href="https://deev.space">deev.space</a></sub></p>
|
||||
</div>
|
||||
|
|
|
|||
11
compose.yaml
Normal file
11
compose.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
services:
|
||||
bot:
|
||||
build: .
|
||||
image: ghcr.io/edeev/vkrepost_to_tg:latest
|
||||
env_file: .env
|
||||
volumes:
|
||||
- db:/app/db
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
db:
|
||||
3
requirements-dev.txt
Normal file
3
requirements-dev.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
-r requirements.txt
|
||||
pytest==8.4.2
|
||||
ruff==0.14.0
|
||||
|
|
@ -1,18 +1,3 @@
|
|||
# Telegram Bot Framework
|
||||
aiogram==2.25.1
|
||||
|
||||
# VK API
|
||||
vk-api==11.9.9
|
||||
|
||||
# Database
|
||||
sqlite3
|
||||
|
||||
# Async support
|
||||
asyncio
|
||||
|
||||
# Emoji support
|
||||
emoji==2.8.0
|
||||
|
||||
# Additional dependencies
|
||||
requests>=2.28.0
|
||||
aiohttp>=3.8.0
|
||||
aiogram==3.23.0
|
||||
vk_api==11.10.1
|
||||
emoji==2.16.0
|
||||
|
|
|
|||
4
tests/conftest.py
Normal file
4
tests/conftest.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "code"))
|
||||
96
tests/test_post.py
Normal file
96
tests/test_post.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import asyncio
|
||||
from types import SimpleNamespace
|
||||
|
||||
import scripts
|
||||
import vk_scripts
|
||||
|
||||
|
||||
def wall(text, attachments, repost=None):
|
||||
post = {"id": 7, "owner_id": -100, "text": text, "attachments": attachments}
|
||||
if repost:
|
||||
post["copy_history"] = [repost]
|
||||
return {"items": [post], "groups": [{"id": 100, "name": "Паблик <&>", "screen_name": "pub"}],
|
||||
"profiles": [{"id": 5, "first_name": "Иван", "last_name": "Петров", "screen_name": "ivan"}]}
|
||||
|
||||
|
||||
def parse(response):
|
||||
class Api:
|
||||
class wall:
|
||||
@staticmethod
|
||||
def get(**_): return response
|
||||
parser = vk_scripts.VkParser.__new__(vk_scripts.VkParser)
|
||||
parser.vk = Api
|
||||
return parser.last_post(owner_id=-100)
|
||||
|
||||
|
||||
def photo(url):
|
||||
return {"type": "photo", "photo": {"sizes": [{"url": url + "-small"}, {"url": url}]}}
|
||||
|
||||
|
||||
def test_text_is_escaped_and_mentions_become_links():
|
||||
out = parse(wall("1 < 2 & [id5|Иван] пишет <b>", []))
|
||||
text, audio, media = scripts.pars_post(out)
|
||||
assert "1 < 2 & <a href='https://vk.com/id5'>Иван</a> пишет <b>" in text
|
||||
assert "Паблик <&>" in text
|
||||
assert audio == [] and media == []
|
||||
|
||||
|
||||
def test_attachments_links_are_html_and_media_limited():
|
||||
atts = [{"type": "link", "link": {"title": "Сайт", "url": "https://example.com/?a=1&b=2"}}]
|
||||
atts += [photo(f"https://img/{i}") for i in range(12)]
|
||||
text, audio, media = scripts.pars_post(parse(wall("пост", atts)))
|
||||
assert "<a href='https://example.com/?a=1&b=2'>Сайт</a>" in text
|
||||
assert "](" not in text # раньше ссылки оформлялись Markdown-синтаксисом
|
||||
assert len(media) == 10 and media[0].media == "https://img/0"
|
||||
|
||||
|
||||
def test_repost_has_both_authors():
|
||||
orig = {"id": 3, "owner_id": 5, "text": "оригинал", "attachments": []}
|
||||
text, _, _ = scripts.pars_post(parse(wall("мой комментарий", [], repost=orig)))
|
||||
assert "Автор репоста" in text and "Иван Петров" in text and "<blockquote>мой комментарий</blockquote>" in text
|
||||
|
||||
|
||||
def test_split_text_keeps_lines():
|
||||
text = "\n".join(f"<b>строка {i}</b>" for i in range(1000))
|
||||
parts = scripts.split_text(text, 4096)
|
||||
assert all(len(p) <= 4096 for p in parts)
|
||||
assert "".join(parts).count("<b>") == 1000 and all(p.count("<b>") == p.count("</b>") for p in parts)
|
||||
|
||||
|
||||
class FakeBot:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def __getattr__(self, name):
|
||||
async def method(**kwargs):
|
||||
self.calls.append((name, kwargs))
|
||||
msg = SimpleNamespace(message_id=len(self.calls))
|
||||
return [msg] if name == "send_media_group" else msg
|
||||
return method
|
||||
|
||||
|
||||
def send(text, audio, media):
|
||||
bot = FakeBot()
|
||||
asyncio.run(scripts.send_post(bot, 1, text, audio, media))
|
||||
return bot.calls
|
||||
|
||||
|
||||
def test_single_photo_is_sent_as_photo_not_album():
|
||||
_, _, media = scripts.pars_post(parse(wall("пост", [photo("https://img/1")])))
|
||||
calls = send("короткий текст", [], media)
|
||||
assert [c[0] for c in calls] == ["send_photo"] and calls[0][1]["caption"] == "короткий текст"
|
||||
|
||||
|
||||
def test_long_text_with_album_goes_as_separate_messages():
|
||||
_, _, media = scripts.pars_post(parse(wall("пост", [photo("a"), photo("b")])))
|
||||
calls = send("x" * 3000, [], media)
|
||||
assert calls[0][0] == "send_media_group" and calls[0][1]["media"][0].caption is None
|
||||
assert calls[1][0] == "send_message" and calls[1][1]["reply_to_message_id"] == 1
|
||||
|
||||
|
||||
def test_text_with_single_audio():
|
||||
atts = [{"type": "audio", "audio": {"title": "Песня", "url": "https://a/1.mp3", "artist": "Автор"}}]
|
||||
text, audio, media = scripts.pars_post(parse(wall("пост", atts)))
|
||||
calls = send(text, audio, media)
|
||||
assert [c[0] for c in calls] == ["send_message", "send_audio"]
|
||||
assert calls[1][1]["reply_to_message_id"] == 1
|
||||
Loading…
Add table
Reference in a new issue