diff --git a/README.md b/README.md new file mode 100644 index 0000000..40afb15 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# VK-TG-SYNC | Синхронизация чатов между Вконтакте и Телеграмом + +> Этот скрипт предназначен для пользователей, которые уже давно сделали Телеграм своим основным мессенджером, но у них все еще есть чаты во Вконтакте, в которых им приходится участвовать. Скрипт позволяет перенести эти чаты из Вконтакте в Телеграм и упростить процесс коммуникации. + +⚠️ **Примечание:** На данный момент скрипт находится в недоделанном виде и может иметь ограничения и проблемы. Пожалуйста, учитывайте это при его использовании. + +![Поддерживаемые версии Python](https://img.shields.io/badge/python-3.7+-blue.svg) + +## 🚀 Установка + +1. Склонируйте репозиторий GitHub: + + ```shell + git clone https://github.com/Delitel-WEB/vk-tg-sync.git + ``` + +2. Перейдите в директорию проекта: + + ```shell + cd vk-tg-sync + ``` + +3. Установите необходимые пакеты Python из файла `req.txt`: + ```shell + pip install -r req.txt + ``` + +## ⚙️ Настройка + +Для настройки скрипта создайте файл `settings.sh` для Linux. + +- Для Linux: `settings.sh` + ```shell + export tgBotToken="" + export vkBotToken="" + export dbPassword="" + ``` + +Выполните следующую команду, чтобы создать таблицы в базе данных: + +```shell +alembic upgrade head +``` + +⚠️ Перед выполнением этой команды убедитесь, что у вас установлена база данных MySQL и создана пустая таблица `vk_to_tg`. + +## ▶️ Запуск бота + +⚠️ Перед запуском убедитесь, что вы установили значения переменных в файле `settings.sh`. + +Перед запуском выполните команду: + +```shell +source settings.sh +``` + +И запустите скрипт: + +```shell +python main.py +``` + +## Что реализовано на данный момент + +> **Синхронизация указана в виде мессенджер ➔ мессенджер. То есть если галочка стоит у ВКонтакте, то сделана синхронизация VK ➔ Telegram и наоборот. Если галочки стоят сразу на двух мессенджерах, значит, синхронизация двусторонняя.** + + +| Виды синхронизации | Telegram | VKontakte | +| ------------------------ | -------- | --------- | +| **Сообщения** | | | +| Текстовые сообщения | ❌ | ✅ | +| Голосовые сообщения | ❌ | ❌ | +| Стикеры | ❌ | ❌ | +| Пересланные сообщения | ❌ | ❌ | +| Документы | ❌ | ❌ | +| Удаление сообщений | ❌ | ❌ | +| Редактирование сообщений | ❌ | ❌ | +| **Медиа** | | | +| Фото | ❌ | ❌ | +| Видео | ❌ | ❌ | +| Геолокация | ❌ | ❌ | +| Гифки | ❌ | ❌ | +| **Действия с чатами** | | | +| Изменение Фото | ❌ | ❌ | +| Изменение Названия Чата | ❌ | ❌ | diff --git a/req.txt b/req.txt index 4bcd639..cb3ff31 100644 --- a/req.txt +++ b/req.txt @@ -1,7 +1,9 @@ vkbottle==4.3.12 aiogram==2.25.1 +cryptography==41.0.4 SQLAlchemy==2.0.20 alembic==1.12.0 aiomysql==0.2.0 PyMySQL==1.1.0 -black==23.9.1 \ No newline at end of file +black==23.9.1 +aiohttp==3.8.5 \ No newline at end of file diff --git a/sync/tg/handlers/bind_chats.py b/sync/tg/handlers/bind_chats.py index 1dae95e..2fc0a10 100644 --- a/sync/tg/handlers/bind_chats.py +++ b/sync/tg/handlers/bind_chats.py @@ -7,7 +7,8 @@ from aiogram.utils.exceptions import ChatNotFound from ...db import Sessions from ...db.models import Conversations -from sqlalchemy import select +from sqlalchemy import select, or_ +from ..utils import update_chat_info @dp.message_handler(commands=["bind_chat"], state="*") @@ -49,7 +50,7 @@ async def get_vk_id(message: Message, state: FSMContext): if data["chat_type"] == "DM": chat = await vk.api.users.get(message.text) if chat: - data["vk_chat_id"] = 2000000000 + int(message.text) + data["vk_chat_id"] = message.text await state.set_data(data) await BindChats.verify_choice_vk.set() @@ -137,7 +138,10 @@ async def get_tg_id(message: Message, state: FSMContext): data = await state.get_data() chat_exists = await session.scalar( select(Conversations).where( - Conversations.vk_id == data["vk_chat_id"] + or_( + Conversations.vk_id == data["vk_chat_id"], + Conversations.tg_id == message.text, + ) ) ) if not chat_exists: @@ -149,6 +153,27 @@ async def get_tg_id(message: Message, state: FSMContext): await session.commit() await message.answer("Отлично!\n" "Чаты успешно связаны!") + await state.finish() + + if data["chat_type"] == "GR": + chat = await vk.api.messages.get_conversations_by_id( + data["vk_chat_id"] + ) + await update_chat_info( + message.text, + chat.items[0].chat_settings.title, + preview=chat.items[0].chat_settings.photo.photo_200 + if chat.items[0].chat_settings.photo + else None, + ) + else: + chat = await vk.api.users.get( + data["vk_chat_id"], fields=["photo_max_orig"] + ) + full_name = f"{chat[0].first_name} {chat[0].last_name}" + await update_chat_info( + message.text, full_name, preview=chat[0].photo_max_orig + ) else: await message.answer("Чат уже к чему-то подключен!") diff --git a/sync/tg/utils/__init__.py b/sync/tg/utils/__init__.py new file mode 100644 index 0000000..f4a48be --- /dev/null +++ b/sync/tg/utils/__init__.py @@ -0,0 +1,2 @@ +from .chats import update_chat_info +from .photos import get_photo_from_url diff --git a/sync/tg/utils/chats.py b/sync/tg/utils/chats.py new file mode 100644 index 0000000..dd5320a --- /dev/null +++ b/sync/tg/utils/chats.py @@ -0,0 +1,15 @@ +from ..core import bot +from .photos import get_photo_from_url + + +async def update_chat_info(chat_id: int, title: str, preview: str): + """ + Обновление информации о группе. + Название, Фото + """ + + if preview: + photo = await get_photo_from_url(preview) + await bot.set_chat_photo(chat_id, photo) + + await bot.set_chat_title(chat_id, title) diff --git a/sync/tg/utils/photos.py b/sync/tg/utils/photos.py new file mode 100644 index 0000000..af22099 --- /dev/null +++ b/sync/tg/utils/photos.py @@ -0,0 +1,15 @@ +import aiohttp +from io import BytesIO + + +async def get_photo_from_url(photo_url): + try: + async with aiohttp.ClientSession() as session: + async with session.get(photo_url) as response: + if response.status == 200: + photo_bytes = await response.read() + return BytesIO(photo_bytes) + else: + return None + except Exception as e: + return None diff --git a/sync/vk/__init__.py b/sync/vk/__init__.py index 27e23fe..d336fdf 100644 --- a/sync/vk/__init__.py +++ b/sync/vk/__init__.py @@ -1,4 +1,5 @@ from .core import bot +from . import handlers def start_vk(): diff --git a/sync/vk/handlers/__init__.py b/sync/vk/handlers/__init__.py new file mode 100644 index 0000000..8330732 --- /dev/null +++ b/sync/vk/handlers/__init__.py @@ -0,0 +1 @@ +from . import messages diff --git a/sync/vk/handlers/messages.py b/sync/vk/handlers/messages.py new file mode 100644 index 0000000..cb0cf6a --- /dev/null +++ b/sync/vk/handlers/messages.py @@ -0,0 +1,24 @@ +from ..core import bot +from vkbottle.user import Message +from ...db import Sessions +from ...db.models import Conversations +from sqlalchemy import select +from ...tg.core import bot as tg +from ..utils import create_vk_link + + +@bot.on.message() +async def on_message(message: Message): + async with Sessions() as session: + bundle = await session.scalar( + select(Conversations).where(Conversations.vk_id == message.peer_id) + ) + if bundle: + users_info = await bot.api.users.get(message.from_id) + await tg.send_message( + bundle.tg_id, + f"{users_info[0].first_name} {users_info[0].last_name}\n" + + "_" * 10 + + f"\n{message.text}", + parse_mode="html", + ) diff --git a/sync/vk/utils/__init__.py b/sync/vk/utils/__init__.py new file mode 100644 index 0000000..e1dfb4a --- /dev/null +++ b/sync/vk/utils/__init__.py @@ -0,0 +1 @@ +from .links import create_vk_link diff --git a/sync/vk/utils/links.py b/sync/vk/utils/links.py new file mode 100644 index 0000000..2475545 --- /dev/null +++ b/sync/vk/utils/links.py @@ -0,0 +1,3 @@ +def create_vk_link(id: int): + """Создание ссылки на профиль в вк из id""" + return f"https://vk.com/id{id}"