Big refactor updates, fixes & fixes

- Big code refactor
- Gpt4All fixes
- Some changes
- Added /start basic functions
This commit is contained in:
kittyneverdies 2023-12-18 17:43:41 +03:00
parent 0d08c1a7c5
commit 1bebfe5ba4
70 changed files with 518 additions and 227 deletions

View File

@ -4,7 +4,7 @@ import logging
import g4f import g4f
from bozenka.instances.telegram import launch_telegram_instance from bozenka.instances.telegram import launch_telegram_instance
from bozenka.db import generate_url, get_async_engine, get_sessions_maker from bozenka.database import generate_url, get_async_engine, get_sessions_maker
def launch_instances() -> None: def launch_instances() -> None:

View File

@ -0,0 +1,5 @@
__all__ = ["MainModel", "get_async_engine", "get_sessions_maker", "Users", "get_user", "generate_url"]
from .main import MainModel
from .engine import get_async_engine, get_sessions_maker, generate_url
from bozenka.database.tables.telegram import Users, get_user

View File

@ -7,27 +7,14 @@ from sqlalchemy.orm import sessionmaker
def get_async_engine(url: URL | str) -> AsyncEngine: def get_async_engine(url: URL | str) -> AsyncEngine:
""" """
Creates AsyncEngine Creates AsyncEngine, it needs to create async
session maker by get_sessions_maker()
:param url: :param url:
:return: :return:
""" """
return create_async_engine(url=url, echo=True, pool_pre_ping=True) return create_async_engine(url=url, echo=True, pool_pre_ping=True)
@DeprecationWarning
async def schemas(engine: AsyncEngine, metadata) -> None:
"""
Commiting all changes & create databases
:param engine:
:param metadata:
:return:
"""
"""
async with engine.begin() as connect:
await connect.run_sync(metadata.create_all)
"""
def get_sessions_maker(engine: AsyncEngine) -> async_sessionmaker: def get_sessions_maker(engine: AsyncEngine) -> async_sessionmaker:
""" """
Creates SessionMaker (Async!) Creates SessionMaker (Async!)

View File

@ -7,7 +7,7 @@ from sqlalchemy.ext.asyncio import async_engine_from_config
from alembic import context from alembic import context
import bozenka.db.main import bozenka.database.main
# this is the Alembic Config object, which provides # this is the Alembic Config object, which provides
# access to the values within the .ini file in use. # access to the values within the .ini file in use.

View File

@ -3,7 +3,7 @@ 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
from sqlalchemy.ext.asyncio import async_sessionmaker from sqlalchemy.ext.asyncio import async_sessionmaker
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from bozenka.db.main import MainModel from bozenka.database.main import MainModel
class Users(MainModel): class Users(MainModel):
@ -56,17 +56,16 @@ class ChatSettings(MainModel):
# openai_token = Column(Text) # openai_token = Column(Text)
async def get_settings(user_id: int, chat_id: int, session: async_sessionmaker): async def get_settings(chat_id: int, session: async_sessionmaker):
""" """
Return settings with sessionmaker by chat_id Return settings with sessionmaker by chat_id
:param user_id: :param chat_id: id of telegram chat
:param chat_id: :param session: sessionmaker from dispatcher
:param session:
:return: :return:
""" """
async with session() as session: async with session() as session:
async with session.begin(): async with session.begin():
return (await session.execute(select(Users).where(Users.user_id == user_id and Users.chat_id == chat_id))).one_or_none() return (await session.execute(select(ChatSettings).where(ChatSettings.chat_id == chat_id))).one_or_none()
async def get_user(user_id: int, chat_id: int, session: async_sessionmaker) -> Row[tuple[Any, ...] | Any] | None: async def get_user(user_id: int, chat_id: int, session: async_sessionmaker) -> Row[tuple[Any, ...] | Any] | None:

View File

@ -1,5 +0,0 @@
__all__ = ["MainModel", "get_async_engine", "get_sessions_maker", "schemas", "Users", "get_user", "generate_url"]
from .main import MainModel
from .engine import get_async_engine, get_sessions_maker, schemas, generate_url
from bozenka.db.tables.telegram import Users, get_user

View File

@ -0,0 +1,6 @@
# List of generative categories, what we support
text_generative_categories = [
"Gpt4Free",
"Gpt4All",
]

View File

