добавлен модуль standard.reports

- добавлен модуль standard.reports
- убрана обязательная зависимость в других модулях
- в config добавлен multiple
This commit is contained in:
Maxim Slipenko 2024-08-11 09:47:38 +03:00
parent 067fa52719
commit 01912850e2
13 changed files with 159 additions and 92 deletions

View File

@ -10,7 +10,9 @@ async def main():
[ [
module_loader("standard", "config", safe=False), module_loader("standard", "config", safe=False),
module_loader("standard", "command_helper"), module_loader("standard", "command_helper"),
# module_loader("standard", "report"), # safe=False из-за super().__init__()
module_loader("standard", "filters", safe=False),
module_loader("standard", "report"),
] ]
) )
await ocab.start() await ocab.start()

View File

@ -7,7 +7,8 @@
"privileged": false, "privileged": false,
"dependencies": { "dependencies": {
"required": { "required": {
"standard.filters": "^1.0.0" "standard.filters": "^1.0.0",
"standard.roles": "^1.0.0"
} }
} }
} }

View File

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

View File

@ -5,10 +5,5 @@
"author": "OCAB Team", "author": "OCAB Team",
"version": "1.0.0", "version": "1.0.0",
"privileged": false, "privileged": false,
"dependencies": { "dependencies": {}
"required": {
"standard.roles": "^1.0.0",
"standard.database": "^1.0.0"
}
}
} }

View File

@ -1,28 +1,11 @@
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict from aiogram.types import BotCommand
from aiogram import BaseMiddleware
from aiogram.types import BotCommand, TelegramObject
from ocab_core.modules_system.public_api import ( from ocab_core.modules_system.public_api import (
get_module, set_my_commands, log
register_outer_message_middleware,
set_my_commands,
) )
if TYPE_CHECKING:
from ocab_modules.standard.database import db_api as IDbApi
from ocab_modules.standard.roles import Roles as IRoles
commands = dict() commands = dict()
db_api: "IDbApi" = get_module(
"standard.database",
"db_api",
)
Roles: "IRoles" = get_module("standard.roles", "Roles")
def register_command(command, description, role="USER"): def register_command(command, description, role="USER"):
if role not in commands: if role not in commands:
commands[role] = dict() commands[role] = dict()
@ -31,53 +14,6 @@ def register_command(command, description, role="USER"):
} }
class OuterMiddleware(BaseMiddleware):
async def __call__(
self,
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any],
):
# if not isinstance(event, Message):
# return await handler(event, data)
#
# user = db_api.get_user(event.from_user.id)
#
# if user is None:
# return
#
# roles = Roles()
# role_name = await roles.get_role_name(role_id=user.user_role)
#
# if role_name not in commands:
# return await handler(event, data)
# bot_commands = []
# for role_command in commands[role_name]:
# bot_commands.append(
# BotCommand(
# command=role_command,
# description=commands[role_name][role_command]["description"],
# )
# )
# await event.bot.set_my_commands(
# bot_commands,
# BotCommandScopeChatMember(
# chat_id=event.chat.id,
# user_id=event.from_user.id,
# ),
# )
return await handler(event, data)
async def module_init():
register_outer_message_middleware(OuterMiddleware())
async def set_user_commands(): async def set_user_commands():
bot_commands = [] bot_commands = []
if "USER" in commands: if "USER" in commands:
@ -90,6 +26,8 @@ async def set_user_commands():
) )
) )
log(bot_commands)
await set_my_commands( await set_my_commands(
bot_commands, bot_commands,
) )

View File

