0
0
mirror of https://gitflic.ru/project/maks1ms/ocab.git synced 2025-04-03 16:13:46 +03:00

убрал legacy модули и отрефакторил admin, filters

This commit is contained in:
Maxim Slipenko 2024-07-13 17:36:34 +03:00
parent 4a609db595
commit 212c2836c9
29 changed files with 310 additions and 67 deletions

115
scripts/module.py Normal file
View File

@ -0,0 +1,115 @@
import argparse
import json
import os
DEFAULTS = {
"description": "Очень полезный модуль",
"author": "OCAB Team",
"version": "1.0.0",
"privileged": "false",
}
def create_module(args):
module_dir = os.path.join("src/ocab_modules/standard", args.module_name)
os.makedirs(module_dir, exist_ok=True)
module_info = {
"id": args.id,
"name": args.name,
"description": args.description,
"author": args.author,
"version": args.version,
"privileged": args.privileged.lower() == "true",
"dependencies": {},
}
with open(os.path.join(module_dir, "info.json"), "w", encoding="utf-8") as f:
json.dump(module_info, f, ensure_ascii=False, indent=4)
with open(os.path.join(module_dir, "__init__.py"), "w", encoding="utf-8") as f:
f.write("# Init file for the module\n")
print(f"Module {args.module_name} created successfully.")
def interactive_mode(args):
def get_input(prompt, default=None):
if default:
value = input(f"{prompt} [{default}]: ")
return value if value else default
else:
value = input(f"{prompt}: ")
return value
module_name = get_input("Введите название модуля (папки)")
module_id = get_input("Введите ID")
name = get_input("Введите название модуля")
description = get_input(
"Введите описание модуля", args.description or DEFAULTS["description"]
)
author = get_input("Введите автора", args.author or DEFAULTS["author"])
version = get_input("Введите версию", args.version or DEFAULTS["version"])
privileged = get_input(
"Модуль привилегированный (true/false)",
args.privileged or DEFAULTS["privileged"],
)
args = argparse.Namespace(
command="create",
module_name=module_name,
id=module_id,
name=name,
description=description,
author=author,
version=version,
privileged=privileged,
dependencies="",
)
create_module(args)
def main():
parser = argparse.ArgumentParser(
description="Утилита для создания директории модуля с файлами."
)
subparsers = parser.add_subparsers(dest="command", required=True)
create_parser = subparsers.add_parser("create", help="Создать новый модуль")
create_parser.add_argument("--module_name", help="Название директории модуля")
create_parser.add_argument("--id", help="ID модуля")
create_parser.add_argument("--name", help="Название модуля")
create_parser.add_argument("--description", help="Описание модуля")
create_parser.add_argument("--author", help="Автор модуля")
create_parser.add_argument("--version", help="Версия модуля")
create_parser.add_argument(
"--privileged", help="Привилегированный модуль (true/false)"
)
create_parser.add_argument(
"--dependencies", help="Список зависимостей в формате имя:версия через запятую"
)
args = parser.parse_args()
if args.command == "create":
if not all(
[
args.module_name,
args.id,
args.name,
args.description,
args.author,
args.version,
args.privileged,
args.dependencies,
]
):
print("Переход в интерактивный режим...")
interactive_mode(args)
else:
create_module(args)
if __name__ == "__main__":
main()

View File

@ -17,6 +17,7 @@ bot_modules = [
UnsafeFSLoader(f"{paths.modules_standard}/roles"),
FSLoader(f"{paths.modules_standard}/command_helper"),
FSLoader(f"{paths.modules_standard}/info"),
FSLoader(f"{paths.modules_standard}/filters"),
FSLoader(f"{paths.modules_standard}/create_report_apps"),
FSLoader(f"{paths.modules_standard}/admin"),
]

View File

