The rebuild is complete. This document is now a historical record of how
github.com/gotd/botapiwas rebuilt from a codegen-first OpenAPI/ogen project into a hand-written, MTProto-backed Bot API library. All phases below landed onmain; the library connects, serves updates, and exposes a broad typed Bot API surface. Ongoing work is incremental feature parity, not reconstruction; theconformance_test.godrift guard tracks coverage against the published Bot API. Companion docs:architecture.md,building-blocks.md.
Legend: ☐ todo · ◐ in progress · ☑ done
- ☑ Map
gotd/tdbuilding blocks (building-blocks.md) - ☑ Inventory existing
botapireusable translation logic - ☑ Target architecture (
architecture.md) - ☑ This roadmap
- ☐ Confirm scope decisions with maintainer (see "Decisions needed")
Strip codegen, keep the engine, stand up an empty-but-compiling library. Done.
- ☑ Remove
internal/oas,botdocOAS emission (oas.go),cmd/gotd-bot-oas,_oas/, ogen tooling (tools.go/generate.go) - ☑ Keep
botdocfetch/extract only; moved underinternal/botdoc - ☑ Preserved
internal/botapi+internal/pool+cmd/botapias a non-compiled_seed/reference for re-pointing;internal/botstorage→storage(public). The seed was removed in Phase 7 once everything had been re-pointed. - ☑ New root
Bottype with construction,Run(ctx),Raw() *tg.Client, wiring the verified update chain (peers hook → gaps → dispatcher) - ☑ Repo builds with the translation layer detached —
go mod tidy,go build ./...,go vet ./...andgo test ./...all green - ☑ Bump
gotd/tdto latest (v0.117.0 → v0.156.0);tidydroppedgo-chi/chi, movedogento indirect, addedgotd/log+gotd/log/logzap - ◐ Update
Makefile(done: droppedgenerate, addedlint); rewriteREADME.md(☑ rewritten for the MTProto-backed library)
The hand-written Bot API surface. This is the bulk of the work. Done on
main (commits on top of Phase 1): see enums.go, errors.go, markup.go,
unions.go, types_*.go, message_origin.go, chat_member.go,
input_media.go, update.go.
- ☑ Primitive types:
User,Chat,Message,MessageEntity,PhotoSize,Document,Update,ResponseParameters(+ media/query supporting types) - ☑ Typed enums:
ParseMode,ChatType,ChatAction,MessageEntityType, … + the union discriminators - ☑ Sealed-interface unions:
ChatID,InputFile,ReplyMarkup,MessageOrigin,ChatMember,ReactionType,MenuButton,InputMedia - ◐ Constructors / fluent setters (
ID,Username, keyboard builders) — done. More fluent setters land alongside the methods in Phase 3. - ☑ Typed error hierarchy (
Error,AsFloodWait,ErrNotImplemented) - ☑ Exhaustiveness lint config for unions (
gochecksumtype+exhaustive; golangci config migrated to v2)
Hand-written over the gotd sender on our types. Done on main: methods on
*Bot with shared functional SendOptions; pure translation
(markup_to_tg.go, entities.go, errors_map.go, convert.go) is unit
tested. Live-Telegram paths are compile-/lint-verified.
- ☑
SendMessage(text + HTML/MarkdownV2/Markdown) —send.go - ☑ Media sends:
SendPhoto,SendDocument,SendVideo,SendAudio,SendVoice,SendAnimation,SendVideoNote,SendSticker(file_id viafileid, URL, or upload via the uploader);SendMediaGroup(uploaded albums) - ☑
SendContact,SendDice,SendVenue,SendLocation,SendPoll - ☑
SendChatAction - ☑ Keyboards both directions (
markup_to_tg.goout,convert.goin) - ◐ Edits:
EditMessageText/Caption/ReplyMarkupdone;EditMessageMediaTODO - ☑
ForwardMessage,CopyMessage,DeleteMessage(s) - ◐ Peer/chat-id resolution (
resolve.go: TDLib id + @username); access-hash miss hardening continues as real traffic exposes cases
Deferred within Phase 3: explicit-entity sends (parse modes cover formatting);
SendMediaGroup with file_id/URL items (only uploads compose through the
high-level album API); EditMessageMedia.
Done on main. The framework lives in the root package (on *Bot),
consistent with the Phase 3 methods. installHandlers (called from New) binds
the raw tg.UpdateDispatcher to a concurrency-safe router. See
examples/echo for the end-to-end pipe.
- ☑
tg.Update*→botapi.Updatemapping: new/edited messages, channel posts (broadcast → channel_post, supergroup → message), callback & inline queries (updates_map.go,on.go); senders resolved from harvestedEntities - ☑
convert.gore-point + reply-to + forward-origin resolution (user/hidden/chat/channel) - ☑ Routing:
Group,Use/middleware (global + group-scoped), predicates, first-match dispatch,Context(handler.go,group.go,context.go) - ☑ Built-in middleware:
Recover,Timeout,Logging(rate-limit → Phase 6) - ☑ Built-in predicates:
Command,HasPrefix,HasText,TextEquals,Regex,ChatTypeIs,CallbackData/CallbackPrefix,Not/Or(media predicate → Phase 5 alongside incoming media) - ☑
Bot.On*convenience:OnMessage,OnEditedMessage,OnChannelPost,OnCallbackQuery,OnInlineQuery,OnCommand - ☑ Decision: no
getUpdates/webhook shim. Updates arrive on the persistent MTProto connection; the native handler framework +Bot.Runis the only model. Decided against an HTTP-poll/webhook compatibility surface (resolves Decisions-needed #2).
Deferred within Phase 4: chat_member/my_chat_member, poll/poll_answer
and chosen_inline_result update routing (types exist; dispatcher wiring lands
with the chat-management and query work in Phase 5).
Done on main — the conformance test's deferred list is now empty: every
method published in the doc snapshot is implemented (or, for
getUpdates/webhooks/logOut/close, categorized as not-applicable to the
MTProto model). The methods are on *Bot with the same functional-option style
as Phase 3. See file.go, answer.go, commands.go, chat_member_methods.go,
chat_admin.go, chat_info.go, chat_photo.go, invite_links.go,
live_location.go, edit_media.go, poll.go, inline_query_result.go,
input_message_content.go, sticker.go, sticker_set.go, games.go,
payments.go, send_invoice.go, passport.go.
- ☑
GetFile+ download (DownloadFile/DownloadFileToPath);file_unique_idresolved — derived locally from the decodedfile_idwith the TDLib scheme (web/document exact; legacy photos via volume/local id, newer photo sources fall back to media id). Resolves Decisions-needed #1. No HTTP file server in the MTProto-native model, soGetFileis decode-only. - ☑
AnswerCallbackQuery(+Context.AnswerCallback) andAnswerInlineQuery(+Context.AnswerInline) — theInlineQueryResultunion (article; photo/gif/mpeg4 gif by URL; cached photo/gif/sticker/document/video/voice/ audio by file_id; contact/location/venue) and theInputMessageContentunion (text/location/venue/contact). - ☑ Chat members:
Ban/Unban/Restrict/PromoteChatMember,GetChatMember,GetChatAdministrators,GetChatMemberCount(supergroups/channels viachannels.*);ChatPermissions/ChatAdminRightswith MTProto rights mapping; participant →ChatMemberconverter. - ☑ Chat admin: pin/unpin (
PinChatMessage/UnpinChatMessage/UnpinAllChatMessages),SetChatTitle/SetChatDescription,SetChatPermissions,LeaveChat,SetChatPhoto/DeleteChatPhoto,SetChatStickerSet/DeleteChatStickerSet,SetChatAdministratorCustomTitle; invite links (Export/Create/Edit/RevokeChatInviteLink). - ☑ Chat info:
GetChat,GetUserProfilePhotos. - ☑ Commands:
Set/Get/DeleteMyCommandswith theBotCommandScopeunion. - ☑ Edits:
EditMessageMedia; live location (EditMessageLiveLocation/StopMessageLiveLocation);StopPoll(+ incoming-poll mapping). - ☑ Stickers:
UploadStickerFile,CreateNewStickerSet,AddStickerToSet,DeleteStickerFromSet,SetStickerPositionInSet,GetStickerSet,SetStickerSetThumb(InputSticker/StickerFormat/StickerSet). - ☑ Games:
SendGame,SetGameScore,GetGameHighScores(Game/GameHighScore). - ☑ Payments:
SendInvoice(InvoiceParams),AnswerShippingQuery,AnswerPreCheckoutQuery, incomingShippingQuery/PreCheckoutQuery(On{Shipping,PreCheckout}Query),SetPassportDataErrors(PassportElementErrorunion).
Done on main. See errors_map.go, errors.go, ratelimit.go.
- ☑
tgerr→ Bot API mapping completed: a ~50-entry table of verbatim official descriptions (errors_map.go, mirroring telegram-bot-apiClient.cpp), plus the server's code-normalization (sub-400/404 → 400, SCREAMING_CASE 403 → 400) and prefix/casing fallback for unmapped errors. HelpersAsFloodWait,AsChatMigrated,Code. - ☑ Flood-wait retry + proactive limiter: opt-in
Options.FloodWait(+MaxFloodWaitRetries) andOptions.RequestsPerSecond(+RequestBurst), wired as client invoker middlewares viagotd/contrib(floodwait/ratelimit). Off by default. - ☑ Context-cancellation semantics:
asAPIErrorpassescontext.Canceled/DeadlineExceededthrough unchanged (even when RPC-wrapped) so callers canerrors.Isonctx.Err(). - ◐ Reconnect handled transparently by the gotd client + gaps manager; group →
supergroup migration is surfaced via
AsChatMigrated. A connection-state callback is deferred to Phase 7 polish.
Done on main (the cmd/botapi HTTP server was dropped — the library is
the product).
- ☑
pool.Poolre-pointed at publicBot(pool/): lazy per-token start, shared startup for concurrent callers, per-token bbolt storage, idle GC (RunGC),Kill/Close. - ☑ Examples:
examples/echo(handler + middleware),examples/buttons(inline keyboards + callback queries),examples/inline(inline mode),examples/media(send by URL, echo incoming media by file_id,GetFile),examples/rich(rich messages — every page-block/text constructor),examples/advanced(most of the surface in one bot). - ☑ Allocation tests on hot paths (
bench_test.go): entity/markup/user conversion andfile_unique_id, with-benchmem. - ☑ Conformance test against the
botdocextractor (conformance_test.go): every published method must be implemented on*Bot, covered by other means, or categorized as deferred/not-applicable — failing on uncategorized (drift) methods or stale allowlist entries. - ☑ Docs: package docs (
doc.go), README, and a full usage guide (docs/guide.md).
- ☑ CI (lint,
-racetests, conformance runs ingo test), codecov coverage — workflows modernized (actions/checkout@v6,setup-go@v5,golangci-lint-action@v9for the v2 config). - ☑ Conventional commits enforced (
commitlintworkflow);CHANGELOG.mdadded; first semantic versionv0.1.0tagged. - ◐ Usage guide (
docs/guide.md) done; a public announce is the maintainer's call.
- Phases 2–4 are the critical path; 3 unblocks the most user value.
- Re-pointing reused code only needs the Phase-2 types to exist, so build a
vertical slice first:
User/Chat/Message/ChatID→SendMessage→ echo update flow. Prove the whole pipe before going wide.
— Resolved (Phase 5): derived locally from the decodedfile_unique_idfile_idwith the TDLib scheme (web/document exact; photos best effort, stable per file).HTTP Bot-API compatibility— Resolved (Phase 4): MTProto-native handlers only, nogetUpdates/webhook shim.Unions— Resolved (Phase 2): sealed interfaces, guarded by thegochecksumtypeexhaustiveness linter.Module surface— Resolved (Phases 2–4): single root package;poolre-points in Phase 7.appID/appHash— bundled default vs. caller-provided (currently caller-provided and required).