mirror of
https://gitflic.ru/project/alt-gnome/karkas.git
synced 2025-03-21 01:43:42 +03:00
Merge branch 'OCAB-Lite' into fix/welcome-stage-1
This commit is contained in:
commit
27a37b2f67
8
CONTRIBUTORS
Normal file
8
CONTRIBUTORS
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
Руководитель проекта:
|
||||||
|
- Семен Фомченков (@Armatik), e-mail: armatik@alt-gnome.ru
|
||||||
|
|
||||||
|
Ведущие разработчики:
|
||||||
|
- Максим Слипенко (@Maks1m_S), e-mail: maxim@slipenko.com
|
||||||
|
|
||||||
|
Участники проекта:
|
||||||
|
- Илья Женецкий (@ilyazheprog)
|
@ -2,15 +2,14 @@
|
|||||||
|
|
||||||
## Описание
|
## Описание
|
||||||
|
|
||||||
<!--
|
Подготовленная версия OCAB Lite для интеграции в чат [Альт Линукс](https://t.me/alt_linux)
|
||||||
TODO: добавить описание
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Функционал
|
## Функционал
|
||||||
|
Список OCAB-модулей используемых в боте:
|
||||||
|
|
||||||
<!--
|
* report - Вызов администрации чата одной командой
|
||||||
TODO: описать функционал
|
* welcome - Автоматическая вариативная проверка пользователей на признаки бота или другой автоматической рекламной системы
|
||||||
-->
|
* help - Получение информации об OCAB Lite
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
FROM python:3.12-slim as builder
|
|
||||||
|
|
||||||
RUN pip install poetry
|
|
||||||
RUN mkdir -p /app
|
|
||||||
COPY . /app
|
|
||||||
|
|
||||||
# Фикс
|
|
||||||
|
|
||||||
RUN sed -i '/ocab-core = {/{s/, develop = true//}' /app/src/gnomik/pyproject.toml && \
|
|
||||||
sed -i '/ocab-modules = {/{s/, develop = true//}' /app/src/gnomik/pyproject.toml && \
|
|
||||||
sed -i '/ocab-core = {/{s/, develop = true//}' /app/src/ocab_modules/pyproject.toml
|
|
||||||
|
|
||||||
WORKDIR /app/src/gnomik
|
|
||||||
|
|
||||||
RUN poetry lock && poetry install
|
|
||||||
|
|
||||||
FROM python:3.12-slim as base
|
|
||||||
|
|
||||||
COPY --from=builder /app/src/gnomik /app
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
ENV PATH="/app/.venv/bin:$PATH"
|
|
||||||
CMD ["python", "-m", "gnomik"]
|
|
@ -1,14 +0,0 @@
|
|||||||
**/Dockerfile
|
|
||||||
**/*.dockerignore
|
|
||||||
**/docker-compose.yml
|
|
||||||
|
|
||||||
**/.git
|
|
||||||
**/.gitignore
|
|
||||||
|
|
||||||
**/.venv
|
|
||||||
|
|
||||||
**/.mypy_cache
|
|
||||||
**/__pycache__/
|
|
||||||
|
|
||||||
src/gnomik/config.yaml
|
|
||||||
src/gnomik/database/*
|
|
@ -1,55 +0,0 @@
|
|||||||
# Gnomик
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Чат-бот помощник в [ALT Gnome Chat](https://t.me/alt_gnome_chat).
|
|
||||||
|
|
||||||
|
|
||||||
ALT Regular Gnome Community - открытое сообщество пользователей операционной системы ALT Regular Gnome.
|
|
||||||
|
|
||||||
- [Канал](https://t.me/alt_gnome)
|
|
||||||
- [Wiki](https://alt-gnome.wiki)
|
|
||||||
|
|
||||||
## Описание
|
|
||||||
|
|
||||||
Gnomик - это чат-бот, разработанный на платформе Open Chat AI Bot (OCAB) для Telegram. Он предоставляет различные функции и возможности, помогающие пользователям операционной системы ALT Regular Gnome.
|
|
||||||
|
|
||||||
## Функционал
|
|
||||||
|
|
||||||
<!--
|
|
||||||
TODO: описать функционал
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Запуск
|
|
||||||
|
|
||||||
### Docker
|
|
||||||
|
|
||||||
1. Соберите Docker-образ:
|
|
||||||
```bash
|
|
||||||
docker build -t gnomik .
|
|
||||||
```
|
|
||||||
2. Запустите контейнер:
|
|
||||||
```bash
|
|
||||||
docker run -p 9000:9000 -v ./config.yaml:/app/config.yaml -v ./database:/app/database gnomik
|
|
||||||
```
|
|
||||||
|
|
||||||
Замените `./config.yaml` и `./database` на пути к вашим локальным файлам конфигурации и паки для базы данных.
|
|
||||||
|
|
||||||
### Вручную
|
|
||||||
|
|
||||||
1. Активируйте виртуальное окружение Gnomика:
|
|
||||||
```bash
|
|
||||||
poetry shell
|
|
||||||
```
|
|
||||||
2. Запустите бота:
|
|
||||||
```bash
|
|
||||||
python -m gnomik
|
|
||||||
```
|
|
||||||
|
|
||||||
## Конфигурация
|
|
||||||
|
|
||||||
Конфигурация бота находится в файле `config.yaml`.
|
|
||||||
|
|
||||||
## Модули
|
|
||||||
|
|
||||||
Список загружаемых модулей указан в файле `__main__.py`.
|
|
@ -1,18 +0,0 @@
|
|||||||
core:
|
|
||||||
mode: WEBHOOK
|
|
||||||
token: xxx
|
|
||||||
webhook:
|
|
||||||
public_url: xxx
|
|
||||||
filters:
|
|
||||||
approved_chat_id: -4128011756 | -4128011756
|
|
||||||
default_chat_tag: '@alt_gnome_chat'
|
|
||||||
miniapp:
|
|
||||||
public_url: xxx
|
|
||||||
yandexgpt:
|
|
||||||
catalogid: xxx
|
|
||||||
inword: помогите | не работает
|
|
||||||
prompt: Ты чат-бот ...
|
|
||||||
startword: Бот| Бот, | бот | бот,
|
|
||||||
token: xxx
|
|
||||||
token_for_answer: 2000
|
|
||||||
token_for_request: 8000
|
|
@ -1,12 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: ../..
|
|
||||||
dockerfile: src/gnomik/Dockerfile
|
|
||||||
ports:
|
|
||||||
- 9000:9000
|
|
||||||
volumes:
|
|
||||||
- ./config.yaml:/app/config.yaml
|
|
||||||
- ./database:/app/database
|
|
Binary file not shown.
Before Width: | Height: | Size: 61 KiB |
@ -1,29 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
|
|
||||||
from ocab_core import OCAB
|
|
||||||
from ocab_modules import module_loader
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
ocab = OCAB()
|
|
||||||
await ocab.init_app(
|
|
||||||
[
|
|
||||||
module_loader("standard", "config", safe=False),
|
|
||||||
module_loader("standard", "database", safe=False),
|
|
||||||
module_loader("standard", "fsm_database_storage", safe=False),
|
|
||||||
module_loader("standard", "roles", safe=False),
|
|
||||||
module_loader("external", "yandexgpt", safe=False),
|
|
||||||
#
|
|
||||||
module_loader("standard", "command_helper"),
|
|
||||||
module_loader("standard", "info"),
|
|
||||||
module_loader("standard", "filters"),
|
|
||||||
module_loader("external", "create_report_apps"),
|
|
||||||
module_loader("standard", "admin"),
|
|
||||||
module_loader("standard", "message_processing"),
|
|
||||||
module_loader("standard", "miniapp", safe=False),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
await ocab.start()
|
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
|
2162
src/gnomik/poetry.lock
generated
2162
src/gnomik/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,2 +0,0 @@
|
|||||||
[virtualenvs]
|
|
||||||
in-project = true
|
|
@ -1,15 +0,0 @@
|
|||||||
[tool.poetry]
|
|
||||||
name = "gnomik"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = ""
|
|
||||||
authors = ["Максим Слипенко <maxim@slipenko.com>"]
|
|
||||||
readme = "README.md"
|
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
|
||||||
python = "~3.12"
|
|
||||||
ocab-core = { extras=["webhook"], path = "../ocab_core", develop = true }
|
|
||||||
ocab-modules = { path = "../ocab_modules", develop = true }
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core"]
|
|
||||||
build-backend = "poetry.core.masonry.api"
|
|
@ -1 +0,0 @@
|
|||||||
from . import yandexgpt
|
|
@ -1,19 +0,0 @@
|
|||||||
# Модуль Create Report Apps
|
|
||||||
|
|
||||||
Модуль `create_report_apps` предназначен для помощи пользователям в создании отчетов об ошибках в приложениях.
|
|
||||||
|
|
||||||
## Функциональность
|
|
||||||
|
|
||||||
- Задает пользователю ряд вопросов, необходимых для составления отчета.
|
|
||||||
- Собирает информацию о системе пользователя.
|
|
||||||
- Формирует отчет в текстовом формате.
|
|
||||||
|
|
||||||
## Команды
|
|
||||||
|
|
||||||
- `/create_report_apps` - запустить процесс создания отчета.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
1. Отправьте команду `/create_report_apps` боту в личных сообщениях или в групповом чате.
|
|
||||||
2. Ответьте на вопросы бота.
|
|
||||||
3. Бот сформирует отчет и отправит его вам.
|
|
@ -1 +0,0 @@
|
|||||||
from .main import module_init
|
|
@ -1,136 +0,0 @@
|
|||||||
from aiogram import Bot, Router
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from aiogram.fsm.context import FSMContext
|
|
||||||
from aiogram.fsm.state import State, StatesGroup
|
|
||||||
from aiogram.types import (
|
|
||||||
BufferedInputFile,
|
|
||||||
KeyboardButton,
|
|
||||||
Message,
|
|
||||||
ReplyKeyboardMarkup,
|
|
||||||
ReplyKeyboardRemove,
|
|
||||||
)
|
|
||||||
|
|
||||||
from ocab_core.modules_system.public_api import Utils, get_fsm_context
|
|
||||||
|
|
||||||
from .report import Report
|
|
||||||
|
|
||||||
router = Router()
|
|
||||||
|
|
||||||
|
|
||||||
class ReportState(StatesGroup):
|
|
||||||
input_system_info = State()
|
|
||||||
input_app_name = State()
|
|
||||||
input_problem_step_by_step = State()
|
|
||||||
input_actual_result = State()
|
|
||||||
input_expected_result = State()
|
|
||||||
input_additional_info = State()
|
|
||||||
|
|
||||||
|
|
||||||
system_info_code = """echo "SESSION_TYPE: ${XDG_SESSION_TYPE:-Unknown}"
|
|
||||||
[ -f /etc/os-release ] && grep "^PRETTY_NAME=" /etc/os-release | cut -d= -f2 \
|
|
||||||
| tr -d '"' | xargs echo "OS: "
|
|
||||||
echo "Kernel: $(uname -r)"
|
|
||||||
echo "DE: ${XDG_CURRENT_DESKTOP:-Unknown}"
|
|
||||||
grep "^model name" /proc/cpuinfo | head -n1 | cut -d: -f2 \
|
|
||||||
| xargs echo "CPU: "
|
|
||||||
lspci | grep "VGA compatible controller" | cut -d: -f3 \
|
|
||||||
| xargs -I{} echo "GPU: {}"
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
system_info_message = """Укажите параметры свой системы.
|
|
||||||
Собрать информацию о системе можно с помощью данного скрипта:
|
|
||||||
""" + Utils.code_format(
|
|
||||||
system_info_code,
|
|
||||||
"shell",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def start_report(chat_id: int, bot: Bot):
|
|
||||||
await bot.send_message(
|
|
||||||
chat_id=chat_id,
|
|
||||||
text=system_info_message,
|
|
||||||
parse_mode=ParseMode.HTML,
|
|
||||||
reply_markup=ReplyKeyboardRemove(),
|
|
||||||
)
|
|
||||||
state = await get_fsm_context(chat_id, chat_id)
|
|
||||||
|
|
||||||
await state.set_state(ReportState.input_system_info)
|
|
||||||
|
|
||||||
|
|
||||||
app_info_message = """Укажите название и версию приложения.
|
|
||||||
Узнать можно с помощью данной команды:""" + Utils.code_format(
|
|
||||||
"rpm -qa | grep -i НАЗВАНИЕ_ПРИЛОЖЕНИЯ", "shell"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.message(ReportState.input_system_info)
|
|
||||||
async def system_entered(message: Message, state: FSMContext):
|
|
||||||
await state.update_data(system=message.text)
|
|
||||||
await message.answer(
|
|
||||||
text=app_info_message,
|
|
||||||
parse_mode=ParseMode.HTML,
|
|
||||||
)
|
|
||||||
await state.set_state(ReportState.input_app_name)
|
|
||||||
|
|
||||||
|
|
||||||
step_by_step_message = (
|
|
||||||
"""Опиши проблему пошагово, что ты делал, что происходило, что не так."""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.message(ReportState.input_app_name)
|
|
||||||
async def app_name_entered(message: Message, state: FSMContext):
|
|
||||||
await state.update_data(app=message.text)
|
|
||||||
await message.answer(text=step_by_step_message)
|
|
||||||
await state.set_state(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):
|
|
||||||
await state.update_data(problem_step_by_step=message.text)
|
|
||||||
await message.answer(text="Опиши, что произошло (фактический результат).")
|
|
||||||
await state.set_state(ReportState.input_actual_result)
|
|
||||||
|
|
||||||
|
|
||||||
@router.message(ReportState.input_actual_result)
|
|
||||||
async def actual_result_entered(message: Message, state: FSMContext):
|
|
||||||
await state.update_data(actual=message.text)
|
|
||||||
await message.answer(text="Опиши ожидаемый результат.")
|
|
||||||
await state.set_state(ReportState.input_expected_result)
|
|
||||||
|
|
||||||
|
|
||||||
@router.message(ReportState.input_expected_result)
|
|
||||||
async def expected_result_entered(message: Message, state: FSMContext):
|
|
||||||
await state.update_data(expected=message.text)
|
|
||||||
await message.answer(
|
|
||||||
text="Если есть дополнительная информация, то напиши ее.",
|
|
||||||
reply_markup=ReplyKeyboardMarkup(
|
|
||||||
resize_keyboard=True,
|
|
||||||
keyboard=[
|
|
||||||
[KeyboardButton(text="Дополнительной информации нет")],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
await state.set_state(ReportState.input_additional_info)
|
|
||||||
|
|
||||||
|
|
||||||
@router.message(ReportState.input_additional_info)
|
|
||||||
async def additional_info_entered(message: Message, state: FSMContext):
|
|
||||||
if message.text == "Дополнительной информации нет":
|
|
||||||
additional_info = ""
|
|
||||||
else:
|
|
||||||
additional_info = message.text
|
|
||||||
await state.update_data(additional=additional_info)
|
|
||||||
await message.answer(
|
|
||||||
text="Вот твой отчет сообщением, а также файлом:",
|
|
||||||
reply_markup=ReplyKeyboardRemove(),
|
|
||||||
)
|
|
||||||
data = await state.get_data()
|
|
||||||
|
|
||||||
report = Report(data)
|
|
||||||
file_report = report.export().encode()
|
|
||||||
|
|
||||||
await message.answer(text=report.export())
|
|
||||||
await message.answer_document(document=BufferedInputFile(file_report, "report.txt"))
|
|
||||||
await state.clear()
|
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "external.create_report_apps",
|
|
||||||
"name": "Create Report Apps",
|
|
||||||
"description": "Модуль для создания отчетов о ошибках в приложениях",
|
|
||||||
"author": [
|
|
||||||
"OCAB Team",
|
|
||||||
"Maxim Slipenko"
|
|
||||||
],
|
|
||||||
"version": "1.0.0",
|
|
||||||
"privileged": false,
|
|
||||||
"dependencies": {
|
|
||||||
"standard.command_helper": "^1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
from typing import Union
|
|
||||||
|
|
||||||
from aiogram import Bot, F, Router
|
|
||||||
from aiogram.exceptions import TelegramForbiddenError
|
|
||||||
from aiogram.filters import BaseFilter, Command, CommandStart
|
|
||||||
from aiogram.types import (
|
|
||||||
CallbackQuery,
|
|
||||||
InlineKeyboardButton,
|
|
||||||
InlineKeyboardMarkup,
|
|
||||||
Message,
|
|
||||||
)
|
|
||||||
|
|
||||||
from ocab_core.modules_system.public_api import get_module, register_router
|
|
||||||
|
|
||||||
from .create_report import router as create_report_router
|
|
||||||
from .create_report import start_report
|
|
||||||
|
|
||||||
register_command = get_module("standard.command_helper", "register_command")
|
|
||||||
|
|
||||||
router = Router()
|
|
||||||
|
|
||||||
|
|
||||||
class ChatTypeFilter(BaseFilter):
|
|
||||||
def __init__(self, chat_type: Union[str, list]):
|
|
||||||
self.chat_type = chat_type
|
|
||||||
|
|
||||||
async def __call__(self, message: Message) -> bool:
|
|
||||||
if isinstance(self.chat_type, str):
|
|
||||||
return message.chat.type == self.chat_type
|
|
||||||
return message.chat.type in self.chat_type
|
|
||||||
|
|
||||||
|
|
||||||
@router.message(
|
|
||||||
ChatTypeFilter(chat_type=["group", "supergroup"]), Command("create_report_apps")
|
|
||||||
)
|
|
||||||
async def create_report_apps_command_group(message: Message):
|
|
||||||
keyboard = InlineKeyboardMarkup(
|
|
||||||
inline_keyboard=[
|
|
||||||
[
|
|
||||||
InlineKeyboardButton(
|
|
||||||
text="Да", callback_data=f"create_report:{message.from_user.id}"
|
|
||||||
),
|
|
||||||
InlineKeyboardButton(
|
|
||||||
text="Нет", callback_data=f"cancel_report:{message.from_user.id}"
|
|
||||||
),
|
|
||||||
]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
await message.answer(
|
|
||||||
"Я могу отправить тебе пару вопросов "
|
|
||||||
"для помощи в составлении репорта личными "
|
|
||||||
"сообщениями.",
|
|
||||||
reply_markup=keyboard,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
CommandStart(deep_link=True, magic=F.args == "create_report_apps"),
|
|
||||||
)
|
|
||||||
@router.message(ChatTypeFilter(chat_type=["private"]), Command("create_report_apps"))
|
|
||||||
async def create_report_apps_command(message: Message, bot: Bot):
|
|
||||||
await start_report(message.from_user.id, bot)
|
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(F.data.startswith("cancel_report"))
|
|
||||||
async def cancel_report_callback(callback_query: CallbackQuery):
|
|
||||||
callback_user_id = int(callback_query.data.split(":")[1])
|
|
||||||
if callback_query.from_user.id != callback_user_id:
|
|
||||||
await callback_query.answer("Эта кнопка не для вас.", show_alert=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
await callback_query.message.delete()
|
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(F.data.startswith("create_report"))
|
|
||||||
async def create_report_callback(callback_query: CallbackQuery, bot: Bot):
|
|
||||||
callback_user_id = int(callback_query.data.split(":")[1])
|
|
||||||
if callback_query.from_user.id != callback_user_id:
|
|
||||||
await callback_query.answer("Эта кнопка не для вас.", show_alert=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
user_id = callback_query.from_user.id
|
|
||||||
|
|
||||||
async def on_chat_unavailable():
|
|
||||||
await callback_query.message.edit_text(
|
|
||||||
"Я в личных сообщениях задам тебе вопросы "
|
|
||||||
"для помощи в составлении репорта. "
|
|
||||||
'Но перед этим ты должен нажать кнопку "Запустить"'
|
|
||||||
)
|
|
||||||
info = await bot.get_me()
|
|
||||||
await callback_query.answer(
|
|
||||||
url=f"https://t.me/{info.username}?start=create_report_apps"
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
chat_member = await bot.get_chat_member(chat_id=user_id, user_id=user_id)
|
|
||||||
if chat_member.status != "left":
|
|
||||||
await start_report(user_id, bot)
|
|
||||||
await callback_query.message.edit_text(
|
|
||||||
"Я в личных сообщениях задам тебе "
|
|
||||||
"вопросы для помощи в составлении "
|
|
||||||
"репорта."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
await on_chat_unavailable()
|
|
||||||
except TelegramForbiddenError:
|
|
||||||
await on_chat_unavailable()
|
|
||||||
|
|
||||||
|
|
||||||
async def module_init():
|
|
||||||
router.include_router(create_report_router)
|
|
||||||
|
|
||||||
register_router(router)
|
|
||||||
register_command("create_report_apps", "Написать репорт о приложении")
|
|
@ -1,59 +0,0 @@
|
|||||||
import aiogram
|
|
||||||
|
|
||||||
|
|
||||||
class ReportFormatter:
|
|
||||||
def __init__(self, html=True):
|
|
||||||
self.html = html
|
|
||||||
|
|
||||||
def bold(self, string):
|
|
||||||
if self.html:
|
|
||||||
return f"<b>{self.text(string)}</b>"
|
|
||||||
return self.text(string)
|
|
||||||
|
|
||||||
def text(self, string):
|
|
||||||
if self.html:
|
|
||||||
return aiogram.html.quote(string)
|
|
||||||
return string
|
|
||||||
|
|
||||||
|
|
||||||
class Report:
|
|
||||||
def __init__(self, data: dict):
|
|
||||||
self.data = data
|
|
||||||
|
|
||||||
def export(self):
|
|
||||||
data = self.data
|
|
||||||
|
|
||||||
report = f"""
|
|
||||||
Стенд с ошибкой:
|
|
||||||
==============================
|
|
||||||
|
|
||||||
{data['system']}
|
|
||||||
|
|
||||||
Пакет:
|
|
||||||
==============================
|
|
||||||
|
|
||||||
{data['app']}
|
|
||||||
|
|
||||||
Шаги, приводящие к ошибке:
|
|
||||||
==============================
|
|
||||||
|
|
||||||
{data['problem_step_by_step']}
|
|
||||||
|
|
||||||
Фактический результат:
|
|
||||||
==============================
|
|
||||||
|
|
||||||
{data['actual']}
|
|
||||||
|
|
||||||
Ожидаемый результат:
|
|
||||||
==============================
|
|
||||||
|
|
||||||
{data['expected']}
|
|
||||||
"""
|
|
||||||
if data["additional"] != "":
|
|
||||||
report += f"""
|
|
||||||
Дополнительно:
|
|
||||||
==============================
|
|
||||||
|
|
||||||
{data['additional']}
|
|
||||||
"""
|
|
||||||
return report
|
|
@ -1,22 +0,0 @@
|
|||||||
# Модуль YandexGPT
|
|
||||||
|
|
||||||
Модуль `yandexgpt` интегрирует в бота OCAB нейросеть YandexGPT.
|
|
||||||
|
|
||||||
## Функциональность
|
|
||||||
|
|
||||||
- Позволяет боту отвечать на сообщения пользователей, используя YandexGPT.
|
|
||||||
- Строит линию контекста для нейросети, используя историю сообщений.
|
|
||||||
|
|
||||||
## Конфигурация
|
|
||||||
|
|
||||||
- `yandexgpt::token` - API-ключ для доступа к YandexGPT.
|
|
||||||
- `yandexgpt::catalogid` - идентификатор каталога YandexGPT.
|
|
||||||
- `yandexgpt::prompt` - системная подсказка для YandexGPT.
|
|
||||||
- `yandexgpt::startword` - слова, с которых должно начинаться сообщение, чтобы бот ответил.
|
|
||||||
- `yandexgpt::inword` - слова, которые должны быть в сообщении, чтобы бот ответил.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
1. Настройте конфигурационные параметры модуля.
|
|
||||||
2. Отправьте боту сообщение, которое соответствует условиям, указанным в параметрах `startword` и `inword`.
|
|
||||||
3. Бот ответит на сообщение, используя YandexGPT.
|
|
@ -1,2 +0,0 @@
|
|||||||
from .handlers import answer_to_message
|
|
||||||
from .main import module_init
|
|
@ -1,47 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.types import Message
|
|
||||||
|
|
||||||
from ocab_core.modules_system.public_api import get_module, log
|
|
||||||
|
|
||||||
from .yandexgpt import YandexGPT
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ocab_modules.standard.config import IConfig
|
|
||||||
from ocab_modules.standard.database.db_api import add_message as IAddMessage
|
|
||||||
|
|
||||||
config: "IConfig" = get_module(
|
|
||||||
"standard.config",
|
|
||||||
"config",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_yandexgpt_catalog_id():
|
|
||||||
return config.get("yandexgpt::catalogid")
|
|
||||||
|
|
||||||
|
|
||||||
def get_yandexgpt_token():
|
|
||||||
return config.get("yandexgpt::token")
|
|
||||||
|
|
||||||
|
|
||||||
def get_yandexgpt_prompt():
|
|
||||||
return config.get("yandexgpt::prompt")
|
|
||||||
|
|
||||||
|
|
||||||
add_message: "IAddMessage" = get_module("standard.database", "db_api.add_message")
|
|
||||||
|
|
||||||
|
|
||||||
async def answer_to_message(message: Message, bot: Bot):
|
|
||||||
# print("answer_to_message")
|
|
||||||
log("answer_to_message")
|
|
||||||
yagpt = YandexGPT(get_yandexgpt_token(), get_yandexgpt_catalog_id())
|
|
||||||
text = message.text
|
|
||||||
prompt = get_yandexgpt_prompt()
|
|
||||||
# response = await yagpt.async_yandexgpt(system_prompt=prompt, input_messages=text)
|
|
||||||
response = await yagpt.yandexgpt_request(
|
|
||||||
chat_id=message.chat.id, message_id=message.message_id, type="yandexgpt"
|
|
||||||
)
|
|
||||||
reply = await message.reply(response, parse_mode="Markdown")
|
|
||||||
add_message(reply, message_ai_model="yandexgpt")
|
|
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "external.yandexgpt",
|
|
||||||
"name": "Yandex GPT",
|
|
||||||
"description": "Модуль для работы с Yandex GPT",
|
|
||||||
"author": "OCAB Team",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"privileged": false,
|
|
||||||
"dependencies": {
|
|
||||||
"required": {
|
|
||||||
"standard.config": "^1.0.0",
|
|
||||||
"standard.database": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pythonDependencies": {
|
|
||||||
"required": {
|
|
||||||
"aiohttp": "*",
|
|
||||||
"requests": "*",
|
|
||||||
"json": "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from ocab_core.modules_system.public_api import get_module
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ocab_modules.standard.config import IConfig
|
|
||||||
|
|
||||||
config: "IConfig" = get_module("standard.config", "config")
|
|
||||||
|
|
||||||
|
|
||||||
def module_init():
|
|
||||||
config.register(
|
|
||||||
"yandexgpt::token",
|
|
||||||
"password",
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
config.register(
|
|
||||||
"yandexgpt::token_for_request",
|
|
||||||
"int",
|
|
||||||
default_value=8000,
|
|
||||||
)
|
|
||||||
config.register(
|
|
||||||
"yandexgpt::token_for_answer",
|
|
||||||
"int",
|
|
||||||
default_value=2000,
|
|
||||||
)
|
|
||||||
|
|
||||||
config.register(
|
|
||||||
"yandexgpt::catalogid",
|
|
||||||
"password",
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
config.register(
|
|
||||||
"yandexgpt::prompt",
|
|
||||||
"string",
|
|
||||||
default_value="Ты чат-бот ...",
|
|
||||||
)
|
|
||||||
|
|
||||||
config.register(
|
|
||||||
"yandexgpt::startword",
|
|
||||||
"string",
|
|
||||||
default_value="Бот| Бот, | бот | бот,",
|
|
||||||
)
|
|
||||||
|
|
||||||
config.register(
|
|
||||||
"yandexgpt::inword",
|
|
||||||
"string",
|
|
||||||
default_value="помогите | не работает",
|
|
||||||
)
|
|
@ -1,10 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
from aiogram import F, Router
|
|
||||||
|
|
||||||
from .handlers import answer_to_message
|
|
||||||
|
|
||||||
router = Router()
|
|
||||||
# Если сообщение содержит в начале текст "Гномик" или "гномик" или отвечает на сообщение бота, то вызывается функция answer_to_message
|
|
||||||
router.message.register(
|
|
||||||
answer_to_message, F.text.startswith("Гномик") | F.text.startswith("гномик")
|
|
||||||
)
|
|
@ -1,312 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
import json
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from ocab_core.modules_system.public_api import get_module, log
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ocab_modules.standard.config import IConfig
|
|
||||||
from ocab_modules.standard.database import db_api as IDbApi
|
|
||||||
|
|
||||||
db_api: "IDbApi" = get_module("standard.database", "db_api")
|
|
||||||
|
|
||||||
config: "IConfig" = get_module("standard.config", "config")
|
|
||||||
|
|
||||||
|
|
||||||
def get_yandexgpt_token_for_answer():
|
|
||||||
return config.get("yandexgpt::token_for_answer")
|
|
||||||
|
|
||||||
|
|
||||||
def get_yandexgpt_token_for_request():
|
|
||||||
return config.get("yandexgpt::token_for_request")
|
|
||||||
|
|
||||||
|
|
||||||
def get_yandexgpt_prompt():
|
|
||||||
return config.get("yandexgpt::prompt")
|
|
||||||
|
|
||||||
|
|
||||||
class YandexGPT:
|
|
||||||
token = None
|
|
||||||
catalog_id = None
|
|
||||||
languages = {
|
|
||||||
"ru": "русский язык",
|
|
||||||
"en": "английский язык",
|
|
||||||
"de": "немецкий язык",
|
|
||||||
"uk": "украинский язык",
|
|
||||||
"es": "испанский язык",
|
|
||||||
"be": "белорусский язык",
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, token, catalog_id):
|
|
||||||
self.token = token
|
|
||||||
self.catalog_id = catalog_id
|
|
||||||
|
|
||||||
async def async_request(self, url, headers, prompt) -> dict:
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
async with session.post(url, headers=headers, json=prompt) as response:
|
|
||||||
return await response.json()
|
|
||||||
|
|
||||||
async def async_token_check(
|
|
||||||
self, messages, gpt, max_tokens, stream, temperature, del_msg_id=1
|
|
||||||
):
|
|
||||||
url = "https://llm.api.cloud.yandex.net/foundationModels/v1/tokenizeCompletion"
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": f"Api-Key {self.token}",
|
|
||||||
}
|
|
||||||
answer_token = get_yandexgpt_token_for_answer()
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
request = {
|
|
||||||
"modelUri": gpt,
|
|
||||||
"completionOptions": {
|
|
||||||
"stream": stream,
|
|
||||||
"temperature": temperature,
|
|
||||||
"maxTokens": max_tokens,
|
|
||||||
},
|
|
||||||
"messages": messages,
|
|
||||||
}
|
|
||||||
response = await self.async_request(
|
|
||||||
url=url, headers=headers, prompt=request
|
|
||||||
)
|
|
||||||
except Exception as e: # TODO: Переделать обработку ошибок
|
|
||||||
# print(e)
|
|
||||||
log(f"Error: {e}")
|
|
||||||
|
|
||||||
continue
|
|
||||||
if int(len(response["tokens"])) < (max_tokens - answer_token):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
messages.pop(del_msg_id)
|
|
||||||
except IndexError:
|
|
||||||
Exception("IndexError: list index out of range")
|
|
||||||
return messages
|
|
||||||
|
|
||||||
async def async_yandexgpt_lite(
|
|
||||||
self,
|
|
||||||
system_prompt,
|
|
||||||
input_messages,
|
|
||||||
stream=False,
|
|
||||||
temperature=0.6,
|
|
||||||
max_tokens=8000,
|
|
||||||
):
|
|
||||||
url = "https://llm.api.cloud.yandex.net/foundationModels/v1/completion"
|
|
||||||
gpt = f"gpt://{self.catalog_id}/yandexgpt-lite/latest"
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": f"Api-Key {self.token}",
|
|
||||||
}
|
|
||||||
|
|
||||||
messages = [{"role": "system", "text": system_prompt}]
|
|
||||||
for message in input_messages:
|
|
||||||
messages.append(message)
|
|
||||||
messages = await self.async_token_check(messages, gpt, max_tokens)
|
|
||||||
|
|
||||||
prompt = {
|
|
||||||
"modelUri": gpt,
|
|
||||||
"completionOptions": {
|
|
||||||
"stream": stream,
|
|
||||||
"temperature": temperature,
|
|
||||||
"maxTokens": max_tokens,
|
|
||||||
},
|
|
||||||
"messages": messages,
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.post(url, headers=headers, json=prompt).text # nosec
|
|
||||||
return json.loads(response)["result"]["alternatives"][0]["message"]["text"]
|
|
||||||
|
|
||||||
async def async_yandexgpt(
|
|
||||||
self,
|
|
||||||
system_prompt,
|
|
||||||
input_messages,
|
|
||||||
stream=False,
|
|
||||||
temperature=0.6,
|
|
||||||
max_tokens=None,
|
|
||||||
):
|
|
||||||
if max_tokens is None:
|
|
||||||
max_tokens = get_yandexgpt_token_for_request()
|
|
||||||
|
|
||||||
url = "https://llm.api.cloud.yandex.net/foundationModels/v1/completion"
|
|
||||||
gpt = f"gpt://{self.catalog_id}/yandexgpt/latest"
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": f"Api-Key {self.token}",
|
|
||||||
}
|
|
||||||
|
|
||||||
messages = []
|
|
||||||
messages.append({"role": "system", "text": system_prompt})
|
|
||||||
for message in input_messages:
|
|
||||||
messages.append(message)
|
|
||||||
|
|
||||||
messages = await self.async_token_check(
|
|
||||||
messages, gpt, max_tokens, stream, temperature
|
|
||||||
)
|
|
||||||
|
|
||||||
request = {
|
|
||||||
"modelUri": gpt,
|
|
||||||
"completionOptions": {
|
|
||||||
"stream": stream,
|
|
||||||
"temperature": temperature,
|
|
||||||
"maxTokens": max_tokens,
|
|
||||||
},
|
|
||||||
"messages": messages,
|
|
||||||
}
|
|
||||||
response = await self.async_request(
|
|
||||||
url=url, headers=headers, prompt=request
|
|
||||||
) # nosec
|
|
||||||
return response["result"]["alternatives"][0]["message"]["text"]
|
|
||||||
|
|
||||||
async def async_yandexgpt_translate(self, input_language, output_language, text):
|
|
||||||
input_language = self.languages[input_language]
|
|
||||||
output_language = self.languages[output_language]
|
|
||||||
|
|
||||||
return await self.async_yandexgpt(
|
|
||||||
f"Переведи на {output_language} сохранив оригинальный смысл текста. Верни только результат:",
|
|
||||||
[{"role": "user", "text": text}],
|
|
||||||
stream=False,
|
|
||||||
temperature=0.6,
|
|
||||||
max_tokens=8000,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def async_yandexgpt_spelling_check(self, input_language, text):
|
|
||||||
input_language = self.languages[input_language]
|
|
||||||
|
|
||||||
return await self.async_yandexgpt(
|
|
||||||
f"Проверьте орфографию и пунктуацию текста на {input_language}. Верни исправленный текст "
|
|
||||||
f"без смысловых искажений:",
|
|
||||||
[{"role": "user", "text": text}],
|
|
||||||
stream=False,
|
|
||||||
temperature=0.6,
|
|
||||||
max_tokens=8000,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def async_yandexgpt_text_history(
|
|
||||||
self, input_messages, stream=False, temperature=0.6, max_tokens=8000
|
|
||||||
):
|
|
||||||
url = "https://llm.api.cloud.yandex.net/foundationModels/v1/completion"
|
|
||||||
gpt = f"gpt://{self.catalog_id}/summarization/latest"
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": f"Api-Key {self.token}",
|
|
||||||
}
|
|
||||||
|
|
||||||
messages = []
|
|
||||||
for message in input_messages:
|
|
||||||
messages.append(message)
|
|
||||||
messages = await self.async_token_check(messages, gpt, max_tokens, del_msg_id=0)
|
|
||||||
|
|
||||||
prompt = {
|
|
||||||
"modelUri": gpt,
|
|
||||||
"completionOptions": {
|
|
||||||
"stream": stream,
|
|
||||||
"temperature": temperature,
|
|
||||||
"maxTokens": max_tokens,
|
|
||||||
},
|
|
||||||
"messages": messages,
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.post(url, headers=headers, json=prompt).text # nosec
|
|
||||||
return json.loads(response)["result"]["alternatives"][0]["message"]["text"]
|
|
||||||
|
|
||||||
async def async_yandex_cloud_text_to_speech(
|
|
||||||
self, text, voice, emotion, speed, format, quality
|
|
||||||
):
|
|
||||||
tts = "tts.api.cloud.yandex.net/speech/v1/tts:synthesize"
|
|
||||||
# TODO: Сделать функцию TTS
|
|
||||||
return 0
|
|
||||||
|
|
||||||
async def async_yandex_cloud_vision(self, image, features, language):
|
|
||||||
# TODO: Сделать функцию Vision
|
|
||||||
return 0
|
|
||||||
|
|
||||||
async def collect_messages(self, message_id, chat_id):
|
|
||||||
messages = []
|
|
||||||
# Собираем цепочку сообщений в формате: [{"role": "user", "text": "<Имя_пользователя>: Привет!"},
|
|
||||||
# {"role": "assistant", "text": "Привет!"}]
|
|
||||||
while True:
|
|
||||||
message = db_api.get_message_text(chat_id, message_id)
|
|
||||||
if db_api.get_message_ai_model(chat_id, message_id) != None:
|
|
||||||
messages.append({"role": "assistant", "text": message})
|
|
||||||
else:
|
|
||||||
sender_name = db_api.get_user_name(
|
|
||||||
db_api.get_message_sender_id(chat_id, message_id)
|
|
||||||
)
|
|
||||||
messages.append({"role": "user", "text": sender_name + ": " + message})
|
|
||||||
message_id = db_api.get_answer_to_message_id(chat_id, message_id)
|
|
||||||
if message_id is None:
|
|
||||||
break
|
|
||||||
return list(reversed(messages))
|
|
||||||
|
|
||||||
async def collecting_messages_for_history(
|
|
||||||
self, start_message_id, end_message_id, chat_id
|
|
||||||
):
|
|
||||||
messages = []
|
|
||||||
# Собираем цепочку сообщений в формате: [{"role": "user", "text": "<Имя_пользователя>: Привет!"},
|
|
||||||
# {"role": "assistant", "text": "Привет!"}]
|
|
||||||
while True:
|
|
||||||
message = db_api.get_message_text(chat_id, start_message_id)
|
|
||||||
if db_api.get_message_ai_model(chat_id, start_message_id) != None:
|
|
||||||
messages.append({"role": "assistant", "text": message})
|
|
||||||
else:
|
|
||||||
sender_name = db_api.get_user_name(
|
|
||||||
db_api.get_message_sender_id(chat_id, start_message_id)
|
|
||||||
)
|
|
||||||
messages.append({"role": "user", "text": sender_name + ": " + message})
|
|
||||||
start_message_id -= 1
|
|
||||||
if start_message_id <= end_message_id:
|
|
||||||
break
|
|
||||||
return messages.reverse()
|
|
||||||
|
|
||||||
async def yandexgpt_request(
|
|
||||||
self,
|
|
||||||
message_id=None,
|
|
||||||
type="yandexgpt-lite",
|
|
||||||
chat_id=None,
|
|
||||||
message_id_end=None,
|
|
||||||
input_language=None,
|
|
||||||
output_language=None,
|
|
||||||
text=None,
|
|
||||||
):
|
|
||||||
if type == "yandexgpt-lite":
|
|
||||||
messages = await self.collect_messages(message_id, chat_id)
|
|
||||||
return await self.async_yandexgpt_lite(
|
|
||||||
system_prompt=get_yandexgpt_prompt(),
|
|
||||||
input_messages=messages,
|
|
||||||
stream=False,
|
|
||||||
temperature=0.6,
|
|
||||||
max_tokens=8000,
|
|
||||||
)
|
|
||||||
elif type == "yandexgpt":
|
|
||||||
# print("yandexgpt_request")
|
|
||||||
log("yandexgpt_request")
|
|
||||||
messages = await self.collect_messages(message_id, chat_id)
|
|
||||||
return await self.async_yandexgpt(
|
|
||||||
system_prompt=get_yandexgpt_prompt(),
|
|
||||||
input_messages=messages,
|
|
||||||
stream=False,
|
|
||||||
temperature=0.6,
|
|
||||||
max_tokens=get_yandexgpt_token_for_request(),
|
|
||||||
)
|
|
||||||
elif type == "yandexgpt-translate":
|
|
||||||
return await self.async_yandexgpt_translate(
|
|
||||||
input_language,
|
|
||||||
output_language,
|
|
||||||
text=db_api.get_message_text(chat_id, message_id),
|
|
||||||
)
|
|
||||||
elif type == "yandexgpt-spelling-check":
|
|
||||||
return await self.async_yandexgpt_spelling_check(
|
|
||||||
input_language, text=db_api.get_message_text(chat_id, message_id)
|
|
||||||
)
|
|
||||||
elif type == "yandexgpt-text-history":
|
|
||||||
messages = await self.collect_messages_for_history(
|
|
||||||
message_id, message_id_end, chat_id
|
|
||||||
)
|
|
||||||
return await self.async_yandexgpt_text_history(
|
|
||||||
messages=messages, stream=False, temperature=0.6, max_tokens=8000
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return "Ошибка: Неизвестный тип запроса | Error: Unknown request type"
|
|
@ -1 +0,0 @@
|
|||||||
from .moderation import ban_user, unmute_user
|
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Moderation",
|
|
||||||
"description": "Moderation commands for OCAB",
|
|
||||||
"author": "OCAB Team",
|
|
||||||
"version": "1.0"
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import time
|
|
||||||
|
|
||||||
import aiogram
|
|
||||||
import aiohttp
|
|
||||||
|
|
||||||
from ocab_modules.standard.config.config import *
|
|
||||||
from ocab_modules.standard.roles.roles import *
|
|
||||||
|
|
||||||
|
|
||||||
class Moderation:
|
|
||||||
def __init__(self):
|
|
||||||
access_rights = get_access_rights()
|
|
||||||
bot_check_message = bool(self.access_rights["BOT_CHECK_MESSAGE"])
|
|
||||||
bot_ban_user = bool(self.access_rights["BOT_BAN_USER"])
|
|
||||||
bot_mute_user = bool(self.access_rights["BOT_MUTE_USER"])
|
|
||||||
moderator_rights = self.access_rights["MODERATOR_RIGHTS"]
|
|
||||||
ai_check_message = bool(self.access_rights["AI_CHECK_MESSAGE"])
|
|
||||||
beta_ai_check_message = bool(self.access_rights["BETA_AI_CHECK_MESSAGE"])
|
|
||||||
|
|
||||||
async def time_to_seconds(time):
|
|
||||||
# Конвертация текстового указания времени по типу 3h, 5m, 10s в минуты
|
|
||||||
if time[-1] == "d":
|
|
||||||
return int(time[:-1]) * 86400
|
|
||||||
elif time[-1] == "h":
|
|
||||||
return int(time[:-1]) * 3600
|
|
||||||
elif time[-1] == "m":
|
|
||||||
return int(time[:-1]) * 60
|
|
||||||
elif time[-1] == "s":
|
|
||||||
return int(time[:-1])
|
|
||||||
|
|
||||||
async def short_time_to_time(self, time):
|
|
||||||
# Конвертация времени в длинное название
|
|
||||||
if time[-1] == "d":
|
|
||||||
return str(f"{time[0:-1]} дней")
|
|
||||||
elif time[-1] == "h":
|
|
||||||
return str(f"{time[0:-1]} часов")
|
|
||||||
elif time[-1] == "m":
|
|
||||||
return str(f"{time[0:-1]} минут")
|
|
||||||
elif time[-1] == "s":
|
|
||||||
return str(f"{time[0:-1]} секунд")
|
|
||||||
|
|
||||||
async def delete_message(self, chat_id, message_id, bot: aiogram.Bot):
|
|
||||||
await bot.delete_message(chat_id, message_id)
|
|
||||||
|
|
||||||
async def ban_user(self, chat_id, user_id, bot: aiogram.Bot):
|
|
||||||
await bot.ban_chat_member(chat_id, user_id)
|
|
||||||
|
|
||||||
|
|
||||||
async def mute_user(chat_id, user_id, time, bot: aiogram.Bot):
|
|
||||||
mutePermissions = {
|
|
||||||
"can_send_messages": False,
|
|
||||||
"can_send_audios": False,
|
|
||||||
"can_send_documents": False,
|
|
||||||
"can_send_photos": False,
|
|
||||||
"can_send_videos": False,
|
|
||||||
"can_send_video_notes": False,
|
|
||||||
"can_send_voice_notes": False,
|
|
||||||
"can_send_polls": False,
|
|
||||||
"can_send_other_messages": False,
|
|
||||||
"can_add_web_page_previews": False,
|
|
||||||
"can_change_info": False,
|
|
||||||
"can_invite_users": False,
|
|
||||||
"can_pin_messages": False,
|
|
||||||
"can_manage_topics": False,
|
|
||||||
}
|
|
||||||
end_time = time + int(time.time())
|
|
||||||
await bot.restrict_chat_member(
|
|
||||||
chat_id, user_id, until_date=end_time, **mutePermissions
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def unmute_user(chat_id, user_id, bot: aiogram.Bot):
|
|
||||||
await bot.restrict_chat_member(
|
|
||||||
chat_id, user_id, use_independent_chat_permissions=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def ban_user(chat_id, user_id, bot: aiogram.Bot):
|
|
||||||
await bot.ban_chat_member(chat_id, user_id)
|
|
@ -1,84 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import random
|
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.types import inline_keyboard_button as types
|
|
||||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
|
||||||
|
|
||||||
from ocab_modules.legacy.moderation import ban_user, unmute_user
|
|
||||||
from src.ocab_modules.standard.config.config import get_telegram_check_bot
|
|
||||||
from src.ocab_modules.standard.database.db_api import *
|
|
||||||
|
|
||||||
|
|
||||||
async def create_math_task():
|
|
||||||
first_number = random.randint(1, 100) # nosec
|
|
||||||
second_number = random.randint(1, 100) # nosec
|
|
||||||
answer = first_number + second_number
|
|
||||||
fake_answers = []
|
|
||||||
for i in range(3):
|
|
||||||
diff = random.randint(1, 10) # nosec
|
|
||||||
diff_sign = random.choice(["+", "-"]) # nosec
|
|
||||||
fake_answers.append(answer + diff if diff_sign == "+" else answer - diff)
|
|
||||||
fake_answers.append(answer)
|
|
||||||
random.shuffle(fake_answers)
|
|
||||||
return [answer, first_number, second_number, fake_answers]
|
|
||||||
|
|
||||||
|
|
||||||
async def ban_user_timer(chat_id: int, user_id: int, time: int, bot: Bot):
|
|
||||||
await asyncio.sleep(time)
|
|
||||||
if get_user(user_id) is not None:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
await ban_user()
|
|
||||||
|
|
||||||
|
|
||||||
async def check_new_user(message: Message, bot: Bot):
|
|
||||||
print("check_new_user")
|
|
||||||
if get_telegram_check_bot():
|
|
||||||
# Проверяем наличие пользователя в базе данных
|
|
||||||
|
|
||||||
if get_user(message.from_user.id) is None:
|
|
||||||
# Выдаём пользователю ограничение на отправку сообщений на 3 минуты
|
|
||||||
ban_task = Thread(
|
|
||||||
target=ban_user_timer,
|
|
||||||
args=(message.chat.id, message.from_user.id, 180, bot),
|
|
||||||
)
|
|
||||||
ban_task.start()
|
|
||||||
# Создаём задачу с отложенным выполнением на 3 минуты
|
|
||||||
|
|
||||||
math_task = await create_math_task()
|
|
||||||
text = f"{math_task[1]} + {math_task[2]}"
|
|
||||||
builder = InlineKeyboardBuilder()
|
|
||||||
for answer in math_task[3]:
|
|
||||||
if answer == math_task[0]:
|
|
||||||
builder.add(
|
|
||||||
types.InlineKeyboardButton(
|
|
||||||
text=answer, callback_data=f"check_math_task_true"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
builder.add(
|
|
||||||
types.InlineKeyboardButton(
|
|
||||||
text=answer, callback_data=f"check_math_task_false"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
await message.reply(
|
|
||||||
f"Приветствую, {message.from_user.first_name}!\n"
|
|
||||||
f"Для продолжения работы с ботом, пожалуйста, решите математический пример в течении 3х минут:\n"
|
|
||||||
f"*{text}*",
|
|
||||||
reply_markup=builder.as_markup(),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def math_task_true(message: Message, bot: Bot):
|
|
||||||
await message.reply(f"Верно! Добро пожаловать в чат {message.from_user.first_name}")
|
|
||||||
await unmute_user(message.chat.id, message.from_user.id, bot)
|
|
||||||
add_user(
|
|
||||||
message.from_user.id,
|
|
||||||
message.from_user.first_name + " " + message.from_user.last_name,
|
|
||||||
message.from_user.username,
|
|
||||||
)
|
|
||||||
pass
|
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Welcome",
|
|
||||||
"description": "Мо",
|
|
||||||
"author": "OCAB Team",
|
|
||||||
"version": "1.0"
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
from aiogram import F, Router
|
|
||||||
|
|
||||||
from .handlers import check_new_user
|
|
||||||
|
|
||||||
router = Router()
|
|
||||||
|
|
||||||
# Если в чат пришел новый пользователь
|
|
||||||
router.message.register(check_new_user, F.new_chat_members.exists())
|
|
||||||
# Ловин колбеки от кнопок с callback_data=f"check_math_task_true"
|
|
||||||
router.callback_query.register(
|
|
||||||
check_new_user, F.callback_data == "check_math_task_true"
|
|
||||||
)
|
|
Loading…
Reference in New Issue
Block a user