Skip to content

Commit

Permalink
Merge branch 'master' into delete-report-karma-award-message-test
Browse files Browse the repository at this point in the history
  • Loading branch information
Desiders committed Nov 25, 2023
2 parents 01e7257 + d583752 commit 41b1d78
Show file tree
Hide file tree
Showing 17 changed files with 478 additions and 185 deletions.
18 changes: 18 additions & 0 deletions app/filters/reports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from aiogram import types
from aiogram.filters import BaseFilter

from app.infrastructure.database.models import Chat
from app.infrastructure.database.repo.report import ReportRepo


class HasResolvedReport(BaseFilter):
"""Check if reported message already resolved report"""

async def __call__(
self, message: types.Message, chat: Chat, report_repo: ReportRepo
) -> bool:
if not message.reply_to_message:
return False
return await report_repo.has_resolved_report(
chat_id=chat.chat_id, message_id=message.reply_to_message.message_id
)
5 changes: 3 additions & 2 deletions app/handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from aiogram.utils.markdown import hbold, hpre

from app.infrastructure.database.models import Chat
from app.infrastructure.database.repo.chat import ChatRepo
from app.utils.log import Logger

logger = Logger(__name__)
Expand Down Expand Up @@ -83,9 +84,9 @@ async def cancel_state(message: types.Message, state: FSMContext):


@router.message(F.message.content_types == types.ContentType.MIGRATE_TO_CHAT_ID)
async def chat_migrate(message: types.Message, chat: Chat):
async def chat_migrate(message: types.Message, chat: Chat, chat_repo: ChatRepo):
old_id = message.chat.id
new_id = message.migrate_to_chat_id
chat.chat_id = new_id
await chat.save()
await chat_repo.update(chat)
logger.info(f"Migrate chat from {old_id} to {new_id}")
5 changes: 3 additions & 2 deletions app/handlers/karma.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from aiogram.utils.text_decorations import html_decoration as hd

from app.infrastructure.database.models import Chat, User
from app.infrastructure.database.repo.chat import ChatRepo
from app.models.config import Config
from app.services.karma import get_me_chat_info, get_me_info
from app.services.karma import get_top as get_karma_top
Expand All @@ -16,10 +17,10 @@


@router.message(Command("top", prefix="!"), F.chat.type == "private")
async def get_top_from_private(message: types.Message, user: User):
async def get_top_from_private(message: types.Message, user: User, chat_repo: ChatRepo):
parts = message.text.split(maxsplit=1)
if len(parts) > 1:
chat = await Chat.get(chat_id=int(parts[1]))
chat = await chat_repo.get_by_id(chat_id=int(parts[1]))
else:
return await message.reply(
"Эту команду можно использовать только в группах "
Expand Down
197 changes: 109 additions & 88 deletions app/handlers/moderator.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import asyncio
import random

from aiogram import Bot, F, Router, types
from aiogram.enums import ChatMemberStatus
from aiogram.exceptions import TelegramUnauthorizedError
from aiogram.filters import Command, CommandObject, MagicData
from aiogram.utils.text_decorations import html_decoration as hd
from tortoise.transactions import in_transaction

from app.filters import (
BotHasPermissions,
HasPermissions,
HasTargetFilter,
TargetHasPermissions,
)
from app.filters.reports import HasResolvedReport
from app.handlers import keyboards as kb
from app.infrastructure.database.models import Chat, ChatSettings, ReportStatus, User
from app.infrastructure.database.repo.report import ReportRepo
from app.models.config import Config
from app.services.moderation import (
ban_user,
delete_moderator_event,
get_duration,
get_mentions_admins,
ro_user,
warn_user,
)
Expand All @@ -29,10 +29,18 @@
delete_message,
remove_kb,
)
from app.services.report import register_report, resolve_report, reward_reporter
from app.services.report import (
cancel_report,
cleanup_reports_dialog,
register_report,
resolve_report,
reward_reporter,
set_report_bot_reply,
)
from app.services.user_info import get_user_info
from app.utils.exceptions import ModerationError, TimedeltaParseError
from app.utils.log import Logger
from app.utils.view import hidden_link

logger = Logger(__name__)
router = Router(name=__name__)
Expand All @@ -41,32 +49,51 @@
@router.message(
F.chat.type.in_(["group", "supergroup"]),
HasTargetFilter(),
~HasResolvedReport(),
Command("report", "admin", "spam", prefix="/!@"),
)
async def report_message(
message: types.Message, chat: Chat, user: User, target: User, bot: Bot
message: types.Message,
chat: Chat,
user: User,
target: User,
bot: Bot,
report_repo: ReportRepo,
):
logger.info(
"user {user} report for message {message}",
"User {user} reported message {message} in chat {chat}",
user=message.from_user.id,
message=message.message_id,
chat=message.chat.id,
)
answer_message = "Спасибо за сообщение. Мы обязательно разберёмся"
admins_mention = await get_mentions_admins(message.chat, bot)

async with in_transaction() as db_session:
report = await register_report(
reporter=user,
reported_user=target,
chat=chat,
reported_message=message.reply_to_message,
db_session=db_session,
)
report = await register_report(
reporter=user,
reported_user=target,
chat=chat,
reported_message=message.reply_to_message,
command_message=message,
report_repo=report_repo,
)

reaction_keyboard = kb.get_report_reaction_kb(report=report, user=user)
await message.reply(
bot_reply = await message.reply(
f"{answer_message}.{admins_mention}", reply_markup=reaction_keyboard
)
await set_report_bot_reply(report, bot_reply, report_repo)


@router.message(
F.chat.type.in_(["group", "supergroup"]),
HasTargetFilter(),
HasResolvedReport(),
Command("report", "admin", "spam", prefix="/!@"),
)
async def report_already_reported(message: types.Message):
reply = await message.reply("Сообщение уже было рассмотрено ранее")
asyncio.create_task(cleanup_command_dialog(reply, True, delay=60))


@router.message(
Expand All @@ -79,42 +106,6 @@ async def report_private(message: types.Message):
)


async def get_mentions_admins(
chat: types.Chat,
bot: Bot,
ignore_anonymous: bool = True,
):
admins = await bot.get_chat_administrators(chat.id)
random.shuffle(admins) # чтобы попадались разные админы
admins_mention = ""
notifiable_admins = [
admin for admin in admins if need_notify_admin(admin, ignore_anonymous)
]
random_five_admins = notifiable_admins[:5]
for admin in random_five_admins:
admins_mention += hd.link("⁠", admin.user.url)
return admins_mention


def need_notify_admin(
admin: types.ChatMemberAdministrator | types.ChatMemberOwner,
ignore_anonymous: bool = True,
):
"""
Проверяет, нужно ли уведомлять администратора о жалобе.
:param admin: Администратор, которого нужно проверить.
:param ignore_anonymous: Игнорировать ли анонимных администраторов.
"""
if admin.user.is_bot or (ignore_anonymous and admin.is_anonymous):
return False
return (
admin.status == ChatMemberStatus.CREATOR
or admin.can_delete_messages
or admin.can_restrict_members
)


@router.message(
F.chat.type.in_(["group", "supergroup"]),
HasTargetFilter(),
Expand Down Expand Up @@ -310,7 +301,9 @@ async def cancel_warn(
await delete_moderator_event(callback_data.moderator_event_id, moderator=from_user)

await callback_query.answer("Вы отменили предупреждение", show_alert=True)
await cleanup_command_dialog(message=callback_query.message, delete_bot_reply=True)
await cleanup_command_dialog(
bot_message=callback_query.message, delete_bot_reply=True
)


@router.callback_query(
Expand All @@ -325,69 +318,97 @@ async def approve_report_handler(
bot: Bot,
config: Config,
chat_settings: ChatSettings,
report_repo: ReportRepo,
):
async with in_transaction() as db_session:
await resolve_report(
report_id=callback_data.report_id,
resolved_by=user,
resolution=ReportStatus.APPROVED,
db_session=db_session,
)
if chat_settings.karma_counting and config.report_karma_award:
logger.info(
"Moderator {moderator} approved report {report}",
moderator=callback_query.from_user.id,
report=callback_data.report_id,
)
first_report, *linked_reports = await resolve_report(
report_id=callback_data.report_id,
resolved_by=user,
resolution=ReportStatus.APPROVED,
report_repo=report_repo,
)
award_enabled = chat_settings.karma_counting and config.report_karma_award
if award_enabled:
karma_change_result = await reward_reporter(
reporter_id=callback_data.reporter_id,
reporter_id=first_report.reporter.id,
chat=chat,
reward_amount=config.report_karma_award,
bot=bot,
)
await callback_query.message.edit_text(
"<b>{reporter}</b> получил <b>+{reward_amount}</b> кармы в награду за репорт!".format(
await bot.edit_message_text(
"<b>{reporter}</b> получил <b>+{reward_amount}</b> кармы "
"в награду за репорт{admin_url}".format(
reporter=hd.quote(karma_change_result.karma_event.user_to.fullname),
reward_amount=config.report_karma_award,
)
admin_url=hidden_link(user.link),
),
chat_id=first_report.chat.chat_id,
message_id=first_report.bot_reply_message_id,
)
delete_bot_reply = False
else:
delete_bot_reply = True

await callback_query.answer("Вы подтвердили репорт", show_alert=delete_bot_reply)
await cleanup_command_dialog(
message=callback_query.message, delete_bot_reply=delete_bot_reply
await callback_query.answer("Вы подтвердили репорт", show_alert=not award_enabled)
await cleanup_reports_dialog(
first_report=first_report,
linked_reports=linked_reports,
delete_first_reply=not award_enabled,
bot=bot,
)


@router.callback_query(
kb.DeclineReportCb.filter(), HasPermissions(can_restrict_members=True)
)
async def decline_report_handler(
callback_query: types.CallbackQuery, callback_data: kb.DeclineReportCb, user: User
callback_query: types.CallbackQuery,
callback_data: kb.DeclineReportCb,
user: User,
bot: Bot,
report_repo: ReportRepo,
):
async with in_transaction() as db_session:
await resolve_report(
report_id=callback_data.report_id,
resolved_by=user,
resolution=ReportStatus.DECLINED,
db_session=db_session,
)
logger.info(
"Moderator {moderator} declined report {report}",
moderator=callback_query.from_user.id,
report=callback_data.report_id,
)
first_report, *linked_reports = await resolve_report(
report_id=callback_data.report_id,
resolved_by=user,
resolution=ReportStatus.DECLINED,
report_repo=report_repo,
)
await cleanup_reports_dialog(
first_report, linked_reports, delete_first_reply=True, bot=bot
)
await callback_query.answer("Вы отклонили репорт", show_alert=True)
await cleanup_command_dialog(message=callback_query.message, delete_bot_reply=True)


@router.callback_query(
kb.CancelReportCb.filter(), MagicData(F.user.id == F.callback_data.reporter_id)
)
async def cancel_report_handler(
callback_query: types.CallbackQuery, callback_data: kb.CancelReportCb, user: User
callback_query: types.CallbackQuery,
callback_data: kb.CancelReportCb,
user: User,
report_repo: ReportRepo,
):
async with in_transaction() as db_session:
await resolve_report(
report_id=callback_data.report_id,
resolved_by=user,
resolution=ReportStatus.CANCELLED,
db_session=db_session,
)
logger.info(
"User {user} cancelled report {report}",
user=callback_query.from_user.id,
report=callback_data.report_id,
)
await cancel_report(
report_id=callback_data.report_id,
resolved_by=user,
report_repo=report_repo,
)
await callback_query.answer("Вы отменили репорт", show_alert=True)
await cleanup_command_dialog(message=callback_query.message, delete_bot_reply=True)
await cleanup_command_dialog(
bot_message=callback_query.message, delete_bot_reply=True
)


@router.callback_query(
Expand Down
2 changes: 2 additions & 0 deletions app/infrastructure/database/models/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class Report(Model):
)
created_time = fields.DatetimeField(auto_now=True, null=False)
resolution_time = fields.DatetimeField(null=True)
command_message_id = fields.BigIntField(generated=False, null=False)
bot_reply_message_id = fields.BigIntField(generated=False, null=True)
reported_message_id = fields.BigIntField(generated=False, null=False)
reported_message_content = fields.CharField(
null=False, max_length=TG_MESSAGE_MAX_LEN
Expand Down
6 changes: 5 additions & 1 deletion app/infrastructure/database/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ async def get_or_create_from_tg_user(cls, user_tg: types.User | dto.TargetUser):

@property
def mention_link(self):
return hd.link(hd.quote(self.fullname), f"tg://user?id={self.tg_id}")
return hd.link(hd.quote(self.fullname), self.link)

@property
def link(self):
return f"tg://user?id={self.tg_id}"

@property
def mention_no_link(self):
Expand Down
Empty file.
Loading

0 comments on commit 41b1d78

Please sign in to comment.