Repository navigation
feat(webapp): open a bot's Mini App with real signed launch data - #295
Closed
ApexAiOfficial wants to merge 21 commits into
Closed
ApexAiOfficial wants to merge 21 commits into
ApexAiOfficial wants to merge 21 commits into
Conversation
Several tg processes routinely share one session directory (parallel agent commands, or two agents driving the same account). gotd's FileStorage and the peer cache truncated files in place, so a concurrent reader could load an empty or torn session or cache and fail. - internal/atomicfile: write to a temp file in the same directory, then rename over the target (fsync for sessions/config; no fsync for the cache). - File session store keeps gotd's on-disk format and path. - Peer cache merges entries other processes saved since it was loaded, so concurrent commands no longer drop each other's access hashes, and treats a corrupt cache as empty instead of failing every command. - writeConfig refuses to overwrite with O_EXCL instead of stat-then-write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Make tg a dependable harness for agents that test Telegram bots.
Bot testing:
- Messages now carry reply_markup (inline buttons and reply keyboards, with
callback data), entities (with the covered text), file details (kind, name,
MIME, size, duration, dimensions), edit_date, via_bot_id and grouped_id.
All new fields are omitted when empty, so existing JSON consumers see the
same shape.
- tg click presses a button like a client: callback queries return the bot's
answer (an unanswered callback is not an error), reply-keyboard buttons send
their label, URL/copy buttons report their target. Buttons are chosen by
label, callback data, index or row/col; errors list the available buttons.
- --wait on send, reply, click and start-bot listens before acting and
collects the bot's replies and edits until it settles (--settle, extended
while the bot is typing), --replies arrive, or --wait elapses.
- tg start-bot sends a deep-link /start with a parameter (messages.startBot),
also accepting t.me/<bot>?start=<param> links.
- tg bot-info shows a bot's descriptions, command menu, menu button and
inline/group capabilities.
- --stdin on send, reply and edit keeps secrets out of the process list.
- history --after <id> returns only newer messages.
Reliability:
- Exit codes: 2 usage, 3 auth, 4 not found, 5 flood wait, 6 timeout, 7 other
RPC error, 130 interrupted; 1 stays the generic failure.
- With -o json a failure also writes {"schema":1,"error":{...}} to stdout
(code, exit, message, retry_after, rpc); stderr is unchanged.
- Global --timeout (TG_TIMEOUT) bounds any command; watch ends cleanly at it.
- Flood waits are logged instead of looking like a hang.
- tg version / --version report the build, commit and whether app
credentials are embedded.
- watch/wait register update handlers before connecting (the dispatcher map
was modified while updates were being dispatched).
- getMessage checks the message belongs to the requested chat:
messages.getMessages is not scoped to one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
watch and wait passed their peer argument straight to the username resolver, so `tg wait me` failed with USERNAME_INVALID and id:<n> filters never worked. A shared resolveManagedPeer handles me/self, id:<n> and the usual username/phone/link forms; bot-info uses it too, so `bot-info me` reports "not a bot" instead of a lookup failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The walk callback called info.IsDir() before checking the walk error, so `tg upload ./missing` dereferenced a nil FileInfo and panicked. The path is now checked before connecting, and the walk lives in a tested helper. upload gains --wait/--settle/--replies, so a bot's handling of documents, photos and voice notes can be verified in one call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README: fork notes, Testing bots, Exit codes, --timeout, source install. - skills/tg: bot-testing loop for agents (bot-info, --wait, click, start-bot, media), result shapes, exit-code branching, rate-limit etiquette. - CLAUDE.md: error classification, collector registration rule, JSON compatibility rule, atomic shared files, local toolchain paths. - scripts/install-local.sh: build with -trimpath, embed app credentials from APP_ID/APP_HASH or an existing binary's build info (values never printed), keep versioned builds and switch <prefix>/bin/tg atomically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TG_CONFIG sets the default --config path, matching TG_ACCOUNT, TG_PROXY and TG_TIMEOUT. The tg skill tells agents to pass the user's configured -c/-a on every call and never to run tg init/login in such a setup. - golangci-lint 2.12.1 with Go 1.25 (the CI pair) now reports 0 issues: named constants for file kinds, peer aliases and command names (goconst also counts literals in tests), classification lists as functions, promoted marshalText calls, unused newRootCmd wrapper removed. - Test pinning the send/reply JSON shape with and without --wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Go omits -ldflags from a binary's build info when building with -trimpath, so a tg installed by install-local.sh carried no recoverable credentials and the next run (default --creds-from ~/.local/bin/tg) failed. Build without -trimpath, as release builds do, and refuse to install a binary whose build info does not round-trip the credentials. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Setting a cobra version template linked text/template, whose reflective
method lookup (MethodByName) disables the linker's dead-method elimination,
so every exported method of gotd/td's generated API was kept: the binary grew
from 19 MB to 45 MB. Use cobra's template-free default ("tg version …") and
document the trap with a -dumpdep check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Atomic replacement stopped torn reads, but two tg processes that both read, merged and replaced the cache still lost each other's entries: an overlapping-writers test (8 writers x 25 saves) lost 18-64 of 200 entries. internal/filelock takes an advisory exclusive lock on a sidecar <file>.lock (flock on Unix, LockFileEx on Windows, no-op where single-process), released by the OS if the holder dies. The peer cache holds it for the whole read-merge-write cycle; the regression test now keeps every entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gotd re-saves the session on every (re)connect to the primary DC, re-reading the file and writing its own auth key into it. A long-running process (e.g. tg watch) that reconnected after another process ran tg login or tg logout therefore wrote its old key back, undoing the login or resurrecting the logged-out session. The file store now remembers the auth key it loaded and, under a lock on <file>.lock, skips a write of that key when the file was re-keyed or removed by someone else. Same-key writers remain last-writer-wins, which is safe: the copies differ only in volatile state such as the server salt. The guarantees are documented on fileSessionStore, and tests cover re-login, logout, same-key writers and this process re-keying. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
connectWith treats context cancellation as a clean stop, so a command interrupted by Ctrl-C usually returned nil and tg exited 0, contradicting the documented exit code 130. main now records which signal stopped tg and execute reports it whatever the command returned: 130 for SIGINT, 143 for SIGTERM (128+signal, the shell convention), with the JSON error envelope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- writeConfig created the final file with O_EXCL and then wrote into it, so a crash mid-write left a partial config that a retried tg init refused to replace. atomicfile.Create writes and syncs a temp file and hard-links it into place (atomic, fails if the target exists), falling back to an exclusive synced create on filesystems without hard links. - Durable writes now also fsync the parent directory, so a completed rename survives power loss (skipped on Windows, which cannot sync a directory). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getMessage skipped the chat check for "me", so `tg download me 123` (and click/reactions on me) returned message 123 of whichever private chat or basic group held that id. A Saved Messages hit is now accepted only when the response marks the message's user as the current user, without an extra RPC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The collector attributed every message from the peer during the wait to the action, but Telegram does not record which message a bot is answering. The result now separates what can be proven: - replies: the peer's messages ordered after the action's own message (observed activity; documented as such). - linked: ids Telegram ties to the action, i.e. replies whose reply_to is the sent message and edits of the pressed message (deterministic). - earlier: messages ordered before the action (message ids are monotonic per chat), e.g. a late answer to a previous command; never responses, and no longer counted toward --replies. send/reply/start-bot/upload/click record their action once its RPC returns. README, the tg skill and CLAUDE.md document the semantics, the narrowed session-sharing guarantees and the 128+signal exit codes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
golangci-lint goconst (which also counts test literals) flagged "version" and "windows"; syncDir is now selected by build tags instead of a runtime GOOS check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"interrupted by interrupt" (Go's signal String) becomes "interrupted by SIGINT" / "interrupted by SIGTERM". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tg doctor runs the checks an agent otherwise reconstructs from exit codes: config, selected account, app credentials (source only, never values), the stored session (present and parseable; Keychain-aware), the peer cache, and unless --offline a live connection and login status with round-trip time. Each failure carries a fix hint; the result is printed in full and the exit code is 3 when not logged in, 1 for any other failure. --account all checks every account. Commands that already printed their result now return reportedError so execute adds no second error report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat: bot-testing harness, stable exit codes, safely shared session files
replies is always a list; edits, linked, earlier and timed_out follow the omitempty convention of tg's JSON. Show consumers how to read them with a default (reported by the Scrawl-Bot session's smoke test). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(doctor): diagnose config, credentials, session and connection
tg webapp asks Telegram to open a Mini App as a client does (the menu button, a web app button, a URL, or the main app), so a test can drive the app's API or load it in a browser with genuine initData and no GUI client. The launch data is a credential: it is printed only with --reveal, and --out writes it to an owner-only file.
Author
|
Opened here by mistake (a tooling default targeted upstream instead of our fork). Closing, sorry for the noise. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
tg webapp <bot>: it asks Telegram to open a Mini App the way a client does, so a test gets genuineinitData, signed by Telegram, with no GUI client.Opens: the menu button (default), a web app button (
--text), a URL (--url) or the main Mini App (--main, with--start-param).--platformsets the reported client.The credential: launch data lets its holder act as this account in that Mini App. It's left out of stdout and JSON unless
--revealis passed,--outwrites it to a 0600 file, and parse errors never quote it.Tests: URL and launch-data parsing, redaction, the file's mode, button selection, and that errors don't leak the credential. A real run against Shop Bot's staging bot follows.
🤖 Generated with Claude Code