openclaw-quiubo 2.6.48 → 2.6.51
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.map +1 -1
- package/dist/index.js +237 -46
- package/dist/index.js.map +4 -4
- package/dist/src/api.d.ts +2 -0
- package/dist/src/api.d.ts.map +1 -1
- package/dist/src/channel.d.ts.map +1 -1
- package/dist/src/create-post-tool.d.ts +2 -2
- package/dist/src/create-post-tool.d.ts.map +1 -1
- package/dist/src/group-context-hook.d.ts.map +1 -1
- package/dist/src/realtime-gateway.d.ts +4 -0
- package/dist/src/realtime-gateway.d.ts.map +1 -1
- package/dist/src/session-utils.d.ts +18 -0
- package/dist/src/session-utils.d.ts.map +1 -0
- package/dist/src/status.d.ts +35 -0
- package/dist/src/status.d.ts.map +1 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,sBAAsB,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACvK,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC1F,YAAY,EACV,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC;AAGxB,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,eAAe,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,GAAG,CAAA;KAAE,KAAK,IAAI,CAAC;IAEjD,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAE5D,EAAE,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAAE,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC5F;AAED,QAAA,MAAM,MAAM;;;;kBAII,iBAAiB;CAOhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9008,7 +9008,8 @@ var QuiuboApiClient = class {
|
|
|
9008
9008
|
...opts.ciphertext ? { ciphertext: opts.ciphertext } : { plaintext: opts.plaintext ?? "" },
|
|
9009
9009
|
clientMessageId: opts.clientMessageId ?? randomUUID(),
|
|
9010
9010
|
...opts.metadata ? { metadata: opts.metadata } : {},
|
|
9011
|
-
...opts.attachments?.length ? { attachments: opts.attachments } : {}
|
|
9011
|
+
...opts.attachments?.length ? { attachments: opts.attachments } : {},
|
|
9012
|
+
...opts.replyToMessageId ? { replyToMessageId: opts.replyToMessageId } : {}
|
|
9012
9013
|
});
|
|
9013
9014
|
}
|
|
9014
9015
|
/**
|
|
@@ -12644,6 +12645,14 @@ var RealtimeGateway = class {
|
|
|
12644
12645
|
getBotConfig(groupId) {
|
|
12645
12646
|
return this.botConfigCache.get(groupId);
|
|
12646
12647
|
}
|
|
12648
|
+
/** Number of groups in the bot config cache */
|
|
12649
|
+
getGroupCount() {
|
|
12650
|
+
return this.botConfigCache.size;
|
|
12651
|
+
}
|
|
12652
|
+
/** All group IDs in the bot config cache */
|
|
12653
|
+
getGroupIds() {
|
|
12654
|
+
return [...this.botConfigCache.keys()];
|
|
12655
|
+
}
|
|
12647
12656
|
/** Set the cache-miss callback */
|
|
12648
12657
|
setOnCacheMiss(fn) {
|
|
12649
12658
|
this.onCacheMiss = fn;
|
|
@@ -13343,6 +13352,100 @@ var AgentKeyManager = class {
|
|
|
13343
13352
|
}
|
|
13344
13353
|
};
|
|
13345
13354
|
|
|
13355
|
+
// src/status.ts
|
|
13356
|
+
var accountStatuses = /* @__PURE__ */ new Map();
|
|
13357
|
+
function updateAccountStatus(accountId, update) {
|
|
13358
|
+
const existing = accountStatuses.get(accountId) ?? {
|
|
13359
|
+
connected: false,
|
|
13360
|
+
groupCount: 0,
|
|
13361
|
+
activeGroups: [],
|
|
13362
|
+
e2ee: false
|
|
13363
|
+
};
|
|
13364
|
+
accountStatuses.set(accountId, { ...existing, ...update });
|
|
13365
|
+
}
|
|
13366
|
+
function trackLastMessage(accountId) {
|
|
13367
|
+
const existing = accountStatuses.get(accountId);
|
|
13368
|
+
if (existing) {
|
|
13369
|
+
existing.lastMessageAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13370
|
+
}
|
|
13371
|
+
}
|
|
13372
|
+
function clearAccountStatus(accountId) {
|
|
13373
|
+
accountStatuses.delete(accountId);
|
|
13374
|
+
}
|
|
13375
|
+
function inspectAccount(cfg, accountId) {
|
|
13376
|
+
const channelCfg = cfg?.channels?.quiubo?.accounts?.[accountId];
|
|
13377
|
+
if (!channelCfg) {
|
|
13378
|
+
return {
|
|
13379
|
+
accountId,
|
|
13380
|
+
enabled: false,
|
|
13381
|
+
configured: false
|
|
13382
|
+
};
|
|
13383
|
+
}
|
|
13384
|
+
const hasApiKey = Boolean(channelCfg.apiKey);
|
|
13385
|
+
const hasBotIdentity = Boolean(channelCfg.botIdentityId);
|
|
13386
|
+
const status = accountStatuses.get(accountId);
|
|
13387
|
+
return {
|
|
13388
|
+
accountId,
|
|
13389
|
+
enabled: channelCfg.enabled !== false && hasApiKey,
|
|
13390
|
+
configured: hasApiKey && hasBotIdentity,
|
|
13391
|
+
tokenStatus: hasApiKey ? "available" : "missing",
|
|
13392
|
+
botIdentityStatus: hasBotIdentity ? "available" : "missing",
|
|
13393
|
+
apiUrl: channelCfg.apiUrl ?? "https://api.quiubo.io",
|
|
13394
|
+
...status && {
|
|
13395
|
+
connected: status.connected,
|
|
13396
|
+
groups: status.groupCount,
|
|
13397
|
+
e2ee: status.e2ee,
|
|
13398
|
+
lastMessage: status.lastMessageAt,
|
|
13399
|
+
uptime: status.startedAt ? formatUptime(Date.now() - new Date(status.startedAt).getTime()) : void 0
|
|
13400
|
+
}
|
|
13401
|
+
};
|
|
13402
|
+
}
|
|
13403
|
+
function registerStatusRpc(api) {
|
|
13404
|
+
api.registerGatewayMethod(
|
|
13405
|
+
"quiubo.status",
|
|
13406
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13407
|
+
({ respond }) => {
|
|
13408
|
+
const result = { accounts: {} };
|
|
13409
|
+
for (const [accountId, status] of accountStatuses.entries()) {
|
|
13410
|
+
result.accounts[accountId] = {
|
|
13411
|
+
connected: status.connected,
|
|
13412
|
+
groups: status.groupCount,
|
|
13413
|
+
activeGroups: status.activeGroups,
|
|
13414
|
+
e2ee: status.e2ee,
|
|
13415
|
+
lastMessage: status.lastMessageAt,
|
|
13416
|
+
uptime: status.startedAt ? formatUptime(Date.now() - new Date(status.startedAt).getTime()) : void 0,
|
|
13417
|
+
...status.pusherState && {
|
|
13418
|
+
pusher: {
|
|
13419
|
+
state: status.pusherState,
|
|
13420
|
+
channels: status.pusherChannels
|
|
13421
|
+
}
|
|
13422
|
+
}
|
|
13423
|
+
};
|
|
13424
|
+
}
|
|
13425
|
+
const config = api.runtime?.config ?? api.config;
|
|
13426
|
+
const channelAccounts = config?.channels?.quiubo?.accounts;
|
|
13427
|
+
if (channelAccounts) {
|
|
13428
|
+
for (const id of Object.keys(channelAccounts)) {
|
|
13429
|
+
if (!result.accounts[id]) {
|
|
13430
|
+
result.accounts[id] = {
|
|
13431
|
+
connected: false,
|
|
13432
|
+
configured: Boolean(channelAccounts[id]?.apiKey),
|
|
13433
|
+
enabled: channelAccounts[id]?.enabled !== false
|
|
13434
|
+
};
|
|
13435
|
+
}
|
|
13436
|
+
}
|
|
13437
|
+
}
|
|
13438
|
+
respond(true, result);
|
|
13439
|
+
}
|
|
13440
|
+
);
|
|
13441
|
+
}
|
|
13442
|
+
function formatUptime(ms) {
|
|
13443
|
+
const hours = Math.floor(ms / 36e5);
|
|
13444
|
+
const minutes = Math.floor(ms % 36e5 / 6e4);
|
|
13445
|
+
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
13446
|
+
return `${minutes}m`;
|
|
13447
|
+
}
|
|
13448
|
+
|
|
13346
13449
|
// src/channel.ts
|
|
13347
13450
|
var KEYS_DIR = join2(process.env.HOME ?? process.env.USERPROFILE ?? "", ".openclaw", "cron");
|
|
13348
13451
|
async function loadOrGenerateKeys(cfg, accountId) {
|
|
@@ -13437,6 +13540,76 @@ var loggers = /* @__PURE__ */ new Map();
|
|
|
13437
13540
|
var CHANNEL_ID = "quiubo";
|
|
13438
13541
|
var DEFAULT_API_URL = "https://api.quiubo.io";
|
|
13439
13542
|
var DEFAULT_ACCOUNT_ID = "default";
|
|
13543
|
+
async function loadGroupIdsFromCursors(accountId) {
|
|
13544
|
+
const cursorsFile = join2(
|
|
13545
|
+
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
13546
|
+
".openclaw",
|
|
13547
|
+
"cron",
|
|
13548
|
+
`quiubo-cursors-${accountId}.json`
|
|
13549
|
+
);
|
|
13550
|
+
try {
|
|
13551
|
+
const raw = await readFile2(cursorsFile, "utf-8");
|
|
13552
|
+
const data = JSON.parse(raw);
|
|
13553
|
+
return Object.keys(data).filter((k) => typeof data[k] === "string" && data[k]);
|
|
13554
|
+
} catch {
|
|
13555
|
+
return [];
|
|
13556
|
+
}
|
|
13557
|
+
}
|
|
13558
|
+
async function notifyGroupsViaFallbackAccount(failedAccountId, failedBotIdentityId, reason, log) {
|
|
13559
|
+
let fallbackClient = null;
|
|
13560
|
+
let fallbackBotIdentityId = null;
|
|
13561
|
+
let fallbackAccountId = null;
|
|
13562
|
+
for (const [aid, client] of clients) {
|
|
13563
|
+
if (aid === failedAccountId) continue;
|
|
13564
|
+
const acct = accounts.get(aid);
|
|
13565
|
+
if (acct?.botIdentityId) {
|
|
13566
|
+
fallbackClient = client;
|
|
13567
|
+
fallbackBotIdentityId = acct.botIdentityId;
|
|
13568
|
+
fallbackAccountId = aid;
|
|
13569
|
+
break;
|
|
13570
|
+
}
|
|
13571
|
+
}
|
|
13572
|
+
if (!fallbackClient || !fallbackBotIdentityId) {
|
|
13573
|
+
log?.warn?.(`[${failedAccountId}] No fallback account available to notify groups about rate limit`);
|
|
13574
|
+
return;
|
|
13575
|
+
}
|
|
13576
|
+
log?.info?.(`[${failedAccountId}] Using fallback account "${fallbackAccountId}" to notify groups`);
|
|
13577
|
+
try {
|
|
13578
|
+
const failedGateway = gateways.get(failedAccountId);
|
|
13579
|
+
let groupIds = failedGateway?.getGroupIds() ?? [];
|
|
13580
|
+
if (groupIds.length === 0) {
|
|
13581
|
+
groupIds = await loadGroupIdsFromCursors(failedAccountId);
|
|
13582
|
+
if (groupIds.length > 0) {
|
|
13583
|
+
log?.info?.(`[${failedAccountId}] Loaded ${groupIds.length} group(s) from persisted cursors`);
|
|
13584
|
+
}
|
|
13585
|
+
}
|
|
13586
|
+
if (groupIds.length === 0) {
|
|
13587
|
+
log?.info?.(`[${failedAccountId}] No groups found (first-ever start) \u2014 cannot notify`);
|
|
13588
|
+
return;
|
|
13589
|
+
}
|
|
13590
|
+
const notifyGroups = groupIds.slice(0, 10);
|
|
13591
|
+
const message = `\u26A0\uFE0F **${failedAccountId}** is temporarily unavailable \u2014 ${reason}. Messages to this bot will not be delivered until the limit resets.`;
|
|
13592
|
+
let notified = 0;
|
|
13593
|
+
for (const groupId of notifyGroups) {
|
|
13594
|
+
try {
|
|
13595
|
+
await fallbackClient.sendMessage(groupId, {
|
|
13596
|
+
senderIdentityId: fallbackBotIdentityId,
|
|
13597
|
+
plaintext: message
|
|
13598
|
+
});
|
|
13599
|
+
notified++;
|
|
13600
|
+
log?.info?.(`[${failedAccountId}] Notified group ${groupId} via ${fallbackAccountId}`);
|
|
13601
|
+
} catch (sendErr) {
|
|
13602
|
+
const status = sendErr instanceof QuiuboApiError ? ` [${sendErr.status}]` : "";
|
|
13603
|
+
log?.debug?.(`[${failedAccountId}] Could not notify group ${groupId}${status} \u2014 fallback account may not be a member`);
|
|
13604
|
+
}
|
|
13605
|
+
}
|
|
13606
|
+
if (notified === 0) {
|
|
13607
|
+
log?.warn?.(`[${failedAccountId}] Could not notify any groups \u2014 fallback account "${fallbackAccountId}" is not a member of the failed account's groups`);
|
|
13608
|
+
}
|
|
13609
|
+
} catch (err) {
|
|
13610
|
+
log?.warn?.(`[${failedAccountId}] Failed to send rate limit notifications: ${err}`);
|
|
13611
|
+
}
|
|
13612
|
+
}
|
|
13440
13613
|
function getChannelConfig(cfg) {
|
|
13441
13614
|
return cfg.channels?.[CHANNEL_ID];
|
|
13442
13615
|
}
|
|
@@ -13532,6 +13705,11 @@ var quiuboPlugin = {
|
|
|
13532
13705
|
delete accts[id];
|
|
13533
13706
|
return setChannelConfig(cfg, { ...channel, accounts: accts });
|
|
13534
13707
|
},
|
|
13708
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13709
|
+
inspectAccount({ cfg, accountId }) {
|
|
13710
|
+
const id = accountId ?? DEFAULT_ACCOUNT_ID;
|
|
13711
|
+
return inspectAccount(cfg, id);
|
|
13712
|
+
},
|
|
13535
13713
|
/**
|
|
13536
13714
|
* Resolve a default delivery target for cron/announce when no --to is provided.
|
|
13537
13715
|
* Scans the agent's session store for quiubo group sessions and returns the
|
|
@@ -13818,13 +13996,16 @@ var quiuboPlugin = {
|
|
|
13818
13996
|
if (!senderId) {
|
|
13819
13997
|
return { ok: false, error: "No botIdentityId configured" };
|
|
13820
13998
|
}
|
|
13999
|
+
const rawReplyTo = ctx.replyTo ?? ctx.replyToMessageId ?? ctx.target?.raw?.replyToMessageId;
|
|
14000
|
+
const outboundReplyToId = typeof rawReplyTo === "string" && rawReplyTo.startsWith("quiubo-") ? rawReplyTo.slice("quiubo-".length) : typeof rawReplyTo === "string" && rawReplyTo.match(/^[0-9a-f]{8}-/i) ? rawReplyTo : void 0;
|
|
13821
14001
|
try {
|
|
13822
14002
|
const apiResp = await client.sendMessage(groupId, {
|
|
13823
14003
|
senderIdentityId: senderId,
|
|
13824
14004
|
plaintext: text,
|
|
13825
|
-
metadata: { format: "markdown" }
|
|
14005
|
+
metadata: { format: "markdown" },
|
|
14006
|
+
...outboundReplyToId ? { replyToMessageId: outboundReplyToId } : {}
|
|
13826
14007
|
});
|
|
13827
|
-
log?.info?.(`[${accountId}] [outbound:sendText] sent to group ${groupId} (realtime=${apiResp?.realtimeDelivered})`);
|
|
14008
|
+
log?.info?.(`[${accountId}] [outbound:sendText] sent to group ${groupId} (realtime=${apiResp?.realtimeDelivered}${outboundReplyToId ? `, replyTo=${outboundReplyToId}` : ""})`);
|
|
13828
14009
|
return { ok: true };
|
|
13829
14010
|
} catch (error) {
|
|
13830
14011
|
if (error instanceof QuiuboApiError) {
|
|
@@ -14039,7 +14220,13 @@ var quiuboPlugin = {
|
|
|
14039
14220
|
}
|
|
14040
14221
|
} catch (error) {
|
|
14041
14222
|
if (error instanceof QuiuboApiError) {
|
|
14042
|
-
if (error.status ===
|
|
14223
|
+
if (error.status === 429) {
|
|
14224
|
+
const isMonthlyLimit = error.body?.includes("monthly_limit");
|
|
14225
|
+
const reason = isMonthlyLimit ? "monthly API usage limit reached" : "rate limited";
|
|
14226
|
+
log?.error?.(`[${accountId}] Quiubo: [FATAL] STARTUP FAILED \u2014 ${reason} (429). ${error.body}`);
|
|
14227
|
+
updateAccountStatus(accountId, { connected: false });
|
|
14228
|
+
await notifyGroupsViaFallbackAccount(accountId, botIdentityId, reason, log);
|
|
14229
|
+
} else if (error.status === 401) {
|
|
14043
14230
|
log?.error?.(`[${accountId}] Quiubo: [FATAL] STARTUP FAILED \u2014 API key is invalid or expired (401). Regenerate your SDK API key in the Quiubo dashboard.`);
|
|
14044
14231
|
} else if (error.status === 403) {
|
|
14045
14232
|
log?.error?.(`[${accountId}] Quiubo: [FATAL] STARTUP FAILED \u2014 API key lacks permissions (403). Check your SDK app tier and permissions.`);
|
|
@@ -14245,6 +14432,7 @@ var quiuboPlugin = {
|
|
|
14245
14432
|
},
|
|
14246
14433
|
onMessage: async (msg) => {
|
|
14247
14434
|
log?.info?.(`[${accountId}] [delivery:onMessage] messageId=${msg.messageId} groupId=${msg.groupId} sender=${msg.senderIdentityId} text=${msg.plaintext?.slice(0, 80)}`);
|
|
14435
|
+
trackLastMessage(accountId);
|
|
14248
14436
|
try {
|
|
14249
14437
|
await routeInboundMessage({
|
|
14250
14438
|
runtime: runtime2,
|
|
@@ -14361,6 +14549,14 @@ var quiuboPlugin = {
|
|
|
14361
14549
|
await gateway.loadCursors();
|
|
14362
14550
|
await gateway.start();
|
|
14363
14551
|
log?.info?.(`[${accountId}] Quiubo: [OK] STARTUP COMPLETE \u2014 realtime=${pusherConfig ? "pusher" : "polling"}, agent=${resolvedAgentId ? resolvedAgentName : "none"}, e2ee=${keyManager ? "ready" : "off"}`);
|
|
14552
|
+
updateAccountStatus(accountId, {
|
|
14553
|
+
connected: true,
|
|
14554
|
+
groupCount: gateway.getGroupCount(),
|
|
14555
|
+
activeGroups: gateway.getGroupIds(),
|
|
14556
|
+
e2ee: !!keyManager,
|
|
14557
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14558
|
+
pusherState: pusherConfig ? "connected" : void 0
|
|
14559
|
+
});
|
|
14364
14560
|
return new Promise((resolve) => {
|
|
14365
14561
|
const cleanup = () => {
|
|
14366
14562
|
log?.info?.(`[${accountId}] Quiubo: gateway stopping`);
|
|
@@ -14369,6 +14565,7 @@ var quiuboPlugin = {
|
|
|
14369
14565
|
clients.delete(accountId);
|
|
14370
14566
|
accounts.delete(accountId);
|
|
14371
14567
|
loggers.delete(accountId);
|
|
14568
|
+
clearAccountStatus(accountId);
|
|
14372
14569
|
resolve();
|
|
14373
14570
|
};
|
|
14374
14571
|
if (abortSignal) {
|
|
@@ -14602,6 +14799,7 @@ async function routeInboundMessage(opts) {
|
|
|
14602
14799
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14603
14800
|
deliver: async (payload, info) => {
|
|
14604
14801
|
const agentSource = info.kind === "subagent" ? "subagent" : "agent";
|
|
14802
|
+
const replyToMessageId = payload.replyToId?.startsWith("quiubo-") ? payload.replyToId.slice("quiubo-".length) : void 0;
|
|
14605
14803
|
log?.info?.(`[${accountId}] [delivery:deliver-callback] FIRED for groupId=${groupId} (from closure), kind=${info.kind}, text=${payload.text?.length ?? 0} chars`);
|
|
14606
14804
|
log?.info?.(`[${accountId}] Quiubo: deliver payload keys=${Object.keys(payload).join(",")}, mediaUrl=${payload.mediaUrl ? "yes" : "no"}, mediaUrls=${Array.isArray(payload.mediaUrls) ? payload.mediaUrls.length : "no"}, media=${Array.isArray(payload.media) ? payload.media.length : "no"}, text=${payload.text?.length ?? 0} chars`);
|
|
14607
14805
|
if (Array.isArray(payload.media)) {
|
|
@@ -14669,7 +14867,8 @@ async function routeInboundMessage(opts) {
|
|
|
14669
14867
|
senderIdentityId: botIdentityId,
|
|
14670
14868
|
...ciphertextOut ? { ciphertext: ciphertextOut } : { plaintext: payload.text || " " },
|
|
14671
14869
|
metadata: { format: "markdown" },
|
|
14672
|
-
...nonImageAttachments.length > 0 ? { attachments: nonImageAttachments } : {}
|
|
14870
|
+
...nonImageAttachments.length > 0 ? { attachments: nonImageAttachments } : {},
|
|
14871
|
+
...replyToMessageId ? { replyToMessageId } : {}
|
|
14673
14872
|
});
|
|
14674
14873
|
if (apiResp?.realtimeDelivered === false) {
|
|
14675
14874
|
log?.warn?.(`[${accountId}] Quiubo: reply persisted but Pusher delivery failed for group ${groupId} \u2014 clients will see on next poll`);
|
|
@@ -14713,17 +14912,13 @@ async function routeInboundMessage(opts) {
|
|
|
14713
14912
|
|
|
14714
14913
|
// src/create-post-tool.ts
|
|
14715
14914
|
import { readFile as readFile3 } from "fs/promises";
|
|
14716
|
-
|
|
14717
|
-
|
|
14718
|
-
".jpeg": "image/jpeg",
|
|
14719
|
-
".png": "image/png",
|
|
14720
|
-
".webp": "image/webp"
|
|
14721
|
-
};
|
|
14722
|
-
var MAX_IMAGE_BYTES2 = 5 * 1024 * 1024;
|
|
14723
|
-
var DEFAULT_API_URL2 = "https://api.quiubo.io";
|
|
14915
|
+
|
|
14916
|
+
// src/session-utils.ts
|
|
14724
14917
|
function extractGroupIdFromSessionKey(key) {
|
|
14725
14918
|
if (!key) return void 0;
|
|
14726
|
-
const match = key.match(
|
|
14919
|
+
const match = key.match(
|
|
14920
|
+
/quiubo:([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i
|
|
14921
|
+
);
|
|
14727
14922
|
return match?.[1];
|
|
14728
14923
|
}
|
|
14729
14924
|
function extractAgentIdFromSessionKey(key) {
|
|
@@ -14734,10 +14929,20 @@ function extractAgentIdFromSessionKey(key) {
|
|
|
14734
14929
|
function resolveAccountId(config, agentId) {
|
|
14735
14930
|
const bindings = config?.bindings ?? [];
|
|
14736
14931
|
const matched = bindings.find(
|
|
14932
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14737
14933
|
(b) => b?.match?.channel === "quiubo" && b?.agentId === agentId
|
|
14738
14934
|
);
|
|
14739
14935
|
return matched?.match?.accountId ?? "default";
|
|
14740
14936
|
}
|
|
14937
|
+
|
|
14938
|
+
// src/create-post-tool.ts
|
|
14939
|
+
var IMAGE_MIME_TYPES2 = {
|
|
14940
|
+
".jpg": "image/jpeg",
|
|
14941
|
+
".jpeg": "image/jpeg",
|
|
14942
|
+
".png": "image/png",
|
|
14943
|
+
".webp": "image/webp"
|
|
14944
|
+
};
|
|
14945
|
+
var MAX_IMAGE_BYTES2 = 5 * 1024 * 1024;
|
|
14741
14946
|
function jsonResult(payload) {
|
|
14742
14947
|
return {
|
|
14743
14948
|
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
@@ -14753,13 +14958,9 @@ function errorResult(msg) {
|
|
|
14753
14958
|
function createQuiuboPostToolFactory(ctx) {
|
|
14754
14959
|
const agentId = extractAgentIdFromSessionKey(ctx.sessionKey);
|
|
14755
14960
|
const accountId = resolveAccountId(ctx.config, agentId);
|
|
14756
|
-
const
|
|
14757
|
-
const accountCfg =
|
|
14758
|
-
if (!accountCfg
|
|
14759
|
-
const client = new QuiuboApiClient(
|
|
14760
|
-
accountCfg.apiUrl ?? DEFAULT_API_URL2,
|
|
14761
|
-
accountCfg.apiKey
|
|
14762
|
-
);
|
|
14961
|
+
const client = clients.get(accountId);
|
|
14962
|
+
const accountCfg = accounts.get(accountId);
|
|
14963
|
+
if (!client || !accountCfg) return null;
|
|
14763
14964
|
const botIdentityId = accountCfg.botIdentityId;
|
|
14764
14965
|
const defaultGroupId = extractGroupIdFromSessionKey(ctx.sessionKey);
|
|
14765
14966
|
return {
|
|
@@ -14850,33 +15051,13 @@ function getCached(groupId) {
|
|
|
14850
15051
|
function setCache(groupId, data) {
|
|
14851
15052
|
cache.set(groupId, { data, expiry: Date.now() + CACHE_TTL_MS });
|
|
14852
15053
|
}
|
|
14853
|
-
function extractGroupIdFromSessionKey2(key) {
|
|
14854
|
-
if (!key) return void 0;
|
|
14855
|
-
const match = key.match(
|
|
14856
|
-
/quiubo:([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i
|
|
14857
|
-
);
|
|
14858
|
-
return match?.[1];
|
|
14859
|
-
}
|
|
14860
|
-
function extractAgentIdFromSessionKey2(key) {
|
|
14861
|
-
if (!key) return "main";
|
|
14862
|
-
const match = key.match(/^agent:([^:]+):quiubo:/);
|
|
14863
|
-
return match?.[1] ?? "main";
|
|
14864
|
-
}
|
|
14865
|
-
function resolveAccountId2(config, agentId) {
|
|
14866
|
-
const bindings = config?.bindings ?? [];
|
|
14867
|
-
const matched = bindings.find(
|
|
14868
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14869
|
-
(b) => b?.match?.channel === "quiubo" && b?.agentId === agentId
|
|
14870
|
-
);
|
|
14871
|
-
return matched?.match?.accountId ?? "default";
|
|
14872
|
-
}
|
|
14873
15054
|
async function fetchGroupContext(config, sessionKey) {
|
|
14874
|
-
const groupId =
|
|
15055
|
+
const groupId = extractGroupIdFromSessionKey(sessionKey);
|
|
14875
15056
|
if (!groupId) return void 0;
|
|
14876
15057
|
const cached = getCached(groupId);
|
|
14877
15058
|
if (cached) return cached;
|
|
14878
|
-
const agentId =
|
|
14879
|
-
const accountId =
|
|
15059
|
+
const agentId = extractAgentIdFromSessionKey(sessionKey);
|
|
15060
|
+
const accountId = resolveAccountId(config, agentId);
|
|
14880
15061
|
const client = clients.get(accountId);
|
|
14881
15062
|
const accountCfg = accounts.get(accountId);
|
|
14882
15063
|
if (!client || !accountCfg) return void 0;
|
|
@@ -14891,10 +15072,19 @@ async function fetchGroupContext(config, sessionKey) {
|
|
|
14891
15072
|
botIdentityIds.add(acct.botIdentityId);
|
|
14892
15073
|
}
|
|
14893
15074
|
}
|
|
15075
|
+
const channelAccounts = config?.channels?.quiubo?.accounts;
|
|
15076
|
+
if (channelAccounts && typeof channelAccounts === "object") {
|
|
15077
|
+
for (const acct of Object.values(channelAccounts)) {
|
|
15078
|
+
if (acct?.botIdentityId) {
|
|
15079
|
+
botIdentityIds.add(acct.botIdentityId);
|
|
15080
|
+
}
|
|
15081
|
+
}
|
|
15082
|
+
}
|
|
14894
15083
|
const currentAgent = agents.find((a) => a.identityId === accountCfg.botIdentityId);
|
|
14895
15084
|
const e2eeEnabled = currentAgent?.e2eeConfigured ?? false;
|
|
14896
15085
|
let scopes = [];
|
|
14897
|
-
|
|
15086
|
+
const isPartnerGroup = group.groupType === "agent_channel" || group.management === "partner";
|
|
15087
|
+
if (currentAgent && !isPartnerGroup) {
|
|
14898
15088
|
try {
|
|
14899
15089
|
const status = await client.getGroupAgent(currentAgent.id, groupId);
|
|
14900
15090
|
scopes = status?.grantedScopes ?? [];
|
|
@@ -14907,7 +15097,7 @@ async function fetchGroupContext(config, sessionKey) {
|
|
|
14907
15097
|
groupType: group.groupType ?? "standard",
|
|
14908
15098
|
e2ee: e2eeEnabled,
|
|
14909
15099
|
members: members.map((m) => ({
|
|
14910
|
-
displayName: m.displayName ?? m.identityId,
|
|
15100
|
+
displayName: m.displayName ?? m.username ?? m.identityId,
|
|
14911
15101
|
isBot: botIdentityIds.has(m.identityId),
|
|
14912
15102
|
role: m.role
|
|
14913
15103
|
})),
|
|
@@ -15101,6 +15291,7 @@ var plugin = {
|
|
|
15101
15291
|
api.registerChannel({ plugin: quiuboPlugin });
|
|
15102
15292
|
api.registerTool(createQuiuboPostToolFactory, { name: "quiubo_create_post" });
|
|
15103
15293
|
registerGroupContextHook(api);
|
|
15294
|
+
registerStatusRpc(api);
|
|
15104
15295
|
}
|
|
15105
15296
|
};
|
|
15106
15297
|
var index_default = plugin;
|