vk_chat_bot/tg/tasks.py

139 lines
4.3 KiB
Python

import datetime
import traceback
from asyncio import sleep
from aiogram import Bot
from aiogram.exceptions import TelegramBadRequest, TelegramMigrateToChat
from aiogram.types import ChatMemberBanned, ChatMemberLeft
from aiogram.utils.formatting import Bold
import tg.tg_database as database
from tg.handlers.user import format_rating
async def is_user_in_chat(bot: Bot, chat_id: int, user_id: int) -> bool:
found = True
try:
info = await bot.get_chat_member(chat_id, user_id)
if isinstance(info, ChatMemberLeft) or isinstance(info, ChatMemberBanned):
found = False
except TelegramBadRequest:
found = False
except Exception:
pass
return found
async def cleanup_chats(bot: Bot):
me = await bot.get_me()
# Обработка миграций чатов
for chat in database.DB.get_chats():
chat_id = chat['id']
if chat_id > 0:
continue
try:
await bot.get_chat(chat_id)
except TelegramMigrateToChat as e:
new_id = e.migrate_to_chat_id
new_chat = database.DB.get_chat(new_id)
if new_chat is None or new_chat['active'] == 0:
database.DB.chat_delete(new_id)
database.DB.chat_update(chat_id, id=new_id)
else:
database.DB.chat_delete(chat_id)
except Exception:
continue
# Удаление чатов, в которых больше нет бота
for chat in database.DB.get_chats():
chat_id = chat['id']
if chat_id > 0:
# TODO
continue
if not await is_user_in_chat(bot, chat_id, me.id):
database.DB.chat_delete(chat_id)
async def cleanup_users(bot: Bot):
for chat in database.DB.get_chats():
if chat['active'] == 0:
continue
chat_id = chat['id']
for user in database.DB.get_top_silent(chat_id=chat_id, threshold_days=14):
user_id = user['user_id']
if not await is_user_in_chat(bot, chat_id, user_id):
database.DB.delete_user(chat_id, user_id)
print(f'Из чата (id={chat_id}) удален пользователь (id={user_id})')
async def reset_counters(reset_month: bool, bot: Bot):
database.DB.reset_messages_today()
print('Дневные счетчики сброшены.')
if reset_month:
for chat in database.DB.get_chats():
if chat['active'] == 0:
continue
top_users = database.DB.get_top_messages_month(chat['id'])
if len(top_users) == 0:
continue
message = Bold('Итоговая статистика за прошедший месяц') + '\n'
message += await format_rating(chat['id'], top_users, bot)
await bot.send_message(chat_id=chat['id'], **message.as_kwargs())
database.DB.reset_messages_month()
print('Месячные счетчики сброшены.')
async def wait_until(target_time: datetime.datetime):
now = datetime.datetime.now(target_time.tzinfo)
if now >= target_time:
return
delay_seconds = (target_time - now).total_seconds()
await sleep(delay_seconds)
async def daily_maintenance_task(bot: Bot):
tz = datetime.timezone(datetime.timedelta(hours=3), name="MSK")
target_time = datetime.time(6, 0, 0, tzinfo=tz)
now = datetime.datetime.now(tz)
if now.hour > target_time.hour or now.hour == target_time.hour and now.minute > target_time.minute:
target_date = now.date() + datetime.timedelta(days=1)
else:
target_date = now.date()
target_datetime = datetime.datetime.combine(target_date, target_time)
while True:
await wait_until(target_datetime)
try:
await cleanup_chats(bot)
except Exception:
print(traceback.format_exc())
try:
await cleanup_users(bot)
except Exception:
print(traceback.format_exc())
try:
await reset_counters(target_datetime.day == 1, bot)
except Exception:
print(traceback.format_exc())
target_datetime = target_datetime + datetime.timedelta(days=1)
async def startup_task(bot: Bot):
me = await bot.get_me()
print(f"Бот '{me.full_name}' (id={me.id}) запущен.")