security features

This commit is contained in:
2025-08-20 21:53:40 +09:00
parent 745046c638
commit efdafb0efa
5 changed files with 173 additions and 77 deletions

View File

@@ -1,12 +1,27 @@
import asyncio
from telegram import Update
from telegram.constants import ChatMemberStatus, ChatType, ParseMode
from telegram.ext import ContextTypes
from app.bot.messages import JOIN_INFO_GROUP, JOIN_INFO_CHANNEL
from telegram.error import Forbidden
from app.bot.messages import (
JOIN_DM_GROUP, JOIN_DM_CHANNEL, JOIN_PUBLIC_WITH_ID, NEED_START_DM
)
TTL_SEC = 30 # через столько секунд удаляем публичную подсказку
async def _auto_delete(ctx: ContextTypes.DEFAULT_TYPE, chat_id: int, message_id: int, delay: int = TTL_SEC):
try:
await asyncio.sleep(delay)
await ctx.bot.delete_message(chat_id=chat_id, message_id=message_id)
except Exception:
pass
async def on_my_chat_member(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
"""
Сообщаем инструкцию и chat_id, когда бота добавили в группу/канал
или повысили до администратора.
При добавлении/повышении прав.
1) Пробуем DM актёру (my_chat_member.from_user) с chat_id и инструкцией.
2) Если DM не вышел (нет from_user или нет Start/Forbidden) — пишем в чат
подсказку с chat_id и удаляем её через TTL_SEC.
"""
mcm = update.my_chat_member
if not mcm:
@@ -20,32 +35,43 @@ async def on_my_chat_member(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
title = chat.title or str(chat.id)
chat_id = chat.id
# Текст подсказки
if chat.type in (ChatType.GROUP, ChatType.SUPERGROUP):
text = JOIN_INFO_GROUP.format(title=title, chat_id=chat_id)
# Пытаемся написать прямо в группу
# 1) Пытаемся отправить DM тому, кто совершил действие
actor = getattr(mcm, "from_user", None)
dm_sent = False
if actor:
try:
await ctx.bot.send_message(chat_id=chat_id, text=text, parse_mode=ParseMode.MARKDOWN)
except Exception:
# как запасной вариант — в ЛС пользователю, который добавил
if update.effective_user:
try:
await ctx.bot.send_message(update.effective_user.id, text=text, parse_mode=ParseMode.MARKDOWN)
except Exception:
pass
elif chat.type == ChatType.CHANNEL:
text = JOIN_INFO_CHANNEL.format(title=title, chat_id=chat_id)
# В канале можем не иметь права постинга — пробуем ЛС добавившему
sent = False
try:
# если права даны, сообщим прямо в канал
await ctx.bot.send_message(chat_id=chat_id, text=text, parse_mode=ParseMode.MARKDOWN)
sent = True
if chat.type in (ChatType.GROUP, ChatType.SUPERGROUP):
await ctx.bot.send_message(
actor.id, JOIN_DM_GROUP.format(title=title, chat_id=chat_id),
parse_mode=ParseMode.MARKDOWN
)
elif chat.type == ChatType.CHANNEL:
await ctx.bot.send_message(
actor.id, JOIN_DM_CHANNEL.format(title=title, chat_id=chat_id),
parse_mode=ParseMode.MARKDOWN
)
dm_sent = True
except Forbidden:
# пользователь не нажал Start — подсказка про Start
try:
await ctx.bot.send_message(actor.id, NEED_START_DM)
except Exception:
pass
except Exception:
pass
if not sent and update.effective_user:
try:
await ctx.bot.send_message(update.effective_user.id, text=text, parse_mode=ParseMode.MARKDOWN)
except Exception:
pass
if dm_sent:
return
# 2) DM не удался — публикуем в чат краткий хинт с chat_id, удаляем через TTL
# (для каналов сработает только если бот уже админ и может постить)
try:
msg = await ctx.bot.send_message(
chat_id=chat_id,
text=JOIN_PUBLIC_WITH_ID.format(chat_id=chat_id, ttl=TTL_SEC),
parse_mode=ParseMode.MARKDOWN
)
ctx.application.create_task(_auto_delete(ctx, chat_id, msg.message_id, delay=TTL_SEC))
except Exception:
# Если и сюда не можем — увы, остаётся ручной путь: /id, /add_group и ЛС
pass