mirror of
https://gitflic.ru/project/alt-gnome/karkas.git
synced 2024-12-24 16:44:46 +03:00
удаляет лишние модули
This commit is contained in:
parent
3c0c8630eb
commit
2917c9ee46
@ -1,20 +0,0 @@
|
|||||||
# Модуль Admin
|
|
||||||
|
|
||||||
Модуль `admin` предоставляет администраторам и модераторам чата инструменты для управления:
|
|
||||||
|
|
||||||
## Функциональность
|
|
||||||
|
|
||||||
- Удаление сообщений.
|
|
||||||
- Получение ID чата.
|
|
||||||
|
|
||||||
## Команды
|
|
||||||
|
|
||||||
- `/rm` - удалить сообщение, на которое отвечает команда.
|
|
||||||
- `/chatID` - получить ID текущего чата.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
1. Ответьте на сообщение, которое нужно удалить.
|
|
||||||
2. Отправьте команду `/rm`.
|
|
||||||
|
|
||||||
Чтобы получить ID чата, отправьте команду `/chatID`.
|
|
@ -1 +0,0 @@
|
|||||||
from .main import module_init
|
|
@ -1,65 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.types import Message
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import get_module
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from karkas_blocks.standard.config import IConfig
|
|
||||||
|
|
||||||
config: "IConfig" = get_module("standard.config", "config")
|
|
||||||
|
|
||||||
|
|
||||||
def get_default_chat_tag():
|
|
||||||
return config.get("filters::default_chat_tag")
|
|
||||||
|
|
||||||
|
|
||||||
async def delete_message(message: Message, bot: Bot):
|
|
||||||
reply_message_id = message.reply_to_message.message_id
|
|
||||||
await bot.delete_message(message.chat.id, reply_message_id)
|
|
||||||
|
|
||||||
|
|
||||||
async def error_access(message: Message, bot: Bot):
|
|
||||||
await message.reply("Вы не админ/модератор")
|
|
||||||
|
|
||||||
|
|
||||||
async def get_chat_id(message: Message, bot: Bot):
|
|
||||||
await message.reply(
|
|
||||||
f"ID данного чата: `{message.chat.id}`", parse_mode="MarkdownV2"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def chat_not_in_approve_list(message: Message, bot: Bot):
|
|
||||||
await message.reply(
|
|
||||||
f"Бот недоступен в данном чате, пожалуйста,"
|
|
||||||
f" обратитесь к администратору для добавления чата в список доступных или перейдите в чат "
|
|
||||||
f"{get_default_chat_tag()}"
|
|
||||||
)
|
|
||||||
await get_chat_id(message, bot)
|
|
||||||
|
|
||||||
|
|
||||||
async def mute_user(chat_id: int, user_id: int, time: int, bot: Bot):
|
|
||||||
# *, can_send_messages: bool | None = None, can_send_audios: bool | None = None, can_send_documents: bool | None = None, can_send_photos: bool | None = None, can_send_videos: bool | None = None, can_send_video_notes: bool | None = None, can_send_voice_notes: bool | None = None, can_send_polls: bool | None = None, can_send_other_messages: bool | None = None, can_add_web_page_previews: bool | None = None, can_change_info: bool | None = None, can_invite_users: bool | None = None, can_pin_messages: bool | None = None, can_manage_topics: bool | None = None, **extra_data: Any)
|
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "standard.admin",
|
|
||||||
"name": "Admin",
|
|
||||||
"description": "Модуль для работы с админкой",
|
|
||||||
"author": "Karkas Team",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"privileged": false,
|
|
||||||
"dependencies": {
|
|
||||||
"required": {
|
|
||||||
"standard.filters": "^1.0.0",
|
|
||||||
"standard.roles": "^1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
from karkas_core.modules_system.public_api import register_router
|
|
||||||
|
|
||||||
from .routers import router
|
|
||||||
|
|
||||||
|
|
||||||
async def module_init():
|
|
||||||
register_router(router)
|
|
@ -1,27 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
from aiogram import F, Router
|
|
||||||
from aiogram.filters import Command
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import get_module, log
|
|
||||||
|
|
||||||
from .handlers import (
|
|
||||||
chat_not_in_approve_list,
|
|
||||||
delete_message,
|
|
||||||
error_access,
|
|
||||||
get_chat_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
(ChatModerOrAdminFilter, ChatNotInApproveFilter) = get_module(
|
|
||||||
"standard.filters", ["ChatModerOrAdminFilter", "ChatNotInApproveFilter"]
|
|
||||||
)
|
|
||||||
|
|
||||||
router = Router()
|
|
||||||
|
|
||||||
# Если сообщение содержит какой либо текст и выполняется фильтр ChatNotInApproveFilter, то вызывается функция chat_not_in_approve_list
|
|
||||||
router.message.register(chat_not_in_approve_list, ChatNotInApproveFilter(), F.text)
|
|
||||||
|
|
||||||
router.message.register(get_chat_id, ChatModerOrAdminFilter(), Command("chatID"))
|
|
||||||
|
|
||||||
router.message.register(delete_message, ChatModerOrAdminFilter(), Command("rm"))
|
|
||||||
router.message.register(error_access, Command("rm"))
|
|
||||||
router.message.register(error_access, Command("chatID"))
|
|
@ -1,48 +0,0 @@
|
|||||||
## Модуль DataBase
|
|
||||||
|
|
||||||
Модуль DataBase предназначен для ведения и работы с базами данных Karkas.
|
|
||||||
|
|
||||||
Модуль содержит в себе следующие таблицы:
|
|
||||||
|
|
||||||
* `Chats` - таблица для хранения информации о чатах.
|
|
||||||
* `Users` - таблица для хранения информации о пользователях.
|
|
||||||
* `Messages` - таблица для хранения информации о сообщениях.
|
|
||||||
* `ChatStats` - таблица для хранения статистики чатов по дням.
|
|
||||||
* `UserStats` - таблица для хранения статистики пользователей по дням.
|
|
||||||
|
|
||||||
Cтруктура таблицы `Chats`:
|
|
||||||
* `chat_id` - идентификатор чата.
|
|
||||||
* `chat_name` - название чата.
|
|
||||||
* `chat_type` - тип чата. (0 - Чат администраторов, 1 - Пользовательский чат, 3 - Чат разрешённых личных запросов к боту
|
|
||||||
10 - Не инициализированный чат)
|
|
||||||
* `chat_stats` - количество всех отправленных сообщений в чате.
|
|
||||||
|
|
||||||
Cтруктура таблицы `Users`:
|
|
||||||
* `user_id` - идентификатор пользователя telegram.
|
|
||||||
* `user_tag` - тег пользователя telegram.
|
|
||||||
* `user_name` - имя пользователя telegram.
|
|
||||||
* `user_role` - роль пользователя в чате. (0 - Администратор, 1 - Модератор, 2 - Пользователь)
|
|
||||||
* `user_stats` - количество всех отправленных сообщений пользователем.
|
|
||||||
* `user_rep` - репутация пользователя.
|
|
||||||
|
|
||||||
Cтруктура таблицы `Messages`:
|
|
||||||
* `message_chat_id` - идентификатор чата в котором отправлено сообщение.
|
|
||||||
* `message_id` - идентификатор сообщения.
|
|
||||||
* `messag_sender_id` - идентификатор пользователя отправившего сообщение. Если сообщение отправил бот, то
|
|
||||||
`messag_sender_id` = 0.
|
|
||||||
* `answer_to_message_id` - идентификатор сообщения на которое дан ответ. Если ответа нет или ответ на служебное
|
|
||||||
сообщение о создании топика в чатах с форумным типом, то `answer_to_message_id` = 0.
|
|
||||||
* `message_ai_model` - идентификатор модели нейросети, которая использовалась для генерации ответа. Если ответ'
|
|
||||||
сгенерирован не был, то `message_ai_model` = null.
|
|
||||||
* `message_text` - текст сообщения.
|
|
||||||
|
|
||||||
Cтруктура таблицы `ChatStats`:
|
|
||||||
* `chat_id` - идентификатор чата для которого собрана статистика.
|
|
||||||
* `date` - дата на которую собрана статистика.
|
|
||||||
* `messages_count` - количество сообщений отправленных в чат за день.
|
|
||||||
|
|
||||||
Cтруктура таблицы `UserStats`:
|
|
||||||
* `chat_id` - идентификатор чата для которого собрана статистика.
|
|
||||||
* `user_id` - идентификатор пользователя для которого собрана статистика.
|
|
||||||
* `date` - дата на которую собрана статистика.
|
|
||||||
* `messages_count` - количество сообщений отправленных пользователем в чат за день.
|
|
@ -1,5 +0,0 @@
|
|||||||
from . import db_api, models, repositories
|
|
||||||
|
|
||||||
|
|
||||||
async def module_init():
|
|
||||||
db_api.connect_database()
|
|
@ -1,301 +0,0 @@
|
|||||||
import peewee as pw
|
|
||||||
from aiogram.types import Message
|
|
||||||
|
|
||||||
from .exceptions import NotExpectedModuleName
|
|
||||||
from .models.chat_stats import ChatStats
|
|
||||||
from .models.chats import Chats
|
|
||||||
from .models.db import database_proxy
|
|
||||||
from .models.fsm_data import FSMData
|
|
||||||
from .models.messages import Messages
|
|
||||||
from .models.user_stats import UserStats
|
|
||||||
from .models.users import Users
|
|
||||||
|
|
||||||
|
|
||||||
def connect_database(is_test: bool = False, module: str | None = None):
|
|
||||||
if module:
|
|
||||||
raise NotExpectedModuleName()
|
|
||||||
db_path = "database"
|
|
||||||
|
|
||||||
database = pw.SqliteDatabase(f"{db_path}/Karkas.db")
|
|
||||||
database_proxy.initialize(database)
|
|
||||||
database.connect()
|
|
||||||
create_tables(database)
|
|
||||||
|
|
||||||
return database, f"{db_path}/Karkas.db"
|
|
||||||
|
|
||||||
|
|
||||||
def create_tables(db: pw.SqliteDatabase):
|
|
||||||
"""Создание таблиц"""
|
|
||||||
for table in Chats, Messages, Users, UserStats, ChatStats, FSMData:
|
|
||||||
if not table.table_exists():
|
|
||||||
db.create_tables([table])
|
|
||||||
|
|
||||||
|
|
||||||
def add_chat(chat_id, chat_name, chat_type=10, chat_stats=0):
|
|
||||||
chat, created = Chats.get_or_create(
|
|
||||||
id=chat_id,
|
|
||||||
defaults={
|
|
||||||
"chat_name": chat_name,
|
|
||||||
"chat_type": chat_type,
|
|
||||||
"chat_all_stat": chat_stats,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if not created:
|
|
||||||
# Обновить существующий чат, если он уже существует
|
|
||||||
chat.chat_name = chat_name
|
|
||||||
chat.chat_type = chat_type
|
|
||||||
chat.chat_stats = chat_stats
|
|
||||||
chat.save()
|
|
||||||
|
|
||||||
|
|
||||||
def add_user(
|
|
||||||
user_id,
|
|
||||||
user_first_name,
|
|
||||||
user_last_name=None,
|
|
||||||
user_tag=None,
|
|
||||||
user_role=0,
|
|
||||||
user_stats=0,
|
|
||||||
user_rep=0,
|
|
||||||
):
|
|
||||||
if user_last_name is None:
|
|
||||||
user_name = user_first_name
|
|
||||||
else:
|
|
||||||
user_name = user_first_name + " " + user_last_name
|
|
||||||
|
|
||||||
user, created = Users.get_or_create(
|
|
||||||
id=user_id,
|
|
||||||
defaults={
|
|
||||||
"user_tag": user_tag,
|
|
||||||
"user_name": user_name,
|
|
||||||
"user_role": user_role,
|
|
||||||
"user_stats": user_stats,
|
|
||||||
"user_rep": user_rep,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if not created:
|
|
||||||
# Обновить существующего пользователя, если он уже существует
|
|
||||||
user.user_tag = user_tag
|
|
||||||
user.user_name = user_name
|
|
||||||
user.user_role = user_role
|
|
||||||
user.user_stats = user_stats
|
|
||||||
user.user_rep = user_rep
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
|
|
||||||
def add_message(message: Message, message_ai_model=None):
|
|
||||||
if message.reply_to_message:
|
|
||||||
answer_to_message_id = message.reply_to_message.message_id
|
|
||||||
else:
|
|
||||||
answer_to_message_id = None
|
|
||||||
Messages.create(
|
|
||||||
message_chat_id=message.chat.id,
|
|
||||||
message_id=message.message_id,
|
|
||||||
message_sender_id=message.from_user.id,
|
|
||||||
answer_to_message_id=answer_to_message_id,
|
|
||||||
message_ai_model=message_ai_model,
|
|
||||||
message_text=message.text,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def add_chat_stats(chat_id, date, messages_count):
|
|
||||||
ChatStats.create(chat_id=chat_id, date=date, messages_count=messages_count)
|
|
||||||
|
|
||||||
|
|
||||||
def add_user_stats(chat_id, user_id, date, messages_count):
|
|
||||||
UserStats.create(
|
|
||||||
chat_id=chat_id, user_id=user_id, date=date, messages_count=messages_count
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Работа с таблицей чатов
|
|
||||||
|
|
||||||
|
|
||||||
def get_chat(chat_id):
|
|
||||||
return Chats.get_or_none(Chats.id == chat_id)
|
|
||||||
|
|
||||||
|
|
||||||
def change_chat_name(chat_id, new_chat_name):
|
|
||||||
query = Chats.update(chat_name=new_chat_name).where(Chats.id == chat_id)
|
|
||||||
query.execute()
|
|
||||||
|
|
||||||
|
|
||||||
def change_chat_type(chat_id, new_chat_type):
|
|
||||||
query = Chats.update(chat_type=new_chat_type).where(Chats.id == chat_id)
|
|
||||||
query.execute()
|
|
||||||
|
|
||||||
|
|
||||||
def get_chat_all_stat(chat_id):
|
|
||||||
chat = Chats.get_or_none(Chats.id == chat_id)
|
|
||||||
return chat.chat_all_stat if chat else None
|
|
||||||
|
|
||||||
|
|
||||||
# Работа с таблицей пользователей
|
|
||||||
|
|
||||||
|
|
||||||
def get_user(user_id) -> Users | None:
|
|
||||||
return Users.get_or_none(Users.id == user_id)
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_tag(user_id):
|
|
||||||
user = Users.get_or_none(Users.id == user_id)
|
|
||||||
return user.user_tag if user else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_id(user_tag):
|
|
||||||
user = Users.get_or_none(Users.user_tag == user_tag)
|
|
||||||
return user.id if user else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_name(user_id):
|
|
||||||
user = Users.get_or_none(Users.id == user_id)
|
|
||||||
return user.user_name if user else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_role(user_id: str):
|
|
||||||
user = Users.get_or_none(Users.id == user_id)
|
|
||||||
return user.user_role if user else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_all_stats(user_id):
|
|
||||||
user = Users.get_or_none(Users.id == user_id)
|
|
||||||
return user.user_stats if user else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_rep(user_id):
|
|
||||||
user = Users.get_or_none(Users.id == user_id)
|
|
||||||
return user.user_rep if user else None
|
|
||||||
|
|
||||||
|
|
||||||
def change_user_name(user_id, user_first_name, user_last_name=None):
|
|
||||||
if user_last_name is None:
|
|
||||||
new_user_name = user_first_name
|
|
||||||
else:
|
|
||||||
new_user_name = user_first_name + " " + user_last_name
|
|
||||||
query = Users.update(user_name=new_user_name).where(Users.id == user_id)
|
|
||||||
query.execute()
|
|
||||||
|
|
||||||
|
|
||||||
def change_user_tag(user_id, new_user_tag):
|
|
||||||
query = Users.update(user_tag=new_user_tag).where(Users.id == user_id)
|
|
||||||
query.execute()
|
|
||||||
|
|
||||||
|
|
||||||
def change_user_role(user_id, new_user_role):
|
|
||||||
query = Users.update(user_role=new_user_role).where(Users.id == user_id)
|
|
||||||
query.execute()
|
|
||||||
|
|
||||||
|
|
||||||
# Работа с таблицей сообщений
|
|
||||||
|
|
||||||
|
|
||||||
def get_message(message_chat_id, message_id):
|
|
||||||
return Messages.get_or_none(
|
|
||||||
Messages.message_chat_id == message_chat_id,
|
|
||||||
Messages.message_id == message_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_message_sender_id(message_chat_id, message_id):
|
|
||||||
message = Messages.get_or_none(
|
|
||||||
Messages.message_chat_id == message_chat_id, Messages.message_id == message_id
|
|
||||||
)
|
|
||||||
return message.message_sender_id if message else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_message_text(message_chat_id, message_id):
|
|
||||||
message = Messages.get_or_none(
|
|
||||||
Messages.message_chat_id == message_chat_id, Messages.message_id == message_id
|
|
||||||
)
|
|
||||||
return message.message_text if message else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_message_ai_model(message_chat_id, message_id):
|
|
||||||
message = Messages.get_or_none(
|
|
||||||
Messages.message_chat_id == message_chat_id, Messages.message_id == message_id
|
|
||||||
)
|
|
||||||
return message.message_ai_model if message else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_answer_to_message_id(message_chat_id, message_id):
|
|
||||||
message = Messages.get_or_none(
|
|
||||||
Messages.message_chat_id == message_chat_id, Messages.message_id == message_id
|
|
||||||
)
|
|
||||||
return message.answer_to_message_id if message else None
|
|
||||||
|
|
||||||
|
|
||||||
# Работа с таблицей статистики чатов
|
|
||||||
|
|
||||||
|
|
||||||
def get_chat_stats(chat_id):
|
|
||||||
chat_stats = {}
|
|
||||||
for chat_stat in ChatStats.select().where(ChatStats.chat_id == chat_id):
|
|
||||||
chat_stats[chat_stat.date] = chat_stat.messages_count
|
|
||||||
return chat_stats
|
|
||||||
|
|
||||||
|
|
||||||
# Работа с таблицей статистики пользователей
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_stats(user_id):
|
|
||||||
user_stats = {}
|
|
||||||
for user_stat in UserStats.select().where(UserStats.user_id == user_id):
|
|
||||||
user_stats[user_stat.date] = user_stat.messages_count
|
|
||||||
return user_stats
|
|
||||||
|
|
||||||
|
|
||||||
# Функции обновления
|
|
||||||
|
|
||||||
|
|
||||||
def update_chat_all_stat(chat_id):
|
|
||||||
query = Chats.update(chat_all_stat=Chats.chat_all_stat + 1).where(
|
|
||||||
Chats.id == chat_id
|
|
||||||
)
|
|
||||||
query.execute()
|
|
||||||
|
|
||||||
|
|
||||||
def update_chat_stats(chat_id, date):
|
|
||||||
chat_stats = ChatStats.get_or_none(
|
|
||||||
ChatStats.chat_id == chat_id, ChatStats.date == date
|
|
||||||
)
|
|
||||||
if chat_stats:
|
|
||||||
query = ChatStats.update(messages_count=ChatStats.messages_count + 1).where(
|
|
||||||
ChatStats.chat_id == chat_id, ChatStats.date == date
|
|
||||||
)
|
|
||||||
query.execute()
|
|
||||||
else:
|
|
||||||
ChatStats.create(chat_id=chat_id, date=date, messages_count=1)
|
|
||||||
|
|
||||||
|
|
||||||
def update_user_all_stat(user_id):
|
|
||||||
user = Users.get_or_none(Users.id == user_id)
|
|
||||||
if user:
|
|
||||||
query = Users.update(user_stats=Users.user_stats + 1).where(Users.id == user_id)
|
|
||||||
query.execute()
|
|
||||||
else:
|
|
||||||
Users.create(id=user_id, user_stats=1)
|
|
||||||
|
|
||||||
|
|
||||||
def update_user_rep(user_id):
|
|
||||||
user = Users.get_or_none(Users.id == user_id)
|
|
||||||
if user:
|
|
||||||
query = Users.update(user_rep=Users.user_rep + 1).where(Users.id == user_id)
|
|
||||||
query.execute()
|
|
||||||
else:
|
|
||||||
Users.create(id=user_id, user_rep=1)
|
|
||||||
|
|
||||||
|
|
||||||
def update_user_stats(chat_id, user_id, date):
|
|
||||||
user_stats = UserStats.get_or_none(
|
|
||||||
UserStats.chat_id == chat_id,
|
|
||||||
UserStats.user_id == user_id,
|
|
||||||
UserStats.date == date,
|
|
||||||
)
|
|
||||||
if user_stats:
|
|
||||||
query = UserStats.update(messages_count=UserStats.messages_count + 1).where(
|
|
||||||
UserStats.chat_id == chat_id,
|
|
||||||
UserStats.user_id == user_id,
|
|
||||||
UserStats.date == date,
|
|
||||||
)
|
|
||||||
query.execute()
|
|
||||||
else:
|
|
||||||
UserStats.create(chat_id=chat_id, user_id=user_id, date=date, messages_count=1)
|
|
@ -1,12 +0,0 @@
|
|||||||
class MissingModuleName(BaseException):
|
|
||||||
def __init__(self):
|
|
||||||
self.message = "Пропущено название директории модуля"
|
|
||||||
|
|
||||||
super().__init__(self.message)
|
|
||||||
|
|
||||||
|
|
||||||
class NotExpectedModuleName(BaseException):
|
|
||||||
def __init__(self):
|
|
||||||
self.message = "Не ожидалось название директории модуля"
|
|
||||||
|
|
||||||
super().__init__(self.message)
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "standard.database",
|
|
||||||
"name": "Database",
|
|
||||||
"description": "Модуль для работы с БД",
|
|
||||||
"author": "Karkas Team",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"privileged": true,
|
|
||||||
"dependencies": {}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
from .fsm_data import FSMData
|
|
@ -1,12 +0,0 @@
|
|||||||
import peewee as pw
|
|
||||||
|
|
||||||
from .db import database_proxy
|
|
||||||
|
|
||||||
|
|
||||||
class ChatStats(pw.Model):
|
|
||||||
class Meta:
|
|
||||||
database = database_proxy
|
|
||||||
|
|
||||||
chat_id = pw.IntegerField(null=False)
|
|
||||||
date = pw.DateField(null=False)
|
|
||||||
messages_count = pw.IntegerField(null=False, default=0)
|
|
@ -1,12 +0,0 @@
|
|||||||
import peewee as pw
|
|
||||||
|
|
||||||
from .db import database_proxy
|
|
||||||
|
|
||||||
|
|
||||||
class Chats(pw.Model):
|
|
||||||
class Meta:
|
|
||||||
database = database_proxy
|
|
||||||
|
|
||||||
chat_name = pw.CharField(null=False)
|
|
||||||
chat_type = pw.IntegerField(null=False, default=10)
|
|
||||||
chat_all_stat = pw.IntegerField(null=False)
|
|
@ -1,3 +0,0 @@
|
|||||||
from peewee import DatabaseProxy
|
|
||||||
|
|
||||||
database_proxy = DatabaseProxy()
|
|
@ -1,12 +0,0 @@
|
|||||||
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)
|
|
@ -1,15 +0,0 @@
|
|||||||
import peewee as pw
|
|
||||||
|
|
||||||
from .db import database_proxy
|
|
||||||
|
|
||||||
|
|
||||||
class Messages(pw.Model):
|
|
||||||
class Meta:
|
|
||||||
database = database_proxy
|
|
||||||
|
|
||||||
message_chat_id = pw.IntegerField(null=False)
|
|
||||||
message_id = pw.IntegerField(null=False)
|
|
||||||
message_sender_id = pw.IntegerField(null=False)
|
|
||||||
answer_to_message_id = pw.IntegerField(null=True)
|
|
||||||
message_ai_model = pw.TextField(null=True)
|
|
||||||
message_text = pw.TextField(null=False)
|
|
@ -1,13 +0,0 @@
|
|||||||
import peewee as pw
|
|
||||||
|
|
||||||
from .db import database_proxy
|
|
||||||
|
|
||||||
|
|
||||||
class UserStats(pw.Model):
|
|
||||||
class Meta:
|
|
||||||
database = database_proxy
|
|
||||||
|
|
||||||
chat_id = pw.IntegerField(null=False)
|
|
||||||
user_id = pw.IntegerField(null=False)
|
|
||||||
date = pw.DateField(null=False)
|
|
||||||
messages_count = pw.IntegerField(null=False, default=0)
|
|
@ -1,14 +0,0 @@
|
|||||||
import peewee as pw
|
|
||||||
|
|
||||||
from .db import database_proxy
|
|
||||||
|
|
||||||
|
|
||||||
class Users(pw.Model):
|
|
||||||
class Meta:
|
|
||||||
database = database_proxy
|
|
||||||
|
|
||||||
user_tag = pw.CharField(null=True)
|
|
||||||
user_name = pw.CharField(null=False) # до 255 символов
|
|
||||||
user_role = pw.IntegerField(null=True, default=3)
|
|
||||||
user_stats = pw.IntegerField(null=True, default=0)
|
|
||||||
user_rep = pw.IntegerField(null=True, default=0)
|
|
@ -1 +0,0 @@
|
|||||||
from .fsm_data import FSMDataRepository
|
|
@ -1,32 +0,0 @@
|
|||||||
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()
|
|
@ -1 +0,0 @@
|
|||||||
Эта директория для тестовой БД
|
|
@ -1,142 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
|
|
||||||
import os
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from ...exceptions.module_exceptions import MissingModuleName, NotExpectedModuleName
|
|
||||||
from ..db_api import *
|
|
||||||
|
|
||||||
|
|
||||||
class TestDatabaseAPI(unittest.TestCase):
|
|
||||||
database = None
|
|
||||||
path = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.database, cls.path = connect_database(is_test=True, module="database")
|
|
||||||
create_tables(cls.database)
|
|
||||||
|
|
||||||
def test_fail_connect(cls):
|
|
||||||
with cls.assertRaises(MissingModuleName):
|
|
||||||
cls.database, cls.path = connect_database(is_test=True)
|
|
||||||
|
|
||||||
with cls.assertRaises(NotExpectedModuleName):
|
|
||||||
cls.database, cls.path = connect_database(module="database")
|
|
||||||
|
|
||||||
def test_add_and_get_chat(self):
|
|
||||||
add_chat(chat_id=21, chat_role=0, chat_stats=0, chat_federation=0)
|
|
||||||
add_chat(chat_id=22, chat_role=1, chat_stats=100, chat_federation=1)
|
|
||||||
|
|
||||||
chat1 = get_chat(21)
|
|
||||||
self.assertIsNotNone(chat1)
|
|
||||||
self.assertEqual(chat1.id, 21)
|
|
||||||
self.assertEqual(chat1.chat_role, 0)
|
|
||||||
|
|
||||||
chat2 = get_chat(22)
|
|
||||||
self.assertIsNotNone(chat2)
|
|
||||||
self.assertEqual(chat2.id, 22)
|
|
||||||
self.assertEqual(chat2.chat_role, 1)
|
|
||||||
|
|
||||||
def test_add_and_get_message(self):
|
|
||||||
add_message(
|
|
||||||
message_id=1, message_text="Test Message 1", message_sender=1, answer_id=2
|
|
||||||
)
|
|
||||||
add_message(
|
|
||||||
message_id=2, message_text="Test Message 2", message_sender=2, answer_id=1
|
|
||||||
)
|
|
||||||
|
|
||||||
message1 = get_message(1)
|
|
||||||
self.assertIsNotNone(message1)
|
|
||||||
self.assertEqual(message1.id, 1)
|
|
||||||
self.assertEqual(message1.message_text, "Test Message 1")
|
|
||||||
|
|
||||||
message2 = get_message(2)
|
|
||||||
self.assertIsNotNone(message2)
|
|
||||||
self.assertEqual(message2.id, 2)
|
|
||||||
self.assertEqual(message2.message_text, "Test Message 2")
|
|
||||||
|
|
||||||
def test_add_and_get_user(self):
|
|
||||||
add_user(
|
|
||||||
user_id=100,
|
|
||||||
user_name="TestUser1",
|
|
||||||
user_tag="TestTag1",
|
|
||||||
user_role=0,
|
|
||||||
user_stats=10,
|
|
||||||
user_rep=5,
|
|
||||||
)
|
|
||||||
add_user(
|
|
||||||
user_id=101,
|
|
||||||
user_name="TestUser2",
|
|
||||||
user_tag="TestTag2",
|
|
||||||
user_role=1,
|
|
||||||
user_stats=20,
|
|
||||||
user_rep=10,
|
|
||||||
)
|
|
||||||
|
|
||||||
user1 = get_user(100)
|
|
||||||
self.assertIsNotNone(user1)
|
|
||||||
self.assertEqual(user1.id, 100)
|
|
||||||
self.assertEqual(user1.user_name, "TestUser1")
|
|
||||||
|
|
||||||
user2 = get_user(101)
|
|
||||||
self.assertIsNotNone(user2)
|
|
||||||
self.assertEqual(user2.id, 101)
|
|
||||||
self.assertEqual(user2.user_name, "TestUser2")
|
|
||||||
|
|
||||||
def test_get_user_role(self):
|
|
||||||
add_user(
|
|
||||||
user_id=102,
|
|
||||||
user_name="TestUser3",
|
|
||||||
user_tag="TestTag3",
|
|
||||||
user_role=0,
|
|
||||||
user_stats=30,
|
|
||||||
user_rep=15,
|
|
||||||
)
|
|
||||||
add_user(
|
|
||||||
user_id=103,
|
|
||||||
user_name="TestUser4",
|
|
||||||
user_tag="TestTag4",
|
|
||||||
user_role=1,
|
|
||||||
user_stats=40,
|
|
||||||
user_rep=20,
|
|
||||||
)
|
|
||||||
|
|
||||||
user_role1 = get_user_role(102)
|
|
||||||
self.assertEqual(user_role1, 0)
|
|
||||||
|
|
||||||
user_role2 = get_user_role(103)
|
|
||||||
self.assertEqual(user_role2, 1)
|
|
||||||
|
|
||||||
def test_change_user_name(self):
|
|
||||||
add_user(
|
|
||||||
user_id=104,
|
|
||||||
user_name="OldName1",
|
|
||||||
user_tag="TestTag5",
|
|
||||||
user_role=0,
|
|
||||||
user_stats=50,
|
|
||||||
user_rep=25,
|
|
||||||
)
|
|
||||||
change_user_name(104, "NewName1")
|
|
||||||
updated_user1 = get_user(104)
|
|
||||||
self.assertEqual(updated_user1.user_name, "NewName1")
|
|
||||||
|
|
||||||
add_user(
|
|
||||||
user_id=105,
|
|
||||||
user_name="OldName2",
|
|
||||||
user_tag="TestTag6",
|
|
||||||
user_role=1,
|
|
||||||
user_stats=60,
|
|
||||||
user_rep=30,
|
|
||||||
)
|
|
||||||
change_user_name(105, "NewName2")
|
|
||||||
updated_user2 = get_user(105)
|
|
||||||
self.assertEqual(updated_user2.user_name, "NewName2")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
cls.database.close()
|
|
||||||
os.system(f"rm {cls.path}") # nosec
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
@ -1,13 +0,0 @@
|
|||||||
# Модуль FSM Database Storage
|
|
||||||
|
|
||||||
Модуль `fsm_database_storage` реализует хранение состояний FSM (Finite State Machine) в базе данных.
|
|
||||||
|
|
||||||
## Функциональность
|
|
||||||
|
|
||||||
- Сохранение состояния FSM в базу данных.
|
|
||||||
- Получение состояния FSM из базы данных.
|
|
||||||
- Обновление данных состояния FSM.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
Модуль автоматически регистрирует хранилище состояний FSM при инициализации.
|
|
@ -1 +0,0 @@
|
|||||||
from .fsm import module_init
|
|
@ -1,129 +0,0 @@
|
|||||||
import json
|
|
||||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
||||||
|
|
||||||
from aiogram.fsm.state import State
|
|
||||||
from aiogram.fsm.storage.base import BaseStorage, StorageKey
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import get_module, log
|
|
||||||
from karkas_core.modules_system.public_api.public_api import set_fsm
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from karkas_blocks.standard.database.repositories import (
|
|
||||||
FSMDataRepository as IFSMDataRepository,
|
|
||||||
)
|
|
||||||
|
|
||||||
FSMDataRepository: "type[IFSMDataRepository]" = 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())
|
|
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "standard.fsm_database_storage",
|
|
||||||
"name": "FSM Database Storage",
|
|
||||||
"description": "Очень полезный модуль",
|
|
||||||
"author": "Karkas Team",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"privileged": false,
|
|
||||||
"dependencies": {
|
|
||||||
"required": {
|
|
||||||
"standard.database": "^1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
# Модуль Info
|
|
||||||
|
|
||||||
Модуль `info` предоставляет информацию о пользователях и чатах.
|
|
||||||
|
|
||||||
## Команды
|
|
||||||
|
|
||||||
- `/info` - получить информацию о пользователе.
|
|
||||||
- `/chatinfo` - получить информацию о чате.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
Чтобы получить информацию о пользователе, отправьте команду `/info`,
|
|
||||||
ответив на сообщение пользователя или указав его тег.
|
|
||||||
|
|
||||||
Чтобы получить информацию о чате, отправьте команду `/chatinfo`.
|
|
@ -1,2 +0,0 @@
|
|||||||
from .handlers import get_chat_info, get_user_info
|
|
||||||
from .main import module_init
|
|
@ -1,86 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.types import Message
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import get_module, log
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from karkas_blocks.standard.database import db_api as IDbApi
|
|
||||||
from karkas_blocks.standard.roles import Roles as IRoles
|
|
||||||
|
|
||||||
db_api: "IDbApi" = get_module(
|
|
||||||
"standard.database",
|
|
||||||
"db_api",
|
|
||||||
)
|
|
||||||
|
|
||||||
Roles: "type[IRoles]" = get_module("standard.roles", "Roles")
|
|
||||||
|
|
||||||
|
|
||||||
async def get_info_answer_by_id(message: Message, bot: Bot, user_id: int):
|
|
||||||
ai_model = db_api.get_message_ai_model(message.chat.id, message.message_id)
|
|
||||||
if ai_model is not None:
|
|
||||||
await message.reply(
|
|
||||||
"Это сообщение было сгенерировано ботом используя модель: " + ai_model
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
if user_id == bot.id:
|
|
||||||
await message.reply("Это сообщение было отправлено ботом")
|
|
||||||
return
|
|
||||||
|
|
||||||
user = db_api.get_user(user_id)
|
|
||||||
|
|
||||||
if user is None:
|
|
||||||
await message.reply("Пользователь не найден")
|
|
||||||
log(f"Пользователь не найден: {user_id}, {user}")
|
|
||||||
return
|
|
||||||
|
|
||||||
roles = Roles()
|
|
||||||
answer = (
|
|
||||||
f"Пользователь: {user.user_name}\n"
|
|
||||||
f"Роль: {await roles.get_role_name(role_id=user.user_role)}\n"
|
|
||||||
f"Тег: @{user.user_tag}\n"
|
|
||||||
f"Кол-во сообщений: {user.user_stats}\n"
|
|
||||||
f"Репутация: {user.user_rep}"
|
|
||||||
)
|
|
||||||
await message.reply(answer)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_user_info(message: Message, bot: Bot):
|
|
||||||
# Проверяем содержимое сообщения, если содержит вторым элементом тег пользователя, то выводим информацию о нем
|
|
||||||
# Если сообщение отвечает на другое сообщение, то выводим информацию о пользователе, на чье сообщение был ответ
|
|
||||||
# Если это бот то выводим информацию, что это бот и какая модель yandexgpt используется
|
|
||||||
try:
|
|
||||||
if len(message.text.split()) > 1 and message.text.split()[1].startswith("@"):
|
|
||||||
user_tag = message.text.split()[1][1:]
|
|
||||||
user_id = db_api.get_user_id(user_tag)
|
|
||||||
if user_id:
|
|
||||||
await get_info_answer_by_id(message, bot, user_id)
|
|
||||||
else:
|
|
||||||
await message.reply(f"Пользователь с тегом @{user_tag} не найден")
|
|
||||||
elif message.reply_to_message:
|
|
||||||
user_id = message.reply_to_message.from_user.id
|
|
||||||
await get_info_answer_by_id(message, bot, user_id)
|
|
||||||
else:
|
|
||||||
await get_info_answer_by_id(message, bot, message.from_user.id)
|
|
||||||
except Exception as e:
|
|
||||||
await message.reply(
|
|
||||||
"В вашем запросе что-то пошло не так,"
|
|
||||||
" попробуйте запросить информацию о пользователе по его тегу или ответив на его сообщение"
|
|
||||||
)
|
|
||||||
# print(e)
|
|
||||||
log(e)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_chat_info(message: Message, bot: Bot):
|
|
||||||
answer = (
|
|
||||||
f"*Название чата:* {message.chat.title}\n"
|
|
||||||
f"*ID чата:* `{message.chat.id}`\n \n"
|
|
||||||
f"*Суммарное количество сообщений в чате:* {db_api.get_chat_all_stat(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))}"
|
|
||||||
)
|
|
||||||
await message.reply(answer, parse_mode="MarkdownV2")
|
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "standard.info",
|
|
||||||
"name": "Info",
|
|
||||||
"description": "Модуль с информацией",
|
|
||||||
"author": "Karkas Team",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"privileged": false,
|
|
||||||
"dependencies": {
|
|
||||||
"required": {
|
|
||||||
"standard.roles": "^1.0.0",
|
|
||||||
"standard.database": "^1.0.0",
|
|
||||||
"standard.command_helper": "^1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
from aiogram import Router
|
|
||||||
from aiogram.filters import Command
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import get_module, register_router
|
|
||||||
|
|
||||||
from .handlers import get_chat_info, get_user_info
|
|
||||||
|
|
||||||
register_command = get_module("standard.command_helper", "register_command")
|
|
||||||
|
|
||||||
|
|
||||||
async def module_init():
|
|
||||||
router = Router()
|
|
||||||
|
|
||||||
router.message.register(get_user_info, Command("info"))
|
|
||||||
router.message.register(get_chat_info, Command("chatinfo"))
|
|
||||||
|
|
||||||
register_router(router)
|
|
||||||
|
|
||||||
register_command("info", "Информация о пользователе")
|
|
||||||
register_command("chatinfo", "Информация о чате")
|
|
@ -1,14 +0,0 @@
|
|||||||
# Модуль Message Processing
|
|
||||||
|
|
||||||
Модуль `message_processing` обрабатывает все входящие сообщения.
|
|
||||||
|
|
||||||
## Функциональность
|
|
||||||
|
|
||||||
- Проверка чата и пользователя на наличие в базе данных.
|
|
||||||
- Обновление информации о чате и пользователе.
|
|
||||||
- Добавление статистики сообщений.
|
|
||||||
- Передача сообщения модулю `yandexgpt`, если оно соответствует условиям.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
Модуль автоматически обрабатывает все входящие сообщения.
|
|
@ -1 +0,0 @@
|
|||||||
from .message_api import module_init
|
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "standard.message_processing",
|
|
||||||
"name": "Info",
|
|
||||||
"description": "Модуль с информацией",
|
|
||||||
"author": "Karkas Team",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"privileged": false,
|
|
||||||
"dependencies": {
|
|
||||||
"required": {
|
|
||||||
"standard.roles": "^1.0.0",
|
|
||||||
"standard.database": "^1.0.0",
|
|
||||||
"standard.command_helper": "^1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from aiogram import Bot, F, Router, types
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import get_module, log, register_router
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from karkas_blocks.standard.config import IConfig
|
|
||||||
|
|
||||||
config: "IConfig" = get_module("standard.config", "config")
|
|
||||||
|
|
||||||
|
|
||||||
def get_yandexgpt_start_words():
|
|
||||||
return config.get("yandexgpt::startword").split(" | ")
|
|
||||||
|
|
||||||
|
|
||||||
def get_yandexgpt_in_words():
|
|
||||||
return config.get("yandexgpt::inword").split(" | ")
|
|
||||||
|
|
||||||
|
|
||||||
chat_not_in_approve = get_module("standard.filters", ["chat_not_in_approve"])
|
|
||||||
|
|
||||||
answer_to_message = get_module("external.yandexgpt", "answer_to_message")
|
|
||||||
|
|
||||||
(
|
|
||||||
get_chat,
|
|
||||||
add_chat,
|
|
||||||
get_user,
|
|
||||||
add_user,
|
|
||||||
get_user_name,
|
|
||||||
change_user_name,
|
|
||||||
get_user_tag,
|
|
||||||
change_user_tag,
|
|
||||||
update_chat_all_stat,
|
|
||||||
update_user_all_stat,
|
|
||||||
add_message,
|
|
||||||
) = get_module(
|
|
||||||
"standard.database",
|
|
||||||
[
|
|
||||||
"db_api.get_chat",
|
|
||||||
"db_api.add_chat",
|
|
||||||
"db_api.get_user",
|
|
||||||
"db_api.add_user",
|
|
||||||
"db_api.get_user_name",
|
|
||||||
"db_api.change_user_name",
|
|
||||||
"db_api.get_user_tag",
|
|
||||||
"db_api.change_user_tag",
|
|
||||||
"db_api.update_chat_all_stat",
|
|
||||||
"db_api.update_user_all_stat",
|
|
||||||
"db_api.add_message",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def chat_check(message: types.Message):
|
|
||||||
# Проверка наличия id чата в базе данных чатов
|
|
||||||
# Если чата нет в базе данных, то проверяем его в наличии в конфиге и если он там есть то добавляем его в БД
|
|
||||||
# Если чат есть в базе данных, то pass
|
|
||||||
if get_chat(message.chat.id) is None:
|
|
||||||
if not chat_not_in_approve(message):
|
|
||||||
# print(f"Chat in approve list: {message.chat.id} {message.chat.title}")
|
|
||||||
log(f"Chat in approve list: {message.chat.id} {message.chat.title}")
|
|
||||||
add_chat(message.chat.id, message.chat.title)
|
|
||||||
# print(f"Chat added: {message.chat.id} {message.chat.title}")
|
|
||||||
log(f"Chat added: {message.chat.id} {message.chat.title}")
|
|
||||||
else:
|
|
||||||
# print(f"Chat not in approve list: {message.chat.id} {message.chat.title}")
|
|
||||||
log(f"Chat not in approve list: {message.chat.id} {message.chat.title}")
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# Проверяем обновление названия чата
|
|
||||||
chat = get_chat(message.chat.id)
|
|
||||||
if chat.chat_name != message.chat.title:
|
|
||||||
chat.chat_name = message.chat.title
|
|
||||||
chat.save()
|
|
||||||
# print(f"Chat updated: {message.chat.id} {message.chat.title}")
|
|
||||||
log(f"Chat updated: {message.chat.id} {message.chat.title}")
|
|
||||||
else:
|
|
||||||
# print(f"Chat already exists: {message.chat.id} {message.chat.title}")
|
|
||||||
log(f"Chat already exists: {message.chat.id} {message.chat.title}")
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
async def user_check(message: types.Message):
|
|
||||||
# Проверка наличия id пользователя в базе данных пользователей
|
|
||||||
# Если пользователя нет в базе данных, то добавляем его
|
|
||||||
# Если пользователь есть в базе данных, то pass
|
|
||||||
current_user_name = ""
|
|
||||||
if message.from_user.last_name is None:
|
|
||||||
current_user_name = message.from_user.first_name
|
|
||||||
else:
|
|
||||||
current_user_name = (
|
|
||||||
message.from_user.first_name + " " + message.from_user.last_name
|
|
||||||
)
|
|
||||||
|
|
||||||
if get_user(message.from_user.id) is None:
|
|
||||||
add_user(
|
|
||||||
message.from_user.id,
|
|
||||||
message.from_user.first_name,
|
|
||||||
message.from_user.last_name,
|
|
||||||
message.from_user.username,
|
|
||||||
)
|
|
||||||
log(f"User added: {message.from_user.id} {current_user_name}")
|
|
||||||
else:
|
|
||||||
log(
|
|
||||||
f"User already exists: {message.from_user.id} {current_user_name} {message.from_user.username}"
|
|
||||||
)
|
|
||||||
# Проверяем обновление имени пользователя
|
|
||||||
if get_user_name(message.from_user.id) != current_user_name:
|
|
||||||
change_user_name(message.from_user.id, current_user_name)
|
|
||||||
log(f"User name updated: {message.from_user.id} {current_user_name}")
|
|
||||||
# Проверяем обновление username пользователя
|
|
||||||
if get_user_tag(message.from_user.id) != message.from_user.username:
|
|
||||||
change_user_tag(message.from_user.id, message.from_user.username)
|
|
||||||
log(
|
|
||||||
f"User tag updated: {message.from_user.id} {message.from_user.username}"
|
|
||||||
)
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
async def add_stats(message: types.Message):
|
|
||||||
# Добавляем пользователю и чату статистику
|
|
||||||
update_chat_all_stat(message.chat.id)
|
|
||||||
update_user_all_stat(message.from_user.id)
|
|
||||||
|
|
||||||
|
|
||||||
async def message_processing(message: types.Message, bot: Bot):
|
|
||||||
await chat_check(message)
|
|
||||||
await user_check(message)
|
|
||||||
await add_stats(message)
|
|
||||||
|
|
||||||
add_message(message)
|
|
||||||
# Если сообщение в начале содержит слово из списка или внутри сообщения содержится слово из списка или сообщение отвечает на сообщение бота
|
|
||||||
|
|
||||||
if (message.text.split(" ")[0] in get_yandexgpt_start_words()) or (
|
|
||||||
any(word in message.text for word in get_yandexgpt_in_words())
|
|
||||||
):
|
|
||||||
log("message_processing")
|
|
||||||
await answer_to_message(message, bot)
|
|
||||||
|
|
||||||
elif message.reply_to_message is not None:
|
|
||||||
if message.reply_to_message.from_user.is_bot:
|
|
||||||
log("message_processing")
|
|
||||||
await answer_to_message(message, bot)
|
|
||||||
|
|
||||||
|
|
||||||
router = Router()
|
|
||||||
# Если сообщение содержит текст то вызывается функция message_processing
|
|
||||||
router.message.register(message_processing, F.text)
|
|
||||||
|
|
||||||
|
|
||||||
async def module_init():
|
|
||||||
register_router(router)
|
|
@ -1,23 +0,0 @@
|
|||||||
# Модуль Miniapp
|
|
||||||
|
|
||||||
Модуль `miniapp` реализует веб-интерфейс для бота, доступный через Telegram Mini Apps.
|
|
||||||
|
|
||||||
## Функциональность
|
|
||||||
|
|
||||||
- Регистрация страниц веб-интерфейса.
|
|
||||||
- Авторизация пользователей через Telegram.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
1. Импортируйте функцию `register_page`.
|
|
||||||
2. Вызовите функцию `register_page`, передав ей название страницы, ее путь, blueprint Dash и префикс.
|
|
||||||
|
|
||||||
## Пример
|
|
||||||
|
|
||||||
```python
|
|
||||||
from karkas_core.modules_system.public_api import get_module
|
|
||||||
|
|
||||||
register_page = get_module("standard.miniapp", "register_page")
|
|
||||||
|
|
||||||
register_page("Моя страница", "/my_page", my_blueprint, prefix="my_page")
|
|
||||||
```
|
|
@ -1,2 +0,0 @@
|
|||||||
from .lib import register_page
|
|
||||||
from .main import module_init, module_late_init
|
|
@ -1,146 +0,0 @@
|
|||||||
import flask
|
|
||||||
from aiogram.utils.web_app import safe_parse_webapp_init_data
|
|
||||||
from dash import Dash
|
|
||||||
from dash_extensions.enrich import Input, Output
|
|
||||||
from flask import request
|
|
||||||
|
|
||||||
# TODO: добавить прокидывание BASE_PATH, т.к. это параметр из настроек
|
|
||||||
|
|
||||||
WEBAPP_LOADER_TEMPLATE = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Karkas</title>
|
|
||||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
|
||||||
<script>
|
|
||||||
window.addEventListener('message', function(event) {
|
|
||||||
if (event.origin !== window.location.origin) return;
|
|
||||||
if (event.data.type === 'iframe-url-changed') {
|
|
||||||
history.pushState(
|
|
||||||
null,
|
|
||||||
'',
|
|
||||||
window.BASE_PATH + event.data.pathname.substring(
|
|
||||||
window.INTERNAL_PATH.length
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.addEventListener('popstate', function(event) {
|
|
||||||
var iframe = document.getElementById('app-frame');
|
|
||||||
var iframeWindow = iframe.contentWindow;
|
|
||||||
iframeWindow.history.back();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
#app-frame {
|
|
||||||
display:none;
|
|
||||||
width:100%;
|
|
||||||
height:100vh;
|
|
||||||
border:none;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="loading">Loading...</div>
|
|
||||||
<iframe id="app-frame"></iframe>
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const tg = window.Telegram.WebApp;
|
|
||||||
document.cookie = `tg_init_data=${JSON.stringify(tg.initData)}; path=/`;
|
|
||||||
|
|
||||||
// if (!tg.initData) return;
|
|
||||||
|
|
||||||
const iframe = document.getElementById('app-frame');
|
|
||||||
|
|
||||||
// Константы для путей
|
|
||||||
const BASE_PATH = '/webapp';
|
|
||||||
const INTERNAL_PATH = '/webapp/_internal';
|
|
||||||
|
|
||||||
window.BASE_PATH = BASE_PATH;
|
|
||||||
window.INTERNAL_PATH = INTERNAL_PATH
|
|
||||||
|
|
||||||
// Текущий путь страницы
|
|
||||||
const currentPath = window.location.pathname;
|
|
||||||
|
|
||||||
// Формируем новый путь для iframe
|
|
||||||
let iframeSrc = INTERNAL_PATH;
|
|
||||||
|
|
||||||
// Если текущий путь начинается с BASE_PATH, убираем BASE_PATH из текущего пути
|
|
||||||
if (currentPath.startsWith(BASE_PATH)
|
|
||||||
&& currentPath.length > BASE_PATH.length) {
|
|
||||||
iframeSrc += currentPath.substring(BASE_PATH.length);
|
|
||||||
} else if (currentPath !== '/') {
|
|
||||||
iframeSrc += currentPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe.src = iframeSrc;
|
|
||||||
|
|
||||||
iframe.onload = function() {
|
|
||||||
document.getElementById('loading').style.display = 'none';
|
|
||||||
iframe.style.display = 'block';
|
|
||||||
};
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def get_auth_server(bot_token: str):
|
|
||||||
server = flask.Flask(__name__)
|
|
||||||
|
|
||||||
@server.route("/<path:rest>")
|
|
||||||
@server.route("/")
|
|
||||||
def webapp_loader(rest=None):
|
|
||||||
return flask.Response(WEBAPP_LOADER_TEMPLATE, mimetype="text/html")
|
|
||||||
|
|
||||||
@server.before_request
|
|
||||||
def add_auth_data():
|
|
||||||
init_data = request.cookies.get("tg_init_data")
|
|
||||||
if init_data:
|
|
||||||
try:
|
|
||||||
data = safe_parse_webapp_init_data(token=bot_token, init_data=init_data)
|
|
||||||
flask.g.user = data.user.model_dump()
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return server
|
|
||||||
|
|
||||||
|
|
||||||
def setup_auth_clientcallbacks(app: Dash):
|
|
||||||
app.clientside_callback(
|
|
||||||
"""
|
|
||||||
function(n_intervals) {
|
|
||||||
if (window.webAppData) {
|
|
||||||
return window.webAppData;
|
|
||||||
}
|
|
||||||
|
|
||||||
function receiveMessage(event) {
|
|
||||||
if (event.data.type === 'webAppData') {
|
|
||||||
window.webAppData = event.data.webApp;
|
|
||||||
window.removeEventListener('message', receiveMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('message', receiveMessage, false);
|
|
||||||
|
|
||||||
return window.dash_clientside.no_update;
|
|
||||||
}
|
|
||||||
""",
|
|
||||||
Output("hidden-div", "children"),
|
|
||||||
Input("interval-component", "n_intervals"),
|
|
||||||
)
|
|
||||||
|
|
||||||
app.clientside_callback(
|
|
||||||
"""
|
|
||||||
function(pathname) {
|
|
||||||
window.parent.postMessage({ type: 'iframe-url-changed', pathname }, '*');
|
|
||||||
}
|
|
||||||
""",
|
|
||||||
Input("url", "pathname"),
|
|
||||||
)
|
|
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "standard.miniapp",
|
|
||||||
"name": "Miniapp",
|
|
||||||
"description": "Очень полезный модуль",
|
|
||||||
"author": "Karkas Team",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"privileged": false,
|
|
||||||
"dependencies": {
|
|
||||||
"required": {
|
|
||||||
"standard.config": {
|
|
||||||
"version": "^1.0.0",
|
|
||||||
"uses": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pythonDependencies": {
|
|
||||||
"required": {
|
|
||||||
"dash": "^2.17.1",
|
|
||||||
"dash_extensions": "^1.0.18",
|
|
||||||
"dash_bootstrap_components": "^1.6.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,191 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
from collections import OrderedDict
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import dash
|
|
||||||
import dash_bootstrap_components as dbc
|
|
||||||
import flask
|
|
||||||
from dash_extensions.enrich import DashBlueprint, DashProxy, Input, Output, dcc, html
|
|
||||||
from dash_extensions.pages import setup_page_components
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import get_module, log
|
|
||||||
|
|
||||||
from .dash_telegram_auth import get_auth_server, setup_auth_clientcallbacks
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from karkas_blocks.standard.config import IConfig
|
|
||||||
from karkas_blocks.standard.roles import Roles as IRoles
|
|
||||||
|
|
||||||
pages = OrderedDict()
|
|
||||||
|
|
||||||
|
|
||||||
def register_page(name, path, blueprint, prefix="", role="USER"):
|
|
||||||
pages[path] = {
|
|
||||||
"name": name,
|
|
||||||
"blueprint": blueprint,
|
|
||||||
"prefix": prefix,
|
|
||||||
"role": role,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def register_home_page():
|
|
||||||
page = DashBlueprint()
|
|
||||||
page.layout = html.Div([html.H1("Главная")])
|
|
||||||
register_page("Главная", path="/", blueprint=page)
|
|
||||||
|
|
||||||
|
|
||||||
register_home_page()
|
|
||||||
|
|
||||||
config: "IConfig" = get_module("standard.config", "config")
|
|
||||||
Roles: "type[IRoles]" = get_module("standard.roles", "Roles")
|
|
||||||
|
|
||||||
|
|
||||||
def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
|
|
||||||
log(requests_pathname_prefix)
|
|
||||||
|
|
||||||
real_prefix = f"{requests_pathname_prefix}_internal/"
|
|
||||||
|
|
||||||
server = get_auth_server(config.get("core::token"))
|
|
||||||
|
|
||||||
app = DashProxy(
|
|
||||||
pages_folder="",
|
|
||||||
use_pages=True,
|
|
||||||
suppress_callback_exceptions=True,
|
|
||||||
external_stylesheets=[
|
|
||||||
dbc.themes.BOOTSTRAP,
|
|
||||||
dbc.icons.BOOTSTRAP,
|
|
||||||
],
|
|
||||||
external_scripts=[
|
|
||||||
#
|
|
||||||
"https://telegram.org/js/telegram-web-app.js"
|
|
||||||
],
|
|
||||||
server=server,
|
|
||||||
requests_pathname_prefix=real_prefix,
|
|
||||||
routes_pathname_prefix="/_internal/",
|
|
||||||
# requests_pathname_prefix=requests_pathname_prefix,
|
|
||||||
meta_tags=[
|
|
||||||
{"name": "viewport", "content": "width=device-width, initial-scale=1"},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
# app.enable_dev_tools(
|
|
||||||
# dev_tools_ui=True,
|
|
||||||
# dev_tools_serve_dev_bundles=True,
|
|
||||||
# )
|
|
||||||
|
|
||||||
# Register pages
|
|
||||||
for path, page in pages.items():
|
|
||||||
page["blueprint"].register(app, path, prefix=page["prefix"])
|
|
||||||
|
|
||||||
# Create navbar
|
|
||||||
navbar = dbc.Navbar(
|
|
||||||
dbc.Container(
|
|
||||||
[
|
|
||||||
dbc.Button(
|
|
||||||
html.I(className="bi bi-list"),
|
|
||||||
id="open-offcanvas",
|
|
||||||
color="light",
|
|
||||||
className="me-2",
|
|
||||||
),
|
|
||||||
dbc.NavbarBrand("Karkas"),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
color="primary",
|
|
||||||
dark=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
roles = Roles()
|
|
||||||
|
|
||||||
def create_layout():
|
|
||||||
user = getattr(flask.g, "user", None)
|
|
||||||
|
|
||||||
if not user:
|
|
||||||
return html.Div()
|
|
||||||
|
|
||||||
user_id = user["id"]
|
|
||||||
user_permission = asyncio.run(roles.get_user_permission(user_id)) or "USER"
|
|
||||||
|
|
||||||
available_pages = {
|
|
||||||
path: page
|
|
||||||
for path, page in pages.items()
|
|
||||||
if (isinstance(page["role"], list) and user_permission in page["role"])
|
|
||||||
or page["role"] == user_permission
|
|
||||||
or page["role"] == "USER"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create sidebar
|
|
||||||
sidebar = dbc.Offcanvas(
|
|
||||||
id="offcanvas",
|
|
||||||
title="Меню",
|
|
||||||
is_open=False,
|
|
||||||
children=[
|
|
||||||
dbc.Nav(
|
|
||||||
[
|
|
||||||
dbc.NavLink(
|
|
||||||
page["name"],
|
|
||||||
href=f"{real_prefix}/{path.lstrip('/')}",
|
|
||||||
id={"type": "nav-link", "index": path},
|
|
||||||
)
|
|
||||||
for path, page in available_pages.items()
|
|
||||||
],
|
|
||||||
vertical=True,
|
|
||||||
pills=True,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
layout = html.Div(
|
|
||||||
[
|
|
||||||
dcc.Location(id="url", refresh=False),
|
|
||||||
dcc.Interval(
|
|
||||||
id="init-telegram-interval",
|
|
||||||
interval=100,
|
|
||||||
n_intervals=0,
|
|
||||||
max_intervals=1,
|
|
||||||
),
|
|
||||||
navbar,
|
|
||||||
sidebar,
|
|
||||||
dash.page_container,
|
|
||||||
setup_page_components(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
return layout
|
|
||||||
|
|
||||||
app.layout = create_layout
|
|
||||||
|
|
||||||
setup_auth_clientcallbacks(app)
|
|
||||||
|
|
||||||
# Открытие на кнопку меню
|
|
||||||
app.clientside_callback(
|
|
||||||
"""
|
|
||||||
function(n_clicks) {
|
|
||||||
if (n_clicks == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
""",
|
|
||||||
Output(
|
|
||||||
"offcanvas",
|
|
||||||
"is_open",
|
|
||||||
),
|
|
||||||
Input("open-offcanvas", "n_clicks"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Закрываем offcanvas при клике на ссылку в меню
|
|
||||||
app.clientside_callback(
|
|
||||||
"""
|
|
||||||
function(n_clicks) {
|
|
||||||
if (n_clicks == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
""",
|
|
||||||
Output("offcanvas", "is_open", allow_duplicate=True),
|
|
||||||
Input({"type": "nav-link", "index": dash.dependencies.ALL}, "n_clicks"),
|
|
||||||
prevent_initial_call="initial_duplicate",
|
|
||||||
)
|
|
||||||
|
|
||||||
return app
|
|
@ -1,54 +0,0 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from aiogram import types
|
|
||||||
from fastapi.middleware.wsgi import WSGIMiddleware
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import (
|
|
||||||
Storage,
|
|
||||||
get_module,
|
|
||||||
set_chat_menu_button,
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from karkas_blocks.standard.config import IConfig
|
|
||||||
|
|
||||||
config: "IConfig" = get_module("standard.config", "config")
|
|
||||||
|
|
||||||
|
|
||||||
def get_link():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def module_init():
|
|
||||||
|
|
||||||
config.register(
|
|
||||||
"miniapp::prefix",
|
|
||||||
"string",
|
|
||||||
default_value="/webapp/",
|
|
||||||
visible=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
config.register(
|
|
||||||
"miniapp::public_url",
|
|
||||||
"string",
|
|
||||||
visible=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def register_page():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
async def module_late_init():
|
|
||||||
from .lib import create_dash_app
|
|
||||||
|
|
||||||
dash_app = create_dash_app(requests_pathname_prefix=config.get("miniapp::prefix"))
|
|
||||||
|
|
||||||
Storage.set("webapp", WSGIMiddleware(dash_app.server))
|
|
||||||
|
|
||||||
web_app_info = types.WebAppInfo(url=config.get("miniapp::public_url"))
|
|
||||||
menu_button = types.MenuButtonWebApp(text="Меню", web_app=web_app_info)
|
|
||||||
|
|
||||||
await set_chat_menu_button(menu_button)
|
|
@ -1,36 +0,0 @@
|
|||||||
# Модуль Roles
|
|
||||||
|
|
||||||
Модуль `roles` управляет ролями пользователей.
|
|
||||||
|
|
||||||
## Роли
|
|
||||||
|
|
||||||
- `USER` - обычный пользователь.
|
|
||||||
- `MODERATOR` - модератор.
|
|
||||||
- `ADMIN` - администратор.
|
|
||||||
- `BOT` - бот.
|
|
||||||
|
|
||||||
## Функциональность
|
|
||||||
|
|
||||||
- Проверка роли пользователя.
|
|
||||||
- Получение имени роли по ID.
|
|
||||||
- Получение ID роли по имени.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
1. Импортируйте класс `Roles`.
|
|
||||||
2. Создайте экземпляр класса.
|
|
||||||
3. Вызовите методы класса для проверки роли пользователя или получения имени роли.
|
|
||||||
|
|
||||||
## Пример
|
|
||||||
|
|
||||||
```python
|
|
||||||
from karkas_core.modules_system.public_api import get_module
|
|
||||||
|
|
||||||
Roles = get_module("standard.roles", "Roles")
|
|
||||||
|
|
||||||
roles = Roles()
|
|
||||||
|
|
||||||
is_admin = await roles.check_admin_permission(user_id)
|
|
||||||
|
|
||||||
role_name = await roles.get_role_name(role_id)
|
|
||||||
```
|
|
@ -1,2 +0,0 @@
|
|||||||
from .main import module_init
|
|
||||||
from .roles import Roles
|
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "standard.roles",
|
|
||||||
"name": "Roles",
|
|
||||||
"description": "Модуль для работы с ролями",
|
|
||||||
"author": "Karkas Team",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"privileged": true,
|
|
||||||
"dependencies": {
|
|
||||||
"required": {
|
|
||||||
"standard.config": "^1.0.0",
|
|
||||||
"standard.database": "^1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import get_module
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from karkas_blocks.standard.config import IConfig
|
|
||||||
|
|
||||||
|
|
||||||
def module_init():
|
|
||||||
config: "IConfig" = get_module("standard.config", "config")
|
|
||||||
|
|
||||||
config.register(
|
|
||||||
"roles::admin",
|
|
||||||
"number",
|
|
||||||
default_value=2,
|
|
||||||
visible=False,
|
|
||||||
)
|
|
||||||
config.register(
|
|
||||||
"roles::moderator",
|
|
||||||
"number",
|
|
||||||
default_value=1,
|
|
||||||
visible=False,
|
|
||||||
)
|
|
||||||
config.register(
|
|
||||||
"roles::user",
|
|
||||||
"number",
|
|
||||||
default_value=0,
|
|
||||||
visible=False,
|
|
||||||
)
|
|
||||||
config.register(
|
|
||||||
"roles::bot",
|
|
||||||
"number",
|
|
||||||
default_value=3,
|
|
||||||
visible=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pass
|
|
@ -1,72 +0,0 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from karkas_core.modules_system.public_api import get_module
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from karkas_blocks.standard.config import IConfig
|
|
||||||
from karkas_blocks.standard.database.db_api import get_user_role as IGetUserRoleType
|
|
||||||
|
|
||||||
get_user_role: "IGetUserRoleType" = get_module(
|
|
||||||
"standard.database", "db_api.get_user_role"
|
|
||||||
)
|
|
||||||
config: "IConfig" = get_module("standard.config", "config")
|
|
||||||
|
|
||||||
|
|
||||||
class Roles:
|
|
||||||
user = "USER"
|
|
||||||
moderator = "MODERATOR"
|
|
||||||
admin = "ADMIN"
|
|
||||||
bot = "BOT"
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_roles(self):
|
|
||||||
self.user_role_id = config.get("roles::user")
|
|
||||||
self.moderator_role_id = config.get("roles::moderator")
|
|
||||||
self.admin_role_id = config.get("roles::admin")
|
|
||||||
self.bot_role_id = config.get("roles::bot")
|
|
||||||
|
|
||||||
async def check_admin_permission(self, user_id):
|
|
||||||
self.update_roles()
|
|
||||||
match get_user_role(user_id):
|
|
||||||
case self.admin_role_id:
|
|
||||||
return True
|
|
||||||
case _:
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def check_moderator_permission(self, user_id):
|
|
||||||
self.update_roles()
|
|
||||||
match get_user_role(user_id):
|
|
||||||
case self.moderator_role_id:
|
|
||||||
return True
|
|
||||||
case _:
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def get_role_name(self, role_id):
|
|
||||||
self.update_roles()
|
|
||||||
match role_id:
|
|
||||||
case self.admin_role_id:
|
|
||||||
return self.admin
|
|
||||||
case self.moderator_role_id:
|
|
||||||
return self.moderator
|
|
||||||
case self.user_role_id:
|
|
||||||
return self.user
|
|
||||||
case self.bot_role_id:
|
|
||||||
return self.bot
|
|
||||||
case _:
|
|
||||||
raise ValueError(f"Нет роли с id={role_id}")
|
|
||||||
|
|
||||||
async def get_user_permission(self, user_id):
|
|
||||||
self.update_roles()
|
|
||||||
match get_user_role(user_id):
|
|
||||||
case self.admin_role_id:
|
|
||||||
return self.admin
|
|
||||||
case self.moderator_role_id:
|
|
||||||
return self.moderator
|
|
||||||
case self.user_role_id:
|
|
||||||
return self.user
|
|
||||||
case self.bot_role_id:
|
|
||||||
return self.bot
|
|
||||||
case _:
|
|
||||||
return None
|
|
@ -1,80 +0,0 @@
|
|||||||
import os
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from ...database.db_api import add_user, connect_database, create_tables
|
|
||||||
from ..roles import Roles
|
|
||||||
|
|
||||||
|
|
||||||
class TestRoles(unittest.IsolatedAsyncioTestCase):
|
|
||||||
database = None
|
|
||||||
path = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.roles = Roles()
|
|
||||||
cls.database, cls.path = connect_database(is_test=True, module="roles")
|
|
||||||
create_tables(cls.database)
|
|
||||||
|
|
||||||
add_user(
|
|
||||||
user_id=1,
|
|
||||||
user_name="TestUser1",
|
|
||||||
user_tag="TestTag1",
|
|
||||||
user_role=0,
|
|
||||||
user_stats=30,
|
|
||||||
user_rep=15,
|
|
||||||
)
|
|
||||||
add_user(
|
|
||||||
user_id=2,
|
|
||||||
user_name="TestUser3",
|
|
||||||
user_tag="TestTag3",
|
|
||||||
user_role=1,
|
|
||||||
user_stats=30,
|
|
||||||
user_rep=15,
|
|
||||||
)
|
|
||||||
add_user(
|
|
||||||
user_id=3,
|
|
||||||
user_name="TestUser4",
|
|
||||||
user_tag="TestTag4",
|
|
||||||
user_role=2,
|
|
||||||
user_stats=30,
|
|
||||||
user_rep=15,
|
|
||||||
)
|
|
||||||
add_user(
|
|
||||||
user_id=4,
|
|
||||||
user_name="TestUser2",
|
|
||||||
user_tag="TestTag2",
|
|
||||||
user_role=3,
|
|
||||||
user_stats=30,
|
|
||||||
user_rep=15,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def test_check_admin_permission(cls):
|
|
||||||
cls.assertTrue(await cls.roles.check_admin_permission(1))
|
|
||||||
cls.assertFalse(await cls.roles.check_admin_permission(2))
|
|
||||||
cls.assertFalse(await cls.roles.check_admin_permission(3))
|
|
||||||
cls.assertFalse(await cls.roles.check_admin_permission(4))
|
|
||||||
|
|
||||||
async def test_check_moderator_permission(cls):
|
|
||||||
cls.assertTrue(await cls.roles.check_moderator_permission(2))
|
|
||||||
cls.assertFalse(await cls.roles.check_moderator_permission(0))
|
|
||||||
cls.assertFalse(await cls.roles.check_moderator_permission(1))
|
|
||||||
cls.assertFalse(await cls.roles.check_moderator_permission(3))
|
|
||||||
|
|
||||||
async def test_get_role_name(cls):
|
|
||||||
cls.assertEqual(await cls.roles.get_role_name(cls.roles.admin_role_id), "ADMIN")
|
|
||||||
cls.assertEqual(
|
|
||||||
await cls.roles.get_role_name(cls.roles.moderator_role_id), "MODERATOR"
|
|
||||||
)
|
|
||||||
cls.assertEqual(await cls.roles.get_role_name(cls.roles.user_role_id), "USER")
|
|
||||||
cls.assertEqual(await cls.roles.get_role_name(cls.roles.bot_role_id), "BOT")
|
|
||||||
with cls.assertRaises(ValueError):
|
|
||||||
await cls.roles.get_role_name(999) # Несуществующий ID роли
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
cls.database.close()
|
|
||||||
os.system(f"rm {cls.path}") # nosec
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
@ -9,13 +9,13 @@ readme = "README.md"
|
|||||||
python = "~3.12"
|
python = "~3.12"
|
||||||
karkas-core = { path = "../karkas_core", develop = true }
|
karkas-core = { path = "../karkas_core", develop = true }
|
||||||
|
|
||||||
peewee = "^3.17.6"
|
# peewee = "^3.17.6"
|
||||||
|
|
||||||
pyyaml = "^6.0.1"
|
pyyaml = "^6.0.1"
|
||||||
|
|
||||||
dash = "^2.17.1"
|
# dash = "^2.17.1"
|
||||||
dash-extensions = "^1.0.18"
|
# dash-extensions = "^1.0.18"
|
||||||
dash-bootstrap-components = "^1.6.0"
|
# dash-bootstrap-components = "^1.6.0"
|
||||||
|
|
||||||
[tool.poetry-monorepo.deps]
|
[tool.poetry-monorepo.deps]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
Loading…
Reference in New Issue
Block a user