- MAX→TG album: caption over Telegram's 1024 media-group limit is no longer lost —
the album is sent without an inline caption and the full text follows as a
separate message (sendMaxAlbumToTg, covers both direct and queued delivery).
- Forwarded text: strip Telegram custom_emoji (MAX can't render them; the fallback
glyph only littered the copy) in tgEntitiesToHTML, and collapse whitespace padding
(runs of spaces/nbsp, trailing spaces, 3+ blank lines) in crosspost captions —
cuts bloated length (MAX ~4000 char limit) and fixes ugly gaps.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ajcn7YhgeexPwbfrUNMj8u
- MAX media upload: drop deprecated &v=1.2.5 from /uploads and /messages
(MAX returns 404 with it; photos/videos broke, text was unaffected).
- Run group moderation before discussion-comment ingest.
- Generic addon extension hooks; core stays agnostic to their semantics:
CrosspostAllowed, CrosspostDeliverable, HandleMaxCommand.
- Customizable /help and /start from a file next to the binary (HELP_FILE).
- /link command; optional web_app menu button (MINIAPP_URL).
- Track bot chats and pair owners; migrations 017-019.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migration 000016. Existing 825 rows keep created_at=0 (we can't
backfill accurately); new /bridge pairs get a real unix timestamp.
SQLite uses INSERT … ON CONFLICT … DO UPDATE SET prefix=0 (previously
INSERT OR REPLACE, which would have wiped created_at on conflict).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Issue #38 part 2. Allows using multiple MAX group chats as per-thread
mirrors of a TG forum group, since MAX has no native thread concept.
- New table thread_pairs (tg_chat_id, tg_thread_id, max_chat_id) with
unique(max_chat_id) — one MAX chat = at most one TG thread.
- pending gains thread_id column for thread-bridge key exchange.
- Repo methods: StartThreadBridge (TG issues key), CompleteThreadBridge
(MAX consumes key), GetThreadMaxChat, GetThreadTgPair, UnpairThread,
UnpairThreadByMax.
- Commands:
* /thread_bridge in a TG forum thread (admin, non-General) -> key
* /thread_bridge <key> in a MAX chat -> binds
* /thread_unbridge on either side
TG's BOT_COMMAND_INVALID rule forbids hyphens, so the commands use
underscore.
- Routing priority: thread-bridge > regular pair.
TG->MAX: if msg.MessageThreadID has a thread_pair, route there.
MAX->TG: if MAX chat is in thread_pairs, route to (tg_chat, thread).
- For thread-paired MAX chats, the Part 1 reply-to-source-thread
override is disabled: thread-paired chats have a fixed target thread,
replies stay there.
- Safeguard: a MAX chat cannot be in both pairs and thread_pairs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Issue #38 part 1: MAX reply to a bridged TG message now lands in the
same TG forum thread as the original, not in the pair's default
thread. messages table gets tg_thread_id column (migration 000014);
SaveMsg stores the TG thread, LookupTgMsgID returns it, forwardMaxToTg
applies source thread for reply-routing (both body.ReplyTo and
Link.Type=reply paths).
- Fix crosspost attribution heuristic: forwardMaxToTg used
"caption != text" to detect bridge mode, which broke when MaxToTg
replacements or whitespace made caption differ from raw body.Text —
MAX→TG crossposts then got [MAX] prefix and bold name. Now explicit
isCrosspost flag.
- /bridge without key in an already-linked chat no longer generates a
fresh key; instead shows "already linked" hint with pairing guidance
(both TG and MAX sides).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New setting per crosspost pair — disabled by default:
- Toggle via inline button "Синк правок" in TG and MAX
- When enabled: edits and deletes sync between channels (respecting direction)
- When disabled (default): only new posts are crossposted
- Migration 000013 adds sync_edits column to crossposts table
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Store tg_thread_id in pairs table (from /bridge command location)
- tgSendText/tgSendMediaToThread with message_thread_id support
- Text messages in MAX→TG now sent to correct topic
- Raw HTTP for thread_id since go-telegram-bot-api v5 lacks support
Fixes TOPIC_CLOSED errors in TG forum groups.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per-crosspost configurable find/replace rules applied during forwarding.
Supports string and regex replacements, scoped to full text or links only.
Each direction (TG→MAX, MAX→TG) has its own set of rules.
UI: inline buttons for add/delete/toggle target type in both TG and MAX bots.
Closes#14
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Failed MAX→TG messages now enqueued with media URL and retried
automatically. Supports photo, video, audio, file, sticker.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add users table: TouchUser on every DM interaction
- Add tg_owner_id to crossposts: same person has different IDs on TG/MAX
- isCrosspostOwner checks both owner_id and tg_owner_id
- ListCrossposts matches by either owner ID
- TG user ID captured when forwarding channel post
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of DELETE, crossposts are marked with deleted_at timestamp
and deleted_by user ID. All queries filter by deleted_at = 0.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Only the user who created a crosspost link can change direction
or delete it. Prevents unauthorized users from managing others'
crosspost links via /crosspost list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Messages that fail to send to MAX are saved to DB and retried
automatically every 10s with exponential backoff (10s→30s→1m→2m).
Max 20 attempts or 30 min age, then dropped with notification.
Also: /whoami command, circuit breaker notifications, 15s HTTP timeout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Channels should forward content as-is without [TG]/[MAX] prefixes.
Removed prefix column from crossposts table, HasCrosspostPrefix/
SetCrosspostPrefix methods, and /crosspost prefix command.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Support TG channel posts (update.ChannelPost/EditedChannelPost),
add direction control (tg>max, max>tg, both), prefix toggle,
and shared forwarding helpers (forwardTgToMax, forwardMaxToTg).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Webhook paths now include token-derived secret (prevents spoofed updates)
- HTTP server with Read/Write/Idle timeouts (prevents slowloris)
- Shared HTTP client with 60s timeout for all uploads/downloads
- Removed tokens and sensitive data from debug logs
- Retry loop respects context cancellation instead of blocking sleep
- Pending bridge keys expire after 1 hour (migration 000003)
- Increased bridge key entropy from 32 to 64 bits (16 hex chars)
- Docker container runs as non-root user
- Extracted admin check helpers with unit tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bi-directional message bridge between Telegram and MAX messengers.
Features: 1:1 chat pairing, reply support, message formatting,
prefix toggle, SQLite/PostgreSQL with golang-migrate, Dockerfile,
CI/CD with GitHub Actions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>