Skip to content

Commit 14d55d8

Browse files
committed
In the "main.py" added support for admin commands to manage tracked users, improved message generation for sending to Telegram, and fixed error handling. Virtual environment directories and a file with a list of monitored users have been added to ".gitignore". In "requirements.txt" added the missing vk_api package. In "deploy.sh" the name of the service (daemon) has been changed
1 parent d2a2799 commit 14d55d8

4 files changed

Lines changed: 203 additions & 26 deletions

File tree

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
.idea
1+
.idea/
2+
.venv/
3+
venv/
4+
vk_users.json

deploy.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/usr/bin/env bash
22

33
# Имя сервиса
4-
SERVICE_NAME=duplicatemessagebot.service
4+
SERVICE_NAME=duplicate-message-bot.service
55

66
# ----------------------------
7-
# Обновление системы и зависимостей
7+
# Обновление системы и установка нужных пакетов
88
# ----------------------------
99
echo "Обновление системы и установка нужных пакетов"
1010
sudo apt update -y

main.py

Lines changed: 197 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,212 @@
11
from vk_api.longpoll import VkLongPoll, VkEventType
2+
from vk_api.utils import get_random_id
23
import vk_api
34
import telebot
45
import os
56
import requests
7+
import json
8+
import time
69

10+
# Токены от ботов
711
VK_DM_BOT_TOKEN = os.environ["VK_DM_BOT_TOKEN"]
812
TG_DM_BOT_TOKEN = os.environ["TG_DM_BOT_TOKEN"]
913

10-
VK_CHAT_IDS = [int(cid) for cid in os.environ["VK_CHAT_IDS"].split(":")]
11-
VK_USER_IDS = [int(uid) for uid in os.environ["VK_USER_IDS"].split(":")]
12-
TG_CHAT_IDS = [int(cid) for cid in os.environ["TG_CHAT_IDS"].split(":")]
14+
# Кортежи с айдишниками чатов и админов
15+
VK_CHAT_IDS = tuple(int(cid) for cid in os.environ["VK_CHAT_IDS"].split(":"))
16+
VK_ADMIN_IDS = tuple(int(uid) for uid in os.environ["VK_ADMIN_IDS"].split(":"))
17+
TG_CHAT_IDS = tuple(int(cid) for cid in os.environ["TG_CHAT_IDS"].split(":"))
1318

19+
# Файл со списком отслеживаемых пользователей
20+
VK_USERS_FILE = "vk_users.json"
21+
22+
23+
# Вспомогательные функции для работы с файлом
24+
def load_users() -> set:
25+
if not os.path.exists(VK_USERS_FILE):
26+
return set()
27+
with open(VK_USERS_FILE, "r") as f:
28+
return set(json.load(f).get("users", []))
29+
30+
def save_users(users: set) -> None:
31+
with open(VK_USERS_FILE, "w") as f:
32+
json.dump({"users": list(users)}, f, indent=2)
33+
34+
35+
# Список айдишников отслеживаемых пользователей
36+
vk_user_ids = load_users()
37+
38+
39+
# Переменные с сессиями ботов
1440
VK_BOT = vk_api.VkApi(token=VK_DM_BOT_TOKEN)
41+
VK_LONG_POLL = VkLongPoll(VK_BOT, preload_messages=True)
42+
VK_API = VK_BOT.get_api()
1543
TG_BOT = telebot.TeleBot(TG_DM_BOT_TOKEN)
1644

