select_target -> disabled COPY using send_
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
@@ -140,6 +140,151 @@ async def select_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
await update.message.reply_text('Выберите, куда отправить пост:', reply_markup=InlineKeyboardMarkup(keyboard))
|
await update.message.reply_text('Выберите, куда отправить пост:', reply_markup=InlineKeyboardMarkup(keyboard))
|
||||||
return SELECT_TARGET
|
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
|
||||||
|
# async with AsyncSessionLocal() as session:
|
||||||
|
# chat_id = None
|
||||||
|
# markup = None
|
||||||
|
# selected_kind = None # "channel" | "group"
|
||||||
|
# selected_title = None
|
||||||
|
|
||||||
|
# if data and data.startswith('channel_'):
|
||||||
|
# selected_kind = "channel"
|
||||||
|
# channel_id = int(data.split('_')[1])
|
||||||
|
|
||||||
|
# # ACL: право постинга
|
||||||
|
# me = await get_or_create_admin(session, update.effective_user.id)
|
||||||
|
# allowed = await has_scope_on_channel(session, me.id, channel_id, SCOPE_POST)
|
||||||
|
# if not allowed:
|
||||||
|
# await query.edit_message_text("У вас нет права постить в этот канал.")
|
||||||
|
# return ConversationHandler.END
|
||||||
|
|
||||||
|
# channel = (await session.execute(sa_select(Channel).where(Channel.id == channel_id))).scalar_one_or_none()
|
||||||
|
# if not channel:
|
||||||
|
# await query.edit_message_text("Канал не найден.")
|
||||||
|
# return ConversationHandler.END
|
||||||
|
# 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]
|
||||||
|
# )
|
||||||
|
|
||||||
|
# elif data and data.startswith('group_'):
|
||||||
|
# selected_kind = "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("Группа не найдена.")
|
||||||
|
# return ConversationHandler.END
|
||||||
|
# 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]
|
||||||
|
# )
|
||||||
|
|
||||||
|
# if not chat_id or not (chat_id.startswith('@') or chat_id.startswith('-')):
|
||||||
|
# await query.edit_message_text('Ошибка: ссылка должна быть username (@channel) или числовой ID (-100...)')
|
||||||
|
# return ConversationHandler.END
|
||||||
|
|
||||||
|
# ud = context.user_data or {}
|
||||||
|
# text: str = ud.get("text", "") or ""
|
||||||
|
# entities: List[MessageEntity] = ud.get("entities", []) or []
|
||||||
|
|
||||||
|
# # санация для custom_emoji
|
||||||
|
# entities = _strip_broken_entities(entities)
|
||||||
|
# entities = _split_custom_emoji_by_utf16(text, entities)
|
||||||
|
|
||||||
|
# # если есть клавиатура, ПРЕДПОЧИТАЕМ отправку "вручную" (а не copyMessage),
|
||||||
|
# # т.к. в некоторых кейсах клавиатура при копировании может не приклеиться
|
||||||
|
# prefer_manual_send = markup is not None
|
||||||
|
|
||||||
|
# # определим, были ли “части медиа”
|
||||||
|
# has_media_parts = any(k in ud for k in ("photo","animation","video","document","audio","voice","sticker"))
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# if not prefer_manual_send and ud.get("src_chat_id") and ud.get("src_msg_id") and not has_media_parts:
|
||||||
|
# # 1) copyMessage без медиа и без клавиатуры — максимальная идентичность
|
||||||
|
# msg_id_obj = await context.bot.copy_message(
|
||||||
|
# chat_id=chat_id,
|
||||||
|
# from_chat_id=ud["src_chat_id"],
|
||||||
|
# message_id=ud["src_msg_id"],
|
||||||
|
# reply_markup=markup # если вдруг есть, попробуем сразу
|
||||||
|
# )
|
||||||
|
# # страховка: если клавиатуры нет/не приклеилась — прикрутим edit_message_reply_markup
|
||||||
|
# if markup and getattr(msg_id_obj, "message_id", None):
|
||||||
|
# try:
|
||||||
|
# await context.bot.edit_message_reply_markup(
|
||||||
|
# chat_id=chat_id,
|
||||||
|
# message_id=msg_id_obj.message_id,
|
||||||
|
# reply_markup=markup
|
||||||
|
# )
|
||||||
|
# except Exception:
|
||||||
|
# pass
|
||||||
|
# await query.edit_message_text(f'Пост отправлен{" в: " + selected_title if selected_title else "!"}')
|
||||||
|
# return ConversationHandler.END
|
||||||
|
|
||||||
|
# # 2) fallback / manual — ВСЕГДА прикладываем reply_markup
|
||||||
|
# sent = False
|
||||||
|
# if "photo" in ud:
|
||||||
|
# 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 = True
|
||||||
|
# elif "animation" in ud:
|
||||||
|
# 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 = True
|
||||||
|
# elif "video" in ud:
|
||||||
|
# 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 = True
|
||||||
|
# elif "document" in ud:
|
||||||
|
# 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 = True
|
||||||
|
# elif "audio" in ud:
|
||||||
|
# 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 = True
|
||||||
|
# elif "voice" in ud:
|
||||||
|
# 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 = True
|
||||||
|
# elif "sticker" in ud:
|
||||||
|
# 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)
|
||||||
|
# sent = True
|
||||||
|
# else:
|
||||||
|
# await context.bot.send_message(chat_id=chat_id, text=text, entities=entities, reply_markup=markup)
|
||||||
|
# sent = True
|
||||||
|
|
||||||
|
# await query.edit_message_text(f'Пост отправлен{" в: " + selected_title if selected_title else "!"}' if sent else 'Ошибка: не удалось отправить сообщение.')
|
||||||
|
|
||||||
|
# except BadRequest as e:
|
||||||
|
# await query.edit_message_text(f'Ошибка отправки поста: {e}')
|
||||||
|
|
||||||
|
# return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
if not query:
|
if not query:
|
||||||
@@ -150,14 +295,14 @@ async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
async with AsyncSessionLocal() as session:
|
async with AsyncSessionLocal() as session:
|
||||||
chat_id = None
|
chat_id = None
|
||||||
markup = None
|
markup = None
|
||||||
|
selected_title = None
|
||||||
|
|
||||||
if data and data.startswith('channel_'):
|
if data and data.startswith('channel_'):
|
||||||
channel_id = int(data.split('_')[1])
|
channel_id = int(data.split('_')[1])
|
||||||
|
|
||||||
# ACL: проверить право постинга
|
# ACL
|
||||||
me = await get_or_create_admin(session, update.effective_user.id)
|
me = await get_or_create_admin(session, update.effective_user.id)
|
||||||
allowed = await has_scope_on_channel(session, me.id, channel_id, SCOPE_POST)
|
if not await has_scope_on_channel(session, me.id, channel_id, SCOPE_POST):
|
||||||
if not allowed:
|
|
||||||
await query.edit_message_text("У вас нет права постить в этот канал.")
|
await query.edit_message_text("У вас нет права постить в этот канал.")
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
@@ -165,9 +310,10 @@ async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
if not channel:
|
if not channel:
|
||||||
await query.edit_message_text("Канал не найден.")
|
await query.edit_message_text("Канал не найден.")
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
chat_id = channel.link
|
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()
|
btns = (await session.execute(sa_select(Button).where(Button.channel_id == channel_id))).scalars().all()
|
||||||
if btns:
|
if btns:
|
||||||
markup = InlineKeyboardMarkup([[InlineKeyboardButton(str(b.name), url=str(b.url))] for b in btns])
|
markup = InlineKeyboardMarkup([[InlineKeyboardButton(str(b.name), url=str(b.url))] for b in btns])
|
||||||
@@ -178,82 +324,66 @@ async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
if not group:
|
if not group:
|
||||||
await query.edit_message_text("Группа не найдена.")
|
await query.edit_message_text("Группа не найдена.")
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
chat_id = group.link
|
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()
|
btns = (await session.execute(sa_select(Button).where(Button.group_id == group_id))).scalars().all()
|
||||||
if btns:
|
if btns:
|
||||||
markup = InlineKeyboardMarkup([[InlineKeyboardButton(str(b.name), url=str(b.url))] for b in btns])
|
markup = InlineKeyboardMarkup([[InlineKeyboardButton(str(b.name), url=str(b.url))] for b in btns])
|
||||||
|
|
||||||
if not chat_id:
|
if not chat_id or not (chat_id.startswith('@') or chat_id.startswith('-')):
|
||||||
await query.edit_message_text('Ошибка: объект не найден.')
|
await query.edit_message_text('Ошибка: ссылка должна быть @username или -100...')
|
||||||
return ConversationHandler.END
|
|
||||||
|
|
||||||
chat_id = chat_id.strip()
|
|
||||||
if not (chat_id.startswith('@') or chat_id.startswith('-')):
|
|
||||||
await query.edit_message_text('Ошибка: ссылка должна быть username (@channel) или числовой ID (-100...)')
|
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
ud = context.user_data or {}
|
ud = context.user_data or {}
|
||||||
text: str = ud.get("text", "") or ""
|
text: str = ud.get("text", "") or ""
|
||||||
entities: List[MessageEntity] = ud.get("entities", []) or []
|
entities: List[MessageEntity] = ud.get("entities", []) or []
|
||||||
|
|
||||||
# санация перед отправкой (custom_emoji/UTF-16)
|
# санация custom_emoji/UTF-16
|
||||||
entities = _strip_broken_entities(entities)
|
entities = _strip_broken_entities(entities)
|
||||||
entities = _split_custom_emoji_by_utf16(text, entities)
|
entities = _split_custom_emoji_by_utf16(text, entities)
|
||||||
|
|
||||||
# 1) Пытаемся «бит-в-бит» копию + КЛАВИАТУРУ
|
# >>> ВАЖНО: НИКАКОГО copyMessage. Всегда manual send с reply_markup <<<
|
||||||
has_media_parts = any(k in ud for k in ("photo","animation","video","document","audio","voice","sticker"))
|
|
||||||
try:
|
|
||||||
if ud.get("src_chat_id") and ud.get("src_msg_id") and not has_media_parts:
|
|
||||||
await context.bot.copy_message(
|
|
||||||
chat_id=chat_id,
|
|
||||||
from_chat_id=ud["src_chat_id"],
|
|
||||||
message_id=ud["src_msg_id"],
|
|
||||||
reply_markup=markup, # <— ВАЖНО
|
|
||||||
)
|
|
||||||
await query.edit_message_text("Пост отправлен!")
|
|
||||||
return ConversationHandler.END
|
|
||||||
except BadRequest:
|
|
||||||
pass # упадём в fallback
|
|
||||||
|
|
||||||
# 2) fallback — отправка с entities/caption_entities и КЛАВИАТУРОЙ
|
|
||||||
try:
|
try:
|
||||||
sent = False
|
sent = False
|
||||||
if "photo" in ud:
|
if "photo" in ud:
|
||||||
await context.bot.send_photo(chat_id=chat_id, photo=ud["photo"],
|
await context.bot.send_photo(chat_id=chat_id, photo=ud["photo"],
|
||||||
caption=(text or None), caption_entities=(entities if text else None),
|
caption=(text or None),
|
||||||
|
caption_entities=(entities if text else None),
|
||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
sent = True
|
sent = True
|
||||||
elif "animation" in ud:
|
elif "animation" in ud:
|
||||||
await context.bot.send_animation(chat_id=chat_id, animation=ud["animation"],
|
await context.bot.send_animation(chat_id=chat_id, animation=ud["animation"],
|
||||||
caption=(text or None), caption_entities=(entities if text else None),
|
caption=(text or None),
|
||||||
|
caption_entities=(entities if text else None),
|
||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
sent = True
|
sent = True
|
||||||
elif "video" in ud:
|
elif "video" in ud:
|
||||||
await context.bot.send_video(chat_id=chat_id, video=ud["video"],
|
await context.bot.send_video(chat_id=chat_id, video=ud["video"],
|
||||||
caption=(text or None), caption_entities=(entities if text else None),
|
caption=(text or None),
|
||||||
|
caption_entities=(entities if text else None),
|
||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
sent = True
|
sent = True
|
||||||
elif "document" in ud:
|
elif "document" in ud:
|
||||||
await context.bot.send_document(chat_id=chat_id, document=ud["document"],
|
await context.bot.send_document(chat_id=chat_id, document=ud["document"],
|
||||||
caption=(text or None), caption_entities=(entities if text else None),
|
caption=(text or None),
|
||||||
|
caption_entities=(entities if text else None),
|
||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
sent = True
|
sent = True
|
||||||
elif "audio" in ud:
|
elif "audio" in ud:
|
||||||
await context.bot.send_audio(chat_id=chat_id, audio=ud["audio"],
|
await context.bot.send_audio(chat_id=chat_id, audio=ud["audio"],
|
||||||
caption=(text or None), caption_entities=(entities if text else None),
|
caption=(text or None),
|
||||||
|
caption_entities=(entities if text else None),
|
||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
sent = True
|
sent = True
|
||||||
elif "voice" in ud:
|
elif "voice" in ud:
|
||||||
await context.bot.send_voice(chat_id=chat_id, voice=ud["voice"],
|
await context.bot.send_voice(chat_id=chat_id, voice=ud["voice"],
|
||||||
caption=(text or None), caption_entities=(entities if text else None),
|
caption=(text or None),
|
||||||
|
caption_entities=(entities if text else None),
|
||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
sent = True
|
sent = True
|
||||||
elif "sticker" in ud:
|
elif "sticker" in ud:
|
||||||
# Можно прикрепить клавиатуру и к стикеру (Telegram поддерживает reply_markup)
|
|
||||||
await context.bot.send_sticker(chat_id=chat_id, sticker=ud["sticker"], reply_markup=markup)
|
await context.bot.send_sticker(chat_id=chat_id, sticker=ud["sticker"], reply_markup=markup)
|
||||||
# Если есть текст — отправим вторым сообщением (кнопки уже в первом)
|
|
||||||
if text:
|
if text:
|
||||||
await context.bot.send_message(chat_id=chat_id, text=text, entities=entities)
|
await context.bot.send_message(chat_id=chat_id, text=text, entities=entities)
|
||||||
sent = True
|
sent = True
|
||||||
@@ -261,13 +391,14 @@ async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
await context.bot.send_message(chat_id=chat_id, text=text, entities=entities, reply_markup=markup)
|
await context.bot.send_message(chat_id=chat_id, text=text, entities=entities, reply_markup=markup)
|
||||||
sent = True
|
sent = True
|
||||||
|
|
||||||
await query.edit_message_text('Пост отправлен!' if sent else 'Ошибка: не удалось отправить сообщение.')
|
await query.edit_message_text(f'Пост отправлен{(" в: " + selected_title) if selected_title else "!"}' if sent else 'Ошибка: не удалось отправить сообщение.')
|
||||||
except BadRequest as e:
|
except BadRequest as e:
|
||||||
await query.edit_message_text(f'Ошибка отправки поста: {e}')
|
await query.edit_message_text(f'Ошибка отправки поста: {e}')
|
||||||
|
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
new_post_conv = ConversationHandler(
|
new_post_conv = ConversationHandler(
|
||||||
entry_points=[CommandHandler("new_post", new_post_start)],
|
entry_points=[CommandHandler("new_post", new_post_start)],
|
||||||
states={
|
states={
|
||||||
|
|||||||
Reference in New Issue
Block a user