@ -0,0 +1,12 @@
import os
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:
:return:
"""
return os.path.exists("models\\" + model_filename)

View File

@ -0,0 +1,40 @@
import g4f
from g4f.Provider import RetryProvider
from varname import nameof
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

View File

@ -5,7 +5,7 @@ from aiogram import Dispatcher, Bot
from aiogram.types import BotCommand from aiogram.types import BotCommand
from sqlalchemy.ext.asyncio import async_sessionmaker from sqlalchemy.ext.asyncio import async_sessionmaker
from bozenka.instances.telegram.cmds import register_handlers from bozenka.instances.telegram.handlers import register_handlers
from bozenka.instances.telegram.utils.simpler import list_of_commands from bozenka.instances.telegram.utils.simpler import list_of_commands

View File

@ -1,45 +0,0 @@
from aiogram.types import Message as Message
from bozenka.instances.telegram.utils.keyboards import start_keyboard
async def start_cmd(msg: Message):
"""
/start command function
:param msg:
:return:
"""
await msg.answer(
'Привет, пользователь, я - Бозенька 👋\n'
'Я мультизадачный телеграм бот, разрабатываемый Bozo Developement\n'
f'Выберите, что будете делать, {msg.from_user.mention_html()}',
reply_markup=start_keyboard.as_markup()
)
async def features_list(msg: Message):
"""
Shows features list from reply keyboard
:param msg:
:return:
"""
await msg.answer("List will be soon")
async def about_devs(msg: Message):
"""
Shows info about devs from reply keyboard
:param msg:
:return:
"""
await msg.answer("Info about developers will be added soon")
async def add_to_chat(msg: Message):
"""
Sends link for adding bot into chat
:param msg:
:return:
"""
await msg.answer("Will be soon")

View File

@ -2,11 +2,11 @@ import logging
from aiogram import Dispatcher from aiogram import Dispatcher
from bozenka.instances.telegram.cmds.admin import register_admin_cmd from bozenka.instances.telegram.handlers.chat_admin import register_admin_cmd
from bozenka.instances.telegram.queries import register_queries from bozenka.instances.telegram.handlers.queries import register_queries
from bozenka.instances.telegram.cmds.dev import register_dev_cmd from bozenka.instances.telegram.handlers.dev import register_dev_cmd
from bozenka.instances.telegram.cmds.main import register_main_cmd from bozenka.instances.telegram.handlers.main import register_main_cmd
from bozenka.instances.telegram.cmds.user import register_user_cmd from bozenka.instances.telegram.handlers.chat_user import register_user_cmd
from bozenka.instances.telegram.utils.middleware import register_middlewares from bozenka.instances.telegram.utils.middleware import register_middlewares

View File

@ -5,10 +5,11 @@ import logging
from aiogram import Router, F from aiogram import Router, F
from aiogram.filters import Command from aiogram.filters import Command
from bozenka.instances.telegram.cmds.admin.mutes import mute, unmute from bozenka.instances.telegram.handlers.chat_admin.help import *
from bozenka.instances.telegram.cmds.admin.pins import pin, unpin, unpin_all from bozenka.instances.telegram.handlers.chat_admin.mutes import mute, unmute
from bozenka.instances.telegram.cmds.admin.topics import * from bozenka.instances.telegram.handlers.chat_admin.pins import pin, unpin, unpin_all
from bozenka.instances.telegram.cmds.admin.bans import ban, unban from bozenka.instances.telegram.handlers.chat_admin.topics import *
from bozenka.instances.telegram.handlers.chat_admin.bans import ban, unban
from bozenka.instances.telegram.utils.filters import ( from bozenka.instances.telegram.utils.filters import (
IsAdminFilter, IsAdminFilter,
UserHasPermissions, BotHasPermissions UserHasPermissions, BotHasPermissions
@ -23,6 +24,13 @@ def register_admin_cmd(router: Router) -> None:
:return: :return:
""" """
logging.log(msg="Registering administrator commands", level=logging.INFO) logging.log(msg="Registering administrator commands", level=logging.INFO)
# Helpig handlers
router.message.register(help_ban, Command(commands=["ban"]))
router.message.register(help_unban, Command(commands=["unban"]))
router.message.register(help_mute, Command(commands=["mute"]))
router.message.register(help_unmute, Command(commands=["mute"]))
router.message.register(help_pin, Command(commands=["pin"]))
router.message.register(help_unpin, Command(commands=["unpin"]))
# Ban / Unban commands handler # Ban / Unban commands handler
router.message.register(ban, Command(commands="ban"), router.message.register(ban, Command(commands="ban"),
IsAdminFilter(True), F.reply_to_message.text) IsAdminFilter(True), F.reply_to_message.text)

View File

@ -0,0 +1,70 @@
from aiogram.types import Message
from bozenka.instances.telegram.utils.keyboards import delete_keyboard
async def help_ban(msg: Message):
"""
Shows help message for /ban
:param msg:
"""
await msg.answer("Использование:\n"
"<pre>/ban [время блокировки] [причина блокировки]</pre>\n"
"Ответьте на сообщение, чтобы заблокировать пользователя",
reply_markup=delete_keyboard(msg.from_user.id))
async def help_unban(msg: Message):
"""
Shows help message for /unban
:param msg:
"""
await msg.answer("Использование:\n"
"<pre>/unban</pre>\n"
"Ответьте на сообщение, чтобы разблокировать пользователя",
reply_markup=delete_keyboard(msg.from_user.id))
async def help_mute(msg: Message):
"""
Shows help message for /mute
:param msg:
"""
await msg.answer("Использование:\n"
"<pre>/mute [время мута] [причина мута]</pre>\n"
"Ответьте на сообщение, чтобы замутить пользователя",
reply_markup=delete_keyboard(msg.from_user.id))
async def help_unmute(msg: Message):
"""
Shows help message for /unmute
:param msg:
"""
await msg.answer("Использование:\n"
"<pre>/unmute</pre>\n"
"Ответьте на сообщение, чтобы замутить пользователя",
reply_markup=delete_keyboard(msg.from_user.id))
async def help_pin(msg: Message):
"""
Shows help message for /mute
:param msg:
"""
await msg.answer("Использование:\n"
"<pre>/pin</pre>\n"
"Ответьте на сообщение, чтобы закрепить сообщение",
reply_markup=delete_keyboard(msg.from_user.id))
async def help_unpin(msg: Message):
"""
Shows help message for /mute
:param msg:
"""
await msg.answer("Использование:\n"
"<pre>/unpin</pre>\n"
"Ответьте на сообщение, чтобы открепить сообщение",
reply_markup=delete_keyboard(msg.from_user.id))

View File

@ -6,10 +6,10 @@ from aiogram.enums import ContentType
from aiogram.filters import Command from aiogram.filters import Command
from aiogram import Router, F from aiogram import Router, F
from bozenka.instances.telegram.cmds.user.about import about from bozenka.instances.telegram.handlers.chat_user.about import about
from bozenka.instances.telegram.cmds.user.invite import invite from bozenka.instances.telegram.handlers.chat_user.invite import invite
from bozenka.instances.telegram.cmds.user.info import chat_info from bozenka.instances.telegram.handlers.chat_user.info import chat_info
from bozenka.instances.telegram.cmds.user.welcome import * from bozenka.instances.telegram.handlers.chat_user.welcome import *
def register_user_cmd(router: Router) -> None: def register_user_cmd(router: Router) -> None:

View File

@ -4,8 +4,8 @@ import logging
from aiogram.filters import Command from aiogram.filters import Command
from bozenka.instances.telegram.cmds.dev.hello import hi, testing from bozenka.instances.telegram.handlers.dev.hello import hi, testing
from bozenka.instances.telegram.cmds.dev.ai import * from bozenka.instances.telegram.handlers.dev.ai import *
from bozenka.instances.telegram.utils.simpler import AnsweringGPT4Free, AnsweringGpt4All from bozenka.instances.telegram.utils.simpler import AnsweringGPT4Free, AnsweringGpt4All
from aiogram import Router from aiogram import Router
@ -20,7 +20,7 @@ def register_dev_cmd(router: Router) -> None:
logging.log(msg="Registering developer commands", level=logging.INFO) logging.log(msg="Registering developer commands", level=logging.INFO)
router.message.register(hi, Command(commands=["hi", "welcome", "sup", "wassup", "hello", "priv", router.message.register(hi, Command(commands=["hi", "welcome", "sup", "wassup", "hello", "priv",
"privet", "хай", "прив", "привет", "ку"])) "privet", "хай", "прив", "привет", "ку"]))
router.message.register(start_gpt_cmd, Command(commands=["conversation"])) router.message.register(start_dialog_cmd, Command(commands=["conversation"]))
router.message.register(g4f_generate_answer, AnsweringGPT4Free.ready_to_answer, ~Command(commands=["cancel"])) router.message.register(g4f_generate_answer, AnsweringGPT4Free.ready_to_answer, ~Command(commands=["cancel"]))
router.message.register(already_answering, AnsweringGpt4All.answering, ~Command(commands=["cancel"])) router.message.register(already_answering, AnsweringGpt4All.answering, ~Command(commands=["cancel"]))
router.message.register(already_answering, AnsweringGPT4Free.answering, ~Command(commands=["cancel"])) router.message.register(already_answering, AnsweringGPT4Free.answering, ~Command(commands=["cancel"]))

View File

@ -5,6 +5,8 @@ import g4f
from gpt4all import GPT4All from gpt4all import GPT4All
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from aiogram.types import Message as Message 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.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 generate_gpt4free_providers, ru_cmds, AnsweringGpt4All, \
AnsweringGPT4Free AnsweringGPT4Free
@ -12,7 +14,8 @@ from bozenka.instances.telegram.utils.simpler import generate_gpt4free_providers
async def already_answering(msg: Message, state: FSMContext): async def already_answering(msg: Message, state: FSMContext):
""" """
Giving response, if we already responsing it Giving response, if answering user now,
but he still asks something
:param msg: :param msg:
:param state: :param state:
:return: :return:
@ -21,7 +24,7 @@ async def already_answering(msg: Message, state: FSMContext):
reply_markup=delete_keyboard(admin_id=msg.from_user.id)) reply_markup=delete_keyboard(admin_id=msg.from_user.id))
async def start_gpt_cmd(msg: Message, state: FSMContext): async def start_dialog_cmd(msg: Message, state: FSMContext):
""" """
/conversation command handler, start /conversation command handler, start
:param msg: :param msg:
@ -37,7 +40,8 @@ async def start_gpt_cmd(msg: Message, state: FSMContext):
async def cancel_answering(msg: Message, state: FSMContext): async def cancel_answering(msg: Message, state: FSMContext):
""" """
Canceling dialog with ChatGPT Canceling dialog with generative model
Works on command /cancel
:param msg: :param msg:
:param state: :param state:
:return: :return:
@ -67,15 +71,14 @@ async def g4a_generate_answer(msg: Message, state: FSMContext):
"Если что-то пойдет не так, мы вам сообщим 👌", "Если что-то пойдет не так, мы вам сообщим 👌",
reply_markup=response_keyboard(user_id=msg.from_user.id)) reply_markup=response_keyboard(user_id=msg.from_user.id))
if not os.path.exists( if not check(models[info["set_model"]]["filename"]):
"D:\\Files\\Documents\\GitHub\\Bozenka\\bozenka\\gpt\\gpt4all\\models\\" + models[info["set_model"]][ main_msg = await main_msg.edit_text(main_msg.text + "\nПодождите пожалуста, мы скачиваем модель...",
"filename"]): reply_markup=main_msg.reply_markup)
main_msg = await main_msg.edit_text(main_msg + "\nПодождите пожалуста, мы скачиваем модель...")
try: try:
# Setting Gpt4All model # Setting Gpt4All model
model = GPT4All(model_name=models[info['set_model']]['filename'], model = GPT4All(model_name=models[info['set_model']]['filename'],
model_path="D:\\Files\\Documents\\GitHub\\Bozenka\\bozenka\\gpt\\gpt4all\\models\\", model_path="/bozenka/generative\\gpt4all\\models\\",
allow_download=True) allow_download=True)
# Setting our chat session if exist # Setting our chat session if exist
model.current_chat_session = [] if not info.get("ready_to_answer") else info["ready_to_answer"] model.current_chat_session = [] if not info.get("ready_to_answer") else info["ready_to_answer"]

View File

@ -4,9 +4,10 @@ import logging
from aiogram import Router, F from aiogram import Router, F
from aiogram.enums import ContentType from aiogram.enums import ContentType
from aiogram.filters import Command from aiogram.filters import Command, CommandStart
from bozenka.instances.telegram.cmds.main.setup import after_adding, setup_cmd from bozenka.instances.telegram.handlers.main.setup import *
from bozenka.instances.telegram.handlers.main.start import *
def register_main_cmd(router: Router) -> None: def register_main_cmd(router: Router) -> None:
@ -18,6 +19,10 @@ def register_main_cmd(router: Router) -> None:
""" """
logging.log(msg="Registering main related commands", level=logging.INFO) logging.log(msg="Registering main related commands", level=logging.INFO)
# Start command handler # 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(start_cmd, CommandStart)
router.message.register(setup_cmd, Command(commands=["setup"])) router.message.register(setup_cmd, Command(commands=["setup"]))
# After adding to chat handler # After adding to chat handler

View File

@ -1,5 +1,5 @@
from aiogram.types import Message as Message from aiogram.types import Message as Message
from bozenka.instances.telegram.utils.simpler import ru_cmds from bozenka.instances.telegram.utils.simpler import SolutionSimpler
from bozenka.instances.telegram.utils.keyboards import setup_keyboard from bozenka.instances.telegram.utils.keyboards import setup_keyboard
@ -22,5 +22,5 @@ async def after_adding(msg: Message):
""" """
await msg.answer("Здраствуйте администраторы чата 👋\n" await msg.answer("Здраствуйте администраторы чата 👋\n"
"Я - <b>бозенька</b>, мультифункциональный бот, разрабатываемый Bozo Developement\n" "Я - <b>бозенька</b>, мультифункциональный бот, разрабатываемый Bozo Developement\n"
"Выдайте мне <b>полные права администратора</b> для моей полной работы." "Выдайте мне <b>полные права администратора</b> для моей полной работы, если не выдали."
"Чтобы настроить функционал, используйте /setup или кнопку под сообщением") "Чтобы настроить функционал, используйте /setup или кнопку под сообщением")

View File

@ -0,0 +1,61 @@
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
async def start_cmd(msg: Message):
"""
/start command function
:param msg:
:return:
"""
await msg.answer(
'Привет, пользователь, я - Бозенька 👋\n'
'Я мультизадачный телеграм (в будущем кросс-платформенный) бот с открытым исходным кодом, разрабатываемый <b>Bozo Developement</b>\n'
f'Выберите, что будете делать, {msg.from_user.mention_html(name="пользователь")}.',
reply_markup=start_keyboard_builder.as_markup(one_time_keyboard=True, resize_keyboard=True)
)
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"
"Исходный код находится под лицензией <b>GPL-3.0</b>. Исходный код проекта всегда будет открыт и доступен.\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()

View File

@ -2,14 +2,16 @@ __all__ = ["ban", "delete", "gpt"]
from aiogram import Router, F 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.utils.callbacks_factory import * from bozenka.instances.telegram.utils.callbacks_factory import *
from bozenka.instances.telegram.queries.ban import * from bozenka.instances.telegram.handlers.queries.ban import *
from bozenka.instances.telegram.queries.pins import * from bozenka.instances.telegram.handlers.queries.pins import *
from bozenka.instances.telegram.queries.threads import * from bozenka.instances.telegram.handlers.queries.threads import *
from bozenka.instances.telegram.queries.delete import * from bozenka.instances.telegram.handlers.queries.delete import *
from bozenka.instances.telegram.queries.revoke import * from bozenka.instances.telegram.handlers.queries.revoke import *
from bozenka.instances.telegram.queries.gpt import * from bozenka.instances.telegram.handlers.queries.gpt import *
from bozenka.instances.telegram.queries.setup import * from bozenka.instances.telegram.handlers.queries.setup import *
def register_queries(router: Router) -> None: def register_queries(router: Router) -> None:
@ -79,3 +81,12 @@ def register_queries(router: Router) -> None:
# Menu of feature to enable or disable # Menu of feature to enable or disable
router.callback_query.register(inline_feature, SetupFeature.filter()) router.callback_query.register(inline_feature, SetupFeature.filter())
# /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())

View File

@ -123,7 +123,7 @@ async def inline_g4f_ready(call: types.CallbackQuery, callback_data: Gpt4freeRes
await call.message.edit_text("Удача ✅\n" await call.message.edit_text("Удача ✅\n"
"Вы теперь можете спокойно вести диалог 🤖\n" "Вы теперь можете спокойно вести диалог 🤖\n"
f"Вы выбрали модель <pre>{callback_data.model}</pre>👾, от провайдера <pre>{callback_data.provider}</pre>👨‍💻\n" f"Вы выбрали модель <b>{callback_data.model}</b>👾, от провайдера <b>{callback_data.provider}</b>👨‍💻\n"
"Чтобы прекратить общение, используйте /cancel ", "Чтобы прекратить общение, используйте /cancel ",
reply_markup=delete_keyboard(admin_id=callback_data.user_id)) reply_markup=delete_keyboard(admin_id=callback_data.user_id))
await call.answer("Вы теперь можете спокойно вести диалог 🤖") await call.answer("Вы теперь можете спокойно вести диалог 🤖")
@ -192,7 +192,7 @@ async def inline_g4a_select_model(call: types.CallbackQuery, callback_data: Gpt4
await call.message.edit_text("Удача ✅\n" await call.message.edit_text("Удача ✅\n"
"Вы теперь можете спокойно вести диалог 🤖\n" "Вы теперь можете спокойно вести диалог 🤖\n"
f"Вы выбрали модель <pre>{models[callback_data.model_index]['name']}</pre>👾 от Gpt4All\n" f"Вы выбрали модель <b>{models[callback_data.model_index]['name']}</b>👾 от Gpt4All\n"
"Чтобы прекратить общение, используйте /cancel ", "Чтобы прекратить общение, используйте /cancel ",
reply_markup=delete_keyboard(admin_id=callback_data.user_id)) reply_markup=delete_keyboard(admin_id=callback_data.user_id))

View File

@ -0,0 +1,49 @@
from aiogram.types import *
from bozenka.instances.telegram.utils.callbacks_factory import *
from bozenka.instances.telegram.utils.keyboards import *
from bozenka.instances.telegram.utils.simpler import list_of_features
async def inline_help_features(call: CallbackQuery, callback_data: HelpCategory):
"""
Query, what shows list of features to get support.
:param call:
:param callback_data:
:return:
"""
await call.message.edit_text("Выберите функцию, по которой нужна помощь",
reply_markup=help_category_keyboard(category=callback_data.category_name))
async def inline_back_help_features(call: CallbackQuery, callback_data: HelpBackCategory):
"""
Query, what shows list of features to get support.
:param call:
:param callback_data:
:return:
"""
await call.message.edit_text("Выберите функцию, по которой нужна помощь",
reply_markup=help_category_keyboard(category=callback_data.back_to_category))
async def inline_back_help_categories(call: CallbackQuery, callback_data: HelpBack):
"""
Query, what shows list of features to get support back.
:param call:
:param callback_data:
:return:
"""
await call.message.edit_text("Выберите категорию, по которой нужна помощь:",
reply_markup=help_keyboard())
async def inline_help_feature(call: CallbackQuery, callback_data: HelpFeature):
"""
Query, what shows list of features to get support.
:param call:
: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))

