0
0
mirror of https://gitflic.ru/project/maks1ms/ocab.git synced 2025-03-13 22:03:50 +03:00
This commit is contained in:
Maxim Slipenko 2024-07-10 11:28:42 +03:00
parent f9f6eaad0d
commit 3295d4acec
81 changed files with 261 additions and 163 deletions

4
.gitignore vendored
View File

@ -7,5 +7,5 @@ venv
__pycache__ __pycache__
OCAB.db OCAB.db
src/paths.json src/paths.json
src/core/config.yaml src/ocab_core/config.yaml
src/core/log/**/* src/ocab_core/log/**/*

3
docs/MODULES-SPEC.md Normal file
View File

@ -0,0 +1,3 @@
# Спецификация модулей
Каждый модуль - это Py

View File

@ -4,9 +4,9 @@ from pathlib import Path
def main(): def main():
pwd = Path().cwd() pwd = Path().cwd()
dir_core = pwd / "src" / "core" dir_core = pwd / "src" / "ocab_core"
dir_modules_standard = pwd / "src" / "modules" / "standard" dir_modules_standard = pwd / "src" / "ocab_modules" / "standard"
dir_modules_custom = pwd / "src" / "modules" / "custom" dir_modules_custom = pwd / "src" / "ocab_modules" / "custom"
json = { json = {
"core": str(dir_core), "core": str(dir_core),

View File

@ -1,2 +1,2 @@
import src.core import ocab_core
import src.service import service

View File

@ -1 +0,0 @@
from .public_api import get_module, register_router

View File

@ -1,13 +0,0 @@
from aiogram import Router
from src.core.singleton import Singleton
def register_router(router: Router):
app = Singleton()
app.dp.include_router(router)
def get_module(module_id: str):
app = Singleton()
return app.modules_manager.get_by_id(module_id)

View File

@ -1 +0,0 @@
from . import db_api, models

View File

@ -1,12 +0,0 @@
from aiogram import F, Router
from src.core.modules_system.public_api import register_router
from .handlers import get_chat_info, get_user_info
router = Router()
router.message.register(get_user_info, F.text.startswith("/info"))
router.message.register(get_chat_info, F.text.startswith("/chatinfo"))
register_router(router)

View File

@ -1,20 +0,0 @@
class IRoles:
user: str
moderator: str
admin: str
bot: str
def __init__(self):
pass
async def check_admin_permission(self, user_id: int) -> bool:
pass
async def check_moderator_permission(self, user_id) -> bool:
pass
async def get_role_name(self, role_id) -> str:
pass
async def get_user_permission(self, user_id) -> str | None:
pass

View File

@ -3,14 +3,13 @@ import traceback
from aiogram import Bot, Dispatcher from aiogram import Bot, Dispatcher
from src.core.logger import setup_logger from ocab_core.logger import setup_logger
from src.core.modules_system import ModulesManager from ocab_core.modules_system import ModulesManager
from src.core.modules_system.loaders import FSLoader from ocab_core.modules_system.loaders import FSLoader
from src.core.modules_system.loaders.unsafe_fs_loader import UnsafeFSLoader from ocab_core.modules_system.loaders.unsafe_fs_loader import UnsafeFSLoader
from src.core.singleton import Singleton from ocab_core.singleton import Singleton
from src.modules.standard.config.config import get_telegram_token from ocab_modules.standard.config.config import get_telegram_token
from src.modules.standard.database.db_api import connect_database, create_tables from service import paths
from src.service import paths
bot_modules = [ bot_modules = [
UnsafeFSLoader(f"{paths.modules_standard}/config"), UnsafeFSLoader(f"{paths.modules_standard}/config"),
@ -29,9 +28,6 @@ async def main():
try: try:
bot = Bot(token=get_telegram_token()) bot = Bot(token=get_telegram_token())
database, path = connect_database()
database.connect()
create_tables(database)
app.dp = Dispatcher() app.dp = Dispatcher()
app.modules_manager = ModulesManager() app.modules_manager = ModulesManager()

View File

@ -3,12 +3,12 @@ from pathlib import Path
from RestrictedPython import compile_restricted_exec from RestrictedPython import compile_restricted_exec
from src.core.modules_system.loaders.fs_loader.policy import ( from ocab_core.modules_system.loaders.fs_loader.policy import (
ALLOWED_IMPORTS, ALLOWED_IMPORTS,
BUILTINS, BUILTINS,
RestrictedPythonPolicy, RestrictedPythonPolicy,
) )
from src.core.modules_system.loaders.unsafe_fs_loader import UnsafeFSLoader from ocab_core.modules_system.loaders.unsafe_fs_loader import UnsafeFSLoader
class FSLoader(UnsafeFSLoader): class FSLoader(UnsafeFSLoader):
@ -48,11 +48,12 @@ class FSLoader(UnsafeFSLoader):
return file_path return file_path
def _hook_import(self, name: str, *args, **kwargs): def _hook_import(self, name: str, *args, **kwargs):
if name in ALLOWED_IMPORTS: for allowed in ALLOWED_IMPORTS:
return ALLOWED_IMPORTS[name] if name == allowed or name.startswith(f"{allowed}."):
return __import__(name, *args, **kwargs)
# TODO: allow only public api for modules # TODO: allow only public api for modules
if name.startswith("src."): if name.startswith("ocab_core."):
return __import__(name, *args, **kwargs) return __import__(name, *args, **kwargs)
module_file_path = self._resolve_module_from_path(name) module_file_path = self._resolve_module_from_path(name)

View File

@ -1,9 +1,6 @@
import typing
import warnings
from _ast import AnnAssign from _ast import AnnAssign
from typing import Any from typing import Any
import aiogram
from RestrictedPython import ( from RestrictedPython import (
RestrictingNodeTransformer, RestrictingNodeTransformer,
limited_builtins, limited_builtins,
@ -11,7 +8,6 @@ from RestrictedPython import (
utility_builtins, utility_builtins,
) )
from RestrictedPython.Eval import default_guarded_getitem from RestrictedPython.Eval import default_guarded_getitem
from RestrictedPython.Guards import full_write_guard
class RestrictedPythonPolicy(RestrictingNodeTransformer): class RestrictedPythonPolicy(RestrictingNodeTransformer):
@ -75,18 +71,19 @@ def _metaclass(name, bases, dict):
return ob return ob
ALLOWED_IMPORTS = { ALLOWED_IMPORTS = [
"typing": typing, "typing",
"aiogram": aiogram, "aiogram",
"warnings": warnings, "warnings",
} ]
BUILTINS = safe_builtins.copy() BUILTINS = safe_builtins.copy()
BUILTINS.update(utility_builtins) BUILTINS.update(utility_builtins)
BUILTINS.update(limited_builtins) BUILTINS.update(limited_builtins)
BUILTINS["__metaclass__"] = _metaclass BUILTINS["__metaclass__"] = _metaclass
BUILTINS["_getitem_"] = default_guarded_getitem BUILTINS["_getitem_"] = default_guarded_getitem
BUILTINS["_write_"] = full_write_guard # BUILTINS["_write_"] = full_write_guard
BUILTINS["staticmethod"] = staticmethod
class GuardedDictType: class GuardedDictType:

View File

@ -3,7 +3,7 @@ import os
import sys import sys
from pathlib import Path from pathlib import Path
from src.core.modules_system.loaders.base import AbstractLoader, ModuleInfo from ocab_core.modules_system.loaders.base import AbstractLoader, ModuleInfo
class UnsafeFSLoader(AbstractLoader): class UnsafeFSLoader(AbstractLoader):

View File

@ -1,4 +1,4 @@
from src.core.modules_system.loaders.base import AbstractLoader from ocab_core.modules_system.loaders.base import AbstractLoader
class ModulesManager: class ModulesManager:
@ -9,13 +9,14 @@ class ModulesManager:
info = loader.info() info = loader.info()
module = loader.load() module = loader.load()
print(module)
self.modules[info.id] = { self.modules[info.id] = {
"info": info, "info": info,
"module": module, "module": module,
} }
if hasattr(module, "module_init"):
module.module_init()
def get_by_id(self, module_id: str): def get_by_id(self, module_id: str):
if module_id not in self.modules: if module_id not in self.modules:
raise Exception(f"Module with id {module_id} not loaded") raise Exception(f"Module with id {module_id} not loaded")

View File

@ -0,0 +1 @@
from .public_api import Storage, get_module, register_router

View File

@ -0,0 +1,54 @@
import types
from typing import Any, Tuple, Union
from aiogram import Router
from ocab_core.singleton import Singleton
def register_router(router: Router):
app = Singleton()
app.dp.include_router(router)
def get_module(
module_id: str, paths=None
) -> Union[types.ModuleType, Union[Any, None], Tuple[Union[Any, None], ...]]:
app = Singleton()
module = app.modules_manager.get_by_id(module_id)
if paths is None:
return module
if isinstance(paths, str):
paths = [paths]
results = []
for path in paths:
current_obj = module
try:
parts = path.split(".")
for part in parts:
current_obj = getattr(current_obj, part)
results.append(current_obj)
except AttributeError:
results.append(None)
if len(results) == 1:
return results[0]
else:
return tuple(results)
class Storage:
@staticmethod
def set(key: str, value: Any):
storage = Singleton().storage
storage[key] = value
@staticmethod
def get(key: str):
storage = Singleton().storage
return storage.get(key)

View File

@ -1,9 +1,9 @@
from aiogram import Dispatcher from aiogram import Dispatcher
from src.modules.standard.admin.routers import router as admin_router from src.ocab_modules.standard.admin.routers import router as admin_router
# from src.modules.standard.info.routers import router as info_router # from src.modules.standard.info.routers import router as info_router
from src.modules.standard.message_processing.message_api import ( from src.ocab_modules.standard.message_processing.message_api import (
router as process_message, router as process_message,
) )

View File

@ -1,6 +1,6 @@
from aiogram import Dispatcher from aiogram import Dispatcher
from src.core.modules_system import ModulesManager from ocab_core.modules_system import ModulesManager
class SingletonMeta(type): class SingletonMeta(type):
@ -16,3 +16,4 @@ class SingletonMeta(type):
class Singleton(metaclass=SingletonMeta): class Singleton(metaclass=SingletonMeta):
dp: Dispatcher = None dp: Dispatcher = None
modules_manager: ModulesManager = None modules_manager: ModulesManager = None
storage = dict()

View File

@ -4,14 +4,13 @@ import asyncio
from aiogram import Bot from aiogram import Bot
from aiogram.types import Message from aiogram.types import Message
from src.core.logger import log from ocab_modules.external.yandexgpt.yandexgpt import *
from src.modules.external.yandexgpt.yandexgpt import * from ocab_modules.standard.config.config import (
from src.modules.standard.config.config import (
get_yandexgpt_catalog_id, get_yandexgpt_catalog_id,
get_yandexgpt_prompt, get_yandexgpt_prompt,
get_yandexgpt_token, get_yandexgpt_token,
) )
from src.modules.standard.database.db_api import add_message from ocab_modules.standard.database.db_api import add_message
async def answer_to_message(message: Message, bot: Bot): async def answer_to_message(message: Message, bot: Bot):

View File

@ -1,7 +1,7 @@
# flake8: noqa # flake8: noqa
from aiogram import F, Router from aiogram import F, Router
from src.modules.external.yandexgpt.handlers import answer_to_message from src.ocab_modules.external.yandexgpt.handlers import answer_to_message
router = Router() router = Router()
# Если сообщение содержит в начале текст "Гномик" или "гномик" или отвечает на сообщение бота, то вызывается функция answer_to_message # Если сообщение содержит в начале текст "Гномик" или "гномик" или отвечает на сообщение бота, то вызывается функция answer_to_message

View File

@ -5,7 +5,7 @@ import json
import aiohttp import aiohttp
import requests import requests
from src.core.logger import log from ocab_core.logger import log
from ...standard.config.config import * from ...standard.config.config import *
from ...standard.database import * from ...standard.database import *

View File

@ -4,7 +4,7 @@ import time
from aiogram import Bot from aiogram import Bot
from aiogram.types import Message from aiogram.types import Message
from src.modules.standard.config.config import get_default_chat_tag from src.ocab_modules.standard.config.config import get_default_chat_tag
async def delete_message(message: Message, bot: Bot): async def delete_message(message: Message, bot: Bot):

View File

@ -1,13 +1,13 @@
# flake8: noqa # flake8: noqa
from aiogram import F, Router from aiogram import F, Router
from src.modules.standard.admin.handlers import ( from src.ocab_modules.standard.admin.handlers import (
chat_not_in_approve_list, chat_not_in_approve_list,
delete_message, delete_message,
error_access, error_access,
get_chat_id, get_chat_id,
) )
from src.modules.standard.filters.filters import ( from src.ocab_modules.standard.filters.filters import (
ChatModerOrAdminFilter, ChatModerOrAdminFilter,
ChatNotInApproveFilter, ChatNotInApproveFilter,
) )

View File

@ -1,6 +1,6 @@
import unittest import unittest
from src.modules.standard.config.config import get_config from src.ocab_modules.standard.config.config import get_config
yaml_load = get_config(is_test=True) yaml_load = get_config(is_test=True)

View File

@ -0,0 +1,5 @@
from . import db_api, models
def module_init():
db_api.connect_database()

View File

@ -6,6 +6,7 @@ from src.service import paths
from .exceptions import MissingModuleName, NotExpectedModuleName from .exceptions import MissingModuleName, NotExpectedModuleName
from .models.chat_stats import ChatStats from .models.chat_stats import ChatStats
from .models.chats import Chats from .models.chats import Chats
from .models.db import database_proxy
from .models.messages import Messages from .models.messages import Messages
from .models.user_stats import UserStats from .models.user_stats import UserStats
from .models.users import Users from .models.users import Users
@ -21,15 +22,12 @@ def connect_database(is_test: bool = False, module: str | None = None):
raise NotExpectedModuleName() raise NotExpectedModuleName()
db_path = f"{paths.core}/database" db_path = f"{paths.core}/database"
# WTF????? database = pw.SqliteDatabase(f"{db_path}/OCAB.db")
_database = pw.SqliteDatabase(f"{db_path}/OCAB.db") database_proxy.initialize(database)
Chats._meta.database = _database database.connect()
Messages._meta.database = _database create_tables(database)
Users._meta.database = _database
UserStats._meta.database = _database
ChatStats._meta.database = _database
return _database, f"{db_path}/OCAB.db" return database, f"{db_path}/OCAB.db"
def create_tables(db: pw.SqliteDatabase): def create_tables(db: pw.SqliteDatabase):
@ -140,7 +138,7 @@ def get_chat_all_stat(chat_id):
# Работа с таблицей пользователей # Работа с таблицей пользователей
def get_user(user_id): def get_user(user_id) -> Users | None:
return Users.get_or_none(Users.id == user_id) return Users.get_or_none(Users.id == user_id)
@ -198,7 +196,8 @@ def change_user_role(user_id, new_user_role):
def get_message(message_chat_id, message_id): def get_message(message_chat_id, message_id):
return Messages.get_or_none( return Messages.get_or_none(
Messages.message_chat_id == message_chat_id, Messages.message_id == message_id Messages.message_chat_id == message_chat_id,
Messages.message_id == message_id,
) )

View File

@ -1,8 +1,11 @@
import peewee as pw import peewee as pw
from .db import database_proxy
class ChatStats(pw.Model): class ChatStats(pw.Model):
class Meta: ... class Meta:
database = database_proxy
chat_id = pw.IntegerField(null=False) chat_id = pw.IntegerField(null=False)
date = pw.DateField(null=False) date = pw.DateField(null=False)

View File

@ -1,8 +1,11 @@
import peewee as pw import peewee as pw
from .db import database_proxy
class Chats(pw.Model): class Chats(pw.Model):
class Meta: ... class Meta:
database = database_proxy
chat_name = pw.CharField(null=False) chat_name = pw.CharField(null=False)
chat_type = pw.IntegerField(null=False, default=10) chat_type = pw.IntegerField(null=False, default=10)

View File

@ -0,0 +1,3 @@
from peewee import DatabaseProxy
database_proxy = DatabaseProxy()

View File

@ -1,8 +1,11 @@
import peewee as pw import peewee as pw
from .db import database_proxy
class Messages(pw.Model): class Messages(pw.Model):
class Meta: ... class Meta:
database = database_proxy
message_chat_id = pw.IntegerField(null=False) message_chat_id = pw.IntegerField(null=False)
message_id = pw.IntegerField(null=False) message_id = pw.IntegerField(null=False)

View File

@ -1,8 +1,11 @@
import peewee as pw import peewee as pw
from .db import database_proxy
class UserStats(pw.Model): class UserStats(pw.Model):
class Meta: ... class Meta:
database = database_proxy
chat_id = pw.IntegerField(null=False) chat_id = pw.IntegerField(null=False)
user_id = pw.IntegerField(null=False) user_id = pw.IntegerField(null=False)

View File

@ -1,8 +1,11 @@
import peewee as pw import peewee as pw
from .db import database_proxy
class Users(pw.Model): class Users(pw.Model):
class Meta: ... class Meta:
database = database_proxy
user_tag = pw.CharField(null=True) user_tag = pw.CharField(null=True)
user_name = pw.CharField(null=False) # до 255 символов user_name = pw.CharField(null=False) # до 255 символов

View File

@ -2,9 +2,9 @@ from aiogram import Bot
from aiogram.filters import BaseFilter from aiogram.filters import BaseFilter
from aiogram.types import Message from aiogram.types import Message
from src.core.logger import log from ocab_core.logger import log
from src.modules.standard.config.config import get_aproved_chat_id from ocab_modules.standard.config.config import get_aproved_chat_id
from src.modules.standard.roles.roles import Roles from ocab_modules.standard.roles.roles import Roles
class ChatModerOrAdminFilter(BaseFilter): class ChatModerOrAdminFilter(BaseFilter):

View File

@ -0,0 +1,14 @@
from aiogram import F, Router
from ocab_core.modules_system.public_api import register_router
from .handlers import get_chat_info, get_user_info
def module_init():
router = Router()
router.message.register(get_user_info, F.text.startswith("/info"))
router.message.register(get_chat_info, F.text.startswith("/chatinfo"))
register_router(router)

View File

@ -1,60 +1,65 @@
# flake8: noqa # flake8: noqa
from typing import Type from typing import Any, Type
from aiogram import Bot from aiogram import Bot
from aiogram.types import Message
from src.core.logger import log from ocab_core.logger import log
from src.core.modules_system.public_api import get_module from ocab_core.modules_system.public_api import get_module
from src.modules.standard.database.db_api import (
Message, from .interfaces import IDbApi, IRoles
get_chat_all_stat,
get_message_ai_model, # from src.modules.standard.database import db_api
get_user,
get_user_all_stats,
get_user_id, db_api: Type[IDbApi] = get_module(
get_user_name, "standard.database",
get_user_rep, "db_api",
get_user_role,
get_user_tag,
) )
from .interfaces import IRoles # db_api.init_db_connection()
Roles: Type[IRoles] = get_module("standard.roles").Roles Roles: Type[IRoles] = get_module("standard.roles", "Roles")
async def get_info_answer_by_id(message: Message, bot: Bot, user_id: int): async def get_info_answer_by_id(message: Message, bot: Bot, user_id: int):
if get_message_ai_model(message.chat.id, message.message_id) is not None: ai_model = db_api.get_message_ai_model(message.chat.id, message.message_id)
if ai_model is not None:
await message.reply( await message.reply(
"Это сообщение было сгенерировано ботом используя модель: " "Это сообщение было сгенерировано ботом используя модель: " + ai_model
+ get_message_ai_model(message.chat.id, message.message_id)
) )
elif user_id == bot.id: return
if user_id == bot.id:
await message.reply("Это сообщение было отправлено ботом") await message.reply("Это сообщение было отправлено ботом")
elif get_user(user_id) is None: return
user = db_api.get_user(user_id)
if user is None:
await message.reply("Пользователь не найден") await message.reply("Пользователь не найден")
# print(get_user(user_id)) await log(f"Пользователь не найден: {user_id}, {user}")
await log(f"Пользователь не найден: {user_id}, {get_user(user_id)}") return
else:
roles = Roles() roles = Roles()
answer = ( answer = (
f"Пользователь: {get_user_name(user_id)}\n" f"Пользователь: {user.user_name}\n"
f"Роль: {await roles.get_role_name(role_id=get_user_role(user_id))}\n" f"Роль: {await roles.get_role_name(role_id=user.user_role)}\n"
f"Тег: @{get_user_tag(user_id)}\n" f"Тег: @{user.user_tag}\n"
f"Кол-во сообщений: {get_user_all_stats(user_id)}\n" f"Кол-во сообщений: {user.user_stats}\n"
f"Репутация: {get_user_rep(user_id)}" f"Репутация: {user.user_rep}"
) )
await message.reply(answer) await message.reply(answer)
async def get_user_info(message: Message, bot: Bot): async def get_user_info(message: Message, bot: Bot):
# Проверяем содержимое сообщения, если содержит вторым элементом тег пользователя, то выводим информацию о нем # Проверяем содержимое сообщения, если содержит вторым элементом тег пользователя, то выводим информацию о нем
# Если сообщение отвечает на другое сообщение, то выводим информацию о пользователе, на чье сообщение был ответ # Если сообщение отвечает на другое сообщение, то выводим информацию о пользователе, на чье сообщение был ответ
# Если это бот то выводим информацию что это бот и какая модель yandexgpt используется # Если это бот то выводим информацию, что это бот и какая модель yandexgpt используется
try: try:
if len(message.text.split()) > 1 and message.text.split()[1].startswith("@"): if len(message.text.split()) > 1 and message.text.split()[1].startswith("@"):
user_tag = message.text.split()[1][1:] user_tag = message.text.split()[1][1:]
user_id = get_user_id(user_tag) user_id = db_api.get_user_id(user_tag)
if user_id: if user_id:
await get_info_answer_by_id(message, bot, user_id) await get_info_answer_by_id(message, bot, user_id)
else: else:
@ -77,7 +82,7 @@ async def get_chat_info(message: Message, bot: Bot):
answer = ( answer = (
f"*Название чата:* {message.chat.title}\n" f"*Название чата:* {message.chat.title}\n"
f"*ID чата:* `{message.chat.id}`\n \n" f"*ID чата:* `{message.chat.id}`\n \n"
f"*Суммарное количество сообщений в чате:* {get_chat_all_stat(message.chat.id)}\n" f"*Суммарное количество сообщений в чате:* {db_api.get_chat_all_stat(message.chat.id)}\n"
f"*Количество пользователей в чате:* {await bot.get_chat_member_count(message.chat.id)}\n" f"*Количество пользователей в чате:* {await bot.get_chat_member_count(message.chat.id)}\n"
f"*Количество администраторов в чате:* {len(await bot.get_chat_administrators(message.chat.id))}" f"*Количество администраторов в чате:* {len(await bot.get_chat_administrators(message.chat.id))}"
) )

View File

@ -0,0 +1,47 @@
from typing import Any
class IRoles:
user: str
moderator: str
admin: str
bot: str
def __init__(self):
pass
async def check_admin_permission(self, user_id: int) -> bool:
pass
async def check_moderator_permission(self, user_id) -> bool:
pass
async def get_role_name(self, role_id) -> str:
pass
async def get_user_permission(self, user_id) -> str | None:
pass
class IUsers:
user_id: int
user_name: str
user_role: int
user_tag: str
user_stats: int
user_rep: int
class IDbApi:
@staticmethod
def get_message_ai_model(message_chat_id: Any, message_id: Any) -> Any | None:
pass
@staticmethod
def get_user(user_id: int) -> IUsers | None:
pass
@staticmethod
def get_chat_all_stat(chat_id: int) -> int:
pass

View File

@ -2,14 +2,14 @@
from aiogram import Bot, F, Router, types from aiogram import Bot, F, Router, types
from src.core.logger import log from ocab_core.logger import log
from src.modules.external.yandexgpt.handlers import answer_to_message from ocab_modules.external.yandexgpt.handlers import answer_to_message
from src.modules.standard.config.config import ( from ocab_modules.standard.config.config import (
get_aproved_chat_id, get_aproved_chat_id,
get_yandexgpt_in_words, get_yandexgpt_in_words,
get_yandexgpt_start_words, get_yandexgpt_start_words,
) )
from src.modules.standard.database.db_api import * from ocab_modules.standard.database.db_api import *
async def chat_check(message: types.Message): async def chat_check(message: types.Message):

View File

@ -1,7 +1,7 @@
from src.core.modules_system.public_api import get_module from ocab_core.modules_system.public_api import get_module
get_user_role = get_module("standard.database").db_api.get_user_role get_user_role = get_module("standard.database", "db_api.get_user_role")
config = get_module("standard.config").config config: dict = get_module("standard.config", "config")
class Roles: class Roles:

View File

@ -9,10 +9,14 @@ from aiogram.types import Message
from aiogram.types import inline_keyboard_button as types from aiogram.types import inline_keyboard_button as types
from aiogram.utils.keyboard import InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder
from src.modules.standard.config.config import get_telegram_check_bot from src.ocab_modules.standard.config.config import get_telegram_check_bot
from src.modules.standard.database.db_api import * from src.ocab_modules.standard.database.db_api import *
from src.modules.standard.moderation.moderation import ban_user, mute_user, unmute_user from src.ocab_modules.standard.moderation.moderation import (
from src.modules.standard.roles.roles import Roles ban_user,
mute_user,
unmute_user,
)
from src.ocab_modules.standard.roles.roles import Roles
async def create_math_task(): async def create_math_task():

View File

@ -1,6 +1,6 @@
from aiogram import F, Router from aiogram import F, Router
from src.modules.standard.welcome.handlers import check_new_user from src.ocab_modules.standard.welcome.handlers import check_new_user
router = Router() router = Router()