Обновление на 4.25
parent
0aeaed5f18
commit
9c315d6f35
96
bot_io.py
96
bot_io.py
|
@ -1,5 +1,5 @@
|
|||
# Bot I/O v1.70
|
||||
# 14/06/2021
|
||||
# Bot I/O v1.82
|
||||
# 15/11/2021
|
||||
# https://t.me/ssleg © 2020 – 2021
|
||||
|
||||
import logging
|
||||
|
@ -20,7 +20,7 @@ client: TelegramClient
|
|||
msk_hour = timedelta(hours=3)
|
||||
sys_wait = 0.045
|
||||
|
||||
manager_runned = GlobalFlag()
|
||||
manager_run = GlobalFlag()
|
||||
work_queue = OutMessagesQueue()
|
||||
tg_flood_wait = GlobalFlag()
|
||||
last_send_time: datetime
|
||||
|
@ -54,7 +54,7 @@ class IncomingMessagesTimeBuffer:
|
|||
self.__buf = []
|
||||
self.__size = size
|
||||
self.__user_id = user_id
|
||||
cursor.execute('select ban_count, ban_to_date from banlist where user_id=?', (user_id,))
|
||||
cursor.execute('select ban_count, ban_to_date from ban_list where user_id=?', (user_id,))
|
||||
row = cursor.fetchone()
|
||||
if row is not None:
|
||||
self.__banned_to = datetime.strptime(row[1], '%Y-%m-%d %H:%M:%S')
|
||||
|
@ -80,17 +80,18 @@ class IncomingMessagesTimeBuffer:
|
|||
def __is_valid_timing(self, mess_date):
|
||||
i = self.__size - 1
|
||||
sec = 0
|
||||
while i > self.__size - 4:
|
||||
# while i > self.__size - 4:
|
||||
while i > 5:
|
||||
prev_date = self.__buf[i][1]
|
||||
if prev_date != 0:
|
||||
tmp = mess_date - prev_date
|
||||
if tmp.seconds < 1:
|
||||
'''if tmp.seconds < 1:
|
||||
levent = 'обнаружено слишком частое обращение: ' + str(tmp.seconds) + ' сек.'
|
||||
logging.warning(levent)
|
||||
return False
|
||||
else:
|
||||
sec += tmp.seconds
|
||||
mess_date = prev_date
|
||||
else:'''
|
||||
sec += tmp.seconds
|
||||
mess_date = prev_date
|
||||
else:
|
||||
return True
|
||||
i -= 1
|
||||
|
@ -98,7 +99,7 @@ class IncomingMessagesTimeBuffer:
|
|||
if sec >= 5:
|
||||
return True
|
||||
else:
|
||||
levent = 'обнаружены 4 сообщенния за ' + str(sec) + ' сек.'
|
||||
levent = 'обнаружены 15 сообщений за ' + str(sec) + ' сек.'
|
||||
logging.warning(levent)
|
||||
return False
|
||||
|
||||
|
@ -127,10 +128,10 @@ class IncomingMessagesTimeBuffer:
|
|||
|
||||
if self.__ban_count == 1:
|
||||
entry = (self.__user_id, self.__ban_count, str(self.__banned_to)[0:19])
|
||||
cursor.execute('insert into banlist (user_id, ban_count, ban_to_date) values (?,?,?)', entry)
|
||||
cursor.execute('insert into ban_list (user_id, ban_count, ban_to_date) values (?,?,?)', entry)
|
||||
else:
|
||||
entry = (self.__ban_count, str(self.__banned_to)[0:19], self.__user_id)
|
||||
cursor.execute('update banlist set ban_count=?, ban_to_date=? where user_id=?', entry)
|
||||
cursor.execute('update ban_list set ban_count=?, ban_to_date=? where user_id=?', entry)
|
||||
con.commit()
|
||||
return rez
|
||||
|
||||
|
@ -143,6 +144,7 @@ class IncomingMessagesTimeBuffer:
|
|||
return 2
|
||||
|
||||
|
||||
# TODO Дополнить логирование кнопок
|
||||
class BotIncomingMessagesOrder:
|
||||
"""класс порядка сообщений в диалогах"""
|
||||
|
||||
|
@ -157,16 +159,16 @@ class BotIncomingMessagesOrder:
|
|||
mess_date = message.date + msk_hour
|
||||
user_id = message.peer_id.user_id
|
||||
txt = message.text
|
||||
indx = self.__users.get(user_id)
|
||||
if indx is None:
|
||||
indx = len(self.__orders)
|
||||
self.__users[user_id] = indx
|
||||
index = self.__users.get(user_id)
|
||||
if index is None:
|
||||
index = len(self.__orders)
|
||||
self.__users[user_id] = index
|
||||
self.__orders.append(IncomingMessagesTimeBuffer(20, user_id))
|
||||
if debug:
|
||||
levent = 'открыт новый буфер, id - ' + str(user_id)
|
||||
logging.info(levent)
|
||||
|
||||
order_result = self.__orders[indx].store_mess(mess_id, mess_date)
|
||||
order_result = self.__orders[index].store_mess(mess_id, mess_date)
|
||||
if debug or io_write:
|
||||
if order_result not in [1, 2]:
|
||||
entry = (mess_id, str(mess_date)[0:19], user_id, txt)
|
||||
|
@ -178,15 +180,15 @@ class BotIncomingMessagesOrder:
|
|||
|
||||
# менеджер отложенной отправки сообщений
|
||||
async def queue_manager():
|
||||
if not manager_runned:
|
||||
manager_runned.set_true()
|
||||
if not manager_run:
|
||||
manager_run.set_true()
|
||||
levent = 'менеджер отложенных сообщений стартовал.'
|
||||
logging.info(levent)
|
||||
now = datetime.now()
|
||||
delta = now - last_send_time
|
||||
if delta.total_seconds() < 0:
|
||||
tsec = delta.total_seconds()
|
||||
seconds = abs(tsec // 1 + 1)
|
||||
t_sec = delta.total_seconds()
|
||||
seconds = abs(t_sec // 1 + 1)
|
||||
levent = 'ожидание разрешения на отправку - ' + str(seconds) + ' сек.'
|
||||
logging.info(levent)
|
||||
await sleep(seconds)
|
||||
|
@ -200,11 +202,14 @@ async def queue_manager():
|
|||
user_id = entry[0]
|
||||
message = entry[1]
|
||||
file_name = entry[2]
|
||||
buttons = entry[3]
|
||||
contact_add = entry[4]
|
||||
await sleep(sys_wait)
|
||||
if debug:
|
||||
levent = 'попытка отправки сообщения для user_id = ' + str(user_id)
|
||||
logging.info(levent)
|
||||
result_flag = await send_reply_message(user_id, message, file_name, log_parameter=True, from_queue=True)
|
||||
result_flag = await send_reply_message(user_id, message, file_name, buttons, log_parameter=True,
|
||||
contact_add=contact_add, from_queue=True)
|
||||
work_queue.set_sending_result(result_flag)
|
||||
if result_flag:
|
||||
mess_success += 1
|
||||
|
@ -212,7 +217,7 @@ async def queue_manager():
|
|||
else:
|
||||
break
|
||||
|
||||
manager_runned.set_false()
|
||||
manager_run.set_false()
|
||||
levent = 'менеджер отложенных сообщений закончил. попыток - ' + str(mess_count) + ', отправлено - ' + str(
|
||||
mess_success)
|
||||
logging.info(levent)
|
||||
|
@ -220,22 +225,24 @@ async def queue_manager():
|
|||
client.loop.create_task(queue_manager())
|
||||
|
||||
|
||||
def message_to_queue(user_id, mess, file_name):
|
||||
work_queue.add_message(user_id, mess, file_name)
|
||||
if not manager_runned:
|
||||
def message_to_queue(user_id, mess, file_name, buttons, contact_add):
|
||||
work_queue.add_message(user_id, mess, file_name, buttons, contact_add)
|
||||
if not manager_run:
|
||||
client.loop.create_task(queue_manager())
|
||||
|
||||
|
||||
# TODO Сделать поддержку форвардов
|
||||
# отправка сообщений пользователю с соблюдением требований тг
|
||||
async def send_reply_message(user_id, message, file_name=None, log_parameter=False, contact_add=True, from_queue=False):
|
||||
async def send_reply_message(user_id, message, file_name=None, buttons=None, log_parameter=False, contact_add=True,
|
||||
from_queue=False):
|
||||
global last_send_time
|
||||
now = datetime.now()
|
||||
delta = now - last_send_time
|
||||
|
||||
if not from_queue:
|
||||
find_flag, indx = work_queue.is_user_in_queue(user_id)
|
||||
find_flag, index = work_queue.is_user_in_queue(user_id)
|
||||
if find_flag:
|
||||
message_to_queue(user_id, message, file_name)
|
||||
message_to_queue(user_id, message, file_name, buttons, contact_add)
|
||||
levent = 'ответ поставлен в очередь. user_id = ' + str(user_id)
|
||||
logging.warning(levent)
|
||||
return False
|
||||
|
@ -245,7 +252,7 @@ async def send_reply_message(user_id, message, file_name=None, log_parameter=Fal
|
|||
user_delta = now - timestamp
|
||||
if user_delta.total_seconds() < 1:
|
||||
if not from_queue:
|
||||
message_to_queue(user_id, message, file_name)
|
||||
message_to_queue(user_id, message, file_name, buttons, contact_add)
|
||||
levent = 'ответ поставлен в очередь, дельта пользователя - ' + str(
|
||||
user_delta.total_seconds()) + ' сек. user_id = ' + str(user_id)
|
||||
logging.warning(levent)
|
||||
|
@ -253,7 +260,7 @@ async def send_reply_message(user_id, message, file_name=None, log_parameter=Fal
|
|||
|
||||
if delta.total_seconds() > 0.04:
|
||||
try:
|
||||
await client.send_message(user_id, message, file=file_name)
|
||||
await client.send_message(user_id, message, file=file_name, buttons=buttons)
|
||||
last_send_time = datetime.now()
|
||||
|
||||
if log_parameter or debug:
|
||||
|
@ -278,25 +285,32 @@ async def send_reply_message(user_id, message, file_name=None, log_parameter=Fal
|
|||
|
||||
except errors.FloodWaitError as e:
|
||||
seconds = e.seconds
|
||||
levent = 'антифлуд телеграм сработал, время ожидания - ' + str(seconds) + ' сек.'
|
||||
levent = 'сработал телеграм flood_wait, время ожидания - ' + str(seconds) + ' сек.'
|
||||
logging.warning(levent)
|
||||
tg_flood_wait.set_true()
|
||||
last_send_time = now + timedelta(seconds=seconds + 1)
|
||||
if not from_queue:
|
||||
message_to_queue(user_id, message, file_name)
|
||||
message_to_queue(user_id, message, file_name, buttons, contact_add)
|
||||
return False
|
||||
|
||||
except errors.UserIsBlockedError as e:
|
||||
levent = 'пользователь заблокировал бота (user_id = ' + str(user_id) + '): ' + str(e)
|
||||
if debug:
|
||||
logging.warning(levent)
|
||||
if from_queue:
|
||||
return True
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
levent = 'что-то пошло не так при отправке (user_id = ' + str(user_id) + '): ' + str(e)
|
||||
if debug:
|
||||
logging.error(levent)
|
||||
else:
|
||||
logging.warning(levent)
|
||||
logging.error(levent)
|
||||
if from_queue:
|
||||
return True
|
||||
return False
|
||||
|
||||
else:
|
||||
if not from_queue:
|
||||
message_to_queue(user_id, message, file_name)
|
||||
message_to_queue(user_id, message, file_name, buttons, contact_add)
|
||||
levent = 'ответ поставлен в очередь, дельта - ' + str(delta.total_seconds()) + ' сек. user_id = ' + str(
|
||||
user_id)
|
||||
logging.warning(levent)
|
||||
|
@ -354,7 +368,7 @@ async def init(cli, debug_mode, adm_ids, io_write_mode):
|
|||
account_name text
|
||||
);
|
||||
|
||||
CREATE TABLE banlist
|
||||
CREATE TABLE ban_list
|
||||
(
|
||||
user_id int,
|
||||
ban_count int,
|
||||
|
@ -374,14 +388,14 @@ async def init(cli, debug_mode, adm_ids, io_write_mode):
|
|||
|
||||
# завершение работы
|
||||
async def terminate():
|
||||
if manager_runned:
|
||||
if manager_run:
|
||||
levent = 'bot I/O, ожидание отправки всех сообщений.'
|
||||
logging.info(levent)
|
||||
count = 6
|
||||
while count > 0:
|
||||
await sleep(5)
|
||||
count -= 1
|
||||
if not manager_runned:
|
||||
if not manager_run:
|
||||
break
|
||||
con.close()
|
||||
levent = 'bot I/O остановлен.'
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# Bot I/O classes v1.00
|
||||
# 14/06/2021
|
||||
# https://t.me/ssleg © 2020 – 2021
|
||||
# Bot I/O classes v1.03
|
||||
# 15/11/2021
|
||||
# https://t.me/ssleg © 2021
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# TODO Сделать нормальную инициализацию модуля
|
||||
class TgBotUsers:
|
||||
"""класс хранения пользователей бота"""
|
||||
|
||||
|
@ -21,8 +22,8 @@ class TgBotUsers:
|
|||
f_name = row[1]
|
||||
l_name = row[2]
|
||||
user_acc = row[3]
|
||||
indx = len(self.__timestamps)
|
||||
self.__users[user_id] = (indx, f_name, l_name, user_acc)
|
||||
index = len(self.__timestamps)
|
||||
self.__users[user_id] = (index, f_name, l_name, user_acc)
|
||||
self.__timestamps.append(datetime(year=2020, month=1, day=1))
|
||||
|
||||
def is_bot_user(self, user_id):
|
||||
|
@ -43,29 +44,29 @@ class TgBotUsers:
|
|||
values (?,?,?,?)''', entry)
|
||||
self.__con.commit()
|
||||
timestamp = datetime.now()
|
||||
indx = len(self.__timestamps)
|
||||
self.__users[user_id] = (indx, f_name, l_name, user_acc)
|
||||
index = len(self.__timestamps)
|
||||
self.__users[user_id] = (index, f_name, l_name, user_acc)
|
||||
self.__timestamps.append(timestamp)
|
||||
|
||||
def update_user_mess_timestamp(self, user_id):
|
||||
timestamp = datetime.now()
|
||||
user = self.__users.get(user_id)
|
||||
if user is not None:
|
||||
indx = user[0]
|
||||
self.__timestamps[indx] = timestamp
|
||||
index = user[0]
|
||||
self.__timestamps[index] = timestamp
|
||||
|
||||
def get_user_mess_timestamp(self, user_id):
|
||||
user = self.__users.get(user_id)
|
||||
if user is not None:
|
||||
indx = user[0]
|
||||
return self.__timestamps[indx]
|
||||
index = user[0]
|
||||
return self.__timestamps[index]
|
||||
else:
|
||||
return datetime(year=2020, month=1, day=1)
|
||||
|
||||
def get_users_list(self):
|
||||
excluded_users = []
|
||||
now = datetime.now()
|
||||
self.__cursor.execute('select user_id, ban_to_date from banlist')
|
||||
self.__cursor.execute('select user_id, ban_to_date from ban_list')
|
||||
for row in self.__cursor.fetchall():
|
||||
banned_to = datetime.strptime(row[1], '%Y-%m-%d %H:%M:%S')
|
||||
delta = now - banned_to
|
||||
|
@ -95,21 +96,21 @@ class OutMessagesQueue:
|
|||
|
||||
def is_user_in_queue(self, user_id):
|
||||
i = 0
|
||||
finded_flag = False
|
||||
found_flag = False
|
||||
while i < len(self.__queue):
|
||||
if self.__queue[i][0] == user_id:
|
||||
finded_flag = True
|
||||
found_flag = True
|
||||
break
|
||||
i += 1
|
||||
|
||||
return finded_flag, i
|
||||
return found_flag, i
|
||||
|
||||
def add_message(self, user_id, message_text, file_name):
|
||||
finded_flag, i = self.is_user_in_queue(user_id)
|
||||
if not finded_flag:
|
||||
def add_message(self, user_id, message_text, file_name, buttons, contact_add):
|
||||
found_flag, i = self.is_user_in_queue(user_id)
|
||||
if not found_flag:
|
||||
self.__queue.append([user_id])
|
||||
|
||||
self.__queue[i].append((user_id, message_text, file_name))
|
||||
self.__queue[i].append((user_id, message_text, file_name, buttons, contact_add))
|
||||
|
||||
def get_next_message(self):
|
||||
element = self.__queue[self.__position_i][1]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Magic wand v4.23
|
||||
# 14/06/2021
|
||||
# Magic wand v4.25
|
||||
# 26/09/2021
|
||||
# https://t.me/ssleg © 2020 – 2021
|
||||
|
||||
import logging
|
||||
|
@ -18,6 +18,7 @@ from telethon import TelegramClient, events
|
|||
|
||||
import bot_io
|
||||
import main_module
|
||||
# import module_two
|
||||
from tg_utils import get_tg_client, get_bot_key, GlobalFlag, GlobalCounter
|
||||
|
||||
# загрузка конфигурации из ini файла
|
||||
|
@ -52,8 +53,8 @@ bot_account = bot_config.get('bot_db_account', 'bot_account')
|
|||
|
||||
# глобальные переменные
|
||||
sigterm_flag = GlobalFlag()
|
||||
logsize = GlobalCounter()
|
||||
laststring = GlobalCounter()
|
||||
log_size = GlobalCounter()
|
||||
last_string = GlobalCounter()
|
||||
|
||||
error_count = GlobalCounter()
|
||||
started_time: datetime
|
||||
|
@ -61,6 +62,7 @@ next_stat = GlobalCounter(3600)
|
|||
|
||||
# инициализация лога и параметров подключения
|
||||
log_file = RotatingFileHandler(logfile_name, 'a', maxBytes=524288, backupCount=10, encoding='utf=8')
|
||||
# noinspection SpellCheckingInspection
|
||||
log_file.setFormatter(logging.Formatter('%(levelname)s %(module)-13s [%(asctime)s] %(message)s'))
|
||||
# noinspection PyArgumentList
|
||||
logging.basicConfig(level=logging.INFO, handlers=[log_file])
|
||||
|
@ -101,14 +103,14 @@ def get_my_version():
|
|||
|
||||
# записывает начальные параметры лог-файла
|
||||
def init_log_var():
|
||||
logsize.set(path.getsize(logfile_name))
|
||||
log_size.set(path.getsize(logfile_name))
|
||||
|
||||
file = open(logfile_name, 'r', encoding='utf-8')
|
||||
allfile = file.readlines()
|
||||
all_file = file.readlines()
|
||||
file.close()
|
||||
|
||||
laststring.set(len(allfile))
|
||||
levent = 'начальные параметры этого логфайла: ' + str(logsize) + ' байт, ' + str(laststring) + ' строк.'
|
||||
last_string.set(len(all_file))
|
||||
levent = 'начальные параметры этого лог-файла: ' + str(log_size) + ' байт, ' + str(last_string) + ' строк.'
|
||||
logging.info(levent)
|
||||
|
||||
|
||||
|
@ -127,12 +129,13 @@ async def init():
|
|||
|
||||
await bot_io.init(client, debug, admins_ids, io_write)
|
||||
await main_module.init(client, debug, admins_ids)
|
||||
# await module_two.init(client)
|
||||
|
||||
levent = 'инициализация завершена.'
|
||||
logging.info(levent)
|
||||
|
||||
|
||||
# возвращает строку со временем аптайма
|
||||
# возвращает строку со временем аптайм
|
||||
def get_uptime():
|
||||
now = datetime.now()
|
||||
delta = now - started_time
|
||||
|
@ -155,7 +158,7 @@ def get_up_seconds():
|
|||
return delta.days * 86400 + delta.seconds
|
||||
|
||||
|
||||
# эвент хэндлер, отвечающий за команды админа (статистика работы, рассылки)
|
||||
# обработчик событий, отвечающий за команды админа (статистика работы, рассылки)
|
||||
async def admins_command(event):
|
||||
from_id = event.message.peer_id.user_id
|
||||
command = event.message.text
|
||||
|
@ -191,7 +194,7 @@ def stat_upload():
|
|||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
# noinspection HttpUrlsUsage
|
||||
stat_upload_url = 'http://77.83.92.107/stat_up'
|
||||
stat_upload_url = 'http://188.124.50.148/stat_up'
|
||||
hash_md5 = md5(bot_key.encode())
|
||||
request_json = {'protocol_version': '1.1', 'application': 'Magic Wand', 'app_version': get_my_version(),
|
||||
'uptime': get_up_seconds(), 'errors': error_count.get(), 'fingerprint': hash_md5.hexdigest()}
|
||||
|
@ -225,11 +228,11 @@ async def send_notices(mess):
|
|||
logging.info(levent)
|
||||
|
||||
|
||||
# чтение изменений логфайла и поиск ошибок в нем
|
||||
# чтение изменений лог-файла и поиск ошибок в нем
|
||||
def log_reader():
|
||||
file = open(logfile_name, 'r', encoding='utf-8')
|
||||
readcount = 0
|
||||
workcount = laststring.get()
|
||||
read_count = 0
|
||||
work_count = last_string.get()
|
||||
i = 0
|
||||
flag = False
|
||||
errmsg = ''
|
||||
|
@ -237,9 +240,9 @@ def log_reader():
|
|||
while i < 1:
|
||||
string = file.readline()
|
||||
if string != '':
|
||||
readcount += 1
|
||||
if readcount > workcount:
|
||||
laststring.increment()
|
||||
read_count += 1
|
||||
if read_count > work_count:
|
||||
last_string.increment()
|
||||
if flag:
|
||||
if len(string) > 1:
|
||||
if string.find('[20') > -1:
|
||||
|
@ -260,12 +263,12 @@ def log_reader():
|
|||
file.close()
|
||||
|
||||
|
||||
# наблюдатель за логфайлом, проверяет изменения раз в минуту
|
||||
# наблюдатель за лог-файлом, проверяет изменения раз в минуту
|
||||
def log_watcher():
|
||||
if sigterm_flag:
|
||||
filesize = path.getsize(logfile_name)
|
||||
if filesize > logsize:
|
||||
logsize.set(filesize)
|
||||
if filesize > log_size:
|
||||
log_size.set(filesize)
|
||||
client.loop.call_soon(log_reader)
|
||||
|
||||
if bot_stats:
|
||||
|
@ -286,7 +289,7 @@ async def terminate():
|
|||
logging.info(levent)
|
||||
|
||||
|
||||
# хэндлер сигнала сигтерм, возникающего при перезапуске системы или бота.
|
||||
# обработчик сигнала sigterm, возникающего при перезапуске системы или бота.
|
||||
# вызывает процедуру корректного завершения бота и модулей
|
||||
# noinspection PyUnusedLocal
|
||||
def sigterm_call(signum, frame):
|
||||
|
@ -297,14 +300,15 @@ def sigterm_call(signum, frame):
|
|||
client.loop.create_task(terminate())
|
||||
|
||||
|
||||
signal.signal(signal.SIGTERM, sigterm_call)
|
||||
|
||||
# старт telethon и основной инициализации
|
||||
client.start(bot_token=bot_key)
|
||||
client.loop.run_until_complete(init())
|
||||
if __name__ == '__main__':
|
||||
signal.signal(signal.SIGTERM, sigterm_call)
|
||||
|
||||
started_time = datetime.now()
|
||||
client.add_event_handler(admins_command, events.NewMessage(chats=admins_ids, incoming=True))
|
||||
client.loop.call_later(60, log_watcher)
|
||||
client.start(bot_token=bot_key)
|
||||
client.loop.run_until_complete(init())
|
||||
|
||||
client.run_until_disconnected()
|
||||
started_time = datetime.now()
|
||||
client.add_event_handler(admins_command, events.NewMessage(chats=admins_ids, incoming=True))
|
||||
client.loop.call_later(60, log_watcher)
|
||||
|
||||
client.run_until_disconnected()
|
||||
|
|
354
tg_utils.py
354
tg_utils.py
|
@ -1,79 +1,144 @@
|
|||
# Tg utils v2.22
|
||||
# 14/06/2021
|
||||
# Tg utils v2.50
|
||||
# 10/10/2021
|
||||
# https://t.me/ssleg © 2020 – 2021
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from time import sleep
|
||||
|
||||
import psycopg2
|
||||
import psycopg2.extensions
|
||||
from telethon import TelegramClient
|
||||
|
||||
|
||||
# TODO Добавить executemany и executescript
|
||||
class PgServer:
|
||||
"""класс postgres-сервер"""
|
||||
|
||||
__slots__ = ['__con', '__control_flag', '__cursor', '__db_name']
|
||||
__slots__ = ['__con', '__cursor', '__db_name', '__db_host', '__retry_count', '__version', '__connected']
|
||||
|
||||
def __init__(self, dbc=None, db_name='mydb'):
|
||||
if dbc is None:
|
||||
self.__con = psycopg2.connect(database=db_name,
|
||||
def __init__(self, db_name='tg_bots', db_host='127.0.0.1'):
|
||||
self.__con: psycopg2.extensions.connection
|
||||
self.__cursor: psycopg2.extensions.cursor
|
||||
self.__db_name = db_name
|
||||
self.__db_host = db_host
|
||||
self.__retry_count = 0
|
||||
self.__connected = False
|
||||
self.__version = ''
|
||||
self.__establish_connect(first_connect=True)
|
||||
|
||||
def __establish_connect(self, first_connect=False):
|
||||
self.__connected = False
|
||||
while not self.__connected and self.__retry_count < 3:
|
||||
self.__connected = self.__connect(first_connect=first_connect)
|
||||
if not self.__connected:
|
||||
self.__retry_count += 1
|
||||
sleep(1)
|
||||
|
||||
if not self.__connected:
|
||||
if first_connect:
|
||||
levent = 'postgresql, подключиться к базе не удалось.'
|
||||
else:
|
||||
levent = 'postgresql, восстановить соединение не удалось.'
|
||||
logging.error(levent)
|
||||
else:
|
||||
if not first_connect:
|
||||
levent = 'postgresql, соединение восстановлено.'
|
||||
logging.info(levent)
|
||||
else:
|
||||
sql = "select setting from pg_config where name='VERSION'"
|
||||
row = self.exec(sql, return_type=1)
|
||||
self.__version = row[0]
|
||||
|
||||
def __connect(self, first_connect=False):
|
||||
try:
|
||||
if not first_connect:
|
||||
self.__cursor.close()
|
||||
self.__con.close()
|
||||
self.__con = psycopg2.connect(database=self.__db_name,
|
||||
user='postgres',
|
||||
password='1234',
|
||||
host='127.0.0.1',
|
||||
host=self.__db_host,
|
||||
port='5432')
|
||||
self.__control_flag = True
|
||||
self.__db_name = db_name
|
||||
else:
|
||||
self.__con = dbc
|
||||
self.__control_flag = False
|
||||
self.__db_name = ''
|
||||
|
||||
self.__cursor = self.__con.cursor()
|
||||
|
||||
def commit(self):
|
||||
self.__con.commit()
|
||||
|
||||
def rollback(self):
|
||||
self.__con.rollback()
|
||||
|
||||
def exec(self, sql, req_data=None, return_type=None, retry_count=0):
|
||||
try:
|
||||
if req_data is not None:
|
||||
self.__cursor.execute(sql, req_data)
|
||||
else:
|
||||
self.__cursor.execute(sql)
|
||||
|
||||
if return_type is not None:
|
||||
if return_type == 0:
|
||||
return self.__cursor.fetchall()
|
||||
else:
|
||||
return self.__cursor.fetchone()
|
||||
else:
|
||||
return True
|
||||
self.__cursor = self.__con.cursor()
|
||||
self.__retry_count = 0
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
levent = 'postgres error: ' + str(e)
|
||||
logging.error(levent)
|
||||
if self.__control_flag:
|
||||
if retry_count < 3:
|
||||
self.__con.close()
|
||||
self.__con = psycopg2.connect(database=self.__db_name,
|
||||
user='postgres',
|
||||
password='1234',
|
||||
host='127.0.0.1',
|
||||
port='5432')
|
||||
self.__cursor = self.__con.cursor()
|
||||
|
||||
return self.exec(sql, req_data, return_type, retry_count=retry_count + 1)
|
||||
|
||||
if first_connect:
|
||||
levent = 'postgresql, ошибка подключения к бд: ' + str(e)
|
||||
else:
|
||||
levent = 'postgresql, ошибка при восстановлении подключения: ' + str(e)
|
||||
logging.warning(levent)
|
||||
return False
|
||||
|
||||
def __del__(self):
|
||||
self.__cursor.close()
|
||||
if self.__control_flag:
|
||||
self.__con.close()
|
||||
else:
|
||||
def commit(self):
|
||||
if self.__connected:
|
||||
self.__con.commit()
|
||||
return True
|
||||
return
|
||||
|
||||
def rollback(self):
|
||||
if self.__connected:
|
||||
self.__con.rollback()
|
||||
return True
|
||||
return False
|
||||
|
||||
def exec(self, sql, req_data=None, return_type=None):
|
||||
if self.__connected:
|
||||
try:
|
||||
if req_data is not None:
|
||||
self.__cursor.execute(sql, req_data)
|
||||
else:
|
||||
self.__cursor.execute(sql)
|
||||
|
||||
if return_type is not None:
|
||||
if return_type == 0:
|
||||
return self.__cursor.fetchall()
|
||||
else:
|
||||
return self.__cursor.fetchone()
|
||||
else:
|
||||
return True
|
||||
|
||||
except psycopg2.IntegrityError as e:
|
||||
levent = 'postgresql, ошибка целостности данных: ' + str(e.pgerror)
|
||||
logging.error(levent)
|
||||
return False
|
||||
|
||||
except psycopg2.OperationalError as e:
|
||||
disconnect_codes = ['57P01', '57P02', '57P03']
|
||||
if e.pgcode in disconnect_codes:
|
||||
levent = 'postgresql отвалился, ошибка: ' + str(e.pgerror)
|
||||
logging.warning(levent)
|
||||
self.__establish_connect()
|
||||
if not self.__connected:
|
||||
return False
|
||||
else:
|
||||
return self.exec(sql, req_data, return_type)
|
||||
else:
|
||||
levent = 'postgresql, операционная ошибка: ' + str(e)
|
||||
logging.error(levent)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
levent = 'postgresql, ошибка: ' + str(e)
|
||||
logging.error(levent)
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def is_connected(self):
|
||||
return self.__connected
|
||||
|
||||
def get_version(self):
|
||||
return self.__version
|
||||
|
||||
def __str__(self):
|
||||
return self.__version
|
||||
|
||||
def __del__(self):
|
||||
if self.__connected:
|
||||
self.__cursor.close()
|
||||
self.__con.close()
|
||||
|
||||
|
||||
class PgLock:
|
||||
|
@ -81,9 +146,9 @@ class PgLock:
|
|||
|
||||
__slots__ = ['__srv', '__cursor', '__lock_name', '__debug']
|
||||
|
||||
def __init__(self, dbc=None, lck='default_lock', debug=False):
|
||||
self.__srv = PgServer(dbc)
|
||||
self.__lock_name = lck
|
||||
def __init__(self, lock_name='default', debug=False):
|
||||
self.__srv = PgServer()
|
||||
self.__lock_name = lock_name
|
||||
self.__debug = debug
|
||||
|
||||
def lock(self):
|
||||
|
@ -157,16 +222,23 @@ class GlobalCounter:
|
|||
def __isub__(self, other):
|
||||
if GlobalCounter.__is_valid_arg(other):
|
||||
self.__count -= other
|
||||
return self
|
||||
|
||||
def __iadd__(self, other):
|
||||
if GlobalCounter.__is_valid_arg(other):
|
||||
self.__count += other
|
||||
return self
|
||||
|
||||
def __eq__(self, other):
|
||||
if self.__count == other:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
if self.__count != other:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __ge__(self, other):
|
||||
if self.__count >= other:
|
||||
return True
|
||||
|
@ -183,7 +255,7 @@ class GlobalCounter:
|
|||
return False
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.__count <= other:
|
||||
if self.__count < other:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -221,6 +293,119 @@ class GlobalFlag:
|
|||
return str(self.__flag)
|
||||
|
||||
|
||||
class GlobalFloat:
|
||||
"""глобальная универсальная переменная"""
|
||||
|
||||
__slots__ = ['__value']
|
||||
|
||||
@staticmethod
|
||||
def __is_valid_arg(arg):
|
||||
if type(arg) is int or type(arg) is float:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __init__(self, init_value=0):
|
||||
if GlobalFloat.__is_valid_arg(init_value):
|
||||
self.__value = float(init_value)
|
||||
else:
|
||||
self.__value = 0
|
||||
|
||||
def get(self):
|
||||
return self.__value
|
||||
|
||||
def set(self, value):
|
||||
if GlobalFloat.__is_valid_arg(value):
|
||||
self.__value = float(value)
|
||||
return True
|
||||
return False
|
||||
|
||||
def __isub__(self, other):
|
||||
if GlobalFloat.__is_valid_arg(other):
|
||||
self.__value -= other
|
||||
return self
|
||||
|
||||
def __iadd__(self, other):
|
||||
if GlobalFloat.__is_valid_arg(other):
|
||||
self.__value += other
|
||||
return self
|
||||
|
||||
def __radd__(self, other):
|
||||
if GlobalFloat.__is_valid_arg(other):
|
||||
return other + self.__value
|
||||
|
||||
def __rsub__(self, other):
|
||||
if GlobalFloat.__is_valid_arg(other):
|
||||
return other - self.__value
|
||||
|
||||
def __add__(self, other):
|
||||
if GlobalFloat.__is_valid_arg(other):
|
||||
return self.__value + other
|
||||
|
||||
def __sub__(self, other):
|
||||
if GlobalFloat.__is_valid_arg(other):
|
||||
return self.__value - other
|
||||
|
||||
def __eq__(self, other):
|
||||
if self.__value == other:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
if self.__value != other:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __ge__(self, other):
|
||||
if self.__value >= other:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __gt__(self, other):
|
||||
if self.__value > other:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __le__(self, other):
|
||||
if self.__value <= other:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.__value < other:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return str(round(self.__value, 2))
|
||||
|
||||
|
||||
class ErrorClass:
|
||||
"""класс возврата ошибки с кодом"""
|
||||
|
||||
__slots__ = ['__success', '__error_code']
|
||||
|
||||
def __init__(self):
|
||||
self.__success = True
|
||||
self.__error_code = 0
|
||||
|
||||
def set_error(self, error_code: int):
|
||||
self.__success = False
|
||||
if 0 < error_code < 1000 and type(error_code) == int:
|
||||
self.__error_code = error_code
|
||||
|
||||
def get_error_code(self):
|
||||
return self.__error_code
|
||||
|
||||
def __bool__(self):
|
||||
return self.__success
|
||||
|
||||
def __str__(self):
|
||||
if self.__success:
|
||||
return 'No error'
|
||||
else:
|
||||
return 'Error code: ' + str(self.__error_code)
|
||||
|
||||
|
||||
def t_stamp():
|
||||
now = datetime.now()
|
||||
stamp = datetime.strftime(now, '%d/%m %Y %H:%M:%S')
|
||||
|
@ -233,12 +418,12 @@ def t_stamp_sh():
|
|||
return stamp
|
||||
|
||||
|
||||
# ловеркейс имени tg канала с проверкой валидности
|
||||
def set_ch_name_lower(teststr):
|
||||
# lowercase имени tg канала с проверкой валидности
|
||||
def set_ch_name_lower(test_str):
|
||||
i = 0
|
||||
rezult = ''
|
||||
while i < len(teststr):
|
||||
char = teststr[i]
|
||||
result = ''
|
||||
while i < len(test_str):
|
||||
char = test_str[i]
|
||||
code = ord(char)
|
||||
|
||||
if code == 95:
|
||||
|
@ -253,13 +438,13 @@ def set_ch_name_lower(teststr):
|
|||
if 96 < code < 123:
|
||||
pass
|
||||
else:
|
||||
levent = 'ошибка конвертации канала: ' + str(i) + ' ' + str(code) + ' ' + char + ' ' + teststr
|
||||
levent = 'ошибка конвертации канала: ' + str(i) + ' ' + str(code) + ' ' + char + ' ' + test_str
|
||||
logging.warning(levent)
|
||||
return False
|
||||
rezult += chr(code)
|
||||
result += chr(code)
|
||||
i += 1
|
||||
|
||||
return rezult
|
||||
return result
|
||||
|
||||
|
||||
# сборка имени юзера в читабельное
|
||||
|
@ -281,45 +466,36 @@ def set_name_printable(first, last, account=None, phone=None, ad=''):
|
|||
|
||||
|
||||
# вывод числа с разделителями тысяч
|
||||
def set_int_printable(integer, razd=' '):
|
||||
def set_int_printable(integer, separator=' '):
|
||||
string = '{:,}'.format(integer)
|
||||
string = string.replace(',', razd)
|
||||
string = string.replace(',', separator)
|
||||
return string
|
||||
|
||||
|
||||
# версия сервера постгрес
|
||||
def get_server_version(dbc=None):
|
||||
srv = PgServer(dbc)
|
||||
sql = "select setting from pg_config where name='VERSION'"
|
||||
row = srv.exec(sql, return_type=1)
|
||||
version = row[0]
|
||||
return version
|
||||
|
||||
|
||||
# создает обьект клиента с параметрами из бд
|
||||
def get_tg_client(name, dbc=None, cust_name=None):
|
||||
srv = PgServer(dbc)
|
||||
# создает объект клиента с параметрами из бд
|
||||
def get_tg_client(name, custom_name=None):
|
||||
srv = PgServer()
|
||||
|
||||
n_api = name + '_api_id'
|
||||
row = srv.exec('select value from parameters where name=%s', (n_api,), return_type=1)
|
||||
row = srv.exec('select value from logins where name=%s', (n_api,), return_type=1)
|
||||
api_id = int(row[0])
|
||||
|
||||
n_hash = name + '_api_hash'
|
||||
row = srv.exec('select value from parameters where name=%s', (n_hash,), return_type=1)
|
||||
row = srv.exec('select value from logins where name=%s', (n_hash,), return_type=1)
|
||||
api_hash = row[0]
|
||||
|
||||
if cust_name is None:
|
||||
if custom_name is None:
|
||||
client = TelegramClient(name, api_id, api_hash)
|
||||
else:
|
||||
client = TelegramClient(cust_name, api_id, api_hash)
|
||||
client = TelegramClient(custom_name, api_id, api_hash)
|
||||
return client
|
||||
|
||||
|
||||
# достает ключ бота из бд
|
||||
def get_bot_key(name, dbc=None):
|
||||
srv = PgServer(dbc)
|
||||
def get_bot_key(name):
|
||||
srv = PgServer()
|
||||
|
||||
bot = name + '_key'
|
||||
row = srv.exec('select value from parameters where name=%s', (bot,), return_type=1)
|
||||
row = srv.exec('select value from logins where name=%s', (bot,), return_type=1)
|
||||
bot_key = row[0]
|
||||
return bot_key
|
||||
|
|
Loading…
Reference in New Issue