feat: 通知脚本支持 Telegram 通道 #96
No files matched your search
@@ -61,6 +61,30 @@ powershell -NoProfile -ExecutionPolicy Bypass -File tools/notify_decision.ps1 \
|
||||
| --- | --- |
|
||||
| 桌面通知 | 默认开启,无需配置 |
|
||||
| 决策日志 | 默认写入 `~/.pi/agent/decisions/pending.md` |
|
||||
| IM 机器人 | `setx PI_NOTIFY_WEBHOOK "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=..."` |
|
||||
| 企业微信/钉钉/飞书 | `notify.json` 里 `webhook` 字段,或环境变量 `PI_NOTIFY_WEBHOOK` |
|
||||
| Telegram | `notify.json` 里 `telegram.token` + `telegram.chatId` |
|
||||
|
||||
企业微信/钉钉用 `{"msgtype":"text",...}`;URL 里含 `feishu`/`lark` 时自动换成飞书格式。
|
||||
`%USERPROFILE%\.pi\agent\notify.json`(**仓库外**,密钥不能进 git):
|
||||
|
||||
```json
|
||||
{
|
||||
"webhook": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=...",
|
||||
"telegram": {
|
||||
"token": "123456789:AAxxxx",
|
||||
"chatId": "123456789",
|
||||
"proxy": "http://127.0.0.1:7897"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 企业微信/钉钉用 `{"msgtype":"text",...}`;URL 含 `feishu`/`lark` 时自动换飞书格式。
|
||||
- Telegram 在大陆需要代理(`telegram.proxy`,未配置时用 `HTTPS_PROXY` / 系统代理)。
|
||||
- 配了多个通道会全部发送。先自检:
|
||||
|
||||
```bash
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File tools/notify_decision.ps1 \
|
||||
-Title probe -Message x -NoToast -NoBeep -Probe
|
||||
```
|
||||
|
||||
自检会打印每个通道的结果(URL/token 打码)。注意企业微信与 Telegram 都可能
|
||||
**出错时仍返回 HTTP 200**,脚本已解析响应体把 `errcode` / `ok=false` 当失败处理。
|
||||
+80
-15
@@ -91,25 +91,74 @@ function Mask-Url {
|
||||
return $u.Substring(0, 32) + '...' + $u.Substring($u.Length - 8)
|
||||
}
|
||||
|
||||
function Get-WebhookUrl {
|
||||
# Order: -Url param > env var > ~/.pi/agent/notify.json > ~/.pi/agent/notify_webhook.txt
|
||||
# Config lives OUTSIDE the repo on purpose: a webhook URL is a secret.
|
||||
if ($Url) { return $Url }
|
||||
if ($env:PI_NOTIFY_WEBHOOK) { return $env:PI_NOTIFY_WEBHOOK }
|
||||
function Mask-Token {
|
||||
param([string]$t)
|
||||
if (-not $t) { return '(none)' }
|
||||
if ($t.Length -le 12) { return '***' }
|
||||
return $t.Substring(0, 10) + '***'
|
||||
}
|
||||
|
||||
$cfg = Join-Path $env:USERPROFILE '.pi\agent\notify.json'
|
||||
if (Test-Path $cfg) {
|
||||
function Get-NotifyConfig {
|
||||
# ~/.pi/agent/notify.json (outside the repo on purpose: tokens are secrets)
|
||||
# {
|
||||
# "webhook": "https://qyapi.weixin.qq.com/...", // WeCom / DingTalk / Feishu
|
||||
# "telegram": { "token": "123456:ABC...", "chatId": "123456789",
|
||||
# "proxy": "http://127.0.0.1:7897" }
|
||||
# }
|
||||
$cfg = @{ webhook = ''; tgToken = ''; tgChat = ''; proxy = '' }
|
||||
$path = Join-Path $env:USERPROFILE '.pi\agent\notify.json'
|
||||
if (Test-Path $path) {
|
||||
try {
|
||||
$o = [System.IO.File]::ReadAllText($cfg, [System.Text.Encoding]::UTF8) | ConvertFrom-Json
|
||||
if ($o.webhook) { return [string]$o.webhook }
|
||||
$o = [System.IO.File]::ReadAllText($path, [System.Text.Encoding]::UTF8) | ConvertFrom-Json
|
||||
if ($o.webhook) { $cfg.webhook = [string]$o.webhook }
|
||||
if ($o.proxy) { $cfg.proxy = [string]$o.proxy }
|
||||
if ($o.telegram) {
|
||||
if ($o.telegram.token) { $cfg.tgToken = [string]$o.telegram.token }
|
||||
if ($o.telegram.chatId) { $cfg.tgChat = [string]$o.telegram.chatId }
|
||||
if ($o.telegram.proxy) { $cfg.proxy = [string]$o.telegram.proxy }
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
$txt = Join-Path $env:USERPROFILE '.pi\agent\notify_webhook.txt'
|
||||
if (Test-Path $txt) {
|
||||
$v = ([System.IO.File]::ReadAllText($txt, [System.Text.Encoding]::UTF8)).Trim()
|
||||
if ($v) { return $v }
|
||||
$txtHook = Join-Path $env:USERPROFILE '.pi\agent\notify_webhook.txt'
|
||||
if (-not $cfg.webhook -and (Test-Path $txtHook)) {
|
||||
$v = ([System.IO.File]::ReadAllText($txtHook, [System.Text.Encoding]::UTF8)).Trim()
|
||||
if ($v) { $cfg.webhook = $v }
|
||||
}
|
||||
return $cfg
|
||||
}
|
||||
|
||||
function Get-ProxyArg {
|
||||
param([string]$p)
|
||||
if (-not $p) { $p = $env:HTTPS_PROXY }
|
||||
if (-not $p) { $p = $env:https_proxy }
|
||||
return $p
|
||||
}
|
||||
|
||||
function Send-Telegram {
|
||||
# Telegram Bot API. In mainland China this needs the local proxy; Invoke-RestMethod
|
||||
# otherwise falls back to the system (IE) proxy setting.
|
||||
param([string]$token, [string]$chatId, [string]$t, [string]$m, [string]$proxy)
|
||||
try {
|
||||
if (-not $token -or -not $chatId) { return 'not configured' }
|
||||
$txt = "$t`n$m"
|
||||
if ($txt.Length -gt 3900) { $txt = $txt.Substring(0, 3900) + ' ...' }
|
||||
$body = @{ chat_id = $chatId; text = $txt; disable_web_page_preview = $true } |
|
||||
ConvertTo-Json -Compress
|
||||
$bytes = [System.Text.Encoding]::UTF8.GetBytes($body)
|
||||
$uri = "https://api.telegram.org/bot$token/sendMessage"
|
||||
$args = @{
|
||||
Uri = $uri; Method = 'Post'
|
||||
ContentType = 'application/json; charset=utf-8'
|
||||
Body = $bytes; TimeoutSec = 15
|
||||
}
|
||||
$px = Get-ProxyArg $proxy
|
||||
if ($px) { $args['Proxy'] = $px }
|
||||
$resp = Invoke-RestMethod @args
|
||||
if ($resp.ok) { return 'ok' }
|
||||
return "not ok: $($resp.description)"
|
||||
} catch {
|
||||
return "http: $($_.Exception.Message)"
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +198,16 @@ if (-not $NoToast) {
|
||||
}
|
||||
if (-not $NoBeep) { try { [console]::Beep(880, 180); [console]::Beep(1175, 220) } catch { } }
|
||||
|
||||
$hook = Get-WebhookUrl
|
||||
$cfg = Get-NotifyConfig
|
||||
$hook = $Url
|
||||
if (-not $hook) { $hook = $env:PI_NOTIFY_WEBHOOK }
|
||||
if (-not $hook) { $hook = $cfg.webhook }
|
||||
|
||||
$tgToken = $env:PI_NOTIFY_TELEGRAM_TOKEN
|
||||
if (-not $tgToken) { $tgToken = $cfg.tgToken }
|
||||
$tgChat = $env:PI_NOTIFY_TELEGRAM_CHAT
|
||||
if (-not $tgChat) { $tgChat = $cfg.tgChat }
|
||||
|
||||
if ($hook) {
|
||||
$st = Post-Webhook -u $hook -t $Title -m $text
|
||||
if ($Probe) { Write-Output ("webhook -> " + (Mask-Url $hook) + " result: " + $st) }
|
||||
@@ -157,4 +215,11 @@ if ($hook) {
|
||||
Write-Output 'webhook not configured'
|
||||
}
|
||||
|
||||
if ($tgToken -and $tgChat) {
|
||||
$st = Send-Telegram -token $tgToken -chatId $tgChat -t $Title -m $text -proxy $cfg.proxy
|
||||
if ($Probe) { Write-Output ("telegram -> token " + (Mask-Token $tgToken) + " chat " + $tgChat + " result: " + $st) }
|
||||
} elseif ($Probe) {
|
||||
Write-Output 'telegram not configured'
|
||||
}
|
||||
|
||||
exit 0
|
||||
Reference in new issue
Block a user