@ -84,7 +84,8 @@ class ConfigManager:
key: str, key: str,
value_type: str, value_type: str,
options: List[Any] = None, options: List[Any] = None,
default_value=None, multiple: bool = False,
default_value = None,
editable: bool = True, editable: bool = True,
shared: bool = False, shared: bool = False,
required: bool = False, required: bool = False,
@ -101,6 +102,7 @@ class ConfigManager:
self._metadata[key] = { self._metadata[key] = {
"type": value_type, "type": value_type,
"multiple": multiple,
"options": options, "options": options,
"default_value": default_value, "default_value": default_value,
"visible": visible, "visible": visible,

View File

@ -1,6 +1,7 @@
from .filters import ( from .filters import (
ChatModerOrAdminFilter, ChatModerOrAdminFilter,
ChatNotInApproveFilter, ChatNotInApproveFilter,
ChatIDFilter,
chat_not_in_approve, chat_not_in_approve,
module_init, module_init,
) )

View File

@ -1,4 +1,5 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing_extensions import deprecated
from aiogram import Bot from aiogram import Bot
from aiogram.filters import BaseFilter from aiogram.filters import BaseFilter
@ -8,35 +9,63 @@ from ocab_core.modules_system.public_api import get_module, log
if TYPE_CHECKING: if TYPE_CHECKING:
from ocab_modules.standard.config import IConfig from ocab_modules.standard.config import IConfig
from ocab_modules.standard.roles import Roles as IRoles
config: "IConfig" = get_module("standard.config", "config") config: "IConfig" = get_module("standard.config", "config")
Roles = get_module("standard.roles", "Roles")
try:
Roles: "type[IRoles]" = get_module("standard.roles", "Roles")
ROLES_MODULE_LOADED = True
except Exception:
ROLES_MODULE_LOADED = False
pass
def module_init(): def module_init():
config.register("filters::approved_chat_id", "string", shared=True) config.register("filters::approved_chat_id", "int", multiple=True, shared=True, default_value=[])
config.register("filters::default_chat_tag", "string", shared=True) config.register("filters::default_chat_tag", "string", shared=True)
def get_approved_chat_id() -> list: def get_approved_chat_id() -> list:
# Возваращем сплитованный список id чатов в формате int return config.get("filters::approved_chat_id")
return [
int(chat_id) for chat_id in config.get("filters::approved_chat_id").split(" | ")
]
@deprecated("Use ChatIDFilter or own implementation")
def chat_not_in_approve(message: Message) -> bool: def chat_not_in_approve(message: Message) -> bool:
chat_id = message.chat.id chat_id = message.chat.id
if chat_id in get_approved_chat_id(): if chat_id in get_approved_chat_id():
log(f"Chat in approve list: {chat_id}") # log(f"Chat in approve list: {chat_id}")
return False return False
else: else:
log(f"Chat not in approve list: {chat_id}") # log(f"Chat not in approve list: {chat_id}")
return True return True
class ChatIDFilter(BaseFilter):
def __init__(self, blacklist = False, approved_chats = None) -> None:
self.blacklist = blacklist
self.approved_chats = approved_chats
super().__init__()
async def __call__(self, message: Message, bot: Bot) -> bool:
chat_id = message.chat.id
approved_chats = self.approved_chats or get_approved_chat_id()
print(approved_chats)
res = chat_id in approved_chats
return res ^ (self.blacklist)
class ChatNotInApproveFilter(ChatIDFilter):
def __init__(self) -> None:
super().__init__(allow = False)
class ChatModerOrAdminFilter(BaseFilter): class ChatModerOrAdminFilter(BaseFilter):
async def __call__(self, message: Message, bot: Bot) -> bool: async def __call__(self, message: Message, bot: Bot) -> bool:
if not ROLES_MODULE_LOADED:
raise Exception("Roles module not loaded")
user_id = message.from_user.id user_id = message.from_user.id
roles = Roles() roles = Roles()
admins = await bot.get_chat_administrators(message.chat.id) admins = await bot.get_chat_administrators(message.chat.id)
@ -45,8 +74,3 @@ class ChatModerOrAdminFilter(BaseFilter):
or await roles.check_moderator_permission(user_id) or await roles.check_moderator_permission(user_id)
or any(user_id == admin.user.id for admin in admins) or any(user_id == admin.user.id for admin in admins)
) )
class ChatNotInApproveFilter(BaseFilter):
async def __call__(self, message: Message, bot: Bot) -> bool:
return chat_not_in_approve(message)

