hyperclaw 4.0.2 → 5.0.1
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/README.md +246 -60
- package/dist/a2ui-protocol-CfBI44-Q.js +75 -0
- package/dist/agents-routing-ChHiZp36.js +327 -0
- package/dist/agents-routing-ChqZ6l2S.js +4 -0
- package/dist/api-keys-guide-BCcOl0Q7.js +149 -0
- package/dist/api-keys-guide-CGn5BSF7.js +149 -0
- package/dist/audit-BJohI_vC.js +441 -0
- package/dist/audit-BaIiyWFu.js +441 -0
- package/dist/bounty-tools-CY_i91DU.js +211 -0
- package/dist/bounty-tools-DWudyZie.js +211 -0
- package/dist/browser-tools-BsTeGMnX.js +5 -0
- package/dist/browser-tools-D8_rLe2p.js +179 -0
- package/dist/claw-tasks-CgTsiNE8.js +80 -0
- package/dist/claw-tasks-Cyzdbhz_.js +80 -0
- package/dist/connector-5N0-X_xs.js +194 -0
- package/dist/connector-B3v0qcXg.js +425 -0
- package/dist/connector-B8R3iBY1.js +280 -0
- package/dist/connector-BAM-08NN.js +189 -0
- package/dist/connector-BC8FIVu4.js +181 -0
- package/dist/connector-BDmwwaVc.js +213 -0
- package/dist/connector-BGjbBy69.js +225 -0
- package/dist/connector-BO2SRzfG.js +218 -0
- package/dist/connector-BfXky0L3.js +167 -0
- package/dist/connector-BiiSJpx3.js +192 -0
- package/dist/connector-BnDmIhIu.js +85 -0
- package/dist/connector-C1HSoUyk.js +189 -0
- package/dist/connector-CKQHZOXg.js +568 -0
- package/dist/connector-CRl-iidy.js +239 -0
- package/dist/connector-Ci9glMD-.js +340 -0
- package/dist/connector-CjtZIEDj.js +181 -0
- package/dist/connector-Ck6JtOsX.js +531 -0
- package/dist/connector-D8Kelee0.js +286 -0
- package/dist/connector-DAnRJ0oP.js +162 -0
- package/dist/connector-DXTp5PE8.js +508 -0
- package/dist/connector-Dih6dUPP.js +173 -0
- package/dist/connector-DqTH_tPX.js +182 -0
- package/dist/connector-DrnEiiyP.js +419 -0
- package/dist/connector-DtR5GGTX.js +167 -0
- package/dist/connector-Tky_qS_K.js +350 -0
- package/dist/connector-ZSc3oTTy.js +305 -0
- package/dist/connector-sW5yhU1m.js +498 -0
- package/dist/connector-u3ICd3Ic.js +552 -0
- package/dist/cost-tracker-Ca1UPZ33.js +103 -0
- package/dist/cost-tracker-DD9wtWsr.js +103 -0
- package/dist/credentials-store-C6ir0Dae.js +4 -0
- package/dist/credentials-store-CA8UtK0T.js +77 -0
- package/dist/credentials-store-Cm7DH-kh.js +4 -0
- package/dist/credentials-store-H13LqOwJ.js +77 -0
- package/dist/cron-tasks-Bli7Kzd2.js +82 -0
- package/dist/cron-tasks-_pqQCmxc.js +82 -0
- package/dist/daemon-7ViroziB.js +5 -0
- package/dist/daemon-BfyKmZhr.js +318 -0
- package/dist/daemon-Bg4GtCmc.js +318 -0
- package/dist/daemon-DhmwY8k4.js +5 -0
- package/dist/delivery-BmIYy9VQ.js +4 -0
- package/dist/delivery-DVHmv1IR.js +4 -0
- package/dist/delivery-DpMX0Yyc.js +95 -0
- package/dist/delivery-pWUPBp1F.js +95 -0
- package/dist/destructive-gate-D6vWOdEl.js +101 -0
- package/dist/destructive-gate-DZt71UZR.js +101 -0
- package/dist/developer-keys-CPWT7Q6S.js +8 -0
- package/dist/developer-keys-DrrcUqFa.js +127 -0
- package/dist/doctor-BvCe8BBk.js +230 -0
- package/dist/doctor-CxyPLYsJ.js +6 -0
- package/dist/engine-B0kLfRL0.js +256 -0
- package/dist/engine-BJUpRUOv.js +7 -0
- package/dist/engine-CEDSqXfw.js +256 -0
- package/dist/engine-Da4JMNpI.js +7 -0
- package/dist/env-resolve-17ekEU6p.js +10 -0
- package/dist/env-resolve-CiXbWYwe.js +10 -0
- package/dist/env-resolve-CmGWhWXJ.js +115 -0
- package/dist/env-resolve-Z2XF6leB.js +115 -0
- package/dist/extraction-tools-HOZstZ0y.js +91 -0
- package/dist/extraction-tools-m4lmAv7l.js +5 -0
- package/dist/form_data-Cz040rio.js +8657 -0
- package/dist/gmail-watch-setup-Du7DVV7S.js +40 -0
- package/dist/health-B-asI__D.js +6 -0
- package/dist/health-Ds2YlpTB.js +152 -0
- package/dist/heartbeat-engine-BYT5ayQH.js +83 -0
- package/dist/heartbeat-engine-Ut6pXBD6.js +83 -0
- package/dist/hub-9LaKnLjY.js +6 -0
- package/dist/hub-CfwUz9YW.js +515 -0
- package/dist/hub-D0XwdjM-.js +515 -0
- package/dist/hub-LiD5Iztb.js +6 -0
- package/dist/hyperclawbot-CBiDSKsa.js +505 -0
- package/dist/hyperclawbot-zvczQgKx.js +505 -0
- package/dist/inference-0mlFQqIm.js +922 -0
- package/dist/inference-BKVkBREb.js +6 -0
- package/dist/inference-DCXH4Q3x.js +922 -0
- package/dist/inference-SzqFe_nk.js +6 -0
- package/dist/knowledge-graph-DE5lSF02.js +131 -0
- package/dist/knowledge-graph-iBG76fvm.js +131 -0
- package/dist/loader-BkDi8MD9.js +400 -0
- package/dist/loader-CC45xGpC.js +4 -0
- package/dist/loader-CnEdOyjT.js +400 -0
- package/dist/loader-DI2qDRPC.js +4 -0
- package/dist/logger-Cp8wC7F8.js +83 -0
- package/dist/logger-ybOp7VOC.js +83 -0
- package/dist/manager-03ipO9R0.js +105 -0
- package/dist/manager-B2Gls5RG.js +218 -0
- package/dist/manager-BpDfbDjg.js +117 -0
- package/dist/manager-Bxl0sqlh.js +4 -0
- package/dist/manager-CWNSML5D.js +117 -0
- package/dist/manager-CrVDn6eN.js +6 -0
- package/dist/manager-FCgF1plu.js +218 -0
- package/dist/manager-SJe9gt-q.js +4 -0
- package/dist/manager-rgCsaWT1.js +40 -0
- package/dist/mcp-CfoSU4Uz.js +139 -0
- package/dist/mcp-loader-CvxRDtPC.js +94 -0
- package/dist/mcp-loader-DkRBsLpk.js +94 -0
- package/dist/memory-BlHL7JCO.js +4 -0
- package/dist/memory-DsS_eFvJ.js +270 -0
- package/dist/memory-auto-BkvtSFUw.js +5 -0
- package/dist/memory-auto-Bnz_-1wP.js +306 -0
- package/dist/memory-auto-CpQHZlEJ.js +306 -0
- package/dist/memory-auto-Z6LCf-iK.js +5 -0
- package/dist/memory-integration-cSYkZyEo.js +91 -0
- package/dist/memory-integration-g2vxwgoE.js +91 -0
- package/dist/moltbook-BtLDZTfM.js +81 -0
- package/dist/moltbook-Cl8cQfxJ.js +81 -0
- package/dist/node-Dw2Gi-cP.js +222 -0
- package/dist/nodes-registry-B8dmrlLv.js +52 -0
- package/dist/nodes-registry-C9dCFwjh.js +52 -0
- package/dist/oauth-flow-CeaaGAz0.js +150 -0
- package/dist/oauth-flow-DQPvMHRH.js +150 -0
- package/dist/oauth-provider-B4dzn56l.js +110 -0
- package/dist/oauth-provider-Uo4Nib_c.js +110 -0
- package/dist/observability-BV-Yx0V9.js +89 -0
- package/dist/observability-nZ3CBIxG.js +89 -0
- package/dist/onboard-0WoDxbv_.js +10 -0
- package/dist/onboard-BBBWcfhp.js +10 -0
- package/dist/onboard-BXNXCQp4.js +4070 -0
- package/dist/onboard-Bw28IRQ3.js +4070 -0
- package/dist/orchestrator-BovkM63z.js +6 -0
- package/dist/orchestrator-DSbpkP1X.js +189 -0
- package/dist/orchestrator-DmnEvMaL.js +189 -0
- package/dist/orchestrator-RI3bpqqc.js +6 -0
- package/dist/osint-B4_m3VHQ.js +277 -0
- package/dist/pairing-6iM27aD8.js +196 -0
- package/dist/pairing-dGoiGepK.js +4 -0
- package/dist/pc-access-CgCsYrpt.js +8 -0
- package/dist/pc-access-_iH2aorG.js +819 -0
- package/dist/pending-approval-BgNjjuI2.js +22 -0
- package/dist/pending-approval-CUXjysAo.js +22 -0
- package/dist/reminders-store-Drjed_-h.js +58 -0
- package/dist/renderer-BVQrd0_g.js +225 -0
- package/dist/rules-BE4GV6cV.js +103 -0
- package/dist/run-main.js +1639 -460
- package/dist/runner-CJFJUtPm.js +1271 -0
- package/dist/runner-DatMMYYE.js +1271 -0
- package/dist/sdk/index.js +2 -2
- package/dist/sdk/index.mjs +2 -2
- package/dist/security-BqNyT4ID.js +4 -0
- package/dist/security-tpgqPWWH.js +73 -0
- package/dist/server-Brl_HQUB.js +1255 -0
- package/dist/server-D4wVHiX9.js +4 -0
- package/dist/server-Dh3JlBFB.js +1255 -0
- package/dist/server-DhfipkwN.js +4 -0
- package/dist/session-store-BUiPz0Vv.js +5 -0
- package/dist/session-store-is4B6qmD.js +113 -0
- package/dist/sessions-tools-CbUTFe4i.js +5 -0
- package/dist/sessions-tools-CeqD7iil.js +95 -0
- package/dist/skill-loader-BaNLVmJy.js +7 -0
- package/dist/skill-loader-HgpF6Vqs.js +159 -0
- package/dist/skill-runtime-BXWd-Ktf.js +102 -0
- package/dist/skill-runtime-CJN24QPW.js +102 -0
- package/dist/skill-runtime-jgklm02e.js +5 -0
- package/dist/skill-runtime-w1ig_lcw.js +5 -0
- package/dist/src-Bhybpk1J.js +63 -0
- package/dist/src-BxPHKO5x.js +63 -0
- package/dist/src-DIc-L2IG.js +20 -0
- package/dist/src-DMJ4-uqk.js +458 -0
- package/dist/src-g_rNx5rh.js +458 -0
- package/dist/sub-agent-tools-CHQoHz9c.js +39 -0
- package/dist/sub-agent-tools-DHY-4WWM.js +39 -0
- package/dist/theme-DcxwcUgZ.js +180 -0
- package/dist/theme-cx0fkgWC.js +8 -0
- package/dist/tool-policy-CNT-mF2Z.js +189 -0
- package/dist/tool-policy-DZvF8xlQ.js +189 -0
- package/dist/tts-elevenlabs-BRosZv-f.js +61 -0
- package/dist/tts-elevenlabs-C06nUxMK.js +61 -0
- package/dist/update-check-C2Dz85wJ.js +81 -0
- package/dist/update-check-w4XuxVl7.js +81 -0
- package/dist/vision-BMmiIKy7.js +121 -0
- package/dist/vision-JOtOS1Br.js +121 -0
- package/dist/vision-tools-CB28ZCO_.js +5 -0
- package/dist/vision-tools-DVuYc17I.js +51 -0
- package/dist/vision-tools-U3YC4L-g.js +5 -0
- package/dist/vision-tools-vPPwQ-0N.js +51 -0
- package/dist/voice-transcription-B555DbWR.js +138 -0
- package/dist/voice-transcription-DBo5hXmu.js +138 -0
- package/dist/website-watch-tools-DFMrJU-R.js +139 -0
- package/dist/website-watch-tools-Du3W5sN7.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
+
const chalk = require_chunk.__toESM(require("chalk"));
|
|
3
|
+
const fs_extra = require_chunk.__toESM(require("fs-extra"));
|
|
4
|
+
const path = require_chunk.__toESM(require("path"));
|
|
5
|
+
const os = require_chunk.__toESM(require("os"));
|
|
6
|
+
const https = require_chunk.__toESM(require("https"));
|
|
7
|
+
const events = require_chunk.__toESM(require("events"));
|
|
8
|
+
|
|
9
|
+
//#region extensions/feishu/src/connector.ts
|
|
10
|
+
const STATE_FILE = path.default.join(os.default.homedir(), ".hyperclaw", "feishu-state.json");
|
|
11
|
+
let feishuToken = null;
|
|
12
|
+
async function getAccessToken(appId, appSecret) {
|
|
13
|
+
if (feishuToken && feishuToken.expiresAt > Date.now() + 6e4) return feishuToken.token;
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const payload = JSON.stringify({
|
|
16
|
+
app_id: appId,
|
|
17
|
+
app_secret: appSecret
|
|
18
|
+
});
|
|
19
|
+
const req = https.default.request({
|
|
20
|
+
hostname: "open.feishu.cn",
|
|
21
|
+
port: 443,
|
|
22
|
+
path: "/open-apis/auth/v3/tenant_access_token/internal",
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: {
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
27
|
+
}
|
|
28
|
+
}, (res) => {
|
|
29
|
+
let data = "";
|
|
30
|
+
res.on("data", (c) => data += c);
|
|
31
|
+
res.on("end", () => {
|
|
32
|
+
try {
|
|
33
|
+
const r = JSON.parse(data);
|
|
34
|
+
feishuToken = {
|
|
35
|
+
token: r.tenant_access_token,
|
|
36
|
+
expiresAt: Date.now() + r.expire * 1e3
|
|
37
|
+
};
|
|
38
|
+
resolve(r.tenant_access_token);
|
|
39
|
+
} catch {
|
|
40
|
+
reject(new Error("Token error"));
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
req.on("error", reject);
|
|
45
|
+
req.write(payload);
|
|
46
|
+
req.end();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function feishuReq(token, method, endpoint, body) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const payload = body ? JSON.stringify(body) : null;
|
|
52
|
+
const req = https.default.request({
|
|
53
|
+
hostname: "open.feishu.cn",
|
|
54
|
+
port: 443,
|
|
55
|
+
path: `/open-apis${endpoint}`,
|
|
56
|
+
method,
|
|
57
|
+
headers: {
|
|
58
|
+
"Authorization": `Bearer ${token}`,
|
|
59
|
+
...payload ? {
|
|
60
|
+
"Content-Type": "application/json",
|
|
61
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
62
|
+
} : {}
|
|
63
|
+
}
|
|
64
|
+
}, (res) => {
|
|
65
|
+
let data = "";
|
|
66
|
+
res.on("data", (c) => data += c);
|
|
67
|
+
res.on("end", () => {
|
|
68
|
+
try {
|
|
69
|
+
const r = JSON.parse(data);
|
|
70
|
+
if (r.code !== 0) reject(new Error(r.msg));
|
|
71
|
+
else resolve(r);
|
|
72
|
+
} catch {
|
|
73
|
+
reject(new Error("Invalid JSON"));
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
req.on("error", reject);
|
|
78
|
+
if (payload) req.write(payload);
|
|
79
|
+
req.end();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
var FeishuConnector = class extends events.EventEmitter {
|
|
83
|
+
config;
|
|
84
|
+
running = false;
|
|
85
|
+
botOpenId = "";
|
|
86
|
+
constructor(config) {
|
|
87
|
+
super();
|
|
88
|
+
this.config = {
|
|
89
|
+
dmPolicy: "allowlist",
|
|
90
|
+
allowFrom: [],
|
|
91
|
+
approvedPairings: [],
|
|
92
|
+
pendingPairings: {},
|
|
93
|
+
...config
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async connect() {
|
|
97
|
+
const token = await getAccessToken(this.config.appId, this.config.appSecret);
|
|
98
|
+
const botInfo = await feishuReq(token, "GET", "/bot/v3/info");
|
|
99
|
+
this.botOpenId = botInfo.bot?.open_id || "";
|
|
100
|
+
await this.loadState();
|
|
101
|
+
this.running = true;
|
|
102
|
+
console.log(chalk.default.green(` 🦅 Feishu: ${botInfo.bot?.app_name || this.config.appId} connected`));
|
|
103
|
+
this.emit("connected", { openId: this.botOpenId });
|
|
104
|
+
}
|
|
105
|
+
disconnect() {
|
|
106
|
+
this.running = false;
|
|
107
|
+
}
|
|
108
|
+
handleChallenge(body) {
|
|
109
|
+
try {
|
|
110
|
+
const r = JSON.parse(body);
|
|
111
|
+
if (r.type === "url_verification") return JSON.stringify({ challenge: r.challenge });
|
|
112
|
+
} catch {}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
async handleWebhook(body) {
|
|
116
|
+
let payload;
|
|
117
|
+
try {
|
|
118
|
+
payload = JSON.parse(body);
|
|
119
|
+
} catch {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const event = payload.event;
|
|
123
|
+
if (!event || payload.header?.event_type !== "im.message.receive_v1") return;
|
|
124
|
+
const senderId = event.sender?.sender_id?.open_id;
|
|
125
|
+
const msgType = event.message?.message_type;
|
|
126
|
+
if (msgType !== "text" || !senderId) return;
|
|
127
|
+
let text = "";
|
|
128
|
+
try {
|
|
129
|
+
text = JSON.parse(event.message.content).text;
|
|
130
|
+
} catch {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!text) return;
|
|
134
|
+
const chatId = event.message?.chat_id || senderId;
|
|
135
|
+
const allowed = await this.checkDMPolicy(senderId, text, chatId);
|
|
136
|
+
if (!allowed) return;
|
|
137
|
+
this.emit("message", {
|
|
138
|
+
id: event.message?.message_id,
|
|
139
|
+
channelId: "feishu",
|
|
140
|
+
from: senderId,
|
|
141
|
+
chatId,
|
|
142
|
+
text,
|
|
143
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
144
|
+
isDM: true
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async checkDMPolicy(from, text, chatId) {
|
|
148
|
+
if (this.config.dmPolicy === "none") return false;
|
|
149
|
+
if (this.config.dmPolicy === "open") return true;
|
|
150
|
+
if (this.config.dmPolicy === "allowlist") {
|
|
151
|
+
if (this.config.allowFrom.includes(from)) return true;
|
|
152
|
+
await this.sendMessage(chatId, "🦅 HyperClaw: Not on allowlist.");
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (this.config.dmPolicy === "pairing") {
|
|
156
|
+
if (this.config.approvedPairings.includes(from)) return true;
|
|
157
|
+
const upper = text.trim().toUpperCase();
|
|
158
|
+
if (this.config.pendingPairings[upper]) {
|
|
159
|
+
this.config.approvedPairings.push(from);
|
|
160
|
+
delete this.config.pendingPairings[upper];
|
|
161
|
+
await this.saveState();
|
|
162
|
+
await this.sendMessage(chatId, "🦅 Paired!");
|
|
163
|
+
this.emit("pairing:approved", {
|
|
164
|
+
userId: from,
|
|
165
|
+
channelId: "feishu"
|
|
166
|
+
});
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
const code = Array.from({ length: 6 }, () => "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"[Math.floor(Math.random() * 32)]).join("");
|
|
170
|
+
this.config.pendingPairings[code] = from;
|
|
171
|
+
await this.saveState();
|
|
172
|
+
await this.sendMessage(chatId, `🦅 Pairing code: ${code}\nApprove: hyperclaw pairing approve feishu ${code}`);
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
async sendMessage(chatId, text) {
|
|
178
|
+
const token = await getAccessToken(this.config.appId, this.config.appSecret);
|
|
179
|
+
await feishuReq(token, "POST", "/im/v1/messages?receive_id_type=chat_id", {
|
|
180
|
+
receive_id: chatId,
|
|
181
|
+
msg_type: "text",
|
|
182
|
+
content: JSON.stringify({ text: text.slice(0, 4e3) })
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
approvePairing(code) {
|
|
186
|
+
const upper = code.toUpperCase();
|
|
187
|
+
if (!this.config.pendingPairings[upper]) return false;
|
|
188
|
+
this.config.approvedPairings.push(this.config.pendingPairings[upper]);
|
|
189
|
+
delete this.config.pendingPairings[upper];
|
|
190
|
+
this.saveState();
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
async loadState() {
|
|
194
|
+
try {
|
|
195
|
+
const s = await fs_extra.default.readJson(STATE_FILE);
|
|
196
|
+
if (s.p) this.config.pendingPairings = s.p;
|
|
197
|
+
if (s.a) this.config.approvedPairings = s.a;
|
|
198
|
+
} catch {}
|
|
199
|
+
}
|
|
200
|
+
async saveState() {
|
|
201
|
+
await fs_extra.default.ensureDir(path.default.dirname(STATE_FILE));
|
|
202
|
+
await fs_extra.default.writeJson(STATE_FILE, {
|
|
203
|
+
p: this.config.pendingPairings,
|
|
204
|
+
a: this.config.approvedPairings
|
|
205
|
+
}, { spaces: 2 });
|
|
206
|
+
}
|
|
207
|
+
isRunning() {
|
|
208
|
+
return this.running;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
//#endregion
|
|
213
|
+
exports.FeishuConnector = FeishuConnector;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
+
const fs_extra = require_chunk.__toESM(require("fs-extra"));
|
|
3
|
+
const path = require_chunk.__toESM(require("path"));
|
|
4
|
+
const os = require_chunk.__toESM(require("os"));
|
|
5
|
+
const child_process = require_chunk.__toESM(require("child_process"));
|
|
6
|
+
const events = require_chunk.__toESM(require("events"));
|
|
7
|
+
|
|
8
|
+
//#region extensions/imessage-native/src/connector.ts
|
|
9
|
+
const STATE_FILE = path.default.join(os.default.homedir(), ".hyperclaw", "imessage-native-state.json");
|
|
10
|
+
const PAIRING_TTL_MS = 60 * 60 * 1e3;
|
|
11
|
+
var IMessageNativeConnector = class extends events.EventEmitter {
|
|
12
|
+
config;
|
|
13
|
+
rpcProc = null;
|
|
14
|
+
running = false;
|
|
15
|
+
lastMessageTs = 0;
|
|
16
|
+
rpcSeq = 1;
|
|
17
|
+
pending = /* @__PURE__ */ new Map();
|
|
18
|
+
buf = "";
|
|
19
|
+
constructor(config = {}) {
|
|
20
|
+
super();
|
|
21
|
+
this.config = {
|
|
22
|
+
cliPath: config.cliPath || process.env.IMSG_PATH || "imsg",
|
|
23
|
+
dbPath: config.dbPath || path.default.join(os.default.homedir(), "Library", "Messages", "chat.db"),
|
|
24
|
+
dmPolicy: config.dmPolicy ?? "pairing",
|
|
25
|
+
allowFrom: config.allowFrom ?? [],
|
|
26
|
+
approvedPairings: config.approvedPairings ?? [],
|
|
27
|
+
pendingPairings: config.pendingPairings ?? {},
|
|
28
|
+
pendingPairingTs: config.pendingPairingTs ?? {}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async connect() {
|
|
32
|
+
if (process.platform !== "darwin") throw new Error("imessage-native is macOS only. For new deployments use BlueBubbles.");
|
|
33
|
+
await this.loadState();
|
|
34
|
+
this.running = true;
|
|
35
|
+
this.emit("connected", {});
|
|
36
|
+
this.startRpc();
|
|
37
|
+
}
|
|
38
|
+
startRpc() {
|
|
39
|
+
if (!this.running) return;
|
|
40
|
+
const bin = this.config.cliPath;
|
|
41
|
+
const args = ["rpc"];
|
|
42
|
+
if (this.config.dbPath) args.push("--db", this.config.dbPath);
|
|
43
|
+
this.rpcProc = (0, child_process.spawn)(bin, args, { stdio: [
|
|
44
|
+
"pipe",
|
|
45
|
+
"pipe",
|
|
46
|
+
"pipe"
|
|
47
|
+
] });
|
|
48
|
+
this.rpcProc.stdout?.on("data", (chunk) => {
|
|
49
|
+
this.buf += chunk.toString();
|
|
50
|
+
const lines = this.buf.split("\n");
|
|
51
|
+
this.buf = lines.pop() || "";
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
if (!line.trim()) continue;
|
|
54
|
+
try {
|
|
55
|
+
this.handleRpcLine(JSON.parse(line));
|
|
56
|
+
} catch {}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
this.rpcProc.stderr?.on("data", () => {});
|
|
60
|
+
this.rpcProc.on("error", () => {});
|
|
61
|
+
this.rpcProc.on("exit", (code) => {
|
|
62
|
+
this.rpcProc = null;
|
|
63
|
+
this.buf = "";
|
|
64
|
+
for (const [, cb] of this.pending) cb.reject(new Error("imsg rpc exited"));
|
|
65
|
+
this.pending.clear();
|
|
66
|
+
if (this.running && code !== 0) setTimeout(() => this.startRpc(), 5e3);
|
|
67
|
+
});
|
|
68
|
+
this.rpcSend("messages.watch", {}).catch(() => {});
|
|
69
|
+
}
|
|
70
|
+
handleRpcLine(msg) {
|
|
71
|
+
if (!msg.id && msg.method === "message" && msg.params) {
|
|
72
|
+
this.onIncomingMessage(msg.params);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (msg.id != null) {
|
|
76
|
+
const cb = this.pending.get(msg.id);
|
|
77
|
+
if (!cb) return;
|
|
78
|
+
this.pending.delete(msg.id);
|
|
79
|
+
if (msg.error) cb.reject(new Error(msg.error.message));
|
|
80
|
+
else cb.resolve(msg.result);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
onIncomingMessage(params) {
|
|
84
|
+
if (params.isFromMe) return;
|
|
85
|
+
const handle = params.handle?.address || params.sender || params.chatId || params.from;
|
|
86
|
+
const text = params.text || params.body || params.message;
|
|
87
|
+
if (!handle || !text) return;
|
|
88
|
+
const ts = params.dateCreated || params.timestamp || Date.now();
|
|
89
|
+
if (ts <= this.lastMessageTs) return;
|
|
90
|
+
this.lastMessageTs = ts;
|
|
91
|
+
this.saveState();
|
|
92
|
+
this.checkDMPolicyAndEmit(handle, text);
|
|
93
|
+
}
|
|
94
|
+
pruneExpiredCodes() {
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
const ts = this.config.pendingPairingTs;
|
|
97
|
+
for (const code of Object.keys(ts)) if (now - ts[code] > PAIRING_TTL_MS) {
|
|
98
|
+
delete this.config.pendingPairings[code];
|
|
99
|
+
delete ts[code];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async checkDMPolicyAndEmit(from, text) {
|
|
103
|
+
if (this.config.dmPolicy === "disabled") return;
|
|
104
|
+
if (this.config.dmPolicy === "open") {
|
|
105
|
+
this.emit("message", {
|
|
106
|
+
chatId: from,
|
|
107
|
+
text
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (this.config.dmPolicy === "allowlist") {
|
|
112
|
+
if (this.config.allowFrom.includes(from)) this.emit("message", {
|
|
113
|
+
chatId: from,
|
|
114
|
+
text
|
|
115
|
+
});
|
|
116
|
+
else await this.sendMessage(from, "HyperClaw: Not on allowlist.");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.pruneExpiredCodes();
|
|
120
|
+
if (this.config.approvedPairings.includes(from)) {
|
|
121
|
+
this.emit("message", {
|
|
122
|
+
chatId: from,
|
|
123
|
+
text
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const code = (text.trim().toUpperCase().match(/[A-Z0-9]{6}/) || [])[0];
|
|
128
|
+
if (code && this.config.pendingPairings[code]) {
|
|
129
|
+
this.config.approvedPairings.push(from);
|
|
130
|
+
delete this.config.pendingPairings[code];
|
|
131
|
+
delete this.config.pendingPairingTs[code];
|
|
132
|
+
await this.saveState();
|
|
133
|
+
await this.sendMessage(from, "Paired!");
|
|
134
|
+
this.emit("pairing:approved", {
|
|
135
|
+
userId: from,
|
|
136
|
+
channelId: "imessage-native"
|
|
137
|
+
});
|
|
138
|
+
this.emit("message", {
|
|
139
|
+
chatId: from,
|
|
140
|
+
text
|
|
141
|
+
});
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const newCode = Array.from({ length: 6 }, () => "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"[Math.floor(Math.random() * 32)]).join("");
|
|
145
|
+
this.config.pendingPairings[newCode] = from;
|
|
146
|
+
this.config.pendingPairingTs[newCode] = Date.now();
|
|
147
|
+
await this.saveState();
|
|
148
|
+
await this.sendMessage(from, `Pairing code: ${newCode}\nApprove: hyperclaw pairing approve imessage-native ${newCode}\n(expires in 1 hour)`);
|
|
149
|
+
}
|
|
150
|
+
async sendMessage(chatId, text) {
|
|
151
|
+
const to = String(chatId).trim();
|
|
152
|
+
if (!to) return;
|
|
153
|
+
if (!this.rpcProc) throw new Error("imsg rpc not running");
|
|
154
|
+
await this.rpcSend("messages.send", {
|
|
155
|
+
to,
|
|
156
|
+
text
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
rpcSend(method, params) {
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
if (!this.rpcProc?.stdin) {
|
|
162
|
+
reject(new Error("imsg rpc stdin unavailable"));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const id = this.rpcSeq++;
|
|
166
|
+
const req = {
|
|
167
|
+
jsonrpc: "2.0",
|
|
168
|
+
id,
|
|
169
|
+
method,
|
|
170
|
+
params
|
|
171
|
+
};
|
|
172
|
+
this.pending.set(id, {
|
|
173
|
+
resolve,
|
|
174
|
+
reject
|
|
175
|
+
});
|
|
176
|
+
this.rpcProc.stdin.write(JSON.stringify(req) + "\n");
|
|
177
|
+
const timer = setTimeout(() => {
|
|
178
|
+
if (this.pending.has(id)) {
|
|
179
|
+
this.pending.delete(id);
|
|
180
|
+
reject(new Error(`imsg rpc timeout for method ${method}`));
|
|
181
|
+
}
|
|
182
|
+
}, 15e3);
|
|
183
|
+
const orig = this.pending.get(id);
|
|
184
|
+
this.pending.set(id, {
|
|
185
|
+
resolve: (v) => {
|
|
186
|
+
clearTimeout(timer);
|
|
187
|
+
orig.resolve(v);
|
|
188
|
+
},
|
|
189
|
+
reject: (e) => {
|
|
190
|
+
clearTimeout(timer);
|
|
191
|
+
orig.reject(e);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
disconnect() {
|
|
197
|
+
this.running = false;
|
|
198
|
+
this.rpcProc?.kill();
|
|
199
|
+
this.rpcProc = null;
|
|
200
|
+
this.buf = "";
|
|
201
|
+
for (const [, cb] of this.pending) cb.reject(new Error("disconnected"));
|
|
202
|
+
this.pending.clear();
|
|
203
|
+
}
|
|
204
|
+
async loadState() {
|
|
205
|
+
try {
|
|
206
|
+
const s = await fs_extra.default.readJson(STATE_FILE);
|
|
207
|
+
this.lastMessageTs = s.lastTs || 0;
|
|
208
|
+
if (s.p) this.config.pendingPairings = s.p;
|
|
209
|
+
if (s.a) this.config.approvedPairings = s.a;
|
|
210
|
+
if (s.pts) this.config.pendingPairingTs = s.pts;
|
|
211
|
+
} catch {}
|
|
212
|
+
}
|
|
213
|
+
async saveState() {
|
|
214
|
+
await fs_extra.default.ensureDir(path.default.dirname(STATE_FILE));
|
|
215
|
+
await fs_extra.default.writeJson(STATE_FILE, {
|
|
216
|
+
lastTs: this.lastMessageTs,
|
|
217
|
+
p: this.config.pendingPairings,
|
|
218
|
+
a: this.config.approvedPairings,
|
|
219
|
+
pts: this.config.pendingPairingTs
|
|
220
|
+
}, { spaces: 2 });
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
//#endregion
|
|
225
|
+
exports.IMessageNativeConnector = IMessageNativeConnector;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
+
const chalk = require_chunk.__toESM(require("chalk"));
|
|
3
|
+
const events = require_chunk.__toESM(require("events"));
|
|
4
|
+
const irc = require_chunk.__toESM(require("irc"));
|
|
5
|
+
|
|
6
|
+
//#region extensions/irc/src/connector.ts
|
|
7
|
+
function resolveEnvConfig(cfg) {
|
|
8
|
+
return {
|
|
9
|
+
server: cfg.server || process.env["IRC_HOST"] || "",
|
|
10
|
+
port: cfg.port ?? (process.env["IRC_PORT"] ? parseInt(process.env["IRC_PORT"], 10) : void 0),
|
|
11
|
+
tls: cfg.tls ?? (process.env["IRC_TLS"] === "true" || process.env["IRC_TLS"] === "1"),
|
|
12
|
+
nick: cfg.nick || process.env["IRC_NICK"] || "hyperclaw-bot",
|
|
13
|
+
username: cfg.username || process.env["IRC_USERNAME"],
|
|
14
|
+
realname: cfg.realname || process.env["IRC_REALNAME"],
|
|
15
|
+
password: cfg.password || process.env["IRC_PASSWORD"],
|
|
16
|
+
channels: cfg.channels?.length ? cfg.channels : process.env["IRC_CHANNELS"]?.split(",").map((c) => c.trim()).filter(Boolean) ?? [],
|
|
17
|
+
nickserv: cfg.nickserv ?? (process.env["IRC_NICKSERV_PASSWORD"] ? {
|
|
18
|
+
enabled: true,
|
|
19
|
+
service: "NickServ",
|
|
20
|
+
password: process.env["IRC_NICKSERV_PASSWORD"],
|
|
21
|
+
registerEmail: process.env["IRC_NICKSERV_REGISTER_EMAIL"]
|
|
22
|
+
} : void 0),
|
|
23
|
+
dmPolicy: cfg.dmPolicy ?? "pairing",
|
|
24
|
+
allowFrom: cfg.allowFrom ?? [],
|
|
25
|
+
groupPolicy: cfg.groupPolicy ?? "allowlist",
|
|
26
|
+
groupAllowFrom: cfg.groupAllowFrom ?? [],
|
|
27
|
+
groups: cfg.groups ?? {},
|
|
28
|
+
dangerouslyAllowNameMatching: cfg.dangerouslyAllowNameMatching ?? false
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Match a sender (nick!user@host or bare nick) against an allowFrom entry. */
|
|
32
|
+
function matchSender(sender, pattern, allowBareNick) {
|
|
33
|
+
if (pattern === "*") return true;
|
|
34
|
+
if (pattern.startsWith("id:")) {
|
|
35
|
+
const id = pattern.slice(3);
|
|
36
|
+
return sender === id || sender.startsWith(id + "!");
|
|
37
|
+
}
|
|
38
|
+
if (pattern === sender) return true;
|
|
39
|
+
if (allowBareNick) {
|
|
40
|
+
const nick = sender.split("!")[0];
|
|
41
|
+
return nick === pattern;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
function senderAllowed(sender, allowList, allowBareNick) {
|
|
46
|
+
if (!allowList.length) return false;
|
|
47
|
+
return allowList.some((p) => matchSender(sender, p, allowBareNick));
|
|
48
|
+
}
|
|
49
|
+
/** Return the first matching ToolsBySender policy for a sender. */
|
|
50
|
+
function resolveToolsBySender(sender, toolsBySender, allowBareNick) {
|
|
51
|
+
for (const [pattern, policy] of Object.entries(toolsBySender)) {
|
|
52
|
+
const pat = pattern.startsWith("id:") ? pattern : `id:${pattern}`;
|
|
53
|
+
if (matchSender(sender, pat === "id:*" ? "*" : pat, allowBareNick)) return policy;
|
|
54
|
+
}
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
/** Resolve effective tool policy for a sender in a channel. */
|
|
58
|
+
function resolveToolPolicy(sender, channelCfg, allowBareNick) {
|
|
59
|
+
if (!channelCfg) return void 0;
|
|
60
|
+
if (channelCfg.toolsBySender) {
|
|
61
|
+
const byS = resolveToolsBySender(sender, channelCfg.toolsBySender, allowBareNick);
|
|
62
|
+
if (byS) return byS;
|
|
63
|
+
}
|
|
64
|
+
return channelCfg.tools;
|
|
65
|
+
}
|
|
66
|
+
/** Find the best group config for a channel name. */
|
|
67
|
+
function getGroupConfig(channel, groups) {
|
|
68
|
+
return groups[channel] ?? groups["*"];
|
|
69
|
+
}
|
|
70
|
+
var IrcConnector = class extends events.EventEmitter {
|
|
71
|
+
client = null;
|
|
72
|
+
config;
|
|
73
|
+
constructor(rawConfig) {
|
|
74
|
+
super();
|
|
75
|
+
this.config = resolveEnvConfig(rawConfig);
|
|
76
|
+
}
|
|
77
|
+
async connect() {
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
const cfg = this.config;
|
|
80
|
+
const useTls = cfg.tls ?? false;
|
|
81
|
+
const port = cfg.port ?? (useTls ? 6697 : 6667);
|
|
82
|
+
const channels = (cfg.channels ?? []).map((c) => c.startsWith("#") ? c : `#${c}`);
|
|
83
|
+
this.client = new irc.default.Client(cfg.server, cfg.nick, {
|
|
84
|
+
port,
|
|
85
|
+
secure: useTls,
|
|
86
|
+
channels,
|
|
87
|
+
userName: cfg.username ?? cfg.nick,
|
|
88
|
+
realName: cfg.realname ?? "HyperClaw IRC Bot",
|
|
89
|
+
password: cfg.password,
|
|
90
|
+
autoConnect: true,
|
|
91
|
+
debug: false,
|
|
92
|
+
showErrors: true,
|
|
93
|
+
stripColors: true
|
|
94
|
+
});
|
|
95
|
+
this.client.on("registered", () => {
|
|
96
|
+
console.log(chalk.default.green(` 🦅 IRC: ${cfg.nick} on ${cfg.server}:${port}${useTls ? " (TLS)" : ""} connected`));
|
|
97
|
+
this._handleNickServ();
|
|
98
|
+
this.emit("connected", {
|
|
99
|
+
server: cfg.server,
|
|
100
|
+
nick: cfg.nick,
|
|
101
|
+
port,
|
|
102
|
+
tls: useTls
|
|
103
|
+
});
|
|
104
|
+
resolve();
|
|
105
|
+
});
|
|
106
|
+
this.client.on("error", (err) => {
|
|
107
|
+
console.log(chalk.default.yellow(` ⚠ IRC error: ${err.message}`));
|
|
108
|
+
reject(err);
|
|
109
|
+
});
|
|
110
|
+
this.client.on("message", (from, to, message) => {
|
|
111
|
+
const isChannel = to.startsWith("#");
|
|
112
|
+
if (isChannel) this._handleChannelMessage(from, to, message);
|
|
113
|
+
else this._handleDm(from, message);
|
|
114
|
+
});
|
|
115
|
+
this.client.on("pm", (from, message) => {
|
|
116
|
+
this._handleDm(from, message);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
_handleNickServ() {
|
|
121
|
+
const ns = this.config.nickserv;
|
|
122
|
+
if (!ns?.enabled) return;
|
|
123
|
+
const service = ns.service ?? "NickServ";
|
|
124
|
+
const nsPassword = ns.password ?? process.env["IRC_NICKSERV_PASSWORD"];
|
|
125
|
+
if (ns.register && ns.registerEmail) {
|
|
126
|
+
console.log(chalk.default.gray(` IRC NickServ: attempting REGISTER`));
|
|
127
|
+
this.client.say(service, `REGISTER ${nsPassword} ${ns.registerEmail}`);
|
|
128
|
+
} else if (nsPassword) {
|
|
129
|
+
console.log(chalk.default.gray(` IRC NickServ: identifying`));
|
|
130
|
+
this.client.say(service, `IDENTIFY ${nsPassword}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
_handleChannelMessage(from, channel, message) {
|
|
134
|
+
const cfg = this.config;
|
|
135
|
+
const text = message.trim();
|
|
136
|
+
if (!text) return;
|
|
137
|
+
const groupCfg = getGroupConfig(channel, cfg.groups ?? {});
|
|
138
|
+
if (cfg.groupPolicy === "allowlist") {
|
|
139
|
+
const defined = cfg.groups && (cfg.groups[channel] !== void 0 || cfg.groups["*"] !== void 0);
|
|
140
|
+
if (!defined) {
|
|
141
|
+
console.log(chalk.default.gray(` irc: drop channel ${channel} (groupPolicy=allowlist, not in groups)`));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const allowBareNick = cfg.dangerouslyAllowNameMatching ?? false;
|
|
146
|
+
const perChannelAllow = groupCfg?.allowFrom ?? [];
|
|
147
|
+
const globalGroupAllow = cfg.groupAllowFrom ?? [];
|
|
148
|
+
const effectiveAllow = perChannelAllow.length ? perChannelAllow : globalGroupAllow;
|
|
149
|
+
if (effectiveAllow.length && !senderAllowed(from, effectiveAllow, allowBareNick)) {
|
|
150
|
+
console.log(chalk.default.gray(` irc: drop group sender ${from} (policy=allowlist)`));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const requireMention = groupCfg?.requireMention ?? true;
|
|
154
|
+
if (requireMention) {
|
|
155
|
+
const mentionPatterns = [
|
|
156
|
+
cfg.nick,
|
|
157
|
+
`${cfg.nick}:`,
|
|
158
|
+
`@${cfg.nick}`,
|
|
159
|
+
`${cfg.nick},`
|
|
160
|
+
];
|
|
161
|
+
const mentioned = mentionPatterns.some((p) => text.toLowerCase().startsWith(p.toLowerCase()));
|
|
162
|
+
if (!mentioned) {
|
|
163
|
+
console.log(chalk.default.gray(` irc: drop channel ${channel} (missing-mention)`));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const toolPolicy = resolveToolPolicy(from, groupCfg, allowBareNick);
|
|
168
|
+
const payload = {
|
|
169
|
+
chatId: channel,
|
|
170
|
+
text,
|
|
171
|
+
from,
|
|
172
|
+
to: channel,
|
|
173
|
+
isChannel: true,
|
|
174
|
+
...toolPolicy ? { toolPolicy } : {}
|
|
175
|
+
};
|
|
176
|
+
this.emit("message", payload);
|
|
177
|
+
}
|
|
178
|
+
_handleDm(from, message) {
|
|
179
|
+
const cfg = this.config;
|
|
180
|
+
const text = message.trim();
|
|
181
|
+
if (!text) return;
|
|
182
|
+
if (cfg.dmPolicy === "none") return;
|
|
183
|
+
if (cfg.dmPolicy === "allowlist") {
|
|
184
|
+
const allowBareNick = cfg.dangerouslyAllowNameMatching ?? false;
|
|
185
|
+
const allowList = cfg.allowFrom ?? [];
|
|
186
|
+
if (!senderAllowed(from, allowList, allowBareNick)) {
|
|
187
|
+
console.log(chalk.default.gray(` irc: drop DM from ${from} (dmPolicy=allowlist)`));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const payload = {
|
|
192
|
+
chatId: from,
|
|
193
|
+
text,
|
|
194
|
+
from,
|
|
195
|
+
to: from,
|
|
196
|
+
isChannel: false
|
|
197
|
+
};
|
|
198
|
+
this.emit("message", payload);
|
|
199
|
+
}
|
|
200
|
+
async sendMessage(chatId, text) {
|
|
201
|
+
if (!this.client) throw new Error("IRC not connected");
|
|
202
|
+
const lines = text.split("\n").filter(Boolean);
|
|
203
|
+
for (const line of lines) this.client.say(chatId, line);
|
|
204
|
+
}
|
|
205
|
+
async disconnect() {
|
|
206
|
+
if (this.client) {
|
|
207
|
+
this.client.disconnect();
|
|
208
|
+
this.client = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
getToolPolicy(sender, channel) {
|
|
212
|
+
const groupCfg = getGroupConfig(channel, this.config.groups ?? {});
|
|
213
|
+
return resolveToolPolicy(sender, groupCfg, this.config.dangerouslyAllowNameMatching ?? false);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
//#endregion
|
|
218
|
+
exports.IrcConnector = IrcConnector;
|