Обновление на 4.25

master
ssleg 2022-09-26 19:06:52 +03:00
parent 0aeaed5f18
commit 9c315d6f35
Signed by: anton
GPG Key ID: 50F7E97F96C07ECF
4 changed files with 373 additions and 178 deletions

View File

@ -1,5 +1,5 @@
# Bot I/O v1.70 # Bot I/O v1.82
# 14/06/2021 # 15/11/2021
# https://t.me/ssleg © 2020 2021 # https://t.me/ssleg © 2020 2021
import logging import logging
@ -20,7 +20,7 @@ client: TelegramClient
msk_hour = timedelta(hours=3) msk_hour = timedelta(hours=3)
sys_wait = 0.045 sys_wait = 0.045
manager_runned = GlobalFlag() manager_run = GlobalFlag()
work_queue = OutMessagesQueue() work_queue = OutMessagesQueue()
tg_flood_wait = GlobalFlag() tg_flood_wait = GlobalFlag()
last_send_time: datetime last_send_time: datetime
@ -54,7 +54,7 @@ class IncomingMessagesTimeBuffer:
self.__buf = [] self.__buf = []
self.__size = size self.__size = size
self.__user_id = user_id 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() row = cursor.fetchone()
if row is not None: if row is not None:
self.__banned_to = datetime.strptime(row[1], '%Y-%m-%d %H:%M:%S') self.__banned_to = datetime.strptime(row[1], '%Y-%m-%d %H:%M:%S')
@ -80,15 +80,16 @@ class IncomingMessagesTimeBuffer:
def __is_valid_timing(self, mess_date): def __is_valid_timing(self, mess_date):
i = self.__size - 1 i = self.__size - 1
sec = 0 sec = 0
while i > self.__size - 4: # while i > self.__size - 4:
while i > 5:
prev_date = self.__buf[i][1] prev_date = self.__buf[i][1]
if prev_date != 0: if prev_date != 0:
tmp = mess_date - prev_date tmp = mess_date - prev_date
if tmp.seconds < 1: '''if tmp.seconds < 1:
levent = 'обнаружено слишком частое обращение: ' + str(tmp.seconds) + ' сек.' levent = 'обнаружено слишком частое обращение: ' + str(tmp.seconds) + ' сек.'
logging.warning(levent) logging.warning(levent)
return False return False
else: else:'''
sec += tmp.seconds sec += tmp.seconds
mess_date = prev_date mess_date = prev_date
else: else:
@ -98,7 +99,7 @@ class IncomingMessagesTimeBuffer:
if sec >= 5: if sec >= 5:
return True return True
else: else:
levent = 'обнаружены 4 сообщенния за ' + str(sec) + ' сек.' levent = 'обнаружены 15 сообщений за ' + str(sec) + ' сек.'
logging.warning(levent) logging.warning(levent)
return False return False
@ -127,10 +128,10 @@ class IncomingMessagesTimeBuffer:
if self.__ban_count == 1: if self.__ban_count == 1:
entry = (self.__user_id, self.__ban_count, str(self.__banned_to)[0:19]) 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: else:
entry = (self.__ban_count, str(self.__banned_to)[0:19], self.__user_id) 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() con.commit()
return rez return rez
@ -143,6 +144,7 @@ class IncomingMessagesTimeBuffer:
return 2 return 2
# TODO Дополнить логирование кнопок
class BotIncomingMessagesOrder: class BotIncomingMessagesOrder:
"""класс порядка сообщений в диалогах""" """класс порядка сообщений в диалогах"""
@ -157,16 +159,16 @@ class BotIncomingMessagesOrder:
mess_date = message.date + msk_hour mess_date = message.date + msk_hour
user_id = message.peer_id.user_id user_id = message.peer_id.user_id
txt = message.text txt = message.text
indx = self.__users.get(user_id) index = self.__users.get(user_id)
if indx is None: if index is None:
indx = len(self.__orders) index = len(self.__orders)
self.__users[user_id] = indx self.__users[user_id] = index
self.__orders.append(IncomingMessagesTimeBuffer(20, user_id)) self.__orders.append(IncomingMessagesTimeBuffer(20, user_id))
if debug: if debug:
levent = 'открыт новый буфер, id - ' + str(user_id) levent = 'открыт новый буфер, id - ' + str(user_id)
logging.info(levent) 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 debug or io_write:
if order_result not in [1, 2]: if order_result not in [1, 2]:
entry = (mess_id, str(mess_date)[0:19], user_id, txt) entry = (mess_id, str(mess_date)[0:19], user_id, txt)
@ -178,15 +180,15 @@ class BotIncomingMessagesOrder:
# менеджер отложенной отправки сообщений # менеджер отложенной отправки сообщений
async def queue_manager(): async def queue_manager():
if not manager_runned: if not manager_run:
manager_runned.set_true() manager_run.set_true()
levent = 'менеджер отложенных сообщений стартовал.' levent = 'менеджер отложенных сообщений стартовал.'
logging.info(levent) logging.info(levent)
now = datetime.now() now = datetime.now()
delta = now - last_send_time delta = now - last_send_time
if delta.total_seconds() < 0: if delta.total_seconds() < 0:
tsec = delta.total_seconds() t_sec = delta.total_seconds()
seconds = abs(tsec // 1 + 1) seconds = abs(t_sec // 1 + 1)
levent = 'ожидание разрешения на отправку - ' + str(seconds) + ' сек.' levent = 'ожидание разрешения на отправку - ' + str(seconds) + ' сек.'
logging.info(levent) logging.info(levent)
await sleep(seconds) await sleep(seconds)
@ -200,11 +202,14 @@ async def queue_manager():
user_id = entry[0] user_id = entry[0]
message = entry[1] message = entry[1]
file_name = entry[2] file_name = entry[2]
buttons = entry[3]
contact_add = entry[4]
await sleep(sys_wait) await sleep(sys_wait)
if debug: if debug:
levent = 'попытка отправки сообщения для user_id = ' + str(user_id) levent = 'попытка отправки сообщения для user_id = ' + str(user_id)
logging.info(levent) 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) work_queue.set_sending_result(result_flag)
if result_flag: if result_flag:
mess_success += 1 mess_success += 1
@ -212,7 +217,7 @@ async def queue_manager():
else: else:
break break
manager_runned.set_false() manager_run.set_false()
levent = 'менеджер отложенных сообщений закончил. попыток - ' + str(mess_count) + ', отправлено - ' + str( levent = 'менеджер отложенных сообщений закончил. попыток - ' + str(mess_count) + ', отправлено - ' + str(
mess_success) mess_success)
logging.info(levent) logging.info(levent)
@ -220,22 +225,24 @@ async def queue_manager():
client.loop.create_task(queue_manager()) client.loop.create_task(queue_manager())
def message_to_queue(user_id, mess, file_name): def message_to_queue(user_id, mess, file_name, buttons, contact_add):
work_queue.add_message(user_id, mess, file_name) work_queue.add_message(user_id, mess, file_name, buttons, contact_add)
if not manager_runned: if not manager_run:
client.loop.create_task(queue_manager()) 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 global last_send_time
now = datetime.now() now = datetime.now()
delta = now - last_send_time delta = now - last_send_time
if not from_queue: 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: 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) levent = 'ответ поставлен в очередь. user_id = ' + str(user_id)
logging.warning(levent) logging.warning(levent)
return False return False
@ -245,7 +252,7 @@ async def send_reply_message(user_id, message, file_name=None, log_parameter=Fal
user_delta = now - timestamp user_delta = now - timestamp
if user_delta.total_seconds() < 1: if user_delta.total_seconds() < 1:
if not from_queue: 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( levent = 'ответ поставлен в очередь, дельта пользователя - ' + str(
user_delta.total_seconds()) + ' сек. user_id = ' + str(user_id) user_delta.total_seconds()) + ' сек. user_id = ' + str(user_id)
logging.warning(levent) 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: if delta.total_seconds() > 0.04:
try: 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() last_send_time = datetime.now()
if log_parameter or debug: 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: except errors.FloodWaitError as e:
seconds = e.seconds seconds = e.seconds
levent = 'антифлуд телеграм сработал, время ожидания - ' + str(seconds) + ' сек.' levent = 'сработал телеграм flood_wait, время ожидания - ' + str(seconds) + ' сек.'
logging.warning(levent) logging.warning(levent)
tg_flood_wait.set_true() tg_flood_wait.set_true()
last_send_time = now + timedelta(seconds=seconds + 1) last_send_time = now + timedelta(seconds=seconds + 1)
if not from_queue: 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 return False
except Exception as e: except Exception as e:
levent = 'что-то пошло не так при отправке (user_id = ' + str(user_id) + '): ' + str(e) levent = 'что-то пошло не так при отправке (user_id = ' + str(user_id) + '): ' + str(e)
if debug:
logging.error(levent) logging.error(levent)
else: if from_queue:
logging.warning(levent) return True
return False return False
else: else:
if not from_queue: 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( levent = 'ответ поставлен в очередь, дельта - ' + str(delta.total_seconds()) + ' сек. user_id = ' + str(
user_id) user_id)
logging.warning(levent) logging.warning(levent)
@ -354,7 +368,7 @@ async def init(cli, debug_mode, adm_ids, io_write_mode):
account_name text account_name text
); );
CREATE TABLE banlist CREATE TABLE ban_list
( (
user_id int, user_id int,
ban_count int, ban_count int,
@ -374,14 +388,14 @@ async def init(cli, debug_mode, adm_ids, io_write_mode):
# завершение работы # завершение работы
async def terminate(): async def terminate():
if manager_runned: if manager_run:
levent = 'bot I/O, ожидание отправки всех сообщений.' levent = 'bot I/O, ожидание отправки всех сообщений.'
logging.info(levent) logging.info(levent)
count = 6 count = 6
while count > 0: while count > 0:
await sleep(5) await sleep(5)
count -= 1 count -= 1
if not manager_runned: if not manager_run:
break break
con.close() con.close()
levent = 'bot I/O остановлен.' levent = 'bot I/O остановлен.'

View File

@ -1,10 +1,11 @@
# Bot I/O classes v1.00 # Bot I/O classes v1.03
# 14/06/2021 # 15/11/2021
# https://t.me/ssleg © 2020 2021 # https://t.me/ssleg © 2021
from datetime import datetime from datetime import datetime
# TODO Сделать нормальную инициализацию модуля
class TgBotUsers: class TgBotUsers:
"""класс хранения пользователей бота""" """класс хранения пользователей бота"""
@ -21,8 +22,8 @@ class TgBotUsers:
f_name = row[1] f_name = row[1]
l_name = row[2] l_name = row[2]
user_acc = row[3] user_acc = row[3]
indx = len(self.__timestamps) index = len(self.__timestamps)
self.__users[user_id] = (indx, f_name, l_name, user_acc) self.__users[user_id] = (index, f_name, l_name, user_acc)
self.__timestamps.append(datetime(year=2020, month=1, day=1)) self.__timestamps.append(datetime(year=2020, month=1, day=1))
def is_bot_user(self, user_id): def is_bot_user(self, user_id):
@ -43,29 +44,29 @@ class TgBotUsers:
values (?,?,?,?)''', entry) values (?,?,?,?)''', entry)
self.__con.commit() self.__con.commit()
timestamp = datetime.now() timestamp = datetime.now()
indx = len(self.__timestamps) index = len(self.__timestamps)
self.__users[user_id] = (indx, f_name, l_name, user_acc) self.__users[user_id] = (index, f_name, l_name, user_acc)
self.__timestamps.append(timestamp) self.__timestamps.append(timestamp)
def update_user_mess_timestamp(self, user_id): def update_user_mess_timestamp(self, user_id):
timestamp = datetime.now() timestamp = datetime.now()
user = self.__users.get(user_id) user = self.__users.get(user_id)
if user is not None: if user is not None:
indx = user[0] index = user[0]
self.__timestamps[indx] = timestamp self.__timestamps[index] = timestamp
def get_user_mess_timestamp(self, user_id): def get_user_mess_timestamp(self, user_id):
user = self.__users.get(user_id) user = self.__users.get(user_id)
if user is not None: if user is not None:
indx = user[0] index = user[0]
return self.__timestamps[indx] return self.__timestamps[index]
else: else:
return datetime(year=2020, month=1, day=1) return datetime(year=2020, month=1, day=1)
def get_users_list(self): def get_users_list(self):
excluded_users = [] excluded_users = []
now = datetime.now() 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(): for row in self.__cursor.fetchall():
banned_to = datetime.strptime(row[1], '%Y-%m-%d %H:%M:%S') banned_to = datetime.strptime(row[1], '%Y-%m-%d %H:%M:%S')
delta = now - banned_to delta = now - banned_to
@ -95,21 +96,21 @@ class OutMessagesQueue:
def is_user_in_queue(self, user_id): def is_user_in_queue(self, user_id):
i = 0 i = 0
finded_flag = False found_flag = False
while i < len(self.__queue): while i < len(self.__queue):
if self.__queue[i][0] == user_id: if self.__queue[i][0] == user_id:
finded_flag = True found_flag = True
break break
i += 1 i += 1
return finded_flag, i return found_flag, i
def add_message(self, user_id, message_text, file_name): def add_message(self, user_id, message_text, file_name, buttons, contact_add):
finded_flag, i = self.is_user_in_queue(user_id) found_flag, i = self.is_user_in_queue(user_id)
if not finded_flag: if not found_flag:
self.__queue.append([user_id]) 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): def get_next_message(self):
element = self.__queue[self.__position_i][1] element = self.__queue[self.__position_i][1]

