@nextclaw/channel-runtime 0.4.1 → 0.4.2
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/dist/index.d.ts +1 -26
- package/dist/index.js +11 -475
- package/package.json +2 -3
package/dist/index.d.ts
CHANGED
|
@@ -97,26 +97,6 @@ declare class EmailChannel extends BaseChannel<Config["channels"]["email"]> {
|
|
|
97
97
|
private fetchNewMessages;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
declare class FeishuChannel extends BaseChannel<Config["channels"]["feishu"]> {
|
|
101
|
-
name: string;
|
|
102
|
-
private clients;
|
|
103
|
-
private processedMessageIds;
|
|
104
|
-
private processedSet;
|
|
105
|
-
private readonly inboundMediaResolver;
|
|
106
|
-
constructor(config: Config["channels"]["feishu"], bus: MessageBus);
|
|
107
|
-
start(): Promise<void>;
|
|
108
|
-
stop(): Promise<void>;
|
|
109
|
-
send(msg: OutboundMessage): Promise<void>;
|
|
110
|
-
private handleIncoming;
|
|
111
|
-
private isDuplicate;
|
|
112
|
-
private addReaction;
|
|
113
|
-
private resolveOutboundAccount;
|
|
114
|
-
private isAllowedByPolicy;
|
|
115
|
-
private resolveMentionState;
|
|
116
|
-
private convertResource;
|
|
117
|
-
private buildInboundPayload;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
100
|
declare class MochatChannel extends BaseChannel<Config["channels"]["mochat"]> {
|
|
121
101
|
name: string;
|
|
122
102
|
private socket;
|
|
@@ -310,11 +290,6 @@ declare const BUILTIN_CHANNEL_RUNTIMES: {
|
|
|
310
290
|
readonly isEnabled: (config: Config) => boolean;
|
|
311
291
|
readonly createChannel: (context: BuiltinChannelCreateContext) => DiscordChannel;
|
|
312
292
|
};
|
|
313
|
-
readonly feishu: {
|
|
314
|
-
readonly id: "feishu";
|
|
315
|
-
readonly isEnabled: (config: Config) => boolean;
|
|
316
|
-
readonly createChannel: (context: BuiltinChannelCreateContext) => FeishuChannel;
|
|
317
|
-
};
|
|
318
293
|
readonly mochat: {
|
|
319
294
|
readonly id: "mochat";
|
|
320
295
|
readonly isEnabled: (config: Config) => boolean;
|
|
@@ -351,4 +326,4 @@ declare const BUILTIN_CHANNEL_PLUGIN_IDS: BuiltinChannelId[];
|
|
|
351
326
|
declare function listBuiltinChannelRuntimes(): BuiltinChannelRuntime[];
|
|
352
327
|
declare function resolveBuiltinChannelRuntime(channelId: string): BuiltinChannelRuntime;
|
|
353
328
|
|
|
354
|
-
export { BUILTIN_CHANNEL_PLUGIN_IDS, type BuiltinChannelId, type BuiltinChannelRuntime, DingTalkChannel, DiscordChannel, EmailChannel,
|
|
329
|
+
export { BUILTIN_CHANNEL_PLUGIN_IDS, type BuiltinChannelId, type BuiltinChannelRuntime, DingTalkChannel, DiscordChannel, EmailChannel, MochatChannel, QQChannel, SlackChannel, TelegramChannel, WeComChannel, WhatsAppChannel, listBuiltinChannelRuntimes, resolveBuiltinChannelRuntime };
|
package/dist/index.js
CHANGED
|
@@ -1264,469 +1264,11 @@ var EmailChannel = class extends BaseChannel {
|
|
|
1264
1264
|
}
|
|
1265
1265
|
};
|
|
1266
1266
|
|
|
1267
|
-
// src/channels/feishu-message-support.ts
|
|
1268
|
-
var TABLE_RE = /((?:^[ \t]*\|.+\|[ \t]*\n)(?:^[ \t]*\|[-:\s|]+\|[ \t]*\n)(?:^[ \t]*\|.+\|[ \t]*\n?)+)/gm;
|
|
1269
|
-
function extractSenderInfo(sender) {
|
|
1270
|
-
const senderIdObj = sender.sender_id ?? {};
|
|
1271
|
-
const senderOpenId = senderIdObj.open_id || sender.open_id || "";
|
|
1272
|
-
const senderUserId = senderIdObj.user_id || sender.user_id || "";
|
|
1273
|
-
const senderUnionId = senderIdObj.union_id || sender.union_id || "";
|
|
1274
|
-
return {
|
|
1275
|
-
senderId: senderOpenId || senderUserId || senderUnionId || "",
|
|
1276
|
-
senderType: sender.sender_type ?? sender.senderType,
|
|
1277
|
-
senderOpenId: senderOpenId || void 0,
|
|
1278
|
-
senderUserId: senderUserId || void 0,
|
|
1279
|
-
senderUnionId: senderUnionId || void 0
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
function extractMessageInfo(message) {
|
|
1283
|
-
const chatId = message.chat_id ?? "";
|
|
1284
|
-
const chatType = message.chat_type ?? "";
|
|
1285
|
-
return {
|
|
1286
|
-
chatId,
|
|
1287
|
-
chatType,
|
|
1288
|
-
isGroup: chatType === "group",
|
|
1289
|
-
msgType: message.msg_type ?? message.message_type ?? "",
|
|
1290
|
-
messageId: message.message_id ?? "",
|
|
1291
|
-
rawContent: typeof message.content === "string" ? message.content : ""
|
|
1292
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
function extractMentions(root, message) {
|
|
1295
|
-
if (Array.isArray(root.mentions)) {
|
|
1296
|
-
return root.mentions;
|
|
1297
|
-
}
|
|
1298
|
-
if (Array.isArray(message.mentions)) {
|
|
1299
|
-
return message.mentions;
|
|
1300
|
-
}
|
|
1301
|
-
return [];
|
|
1302
|
-
}
|
|
1303
|
-
function buildInboundMetadata(params) {
|
|
1304
|
-
return {
|
|
1305
|
-
account_id: params.accountId,
|
|
1306
|
-
accountId: params.accountId,
|
|
1307
|
-
message_id: params.messageInfo.messageId,
|
|
1308
|
-
chat_id: params.messageInfo.chatId,
|
|
1309
|
-
chat_type: params.messageInfo.chatType,
|
|
1310
|
-
msg_type: params.messageInfo.msgType,
|
|
1311
|
-
is_group: params.messageInfo.isGroup,
|
|
1312
|
-
peer_kind: params.messageInfo.isGroup ? "group" : "direct",
|
|
1313
|
-
peer_id: params.messageInfo.isGroup ? params.messageInfo.chatId : params.senderInfo.senderId,
|
|
1314
|
-
sender_open_id: params.senderInfo.senderOpenId,
|
|
1315
|
-
sender_user_id: params.senderInfo.senderUserId,
|
|
1316
|
-
sender_union_id: params.senderInfo.senderUnionId,
|
|
1317
|
-
was_mentioned: params.mentionState.wasMentioned,
|
|
1318
|
-
require_mention: params.mentionState.requireMention
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
function inferFeishuResourceMimeType(resourceType) {
|
|
1322
|
-
if (resourceType === "image" || resourceType === "sticker") {
|
|
1323
|
-
return "image/*";
|
|
1324
|
-
}
|
|
1325
|
-
if (resourceType === "audio") {
|
|
1326
|
-
return "audio/*";
|
|
1327
|
-
}
|
|
1328
|
-
return void 0;
|
|
1329
|
-
}
|
|
1330
|
-
function buildFeishuCardElements(content) {
|
|
1331
|
-
const elements = [];
|
|
1332
|
-
let lastEnd = 0;
|
|
1333
|
-
for (const match of content.matchAll(TABLE_RE)) {
|
|
1334
|
-
const start = match.index ?? 0;
|
|
1335
|
-
const tableText = match[1] ?? "";
|
|
1336
|
-
const before = content.slice(lastEnd, start).trim();
|
|
1337
|
-
if (before) {
|
|
1338
|
-
elements.push({ tag: "markdown", content: before });
|
|
1339
|
-
}
|
|
1340
|
-
elements.push(parseMarkdownTable(tableText) ?? { tag: "markdown", content: tableText });
|
|
1341
|
-
lastEnd = start + tableText.length;
|
|
1342
|
-
}
|
|
1343
|
-
const remaining = content.slice(lastEnd).trim();
|
|
1344
|
-
if (remaining) {
|
|
1345
|
-
elements.push({ tag: "markdown", content: remaining });
|
|
1346
|
-
}
|
|
1347
|
-
if (!elements.length) {
|
|
1348
|
-
elements.push({ tag: "markdown", content });
|
|
1349
|
-
}
|
|
1350
|
-
return elements;
|
|
1351
|
-
}
|
|
1352
|
-
function parseMarkdownTable(tableText) {
|
|
1353
|
-
const lines = tableText.trim().split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1354
|
-
if (lines.length < 3) {
|
|
1355
|
-
return null;
|
|
1356
|
-
}
|
|
1357
|
-
const split = (line) => line.replace(/^\|+|\|+$/g, "").split("|").map((item) => item.trim());
|
|
1358
|
-
const headers = split(lines[0]);
|
|
1359
|
-
const rows = lines.slice(2).map(split);
|
|
1360
|
-
return {
|
|
1361
|
-
tag: "table",
|
|
1362
|
-
page_size: rows.length + 1,
|
|
1363
|
-
columns: headers.map((header, index) => ({
|
|
1364
|
-
tag: "column",
|
|
1365
|
-
name: `c${index}`,
|
|
1366
|
-
display_name: header,
|
|
1367
|
-
width: "auto"
|
|
1368
|
-
})),
|
|
1369
|
-
rows: rows.map((row) => {
|
|
1370
|
-
const values = {};
|
|
1371
|
-
headers.forEach((_, index) => {
|
|
1372
|
-
values[`c${index}`] = row[index] ?? "";
|
|
1373
|
-
});
|
|
1374
|
-
return values;
|
|
1375
|
-
})
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
// src/channels/feishu.ts
|
|
1380
|
-
import {
|
|
1381
|
-
buildFeishuConvertContext,
|
|
1382
|
-
convertFeishuMessageContent,
|
|
1383
|
-
getDefaultFeishuAccountId,
|
|
1384
|
-
getEnabledFeishuAccounts,
|
|
1385
|
-
LarkClient
|
|
1386
|
-
} from "@nextclaw/feishu-core";
|
|
1387
|
-
|
|
1388
|
-
// src/channels/feishu-inbound-media.ts
|
|
1389
|
-
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1390
|
-
import { join as join2 } from "path";
|
|
1391
|
-
var DEFAULT_FEISHU_MEDIA_MAX_MB = 20;
|
|
1392
|
-
function sanitizeAttachmentName2(value) {
|
|
1393
|
-
return value.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "") || "attachment";
|
|
1394
|
-
}
|
|
1395
|
-
function inferAttachmentExtension(resourceType, mimeType) {
|
|
1396
|
-
const normalizedMime = mimeType?.toLowerCase();
|
|
1397
|
-
if (normalizedMime === "image/jpeg") {
|
|
1398
|
-
return ".jpg";
|
|
1399
|
-
}
|
|
1400
|
-
if (normalizedMime === "image/png") {
|
|
1401
|
-
return ".png";
|
|
1402
|
-
}
|
|
1403
|
-
if (normalizedMime === "image/webp") {
|
|
1404
|
-
return ".webp";
|
|
1405
|
-
}
|
|
1406
|
-
if (normalizedMime === "image/gif") {
|
|
1407
|
-
return ".gif";
|
|
1408
|
-
}
|
|
1409
|
-
if (normalizedMime === "audio/ogg") {
|
|
1410
|
-
return ".ogg";
|
|
1411
|
-
}
|
|
1412
|
-
if (normalizedMime === "audio/mpeg") {
|
|
1413
|
-
return ".mp3";
|
|
1414
|
-
}
|
|
1415
|
-
if (normalizedMime === "application/pdf") {
|
|
1416
|
-
return ".pdf";
|
|
1417
|
-
}
|
|
1418
|
-
if (resourceType === "image") {
|
|
1419
|
-
return ".jpg";
|
|
1420
|
-
}
|
|
1421
|
-
if (resourceType === "audio") {
|
|
1422
|
-
return ".ogg";
|
|
1423
|
-
}
|
|
1424
|
-
if (resourceType === "sticker") {
|
|
1425
|
-
return ".webp";
|
|
1426
|
-
}
|
|
1427
|
-
return ".bin";
|
|
1428
|
-
}
|
|
1429
|
-
function buildAttachmentFileName(params) {
|
|
1430
|
-
const extension = inferAttachmentExtension(params.resource.type, params.mimeType);
|
|
1431
|
-
const resourceId = sanitizeAttachmentName2(params.resource.fileKey).slice(0, 64);
|
|
1432
|
-
const messageId = sanitizeAttachmentName2(params.messageId).slice(0, 48);
|
|
1433
|
-
const preferredName = params.resource.fileName?.trim() ? sanitizeAttachmentName2(params.resource.fileName.trim()) : `${params.resource.type}${extension}`;
|
|
1434
|
-
const baseName = preferredName.includes(".") ? preferredName : `${preferredName}${extension}`;
|
|
1435
|
-
return `feishu_${messageId}_${resourceId}_${baseName}`;
|
|
1436
|
-
}
|
|
1437
|
-
function resolveMessageResourceType(resourceType) {
|
|
1438
|
-
return resourceType === "image" ? "image" : "file";
|
|
1439
|
-
}
|
|
1440
|
-
var FeishuInboundMediaResolver = class {
|
|
1441
|
-
maxBytes;
|
|
1442
|
-
constructor(maxMb) {
|
|
1443
|
-
this.maxBytes = Math.max(1, maxMb ?? DEFAULT_FEISHU_MEDIA_MAX_MB) * 1024 * 1024;
|
|
1444
|
-
}
|
|
1445
|
-
async resolve(params) {
|
|
1446
|
-
const { client, messageId, resource } = params;
|
|
1447
|
-
const mimeType = inferFeishuResourceMimeType(resource.type);
|
|
1448
|
-
const baseAttachment = {
|
|
1449
|
-
id: resource.fileKey,
|
|
1450
|
-
name: resource.fileName,
|
|
1451
|
-
source: "feishu",
|
|
1452
|
-
status: "remote-only",
|
|
1453
|
-
mimeType
|
|
1454
|
-
};
|
|
1455
|
-
if (!messageId?.trim()) {
|
|
1456
|
-
return {
|
|
1457
|
-
...baseAttachment,
|
|
1458
|
-
errorCode: "invalid_payload"
|
|
1459
|
-
};
|
|
1460
|
-
}
|
|
1461
|
-
try {
|
|
1462
|
-
const downloaded = await client.downloadMessageResource({
|
|
1463
|
-
messageId,
|
|
1464
|
-
fileKey: resource.fileKey,
|
|
1465
|
-
type: resolveMessageResourceType(resource.type)
|
|
1466
|
-
});
|
|
1467
|
-
if (downloaded.buffer.length > this.maxBytes) {
|
|
1468
|
-
return {
|
|
1469
|
-
...baseAttachment,
|
|
1470
|
-
size: downloaded.buffer.length,
|
|
1471
|
-
errorCode: "too_large"
|
|
1472
|
-
};
|
|
1473
|
-
}
|
|
1474
|
-
const mediaDir = join2(getDataPath(), "media");
|
|
1475
|
-
mkdirSync2(mediaDir, { recursive: true });
|
|
1476
|
-
const fileName = buildAttachmentFileName({ messageId, resource, mimeType });
|
|
1477
|
-
const filePath = join2(mediaDir, fileName);
|
|
1478
|
-
writeFileSync2(filePath, downloaded.buffer);
|
|
1479
|
-
return {
|
|
1480
|
-
...baseAttachment,
|
|
1481
|
-
name: fileName,
|
|
1482
|
-
path: filePath,
|
|
1483
|
-
size: downloaded.buffer.length,
|
|
1484
|
-
status: "ready"
|
|
1485
|
-
};
|
|
1486
|
-
} catch {
|
|
1487
|
-
return {
|
|
1488
|
-
...baseAttachment,
|
|
1489
|
-
errorCode: "download_failed"
|
|
1490
|
-
};
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
};
|
|
1494
|
-
|
|
1495
|
-
// src/channels/feishu.ts
|
|
1496
|
-
function isRecord(value) {
|
|
1497
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1498
|
-
}
|
|
1499
|
-
var FeishuChannel = class extends BaseChannel {
|
|
1500
|
-
name = "feishu";
|
|
1501
|
-
clients = /* @__PURE__ */ new Map();
|
|
1502
|
-
processedMessageIds = [];
|
|
1503
|
-
processedSet = /* @__PURE__ */ new Set();
|
|
1504
|
-
inboundMediaResolver;
|
|
1505
|
-
constructor(config, bus) {
|
|
1506
|
-
super(config, bus);
|
|
1507
|
-
this.inboundMediaResolver = new FeishuInboundMediaResolver(this.config.mediaMaxMb);
|
|
1508
|
-
}
|
|
1509
|
-
async start() {
|
|
1510
|
-
const accounts = getEnabledFeishuAccounts(this.config);
|
|
1511
|
-
if (accounts.length === 0) {
|
|
1512
|
-
throw new Error("Feishu appId/appSecret not configured");
|
|
1513
|
-
}
|
|
1514
|
-
this.running = true;
|
|
1515
|
-
for (const account of accounts) {
|
|
1516
|
-
const client = LarkClient.fromAccount(account);
|
|
1517
|
-
const activeAccount = {
|
|
1518
|
-
accountId: account.accountId,
|
|
1519
|
-
client
|
|
1520
|
-
};
|
|
1521
|
-
const probe = await client.probe();
|
|
1522
|
-
if (probe.ok) {
|
|
1523
|
-
activeAccount.botOpenId = probe.botOpenId;
|
|
1524
|
-
activeAccount.botName = probe.botName;
|
|
1525
|
-
}
|
|
1526
|
-
client.startWebsocket(async (data) => {
|
|
1527
|
-
await this.handleIncoming(account.accountId, data);
|
|
1528
|
-
});
|
|
1529
|
-
this.clients.set(account.accountId, activeAccount);
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
async stop() {
|
|
1533
|
-
this.running = false;
|
|
1534
|
-
for (const account of this.clients.values()) {
|
|
1535
|
-
account.client.closeWebsocket();
|
|
1536
|
-
}
|
|
1537
|
-
this.clients.clear();
|
|
1538
|
-
}
|
|
1539
|
-
async send(msg) {
|
|
1540
|
-
const account = this.resolveOutboundAccount(msg.metadata);
|
|
1541
|
-
if (!account) {
|
|
1542
|
-
return;
|
|
1543
|
-
}
|
|
1544
|
-
const receiveIdType = msg.chatId.startsWith("oc_") ? "chat_id" : "open_id";
|
|
1545
|
-
const elements = buildFeishuCardElements(msg.content ?? "");
|
|
1546
|
-
const card = {
|
|
1547
|
-
config: { wide_screen_mode: true },
|
|
1548
|
-
elements
|
|
1549
|
-
};
|
|
1550
|
-
const content = JSON.stringify(card);
|
|
1551
|
-
await account.client.sendInteractiveCard({
|
|
1552
|
-
receiveId: msg.chatId,
|
|
1553
|
-
receiveIdType,
|
|
1554
|
-
content
|
|
1555
|
-
});
|
|
1556
|
-
}
|
|
1557
|
-
async handleIncoming(accountId, data) {
|
|
1558
|
-
const account = this.clients.get(accountId);
|
|
1559
|
-
if (!account) {
|
|
1560
|
-
return;
|
|
1561
|
-
}
|
|
1562
|
-
const root = isRecord(data.event) ? data.event : data;
|
|
1563
|
-
const message = root.message ?? data.message ?? {};
|
|
1564
|
-
const sender = root.sender ?? message.sender ?? data.sender ?? {};
|
|
1565
|
-
const senderInfo = extractSenderInfo(sender);
|
|
1566
|
-
if (senderInfo.senderType === "bot") {
|
|
1567
|
-
return;
|
|
1568
|
-
}
|
|
1569
|
-
const messageInfo = extractMessageInfo(message);
|
|
1570
|
-
if (!senderInfo.senderId || !messageInfo.chatId) {
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
|
-
if (!this.isAllowedByPolicy({ senderId: senderInfo.senderId, chatId: messageInfo.chatId, isGroup: messageInfo.isGroup })) {
|
|
1574
|
-
return;
|
|
1575
|
-
}
|
|
1576
|
-
if (messageInfo.messageId && this.isDuplicate(`${accountId}:${messageInfo.messageId}`)) {
|
|
1577
|
-
return;
|
|
1578
|
-
}
|
|
1579
|
-
if (messageInfo.messageId) {
|
|
1580
|
-
await this.addReaction(account, messageInfo.messageId, "THUMBSUP");
|
|
1581
|
-
}
|
|
1582
|
-
const mentions = extractMentions(root, message);
|
|
1583
|
-
const mentionState = this.resolveMentionState({
|
|
1584
|
-
account,
|
|
1585
|
-
mentions,
|
|
1586
|
-
chatId: messageInfo.chatId,
|
|
1587
|
-
isGroup: messageInfo.isGroup,
|
|
1588
|
-
rawContent: messageInfo.rawContent
|
|
1589
|
-
});
|
|
1590
|
-
if (mentionState.requireMention && !mentionState.wasMentioned) {
|
|
1591
|
-
return;
|
|
1592
|
-
}
|
|
1593
|
-
const payload = await this.buildInboundPayload(account, messageInfo, mentions);
|
|
1594
|
-
if (!payload) {
|
|
1595
|
-
return;
|
|
1596
|
-
}
|
|
1597
|
-
await this.handleMessage({
|
|
1598
|
-
senderId: senderInfo.senderId,
|
|
1599
|
-
// Always route by Feishu chat_id so DM/group sessions are stable.
|
|
1600
|
-
chatId: messageInfo.chatId,
|
|
1601
|
-
content: payload.content,
|
|
1602
|
-
attachments: payload.attachments,
|
|
1603
|
-
metadata: buildInboundMetadata({
|
|
1604
|
-
accountId,
|
|
1605
|
-
messageInfo,
|
|
1606
|
-
senderInfo,
|
|
1607
|
-
mentionState
|
|
1608
|
-
})
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
isDuplicate(messageId) {
|
|
1612
|
-
if (this.processedSet.has(messageId)) {
|
|
1613
|
-
return true;
|
|
1614
|
-
}
|
|
1615
|
-
this.processedSet.add(messageId);
|
|
1616
|
-
this.processedMessageIds.push(messageId);
|
|
1617
|
-
if (this.processedMessageIds.length > 1e3) {
|
|
1618
|
-
const removed = this.processedMessageIds.splice(0, 500);
|
|
1619
|
-
for (const id of removed) {
|
|
1620
|
-
this.processedSet.delete(id);
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
return false;
|
|
1624
|
-
}
|
|
1625
|
-
async addReaction(account, messageId, emojiType) {
|
|
1626
|
-
try {
|
|
1627
|
-
await account.client.addReaction(messageId, emojiType);
|
|
1628
|
-
} catch {
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
resolveOutboundAccount(metadata) {
|
|
1632
|
-
const accountId = typeof metadata?.accountId === "string" ? metadata.accountId : typeof metadata?.account_id === "string" ? metadata.account_id : getDefaultFeishuAccountId(this.config);
|
|
1633
|
-
return this.clients.get(accountId) ?? this.clients.get(getDefaultFeishuAccountId(this.config)) ?? null;
|
|
1634
|
-
}
|
|
1635
|
-
isAllowedByPolicy(params) {
|
|
1636
|
-
if (!params.isGroup) {
|
|
1637
|
-
if (this.config.dmPolicy === "disabled") {
|
|
1638
|
-
return false;
|
|
1639
|
-
}
|
|
1640
|
-
if (this.config.dmPolicy === "allowlist" || this.config.dmPolicy === "pairing") {
|
|
1641
|
-
return this.isAllowed(params.senderId);
|
|
1642
|
-
}
|
|
1643
|
-
const allowFrom = this.config.allowFrom ?? [];
|
|
1644
|
-
return allowFrom.length === 0 || allowFrom.includes("*") || this.isAllowed(params.senderId);
|
|
1645
|
-
}
|
|
1646
|
-
if (this.config.groupPolicy === "disabled") {
|
|
1647
|
-
return false;
|
|
1648
|
-
}
|
|
1649
|
-
if (this.config.groupPolicy === "allowlist") {
|
|
1650
|
-
const allowFrom = this.config.groupAllowFrom ?? [];
|
|
1651
|
-
return allowFrom.includes("*") || allowFrom.includes(params.chatId);
|
|
1652
|
-
}
|
|
1653
|
-
return true;
|
|
1654
|
-
}
|
|
1655
|
-
resolveMentionState(params) {
|
|
1656
|
-
if (!params.isGroup) {
|
|
1657
|
-
return { wasMentioned: false, requireMention: false };
|
|
1658
|
-
}
|
|
1659
|
-
const groupRule = this.config.groups?.[params.chatId] ?? this.config.groups?.["*"];
|
|
1660
|
-
const requireMention = groupRule?.requireMention ?? this.config.requireMention ?? false;
|
|
1661
|
-
if (!requireMention) {
|
|
1662
|
-
return { wasMentioned: false, requireMention: false };
|
|
1663
|
-
}
|
|
1664
|
-
const patterns = [...this.config.mentionPatterns ?? [], ...groupRule?.mentionPatterns ?? []].map((pattern) => pattern.trim()).filter(Boolean);
|
|
1665
|
-
const rawText = params.rawContent.toLowerCase();
|
|
1666
|
-
const mentionedByPattern = patterns.some((pattern) => {
|
|
1667
|
-
try {
|
|
1668
|
-
return new RegExp(pattern, "i").test(rawText);
|
|
1669
|
-
} catch {
|
|
1670
|
-
return rawText.includes(pattern.toLowerCase());
|
|
1671
|
-
}
|
|
1672
|
-
});
|
|
1673
|
-
const mentionedByIds = params.mentions.some((entry) => {
|
|
1674
|
-
if (!entry || typeof entry !== "object") {
|
|
1675
|
-
return false;
|
|
1676
|
-
}
|
|
1677
|
-
const mention = entry;
|
|
1678
|
-
const openId = (typeof mention.open_id === "string" ? mention.open_id : "") || (mention.id && typeof mention.id === "object" && "open_id" in mention.id ? mention.id.open_id ?? "" : typeof mention.id === "string" ? mention.id : "");
|
|
1679
|
-
const name = typeof mention.name === "string" ? mention.name : "";
|
|
1680
|
-
return openId === params.account.botOpenId || (params.account.botName ? name === params.account.botName : false);
|
|
1681
|
-
});
|
|
1682
|
-
return {
|
|
1683
|
-
wasMentioned: mentionedByPattern || mentionedByIds,
|
|
1684
|
-
requireMention
|
|
1685
|
-
};
|
|
1686
|
-
}
|
|
1687
|
-
async convertResource(params) {
|
|
1688
|
-
return this.inboundMediaResolver.resolve({
|
|
1689
|
-
client: params.account.client,
|
|
1690
|
-
messageId: params.messageId,
|
|
1691
|
-
resource: params.resource
|
|
1692
|
-
});
|
|
1693
|
-
}
|
|
1694
|
-
async buildInboundPayload(account, messageInfo, mentions) {
|
|
1695
|
-
const converted = convertFeishuMessageContent(
|
|
1696
|
-
messageInfo.rawContent,
|
|
1697
|
-
messageInfo.msgType,
|
|
1698
|
-
buildFeishuConvertContext({
|
|
1699
|
-
mentions,
|
|
1700
|
-
stripBotMentions: true,
|
|
1701
|
-
botOpenId: account.botOpenId,
|
|
1702
|
-
botName: account.botName
|
|
1703
|
-
})
|
|
1704
|
-
);
|
|
1705
|
-
const content = converted.content.trim() || `[${messageInfo.msgType || "message"}]`;
|
|
1706
|
-
const attachments = await Promise.all(
|
|
1707
|
-
converted.resources.map(
|
|
1708
|
-
(resource) => this.convertResource({
|
|
1709
|
-
account,
|
|
1710
|
-
messageId: messageInfo.messageId,
|
|
1711
|
-
resource
|
|
1712
|
-
})
|
|
1713
|
-
)
|
|
1714
|
-
);
|
|
1715
|
-
if (!content && attachments.length === 0) {
|
|
1716
|
-
return null;
|
|
1717
|
-
}
|
|
1718
|
-
return {
|
|
1719
|
-
content,
|
|
1720
|
-
attachments
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
|
-
};
|
|
1724
|
-
|
|
1725
1267
|
// src/channels/mochat.ts
|
|
1726
1268
|
import { io } from "socket.io-client";
|
|
1727
1269
|
import { fetch as fetch3 } from "undici";
|
|
1728
|
-
import { join as
|
|
1729
|
-
import { mkdirSync as
|
|
1270
|
+
import { join as join2 } from "path";
|
|
1271
|
+
import { mkdirSync as mkdirSync2, existsSync, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1730
1272
|
var MAX_SEEN_MESSAGE_IDS = 2e3;
|
|
1731
1273
|
var CURSOR_SAVE_DEBOUNCE_MS = 500;
|
|
1732
1274
|
var AsyncLock = class {
|
|
@@ -1745,8 +1287,8 @@ var MochatChannel = class extends BaseChannel {
|
|
|
1745
1287
|
socket = null;
|
|
1746
1288
|
wsConnected = false;
|
|
1747
1289
|
wsReady = false;
|
|
1748
|
-
stateDir =
|
|
1749
|
-
cursorPath =
|
|
1290
|
+
stateDir = join2(getDataPath(), "mochat");
|
|
1291
|
+
cursorPath = join2(this.stateDir, "session_cursors.json");
|
|
1750
1292
|
sessionCursor = {};
|
|
1751
1293
|
cursorSaveTimer = null;
|
|
1752
1294
|
sessionSet = /* @__PURE__ */ new Set();
|
|
@@ -1772,7 +1314,7 @@ var MochatChannel = class extends BaseChannel {
|
|
|
1772
1314
|
if (!this.config.clawToken) {
|
|
1773
1315
|
throw new Error("Mochat clawToken not configured");
|
|
1774
1316
|
}
|
|
1775
|
-
|
|
1317
|
+
mkdirSync2(this.stateDir, { recursive: true });
|
|
1776
1318
|
await this.loadSessionCursors();
|
|
1777
1319
|
this.seedTargetsFromConfig();
|
|
1778
1320
|
await this.refreshTargets(false);
|
|
@@ -2455,13 +1997,13 @@ var MochatChannel = class extends BaseChannel {
|
|
|
2455
1997
|
}
|
|
2456
1998
|
async saveSessionCursors() {
|
|
2457
1999
|
try {
|
|
2458
|
-
|
|
2000
|
+
mkdirSync2(this.stateDir, { recursive: true });
|
|
2459
2001
|
const payload = {
|
|
2460
2002
|
schemaVersion: 1,
|
|
2461
2003
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2462
2004
|
cursors: this.sessionCursor
|
|
2463
2005
|
};
|
|
2464
|
-
|
|
2006
|
+
writeFileSync2(this.cursorPath, JSON.stringify(payload, null, 2) + "\n");
|
|
2465
2007
|
} catch {
|
|
2466
2008
|
return;
|
|
2467
2009
|
}
|
|
@@ -3254,8 +2796,8 @@ var GroqTranscriptionProvider = class {
|
|
|
3254
2796
|
};
|
|
3255
2797
|
|
|
3256
2798
|
// src/channels/telegram.ts
|
|
3257
|
-
import { join as
|
|
3258
|
-
import { mkdirSync as
|
|
2799
|
+
import { join as join3 } from "path";
|
|
2800
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
3259
2801
|
import {
|
|
3260
2802
|
isAssistantStreamResetControlMessage,
|
|
3261
2803
|
isTypingStopControlMessage as isTypingStopControlMessage2,
|
|
@@ -3678,8 +3220,8 @@ Just send me a text message to chat!`;
|
|
|
3678
3220
|
}
|
|
3679
3221
|
const { fileId, mediaType, mimeType } = resolveMedia(message);
|
|
3680
3222
|
if (fileId && mediaType) {
|
|
3681
|
-
const mediaDir =
|
|
3682
|
-
|
|
3223
|
+
const mediaDir = join3(getDataPath(), "media");
|
|
3224
|
+
mkdirSync3(mediaDir, { recursive: true });
|
|
3683
3225
|
const extension = getExtension(mediaType, mimeType);
|
|
3684
3226
|
const downloaded = await this.bot.downloadFile(fileId, mediaDir);
|
|
3685
3227
|
const finalPath = extension && !downloaded.endsWith(extension) ? `${downloaded}${extension}` : downloaded;
|
|
@@ -4387,11 +3929,6 @@ var BUILTIN_CHANNEL_RUNTIMES = {
|
|
|
4387
3929
|
isEnabled: (config) => config.channels.discord.enabled,
|
|
4388
3930
|
createChannel: (context) => new DiscordChannel(context.config.channels.discord, context.bus, context.sessionManager, context.config)
|
|
4389
3931
|
},
|
|
4390
|
-
feishu: {
|
|
4391
|
-
id: "feishu",
|
|
4392
|
-
isEnabled: (config) => config.channels.feishu.enabled,
|
|
4393
|
-
createChannel: (context) => new FeishuChannel(context.config.channels.feishu, context.bus)
|
|
4394
|
-
},
|
|
4395
3932
|
mochat: {
|
|
4396
3933
|
id: "mochat",
|
|
4397
3934
|
isEnabled: (config) => config.channels.mochat.enabled,
|
|
@@ -4441,7 +3978,6 @@ export {
|
|
|
4441
3978
|
DingTalkChannel,
|
|
4442
3979
|
DiscordChannel,
|
|
4443
3980
|
EmailChannel,
|
|
4444
|
-
FeishuChannel,
|
|
4445
3981
|
MochatChannel,
|
|
4446
3982
|
QQChannel,
|
|
4447
3983
|
SlackChannel,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/channel-runtime",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Runtime implementations for NextClaw builtin channel plugins.",
|
|
6
6
|
"type": "module",
|
|
@@ -28,8 +28,7 @@
|
|
|
28
28
|
"undici": "^6.21.0",
|
|
29
29
|
"ws": "^8.18.0",
|
|
30
30
|
"socket.io-msgpack-parser": "^3.0.2",
|
|
31
|
-
"@nextclaw/core": "0.11.1"
|
|
32
|
-
"@nextclaw/feishu-core": "0.2.0"
|
|
31
|
+
"@nextclaw/core": "0.11.1"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"@types/mailparser": "^3.4.6",
|