Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/telegram-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Telegram Live E2E

on:
workflow_dispatch:
inputs:
delete_webhook:
description: Delete an existing webhook before polling
required: true
type: boolean
default: false
drop_pending_updates:
description: Drop queued updates when deleting the webhook
required: true
type: boolean
default: false

concurrency:
group: telegram-live-e2e
cancel-in-progress: false

permissions:
contents: read

jobs:
telegram-e2e:
name: Telegram live long-polling E2E
runs-on: ubuntu-latest
environment: telegram-e2e
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Validate live E2E secrets
shell: bash
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_SENDER_BOT_TOKEN: ${{ secrets.TELEGRAM_SENDER_BOT_TOKEN }}
run: |
test -n "$TELEGRAM_BOT_TOKEN" || { echo "::error::telegram-e2e Environment is missing TELEGRAM_BOT_TOKEN"; exit 1; }
test -n "$TELEGRAM_SENDER_BOT_TOKEN" || { echo "::error::telegram-e2e Environment is missing TELEGRAM_SENDER_BOT_TOKEN"; exit 1; }

- name: Run live Telegram E2E
run: go run ./examples/telegram-e2e
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_SENDER_BOT_TOKEN: ${{ secrets.TELEGRAM_SENDER_BOT_TOKEN }}
TELEGRAM_DELETE_WEBHOOK: ${{ inputs.delete_webhook }}
TELEGRAM_DROP_PENDING_UPDATES: ${{ inputs.drop_pending_updates }}
TELEGRAM_TEST_MESSAGE: telegram-e2e-${{ github.run_id }}-${{ github.run_attempt }}
TELEGRAM_TIMEOUT: 90s
TELEGRAM_POLL_TIMEOUT: 5s
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@
- [ ] 实现 Runner Event 到文本、流式消息和卡片消息的转换
- [ ] 接入企业微信或微信相关通道
- [x] 接入 Telegram long polling 文本通道(Issue #31;单 Binding、Gateway Dispatch、进程内幂等)
- [x] 增加真实 Telegram live E2E 示例与手动 CI workflow(Issue #33;根目录 `examples/telegram-e2e`)
- [ ] 接入 Telegram webhook、媒体/rich update 或其他 IM 通道
- [ ] 实现 webhook 验签、账号与租户绑定、用户身份映射
- [ ] 使用 `tenant + channel + message_id` 实现幂等去重和缓存回复
Expand Down Expand Up @@ -232,6 +233,9 @@
- Issue #31 的 `trpcservice/channels/telegram` 提供单 Binding、`getMe` 身份校验、普通文本
long polling、Gateway Dispatch、进程内幂等和脱敏分段回复;具体边界以
`docs/docs/telegram.md` 为准。
- Issue #33 的 `examples/telegram-e2e` 使用真实 Telegram Bot API 和确定性 Dispatcher 验证
`getMe -> getUpdates -> sendMessage`;live workflow 只手动触发并使用受保护 Environment,
不替代完整模型供应商或生产控制面 E2E。
- Issue #26 的 fake candidate resolver/verifier 与 proof-bearing routing 边界有独立测试,
但这不代表 WeCom/Telegram webhook、媒体能力或持久化消息能力已满足 README 原验收要求。

Expand All @@ -253,6 +257,8 @@
| |-- start.sh # 启动服务
| `-- stop.sh # 停止服务
|-- data # 服务运行时数据
|-- examples # 可运行的外部集成示例
| `-- telegram-e2e # Telegram live long-polling E2E
|-- docs # 各模块说明与架构设计文档
|-- cmd
| `-- trpc-service # 命令行入口,可直接启动服务
Expand Down
5 changes: 5 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Codecov status checks (shown on PRs).
# The hard CI gate lives in scripts/coverage.sh --min (see .github/workflows/ci.yml);
# this file makes the same 85% target visible in Codecov reports and PR comments.
# The live example entrypoint is exercised only by the opt-in Telegram workflow;
# its credential-free unit tests cover the pure helpers and boundary setup.
ignore:
- "examples/telegram-e2e/main.go"

coverage:
status:
project:
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
限流、幂等和服务生命周期契约。
- [Telegram 长轮询 Adapter](telegram.md):Issue #31 的文档先行契约,固定单 Binding、Bot
身份校验、普通文本映射、Dispatch 聚合回复和生命周期边界。
- [Telegram live E2E 示例](https://github.com/XnLemon/trpc-agent-service/tree/main/examples/telegram-e2e):
Issue #33 的真实 Bot API 传输冒烟测试和手动 CI 运行说明。

## 快速开始

Expand Down
17 changes: 17 additions & 0 deletions docs/docs/telegram.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,20 @@ README 和 MkDocs 状态应明确区分已交付与后续能力:
参考:[Telegram Bot API](https://core.telegram.org/bots/api)、
[getUpdates](https://core.telegram.org/bots/api#getting-updates)、
[github.com/go-telegram/bot](https://github.com/go-telegram/bot)。

## 7. 真实 Telegram E2E

Issue #33 提供根目录 `examples/telegram-e2e/` 示例和手动触发的 CI 工作流,
用于验证真实的 `getMe -> getUpdates -> sendMessage` 边界。示例内部使用确定性
`DispatchService`,因此不会把模型供应商凭据和 Telegram 传输冒烟测试混在一起。

本地运行只需要在进程环境中提供 `TELEGRAM_BOT_TOKEN`;Token 不得进入仓库、日志、
trace 或错误。CI 使用受保护的 `telegram-e2e` Environment,至少配置接收 Bot 的
`TELEGRAM_BOT_TOKEN`,并在需要完全自动化入站消息时配置第二个受控测试 Bot 的
`TELEGRAM_SENDER_BOT_TOKEN`。一个 Bot Token 不能模拟普通用户向自己发送入站消息,
所以当前 workflow 必须显式配置第二个受控测试 Bot;本地人工运行可以不配置发送者。

示例和 CI 都只验证普通文本;命令、媒体、rich update、Webhook、持久化 outbox 和
生产模型供应商仍不属于该 E2E 范围。详见
[Telegram live E2E example](https://github.com/XnLemon/trpc-agent-service/tree/main/examples/telegram-e2e)
和 Issue #33。
73 changes: 73 additions & 0 deletions examples/telegram-e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Telegram live E2E example

This example exercises the real Telegram Bot API boundary around the
tenant-scoped long-polling adapter:

```text
getMe -> getUpdates -> trusted Telegram Adapter -> DispatchService -> sendMessage
```

It deliberately uses a deterministic `DispatchService`, so this example tests
Telegram transport, trusted target construction, update normalization, reply
delivery, cancellation, and secret handling without requiring a production
LLM provider.

## Local run

Create a dedicated test Bot with `@BotFather`, revoke any token that has been
shared outside a secret store, and set the replacement token in the process
environment. Do not place it in this repository or print it.

PowerShell:

```powershell
$env:TELEGRAM_BOT_TOKEN = '<receiver bot token>'
go run ./examples/telegram-e2e
```

The command prints a unique ordinary-text marker. Open the receiver Bot in
Telegram, send that marker, and confirm the `telegram-e2e-ok:<run-correlation>` reply. Commands,
media, and rich updates are intentionally outside this first E2E. Press
`Ctrl+C` to stop the local polling process cleanly.

If PowerShell can reach `api.telegram.org` but this command reports
`telegram E2E getMe network failure`, Go is using a different HTTPS path. Go
uses the standard `HTTPS_PROXY`/`HTTP_PROXY` environment variables; it does not
automatically import every Windows system-proxy setting. Configure the proxy
for the same PowerShell process, without printing credentials, and rerun the
command.

If the Bot has a webhook, either remove it before starting long polling or set
`TELEGRAM_DELETE_WEBHOOK=true`. Pending updates are preserved by default; set
`TELEGRAM_DROP_PENDING_UPDATES=true` only when discarding them is intentional.

Optional local settings:

| Variable | Meaning | Default |
| --- | --- | --- |
| `TELEGRAM_TEST_MESSAGE` | Exact marker to wait for | generated per run |
| `TELEGRAM_TIMEOUT` | Maximum run duration | `2m` |
| `TELEGRAM_POLL_TIMEOUT` | Telegram long-poll timeout | `5s` |
| `TELEGRAM_DELETE_WEBHOOK` | Delete an existing webhook | `false` |
| `TELEGRAM_DROP_PENDING_UPDATES` | Drop queued updates when deleting webhook | `false` |

## CI run

The live workflow is intentionally manual and references a protected GitHub
Environment named `telegram-e2e`:

- `TELEGRAM_BOT_TOKEN`: required secret for the receiving test Bot.
- `TELEGRAM_SENDER_BOT_TOKEN`: required secret for a second controlled test Bot
in CI; it sends the unique marker and receives the expected reply. This
sender secret is optional only for local human-driven runs.

For a fully automatic message round trip, enable Telegram Bot-to-Bot
Communication Mode for both dedicated test Bots. A single Bot API token cannot
act as a normal user sending an inbound message to itself. Without the sender
secret, the example remains suitable for a local human-driven run but the CI
job will eventually time out waiting for the marker.

The workflow uses one concurrency group so two runs cannot poll the same test
Bot at the same time. It must not be changed to run automatically on arbitrary
pull requests: live Bot credentials and external Telegram side effects are
intentionally outside the offline PR checks.
Loading
Loading