48
magic.py Executable file → Normal file
View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Magic wand v4.23 # Magic wand v4.25
# 14/06/2021 # 26/09/2021
# https://t.me/ssleg © 2020 2021 # https://t.me/ssleg © 2020 2021
import logging import logging
@ -18,6 +18,7 @@ from telethon import TelegramClient, events
import bot_io import bot_io
import main_module import main_module
# import module_two
from tg_utils import get_tg_client, get_bot_key, GlobalFlag, GlobalCounter from tg_utils import get_tg_client, get_bot_key, GlobalFlag, GlobalCounter
# загрузка конфигурации из ini файла # загрузка конфигурации из ini файла
@ -52,8 +53,8 @@ bot_account = bot_config.get('bot_db_account', 'bot_account')
# глобальные переменные # глобальные переменные
sigterm_flag = GlobalFlag() sigterm_flag = GlobalFlag()
logsize = GlobalCounter() log_size = GlobalCounter()
laststring = GlobalCounter() last_string = GlobalCounter()
error_count = GlobalCounter() error_count = GlobalCounter()
started_time: datetime started_time: datetime
@ -61,6 +62,7 @@ next_stat = GlobalCounter(3600)
# инициализация лога и параметров подключения # инициализация лога и параметров подключения
log_file = RotatingFileHandler(logfile_name, 'a', maxBytes=524288, backupCount=10, encoding='utf=8') 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')) log_file.setFormatter(logging.Formatter('%(levelname)s %(module)-13s [%(asctime)s] %(message)s'))
# noinspection PyArgumentList # noinspection PyArgumentList
logging.basicConfig(level=logging.INFO, handlers=[log_file]) logging.basicConfig(level=logging.INFO, handlers=[log_file])
@ -101,14 +103,14 @@ def get_my_version():
# записывает начальные параметры лог-файла # записывает начальные параметры лог-файла
def init_log_var(): 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') file = open(logfile_name, 'r', encoding='utf-8')
allfile = file.readlines() all_file = file.readlines()
file.close() file.close()
laststring.set(len(allfile)) last_string.set(len(all_file))
levent = 'начальные параметры этого логфайла: ' + str(logsize) + ' байт, ' + str(laststring) + ' строк.' levent = 'начальные параметры этого лог-файла: ' + str(log_size) + ' байт, ' + str(last_string) + ' строк.'
logging.info(levent) logging.info(levent)
@ -127,12 +129,13 @@ async def init():
await bot_io.init(client, debug, admins_ids, io_write) await bot_io.init(client, debug, admins_ids, io_write)
await main_module.init(client, debug, admins_ids) await main_module.init(client, debug, admins_ids)
# await module_two.init(client)
levent = 'инициализация завершена.' levent = 'инициализация завершена.'
logging.info(levent) logging.info(levent)
# возвращает строку со временем аптайма # возвращает строку со временем аптайм
def get_uptime(): def get_uptime():
now = datetime.now() now = datetime.now()
delta = now - started_time delta = now - started_time
@ -155,7 +158,7 @@ def get_up_seconds():
return delta.days * 86400 + delta.seconds return delta.days * 86400 + delta.seconds
# эвент хэндлер, отвечающий за команды админа (статистика работы, рассылки) # обработчик событий, отвечающий за команды админа (статистика работы, рассылки)
async def admins_command(event): async def admins_command(event):
from_id = event.message.peer_id.user_id from_id = event.message.peer_id.user_id
command = event.message.text command = event.message.text
@ -191,7 +194,7 @@ def stat_upload():
'Content-Type': 'application/json; charset=utf-8' 'Content-Type': 'application/json; charset=utf-8'
} }
# noinspection HttpUrlsUsage # 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()) hash_md5 = md5(bot_key.encode())
request_json = {'protocol_version': '1.1', 'application': 'Magic Wand', 'app_version': get_my_version(), 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()} 'uptime': get_up_seconds(), 'errors': error_count.get(), 'fingerprint': hash_md5.hexdigest()}
@ -225,11 +228,11 @@ async def send_notices(mess):
logging.info(levent) logging.info(levent)
# чтение изменений логфайла и поиск ошибок в нем # чтение изменений лог-файла и поиск ошибок в нем
def log_reader(): def log_reader():
file = open(logfile_name, 'r', encoding='utf-8') file = open(logfile_name, 'r', encoding='utf-8')
readcount = 0 read_count = 0
workcount = laststring.get() work_count = last_string.get()
i = 0 i = 0
flag = False flag = False
errmsg = '' errmsg = ''
@ -237,9 +240,9 @@ def log_reader():
while i < 1: while i < 1:
string = file.readline() string = file.readline()
if string != '': if string != '':
readcount += 1 read_count += 1
if readcount > workcount: if read_count > work_count:
laststring.increment() last_string.increment()
if flag: if flag:
if len(string) > 1: if len(string) > 1:
if string.find('[20') > -1: if string.find('[20') > -1:
@ -260,12 +263,12 @@ def log_reader():
file.close() file.close()
# наблюдатель за логфайлом, проверяет изменения раз в минуту # наблюдатель за лог-файлом, проверяет изменения раз в минуту
def log_watcher(): def log_watcher():
if sigterm_flag: if sigterm_flag:
filesize = path.getsize(logfile_name) filesize = path.getsize(logfile_name)
if filesize > logsize: if filesize > log_size:
logsize.set(filesize) log_size.set(filesize)
client.loop.call_soon(log_reader) client.loop.call_soon(log_reader)
if bot_stats: if bot_stats:
@ -286,7 +289,7 @@ async def terminate():
logging.info(levent) logging.info(levent)
# хэндлер сигнала сигтерм, возникающего при перезапуске системы или бота. # обработчик сигнала sigterm, возникающего при перезапуске системы или бота.
# вызывает процедуру корректного завершения бота и модулей # вызывает процедуру корректного завершения бота и модулей
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
def sigterm_call(signum, frame): def sigterm_call(signum, frame):
@ -297,9 +300,10 @@ def sigterm_call(signum, frame):
client.loop.create_task(terminate()) client.loop.create_task(terminate())
# старт telethon и основной инициализации
if __name__ == '__main__':
signal.signal(signal.SIGTERM, sigterm_call) signal.signal(signal.SIGTERM, sigterm_call)
# старт telethon и основной инициализации
client.start(bot_token=bot_key) client.start(bot_token=bot_key)
client.loop.run_until_complete(init()) client.loop.run_until_complete(init())

