mirror of
https://gitflic.ru/project/maks1ms/ocab.git
synced 2025-04-19 16:03:44 +03:00
wip
This commit is contained in:
parent
d624ed4a1b
commit
c53f6025ae
@ -21,16 +21,7 @@ def setup_logger():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def log(message):
|
def log(message):
|
||||||
"""
|
|
||||||
Функция для логирования сообщений
|
|
||||||
|
|
||||||
Она асинхронная, хотя таковой на самом деле не является.
|
|
||||||
"""
|
|
||||||
log_new(message)
|
|
||||||
|
|
||||||
|
|
||||||
def log_new(message):
|
|
||||||
if isinstance(message, Exception):
|
if isinstance(message, Exception):
|
||||||
error_message = f"Error: {str(message)}\n{traceback.format_exc()}"
|
error_message = f"Error: {str(message)}\n{traceback.format_exc()}"
|
||||||
logging.error(error_message)
|
logging.error(error_message)
|
||||||
|
@ -3,7 +3,7 @@ import traceback
|
|||||||
|
|
||||||
from aiogram import Bot, Dispatcher
|
from aiogram import Bot, Dispatcher
|
||||||
|
|
||||||
from ocab_core.logger import setup_logger
|
from ocab_core.logger import log, setup_logger
|
||||||
from ocab_core.modules_system import ModulesManager
|
from ocab_core.modules_system import ModulesManager
|
||||||
from ocab_core.modules_system.loaders import FSLoader
|
from ocab_core.modules_system.loaders import FSLoader
|
||||||
from ocab_core.modules_system.loaders.unsafe_fs_loader import UnsafeFSLoader
|
from ocab_core.modules_system.loaders.unsafe_fs_loader import UnsafeFSLoader
|
||||||
@ -14,6 +14,7 @@ from service import paths
|
|||||||
bot_modules = [
|
bot_modules = [
|
||||||
UnsafeFSLoader(f"{paths.modules_standard}/config"),
|
UnsafeFSLoader(f"{paths.modules_standard}/config"),
|
||||||
UnsafeFSLoader(f"{paths.modules_standard}/database"),
|
UnsafeFSLoader(f"{paths.modules_standard}/database"),
|
||||||
|
UnsafeFSLoader(f"{paths.modules_standard}/fsm_database_storage"),
|
||||||
UnsafeFSLoader(f"{paths.modules_standard}/roles"),
|
UnsafeFSLoader(f"{paths.modules_standard}/roles"),
|
||||||
UnsafeFSLoader(f"{paths.modules_external}/yandexgpt"),
|
UnsafeFSLoader(f"{paths.modules_external}/yandexgpt"),
|
||||||
FSLoader(f"{paths.modules_standard}/command_helper"),
|
FSLoader(f"{paths.modules_standard}/command_helper"),
|
||||||
@ -24,6 +25,11 @@ bot_modules = [
|
|||||||
FSLoader(f"{paths.modules_standard}/message_processing"),
|
FSLoader(f"{paths.modules_standard}/message_processing"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# import logging
|
||||||
|
# logger = logging.getLogger('peewee')
|
||||||
|
# logger.addHandler(logging.StreamHandler())
|
||||||
|
# logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
bot = None
|
bot = None
|
||||||
@ -33,13 +39,20 @@ async def main():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
app.bot = Bot(token=get_telegram_token())
|
app.bot = Bot(token=get_telegram_token())
|
||||||
|
|
||||||
app.dp = Dispatcher()
|
|
||||||
app.modules_manager = ModulesManager()
|
app.modules_manager = ModulesManager()
|
||||||
|
|
||||||
for module_loader in bot_modules:
|
for module_loader in bot_modules:
|
||||||
|
info = module_loader.info()
|
||||||
|
log(f"Loading {info.name}({info.id}) module")
|
||||||
await app.modules_manager.load(module_loader)
|
await app.modules_manager.load(module_loader)
|
||||||
|
|
||||||
|
app.dp = Dispatcher(storage=app.storage["_fsm_storage"])
|
||||||
|
|
||||||
|
app.dp.include_routers(*app.storage["_routers"])
|
||||||
|
|
||||||
|
for middleware in app.storage["_outer_message_middlewares"]:
|
||||||
|
app.dp.message.outer_middleware.register(middleware)
|
||||||
|
|
||||||
await app.modules_manager.late_init()
|
await app.modules_manager.late_init()
|
||||||
await app.dp.start_polling(app.bot)
|
await app.dp.start_polling(app.bot)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -5,20 +5,18 @@ from aiogram import BaseMiddleware, Router
|
|||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
from aiogram.fsm.storage.base import StorageKey
|
from aiogram.fsm.storage.base import StorageKey
|
||||||
|
|
||||||
from ocab_core.logger import log_new
|
from ocab_core.logger import log
|
||||||
from ocab_core.singleton import Singleton
|
from ocab_core.singleton import Singleton
|
||||||
|
|
||||||
log = log_new
|
|
||||||
|
|
||||||
|
|
||||||
def register_router(router: Router):
|
def register_router(router: Router):
|
||||||
app = Singleton()
|
app = Singleton()
|
||||||
app.dp.include_router(router)
|
app.storage["_routers"].append(router)
|
||||||
|
|
||||||
|
|
||||||
def register_outer_message_middleware(middleware: BaseMiddleware):
|
def register_outer_message_middleware(middleware: BaseMiddleware):
|
||||||
app = Singleton()
|
app = Singleton()
|
||||||
app.dp.message.outer_middleware.register(middleware)
|
app.storage["_outer_message_middlewares"].append(middleware)
|
||||||
|
|
||||||
|
|
||||||
async def set_my_commands(commands):
|
async def set_my_commands(commands):
|
||||||
@ -40,6 +38,12 @@ async def get_fsm_context(chat_id: int, user_id: int) -> FSMContext:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def set_fsm(storage):
|
||||||
|
app = Singleton()
|
||||||
|
log(storage)
|
||||||
|
app.storage["_fsm_storage"] = storage
|
||||||
|
|
||||||
|
|
||||||
def get_module(
|
def get_module(
|
||||||
module_id: str, paths=None
|
module_id: str, paths=None
|
||||||
) -> Union[types.ModuleType, Union[Any, None], Tuple[Union[Any, None], ...]]:
|
) -> Union[types.ModuleType, Union[Any, None], Tuple[Union[Any, None], ...]]:
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from aiogram import Bot, Dispatcher
|
from aiogram import Bot, Dispatcher
|
||||||
|
from aiogram.fsm.storage.memory import MemoryStorage
|
||||||
|
|
||||||
from ocab_core.modules_system import ModulesManager
|
from ocab_core.modules_system import ModulesManager
|
||||||
|
|
||||||
@ -17,4 +18,8 @@ class Singleton(metaclass=SingletonMeta):
|
|||||||
bot: Bot
|
bot: Bot
|
||||||
dp: Dispatcher = None
|
dp: Dispatcher = None
|
||||||
modules_manager: ModulesManager = None
|
modules_manager: ModulesManager = None
|
||||||
storage = dict()
|
storage = {
|
||||||
|
"_fsm_storage": MemoryStorage(),
|
||||||
|
"_routers": [],
|
||||||
|
"_outer_message_middlewares": [],
|
||||||
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
import asyncio
|
|
||||||
|
|
||||||
from aiogram import Bot
|
from aiogram import Bot
|
||||||
from aiogram.types import Message
|
from aiogram.types import Message
|
||||||
|
|
||||||
@ -15,7 +13,7 @@ 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):
|
||||||
# print("answer_to_message")
|
# print("answer_to_message")
|
||||||
await log("answer_to_message")
|
log("answer_to_message")
|
||||||
yagpt = YandexGPT(get_yandexgpt_token(), get_yandexgpt_catalog_id())
|
yagpt = YandexGPT(get_yandexgpt_token(), get_yandexgpt_catalog_id())
|
||||||
text = message.text
|
text = message.text
|
||||||
prompt = get_yandexgpt_prompt()
|
prompt = get_yandexgpt_prompt()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
w "id": "external.yandexgpt",
|
"id": "external.yandexgpt",
|
||||||
"name": "Yandex GPT",
|
"name": "Yandex GPT",
|
||||||
"description": "Модуль для работы с Yandex GPT",
|
"description": "Модуль для работы с Yandex GPT",
|
||||||
"author": "OCAB Team",
|
"author": "OCAB Team",
|
||||||
|
@ -57,7 +57,7 @@ class YandexGPT:
|
|||||||
)
|
)
|
||||||
except Exception as e: # TODO: Переделать обработку ошибок
|
except Exception as e: # TODO: Переделать обработку ошибок
|
||||||
# print(e)
|
# print(e)
|
||||||
await log(f"Error: {e}")
|
log(f"Error: {e}")
|
||||||
|
|
||||||
continue
|
continue
|
||||||
if int(len(response["tokens"])) < (max_tokens - answer_token):
|
if int(len(response["tokens"])) < (max_tokens - answer_token):
|
||||||
@ -262,7 +262,7 @@ class YandexGPT:
|
|||||||
)
|
)
|
||||||
elif type == "yandexgpt":
|
elif type == "yandexgpt":
|
||||||
# print("yandexgpt_request")
|
# print("yandexgpt_request")
|
||||||
await log("yandexgpt_request")
|
log("yandexgpt_request")
|
||||||
messages = await self.collect_messages(message_id, chat_id)
|
messages = await self.collect_messages(message_id, chat_id)
|
||||||
return await self.async_yandexgpt(
|
return await self.async_yandexgpt(
|
||||||
system_prompt=get_yandexgpt_prompt(),
|
system_prompt=get_yandexgpt_prompt(),
|
||||||
|
@ -5,7 +5,6 @@ from aiogram.types import BotCommand, Message, TelegramObject
|
|||||||
|
|
||||||
from ocab_core.modules_system.public_api import (
|
from ocab_core.modules_system.public_api import (
|
||||||
get_module,
|
get_module,
|
||||||
log,
|
|
||||||
register_outer_message_middleware,
|
register_outer_message_middleware,
|
||||||
set_my_commands,
|
set_my_commands,
|
||||||
)
|
)
|
||||||
@ -94,5 +93,4 @@ async def set_user_commands():
|
|||||||
|
|
||||||
|
|
||||||
async def module_late_init():
|
async def module_late_init():
|
||||||
await log("module_late_init")
|
|
||||||
await set_user_commands()
|
await set_user_commands()
|
||||||
|
@ -30,7 +30,7 @@ async def start_report(chat_id: int, bot: Bot):
|
|||||||
@router.message(ReportState.input_kernel_info)
|
@router.message(ReportState.input_kernel_info)
|
||||||
async def kernel_version_entered(message: Message, state: FSMContext):
|
async def kernel_version_entered(message: Message, state: FSMContext):
|
||||||
await state.update_data(kernel=message.text)
|
await state.update_data(kernel=message.text)
|
||||||
await message.answer(text="В каком приложении " "возникла проблема?")
|
await message.answer(text="В каком приложении возникла проблема?")
|
||||||
await state.set_state(ReportState.input_app_name)
|
await state.set_state(ReportState.input_app_name)
|
||||||
|
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ async def kernel_version_entered(message: Message, state: FSMContext):
|
|||||||
async def app_name_entered(message: Message, state: FSMContext):
|
async def app_name_entered(message: Message, state: FSMContext):
|
||||||
await state.update_data(app_name=message.text)
|
await state.update_data(app_name=message.text)
|
||||||
await message.answer(
|
await message.answer(
|
||||||
text="Опиши проблему пошагово, " "что ты делал, что происходило, что не так"
|
text="Опиши проблему пошагово, что ты делал, что происходило, что не так"
|
||||||
)
|
)
|
||||||
await state.set_state(ReportState.input_problem_step_by_step)
|
await state.set_state(ReportState.input_problem_step_by_step)
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ async def app_name_entered(message: Message, state: FSMContext):
|
|||||||
@router.message(ReportState.input_problem_step_by_step)
|
@router.message(ReportState.input_problem_step_by_step)
|
||||||
async def problem_step_by_step_entered(message: Message, state: FSMContext):
|
async def problem_step_by_step_entered(message: Message, state: FSMContext):
|
||||||
await state.update_data(problem_step_by_step=message.text)
|
await state.update_data(problem_step_by_step=message.text)
|
||||||
await message.answer(text="Вот твой отчет сообщением, " "а также файлом:")
|
await message.answer(text="Вот твой отчет сообщением, а также файлом:")
|
||||||
data = await state.get_data()
|
data = await state.get_data()
|
||||||
|
|
||||||
report = f"""Стенд с ошибкой:
|
report = f"""Стенд с ошибкой:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from . import db_api, models
|
from . import db_api, models, repositories
|
||||||
|
|
||||||
|
|
||||||
async def module_init():
|
async def module_init():
|
||||||
|
@ -7,6 +7,7 @@ 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.db import database_proxy
|
||||||
|
from .models.fsm_data import FSMData
|
||||||
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
|
||||||
@ -32,7 +33,7 @@ def connect_database(is_test: bool = False, module: str | None = None):
|
|||||||
|
|
||||||
def create_tables(db: pw.SqliteDatabase):
|
def create_tables(db: pw.SqliteDatabase):
|
||||||
"""Создание таблиц"""
|
"""Создание таблиц"""
|
||||||
for table in Chats, Messages, Users, UserStats, ChatStats:
|
for table in Chats, Messages, Users, UserStats, ChatStats, FSMData:
|
||||||
if not table.table_exists():
|
if not table.table_exists():
|
||||||
db.create_tables([table])
|
db.create_tables([table])
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
from .fsm_data import FSMData
|
12
src/ocab_modules/standard/database/models/fsm_data.py
Normal file
12
src/ocab_modules/standard/database/models/fsm_data.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import peewee as pw
|
||||||
|
|
||||||
|
from .db import database_proxy
|
||||||
|
|
||||||
|
|
||||||
|
class FSMData(pw.Model):
|
||||||
|
class Meta:
|
||||||
|
database = database_proxy
|
||||||
|
|
||||||
|
key = pw.CharField(primary_key=True)
|
||||||
|
state = pw.CharField(null=True)
|
||||||
|
data = pw.CharField(null=True)
|
@ -0,0 +1 @@
|
|||||||
|
from .fsm_data import FSMDataRepository
|
32
src/ocab_modules/standard/database/repositories/fsm_data.py
Normal file
32
src/ocab_modules/standard/database/repositories/fsm_data.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from peewee import fn
|
||||||
|
|
||||||
|
from ..models import FSMData
|
||||||
|
|
||||||
|
|
||||||
|
class FSMDataRepository:
|
||||||
|
def get(self, key: str):
|
||||||
|
return FSMData.get_or_none(key=key)
|
||||||
|
|
||||||
|
def set_state(self, key: str, state: str):
|
||||||
|
FSMData.insert(
|
||||||
|
key=key,
|
||||||
|
state=state,
|
||||||
|
data=fn.COALESCE(
|
||||||
|
FSMData.select(FSMData.data).where(FSMData.key == key), None
|
||||||
|
),
|
||||||
|
).on_conflict(
|
||||||
|
conflict_target=[FSMData.key],
|
||||||
|
update={FSMData.state: state},
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
def set_data(self, key: str, data: str):
|
||||||
|
FSMData.insert(
|
||||||
|
key=key,
|
||||||
|
data=data,
|
||||||
|
state=fn.COALESCE(
|
||||||
|
FSMData.select(FSMData.state).where(FSMData.key == key), None
|
||||||
|
),
|
||||||
|
).on_conflict(
|
||||||
|
conflict_target=[FSMData.key],
|
||||||
|
update={FSMData.data: data},
|
||||||
|
).execute()
|
@ -0,0 +1 @@
|
|||||||
|
from .fsm import module_init
|
122
src/ocab_modules/standard/fsm_database_storage/fsm.py
Normal file
122
src/ocab_modules/standard/fsm_database_storage/fsm.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import json
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
from aiogram.fsm.state import State
|
||||||
|
from aiogram.fsm.storage.base import BaseStorage, StorageKey
|
||||||
|
|
||||||
|
from ocab_core.modules_system.public_api import get_module, log
|
||||||
|
from ocab_core.modules_system.public_api.public_api import set_fsm
|
||||||
|
|
||||||
|
FSMDataRepository = get_module("standard.database", "repositories.FSMDataRepository")
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_key(key: StorageKey) -> str:
|
||||||
|
return f"{key.bot_id}:{key.chat_id}:{key.user_id}"
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_object(obj: object) -> str | None:
|
||||||
|
try:
|
||||||
|
return json.dumps(obj)
|
||||||
|
except Exception as e:
|
||||||
|
log(f"Serializing error! {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_object(obj):
|
||||||
|
try:
|
||||||
|
return json.loads(obj)
|
||||||
|
except Exception as e:
|
||||||
|
log(f"Deserializing error! {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class SQLStorage(BaseStorage):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.repo = FSMDataRepository()
|
||||||
|
|
||||||
|
async def set_state(self, key: StorageKey, state: State | None = None) -> None:
|
||||||
|
"""
|
||||||
|
Set state for specified key
|
||||||
|
|
||||||
|
:param key: storage key
|
||||||
|
:param state: new state
|
||||||
|
"""
|
||||||
|
s_key = serialize_key(key)
|
||||||
|
s_state = state.state if isinstance(state, State) else state
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.repo.set_state(s_key, s_state)
|
||||||
|
except Exception as e:
|
||||||
|
log(f"FSM Storage database error: {e}")
|
||||||
|
|
||||||
|
async def get_state(self, key: StorageKey) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
Get key state
|
||||||
|
|
||||||
|
:param key: storage key
|
||||||
|
:return: current state
|
||||||
|
"""
|
||||||
|
s_key = serialize_key(key)
|
||||||
|
|
||||||
|
try:
|
||||||
|
s_state = self.repo.get(s_key)
|
||||||
|
return s_state.state if s_state else None
|
||||||
|
except Exception as e:
|
||||||
|
log(f"FSM Storage database error: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def set_data(self, key: StorageKey, data: Dict[str, Any]) -> None:
|
||||||
|
"""
|
||||||
|
Write data (replace)
|
||||||
|
|
||||||
|
:param key: storage key
|
||||||
|
:param data: new data
|
||||||
|
"""
|
||||||
|
s_key = serialize_key(key)
|
||||||
|
s_data = serialize_object(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.repo.set_data(s_key, s_data)
|
||||||
|
except Exception as e:
|
||||||
|
log(f"FSM Storage database error: {e}")
|
||||||
|
|
||||||
|
async def get_data(self, key: StorageKey) -> Optional[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Get current data for key
|
||||||
|
|
||||||
|
:param key: storage key
|
||||||
|
:return: current data
|
||||||
|
"""
|
||||||
|
s_key = serialize_key(key)
|
||||||
|
|
||||||
|
try:
|
||||||
|
s_data = self.repo.get(s_key)
|
||||||
|
return deserialize_object(s_data.data) if s_data else None
|
||||||
|
except Exception as e:
|
||||||
|
log(f"FSM Storage database error: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def update_data(
|
||||||
|
self, key: StorageKey, data: Dict[str, Any]
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Update data in the storage for key (like dict.update)
|
||||||
|
|
||||||
|
:param key: storage key
|
||||||
|
:param data: partial data
|
||||||
|
:return: new data
|
||||||
|
"""
|
||||||
|
current_data = await self.get_data(key=key)
|
||||||
|
if not current_data:
|
||||||
|
current_data = {}
|
||||||
|
current_data.update(data)
|
||||||
|
await self.set_data(key=key, data=current_data)
|
||||||
|
return current_data.copy()
|
||||||
|
|
||||||
|
async def close(self) -> None: # pragma: no cover
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
async def module_init():
|
||||||
|
set_fsm(SQLStorage())
|
11
src/ocab_modules/standard/fsm_database_storage/info.json
Normal file
11
src/ocab_modules/standard/fsm_database_storage/info.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"id": "standard.fsm_database_storage",
|
||||||
|
"name": "FSM Database Storage",
|
||||||
|
"description": "Очень полезный модуль",
|
||||||
|
"author": "OCAB Team",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"privileged": false,
|
||||||
|
"dependencies": {
|
||||||
|
"standard.database": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
@ -32,7 +32,7 @@ async def get_info_answer_by_id(message: Message, bot: Bot, user_id: int):
|
|||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
await message.reply("Пользователь не найден")
|
await message.reply("Пользователь не найден")
|
||||||
await log(f"Пользователь не найден: {user_id}, {user}")
|
log(f"Пользователь не найден: {user_id}, {user}")
|
||||||
return
|
return
|
||||||
|
|
||||||
roles = Roles()
|
roles = Roles()
|
||||||
@ -69,7 +69,7 @@ async def get_user_info(message: Message, bot: Bot):
|
|||||||
" попробуйте запросить информацию о пользователе по его тегу или ответив на его сообщение"
|
" попробуйте запросить информацию о пользователе по его тегу или ответив на его сообщение"
|
||||||
)
|
)
|
||||||
# print(e)
|
# print(e)
|
||||||
await log(e)
|
log(e)
|
||||||
|
|
||||||
|
|
||||||
async def get_chat_info(message: Message, bot: Bot):
|
async def get_chat_info(message: Message, bot: Bot):
|
||||||
|
Loading…
Reference in New Issue
Block a user