From 3fc4432e4292219f92c4fa87eba7d2bf12d49dd9 Mon Sep 17 00:00:00 2001 From: kittyneverdies <85691197+KittyNeverDies@users.noreply.github.com> Date: Sat, 30 Dec 2023 14:52:36 +0300 Subject: [PATCH] Changelog: - /start finished - started codding /setup database - too much refactors - started codding image generation - reverted changes for /ban recode --- alembic.ini | 2 +- bozenka/__init__.py | 2 + bozenka/database/migrate/env.py | 2 +- bozenka/database/tables/telegram.py | 26 ++- bozenka/generative/__init__.py | 15 +- bozenka/generative/gpt4all/__init__.py | 11 +- bozenka/generative/gpt4free/__init__.py | 28 ++- bozenka/generative/kadinsky/__init__.py | 50 ++++++ bozenka/instances/telegram/__init__.py | 5 +- .../telegram/handlers/chat_admin/__init__.py | 8 +- .../telegram/handlers/chat_admin/bans.py | 86 +++------ .../telegram/handlers/chat_user/welcome.py | 18 +- bozenka/instances/telegram/handlers/dev/ai.py | 28 +-- .../telegram/handlers/main/__init__.py | 14 +- .../instances/telegram/handlers/main/setup.py | 7 +- .../instances/telegram/handlers/main/start.py | 46 +---- .../telegram/handlers/queries/__init__.py | 29 ++- .../handlers/queries/image_generation.py | 27 +++ .../telegram/handlers/queries/setup.py | 45 ++++- .../telegram/handlers/queries/start.py | 81 ++++++++- .../queries/{gpt.py => text_generation.py} | 12 +- .../utils/callbacks_factory/gpt_selector.py | 23 ++- .../telegram/utils/callbacks_factory/setup.py | 9 + .../telegram/utils/filters/__init__.py | 2 +- .../telegram/utils/filters/chat_setting.py | 21 +++ .../telegram/utils/keyboards/inline.py | 166 ++++++++++++++---- .../telegram/utils/simpler/__init__.py | 2 +- .../simpler/{states.py => fsm_states.py} | 0 .../utils/simpler/lists_of_content.py | 100 +++++------ .../utils/simpler/solution_simpler.py | 4 +- 30 files changed, 580 insertions(+), 289 deletions(-) create mode 100644 bozenka/generative/kadinsky/__init__.py create mode 100644 bozenka/instances/telegram/handlers/queries/image_generation.py rename bozenka/instances/telegram/handlers/queries/{gpt.py => text_generation.py} (96%) create mode 100644 bozenka/instances/telegram/utils/filters/chat_setting.py rename bozenka/instances/telegram/utils/simpler/{states.py => fsm_states.py} (100%) diff --git a/alembic.ini b/alembic.ini index 7ab6332..551b68a 100644 --- a/alembic.ini +++ b/alembic.ini @@ -2,7 +2,7 @@ [alembic] # path to migration scripts -script_location = bozenka/db/migrate +script_location = bozenka/database/migrate # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s # Uncomment the line below if you want the files to be prepended with date and time diff --git a/bozenka/__init__.py b/bozenka/__init__.py index c8c1b27..c637666 100644 --- a/bozenka/__init__.py +++ b/bozenka/__init__.py @@ -13,6 +13,8 @@ def launch_instances() -> None: :return: """ logging.log(msg="Setting up g4f logging!", level=logging.INFO) + logging.basicConfig(level=logging.INFO) + logging.log(msg="Setting up logging!", level=logging.INFO) g4f.logging = True db_url = generate_url() diff --git a/bozenka/database/migrate/env.py b/bozenka/database/migrate/env.py index 9bc4056..f1211ff 100644 --- a/bozenka/database/migrate/env.py +++ b/bozenka/database/migrate/env.py @@ -22,7 +22,7 @@ if config.config_file_name is not None: # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata -target_metadata = bozenka.db.main.MainModel.metadata +target_metadata = bozenka.database.main.MainModel.metadata # other values from the config, defined by the needs of env.py, # can be acquired: diff --git a/bozenka/database/tables/telegram.py b/bozenka/database/tables/telegram.py index 76ae399..f569b1d 100644 --- a/bozenka/database/tables/telegram.py +++ b/bozenka/database/tables/telegram.py @@ -1,6 +1,7 @@ +import asyncio from typing import Tuple, Any -from sqlalchemy import Column, Integer, VARCHAR, Boolean, Text, select, BigInteger, Row +from sqlalchemy import Column, Integer, VARCHAR, Boolean, Text, select, BigInteger, Row, inspect from sqlalchemy.ext.asyncio import async_sessionmaker from sqlalchemy.orm import sessionmaker from bozenka.database.main import MainModel @@ -65,9 +66,32 @@ async def get_settings(chat_id: int, session: async_sessionmaker): """ async with session() as session: async with session.begin(): + (await session.execute(select(ChatSettings).where(ChatSettings.chat_id == chat_id))) return (await session.execute(select(ChatSettings).where(ChatSettings.chat_id == chat_id))).one_or_none() +async def get_setting(chat_id: int, session: async_sessionmaker, setting: str) -> bool: + """ + Return setting by sessionmaker and chat_id + :param chat_id: id of telegram chat + :param session: sessionmaker from dispatcher + :param setting: string setting what we need to get + :return: + """ + async with session() as session: + async with session.begin(): + rows = (await session.execute(select(ChatSettings).where(ChatSettings.chat_id == chat_id))).one().__dict__ + + for r in rows: + print(r.__dict__) + + print("!@#1221312321312312313") + print(rows) + for i in rows: + print("+") + print(i) + + async def get_user(user_id: int, chat_id: int, session: async_sessionmaker) -> Row[tuple[Any, ...] | Any] | None: """ Return user with sessionmaker by user_id and chat_id. diff --git a/bozenka/generative/__init__.py b/bozenka/generative/__init__.py index 754a475..e61cab8 100644 --- a/bozenka/generative/__init__.py +++ b/bozenka/generative/__init__.py @@ -1,6 +1,19 @@ -# List of generative categories, what we support +# List of text generative categories, what we support text_generative_categories = [ "Gpt4Free", "Gpt4All", +] + +# List of image generative categories, what we support +image_generative_categories = [ + "Kadinsky" +] + +image_generative_size = [ + "1024x1024", + "1024x576", + "576x1024", + "1024x680", + "680x1024" ] \ No newline at end of file diff --git a/bozenka/generative/gpt4all/__init__.py b/bozenka/generative/gpt4all/__init__.py index 5475cb9..f201624 100644 --- a/bozenka/generative/gpt4all/__init__.py +++ b/bozenka/generative/gpt4all/__init__.py @@ -6,7 +6,16 @@ def check(model_filename: str) -> bool: Checking & downloading our gpt4all models Returns True if it's already downloaded Returns False if it's not downloaded - :param model_filename: + :param model_filename: File name of gpt4all model :return: """ return os.path.exists("models\\" + model_filename) + + +def get_model_path(model_filename: str) -> str: + """ + Just returning path of our gpt4all models. + :param model_filename: File name of gpt4all model + :return: + """ + return os.curdir + "models\\" + model_filename diff --git a/bozenka/generative/gpt4free/__init__.py b/bozenka/generative/gpt4free/__init__.py index 9ceecfc..bb1afbe 100644 --- a/bozenka/generative/gpt4free/__init__.py +++ b/bozenka/generative/gpt4free/__init__.py @@ -1,4 +1,5 @@ import g4f +import g4f.Provider from g4f.Provider import RetryProvider from varname import nameof @@ -8,15 +9,9 @@ def generate_gpt4free_providers(): Generates list of g4f providers :return: """ - provider = {} - for prov in g4f.Provider.__all__: - if prov != "BaseProvider" and prov != "AsyncProvider" and prov != "RetryProvider": - exec(f"provider['{prov}']=g4f.Provider.{prov}") - result = {} - for check in provider: - if provider[check].working: - result[check] = provider[check] - return result + return {prov: g4f.Provider.ProviderUtils.convert[prov] for prov in g4f.Provider.__all__ + if prov != "BaseProvider" and prov != "AsyncProvider" and prov != "RetryProvider" and + g4f.Provider.ProviderUtils.convert[prov].working} def generate_gpt4free_models(): @@ -25,16 +20,17 @@ def generate_gpt4free_models(): :return: """ models = {} - for model, model_name in g4f.models.ModelUtils.convert.items(), g4f.models.ModelUtils.convert.keys(): + for model_name, model in g4f.models.ModelUtils.convert.items(): if type(model.best_provider) is RetryProvider: for pr in model.best_provider.providers: - if pr in models: - models[nameof(pr)].append(model_name) + if pr.__name__ in models: + models[pr.__name__].append(model_name) else: - models[nameof(pr)] = [model_name] + models[pr.__name__] = [model_name] else: - if nameof(model.best_provider) in models: - models[nameof(model.best_provider)].append(model_name) + if model.best_provider.__name__ in models: + models[model.best_provider.__name__].append(model_name) else: - models[nameof(model.best_provider)] = [model_name] + models[model.best_provider.__name__] = [model_name] + print(models) return models diff --git a/bozenka/generative/kadinsky/__init__.py b/bozenka/generative/kadinsky/__init__.py new file mode 100644 index 0000000..169859e --- /dev/null +++ b/bozenka/generative/kadinsky/__init__.py @@ -0,0 +1,50 @@ +import json +import time + +import requests + + +class Kadinsky: + """ + Kadinsky class from their documentation. + """ + def __init__(self, url, api_key, secret_key): + self.URL = url + self.AUTH_HEADERS = { + 'X-Key': f'Key {api_key}', + 'X-Secret': f'Secret {secret_key}', + } + + def get_model(self): + response = requests.get(self.URL + 'key/api/v1/models', headers=self.AUTH_HEADERS) + data = response.json() + return data[0]['id'] + + def generate(self, prompt, model, images=1, width=1024, height=1024): + params = { + "type": "GENERATE", + "numImages": images, + "width": width, + "height": height, + "generateParams": { + "query": f"{prompt}" + } + } + + data = { + 'model_id': (None, model), + 'params': (None, json.dumps(params), 'application/json') + } + response = requests.post(self.URL + 'key/api/v1/text2image/run', headers=self.AUTH_HEADERS, files=data) + data = response.json() + return data['uuid'] + + def check_generation(self, request_id, attempts=10, delay=10): + while attempts > 0: + response = requests.get(self.URL + 'key/api/v1/text2image/status/' + request_id, headers=self.AUTH_HEADERS) + data = response.json() + if data['status'] == 'DONE': + return data['images'] + + attempts -= 1 + time.sleep(delay) \ No newline at end of file diff --git a/bozenka/instances/telegram/__init__.py b/bozenka/instances/telegram/__init__.py index eb8bb9a..d63faf5 100644 --- a/bozenka/instances/telegram/__init__.py +++ b/bozenka/instances/telegram/__init__.py @@ -12,11 +12,10 @@ from bozenka.instances.telegram.utils.simpler import list_of_commands async def launch_telegram_instance(session_maker: async_sessionmaker) -> None: """ Launches telegram bot with token from enviroment + :param session_maker: :return: """ - logging.basicConfig(level=logging.INFO) - logging.log(msg="Setting up logging!", level=logging.INFO) - g4f.logging = True + logging.log(msg="-" * 50 + "TELEGRAM INSTANCE LAUNCH" + "-" * 50, level=logging.INFO) bot = Bot(token=os.getenv("tg_bot_token"), parse_mode="HTML") diff --git a/bozenka/instances/telegram/handlers/chat_admin/__init__.py b/bozenka/instances/telegram/handlers/chat_admin/__init__.py index bee1540..084a578 100644 --- a/bozenka/instances/telegram/handlers/chat_admin/__init__.py +++ b/bozenka/instances/telegram/handlers/chat_admin/__init__.py @@ -9,7 +9,7 @@ from bozenka.instances.telegram.handlers.chat_admin.help import * from bozenka.instances.telegram.handlers.chat_admin.mutes import mute, unmute from bozenka.instances.telegram.handlers.chat_admin.pins import pin, unpin, unpin_all from bozenka.instances.telegram.handlers.chat_admin.topics import * -from bozenka.instances.telegram.handlers.chat_admin.bans import ban, unban +from bozenka.instances.telegram.handlers.chat_admin.bans import ban_command, unban_command from bozenka.instances.telegram.utils.filters import * @@ -29,11 +29,11 @@ def register_admin_cmd(router: Router) -> None: router.message.register(help_pin, Command(commands=["pin"])) router.message.register(help_unpin, Command(commands=["unpin"])) # Ban / Unban commands handler - router.message.register(ban, Command(commands="ban"), + router.message.register(ban_command, Command(commands="ban"), IsAdminFilter(True)) - router.message.register(ban, Command(commands="ban"), + router.message.register(ban_command, Command(commands="ban"), IsAdminFilter(True), F.reply_to_message.text) - router.message.register(unban, Command(commands="unban"), + router.message.register(unban_command, Command(commands="unban"), IsAdminFilter(True), F.reply_to_message.text) # Mute / Unmute commands handler router.message.register(mute, Command(commands=["mute", "re"]), UserHasPermissions(["can_restrict_members"]), diff --git a/bozenka/instances/telegram/handlers/chat_admin/bans.py b/bozenka/instances/telegram/handlers/chat_admin/bans.py index 72b87e0..13199ba 100644 --- a/bozenka/instances/telegram/handlers/chat_admin/bans.py +++ b/bozenka/instances/telegram/handlers/chat_admin/bans.py @@ -8,7 +8,7 @@ from bozenka.instances.telegram.utils.keyboards import ban_keyboard, delete_keyb from bozenka.instances.telegram.utils.simpler import SolutionSimpler, ru_cmds -async def ban(msg: Message, command: CommandObject, session_maker: async_sessionmaker): +async def ban_command(msg: Message, command: CommandObject, session_maker: async_sessionmaker) -> None: """ /ban command function, supports time and reasons. :param msg: Message telegram object @@ -25,68 +25,23 @@ async def ban(msg: Message, command: CommandObject, session_maker: async_session config = await SolutionSimpler.ban_user(msg, command, session_maker) if config["reason"] and config["ban_time"]: - if mentions := [entity for entity in msg.entities if entity.type == 'mention']: - mentions_list = "" - - for mention in mentions: - mentions_list += f"{mention.user.mention_html()} " - - if msg.reply_to_message.from_user: - mentions_list += f"{msg.reply_to_message.from_user.mention_html()} " - - await msg.answer("Удача ✅\n" - f"Пользователи {mentions_list}были заблокирован пользователем {msg.from_user.mention_html()}.\n" - f"По причине {config['reason']}, до даты {config['ban_time']}", - reply_markup=ban_keyboard(msg.from_user.id, msg.reply_to_message.from_user.id)) - else: - await msg.answer("Удача ✅\n" - f"Пользователь {msg.reply_to_message.from_user.mention_html()} был заблокирован пользователем {msg.from_user.mention_html()}.\n" - f"По причине {config['reason']}, до даты {config['ban_time']}", - reply_markup=ban_keyboard(msg.from_user.id, msg.reply_to_message.from_user.id)) + await msg.answer("Удача ✅\n" + f"Пользователь {msg.reply_to_message.from_user.mention_html()} был заблокирован пользователем {msg.from_user.mention_html()}.\n" + f"По причине {config['reason']}, до даты {config['ban_time']}", + reply_markup=ban_keyboard(msg.from_user.id, msg.reply_to_message.from_user.id)) elif config["reason"]: - if mentions := [entity for entity in msg.entities if entity.type == 'mention']: - mentions_list = "" - - for mention in mentions: - mentions_list += f"{mention.user.mention_html()} " - - if msg.reply_to_message.from_user: - mentions_list += f"{msg.reply_to_message.from_user.mention_html()} " - - await msg.answer( - "Удача ✅\n" - f"Пользователи {mentions_list}были заблокирован пользователем {msg.reply_to_message.from_user.mention_html()}.\n" - f"По причине {config['reason']}.", - reply_markup=ban_keyboard(admin_id=msg.from_user.id, ban_id=msg.reply_to_message.from_user.id) - ) - else: - await msg.answer( - "Удача ✅\n" - f"Пользователь {msg.reply_to_message.from_user.mention_html()} был заблокирован пользователем {msg.reply_to_message.from_user.mention_html()}.\n" - f"По причине {config['reason']}.", - reply_markup=ban_keyboard(admin_id=msg.from_user.id, ban_id=msg.reply_to_message.from_user.id) - ) + await msg.answer( + "Удача ✅\n" + f"Пользователь {msg.reply_to_message.from_user.mention_html()} был заблокирован пользователем {msg.reply_to_message.from_user.mention_html()}.\n" + f"По причине {config['reason']}.", + reply_markup=ban_keyboard(admin_id=msg.from_user.id, ban_id=msg.reply_to_message.from_user.id) + ) elif config["ban_time"]: - if mentions := [entity for entity in msg.entities if entity.type == 'mention']: - mentions_list = "" - for mention in mentions: - mentions_list += f"{mention.user.mention_html()} " - - if msg.reply_to_message.from_user: - mentions_list += f"{msg.reply_to_message.from_user.mention_html()} " - - await msg.answer( - "Удача ✅\n" - f"Пользователи {msg.reply_to_message.from_user.mention_html()}были заблокирован пользователем {msg.from_user.mention_html()}\n" - f"До даты {config['ban_time']}.", - reply_markup=ban_keyboard(admin_id=msg.from_user.id, ban_id=msg.reply_to_message.from_user.id) - ) - else: - await msg.answer( - "Удача ✅\n" - f"Пользователь {msg.reply_to_message.from_user.mention_html()} был заблокирован пользователем {msg.from_user.mention_html()}, до даты {config['ban_time']}", - reply_markup=ban_keyboard(admin_id=msg.from_user.id, ban_id=msg.reply_to_message.from_user.id) - ) + await msg.answer( + "Удача ✅\n" + f"Пользователь {msg.reply_to_message.from_user.mention_html()} был заблокирован пользователем {msg.from_user.mention_html()}, до даты {config['ban_time']}", + reply_markup=ban_keyboard(admin_id=msg.from_user.id, ban_id=msg.reply_to_message.from_user.id) + ) else: await msg.answer( "Удача ✅\n" @@ -95,7 +50,7 @@ async def ban(msg: Message, command: CommandObject, session_maker: async_session ) -async def unban(msg: Message, command: CommandObject, session_maker: async_sessionmaker): +async def unban_command(msg: Message, command: CommandObject, session_maker: async_sessionmaker) -> None: """ /unban command function :param msg: Message telegram object @@ -125,13 +80,12 @@ async def unban(msg: Message, command: CommandObject, session_maker: async_sessi ) -async def status(msg: Message, session_maker: async_sessionmaker): +async def status_command(msg: Message, session_maker: async_sessionmaker) -> None: """ /status command function Checks is user banned and muted - :param msg: - :param command: - :param session_maker: + :param msg: Message telegram object + :param session_maker: Session maker object of SqlAlchemy :return: """ config = await SolutionSimpler.get_status(msg, session_maker) diff --git a/bozenka/instances/telegram/handlers/chat_user/welcome.py b/bozenka/instances/telegram/handlers/chat_user/welcome.py index 577f4ce..66bd64e 100644 --- a/bozenka/instances/telegram/handlers/chat_user/welcome.py +++ b/bozenka/instances/telegram/handlers/chat_user/welcome.py @@ -2,14 +2,17 @@ import logging from aiogram import Bot from aiogram.types import Message as Message -from bozenka.instances.telegram.utils.simpler import ru_cmds +from sqlalchemy.ext.asyncio import async_sessionmaker + +from bozenka.instances.telegram.utils.simpler import SolutionSimpler -async def join(msg: Message): +async def join(msg: Message, session_maker: async_sessionmaker): """ Send welcome message, after adding new member to chat. Also works on adding bot to chat and sending welcome message. :param msg: + :param session_maker: :return: """ for new in msg.new_chat_members: @@ -17,13 +20,17 @@ async def join(msg: Message): logging.log(msg=f"Saing welcome for user_id={new.id}, chat_id={msg.chat.id}", level=logging.INFO) await msg.answer( - f"Пользователь {new.mention_html()} переехал в конфу, благодаря {msg.from_user.mention_html()}👋", + f"Пользователь {new.mention_html()} переехал в конфу, благодаря {msg.from_user.mention_html()}👋", ) + await msg.delete() else: logging.log(msg=f"Saing welcome to administrators for chat_id={msg.chat.id}", level=logging.INFO) - await msg.answer(ru_cmds["after_adding"]) - await msg.delete() + await msg.answer("Здраствуйте администраторы чата 👋\n" + "Я - бозенька, мультифункциональный бот, разрабатываемый Bozo Developement\n" + "Выдайте мне полные права администратора для моей полной работы.\n" + "Чтобы настроить функционал, используйте /setup или кнопку под сообщением", ) + await SolutionSimpler.auto_settings(msg=msg, session=session_maker) async def leave(msg: Message, bot: Bot): @@ -41,4 +48,3 @@ async def leave(msg: Message, bot: Bot): await msg.answer( f"Пользователь {msg.left_chat_member.mention_html()} съехал с конфы, благодаря {msg.from_user.mention_html()}👋" ) - diff --git a/bozenka/instances/telegram/handlers/dev/ai.py b/bozenka/instances/telegram/handlers/dev/ai.py index 0480c15..743d5d5 100644 --- a/bozenka/instances/telegram/handlers/dev/ai.py +++ b/bozenka/instances/telegram/handlers/dev/ai.py @@ -8,16 +8,17 @@ from aiogram.types import Message as Message from bozenka.generative.gpt4all import check from bozenka.instances.telegram.utils.keyboards import gpt_categories_keyboard, delete_keyboard, response_keyboard -from bozenka.instances.telegram.utils.simpler import generate_gpt4free_providers, ru_cmds, AnsweringGpt4All, \ +from bozenka.instances.telegram.utils.simpler import AnsweringGpt4All, \ AnsweringGPT4Free +from bozenka.generative.gpt4free import generate_gpt4free_providers async def already_answering(msg: Message, state: FSMContext): """ Giving response, if answering user now, but he still asks something - :param msg: - :param state: + :param msg: Message telegram object + :param state: FSM state of bot :return: """ await msg.answer("Подождите пожалуйста, мы уже генерируем ответ для вас, подождите, когда мы ответим на ваш передыдущий вопрос", @@ -27,8 +28,8 @@ async def already_answering(msg: Message, state: FSMContext): async def start_dialog_cmd(msg: Message, state: FSMContext): """ /conversation command handler, start - :param msg: - :param state: + :param msg: Message telegram object + :param state: FSM state of bot :return: """ if await state.get_state(): @@ -42,8 +43,8 @@ async def cancel_answering(msg: Message, state: FSMContext): """ Canceling dialog with generative model Works on command /cancel - :param msg: - :param state: + :param msg: Message telegram object + :param state: FSM state of bot :return: """ current = await state.get_state() @@ -57,8 +58,8 @@ async def cancel_answering(msg: Message, state: FSMContext): async def g4a_generate_answer(msg: Message, state: FSMContext): """ Generating answer if Gpt4All has been selected - :param msg: - :param state: + :param msg: Message telegram object + :param state: FSM state of bot :return: """ await state.set_state(AnsweringGpt4All.answering) @@ -100,8 +101,8 @@ async def g4a_generate_answer(msg: Message, state: FSMContext): async def g4f_generate_answer(msg: Message, state: FSMContext): """ Generating answer if GPT4Free model and provider has been selected - :param msg: - :param state: + :param msg: Message telegram object + :param state: FSM state of bot :return: """ await state.set_state(AnsweringGPT4Free.answering) @@ -126,7 +127,8 @@ async def g4f_generate_answer(msg: Message, state: FSMContext): provider=providers[info["set_provider"]], stream=False ) - except NameError: + + except NameError or SyntaxError: try: response = g4f.ChatCompletion.create( model=info["set_model"], @@ -152,6 +154,6 @@ async def g4f_generate_answer(msg: Message, state: FSMContext): async def generate_image(msg: Message): """ Image generation, planned in future - :param msg: + :param msg: Message telegram object """ pass diff --git a/bozenka/instances/telegram/handlers/main/__init__.py b/bozenka/instances/telegram/handlers/main/__init__.py index 4909a19..4972517 100644 --- a/bozenka/instances/telegram/handlers/main/__init__.py +++ b/bozenka/instances/telegram/handlers/main/__init__.py @@ -18,13 +18,13 @@ def register_main_cmd(router: Router) -> None: :return: """ 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) - # Routes handler - router.message.register(add_to_chat, F.text == "Добавить в чат 🔌", F.chat.type == ChatType.PRIVATE) - router.message.register(features_list, F.text == "Функционал 🔨", F.chat.type == ChatType.PRIVATE) - # router.message.register(start_cmd, CommandStart) - router.message.register(setup_cmd, Command(commands=["setup"])) + + # Registering command /setup + router.message.register(setup_cmd, Command(commands=["setup"]), ~(F.chat.type == ChatType.PRIVATE)) + # After adding to chat handler - router.message.register(after_adding, F.content_type == ContentType.SUPERGROUP_CHAT_CREATED) - router.message.register(after_adding, F.content_type == ContentType.GROUP_CHAT_CREATED) + router.message.register(group_adding_handler, F.content_type == ContentType.SUPERGROUP_CHAT_CREATED) + router.message.register(group_adding_handler, F.content_type == ContentType.GROUP_CHAT_CREATED) diff --git a/bozenka/instances/telegram/handlers/main/setup.py b/bozenka/instances/telegram/handlers/main/setup.py index 86085cc..10881b5 100644 --- a/bozenka/instances/telegram/handlers/main/setup.py +++ b/bozenka/instances/telegram/handlers/main/setup.py @@ -1,4 +1,6 @@ from aiogram.types import Message as Message +from sqlalchemy.ext.asyncio import async_sessionmaker + from bozenka.instances.telegram.utils.simpler import SolutionSimpler from bozenka.instances.telegram.utils.keyboards import setup_keyboard @@ -7,6 +9,7 @@ async def setup_cmd(msg: Message): """ /setup handler :param msg: + :param session: :return: """ await msg.answer("Привет владелец чата 👋\n" @@ -14,12 +17,14 @@ async def setup_cmd(msg: Message): reply_markup=setup_keyboard()) -async def after_adding(msg: Message): +async def group_adding_handler(msg: Message, session_maker: async_sessionmaker): """ Send message after adding bozenka into group chat :param msg: + :param session_maker: :return: """ + await SolutionSimpler.auto_settings(msg=msg, session=session_maker) await msg.answer("Здраствуйте администраторы чата 👋\n" "Я - бозенька, мультифункциональный бот, разрабатываемый Bozo Developement\n" "Выдайте мне полные права администратора для моей полной работы, если не выдали." diff --git a/bozenka/instances/telegram/handlers/main/start.py b/bozenka/instances/telegram/handlers/main/start.py index 4298597..f804a5f 100644 --- a/bozenka/instances/telegram/handlers/main/start.py +++ b/bozenka/instances/telegram/handlers/main/start.py @@ -2,7 +2,7 @@ from aiogram.enums import ChatType from aiogram.types import Message as Message from aiogram.utils.keyboard import InlineKeyboardBuilder -from bozenka.instances.telegram.utils.keyboards import start_keyboard_builder, help_keyboard +from bozenka.instances.telegram.utils.keyboards import start_keyboard_builder, help_keyboard, start_keyboard async def start_cmd(msg: Message): @@ -15,47 +15,5 @@ async def start_cmd(msg: Message): 'Привет, пользователь, я - Бозенька 👋\n' 'Я мультизадачный телеграм (в будущем кросс-платформенный) бот с открытым исходным кодом, разрабатываемый Bozo Developement\n' f'Выберите, что будете делать, {msg.from_user.mention_html(name="пользователь")}.', - reply_markup=start_keyboard_builder.as_markup(one_time_keyboard=True, resize_keyboard=True) + reply_markup=start_keyboard() ) - - -async def features_list(msg: Message): - """ - Shows features list from reply keyboard - :param msg: - :return: - """ - await msg.answer("Выберите категорию, по которой нужна помощь:", - reply_markup=help_keyboard()) - - -async def about_devs(msg: Message): - """ - Shows info about devs from reply keyboard - :param msg: - :return: - """ - await msg.answer("Бозеьнка разработавается коммандой, состаящей из одного человека.\n" - "Исходный код находится под лицензией GPL-3.0. Исходный код проекта всегда будет открыт и доступен.\n" - "Исходный код проекта всегда можно найти тут: https://github.com/kittyneverdies/bozenka/") - await msg.delete() - - -async def add_to_chat(msg: Message): - """ - Sends link for adding bot into chat - :param msg: - :return: - """ - # Getting bot - me = await msg.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") - # Answering - await msg.answer("Чтобы добавить бозеньку в чат, нажмите на кнопку под сообщением:", - reply_markup=kb.as_markup()) - await msg.delete() diff --git a/bozenka/instances/telegram/handlers/queries/__init__.py b/bozenka/instances/telegram/handlers/queries/__init__.py index 3dd4742..2cc2080 100644 --- a/bozenka/instances/telegram/handlers/queries/__init__.py +++ b/bozenka/instances/telegram/handlers/queries/__init__.py @@ -1,16 +1,13 @@ -__all__ = ["ban", "delete", "gpt"] - from aiogram import Router, F -from bozenka.instances.telegram.handlers.queries.start import inline_help_features, inline_help_feature, \ - inline_back_help_categories, inline_back_help_features +from bozenka.instances.telegram.handlers.queries.start import * from bozenka.instances.telegram.utils.callbacks_factory import * from bozenka.instances.telegram.handlers.queries.ban import * from bozenka.instances.telegram.handlers.queries.pins import * from bozenka.instances.telegram.handlers.queries.threads import * from bozenka.instances.telegram.handlers.queries.delete import * from bozenka.instances.telegram.handlers.queries.revoke import * -from bozenka.instances.telegram.handlers.queries.gpt import * +from bozenka.instances.telegram.handlers.queries.text_generation import * from bozenka.instances.telegram.handlers.queries.setup import * @@ -77,16 +74,34 @@ def register_queries(router: Router) -> None: # /setup command related queries # List of features based on category - router.callback_query.register(inline_setup_features, SetupCategory.filter()) + router.callback_query.register(inline_setup_category, SetupCategory.filter()) # Menu of feature to enable or disable - router.callback_query.register(inline_feature, SetupFeature.filter()) + router.callback_query.register(inline_edit_feature, SetupFeature.filter()) + router.callback_query.register(inline_feature_edited, SetupAction.filter(F.action == "enable")) + router.callback_query.register(inline_feature_edited, SetupAction.filter(F.action == "disable")) + + # Back from feature to category + router.callback_query.register(inline_setup_category_back, SetupAction.filter(F.action == "back")) # /start command related queries # Help of features based on category router.callback_query.register(inline_help_features, HelpCategory.filter()) router.callback_query.register(inline_back_help_categories, HelpBack.filter(F.back_to == "category")) router.callback_query.register(inline_back_help_features, HelpBackCategory.filter()) + # Menu to back router.callback_query.register(inline_help_feature, HelpFeature.filter()) + # Back to /start + router.callback_query.register(inline_start, F.data == "back") + + # Categories of menu + router.callback_query.register(inline_about_developers, F.data == "aboutdevs") + router.callback_query.register(inline_add_to_chat, F.data == "addtochat") + router.callback_query.register(inline_start_chatbot, F.data == "dialogai") + router.callback_query.register(inline_help, F.data == "functional") + + + + diff --git a/bozenka/instances/telegram/handlers/queries/image_generation.py b/bozenka/instances/telegram/handlers/queries/image_generation.py new file mode 100644 index 0000000..f1989f9 --- /dev/null +++ b/bozenka/instances/telegram/handlers/queries/image_generation.py @@ -0,0 +1,27 @@ +import logging + +from aiogram import types +from aiogram.fsm.context import FSMContext +from gpt4all import GPT4All + +from bozenka.instances.telegram.utils.simpler.fsm_states import * + +# Callbacks for GPT +from bozenka.instances.telegram.utils.callbacks_factory import ( + GptCategory, + Gpt4FreeProvider, + Gpt4freeResult, + Gpt4FreeProviderPage, + Gpt4FreeModelPage, GptStop, GptBackMenu, Gpt4AllModel, Gpt4AllSelect +) +# Keyboards for messages +from bozenka.instances.telegram.utils.keyboards import ( + gpt4free_models_keyboard, + gpt4free_providers_keyboard, + delete_keyboard, gpt_categories_keyboard, generate_gpt4all_page, gpt4all_model_menu +) +# Simpler utlilities +from bozenka.instances.telegram.utils.simpler import ( + AnsweringGPT4Free, + AnsweringGpt4All, +) diff --git a/bozenka/instances/telegram/handlers/queries/setup.py b/bozenka/instances/telegram/handlers/queries/setup.py index 1cf2af1..d672989 100644 --- a/bozenka/instances/telegram/handlers/queries/setup.py +++ b/bozenka/instances/telegram/handlers/queries/setup.py @@ -1,11 +1,14 @@ -from aiogram.types import * +from aiogram.types import CallbackQuery, Message +from sqlalchemy import select, Update +from sqlalchemy.ext.asyncio import async_sessionmaker -from bozenka.instances.telegram.utils.callbacks_factory import SetupCategory, SetupFeature -from bozenka.instances.telegram.utils.keyboards import setup_category_keyboard +from bozenka.database.tables.telegram import ChatSettings +from bozenka.instances.telegram.utils.callbacks_factory import SetupCategory, SetupFeature, SetupAction +from bozenka.instances.telegram.utils.keyboards import setup_category_keyboard, setup_feature_keyboard from bozenka.instances.telegram.utils.simpler import list_of_features -async def inline_setup_features(call: CallbackQuery, callback_data: SetupCategory): +async def inline_setup_category(call: CallbackQuery, callback_data: SetupCategory): """ Query, what shows list of features to enable. :param call: @@ -16,7 +19,7 @@ async def inline_setup_features(call: CallbackQuery, callback_data: SetupCategor reply_markup=setup_category_keyboard(category=callback_data.category_name)) -async def inline_setup_features_back(call: CallbackQuery, callback_data: SetupCategory): +async def inline_setup_category_back(call: CallbackQuery, callback_data: SetupAction): """ Query, what shows list of features to enable. :param call: @@ -24,16 +27,40 @@ async def inline_setup_features_back(call: CallbackQuery, callback_data: SetupCa :return: """ await call.message.edit_text("Выберите настройку, которую хотите изменить", - reply_markup=setup_category_keyboard(category=callback_data.category_name)) + reply_markup=setup_category_keyboard(category=callback_data.feature_category)) - -async def inline_feature(call: CallbackQuery, callback_data: SetupFeature): +async def inline_edit_feature(call: CallbackQuery, callback_data: SetupFeature, session_maker: async_sessionmaker): """ Query, what shows menu to enable / disable feature :param call: :param callback_data: + :param session_maker: :return: """ - await call.message.edit_text(list_of_features[callback_data.feature_category][callback_data.feature_index].description) + await call.message.edit_text( + list_of_features[callback_data.feature_category][callback_data.feature_index].description, + reply_markup=await setup_feature_keyboard(category=callback_data.feature_category, + index=callback_data.feature_index, + session=session_maker, msg=call.message)) + +async def inline_feature_edited(call: CallbackQuery, callback_data: SetupAction, session_maker: async_sessionmaker): + """ + Query, what shows menu to enable / disable feature + after editing + :param call: + :param callback_data: + :param session_maker: + :return: + """ + async with session_maker() as session: + async with session.begin(): + await session.execute(Update(ChatSettings) + .values({list_of_features[callback_data.feature_category][callback_data.feature_index].settings_name: callback_data.action == "enable"}) + .where(ChatSettings.chat_id == call.message.chat.id)) + await call.message.edit_text( + list_of_features[callback_data.feature_category][callback_data.feature_index].description, + reply_markup=await setup_feature_keyboard(category=callback_data.feature_category, + index=callback_data.feature_index, + session=session, msg=call.message)) diff --git a/bozenka/instances/telegram/handlers/queries/start.py b/bozenka/instances/telegram/handlers/queries/start.py index 0e576e9..8acd68f 100644 --- a/bozenka/instances/telegram/handlers/queries/start.py +++ b/bozenka/instances/telegram/handlers/queries/start.py @@ -5,6 +5,82 @@ from bozenka.instances.telegram.utils.keyboards import * from bozenka.instances.telegram.utils.simpler import list_of_features +async def inline_start(call: CallbackQuery): + """ + Query, what shows back menu of /start + :param call: + :return: + """ + await call.message.edit_text( + 'Привет, пользователь, я - Бозенька 👋\n' + 'Я мультизадачный телеграм (в будущем кросс-платформенный) бот с открытым исходным кодом, разрабатываемый Bozo Developement\n' + f'Выберите, что будете делать, {call.from_user.mention_html(name="пользователь")}.', + reply_markup=start_keyboard() + ) + + +async def inline_start_chatbot(call: CallbackQuery): + """ + Query, what shows list of Categories, avaible to use as chatbot + :param call: + :return: + """ + await call.message.edit_text("Пожалуста, выберите сервиc / библиотеку, через которую вы будете общаться", + reply_markup=gpt_categories_keyboard + (user_id=call.from_user.id)) + + +async def inline_help(call: CallbackQuery): + """ + Query, what shows information about bozenka and it's development + :param call: + :return: + """ + kb = InlineKeyboardMarkup(inline_keyboard=[[ + + ]]) + await call.message.edit_text("Выберите категорию, по которой нужна помощь:", + reply_markup=help_keyboard()) + + +async def inline_about_developers(call: CallbackQuery): + """ + Query, what shows information about bozenka and it's development + :param call: + :return: + """ + kb = InlineKeyboardMarkup(inline_keyboard=[[ + InlineKeyboardButton(text="Вернуться 🔙", callback_data="back") + ]]) + await call.message.edit_text("Бозенька - это мультифункциональный (в будущем кроссплатформенный бот)." + "Он умеет работать с группами и готовыми нейронными сетями\n" + "Бозеьнка разработавается коммандой, состаящей из одного человека, сам проект был изначально для развития моих навыков в Python\n" + "Исходный код находится под лицензией GPL-3.0. Исходный код проекта всегда будет открыт и доступен.\n" + "Исходный код проекта всегда можно найти по этой ссылке: https://github.com/kittyneverdies/bozenka/\n" + "Исходный код бота для телеграма можно найти по этой ссылке: https://github.com/kittyneverdies/bozenka/branch/telegram", + reply_markup=kb) + + +async def inline_add_to_chat(call: CallbackQuery): + """ + Query, what shows a link to add bozenka into user chat + :param call: + :return: + """ + # 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()) + + async def inline_help_features(call: CallbackQuery, callback_data: HelpCategory): """ Query, what shows list of features to get support. @@ -45,5 +121,6 @@ async def inline_help_feature(call: CallbackQuery, callback_data: HelpFeature): :param callback_data: :return: """ - 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)) \ No newline at end of file + 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)) diff --git a/bozenka/instances/telegram/handlers/queries/gpt.py b/bozenka/instances/telegram/handlers/queries/text_generation.py similarity index 96% rename from bozenka/instances/telegram/handlers/queries/gpt.py rename to bozenka/instances/telegram/handlers/queries/text_generation.py index 3c0305c..2670d1c 100644 --- a/bozenka/instances/telegram/handlers/queries/gpt.py +++ b/bozenka/instances/telegram/handlers/queries/text_generation.py @@ -4,7 +4,7 @@ from aiogram import types from aiogram.fsm.context import FSMContext from gpt4all import GPT4All -from bozenka.instances.telegram.utils.simpler.states import * +from bozenka.instances.telegram.utils.simpler.fsm_states import * # Callbacks for GPT from bozenka.instances.telegram.utils.callbacks_factory import ( @@ -170,10 +170,10 @@ async def inline_g4a_model(call: types.CallbackQuery, callback_data: Gpt4AllMode if callback_data.user_id != call.from_user.id: return models = GPT4All.list_models() - name = models[callback_data.model_index]['name'] + name = models[callback_data.index]['name'] await call.message.edit_text(f"{name}\n" - f"Обученно на основе {models[callback_data.model_index]['parameters']} строк 👨‍💻", - reply_markup=gpt4all_model_menu(user_id=call.from_user.id, index=callback_data.model_index)) + f"Обученно на основе {models[callback_data.index]['parameters']} строк 👨‍💻", + reply_markup=gpt4all_model_menu(user_id=call.from_user.id, index=callback_data.index)) async def inline_g4a_select_model(call: types.CallbackQuery, callback_data: Gpt4AllSelect, state: FSMContext) -> None: @@ -186,13 +186,13 @@ async def inline_g4a_select_model(call: types.CallbackQuery, callback_data: Gpt4 """ if callback_data.user_id != call.from_user.id: return - await state.update_data(set_model=callback_data.model_index) + await state.update_data(set_model=callback_data.index) await state.set_state(AnsweringGpt4All.ready_to_answer) models = GPT4All.list_models() await call.message.edit_text("Удача ✅\n" "Вы теперь можете спокойно вести диалог 🤖\n" - f"Вы выбрали модель {models[callback_data.model_index]['name']}👾 от Gpt4All\n" + f"Вы выбрали модель {models[callback_data.index]['name']}👾 от Gpt4All\n" "Чтобы прекратить общение, используйте /cancel ", reply_markup=delete_keyboard(admin_id=callback_data.user_id)) diff --git a/bozenka/instances/telegram/utils/callbacks_factory/gpt_selector.py b/bozenka/instances/telegram/utils/callbacks_factory/gpt_selector.py index bc21f98..96a3fcd 100644 --- a/bozenka/instances/telegram/utils/callbacks_factory/gpt_selector.py +++ b/bozenka/instances/telegram/utils/callbacks_factory/gpt_selector.py @@ -1,7 +1,24 @@ from aiogram.filters.callback_data import CallbackData -class Gpt4FreeProvider(CallbackData, prefix="provider"): +class ImageGenerationCategory(CallbackData, prefix="igc"): + """ + Callback with information related to image + """ + user_id: int + category: str + + +class ImageGeneration(CallbackData, prefix="igc"): + """ + Callback with information related to image + """ + user_id: int + category: str + size: str + + +class Gpt4FreeProvider(CallbackData, prefix="g4fp"): """ Callback with information related to selected provider """ @@ -48,7 +65,7 @@ class Gpt4AllModel(CallbackData, prefix="g4a"): Callback with information to show GPT4All content """ user_id: int - model_index: int + index: int class GptBackMenu(CallbackData, prefix="gbm"): @@ -64,7 +81,7 @@ class Gpt4AllSelect(CallbackData, prefix="g4s"): Callback with information about selecting model """ user_id: int - model_index: int + index: int class GptStop(CallbackData, prefix="gs"): diff --git a/bozenka/instances/telegram/utils/callbacks_factory/setup.py b/bozenka/instances/telegram/utils/callbacks_factory/setup.py index 4728450..5a41c6c 100644 --- a/bozenka/instances/telegram/utils/callbacks_factory/setup.py +++ b/bozenka/instances/telegram/utils/callbacks_factory/setup.py @@ -23,6 +23,15 @@ class SetupBack(CallbackData, prefix="sb"): back_to: str +class SetupAction(CallbackData, prefix="sa"): + """ + Callback with information to do with a feature + """ + action: str + feature_category: str + feature_index: int + + class SetupEditFeature(CallbackData, prefix="sef"): """ Callback data with information to edit status of bozenka enabled feature diff --git a/bozenka/instances/telegram/utils/filters/__init__.py b/bozenka/instances/telegram/utils/filters/__init__.py index 0245794..d645dba 100644 --- a/bozenka/instances/telegram/utils/filters/__init__.py +++ b/bozenka/instances/telegram/utils/filters/__init__.py @@ -1,2 +1,2 @@ from .permissions import * -from .message_entities import * +from .chat_setting import * diff --git a/bozenka/instances/telegram/utils/filters/chat_setting.py b/bozenka/instances/telegram/utils/filters/chat_setting.py new file mode 100644 index 0000000..63ebeb0 --- /dev/null +++ b/bozenka/instances/telegram/utils/filters/chat_setting.py @@ -0,0 +1,21 @@ +from aiogram.filters import Filter +from aiogram.types import Message +from sqlalchemy.ext.asyncio import async_sessionmaker +from varname.helpers import exec_code + +from bozenka.database.tables.telegram import get_settings + + +class ChatSettingFilter(Filter): + """ + Check, does chat have enabled features + """ + + def __init__(self, settings: str) -> None: + self.settings = settings + + async def __call__(self, msg: Message, session: async_sessionmaker) -> bool: + chat_setting = await get_settings(chat_id=msg.chat.id, session=session) + exec_code(f'return chat_setting.{self.settings}') + return True + diff --git a/bozenka/instances/telegram/utils/keyboards/inline.py b/bozenka/instances/telegram/utils/keyboards/inline.py index bd4f180..8b1863f 100644 --- a/bozenka/instances/telegram/utils/keyboards/inline.py +++ b/bozenka/instances/telegram/utils/keyboards/inline.py @@ -2,13 +2,16 @@ import gpt4all from typing import Any -from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton +from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from gpt4all import GPT4All +from sqlalchemy.ext.asyncio import async_sessionmaker +from bozenka.database.tables.telegram import get_setting from bozenka.instances.telegram.utils.callbacks_factory import * -from bozenka.instances.telegram.utils.simpler import gpt_categories, generate_gpt4free_models, generate_gpt4free_providers, \ - generate_list_of_features +from bozenka.instances.telegram.utils.simpler import generate_list_of_features +from bozenka.generative.gpt4free import generate_gpt4free_models, generate_gpt4free_providers +from bozenka.generative import text_generative_categories, image_generative_categories, image_generative_size """ File, contains inline keyboard & menus and their work. @@ -16,6 +19,22 @@ Right now only on Russian language, multi-language planning soon. """ +def start_keyboard() -> InlineKeyboardMarkup: + """ + Generate keyboard for /start command + """ + kb = InlineKeyboardMarkup( + inline_keyboard=[ + [InlineKeyboardButton(text="Добавить в чат 🔌", callback_data="addtochat")], + [InlineKeyboardButton(text="Функционал 🔨", callback_data="functional")], + [InlineKeyboardButton(text="О разработчиках ℹ️", callback_data="aboutdevs")], + [InlineKeyboardButton(text="Начать диалог с ИИ 🤖", callback_data="dialogai")], + [InlineKeyboardButton(text="Генерация Изображений 🖼", callback_data="dialogimage")], + ] + ) + return kb + + # Help related keyboards def help_keyboard() -> InlineKeyboardMarkup: """ @@ -28,7 +47,9 @@ def help_keyboard() -> InlineKeyboardMarkup: [InlineKeyboardButton(text="Пользователи 👤", callback_data=HelpCategory(category_name="Members").pack())], [InlineKeyboardButton(text="В разработке 👨‍💻", - callback_data=HelpCategory(category_name="Devs").pack())]]) + callback_data=HelpCategory(category_name="Devs").pack())], + [InlineKeyboardButton(text="Вернуться 🔙", callback_data="back")] + ]) return kb @@ -72,6 +93,8 @@ def setup_keyboard() -> InlineKeyboardMarkup: Generate keyboard for /setup command :return: """ + kb = InlineKeyboardBuilder() + kb = InlineKeyboardMarkup(inline_keyboard=[[ InlineKeyboardButton(text="Администраторы 👮‍♂", callback_data=SetupCategory(category_name="Admins").pack())], @@ -99,8 +122,37 @@ def setup_category_keyboard(category: str) -> InlineKeyboardMarkup: return kb.as_markup() -def setup_feature_keyboard() -> InlineKeyboardMarkup: - pass +async def setup_feature_keyboard(category: str, index: int, session: async_sessionmaker, + msg: Message) -> InlineKeyboardMarkup: + """ + Generate keyboard for enabling or disabling + on of features + :param category: + :param index: + :param session: + :param msg: + :return: + """ + + list_of_features = generate_list_of_features(category) + is_enabled = await get_setting( + chat_id=msg.chat.id, + session=session, + setting=list_of_features[index].settings_name) + + kb = InlineKeyboardMarkup(inline_keyboard=[[ + InlineKeyboardButton(text="Выключить ❌", callback_data=SetupAction(action="disable", + feature_category=category, + feature_index=index).pack()) + if is_enabled else + InlineKeyboardButton(text="Включить ✅", callback_data=SetupAction(action="enable", + feature_category=category, + feature_index=index).pack()) + ], [ + InlineKeyboardButton(text="Вернуться 🔙", callback_data=SetupAction(action="back", + feature_category=category, + feature_index=index).pack())]]) + return kb def delete_keyboard(admin_id: int) -> InlineKeyboardMarkup: @@ -116,6 +168,37 @@ def delete_keyboard(admin_id: int) -> InlineKeyboardMarkup: return kb +def image_resolution_keyboard(user_id: int, category: str) -> InlineKeyboardMarkup: + """ + Create keyboard with list of resolution to generate image + :param user_id: + :param category: + :return: + """ + builder = InlineKeyboardBuilder() + for size in image_generative_size: + builder.row(InlineKeyboardButton(text=size, + callback_data=ImageGeneration( + user_id=user_id, + category=category, + size=size + ).pack())) + return builder.as_markup() + + +def image_generation_keyboard(user_id: int) -> InlineKeyboardMarkup: + """ + Create keyboard with list of image generation librarioes avaible in the bot + :param user_id: + :return: + """ + builder = InlineKeyboardBuilder() + for category in image_generative_categories: + builder.row(InlineKeyboardButton(text=category, + callback_data=ImageGenerationCategory(user_id=user_id, category=category).pack())) + return builder.as_markup() + + # LLM / GPT related keyboards # GPT CATEGORIES def gpt_categories_keyboard(user_id: int) -> InlineKeyboardMarkup: @@ -125,8 +208,9 @@ def gpt_categories_keyboard(user_id: int) -> InlineKeyboardMarkup: :return: InlineKeyboardMarkup """ builder = InlineKeyboardBuilder() - for category in gpt_categories: - builder.row(InlineKeyboardButton(text=category, callback_data=GptCategory(user_id=str(user_id), category=category).pack())) + for category in text_generative_categories: + builder.row(InlineKeyboardButton(text=category, + callback_data=GptCategory(user_id=str(user_id), category=category).pack())) return builder.as_markup() @@ -212,30 +296,35 @@ def gpt4free_models_keyboard(user_id: int, provider, page: int) -> InlineKeyboar models[provider].append("") names = items_list_generator(page, models[provider], 4) for name in names: - builder.row(InlineKeyboardButton(text=name.replace('-', ' '), callback_data=Gpt4freeResult(user_id=str(user_id), provider=provider, model=name).pack())) + builder.row(InlineKeyboardButton(text=name.replace('-', ' '), + callback_data=Gpt4freeResult(user_id=str(user_id), provider=provider, + model=name).pack())) pages = [len(models[provider]) // 4 - 1 if page - 1 == -1 else page - 1, 0 if page + 1 >= len(models[provider]) // 4 else page + 1] if len(models[provider]) > 4: builder.row( - # First page button - InlineKeyboardButton(text=str(len(models[provider]) // 4 if page == 0 else "1"), - callback_data=Gpt4FreeModelPage( - page=str(len(models[provider]) // 4 - 1 if page == 0 else "1"), - user_id=user_id,).pack(), - ), - # Page back button - InlineKeyboardButton(text="⬅️", - callback_data=Gpt4FreeModelPage(user_id=str(user_id), page=pages[0], provider=provider).pack()), - # Count of page button - InlineKeyboardButton(text=str(page + 1), callback_data="gotpages"), - # Next page button - InlineKeyboardButton(text="➡️", callback_data=Gpt4FreeModelPage(user_id=str(user_id), page=pages[1], provider=provider).pack()), - # Last page button - InlineKeyboardButton(text=str(len(models[provider]) // 4 if page != 0 else "1"), - callback_data=Gpt4FreeModelPage( - page=str(len(models[provider]) // 4 - 1) if page != 0 else "1", - user_id=user_id, - provider=provider).pack(),)) + # First page button + InlineKeyboardButton(text=str(len(models[provider]) // 4 if page == 0 else "1"), + callback_data=Gpt4FreeModelPage( + page=str(len(models[provider]) // 4 - 1 if page == 0 else "1"), + user_id=user_id, + provider=provider).pack(), + ), + # Page back button + InlineKeyboardButton(text="⬅️", + callback_data=Gpt4FreeModelPage(user_id=str(user_id), page=pages[0], + provider=provider).pack()), + # Count of page button + InlineKeyboardButton(text=str(page + 1), callback_data="gotpages"), + # Next page button + InlineKeyboardButton(text="➡️", callback_data=Gpt4FreeModelPage(user_id=str(user_id), page=pages[1], + provider=provider).pack()), + # Last page button + InlineKeyboardButton(text=str(len(models[provider]) // 4 if page != 0 else "1"), + callback_data=Gpt4FreeModelPage( + page=str(len(models[provider]) // 4 - 1) if page != 0 else "1", + user_id=user_id, + provider=provider).pack(), )) else: if providers[provider].supports_gpt_4: builder.row(InlineKeyboardButton(text="gpt 4", @@ -246,7 +335,8 @@ def gpt4free_models_keyboard(user_id: int, provider, page: int) -> InlineKeyboar builder.row(InlineKeyboardButton(text="gpt 3.5 turbo", callback_data=Gpt4freeResult (user_id=str(user_id), provider=provider, model="gpt-3.5-turbo").pack())) - builder.row(InlineKeyboardButton(text="🔙 Вернуться к провайдерам", callback_data=GptBackMenu(user_id=user_id, back_to="providers").pack())) + builder.row(InlineKeyboardButton(text="🔙 Вернуться к провайдерам", + callback_data=GptBackMenu(user_id=user_id, back_to="providers").pack())) builder.row(InlineKeyboardButton(text="Спасибо, не надо ❌", callback_data=GptStop(user_id=str(user_id)).pack())) return builder.as_markup() @@ -348,7 +438,8 @@ def mute_keyboard(admin_id: int, ban_id: int) -> InlineKeyboardMarkup: :return: """ kb = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text="Спасибо ✅", callback_data=DeleteCallbackData(user_id_clicked=str(admin_id)).pack())], + [InlineKeyboardButton(text="Спасибо ✅", + callback_data=DeleteCallbackData(user_id_clicked=str(admin_id)).pack())], [InlineKeyboardButton(text="Размутить 🛠️", callback_data=UnmuteData(user_id_unmute=ban_id, user_id_clicked=admin_id).pack())]]) return kb @@ -362,8 +453,10 @@ def unmute_keyboard(admin_id: int, ban_id: int) -> InlineKeyboardMarkup: :return: """ kb = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text="Спасибо ✅", callback_data=DeleteCallbackData(user_id_clicked=str(admin_id)).pack())], - [InlineKeyboardButton(text="Замутить 🛠️", callback_data=MuteData(user_id_mute=ban_id, user_id_clicked=admin_id).pack())]]) + [InlineKeyboardButton(text="Спасибо ✅", + callback_data=DeleteCallbackData(user_id_clicked=str(admin_id)).pack())], + [InlineKeyboardButton(text="Замутить 🛠️", + callback_data=MuteData(user_id_mute=ban_id, user_id_clicked=admin_id).pack())]]) return kb @@ -379,7 +472,8 @@ def invite_keyboard(link: str, admin_id: int, chat_name: str) -> InlineKeyboardM link = link.replace("https://", "") kb = InlineKeyboardMarkup(inline_keyboard=[[ InlineKeyboardButton(text=chat_name, url=link)], - [InlineKeyboardButton(text="Отозвать 🛠️", callback_data=RevokeCallbackData(admin_id=admin_id, link=link).pack())], + [InlineKeyboardButton(text="Отозвать 🛠️", + callback_data=RevokeCallbackData(admin_id=admin_id, link=link).pack())], [InlineKeyboardButton(text="Спасибо ✅", callback_data=DeleteCallbackData(user_id_clicked=str(admin_id)).pack())]]) return kb @@ -421,7 +515,8 @@ def pin_msg_keyboard(user_id: int, msg_id: int) -> InlineKeyboardMarkup: :return: """ kb = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text="Открепить сообщение 📌", callback_data=UnpinMsg(user_id=user_id, msg_id=msg_id).pack())], + [InlineKeyboardButton(text="Открепить сообщение 📌", + callback_data=UnpinMsg(user_id=user_id, msg_id=msg_id).pack())], [InlineKeyboardButton(text="Спасибо ✅", callback_data=DeleteCallbackData(user_id_clicked=str(user_id)).pack())] ]) return kb @@ -435,7 +530,8 @@ def unpin_msg_keyboard(user_id: int, msg_id: int) -> InlineKeyboardMarkup: :return: """ kb = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text="Открепить сообщение 📌", callback_data=PinMsg(user_id=user_id, msg_id=msg_id).pack())], + [InlineKeyboardButton(text="Открепить сообщение 📌", + callback_data=PinMsg(user_id=user_id, msg_id=msg_id).pack())], [InlineKeyboardButton(text="Спасибо ✅", callback_data=DeleteCallbackData(user_id_clicked=str(user_id)).pack())] ]) return kb diff --git a/bozenka/instances/telegram/utils/simpler/__init__.py b/bozenka/instances/telegram/utils/simpler/__init__.py index f50ffa4..0b5517c 100644 --- a/bozenka/instances/telegram/utils/simpler/__init__.py +++ b/bozenka/instances/telegram/utils/simpler/__init__.py @@ -1,6 +1,6 @@ from .solution_simpler import SolutionSimpler from .lists_of_content import * -from .states import * +from .fsm_states import * diff --git a/bozenka/instances/telegram/utils/simpler/states.py b/bozenka/instances/telegram/utils/simpler/fsm_states.py similarity index 100% rename from bozenka/instances/telegram/utils/simpler/states.py rename to bozenka/instances/telegram/utils/simpler/fsm_states.py diff --git a/bozenka/instances/telegram/utils/simpler/lists_of_content.py b/bozenka/instances/telegram/utils/simpler/lists_of_content.py index 024ac2c..38e5769 100644 --- a/bozenka/instances/telegram/utils/simpler/lists_of_content.py +++ b/bozenka/instances/telegram/utils/simpler/lists_of_content.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass from typing import List import g4f @@ -6,18 +7,36 @@ from g4f.Provider import RetryProvider from varname import nameof +@dataclass class BaseFeature: """ Basic class of Feature. Have inside desription, name, callback name, """ - def __init__(self, name: str, description: str, callback_name: str): - self.name = name - self.description = description - self.callback_name = callback_name + name: str + description: str + callback_name: str + settings_name: str -# List of features, avaible in bozenka +@dataclass +class BaseCategory: + """ + Basic class of Feature category + Have inside category name, callbackname + """ + name: str + callback_name: str + + +# List of categories, avaible in bot +list_of_categories = [ + BaseCategory(name="Администраторы 👮‍♂", callback_name="Admins"), + BaseCategory(name="Пользователи 👤", callback_name="Members"), + BaseCategory(name="В разработке 👨‍💻", callback_name="Devs") +] + +# List of features, avaible in bot list_of_features = { "Admins": [ BaseFeature( @@ -28,7 +47,8 @@ list_of_features = { "/unpin - открепляет сообщение" "/unpin_all - открепляет все сообщения, которые видит бот" "Для исполнения требует соответсвующих прав от пользователя и их наличие у бота.", - callback_name="pins" + callback_name="pins", + settings_name="pins" ), BaseFeature( name="Модерация чата 🕵️", @@ -46,7 +66,8 @@ list_of_features = { "чтобы выполнить одну из комманд по отношению к пользователю, " "ответьте на сообщение пользователя и используйте команду\n" "Для исполнения требует соответсвующих прав от пользователя и их наличие у бота.", - callback_name="moderation" + callback_name="moderation", + settings_name="moderation" ), BaseFeature( name="Работа с Форумом 💬", @@ -59,14 +80,16 @@ list_of_features = { "/show_general - показывает основную тему форума\n" "Для исполнения требует соответсвующих прав от пользователя и их наличие у бота. Также должен быть" "включен форум", - callback_name="topics" + callback_name="topics", + settings_name="topics" ), BaseFeature( name="Приглашения в Чат ✉", description="Генератор приглашения в Чат ✉\n" "Разрешает использование комманды /invite в чате, для созданния приглашений.\n" "Для исполнения требует соответсвующих прав от пользователя и их наличие у бота.", - callback_name="invites" + callback_name="invites", + settings_name="invite_generator" ) ], "Members": [ @@ -74,13 +97,15 @@ list_of_features = { name="Приветсвенные сообщения 👋", description="Приветсвенные сообщения 👋" "\nПриветсвенные сообщения новым и ушедшим пользователям.", - callback_name="welcome" + callback_name="welcome", + settings_name="welcome_messages" ), BaseFeature( name="Оповещение о муте 📬", description="Оповещение о муте 📬" "\nОповещает пользователя в личных сообщениях, что тот был: замучен, размучен, забанен, разбанен", callback_name="notify", + settings_name="restrict_notification" ) ], "Devs": [ @@ -89,7 +114,8 @@ list_of_features = { description="Функция `Привет` 👋" "\nБот будет отвечать на комманды " "/hi, /hello, /privet и т.п., отвечая приветсвием на сообщение пользователя.", - callback_name="hi" + callback_name="hi", + settings_name="hi_command" ), BaseFeature( name="ИИ ЧатБот 🤖", @@ -100,24 +126,20 @@ list_of_features = { "Для использования:\n" "
/conversations
" "\nНаходится в разработке, планируется в будущем. Следите за обновлениями 😘", - callback_name="gtm" + callback_name="gtm", + settings_name="gpt_conversations" ), BaseFeature( name="Генерация изображений 📸", description="Генерация изображений 🤖" "\nНаходится в разработке, планируется в будущем. Следите за обновлениями 😘", - callback_name="gpm" + callback_name="gpm", + settings_name="123" ) ] } -# List of gpt categories, avaible in bozenka now -gpt_categories = [ - "Gpt4Free", - "Gpt4All", -] - def generate_list_of_features(category: str) -> list[BaseFeature]: @@ -130,45 +152,6 @@ def generate_list_of_features(category: str) -> list[BaseFeature]: return list_of_features[category] -def generate_gpt4free_providers(): - """ - Generates list of g4f providers - :return: - """ - provider = {} - for prov in g4f.Provider.__all__: - if prov != "BaseProvider" and prov != "AsyncProvider" and prov != "RetryProvider": - exec(f"provider['{prov}']=g4f.Provider.{prov}") - result = {} - for check in provider: - if provider[check].working: - result[check] = provider[check] - return result - - -def generate_gpt4free_models(): - """ - Generates list of g4f models - :return: - """ - models = {} - for model, model_name in g4f.models.ModelUtils.convert.items(), g4f.models.ModelUtils.convert.keys(): - if type(model.best_provider) is RetryProvider: - for pr in model.best_provider.providers: - if pr in models: - models[nameof(pr)].append(model_name) - else: - models[nameof(pr)] = [model_name] - else: - if nameof(model.best_provider) in models: - models[nameof(model.best_provider)].append(model_name) - else: - models[nameof(model.best_provider)] = [model_name] - return models - - -en_cmds = {} - ru_cmds = { # /info command translation "info": "Информация об чате с названием nameofchathere" @@ -278,5 +261,4 @@ list_of_commands = { translations = { "ru": ru_cmds, - "en": en_cmds } diff --git a/bozenka/instances/telegram/utils/simpler/solution_simpler.py b/bozenka/instances/telegram/utils/simpler/solution_simpler.py index c830015..66c5b93 100644 --- a/bozenka/instances/telegram/utils/simpler/solution_simpler.py +++ b/bozenka/instances/telegram/utils/simpler/solution_simpler.py @@ -205,6 +205,7 @@ class SolutionSimpler: msg=f"Muted user @{msg.reply_to_message.from_user.full_name} id={msg.reply_to_message.from_user.id}", level=logging.INFO) user = await get_user(user_id=msg.from_user.id, chat_id=msg.chat.id, session=session) + user if not user: new_user = Users( user_id=msg.from_user.id, @@ -311,8 +312,9 @@ class SolutionSimpler: :param msg: Message telegram object :param session: Object of telegram command """ - + print("123") chat_data = await get_settings(msg.chat.id, session) + print(chat_data) if not chat_data: new_chat_data = ChatSettings(chat_id=msg.chat.id) async with session() as session: