tmex-cli 0.12.4 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -6
- package/dist/runtime/server.js +1639 -102
- package/package.json +1 -1
- package/resources/fe-dist/assets/DevicePage-Cj9jwadE.js +19 -0
- package/resources/fe-dist/assets/DevicesPage-BbypaGC7.js +1 -0
- package/resources/fe-dist/assets/{FilePage-D1t8_zlX.js → FilePage-CbHZVJbn.js} +3 -4
- package/resources/fe-dist/assets/SettingsPage-CCV0NSQF.js +39 -0
- package/resources/fe-dist/assets/agent-tab-DPwxh08T.js +38 -0
- package/resources/fe-dist/assets/api-DlcFE5in.js +10 -0
- package/resources/fe-dist/assets/{arc-Cv0H1Phu.js → arc-Bzmm6y8F.js} +1 -2
- package/resources/fe-dist/assets/{architectureDiagram-3BPJPVTR-DG7npZeK.js → architectureDiagram-3BPJPVTR-BdsN6XRe.js} +1 -2
- package/resources/fe-dist/assets/{blockDiagram-GPEHLZMM-weB7PKUK.js → blockDiagram-GPEHLZMM-CXdQhkqo.js} +1 -2
- package/resources/fe-dist/assets/{c4Diagram-AAUBKEIU-C0sEabtp.js → c4Diagram-AAUBKEIU-CE_W2_pV.js} +1 -2
- package/resources/fe-dist/assets/{card-Spfb5xRZ.js → card-B_sl9KIo.js} +1 -2
- package/resources/fe-dist/assets/channel-CuapxAWT.js +1 -0
- package/resources/fe-dist/assets/{chunk-2J33WTMH-BxOp2sIC.js → chunk-2J33WTMH-BAdEIJQO.js} +1 -2
- package/resources/fe-dist/assets/{chunk-4BX2VUAB-bhg4hxVT.js → chunk-4BX2VUAB-BZ0yj21c.js} +1 -2
- package/resources/fe-dist/assets/{chunk-55IACEB6-v6jayOak.js → chunk-55IACEB6-BknLtkSo.js} +1 -2
- package/resources/fe-dist/assets/{chunk-727SXJPM-IY0KVws8.js → chunk-727SXJPM-CxVON9n5.js} +1 -2
- package/resources/fe-dist/assets/{chunk-AQP2D5EJ-CgwymoWa.js → chunk-AQP2D5EJ-CLqOK2pR.js} +1 -2
- package/resources/fe-dist/assets/{chunk-FMBD7UC4-DN35690f.js → chunk-FMBD7UC4-Dt5Vot-R.js} +1 -2
- package/resources/fe-dist/assets/{chunk-ND2GUHAM-lJmtxdhS.js → chunk-ND2GUHAM-t9HYlb8C.js} +1 -2
- package/resources/fe-dist/assets/{chunk-QZHKN3VN-DFeaB8rn.js → chunk-QZHKN3VN-DZReNtLB.js} +1 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-C0k136Ud.js +1 -0
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-C0k136Ud.js +1 -0
- package/resources/fe-dist/assets/copy-Z9Fsj1r-.js +6 -0
- package/resources/fe-dist/assets/{cose-bilkent-S5V4N54A-Dw2yYQz4.js → cose-bilkent-S5V4N54A-BCTtGF2n.js} +1 -2
- package/resources/fe-dist/assets/cytoscape.esm-DTSO7Bv0.js +0 -1
- package/resources/fe-dist/assets/{dagre-BM42HDAG-BeMjJ7ln.js → dagre-BM42HDAG-BQ48EqSj.js} +1 -2
- package/resources/fe-dist/assets/defaultLocale-DX6XiGOO.js +0 -1
- package/resources/fe-dist/assets/{diagram-2AECGRRQ-Dur8_CuN.js → diagram-2AECGRRQ-BXjt2moR.js} +1 -2
- package/resources/fe-dist/assets/{diagram-5GNKFQAL-GJc7gVa_.js → diagram-5GNKFQAL-B2bvNTQO.js} +1 -2
- package/resources/fe-dist/assets/{diagram-KO2AKTUF-pxckXfjw.js → diagram-KO2AKTUF-DLHWfbBH.js} +1 -2
- package/resources/fe-dist/assets/{diagram-LMA3HP47-B9E8-X7C.js → diagram-LMA3HP47-BCFT3Bur.js} +1 -2
- package/resources/fe-dist/assets/{diagram-OG6HWLK6-DrhHw5wy.js → diagram-OG6HWLK6-bNZtxUJ2.js} +1 -2
- package/resources/fe-dist/assets/en_US-B07wSHDj.js +1 -0
- package/resources/fe-dist/assets/{erDiagram-TEJ5UH35-DY9Wg1A1.js → erDiagram-TEJ5UH35-KoAzrx4j.js} +1 -2
- package/resources/fe-dist/assets/files-tab-DdBDTjfs.js +70 -0
- package/resources/fe-dist/assets/{flowDiagram-I6XJVG4X-eq3WE9d6.js → flowDiagram-I6XJVG4X-C6wGExWt.js} +1 -2
- package/resources/fe-dist/assets/{ganttDiagram-6RSMTGT7-BlXISXe1.js → ganttDiagram-6RSMTGT7-BIWybjes.js} +1 -2
- package/resources/fe-dist/assets/{gitGraphDiagram-PVQCEYII-Bg-xHeIY.js → gitGraphDiagram-PVQCEYII-CD6tz24l.js} +1 -2
- package/resources/fe-dist/assets/graph-CAnANduQ.js +0 -1
- package/resources/fe-dist/assets/index-CUCrFB_J.js +29 -0
- package/resources/fe-dist/assets/index-CwH-l50P.js +299 -0
- package/resources/fe-dist/assets/index-D-q7dOhH.css +1 -0
- package/resources/fe-dist/assets/{infoDiagram-5YYISTIA-CW2drAHG.js → infoDiagram-5YYISTIA-CrI-qlYw.js} +1 -2
- package/resources/fe-dist/assets/init-Gi6I4Gst.js +0 -1
- package/resources/fe-dist/assets/{ishikawaDiagram-YF4QCWOH-Qchz0nqR.js → ishikawaDiagram-YF4QCWOH-D9v5anxN.js} +1 -2
- package/resources/fe-dist/assets/ja_JP-DjF6Lrmx.js +1 -0
- package/resources/fe-dist/assets/{journeyDiagram-JHISSGLW-Dk-UsexE.js → journeyDiagram-JHISSGLW-BH38CiJ0.js} +1 -2
- package/resources/fe-dist/assets/{kanban-definition-UN3LZRKU-DKeGp7b5.js → kanban-definition-UN3LZRKU-CKZ3_oXD.js} +1 -2
- package/resources/fe-dist/assets/layout-DGIYPm2g.js +0 -1
- package/resources/fe-dist/assets/{linear-Bt4f_qDW.js → linear-C_oE01xA.js} +1 -2
- package/resources/fe-dist/assets/{markdown-preview-DXv9GzaA.js → markdown-preview-BtNbNwlP.js} +6 -7
- package/resources/fe-dist/assets/{mermaid.core-B-wSkmMd.js → mermaid.core-4dbvlhht.js} +5 -6
- package/resources/fe-dist/assets/{mindmap-definition-RKZ34NQL-DXBwCMXu.js → mindmap-definition-RKZ34NQL-C5o4mnnX.js} +1 -2
- package/resources/fe-dist/assets/ordinal-BENe2yWM.js +0 -1
- package/resources/fe-dist/assets/{pieDiagram-4H26LBE5--KxRgbWG.js → pieDiagram-4H26LBE5-2ss3HotB.js} +1 -2
- package/resources/fe-dist/assets/{quadrantDiagram-W4KKPZXB-DqXsCI9E.js → quadrantDiagram-W4KKPZXB-Bolcfx55.js} +1 -2
- package/resources/fe-dist/assets/{requirementDiagram-4Y6WPE33-C2KeTWqB.js → requirementDiagram-4Y6WPE33-B-atSHRR.js} +1 -2
- package/resources/fe-dist/assets/{sankeyDiagram-5OEKKPKP-CC5tcr7d.js → sankeyDiagram-5OEKKPKP-ByynRcAV.js} +1 -2
- package/resources/fe-dist/assets/selection-clipboard-Dq6Zemfd.js +1 -0
- package/resources/fe-dist/assets/send-D0P5wgcD.js +11 -0
- package/resources/fe-dist/assets/{sequenceDiagram-3UESZ5HK-CqlMDtOG.js → sequenceDiagram-3UESZ5HK-CNC9IxV4.js} +1 -2
- package/resources/fe-dist/assets/{stateDiagram-AJRCARHV-CVcrlBiw.js → stateDiagram-AJRCARHV-CqzYDImp.js} +1 -2
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-C3azxyWE.js +1 -0
- package/resources/fe-dist/assets/{terminal-settings-panel-CYV9svzz.js → terminal-settings-panel-cjw9P_wY.js} +6 -7
- package/resources/fe-dist/assets/{timeline-definition-PNZ67QCA-CEBskfkL.js → timeline-definition-PNZ67QCA-C38KIECs.js} +1 -2
- package/resources/fe-dist/assets/transfer-toast-5tb-zYhi.js +6 -0
- package/resources/fe-dist/assets/triangle-alert-CQzql6mY.js +6 -0
- package/resources/fe-dist/assets/{vennDiagram-CIIHVFJN-CrxyVTtu.js → vennDiagram-CIIHVFJN-BJudKfzJ.js} +1 -2
- package/resources/fe-dist/assets/{wardley-L42UT6IY-CSmsRb8z.js → wardley-L42UT6IY-B6p_luMy.js} +1 -2
- package/resources/fe-dist/assets/{wardleyDiagram-YWT4CUSO-DoDzNeOA.js → wardleyDiagram-YWT4CUSO-DZJeOiWQ.js} +1 -2
- package/resources/fe-dist/assets/{xychartDiagram-2RQKCTM6-Dbq_OKsv.js → xychartDiagram-2RQKCTM6-DOMCute2.js} +1 -2
- package/resources/fe-dist/assets/zap-DWNtdoic.js +6 -0
- package/resources/fe-dist/assets/zh_CN-D10j4J8Y.js +1 -0
- package/resources/fe-dist/index.html +2 -2
- package/resources/gateway-drizzle/0010_lucky_kabuki.sql +32 -0
- package/resources/gateway-drizzle/meta/_journal.json +7 -0
- package/resources/fe-dist/assets/DevicePage-C4JS6baR.js +0 -15
- package/resources/fe-dist/assets/DevicesPage-p0CfWP1T.js +0 -2
- package/resources/fe-dist/assets/SettingsPage-Cq6n8Xjc.js +0 -27
- package/resources/fe-dist/assets/channel-DDiDKfY0.js +0 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-CXFjdzka.js +0 -2
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-CXFjdzka.js +0 -2
- package/resources/fe-dist/assets/index-CXIj7HZi.css +0 -1
- package/resources/fe-dist/assets/index-CYmVLCAD.js +0 -488
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-B0BDuZBM.js +0 -2
package/dist/runtime/server.js
CHANGED
|
@@ -22745,6 +22745,8 @@ var I18N_RESOURCES = {
|
|
|
22745
22745
|
enableBrowserNotificationToast: "Enable Browser Notification Toast",
|
|
22746
22746
|
enableTelegramBellPush: "Enable Telegram Bell Push",
|
|
22747
22747
|
enableTelegramNotificationPush: "Enable Telegram Notification Push",
|
|
22748
|
+
enableWeixinBellPush: "Enable WeChat Bell Push",
|
|
22749
|
+
enableWeixinNotificationPush: "Enable WeChat Notification Push",
|
|
22748
22750
|
sshReconnectRetries: "SSH Reconnect Retries",
|
|
22749
22751
|
sshReconnectDelay: "SSH Reconnect Delay (seconds)",
|
|
22750
22752
|
language: "Language",
|
|
@@ -22984,6 +22986,81 @@ Time: {{time}}`,
|
|
|
22984
22986
|
botNotFound: "Bot not found or unavailable",
|
|
22985
22987
|
botNotRunning: "Bot is not running or unavailable"
|
|
22986
22988
|
},
|
|
22989
|
+
weixin: {
|
|
22990
|
+
title: "WeChat (ClawBot) Management",
|
|
22991
|
+
subtitle: "Push tmex alerts to your personal WeChat via the iLink bot protocol.",
|
|
22992
|
+
replyOnlyNotice: "iLink can only reply inside an active conversation window \u2014 message the bot first to activate; alerts then reuse that session. If it goes quiet too long the session expires; message the bot again to re-activate.",
|
|
22993
|
+
accountName: "Account Name",
|
|
22994
|
+
accountNamePlaceholder: "e.g., my-wechat",
|
|
22995
|
+
addAccount: "Add Account",
|
|
22996
|
+
editAccount: "Edit Account",
|
|
22997
|
+
enableAccount: "Enable Account",
|
|
22998
|
+
allowAuthRequests: "Allow Authorization Requests",
|
|
22999
|
+
scanToLogin: "Scan to Log In",
|
|
23000
|
+
relogin: "Re-login (Re-scan)",
|
|
23001
|
+
loggedIn: "Logged in",
|
|
23002
|
+
notLoggedIn: "Not logged in",
|
|
23003
|
+
scanQrcodeHint: "Open WeChat, scan the QR code, and confirm on your phone.",
|
|
23004
|
+
loginPending: "Waiting for scan\u2026",
|
|
23005
|
+
loginConfirmed: "Login confirmed.",
|
|
23006
|
+
loginExpired: "QR code expired. Please retry.",
|
|
23007
|
+
loginError: "Login failed: {{message}}",
|
|
23008
|
+
loginFailed: "Login failed",
|
|
23009
|
+
refreshQrcode: "Refresh QR Code",
|
|
23010
|
+
closeLogin: "Close",
|
|
23011
|
+
pendingUsers: "Pending Authorization",
|
|
23012
|
+
authorizedUsers: "Authorized Users",
|
|
23013
|
+
noPendingUsers: "No pending users",
|
|
23014
|
+
noAuthorizedUsers: "No authorized users",
|
|
23015
|
+
needsReactivation: "Session expired",
|
|
23016
|
+
reactivationHint: "Ask this user to send the bot any message to re-activate alerts.",
|
|
23017
|
+
approve: "Approve",
|
|
23018
|
+
revokeAuth: "Revoke",
|
|
23019
|
+
removeUser: "Remove",
|
|
23020
|
+
testMessage: "Test Message",
|
|
23021
|
+
sendTestMessage: "Send Test Message",
|
|
23022
|
+
deleteAccount: "Delete Account",
|
|
23023
|
+
accountCreated: "Account created",
|
|
23024
|
+
accountUpdated: "Account updated",
|
|
23025
|
+
accountDeleted: "Account deleted",
|
|
23026
|
+
authApproved: "Authorization approved",
|
|
23027
|
+
userRemoved: "User removed",
|
|
23028
|
+
testMessageSent: "Test message sent",
|
|
23029
|
+
createFailed: "Failed to create account",
|
|
23030
|
+
updateFailed: "Failed to update account",
|
|
23031
|
+
deleteFailed: "Failed to delete account",
|
|
23032
|
+
approveFailed: "Failed to approve",
|
|
23033
|
+
removeFailed: "Failed to remove user",
|
|
23034
|
+
testMessageFailed: "Failed to send test message",
|
|
23035
|
+
loadAccountsFailed: "Failed to load accounts",
|
|
23036
|
+
loadUsersFailed: "Failed to load users",
|
|
23037
|
+
noAccounts: "No accounts yet, add one first.",
|
|
23038
|
+
userCount: "{{authorized}} authorized / {{pending}} pending (max 16)",
|
|
23039
|
+
userId: "WeChat User ID",
|
|
23040
|
+
applyTime: "First Contact",
|
|
23041
|
+
expand: "Expand",
|
|
23042
|
+
collapse: "Collapse",
|
|
23043
|
+
accountNameRequired: "Account name is required",
|
|
23044
|
+
accountNotFound: "Account not found or unavailable",
|
|
23045
|
+
accountNotRunning: "Account is not logged in or not running",
|
|
23046
|
+
userNotFound: "User not found",
|
|
23047
|
+
authSuccess: "\u2705 Authorized. You will now receive notifications.",
|
|
23048
|
+
authPending: "\u23F3 Authorization request received. Please approve in tmex settings.",
|
|
23049
|
+
testMessageTemplate: `\uD83E\uDDEA Test Message
|
|
23050
|
+
Site: {{siteName}}
|
|
23051
|
+
Time: {{time}}`,
|
|
23052
|
+
approveMessageTemplate: `\u2705 Authorized via tmex.
|
|
23053
|
+
Account: {{accountName}}
|
|
23054
|
+
Time: {{time}}`,
|
|
23055
|
+
keepalivePrompt: "\u3010tmex\u3011To keep notifications flowing, just reply with anything to keep the session alive \uD83D\uDE4F",
|
|
23056
|
+
scanConfirmedSendHint: "Scan confirmed! Now send any message to this bot in WeChat to finish binding.",
|
|
23057
|
+
bindingInProgress: "Message received, finishing binding\u2026",
|
|
23058
|
+
bindSuccess: "WeChat bound. You will now receive notifications.",
|
|
23059
|
+
bound: "Bound",
|
|
23060
|
+
unbound: "Not bound",
|
|
23061
|
+
bindAction: "Bind (scan)",
|
|
23062
|
+
gatewayOnline: "\uD83D\uDFE2 tmex online @ {{siteName}}"
|
|
23063
|
+
},
|
|
22987
23064
|
webhook: {
|
|
22988
23065
|
title: "Webhooks",
|
|
22989
23066
|
url: "Webhook URL",
|
|
@@ -23659,6 +23736,8 @@ Time: {{time}}`,
|
|
|
23659
23736
|
enableBrowserNotificationToast: "\u5F00\u542F\u6D4F\u89C8\u5668\u901A\u77E5 Toast",
|
|
23660
23737
|
enableTelegramBellPush: "\u5F00\u542F Telegram Bell \u63A8\u9001",
|
|
23661
23738
|
enableTelegramNotificationPush: "\u5F00\u542F Telegram \u901A\u77E5\u63A8\u9001",
|
|
23739
|
+
enableWeixinBellPush: "\u542F\u7528\u5FAE\u4FE1\u54CD\u94C3\u63A8\u9001",
|
|
23740
|
+
enableWeixinNotificationPush: "\u542F\u7528\u5FAE\u4FE1\u901A\u77E5\u63A8\u9001",
|
|
23662
23741
|
sshReconnectRetries: "SSH \u91CD\u8FDE\u6B21\u6570",
|
|
23663
23742
|
sshReconnectDelay: "SSH \u91CD\u8FDE\u7B49\u5F85\uFF08\u79D2\uFF09",
|
|
23664
23743
|
language: "\u8BED\u8A00",
|
|
@@ -23898,6 +23977,81 @@ Bot\uFF1A{{botName}}
|
|
|
23898
23977
|
botNotFound: "Bot \u672A\u542F\u52A8\u6216\u4E0D\u53EF\u7528",
|
|
23899
23978
|
botNotRunning: "Bot \u672A\u542F\u52A8\u6216\u4E0D\u53EF\u7528"
|
|
23900
23979
|
},
|
|
23980
|
+
weixin: {
|
|
23981
|
+
title: "\u5FAE\u4FE1 (ClawBot) \u7BA1\u7406",
|
|
23982
|
+
subtitle: "\u901A\u8FC7 iLink bot \u534F\u8BAE\u628A tmex \u544A\u8B66\u63A8\u9001\u5230\u4F60\u7684\u4E2A\u4EBA\u5FAE\u4FE1\u3002",
|
|
23983
|
+
replyOnlyNotice: "iLink \u53EA\u80FD\u5728\u4F1A\u8BDD\u6FC0\u6D3B\u7A97\u53E3\u5185\u56DE\u590D\u2014\u2014\u5148\u7ED9 bot \u53D1\u6761\u6D88\u606F\u6FC0\u6D3B\uFF0C\u544A\u8B66\u968F\u540E\u590D\u7528\u8BE5\u4F1A\u8BDD\uFF1B\u95F2\u7F6E\u8FC7\u4E45\u4F1A\u8BDD\u4F1A\u8FC7\u671F\uFF0C\u9700\u518D\u7ED9 bot \u53D1\u6761\u6D88\u606F\u91CD\u65B0\u6FC0\u6D3B\u3002",
|
|
23984
|
+
accountName: "\u8D26\u53F7\u540D\u79F0",
|
|
23985
|
+
accountNamePlaceholder: "\u5982\uFF1A\u6211\u7684\u5FAE\u4FE1",
|
|
23986
|
+
addAccount: "\u6DFB\u52A0\u8D26\u53F7",
|
|
23987
|
+
editAccount: "\u7F16\u8F91\u8D26\u53F7",
|
|
23988
|
+
enableAccount: "\u542F\u7528\u8D26\u53F7",
|
|
23989
|
+
allowAuthRequests: "\u5141\u8BB8\u6388\u6743\u7533\u8BF7",
|
|
23990
|
+
scanToLogin: "\u626B\u7801\u767B\u5F55",
|
|
23991
|
+
relogin: "\u91CD\u65B0\u767B\u5F55\uFF08\u91CD\u65B0\u626B\u7801\uFF09",
|
|
23992
|
+
loggedIn: "\u5DF2\u767B\u5F55",
|
|
23993
|
+
notLoggedIn: "\u672A\u767B\u5F55",
|
|
23994
|
+
scanQrcodeHint: "\u6253\u5F00\u5FAE\u4FE1\u626B\u63CF\u4E8C\u7EF4\u7801\uFF0C\u5E76\u5728\u624B\u673A\u4E0A\u786E\u8BA4\u3002",
|
|
23995
|
+
loginPending: "\u7B49\u5F85\u626B\u7801\u2026",
|
|
23996
|
+
loginConfirmed: "\u767B\u5F55\u5DF2\u786E\u8BA4\u3002",
|
|
23997
|
+
loginExpired: "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
23998
|
+
loginError: "\u767B\u5F55\u5931\u8D25\uFF1A{{message}}",
|
|
23999
|
+
loginFailed: "\u767B\u5F55\u5931\u8D25",
|
|
24000
|
+
refreshQrcode: "\u5237\u65B0\u4E8C\u7EF4\u7801",
|
|
24001
|
+
closeLogin: "\u5173\u95ED",
|
|
24002
|
+
pendingUsers: "\u5F85\u6388\u6743",
|
|
24003
|
+
authorizedUsers: "\u5DF2\u6388\u6743\u7528\u6237",
|
|
24004
|
+
noPendingUsers: "\u6682\u65E0\u5F85\u6388\u6743\u7528\u6237",
|
|
24005
|
+
noAuthorizedUsers: "\u6682\u65E0\u5DF2\u6388\u6743\u7528\u6237",
|
|
24006
|
+
needsReactivation: "\u4F1A\u8BDD\u5DF2\u8FC7\u671F",
|
|
24007
|
+
reactivationHint: "\u8BF7\u8BE5\u7528\u6237\u7ED9 bot \u53D1\u6761\u6D88\u606F\u4EE5\u91CD\u65B0\u6FC0\u6D3B\u544A\u8B66\u3002",
|
|
24008
|
+
approve: "\u6279\u51C6",
|
|
24009
|
+
revokeAuth: "\u64A4\u9500\u6388\u6743",
|
|
24010
|
+
removeUser: "\u79FB\u9664",
|
|
24011
|
+
testMessage: "\u6D4B\u8BD5\u6D88\u606F",
|
|
24012
|
+
sendTestMessage: "\u53D1\u9001\u6D4B\u8BD5\u6D88\u606F",
|
|
24013
|
+
deleteAccount: "\u5220\u9664\u8D26\u53F7",
|
|
24014
|
+
accountCreated: "\u8D26\u53F7\u5DF2\u521B\u5EFA",
|
|
24015
|
+
accountUpdated: "\u8D26\u53F7\u5DF2\u66F4\u65B0",
|
|
24016
|
+
accountDeleted: "\u8D26\u53F7\u5DF2\u5220\u9664",
|
|
24017
|
+
authApproved: "\u6388\u6743\u5DF2\u6279\u51C6",
|
|
24018
|
+
userRemoved: "\u7528\u6237\u5DF2\u79FB\u9664",
|
|
24019
|
+
testMessageSent: "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001",
|
|
24020
|
+
createFailed: "\u521B\u5EFA\u8D26\u53F7\u5931\u8D25",
|
|
24021
|
+
updateFailed: "\u66F4\u65B0\u8D26\u53F7\u5931\u8D25",
|
|
24022
|
+
deleteFailed: "\u5220\u9664\u8D26\u53F7\u5931\u8D25",
|
|
24023
|
+
approveFailed: "\u6279\u51C6\u5931\u8D25",
|
|
24024
|
+
removeFailed: "\u79FB\u9664\u7528\u6237\u5931\u8D25",
|
|
24025
|
+
testMessageFailed: "\u53D1\u9001\u6D4B\u8BD5\u6D88\u606F\u5931\u8D25",
|
|
24026
|
+
loadAccountsFailed: "\u52A0\u8F7D\u8D26\u53F7\u5217\u8868\u5931\u8D25",
|
|
24027
|
+
loadUsersFailed: "\u52A0\u8F7D\u7528\u6237\u5217\u8868\u5931\u8D25",
|
|
24028
|
+
noAccounts: "\u8FD8\u6CA1\u6709\u8D26\u53F7\uFF0C\u5148\u6DFB\u52A0\u4E00\u4E2A\u3002",
|
|
24029
|
+
userCount: "{{authorized}} \u5DF2\u6388\u6743 / {{pending}} \u5F85\u6388\u6743\uFF08\u4E0A\u9650 16\uFF09",
|
|
24030
|
+
userId: "\u5FAE\u4FE1\u7528\u6237 ID",
|
|
24031
|
+
applyTime: "\u9996\u6B21\u8054\u7CFB\u65F6\u95F4",
|
|
24032
|
+
expand: "\u5C55\u5F00",
|
|
24033
|
+
collapse: "\u6536\u8D77",
|
|
24034
|
+
accountNameRequired: "\u8D26\u53F7\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A",
|
|
24035
|
+
accountNotFound: "\u8D26\u53F7\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528",
|
|
24036
|
+
accountNotRunning: "\u8D26\u53F7\u672A\u767B\u5F55\u6216\u672A\u8FD0\u884C",
|
|
24037
|
+
userNotFound: "\u7528\u6237\u4E0D\u5B58\u5728",
|
|
24038
|
+
authSuccess: "\u2705 \u5DF2\u6388\u6743\uFF0C\u4F60\u5C06\u5F00\u59CB\u6536\u5230\u901A\u77E5\u3002",
|
|
24039
|
+
authPending: "\u23F3 \u5DF2\u6536\u5230\u6388\u6743\u7533\u8BF7\uFF0C\u8BF7\u5728 tmex \u8BBE\u7F6E\u4E2D\u6279\u51C6\u3002",
|
|
24040
|
+
testMessageTemplate: `\uD83E\uDDEA \u6D4B\u8BD5\u6D88\u606F
|
|
24041
|
+
\u7AD9\u70B9\uFF1A{{siteName}}
|
|
24042
|
+
\u65F6\u95F4\uFF1A{{time}}`,
|
|
24043
|
+
approveMessageTemplate: `\u2705 \u5DF2\u901A\u8FC7 tmex \u6388\u6743\u3002
|
|
24044
|
+
\u8D26\u53F7\uFF1A{{accountName}}
|
|
24045
|
+
\u65F6\u95F4\uFF1A{{time}}`,
|
|
24046
|
+
keepalivePrompt: "\u3010tmex\u3011\u4E3A\u786E\u4FDD\u4F60\u80FD\u6B63\u5E38\u6536\u5230\u901A\u77E5\uFF0C\u8BF7\u56DE\u590D\u4EFB\u610F\u5185\u5BB9\u5373\u53EF\u4FDD\u6301\u8FDE\u63A5 \uD83D\uDE4F",
|
|
24047
|
+
scanConfirmedSendHint: "\u626B\u7801\u6210\u529F\uFF01\u8BF7\u5728\u5FAE\u4FE1\u91CC\u7ED9\u8BE5 bot \u53D1\u9001\u4EFB\u610F\u4E00\u6761\u6D88\u606F\u4EE5\u5B8C\u6210\u7ED1\u5B9A\u3002",
|
|
24048
|
+
bindingInProgress: "\u5DF2\u6536\u5230\u6D88\u606F\uFF0C\u6B63\u5728\u5B8C\u6210\u7ED1\u5B9A\u2026",
|
|
24049
|
+
bindSuccess: "\u5FAE\u4FE1\u5DF2\u7ED1\u5B9A\uFF0C\u5F00\u59CB\u63A5\u6536\u901A\u77E5\u3002",
|
|
24050
|
+
bound: "\u5DF2\u7ED1\u5B9A",
|
|
24051
|
+
unbound: "\u672A\u7ED1\u5B9A",
|
|
24052
|
+
bindAction: "\u626B\u7801\u7ED1\u5B9A",
|
|
24053
|
+
gatewayOnline: "\uD83D\uDFE2 tmex \u5DF2\u4E0A\u7EBF @ {{siteName}}"
|
|
24054
|
+
},
|
|
23901
24055
|
webhook: {
|
|
23902
24056
|
title: "Webhooks",
|
|
23903
24057
|
url: "Webhook \u5730\u5740",
|
|
@@ -24573,6 +24727,8 @@ Bot\uFF1A{{botName}}
|
|
|
24573
24727
|
enableBrowserNotificationToast: "\u30D6\u30E9\u30A6\u30B6\u901A\u77E5 Toast \u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
24574
24728
|
enableTelegramBellPush: "Telegram \u30D9\u30EB\u30D7\u30C3\u30B7\u30E5\u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
24575
24729
|
enableTelegramNotificationPush: "Telegram \u901A\u77E5\u30D7\u30C3\u30B7\u30E5\u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
24730
|
+
enableWeixinBellPush: "WeChat \u30D9\u30EB\u901A\u77E5\u3092\u6709\u52B9\u5316",
|
|
24731
|
+
enableWeixinNotificationPush: "WeChat \u901A\u77E5\u30D7\u30C3\u30B7\u30E5\u3092\u6709\u52B9\u5316",
|
|
24576
24732
|
sshReconnectRetries: "SSH \u518D\u63A5\u7D9A\u8A66\u884C\u56DE\u6570",
|
|
24577
24733
|
sshReconnectDelay: "SSH \u518D\u63A5\u7D9A\u5F85\u6A5F\uFF08\u79D2\uFF09",
|
|
24578
24734
|
language: "\u8A00\u8A9E",
|
|
@@ -24812,6 +24968,81 @@ Bot\uFF1A{{botName}}
|
|
|
24812
24968
|
botNotFound: "Bot \u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
24813
24969
|
botNotRunning: "Bot \u304C\u8D77\u52D5\u3057\u3066\u3044\u306A\u3044\u304B\u5229\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
24814
24970
|
},
|
|
24971
|
+
weixin: {
|
|
24972
|
+
title: "WeChat (ClawBot) \u7BA1\u7406",
|
|
24973
|
+
subtitle: "iLink bot \u30D7\u30ED\u30C8\u30B3\u30EB\u3067 tmex \u306E\u30A2\u30E9\u30FC\u30C8\u3092\u500B\u4EBA WeChat \u3078\u9001\u4FE1\u3057\u307E\u3059\u3002",
|
|
24974
|
+
replyOnlyNotice: "iLink \u306F\u30A2\u30AF\u30C6\u30A3\u30D6\u306A\u4F1A\u8A71\u30A6\u30A3\u30F3\u30C9\u30A6\u5185\u3067\u306E\u307F\u8FD4\u4FE1\u3067\u304D\u307E\u3059\u3002\u307E\u305A bot \u306B\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u3063\u3066\u6709\u52B9\u5316\u3059\u308B\u3068\u3001\u4EE5\u964D\u306E\u30A2\u30E9\u30FC\u30C8\u306F\u305D\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u518D\u5229\u7528\u3057\u307E\u3059\u3002\u4E00\u5B9A\u6642\u9593\u64CD\u4F5C\u304C\u306A\u3044\u3068\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u5931\u52B9\u3059\u308B\u305F\u3081\u3001\u518D\u5EA6 bot \u306B\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u3063\u3066\u518D\u6709\u52B9\u5316\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
24975
|
+
accountName: "\u30A2\u30AB\u30A6\u30F3\u30C8\u540D",
|
|
24976
|
+
accountNamePlaceholder: "\u4F8B\uFF1Amy-wechat",
|
|
24977
|
+
addAccount: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u8FFD\u52A0",
|
|
24978
|
+
editAccount: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u7DE8\u96C6",
|
|
24979
|
+
enableAccount: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u6709\u52B9\u5316",
|
|
24980
|
+
allowAuthRequests: "\u8A8D\u8A3C\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u8A31\u53EF",
|
|
24981
|
+
scanToLogin: "\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u30ED\u30B0\u30A4\u30F3",
|
|
24982
|
+
relogin: "\u518D\u30ED\u30B0\u30A4\u30F3\uFF08\u518D\u30B9\u30AD\u30E3\u30F3\uFF09",
|
|
24983
|
+
loggedIn: "\u30ED\u30B0\u30A4\u30F3\u6E08\u307F",
|
|
24984
|
+
notLoggedIn: "\u672A\u30ED\u30B0\u30A4\u30F3",
|
|
24985
|
+
scanQrcodeHint: "WeChat \u3092\u958B\u3044\u3066 QR \u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3057\u3001\u30B9\u30DE\u30DB\u3067\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
24986
|
+
loginPending: "\u30B9\u30AD\u30E3\u30F3\u5F85\u3061\u2026",
|
|
24987
|
+
loginConfirmed: "\u30ED\u30B0\u30A4\u30F3\u304C\u78BA\u8A8D\u3055\u308C\u307E\u3057\u305F\u3002",
|
|
24988
|
+
loginExpired: "QR \u30B3\u30FC\u30C9\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u307E\u3057\u305F\u3002\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
24989
|
+
loginError: "\u30ED\u30B0\u30A4\u30F3\u5931\u6557\uFF1A{{message}}",
|
|
24990
|
+
loginFailed: "\u30ED\u30B0\u30A4\u30F3\u5931\u6557",
|
|
24991
|
+
refreshQrcode: "QR \u30B3\u30FC\u30C9\u3092\u66F4\u65B0",
|
|
24992
|
+
closeLogin: "\u9589\u3058\u308B",
|
|
24993
|
+
pendingUsers: "\u627F\u8A8D\u5F85\u3061",
|
|
24994
|
+
authorizedUsers: "\u627F\u8A8D\u6E08\u307F\u30E6\u30FC\u30B6\u30FC",
|
|
24995
|
+
noPendingUsers: "\u627F\u8A8D\u5F85\u3061\u306E\u30E6\u30FC\u30B6\u30FC\u306F\u3044\u307E\u305B\u3093",
|
|
24996
|
+
noAuthorizedUsers: "\u627F\u8A8D\u6E08\u307F\u306E\u30E6\u30FC\u30B6\u30FC\u306F\u3044\u307E\u305B\u3093",
|
|
24997
|
+
needsReactivation: "\u30BB\u30C3\u30B7\u30E7\u30F3\u5931\u52B9",
|
|
24998
|
+
reactivationHint: "\u3053\u306E\u30E6\u30FC\u30B6\u30FC\u306B bot \u3078\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u3063\u3066\u3082\u3089\u3044\u3001\u30A2\u30E9\u30FC\u30C8\u3092\u518D\u6709\u52B9\u5316\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
24999
|
+
approve: "\u627F\u8A8D",
|
|
25000
|
+
revokeAuth: "\u627F\u8A8D\u3092\u53D6\u308A\u6D88\u3059",
|
|
25001
|
+
removeUser: "\u524A\u9664",
|
|
25002
|
+
testMessage: "\u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8",
|
|
25003
|
+
sendTestMessage: "\u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1",
|
|
25004
|
+
deleteAccount: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u524A\u9664",
|
|
25005
|
+
accountCreated: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u4F5C\u6210\u3057\u307E\u3057\u305F",
|
|
25006
|
+
accountUpdated: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u66F4\u65B0\u3057\u307E\u3057\u305F",
|
|
25007
|
+
accountDeleted: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u524A\u9664\u3057\u307E\u3057\u305F",
|
|
25008
|
+
authApproved: "\u8A8D\u8A3C\u3092\u627F\u8A8D\u3057\u307E\u3057\u305F",
|
|
25009
|
+
userRemoved: "\u30E6\u30FC\u30B6\u30FC\u3092\u524A\u9664\u3057\u307E\u3057\u305F",
|
|
25010
|
+
testMessageSent: "\u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1\u3057\u307E\u3057\u305F",
|
|
25011
|
+
createFailed: "\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u4F5C\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25012
|
+
updateFailed: "\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u66F4\u65B0\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25013
|
+
deleteFailed: "\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u524A\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25014
|
+
approveFailed: "\u627F\u8A8D\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25015
|
+
removeFailed: "\u30E6\u30FC\u30B6\u30FC\u306E\u524A\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25016
|
+
testMessageFailed: "\u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u9001\u4FE1\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25017
|
+
loadAccountsFailed: "\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25018
|
+
loadUsersFailed: "\u30E6\u30FC\u30B6\u30FC\u4E00\u89A7\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25019
|
+
noAccounts: "\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u307E\u3060\u3042\u308A\u307E\u305B\u3093\u3002\u307E\u305A\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
25020
|
+
userCount: "{{authorized}} \u627F\u8A8D\u6E08\u307F / {{pending}} \u627F\u8A8D\u5F85\u3061\uFF08\u6700\u5927 16\uFF09",
|
|
25021
|
+
userId: "WeChat \u30E6\u30FC\u30B6\u30FC ID",
|
|
25022
|
+
applyTime: "\u521D\u56DE\u9023\u7D61",
|
|
25023
|
+
expand: "\u5C55\u958B",
|
|
25024
|
+
collapse: "\u6298\u308A\u305F\u305F\u3080",
|
|
25025
|
+
accountNameRequired: "\u30A2\u30AB\u30A6\u30F3\u30C8\u540D\u306F\u5FC5\u9808\u3067\u3059",
|
|
25026
|
+
accountNotFound: "\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
25027
|
+
accountNotRunning: "\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u672A\u30ED\u30B0\u30A4\u30F3\u307E\u305F\u306F\u672A\u8D77\u52D5\u3067\u3059",
|
|
25028
|
+
userNotFound: "\u30E6\u30FC\u30B6\u30FC\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",
|
|
25029
|
+
authSuccess: "\u2705 \u8A8D\u8A3C\u3055\u308C\u307E\u3057\u305F\u3002\u901A\u77E5\u3092\u53D7\u3051\u53D6\u308C\u307E\u3059\u3002",
|
|
25030
|
+
authPending: "\u23F3 \u8A8D\u8A3C\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u53D7\u3051\u53D6\u308A\u307E\u3057\u305F\u3002tmex \u306E\u8A2D\u5B9A\u3067\u627F\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
25031
|
+
testMessageTemplate: `\uD83E\uDDEA \u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8
|
|
25032
|
+
\u30B5\u30A4\u30C8\uFF1A{{siteName}}
|
|
25033
|
+
\u6642\u523B\uFF1A{{time}}`,
|
|
25034
|
+
approveMessageTemplate: `\u2705 tmex \u7D4C\u7531\u3067\u8A8D\u8A3C\u3057\u307E\u3057\u305F\u3002
|
|
25035
|
+
\u30A2\u30AB\u30A6\u30F3\u30C8\uFF1A{{accountName}}
|
|
25036
|
+
\u6642\u523B\uFF1A{{time}}`,
|
|
25037
|
+
keepalivePrompt: "\u3010tmex\u3011\u901A\u77E5\u3092\u78BA\u5B9F\u306B\u53D7\u3051\u53D6\u308B\u305F\u3081\u3001\u4EFB\u610F\u306E\u5185\u5BB9\u3092\u8FD4\u4FE1\u3057\u3066\u63A5\u7D9A\u3092\u7DAD\u6301\u3057\u3066\u304F\u3060\u3055\u3044 \uD83D\uDE4F",
|
|
25038
|
+
scanConfirmedSendHint: "\u30B9\u30AD\u30E3\u30F3\u5B8C\u4E86\uFF01WeChat \u3067\u3053\u306E bot \u306B\u4EFB\u610F\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1\u3057\u3066\u7D10\u4ED8\u3051\u3092\u5B8C\u4E86\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
25039
|
+
bindingInProgress: "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D7\u4FE1\u3001\u7D10\u4ED8\u3051\u3092\u5B8C\u4E86\u3057\u3066\u3044\u307E\u3059\u2026",
|
|
25040
|
+
bindSuccess: "WeChat \u3092\u7D10\u4ED8\u3051\u307E\u3057\u305F\u3002\u901A\u77E5\u3092\u53D7\u3051\u53D6\u308C\u307E\u3059\u3002",
|
|
25041
|
+
bound: "\u7D10\u4ED8\u3051\u6E08\u307F",
|
|
25042
|
+
unbound: "\u672A\u7D10\u4ED8\u3051",
|
|
25043
|
+
bindAction: "\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u7D10\u4ED8\u3051",
|
|
25044
|
+
gatewayOnline: "\uD83D\uDFE2 tmex \u30AA\u30F3\u30E9\u30A4\u30F3 @ {{siteName}}"
|
|
25045
|
+
},
|
|
24815
25046
|
webhook: {
|
|
24816
25047
|
title: "Webhooks",
|
|
24817
25048
|
url: "Webhook URL",
|
|
@@ -32855,6 +33086,8 @@ function drizzle(...params) {
|
|
|
32855
33086
|
// ../../apps/gateway/src/db/schema.ts
|
|
32856
33087
|
var exports_schema2 = {};
|
|
32857
33088
|
__export(exports_schema2, {
|
|
33089
|
+
weixinAccounts: () => weixinAccounts,
|
|
33090
|
+
weixinAccountUsers: () => weixinAccountUsers,
|
|
32858
33091
|
webhookEndpoints: () => webhookEndpoints,
|
|
32859
33092
|
watchRules: () => watchRules,
|
|
32860
33093
|
watchRuleState: () => watchRuleState,
|
|
@@ -32887,6 +33120,10 @@ var siteSettings = sqliteTable("site_settings", {
|
|
|
32887
33120
|
enableTelegramNotificationPush: integer("enable_telegram_notification_push", {
|
|
32888
33121
|
mode: "boolean"
|
|
32889
33122
|
}).notNull().default(true),
|
|
33123
|
+
enableWeixinBellPush: integer("enable_weixin_bell_push", { mode: "boolean" }).notNull().default(false),
|
|
33124
|
+
enableWeixinNotificationPush: integer("enable_weixin_notification_push", {
|
|
33125
|
+
mode: "boolean"
|
|
33126
|
+
}).notNull().default(false),
|
|
32890
33127
|
sshReconnectMaxRetries: integer("ssh_reconnect_max_retries").notNull(),
|
|
32891
33128
|
sshReconnectDelaySeconds: integer("ssh_reconnect_delay_seconds").notNull(),
|
|
32892
33129
|
language: text("language").notNull().default("en_US"),
|
|
@@ -33091,6 +33328,34 @@ var telegramBotChats = sqliteTable("telegram_bot_chats", {
|
|
|
33091
33328
|
check("telegram_bot_chats_status_check", sql`${table.status} in ('pending', 'authorized')`),
|
|
33092
33329
|
check("telegram_bot_chats_chat_type_check", sql`${table.chatType} in ('private', 'group', 'supergroup', 'channel', 'unknown')`)
|
|
33093
33330
|
]);
|
|
33331
|
+
var weixinAccounts = sqliteTable("weixin_accounts", {
|
|
33332
|
+
id: text("id").primaryKey(),
|
|
33333
|
+
name: text("name").notNull(),
|
|
33334
|
+
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
33335
|
+
allowAuthRequests: integer("allow_auth_requests", { mode: "boolean" }).notNull().default(true),
|
|
33336
|
+
weixinUin: text("weixin_uin"),
|
|
33337
|
+
botTokenEnc: text("bot_token_enc"),
|
|
33338
|
+
baseUrl: text("base_url"),
|
|
33339
|
+
syncBuf: text("sync_buf"),
|
|
33340
|
+
createdAt: text("created_at").notNull(),
|
|
33341
|
+
updatedAt: text("updated_at").notNull()
|
|
33342
|
+
});
|
|
33343
|
+
var weixinAccountUsers = sqliteTable("weixin_account_users", {
|
|
33344
|
+
id: text("id").primaryKey(),
|
|
33345
|
+
accountId: text("account_id").notNull().references(() => weixinAccounts.id, { onDelete: "cascade" }),
|
|
33346
|
+
userId: text("user_id").notNull(),
|
|
33347
|
+
displayName: text("display_name").notNull(),
|
|
33348
|
+
status: text("status").notNull(),
|
|
33349
|
+
lastContextToken: text("last_context_token"),
|
|
33350
|
+
lastInboundAt: text("last_inbound_at"),
|
|
33351
|
+
needsReactivation: integer("needs_reactivation", { mode: "boolean" }).notNull().default(false),
|
|
33352
|
+
appliedAt: text("applied_at").notNull(),
|
|
33353
|
+
authorizedAt: text("authorized_at"),
|
|
33354
|
+
updatedAt: text("updated_at").notNull()
|
|
33355
|
+
}, (table) => [
|
|
33356
|
+
unique("weixin_account_users_account_user_unique").on(table.accountId, table.userId),
|
|
33357
|
+
check("weixin_account_users_status_check", sql`${table.status} in ('pending', 'authorized')`)
|
|
33358
|
+
]);
|
|
33094
33359
|
|
|
33095
33360
|
// ../../apps/gateway/src/db/client.ts
|
|
33096
33361
|
var sqliteClient = null;
|
|
@@ -33151,6 +33416,8 @@ function toSiteSettings(row) {
|
|
|
33151
33416
|
enableBrowserNotificationToast: row.enableBrowserNotificationToast,
|
|
33152
33417
|
enableTelegramBellPush: row.enableTelegramBellPush,
|
|
33153
33418
|
enableTelegramNotificationPush: row.enableTelegramNotificationPush,
|
|
33419
|
+
enableWeixinBellPush: row.enableWeixinBellPush,
|
|
33420
|
+
enableWeixinNotificationPush: row.enableWeixinNotificationPush,
|
|
33154
33421
|
sshReconnectMaxRetries: row.sshReconnectMaxRetries,
|
|
33155
33422
|
sshReconnectDelaySeconds: row.sshReconnectDelaySeconds,
|
|
33156
33423
|
language: normalizeLocale(row.language),
|
|
@@ -33182,6 +33449,35 @@ function toTelegramChat(row) {
|
|
|
33182
33449
|
updatedAt: row.updatedAt
|
|
33183
33450
|
};
|
|
33184
33451
|
}
|
|
33452
|
+
function toWeixinAccountRecord(row) {
|
|
33453
|
+
return {
|
|
33454
|
+
id: row.id,
|
|
33455
|
+
name: row.name,
|
|
33456
|
+
enabled: row.enabled,
|
|
33457
|
+
allowAuthRequests: row.allowAuthRequests,
|
|
33458
|
+
loggedIn: row.botTokenEnc != null,
|
|
33459
|
+
weixinUin: row.weixinUin ?? null,
|
|
33460
|
+
botTokenEnc: row.botTokenEnc ?? null,
|
|
33461
|
+
baseUrl: row.baseUrl ?? null,
|
|
33462
|
+
syncBuf: row.syncBuf ?? null,
|
|
33463
|
+
createdAt: row.createdAt,
|
|
33464
|
+
updatedAt: row.updatedAt
|
|
33465
|
+
};
|
|
33466
|
+
}
|
|
33467
|
+
function toWeixinAccountUser(row) {
|
|
33468
|
+
return {
|
|
33469
|
+
id: row.id,
|
|
33470
|
+
accountId: row.accountId,
|
|
33471
|
+
userId: row.userId,
|
|
33472
|
+
displayName: row.displayName,
|
|
33473
|
+
status: row.status,
|
|
33474
|
+
needsReactivation: row.needsReactivation,
|
|
33475
|
+
lastInboundAt: row.lastInboundAt ?? null,
|
|
33476
|
+
appliedAt: row.appliedAt,
|
|
33477
|
+
authorizedAt: row.authorizedAt ?? null,
|
|
33478
|
+
updatedAt: row.updatedAt
|
|
33479
|
+
};
|
|
33480
|
+
}
|
|
33185
33481
|
function toWebhookEndpoint(row) {
|
|
33186
33482
|
return {
|
|
33187
33483
|
id: row.id,
|
|
@@ -33206,6 +33502,8 @@ function ensureSiteSettingsInitialized() {
|
|
|
33206
33502
|
enableBrowserNotificationToast: true,
|
|
33207
33503
|
enableTelegramBellPush: true,
|
|
33208
33504
|
enableTelegramNotificationPush: true,
|
|
33505
|
+
enableWeixinBellPush: false,
|
|
33506
|
+
enableWeixinNotificationPush: false,
|
|
33209
33507
|
sshReconnectMaxRetries: config.sshReconnectMaxRetriesDefault,
|
|
33210
33508
|
sshReconnectDelaySeconds: config.sshReconnectDelaySecondsDefault,
|
|
33211
33509
|
language: normalizeLocale(config.languageDefault),
|
|
@@ -33402,6 +33700,8 @@ function updateSiteSettings(updates) {
|
|
|
33402
33700
|
enableBrowserNotificationToast: updates.enableBrowserNotificationToast ?? current.enableBrowserNotificationToast,
|
|
33403
33701
|
enableTelegramBellPush: updates.enableTelegramBellPush ?? current.enableTelegramBellPush,
|
|
33404
33702
|
enableTelegramNotificationPush: updates.enableTelegramNotificationPush ?? current.enableTelegramNotificationPush,
|
|
33703
|
+
enableWeixinBellPush: updates.enableWeixinBellPush ?? current.enableWeixinBellPush,
|
|
33704
|
+
enableWeixinNotificationPush: updates.enableWeixinNotificationPush ?? current.enableWeixinNotificationPush,
|
|
33405
33705
|
sshReconnectMaxRetries: updates.sshReconnectMaxRetries ?? current.sshReconnectMaxRetries,
|
|
33406
33706
|
sshReconnectDelaySeconds: updates.sshReconnectDelaySeconds ?? current.sshReconnectDelaySeconds,
|
|
33407
33707
|
language: updates.language ? normalizeLocale(updates.language) : current.language,
|
|
@@ -33417,6 +33717,8 @@ function updateSiteSettings(updates) {
|
|
|
33417
33717
|
enableBrowserNotificationToast: next.enableBrowserNotificationToast,
|
|
33418
33718
|
enableTelegramBellPush: next.enableTelegramBellPush,
|
|
33419
33719
|
enableTelegramNotificationPush: next.enableTelegramNotificationPush,
|
|
33720
|
+
enableWeixinBellPush: next.enableWeixinBellPush,
|
|
33721
|
+
enableWeixinNotificationPush: next.enableWeixinNotificationPush,
|
|
33420
33722
|
sshReconnectMaxRetries: next.sshReconnectMaxRetries,
|
|
33421
33723
|
sshReconnectDelaySeconds: next.sshReconnectDelaySeconds,
|
|
33422
33724
|
language: next.language,
|
|
@@ -33650,6 +33952,200 @@ function deleteTelegramChat(botId, chatId) {
|
|
|
33650
33952
|
const orm = getDb();
|
|
33651
33953
|
orm.delete(telegramBotChats).where(and(eq(telegramBotChats.botId, botId), eq(telegramBotChats.chatId, chatId))).run();
|
|
33652
33954
|
}
|
|
33955
|
+
var WEIXIN_USER_CAP = 16;
|
|
33956
|
+
function createWeixinAccount(record) {
|
|
33957
|
+
const orm = getDb();
|
|
33958
|
+
orm.insert(weixinAccounts).values({
|
|
33959
|
+
id: record.id,
|
|
33960
|
+
name: record.name,
|
|
33961
|
+
enabled: record.enabled,
|
|
33962
|
+
allowAuthRequests: record.allowAuthRequests,
|
|
33963
|
+
weixinUin: record.weixinUin,
|
|
33964
|
+
botTokenEnc: record.botTokenEnc,
|
|
33965
|
+
baseUrl: record.baseUrl,
|
|
33966
|
+
syncBuf: record.syncBuf,
|
|
33967
|
+
createdAt: record.createdAt,
|
|
33968
|
+
updatedAt: record.updatedAt
|
|
33969
|
+
}).run();
|
|
33970
|
+
}
|
|
33971
|
+
function getWeixinAccountById(accountId) {
|
|
33972
|
+
const orm = getDb();
|
|
33973
|
+
const row = orm.select().from(weixinAccounts).where(eq(weixinAccounts.id, accountId)).get();
|
|
33974
|
+
if (!row) {
|
|
33975
|
+
return null;
|
|
33976
|
+
}
|
|
33977
|
+
return toWeixinAccountRecord(row);
|
|
33978
|
+
}
|
|
33979
|
+
function getAllWeixinAccounts() {
|
|
33980
|
+
const orm = getDb();
|
|
33981
|
+
return orm.select().from(weixinAccounts).orderBy(desc(weixinAccounts.createdAt)).all().map(toWeixinAccountRecord);
|
|
33982
|
+
}
|
|
33983
|
+
function getWeixinAccountsWithStats() {
|
|
33984
|
+
const orm = getDb();
|
|
33985
|
+
const accounts = orm.select().from(weixinAccounts).orderBy(desc(weixinAccounts.createdAt)).all();
|
|
33986
|
+
const counters = new Map;
|
|
33987
|
+
const userRows = orm.select({
|
|
33988
|
+
accountId: weixinAccountUsers.accountId,
|
|
33989
|
+
status: weixinAccountUsers.status,
|
|
33990
|
+
needsReactivation: weixinAccountUsers.needsReactivation
|
|
33991
|
+
}).from(weixinAccountUsers).all();
|
|
33992
|
+
for (const row of userRows) {
|
|
33993
|
+
const current = counters.get(row.accountId) ?? {
|
|
33994
|
+
pending: 0,
|
|
33995
|
+
authorized: 0,
|
|
33996
|
+
needsReactivation: 0
|
|
33997
|
+
};
|
|
33998
|
+
if (row.status === "pending") {
|
|
33999
|
+
current.pending += 1;
|
|
34000
|
+
}
|
|
34001
|
+
if (row.status === "authorized") {
|
|
34002
|
+
current.authorized += 1;
|
|
34003
|
+
if (row.needsReactivation) {
|
|
34004
|
+
current.needsReactivation += 1;
|
|
34005
|
+
}
|
|
34006
|
+
}
|
|
34007
|
+
counters.set(row.accountId, current);
|
|
34008
|
+
}
|
|
34009
|
+
return accounts.map((account) => {
|
|
34010
|
+
const counter = counters.get(account.id) ?? {
|
|
34011
|
+
pending: 0,
|
|
34012
|
+
authorized: 0,
|
|
34013
|
+
needsReactivation: 0
|
|
34014
|
+
};
|
|
34015
|
+
return {
|
|
34016
|
+
id: account.id,
|
|
34017
|
+
name: account.name,
|
|
34018
|
+
enabled: account.enabled,
|
|
34019
|
+
allowAuthRequests: account.allowAuthRequests,
|
|
34020
|
+
loggedIn: account.botTokenEnc != null,
|
|
34021
|
+
createdAt: account.createdAt,
|
|
34022
|
+
updatedAt: account.updatedAt,
|
|
34023
|
+
pendingCount: counter.pending,
|
|
34024
|
+
authorizedCount: counter.authorized,
|
|
34025
|
+
needsReactivationCount: counter.needsReactivation
|
|
34026
|
+
};
|
|
34027
|
+
});
|
|
34028
|
+
}
|
|
34029
|
+
function updateWeixinAccount(accountId, updates) {
|
|
34030
|
+
const orm = getDb();
|
|
34031
|
+
const setValues = {
|
|
34032
|
+
updatedAt: new Date().toISOString()
|
|
34033
|
+
};
|
|
34034
|
+
if (updates.name !== undefined) {
|
|
34035
|
+
setValues.name = updates.name;
|
|
34036
|
+
}
|
|
34037
|
+
if (updates.enabled !== undefined) {
|
|
34038
|
+
setValues.enabled = updates.enabled;
|
|
34039
|
+
}
|
|
34040
|
+
if (updates.allowAuthRequests !== undefined) {
|
|
34041
|
+
setValues.allowAuthRequests = updates.allowAuthRequests;
|
|
34042
|
+
}
|
|
34043
|
+
if (updates.weixinUin !== undefined) {
|
|
34044
|
+
setValues.weixinUin = updates.weixinUin;
|
|
34045
|
+
}
|
|
34046
|
+
if (updates.botTokenEnc !== undefined) {
|
|
34047
|
+
setValues.botTokenEnc = updates.botTokenEnc;
|
|
34048
|
+
}
|
|
34049
|
+
if (updates.baseUrl !== undefined) {
|
|
34050
|
+
setValues.baseUrl = updates.baseUrl;
|
|
34051
|
+
}
|
|
34052
|
+
if (updates.syncBuf !== undefined) {
|
|
34053
|
+
setValues.syncBuf = updates.syncBuf;
|
|
34054
|
+
}
|
|
34055
|
+
orm.update(weixinAccounts).set(setValues).where(eq(weixinAccounts.id, accountId)).run();
|
|
34056
|
+
return getWeixinAccountById(accountId);
|
|
34057
|
+
}
|
|
34058
|
+
function deleteWeixinAccount(accountId) {
|
|
34059
|
+
const orm = getDb();
|
|
34060
|
+
orm.delete(weixinAccounts).where(eq(weixinAccounts.id, accountId)).run();
|
|
34061
|
+
}
|
|
34062
|
+
function getWeixinUserCount(accountId) {
|
|
34063
|
+
const orm = getDb();
|
|
34064
|
+
const row = orm.select({ total: count() }).from(weixinAccountUsers).where(eq(weixinAccountUsers.accountId, accountId)).get();
|
|
34065
|
+
return Number(row?.total ?? 0);
|
|
34066
|
+
}
|
|
34067
|
+
function getWeixinUserByAccountAndUserId(accountId, userId) {
|
|
34068
|
+
const orm = getDb();
|
|
34069
|
+
const row = orm.select().from(weixinAccountUsers).where(and(eq(weixinAccountUsers.accountId, accountId), eq(weixinAccountUsers.userId, userId))).get();
|
|
34070
|
+
if (!row) {
|
|
34071
|
+
return null;
|
|
34072
|
+
}
|
|
34073
|
+
return toWeixinAccountUser(row);
|
|
34074
|
+
}
|
|
34075
|
+
function upsertWeixinUserOnInbound(params) {
|
|
34076
|
+
const existing = getWeixinUserByAccountAndUserId(params.accountId, params.userId);
|
|
34077
|
+
const orm = getDb();
|
|
34078
|
+
if (existing) {
|
|
34079
|
+
const setValues = {
|
|
34080
|
+
displayName: params.displayName,
|
|
34081
|
+
lastInboundAt: params.at,
|
|
34082
|
+
needsReactivation: false,
|
|
34083
|
+
updatedAt: params.at
|
|
34084
|
+
};
|
|
34085
|
+
if (params.contextToken != null) {
|
|
34086
|
+
setValues.lastContextToken = params.contextToken;
|
|
34087
|
+
}
|
|
34088
|
+
orm.update(weixinAccountUsers).set(setValues).where(eq(weixinAccountUsers.id, existing.id)).run();
|
|
34089
|
+
return getWeixinUserByAccountAndUserId(params.accountId, params.userId);
|
|
34090
|
+
}
|
|
34091
|
+
if (!params.allowAuthRequests) {
|
|
34092
|
+
return null;
|
|
34093
|
+
}
|
|
34094
|
+
if (getWeixinUserCount(params.accountId) >= WEIXIN_USER_CAP) {
|
|
34095
|
+
throw new Error(instance.t("apiError.invalidRequest"));
|
|
34096
|
+
}
|
|
34097
|
+
orm.insert(weixinAccountUsers).values({
|
|
34098
|
+
id: crypto.randomUUID(),
|
|
34099
|
+
accountId: params.accountId,
|
|
34100
|
+
userId: params.userId,
|
|
34101
|
+
displayName: params.displayName,
|
|
34102
|
+
status: "pending",
|
|
34103
|
+
lastContextToken: params.contextToken,
|
|
34104
|
+
lastInboundAt: params.at,
|
|
34105
|
+
needsReactivation: false,
|
|
34106
|
+
appliedAt: params.at,
|
|
34107
|
+
authorizedAt: null,
|
|
34108
|
+
updatedAt: params.at
|
|
34109
|
+
}).run();
|
|
34110
|
+
return getWeixinUserByAccountAndUserId(params.accountId, params.userId);
|
|
34111
|
+
}
|
|
34112
|
+
function listWeixinUsersByAccount(accountId) {
|
|
34113
|
+
const orm = getDb();
|
|
34114
|
+
return orm.select().from(weixinAccountUsers).where(eq(weixinAccountUsers.accountId, accountId)).orderBy(desc(weixinAccountUsers.appliedAt)).all().map(toWeixinAccountUser);
|
|
34115
|
+
}
|
|
34116
|
+
function listAuthorizedWeixinUsersByAccount(accountId) {
|
|
34117
|
+
const orm = getDb();
|
|
34118
|
+
return orm.select().from(weixinAccountUsers).where(and(eq(weixinAccountUsers.accountId, accountId), eq(weixinAccountUsers.status, "authorized"))).orderBy(desc(weixinAccountUsers.authorizedAt)).all().map(toWeixinAccountUser);
|
|
34119
|
+
}
|
|
34120
|
+
function getWeixinUserContextTokens(accountId) {
|
|
34121
|
+
const orm = getDb();
|
|
34122
|
+
return orm.select({
|
|
34123
|
+
userId: weixinAccountUsers.userId,
|
|
34124
|
+
contextToken: weixinAccountUsers.lastContextToken
|
|
34125
|
+
}).from(weixinAccountUsers).where(eq(weixinAccountUsers.accountId, accountId)).all().filter((row) => row.contextToken != null);
|
|
34126
|
+
}
|
|
34127
|
+
function approveWeixinUser(accountId, userId) {
|
|
34128
|
+
const existing = getWeixinUserByAccountAndUserId(accountId, userId);
|
|
34129
|
+
if (!existing) {
|
|
34130
|
+
return null;
|
|
34131
|
+
}
|
|
34132
|
+
const now = new Date().toISOString();
|
|
34133
|
+
const orm = getDb();
|
|
34134
|
+
orm.update(weixinAccountUsers).set({
|
|
34135
|
+
status: "authorized",
|
|
34136
|
+
authorizedAt: now,
|
|
34137
|
+
updatedAt: now
|
|
34138
|
+
}).where(eq(weixinAccountUsers.id, existing.id)).run();
|
|
34139
|
+
return getWeixinUserByAccountAndUserId(accountId, userId);
|
|
34140
|
+
}
|
|
34141
|
+
function deleteWeixinUser(accountId, userId) {
|
|
34142
|
+
const orm = getDb();
|
|
34143
|
+
orm.delete(weixinAccountUsers).where(and(eq(weixinAccountUsers.accountId, accountId), eq(weixinAccountUsers.userId, userId))).run();
|
|
34144
|
+
}
|
|
34145
|
+
function setWeixinUserNeedsReactivation(accountId, userId, value) {
|
|
34146
|
+
const orm = getDb();
|
|
34147
|
+
orm.update(weixinAccountUsers).set({ needsReactivation: value, updatedAt: new Date().toISOString() }).where(and(eq(weixinAccountUsers.accountId, accountId), eq(weixinAccountUsers.userId, userId))).run();
|
|
34148
|
+
}
|
|
33653
34149
|
|
|
33654
34150
|
// ../../apps/gateway/src/db/agent.ts
|
|
33655
34151
|
function ensureAgentSettingsInitialized() {
|
|
@@ -87048,13 +87544,7 @@ var _a21;
|
|
|
87048
87544
|
_a21 = symbol21;
|
|
87049
87545
|
var defaultDownload2 = createDownload();
|
|
87050
87546
|
|
|
87051
|
-
// ../../apps/gateway/src/events/
|
|
87052
|
-
function escapeTelegramHtmlText(input) {
|
|
87053
|
-
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
87054
|
-
}
|
|
87055
|
-
function escapeTelegramHtmlAttribute(input) {
|
|
87056
|
-
return escapeTelegramHtmlText(input).replace(/"/g, """);
|
|
87057
|
-
}
|
|
87547
|
+
// ../../apps/gateway/src/events/channels/pane-url.ts
|
|
87058
87548
|
function trimTrailingSlash(url2) {
|
|
87059
87549
|
return url2.endsWith("/") ? url2.slice(0, -1) : url2;
|
|
87060
87550
|
}
|
|
@@ -87083,100 +87573,17 @@ function normalizeHttpUrl(input) {
|
|
|
87083
87573
|
}
|
|
87084
87574
|
}
|
|
87085
87575
|
|
|
87086
|
-
|
|
87087
|
-
|
|
87088
|
-
|
|
87089
|
-
|
|
87090
|
-
|
|
87091
|
-
|
|
87092
|
-
|
|
87093
|
-
|
|
87094
|
-
|
|
87095
|
-
|
|
87096
|
-
this.webhooks = getAllWebhookEndpoints().filter((w) => w.enabled);
|
|
87097
|
-
this.lastRefresh = now2;
|
|
87098
|
-
console.log(`[events] refreshed config: ${this.webhooks.length} webhooks`);
|
|
87099
|
-
}
|
|
87576
|
+
// ../../apps/gateway/src/events/channels/telegram.ts
|
|
87577
|
+
function escapeTelegramHtmlText(input) {
|
|
87578
|
+
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
87579
|
+
}
|
|
87580
|
+
function escapeTelegramHtmlAttribute(input) {
|
|
87581
|
+
return escapeTelegramHtmlText(input).replace(/"/g, """);
|
|
87582
|
+
}
|
|
87583
|
+
|
|
87584
|
+
class TelegramChannel {
|
|
87585
|
+
id = "telegram";
|
|
87100
87586
|
async notify(eventType, event) {
|
|
87101
|
-
this.refreshConfig();
|
|
87102
|
-
const fullEvent = {
|
|
87103
|
-
...event,
|
|
87104
|
-
eventType,
|
|
87105
|
-
timestamp: new Date().toISOString()
|
|
87106
|
-
};
|
|
87107
|
-
if (eventType === "terminal_bell") {
|
|
87108
|
-
if (!this.shouldPassBellThrottle(fullEvent)) {
|
|
87109
|
-
return;
|
|
87110
|
-
}
|
|
87111
|
-
} else if (eventType === "terminal_notification") {
|
|
87112
|
-
if (!this.shouldPassNotificationThrottle(fullEvent)) {
|
|
87113
|
-
return;
|
|
87114
|
-
}
|
|
87115
|
-
}
|
|
87116
|
-
await Promise.all([
|
|
87117
|
-
this.sendWebhooks(eventType, fullEvent),
|
|
87118
|
-
this.sendTelegramNotifications(eventType, fullEvent)
|
|
87119
|
-
]);
|
|
87120
|
-
}
|
|
87121
|
-
shouldPassBellThrottle(event) {
|
|
87122
|
-
const settings = getSiteSettings();
|
|
87123
|
-
const throttleMs = Math.max(0, settings.bellThrottleSeconds) * 1000;
|
|
87124
|
-
if (throttleMs === 0) {
|
|
87125
|
-
return true;
|
|
87126
|
-
}
|
|
87127
|
-
const key = `${event.device.id}:${event.tmux?.paneId ?? "-"}:${event.eventType}`;
|
|
87128
|
-
const now2 = Date.now();
|
|
87129
|
-
const previous = this.bellThrottleMap.get(key) ?? 0;
|
|
87130
|
-
if (now2 - previous < throttleMs) {
|
|
87131
|
-
return false;
|
|
87132
|
-
}
|
|
87133
|
-
this.bellThrottleMap.set(key, now2);
|
|
87134
|
-
return true;
|
|
87135
|
-
}
|
|
87136
|
-
shouldPassNotificationThrottle(event) {
|
|
87137
|
-
const settings = getSiteSettings();
|
|
87138
|
-
const throttleMs = Math.max(0, settings.notificationThrottleSeconds) * 1000;
|
|
87139
|
-
if (throttleMs === 0) {
|
|
87140
|
-
return true;
|
|
87141
|
-
}
|
|
87142
|
-
const source = typeof event.payload?.source === "string" ? event.payload.source : "unknown";
|
|
87143
|
-
const key = `${event.device.id}:${event.tmux?.paneId ?? "-"}:notification:${source}`;
|
|
87144
|
-
const now2 = Date.now();
|
|
87145
|
-
const previous = this.notificationThrottleMap.get(key) ?? 0;
|
|
87146
|
-
if (now2 - previous < throttleMs) {
|
|
87147
|
-
return false;
|
|
87148
|
-
}
|
|
87149
|
-
this.notificationThrottleMap.set(key, now2);
|
|
87150
|
-
return true;
|
|
87151
|
-
}
|
|
87152
|
-
async sendWebhooks(eventType, event) {
|
|
87153
|
-
const targets = this.webhooks.filter((w) => w.eventMask.includes(eventType));
|
|
87154
|
-
await Promise.all(targets.map(async (webhook) => {
|
|
87155
|
-
try {
|
|
87156
|
-
await this.sendWebhook(webhook, event);
|
|
87157
|
-
} catch (err) {
|
|
87158
|
-
console.error(`[webhook] failed to send to ${webhook.url}:`, err);
|
|
87159
|
-
}
|
|
87160
|
-
}));
|
|
87161
|
-
}
|
|
87162
|
-
async sendWebhook(webhook, event) {
|
|
87163
|
-
const body = JSON.stringify(event);
|
|
87164
|
-
const signature = await this.generateHmac(webhook.secret, body);
|
|
87165
|
-
const response = await fetch(webhook.url, {
|
|
87166
|
-
method: "POST",
|
|
87167
|
-
headers: {
|
|
87168
|
-
"Content-Type": "application/json",
|
|
87169
|
-
"X-Tmex-Signature": `sha256=${signature}`,
|
|
87170
|
-
"X-Tmex-Event": event.eventType,
|
|
87171
|
-
"X-Tmex-Timestamp": event.timestamp
|
|
87172
|
-
},
|
|
87173
|
-
body
|
|
87174
|
-
});
|
|
87175
|
-
if (!response.ok) {
|
|
87176
|
-
console.error(`[webhook] ${webhook.url} returned ${response.status}`);
|
|
87177
|
-
}
|
|
87178
|
-
}
|
|
87179
|
-
async sendTelegramNotifications(eventType, event) {
|
|
87180
87587
|
const settings = getSiteSettings();
|
|
87181
87588
|
if (eventType === "terminal_bell") {
|
|
87182
87589
|
if (!settings.enableTelegramBellPush) {
|
|
@@ -87290,6 +87697,51 @@ class EventNotifier {
|
|
|
87290
87697
|
return lines.join(`
|
|
87291
87698
|
`);
|
|
87292
87699
|
}
|
|
87700
|
+
}
|
|
87701
|
+
var telegramChannel = new TelegramChannel;
|
|
87702
|
+
|
|
87703
|
+
// ../../apps/gateway/src/events/channels/webhook.ts
|
|
87704
|
+
class WebhookChannel {
|
|
87705
|
+
id = "webhook";
|
|
87706
|
+
webhooks = [];
|
|
87707
|
+
lastRefresh = 0;
|
|
87708
|
+
REFRESH_INTERVAL = 60000;
|
|
87709
|
+
refreshConfig() {
|
|
87710
|
+
const now2 = Date.now();
|
|
87711
|
+
if (now2 - this.lastRefresh < this.REFRESH_INTERVAL)
|
|
87712
|
+
return;
|
|
87713
|
+
this.webhooks = getAllWebhookEndpoints().filter((w) => w.enabled);
|
|
87714
|
+
this.lastRefresh = now2;
|
|
87715
|
+
console.log(`[events] refreshed config: ${this.webhooks.length} webhooks`);
|
|
87716
|
+
}
|
|
87717
|
+
async notify(eventType, event) {
|
|
87718
|
+
this.refreshConfig();
|
|
87719
|
+
const targets = this.webhooks.filter((w) => w.eventMask.includes(eventType));
|
|
87720
|
+
await Promise.all(targets.map(async (webhook) => {
|
|
87721
|
+
try {
|
|
87722
|
+
await this.sendWebhook(webhook, event);
|
|
87723
|
+
} catch (err) {
|
|
87724
|
+
console.error(`[webhook] failed to send to ${webhook.url}:`, err);
|
|
87725
|
+
}
|
|
87726
|
+
}));
|
|
87727
|
+
}
|
|
87728
|
+
async sendWebhook(webhook, event) {
|
|
87729
|
+
const body = JSON.stringify(event);
|
|
87730
|
+
const signature = await this.generateHmac(webhook.secret, body);
|
|
87731
|
+
const response = await fetch(webhook.url, {
|
|
87732
|
+
method: "POST",
|
|
87733
|
+
headers: {
|
|
87734
|
+
"Content-Type": "application/json",
|
|
87735
|
+
"X-Tmex-Signature": `sha256=${signature}`,
|
|
87736
|
+
"X-Tmex-Event": event.eventType,
|
|
87737
|
+
"X-Tmex-Timestamp": event.timestamp
|
|
87738
|
+
},
|
|
87739
|
+
body
|
|
87740
|
+
});
|
|
87741
|
+
if (!response.ok) {
|
|
87742
|
+
console.error(`[webhook] ${webhook.url} returned ${response.status}`);
|
|
87743
|
+
}
|
|
87744
|
+
}
|
|
87293
87745
|
async generateHmac(secret, message) {
|
|
87294
87746
|
const encoder2 = new TextEncoder;
|
|
87295
87747
|
const key = await crypto.subtle.importKey("raw", encoder2.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
@@ -87297,6 +87749,897 @@ class EventNotifier {
|
|
|
87297
87749
|
return Buffer.from(signature).toString("hex");
|
|
87298
87750
|
}
|
|
87299
87751
|
}
|
|
87752
|
+
var webhookChannel = new WebhookChannel;
|
|
87753
|
+
|
|
87754
|
+
// ../../apps/gateway/src/weixin/ilink/types.ts
|
|
87755
|
+
var ILINK_LOGIN_HOST = "https://ilinkai.weixin.qq.com";
|
|
87756
|
+
var ILINK_BOT_TYPE = 3;
|
|
87757
|
+
var CHANNEL_VERSION = "1.0.3";
|
|
87758
|
+
var CLIENT_ID_PREFIX = "openclaw-weixin-";
|
|
87759
|
+
var MESSAGE_TYPE_BOT = 2;
|
|
87760
|
+
var MESSAGE_STATE_FINISH = 2;
|
|
87761
|
+
var ITEM_TYPE_TEXT = 1;
|
|
87762
|
+
var SESSION_EXPIRED_ERRCODE = -14;
|
|
87763
|
+
|
|
87764
|
+
// ../../apps/gateway/src/weixin/ilink/api.ts
|
|
87765
|
+
function resolveFetch(fetchImpl) {
|
|
87766
|
+
return fetchImpl ?? globalThis.fetch;
|
|
87767
|
+
}
|
|
87768
|
+
function trimTrailingSlash2(url2) {
|
|
87769
|
+
return url2.endsWith("/") ? url2.slice(0, -1) : url2;
|
|
87770
|
+
}
|
|
87771
|
+
function generateWechatUin() {
|
|
87772
|
+
const buf = new Uint32Array(1);
|
|
87773
|
+
crypto.getRandomValues(buf);
|
|
87774
|
+
const decimal = String(buf[0]);
|
|
87775
|
+
return Buffer.from(decimal, "utf8").toString("base64");
|
|
87776
|
+
}
|
|
87777
|
+
function buildAuthHeaders(botToken) {
|
|
87778
|
+
const headers = {
|
|
87779
|
+
"Content-Type": "application/json",
|
|
87780
|
+
AuthorizationType: "ilink_bot_token",
|
|
87781
|
+
"X-WECHAT-UIN": generateWechatUin()
|
|
87782
|
+
};
|
|
87783
|
+
const token = botToken?.trim();
|
|
87784
|
+
if (token) {
|
|
87785
|
+
headers.Authorization = `Bearer ${token}`;
|
|
87786
|
+
}
|
|
87787
|
+
return headers;
|
|
87788
|
+
}
|
|
87789
|
+
function buildBaseInfo() {
|
|
87790
|
+
return { channel_version: CHANNEL_VERSION };
|
|
87791
|
+
}
|
|
87792
|
+
async function readJson(resp) {
|
|
87793
|
+
if (!resp.ok) {
|
|
87794
|
+
const body = await resp.text().catch(() => "");
|
|
87795
|
+
throw new Error(`iLink HTTP ${resp.status} ${resp.statusText}${body ? `: ${body.slice(0, 200)}` : ""}`);
|
|
87796
|
+
}
|
|
87797
|
+
const text3 = await resp.text();
|
|
87798
|
+
if (!text3) {
|
|
87799
|
+
return {};
|
|
87800
|
+
}
|
|
87801
|
+
return JSON.parse(text3);
|
|
87802
|
+
}
|
|
87803
|
+
async function getBotQrcode(opts = {}) {
|
|
87804
|
+
const host = trimTrailingSlash2(opts.host ?? ILINK_LOGIN_HOST);
|
|
87805
|
+
const botType = opts.botType ?? ILINK_BOT_TYPE;
|
|
87806
|
+
const url2 = `${host}/ilink/bot/get_bot_qrcode?bot_type=${botType}`;
|
|
87807
|
+
const resp = await resolveFetch(opts.fetchImpl)(url2, {
|
|
87808
|
+
method: "GET",
|
|
87809
|
+
headers: buildAuthHeaders(),
|
|
87810
|
+
signal: opts.signal
|
|
87811
|
+
});
|
|
87812
|
+
return readJson(resp);
|
|
87813
|
+
}
|
|
87814
|
+
async function getQrcodeStatus(qrcode, opts = {}) {
|
|
87815
|
+
const host = trimTrailingSlash2(opts.host ?? ILINK_LOGIN_HOST);
|
|
87816
|
+
const url2 = `${host}/ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(qrcode)}`;
|
|
87817
|
+
const resp = await resolveFetch(opts.fetchImpl)(url2, {
|
|
87818
|
+
method: "GET",
|
|
87819
|
+
headers: buildAuthHeaders(),
|
|
87820
|
+
signal: opts.signal
|
|
87821
|
+
});
|
|
87822
|
+
return readJson(resp);
|
|
87823
|
+
}
|
|
87824
|
+
async function getUpdates(opts) {
|
|
87825
|
+
const baseUrl = trimTrailingSlash2(opts.baseUrl);
|
|
87826
|
+
const url2 = `${baseUrl}/ilink/bot/getupdates`;
|
|
87827
|
+
const body = {
|
|
87828
|
+
get_updates_buf: opts.getUpdatesBuf ?? "",
|
|
87829
|
+
base_info: buildBaseInfo()
|
|
87830
|
+
};
|
|
87831
|
+
const resp = await resolveFetch(opts.fetchImpl)(url2, {
|
|
87832
|
+
method: "POST",
|
|
87833
|
+
headers: buildAuthHeaders(opts.botToken),
|
|
87834
|
+
body: JSON.stringify(body),
|
|
87835
|
+
signal: opts.signal
|
|
87836
|
+
});
|
|
87837
|
+
return readJson(resp);
|
|
87838
|
+
}
|
|
87839
|
+
async function sendMessage(opts) {
|
|
87840
|
+
const baseUrl = trimTrailingSlash2(opts.baseUrl);
|
|
87841
|
+
const url2 = `${baseUrl}/ilink/bot/sendmessage`;
|
|
87842
|
+
const itemList = opts.items.map((item) => ({
|
|
87843
|
+
type: ITEM_TYPE_TEXT,
|
|
87844
|
+
text_item: { text: item.text }
|
|
87845
|
+
}));
|
|
87846
|
+
const req = {
|
|
87847
|
+
msg: {
|
|
87848
|
+
to_user_id: opts.toUserId,
|
|
87849
|
+
message_type: MESSAGE_TYPE_BOT,
|
|
87850
|
+
message_state: MESSAGE_STATE_FINISH,
|
|
87851
|
+
context_token: opts.contextToken,
|
|
87852
|
+
client_id: opts.clientId,
|
|
87853
|
+
item_list: itemList
|
|
87854
|
+
},
|
|
87855
|
+
base_info: buildBaseInfo()
|
|
87856
|
+
};
|
|
87857
|
+
const resp = await resolveFetch(opts.fetchImpl)(url2, {
|
|
87858
|
+
method: "POST",
|
|
87859
|
+
headers: buildAuthHeaders(opts.botToken),
|
|
87860
|
+
body: JSON.stringify(req),
|
|
87861
|
+
signal: opts.signal
|
|
87862
|
+
});
|
|
87863
|
+
return readJson(resp);
|
|
87864
|
+
}
|
|
87865
|
+
|
|
87866
|
+
// ../../apps/gateway/src/weixin/ilink/client.ts
|
|
87867
|
+
class WeixinNoContextTokenError extends Error {
|
|
87868
|
+
constructor(toUserId) {
|
|
87869
|
+
super(`No context_token for user ${toUserId}. Receive a message from them first.`);
|
|
87870
|
+
this.name = "WeixinNoContextTokenError";
|
|
87871
|
+
}
|
|
87872
|
+
}
|
|
87873
|
+
|
|
87874
|
+
class WeixinSessionExpiredError extends Error {
|
|
87875
|
+
constructor() {
|
|
87876
|
+
super("iLink bot session expired; re-login required.");
|
|
87877
|
+
this.name = "WeixinSessionExpiredError";
|
|
87878
|
+
}
|
|
87879
|
+
}
|
|
87880
|
+
var DEFAULT_LOGIN_TIMEOUT_MS = 480000;
|
|
87881
|
+
var DEFAULT_QRCODE_POLL_INTERVAL_MS = 1000;
|
|
87882
|
+
var MAX_QRCODE_REFRESH = 3;
|
|
87883
|
+
var RETRY_DELAY_MS = 2000;
|
|
87884
|
+
var BACKOFF_DELAY_MS = 30000;
|
|
87885
|
+
var DEFAULT_LONGPOLL_TIMEOUT_MS = 60000;
|
|
87886
|
+
var LONGPOLL_TIMEOUT_MARGIN_MS = 1e4;
|
|
87887
|
+
function generateClientId() {
|
|
87888
|
+
const bytes = new Uint8Array(16);
|
|
87889
|
+
crypto.getRandomValues(bytes);
|
|
87890
|
+
const hex3 = Array.from(bytes, (b3) => b3.toString(16).padStart(2, "0")).join("");
|
|
87891
|
+
return `${CLIENT_ID_PREFIX}${hex3}`;
|
|
87892
|
+
}
|
|
87893
|
+
|
|
87894
|
+
class AbortError extends Error {
|
|
87895
|
+
constructor() {
|
|
87896
|
+
super("aborted");
|
|
87897
|
+
this.name = "AbortError";
|
|
87898
|
+
}
|
|
87899
|
+
}
|
|
87900
|
+
function sleep3(ms, signal) {
|
|
87901
|
+
return new Promise((resolve3, reject) => {
|
|
87902
|
+
if (signal?.aborted) {
|
|
87903
|
+
reject(new AbortError);
|
|
87904
|
+
return;
|
|
87905
|
+
}
|
|
87906
|
+
const timer = setTimeout(() => {
|
|
87907
|
+
signal?.removeEventListener("abort", onAbort);
|
|
87908
|
+
resolve3();
|
|
87909
|
+
}, ms);
|
|
87910
|
+
const onAbort = () => {
|
|
87911
|
+
clearTimeout(timer);
|
|
87912
|
+
reject(new AbortError);
|
|
87913
|
+
};
|
|
87914
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
87915
|
+
});
|
|
87916
|
+
}
|
|
87917
|
+
function isAbort(err, signal) {
|
|
87918
|
+
if (signal?.aborted)
|
|
87919
|
+
return true;
|
|
87920
|
+
if (err instanceof AbortError)
|
|
87921
|
+
return true;
|
|
87922
|
+
if (err instanceof DOMException && err.name === "AbortError")
|
|
87923
|
+
return true;
|
|
87924
|
+
if (err instanceof Error && err.name === "AbortError")
|
|
87925
|
+
return true;
|
|
87926
|
+
return false;
|
|
87927
|
+
}
|
|
87928
|
+
function isSessionExpired(resp) {
|
|
87929
|
+
return resp.ret === SESSION_EXPIRED_ERRCODE || resp.errcode === SESSION_EXPIRED_ERRCODE;
|
|
87930
|
+
}
|
|
87931
|
+
|
|
87932
|
+
class WeixinClient {
|
|
87933
|
+
creds = null;
|
|
87934
|
+
fetchImpl;
|
|
87935
|
+
contextTokens = new Map;
|
|
87936
|
+
running = false;
|
|
87937
|
+
internalAbort = null;
|
|
87938
|
+
constructor(opts = {}) {
|
|
87939
|
+
this.fetchImpl = opts.fetchImpl;
|
|
87940
|
+
if (opts.accountId && opts.botToken && opts.baseUrl) {
|
|
87941
|
+
this.creds = {
|
|
87942
|
+
accountId: opts.accountId,
|
|
87943
|
+
botToken: opts.botToken,
|
|
87944
|
+
baseUrl: opts.baseUrl
|
|
87945
|
+
};
|
|
87946
|
+
}
|
|
87947
|
+
}
|
|
87948
|
+
get credentials() {
|
|
87949
|
+
return this.creds;
|
|
87950
|
+
}
|
|
87951
|
+
getContextToken(userId) {
|
|
87952
|
+
return this.contextTokens.get(userId);
|
|
87953
|
+
}
|
|
87954
|
+
setContextToken(userId, token) {
|
|
87955
|
+
this.contextTokens.set(userId, token);
|
|
87956
|
+
}
|
|
87957
|
+
isRunning() {
|
|
87958
|
+
return this.running;
|
|
87959
|
+
}
|
|
87960
|
+
static extractText(raw) {
|
|
87961
|
+
const msg = raw;
|
|
87962
|
+
if (!msg || typeof msg !== "object" || !Array.isArray(msg.item_list)) {
|
|
87963
|
+
return "";
|
|
87964
|
+
}
|
|
87965
|
+
const parts = [];
|
|
87966
|
+
for (const item of msg.item_list) {
|
|
87967
|
+
if (item?.type === ITEM_TYPE_TEXT && typeof item.text_item?.text === "string") {
|
|
87968
|
+
parts.push(item.text_item.text);
|
|
87969
|
+
}
|
|
87970
|
+
}
|
|
87971
|
+
return parts.join("");
|
|
87972
|
+
}
|
|
87973
|
+
async login(opts) {
|
|
87974
|
+
const { onQrcode, signal } = opts;
|
|
87975
|
+
const timeoutMs = Math.max(1000, opts.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS);
|
|
87976
|
+
const pollIntervalMs = opts.pollIntervalMs ?? DEFAULT_QRCODE_POLL_INTERVAL_MS;
|
|
87977
|
+
const deadline = Date.now() + timeoutMs;
|
|
87978
|
+
let qrcodeId = await this.fetchQrcode(onQrcode, signal);
|
|
87979
|
+
let refreshes = 0;
|
|
87980
|
+
while (Date.now() < deadline) {
|
|
87981
|
+
if (signal?.aborted)
|
|
87982
|
+
throw new AbortError;
|
|
87983
|
+
await sleep3(pollIntervalMs, signal);
|
|
87984
|
+
let status;
|
|
87985
|
+
try {
|
|
87986
|
+
status = await getQrcodeStatus(qrcodeId, {
|
|
87987
|
+
fetchImpl: this.fetchImpl,
|
|
87988
|
+
signal
|
|
87989
|
+
});
|
|
87990
|
+
} catch (err) {
|
|
87991
|
+
if (isAbort(err, signal))
|
|
87992
|
+
throw err;
|
|
87993
|
+
continue;
|
|
87994
|
+
}
|
|
87995
|
+
switch (status.status) {
|
|
87996
|
+
case "confirmed": {
|
|
87997
|
+
if (!status.bot_token || !status.baseurl) {
|
|
87998
|
+
throw new Error("iLink login confirmed but bot_token/baseurl missing.");
|
|
87999
|
+
}
|
|
88000
|
+
const accountId = status.ilink_bot_id ?? status.ilink_user_id ?? status.bot_token;
|
|
88001
|
+
this.creds = {
|
|
88002
|
+
accountId,
|
|
88003
|
+
botToken: status.bot_token,
|
|
88004
|
+
baseUrl: status.baseurl
|
|
88005
|
+
};
|
|
88006
|
+
return this.creds;
|
|
88007
|
+
}
|
|
88008
|
+
case "expired": {
|
|
88009
|
+
if (refreshes >= MAX_QRCODE_REFRESH) {
|
|
88010
|
+
throw new Error("iLink qrcode expired and refresh limit reached.");
|
|
88011
|
+
}
|
|
88012
|
+
refreshes += 1;
|
|
88013
|
+
qrcodeId = await this.fetchQrcode(onQrcode, signal);
|
|
88014
|
+
break;
|
|
88015
|
+
}
|
|
88016
|
+
default:
|
|
88017
|
+
break;
|
|
88018
|
+
}
|
|
88019
|
+
}
|
|
88020
|
+
throw new Error("iLink login timed out.");
|
|
88021
|
+
}
|
|
88022
|
+
async fetchQrcode(onQrcode, signal) {
|
|
88023
|
+
const resp = await getBotQrcode({ fetchImpl: this.fetchImpl, signal });
|
|
88024
|
+
if (!resp.qrcode) {
|
|
88025
|
+
throw new Error("iLink get_bot_qrcode returned no qrcode.");
|
|
88026
|
+
}
|
|
88027
|
+
if (!resp.qrcode_img_content) {
|
|
88028
|
+
throw new Error("iLink get_bot_qrcode returned no qrcode content.");
|
|
88029
|
+
}
|
|
88030
|
+
onQrcode({
|
|
88031
|
+
url: resp.qrcode_img_content,
|
|
88032
|
+
qrcodeId: resp.qrcode
|
|
88033
|
+
});
|
|
88034
|
+
return resp.qrcode;
|
|
88035
|
+
}
|
|
88036
|
+
async start(opts = {}) {
|
|
88037
|
+
if (!this.creds) {
|
|
88038
|
+
throw new Error("WeixinClient.start called without credentials; login first.");
|
|
88039
|
+
}
|
|
88040
|
+
if (this.running) {
|
|
88041
|
+
throw new Error("WeixinClient already running.");
|
|
88042
|
+
}
|
|
88043
|
+
if (opts.initialContextTokens) {
|
|
88044
|
+
for (const [userId, token] of Object.entries(opts.initialContextTokens)) {
|
|
88045
|
+
this.contextTokens.set(userId, token);
|
|
88046
|
+
}
|
|
88047
|
+
}
|
|
88048
|
+
this.internalAbort = new AbortController;
|
|
88049
|
+
const signal = this.linkSignals(opts.signal, this.internalAbort.signal);
|
|
88050
|
+
this.running = true;
|
|
88051
|
+
let getUpdatesBuf = await opts.loadSyncBuf?.() ?? "";
|
|
88052
|
+
let failures = 0;
|
|
88053
|
+
let longpollTimeoutMs = opts.longpollTimeoutMs ?? DEFAULT_LONGPOLL_TIMEOUT_MS;
|
|
88054
|
+
try {
|
|
88055
|
+
while (!signal.aborted) {
|
|
88056
|
+
let resp;
|
|
88057
|
+
try {
|
|
88058
|
+
const perRequestSignal = AbortSignal.any([
|
|
88059
|
+
signal,
|
|
88060
|
+
AbortSignal.timeout(longpollTimeoutMs)
|
|
88061
|
+
]);
|
|
88062
|
+
resp = await getUpdates({
|
|
88063
|
+
baseUrl: this.creds.baseUrl,
|
|
88064
|
+
botToken: this.creds.botToken,
|
|
88065
|
+
getUpdatesBuf,
|
|
88066
|
+
fetchImpl: this.fetchImpl,
|
|
88067
|
+
signal: perRequestSignal
|
|
88068
|
+
});
|
|
88069
|
+
} catch (err) {
|
|
88070
|
+
if (signal.aborted)
|
|
88071
|
+
break;
|
|
88072
|
+
opts.onError?.(err);
|
|
88073
|
+
failures += 1;
|
|
88074
|
+
await this.backoffSleep(failures, signal);
|
|
88075
|
+
continue;
|
|
88076
|
+
}
|
|
88077
|
+
if (isSessionExpired(resp)) {
|
|
88078
|
+
opts.onSessionExpired?.();
|
|
88079
|
+
throw new WeixinSessionExpiredError;
|
|
88080
|
+
}
|
|
88081
|
+
if (typeof resp.ret === "number" && resp.ret !== 0) {
|
|
88082
|
+
opts.onError?.(new Error(`getupdates ret=${resp.ret} errmsg=${resp.errmsg ?? ""}`));
|
|
88083
|
+
failures += 1;
|
|
88084
|
+
await this.backoffSleep(failures, signal);
|
|
88085
|
+
continue;
|
|
88086
|
+
}
|
|
88087
|
+
failures = 0;
|
|
88088
|
+
if (typeof resp.longpolling_timeout_ms === "number" && resp.longpolling_timeout_ms > 0) {
|
|
88089
|
+
longpollTimeoutMs = resp.longpolling_timeout_ms + LONGPOLL_TIMEOUT_MARGIN_MS;
|
|
88090
|
+
}
|
|
88091
|
+
const msgs = resp.msgs ?? [];
|
|
88092
|
+
for (const msg of msgs) {
|
|
88093
|
+
if (signal.aborted)
|
|
88094
|
+
break;
|
|
88095
|
+
const inbound = this.toInbound(msg);
|
|
88096
|
+
if (msg.from_user_id && msg.context_token) {
|
|
88097
|
+
this.contextTokens.set(msg.from_user_id, msg.context_token);
|
|
88098
|
+
}
|
|
88099
|
+
try {
|
|
88100
|
+
await opts.onMessage?.(inbound);
|
|
88101
|
+
} catch (err) {
|
|
88102
|
+
opts.onError?.(err);
|
|
88103
|
+
}
|
|
88104
|
+
}
|
|
88105
|
+
if (resp.get_updates_buf != null && resp.get_updates_buf !== "") {
|
|
88106
|
+
getUpdatesBuf = resp.get_updates_buf;
|
|
88107
|
+
await opts.saveSyncBuf?.(getUpdatesBuf);
|
|
88108
|
+
}
|
|
88109
|
+
}
|
|
88110
|
+
} finally {
|
|
88111
|
+
this.running = false;
|
|
88112
|
+
this.internalAbort = null;
|
|
88113
|
+
}
|
|
88114
|
+
}
|
|
88115
|
+
stop() {
|
|
88116
|
+
this.internalAbort?.abort();
|
|
88117
|
+
}
|
|
88118
|
+
async sendText(toUserId, text3, contextToken) {
|
|
88119
|
+
if (!this.creds) {
|
|
88120
|
+
throw new Error("WeixinClient.sendText called without credentials; login first.");
|
|
88121
|
+
}
|
|
88122
|
+
const token = contextToken ?? this.contextTokens.get(toUserId);
|
|
88123
|
+
if (!token) {
|
|
88124
|
+
throw new WeixinNoContextTokenError(toUserId);
|
|
88125
|
+
}
|
|
88126
|
+
const resp = await sendMessage({
|
|
88127
|
+
baseUrl: this.creds.baseUrl,
|
|
88128
|
+
botToken: this.creds.botToken,
|
|
88129
|
+
toUserId,
|
|
88130
|
+
contextToken: token,
|
|
88131
|
+
clientId: generateClientId(),
|
|
88132
|
+
items: [{ text: text3 }],
|
|
88133
|
+
fetchImpl: this.fetchImpl
|
|
88134
|
+
});
|
|
88135
|
+
if (resp.ret === SESSION_EXPIRED_ERRCODE || resp.errcode === SESSION_EXPIRED_ERRCODE) {
|
|
88136
|
+
throw new WeixinSessionExpiredError;
|
|
88137
|
+
}
|
|
88138
|
+
if (typeof resp.ret === "number" && resp.ret !== 0) {
|
|
88139
|
+
throw new Error(`sendmessage ret=${resp.ret} errmsg=${resp.errmsg ?? ""}`);
|
|
88140
|
+
}
|
|
88141
|
+
}
|
|
88142
|
+
toInbound(msg) {
|
|
88143
|
+
return {
|
|
88144
|
+
fromUserId: msg.from_user_id ?? "",
|
|
88145
|
+
contextToken: msg.context_token ?? null,
|
|
88146
|
+
text: WeixinClient.extractText(msg),
|
|
88147
|
+
raw: msg
|
|
88148
|
+
};
|
|
88149
|
+
}
|
|
88150
|
+
async backoffSleep(failures, signal) {
|
|
88151
|
+
const delay2 = Math.min(RETRY_DELAY_MS * 2 ** Math.max(0, failures - 1), BACKOFF_DELAY_MS);
|
|
88152
|
+
try {
|
|
88153
|
+
await sleep3(delay2, signal);
|
|
88154
|
+
} catch {}
|
|
88155
|
+
}
|
|
88156
|
+
linkSignals(external3, internal) {
|
|
88157
|
+
if (!external3)
|
|
88158
|
+
return internal;
|
|
88159
|
+
const controller = new AbortController;
|
|
88160
|
+
const onAbort = () => controller.abort();
|
|
88161
|
+
if (external3.aborted || internal.aborted) {
|
|
88162
|
+
controller.abort();
|
|
88163
|
+
return controller.signal;
|
|
88164
|
+
}
|
|
88165
|
+
external3.addEventListener("abort", onAbort, { once: true });
|
|
88166
|
+
internal.addEventListener("abort", onAbort, { once: true });
|
|
88167
|
+
return controller.signal;
|
|
88168
|
+
}
|
|
88169
|
+
}
|
|
88170
|
+
|
|
88171
|
+
// ../../apps/gateway/src/weixin/service.ts
|
|
88172
|
+
var KEEPALIVE_INTERVAL_MS = 8 * 60 * 60 * 1000;
|
|
88173
|
+
var KEEPALIVE_SWEEP_MS = 30 * 60 * 1000;
|
|
88174
|
+
|
|
88175
|
+
class WeixinService {
|
|
88176
|
+
createClient;
|
|
88177
|
+
runningAccounts = new Map;
|
|
88178
|
+
loginSessions = new Map;
|
|
88179
|
+
sessionExpiredNotified = new Set;
|
|
88180
|
+
refreshChain = Promise.resolve();
|
|
88181
|
+
keepaliveTimer = null;
|
|
88182
|
+
lastKeepaliveAt = new Map;
|
|
88183
|
+
constructor(createClient = (opts) => new WeixinClient(opts)) {
|
|
88184
|
+
this.createClient = createClient;
|
|
88185
|
+
}
|
|
88186
|
+
async refresh() {
|
|
88187
|
+
const run = this.refreshChain.then(() => this.doRefresh());
|
|
88188
|
+
this.refreshChain = run.catch(() => {});
|
|
88189
|
+
return run;
|
|
88190
|
+
}
|
|
88191
|
+
async doRefresh() {
|
|
88192
|
+
this.ensureKeepaliveTimer();
|
|
88193
|
+
const accounts = getAllWeixinAccounts();
|
|
88194
|
+
const activeIds = new Set(accounts.map((a) => a.id));
|
|
88195
|
+
for (const [id, session] of this.loginSessions) {
|
|
88196
|
+
if (!activeIds.has(id)) {
|
|
88197
|
+
session.abort.abort();
|
|
88198
|
+
this.loginSessions.delete(id);
|
|
88199
|
+
}
|
|
88200
|
+
}
|
|
88201
|
+
const toStop = [];
|
|
88202
|
+
for (const [id] of this.runningAccounts) {
|
|
88203
|
+
if (!activeIds.has(id)) {
|
|
88204
|
+
toStop.push(id);
|
|
88205
|
+
}
|
|
88206
|
+
}
|
|
88207
|
+
await Promise.all(toStop.map((id) => this.stopAccount(id)));
|
|
88208
|
+
for (const account of accounts) {
|
|
88209
|
+
const shouldRun = account.enabled && account.botTokenEnc != null && account.baseUrl != null;
|
|
88210
|
+
if (!shouldRun) {
|
|
88211
|
+
await this.stopAccount(account.id);
|
|
88212
|
+
continue;
|
|
88213
|
+
}
|
|
88214
|
+
let botToken;
|
|
88215
|
+
try {
|
|
88216
|
+
botToken = await decryptWithContext(account.botTokenEnc, {
|
|
88217
|
+
scope: "weixin_account",
|
|
88218
|
+
entityId: account.id,
|
|
88219
|
+
field: "bot_token_enc"
|
|
88220
|
+
});
|
|
88221
|
+
} catch (err) {
|
|
88222
|
+
console.error(`[weixin] failed to decrypt token for ${account.id}:`, err);
|
|
88223
|
+
continue;
|
|
88224
|
+
}
|
|
88225
|
+
const running = this.runningAccounts.get(account.id);
|
|
88226
|
+
if (running && running.botToken === botToken) {
|
|
88227
|
+
continue;
|
|
88228
|
+
}
|
|
88229
|
+
if (running) {
|
|
88230
|
+
await this.stopAccount(account.id);
|
|
88231
|
+
}
|
|
88232
|
+
this.startAccount(account, botToken);
|
|
88233
|
+
}
|
|
88234
|
+
}
|
|
88235
|
+
startAccount(account, botToken) {
|
|
88236
|
+
const baseUrl = account.baseUrl ?? "";
|
|
88237
|
+
const weixinUin = account.weixinUin ?? account.id;
|
|
88238
|
+
const client = this.createClient({ accountId: weixinUin, botToken, baseUrl });
|
|
88239
|
+
for (const { userId, contextToken } of getWeixinUserContextTokens(account.id)) {
|
|
88240
|
+
client.setContextToken(userId, contextToken);
|
|
88241
|
+
}
|
|
88242
|
+
this.runningAccounts.set(account.id, { id: account.id, botToken, client });
|
|
88243
|
+
this.sessionExpiredNotified.delete(account.id);
|
|
88244
|
+
client.start({
|
|
88245
|
+
loadSyncBuf: () => getWeixinAccountById(account.id)?.syncBuf ?? undefined,
|
|
88246
|
+
saveSyncBuf: (buf) => {
|
|
88247
|
+
updateWeixinAccount(account.id, { syncBuf: buf });
|
|
88248
|
+
},
|
|
88249
|
+
onMessage: (msg) => this.handleInbound(account.id, msg),
|
|
88250
|
+
onSessionExpired: () => this.handleSessionExpired(account.id),
|
|
88251
|
+
onError: (err) => console.error(`[weixin] account ${account.id} poll error:`, err)
|
|
88252
|
+
}).catch((err) => {
|
|
88253
|
+
if (!(err instanceof WeixinSessionExpiredError)) {
|
|
88254
|
+
console.error(`[weixin] account ${account.id} loop crashed:`, err);
|
|
88255
|
+
}
|
|
88256
|
+
}).finally(() => {
|
|
88257
|
+
const cur = this.runningAccounts.get(account.id);
|
|
88258
|
+
if (cur && cur.client === client) {
|
|
88259
|
+
this.runningAccounts.delete(account.id);
|
|
88260
|
+
}
|
|
88261
|
+
});
|
|
88262
|
+
console.log(`[weixin] account started: ${account.name} (${account.id})`);
|
|
88263
|
+
}
|
|
88264
|
+
async stopAccount(accountId) {
|
|
88265
|
+
const running = this.runningAccounts.get(accountId);
|
|
88266
|
+
if (!running) {
|
|
88267
|
+
return;
|
|
88268
|
+
}
|
|
88269
|
+
running.client.stop();
|
|
88270
|
+
this.runningAccounts.delete(accountId);
|
|
88271
|
+
console.log(`[weixin] account stopped: ${accountId}`);
|
|
88272
|
+
}
|
|
88273
|
+
async handleInbound(accountId, msg) {
|
|
88274
|
+
const account = getWeixinAccountById(accountId);
|
|
88275
|
+
if (!account) {
|
|
88276
|
+
return;
|
|
88277
|
+
}
|
|
88278
|
+
const userId = msg.fromUserId;
|
|
88279
|
+
if (!userId) {
|
|
88280
|
+
return;
|
|
88281
|
+
}
|
|
88282
|
+
const now2 = new Date().toISOString();
|
|
88283
|
+
try {
|
|
88284
|
+
upsertWeixinUserOnInbound({
|
|
88285
|
+
accountId,
|
|
88286
|
+
userId,
|
|
88287
|
+
displayName: userId,
|
|
88288
|
+
contextToken: msg.contextToken,
|
|
88289
|
+
allowAuthRequests: account.allowAuthRequests,
|
|
88290
|
+
at: now2
|
|
88291
|
+
});
|
|
88292
|
+
} catch (err) {
|
|
88293
|
+
console.error("[weixin] failed to upsert user on inbound:", err);
|
|
88294
|
+
}
|
|
88295
|
+
}
|
|
88296
|
+
handleSessionExpired(accountId) {
|
|
88297
|
+
if (!this.sessionExpiredNotified.has(accountId)) {
|
|
88298
|
+
this.sessionExpiredNotified.add(accountId);
|
|
88299
|
+
const account = getWeixinAccountById(accountId);
|
|
88300
|
+
console.warn(`[weixin] account ${account?.name ?? accountId} session expired; re-login required.`);
|
|
88301
|
+
for (const user of listAuthorizedWeixinUsersByAccount(accountId)) {
|
|
88302
|
+
setWeixinUserNeedsReactivation(accountId, user.userId, true);
|
|
88303
|
+
}
|
|
88304
|
+
}
|
|
88305
|
+
updateWeixinAccount(accountId, { botTokenEnc: null, baseUrl: null, weixinUin: null });
|
|
88306
|
+
}
|
|
88307
|
+
async sendGatewayOnlineMessage(siteName) {
|
|
88308
|
+
await this.sendToAuthorizedUsers({ text: t2("weixin.gatewayOnline", { siteName }) });
|
|
88309
|
+
}
|
|
88310
|
+
async sendToAuthorizedUsers(params) {
|
|
88311
|
+
for (const [accountId, running] of this.runningAccounts) {
|
|
88312
|
+
for (const user of listAuthorizedWeixinUsersByAccount(accountId)) {
|
|
88313
|
+
const cont = await this.sendToUser(accountId, running, user, params.text);
|
|
88314
|
+
if (!cont)
|
|
88315
|
+
break;
|
|
88316
|
+
}
|
|
88317
|
+
}
|
|
88318
|
+
}
|
|
88319
|
+
async sendToUser(accountId, running, user, text3) {
|
|
88320
|
+
try {
|
|
88321
|
+
await running.client.sendText(user.userId, text3);
|
|
88322
|
+
if (user.needsReactivation) {
|
|
88323
|
+
setWeixinUserNeedsReactivation(accountId, user.userId, false);
|
|
88324
|
+
}
|
|
88325
|
+
return true;
|
|
88326
|
+
} catch (err) {
|
|
88327
|
+
if (err instanceof WeixinSessionExpiredError) {
|
|
88328
|
+
this.handleSessionExpired(accountId);
|
|
88329
|
+
return false;
|
|
88330
|
+
}
|
|
88331
|
+
if (!user.needsReactivation) {
|
|
88332
|
+
console.warn(`[weixin] send to ${user.userId} failed; marking needs-reactivation:`, err instanceof Error ? err.message : err);
|
|
88333
|
+
}
|
|
88334
|
+
setWeixinUserNeedsReactivation(accountId, user.userId, true);
|
|
88335
|
+
return true;
|
|
88336
|
+
}
|
|
88337
|
+
}
|
|
88338
|
+
ensureKeepaliveTimer() {
|
|
88339
|
+
if (this.keepaliveTimer) {
|
|
88340
|
+
return;
|
|
88341
|
+
}
|
|
88342
|
+
this.keepaliveTimer = setInterval(() => void this.runKeepaliveSweep(), KEEPALIVE_SWEEP_MS);
|
|
88343
|
+
this.keepaliveTimer.unref?.();
|
|
88344
|
+
}
|
|
88345
|
+
async runKeepaliveSweep() {
|
|
88346
|
+
const now2 = Date.now();
|
|
88347
|
+
for (const [accountId, running] of this.runningAccounts) {
|
|
88348
|
+
for (const user of listAuthorizedWeixinUsersByAccount(accountId)) {
|
|
88349
|
+
const key = `${accountId}:${user.userId}`;
|
|
88350
|
+
const lastInbound = user.lastInboundAt ? Date.parse(user.lastInboundAt) : 0;
|
|
88351
|
+
const since = Math.max(lastInbound, this.lastKeepaliveAt.get(key) ?? 0);
|
|
88352
|
+
if (now2 - since < KEEPALIVE_INTERVAL_MS) {
|
|
88353
|
+
continue;
|
|
88354
|
+
}
|
|
88355
|
+
this.lastKeepaliveAt.set(key, now2);
|
|
88356
|
+
const cont = await this.sendToUser(accountId, running, user, t2("weixin.keepalivePrompt"));
|
|
88357
|
+
if (!cont)
|
|
88358
|
+
break;
|
|
88359
|
+
}
|
|
88360
|
+
}
|
|
88361
|
+
}
|
|
88362
|
+
async sendTestMessage(accountId, userId, text3) {
|
|
88363
|
+
const running = this.runningAccounts.get(accountId);
|
|
88364
|
+
if (!running) {
|
|
88365
|
+
throw new Error(t2("weixin.accountNotRunning"));
|
|
88366
|
+
}
|
|
88367
|
+
await running.client.sendText(userId, text3);
|
|
88368
|
+
}
|
|
88369
|
+
async sendTestMessageToBoundUser(accountId, text3) {
|
|
88370
|
+
const [user] = listAuthorizedWeixinUsersByAccount(accountId);
|
|
88371
|
+
if (!user) {
|
|
88372
|
+
throw new Error(t2("weixin.userNotFound"));
|
|
88373
|
+
}
|
|
88374
|
+
await this.sendTestMessage(accountId, user.userId, text3);
|
|
88375
|
+
}
|
|
88376
|
+
async startLogin(accountId) {
|
|
88377
|
+
const account = getWeixinAccountById(accountId);
|
|
88378
|
+
if (!account) {
|
|
88379
|
+
throw new Error(t2("weixin.accountNotFound"));
|
|
88380
|
+
}
|
|
88381
|
+
const prev = this.loginSessions.get(accountId);
|
|
88382
|
+
if (prev) {
|
|
88383
|
+
prev.abort.abort();
|
|
88384
|
+
this.loginSessions.delete(accountId);
|
|
88385
|
+
}
|
|
88386
|
+
const abort = new AbortController;
|
|
88387
|
+
const client = this.createClient({});
|
|
88388
|
+
return await new Promise((resolve3, reject) => {
|
|
88389
|
+
let resolved = false;
|
|
88390
|
+
const loginPromise = client.login({
|
|
88391
|
+
signal: abort.signal,
|
|
88392
|
+
onQrcode: (qr) => {
|
|
88393
|
+
this.loginSessions.set(accountId, {
|
|
88394
|
+
status: "pending",
|
|
88395
|
+
qrcodeUrl: qr.url,
|
|
88396
|
+
qrcodeId: qr.qrcodeId,
|
|
88397
|
+
client,
|
|
88398
|
+
abort
|
|
88399
|
+
});
|
|
88400
|
+
if (!resolved) {
|
|
88401
|
+
resolved = true;
|
|
88402
|
+
resolve3({ qrcodeUrl: qr.url, qrcodeId: qr.qrcodeId });
|
|
88403
|
+
}
|
|
88404
|
+
}
|
|
88405
|
+
});
|
|
88406
|
+
loginPromise.then(async (creds) => {
|
|
88407
|
+
await this.persistLogin(accountId, creds);
|
|
88408
|
+
const session = this.loginSessions.get(accountId);
|
|
88409
|
+
if (session) {
|
|
88410
|
+
session.status = "confirmed";
|
|
88411
|
+
}
|
|
88412
|
+
await this.refresh();
|
|
88413
|
+
}).catch((err) => {
|
|
88414
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
88415
|
+
const status = /expired|timed out/i.test(message) ? "expired" : "error";
|
|
88416
|
+
const session = this.loginSessions.get(accountId);
|
|
88417
|
+
if (session) {
|
|
88418
|
+
session.status = status;
|
|
88419
|
+
session.message = message;
|
|
88420
|
+
} else {
|
|
88421
|
+
this.loginSessions.set(accountId, {
|
|
88422
|
+
status,
|
|
88423
|
+
message,
|
|
88424
|
+
qrcodeUrl: "",
|
|
88425
|
+
qrcodeId: "",
|
|
88426
|
+
client,
|
|
88427
|
+
abort
|
|
88428
|
+
});
|
|
88429
|
+
}
|
|
88430
|
+
if (!resolved) {
|
|
88431
|
+
resolved = true;
|
|
88432
|
+
reject(err);
|
|
88433
|
+
}
|
|
88434
|
+
});
|
|
88435
|
+
});
|
|
88436
|
+
}
|
|
88437
|
+
getLoginStatus(accountId) {
|
|
88438
|
+
const account = getWeixinAccountById(accountId);
|
|
88439
|
+
const loggedIn = account?.botTokenEnc != null;
|
|
88440
|
+
const session = this.loginSessions.get(accountId);
|
|
88441
|
+
if (!session) {
|
|
88442
|
+
return { status: loggedIn ? "confirmed" : "pending", loggedIn };
|
|
88443
|
+
}
|
|
88444
|
+
return { status: session.status, loggedIn, message: session.message };
|
|
88445
|
+
}
|
|
88446
|
+
async persistLogin(accountId, creds) {
|
|
88447
|
+
const botTokenEnc = await encrypt(creds.botToken);
|
|
88448
|
+
updateWeixinAccount(accountId, {
|
|
88449
|
+
weixinUin: creds.accountId,
|
|
88450
|
+
botTokenEnc,
|
|
88451
|
+
baseUrl: creds.baseUrl
|
|
88452
|
+
});
|
|
88453
|
+
this.sessionExpiredNotified.delete(accountId);
|
|
88454
|
+
}
|
|
88455
|
+
async stopAll() {
|
|
88456
|
+
if (this.keepaliveTimer) {
|
|
88457
|
+
clearInterval(this.keepaliveTimer);
|
|
88458
|
+
this.keepaliveTimer = null;
|
|
88459
|
+
}
|
|
88460
|
+
for (const session of this.loginSessions.values()) {
|
|
88461
|
+
session.abort.abort();
|
|
88462
|
+
}
|
|
88463
|
+
this.loginSessions.clear();
|
|
88464
|
+
const ids = Array.from(this.runningAccounts.keys());
|
|
88465
|
+
await Promise.all(ids.map((id) => this.stopAccount(id)));
|
|
88466
|
+
}
|
|
88467
|
+
}
|
|
88468
|
+
var weixinService = new WeixinService;
|
|
88469
|
+
|
|
88470
|
+
// ../../apps/gateway/src/events/channels/weixin.ts
|
|
88471
|
+
var EMOJI_MAP = {
|
|
88472
|
+
terminal_bell: "\uD83D\uDD14",
|
|
88473
|
+
terminal_notification: "\uD83D\uDD14",
|
|
88474
|
+
tmux_window_close: "\uD83E\uDE9F",
|
|
88475
|
+
tmux_pane_close: "\uD83D\uDCF1",
|
|
88476
|
+
device_tmux_missing: "\u26A0\uFE0F",
|
|
88477
|
+
device_disconnect: "\uD83D\uDD0C",
|
|
88478
|
+
session_created: "\uD83C\uDD95",
|
|
88479
|
+
session_closed: "\uD83D\uDEAA",
|
|
88480
|
+
agent_confirmation_pending: "\uD83E\uDD16",
|
|
88481
|
+
agent_turn_finished: "\uD83E\uDD16",
|
|
88482
|
+
agent_error: "\uD83E\uDD16",
|
|
88483
|
+
watch_triggered: "\uD83D\uDC41\uFE0F",
|
|
88484
|
+
watch_model_unavailable: "\uD83D\uDC41\uFE0F",
|
|
88485
|
+
watch_rule_error: "\uD83D\uDC41\uFE0F"
|
|
88486
|
+
};
|
|
88487
|
+
|
|
88488
|
+
class WeixinChannel {
|
|
88489
|
+
id = "weixin";
|
|
88490
|
+
async notify(eventType, event) {
|
|
88491
|
+
const settings = getSiteSettings();
|
|
88492
|
+
if (eventType === "terminal_bell") {
|
|
88493
|
+
if (!settings.enableWeixinBellPush) {
|
|
88494
|
+
return;
|
|
88495
|
+
}
|
|
88496
|
+
await weixinService.sendToAuthorizedUsers({ text: this.formatBellMessage(event) });
|
|
88497
|
+
return;
|
|
88498
|
+
}
|
|
88499
|
+
if (!settings.enableWeixinNotificationPush) {
|
|
88500
|
+
return;
|
|
88501
|
+
}
|
|
88502
|
+
const text3 = eventType === "terminal_notification" ? this.formatNotificationMessage(event) : this.formatGenericMessage(event, settings);
|
|
88503
|
+
await weixinService.sendToAuthorizedUsers({ text: text3 });
|
|
88504
|
+
}
|
|
88505
|
+
buildTerminalTopbarLabel(event) {
|
|
88506
|
+
const windowLabel = typeof event.tmux?.windowIndex === "number" ? `${event.tmux.windowIndex}` : event.tmux?.windowId ?? "?";
|
|
88507
|
+
const paneLabel = typeof event.tmux?.paneIndex === "number" ? `${event.tmux.paneIndex}` : event.tmux?.paneId ?? "?";
|
|
88508
|
+
return t2("notification.telegramBell.terminalTopbarLabel", {
|
|
88509
|
+
window: windowLabel,
|
|
88510
|
+
pane: paneLabel,
|
|
88511
|
+
device: event.device.name
|
|
88512
|
+
});
|
|
88513
|
+
}
|
|
88514
|
+
buildPaneMetaLines(event) {
|
|
88515
|
+
const lines = [];
|
|
88516
|
+
if (event.tmux?.paneTitle) {
|
|
88517
|
+
lines.push(`${t2("notification.paneTitle")}\uFF1A${event.tmux.paneTitle}`);
|
|
88518
|
+
}
|
|
88519
|
+
if (event.tmux?.paneCurrentCommand) {
|
|
88520
|
+
lines.push(`${t2("notification.process")}\uFF1A${event.tmux.paneCurrentCommand}`);
|
|
88521
|
+
}
|
|
88522
|
+
return lines;
|
|
88523
|
+
}
|
|
88524
|
+
formatBellMessage(event) {
|
|
88525
|
+
const lines = [
|
|
88526
|
+
t2("notification.telegramBell.title", {
|
|
88527
|
+
siteName: event.site.name,
|
|
88528
|
+
terminalTopbarLabel: this.buildTerminalTopbarLabel(event)
|
|
88529
|
+
})
|
|
88530
|
+
];
|
|
88531
|
+
lines.push(...this.buildPaneMetaLines(event));
|
|
88532
|
+
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
88533
|
+
if (paneUrl) {
|
|
88534
|
+
lines.push("", paneUrl);
|
|
88535
|
+
}
|
|
88536
|
+
return lines.join(`
|
|
88537
|
+
`);
|
|
88538
|
+
}
|
|
88539
|
+
formatNotificationMessage(event) {
|
|
88540
|
+
const title = typeof event.payload?.title === "string" ? event.payload.title : "";
|
|
88541
|
+
const body = typeof event.payload?.message === "string" ? event.payload.message : "";
|
|
88542
|
+
const lines = [];
|
|
88543
|
+
if (title) {
|
|
88544
|
+
lines.push(title);
|
|
88545
|
+
}
|
|
88546
|
+
if (body) {
|
|
88547
|
+
lines.push(body);
|
|
88548
|
+
}
|
|
88549
|
+
lines.push(...this.buildPaneMetaLines(event));
|
|
88550
|
+
const footer = `from ${event.site.name}: ${this.buildTerminalTopbarLabel(event)}`;
|
|
88551
|
+
lines.push("", footer);
|
|
88552
|
+
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
88553
|
+
if (paneUrl) {
|
|
88554
|
+
lines.push(paneUrl);
|
|
88555
|
+
}
|
|
88556
|
+
return lines.join(`
|
|
88557
|
+
`);
|
|
88558
|
+
}
|
|
88559
|
+
formatGenericMessage(event, settings) {
|
|
88560
|
+
const eventTypeLabel = t2(`notification.eventType.${event.eventType}`);
|
|
88561
|
+
const windowLabel = event.tmux?.windowIndex !== undefined ? `${event.tmux.windowIndex} (${event.tmux.windowId ?? "-"})` : event.tmux?.windowId ?? "-";
|
|
88562
|
+
const paneLabel = event.tmux?.paneIndex !== undefined ? `${event.tmux.paneIndex} (${event.tmux.paneId ?? "-"})` : event.tmux?.paneId ?? "-";
|
|
88563
|
+
const lines = [
|
|
88564
|
+
`${EMOJI_MAP[event.eventType] ?? "\uD83D\uDCE2"} ${eventTypeLabel}`,
|
|
88565
|
+
`${t2("notification.site")}\uFF1A${event.site.name}`,
|
|
88566
|
+
`${t2("notification.time")}\uFF1A${new Date(event.timestamp).toLocaleString(toBCP47(settings.language))}`,
|
|
88567
|
+
`${t2("notification.device")}\uFF1A${event.device.name} (${event.device.type})`,
|
|
88568
|
+
`${t2("notification.window")}\uFF1A${windowLabel}`,
|
|
88569
|
+
`${t2("notification.pane")}\uFF1A${paneLabel}`
|
|
88570
|
+
];
|
|
88571
|
+
lines.push(...this.buildPaneMetaLines(event));
|
|
88572
|
+
if (typeof event.payload?.message === "string") {
|
|
88573
|
+
lines.push(`${t2("notification.message")}\uFF1A${event.payload.message}`);
|
|
88574
|
+
}
|
|
88575
|
+
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
88576
|
+
if (paneUrl) {
|
|
88577
|
+
lines.push("", paneUrl);
|
|
88578
|
+
}
|
|
88579
|
+
return lines.join(`
|
|
88580
|
+
`);
|
|
88581
|
+
}
|
|
88582
|
+
}
|
|
88583
|
+
var weixinChannel = new WeixinChannel;
|
|
88584
|
+
|
|
88585
|
+
// ../../apps/gateway/src/events/index.ts
|
|
88586
|
+
class EventNotifier {
|
|
88587
|
+
bellThrottleMap = new Map;
|
|
88588
|
+
notificationThrottleMap = new Map;
|
|
88589
|
+
channels = [
|
|
88590
|
+
webhookChannel,
|
|
88591
|
+
telegramChannel,
|
|
88592
|
+
weixinChannel
|
|
88593
|
+
];
|
|
88594
|
+
async notify(eventType, event) {
|
|
88595
|
+
const fullEvent = {
|
|
88596
|
+
...event,
|
|
88597
|
+
eventType,
|
|
88598
|
+
timestamp: new Date().toISOString()
|
|
88599
|
+
};
|
|
88600
|
+
if (eventType === "terminal_bell") {
|
|
88601
|
+
if (!this.shouldPassBellThrottle(fullEvent)) {
|
|
88602
|
+
return;
|
|
88603
|
+
}
|
|
88604
|
+
} else if (eventType === "terminal_notification") {
|
|
88605
|
+
if (!this.shouldPassNotificationThrottle(fullEvent)) {
|
|
88606
|
+
return;
|
|
88607
|
+
}
|
|
88608
|
+
}
|
|
88609
|
+
await Promise.all(this.channels.map((channel) => channel.notify(eventType, fullEvent)));
|
|
88610
|
+
}
|
|
88611
|
+
shouldPassBellThrottle(event) {
|
|
88612
|
+
const settings = getSiteSettings();
|
|
88613
|
+
const throttleMs = Math.max(0, settings.bellThrottleSeconds) * 1000;
|
|
88614
|
+
if (throttleMs === 0) {
|
|
88615
|
+
return true;
|
|
88616
|
+
}
|
|
88617
|
+
const key = `${event.device.id}:${event.tmux?.paneId ?? "-"}:${event.eventType}`;
|
|
88618
|
+
const now2 = Date.now();
|
|
88619
|
+
const previous = this.bellThrottleMap.get(key) ?? 0;
|
|
88620
|
+
if (now2 - previous < throttleMs) {
|
|
88621
|
+
return false;
|
|
88622
|
+
}
|
|
88623
|
+
this.bellThrottleMap.set(key, now2);
|
|
88624
|
+
return true;
|
|
88625
|
+
}
|
|
88626
|
+
shouldPassNotificationThrottle(event) {
|
|
88627
|
+
const settings = getSiteSettings();
|
|
88628
|
+
const throttleMs = Math.max(0, settings.notificationThrottleSeconds) * 1000;
|
|
88629
|
+
if (throttleMs === 0) {
|
|
88630
|
+
return true;
|
|
88631
|
+
}
|
|
88632
|
+
const source = typeof event.payload?.source === "string" ? event.payload.source : "unknown";
|
|
88633
|
+
const key = `${event.device.id}:${event.tmux?.paneId ?? "-"}:notification:${source}`;
|
|
88634
|
+
const now2 = Date.now();
|
|
88635
|
+
const previous = this.notificationThrottleMap.get(key) ?? 0;
|
|
88636
|
+
if (now2 - previous < throttleMs) {
|
|
88637
|
+
return false;
|
|
88638
|
+
}
|
|
88639
|
+
this.notificationThrottleMap.set(key, now2);
|
|
88640
|
+
return true;
|
|
88641
|
+
}
|
|
88642
|
+
}
|
|
87300
88643
|
var eventNotifier = new EventNotifier;
|
|
87301
88644
|
|
|
87302
88645
|
// ../../node_modules/.bun/@ai-sdk+openai@3.0.71+68a1e3a0c4588df3/node_modules/@ai-sdk/openai/dist/index.mjs
|
|
@@ -105079,8 +106422,8 @@ function getBaseVersion() {
|
|
|
105079
106422
|
if (cachedBase !== undefined)
|
|
105080
106423
|
return cachedBase;
|
|
105081
106424
|
let base = null;
|
|
105082
|
-
if ("0.
|
|
105083
|
-
base = "0.
|
|
106425
|
+
if ("0.13.0") {
|
|
106426
|
+
base = "0.13.0";
|
|
105084
106427
|
}
|
|
105085
106428
|
if (!base && config.isProd) {
|
|
105086
106429
|
base = readInstallMeta()?.cliVersion ?? null;
|
|
@@ -106878,6 +108221,18 @@ function normalizeSiteSettingsInput(body) {
|
|
|
106878
108221
|
}
|
|
106879
108222
|
updates.enableTelegramNotificationPush = body.enableTelegramNotificationPush;
|
|
106880
108223
|
}
|
|
108224
|
+
if (body.enableWeixinBellPush !== undefined) {
|
|
108225
|
+
if (typeof body.enableWeixinBellPush !== "boolean") {
|
|
108226
|
+
throw new Error(t2("apiError.invalidRequest"));
|
|
108227
|
+
}
|
|
108228
|
+
updates.enableWeixinBellPush = body.enableWeixinBellPush;
|
|
108229
|
+
}
|
|
108230
|
+
if (body.enableWeixinNotificationPush !== undefined) {
|
|
108231
|
+
if (typeof body.enableWeixinNotificationPush !== "boolean") {
|
|
108232
|
+
throw new Error(t2("apiError.invalidRequest"));
|
|
108233
|
+
}
|
|
108234
|
+
updates.enableWeixinNotificationPush = body.enableWeixinNotificationPush;
|
|
108235
|
+
}
|
|
106881
108236
|
if (body.sshReconnectMaxRetries !== undefined) {
|
|
106882
108237
|
const value = Math.floor(Number(body.sshReconnectMaxRetries));
|
|
106883
108238
|
if (Number.isNaN(value) || value < 0 || value > 20) {
|
|
@@ -106964,6 +108319,39 @@ function handleApiRequest(req, _server) {
|
|
|
106964
108319
|
if (path.match(/^\/api\/settings\/telegram\/bots\/[^/]+\/chats\/[^/]+$/) && req.method === "DELETE") {
|
|
106965
108320
|
return handleDeleteTelegramChat(path.split("/")[5], decodeURIComponent(path.split("/")[7]));
|
|
106966
108321
|
}
|
|
108322
|
+
if (path === "/api/settings/weixin/accounts" && req.method === "GET") {
|
|
108323
|
+
return handleGetWeixinAccounts();
|
|
108324
|
+
}
|
|
108325
|
+
if (path === "/api/settings/weixin/accounts" && req.method === "POST") {
|
|
108326
|
+
return handleCreateWeixinAccount(req);
|
|
108327
|
+
}
|
|
108328
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+$/) && req.method === "PATCH") {
|
|
108329
|
+
return handleUpdateWeixinAccount(req, path.split("/")[5]);
|
|
108330
|
+
}
|
|
108331
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+$/) && req.method === "DELETE") {
|
|
108332
|
+
return handleDeleteWeixinAccount(path.split("/")[5]);
|
|
108333
|
+
}
|
|
108334
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/login\/start$/) && req.method === "POST") {
|
|
108335
|
+
return handleStartWeixinLogin(path.split("/")[5]);
|
|
108336
|
+
}
|
|
108337
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/login\/status$/) && req.method === "GET") {
|
|
108338
|
+
return handleGetWeixinLoginStatus(path.split("/")[5]);
|
|
108339
|
+
}
|
|
108340
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/test$/) && req.method === "POST") {
|
|
108341
|
+
return handleTestWeixinAccount(path.split("/")[5]);
|
|
108342
|
+
}
|
|
108343
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/users$/) && req.method === "GET") {
|
|
108344
|
+
return handleListWeixinUsers(path.split("/")[5]);
|
|
108345
|
+
}
|
|
108346
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/users\/[^/]+\/approve$/) && req.method === "POST") {
|
|
108347
|
+
return handleApproveWeixinUser(path.split("/")[5], decodeURIComponent(path.split("/")[7]));
|
|
108348
|
+
}
|
|
108349
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/users\/[^/]+\/test$/) && req.method === "POST") {
|
|
108350
|
+
return handleTestWeixinUser(path.split("/")[5], decodeURIComponent(path.split("/")[7]));
|
|
108351
|
+
}
|
|
108352
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/users\/[^/]+$/) && req.method === "DELETE") {
|
|
108353
|
+
return handleDeleteWeixinUser(path.split("/")[5], decodeURIComponent(path.split("/")[7]));
|
|
108354
|
+
}
|
|
106967
108355
|
if (path.startsWith("/api/llm/")) {
|
|
106968
108356
|
const llmResponse = handleLlmApiRequest(req, path);
|
|
106969
108357
|
if (llmResponse) {
|
|
@@ -107266,6 +108654,152 @@ async function handleTestTelegramChat(botId, chatId) {
|
|
|
107266
108654
|
}));
|
|
107267
108655
|
return json9({ success: true });
|
|
107268
108656
|
}
|
|
108657
|
+
async function handleGetWeixinAccounts() {
|
|
108658
|
+
const accounts = getWeixinAccountsWithStats();
|
|
108659
|
+
return json9({ accounts });
|
|
108660
|
+
}
|
|
108661
|
+
async function handleCreateWeixinAccount(req) {
|
|
108662
|
+
const body = await req.json();
|
|
108663
|
+
if (!body.name?.trim()) {
|
|
108664
|
+
return json9({ error: t2("weixin.accountNameRequired") }, 400);
|
|
108665
|
+
}
|
|
108666
|
+
const now2 = new Date().toISOString();
|
|
108667
|
+
const id = v4_default();
|
|
108668
|
+
createWeixinAccount({
|
|
108669
|
+
id,
|
|
108670
|
+
name: body.name.trim(),
|
|
108671
|
+
enabled: body.enabled ?? true,
|
|
108672
|
+
allowAuthRequests: body.allowAuthRequests ?? true,
|
|
108673
|
+
loggedIn: false,
|
|
108674
|
+
weixinUin: null,
|
|
108675
|
+
botTokenEnc: null,
|
|
108676
|
+
baseUrl: null,
|
|
108677
|
+
syncBuf: null,
|
|
108678
|
+
createdAt: now2,
|
|
108679
|
+
updatedAt: now2
|
|
108680
|
+
});
|
|
108681
|
+
return json9({ success: true, accountId: id }, 201);
|
|
108682
|
+
}
|
|
108683
|
+
async function handleUpdateWeixinAccount(req, accountId) {
|
|
108684
|
+
const existing = getWeixinAccountById(accountId);
|
|
108685
|
+
if (!existing) {
|
|
108686
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108687
|
+
}
|
|
108688
|
+
const body = await req.json();
|
|
108689
|
+
const updates = {};
|
|
108690
|
+
if (body.name !== undefined) {
|
|
108691
|
+
const value = body.name.trim();
|
|
108692
|
+
if (!value) {
|
|
108693
|
+
return json9({ error: t2("weixin.accountNameRequired") }, 400);
|
|
108694
|
+
}
|
|
108695
|
+
updates.name = value;
|
|
108696
|
+
}
|
|
108697
|
+
if (body.enabled !== undefined) {
|
|
108698
|
+
updates.enabled = body.enabled;
|
|
108699
|
+
}
|
|
108700
|
+
if (body.allowAuthRequests !== undefined) {
|
|
108701
|
+
updates.allowAuthRequests = body.allowAuthRequests;
|
|
108702
|
+
}
|
|
108703
|
+
updateWeixinAccount(accountId, updates);
|
|
108704
|
+
await weixinService.refresh();
|
|
108705
|
+
return json9({ success: true });
|
|
108706
|
+
}
|
|
108707
|
+
async function handleDeleteWeixinAccount(accountId) {
|
|
108708
|
+
const existing = getWeixinAccountById(accountId);
|
|
108709
|
+
if (!existing) {
|
|
108710
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108711
|
+
}
|
|
108712
|
+
deleteWeixinAccount(accountId);
|
|
108713
|
+
await weixinService.refresh();
|
|
108714
|
+
return json9({ success: true });
|
|
108715
|
+
}
|
|
108716
|
+
async function handleStartWeixinLogin(accountId) {
|
|
108717
|
+
const existing = getWeixinAccountById(accountId);
|
|
108718
|
+
if (!existing) {
|
|
108719
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108720
|
+
}
|
|
108721
|
+
try {
|
|
108722
|
+
const result = await weixinService.startLogin(accountId);
|
|
108723
|
+
return json9(result);
|
|
108724
|
+
} catch (err) {
|
|
108725
|
+
return json9({ error: err instanceof Error ? err.message : t2("weixin.loginFailed") }, 502);
|
|
108726
|
+
}
|
|
108727
|
+
}
|
|
108728
|
+
async function handleGetWeixinLoginStatus(accountId) {
|
|
108729
|
+
const existing = getWeixinAccountById(accountId);
|
|
108730
|
+
if (!existing) {
|
|
108731
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108732
|
+
}
|
|
108733
|
+
return json9(weixinService.getLoginStatus(accountId));
|
|
108734
|
+
}
|
|
108735
|
+
async function handleListWeixinUsers(accountId) {
|
|
108736
|
+
const existing = getWeixinAccountById(accountId);
|
|
108737
|
+
if (!existing) {
|
|
108738
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108739
|
+
}
|
|
108740
|
+
const users = listWeixinUsersByAccount(accountId);
|
|
108741
|
+
return json9({ users });
|
|
108742
|
+
}
|
|
108743
|
+
async function handleApproveWeixinUser(accountId, userId) {
|
|
108744
|
+
const existing = getWeixinAccountById(accountId);
|
|
108745
|
+
if (!existing) {
|
|
108746
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108747
|
+
}
|
|
108748
|
+
const user = approveWeixinUser(accountId, userId);
|
|
108749
|
+
if (!user) {
|
|
108750
|
+
return json9({ error: t2("weixin.userNotFound") }, 404);
|
|
108751
|
+
}
|
|
108752
|
+
const settings = getSiteSettings();
|
|
108753
|
+
try {
|
|
108754
|
+
await weixinService.sendTestMessage(accountId, userId, t2("weixin.approveMessageTemplate", {
|
|
108755
|
+
accountName: existing.name,
|
|
108756
|
+
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
108757
|
+
}));
|
|
108758
|
+
} catch (err) {
|
|
108759
|
+
console.error("[weixin] approve ack failed:", err);
|
|
108760
|
+
}
|
|
108761
|
+
return json9({ user });
|
|
108762
|
+
}
|
|
108763
|
+
async function handleTestWeixinUser(accountId, userId) {
|
|
108764
|
+
const existing = getWeixinAccountById(accountId);
|
|
108765
|
+
if (!existing) {
|
|
108766
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108767
|
+
}
|
|
108768
|
+
const settings = getSiteSettings();
|
|
108769
|
+
try {
|
|
108770
|
+
await weixinService.sendTestMessage(accountId, userId, t2("weixin.testMessageTemplate", {
|
|
108771
|
+
siteName: settings.siteName,
|
|
108772
|
+
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
108773
|
+
}));
|
|
108774
|
+
} catch (err) {
|
|
108775
|
+
return json9({ error: err instanceof Error ? err.message : t2("weixin.testMessageFailed") }, 400);
|
|
108776
|
+
}
|
|
108777
|
+
return json9({ success: true });
|
|
108778
|
+
}
|
|
108779
|
+
async function handleTestWeixinAccount(accountId) {
|
|
108780
|
+
const existing = getWeixinAccountById(accountId);
|
|
108781
|
+
if (!existing) {
|
|
108782
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108783
|
+
}
|
|
108784
|
+
const settings = getSiteSettings();
|
|
108785
|
+
try {
|
|
108786
|
+
await weixinService.sendTestMessageToBoundUser(accountId, t2("weixin.testMessageTemplate", {
|
|
108787
|
+
siteName: settings.siteName,
|
|
108788
|
+
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
108789
|
+
}));
|
|
108790
|
+
} catch (err) {
|
|
108791
|
+
return json9({ error: err instanceof Error ? err.message : t2("weixin.testMessageFailed") }, 400);
|
|
108792
|
+
}
|
|
108793
|
+
return json9({ success: true });
|
|
108794
|
+
}
|
|
108795
|
+
async function handleDeleteWeixinUser(accountId, userId) {
|
|
108796
|
+
const existing = getWeixinAccountById(accountId);
|
|
108797
|
+
if (!existing) {
|
|
108798
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108799
|
+
}
|
|
108800
|
+
deleteWeixinUser(accountId, userId);
|
|
108801
|
+
return json9({ success: true });
|
|
108802
|
+
}
|
|
107269
108803
|
async function handleGetWebhooks() {
|
|
107270
108804
|
const webhooks = getAllWebhookEndpoints();
|
|
107271
108805
|
return json9({ webhooks });
|
|
@@ -108899,12 +110433,14 @@ async function createGatewayRuntime(options = {}) {
|
|
|
108899
110433
|
wsServer.broadcastDeviceError(deviceId, payload);
|
|
108900
110434
|
});
|
|
108901
110435
|
await telegramService.refresh();
|
|
110436
|
+
await weixinService.refresh();
|
|
108902
110437
|
await pushSupervisor.start();
|
|
108903
110438
|
await agentSupervisor.start();
|
|
108904
110439
|
await watchService.start();
|
|
108905
110440
|
try {
|
|
108906
110441
|
const settings = getSiteSettings();
|
|
108907
110442
|
await telegramService.sendGatewayOnlineMessage(settings.siteName);
|
|
110443
|
+
await weixinService.sendGatewayOnlineMessage(settings.siteName);
|
|
108908
110444
|
} catch (err) {
|
|
108909
110445
|
console.error("[gateway] failed to push startup message:", err);
|
|
108910
110446
|
}
|
|
@@ -108949,6 +110485,7 @@ async function createGatewayRuntime(options = {}) {
|
|
|
108949
110485
|
await pushSupervisor.stopAll();
|
|
108950
110486
|
await tmuxRuntimeRegistry.shutdownAll();
|
|
108951
110487
|
await telegramService.stopAll();
|
|
110488
|
+
await weixinService.stopAll();
|
|
108952
110489
|
}
|
|
108953
110490
|
};
|
|
108954
110491
|
}
|