Refactoring: handlers now implemented as classes. Module structure

This commit is contained in:
2024-02-11 12:40:11 +03:00
parent c2d7fd41d7
commit f3cdeeff3c
15 changed files with 330 additions and 316 deletions

View File

View File

@@ -0,0 +1,22 @@
from aiogram import types
from aiogram.filters import Command
from neuroapi import neuroapi
from .handler import MessageHandlerABC
class EditCommand(MessageHandlerABC):
filter = Command('edit')
async def _command(self, message: types.Message):
command = message.text.split(' ', 2)
if len(command)<3:
await message.reply('Недостаточно аргументов!')
return
try:
await neuroapi.post.edit_text_by_order_num(command[1], command[2], message.entities)
#TODO: Message Entities для уведомления об изменении поста
await message.reply(f'Текст поста успешно изменен на: {command[2]}')
except Exception as e:
await message.reply(f'Ошибка: {e}')

View File

@@ -0,0 +1,34 @@
from typing import List
from aiogram import F, types
from aiogram.enums import ChatMemberStatus
from neuroapi import neuroapi
from neuroapi.types import Admin as AdminType
from neuroapi.types import BotSettings
from .handler import MessageHandlerABC
class ForwardMessageCommand(MessageHandlerABC):
filter = F.chat.type == 'private'
async def _command(self, message: types.Message):
self.settings = BotSettings.get_instance()
user = await self.bot.get_chat_member(self.settings.channel, message.from_user.id)
if user is None:
await message.reply('Ошибка')
return
user_in_channel = user.status == ChatMemberStatus.LEFT
admins: List[AdminType] = await neuroapi.admin.get()
canReply = True
for admin in admins:
await self.bot.send_message(admin.user_id, f'Вам новое сообщение от пользователя {message.from_user.full_name}. ' +
(f'\nНик: @{message.from_user.username}' if message.from_user.username else f'ID: {message.from_user.id}') +
f'\nПользователь{" не " if user_in_channel else " "}состоит в канале')
try:
forwarded_message = await self.bot.forward_message(admin.user_id, message.chat.id, message.message_id)
if forwarded_message.forward_from is None:
canReply = False
except:
pass
await message.reply('Ваше сообщение было отправлено администраторам'+('' if canReply else '\nНо они не смогут вам ответить из-за ваших настроек конфиденциальности.'))

View File

@@ -0,0 +1,26 @@
from abc import ABC, abstractmethod
from typing import Any, Coroutine, Dict
from aiogram import Bot
from aiogram.filters import Filter
class MessageHandlerABC(ABC):
bot: Bot
def __init__(self, bot: Bot, *args: Any, **kwargs: Dict[str, Any]) -> None:
assert isinstance(bot, Bot)
self.bot = bot
@abstractmethod
def _command(self, *args, **kwargs):
raise NotImplementedError
@property
def handler(self) -> Coroutine[None, None, None]:
return self._command
@property
@abstractmethod
def filter(self) -> Filter:
raise NotImplementedError

View File

@@ -0,0 +1,49 @@
from typing import List
from aiogram import types
from aiogram.filters import Command
import neuroapi.types as neuroTypes
from neuroapi import neuroapi
from .handler import MessageHandlerABC
class InfoCommand(MessageHandlerABC):
filter = Command('info')
async def _command(self, message: types.Message):
posts: List[neuroTypes.Post] = await neuroapi.post.get_will_post()
admins: List[neuroTypes.Admin] = await neuroapi.admin.get()
post_c = {}
k = 1
for post in posts:
if post.from_user_id not in post_c:
post_c[post.from_user_id] = 1
else:
post_c[post.from_user_id] += 1
res = "Количество постов от админов:\n"
res2 = "\nПосты:\n"
posts_entities: List[types.MessageEntity] = []
for admin in admins:
if admin.user_id in post_c:
res += f'[{admin.user_name}](tg://user?id={admin.user_id}): {post_c[admin.user_id]}\n'
else:
res += f'[{admin.user_name}](tg://user?id={admin.user_id}): 0\n'
admin_posts = list(
filter(lambda x: x.from_user_id == admin.user_id, posts))
res2 += f'\nПосты от {admin.user_name}:\n'
if len(admin_posts):
for i, post in enumerate(admin_posts):
# TODO: Если возможно, сделать чтоб было ссылкой на сообщений с /newpost
s = f'{i+1}.({posts.index(post)+1}) {post.text}\n'
k+=1
res2 += s
for entity in post.message_entities:
entity.offset += 6+res2.index(s)
posts_entities.append(entity)
else:
res2 += 'Их нет)\n'
await message.answer(res.replace('#', '\#').replace(
"_", "\_").replace('.', '\.').replace(',', '\,').replace('!', '\!').replace('-', '\-').replace(':', '\:').replace('+', '\+'), parse_mode='markdownv2')
await message.answer(res2, entities=posts_entities)

View File