View File

@ -1,42 +1,90 @@
# Tg utils v2.22 # Tg utils v2.50
# 14/06/2021 # 10/10/2021
# https://t.me/ssleg © 2020 2021 # https://t.me/ssleg © 2020 2021
import logging import logging
from datetime import datetime from datetime import datetime
from time import sleep
import psycopg2 import psycopg2
import psycopg2.extensions
from telethon import TelegramClient from telethon import TelegramClient
# TODO Добавить executemany и executescript
class PgServer: class PgServer:
"""класс postgres-сервер""" """класс 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'): def __init__(self, db_name='tg_bots', db_host='127.0.0.1'):
if dbc is None: self.__con: psycopg2.extensions.connection
self.__con = psycopg2.connect(database=db_name, 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', user='postgres',
password='1234', password='1234',
host='127.0.0.1', host=self.__db_host,
port='5432') 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() self.__cursor = self.__con.cursor()
self.__retry_count = 0
return True
except Exception as e:
if first_connect:
levent = 'postgresql, ошибка подключения к бд: ' + str(e)
else:
levent = 'postgresql, ошибка при восстановлении подключения: ' + str(e)
logging.warning(levent)
return False
def commit(self): def commit(self):
if self.__connected:
self.__con.commit() self.__con.commit()
return True
return
def rollback(self): def rollback(self):
if self.__connected:
self.__con.rollback() self.__con.rollback()
return True
return False
def exec(self, sql, req_data=None, return_type=None, retry_count=0): def exec(self, sql, req_data=None, return_type=None):
if self.__connected:
try: try:
if req_data is not None: if req_data is not None:
self.__cursor.execute(sql, req_data) self.__cursor.execute(sql, req_data)
@ -51,29 +99,46 @@ class PgServer:
else: else:
return True return True
except Exception as e: except psycopg2.IntegrityError as e:
levent = 'postgres error: ' + str(e) levent = 'postgresql, ошибка целостности данных: ' + str(e.pgerror)
logging.error(levent) logging.error(levent)
if self.__control_flag: return False
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) 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 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): def __del__(self):
if self.__connected:
self.__cursor.close() self.__cursor.close()
if self.__control_flag:
self.__con.close() self.__con.close()
else:
self.__con.rollback()
class PgLock: class PgLock:
@ -81,9 +146,9 @@ class PgLock:
__slots__ = ['__srv', '__cursor', '__lock_name', '__debug'] __slots__ = ['__srv', '__cursor', '__lock_name', '__debug']
def __init__(self, dbc=None, lck='default_lock', debug=False): def __init__(self, lock_name='default', debug=False):
self.__srv = PgServer(dbc) self.__srv = PgServer()
self.__lock_name = lck self.__lock_name = lock_name
self.__debug = debug self.__debug = debug
def lock(self): def lock(self):
@ -157,16 +222,23 @@ class GlobalCounter:
def __isub__(self, other): def __isub__(self, other):
if GlobalCounter.__is_valid_arg(other): if GlobalCounter.__is_valid_arg(other):
self.__count -= other self.__count -= other
return self
def __iadd__(self, other): def __iadd__(self, other):
if GlobalCounter.__is_valid_arg(other): if GlobalCounter.__is_valid_arg(other):
self.__count += other self.__count += other
return self
def __eq__(self, other): def __eq__(self, other):
if self.__count == other: if self.__count == other:
return True return True
return False return False
def __ne__(self, other):
if self.__count != other:
return True
return False
def __ge__(self, other): def __ge__(self, other):
if self.__count >= other: if self.__count >= other:
return True return True
@ -183,7 +255,7 @@ class GlobalCounter:
return False return False
def __lt__(self, other): def __lt__(self, other):
if self.__count <= other: if self.__count < other:
return True return True
return False return False
@ -221,6 +293,119 @@ class GlobalFlag:
return str(self.__flag) 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(): def t_stamp():
now = datetime.now() now = datetime.now()
stamp = datetime.strftime(now, '%d/%m %Y %H:%M:%S') stamp = datetime.strftime(now, '%d/%m %Y %H:%M:%S')
@ -233,12 +418,12 @@ def t_stamp_sh():
return stamp return stamp
# ловеркейс имени tg канала с проверкой валидности # lowercase имени tg канала с проверкой валидности
def set_ch_name_lower(teststr): def set_ch_name_lower(test_str):
i = 0 i = 0
rezult = '' result = ''
while i < len(teststr): while i < len(test_str):
char = teststr[i] char = test_str[i]
code = ord(char) code = ord(char)
if code == 95: if code == 95:
@ -253,13 +438,13 @@ def set_ch_name_lower(teststr):
if 96 < code < 123: if 96 < code < 123:
pass pass
else: else:
levent = 'ошибка конвертации канала: ' + str(i) + ' ' + str(code) + ' ' + char + ' ' + teststr levent = 'ошибка конвертации канала: ' + str(i) + ' ' + str(code) + ' ' + char + ' ' + test_str
logging.warning(levent) logging.warning(levent)
return False return False
rezult += chr(code) result += chr(code)
i += 1 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 = '{:,}'.format(integer)
string = string.replace(',', razd) string = string.replace(',', separator)
return string return string
# версия сервера постгрес # создает объект клиента с параметрами из бд
def get_server_version(dbc=None): def get_tg_client(name, custom_name=None):
srv = PgServer(dbc) srv = PgServer()
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)
n_api = name + '_api_id' 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]) api_id = int(row[0])
n_hash = name + '_api_hash' 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] api_hash = row[0]
if cust_name is None: if custom_name is None:
client = TelegramClient(name, api_id, api_hash) client = TelegramClient(name, api_id, api_hash)
else: else:
client = TelegramClient(cust_name, api_id, api_hash) client = TelegramClient(custom_name, api_id, api_hash)
return client return client
# достает ключ бота из бд # достает ключ бота из бд
def get_bot_key(name, dbc=None): def get_bot_key(name):
srv = PgServer(dbc) srv = PgServer()
bot = name + '_key' 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] bot_key = row[0]
return bot_key return bot_key