Contin refactoring pt. 2

This commit is contained in:
kittyneverdies 2024-02-01 19:41:50 +03:00
parent 104e91373c
commit 4839b94cbc
18 changed files with 998 additions and 38 deletions

View file

@ -1,5 +1,6 @@
from typing import Callable
from aiogram import Dispatcher
from aiogram.filters import CommandObject
from aiogram.types import InlineKeyboardMarkup, Message, CallbackQuery
from sqlalchemy.ext.asyncio import async_sessionmaker
@ -7,15 +8,16 @@ from sqlalchemy.ext.asyncio import async_sessionmaker
from bozenka.database.tables.telegram import TelegramChatSettings
class LineralFeature:
class BasicFeature:
"""
A classic class of lineral (basic)
feature of bozenka. IN FUTURE!
"""
cmd_description: str = "Your description of command"
@NotImplemented
'''
Here is example of code you can add:
async def generate_telegram_inline_keyboard(self) -> InlineKeyboardMarkup:
"""
Generates a special telegram keyboard (menu)
@ -23,10 +25,10 @@ class LineralFeature:
"""
pass
@NotImplemented
async def telegram_command_handler(self, msg: Message, cmd: CommandObject, session_maker: async_sessionmaker) -> None:
"""
A special telegram handler for command (if exist)
It is just an example
:param msg: Telegram message object
:param cmd: Aiogram command object
:param session_maker: Async session maker object of SQLAlchemy
@ -34,17 +36,17 @@ class LineralFeature:
"""
pass
@NotImplemented
async def telegram_callback_handler(self, call: CallbackQuery, callback_data, session_maker: async_sessionmaker) -> None:
"""
A special telegram handler for command (if exist)
It is just an example
:param call: Telegram callbackquery object
:param callback_data: A custom callback data created by callback factory
:param session_maker: Async session maker object of SQLAlchemy
:return: Nothing
"""
pass
'''
def __init__(self):
"""
All information about feature
@ -56,3 +58,15 @@ class LineralFeature:
self.telegram_commands: list[str | None] = ["test"]
self.telegram_cmd_avaible = True # Is a feature have a commands
self.telegram_callback_factory = None
self.telegram_message_handlers = {
"""
Format is
Handler: [Filters]
"""
}
self.telegram_callback_handlers = {
"""
Format is
Handler: [Filters]
"""
}

View file

View file

View file

@ -0,0 +1,469 @@
import logging
from aiogram import F
from aiogram.enums import ChatType, ChatMemberStatus
from aiogram.filters import Command, CommandObject
from aiogram.types import InlineKeyboardMarkup, Message, CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy.ext.asyncio import async_sessionmaker
from bozenka.database.tables.telegram import get_chat_config_value
from bozenka.features import BasicFeature
from bozenka.instances.telegram.utils.callbacks_factory import HelpCategory, HelpBackCategory, HelpFeature, HelpBack, \
UnbanData, BanData, UnmuteData, MuteData
from bozenka.instances.telegram.utils.keyboards import help_category_keyboard, help_keyboard, \
help_feature_keyboard, gpt_categories_keyboard, ban_keyboard, delete_keyboard, mute_keyboard, unmute_keyboard, \
unban_keyboard
from bozenka.instances.telegram.utils.simpler import list_of_features, SolutionSimpler
from bozenka.instances.version import build, is_updated
class Moderation(BasicFeature):
"""
A class of moderation related feature
All staff related to it will be here
"""
cmd_description: str = "Basic command to show main menu"
@staticmethod
async def telegram_ban_callback_handler(call: CallbackQuery, callback_data: BanData,
session_maker: async_sessionmaker) -> None:
"""
CallbackQuery handler, what bannes users after callback
:param call: CallBackQuery telegram object
:param callback_data: BanData object
:param session_maker: AsyncSessionmaker object
:return: None
"""
clicked_user = await call.message.chat.get_member(call.from_user.id)
banned_user = await call.message.chat.get_member(int(callback_data.user_id_ban))
send_notification = await get_chat_config_value(chat_id=call.message.chat.id, session=session_maker,
setting=list_of_features["Admin"][5])
if call.from_user.id != callback_data.user_id_clicked \
and clicked_user.status not in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.CREATOR]:
return
await SolutionSimpler.inline_ban_user(call=call, data=callback_data, session=session_maker)
if not banned_user.is_member and banned_user.status == ChatMemberStatus.KICKED:
await call.answer("Уже заблокирован ✅")
else:
await call.answer("Успешно заблокирован ✅")
await call.message.edit_text(
"Удача ✅\n"
f"{banned_user.user.mention_html('Этот пользователь')} был заблокирован {call.from_user.mention_html('этим пользователем')}.",
reply_markup=ban_keyboard(admin_id=call.from_user.id, ban_id=banned_user.user.id)
)
if send_notification:
await call.message.bot.send_message(
chat_id=banned_user.user.id,
text=f"{banned_user.user.mention_html('Вы')} были заблокированы {call.from_user.mention_html('этим пользователем')} в чате <code>{call.message.chat.id}</code>.",
reply_markup=delete_keyboard(admin_id=banned_user.user.id)
)
logging.log(msg=f"Banned user @{banned_user.user.full_name} user_id=f{banned_user.user.id}", level=logging.INFO)
@staticmethod
async def telegram_unban_callback_handler(call: CallbackQuery, callback_data: UnbanData,
session_maker: async_sessionmaker) -> None:
"""
CallbackQuery handler, what unbannes users after callback
:param call: CallBackQuery telegram object
:param callback_data: UnbanData object
:param session_maker: AsyncSessionmaker object
:return: None
"""
clicked_user = await call.message.chat.get_member(call.from_user.id)
unbanned_user = await call.message.chat.get_member(int(callback_data.user_id_unban))
if call.from_user.id != callback_data.user_id_clicked \
and clicked_user.status not in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.CREATOR]:
return
await SolutionSimpler.inline_unban_user(call=call, data=callback_data, session=session_maker)
if unbanned_user.is_member and unbanned_user.status != ChatMemberStatus.KICKED:
await call.answer("Уже разблокирован ✅")
else:
await call.answer("Успешно разблокирован ✅")
await call.message.edit_text(
"Удача ✅\n"
f"{unbanned_user.user.mention_html('Этот пользователь')} был разблокирован {call.from_user.mention_html('этим пользователем')}.",
reply_markup=unban_keyboard(admin_id=call.from_user.id, ban_id=unbanned_user.user.id)
)
if await get_chat_config_value(chat_id=call.message.chat.id, session=session_maker,
setting=list_of_features["Admin"][5]):
await call.message.bot.send_message(
chat_id=unbanned_user.user.id,
text=f"{unbanned_user.user.mention_html('Вы')} были разблокирован {call.from_user.mention_html('этим пользователем')} в чате <code>{call.message.chat.id}</code>.",
reply_markup=delete_keyboard(admin_id=unbanned_user.user.id)
)
logging.log(msg=f"Unbanned user @{unbanned_user.user.full_name} user_id=f{unbanned_user.user.id}",
level=logging.INFO)
@staticmethod
async def telegram_ban_cmd_handler(msg: Message, command: CommandObject, session_maker: async_sessionmaker) -> None:
"""
/ban command function, supports time and reasons.
:param msg: Message telegram object
:param command: Object of telegram command
:param session_maker: Session maker object of SqlAlchemy
:return: Nothing
"""
banned_user = await msg.chat.get_member(msg.reply_to_message.from_user.id)
send_to_dm = await get_chat_config_value(chat_id=msg.chat.id, session=session_maker,
setting=list_of_features["Admin"][4])
send_notification = await get_chat_config_value(chat_id=msg.chat.id, session=session_maker,
setting=list_of_features["Admin"][5])
where_send = {
True: msg.from_user.id,
False: msg.chat.id
}
if banned_user.status == ChatMemberStatus.KICKED:
await msg.bot.send_message(chat_id=where_send[send_to_dm],
text="Ошибка ❌\n"
"Этот пользователь уже удален из группы",
reply_markup=delete_keyboard(msg.from_user.id))
return
config = await SolutionSimpler.ban_user(msg, command, session_maker)
if config["reason"] and config["ban_time"]:
await msg.bot.send_message(chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"{msg.reply_to_message.from_user.mention_html('Этот пользователь')} "
f"был заблокирован {msg.from_user.mention_html('этим пользователем')}.\n"
f"По причине <code>{config['reason']}</code>, до даты <code>{config['ban_time']}</code>",
reply_markup=ban_keyboard(msg.from_user.id, msg.reply_to_message.from_user.id))
if send_notification:
await msg.bot.send_message(chat_id=banned_user.user.id,
text="Вы "
f"были заблокированы {msg.from_user.mention_html('этим пользователем')} в чате <code>{msg.chat.title}</code>.\n"
f"По причине <code>{config['reason']}, до даты <code>{config['ban_time']}</code>",
reply_markup=delete_keyboard(admin_id=banned_user.user.id))
elif config["reason"]:
await msg.bot.send_message(chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"{msg.reply_to_message.from_user.mention_html('Этот пользователь')} "
f"был заблокирован {msg.reply_to_message.from_user.mention_html('этим пользователем')}.\n"
f"По причине <code>{config['reason']}</code>.",
reply_markup=ban_keyboard(admin_id=msg.from_user.id,
ban_id=msg.reply_to_message.from_user.id))
if send_notification:
await msg.bot.send_message(chat_id=banned_user.user.id,
text=f"Вы "
f"были заблокированы {msg.from_user.mention_html('этим пользователем')} в чате <code>{msg.chat.title}</code>.\n"
f"По причине <code>{config['reason']}</code>.",
reply_markup=delete_keyboard(admin_id=banned_user.user.id))
elif config["ban_time"]:
await msg.bot.send_message(chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"{msg.reply_to_message.from_user.mention_html('Этот пользователь')} "
f"был заблокирован {msg.from_user.mention_html('этим пользователем')}, до даты <code>{config['ban_time']}</code>",
reply_markup=ban_keyboard(admin_id=msg.from_user.id,
ban_id=msg.reply_to_message.from_user.id))
if send_notification:
await msg.bot.send_message(chat_id=banned_user.user.id,
text=f"Вы "
f"были заблокированы {msg.from_user.mention_html('этим пользователем')} в чате <code>{msg.chat.title}</code>.\n"
f"До даты <code>{config['ban_time']}</code>.",
reply_markup=delete_keyboard(admin_id=banned_user.user.id))
else:
await msg.bot.send_message(chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"{msg.reply_to_message.from_user.mention_html('Этот пользователь')}"
f" был заблокирован {msg.from_user.mention_html('этим пользователем')}.",
reply_markup=ban_keyboard(msg.from_user.id, msg.reply_to_message.from_user.id))
if send_notification:
await msg.bot.send_message(chat_id=banned_user.user.id,
text=f"Вы "
f"были заблокированы {msg.from_user.mention_html('этим пользователем')} в чате "
f"<code>{msg.chat.title}</code>.\n",
reply_markup=delete_keyboard(admin_id=banned_user.user.id))
@staticmethod
async def telegram_unban_cmd_handler(msg: Message, command: CommandObject,
session_maker: async_sessionmaker) -> None:
"""
/unban command function
:param msg: Message telegram object
:param command: Object of telegram command
:param session_maker: Session maker object of SqlAlchemy
"""
await SolutionSimpler.unban_user(msg, session_maker)
unbanned_user = await msg.chat.get_member(msg.reply_to_message.from_user.id)
send_to_dm = await get_chat_config_value(chat_id=msg.chat.id, session=session_maker,
setting=list_of_features["Admin"][4])
send_notification = await get_chat_config_value(chat_id=msg.chat.id, session=session_maker,
setting=list_of_features["Admin"][5])
where_send = {
True: msg.from_user.id,
False: msg.chat.id
}
if unbanned_user.is_member and unbanned_user.status != ChatMemberStatus.KICKED:
await msg.bot.send_message(
chat_id=where_send[send_to_dm],
text="Ошибка ❌\n"
"Этот пользователь не находится в бане.",
reply_markup=delete_keyboard(admin_id=msg.from_user.id)
)
return
elif not command.text:
await msg.bot.send_message(
chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"{msg.reply_to_message.from_user.mention_html('Этот пользователь')} был разблокирован "
f"{msg.from_user.mention_html('этим пользователем')}.\n",
reply_markup=unban_keyboard(admin_id=msg.from_user.id, ban_id=msg.reply_to_message.from_user.id)
)
if send_notification:
await msg.bot.send_message(
chat_id=unbanned_user.user.id,
text=f"{msg.reply_to_message.from_user.mention_html('Вы')} "
f"был разблокированы {msg.from_user.mention_html('этим пользователем')} в чате <code>{msg.chat.title}</code>.\n",
reply_markup=delete_keyboard(admin_id=unbanned_user.user.id)
)
else:
await msg.bot.send_message(
chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"Пользователь {msg.reply_to_message.from_user.mention_html()} был разблокирован пользователем {msg.from_user.mention_html()}.\n"
f"По причине {CommandObject.text}.",
reply_markup=delete_keyboard(admin_id=unbanned_user.user.id)
)
if send_notification:
await msg.bot.send_message(
chat_id=unbanned_user.user.id,
text=f"{msg.reply_to_message.from_user.mention_html('Вы')} "
f"был разблокированы {msg.from_user.mention_html('этим пользователем')} в чате <code>{msg.chat.title}</code>.\n"
f"По причине <code>{CommandObject.text}</code>",
reply_markup=delete_keyboard(admin_id=unbanned_user.user.id)
)
@staticmethod
async def telegram_mute_callback_handler(call: CallbackQuery, callback_data: MuteData,
session_maker: async_sessionmaker) -> None:
"""
Query, what mutes users after callback
:param call: CallBackQuery telegram object
:param callback_data: BanData object
:param session_maker: AsyncSessionmaker object
:return:
"""
clicked_user = await call.message.chat.get_member(call.from_user.id)
muted_user = await call.message.chat.get_member(int(callback_data.user_id_mute))
if call.from_user.id != callback_data.user_id_clicked \
and clicked_user.status not in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.CREATOR]:
return
await SolutionSimpler.inline_mute_user(call=call, data=callback_data, session=session_maker)
if not muted_user.can_send_messages and muted_user.status == ChatMemberStatus.RESTRICTED:
await call.answer("Уже замучен ✅")
else:
await call.answer("Успешно замучен ✅")
await call.message.edit_text(
"Удача ✅\n"
f"{muted_user.user.mention_html('Этот пользователь')} был замучен {call.from_user.mention_html('этим пользователем')}.",
reply_markup=mute_keyboard(admin_id=call.from_user.id, mute_id=callback_data.user_id_mute)
)
send_notification = await get_chat_config_value(chat_id=call.message.chat.id, session=session_maker,
setting=list_of_features["Admin"][5])
if send_notification:
await call.message.bot.send_message(
chat_id=muted_user.user.id,
text=f"{muted_user.user.mention_html('Вы')} были замучены {call.from_user.mention_html('этим пользователем')} в чате <code>{call.message.chat.id}</code>.",
reply_markup=delete_keyboard(admin_id=muted_user.user.id)
)
logging.log(msg=f"Muted user @{muted_user.user.full_name} user_id=f{muted_user.user.id}", level=logging.INFO)
@staticmethod
async def telegram_unmute_callback_handler(call: CallbackQuery, callback_data: UnmuteData,
session_maker: async_sessionmaker) -> None:
"""
Query, what unbannes users after callback
:param call: CallBackQuery telegram object
:param callback_data: UnbanData object
:param session_maker: AsyncSessionmaker object
:return:
"""
clicked_user = await call.message.chat.get_member(call.from_user.id)
unmuted_user = await call.message.chat.get_member(int(callback_data.user_id_unmute))
if call.from_user.id != callback_data.user_id_clicked \
and clicked_user.status not in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.CREATOR]:
return
await SolutionSimpler.inline_unmute_user(call=call, data=callback_data, session=session_maker)
if unmuted_user.can_send_messages or unmuted_user.status == ChatMemberStatus.RESTRICTED:
await call.answer("Уже размучен ✅")
else:
await call.answer("Успешно размучен ✅")
await call.message.edit_text(
"Удача ✅\n"
f"{unmuted_user.user.mention_html('Этот пользователь')} был размучен {call.from_user.mention_html('этим пользователем')}.",
reply_markup=unmute_keyboard(admin_id=call.from_user.id, unmute_id=unmuted_user.user.id)
)
send_notification = await get_chat_config_value(chat_id=call.message.chat.id, session=session_maker,
setting=list_of_features["Admin"][5])
if send_notification:
await call.message.bot.send_message(
chat_id=unmuted_user.user.id,
text=f"{unmuted_user.user.mention_html('Вы')} были размучены {call.from_user.mention_html('этим пользователем')} в чате <code>{call.message.chat.id}</code>.",
reply_markup=delete_keyboard(admin_id=unmuted_user.user.id)
)
logging.log(msg=f"Unbanned user @{unmuted_user.user.full_name} user_id=f{unmuted_user.user.id}",
level=logging.INFO)
@staticmethod
async def telegram_mute_cmd_handler(msg: Message, command: CommandObject,
session_maker: async_sessionmaker) -> None:
"""
Handler of command /mute
Restricts member from using chat
:param msg: Message telegram object
:param command: Object of telegram command
:param session_maker: Session maker object of SqlAlchemy
:return: Nothing
"""
mute_user = await msg.chat.get_member(msg.reply_to_message.from_user.id)
send_to_dm = await get_chat_config_value(chat_id=msg.chat.id, session=session_maker,
setting=list_of_features["Admin"][4])
send_notification = await get_chat_config_value(chat_id=msg.chat.id, session=session_maker,
setting=list_of_features["Admin"][5])
where_send = {
True: msg.from_user.id,
False: msg.chat.id
}
if mute_user.status == ChatMemberStatus.LEFT or mute_user.status == ChatMemberStatus.KICKED:
return
config = await SolutionSimpler.mute_user(msg, command, session_maker)
if config["mute_time"] and config["reason"] != "":
await msg.bot.send_message(
chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"{msg.from_user.mention_html('Этот пользователь')} запретил писать "
f"сообщения {msg.reply_to_message.from_user.mention_html('этому пользователю')}.\n"
f"По причине <code>{config['reason']}</code>, до даты <code>{config['mute_time']}</code>",
reply_markup=mute_keyboard(msg.from_user.id, mute_user.user.id))
if send_notification:
await msg.bot.send_message(
chat_id=mute_user.user.id,
text=f"{msg.from_user.mention_html('Этот пользователь')} запретил писать "
f"сообщения {msg.reply_to_message.from_user.mention_html('вам')} в чате {msg.chat.title}.\n"
f"По причине <code>{config['reason']}</code>, до даты <code>{config['mute_time']}</code>",
reply_markup=delete_keyboard(admin_id=mute_user.user.id))
elif config["reason"] != "":
await msg.bot.send_message(
chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"{msg.from_user.mention_html('Этот пользователь')} запретил писать "
f"сообщения {msg.reply_to_message.from_user.mention_html('этому пользователю')}.\n"
f"По причине <code>{config['reason']}</code>.",
reply_markup=mute_keyboard(msg.from_user.id, mute_user.user.id))
if send_notification:
await msg.bot.send_message(
chat_id=mute_user.user.id,
text=f"{msg.from_user.mention_html('Этот пользователь')} запретил писать "
f"сообщения {msg.reply_to_message.from_user.mention_html('вам')} в чате {msg.chat.title}.\n"
f"По причине <code>{config['reason']}</code>.",
reply_markup=delete_keyboard(admin_id=mute_user.user.id))
elif config["mute_time"]:
await msg.bot.send_message(
chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"{msg.from_user.mention_html('Этот пользователь')} запретил писать "
f"сообщения {msg.reply_to_message.from_user.mention_html('этому пользователю')}.\n"
f"До даты <code>{config['mute_time']}</code>",
reply_markup=mute_keyboard(msg.from_user.id, mute_user.user.id))
if send_notification:
await msg.bot.send_message(
chat_id=mute_user.user.id,
text=f"{msg.from_user.mention_html('Этот пользователь')} запретил писать "
f"сообщения {msg.reply_to_message.from_user.mention_html('вам')} в чате {msg.chat.title}.\n"
f"До даты <code>{config['mute_time']}</code>",
reply_markup=delete_keyboard(admin_id=mute_user.user.id))
else:
await msg.bot.send_message(
chat_id=where_send[send_to_dm],
text="Удача ✅\n"
f"{msg.from_user.mention_html('Этот пользователь')} запретил писать "
f"сообщения {msg.reply_to_message.from_user.mention_html('этому пользователю')}.\n",
reply_markup=mute_keyboard(msg.from_user.id, mute_user.user.id))
if send_notification:
await msg.bot.send_message(
chat_id=mute_user.user.id,
text=f"{msg.from_user.mention_html('Этот пользователь')} запретил писать "
f"сообщения {msg.reply_to_message.from_user.mention_html('вам')} в чате {msg.chat.title}.\n",
reply_markup=delete_keyboard(admin_id=mute_user.user.id))
@staticmethod
async def telegram_unmute_cmd_handler(msg: Message, session_maker: async_sessionmaker) -> None:
"""
Handler of command /unmute
Gives access member to send messages into chat
:param msg: Message telegram object
:param session_maker: Session maker object of SqlAlchemy
:return: Nothing
"""
await SolutionSimpler.unmute_user(msg, session_maker)
send_to_dm = await get_chat_config_value(chat_id=msg.chat.id, session=session_maker,
setting=list_of_features["Admin"][4])
send_notification = await get_chat_config_value(chat_id=msg.chat.id, session=session_maker,
setting=list_of_features["Admin"][5])
where_send = {
True: msg.from_user.id,
False: msg.chat.id
}
await msg.bot.send_message(
user_id=where_send[send_to_dm],
text="Удача ✅"
f"{msg.from_user.mention_html('Этот пользователь')} разрешил писать\n"
f"сообщения {msg.reply_to_message.from_user.mention_html('этому пользователю')}",
reply_markup=unmute_keyboard(msg.from_user.id, msg.reply_to_message.from_user.id))
if send_notification:
await msg.bot.send_message(
user_id=msg.reply_to_message.from_user.id,
text=f"{msg.from_user.mention_html('Этот пользователь')} разрешил писать\n"
f"сообщения {msg.reply_to_message.from_user.mention_html('вам')}",
reply_markup=delete_keyboard(admin_id=msg.reply_to_message.from_user.id))
def __init__(self):
"""
All information about feature
will be inside this function
"""
super().__init__()
self.cmd_description: str = "Your description of command"
# Telegram feature settings
self.telegram_setting = None
self.telegram_commands: list[str | None] = ["start"]
self.telegram_cmd_avaible = True # Is a feature have a commands
self.telegram_callback_factory = None
self.telegram_message_handlers = {
# Your message handlers
}
self.telegram_callback_handlers = {
# Start menu
}

View file

@ -0,0 +1,104 @@
from aiogram.types import Message, CallbackQuery
from bozenka.features import BasicFeature
from bozenka.instances.telegram.utils.callbacks_factory import PinMsg, UnpinMsg
from bozenka.instances.telegram.utils.keyboards import unpin_msg_keyboard, delete_keyboard, pin_msg_keyboard
from bozenka.instances.telegram.utils.simpler import SolutionSimpler
class Pins(BasicFeature):
"""
A class of pins related commands
All staff related to it will be here
"""
@staticmethod
async def telegram_pin_callback_handler(call: CallbackQuery, callback_data: PinMsg) -> None:
"""
Query, what pins message
:param call:
:param callback_data:
:return:
"""
if callback_data.user_id == call.from_user.id:
return
await call.message.chat.pin_message(message_id=callback_data.msg_id)
await call.message.edit_text("Удача ✅\n"
"Сообщение было закреплено 📌",
reply_markup=pin_msg_keyboard(user_id=call.from_user.id,
msg_id=callback_data.msg_id))
@staticmethod
async def telegram_unpin_callback_handler(call: CallbackQuery, callback_data: UnpinMsg) -> None:
"""
Query, what unpins message
:param call:
:param callback_data:
:return:
"""
if callback_data.user_id == call.from_user.id:
return
await call.message.chat.pin_message(message_id=callback_data.msg_id)
await call.message.edit_text("Удача ✅\n"
"Сообщение было откреплено 📌",
reply_markup=unpin_msg_keyboard(user_id=call.from_user.id,
msg_id=callback_data.msg_id))
@staticmethod
async def telegram_pin_cmd(msg: Message) -> None:
"""
/pin command function, pins replied command
:param msg: Message telegram object
:return: Nothing
"""
await SolutionSimpler.pin_msg(msg)
await msg.answer("Удача ✅\n"
"Сообщение было закреплено 📌",
reply_markup=pin_msg_keyboard(msg_id=msg.reply_to_message.message_id,
user_id=msg.from_user.id))
@staticmethod
async def telegram_unpin_cmd(msg: Message) -> None:
"""
/unpin command function, unpins replied command
:param msg: Message telegram object
:return: Nothing
"""
await SolutionSimpler.unpin_msg(msg)
await msg.answer("Удача ✅\n"
"Сообщение было откреплено 📌",
reply_markup=unpin_msg_keyboard(msg_id=msg.reply_to_message.message_id,
user_id=msg.from_user.id))
@staticmethod
async def telegram_unpinall_cmd(msg: Message) -> None:
"""
/unpin_all command function, unpins all messages in chat
:param msg: Message telegram object
:return: Nothing
"""
await SolutionSimpler.unpin_all_messages(msg)
await msg.answer("Удача ✅\n"
"Все сообщения были откреплены 📌",
reply_markup=delete_keyboard(admin_id=msg.from_user.id))
def __init__(self):
"""
All information about feature
will be inside this function
"""
super().__init__()
self.cmd_description: str = "Your description of command"
# Telegram feature settings
self.telegram_setting = None
self.telegram_commands: list[str | None] = ["start"]
self.telegram_cmd_avaible = True # Is a feature have a commands
self.telegram_callback_factory = None
self.telegram_message_handlers = {
}
self.telegram_callback_handlers = {
}

View file

@ -0,0 +1,152 @@
from aiogram import F, Bot
from aiogram.enums import ChatType
from aiogram.filters import Command
from aiogram.types import InlineKeyboardMarkup, Message, CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from bozenka.features import BasicFeature
from bozenka.instances.telegram.utils.callbacks_factory import HelpCategory, HelpBackCategory, HelpFeature, HelpBack, \
PinMsg, UnpinMsg, CloseThread, OpenThread
from bozenka.instances.telegram.utils.keyboards import help_category_keyboard, help_keyboard, \
help_feature_keyboard, gpt_categories_keyboard, unpin_msg_keyboard, delete_keyboard, pin_msg_keyboard, \
close_thread_keyboard, open_thread_keyboard
from bozenka.instances.telegram.utils.simpler import list_of_features, SolutionSimpler
from bozenka.instances.version import build, is_updated
class Threads(BasicFeature):
"""
A class of topics / threads related commands
All staff related to it will be here
"""
@staticmethod
async def telegram_close_topic_cmd_handler(msg: Message, bot: Bot) -> None:
"""
/close command function. Closing thread
:param msg: Message telegram object
:param bot: Object of telegram bot
:return: Nothing
"""
config = await SolutionSimpler.close_topic(msg=msg)
await msg.answer(config[0],
reply_markup=close_thread_keyboard(user_id=msg.from_user.id)
if config[1] else delete_keyboard(msg.from_user.id))
@staticmethod
async def telegram_reopen_topic_cmd_handler(msg: Message, bot: Bot) -> None:
"""
/open command function. Opens thread
:param msg: Message telegram object
:param bot: Object of telegram bot
:return: Nothing
"""
config = await SolutionSimpler.open_topic(msg=msg)
await msg.answer(config[0],
reply_markup=open_thread_keyboard(user_id=msg.from_user.id)
if config[1] else delete_keyboard(msg.from_user.id))
@staticmethod
async def telegram_close_general_topic_cmd_handler(msg: Message, bot: Bot) -> None:
"""
/close_general command function. Closes general thread
:param msg: Message telegram object
:param bot: Object of telegram bot
:return: Nothing
"""
config = await SolutionSimpler.close_general_topic(msg=msg)
await msg.answer(config[0],
reply_markup=close_thread_keyboard(user_id=msg.from_user.id)
if config[1] else delete_keyboard(msg.from_user.id))
@staticmethod
async def telegram_reopen_general_topic_cmd(msg: Message, bot: Bot) -> None:
"""
/open_general command function. Opens general thread
:param msg: Message telegram object
:param bot: Object of telegram bot
:return: Nothing
"""
config = await SolutionSimpler.open_general_topic(msg=msg)
await msg.answer(config[0],
reply_markup=open_thread_keyboard(user_id=msg.from_user.id)
if config[1] else delete_keyboard(msg.from_user.id))
@staticmethod
async def telegram_hide_general_topic_cmd_handler(msg: Message, bot: Bot) -> None:
"""
/hide_general command function. Hides general thread
:param msg: Message telegram object
:param bot: Object of telegram bot
:return: Nothing
"""
config = await SolutionSimpler.hide_general_topic(msg=msg)
await msg.answer(config[0],
reply_markup=delete_keyboard(msg.from_user.id))
@staticmethod
async def telegram_unhide_general_topic_cmd(msg: Message, bot: Bot) -> None:
"""
/show_general command function. Shows back general thread.
:param msg: Message telegram object
:param bot: Object of telegram bot
:return: Nothing
"""
config = await SolutionSimpler.show_general_topic(msg=msg)
await msg.answer(config[0],
reply_markup=delete_keyboard(msg.from_user.id))
@staticmethod
async def telegram_close_thread_callback_handler(call: CallbackQuery, callback_data: CloseThread) -> None:
"""
Query, what close thread
:param call: CallbackQuery object
:param callback_data: ClosetThread object
:return: None
"""
if callback_data.user_id != call.from_user.id or not call.message.chat.is_forum:
return
config = await SolutionSimpler.close_topic(msg=call.message, call=call)
await call.message.edit_text(
config[0],
reply_markup=close_thread_keyboard(user_id=call.from_user.id) if config[1] else
delete_keyboard(admin_id=call.from_user.id)
)
@staticmethod
async def inline_open_thread(call: CallbackQuery, callback_data: OpenThread) -> None:
"""
Query, what opens thread
:param call: CallbackQuery object
:param callback_data: OpenThread
:return: None
"""
if callback_data.user_id != call.from_user.id or not call.message.chat.is_forum:
return
config = await SolutionSimpler.open_topic(msg=call.message, call=call)
await call.message.edit_text(
config[0],
reply_markup=open_thread_keyboard(user_id=call.from_user.id) if config[1] else
delete_keyboard(admin_id=call.from_user.id)
)
def __init__(self):
"""
All information about feature
will be inside this function
"""
super().__init__()
self.cmd_description: str = "Your description of command"
# Telegram feature settings
self.telegram_setting = None
self.telegram_commands: list[str | None] = ["start"]
self.telegram_cmd_avaible = True # Is a feature have a commands
self.telegram_callback_factory = None
self.telegram_message_handlers = {
}
self.telegram_callback_handlers = {
}

View file

View file

View file

@ -0,0 +1,225 @@
from aiogram import F
from aiogram.enums import ChatType
from aiogram.filters import Command
from aiogram.types import InlineKeyboardMarkup, Message, CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from bozenka.features import BasicFeature
from bozenka.instances.telegram.utils.callbacks_factory import HelpCategory, HelpBackCategory, HelpFeature, HelpBack
from bozenka.instances.telegram.utils.keyboards import help_category_keyboard, help_keyboard, \
help_feature_keyboard, gpt_categories_keyboard
from bozenka.instances.telegram.utils.simpler import list_of_features
from bozenka.instances.version import build, is_updated
class Start(BasicFeature):
"""
A class of /start command
All staff related to it will be here
"""
cmd_description: str = "Basic command to show main menu"
main_text = """
Привет 👋
Я - бозенька, бот с открытым исходным кодом, который поможет тебе в различных задачах.
Вот что ты можешь сделать с помощью меню:
Добавить в чат: добавляет меня в групповой чат, чтобы я мог выполнять свои функции внутри него.
Функционал: показывает список доступных функций и команд, которые я могу выполнить.
О разработчиках: предоставляет информацию о команде разработчиков, которые создали и поддерживают этого бота.
О запущенном экземпляре: выводит информацию о текущей версии и состоянии запущенного экземпляра бота.
Начать диалог с ИИ: позволяет начать диалог с искусственным интеллектом, который может отвечать на вопросы и предоставлять информацию.
Генерация изображений: позволяет сгенерировать изображения на основе заданных параметров и промта
Вот нужные ссылки обо мне:
<a href='https://t.me/bozodevelopment'>Канал с новостями об разработке</a>
<a href='https://github.com/kittyneverdies/bozenka/'>Исходный код на Github</a>
Чтобы воспользоваться какой-либо функцией, просто нажми на соответствующую кнопку ниже.
Если у тебя возникнут вопросы или проблемы, не стесняйся обратиться к команде разработчиков или написать в обсуждении телеграм канала.
Удачного использования!
"""
telegram_main_menu = InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="Добавить в ваш групповой чат 🔌", callback_data="addtochat")],
[InlineKeyboardButton(text="Информация об функционале бота 🔨", callback_data="functional")],
[InlineKeyboardButton(text="Об данном проекте ", callback_data="aboutdevs")],
[InlineKeyboardButton(text="О данном запущенном экзепляре ", callback_data="aboutbot")],
[InlineKeyboardButton(text="Начать диалог с текстовым ИИ 🤖", callback_data="dialogai")],
[InlineKeyboardButton(text="Начать генерацию изображений 🖼", callback_data="dialogimage")],
]
)
# There starting a help category of handlers
# It's related to one of menus
# Showing information about features, will be remade later :D
@staticmethod
async def back_help_categories_handler(call: CallbackQuery, callback_data: HelpBack) -> None:
"""
Query, what shows list of features to get support back.
:param call: CallbackQuery object
:param callback_data: Helpback class
:return: None
"""
await call.message.edit_text("Выберите категорию, по которой нужна помощь:",
reply_markup=help_keyboard())
await call.answer()
@staticmethod
async def help_features_handler(call: CallbackQuery, callback_data: HelpCategory | HelpBackCategory) -> None:
"""
Handler of CallbackQuery, what shows list of features to get support.
:param call: CallbackQuery object
:param callback_data: HelpCategory or HelpBack class
:return: None
"""
await call.message.edit_text("Выберите функцию, по которой нужна помощь",
reply_markup=help_category_keyboard(category=callback_data.category_name))
await call.answer()
@staticmethod
async def feature_info_handler(call: CallbackQuery, callback_data: HelpFeature) -> None:
"""
Handler of CallbackQuery, what shows information about bozenka feature
:param call: CallbackQuery ojbect
:param callback_data: HelpFeature object
:return: None
"""
await call.message.edit_text(
list_of_features[callback_data.feature_category][callback_data.feature_index].description,
reply_markup=help_feature_keyboard(category=callback_data.feature_category))
await call.answer()
@staticmethod
async def help_menu_handler(call: CallbackQuery) -> None:
"""
Handler of CallbackQuery, what shows menu of help
:param call: CallbackQuery object
:return: None
"""
await call.message.edit_text("Выберите категорию функций, по которой вам нужна помощь 🤖",
reply_markup=help_keyboard())
await call.answer()
@staticmethod
async def add_to_menu_handler(call: CallbackQuery) -> None:
"""
Handler of CallbackQuery, what shows a link to add bozenka into user group chat
:param call: CallbackQuery object
:return: None
"""
# Getting bot
me = await call.message.bot.me()
# Generating special keyboard
kb = InlineKeyboardBuilder()
kb.button(text="Добавить в ваш груповой чат 🔌",
url="https://t.me/"
f"{me.username}?"
"startgroup&"
"admin=promote_members+delete_messages+restrict_members+invite_users+pin_messages+manage_video_chats")
kb.row(InlineKeyboardButton(text="Вернуться 🔙", callback_data="back"))
# Answering
await call.message.edit_text("Чтобы добавить бозеньку в ваш групповой чат, нажмите на кнопку под сообщением:",
reply_markup=kb.as_markup())
await call.answer()
@staticmethod
async def about_instance_callback_handler(call: CallbackQuery) -> None:
"""
Handler of CallbackQuery, what shows information about current running instance
:param call: CallbackQuery object
:return: Nothing
"""
kb = InlineKeyboardMarkup(inline_keyboard=[[
InlineKeyboardButton(text="Вернуться 🔙", callback_data="back")
]])
me = await call.message.bot.get_me()
update_status = {False: "требуется обновление бота 🔼",
True: "обновление не требуется, последняя версия ✅"}
await call.message.edit_text(
f"Информация об данном запущенном экземпляре бозеньки:\n"
f"Аккаунт бота: {me.mention_html()}\n"
f"Запущенная версия бота <code>{build}</code>\n",
f"Нужно ли обновление: {update_status[is_updated]}",
reply_markup=kb)
await call.answer()
@staticmethod
async def about_developers_handler(call: CallbackQuery) -> None:
"""
Handler of CallbackQuery, what shows information about bozenka & it's development
:param call: CallbackQuery object
:return: Nothing
"""
kb = InlineKeyboardMarkup(inline_keyboard=[[
InlineKeyboardButton(text="Вернуться 🔙", callback_data="back")
]])
await call.message.edit_text("""
Бозенька - это мультифункциональный (в будущем кроссплатформенный) бот.\n
Он умеет работать с групповыми чатами и готовыми нейронными сетями для генерации текста и изображений.
Бозенька разрабатывается коммандой, которая состоит на данный момент из одного человека.\n
Исходный код проекта\n
Исходный код находится под лицензией GPL-3.0, исходный код проекта можно посмотреть всегда <a href="https://github.com/kittyneverdies/bozenka/">здесь</a>
Канал с новостями разработки находится <a href="https://t.me/bozodevelopment">здесь</a>
""", reply_markup=kb, disable_web_page_preview=True)
@staticmethod
async def start_dialog_handler(call: CallbackQuery) -> None:
"""
Handler of CallbackQuery, what shows list of Categories, avaible to use as chatbot
:param call: CallbackQuery object
:return: Nothing
"""
await call.message.edit_text("Пожалуста, выберите сервиc / библиотеку, через которую вы будете общаться",
reply_markup=gpt_categories_keyboard
(user_id=call.from_user.id))
async def start_callback_handler(self, call: CallbackQuery) -> None:
"""
/start command function handler. Just back it by clicking on button.
Shows menu and basic information about bozenka
:param call: Message telegram object
:return: Nothing
"""
await call.message.edit_text(self.main_text,
reply_markup=self.telegram_main_menu, disable_web_page_preview=True)
async def start_cmd_handler(self, msg: Message) -> None:
"""
/start command function handler
Shows menu and basic information about bozenka
:param msg: Message telegram object
:return: Nothing
"""
await msg.answer(self.main_text,
reply_markup=self.telegram_main_menu, disable_web_page_preview=True)
def __init__(self):
"""
All information about feature
will be inside this function
"""
super().__init__()
self.cmd_description: str = "Your description of command"
# Telegram feature settings
self.telegram_setting = None
self.telegram_commands: list[str | None] = ["start"]
self.telegram_cmd_avaible = True # Is a feature have a commands
self.telegram_callback_factory = None
self.telegram_message_handlers = {
self.start_cmd_handler: [Command(commands=["start"]), F.chat.type == ChatType.PRIVATE],
}
self.telegram_callback_handlers = {
# Start menu
self.start_dialog_handler: [F.data == "dialogai"],
self.add_to_menu_handler: [F.data == "addtochat"],
self.about_developers_handler: [F.data == "aboutdevs"],
self.about_instance_callback_handler: [F.data == "aboutbot"],
self.start_callback_handler: [F.data == "back"],
# Help menu
self.feature_info_handler: [HelpFeature.filter() or HelpBackCategory.filter()],
self.help_menu_handler: [HelpCategory.filter() or HelpBack.filter(F.back_to == "category")],
}

View file

View file

View file

View file

@ -19,7 +19,11 @@ def register_main_cmd(router: Router) -> None:
logging.log(msg="Registering main related commands", level=logging.INFO)
# Start command handler
"""
router.message.register(start_cmd, Command(commands=["start"]), F.chat.type == ChatType.PRIVATE)
"""
router.message.register(start_cmd, *[Command(commands=["start"]), F.chat.type == ChatType.PRIVATE])
# Registering command /setup
router.message.register(setup_cmd, Command(commands=["setup"]), ~(F.chat.type == ChatType.PRIVATE))

View file

@ -28,7 +28,7 @@ class HelpBackCategory(CallbackData, prefix="hbc"):
Callback data to back to list of features in one
of categories in menu
"""
back_to_category: str
category_name: str
class BackStart(CallbackData, prefix="start"):

