@agent-wechat/wechat 0.8.0 → 0.8.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 +2 -2
- package/dist/index.js +312 -68
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -146,8 +146,8 @@ All config lives under `channels.wechat` in OpenClaw's config file:
|
|
|
146
146
|
| `dmPolicy` | `"open" \| "allowlist" \| "disabled"` | `"disabled"` | Who can DM the bot |
|
|
147
147
|
| `allowFrom` | string[] | `[]` | wxid allowlist for DMs (when policy is `allowlist`) |
|
|
148
148
|
| `groupPolicy` | `"open" \| "allowlist" \| "disabled"` | `"disabled"` | Group message policy |
|
|
149
|
-
| `groupAllowFrom` | string[] | `[]` |
|
|
150
|
-
| `groups` | object | `{}` | Per-group overrides (e.g. `{ "id@chatroom": { "requireMention": false } }`) |
|
|
149
|
+
| `groupAllowFrom` | string[] | `[]` | Global allowlist of group sender IDs (`wxid_...`) |
|
|
150
|
+
| `groups` | object | `{}` | Per-group overrides (e.g. `{ "id@chatroom": { "requireMention": false, "enabled": true, "groupPolicy": "allowlist", "allowFrom": ["wxid_..."] } }`) |
|
|
151
151
|
| `pollIntervalMs` | integer | `1000` | Message polling interval |
|
|
152
152
|
| `authPollIntervalMs` | integer | `30000` | Auth status check interval |
|
|
153
153
|
|
package/dist/index.js
CHANGED
|
@@ -1085,6 +1085,12 @@ import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
|
1085
1085
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID2 } from "openclaw/plugin-sdk";
|
|
1086
1086
|
|
|
1087
1087
|
// src/types.ts
|
|
1088
|
+
function normalizeDmPolicy(policy) {
|
|
1089
|
+
return policy === "allowlist" || policy === "open" || policy === "disabled" ? policy : "disabled";
|
|
1090
|
+
}
|
|
1091
|
+
function normalizeGroupPolicy(policy) {
|
|
1092
|
+
return policy === "open" || policy === "disabled" || policy === "allowlist" ? policy : "disabled";
|
|
1093
|
+
}
|
|
1088
1094
|
var DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
1089
1095
|
var DEFAULT_AUTH_POLL_INTERVAL_MS = 3e4;
|
|
1090
1096
|
var DEFAULT_ACCOUNT_ID = "default";
|
|
@@ -1096,9 +1102,9 @@ function resolveWeChatAccount(cfg, accountId) {
|
|
|
1096
1102
|
enabled: wechat.enabled !== false,
|
|
1097
1103
|
serverUrl: wechat.serverUrl,
|
|
1098
1104
|
token: wechat.token,
|
|
1099
|
-
dmPolicy: wechat.dmPolicy
|
|
1105
|
+
dmPolicy: normalizeDmPolicy(wechat.dmPolicy),
|
|
1100
1106
|
allowFrom: wechat.allowFrom ?? [],
|
|
1101
|
-
groupPolicy: wechat.groupPolicy
|
|
1107
|
+
groupPolicy: normalizeGroupPolicy(wechat.groupPolicy),
|
|
1102
1108
|
groupAllowFrom: wechat.groupAllowFrom ?? [],
|
|
1103
1109
|
groups: wechat.groups ?? {},
|
|
1104
1110
|
pollIntervalMs: wechat.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
|
|
@@ -5505,6 +5511,172 @@ var agentConfigSchema = external_exports.object({
|
|
|
5505
5511
|
|
|
5506
5512
|
// src/monitor.ts
|
|
5507
5513
|
import { createReplyPrefixOptions } from "openclaw/plugin-sdk";
|
|
5514
|
+
|
|
5515
|
+
// src/access-control.ts
|
|
5516
|
+
import {
|
|
5517
|
+
buildChannelKeyCandidates,
|
|
5518
|
+
resolveAllowlistProviderRuntimeGroupPolicy,
|
|
5519
|
+
resolveChannelEntryMatchWithFallback,
|
|
5520
|
+
resolveDefaultGroupPolicy,
|
|
5521
|
+
resolveSenderCommandAuthorization
|
|
5522
|
+
} from "openclaw/plugin-sdk";
|
|
5523
|
+
function unique(values) {
|
|
5524
|
+
return Array.from(new Set(values));
|
|
5525
|
+
}
|
|
5526
|
+
function normalizeDmPolicy2(policy) {
|
|
5527
|
+
if (policy === "open" || policy === "allowlist" || policy === "disabled") {
|
|
5528
|
+
return policy;
|
|
5529
|
+
}
|
|
5530
|
+
return "disabled";
|
|
5531
|
+
}
|
|
5532
|
+
function normalizeGroupPolicy2(policy) {
|
|
5533
|
+
if (policy === "open" || policy === "allowlist" || policy === "disabled") {
|
|
5534
|
+
return policy;
|
|
5535
|
+
}
|
|
5536
|
+
return void 0;
|
|
5537
|
+
}
|
|
5538
|
+
function firstDefined(...values) {
|
|
5539
|
+
for (const value of values) {
|
|
5540
|
+
if (value !== void 0) {
|
|
5541
|
+
return value;
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
return void 0;
|
|
5545
|
+
}
|
|
5546
|
+
function normalizeWeChatId(raw) {
|
|
5547
|
+
const trimmed = raw.trim();
|
|
5548
|
+
if (!trimmed) {
|
|
5549
|
+
return "";
|
|
5550
|
+
}
|
|
5551
|
+
return trimmed.replace(/^wechat:/i, "").trim();
|
|
5552
|
+
}
|
|
5553
|
+
function normalizeWeChatAllowFrom(values) {
|
|
5554
|
+
const normalized = (values ?? []).map((entry) => String(entry).trim()).filter(Boolean).map((entry) => entry === "*" ? "*" : normalizeWeChatId(entry)).filter(Boolean);
|
|
5555
|
+
return unique(normalized);
|
|
5556
|
+
}
|
|
5557
|
+
function isWeChatSenderAllowed(senderId, allowFrom) {
|
|
5558
|
+
if (allowFrom.includes("*")) {
|
|
5559
|
+
return true;
|
|
5560
|
+
}
|
|
5561
|
+
const normalizedSender = senderId ? normalizeWeChatId(senderId) : "";
|
|
5562
|
+
if (!normalizedSender) {
|
|
5563
|
+
return false;
|
|
5564
|
+
}
|
|
5565
|
+
return allowFrom.includes(normalizedSender);
|
|
5566
|
+
}
|
|
5567
|
+
function resolveGroupEntry(params) {
|
|
5568
|
+
const groups = params.account.groups ?? {};
|
|
5569
|
+
const normalizedChatId = normalizeWeChatId(params.chatId);
|
|
5570
|
+
const keys = buildChannelKeyCandidates(params.chatId, normalizedChatId);
|
|
5571
|
+
const match = resolveChannelEntryMatchWithFallback({
|
|
5572
|
+
entries: groups,
|
|
5573
|
+
keys,
|
|
5574
|
+
wildcardKey: "*",
|
|
5575
|
+
normalizeKey: normalizeWeChatId
|
|
5576
|
+
});
|
|
5577
|
+
return {
|
|
5578
|
+
groupEntry: match.entry,
|
|
5579
|
+
wildcardEntry: match.wildcardEntry
|
|
5580
|
+
};
|
|
5581
|
+
}
|
|
5582
|
+
function resolveWeChatPolicyContext(params) {
|
|
5583
|
+
const dmPolicy = normalizeDmPolicy2(params.account.dmPolicy);
|
|
5584
|
+
const configuredAllowFrom = normalizeWeChatAllowFrom(params.account.allowFrom);
|
|
5585
|
+
const configuredGroupAllowFrom = normalizeWeChatAllowFrom(params.account.groupAllowFrom);
|
|
5586
|
+
const normalizedStoreAllowFrom = dmPolicy === "allowlist" ? [] : normalizeWeChatAllowFrom(params.storeAllowFrom);
|
|
5587
|
+
const effectiveAllowFrom = unique([...configuredAllowFrom, ...normalizedStoreAllowFrom]);
|
|
5588
|
+
const groupBase = configuredGroupAllowFrom.length > 0 ? configuredGroupAllowFrom : configuredAllowFrom;
|
|
5589
|
+
const effectiveGroupAllowFrom = unique([...groupBase, ...normalizedStoreAllowFrom]);
|
|
5590
|
+
const { groupEntry, wildcardEntry } = resolveGroupEntry({
|
|
5591
|
+
account: params.account,
|
|
5592
|
+
chatId: params.chatId
|
|
5593
|
+
});
|
|
5594
|
+
const groupEnabled = firstDefined(groupEntry?.enabled, wildcardEntry?.enabled, true) !== false;
|
|
5595
|
+
const requireMention = firstDefined(groupEntry?.requireMention, wildcardEntry?.requireMention, true) !== false;
|
|
5596
|
+
const defaultGroupPolicy = resolveDefaultGroupPolicy(params.cfg);
|
|
5597
|
+
const { groupPolicy: fallbackGroupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({
|
|
5598
|
+
providerConfigPresent: params.cfg.channels?.wechat !== void 0,
|
|
5599
|
+
groupPolicy: normalizeGroupPolicy2(params.account.groupPolicy),
|
|
5600
|
+
defaultGroupPolicy: normalizeGroupPolicy2(defaultGroupPolicy)
|
|
5601
|
+
});
|
|
5602
|
+
const groupPolicy = normalizeGroupPolicy2(
|
|
5603
|
+
firstDefined(
|
|
5604
|
+
groupEntry?.groupPolicy,
|
|
5605
|
+
wildcardEntry?.groupPolicy,
|
|
5606
|
+
params.account.groupPolicy,
|
|
5607
|
+
defaultGroupPolicy
|
|
5608
|
+
)
|
|
5609
|
+
) ?? fallbackGroupPolicy;
|
|
5610
|
+
const groupAllowOverride = normalizeWeChatAllowFrom(
|
|
5611
|
+
firstDefined(groupEntry?.allowFrom, wildcardEntry?.allowFrom)
|
|
5612
|
+
);
|
|
5613
|
+
const groupAllowFrom = groupAllowOverride.length > 0 ? unique([...groupAllowOverride, ...normalizedStoreAllowFrom]) : effectiveGroupAllowFrom;
|
|
5614
|
+
return {
|
|
5615
|
+
dmPolicy,
|
|
5616
|
+
groupPolicy,
|
|
5617
|
+
requireMention,
|
|
5618
|
+
groupEnabled,
|
|
5619
|
+
effectiveAllowFrom,
|
|
5620
|
+
effectiveGroupAllowFrom: groupAllowFrom
|
|
5621
|
+
};
|
|
5622
|
+
}
|
|
5623
|
+
function resolveWeChatInboundAccessDecision(params) {
|
|
5624
|
+
if (params.isGroup) {
|
|
5625
|
+
if (!params.policy.groupEnabled) {
|
|
5626
|
+
return { allowed: false, reason: "group-config-disabled" };
|
|
5627
|
+
}
|
|
5628
|
+
if (params.policy.groupPolicy === "disabled") {
|
|
5629
|
+
return { allowed: false, reason: "groupPolicy=disabled" };
|
|
5630
|
+
}
|
|
5631
|
+
if (params.policy.groupPolicy === "allowlist") {
|
|
5632
|
+
if (params.policy.effectiveGroupAllowFrom.length === 0) {
|
|
5633
|
+
return { allowed: false, reason: "groupPolicy=allowlist (empty allowlist)" };
|
|
5634
|
+
}
|
|
5635
|
+
if (!isWeChatSenderAllowed(params.senderId, params.policy.effectiveGroupAllowFrom)) {
|
|
5636
|
+
return { allowed: false, reason: "groupPolicy=allowlist (sender not allowlisted)" };
|
|
5637
|
+
}
|
|
5638
|
+
}
|
|
5639
|
+
return { allowed: true, reason: `groupPolicy=${params.policy.groupPolicy}` };
|
|
5640
|
+
}
|
|
5641
|
+
if (params.policy.dmPolicy === "disabled") {
|
|
5642
|
+
return { allowed: false, reason: "dmPolicy=disabled" };
|
|
5643
|
+
}
|
|
5644
|
+
if (params.policy.dmPolicy === "allowlist") {
|
|
5645
|
+
if (params.policy.effectiveAllowFrom.length === 0) {
|
|
5646
|
+
return { allowed: false, reason: "dmPolicy=allowlist (empty allowlist)" };
|
|
5647
|
+
}
|
|
5648
|
+
if (!isWeChatSenderAllowed(params.senderId, params.policy.effectiveAllowFrom)) {
|
|
5649
|
+
return { allowed: false, reason: "dmPolicy=allowlist (sender not allowlisted)" };
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
return { allowed: true, reason: `dmPolicy=${params.policy.dmPolicy}` };
|
|
5653
|
+
}
|
|
5654
|
+
async function resolveWeChatCommandAuthorization(params) {
|
|
5655
|
+
const normalizedSenderId = normalizeWeChatId(params.senderId ?? "");
|
|
5656
|
+
const { commandAuthorized } = await resolveSenderCommandAuthorization({
|
|
5657
|
+
cfg: params.cfg,
|
|
5658
|
+
rawBody: params.rawBody,
|
|
5659
|
+
isGroup: params.isGroup,
|
|
5660
|
+
dmPolicy: params.dmPolicy,
|
|
5661
|
+
configuredAllowFrom: params.allowFromForCommands,
|
|
5662
|
+
senderId: normalizedSenderId,
|
|
5663
|
+
isSenderAllowed: (senderId, allowFrom) => isWeChatSenderAllowed(senderId, normalizeWeChatAllowFrom(allowFrom)),
|
|
5664
|
+
readAllowFromStore: async () => normalizeWeChatAllowFrom(await params.deps.readAllowFromStore()),
|
|
5665
|
+
shouldComputeCommandAuthorized: params.deps.shouldComputeCommandAuthorized,
|
|
5666
|
+
resolveCommandAuthorizedFromAuthorizers: params.deps.resolveCommandAuthorizedFromAuthorizers
|
|
5667
|
+
});
|
|
5668
|
+
return commandAuthorized;
|
|
5669
|
+
}
|
|
5670
|
+
function resolveWeChatMentionGate(params) {
|
|
5671
|
+
const implicitMention = params.implicitMention === true;
|
|
5672
|
+
const baseWasMentioned = params.wasMentioned || implicitMention;
|
|
5673
|
+
const shouldBypassMention = params.isGroup && params.requireMention && !baseWasMentioned && params.allowTextCommands && params.hasControlCommand && params.commandAuthorized;
|
|
5674
|
+
const effectiveWasMentioned = baseWasMentioned || shouldBypassMention;
|
|
5675
|
+
const shouldSkip = params.requireMention && params.canDetectMention && !effectiveWasMentioned;
|
|
5676
|
+
return { effectiveWasMentioned, shouldSkip, shouldBypassMention };
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
// src/monitor.ts
|
|
5508
5680
|
var MEDIA_TYPES = /* @__PURE__ */ new Set([3, 34]);
|
|
5509
5681
|
var HISTORY_CONTEXT_MARKER = "[Chat messages since your last reply - for context]";
|
|
5510
5682
|
var CURRENT_MESSAGE_MARKER = "[Current message - respond to this]";
|
|
@@ -5533,20 +5705,6 @@ async function pollMedia(client, chatId, localId, log, maxAttempts = 15, interva
|
|
|
5533
5705
|
}
|
|
5534
5706
|
return null;
|
|
5535
5707
|
}
|
|
5536
|
-
function isMessageAllowed(account, isGroup, senderId) {
|
|
5537
|
-
if (isGroup) {
|
|
5538
|
-
if (account.groupPolicy === "disabled") return false;
|
|
5539
|
-
if (account.groupPolicy === "allowlist") {
|
|
5540
|
-
return account.groupAllowFrom.includes(senderId);
|
|
5541
|
-
}
|
|
5542
|
-
return true;
|
|
5543
|
-
}
|
|
5544
|
-
if (account.dmPolicy === "disabled") return false;
|
|
5545
|
-
if (account.dmPolicy === "allowlist") {
|
|
5546
|
-
return account.allowFrom.includes(senderId);
|
|
5547
|
-
}
|
|
5548
|
-
return true;
|
|
5549
|
-
}
|
|
5550
5708
|
function enqueueWeChatSystemEvent(text, contextKey) {
|
|
5551
5709
|
try {
|
|
5552
5710
|
const core = getWeChatRuntime();
|
|
@@ -5684,7 +5842,7 @@ async function startWeChatMonitor(opts) {
|
|
|
5684
5842
|
connected: false
|
|
5685
5843
|
});
|
|
5686
5844
|
}
|
|
5687
|
-
async function prepareMessage(client, msg, chatId, chat, liveAccount, log) {
|
|
5845
|
+
async function prepareMessage(client, msg, chatId, chat, liveAccount, policy, log) {
|
|
5688
5846
|
const core = getWeChatRuntime();
|
|
5689
5847
|
if (msg.isSelf) {
|
|
5690
5848
|
log?.info?.(`[wechat:${liveAccount.accountId}] Skipping self-sent msg ${msg.localId}`);
|
|
@@ -5693,8 +5851,15 @@ async function prepareMessage(client, msg, chatId, chat, liveAccount, log) {
|
|
|
5693
5851
|
const isGroup = chatId.includes("@chatroom");
|
|
5694
5852
|
const senderId = msg.sender ?? chatId;
|
|
5695
5853
|
const senderName = msg.senderName ?? msg.sender ?? chat.name;
|
|
5696
|
-
|
|
5697
|
-
|
|
5854
|
+
const access = resolveWeChatInboundAccessDecision({
|
|
5855
|
+
isGroup,
|
|
5856
|
+
senderId,
|
|
5857
|
+
policy
|
|
5858
|
+
});
|
|
5859
|
+
if (!access.allowed) {
|
|
5860
|
+
log?.info?.(
|
|
5861
|
+
`[wechat:${liveAccount.accountId}] Blocked by policy (${access.reason}) from ${senderId}`
|
|
5862
|
+
);
|
|
5698
5863
|
return null;
|
|
5699
5864
|
}
|
|
5700
5865
|
let mediaPath;
|
|
@@ -5772,6 +5937,7 @@ ${replyBlock}` : replyBlock;
|
|
|
5772
5937
|
return {
|
|
5773
5938
|
msg,
|
|
5774
5939
|
rawBody,
|
|
5940
|
+
commandBody: rawBody,
|
|
5775
5941
|
mediaPath,
|
|
5776
5942
|
mediaMime,
|
|
5777
5943
|
senderName,
|
|
@@ -5801,27 +5967,50 @@ function buildSegments(processed) {
|
|
|
5801
5967
|
}
|
|
5802
5968
|
return segments;
|
|
5803
5969
|
}
|
|
5804
|
-
async function dispatchSegment(segment, client, chatId, chat, liveAccount, cfg, log, remainingSegments
|
|
5970
|
+
async function dispatchSegment(segment, client, chatId, chat, liveAccount, policy, storeAllowFrom, allowTextCommands, cfg, log, remainingSegments) {
|
|
5805
5971
|
const core = getWeChatRuntime();
|
|
5806
5972
|
const lastMsg = segment[segment.length - 1];
|
|
5807
|
-
const { isGroup, senderId, senderName, timestamp, rawBody, msg } = lastMsg;
|
|
5973
|
+
const { isGroup, senderId, senderName, timestamp, rawBody, commandBody, msg } = lastMsg;
|
|
5808
5974
|
const mediaMsg = segment.find((pm) => pm.mediaPath);
|
|
5809
5975
|
const mediaPath = mediaMsg?.mediaPath;
|
|
5810
5976
|
const mediaMime = mediaMsg?.mediaMime;
|
|
5811
5977
|
log?.info?.(
|
|
5812
5978
|
`[wechat:${liveAccount.accountId}] Dispatching segment: ${segment.length} msg(s), last=${msg.localId}${mediaPath ? ` media=${mediaPath}` : ""}`
|
|
5813
5979
|
);
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5980
|
+
const hasControlCommand = allowTextCommands && core.channel.text.hasControlCommand(commandBody, cfg);
|
|
5981
|
+
const commandAuthorized = await resolveWeChatCommandAuthorization({
|
|
5982
|
+
cfg,
|
|
5983
|
+
rawBody: commandBody,
|
|
5984
|
+
isGroup,
|
|
5985
|
+
senderId,
|
|
5986
|
+
dmPolicy: policy.dmPolicy,
|
|
5987
|
+
allowFromForCommands: isGroup ? policy.effectiveGroupAllowFrom : policy.effectiveAllowFrom,
|
|
5988
|
+
deps: {
|
|
5989
|
+
shouldComputeCommandAuthorized: (raw, loadedCfg) => core.channel.commands.shouldComputeCommandAuthorized(raw, loadedCfg),
|
|
5990
|
+
resolveCommandAuthorizedFromAuthorizers: (params) => core.channel.commands.resolveCommandAuthorizedFromAuthorizers(params),
|
|
5991
|
+
readAllowFromStore: async () => storeAllowFrom
|
|
5824
5992
|
}
|
|
5993
|
+
});
|
|
5994
|
+
if (isGroup && allowTextCommands && hasControlCommand && commandAuthorized !== true) {
|
|
5995
|
+
log?.info?.(
|
|
5996
|
+
`[wechat:${liveAccount.accountId}] Dropping unauthorized group control command from ${senderId} in ${chatId}`
|
|
5997
|
+
);
|
|
5998
|
+
return false;
|
|
5999
|
+
}
|
|
6000
|
+
const mentionGate = resolveWeChatMentionGate({
|
|
6001
|
+
isGroup,
|
|
6002
|
+
requireMention: policy.requireMention,
|
|
6003
|
+
canDetectMention: true,
|
|
6004
|
+
wasMentioned: segment.some((pm) => pm.isMentioned),
|
|
6005
|
+
allowTextCommands,
|
|
6006
|
+
hasControlCommand,
|
|
6007
|
+
commandAuthorized: commandAuthorized === true
|
|
6008
|
+
});
|
|
6009
|
+
if (isGroup && mentionGate.shouldSkip) {
|
|
6010
|
+
log?.info?.(
|
|
6011
|
+
`[wechat:${liveAccount.accountId}] Skipping group segment (mention required) in ${chatId}`
|
|
6012
|
+
);
|
|
6013
|
+
return false;
|
|
5825
6014
|
}
|
|
5826
6015
|
try {
|
|
5827
6016
|
const route = core.channel.routing.resolveAgentRoute({
|
|
@@ -5896,7 +6085,7 @@ async function dispatchSegment(segment, client, chatId, chat, liveAccount, cfg,
|
|
|
5896
6085
|
Body: body,
|
|
5897
6086
|
BodyForAgent: rawBody,
|
|
5898
6087
|
RawBody: rawBody,
|
|
5899
|
-
CommandBody:
|
|
6088
|
+
CommandBody: commandBody,
|
|
5900
6089
|
InboundHistory: inboundHistory,
|
|
5901
6090
|
From: isGroup ? `wechat:group:${chatId}` : `wechat:${senderId}`,
|
|
5902
6091
|
To: `wechat:${chatId}`,
|
|
@@ -5909,7 +6098,8 @@ async function dispatchSegment(segment, client, chatId, chat, liveAccount, cfg,
|
|
|
5909
6098
|
Provider: "wechat",
|
|
5910
6099
|
Surface: "wechat",
|
|
5911
6100
|
MessageSid: `wechat:${chatId}:${msg.localId}`,
|
|
5912
|
-
WasMentioned: isGroup ?
|
|
6101
|
+
WasMentioned: isGroup ? mentionGate.effectiveWasMentioned : void 0,
|
|
6102
|
+
CommandAuthorized: commandAuthorized,
|
|
5913
6103
|
OriginatingChannel: "wechat",
|
|
5914
6104
|
OriginatingTo: `wechat:${chatId}`,
|
|
5915
6105
|
...mediaPath ? { MediaPath: mediaPath, MediaUrl: mediaPath, MediaType: mediaMime } : {},
|
|
@@ -6012,13 +6202,12 @@ async function dispatchSegment(segment, client, chatId, chat, liveAccount, cfg,
|
|
|
6012
6202
|
direction: "inbound",
|
|
6013
6203
|
at: timestamp
|
|
6014
6204
|
});
|
|
6015
|
-
|
|
6016
|
-
groupHistory.set(chatId, []);
|
|
6017
|
-
}
|
|
6205
|
+
return true;
|
|
6018
6206
|
} catch (err) {
|
|
6019
6207
|
log?.error?.(
|
|
6020
6208
|
`[wechat:${liveAccount.accountId}] Failed to dispatch segment (last msg ${msg.localId}): ${err}`
|
|
6021
6209
|
);
|
|
6210
|
+
return false;
|
|
6022
6211
|
}
|
|
6023
6212
|
}
|
|
6024
6213
|
function bufferGroupHistory(groupHistory, chatId, pm, limit) {
|
|
@@ -6035,8 +6224,20 @@ function bufferGroupHistory(groupHistory, chatId, pm, limit) {
|
|
|
6035
6224
|
}
|
|
6036
6225
|
}
|
|
6037
6226
|
async function processUnreadChat(client, chat, lastSeenId, account, cfg, log, skipOpen, groupHistory, groupHistoryLimit) {
|
|
6227
|
+
const core = getWeChatRuntime();
|
|
6038
6228
|
const liveAccount = resolveWeChatAccount(cfg, account.accountId) ?? account;
|
|
6039
6229
|
const chatId = chat.username ?? chat.id;
|
|
6230
|
+
const storeAllowFrom = await core.channel.pairing.readAllowFromStore("wechat", process.env, liveAccount.accountId).catch(() => []);
|
|
6231
|
+
const policy = resolveWeChatPolicyContext({
|
|
6232
|
+
account: liveAccount,
|
|
6233
|
+
cfg,
|
|
6234
|
+
chatId,
|
|
6235
|
+
storeAllowFrom
|
|
6236
|
+
});
|
|
6237
|
+
const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
|
|
6238
|
+
cfg,
|
|
6239
|
+
surface: "wechat"
|
|
6240
|
+
});
|
|
6040
6241
|
if (!skipOpen) {
|
|
6041
6242
|
log?.info?.(`[wechat:${liveAccount.accountId}] Opening chat ${chatId}...`);
|
|
6042
6243
|
try {
|
|
@@ -6094,20 +6295,18 @@ async function processUnreadChat(client, chat, lastSeenId, account, cfg, log, sk
|
|
|
6094
6295
|
log?.info?.(
|
|
6095
6296
|
`[wechat:${liveAccount.accountId}] Processing msg ${msg.localId}: type=${msg.type}, sender=${msg.sender}, isSelf=${msg.isSelf}, content=${(msg.content || "").slice(0, 50)}`
|
|
6096
6297
|
);
|
|
6097
|
-
const pm = await prepareMessage(client, msg, chatId, chat, liveAccount, log);
|
|
6298
|
+
const pm = await prepareMessage(client, msg, chatId, chat, liveAccount, policy, log);
|
|
6098
6299
|
if (pm) {
|
|
6099
6300
|
processed.push(pm);
|
|
6100
6301
|
}
|
|
6101
6302
|
}
|
|
6102
6303
|
const isGroup = chatId.includes("@chatroom");
|
|
6304
|
+
let clearBufferedHistory = false;
|
|
6305
|
+
const hasControlCommandInWindow = allowTextCommands && processed.some((pm) => core.channel.text.hasControlCommand(pm.commandBody, cfg));
|
|
6103
6306
|
if (isGroup && groupHistory) {
|
|
6104
|
-
|
|
6105
|
-
const groupEntry = wechatCfg?.groups?.[chatId];
|
|
6106
|
-
const defaultEntry = wechatCfg?.groups?.["*"];
|
|
6107
|
-
const requireMention = groupEntry?.requireMention ?? defaultEntry?.requireMention ?? true;
|
|
6108
|
-
if (requireMention) {
|
|
6307
|
+
if (policy.requireMention) {
|
|
6109
6308
|
const hasMention = processed.some((pm) => pm.isMentioned);
|
|
6110
|
-
if (!hasMention) {
|
|
6309
|
+
if (!hasMention && !hasControlCommandInWindow) {
|
|
6111
6310
|
const limit = groupHistoryLimit ?? 50;
|
|
6112
6311
|
for (const pm of processed) {
|
|
6113
6312
|
bufferGroupHistory(groupHistory, chatId, pm, limit);
|
|
@@ -6117,37 +6316,67 @@ async function processUnreadChat(client, chat, lastSeenId, account, cfg, log, sk
|
|
|
6117
6316
|
lastSeenId.set(chatId, maxId2);
|
|
6118
6317
|
return;
|
|
6119
6318
|
}
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6319
|
+
if (hasMention) {
|
|
6320
|
+
clearBufferedHistory = true;
|
|
6321
|
+
const buffered = groupHistory.get(chatId) ?? [];
|
|
6322
|
+
if (buffered.length > 0) {
|
|
6323
|
+
for (const pm of buffered) {
|
|
6324
|
+
pm.isMentioned = true;
|
|
6325
|
+
}
|
|
6326
|
+
processed.unshift(...buffered);
|
|
6327
|
+
log?.info?.(
|
|
6328
|
+
`[wechat:${liveAccount.accountId}] Injected ${buffered.length} buffered msg(s) as history in ${chatId}`
|
|
6329
|
+
);
|
|
6124
6330
|
}
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
if (processed[i].mediaPath) {
|
|
6132
|
-
latestMediaIdx = i;
|
|
6133
|
-
break;
|
|
6331
|
+
let latestMediaIdx = -1;
|
|
6332
|
+
for (let i = processed.length - 1; i >= 0; i--) {
|
|
6333
|
+
if (processed[i].mediaPath) {
|
|
6334
|
+
latestMediaIdx = i;
|
|
6335
|
+
break;
|
|
6336
|
+
}
|
|
6134
6337
|
}
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6338
|
+
for (let i = 0; i < processed.length; i++) {
|
|
6339
|
+
if (processed[i].mediaPath && i !== latestMediaIdx) {
|
|
6340
|
+
processed[i] = {
|
|
6341
|
+
...processed[i],
|
|
6342
|
+
mediaPath: void 0,
|
|
6343
|
+
mediaMime: void 0,
|
|
6344
|
+
hasMedia: false
|
|
6345
|
+
};
|
|
6346
|
+
}
|
|
6139
6347
|
}
|
|
6140
6348
|
}
|
|
6349
|
+
} else {
|
|
6350
|
+
clearBufferedHistory = true;
|
|
6141
6351
|
}
|
|
6142
6352
|
}
|
|
6143
6353
|
if (processed.length > 0) {
|
|
6144
|
-
const segments = buildSegments(processed);
|
|
6354
|
+
const segments = hasControlCommandInWindow ? processed.map((pm) => [pm]) : buildSegments(processed);
|
|
6145
6355
|
log?.info?.(
|
|
6146
6356
|
`[wechat:${liveAccount.accountId}] ${chatId}: ${processed.length} dispatchable msg(s) in ${segments.length} segment(s)`
|
|
6147
6357
|
);
|
|
6358
|
+
let allDispatched = true;
|
|
6148
6359
|
for (let i = 0; i < segments.length; i++) {
|
|
6149
6360
|
const remaining = segments.length - i - 1;
|
|
6150
|
-
await dispatchSegment(
|
|
6361
|
+
const dispatched = await dispatchSegment(
|
|
6362
|
+
segments[i],
|
|
6363
|
+
client,
|
|
6364
|
+
chatId,
|
|
6365
|
+
chat,
|
|
6366
|
+
liveAccount,
|
|
6367
|
+
policy,
|
|
6368
|
+
storeAllowFrom,
|
|
6369
|
+
allowTextCommands,
|
|
6370
|
+
cfg,
|
|
6371
|
+
log,
|
|
6372
|
+
hasControlCommandInWindow ? void 0 : remaining
|
|
6373
|
+
);
|
|
6374
|
+
if (!dispatched) {
|
|
6375
|
+
allDispatched = false;
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
if (clearBufferedHistory && allDispatched && groupHistory) {
|
|
6379
|
+
groupHistory.set(chatId, []);
|
|
6151
6380
|
}
|
|
6152
6381
|
}
|
|
6153
6382
|
const maxId = Math.max(...newMessages.map((m) => m.localId));
|
|
@@ -6521,7 +6750,7 @@ var wechatOnboardingAdapter = {
|
|
|
6521
6750
|
wechatCfg.groupPolicy = groupPolicy;
|
|
6522
6751
|
if (groupPolicy === "allowlist") {
|
|
6523
6752
|
const raw = await prompter.text({
|
|
6524
|
-
message: "Allowed group IDs (comma-separated
|
|
6753
|
+
message: "Allowed group sender IDs (comma-separated wxid_xxx values; use * to allow any sender)"
|
|
6525
6754
|
});
|
|
6526
6755
|
wechatCfg.groupAllowFrom = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
6527
6756
|
}
|
|
@@ -6755,7 +6984,13 @@ var wechatPlugin = {
|
|
|
6755
6984
|
additionalProperties: {
|
|
6756
6985
|
type: "object",
|
|
6757
6986
|
properties: {
|
|
6758
|
-
|
|
6987
|
+
enabled: { type: "boolean" },
|
|
6988
|
+
requireMention: { type: "boolean" },
|
|
6989
|
+
groupPolicy: {
|
|
6990
|
+
type: "string",
|
|
6991
|
+
enum: ["open", "allowlist", "disabled"]
|
|
6992
|
+
},
|
|
6993
|
+
allowFrom: { type: "array", items: { type: "string" } }
|
|
6759
6994
|
}
|
|
6760
6995
|
}
|
|
6761
6996
|
},
|
|
@@ -6800,7 +7035,8 @@ var wechatPlugin = {
|
|
|
6800
7035
|
allowFrom: account.allowFrom ?? [],
|
|
6801
7036
|
allowFromPath: "channels.wechat.allowFrom",
|
|
6802
7037
|
policyPath: "channels.wechat.dmPolicy",
|
|
6803
|
-
approveHint: "Add the wxid to channels.wechat.allowFrom"
|
|
7038
|
+
approveHint: "Add the wxid to channels.wechat.allowFrom",
|
|
7039
|
+
normalizeEntry: (raw) => raw.replace(/^wechat:/i, "").trim()
|
|
6804
7040
|
})
|
|
6805
7041
|
},
|
|
6806
7042
|
// ---- Groups adapter ----
|
|
@@ -6808,10 +7044,18 @@ var wechatPlugin = {
|
|
|
6808
7044
|
resolveRequireMention: ({ cfg, groupId }) => {
|
|
6809
7045
|
const wechat = cfg?.channels?.wechat;
|
|
6810
7046
|
if (!wechat) return true;
|
|
6811
|
-
if (groupId
|
|
6812
|
-
return wechat.groups[
|
|
7047
|
+
if (!groupId) {
|
|
7048
|
+
return wechat.groups?.["*"]?.requireMention ?? true;
|
|
7049
|
+
}
|
|
7050
|
+
const exact = wechat.groups?.[groupId];
|
|
7051
|
+
if (exact?.requireMention != null) {
|
|
7052
|
+
return exact.requireMention;
|
|
7053
|
+
}
|
|
7054
|
+
const normalizedGroupId = normalizeWeChatId(groupId);
|
|
7055
|
+
if (normalizedGroupId && wechat.groups?.[normalizedGroupId]?.requireMention != null) {
|
|
7056
|
+
return wechat.groups[normalizedGroupId].requireMention;
|
|
6813
7057
|
}
|
|
6814
|
-
return true;
|
|
7058
|
+
return wechat.groups?.["*"]?.requireMention ?? true;
|
|
6815
7059
|
}
|
|
6816
7060
|
},
|
|
6817
7061
|
// ---- Messaging adapter ----
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-wechat/wechat",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "esbuild index.ts --bundle --format=esm --platform=node --outfile=dist/index.js --external:openclaw",
|
|
44
|
-
"typecheck": "tsc --noEmit"
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"test": "node --test --experimental-strip-types src/*.test.ts"
|
|
45
46
|
}
|
|
46
47
|
}
|