View File

@ -4,11 +4,13 @@
"description": "Модуль с фильтрами", "description": "Модуль с фильтрами",
"author": "OCAB Team", "author": "OCAB Team",
"version": "1.0.0", "version": "1.0.0",
"privileged": false, "privileged": true,
"dependencies": { "dependencies": {
"required": { "required": {
"standard.roles": "^1.0.0",
"standard.config": "^1.0.0" "standard.config": "^1.0.0"
},
"optional": {
"standard.roles": "^1.0.0"
} }
} }
} }

View File

@ -0,0 +1,18 @@
# Модуль Report
Модуль `report` позволяет пользователям сообщать о спам-сообщениях в чате.
## Команды
- `/report` - пожаловаться на сообщение как на спам.
## Использование
Чтобы сообщить о сообщении как о спаме, отправьте команду `/report`, ответив на сообщение, которое вы хотите отметить. Модуль уведомит администраторов, которые имеют права модерации.
### Пример использования
1. Найдите сообщение, которое вы хотите отметить как спам.
2. Ответьте на это сообщение командой `/report`.
Примечание: Команда `/report` должна быть отправлена в ответ на сообщение, которое вы хотите отметить.

View File

@ -0,0 +1 @@
from .main import module_init

View File

@ -0,0 +1,14 @@
{
"id": "standard.report",
"name": "Report",
"description": "Модуль для быстрой жалобы на спам",
"author": "OCAB Team",
"version": "1.0.0",
"privileged": false,
"dependencies": {
"optional": {
"standard.command_helper": "^1.0.0",
"standard.filters": "^1.0.0"
}
}
}

View File

@ -0,0 +1,69 @@
from typing import TYPE_CHECKING
from aiogram import Router
from aiogram.filters import Command
from aiogram.types import Message, ChatMemberOwner, ChatMemberAdministrator
from ocab_core.modules_system.public_api import get_module, register_router, log
if TYPE_CHECKING:
from ocab_modules.standard.filters import ChatIDFilter as IChatIDFilter
try:
ChatIDFilter: "type[IChatIDFilter]" = get_module("standard.filters", "ChatIDFilter")
FILTERS_MODULE_LOADED = True
except Exception as e:
FILTERS_MODULE_LOADED = False
pass
try:
register_command = get_module("standard.command_helper", "register_command")
COMMAND_HELPER_MODULE_LOADED = True
except Exception as e:
COMMAND_HELPER_MODULE_LOADED = False
pass
def can_moderate(admin: ChatMemberOwner | ChatMemberAdministrator) -> bool:
if isinstance(admin, ChatMemberOwner):
return True
return (
admin.user.is_bot == False and
(
admin.can_delete_messages and
admin.can_restrict_members
)
)
async def report(message: Message):
try:
if message.reply_to_message is None:
await message.reply("Пожалуйста, используйте команду /report в ответ на сообщение, которое вы хотите отметить как спам.")
return
admins = await message.chat.get_administrators()
admin_usernames = [
admin.user.mention_html()
for admin in admins
if can_moderate(admin)
]
if admin_usernames:
ping_message = "⚠️ Внимание, жалоба на спам! " + ", ".join(admin_usernames)
await message.reply_to_message.reply(ping_message, parse_mode="HTML")
except Exception as e:
log(e)
async def module_init():
router = Router()
if FILTERS_MODULE_LOADED:
router.message.register(report, ChatIDFilter(), Command("report"))
else:
router.message.register(report, Command("report"))
register_router(router)
if COMMAND_HELPER_MODULE_LOADED:
register_command = get_module("standard.command_helper", "register_command")
register_command("report", "Пожаловаться на спам")