-
Notifications
You must be signed in to change notification settings - Fork 0
/
oblivion.py
223 lines (184 loc) · 10 KB
/
oblivion.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# dependencies: []
"""
Финальное задание деда по рассылке прощальной речи.
Эдакий апофеоз с небольшой надеждой на будущую реинкарнацию и второе пришествие. Но уже, вероятно, от других Создателей.
"""
import vk_api
from vk_api.utils import get_random_id
import telebot
import toml
import sys
import logging
import time
import traceback
from datetime import datetime, date, timedelta
import math
import sqlite3
import pickle
global config
global vk_session
global vk
global num_of_base
# common init
logger = logging.getLogger('oblivion')
console_handler = logging.StreamHandler()
logger.setLevel(logging.INFO)
logger.addHandler(console_handler)
try:
config = toml.load('configuration.toml')
except FileNotFoundError:
logger.critical('configuration.toml не найден!')
sys.exit()
token = config.get('Kiberded').get('token')
tg_token = config.get('Kiberded').get('token_telegram')
path = config.get('Kiberded').get('path')
tg_admin_chat = config.get('Kiberded').get('telegram_admin_chat')
# /common init
bot = telebot.TeleBot(tg_token)
now_date = datetime.now().strftime('%Y-%m-%d')
def send_message(message, peer_id, attachment=''):
"""
Отправка сообщения с обработкой Flood-control
:param int peer_id: id беседы или пользователя для отправки сообщения.
:param str message: сообщение, обязательный аргумент
:param attachment: вложение (опционально)
"""
try:
return vk_session.method("messages.send", {'v': 5.131,
'random_id': get_random_id(),
'message': message,
'attachment': attachment,
'peer_ids': peer_id})
except vk_api.exceptions.ApiError as vk_error:
if '[9]' in str(vk_error): # ошибка flood-control: если флудим, то ждем секунду ответа
time.sleep(1)
send_message(message, peer_id)
logger.warning('Flood-control, спим секунду')
elif '[10]' in str(vk_error): # Internal server error (чиво?)
logger.warning(f'Сообщение не отправлено: {vk_error}, message={message}, peer_id={peer_id}')
elif '[7]' in str(vk_error): # Permission to perform this action is denied
logger.warning(f'Сообщение не отправлено: {vk_error}, message={message}, peer_id={peer_id}')
elif '[901]' in str(vk_error): # Can't send messages for users without permission
logger.warning(f'Сообщение не отправлено: {vk_error}, message={message}, peer_id={peer_id}')
elif '[914]' in str(vk_error): # message is too long
print(f'Сообщение слишком длинное, разбиваем на части')
for i in range(math.floor(len(message)/4096)): # разбиение сообщение на части по 4кб
send_message(message[i*4096:i*4096+4096], peer_id)
if len(message) % 4096 != 0: # последний кусок сообщения
send_message(message[-(len(message) % 4096):], peer_id, attachment)
elif attachment: # если вдруг длина сообщения кратна 4кб и есть вложение - отправляем его без текста
send_message(message='', peer_id=peer_id, attachment=attachment)
elif '[925]' in str(vk_error):
pass
else:
logger.error(f'Сообщение не отправлено: measage={message}, peer_id={peer_id}')
raise Exception(vk_error)
def send_tg_message(chat_id, text, **kwargs) -> telebot.types.Message:
"""
Функция отправки сообщений. Создана одновременно и для обхода ограничения на максимальную длину текста, и для
автодампа сообщения. Возвращается API-reply отправленного сообщения; если текст больше 4096 символов - то оно
делится и возвращается api-peply последнего отправленного сообщения
Telegram documentation: https://core.telegram.org/bots/api#sendmessage
:param chat_id: Unique identifier for the target chat or username of the target channel (in the format
@channelusername)
:param text: Text of the message to be sent
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline
URLs in your bot's message.
:param entities: List of special entities that appear in message text, which can be specified instead of parse_mode
:param disable_web_page_preview: Disables link previews for links in this message
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
:param protect_content: If True, the message content will be hidden for all users except for the target user
:param reply_to_message_id: If the message is a reply, ID of the original message
:param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to
message is not found
:param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply
keyboard, instructions to remove reply keyboard or to force a reply from the user.
:param timeout:
:return: API reply (JSON-serialized message object)
"""
msg = 0 # дабы IDE не ругалась, далее эта переменная так и так перезапишется
if len(text) > 4096: # обход ограничения
splitted_text = telebot.util.smart_split(text, chars_per_string=3000)
for text in splitted_text:
msg = bot.send_message(chat_id, text, **kwargs)
else:
msg = bot.send_message(chat_id, text, **kwargs)
return msg
def get_users(source='vk') -> list: # список юзеров
"""
Получение списка всех пользователей
:param str source: 'vk' / 'tg' - источник сообщения
:return: список [(user_id, count), ...]
"""
with sqlite3.connect(f'{path}admindb/databases/group_ids.db') as con:
cursor = con.cursor()
data = []
for row in cursor.execute(f'SELECT * FROM user_ids WHERE {source}_id'):
data.append((int(row[0]))) if source == 'vk' else data.append((int(row[4])))
con.close()
return data
def get_groups(source='vk') -> list: # список бесед
"""
Получение списка всех бесед
:param str source: 'vk' / 'tg' - источник сообщения
:return: список [(user_id, count), ...]
"""
with sqlite3.connect(f'{path}admindb/databases/group_ids.db') as con:
cursor = con.cursor()
data = []
for row in cursor.execute(f'SELECT * FROM group_gcals WHERE {source}_chat_id'):
data.append((int(row[6]))) if source == 'vk' else data.append((int(row[7])))
con.close()
return data
def initialization():
global vk_session
global vk
vk_session = vk_api.VkApi(token=token)
vk = vk_session.get_api()
send_message('Запуск "Обливиона"', 2000000001)
send_tg_message(-1001668185586, 'Запуск "Обливиона"')
logger.warning('Запуск "Обливиона"')
return 0
if initialization():
logger.critical('Инициализация пошла по одному месту.')
sys.exit()
# all_vk_users = get_users('vk')
all_vk_users = []
all_tg_users = get_users('tg')
all_vk_groups = get_groups('vk')
all_tg_groups = get_groups('tg')
with open(f'{path}last_message.txt', 'r', encoding='utf-8') as file:
message = file.read()
error_counts = 0
global_error = ''
for vk_user in all_vk_users:
try:
send_message(message, vk_user)
except Exception as e:
error_counts += 1
global_error += f'vk_user {vk_user}\n'
logger.warning(f'Произошла ошибка при выполнении Oblivion (vk_user {vk_user}): {str(e)}\n{traceback.format_exc()}')
for tg_user in all_tg_users:
try:
send_tg_message(tg_user, message, disable_web_page_preview=True)
except Exception as e:
error_counts += 1
global_error += f'tg_user {tg_user}\n'
logger.warning(f'Произошла ошибка при выполнении Oblivion (tg_user {tg_user}): {str(e)}\n{traceback.format_exc()}')
for vk_group in all_vk_groups:
try:
send_message(message, vk_group)
except Exception as e:
error_counts += 1
global_error += f'vk_group {vk_group}\n'
logger.warning(f'Произошла ошибка при выполнении Oblivion (vk_group {vk_group}): {str(e)}\n{traceback.format_exc()}')
for tg_group in all_tg_groups:
try:
send_tg_message(tg_group, message, disable_web_page_preview=True)
except Exception as e:
error_counts += 1
global_error += f'tg_group {tg_group}\n'
logger.warning(f'Произошла ошибка при выполнении Oblivion (tg_group {tg_group}): {str(e)}\n{traceback.format_exc()}')
send_tg_message(tg_admin_chat, f'Код Обливиона выполнен.\n\nПользователей ВК: {len(all_vk_users)}\nГрупп ВК: {len(all_vk_groups)}\nПользователей ТГ: {len(all_tg_users)}\nГрупп ТГ: {len(all_tg_groups)}\n\nВсего ошибок при отправке: {error_counts}\n\nСообщения не доставлены следующим адресатам:{global_error}')