second init

This commit is contained in:
Alexander Popov 2024-11-27 02:58:28 +03:00
commit 10aa70702c
19 changed files with 875 additions and 0 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_style = space
indent_size = 4
[*.md]
trim_trailing_whitespace = false

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
# Pyrogram files
*.session
*.session-journal
# App
.env
db.sqlite

16
LICENSE Normal file
View File

@ -0,0 +1,16 @@
MIT No Attribution
Copyright 2024 Alexander Popov
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# TODO
- [ ] Добавить уведомления, если что-то пошло не так

27
app/__init__.py Normal file
View File

@ -0,0 +1,27 @@
# Информация о программе
__author__ = 'Alexander Popov'
__version__ = (0, 0, 1)
# Импорт модулей стандартной библиотеки
from os import getenv
# Импорт сторонних модулей
from loguru import logger
from dotenv import load_dotenv
from pyrogram import Client
# Импорт модулей приложения
from .db import DataBase
load_dotenv() # Выполняет чтение .env
# Приложение
app = Client(getenv('ACCOUNT'), api_id=getenv('APP_ID'), api_hash=getenv('APP_HASH'))
# База данных
db = DataBase(getenv('DB_PATH'))
# Логгер
logger.add(getenv('LOG_PATH'), compression='zip')
BOT_NAME = '@anicardplaybot'

32
app/__main__.py Normal file
View File

@ -0,0 +1,32 @@
# Импорт модулей стандартной библиотеки
import time
# Импорт модулей приложения
from . import app, db, logger
from .actions import get_top_wins, get_top_donates
async def main():
await app.start()
logger.info('Клиент Telegram запущен')
# Основной цикл программы
while True:
await get_top_wins() # Получает топ клана по победам
await get_top_donates() # Получает топ клана по пожертвованиям
time.sleep(12 * 60 * 60) # 12 часов
await app.stop()
logger.info('Клиент Telegram остановлен')
if __name__ == '__main__':
logger.info('Выполняется запуск приложения')
logger.info('Выполняется подключение к базе данных')
db.connect()
app.run(main())
db.close()
logger.info('Приложение завершило работу')

76
app/actions.py Normal file
View File

@ -0,0 +1,76 @@
# Импорт модулей стандартной библиотеки
import time
# Импорт модулей приложения
from . import app, logger, BOT_NAME
from .collect import parse_wins_top, parse_donates_top
async def get_top_wins() -> None:
logger.info('Выполняется получения списка побед клана')
await app.send_message(BOT_NAME, '🛡 Мой клан')
time.sleep(1)
message_id = 0
async for message in app.get_chat_history(BOT_NAME, limit=1):
message_id = message.id
await app.request_callback_answer(
chat_id=BOT_NAME,
message_id=message_id,
callback_data='my_clan:tops:91:0:0:0:0:1',
)
time.sleep(1)
async for message in app.get_chat_history(BOT_NAME, limit=1):
message_id = message.id
await app.request_callback_answer(
chat_id=BOT_NAME,
message_id=message_id,
callback_data='my_clan:top-wins:91:0:0:0:0:1',
)
time.sleep(1)
async for message in app.get_chat_history(BOT_NAME, limit=1):
parse_wins_top(message)
await app.read_chat_history(BOT_NAME)
logger.info('Получение списка побед клана завершено')
async def get_top_donates() -> None:
logger.info('Выполняется получения списка пожертвований клана')
await app.send_message(BOT_NAME, '🛡 Мой клан')
time.sleep(1)
message_id = 0
async for message in app.get_chat_history(BOT_NAME, limit=1):
message_id = message.id
await app.request_callback_answer(
chat_id=BOT_NAME,
message_id=message_id,
callback_data='my_clan:tops:91:0:0:0:0:1',
)
time.sleep(1)
async for message in app.get_chat_history(BOT_NAME, limit=1):
message_id = message.id
await app.request_callback_answer(
chat_id=BOT_NAME,
message_id=message_id,
callback_data='my_clan:top-donates:91:0:0:0:0:1',
)
time.sleep(1)
async for message in app.get_chat_history(BOT_NAME, limit=1):
parse_donates_top(message)
await app.read_chat_history(BOT_NAME)
logger.info('Получение списка пожертвований клана завершено')

81
app/collect.py Normal file
View File