View File

@ -1,6 +1,5 @@
from .admin import * from .admin import *
from .delete import DeleteCallbackData from .delete import DeleteCallbackData
from .revoke import RevokeCallbackData
from .gpt_selector import * from .gpt_selector import *
from .setup import * from .setup import *
from .threads import * from .start import *

View File

@ -65,3 +65,13 @@ class UnpinMsg(CallbackData, prefix='up'):
""" """
user_id: int user_id: int
msg_id: int msg_id: int
# Link revoke
class RevokeCallbackData(CallbackData, prefix="mute"):
"""
Callback with information to revoke invite link
"""
admin_id: int
link: str

View File

@ -1,6 +0,0 @@
from aiogram.filters.callback_data import CallbackData
class RevokeCallbackData(CallbackData, prefix="mute"):
admin_id: int
link: str

View File

@ -0,0 +1,38 @@
from aiogram.filters.callback_data import CallbackData
class HelpCategory(CallbackData, prefix="hc"):
"""
Callback data of help categories
"""
category_name: str
class HelpFeature(CallbackData, prefix="hf"):
"""
Callback data of features category
"""
feature_index: int
feature_category: str
class HelpBack(CallbackData, prefix="hb"):
"""
Callback data to back to categories in help menu
"""
back_to: str
class HelpBackCategory(CallbackData, prefix="hbc"):
"""
Callback data to back to list of features in one
of categories in menu
"""
back_to_category: str
class BackStart(CallbackData, prefix="start"):
"""
Callback data to back to /start
"""
pass

