"""/autopost: the posts of a whole series - "Episodes 01 to 20", "Episodes 21 to 40", ... - each with a download button.
The ranges are planned here (pure functions, easy to test); the handler in handlers/autopost.py sends them.
"""
from __future__ import annotations
PLACEHOLDER_LINK = "https://t.me/English" # every button starts with this link; the real ones are asked for afterwards
MAX_POSTS = 200 # the most one /autopost run may post
MAX_EPISODES = 100_000
USAGE = (
"Usage: /autopost <total episodes> <interval>\n"
"Example: /autopost 114 20 posts Episodes 01 to 20, 21 to 40 ... up to 114, each with a download "
"button, and then asks you for the real link of every button."
)
def parse_autopost_args(raw: str) -> tuple:
"""'114 20' -> (114, 20). ValueError (with a message to show) when it can't be used."""
parts = (raw or "").replace(",", " ").split()
if len(parts) != 2 or not all(p.isdigit() for p in parts):
raise ValueError("I need two numbers: the total number of episodes and the interval.")
total, interval = int(parts[0]), int(parts[1])
if total < 1 or interval < 1:
raise ValueError("Both numbers must be at least 1.")
if total > MAX_EPISODES:
raise ValueError(f"The total can be at most {MAX_EPISODES}.")
return total, interval
def plan_ranges(total: int, interval: int) -> list:
"""[(first, last), ...] covering 1..total in steps of `interval`.
The last, shorter range gets its own post when it is longer than half an interval (114 with 20 -> 101 to 114);
when it is half an interval or less it is added to the post before it (102 with 20 -> 81 to 102)."""
if total < 1 or interval < 1:
raise ValueError("total and interval must be at least 1")
ranges, start = [], 1
while start <= total:
end = min(start + interval - 1, total)
ranges.append((start, end))
start = end + 1
if len(ranges) >= 2:
a, b = ranges[-1]
size = b - a + 1
if size < interval and size * 2 <= interval:
ranges.pop()
ranges[-1] = (ranges[-1][0], b)
return ranges
def pad(n: int) -> str:
return f"{n:02d}"
def span(a: int, b: int) -> str:
return f"{pad(a)} to {pad(b)}"
def title_of(a: int, b: int) -> str:
return f"Episodes {span(a, b)}" if a != b else f"Episode {pad(a)}"
def post_text(a: int, b: int) -> str:
return f"{title_of(a, b)}\n\nClick the button below to download 👇"
def button_label(a: int, b: int) -> str:
return f"Download {title_of(a, b)}"
def post_buttons(a: int, b: int, link: str = PLACEHOLDER_LINK) -> list:
return [[{"t": button_label(a, b), "u": link}]]
def ranges_summary(ranges: list, show: int = 8) -> str:
"""'01–20 · 21–40 · 41–60 · … · 101–114' (the middle is left out of long lists)."""
parts = [f"{pad(a)}–{pad(b)}" if a != b else pad(a) for a, b in ranges]
if len(parts) > show:
parts = parts[: show - 2] + ["…"] + parts[-2:]
return " · ".join(parts)