Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
2c8656c296
|
|||
501ba5d4a7
|
|||
cf46592915
|
|||
1686c99600
|
@ -1,6 +1,6 @@
|
|||||||
# Информация о программе
|
# Информация о программе
|
||||||
__author__ = 'Alexander Popov'
|
__author__ = 'Alexander Popov'
|
||||||
__version__ = (2, 2, 3)
|
__version__ = (2, 2, 4)
|
||||||
|
|
||||||
# Импорт модулей стандартной библиотеки
|
# Импорт модулей стандартной библиотеки
|
||||||
from os import getenv
|
from os import getenv
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
# Импорт модулей стандартной библиотеки
|
# Импорт модулей стандартной библиотеки
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
# Импорт сторонних модулей
|
||||||
|
from pyrogram.errors import exceptions
|
||||||
|
from rich import print as rich_print
|
||||||
|
|
||||||
# Импорт модулей приложения
|
# Импорт модулей приложения
|
||||||
from . import __version__, client, db, logger, DELAY_TIME
|
from . import __version__, client, db, logger, DELAY_TIME
|
||||||
from .actions import get_top_wins, get_top_donates
|
from .actions import get_top_wins, get_top_donates
|
||||||
@ -33,8 +37,8 @@ async def main():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
logger.warning('ClanStat :: {}', '.'.join(map(str, __version__)))
|
logger.warning('🎴 ClanStat 🆔 {}', '.'.join(map(str, __version__)))
|
||||||
logger.warning('URL: {}', 'https://git.a2s.su/AniCard/ClanStat/')
|
logger.warning('🌐 URL: {}', 'https://git.a2s.su/AniCard/ClanStat/')
|
||||||
logger.info('Выполняется запуск приложения')
|
logger.info('Выполняется запуск приложения')
|
||||||
|
|
||||||
logger.info('Выполняется подключение к базе данных')
|
logger.info('Выполняется подключение к базе данных')
|
||||||
@ -42,6 +46,12 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
client.run(main())
|
client.run(main())
|
||||||
|
except exceptions.unauthorized_401.AuthKeyUnregistered:
|
||||||
|
rich_print('Ошибка Telegram: [bold red][401 AUTH_KEY_UNREGISTERED][/bold red]')
|
||||||
|
rich_print(
|
||||||
|
'Удалите файлы авторизации [bold green]Pyrogram[/bold green]:'
|
||||||
|
' [italic yellow]*.session, *.session-journal[/italic yellow]'
|
||||||
|
)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
🩻 ...
|
||||||
|
"""
|
||||||
|
|
||||||
# Импорт модулей стандартной библиотеки
|
# Импорт модулей стандартной библиотеки
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -7,8 +11,8 @@ from .collect import parse_wins_top, parse_donates_top
|
|||||||
|
|
||||||
|
|
||||||
async def get_top_wins() -> None:
|
async def get_top_wins() -> None:
|
||||||
"""Получает топ клана по победам"""
|
"""⚔️ Получает топ клана по победам"""
|
||||||
logger.info('Выполняется получения списка побед клана')
|
logger.info('⚔️ Выполняется получения списка побед клана')
|
||||||
|
|
||||||
await client.send_message(BOT_NAME, '🛡 Мой клан')
|
await client.send_message(BOT_NAME, '🛡 Мой клан')
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@ -22,7 +26,7 @@ async def get_top_wins() -> None:
|
|||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
callback_data='my_clan:tops:91:0:0:0:0:1',
|
callback_data='my_clan:tops:91:0:0:0:0:1',
|
||||||
)
|
)
|
||||||
time.sleep(1)
|
time.sleep(2)
|
||||||
|
|
||||||
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
||||||
message_id = message.id
|
message_id = message.id
|
||||||
@ -32,22 +36,22 @@ async def get_top_wins() -> None:
|
|||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
callback_data='my_clan:top-wins:91:0:0:0:0:1',
|
callback_data='my_clan:top-wins:91:0:0:0:0:1',
|
||||||
)
|
)
|
||||||
time.sleep(1)
|
time.sleep(2)
|
||||||
|
|
||||||
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
||||||
await parse_wins_top(message)
|
await parse_wins_top(message)
|
||||||
|
|
||||||
await client.read_chat_history(BOT_NAME)
|
await client.read_chat_history(BOT_NAME) # Помечает сообщения в чате прочитанными
|
||||||
|
|
||||||
logger.info('Получение списка побед клана завершено')
|
logger.info('Получение списка побед клана завершено')
|
||||||
|
|
||||||
|
|
||||||
async def get_top_donates() -> None:
|
async def get_top_donates() -> None:
|
||||||
"""Получает топ клана по пожертвованиям"""
|
"""💠 Получает топ клана по пожертвованиям"""
|
||||||
logger.info('Выполняется получения списка пожертвований клана')
|
logger.info('💠 Выполняется получения списка пожертвований клана')
|
||||||
|
|
||||||
await client.send_message(BOT_NAME, '🛡 Мой клан')
|
await client.send_message(BOT_NAME, '🛡 Мой клан')
|
||||||
time.sleep(1)
|
time.sleep(2)
|
||||||
|
|
||||||
message_id = 0
|
message_id = 0
|
||||||
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
||||||
@ -58,7 +62,7 @@ async def get_top_donates() -> None:
|
|||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
callback_data='my_clan:tops:91:0:0:0:0:1',
|
callback_data='my_clan:tops:91:0:0:0:0:1',
|
||||||
)
|
)
|
||||||
time.sleep(1)
|
time.sleep(2)
|
||||||
|
|
||||||
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
||||||
message_id = message.id
|
message_id = message.id
|
||||||
@ -73,6 +77,6 @@ async def get_top_donates() -> None:
|
|||||||
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
async for message in client.get_chat_history(BOT_NAME, limit=1):
|
||||||
await parse_donates_top(message)
|
await parse_donates_top(message)
|
||||||
|
|
||||||
await client.read_chat_history(BOT_NAME)
|
await client.read_chat_history(BOT_NAME) # Помечает сообщения в чате прочитанными
|
||||||
|
|
||||||
logger.info('Получение списка пожертвований клана завершено')
|
logger.info('Получение списка пожертвований клана завершено')
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
|
"""
|
||||||
|
🩻 ...
|
||||||
|
"""
|
||||||
|
|
||||||
# Импорт модулей стандартной библиотеки
|
# Импорт модулей стандартной библиотеки
|
||||||
import re
|
import re
|
||||||
|
import io
|
||||||
|
|
||||||
# Импорт сторонних модулей
|
# Импорт сторонних модулей
|
||||||
|
from rich import print as rich_print
|
||||||
|
from rich.table import Table
|
||||||
from pyrogram.types import Message
|
from pyrogram.types import Message
|
||||||
from pyrogram.enums import MessageEntityType
|
from pyrogram.enums import MessageEntityType
|
||||||
|
|
||||||
@ -11,70 +18,76 @@ from .utils import get_telegram_id, get_telegram_data, clean_icons
|
|||||||
|
|
||||||
|
|
||||||
async def parse_wins_top(message: Message) -> None:
|
async def parse_wins_top(message: Message) -> None:
|
||||||
"""Выполняет парсинг данных топа побед членов клана"""
|
"""⚔️ Выполняет парсинг данных топа побед членов клана"""
|
||||||
if message.text.startswith('🏆 Топ по победам'):
|
|
||||||
players = message.text.split('\n')
|
|
||||||
players.pop(0)
|
|
||||||
players.pop(0)
|
|
||||||
|
|
||||||
WINS = list()
|
if not message.text.startswith('🏆 Топ по победам'):
|
||||||
|
return
|
||||||
|
|
||||||
for idx, player in enumerate(players):
|
WINS = await parse_tops(message, '⚔')
|
||||||
player = re.sub(r'^\d+. ', '', player) # удаляет нумерацию
|
|
||||||
player, battle_count = player.split(' - ') # разделяет ник и количество побед в клановых сражениях
|
|
||||||
player = clean_icons(player)
|
|
||||||
|
|
||||||
battle_count = battle_count.replace(' ⚔', '') # удаляем эмодзи мечей
|
# 📟 Отрисовывает таблицу побед
|
||||||
battle_count = int(re.sub(r'[^\x00-\x7F]', '', battle_count)) # преобразовывает строку в число
|
table = Table(show_header=True)
|
||||||
|
table.add_column('ID', style='dim', justify='right', width=12)
|
||||||
|
table.add_column('Username', style='magenta')
|
||||||
|
table.add_column('Count', style='green', width=7)
|
||||||
|
table.add_column('URL', style='yellow')
|
||||||
|
for i in WINS:
|
||||||
|
table.add_row(str(i['telegram_id']), i['username'], str(i['count']), i['url'])
|
||||||
|
rich_print(table)
|
||||||
|
|
||||||
player_url, player_telegram_id = await get_telegram_data(message, idx)
|
db.add_data(WINS, True) # Добавляет данные в базу данных
|
||||||
|
|
||||||
WINS.append(
|
|
||||||
{
|
|
||||||
'telegram_id': player_telegram_id,
|
|
||||||
'username': player,
|
|
||||||
'count': battle_count,
|
|
||||||
'url': player_url,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
print(WINS)
|
|
||||||
db.add_data(WINS, True)
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
async def parse_donates_top(message: Message) -> None:
|
async def parse_donates_top(message: Message) -> None:
|
||||||
"""Выполняет парсинг данных топа пожертвований членов клана"""
|
"""💠 Выполняет парсинг данных топа пожертвований членов клана"""
|
||||||
if message.text.startswith('🏆 Топ по пожертвованиям'):
|
|
||||||
players = message.text.split('\n')
|
|
||||||
|
|
||||||
# Удаляет из массива строку с заголовком и последующую пустую строку
|
if not message.text.startswith('🏆 Топ по пожертвованиям'):
|
||||||
players.pop(0)
|
return
|
||||||
players.pop(0)
|
|
||||||
|
|
||||||
DONATES = list()
|
DONATES = await parse_tops(message, '💠')
|
||||||
|
|
||||||
for idx, player in enumerate(players):
|
# 📟 Отрисовывает таблицу побед
|
||||||
player = re.sub(r'^\d+. ', '', player) # удаляет нумерацию
|
table = Table(show_header=True)
|
||||||
player, donates_count = player.split(' - ') # разделяет ник и количество пожертвований в клановую сокровищницу
|
table.add_column('ID', style='dim', justify='right', width=12)
|
||||||
player = clean_icons(player)
|
table.add_column('Username', style='magenta')
|
||||||
|
table.add_column('Count', style='green', width=7)
|
||||||
|
table.add_column('URL', style='yellow')
|
||||||
|
for i in DONATES:
|
||||||
|
table.add_row(str(i['telegram_id']), i['username'], str(i['count']), i['url'])
|
||||||
|
rich_print(table)
|
||||||
|
|
||||||
donates_count = donates_count.replace(' 💠', '') # удаляем эмодзи пожертвований
|
db.add_data(DONATES, False) # Добавляет данные в базу данных
|
||||||
donates_count = int(re.sub(r'[^\x00-\x7F]', '', donates_count)) # преобразовывает строку в число
|
|
||||||
|
|
||||||
player_url, player_telegram_id = await get_telegram_data(message, idx)
|
|
||||||
|
|
||||||
DONATES.append(
|
async def parse_tops(message: Message, emoji_replace: str) -> list:
|
||||||
{
|
"""..."""
|
||||||
'telegram_id': player_telegram_id,
|
|
||||||
'username': player,
|
|
||||||
'count': donates_count,
|
|
||||||
'url': player_url,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
print(DONATES)
|
top_data = list()
|
||||||
db.add_data(DONATES, False)
|
|
||||||
else:
|
msg_buffer = io.StringIO(message.text)
|
||||||
pass
|
msg_buffer.readline()
|
||||||
|
msg_buffer.readline()
|
||||||
|
|
||||||
|
for e in message.entities:
|
||||||
|
player_line = msg_buffer.readline()
|
||||||
|
player_name, battle_count = player_line.split(' - ')
|
||||||
|
player = clean_icons(message.text[e.offset : e.offset + e.length])
|
||||||
|
player_name = clean_icons(re.sub(r'^\d+. ', '', player_name))
|
||||||
|
battle_count = battle_count.replace(f' {emoji_replace}', '') # Удаляет эмодзи
|
||||||
|
battle_count = int(re.sub(r'[^\x00-\x7F]', '', battle_count))
|
||||||
|
|
||||||
|
if player not in player_name:
|
||||||
|
top_data.append({'telegram_id': None, 'username': player_name, 'count': battle_count, 'url': None})
|
||||||
|
msg_buffer.readline()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if e.type is MessageEntityType.TEXT_LINK:
|
||||||
|
player_url = e.url
|
||||||
|
player_telegram_id = await get_telegram_id(player_url.replace('http://t.me/', ''))
|
||||||
|
elif e.type is MessageEntityType.TEXT_MENTION:
|
||||||
|
player_url = e.url
|
||||||
|
player_telegram_id = e.user.id
|
||||||
|
|
||||||
|
top_data.append({'telegram_id': player_telegram_id, 'username': player, 'count': battle_count, 'url': player_url})
|
||||||
|
|
||||||
|
return top_data
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
🗃️ Работа с базой данных
|
||||||
|
"""
|
||||||
|
|
||||||
# Импорт модулей стандартной библиотеки
|
# Импорт модулей стандартной библиотеки
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@ -23,13 +27,26 @@ class DataBase(object):
|
|||||||
|
|
||||||
def connect(self) -> bool:
|
def connect(self) -> bool:
|
||||||
"""Выполняет подключение к базе данных"""
|
"""Выполняет подключение к базе данных"""
|
||||||
self.conn = psycopg2.connect(
|
|
||||||
dbname=self.name,
|
connection_kwargs = {
|
||||||
host=self.host,
|
"host": self.host,
|
||||||
user=self.user,
|
"port": self.port,
|
||||||
password=self.password,
|
"user": self.user,
|
||||||
port=self.port,
|
"password": self.password,
|
||||||
)
|
"database": self.name,
|
||||||
|
}
|
||||||
|
"""Настройки соединения"""
|
||||||
|
|
||||||
|
keepalive_kwargs = {
|
||||||
|
"keepalives": 1,
|
||||||
|
"keepalives_idle": 5,
|
||||||
|
"keepalives_interval": 5,
|
||||||
|
"keepalives_count": 2,
|
||||||
|
"tcp_user_timeout": 1000,
|
||||||
|
}
|
||||||
|
"""Настройки TCP"""
|
||||||
|
|
||||||
|
self.conn = psycopg2.connect(**connection_kwargs, **keepalive_kwargs) # Выполняет подключение к базе данных
|
||||||
self.conn.autocommit = True # Активирует автоматический коммит транзакций
|
self.conn.autocommit = True # Активирует автоматический коммит транзакций
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -39,12 +56,23 @@ class DataBase(object):
|
|||||||
self.conn.close()
|
self.conn.close()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def check_connect(self) -> None:
|
||||||
|
"""Проверяет статус подключения"""
|
||||||
|
try:
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
cur.execute('SELECT 1')
|
||||||
|
cur.close()
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
return False
|
||||||
|
|
||||||
def commit(self) -> None:
|
def commit(self) -> None:
|
||||||
"""Выполняет коммит"""
|
"""Выполняет коммит"""
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def add_data(self, data, wins: bool) -> bool: # wins изменит на Enum
|
def add_data(self, data, wins: bool) -> bool: # wins изменить на Enum
|
||||||
"""Добавляет данные в базу данных"""
|
"""Добавляет данные в базу данных"""
|
||||||
if wins:
|
if wins:
|
||||||
table = 'wins'
|
table = 'wins'
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
🧰 Утилиты
|
||||||
|
"""
|
||||||
|
|
||||||
# Импорт сторонних модулей
|
# Импорт сторонних модулей
|
||||||
from pyrogram.types import Message
|
from pyrogram.types import Message
|
||||||
from pyrogram.enums import MessageEntityType
|
from pyrogram.enums import MessageEntityType
|
||||||
@ -6,7 +10,7 @@ from . import client
|
|||||||
|
|
||||||
|
|
||||||
async def get_telegram_id(username: str) -> int:
|
async def get_telegram_id(username: str) -> int:
|
||||||
"""Возвращает Telegram ID"""
|
"""🆔 Возвращает Telegram ID"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
telegram_user = await client.get_users(username)
|
telegram_user = await client.get_users(username)
|
||||||
@ -17,6 +21,7 @@ async def get_telegram_id(username: str) -> int:
|
|||||||
|
|
||||||
async def get_telegram_data(message: Message, idx: int) -> tuple():
|
async def get_telegram_data(message: Message, idx: int) -> tuple():
|
||||||
"""..."""
|
"""..."""
|
||||||
|
|
||||||
if message.entities[idx].type == MessageEntityType.TEXT_LINK:
|
if message.entities[idx].type == MessageEntityType.TEXT_LINK:
|
||||||
player_url = message.entities[idx].url
|
player_url = message.entities[idx].url
|
||||||
player_telegram_id = await get_telegram_id(player_url.replace('http://t.me/', ''))
|
player_telegram_id = await get_telegram_id(player_url.replace('http://t.me/', ''))
|
||||||
@ -31,7 +36,7 @@ async def get_telegram_data(message: Message, idx: int) -> tuple():
|
|||||||
|
|
||||||
|
|
||||||
def clean_icons(text: str) -> str:
|
def clean_icons(text: str) -> str:
|
||||||
"""Удаляет иконки подписок из ников"""
|
"""💎 Удаляет иконки подписок из ников"""
|
||||||
|
|
||||||
# оставляет ники игроков, Python не может в эмодзи в RegExp, по этому пришлось делать через str.replace()
|
# оставляет ники игроков, Python не может в эмодзи в RegExp, по этому пришлось делать через str.replace()
|
||||||
# player = re.sub(r'\s[⚡⚜]$', '', player)
|
# player = re.sub(r'\s[⚡⚜]$', '', player)
|
||||||
|
@ -11,3 +11,10 @@ include_toc: true
|
|||||||
* 🟢 – Добавлено
|
* 🟢 – Добавлено
|
||||||
* 🔴 – Удалёно
|
* 🔴 – Удалёно
|
||||||
* ♻️ – Исправлено
|
* ♻️ – Исправлено
|
||||||
|
|
||||||
|
## 2.2.4
|
||||||
|
|
||||||
|
* 🟢 Добавлено сообщение об ошибке **401** `AUTH_KEY_UNREGISTERED`
|
||||||
|
* 🟢 Добавлены emoji в приветственное сообщение
|
||||||
|
* 🟢 Результаты в терминале отображаются в таблице
|
||||||
|
* 🟢 Изменён алгоритм получения значений побед и пожертвований
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
loguru==0.7.2
|
loguru==0.7.2
|
||||||
|
psycopg2==2.9.10
|
||||||
Pyrogram==2.0.106
|
Pyrogram==2.0.106
|
||||||
python-dotenv==1.0.1
|
python-dotenv==1.0.1
|
||||||
|
rich==14.0.0
|
||||||
TgCrypto==1.2.5
|
TgCrypto==1.2.5
|
||||||
psycopg2==2.9.10
|
zstandard==0.23.0
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
# author: Alexander Popov
|
||||||
|
# mail: iiiypuk {at} fastmail.fm
|
||||||
|
# license: Public Domain
|
||||||
|
# desc: Собирает AppImage
|
||||||
|
|
||||||
# Импортирует скрипт с определениями цветов
|
# Импортирует скрипт с определениями цветов
|
||||||
source ../3rdparty/colors.sh/colors.sh
|
source ./3rdparty/colors.sh/colors.sh
|
||||||
|
|
||||||
APPDIR="../AppDir"
|
APPDIR="../AppDir"
|
||||||
GIT_HASH=$(git rev-parse --short HEAD)
|
GIT_HASH=$(git rev-parse --short HEAD)
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
pdoc -o docs/ app
|
# author: Alexander Popov
|
||||||
|
# mail: iiiypuk {at} fastmail.fm
|
||||||
|
# license: Public Domain
|
||||||
|
# desc: Генерирует документацию Python кода
|
||||||
|
|
||||||
|
# Импортирует скрипт с определениями цветов
|
||||||
|
source ./3rdparty/colors.sh/colors.sh
|
||||||
|
|
||||||
|
echo -e "${CYAN}Генерация документации...${NC}"
|
||||||
|
|
||||||
|
pdoc -o docs/ ClanStat
|
||||||
|
|
||||||
|
echo -e "${GREEN}Генерация завершена. Смотрите директорию 'docs'.${NC}"
|
||||||
|
Reference in New Issue
Block a user