一个免费、开源的 Dujiao-Next Telegram 商店机器人。项目使用 Go 开发,通过 Dujiao-Next Channel API 读取商店配置、商品、订单、钱包和支付信息。
这是社区项目,不是 Dujiao-Next 官方收费机器人的替代授权,也不隶属于 Telegram。
左侧为本项目 Telegram Bot 的商品列表、钱包和首页菜单,右侧为对接的 Dujiao-Next 管理后台。
图片为开发版测试环境的界面合成示意,商品、余额及统计信息均为演示数据;右侧后台属于 Dujiao-Next,并非本项目另行提供的管理系统。
- Telegram 用户自动解析和创建,支持
/start <邀请码> - 简体中文、繁体中文、英文切换
- 首页、商品、钱包、订单与支付页使用紧凑分组按钮,主操作突出、短操作两列;数量保留减/输入/加与批量快捷值,固定数量隐藏无效调整,购买确认可直接修改数量/优惠码/资料;长名称及取消/重发确认保留整行
- 推广、提现与售后延续紧凑布局:推广链接一键复制,佣金/提现记录直接切换或申请;提现渠道两列、确认独立,售后刷新与返回并排,结果不明的提现不会提供快捷重提
- 帮助中心可直达订单、钱包和公告;三种语言同排并标记当前选择;公告订阅状态本地化、管理页原位更新,历史公告退订不覆盖原文,订阅可选且不影响交易通知
- 商品分类、分页列表、商品详情、SKU 与购买数量选择;单规格直接选数量,支持快捷选择及输入 2500/5000 等大数量
- 起购、限购和可见库存数量校验
- 订单预览与创建,人工交付商品可在私聊中逐项填写购买资料
- 自定义表单支持文本、多行文本、邮箱、电话、数字、单选和多选,校验必填、格式、长度与范围
- 填写中支持返回上一项、跳过选填项、取消,预览后可修改资料
- 订单预览支持输入、更换和移除优惠码,展示优惠明细,正式下单携带相同优惠码
- 余额或在线支付渠道选择、支付链接与二维码展示
- 待支付订单自动轮询,成功后清理二维码并展示交付结果
- 待支付订单继续使用原支付记录,显示剩余时限和超时提醒;取消前复核订单,支持返回、一次性确认和 Redis 跨重启重复提交保护
- 订单号直达查找、状态筛选与保留页码返回;详情分开显示付款/交付进度,支持安全取消与完整 TXT 文件交付
- 订单售后入口、携订单号直达 Telegram 客服、支付/交付/退款咨询话术与消息复制,不自动发送或执行退款
- 钱包余额、钱包充值与分页交易流水
- 充值预设金额、自定义金额、渠道费率/固定费用参考,以及到账金额、渠道手续费和实付金额分开展示
- 礼品卡兑换,展示到账金额、最新余额和兑换流水,支持无效、过期、停用与已兑换提示
- 推广中心、开通推广、专属 Bot 链接分享、访问及转化数据、佣金和提现记录分页查询
- 私聊提现申请,校验金额、最低门槛和可用佣金,填写渠道与收款信息,二次确认、提交前复核和防重复提交
- 可选 Redis 会话持久化、加密草稿和跨重启提现保护;Redis 故障时停止提交,不回退内存
- 管理员私聊关键事件摘要与提现异常提醒,白名单授权、有界异步队列、去重和有限重试
- 资金操作的结构化审计,Docker 持久卷保存轮转 JSONL,不记录兑换码、令牌、表单和收款全文
- 用户交互、资金写操作、提现及签名回调的分层限流,支持 Redis 原子计数与跨重启保留额度
- 轻量纯文本公告群发:主动订阅、管理员预览及二次确认、退订、逐条限速和重启后人工恢复
- 推广链接进入时记录访问,订单预览和创建携带一致访客标识,由 Dujiao-Next 保存和校验归因
- 订单支付、交付、钱包充值成功的签名回调通知,支持直接查看订单、下载 TXT 与查看余额
- Dujiao-Next 远端机器人配置定时刷新与心跳
- 长轮询运行,不要求 Telegram Webhook 或公网 HTTPS
- Docker Compose 部署和
/healthz健康检查
暂不支持分销站管理、媒体/定时群发或向任意用户导入群发。管理员通知不包含审核、解锁他人保护或直接打款指令;提现审核和实际打款由商店处理。
后续迭代顺序和完成标准见 ROADMAP.md。
当前版本:v0.4.0。变更记录见 CHANGELOG.md,下载校验、新部署、升级/回滚和故障处理见 RELEASE.md,测试范围见 整体验收记录。
main 正在开发 1.0.0-dev。兼容性矩阵与升级测试 已加入固定源码证据、只读预检、Bot Redis 跨版本测试,以及三个官方 Dujiao-Next 镜像的真实联调和 SQLite/PostgreSQL 逐版本迁移、备份恢复测试。可运行 make real-upstream-test 复验;全部使用独立合成数据,不代表 v1.0 已正式发布或商家真实支付渠道已验收。
v1.0 的整体验收矩阵覆盖购买、支付、充值、交付、查单及售后,以及同一买家的跨功能旅程。交互巡检约定记录按钮、输入纠错和返回逻辑。干净已提交源码可运行 make release-candidate 生成四平台候选包并自动校验来源、摘要、架构、许可证和公开文档;发布准备与人工门槛明确剩余真人客户端/支付沙箱事项,不自动发布正式版。
开发版另补齐支付/充值回调故障与 Telegram 全链路验收:通知回执去重、Redis 重启恢复、Telegram 限流/断连处理;make telegram-e2e 在两种数据库上运行真实 Bot + 官方 v1.5.0 + 隔离 Telegram/支付模拟端点,不需要真实 Token。响应丢失时仍可能重复通知,不承诺消息恰好一次。
make telegram-e2e 还覆盖自动卡密分支;make soak-test 默认在两种数据库各运行五分钟持续负载,检查通知恢复、延迟、限流及资源预算。模型、门槛和边界见持续压力与自动卡密验收,不代表商店支付 TPS 或真实 Telegram 发送能力。
开发版已提供离线安装向导:go run ./cmd/bot setup init 创建新的私有配置,go run ./cmd/bot setup check --env-file .env --profile compose-redis 脱敏检查;已有配置不会被覆盖。升级比较、备份/回滚、故障排查及尚未执行的商家沙箱清单见 v1.0 开发版运维指南。检查不联网、不迁移数据、不自动启动 Bot。
- Dujiao-Next
v1.5.0或兼容 Channel API 的版本 - Go 1.25+(本地构建)或 Docker Compose
- 一个通过 @BotFather 创建的 Telegram Bot
项目使用仍在维护的 github.com/go-telegram/bot,没有使用多年未发布新版本的旧 go-telegram-bot-api/v5。
当前推荐 v1.5.0 作为联调基线;v1.4.8 / v1.4.9 的渠道邀请码注册有限制。具体证据级别、版本边界与升级前后只读诊断见 兼容性矩阵,不要将 Mock 或源码复核当作真实商店验收。
-
在 Dujiao-Next 后台配置 Telegram Bot 服务,取得 Channel Key 和 Channel Secret,并填入 Bot Token。
-
复制环境变量模板:
cp .env.example .env
-
编辑
.env:DUJIAO_BASE_URL=https://shop.example.com DUJIAO_CHANNEL_KEY=your-channel-key DUJIAO_CHANNEL_SECRET=your-channel-secret # 可不填;为空时从 Dujiao-Next 的 Telegram 服务配置读取 TELEGRAM_BOT_TOKEN=
-
如果需要支付/交付主动通知,将 Dujiao-Next Telegram 服务的回调地址设置为机器人可访问的地址,例如:
https://bot.example.com/internal/order-paidDujiao-Next 会自动将路径替换成实际事件路径。机器人同时接收:
/internal/order-paid/internal/order-fulfilled/internal/wallet-recharge-succeeded
回调必须能从 Dujiao-Next 服务器访问。如果两者在同一个 Docker 网络,可以直接使用内网服务地址;Telegram 长轮询本身不需要这个公网入口。
Docker Compose:
docker compose up -d --build
docker compose logs -f bot本地运行:
go run ./cmd/bot不设置 BOT_REDIS_URL 时保持内存模式,重启会丢失会话和未决提现保护。首次从内存切换到 Redis 前,先核对所有未决提现;运行中的旧内存会话无法自动迁移。生产部署可使用内置覆盖配置:
-
运行
openssl rand -hex 32生成一次加密密钥,将结果填入.env的BOT_SESSION_ENCRYPTION_KEY。不要使用演示密钥,不要每次启动重新生成。 -
启动带 Redis 的部署:
docker compose -f compose.yml -f compose.redis.yml up -d --build
-
后续重启、升级、停止都使用相同的 Compose 文件、项目名和持久卷;不要加
down -v。
使用已有独立 Redis 时,在 .env 设置 BOT_REDIS_URL=redis://redis-host:6379/0(支持带认证及 rediss://),同时设置加密密钥。Redis 需要支持 Lua,数据必须可靠持久化;推荐独立实例、AOF appendonly yes、appendfsync always、maxmemory-policy noeviction 和持久磁盘卷。覆盖配置已经包含这些设置且不向宿主机暴露 Redis 端口;跨主机部署请自行配置 TLS、认证和网络隔离。always 的持久化开销是资金防重的取舍,见 Redis 持久化说明。代码不会替外部 Redis 修改持久化/淘汰配置,也无法验证磁盘或托管服务的实际耐久性。
语言和不含收款信息的提现保护单独保存且不设 TTL;表单/优惠码草稿 30 分钟、充值/礼品卡提示/提现草稿 10 分钟,邀请码输入最多 24 小时。草稿用 AES-256-GCM 加密,Redis TTL 到期即移除在线草稿。Redis AOF、备份及 Telegram 聊天记录不因此物理擦除;备份与加密密钥仍需妥善保护。未决保护仅在明确成功、明确拒绝、确认尚未发出请求,或用户核对历史后主动解锁时清除。
启动时 Redis 连接失败或密钥不匹配会直接失败,运行时故障会拒绝新的会话操作;/healthz 在 Redis 不可达或检测到数据集被清空时返回 503。不会自动切回内存。并发更新冲突不会自动重放,请刷新页面后操作;若已有提现保护则先核对记录。
重启后保留“检查中/提交中”保护,不自动重发。单次提现处理上限 2 分钟,保护租约为 3 分钟;租约届满后保守转为“结果不明”,保留锁定,只有查看提现记录、联系商家确认后才能主动解除。重启不是解除保护的方法。
边界:Channel API 没有调用方提现幂等键。Redis 卷丢失、删除键、数据淘汰、快照回滚/故障切换丢写、改用其他 Redis/数据库、更改商店 URL/Channel Key 或用户主动解锁,都无法保证全局恰好一次。商店 URL 与 Channel Key 共同隔离会话,变更配置前须先核对未决申请;不要通过清空 Redis 或换密钥绕过保护。请保留并备份原有卷和密钥,数据丢失时暂停提现并核对商店。Telegram 长轮询仍应只运行一个实例;Redis 原子更新的并发保护不代表支持同 Token 多实例长轮询。
验证:
curl http://127.0.0.1:8444/healthz
go test ./...商品卡片仅显示短说明,长说明可点“商品详情”查看,返回后保留所选数量;确认下单仍为订单预览页的首要操作。
单规格商品直接展示数量卡片,支持 − / 输入数量 / + 和可用范围内的 10 / 100 / 1000 快捷选择。点击“输入数量”后在私聊发送整数即可,例如 2500,不用连续点加号;数量卡片展示预估小计,折扣和实际应付仍以商店订单预览为准。输入不会自动下单,仍需“下一步 → 确认下单”。订单预览里的“修改数量”保留已填资料和优惠码并重新预览,旧确认按钮失效。
输入会实时复查起购、限购、规格及可见库存,不把超限输入悄悄改成其他数量。支持返回按钮、/back 和 /cancel;新数量输入十分钟有效,关联购买资料保留原有效期。只有当前私聊提示可接收输入,不在日志保存输入原文。
订单详情只显示交付摘要;点“📄 下载交付 TXT”在当前私聊获取完整 UTF-8 文件,包含主/子订单交付和使用说明,不把几千条卡密塞入聊天或截断。也可直接从交付完成通知下载,不必先进入“我的订单”。单文件上限 4 MiB,超限明确提示联系商家,不发送残缺文件。领取前每次向商店重新校验当前用户的订单归属,不接受群聊领取,也不缓存或跨用户复用 Telegram file_id。
同一用户、订单的并发和重复点击只会发送一次;成功后请查看聊天中的文件。需要重发时,等待一分钟后再次点击下载,并点“确认重新发送 TXT”;旧确认按钮不能重复使用。发送超时、5xx 或进程中断不自动重试,先核对聊天,至少两分钟后才可主动确认重发;明确的 Telegram 429 按冷却提示重试。下载/重发不创建新订单、不扣款、不再次分配库存。
Redis 独立保存七天防重元数据(不含卡密、文件内容或 file_id),不受 /cancel、导航或重启影响;存储失败停止发送,不回退到内存。未配置 Redis 的内存模式不能保证进程重启防重。七天后可重新领取;用户明确确认重发也可能收到同一内容,不承诺 Telegram 网络不确定情况下的严格 exactly-once。文件可下载保存,请勿转发给他人;机器人不能阻止买家主动分享文件。实现边界与验收见 TXT 文件交付。
支付成功通知提供“查看订单”,交付完成通知将“📄 下载交付 TXT”作为首要操作,并保留“查看订单”;充值成功通知提供“查看余额”。各通知仅保留相关操作及“返回商店”,不重复铺开完整菜单,未交付的支付通知不提供下载按钮。
发送通知不会额外查询商店、上传文件或中断购买草稿。点击时才按当前 Telegram 用户重新查订单;即使通知延迟、顺序变化或 Bot 重启,也以商店最新状态为准,不能凭旧的交付通知绕过退款/取消限制。通知与订单详情共用同一 TXT 防重记录,切换入口不重复领取、下单或扣款。新版本只影响之后发送的通知,不自动改写或补发历史通知。测试与边界见 回调与全链路验收。
“我的订单”顶部提供“订单号查找”和状态筛选;也可私聊发送 /orders 完整订单号 直接查看。查找不受当前筛选限制,只能读取当前 Telegram 用户名下的订单,不提供模糊搜索、全店搜索或他人订单存在性提示。输入提示五分钟有效,启用 Redis 后重启可继续;返回菜单或 /cancel 退出。
列表显示总数、页码和交付摘要,从列表进入详情后,刷新和返回保留原筛选与页码。人工交付的“已交付”和自动交付的“已完成”按商店原始状态分别筛选;详情分开显示付款与交付,区分等待人工交付、交付中、部分交付和已交付。部分交付只下载当前已有内容,不能视作整单完成;已标记交付但没有内容时提示联系商家,不显示无效下载按钮。规则与验收见 订单导航说明。
在 Dujiao-Next 的 Telegram Bot 配置中填写 basic.support_url(推荐 https://t.me/你的客服用户名)。订单详情可带完整订单号直接打开普通 Telegram 客服聊天的预填消息,用户确认后发送;售后页支持支付、交付、退款咨询话术与复制,返回保留原筛选/页码。
客服机器人、Business 链接和网站保留原地址,先复制消息再打开;未配置或链接不可用时明确提示,不提供假入口。消息仅含订单号与问题类型,不附加卡密、表单或支付资料,不自动发消息、提交工单或退款。老消息不会随配置变更自动改写;预填与复制仍需真人客户端体验。兼容范围和隔离说明见 订单售后说明。
待支付订单的恢复、超时和取消规则见 待支付订单体验:从“我的订单”点“继续支付”会先读取原支付,不会直接再建支付单;取消不代表退款。
/start打开商店并初始化账号/products浏览商品/orders查看订单;/orders 完整订单号直接查找本人订单/wallet查看余额/recharge发起钱包充值/redeem兑换礼品卡到钱包余额/affiliate打开推广中心/language切换语言/help查看帮助/cancel取消邀请码输入等当前操作
填写购买资料时,回复当前问题输入文本,单选、多选使用消息下方按钮。可发送 /back 返回上一项、/skip 跳过选填项或 /cancel 取消;填写结束后会展示订单和资料摘要供确认。表单仅在私聊中使用,草稿 30 分钟内有效;启用 Redis 后,重启可继续未过期草稿,内存模式需重新填写。
确认订单前可点击“输入优惠码”,在私聊中回复优惠码。优惠券规则和金额以 Dujiao-Next 预览结果为准;可更换、移除优惠码,或发送 /back 保留原优惠码返回预览。无效优惠码不会清除已填的购买资料;提交前若优惠后金额变化,会展示新预览并要求再次确认。订单草稿和优惠码有效期为 30 分钟,启用 Redis 后重启可继续选择;已标记提交中的操作不会自动重发。
在私聊中打开“钱包 → 礼品卡兑换”,或发送 /redeem,回复当前提示输入兑换码。可发送 /back 返回钱包、/cancel 取消;输入提示 10 分钟内有效。兑换结果来自 Dujiao-Next 的 /api/v1/channel/wallet/gift-card/redeem 接口,成功后显示到账金额和余额,流水类型为 gift_card_redeem。网络异常或结果不明确时不会自动重试,请先查看余额和流水。兑换码不写入机器人本地会话或日志。
发送 /recharge 或在钱包中点击充值,可选择 10 / 50 / 100 / 200 / 500 的预设金额,也可输入自定义金额(大于 0、最多两位小数、上限 1,000,000;币种来自商店钱包)。点击“自定义金额”后请回复当前提示;发送 /cancel 取消。充值选择仅限私聊,10 分钟内有效,退出、重新选择或提交后旧按钮失效,同一选择不会重复创建充值单。网络异常时不会自动重试,请先核对钱包、流水和支付账户。
支付渠道页展示费率、固定费用和渠道手续费参考,采用十进制定点计算和四舍五入。Channel API 未提供“用户承担/商家承担手续费”配置,所以参考费用不是最终报价。创建充值单后,到账金额、渠道手续费与应付/实付金额分别展示;支付金额直接使用商店返回值,不重复加手续费。同币种且渠道费用由商家承担时明确注明未加收;发生换汇时保留钱包和支付币种,不混算不同币种金额。
私聊发送 /affiliate 或点击首页“推广中心”。未开通时需要点击“开通推广”;已开通后展示专属 Bot 链接、推广访问、有效订单、转化率、待确认/可用/已提现佣金及最低提现金额。支持分享链接、刷新、分页查询佣金/提现记录和申请提现;记录中的收款账号默认脱敏。审核中的已绑定佣金会显示为冻结状态,不能再次用于提现。
点击“申请提现”,回复提示输入金额,再选择商店允许的渠道并填写收款账号/地址。金额必须大于 0、最多两位小数、不低于商店门槛且不超过可用佣金;币种来自商店钱包,不使用钱包余额或待确认佣金充当可提现金额。商店未限制渠道列表时,可按商家说明填写渠道名称。收款信息为 1–200 字符的单行文字,可附商家要求的姓名、网络说明;禁止控制字符,仅做通用格式校验,不保证账号/地址有效。不要发送密码、验证码、私钥或助记词。
确认页在私聊中展示完整收款信息,只有再次点击“确认提交提现申请”才会发起请求;可修改收款信息、重新填写,或发送 /back 返回上一步、/cancel 取消。提交前再次核对门槛、可用佣金、渠道和币种,配置失效时不会提交。申请交由商店审核,不代表已打款;提交之后 /cancel 不能撤回商店中的申请。Bot 不计算未公开的提现手续费或换汇结果。
提现草稿有效期为 10 分钟;取消、导航退出、提交后清除。内存模式过期后读取时清除,重启不保留;Redis 模式加密保存、到期自动移除,重启可继续未过期填写。收款信息不写日志或回调按钮;Redis 持久文件及备份可能保留加密数据,确认页和用户输入仍可能保留在 Telegram 聊天记录中,清除在线草稿不等于擦除历史介质或聊天记录。
同一申请的重复消息/连点只允许一次写请求。提交期间的保护不会被退出菜单、/cancel 或重新打开提现覆盖。网络错误、服务端 5xx、返回数据缺失或不一致时不自动重试,而是暂时锁住新申请;须先打开提现记录并联系商家核对,仅在确认可重新申请后点击“已核对,解除重试保护”。解除保护不会撤回原申请,也不会自动重试。商店明确拒绝的业务错误可在刷新后重新填写。
启用可靠 Redis 后,未决提现保护与草稿分开保存,重启、草稿过期和退出菜单不会解除保护;多个连接同时确认同一草稿也只允许一方成功保存提交标记。Redis 写入失败或响应丢失时不发出提现请求;即便商店已成功处理、但保护清理失败,仍保留保护直到人工核对。全局幂等限制和部署要求见上文“Redis 持久化”。
链接格式为 https://t.me/<实际机器人用户名>?start=aff_<推广码>,其中 aff_ 是保留的推广前缀,不能作为普通注册邀请码前缀。普通 /start <邀请码> 行为不变。推广码不是邀请码:要求邀请注册的商店仍需另外输入有效邀请码。
访问推广链接时调用 /affiliate/click,传入固定访客标识 telegram:<Telegram 用户 ID>;普通和人工交付商品的订单预览、最终下单均发送相同的 affiliate_visitor_key。不把推广码持久化在 Bot 会话,也不在所有订单里重复提交原始推广码。归因、去重、有效期、自购、退款、佣金结算和提现审核均由商店处理;目前核对的 Dujiao-Next 实现采用最近 30 天有效访问归因、同访客同落地页 10 分钟去重。Bot 重启或取消购买不会主动清除商店保存的归因,实际期限以商店版本为准。
推广 API 未启用、商店版本不支持或账号不可用时会提示原因。记录推广访问失败会提示用户重开链接,但不会阻止打开商店。运行需要提供 /api/v1/channel/affiliate/* 接口的商店版本;旧版本的已有购物功能不依赖这些推广接口。
如使用商店自定义首页菜单,可使用 affiliate 或 my_affiliate 菜单项键添加推广中心入口;无论是否配置入口,命令 /affiliate 都可使用。
在机器人私聊发送 /whoami 获取自己的 Telegram 用户 ID,将 .env 的 BOT_ADMIN_USER_IDS 设置为逗号分隔的数字,例如 123456789,987654321,重建机器人后在私聊发送 /admin 测试。最多 10 名接收人,仅接受正整数私人用户 ID;管理员需先与机器人建立私聊且未屏蔽 Bot。群组、用户名、普通用户或群内的 /admin 不会触发管理操作。留空则关闭管理员通知,不影响审计和限流。演示栈也读取该配置;隔离测试栈使用固定的 Mock 接收人,不向真实 Telegram 发送通知。
通知包含新订单、订单支付/交付和充值成功回调、提现申请成功、结果不明、用户主动解除提现保护、会话存储故障,以及群发完成/发送结果不明。开发版使用明确的“管理员通知”标题和中文事件/结果,订单创建特别说明不代表已付款或发货,提现申请不代表已打款;审计日志仍使用稳定的机器事件码。摘要仅包含事件类型、结果、Telegram 用户 ID、关联数字 ID 和诊断编号,不含姓名、商品表单、金额、兑换码、支付链接、令牌、收款账号或公告全文。不表示所有公告均送达。非管理员的提现保护仍只能由本人核对记录后主动解除。
通知使用独立的 128 项内存队列,重复资源事件通常在 5 分钟内合并,每位接收人顺序限速;发送失败最多尝试 3 次,每次 3 秒超时,间隔 1/3 秒。失败、队列满或关闭丢弃都会写 admin.delivery 审计,不回滚已成功操作、不重发资金请求,也不让上游因管理员投递失败重试回调。队列和通知去重不持久化,崩溃可能丢失排队通知,重启可能再次通知;它不是可靠消息队列,不提供恰好一次投递保证,关键事件请以审计和商店记录核对。大量事件时通知可能合并、延后或丢弃。
资金接口调用前记录 attempt,结束后记录 succeeded / rejected / unknown,同一调用使用相同 event_id。还记录提现保护解锁、鉴权拒绝、存储故障和限流事件;HTTP 入口异常审计按类采样,避免日志放大。审计只接受固定事件/结果枚举及数字 ID,使用独立 INFO 级别,即使 BOT_LOG_LEVEL=error 也保留。不要将仅有 attempt 的记录解释为失败:请求可能已发出但进程在记录结果前中断,须到商店核对。
默认输出 JSON 审计到 stdout。本地可设置 BOT_AUDIT_FILE 到已有可写目录;配置后路径不可写会启动失败,运行时文件异常在 stderr 提示,stdout 审计保留。Docker 栈默认同时追加 /var/lib/dujiao-bot/audit.jsonl,审计卷与 Redis 卷独立,重建 Bot 不清除历史;文件权限 0600,单文件上限 10 MiB、最多 5 份轮转备份(合计约 60 MiB)。请定期导出到外部日志系统;轮转会覆盖最旧记录,删除卷会丢历史,崩溃可能丢失尚未刷盘的数据。这是运营日志,不是事务性、不可篡改审计或财务账本,写审计失败不会撤回资金操作。
默认额度如下,均为 GCRA 突发额度并逐步恢复,而不是整分钟归零;BOT_RATE_* 可设置 1..100000,不接受 0 关闭保护:
| 环境变量 | 默认值 | 作用范围 |
|---|---|---|
BOT_RATE_USER_PER_MINUTE |
60 | 单用户文本和按钮交互合计 |
BOT_RATE_WRITE_PER_MINUTE |
10 | 单用户下单、支付、取消订单、充值、礼品卡、推广开通及保护解锁共用 |
BOT_RATE_WITHDRAWAL_PER_10_MINUTES |
3 | 单用户提交提现的额外额度 |
BOT_RATE_CALLBACK_PER_MINUTE |
120 | 验签后的单用户回调,共用所有事件类型 |
BOT_RATE_HTTP_PER_MINUTE |
3000 | 回调入口单 TCP 来源;进程总入口额度为其两倍,最多 100000 |
BOT_RATE_GLOBAL_PER_MINUTE |
3000 | Bot Telegram 交互总额度 |
资金限流在消费草稿和提交请求之前检查,被拒绝时不会消耗已填草稿或解锁提现保护。/cancel 使用独立 6 次/分钟额度,不受普通用户桶限制,仍受进程总额度及 Redis 可用性约束。超额按钮会收到简短回调提示,超额普通文本静默丢弃,避免刷消息引发更多发送;无效格式可能在通用输入层被拒绝,不代表已经提交申请。
启用 Redis 后,Telegram 全局/用户、资金和已验签回调额度使用 Redis 时间与原子操作共享、按闲置 TTL 回收,重启不会恢复额度;Redis 故障不回退内存。未启用 Redis 时额度只在单进程内、重启重置。HTTP 来源预限流及通知去重仍是进程内的有界存储;HTTP 不信任 X-Forwarded-For,反向代理后按代理连接 IP 计算来源额度。回调超限返回 429,Redis 限流不可用返回 503,均带 Retry-After;调用方需要按商店能力重新签名重试,Bot 不保证上游一定重试。不限制健康探针 /healthz,也不取代代理层的连接限制、防火墙和 DDoS 防护。
仅向主动订阅公告的私人用户发送,不自动加入已有买家,不从订单或日志导入用户。用户发送 /subscribe 或通过首页“公告订阅”入口主动订阅;每条公告均有退订按钮及 /unsubscribe 提示。退订只关闭公告,不影响订单、支付、交付及充值通知。退订命令和按钮与 /cancel 共用独立的 6 次/分钟额度,仍受总入口限额及 Redis 可用性约束。已经在途的一条公告可能仍送达,无法从 Telegram 撤回。
管理员必须在 BOT_ADMIN_USER_IDS 白名单中,且只可在机器人私聊操作:
- 发送
/broadcast,回复当前填写提示输入公告。 - 查看完整预览,可修改或取消;此时不向订阅者发送。
- 点击“下一步:核对受众”,查看固定名单人数、限速和最终全文,再点击“确认开始群发”。两次操作绑定管理员、当前消息和轮换随机令牌,不能跳过预览;旧按钮和并发连点不会创建第二个任务。
- 使用
/broadcast_status查看最近一个任务的成功、失败、跳过、结果不明和剩余数量,可停止剩余发送。停止不能撤回在途消息。新任务覆盖已结束任务的状态页,历史元数据保留在轮转审计中。
公告为纯文本,正文最多 3000 个 UTF-16 单位(例如普通汉字计 1、常见 emoji 计 2),拒绝控制及隐藏格式字符;不解析 HTML/Markdown,不展开链接预览,不支持图片、任意自定义按钮、定时、导入用户 ID 或付费加速。草稿 15 分钟有效,/cancel、其他购物命令或返回购物按钮会使草稿失效;查看群发状态、订阅/退订不取消草稿。已确认任务不受普通导航影响,须在状态页停止。
默认每秒最多发送一条公告,BOT_BROADCAST_INTERVAL 可设置 1s..10s;BOT_BROADCAST_MAX_RECIPIENTS 默认 1000,可设置 1..5000。订阅登记总上限 5000,同时只运行一个群发任务;超过单次人数上限时拒绝开始,不静默截断名单。受众在最终确认页生成时固定,新订阅者不加入;实际发送前检查同一份订阅授权,退订或退订后重订的用户跳过本次旧任务。
Telegram 明确返回 429 时保存并遵守 retry_after,同一收件人最多尝试三次;冷却对后续公告任务也有效,不因停止、改建任务或 Redis 模式重启而绕过。403 自动移除对应旧订阅,400/404 计失败并继续。网络中断、超时、5xx 或无法确认的回执计结果不明并暂停,不自动重发该收件人;状态仅反映 Telegram 接口结果,不保证用户已阅读。这里采用保守的免费速率,不启用 allow_paid_broadcast,仍须遵循 Telegram 广播限制。
Redis 模式加密保存公告正文、草稿、受众快照和进度,订阅 ID/授权版本单独保存且不设 TTL。草稿逻辑有效期 15 分钟、任务 24 小时,读取/修改时清理过期正文;加密控制记录闲置 48 小时自动过期,因此过期草稿的密文并非必然在第 15 分钟物理删除。Redis AOF/备份及 Telegram 聊天历史可能保留内容。审计只记录操作、结果、用户 ID 和随机任务 ID,不含公告全文或受众列表;完成和结果不明会通过现有管理员摘要通知。
重启不自动恢复发送,任务转为暂停;仅原创建管理员核对后可点击“继续剩余”,其他管理员可查看或停止。未确认保存的任务保持 prepared(待人工启动);发送前先持久化领取标记和游标,领取保存报错不发送,未决标记最长约一分钟后转结果不明。恢复仅继续尚未领取的名单,绝不重发结果不明者,宁可漏发也不自动重发。在途状态保存失败、进程中断、数据删除/回滚或 Telegram 的不确定结果下,不承诺全局恰好一次投递。
内存模式可试用,但重启会丢失订阅、草稿、任务和限速状态;正式使用建议启用可靠 Redis 并保留稳定密钥与数据卷。保持 Telegram 长轮询单实例,不把这套轻量队列当作多实例广播平台。它与订单/充值通知和提现保护独立,不执行任何资金操作。
- Channel Secret 和 Bot Token 只通过环境变量传入,不要提交
.env。 - 所有 Dujiao-Next Channel API 请求使用协议要求的 HMAC-SHA256 签名。
- 所有内部回调会校验 Channel Key、签名及五分钟时间窗口。
- 用户、订单和支付数据以 Dujiao-Next 为准;会话可选择内存或 Redis。Redis 加密保存短期交互草稿,提现防重元数据不随草稿过期,具体保护边界见上文。
make test
make lint
make build版本整体验收使用 make verify 和 make acceptance,维护者打包使用 make release;详细边界见 发布与运维说明。二进制或容器内执行 --version 可在不加载密钥、不连接网络的情况下查看版本。
在 .env 中只填写 TELEGRAM_BOT_TOKEN,即可用内置演示商品启动真实 Telegram Bot;演示充值会即时到账,不会产生真实付款:
make demo-up
docker compose -f compose.demo.yml logs -f bot演示栈默认启用带持久卷的 Redis,健康端点为 http://127.0.0.1:19444/healthz。公开演示加密密钥仅用于 Mock 商店和虚构收款信息,严禁用于真实商店。make demo-down 保留 Redis 卷,重启 Bot 可继续草稿和未决保护;演示商店本身仍使用内存,重建商店会重置历史,验证会话恢复请只重启 bot。测试结束后运行:
make demo-down“人工配置服务”演示域名、邮箱、区域单选、配置多选和可跳过的备注。可使用 demo.example.com、demo@example.com 等虚构资料测试。订单付款后进入“处理中”,不会生成自动发货授权码。
演示优惠码:SAVE10 打九折;LESS5 满 20 CNY 减 5 CNY;EXPIRED 用于测试无效或过期提示。以上规则仅适用于演示商城,真实商店使用后台配置的优惠券。
演示礼品卡:GIFT50 兑换 50 CNY、GIFT100 兑换 100 CNY;GIFT-EXPIRED、GIFT-DISABLED、GIFT-USED 分别测试过期、停用和已兑换提示。每张有效演示卡仅能兑换一次,重启演示商城后重置,不会产生真实资金。
演示充值有免手续费、用户承担手续费、商家承担手续费三条渠道,均模拟即时到账,不收取真实资金。以充值 100 CNY 为例:免手续费实付 100.00;用户承担 2.50% + 1.00 CNY 时渠道手续费 3.50、实付 103.50;商家承担时仍显示渠道手续费 3.50,但实付为 100.00。三种渠道的钱包到账金额均为 100.00 CNY。重建演示商城会重置余额、订单和演示卡状态。
演示推广:先在推广者账号发送 /affiliate 并开通,将生成的链接发给另一个 Telegram 账号。购买者通过链接打开商店并完成演示付款后,推广者刷新可看到访问、有效订单和待确认佣金。演示佣金按优惠后实付本金的 10% 四舍五入计算,重复付款不会重复产生佣金,自购不计佣金;这仅用于演示,不代表真实商店规则,也不提供真实结算或打款。重建演示商店会重置推广档案和演示佣金。
演示提现:compose.demo.yml 为演示商店设置 DEMO_AFFILIATE_SEED_COMMISSION=100.00,新开通的推广档案会获得一条 DEMO-SEED 可用演示佣金,方便直接测试提现;该模拟初始化金额不计作有效推广订单。最低提现为 10 CNY,支持 alipay / wechat / usdt。提交 20 CNY 后记录为“待审核”,可用演示佣金变为 80 CNY,不会真实打款,也不改变演示钱包余额。收款信息请使用虚构数据。此变量只作用于 Mock 商店;隔离测试栈默认不预置佣金,重建演示商店会重置全部演示数据。
不需要真实密钥即可启动 Mock Dujiao-Next、Mock Telegram API 和真实机器人镜像:
make integration-up
docker compose -f compose.test.yml ps测试栈将机器人健康端点映射到 http://127.0.0.1:18444/healthz。运行内置压测:
make loadtest也可以调整请求量和并发数:
go run ./cmd/loadtest -mode health -n 5000 -c 100
go run ./cmd/loadtest -mode callback -n 2000 -c 50 -users 100默认回调压测分散到 100 个虚构用户,验证正常流量;另外可验证单用户突发被保护(仅隔离 Mock 栈):
go run ./cmd/loadtest -mode callback -n 500 -c 25 -users 1 -expect-rate-limit该模式要求至少出现一次带 Retry-After 的 429,正常 2xx 和预期 429 均不算失败。不要通过提高生产额度来掩盖保护测试。隔离栈的管理员队列也会在压测中触发背压,满队列写审计而非扩大内存或阻塞资金请求。
callback 模式会覆盖“Dujiao 签名回调 → 验签 → 机器人通知 → Telegram API”的完整链路。结束后清理测试容器:
make integration-down隔离测试栈同样默认启用 Redis,端口仅绑定本机 127.0.0.1:16379;停止栈保留卷。可单独验证真实 AOF 跨 Redis 重启恢复(只对隔离测试 Redis 执行):
BOT_TEST_REDIS_URL=redis://127.0.0.1:16379 BOT_TEST_REDIS_PHASE=seed go test ./internal/session -run '^TestRedisAOFRestart$' -count=1
docker compose -f compose.test.yml restart redis
BOT_TEST_REDIS_URL=redis://127.0.0.1:16379 BOT_TEST_REDIS_PHASE=verify go test ./internal/session -run '^TestRedisAOFRestart$' -count=1这两阶段测试只操作固定测试命名空间 dujiao-bot:test-aof,不要连接真实商店 Redis。普通 go test -race ./... 使用内存 Redis 测试实例,不依赖外部服务;覆盖重启恢复、并发 CAS、故障及丢失响应等场景。
TELEGRAM_API_URL 仅用于自建 Telegram Bot API 或本地测试,正常部署时保持为空。
目录说明:
cmd/bot/ 启动与生命周期管理
cmd/loadtest/ 本地 HTTP/回调压测工具
cmd/mock-api/ Docker 集成测试 Mock API
internal/callback/ Dujiao-Next 主动回调验签
internal/broadcast/ 主动订阅、固定受众与保守恢复的轻量群发队列
internal/config/ 环境变量配置
internal/dujiao/ Channel API 客户端与签名
internal/session/ 内存/Redis 会话、加密与提现保护
internal/operations/ 安全事件审计、持久日志轮转与管理员通知
internal/ratelimit/ 有界内存限流及策略(Redis 实现在 session)
internal/telegram/ Telegram 交互和业务流程