45+
46+
# Вспомогательные функции для бота в ВК
47+
def get_entity_fullname(entity_info: dict) -> str:
48+
if entity_info.get("name", ""):
49+
return entity_info["name"]
50+
else:
51+
return f"{entity_info['first_name']} {entity_info['last_name']}"
52+
53+
def if_empty2attachment(message_text: str) -> str:
54+
return message_text if message_text else "[<b>Вложения не поддерживаются</b>]"
55+
56+
def is_negative(entity_id: str) -> bool:
57+
return entity_id.startswith("-") and entity_id[1:].isdigit()
58+
59+
def resolve_entity(entity_id: int | str) -> dict:
60+
entity_id = str(entity_id)
61+
if is_negative(entity_id):
62+
resp = VK_API.groups.get_by_id(group_id=abs(int(entity_id)))
63+
else:
64+
resp = VK_API.users.get(user_ids=entity_id)
65+
if resp:
66+
return resp[0]
67+
else:
68+
return {}
69+
70+
def get_numeric_entity_id(entity_id: str) -> int:
71+
entity_id = entity_id.strip().replace("@id", "").replace("@", "")
72+
if entity_id.isdigit() or is_negative(entity_id):
73+
return int(entity_id)
74+
entity_info = resolve_entity(entity_id)
75+
return entity_info["id"] if entity_info else 0
76+
77+
78+
# Функции команд для бота в ВК
79+
def cmd_add(chat_id: int, argument: str) -> None:
80+
if argument:
81+
uid = get_numeric_entity_id(argument)
82+
if uid < 1:
83+
msg = "Такого пользователя не существует :("
84+
elif uid in vk_user_ids:
85+
msg = "Пользователь уже есть в списке :("
86+
else:
87+
vk_user_ids.add(uid)
88+
save_users(vk_user_ids)
89+
msg = f"Пользователь @id{uid} добавлен"
90+
else:
91+
msg = "Пользователь не указан :("
92+
93+
VK_API.messages.send(peer_id=chat_id, message=msg, random_id=get_random_id())
94+
95+
def cmd_del(chat_id: int, argument: str) -> None:
96+
if argument:
97+
uid = get_numeric_entity_id(argument)
98+
if uid < 1:
99+
msg = "Такого пользователя не существует :("
100+
elif uid not in vk_user_ids:
101+
msg = "Такого пользователя нет в списке :("
102+
else:
103+
vk_user_ids.remove(uid)
104+
save_users(vk_user_ids)
105+
msg = f"Пользователь @id{uid} удалён"
106+
else:
107+
msg = "Пользователь не указан :("
108+
109+
VK_API.messages.send(peer_id=chat_id, message=msg, random_id=get_random_id())
110+
111+
def cmd_list(chat_id: int, _) -> None:
112+
if not vk_user_ids:
113+
msg = "Список отслеживаемых пользователей пуст :("
114+
else:
115+
msg = (
116+
"Список отслеживаемых пользователей:\n"
117+
+ "\n".join(f"@id{uid}" for uid in vk_user_ids)
118+
)
119+
VK_API.messages.send(peer_id=chat_id, message=msg, random_id=get_random_id())
120+
121+
122+
# Словарь команд
123+
COMMANDS = {
124+
"/del": cmd_del,
125+
"/add": cmd_add,
126+
"/list": cmd_list,
127+
}
128+
129+
# Цикл бота в ВК
17130
while 1:
18131
try:
19-
for event in VkLongPoll(VK_BOT, preload_messages=True).listen():
20-
if event.type == VkEventType.MESSAGE_NEW and event.from_chat:
21-
peer_id = event.peer_id
22-
user_id = event.user_id
23-
if peer_id in VK_CHAT_IDS and user_id in VK_USER_IDS:
24-
vk_msg = "Сообщение из ВК:\n<blockquote>" + event.text + "</blockquote>"
25-
vk_fwd_msgs = event.message_data["fwd_messages"]
26-
tg_msg = vk_msg
27-
if vk_fwd_msgs:
28-
tg_msg += "\n\nВложенные в него сообщения:\n<blockquote>"
29-
for vk_fwd_msg in vk_fwd_msgs:
30-
tg_msg += ("<tg-emoji emoji-id='5453969572354878595'>"
31-
"⭐</tg-emoji> " + vk_fwd_msg["text"] + "\n\n")
32-
tg_msg += "</blockquote>"
33-
34-
for tg_chat_id in TG_CHAT_IDS:
35-
TG_BOT.send_message(tg_chat_id, tg_msg, parse_mode="HTML")
36-
37-
except requests.exceptions.ReadTimeout or requests.exceptions.ConnectionError:
38-
print("Временно потеряно соедиение с сервером ВК")
132+
for event in VK_LONG_POLL.listen():
133+
if event.type != VkEventType.MESSAGE_NEW:
134+
continue
135+
if not event.from_chat or hasattr(event, "source_act"):
136+
continue
137+
138+
peer_id = event.peer_id
139+
user_id = event.user_id
140+
text = event.text
141+
142+
# Команды для админов
143+
if user_id in VK_ADMIN_IDS and text.startswith("/"):
144+
parts = text.split(maxsplit=1)
145+
cmd = parts[0]
146+
arg = parts[1] if len(parts) > 1 else ""
147+
148+
if cmd in COMMANDS:
149+
COMMANDS[cmd](peer_id, arg)
150+
151+
# Отслеживание сообщений ВК и отправка в ТГ
152+
if peer_id in VK_CHAT_IDS and user_id in vk_user_ids:
153+
user = resolve_entity(user_id)
154+
fullname = get_entity_fullname(user)
155+
156+
tg_msg = (
157+
f"Сообщение от пользователя <b>{fullname}</b>:\n"
158+
f"<blockquote>{if_empty2attachment(text)}</blockquote>"
159+
)
160+
161+
if hasattr(event, "message_data"):
162+
message_data = event.message_data
163+
else:
164+
message_data = {}
165+
vk_fwd_msgs = message_data.get("fwd_messages", [])
166+
vk_rpl_msg = message_data.get("reply_message", {})
167+
168+
if vk_fwd_msgs:
169+
tg_msg += "\n\nВложенные сообщения:\n<blockquote>"
170+
for vk_fwd_msg in vk_fwd_msgs:
171+
vk_fwd_entity_id = vk_fwd_msg["from_id"]
172+
entity = resolve_entity(vk_fwd_entity_id)
173+
fullname = get_entity_fullname(entity)
174+
tg_msg += (
175+
"<tg-emoji emoji-id='5453969572354878595'>⭐</tg-emoji> "
176+
f"<b>{fullname}</b>: {if_empty2attachment(vk_fwd_msg['text'])}\n\n"
177+
)
178+
tg_msg += "</blockquote>"
179+
180+
if vk_rpl_msg:
181+
vk_rpl_entity_id = vk_rpl_msg["from_id"]
182+
entity = resolve_entity(vk_rpl_entity_id)
183+
fullname = get_entity_fullname(entity)
184+
tg_msg += (
185+
f"\n\nВ ответ на сообщение <b>{fullname}</b>:\n"
186+
f"<blockquote>"
187+
f"{if_empty2attachment(vk_rpl_msg['text'])}"
188+
"</blockquote>"
189+
)
190+
191+
for tg_chat_id in TG_CHAT_IDS:
192+
TG_BOT.send_message(
193+
tg_chat_id,
194+
tg_msg,
195+
parse_mode="HTML"
196+
)
197+
198+
except (
199+
requests.exceptions.ReadTimeout,
200+
requests.exceptions.ConnectionError
201+
):
202+
print("Временно потеряно соединение с сервером ВК")
203+
time.sleep(5)
204+
except vk_api.exceptions.ApiError:
205+
print("Произошла ошибка при обращении к VK API")
206+
time.sleep(1)
207+
except telebot.apihelper.ApiTelegramException:
208+
print("Произошла ошибка при обращении к Telegram Bot API")
209+
time.sleep(1)
210+
except Exception as e:
211+
print(f"Произошла непредвиденная ошибка: {repr(e)}")
212+
time.sleep(1)

requirements.txt

34 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)