View File

@ -1,3 +0,0 @@
from aiogram.filters.callback_data import CallbackData

View File

@ -0,0 +1,8 @@
from aiogram.filters import Filter
class IsChatType(Filter):
"""
"""

View File

@ -1,2 +1,2 @@
from .inline import * from .inline import *
from .reply import start_keyboard from .reply import start_keyboard_builder

View File

@ -7,7 +7,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from gpt4all import GPT4All from gpt4all import GPT4All
from bozenka.instances.telegram.utils.callbacks_factory import * from bozenka.instances.telegram.utils.callbacks_factory import *
from bozenka.instances.telegram.utils.simpler import gpt_categories, gpt4free_providers, generate_gpt4free_providers, \ from bozenka.instances.telegram.utils.simpler import gpt_categories, generate_gpt4free_models, generate_gpt4free_providers, \
generate_list_of_features generate_list_of_features
""" """
@ -16,6 +16,57 @@ Right now only on Russian language, multi-language planning soon.
""" """
# Help related keyboards
def help_keyboard() -> InlineKeyboardMarkup:
"""
Generate keyboard for /help command
:return:
"""
kb = InlineKeyboardMarkup(inline_keyboard=[[
InlineKeyboardButton(text="Администраторы 👮‍♂",
callback_data=HelpCategory(category_name="Admins").pack())],
[InlineKeyboardButton(text="Пользователи 👤",
callback_data=HelpCategory(category_name="Members").pack())],
[InlineKeyboardButton(text="В разработке 👨‍💻",
callback_data=HelpCategory(category_name="Devs").pack())]])
return kb
def help_category_keyboard(category: str) -> InlineKeyboardMarkup:
"""
Generate keyboard for one of categories
:param category:
:return:
"""
kb = InlineKeyboardBuilder()
list_of_features = generate_list_of_features(category)
for setting in list_of_features:
kb.row(InlineKeyboardButton(text=setting.name,
callback_data=HelpFeature(
feature_index=list_of_features.index(setting),
feature_category=category
).pack()))
kb.row(InlineKeyboardButton(text="🔙 Назад к категориям",
callback_data=HelpBack(back_to="category").pack()))
return kb.as_markup()
def help_feature_keyboard(category: str) -> InlineKeyboardMarkup:
"""
Just button for function of /help
:param category:
:return:
"""
kb = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🔙 Назад к функциям",
callback_data=HelpBackCategory(back_to_category=category).pack())],
[InlineKeyboardButton(text="🔙 Назад к функциям",
callback_data=HelpBackCategory(back_to_category=category).pack())]
])
return kb
# Setup related keyboards
def setup_keyboard() -> InlineKeyboardMarkup: def setup_keyboard() -> InlineKeyboardMarkup:
""" """
Generate keyboard for /setup command Generate keyboard for /setup command
@ -52,8 +103,7 @@ def setup_feature_keyboard() -> InlineKeyboardMarkup:
pass pass
def delete_keyboard(admin_id: int) -> InlineKeyboardMarkup:
def delete_keyboard(admin_id) -> InlineKeyboardMarkup:
""" """
Basic keyboard for all messages from bot. Basic keyboard for all messages from bot.
By pressing this button, message from bot will get deleted. By pressing this button, message from bot will get deleted.
@ -94,7 +144,6 @@ def items_list_generator(page: int, list_of_items, count_of_items: int) -> list[
if count not in required_items: if count not in required_items:
continue continue
items.append(item) items.append(item)
print(items)
return items return items
@ -156,18 +205,22 @@ def gpt4free_models_keyboard(user_id: int, provider, page: int) -> InlineKeyboar
:param page: :param page:
""" """
builder = InlineKeyboardBuilder() builder = InlineKeyboardBuilder()
if provider in gpt4free_providers: models = generate_gpt4free_models()
names = items_list_generator(page, gpt4free_providers[provider], 4) providers = generate_gpt4free_providers()
if provider in models:
if providers[provider].supports_gpt_4:
models[provider].append("")
names = items_list_generator(page, models[provider], 4)
for name in names: 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(gpt4free_providers[provider]) // 4 - 1 if page - 1 == -1 else page - 1, pages = [len(models[provider]) // 4 - 1 if page - 1 == -1 else page - 1,
0 if page + 1 >= len(gpt4free_providers[provider]) // 4 else page + 1] 0 if page + 1 >= len(models[provider]) // 4 else page + 1]
if len(gpt4free_providers[provider]) > 4: if len(models[provider]) > 4:
builder.row( builder.row(
# First page button # First page button
InlineKeyboardButton(text=str(len(gpt4free_providers[provider]) // 4 if page == 0 else "1"), InlineKeyboardButton(text=str(len(models[provider]) // 4 if page == 0 else "1"),
callback_data=Gpt4FreeModelPage( callback_data=Gpt4FreeModelPage(
page=str(len(gpt4free_providers[provider]) // 4 - 1 if page == 0 else "1"), page=str(len(models[provider]) // 4 - 1 if page == 0 else "1"),
user_id=user_id,).pack(), user_id=user_id,).pack(),
), ),
# Page back button # Page back button
@ -178,13 +231,12 @@ def gpt4free_models_keyboard(user_id: int, provider, page: int) -> InlineKeyboar
# Next page button # Next page button
InlineKeyboardButton(text="➡️", callback_data=Gpt4FreeModelPage(user_id=str(user_id), page=pages[1], provider=provider).pack()), InlineKeyboardButton(text="➡️", callback_data=Gpt4FreeModelPage(user_id=str(user_id), page=pages[1], provider=provider).pack()),
# Last page button # Last page button
InlineKeyboardButton(text=str(len(gpt4free_providers[provider]) // 4 if page != 0 else "1"), InlineKeyboardButton(text=str(len(models[provider]) // 4 if page != 0 else "1"),
callback_data=Gpt4FreeModelPage( callback_data=Gpt4FreeModelPage(
page=str(len(gpt4free_providers[provider]) // 4 - 1) if page != 0 else "1", page=str(len(models[provider]) // 4 - 1) if page != 0 else "1",
user_id=user_id, user_id=user_id,
provider=provider).pack(),)) provider=provider).pack(),))
else: else:
providers = generate_gpt4free_providers()
if providers[provider].supports_gpt_4: if providers[provider].supports_gpt_4:
builder.row(InlineKeyboardButton(text="gpt 4", builder.row(InlineKeyboardButton(text="gpt 4",
callback_data=Gpt4freeResult(user_id=str(user_id), callback_data=Gpt4freeResult(user_id=str(user_id),

View File

@ -2,10 +2,10 @@
from aiogram.types import KeyboardButton from aiogram.types import KeyboardButton
from aiogram.utils.keyboard import ReplyKeyboardBuilder from aiogram.utils.keyboard import ReplyKeyboardBuilder
start_keyboard = ReplyKeyboardBuilder() start_keyboard_builder = ReplyKeyboardBuilder()
start_keyboard.row( start_keyboard_builder.row(
KeyboardButton(text="Добавить в чат 🔌"), KeyboardButton(text="Добавить в чат 🔌"),
KeyboardButton(text="Функционал 🔨") KeyboardButton(text="Функционал 🔨")
) )
start_keyboard.adjust(1,2) start_keyboard_builder.adjust(1, 2)
start_keyboard.add(KeyboardButton(text="О разработчиках ")) start_keyboard_builder.add(KeyboardButton(text="О разработчиках "))

View File

@ -8,7 +8,7 @@ from aiogram import Router, Dispatcher
def register_middlewares(dp: Dispatcher): def register_middlewares(dp: Dispatcher):
""" """
Registering all middlewares of bot. Registering all middlewares of bot.
:param router: :param dp:
:return: :return:
""" """
logging.log(msg=f"Registering middlewares of bot", level=logging.INFO) logging.log(msg=f"Registering middlewares of bot", level=logging.INFO)

View File

@ -0,0 +1 @@
# Will be added soon

View File

@ -1,5 +1,5 @@
from .solution_simpler import SolutionSimpler from .solution_simpler import SolutionSimpler
from .texts import * from .lists_of_content import *
from .states import * from .states import *

View File

@ -1,7 +0,0 @@
def registrate_commands():
"""
Registrate commands for telegram automatic tips
:return:
"""
pass

View File

@ -2,6 +2,8 @@ from typing import List
import g4f import g4f
from g4f import Provider from g4f import Provider
from g4f.Provider import RetryProvider
from varname import nameof
class BaseFeature: class BaseFeature:
@ -15,58 +17,7 @@ class BaseFeature:
self.callback_name = callback_name self.callback_name = callback_name
gpt4free_providers = { # List of features, avaible in bozenka
"AItianhu": ["gpt-3.5-turbo", "gpt-4"],
"Acytoo": ["gpt-3.5-turbo"],
"AiService": ["gpt-3.5-turbo"],
"Aichat": ["gpt-3.5-turbo"],
"Ails": ["gpt-3.5-turbo"],
"Bard": ["palm"],
"Bing": ["gpt-4"],
"ChatgptAi": ["gpt-4"],
"ChatgptLogin": ["gpt-3.5-turbo"],
"DeepAi": ["gpt-3.5-turbo"],
"DfeHub": ["gpt-3.5-turbo"],
"EasyChat": ["gpt-3.5-turbo"],
"Forefront": ["gpt-3.5-turbo"],
"GetGpt": ["gpt-3.5-turbo"],
"H2o": ["falcon-40b", "falcon-7b", "llama-13b"],
"Liaobots": ["gpt-3.5-turbo", "gpt-4"],
"Lockchat": ["gpt-3.5-turbo", "gpt-4"],
"Opchatgpts": ["gpt-3.5-turbo"],
"Raycast": ["gpt-3.5-turbo", "gpt-4"],
"Theb": ["gpt-3.5-turbo"],
# Vercel, biggest part of list
"Vercel": [
"gpt-3.5-turbo",
"claude-instant-v1",
"claude-v1",
"claude-v2",
"command-light-nightly",
"command-nightly",
"gpt-neox-20b",
"oasst-sft-1-pythia-12b",
"oasst-sft-4-pythia-12b-epoch-3.5",
"santacoder",
"bloom",
"flan-t5-xxl",
"code-davinci-002",
"gpt-3.5-turbo-16k",
"gpt-3.5-turbo-16k-0613",
"gpt-4-0613",
"text-ada-001",
"text-babbage-001",
"text-curie-001",
"text-davinci-002",
"text-davinci-003",
"llama13b-v2-chat",
"llama7b-v2-chat"
],
"Wewordle": ["gpt-3.5-turbo"],
"You": ["gpt-3.5-turbo"],
"Yqcloud": ["gpt-3.5-turbo"]
}
list_of_features = { list_of_features = {
"Admins": [ "Admins": [
BaseFeature( BaseFeature(
@ -82,15 +33,15 @@ list_of_features = {
BaseFeature( BaseFeature(
name="Модерация чата 🕵️", name="Модерация чата 🕵️",
description="<b>Модерация чата</b>🕵️\nДанная настройка включает следущие комманды:" description="<b>Модерация чата</b>🕵️\nДанная настройка включает следущие комманды:"
"\n/ban [время блокировки] [причина блокировки] - блокировка пользователя" "\n<pre>/ban [время блокировки] [причина блокировки] - блокировка пользователя"
"\n/unban - разблокировка пользователя\n" "\n/unban - разблокировка пользователя\n"
"/mute [время мута] [причина мута] - мут пользователя " "/mute [время мута] [причина мута] - мут пользователя\n"
"/unmute - Размут пользователя\n" "/unmute - Размут пользователя</pre>\n"
"Время обозначается как:" "Время обозначается как:"
"1h - один час, " "<pre>1h - один час, "
"1d - один день, " "1d - один день, "
"1m - одна минута, " "1m - одна минута, "
"1s - одна секунда\n" "1s - одна секунда</pre>\n"
"Для того, " "Для того, "
"чтобы выполнить одну из комманд по отношению к пользователю, " "чтобы выполнить одну из комманд по отношению к пользователю, "
"ответьте на сообщение пользователя и используйте команду\n" "ответьте на сообщение пользователя и используйте команду\n"
@ -100,12 +51,12 @@ list_of_features = {
BaseFeature( BaseFeature(
name="Работа с Форумом 💬", name="Работа с Форумом 💬",
description="<b>Работа с Форумом</b>💬\nДанная настройка включает следущие комманды:\n" description="<b>Работа с Форумом</b>💬\nДанная настройка включает следущие комманды:\n"
"/open - открывают тему форума\n" "<pre>/open - открывают тему форума\n"
"/close - закрывают тему форума\n" "/close - закрывают тему форума\n"
"/open_general - открывают основную тему форума\n" "/open_general - открывают основную тему форума\n"
"/close_general - закрывает основную тему форума\n" "/close_general - закрывает основную тему форума\n"
"/hide_general - прячет основную тему форума\n" "/hide_general - прячет основную тему форума\n"
"/show_general - показывает основную тему форума\n" "/show_general - показывает основную тему форума</pre>\n"
"Для исполнения <b>требует соответсвующих прав от пользователя и их наличие у бота. Также должен быть" "Для исполнения <b>требует соответсвующих прав от пользователя и их наличие у бота. Также должен быть"
"включен форум</b>", "включен форум</b>",
callback_name="topics" callback_name="topics"
@ -143,8 +94,11 @@ list_of_features = {
BaseFeature( BaseFeature(
name="ИИ ЧатБот 🤖", name="ИИ ЧатБот 🤖",
description="<b>ИИ ЧатБот </b>🤖" description="<b>ИИ ЧатБот </b>🤖"
"Есть поддержка провайдеров из Gpt4Free\n" "\nЕсть поддержка:\n"
"Есть поддержка моделей Gpt4All" "- Моделей Gpt4All\n"
"- Провайдеров Gpt4Free и моделей\n"
"Для использования:\n"
"<pre>/conversations</pre>"
"\nНаходится в разработке, планируется в будущем. Следите за обновлениями 😘", "\nНаходится в разработке, планируется в будущем. Следите за обновлениями 😘",
callback_name="gtm" callback_name="gtm"
), ),
@ -158,6 +112,13 @@ list_of_features = {
} }
# List of gpt categories, avaible in bozenka now
gpt_categories = [
"Gpt4Free",
"Gpt4All",
]
def generate_list_of_features(category: str) -> list[BaseFeature]: def generate_list_of_features(category: str) -> list[BaseFeature]:
""" """
@ -176,7 +137,7 @@ def generate_gpt4free_providers():
""" """
provider = {} provider = {}
for prov in g4f.Provider.__all__: for prov in g4f.Provider.__all__:
if prov != "BaseProvider": if prov != "BaseProvider" and prov != "AsyncProvider" and prov != "RetryProvider":
exec(f"provider['{prov}']=g4f.Provider.{prov}") exec(f"provider['{prov}']=g4f.Provider.{prov}")
result = {} result = {}
for check in provider: for check in provider:
@ -185,13 +146,27 @@ def generate_gpt4free_providers():
return result return result
gpt_categories = [ def generate_gpt4free_models():
"Gpt4Free", """
"Gpt4All", Generates list of g4f models
"StableLM", :return:
"H20Gpt", """
"RWKV" 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 = {} en_cmds = {}
ru_cmds = { ru_cmds = {

View File

@ -12,10 +12,8 @@ from aiogram.enums import ChatMemberStatus
from aiogram.types import ChatPermissions, ChatAdministratorRights from aiogram.types import ChatPermissions, ChatAdministratorRights
from sqlalchemy.ext.asyncio import async_sessionmaker from sqlalchemy.ext.asyncio import async_sessionmaker
from bozenka.database import get_user, Users
from bozenka.db import get_user, Users from bozenka.database.tables.telegram import get_settings, ChatSettings
def count_time(counted_time: str) -> int: def count_time(counted_time: str) -> int:
@ -44,8 +42,10 @@ class SolutionSimpler:
Making feature 'result in your direct message' easy and cleaner to complete. Making feature 'result in your direct message' easy and cleaner to complete.
Including logging and debugging. Including logging and debugging.
""" """
@staticmethod @staticmethod
async def ban_user(msg: types.Message, cmd: CommandObject, session: async_sessionmaker) -> dict[str, None | str | bool]: async def ban_user(msg: types.Message, cmd: CommandObject, session: async_sessionmaker) -> dict[
str, None | str | bool]:
""" """
Bans user, returns config, by config you can send special message. Bans user, returns config, by config you can send special message.
:param msg: :param msg:
@ -261,3 +261,19 @@ class SolutionSimpler:
msg=f"Created invite into chat by @{msg.from_user.full_name} chat_id={msg.chat.id}", msg=f"Created invite into chat by @{msg.from_user.full_name} chat_id={msg.chat.id}",
level=logging.INFO) level=logging.INFO)
return link.invite_link return link.invite_link
@staticmethod
async def auto_settings(msg: types.Message, session: async_sessionmaker):
"""
Creating setting automaticly
:param msg:
:param session:
"""
chat_data = await get_settings(msg.chat.id, session)
if not chat_data:
new_chat_data = ChatSettings(chat_id=msg.chat.id)
async with session() as session:
async with session.begin():
await session.merge(new_chat_data)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 KiB

After

Width:  |  Height:  |  Size: 398 KiB

View File

@ -1,6 +1,7 @@
SQLAlchemy~=2.0.20 SQLAlchemy~=2.0.20
alembic~=1.12.0 alembic~=1.12.0
nextcord~=2.6.0 nextcord~=2.6.0
g4f~=0.1.9.0 gpt4all~=2.0.2
gpt4all~=1.0.8
aiogram~=3.2.0 aiogram~=3.2.0
varname==0.12.2
g4f~=0.1.9.1

3
run.py
View File

@ -1,10 +1,11 @@
import os
import logging import logging
import gpt4all
from bozenka import launch_instances from bozenka import launch_instances
if __name__ == "__main__": if __name__ == "__main__":
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
os.system("pip install -r requirements.txt")
logging.log(msg="Starting bozenka, lets go!", level=logging.INFO) logging.log(msg="Starting bozenka, lets go!", level=logging.INFO)
try: try:
launch_instances() launch_instances()