diff --git a/handlers/new_post.py b/handlers/new_post.py index 701929b..02cfb8d 100644 --- a/handlers/new_post.py +++ b/handlers/new_post.py @@ -140,26 +140,26 @@ async def select_text(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text('Выберите, куда отправить пост:', reply_markup=InlineKeyboardMarkup(keyboard)) return SELECT_TARGET - - async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query if not query: return ConversationHandler.END await query.answer() - data = query.data + data = (query.data or "") async with AsyncSessionLocal() as session: - chat_id = None - markup = None - selected_title = None + chat_id: str | None = None + markup: InlineKeyboardMarkup | None = None + selected_title: str | None = None btns = [] - if data and data.startswith('channel_'): - channel_id = int(data.split('_')[1]) + if data.startswith('channel_'): + channel_id = int(data.split('_', 1)[1]) + # ACL: право постинга в канал me = await get_or_create_admin(session, update.effective_user.id) - if not await has_scope_on_channel(session, me.id, channel_id, SCOPE_POST): + allowed = await has_scope_on_channel(session, me.id, channel_id, SCOPE_POST) + if not allowed: await query.edit_message_text("У вас нет права постить в этот канал.") return ConversationHandler.END @@ -171,12 +171,15 @@ async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE): chat_id = (channel.link or "").strip() selected_title = channel.name + # Кнопки канала btns = (await session.execute(sa_select(Button).where(Button.channel_id == channel_id))).scalars().all() if btns: - markup = InlineKeyboardMarkup([[InlineKeyboardButton(str(b.name), url=str(b.url))] for b in btns]) + rows = [[InlineKeyboardButton(str(b.name), url=str(b.url))] for b in btns] + markup = InlineKeyboardMarkup(rows) + + elif data.startswith('group_'): + group_id = int(data.split('_', 1)[1]) - elif data and data.startswith('group_'): - group_id = int(data.split('_')[1]) group = (await session.execute(sa_select(Group).where(Group.id == group_id))).scalar_one_or_none() if not group: await query.edit_message_text("Группа не найдена.") @@ -185,75 +188,105 @@ async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE): chat_id = (group.link or "").strip() selected_title = group.name + # Кнопки группы btns = (await session.execute(sa_select(Button).where(Button.group_id == group_id))).scalars().all() if btns: - markup = InlineKeyboardMarkup([[InlineKeyboardButton(str(b.name), url=str(b.url))] for b in btns]]) + rows = [[InlineKeyboardButton(str(b.name), url=str(b.url))] for b in btns] + markup = InlineKeyboardMarkup(rows) if not chat_id or not (chat_id.startswith('@') or chat_id.startswith('-')): - await query.edit_message_text('Ошибка: ссылка должна быть @username или -100…') + await query.edit_message_text('Ошибка: ссылка должна быть username (@channel) или числовой ID (-100...)') return ConversationHandler.END - # DEBUG: покажем, сколько кнопок нашли и куда шлём + # DEBUG: сколько кнопок нашли и есть ли markup print(f"[DEBUG] send -> chat_id={chat_id} title={selected_title!r} buttons={len(btns)} has_markup={bool(markup)}") + # Текст и entities (без parse_mode) ud = context.user_data or {} text: str = ud.get("text", "") or "" entities: List[MessageEntity] = ud.get("entities", []) or [] entities = _strip_broken_entities(entities) entities = _split_custom_emoji_by_utf16(text, entities) + # Всегда ручная отправка (send_*), чтобы гарантированно приклеить inline-клавиатуру try: sent_msg = None - - # ВСЕГДА ручная отправка (никакого copyMessage), чтобы приклеить reply_markup if "photo" in ud: - sent_msg = await context.bot.send_photo(chat_id=chat_id, photo=ud["photo"], - caption=(text or None), - caption_entities=(entities if text else None), - reply_markup=markup) + sent_msg = await context.bot.send_photo( + chat_id=chat_id, + photo=ud["photo"], + caption=(text or None), + caption_entities=(entities if text else None), + reply_markup=markup, + ) elif "animation" in ud: - sent_msg = await context.bot.send_animation(chat_id=chat_id, animation=ud["animation"], - caption=(text or None), - caption_entities=(entities if text else None), - reply_markup=markup) + sent_msg = await context.bot.send_animation( + chat_id=chat_id, + animation=ud["animation"], + caption=(text or None), + caption_entities=(entities if text else None), + reply_markup=markup, + ) elif "video" in ud: - sent_msg = await context.bot.send_video(chat_id=chat_id, video=ud["video"], - caption=(text or None), - caption_entities=(entities if text else None), - reply_markup=markup) + sent_msg = await context.bot.send_video( + chat_id=chat_id, + video=ud["video"], + caption=(text or None), + caption_entities=(entities if text else None), + reply_markup=markup, + ) elif "document" in ud: - sent_msg = await context.bot.send_document(chat_id=chat_id, document=ud["document"], - caption=(text or None), - caption_entities=(entities if text else None), - reply_markup=markup) + sent_msg = await context.bot.send_document( + chat_id=chat_id, + document=ud["document"], + caption=(text or None), + caption_entities=(entities if text else None), + reply_markup=markup, + ) elif "audio" in ud: - sent_msg = await context.bot.send_audio(chat_id=chat_id, audio=ud["audio"], - caption=(text or None), - caption_entities=(entities if text else None), - reply_markup=markup) + sent_msg = await context.bot.send_audio( + chat_id=chat_id, + audio=ud["audio"], + caption=(text or None), + caption_entities=(entities if text else None), + reply_markup=markup, + ) elif "voice" in ud: - sent_msg = await context.bot.send_voice(chat_id=chat_id, voice=ud["voice"], - caption=(text or None), - caption_entities=(entities if text else None), - reply_markup=markup) + sent_msg = await context.bot.send_voice( + chat_id=chat_id, + voice=ud["voice"], + caption=(text or None), + caption_entities=(entities if text else None), + reply_markup=markup, + ) elif "sticker" in ud: - sent_msg = await context.bot.send_sticker(chat_id=chat_id, sticker=ud["sticker"], reply_markup=markup) + sent_msg = await context.bot.send_sticker( + chat_id=chat_id, + sticker=ud["sticker"], + reply_markup=markup, + ) if text: await context.bot.send_message(chat_id=chat_id, text=text, entities=entities) else: - sent_msg = await context.bot.send_message(chat_id=chat_id, text=text, entities=entities, reply_markup=markup) + sent_msg = await context.bot.send_message( + chat_id=chat_id, + text=text, + entities=entities, + reply_markup=markup, + ) - # страховка: если вдруг Telegram проглотил клаву — доклеим через edit_message_reply_markup + # Страховка: если вдруг Telegram проглотил клаву — доклеим её if markup and getattr(sent_msg, "message_id", None): try: - await context.bot.edit_message_reply_markup(chat_id=chat_id, - message_id=sent_msg.message_id, - reply_markup=markup) + await context.bot.edit_message_reply_markup( + chat_id=chat_id, + message_id=sent_msg.message_id, + reply_markup=markup, + ) except Exception: pass await query.edit_message_text(f'Пост отправлен{(" в: " + selected_title) if selected_title else "!"}') - except BadRequest as e: await query.edit_message_text(f'Ошибка отправки поста: {e}') @@ -261,6 +294,7 @@ async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE): + new_post_conv = ConversationHandler( entry_points=[CommandHandler("new_post", new_post_start)], states={