@ -0,0 +1,81 @@
# Импорт модулей стандартной библиотеки
import re
# Импорт сторонних модулей
from pyrogram.types import Message
# Импорт модулей приложения
from . import db
def parse_wins_top(message: Message) -> None:
if message.text.startswith('🏆 Топ по победам'):
gamers = message.text.split('\n')
gamers.pop(0)
gamers.pop(0)
WINS = list()
for idx, gamer in enumerate(gamers):
gamer = re.sub(r'^\d+. ', '', gamer) # удаляет нумерацию
gamer, battle_count = gamer.split(' - ') # разделяет ник и количество побед в клановых сражениях
# оставляет ники игроков, Python не может в эмодзи в RegExp, по этому пришлось делать через str.replace()
# gamer = re.sub(r'\s[⚡⚜]$', '', gamer)
gamer = gamer.replace('', '')
gamer = gamer.replace('', '')
battle_count = battle_count.replace('', '') # удаляем эмодзи мечей
battle_count = int(re.sub(r'[^\x00-\x7F]', '', battle_count)) # преобразовывает строку в число
WINS.append(
{
'telegram_id': message.entities[idx].url.strip('http://t.me/'),
'username': gamer,
'count': battle_count,
}
)
# print('{0} - {1} (@{2})'.format(gamer, battle_count, message.entities[idx].url.strip('http://t.me/')))
db.add_data(WINS, True)
else:
pass
def parse_donates_top(message: Message) -> None:
if message.text.startswith('🏆 Топ по пожертвованиям'):
players = message.text.split('\n')
# Удаляет из массива строку с заголовком и последующую пустую строку
players.pop(0)
players.pop(0)
DONATES = list()
for idx, player in enumerate(players):
player = re.sub(r'^\d+. ', '', player) # удаляет нумерацию
player, donates_count = player.split(' - ') # разделяет ник и количество пожертвований в клановую сокровищницу
# Оставляет только ники игроков.
# Python не может парсить эмодзи в RegExp, по этому пришлось делать через str.replace()
# player = re.sub(r'\s[⚡⚜]$', '', player)
player = player.replace('', '')
player = player.replace('', '')
donates_count = donates_count.replace(' 💠', '') # удаляем эмодзи пожертвований
donates_count = int(re.sub(r'[^\x00-\x7F]', '', donates_count)) # преобразовывает строку в число
DONATES.append(
{
'telegram_id': message.entities[idx].url.strip('http://t.me/'),
'username': player,
'count': donates_count,
}
)
# print('{0} - {1} (@{2})'.format(player, donates_count, message.entities[idx].url.strip('http://t.me/')))
db.add_data(DONATES, False)
else:
pass

36
app/db.py Normal file
View File

@ -0,0 +1,36 @@
# Импорт модулей стандартной библиотеки
import sqlite3
import json
class DataBase(object):
"""..."""
def __init__(self, path):
super(DataBase, self).__init__()
self.path = path
def connect(self) -> bool:
self.conn = sqlite3.connect(self.path)
return True
def close(self) -> bool:
self.conn.close()
return True
def commit(self) -> None:
self.conn.commit()
return True
def add_data(self, data, wins: bool) -> bool: # wins изменит на Enum
if wins:
table = 'wins'
else:
table = 'donates'
cur = self.conn.cursor()
cur.execute('INSERT INTO {table} (\'data\') VALUES (\'{data}\')'.format(table=table, data=json.dumps(data)))
self.commit()
cur.close()
return True

8
app/utils.py Normal file
View File

@ -0,0 +1,8 @@
from . import app
async def get_telegram_id(username: str) -> int:
"""Возвращает Telegram ID по имени пользователя"""
telegram_user = await app.get_users(username)
return telegram_user.id

5
env.example Normal file
View File

@ -0,0 +1,5 @@
APP_ID=""
APP_HASH=""
ACCOUNT=""
LOG_PATH="/tmp/test.log"
DB_PATH="./db.sqlite"

3
pyproject.toml Normal file
View File

@ -0,0 +1,3 @@
[tool.black]
line-length = 123
skip-string-normalization = 1

1
requirements-dev.txt Normal file
View File

@ -0,0 +1 @@
black==24.10.0

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
Pyrogram==2.0.106
TgCrypto==1.2.5
python_dotenv==1.0.1

1
sql/README.md Normal file
View File