View file

@ -3,8 +3,7 @@ import logging
from aiogram import Router, Dispatcher
from bozenka.instances.telegram.utils.middleware.antiflood import MessageThrottlingMiddleware, \
CallbackThrottlingMiddleware
from bozenka.instances.telegram.utils.middleware.retry import RetryMessageMiddleware, RetryCallbackMiddleware
CallbackThrottlingMiddleware, CounterMiddleware
def register_middlewares(dp: Dispatcher) -> None:
@ -16,8 +15,13 @@ def register_middlewares(dp: Dispatcher) -> None:
logging.log(msg=f"Registering middlewares of bot", level=logging.INFO)
# Throttling middlewares
dp.message.middleware.register(MessageThrottlingMiddleware)
"""
dp.message.middleware(CounterMiddleware)
dp.callback_query.middleware(CallbackThrottlingMiddleware)
# Retry middlewares
"""
"""
dp.error.middleware(RetryMessageMiddleware)
dp.error.middleware(RetryCallbackMiddleware)
"""

View file

@ -5,6 +5,22 @@ from aiogram.types import Update, Message, CallbackQuery
from cachetools import TTLCache
class CounterMiddleware(BaseMiddleware):
def __init__(self) -> None:
self.counter = 0
async def __call__(
self,
handler: Callable[[Message, Dict[str, Any]], Awaitable[Any]],
event: Message,
data: Dict[str, Any],
**kwargs: Any
) -> Any:
self.counter += 1
print(self.counter)
return await handler(event, data)
class MessageThrottlingMiddleware(BaseMiddleware):
"""
This middleware is skidded from public codes

View file

@ -6,31 +6,3 @@ from aiogram.exceptions import TelegramRetryAfter
from aiogram.types import Message, ErrorEvent, Update, CallbackQuery
class RetryMessageMiddleware(BaseMiddleware):
"""
Protects from user don't get update by message
"""
async def __call__(
self,
handler: Callable[[Message, Dict[str, Any]], Awaitable[Any]],
event: ErrorEvent[TelegramRetryAfter, Update],
data: Dict[str, Any]
) -> Any:
time.sleep(event.exception.retry_after)
return await handler(event.update.message, data)
class RetryCallbackMiddleware(BaseMiddleware):
"""
Protects from user don't get update by callbackquery
"""
async def __call__(
self,
handler: Callable[[CallbackQuery, Dict[str, Any]], Awaitable[Any]],
event: ErrorEvent[TelegramRetryAfter, Update],
data: Dict[str, Any]
) -> Any:
time.sleep(event.exception.retry_after)
return await handler(event.update.callback_query, data)