"""/new: create a post, with the episode-range auto-suggestion.""" from __future__ import annotations import logging from telethon import Button from .. import picker from ..buttons import norm_url, parse_buttons_text, set_field from ..common import Ctx, UserError, clear_state, cmd, discard_temp, guard, on_cb, on_text, say, set_state, show from ..suggest import make_suggestion from ..tgutil import esc, short from ..ui import CANCEL, KIND_NAME from .panel import markup_note, read_content, show_panel log = logging.getLogger(__name__) BUTTON_HELP = ( "βž• Now the buttons\n" "Send one row per line as Button text - link.\n" "Several buttons in one row: separate them with | .\n\n" "Example:\nDownload Episodes 01 to 20 - https://t.me/yourbot?start=abc\n\n" "Or send /skip to post without buttons." ) def register(ctx: Ctx) -> None: client, db = ctx.client, ctx.db async def ask_content(event, cid: int) -> None: set_state(ctx, event.sender_id, "new_content", cid=cid) await show( event, "✍️ Send the post now.\nText, or a photo / video / GIF / file with a caption. " "Formatting (bold, links, spoilers...) is kept. No Telegram editor at hand? Type HTML or Markdown - /formatting shows how; " "headings, lists, tables, math and the other rich blocks: /rich.", CANCEL, ) async def start_for_channel(event, cid: int) -> None: ch = await db.get_channel(cid) if ch is None: raise UserError("That channel is not registered. Use /addchannel.") sug = await make_suggestion(db, cid) if sug is None: await ask_content(event, cid) return lines = [ f"πŸ’‘ Suggestion for {esc(ch.title)}", f"Your last button post: {esc(sug.prev_label)}", "The next one would be:", ] if (sug.text or "").strip(): lines.append(f"β€’ Message: {esc(short(sug.text, 120))}") lines.append(f"β€’ Button: {esc(sug.new_label)}") if sug.media_kind: lines.append(f"β€’ Media: same {KIND_NAME.get(sug.media_kind, 'file')} as that post (you can change it before publishing)") lines.append("\nUse it and I'll only ask for the button link.") kb = [ [Button.inline("βœ… Use suggestion", f"ns:{cid}"), Button.inline("✏️ Customize", f"nn:{cid}")], CANCEL[0], ] await show(event, "\n".join(lines), kb) async def ask_link(event, post, pos) -> None: r, c = pos label = post.buttons[r][c]["t"] await say( event, f"πŸ”— Send the link for the button {esc(label)}\n" "(for example https://t.me/yourbot?start=abc)", CANCEL, ) @client.on(cmd("new")) @guard(ctx) async def h_new(event): uid = event.sender_id await discard_temp(ctx, uid) clear_state(ctx, uid) chans = await db.list_channels() if not chans: raise UserError("No channels yet. Register one with /addchannel first.") if len(chans) == 1: await start_for_channel(event, chans[0].id) return await show_picker(event, "", 0) async def show_picker(event, arg: str, page: int) -> None: chans = await db.list_channels() if not chans: raise UserError("No channels yet. Register one with /addchannel first.") text, kb = picker.render( chans, page, head="πŸ“’ Which channel is this post for?", kind="nc", choose=lambda c: f"nc:{c.id}", extra_rows=CANCEL ) await show(event, text, kb) ctx.pickers["nc"] = show_picker @on_cb(ctx, "nc") async def cb_channel(event, parts): await discard_temp(ctx, event.sender_id) await start_for_channel(event, int(parts[0])) @on_cb(ctx, "nn") async def cb_customize(event, parts): await ask_content(event, int(parts[0])) @on_cb(ctx, "ns") async def cb_use_suggestion(event, parts): uid, cid = event.sender_id, int(parts[0]) sug = await make_suggestion(db, cid) if sug is None: raise UserError("There is nothing to suggest any more - send the post yourself with /new.") await discard_temp(ctx, uid) post = await db.create_post( channel_id=cid, created_by=uid, status="draft", source="bot", text=sug.text, entities=sug.entities, buttons=sug.buttons, media_kind=sug.media_kind, media_file_id=sug.media_file_id, link_preview=False, ) set_state(ctx, uid, "new_link", pid=post.id, pending=list(sug.pending), temp=True) await show(event, f"βœ… Using the suggestion: {esc(sug.new_label)}") await ask_link(event, post, sug.pending[0]) @on_text(ctx, "new_link") async def h_new_link(event, st): url = norm_url(event.raw_text or "") if not url: raise UserError("That doesn't look like a link. Send something like https://t.me/yourbot?start=abc") post = await db.get_post(st["pid"]) if post is None: clear_state(ctx, event.sender_id) raise UserError("That draft is gone. Start again with /new.") r, c = st["pending"][0] post = await db.update_post(post.id, buttons=set_field(post.buttons, r, c, u=url)) st["pending"] = st["pending"][1:] if st["pending"]: await ask_link(event, post, st["pending"][0]) return clear_state(ctx, event.sender_id) await show_panel(ctx, event, post.id, note="βœ… Ready. Preview it, or tap Publish.", new=True) @on_text(ctx, "new_content") async def h_new_content(event, st): m = event.message if m.grouped_id and st.get("group") == m.grouped_id: return # the other items of an album: only the first one is used if m.grouped_id: st["group"] = m.grouped_id content = await read_content(ctx, event) post = await db.create_post( channel_id=st["cid"], created_by=event.sender_id, status="draft", source="bot", buttons=[], link_preview=False, **content, ) set_state(ctx, event.sender_id, "new_buttons", pid=post.id, temp=True) await say(event, markup_note(content.how) + BUTTON_HELP, CANCEL) @on_text(ctx, "new_buttons") async def h_new_buttons(event, st): if event.message.grouped_id: return try: rows = parse_buttons_text(event.raw_text or "") except ValueError as e: raise UserError(f"❌ {e}\n\nTry again, or send /skip.") if not rows: raise UserError("Send at least one button as β€œText - link”, or /skip.") await db.update_post(st["pid"], buttons=rows) clear_state(ctx, event.sender_id) await show_panel(ctx, event, st["pid"], note="βœ… Ready. Preview it, or tap Publish.", new=True) @client.on(cmd("skip")) @guard(ctx) async def h_skip(event): st = ctx.state.get(event.sender_id) if not st or st["mode"] != "new_buttons": raise UserError("There is nothing to skip right now.") clear_state(ctx, event.sender_id) await show_panel(ctx, event, st["pid"], note="βœ… Ready (no buttons). Preview it, or tap Publish.", new=True)