magic-wand_4/tg_utils.py

502 lines
14 KiB
Python
Raw Normal View History

2022-09-26 19:06:52 +03:00
# Tg utils v2.50
# 10/10/2021
2021-06-14 17:05:26 +03:00
# https://t.me/ssleg © 2020 2021
import logging
from datetime import datetime
2022-09-26 19:06:52 +03:00
from time import sleep
2021-06-14 17:05:26 +03:00
import psycopg2
2022-09-26 19:06:52 +03:00
import psycopg2.extensions
2021-06-14 17:05:26 +03:00
from telethon import TelegramClient
2022-09-26 19:06:52 +03:00
# TODO Добавить executemany и executescript
2021-06-14 17:05:26 +03:00
class PgServer:
"""класс postgres-сервер"""
2022-09-26 19:06:52 +03:00
__slots__ = ['__con', '__cursor', '__db_name', '__db_host', '__retry_count', '__version', '__connected']
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]
2021-06-14 17:05:26 +03:00
2022-09-26 19:06:52 +03:00
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,
2021-06-14 17:05:26 +03:00
user='postgres',
password='1234',
2022-09-26 19:06:52 +03:00
host=self.__db_host,
2021-06-14 17:05:26 +03:00
port='5432')
2022-09-26 19:06:52 +03:00
self.__cursor = self.__con.cursor()
self.__retry_count = 0
return True
2021-06-14 17:05:26 +03:00
2022-09-26 19:06:52 +03:00
except Exception as e:
if first_connect:
levent = 'postgresql, ошибка подключения к бд: ' + str(e)
else:
levent = 'postgresql, ошибка при восстановлении подключения: ' + str(e)
logging.warning(levent)
return False
2021-06-14 17:05:26 +03:00
def commit(self):
2022-09-26 19:06:52 +03:00
if self.__connected:
self.__con.commit()
return True
return
2021-06-14 17:05:26 +03:00
def rollback(self):
2022-09-26 19:06:52 +03:00
if self.__connected:
self.__con.rollback()
return True
return False
2021-06-14 17:05:26 +03:00
2022-09-26 19:06:52 +03:00
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)
2021-06-14 17:05:26 +03:00
2022-09-26 19:06:52 +03:00
if return_type is not None:
if return_type == 0:
return self.__cursor.fetchall()
else:
return self.__cursor.fetchone()
2021-06-14 17:05:26 +03:00
else:
2022-09-26 19:06:52 +03:00
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
2021-06-14 17:05:26 +03:00
2022-09-26 19:06:52 +03:00
except Exception as e:
levent = 'postgresql, ошибка: ' + str(e)
logging.error(levent)
return False
2021-06-14 17:05:26 +03:00
2022-09-26 19:06:52 +03:00
return False
def is_connected(self):
return self.__connected
def get_version(self):
return self.__version
def __str__(self):
return self.__version
2021-06-14 17:05:26 +03:00
def __del__(self):
2022-09-26 19:06:52 +03:00
if self.__connected:
self.__cursor.close()
2021-06-14 17:05:26 +03:00
self.__con.close()
class PgLock:
"""класс блокировки базы данных"""
__slots__ = ['__srv', '__cursor', '__lock_name', '__debug']
2022-09-26 19:06:52 +03:00
def __init__(self, lock_name='default', debug=False):
self.__srv = PgServer()
self.__lock_name = lock_name
2021-06-14 17:05:26 +03:00
self.__debug = debug
def lock(self):
self.__srv.exec('update locks set status=True where lock_name=%s', (self.__lock_name,))
self.__srv.commit()
if self.__debug:
levent = 'locked #' + self.__lock_name
logging.info(levent)
def unlock(self):
self.__srv.exec('update locks set status=False where lock_name=%s', (self.__lock_name,))
self.__srv.commit()
if self.__debug:
levent = 'unlocked #' + self.__lock_name
logging.info(levent)
def lock_status(self):
row = self.__srv.exec('select status from locks where lock_name=%s', (self.__lock_name,), return_type=1)
self.__srv.rollback()
if row is not None:
lock_status = row[0]
return lock_status
else:
return False
class GlobalCounter:
"""глобальный универсальный счетчик"""
__slots__ = ['__count']
@staticmethod
def __is_valid_arg(arg):
if type(arg) is int:
if arg > 0:
return True
return False
def __init__(self, init_count=0):
if GlobalCounter.__is_valid_arg(init_count):
self.__count = init_count
else:
self.__count = 0
def increment(self, step=1):
if GlobalCounter.__is_valid_arg(step):
self.__count += step
return True
return False
def decrement(self, step=1):
if GlobalCounter.__is_valid_arg(step):
if self.__count - step >= 0:
self.__count -= step
return True
return False
def get(self):
return self.__count
def set(self, value):
if GlobalCounter.__is_valid_arg(value):
self.__count = value
return True
return False
def clear(self):
self.__count = 0
def __isub__(self, other):
if GlobalCounter.__is_valid_arg(other):
self.__count -= other
2022-09-26 19:06:52 +03:00
return self
2021-06-14 17:05:26 +03:00
def __iadd__(self, other):
if GlobalCounter.__is_valid_arg(other):
self.__count += other
2022-09-26 19:06:52 +03:00
return self
2021-06-14 17:05:26 +03:00
def __eq__(self, other):
if self.__count == other:
return True
return False
2022-09-26 19:06:52 +03:00
def __ne__(self, other):
if self.__count != other:
return True
return False
2021-06-14 17:05:26 +03:00
def __ge__(self, other):
if self.__count >= other:
return True
return False
def __gt__(self, other):
if self.__count > other:
return True
return False
def __le__(self, other):
if self.__count <= other:
return True
return False
def __lt__(self, other):
2022-09-26 19:06:52 +03:00
if self.__count < other:
2021-06-14 17:05:26 +03:00
return True
return False
def __str__(self):
return str(self.__count)
class GlobalFlag:
"""глобальный универсальный флаг"""
__slots__ = ['__flag']
def __init__(self):
self.__flag = False
def set_true(self):
self.__flag = True
def set_false(self):
self.__flag = False
def switch(self):
if self.__flag:
self.__flag = False
else:
self.__flag = True
def get(self):
return self.__flag
def __bool__(self):
return self.__flag
def __str__(self):
return str(self.__flag)
2022-09-26 19:06:52 +03:00
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)
2021-06-14 17:05:26 +03:00
def t_stamp():
now = datetime.now()
stamp = datetime.strftime(now, '%d/%m %Y %H:%M:%S')
return stamp
def t_stamp_sh():
now = datetime.now()
stamp = datetime.strftime(now, '%H:%M')
return stamp
2022-09-26 19:06:52 +03:00
# lowercase имени tg канала с проверкой валидности
def set_ch_name_lower(test_str):
2021-06-14 17:05:26 +03:00
i = 0
2022-09-26 19:06:52 +03:00
result = ''
while i < len(test_str):
char = test_str[i]
2021-06-14 17:05:26 +03:00
code = ord(char)
if code == 95:
pass
else:
if 47 < code < 58:
pass
else:
if 64 < code < 91:
code += 32
else:
if 96 < code < 123:
pass
else:
2022-09-26 19:06:52 +03:00
levent = 'ошибка конвертации канала: ' + str(i) + ' ' + str(code) + ' ' + char + ' ' + test_str
2021-06-14 17:05:26 +03:00
logging.warning(levent)
return False
2022-09-26 19:06:52 +03:00
result += chr(code)
2021-06-14 17:05:26 +03:00
i += 1
2022-09-26 19:06:52 +03:00
return result
2021-06-14 17:05:26 +03:00
# сборка имени юзера в читабельное
def set_name_printable(first, last, account=None, phone=None, ad=''):
name = str(first) + ' '
if last is not None:
name = name + str(last) + ' '
if account is not None:
name = name + '(' + ad + str(account) + ')' + ' '
if phone is not None:
name = name + '+' + str(phone) + ' '
ind = len(name)
res = name[0:ind - 1]
return res
# вывод числа с разделителями тысяч
2022-09-26 19:06:52 +03:00
def set_int_printable(integer, separator=' '):
2021-06-14 17:05:26 +03:00
string = '{:,}'.format(integer)
2022-09-26 19:06:52 +03:00
string = string.replace(',', separator)
2021-06-14 17:05:26 +03:00
return string
2022-09-26 19:06:52 +03:00
# создает объект клиента с параметрами из бд
def get_tg_client(name, custom_name=None):
srv = PgServer()
2021-06-14 17:05:26 +03:00
n_api = name + '_api_id'
2022-09-26 19:06:52 +03:00
row = srv.exec('select value from logins where name=%s', (n_api,), return_type=1)
2021-06-14 17:05:26 +03:00
api_id = int(row[0])
n_hash = name + '_api_hash'
2022-09-26 19:06:52 +03:00
row = srv.exec('select value from logins where name=%s', (n_hash,), return_type=1)
2021-06-14 17:05:26 +03:00
api_hash = row[0]
2022-09-26 19:06:52 +03:00
if custom_name is None:
2021-06-14 17:05:26 +03:00
client = TelegramClient(name, api_id, api_hash)
else:
2022-09-26 19:06:52 +03:00
client = TelegramClient(custom_name, api_id, api_hash)
2021-06-14 17:05:26 +03:00
return client
# достает ключ бота из бд
2022-09-26 19:06:52 +03:00
def get_bot_key(name):
srv = PgServer()
2021-06-14 17:05:26 +03:00
bot = name + '_key'
2022-09-26 19:06:52 +03:00
row = srv.exec('select value from logins where name=%s', (bot,), return_type=1)
2021-06-14 17:05:26 +03:00
bot_key = row[0]
return bot_key