@ -0,0 +1 @@
`wins` заменить на `donates` для создания второй таблицы.

12
sql/table.sql Normal file
View File

@ -0,0 +1,12 @@
BEGIN;
CREATE TABLE IF NOT EXISTS 'wins'
(
'id' Integer PRIMARY KEY AUTOINCREMENT,
'timestamp' DateTime NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')),
'data' Text NOT NULL
);
CREATE INDEX IF NOT EXISTS 'index_timestamp' ON 'wins' ('timestamp');
COMMIT;

View File

@ -0,0 +1,14 @@
[Unit]
Description=AniCard Clan Stats
After=network.target network-online.target
[Service]
Type=exec
Environment=APP_DIR=/home/bot/AniCardClan
Environment=VIRTUAL_ENV=${APP_DIR}/venv
Environment=PYTHONPATH=${APP_DIR}
Environment=PATH=${VIRTUAL_ENV}/bin:$PATH
ExecStart=/bin/bash -c '${VIRTUAL_ENV}/bin/python3 -m norify'
[Install]
WantedBy=default.target

267
tests/donates-top.json Normal file
View File

@ -0,0 +1,267 @@
{
"_": "Message",
"id": 72311,
"from_user": {
"_": "User",
"id": 6018105307,
"is_self": false,
"is_contact": false,
"is_mutual_contact": false,
"is_deleted": false,
"is_bot": true,
"is_verified": false,
"is_restricted": false,
"is_scam": false,
"is_fake": false,
"is_support": false,
"is_premium": false,
"first_name": "AniCard",
"username": "anicardplaybot",
"dc_id": 2,
"photo": {
"_": "ChatPhoto",
"small_file_id": "AQADAgADiN0xG9osoEgAEAIAA9v_tGYBAAOnb8uWDDJj8AAEHgQ",
"small_photo_unique_id": "AgADiN0xG9osoEg",
"big_file_id": "AQADAgADiN0xG9osoEgAEAMAA9v_tGYBAAOnb8uWDDJj8AAEHgQ",
"big_photo_unique_id": "AgADiN0xG9osoEg"
}
},
"date": "2024-11-15 21:37:15",
"chat": {
"_": "Chat",
"id": 6018105307,
"type": "ChatType.BOT",
"is_verified": false,
"is_restricted": false,
"is_scam": false,
"is_fake": false,
"is_support": false,
"username": "anicardplaybot",
"first_name": "AniCard",
"photo": {
"_": "ChatPhoto",
"small_file_id": "AQADAgADiN0xG9osoEgAEAIAA9v_tGYBAAOnb8uWDDJj8AAEHgQ",
"small_photo_unique_id": "AgADiN0xG9osoEg",
"big_file_id": "AQADAgADiN0xG9osoEgAEAMAA9v_tGYBAAOnb8uWDDJj8AAEHgQ",
"big_photo_unique_id": "AgADiN0xG9osoEg"
},
"dc_id": 2
},
"mentioned": false,
"scheduled": false,
"from_scheduled": false,
"edit_date": "2024-11-15 21:37:17",
"has_protected_content": false,
"text": "🏆 Топ по пожертвованиям 💠\n\n1. Seraph ⚜ - 2825 💠\n2. Timur ⚡ - 2220 💠\n3. Valerix 02 ⚡ - 1666 💠\n4. Thomas ⚡ - 1623 💠\n5. Nota ⚡ - 1548 💠\n6. Marpol ⚡ - 1231 💠\n7. Юсти ⚡ - 1199 💠\n8. K_L_E_Y--- ⚡ - 1144 💠\n9. Abror ⚡ - 1062 💠\n10. Миша ⚡ - 1048 💠\n11. Easy ⚜ - 1000 💠\n12. Ssoh ⚡ - 823 💠\n13. Glad_loz ⚡ - 795 💠\n14. You are my sunshine ⚡ - 775 💠\n15. Po ⚡ - 774 💠\n16. Vladimir ⚡ - 715 💠\n17. Влад ⚡ - 671 💠\n18. Name ⚡ - 658 💠\n19. Мизик ⚡ - 611 💠\n20. Ksw ⚡ - 599 💠\n21. Тимофей ⚡ - 562 💠\n22. W1zard ⚡ - 550 💠\n23. Андрей ⚡ - 498 💠\n24. Данила ⚡ - 364 💠\n25.   Lqidator      ⚡ - 293 💠\n26. Александр ⚡ - 213 💠\n27. Ch0my_ ⚡ - 0 💠\n28. Musso ⚡ - 0 💠",
"entities": [
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 32,
"length": 9,
"url": "http://t.me/owariserph"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 55,
"length": 8,
"url": "http://t.me/timurgatiatullin"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 77,
"length": 13,
"url": "http://t.me/valerix02"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 104,
"length": 9,
"url": "http://t.me/thomas_02"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 127,
"length": 7,
"url": "http://t.me/NoTa145"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 148,
"length": 9,
"url": "http://t.me/marpol_l"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 171,
"length": 7,
"url": "http://t.me/Yustes_GuardianOfTheWorlds"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 192,
"length": 13,
"url": "http://t.me/KLEY_MOMENTS"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 219,
"length": 8,
"url": "http://t.me/Abroryy"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 242,
"length": 7,
"url": "http://t.me/alien1life"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 264,
"length": 7,
"url": "http://t.me/db_o_qp"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 286,
"length": 7,
"url": "http://t.me/Gnu_Linuks"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 307,
"length": 11,
"url": "http://t.me/Glad_loz1"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 332,
"length": 22,
"url": "http://t.me/Tailer_Jirden"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 368,
"length": 5,
"url": "http://t.me/Poooooooooooopp"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 387,
"length": 11,
"url": "http://t.me/h1ssoka"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 412,
"length": 7,
"url": "http://t.me/vlad_n5q"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 433,
"length": 7,
"url": "http://t.me/MrGhoul279"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 454,
"length": 8,
"url": "http://t.me/mizlony"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 476,
"length": 6,
"url": "http://t.me/Aassbube"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 496,
"length": 10,
"url": "http://t.me/halzov10"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 520,
"length": 9,
"url": "http://t.me/W1zard00"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 543,
"length": 9,
"url": "http://t.me/Allastar123"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 566,
"length": 9,
"url": "http://t.me/Staldent"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 589,
"length": 59,
"url": "http://t.me/qaak01"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 662,
"length": 12,
"url": "http://t.me/yneznauchto"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 688,
"length": 9,
"url": "http://t.me/to4no_ch0my"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 709,
"length": 8,
"url": "http://t.me/mus_wf"
}
],
"outgoing": false,
"reply_markup": {
"_": "InlineKeyboardMarkup",
"inline_keyboard": [
[
{
"_": "InlineKeyboardButton",
"text": "Назад 🔙",
"callback_data": "my_clan:tops:91:0:0:0:0:1"
}
]
]
}
}

