Skip to content

ShyLess112/license-agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

钉钉授权申请 Agent

这是一个通过钉钉驱动的售前测试产品授权申请 Agent MVP 脚手架。

第一版重点解决:

  • 将钉钉文本解析成结构化申请。
  • 按不同产品收集对应必填字段。
  • 通过可替换执行器提交正常申请。
  • 审批是否通过仍交给现有后续流程处理。

完整闭环已经包含:

  • 提交后记录申请任务、CRMID、申请人和原始机器码。
  • 后台定时巡检 Release 详情页。
  • 授权生成后进入下载流程,按申请时保存的机器码回填下载页。
  • 下载 license 文件,并通过钉钉机器人发送短期下载链接。

目录结构

license_agent/          Python 包
config/products.json    产品字段目录
tests/                  核心解析与会话测试

测试核心逻辑

cd dingtalk-license-agent
python3 -m unittest discover tests

启动 Webhook 服务

在你的 Python 环境中安装运行依赖:

cd dingtalk-license-agent
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
uvicorn license_agent.main:app --host 0.0.0.0 --port 8000

钉钉机器人回调地址配置为:

http://你的服务器:8000/dingtalk/webhook

无公网 IP:启用钉钉 Stream 模式

如果部署机器没有公网 IP 或无法暴露 /dingtalk/webhook,可以启用钉钉 Stream 模式。Stream 模式由 Agent 主动连接钉钉,接收用户消息不需要公网回调地址。

.env 中配置钉钉开放平台应用凭证:

DINGTALK_STREAM_ENABLED=true
DINGTALK_STREAM_CLIENT_ID=你的钉钉应用Client ID或App Key
DINGTALK_STREAM_CLIENT_SECRET=你的钉钉应用Client Secret或App Secret
DINGTALK_STREAM_REPLY_TIMEOUT_SECONDS=3
DINGTALK_STREAM_ASYNC_SUBMIT=true

然后按原方式启动服务即可,tools/run_agent.py 和 systemd 都会在 FastAPI 启动时同时启动 Stream 长连接。HTTP webhook 会保留,方便有公网地址时继续使用。

注意:Stream 只解决“接收钉钉消息不需要公网 IP”。下载完成通知可以继续使用自定义机器人 webhook,也可以把 DINGTALK_NOTIFY_MODE=openapi 配成企业内部应用机器人 OpenAPI 单聊通知;但如果通知内容是 /license-files/{token} 短期下载链接,PUBLIC_BASE_URL 仍然需要是用户能访问的地址。没有公网文件地址时,后续应改接钉钉文件上传或对象存储链接。

Linux 服务器部署

服务器侧脚本和 systemd 模板已经放在 deploy/ 目录。部署时先在服务器拉取 GitHub 仓库:

cd /opt
sudo git clone git@github.com:ShyLess112/license-agent.git license-agent
cd /opt/license-agent
sudo bash deploy/install_server.sh

然后上传 config/release-storage-state.json 到:

/opt/license-agent/secrets/release-storage-state.json

详细步骤见 deploy/README.md

启用真实 Release 网页自动化

默认执行器是 mock,只用于对话和字段收集测试。真实填单需要启用 Playwright:

python -m playwright install chromium
export LICENSE_EXECUTOR=web
export RELEASE_DRY_RUN=true
export RELEASE_HEADLESS=true
export RELEASE_STORAGE_STATE=/path/to/release-storage-state.json
export RELEASE_SCREENSHOT_DIR=traces
uvicorn license_agent.main:app --host 0.0.0.0 --port 8000

RELEASE_DRY_RUN=true 时,Agent 会把 Release 表单填到提交前并保存截图,不会点击提交。确认流程稳定后,再改为:

生产部署推荐改用服务器持久 Chromium 目录,避免本地登录态上传后因服务端会话、 出口 IP 或浏览器环境变化而失效:

unset RELEASE_STORAGE_STATE
export RELEASE_USER_DATA_DIR=/AI/lisence-agent/browser-profile
export RELEASE_DATA_DIR=/AI/lisence-agent/data

首次登录通过 SSH 隧道和仅监听服务器回环地址的 VNC 完成,具体步骤见 deploy/README.md。“storage state 文件”和“持久浏览器目录”只能选择一种。

export RELEASE_DRY_RUN=false

授权下载闭环

Release 下载授权时还需要输入机器码,所以申请阶段必须保留原始 machine_code,不能只保存推导出来的设备数量。当前代码会同时保存:

  • machine_code:用户输入的原始机器码,用于后续下载授权。
  • device_count:根据机器码数量推导出来,用于申请页面的设备数量字段。

