init commit

This commit is contained in:
2025-08-20 21:10:31 +09:00
parent 36a9382cb6
commit 745046c638
31 changed files with 1074 additions and 1 deletions

View File

@@ -0,0 +1,123 @@
from telegram import Update
from telegram.constants import ParseMode, ChatType
from telegram.ext import ContextTypes
from telegram.error import BadRequest
from app.bot.messages import (
ASK_ADD_GROUP,
GROUP_BOUND,
NEED_ADD_FIRST,
NO_RIGHTS_CHANNEL,
NO_RIGHTS_GROUP,
)
from app.db.session import get_session
from app.db.models import User, Chat
from .utils import parse_chat_id, verify_and_fetch_chat
STATE_KEY = "await_chat_id"
def _get_forwarded_chat_id(message) -> int | None:
"""
Универсально достаём chat_id источника пересылки.
Поддерживает старые поля (forward_from_chat) и новые (forward_origin.chat).
"""
if not message:
return None
# Старое поле (иногда ещё присутствует)
fwd_chat = getattr(message, "forward_from_chat", None)
if fwd_chat:
return fwd_chat.id
# Новая схема Bot API: forward_origin с типами MessageOrigin*
origin = getattr(message, "forward_origin", None)
if origin:
chat = getattr(origin, "chat", None) # у MessageOriginChat/Channel есть .chat
if chat:
return chat.id
return None
async def add_group_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
# ensure user exists
with get_session() as s:
u = s.query(User).filter_by(tg_id=update.effective_user.id).first()
if not u:
u = User(tg_id=update.effective_user.id, name=update.effective_user.full_name)
s.add(u)
s.commit()
ctx.user_data[STATE_KEY] = True
await update.effective_message.reply_text(ASK_ADD_GROUP, parse_mode=ParseMode.MARKDOWN)
async def add_group_capture(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
if update.effective_chat.type != ChatType.PRIVATE:
return
if not ctx.user_data.get(STATE_KEY):
return
msg = update.effective_message
raw = None
# 1) Пересланное сообщение из чата/канала
fwd_cid = _get_forwarded_chat_id(msg)
if fwd_cid:
raw = fwd_cid
else:
# 2) Ввод в виде текста или подписи (caption)
txt = (getattr(msg, "text", None) or getattr(msg, "caption", None) or "").strip()
if not txt:
await msg.reply_text("Вставьте chat_id или перешлите сообщение из группы/канала.")
return
if txt.startswith("@") or "t.me/" in txt:
raw = txt
else:
cid = parse_chat_id(txt)
if cid is None:
await msg.reply_text("Не удалось распознать chat_id. Пример: -1001234567890 или @username.")
return
raw = cid
# Проверяем, что бот в чате и что у него есть право постить (если нужно)
try:
chat, member, can_post = await verify_and_fetch_chat(ctx, raw)
except BadRequest:
await msg.reply_text("Такого чата не существует или у меня нет доступа. Проверьте chat_id/username.")
return
if member is None:
await msg.reply_text(NEED_ADD_FIRST.format(title_or_id=(chat.title or chat.id)))
return
# Сохраняем привязку
with get_session() as s:
me = s.query(User).filter_by(tg_id=update.effective_user.id).first()
row = s.query(Chat).filter_by(chat_id=chat.id).first()
if not row:
row = Chat(
chat_id=chat.id,
type=chat.type,
title=chat.title,
owner_user_id=me.id,
can_post=can_post,
)
s.add(row)
else:
row.title = chat.title
row.type = chat.type
if row.owner_user_id is None:
row.owner_user_id = me.id
row.can_post = can_post
s.commit()
ctx.user_data.pop(STATE_KEY, None)
rights = (
"У меня есть право публиковать."
if can_post
else (NO_RIGHTS_CHANNEL if chat.type == ChatType.CHANNEL else NO_RIGHTS_GROUP)
)
await msg.reply_text(GROUP_BOUND.format(title_or_id=(chat.title or chat.id), rights=rights))