Bot become a Community Guard & Post send manager

added: dictionary support for censore
message/user management with dict triggers
This commit is contained in:
2025-08-22 21:44:14 +09:00
parent efdafb0efa
commit c16ec54891
27 changed files with 1746 additions and 184 deletions

View File

@@ -1,51 +1,41 @@
from datetime import datetime
from telegram import Update
from telegram import Update, InputMediaPhoto, InputMediaVideo, InputMediaAnimation
from telegram.constants import ChatType, ParseMode
from telegram.ext import ContextTypes
from app.db.session import get_session
from app.db.models import User, Draft
from app.bot.messages import (
ASK_MEDIA, ASK_TEXT, TEXT_ADDED, CONFIRM, ALREADY_AT_TEXT, ALREADY_READY, NEED_START_NEW
)
from app.bot.messages import ASK_MEDIA, ASK_TEXT, CONFIRM, NEED_START_NEW
from app.bot.keyboards.common import kb_next_text, kb_confirm
from .add_group import add_group_capture, STATE_KEY # /add_group ожидание
from .add_group import add_group_capture, STATE_KEY
# Состояния пошагового редактора
STATE_DRAFT = "draft_state"
KEY_DRAFT_ID = "draft_id"
STATE_AWAIT_MEDIA = "await_media"
STATE_AWAIT_TEXT = "await_text"
STATE_CONFIRM = "confirm"
def _start_new_draft(tg_id: int) -> Draft:
with get_session() as s:
u = s.query(User).filter_by(tg_id=tg_id).first()
if not u:
u = User(tg_id=tg_id, name="")
s.add(u); s.commit(); s.refresh(u)
# Закрываем предыдущие "editing" как cancelled (по желанию)
u = User(tg_id=tg_id, name=""); s.add(u); s.commit(); s.refresh(u)
s.query(Draft).filter(Draft.user_id == u.id, Draft.status == "editing").update({"status": "cancelled"})
d = Draft(user_id=u.id, status="editing")
s.add(d); s.commit(); s.refresh(d)
return d
def _get_draft(draft_id: int) -> Draft | None:
with get_session() as s:
return s.get(Draft, draft_id)
async def new_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
d = _start_new_draft(update.effective_user.id)
ctx.user_data[KEY_DRAFT_ID] = d.id
ctx.user_data[STATE_DRAFT] = STATE_AWAIT_MEDIA
await update.effective_message.reply_text(ASK_MEDIA, reply_markup=kb_next_text(d.id))
# Кнопку «Дальше — текст» теперь показываем после добавления медиа,
# поэтому здесь — только инструкция
await update.effective_message.reply_text(ASK_MEDIA)
async def on_text(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
# Если ждём chat_id для /add_group, отдаём управление туда
# Если ждём chat_id для /add_group — передаём управление
if ctx.user_data.get("await_dict_file"):
return
if ctx.user_data.get(STATE_KEY):
return await add_group_capture(update, ctx)
@@ -60,20 +50,48 @@ async def on_text(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
return
if state == STATE_AWAIT_MEDIA:
# Сначала медиа
await update.effective_message.reply_text("Сначала пришлите медиа и нажмите «Дальше — текст».")
await update.effective_message.reply_text("Сначала добавьте медиа и нажмите «Дальше — текст».")
return
if state == STATE_CONFIRM:
await update.effective_message.reply_text(ALREADY_READY)
await update.effective_message.reply_text("Пост уже готов — нажмите «Отправить» или «Отменить».")
return
if state == STATE_AWAIT_TEXT:
# Сохраняем текст
with get_session() as s:
d = s.get(Draft, draft_id)
d.text = update.effective_message.text_html_urled
d.updated_at = datetime.utcnow()
s.commit()
media = sorted(d.media, key=lambda m: m.order)
# Предпросмотр
if media:
if len(media) > 1:
im = []
for i, m in enumerate(media):
cap = d.text if i == 0 else None
if m.kind == "photo":
im.append(InputMediaPhoto(media=m.file_id, caption=cap, parse_mode=ParseMode.HTML))
elif m.kind == "video":
im.append(InputMediaVideo(media=m.file_id, caption=cap, parse_mode=ParseMode.HTML))
else:
im.append(InputMediaAnimation(media=m.file_id, caption=cap, parse_mode=ParseMode.HTML))
await update.effective_chat.send_media_group(media=im)
else:
m = media[0]
if m.kind == "photo":
await update.effective_chat.send_photo(photo=m.file_id, caption=d.text, parse_mode=ParseMode.HTML)
elif m.kind == "video":
await update.effective_chat.send_video(video=m.file_id, caption=d.text, parse_mode=ParseMode.HTML)
else:
await update.effective_chat.send_animation(animation=m.file_id, caption=d.text, parse_mode=ParseMode.HTML)
else:
await update.effective_chat.send_message(text=d.text or "(пусто)", parse_mode=ParseMode.HTML)
# Переходим к подтверждению и показываем кнопки
ctx.user_data[STATE_DRAFT] = STATE_CONFIRM
await update.effective_message.reply_text(TEXT_ADDED, parse_mode=ParseMode.HTML)
await update.effective_message.reply_text(CONFIRM, reply_markup=kb_confirm(draft_id))