如果一次申请包含多个机器码,后续下载任务会拆成多个目标,每个目标使用同一个申请编号和一个机器码分别下载。下载页流程录制完成后,应把页面自动化实现接到 license_agent.download.LicenseDownloader

当前已实现 WebLicenseDownloader,可打开 Release 详情页,校验页面机器码,点击 下载 License 并把文件保存到 RELEASE_DOWNLOAD_DIR

生产回传采用短期签名下载链接:Agent 下载真实授权文件到服务器后,生成 /license-files/{token} 链接,并通过钉钉机器人发送给用户。链接会校验签名、过期时间和文件目录,避免授权文件永久暴露。

注意:详情页 URL 是 /license/{记录ID},页面里展示的 License ID 是另一个值。自动下载需要使用详情页 URL 里的记录 ID,或者直接传完整详情页 URL。

审批完成后自动巡检下载

提交成功后,Agent 会把详情页记录 ID、详情页 URL、CRMID、申请人和原始机器码写入 PENDING_DOWNLOAD_STORE。服务重启后会继续读取这个 JSON 队列。

启用后台巡检:

export PENDING_DOWNLOAD_STORE=/opt/license-agent/data/pending-downloads.json
export LICENSE_DOWNLOAD_QUEUE_ENABLED=true
export LICENSE_DOWNLOAD_MONITOR_ENABLED=true
export LICENSE_DOWNLOAD_POLL_INTERVAL_SECONDS=300
export LICENSE_DOWNLOAD_MAX_ATTEMPTS=0
export LICENSE_DOWNLOAD_NOTIFY=true

巡检逻辑:

  • 页面还没审批通过或未出现下载按钮时,任务保持 waiting,下次继续检查。
  • 页面可下载后,自动点击下载,必要时输入申请时保存的机器码。
  • 下载成功后任务标记为 done,文件路径写回队列。
  • LICENSE_DOWNLOAD_NOTIFY=true 时,通过钉钉机器人发送短期下载链接。

单独测试下载:

export RELEASE_STORAGE_STATE=/path/to/release-storage-state.json
export RELEASE_DOWNLOAD_DIR=downloads
python tools/download_license.py af04d667-b3a1-4e5f-83d1-09ee769d0935 UAHD-PE7X-7P47-FS4O

也可以直接传完整详情页 URL:

python tools/download_license.py https://release.chaitin.net/license/af04d667-b3a1-4e5f-83d1-09ee769d0935 UAHD-PE7X-7P47-FS4O

下载后通过钉钉发送短期链接:

export PUBLIC_BASE_URL=https://你的Agent公网域名
export DOWNLOAD_TOKEN_SECRET=请换成随机长密钥
export DOWNLOAD_LINK_TTL_SECONDS=86400
export DINGTALK_WEBHOOK_URL=https://oapi.dingtalk.com/robot/send?access_token=xxx
export DINGTALK_WEBHOOK_SECRET=钉钉机器人加签密钥
python tools/download_license.py af04d667-b3a1-4e5f-83d1-09ee769d0935 UAHD-PE7X-7P47-FS4O --notify

如果后续要做到“钉钉会话里直接出现附件文件”,需要接企业内部应用的文件上传/发送接口;当前自定义机器人链路先用短期下载链接,稳定且更容易审计。

保存 Release 登录态

远程 Linux 不需要桌面浏览器,但需要一个 Playwright 登录态文件。可以先在本地有浏览器的机器执行:

cd dingtalk-license-agent
source .venv/bin/activate
python tools/save_release_login.py --output config/release-storage-state.json

在打开的浏览器里完成 Release 登录并确认已经进入申请页,回到终端按回车保存。脚本会先把登录态写入临时文件,再用无头浏览器实时访问 Release 页面;校验通过后才会覆盖正式文件,避免保存“有 Cookie 但实际已失效”的登录态。

如果校验失败,脚本会提示最终跳转 URL。常见原因是还停留在登录页、登录后没有真正进入 Release 页面、或者服务端会话已经失效。重新登录后再按回车保存即可。

然后把这个 JSON 文件安全上传到远程虚拟机,并把 RELEASE_STORAGE_STATE 指向它。

检查登录态文件的 Cookie 过期时间:

python tools/check_release_login.py --storage config/release-storage-state.json

在 Linux 服务器上做实时访问校验:

python tools/check_release_login.py --storage /opt/license-agent/secrets/release-storage-state.json --live

--live 会启动无头浏览器访问 Release,输出最终 URL。只看 Cookie 过期时间不够可靠,因为 Release 服务端可能提前让会话失效;部署前以 --live 结果为准。

真实钉钉 token、签名密钥、Release 登录态和客户机器码都属于敏感信息,请通过环境变量或密钥管理配置,不要提交到代码仓库。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors