mirror of
https://gitflic.ru/project/alt-gnome/karkas.git
synced 2024-12-24 00:33:06 +03:00
Merged with feat/add-report-module
This commit is contained in:
commit
e3443f835a
@ -15,6 +15,10 @@
|
||||
{
|
||||
"name": "Gnomik",
|
||||
"path": "src/gnomik"
|
||||
},
|
||||
{
|
||||
"name": "ALT Linux",
|
||||
"path": "src/altlinux"
|
||||
}
|
||||
],
|
||||
"extensions": {
|
||||
|
23
src/altlinux/Dockerfile
Normal file
23
src/altlinux/Dockerfile
Normal file
@ -0,0 +1,23 @@
|
||||
FROM python:3.12-slim as builder
|
||||
|
||||
RUN pip install poetry
|
||||
RUN mkdir -p /app
|
||||
COPY . /app
|
||||
|
||||
# Фикс
|
||||
|
||||
RUN sed -i '/ocab-core = {/{s/, develop = true//}' /app/src/altlinux/pyproject.toml && \
|
||||
sed -i '/ocab-modules = {/{s/, develop = true//}' /app/src/altlinux/pyproject.toml && \
|
||||
sed -i '/ocab-core = {/{s/, develop = true//}' /app/src/ocab_modules/pyproject.toml
|
||||
|
||||
WORKDIR /app/src/altlinux
|
||||
|
||||
RUN poetry lock && poetry install
|
||||
|
||||
FROM python:3.12-slim as base
|
||||
|
||||
COPY --from=builder /app/src/altlinux /app
|
||||
|
||||
WORKDIR /app
|
||||
ENV PATH="/app/.venv/bin:$PATH"
|
||||
CMD ["python", "-m", "altlinux"]
|
14
src/altlinux/Dockerfile.dockerignore
Normal file
14
src/altlinux/Dockerfile.dockerignore
Normal file
@ -0,0 +1,14 @@
|
||||
**/Dockerfile
|
||||
**/*.dockerignore
|
||||
**/docker-compose.yml
|
||||
|
||||
**/.git
|
||||
**/.gitignore
|
||||
|
||||
**/.venv
|
||||
|
||||
**/.mypy_cache
|
||||
**/__pycache__/
|
||||
|
||||
src/gnomik/config.yaml
|
||||
src/gnomik/database/*
|
47
src/altlinux/README.md
Normal file
47
src/altlinux/README.md
Normal file
@ -0,0 +1,47 @@
|
||||
# ALT Linux
|
||||
|
||||
## Описание
|
||||
|
||||
<!--
|
||||
TODO: добавить описание
|
||||
-->
|
||||
|
||||
## Функционал
|
||||
|
||||
<!--
|
||||
TODO: описать функционал
|
||||
-->
|
||||
|
||||
## Запуск
|
||||
|
||||
### Docker
|
||||
|
||||
1. Соберите Docker-образ:
|
||||
```bash
|
||||
docker build -t gnomik .
|
||||
```
|
||||
2. Запустите контейнер:
|
||||
```bash
|
||||
docker run -p 9000:9000 -v ./config.yaml:/app/config.yaml -v ./database:/app/database gnomik
|
||||
```
|
||||
|
||||
Замените `./config.yaml` и `./database` на пути к вашим локальным файлам конфигурации и паки для базы данных.
|
||||
|
||||
### Вручную
|
||||
|
||||
1. Активируйте виртуальное окружение Gnomика:
|
||||
```bash
|
||||
poetry shell
|
||||
```
|
||||
2. Запустите бота:
|
||||
```bash
|
||||
python -m gnomik
|
||||
```
|
||||
|
||||
## Конфигурация
|
||||
|
||||
Конфигурация бота находится в файле `config.yaml`.
|
||||
|
||||
## Модули
|
||||
|
||||
Список загружаемых модулей указан в файле `__main__.py`.
|
0
src/altlinux/altlinux/__init__.py
Normal file
0
src/altlinux/altlinux/__init__.py
Normal file
21
src/altlinux/altlinux/__main__.py
Normal file
21
src/altlinux/altlinux/__main__.py
Normal file
@ -0,0 +1,21 @@
|
||||
import asyncio
|
||||
|
||||
from ocab_core import OCAB
|
||||
from ocab_modules import module_loader
|
||||
|
||||
|
||||
async def main():
|
||||
ocab = OCAB()
|
||||
await ocab.init_app(
|
||||
[
|
||||
module_loader("standard", "config", safe=False),
|
||||
module_loader("standard", "command_helper"),
|
||||
# safe=False из-за super().__init__()
|
||||
module_loader("standard", "filters", safe=False),
|
||||
module_loader("standard", "report"),
|
||||
]
|
||||
)
|
||||
await ocab.start()
|
||||
|
||||
|
||||
asyncio.run(main())
|
7
src/altlinux/config-example.yaml
Normal file
7
src/altlinux/config-example.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
core:
|
||||
mode: LONG_POLLING
|
||||
token: xxx
|
||||
|
||||
filters:
|
||||
approved_chat_id:
|
||||
- -111111
|
9
src/altlinux/docker-compose.yml
Normal file
9
src/altlinux/docker-compose.yml
Normal file
@ -0,0 +1,9 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: src/altlinux/Dockerfile
|
||||
volumes:
|
||||
- ./config.yaml:/app/config.yaml
|
2162
src/altlinux/poetry.lock
generated
Normal file
2162
src/altlinux/poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
2
src/altlinux/poetry.toml
Normal file
2
src/altlinux/poetry.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[virtualenvs]
|
||||
in-project = true
|
17
src/altlinux/pyproject.toml
Normal file
17
src/altlinux/pyproject.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[tool.poetry]
|
||||
name = "altlinux"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = [
|
||||
"Максим Слипенко <maxim@slipenko.com>"
|
||||
]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "~3.12"
|
||||
ocab-core = { extras=["webhook"], path = "../ocab_core", develop = true }
|
||||
ocab-modules = { path = "../ocab_modules", develop = true }
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
@ -7,7 +7,8 @@
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"required": {
|
||||
"standard.filters": "^1.0.0"
|
||||
"standard.filters": "^1.0.0",
|
||||
"standard.roles": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
from .main import module_init, register_command
|
||||
from .main import module_late_init, register_command
|
||||
|
@ -5,10 +5,5 @@
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"required": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.database": "^1.0.0"
|
||||
}
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
@ -1,28 +1,11 @@
|
||||
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict
|
||||
|
||||
from aiogram import BaseMiddleware
|
||||
from aiogram.types import BotCommand, TelegramObject
|
||||
from aiogram.types import BotCommand
|
||||
|
||||
from ocab_core.modules_system.public_api import (
|
||||
get_module,
|
||||
register_outer_message_middleware,
|
||||
set_my_commands,
|
||||
set_my_commands, log
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ocab_modules.standard.database import db_api as IDbApi
|
||||
from ocab_modules.standard.roles import Roles as IRoles
|
||||
|
||||
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"):
|
||||
if role not in commands:
|
||||
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():
|
||||
bot_commands = []
|
||||
if "USER" in commands:
|
||||
@ -90,6 +26,8 @@ async def set_user_commands():
|
||||
)
|
||||
)
|
||||
|
||||
log(bot_commands)
|
||||
|
||||
await set_my_commands(
|
||||
bot_commands,
|
||||
)
|
||||
|
@ -84,7 +84,8 @@ class ConfigManager:
|
||||
key: str,
|
||||
value_type: str,
|
||||
options: List[Any] = None,
|
||||
default_value=None,
|
||||
multiple: bool = False,
|
||||
default_value = None,
|
||||
editable: bool = True,
|
||||
shared: bool = False,
|
||||
required: bool = False,
|
||||
@ -101,6 +102,7 @@ class ConfigManager:
|
||||
|
||||
self._metadata[key] = {
|
||||
"type": value_type,
|
||||
"multiple": multiple,
|
||||
"options": options,
|
||||
"default_value": default_value,
|
||||
"visible": visible,
|
||||
|
@ -1,6 +1,7 @@
|
||||
from .filters import (
|
||||
ChatModerOrAdminFilter,
|
||||
ChatNotInApproveFilter,
|
||||
ChatIDFilter,
|
||||
chat_not_in_approve,
|
||||
module_init,
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from typing_extensions import deprecated
|
||||
|
||||
from aiogram import Bot
|
||||
from aiogram.filters import BaseFilter
|
||||
@ -8,35 +9,63 @@ from ocab_core.modules_system.public_api import get_module, log
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ocab_modules.standard.config import IConfig
|
||||
from ocab_modules.standard.roles import Roles as IRoles
|
||||
|
||||
|
||||
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():
|
||||
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)
|
||||
|
||||
|
||||
def get_approved_chat_id() -> list:
|
||||
# Возваращем сплитованный список id чатов в формате int
|
||||
return [
|
||||
int(chat_id) for chat_id in config.get("filters::approved_chat_id").split(" | ")
|
||||
]
|
||||
return config.get("filters::approved_chat_id")
|
||||
|
||||
|
||||
@deprecated("Use ChatIDFilter or own implementation")
|
||||
def chat_not_in_approve(message: Message) -> bool:
|
||||
chat_id = message.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
|
||||
else:
|
||||
log(f"Chat not in approve list: {chat_id}")
|
||||
# log(f"Chat not in approve list: {chat_id}")
|
||||
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):
|
||||
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
|
||||
roles = Roles()
|
||||
admins = await bot.get_chat_administrators(message.chat.id)
|
||||
@ -44,9 +73,4 @@ class ChatModerOrAdminFilter(BaseFilter):
|
||||
await roles.check_admin_permission(user_id)
|
||||
or await roles.check_moderator_permission(user_id)
|
||||
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)
|
||||
)
|
@ -4,11 +4,13 @@
|
||||
"description": "Модуль с фильтрами",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"privileged": true,
|
||||
"dependencies": {
|
||||
"required": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.config": "^1.0.0"
|
||||
},
|
||||
"optional": {
|
||||
"standard.roles": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/ocab_modules/ocab_modules/standard/report/README.md
Normal file
18
src/ocab_modules/ocab_modules/standard/report/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Модуль Report
|
||||
|
||||
Модуль `report` позволяет пользователям сообщать о спам-сообщениях в чате.
|
||||
|
||||
## Команды
|
||||
|
||||
- `/report` - пожаловаться на сообщение как на спам.
|
||||
|
||||
## Использование
|
||||
|
||||
Чтобы сообщить о сообщении как о спаме, отправьте команду `/report`, ответив на сообщение, которое вы хотите отметить. Модуль уведомит администраторов, которые имеют права модерации.
|
||||
|
||||
### Пример использования
|
||||
|
||||
1. Найдите сообщение, которое вы хотите отметить как спам.
|
||||
2. Ответьте на это сообщение командой `/report`.
|
||||
|
||||
Примечание: Команда `/report` должна быть отправлена в ответ на сообщение, которое вы хотите отметить.
|
@ -0,0 +1 @@
|
||||
from .main import module_init
|
14
src/ocab_modules/ocab_modules/standard/report/info.json
Normal file
14
src/ocab_modules/ocab_modules/standard/report/info.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
69
src/ocab_modules/ocab_modules/standard/report/main.py
Normal file
69
src/ocab_modules/ocab_modules/standard/report/main.py
Normal 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", "Пожаловаться на спам")
|
Loading…
Reference in New Issue
Block a user