@ -3,12 +3,12 @@ from pathlib import Path
from RestrictedPython import compile_restricted_exec
from ocab_core.modules_system.loaders.fs_loader.policy import (
from ocab_core.modules_system.loaders.unsafe_fs_loader import UnsafeFSLoader
from ocab_core.modules_system.safe.policy import (
ALLOWED_IMPORTS,
BUILTINS,
RestrictedPythonPolicy,
)
from ocab_core.modules_system.loaders.unsafe_fs_loader import UnsafeFSLoader
class FSLoader(UnsafeFSLoader):

View File

@ -1,9 +1,8 @@
from ocab_core.logger import log # noqa
from .public_api import (
Storage,
get_fsm_context,
get_module,
log,
register_outer_message_middleware,
register_router,
set_my_commands,

View File

@ -5,8 +5,11 @@ from aiogram import BaseMiddleware, Router
from aiogram.fsm.context import FSMContext
from aiogram.fsm.storage.base import StorageKey
from ocab_core.logger import log_new
from ocab_core.singleton import Singleton
log = log_new
def register_router(router: Router):
app = Singleton()

View File

@ -9,9 +9,14 @@ from RestrictedPython import (
utility_builtins,
)
from RestrictedPython.Eval import default_guarded_getitem, default_guarded_getiter
from RestrictedPython.Guards import full_write_guard, safer_getattr
from RestrictedPython.Guards import (
full_write_guard,
guarded_unpack_sequence,
safer_getattr,
)
from ocab_core.logger import log_new
from ocab_core.modules_system.safe.zope_guards import extra_safe_builtins
class RestrictedPythonPolicy(RestrictingNodeTransformer):
@ -83,7 +88,6 @@ ALLOWED_IMPORTS = [
def safes_getattr(object, name, default=None, getattr=safer_getattr):
if isinstance(object, Bot) and name == "token":
log_new("Bot.token is not allowed")
raise Exception("Bot.token is not allowed")
@ -94,21 +98,12 @@ def safes_getattr(object, name, default=None, getattr=safer_getattr):
BUILTINS = safe_builtins.copy()
BUILTINS.update(utility_builtins)
BUILTINS.update(limited_builtins)
BUILTINS.update(extra_safe_builtins)
BUILTINS["__metaclass__"] = _metaclass
BUILTINS["_getitem_"] = default_guarded_getitem
BUILTINS["_getattr_"] = safes_getattr
BUILTINS["_getiter_"] = default_guarded_getiter
BUILTINS["_write_"] = full_write_guard
BUILTINS["_unpack_sequence_"] = guarded_unpack_sequence
BUILTINS["staticmethod"] = staticmethod
class GuardedDictType:
def __call__(self, *args, **kwargs):
return dict(*args, **kwargs)
@staticmethod
def fromkeys(iterable, value=None):
return dict.fromkeys(iterable, value)
BUILTINS["dict"] = GuardedDictType()
BUILTINS["tuple"] = tuple

View File

@ -0,0 +1,111 @@
#############################################################################
#
# Copyright (c) 2024 OCAB Team
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software includes a function derived from the software subject to the
# provisions of the Zope Public License, Version 2.1 (ZPL). A copy of the ZPL
# should accompany this distribution. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY
# AND ALL EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST
# INFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE.
#
#
##############################################################################
extra_safe_builtins = {}
class GuardedDictType:
def __call__(self, *args, **kwargs):
return dict(*args, **kwargs)
def fromkeys(self, S, v=None):
return dict.fromkeys(S, v)
extra_safe_builtins["dict"] = GuardedDictType()
ContainerAssertions = {
type(()): 1,
bytes: 1,
str: 1,
range: 1,
}
Containers = ContainerAssertions.get
def _error(index):
raise Exception("unauthorized access to element")
def guard(container, value, index=None):
# if Containers(type(container)) and Containers(type(value)):
# # Simple type. Short circuit.
# return
# I don't know how to do this.
# if getSecurityManager().validate(container, container, index, value):
# return
# _error(index)
return
class SafeIter:
__allow_access_to_unprotected_subobjects__ = 1
def __init__(self, ob, container=None):
self._iter = iter(ob)
if container is None:
container = ob
self.container = container
def __iter__(self):
return self
def __next__(self):
ob = next(self._iter)
guard(self.container, ob)
return ob
next = __next__
class NullIter(SafeIter):
def __init__(self, ob):
self._iter = ob
def __next__(self):
return next(self._iter)
next = __next__
def guarded_iter(*args):
if len(args) == 1:
i = args[0]
# Don't double-wrap
if isinstance(i, SafeIter):
return i
if not isinstance(i, range):
return SafeIter(i)
# Other call styles / targets don't need to be guarded
return NullIter(iter(*args))
extra_safe_builtins["iter"] = guarded_iter
def guarded_any(seq):
return any(guarded_iter(seq))
extra_safe_builtins["any"] = guarded_any
def guarded_all(seq):
return all(guarded_iter(seq))
extra_safe_builtins["all"] = guarded_all

View File

@ -1,8 +1,5 @@
from aiogram import Dispatcher
from src.ocab_modules.standard.admin.routers import router as admin_router
# from src.modules.standard.info.routers import router as info_router
from src.ocab_modules.standard.message_processing.message_api import (
router as process_message,
)
@ -14,5 +11,5 @@ async def include_routers(dp: Dispatcher):
dp.include_router()
"""
# dp.include_router(info_router)
dp.include_router(admin_router)
# dp.include_router(admin_router)
dp.include_router(process_message)

View File

@ -6,8 +6,8 @@ import time
import aiogram
import aiohttp
from ...standard.config.config import *
from ...standard.roles.roles import *
from ocab_modules.standard.config.config import *
from ocab_modules.standard.roles.roles import *
class Moderation:

View File

@ -5,18 +5,12 @@ import random
from threading import Thread
from aiogram import Bot
from aiogram.types import Message
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 *
from src.ocab_modules.standard.moderation.moderation import (
ban_user,
mute_user,
unmute_user,
)
from src.ocab_modules.standard.roles.roles import Roles
async def create_math_task():

View File

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

View File

@ -1 +1 @@
from . import routers
from .main import module_init

View File

@ -1,10 +1,10 @@
# flake8: noqa
import time
from aiogram import Bot
from aiogram.types import Message
from src.ocab_modules.standard.config.config import get_default_chat_tag
from ocab_core.modules_system.public_api import get_module
get_default_chat_tag = get_module("standard.config", "get_default_chat_tag")
async def delete_message(message: Message, bot: Bot):

View File

@ -1,6 +1,11 @@
{
"id": "standard.admin",
"name": "Admin",
"description": "Модуль для работы с админкой",
"author": "OCAB Team",
"version": "1.0"
"version": "1.0.0",
"privileged": false,
"dependencies": {
"standard.filters": "^1.0.0"
}
}

View File

@ -0,0 +1,7 @@
from ocab_core.modules_system.public_api import register_router
from .routers import router
async def module_init():
register_router(router)

View File

@ -1,24 +1,30 @@
# flake8: noqa
from aiogram import F, Router
from aiogram.filters import Command
from src.ocab_modules.standard.admin.handlers import (
from ocab_core.modules_system.public_api import get_module, log
from .handlers import (
chat_not_in_approve_list,
delete_message,
error_access,
get_chat_id,
)
from src.ocab_modules.standard.filters.filters import (
ChatModerOrAdminFilter,
ChatNotInApproveFilter,
(ChatModerOrAdminFilter, ChatNotInApproveFilter) = get_module(
"standard.filters", ["ChatModerOrAdminFilter", "ChatNotInApproveFilter"]
)
log(ChatModerOrAdminFilter)
log(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(), F.text == "/chatID")
router.message.register(get_chat_id, ChatModerOrAdminFilter(), Command("chatID"))
router.message.register(delete_message, ChatModerOrAdminFilter(), F.text == "/rm")
router.message.register(error_access, F.text == "/rm")
router.message.register(error_access, F.text == "/chatID")
router.message.register(delete_message, ChatModerOrAdminFilter(), Command("rm"))
router.message.register(error_access, Command("rm"))
router.message.register(error_access, Command("chatID"))

View File

@ -1 +1 @@
from .main import module_init, module_late_init, register_command
from .main import module_init, register_command

View File

@ -1 +1 @@
from .config import config
from .config import get_approved_chat_id, get_default_chat_tag, get_roles

View File

@ -27,13 +27,17 @@ def get_telegram_check_bot() -> bool:
return config["TELEGRAM"]["CHECK_BOT"]
def get_aproved_chat_id() -> list:
def get_approved_chat_id() -> list:
# Возваращем сплитованный список id чатов в формате int
return [
int(chat_id) for chat_id in config["TELEGRAM"]["APPROVED_CHAT_ID"].split(" | ")
]
def get_roles():
return config["ROLES"]
def get_user_role_name(role_number) -> dict:
# Возвращаем название роли пользвателя по номеру роли, если такой роли нет, возвращаем неизвестно
return config["ROLES"].get(role_number, "Неизвестно")

View File

@ -0,0 +1 @@
from .filters import ChatModerOrAdminFilter, ChatNotInApproveFilter

View File

@ -2,9 +2,10 @@ from aiogram import Bot
from aiogram.filters import BaseFilter
from aiogram.types import Message
from ocab_core.logger import log
from ocab_modules.standard.config.config import get_aproved_chat_id
from ocab_modules.standard.roles.roles import Roles
from ocab_core.modules_system.public_api import get_module, log
get_approved_chat_id = get_module("standard.config", "get_approved_chat_id")
Roles = get_module("standard.roles", "Roles")
class ChatModerOrAdminFilter(BaseFilter):
@ -21,14 +22,11 @@ class ChatModerOrAdminFilter(BaseFilter):
class ChatNotInApproveFilter(BaseFilter):
async def __call__(self, message: Message, bot: Bot) -> bool:
# print("chat_check")
await log("chat_check")
log("chat_check")
chat_id = message.chat.id
if chat_id in get_aproved_chat_id():
# print(f"Chat in approve list: {chat_id}")
await log(f"Chat in approve list: {chat_id}")
if chat_id in get_approved_chat_id():
log(f"Chat in approve list: {chat_id}")
return False
else:
# print(f"Chat not in approve list: {chat_id}")
await log(f"Chat not in approve list: {chat_id}")
log(f"Chat not in approve list: {chat_id}")
return True

View File

@ -1,6 +1,12 @@
{
"id": "standard.filters",
"name": "Filters",
"description": "Модуль с фильтрами",
"author": "OCAB Team",
"version": "1.0"
"version": "1.0.0",
"privileged": false,
"dependencies": {
"standard.roles": "^1.0.0",
"standard.config": "^1.0.0"
}
}

View File

@ -5,7 +5,7 @@ from aiogram import Bot, F, Router, types
from ocab_core.logger import log
from ocab_modules.external.yandexgpt.handlers import answer_to_message
from ocab_modules.standard.config.config import (
get_aproved_chat_id,
get_approved_chat_id,
get_yandexgpt_in_words,
get_yandexgpt_start_words,
)
@ -17,7 +17,7 @@ async def chat_check(message: types.Message):
# Если чата нет в базе данных, то проверяем его в наличии в конфиге и если он там есть то добавляем его в БД
# Если чат есть в базе данных, то pass
if get_chat(message.chat.id) is None:
if message.chat.id in get_aproved_chat_id():
if message.chat.id in get_approved_chat_id():
# print(f"Chat in approve list: {message.chat.id} {message.chat.title}")
await log(f"Chat in approve list: {message.chat.id} {message.chat.title}")
add_chat(message.chat.id, message.chat.title)

View File

@ -1,7 +1,9 @@
from ocab_core.modules_system.public_api import get_module
get_user_role = get_module("standard.database", "db_api.get_user_role")
config: dict = get_module("standard.config", "config")
get_roles = get_module("standard.config", "get_roles")
roles = get_roles()
class Roles:
@ -9,13 +11,12 @@ class Roles:
moderator = "MODERATOR"
admin = "ADMIN"
bot = "BOT"
__roles = config["ROLES"]
def __init__(self):
self.user_role_id = self.__roles[self.user]
self.moderator_role_id = self.__roles[self.moderator]
self.admin_role_id = self.__roles[self.admin]
self.bot_role_id = self.__roles[self.bot]
self.user_role_id = roles[self.user]
self.moderator_role_id = roles[self.moderator]
self.admin_role_id = roles[self.admin]
self.bot_role_id = roles[self.bot]
async def check_admin_permission(self, user_id):
match get_user_role(user_id):