@@ -0,0 +1,21 @@
from aiogram import types
import neuroapi.types as neuroTypes
from handlers.filters.new_post import NewPostFilter, NewSoloPostFilter
from neuroapi import neuroapi
from .handler import MessageHandlerABC
class NewPostCommand(MessageHandlerABC):
filter = NewPostFilter()
async def _command(self, message: types.Message):
post: neuroTypes.Post = await neuroapi.post.get_by_media_group_id(message.media_group_id)
await neuroapi.image.add(str(post.uuid), message.photo[-1].file_id, message.has_media_spoiler, message.message_id)
class NewPostSoloCommand(MessageHandlerABC):
filter = NewSoloPostFilter()
async def _command(self, message: types.Message):
post: neuroTypes.Post = await neuroapi.post.new(message.caption.replace('/newpost ', ''), message.from_user.id, message_entities=message.caption_entities)
await neuroapi.image.add(str(post.uuid), message.photo[-1].file_id, message.has_media_spoiler, message.message_id)
await message.answer('Пост успешно добавлен!')

View File

@@ -0,0 +1,31 @@
from aiogram import types
from aiogram.filters import Command
from aiogram.utils.media_group import MediaGroupBuilder
import neuroapi.types as neuroTypes
from neuroapi import neuroapi
from .handler import MessageHandlerABC
class PostCommand(MessageHandlerABC):
filter = Command('post')
async def _command(self, message: types.Message):
settings = neuroTypes.BotSettings.get_instance()
try:
post = await neuroapi.post.get_post_to_post()
if (post):
images = MediaGroupBuilder(
caption=post.text + '\n\nПредложка: @neur0w0men_reply_bot', caption_entities=post.message_entities)
image: neuroTypes.Image
for image in sorted(post.images, key=lambda x: x.message_id):
images.add_photo(image.file_id,
has_spoiler=image.has_spoiler)
await self.bot.send_media_group(settings.channel, images.build())
if message:
await message.answer('Пост успешно опубликован!')
elif message:
await message.answer('Нет постов')
except Exception as e:
if message:
await message.answer(f'Ошибка {e}')

View File

@@ -0,0 +1,18 @@
from aiogram import types
from handlers.filters.reply_to_user import ReplyToUser
from .handler import MessageHandlerABC
class ReplyToUserCommand(MessageHandlerABC):
filter = ReplyToUser()
async def _command(self, message: types.Message):
if message.reply_to_message.forward_from is None:
await message.reply('Пользователь стесняшка и не разрешает отвечать на его сообщения...')
else:
try:
await self.bot.send_message(message.reply_to_message.forward_from.id, f'Вам ответил админ:\n{message.text}', entities=message.entities)
await message.reply('Ваше сообщение было отправлено!')
except Exception as e:
await message.reply(f'Ошибка! "{e}"')

View File

@@ -0,0 +1,14 @@
from aiogram import types
from aiogram.filters import Command
from neuroapi.types import BotSettings
from .handler import MessageHandlerABC
class SettingsCommand(MessageHandlerABC):
filter = Command('settings')
async def _command(self, message: types.Message):
self.settings = BotSettings.get_instance()
s = f"Текущие настройки:\n{self.settings.get_text()}"
await message.answer(s)

View File

@@ -0,0 +1,12 @@
from aiogram import types
from aiogram.filters import CommandStart
from .handler import MessageHandlerABC
class StartCommand(MessageHandlerABC):
filter = CommandStart()
async def _command(self, message: types.Message):
await message.answer("Добро пожаловать! Данный бот - предложка для канала @neur0w0men. Отправляйте свои пожелания насчет нейрокартинок, а также свои картинки, а админы постараются заняться этим!\nДанный бот принимает текст, картинки, документы и стикеры.")

View File

@@ -0,0 +1,45 @@
import asyncio
from typing import Coroutine
import aioschedule as schedule
from aiogram import Bot, types
from aiogram.filters import Command
from neuroapi import neuroapi
from neuroapi.types import BotSettings
from .handler import MessageHandlerABC
class UpdateSettingsCommand(MessageHandlerABC):
settings: BotSettings
post: Coroutine
filter = Command('update_settings')
async def settings_and_schedule_checker(self):
await self._command()
while 1:
await schedule.run_pending()
await asyncio.sleep(1)
def __init__(self, bot: Bot, post_command: Coroutine, *args) -> None:
super().__init__(bot)
self.post = post_command
asyncio.create_task(self.settings_and_schedule_checker())
async def _command(self, mes: types.Message | None = None):
self.settings = await neuroapi.bot_settings.get()
schedule.clear()
schedule.every().minute.do(self._command, None)
# TODO: Сделать в бэке и в боте, чтоб дни тоже можно было в настройках хранить
for i in self.settings.message_times:
schedule.every().monday.at(i).do(self.post, None)
schedule.every().tuesday.at(i).do(self.post, None)
schedule.every().wednesday.at(i).do(self.post, None)
schedule.every().thursday.at(i).do(self.post, None)
schedule.every().friday.at(i).do(self.post, None)
schedule.every().sunday.at(i).do(self.post, None)
if mes:
await mes.answer('Настройки обновлены!')