267
tests/win-top.json Normal file
View File

@ -0,0 +1,267 @@
{
"_": "Message",
"id": 71945,
"from_user": {
"_": "User",
"id": 6018105307,
"is_self": false,
"is_contact": false,
"is_mutual_contact": false,
"is_deleted": false,
"is_bot": true,
"is_verified": false,
"is_restricted": false,
"is_scam": false,
"is_fake": false,
"is_support": false,
"is_premium": false,
"first_name": "AniCard",
"username": "anicardplaybot",
"dc_id": 2,
"photo": {
"_": "ChatPhoto",
"small_file_id": "AQADAgADiN0xG9osoEgAEAIAA9v_tGYBAAOnb8uWDDJj8AAEHgQ",
"small_photo_unique_id": "AgADiN0xG9osoEg",
"big_file_id": "AQADAgADiN0xG9osoEgAEAMAA9v_tGYBAAOnb8uWDDJj8AAEHgQ",
"big_photo_unique_id": "AgADiN0xG9osoEg"
}
},
"date": "2024-11-15 19:23:05",
"chat": {
"_": "Chat",
"id": 6018105307,
"type": "ChatType.BOT",
"is_verified": false,
"is_restricted": false,
"is_scam": false,
"is_fake": false,
"is_support": false,
"username": "anicardplaybot",
"first_name": "AniCard",
"photo": {
"_": "ChatPhoto",
"small_file_id": "AQADAgADiN0xG9osoEgAEAIAA9v_tGYBAAOnb8uWDDJj8AAEHgQ",
"small_photo_unique_id": "AgADiN0xG9osoEg",
"big_file_id": "AQADAgADiN0xG9osoEgAEAMAA9v_tGYBAAOnb8uWDDJj8AAEHgQ",
"big_photo_unique_id": "AgADiN0xG9osoEg"
},
"dc_id": 2
},
"mentioned": false,
"scheduled": false,
"from_scheduled": false,
"edit_date": "2024-11-15 19:23:07",
"has_protected_content": false,
"text": "🏆 Топ по победам ⚔\n\n1. Valerix 02 ⚡ - 110 ⚔\n2. Thomas ⚡ - 94 ⚔\n3. Nota ⚡ - 93 ⚔\n4. Timur ⚡ - 86 ⚔\n5. Marpol ⚡ - 65 ⚔\n6. Easy ⚜ - 60 ⚔\n7. Vladimir ⚡ - 58 ⚔\n8. Abror ⚡ - 57 ⚔\n9. Миша ⚡ - 52 ⚔\n10. Юсти ⚡ - 52 ⚔\n11. Ssoh ⚡ - 49 ⚔\n12. K_L_E_Y--- ⚡ - 41 ⚔\n13. Влад ⚡ - 40 ⚔\n14. Name ⚡ - 39 ⚔\n15. Seraph ⚜ - 39 ⚔\n16. Po ⚡ - 32 ⚔\n17. You are my sunshine ⚡ - 31 ⚔\n18. W1zard ⚡ - 30 ⚔\n19. Ksw ⚡ - 22 ⚔\n20. Тимофей ⚡ - 17 ⚔\n21. Мизик ⚡ - 16 ⚔\n22. Glad_loz ⚡ - 15 ⚔\n23. Андрей ⚡ - 15 ⚔\n24. Данила ⚡ - 9 ⚔\n25.   Lqidator      ⚡ - 6 ⚔\n26. Ch0my_ ⚡ - 6 ⚔\n27. Musso ⚡ - 6 ⚔\n28. Александр ⚡ - 5 ⚔",
"entities": [
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 25,
"length": 13,
"url": "http://t.me/valerix02"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 51,
"length": 9,
"url": "http://t.me/thomas_02"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 72,
"length": 7,
"url": "http://t.me/NoTa145"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 91,
"length": 8,
"url": "http://t.me/timurgatiatullin"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 111,
"length": 9,
"url": "http://t.me/marpol_l"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 132,
"length": 7,
"url": "http://t.me/db_o_qp"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 151,
"length": 11,
"url": "http://t.me/h1ssoka"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 174,
"length": 8,
"url": "http://t.me/Abroryy"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 194,
"length": 7,
"url": "http://t.me/alien1life"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 214,
"length": 7,
"url": "http://t.me/Yustes_GuardianOfTheWorlds"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 234,
"length": 7,
"url": "http://t.me/Gnu_Linuks"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 254,
"length": 13,
"url": "http://t.me/KLEY_MOMENTS"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 280,
"length": 7,
"url": "http://t.me/vlad_n5q"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 300,
"length": 7,
"url": "http://t.me/MrGhoul279"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 320,
"length": 9,
"url": "http://t.me/owariserph"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 342,
"length": 5,
"url": "http://t.me/Poooooooooooopp"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 360,
"length": 22,
"url": "http://t.me/Tailer_Jirden"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 395,
"length": 9,
"url": "http://t.me/W1zard00"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 417,
"length": 6,
"url": "http://t.me/Aassbube"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 436,
"length": 10,
"url": "http://t.me/halzov10"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 459,
"length": 8,
"url": "http://t.me/mizlony"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 480,
"length": 11,
"url": "http://t.me/Glad_loz1"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 504,
"length": 9,
"url": "http://t.me/Allastar123"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 526,
"length": 9,
"url": "http://t.me/Staldent"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 547,
"length": 59,
"url": "http://t.me/qaak01"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 618,
"length": 9,
"url": "http://t.me/to4no_ch0my"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 639,
"length": 8,
"url": "http://t.me/mus_wf"
},
{
"_": "MessageEntity",
"type": "MessageEntityType.TEXT_LINK",
"offset": 659,
"length": 12,
"url": "http://t.me/yneznauchto"
}
],
"outgoing": false,
"reply_markup": {
"_": "InlineKeyboardMarkup",
"inline_keyboard": [
[
{
"_": "InlineKeyboardButton",
"text": "Назад 🔙",
"callback_data": "my_clan:tops:91:0:0:0:0:1"
}
]
]
}
}