一键导入
x-tdlib
Применяй когда работаешь с TDLib-адаптером: `clientAdapter`, `telegramRepo` (consumer-side), тестовая стратегия, авторизация и lifecycle
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Применяй когда работаешь с TDLib-адаптером: `clientAdapter`, `telegramRepo` (consumer-side), тестовая стратегия, авторизация и lifecycle
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Применяй при написании или правке API BDD через godog: общий каталог `features/`, теги `@api` в monorepo, layout API runner-а, TestMain, State, step definitions и testcontainers
Применяй при написании или правке browser BDD: `features/` Gherkin-сценарии, `@browser` в monorepo, playwright-bdd поверх Playwright Test, TypeScript steps, fixtures и page objects
Применяй при реализации функционала по готовому .feature: скелет шагов (red) → реализация до green → рефакторинг (blue)
Применяй при ревью или реализации BDD steps на godog или playwright-bdd, когда нужно выявить FAKE GREEN: stateful-заглушки, no-op шаги, self-fulfilling Then, локальную подстановку ошибок, When без observable effect и сценарии, которые проходят без проверки реальной реализации. Используй каждый раз при аудите BDD на честность, при переводе undefined/pending в green и в bdd-reviewer.
Применяй для конвертации фрагментарных legacy-знаний (код, doc.go, docs/, git history, интервью) в структурированные .feature файлы
Применяй на этапе планирования фичи: PRD → бизнес-интервью → драфт .feature → RFP разработке → разбор вопросов → готовые сценарии
| name | x-tdlib |
| description | Применяй когда работаешь с TDLib-адаптером: `clientAdapter`, `telegramRepo` (consumer-side), тестовая стратегия, авторизация и lifecycle |
Этот skill — канонический владелец контракта TDLib-адаптера и тестовой стратегии взаимодействия с Telegram.
internal/repo/telegram/telegramRepoНе применяй для:
x-testing-conventions)x-unit-test-partial-interface)internal/repo/telegram/| Файл | Назначение |
|---|---|
doc.go | Контракт пакета, env-переменные |
client_adapter.go | Интерфейс clientAdapter + тонкие обёртки *Repo с raw-TDLib сигнатурами |
repo.go | Repo struct, lifecycle (New, Start, Close), авторизационный flow, каналы обновлений, композитные операции без аналога в go-tdlib |
Разделение простое и строгое:
client_adapter.go как обёртка 1-в-1repo.go (lifecycle, auth submit, SendMessageAndWait и подобные композиты)clientAdapter — внутренний интерфейс-обёртка над *client.ClientclientAdapter — это внутренний интерфейс внутри самого repo/telegram/-пакета, скрывающий конкретный SDK go-tdlib от остальной части Repo.
Не путать с:
x-new-adapter — там речь о пакете целиком (doc.go, Config, lifecycle, observability) как единице ownership внешней системы. clientAdapter — это одна деталь внутри такого пакета, не самостоятельная единица.x-unit-test-partial-interface — тот паттерн про consumer-side контракт между модулями проекта. clientAdapter живёт внутри одного модуля и абстрагирует внешний SDK, а не другой внутренний модуль.Общее между этими техниками только слово «adapter»/«интерфейс» и правило «только реально используемые методы». Ownership и контекст — разные.
Содержимое clientAdapter:
Repo, а не полная поверхность *client.Client*client.Client выставляет сотни методов, большая их часть проекту не нужнаclientAdapter только когда в repo.go появился живой потребитель этого метода; убирается — когда потребитель исчезСигнатуры 1-в-1 с go-tdlib, без контекста и без domain-типов:
type clientAdapter interface {
SendMessage(*client.SendMessageRequest) (*client.Message, error)
ForwardMessages(*client.ForwardMessagesRequest) (*client.Messages, error)
GetMessage(*client.GetMessageRequest) (*client.Message, error)
// ...только реально используемые методы
GetListener() *client.Listener
}
Обёртки живут на *Repo — тонкая пересылка в r.tdClient.X(req):
func (r *Repo) SendMessage(req *client.SendMessageRequest) (*client.Message, error) {
return r.tdClient.SendMessage(req)
}
Статическая проверка:
var _ clientAdapter = (*Repo)(nil)
Роль clientAdapter:
Repo выставляет наружу именно TDLib-поверхность, а не параллельную domain-обёрткуRepo (редкий случай — обычно мокают уровнем выше, через consumer-side telegramRepo)Статические пакетные функции go-tdlib (client.ParseTextEntities, client.GetMarkdownText, client.GetOption) не являются методами *client.Client и в clientAdapter не входят. Они вызываются из repo.go напрямую.
telegramRepoПотребители (handler, facade, transform, auth, term) объявляют частично применяемый интерфейс telegramRepo только с нужными им методами. В интерфейсе равноправно сосуществуют два класса методов, потому что оба они принадлежат поверхности *Repo:
clientAdapter — тонкие делегаты к go-tdlib (SendMessage, ForwardMessages, GetMessage, …). Сигнатуры — raw-TDLib типы.Repo — то, чего нет в go-tdlib:
SendMessageAndWait — SendMessage + ожидание UpdateMessageSendSucceeded)AuthStates() <-chan domain.AuthStateEvent, Updates() <-chan client.Type, ClientDone() <-chan struct{})SubmitPhone, SubmitCode, SubmitPassword)LogOut, CleanUp)Пример:
// в handler/handler.go
type telegramRepo interface {
// обёртки clientAdapter
SendMessage(req *client.SendMessageRequest) (*client.Message, error)
ForwardMessages(req *client.ForwardMessagesRequest) (*client.Messages, error)
// собственный метод Repo (композит)
SendMessageAndWait(ctx context.Context, req *client.SendMessageRequest) (*client.Message, error)
// ...только методы, реально используемые handler-ом
}
Потребителю не важно, какой класс у метода — он просто тащит то, что ему нужно. Разделение существует только внутри пакета repo/telegram/ (см. «Структура пакета»): что есть в go-tdlib → client_adapter.go, чего нет → repo.go.
Правила про импорты и типы в сигнатуре частично применяемого интерфейса — в x-unit-test-partial-interface. Кратко:
*client.Message, *client.SendMessageRequest, *client.FormattedText и прочие TDLib-типы — это и есть рабочий контрактinternal/domain/Message / internal/domain/InputMessageContent / internal/domain/FormattedText как «тонкий фасад» над TDLib — запрещёнinternal/domain/ для Telegram оставляет только то, чего в go-tdlib нет: AuthStateEvent/AuthState*/WaitPasswordState (внутренний контракт с service.auth) и утилиты вроде MaskPhoneNumberСоглашение по именованию:
telegramRepo (не telegramGateway, не telegramClient)telegramRepo telegramRepo (имя = тип)Операции, которых нет в go-tdlib (требуют комбинации нескольких TDLib-вызовов или ожидания асинхронного update), живут в repo.go, а не в client_adapter.go.
Пример — SendMessageAndWait: SendMessage возвращает temporary ID, а permanent ID приходит асинхронно через UpdateMessageSendSucceeded. Метод принимает и возвращает TDLib-типы, но внутри делает композит (отправка + listener + таймаут):
func (r *Repo) SendMessageAndWait(
ctx context.Context,
req *client.SendMessageRequest,
) (*client.Message, error) { ... }
ctx здесь уместен: он реально используется (<-ctx.Done() при ожидании update). В тонких обёртках client_adapter.go контекста нет, потому что TDLib синхронный и ctx не использует.
Через mockery-моки consumer-side интерфейса telegramRepo:
tg := mocks.NewTelegramRepo(t)
tg.EXPECT().
SendMessage(mock.MatchedBy(func(r *client.SendMessageRequest) bool {
return r.ChatId == chatID
})).
Return(&client.Message{Id: 42}, nil)
Unit-тесты не зависят от TDLib. Они проверяют логику компонентов-потребителей, работая напрямую с TDLib-типами.
Работают через живой TDLib с тестовым DC. BDD проверяет бизнес-сценарии на реальном Telegram — форвардинг, фильтрация, трансформация и т.д.
Нюансы поведения TDLib (асинхронность, различия типов чатов, rate limits) проявляются естественно в BDD-тестах и при ручном тестировании. Отдельный слой adapter-тестов для TDLib не нужен — это тестирование чужого продукта.
Fake-реализации TDLib (эмуляция *client.Client без живой либы) — это scaffolding раннего этапа. Когда реальный TDLib подключён, Fake удаляется. Он не поддерживается как долгосрочный test double: эмуляция нюансов TDLib создаёт ложное чувство безопасности и требует бесконечной синхронизации с реальным поведением.
Вместе с Fake удаляется всё, что он провоцировал создать — в первую очередь параллельные domain.* типы, дублирующие *client.* (см. x-unit-test-partial-interface).
Авторизационный flow живёт в repo.go, потому что управляет состоянием канала clientDone у Repo. Flow реализован как state machine: WaitPhone → WaitCode → WaitPassword → Ready.
Контракт между TDLib-адаптером и UI авторизации (терминал, бот, веб) выражается через domain.AuthStateEvent и domain.AuthState* — это тот случай, когда internal/domain/ оправдан: в go-tdlib нет такого объединённого события (у него внутренний AuthorizationState с 12+ вариантами, из которых UI релевантны только 5).
SubmitPhone/SubmitCode/SubmitPassword в repo.go передают ввод в authorizer go-tdlib через каналы.
LogOut и CleanUp — тоже в repo.go: LogOut комбинирует client.LogOut() + сброс внутреннего состояния Repo; CleanUp удаляет локальные файлы TDLib (нет аналога в go-tdlib).
client_adapter.gorepo.goclientAdapter содержит только реально используемые методы go-tdlib, а не полную поверхность *client.Clientclient_adapter.go — только пересылка, никакого mappingtelegramRepo использует raw-TDLib типы, domain-дубли запрещеныtelegramRepo telegramRepo)x-unit-test-partial-interfacex-testing-conventionsx-mockeryx-doc-gox-env-config