@nextclaw/channel-extension-weixin 0.1.3 → 0.1.5

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/main.js CHANGED
@@ -3,10 +3,22 @@ import { WeixinChannelAdapter } from "./services/weixin-channel-adapter.service.
3
3
  import { WeixinExtensionRuntime } from "./services/weixin-extension-runtime.service.js";
4
4
  import { NextClawExtension } from "@nextclaw/extension-sdk";
5
5
  //#region src/main.ts
6
+ function readRequiredString(value, name) {
7
+ if (typeof value !== "string" || !value.trim()) throw new Error(`${name} is required`);
8
+ return value.trim();
9
+ }
10
+ function readOptionalString(value) {
11
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
12
+ }
6
13
  const extension = new NextClawExtension();
7
14
  const weixin = extension.channels.use("weixin");
8
15
  const runtime = new WeixinExtensionRuntime(weixin, new WeixinChannelAdapter());
9
16
  extension.capabilities.provide("channel.auth", new WeixinAuthCapability({ channel: weixin }));
17
+ extension.capabilities.provideHandler("channel.outbound.sendText", async (payload) => await runtime.sendOutboundText({
18
+ to: readRequiredString(payload.to, "to"),
19
+ text: readRequiredString(payload.text, "text"),
20
+ accountId: readOptionalString(payload.accountId)
21
+ }));
10
22
  await runtime.start();
11
23
  //#endregion
12
24
  export {};
@@ -38,6 +38,11 @@ declare class WeixinChannelAdapter {
38
38
  text: string;
39
39
  contextToken?: string;
40
40
  }) => Promise<void>;
41
+ readonly sendOutboundText: (params: {
42
+ to: string;
43
+ text: string;
44
+ accountId?: string | null;
45
+ }) => Promise<void>;
41
46
  private readonly resolveReplySession;
42
47
  private readonly listAvailableAccountIds;
43
48
  private readonly resolveCanonicalRoute;
@@ -161,6 +161,20 @@ var WeixinChannelAdapter = class {
161
161
  contextToken
162
162
  });
163
163
  };
164
+ sendOutboundText = async (params) => {
165
+ const { accountId, text, to } = params;
166
+ const target = {
167
+ conversationId: to,
168
+ ...accountId ? { accountId } : {}
169
+ };
170
+ const account = this.resolveSendAccount(target);
171
+ await this.sendText({
172
+ account,
173
+ conversationId: target.conversationId,
174
+ text,
175
+ contextToken: this.resolveContextToken(target, account.accountId)
176
+ });
177
+ };
164
178
  resolveReplySession = (sessionId, route) => {
165
179
  const existing = this.replySessions.get(sessionId);
166
180
  if (existing) return existing;
@@ -5,15 +5,22 @@ import { ExtensionChannel } from "@nextclaw/extension-sdk";
5
5
  declare class WeixinExtensionRuntime {
6
6
  private readonly channel;
7
7
  private readonly adapter;
8
- private unsubscribeConfig;
9
- private unsubscribeMessages;
10
- private unsubscribeNcpEvents;
8
+ private readonly cleanups;
9
+ private started;
11
10
  constructor(channel: ExtensionChannel, adapter: WeixinChannelAdapter);
12
11
  readonly start: () => Promise<void>;
13
12
  readonly stop: () => Promise<void>;
13
+ readonly sendOutboundText: (params: {
14
+ to: string;
15
+ text: string;
16
+ accountId?: string | null;
17
+ }) => Promise<{
18
+ accepted: true;
19
+ }>;
14
20
  private readonly applyConfig;
15
21
  private readonly submitMessage;
16
22
  private readonly sendNcpEvent;
23
+ private readonly drainCleanups;
17
24
  }
18
25
  //#endregion
19
26
  export { WeixinExtensionRuntime };
@@ -1,29 +1,31 @@
1
1
  //#region src/services/weixin-extension-runtime.service.ts
2
2
  var WeixinExtensionRuntime = class {
3
- unsubscribeConfig = null;
4
- unsubscribeMessages = null;
5
- unsubscribeNcpEvents = null;
3
+ cleanups = [];
4
+ started = false;
6
5
  constructor(channel, adapter) {
7
6
  this.channel = channel;
8
7
  this.adapter = adapter;
9
8
  }
10
9
  start = async () => {
11
- this.unsubscribeMessages = this.adapter.onMessage(this.submitMessage);
12
- this.unsubscribeNcpEvents = this.channel.onNcpEvent(this.sendNcpEvent);
13
- this.unsubscribeConfig = this.channel.config.onChange(async () => {
10
+ if (this.started) return;
11
+ this.started = true;
12
+ this.cleanups.push(this.adapter.onMessage(this.submitMessage));
13
+ this.cleanups.push(this.channel.onNcpEvent(this.sendNcpEvent));
14
+ this.cleanups.push(this.channel.config.onChange(async () => {
14
15
  await this.applyConfig();
15
- });
16
+ }));
16
17
  await this.applyConfig();
17
18
  };
18
19
  stop = async () => {
19
- this.unsubscribeConfig?.();
20
- this.unsubscribeMessages?.();
21
- this.unsubscribeNcpEvents?.();
22
- this.unsubscribeConfig = null;
23
- this.unsubscribeMessages = null;
24
- this.unsubscribeNcpEvents = null;
20
+ if (!this.started) return;
21
+ this.started = false;
22
+ this.drainCleanups();
25
23
  await this.adapter.stop();
26
24
  };
25
+ sendOutboundText = async (params) => {
26
+ await this.adapter.sendOutboundText(params);
27
+ return { accepted: true };
28
+ };
27
29
  applyConfig = async () => {
28
30
  const config = await this.channel.config.get();
29
31
  await this.adapter.configure(config);
@@ -60,6 +62,9 @@ var WeixinExtensionRuntime = class {
60
62
  console.warn(`[weixin] failed to send NCP event: ${message}`);
61
63
  }
62
64
  };
65
+ drainCleanups = () => {
66
+ for (const cleanup of this.cleanups.splice(0).reverse()) cleanup();
67
+ };
63
68
  };
64
69
  //#endregion
65
70
  export { WeixinExtensionRuntime };
@@ -38,6 +38,11 @@ type WeixinChannelAdapter = {
38
38
  stop: () => Promise<void>;
39
39
  onMessage: (handler: (message: WeixinInboundMessage) => void | Promise<void>) => () => void;
40
40
  sendNcpEvent: (event: NcpEndpointEvent) => Promise<void>;
41
+ sendOutboundText: (params: {
42
+ to: string;
43
+ text: string;
44
+ accountId?: string | null;
45
+ }) => Promise<void>;
41
46
  };
42
47
  //#endregion
43
48
  export { WeixinAccountConfig, WeixinChannelAdapter, WeixinChannelConfig, WeixinInboundMessage, WeixinRuntimeAccount };
@@ -13,6 +13,9 @@
13
13
  "id": "weixin",
14
14
  "name": "Weixin",
15
15
  "description": "Weixin QR login + getupdates long-poll channel",
16
+ "outbound": {
17
+ "text": true
18
+ },
16
19
  "auth": {
17
20
  "type": "request-response"
18
21
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/channel-extension-weixin",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "private": false,
5
5
  "description": "NextClaw Weixin channel extension process.",
6
6
  "type": "module",
@@ -18,9 +18,9 @@
18
18
  "README.md"
19
19
  ],
20
20
  "dependencies": {
21
- "@nextclaw/extension-sdk": "0.1.3",
22
- "@nextclaw/ncp": "0.5.9",
23
- "@nextclaw/ncp-toolkit": "0.5.14"
21
+ "@nextclaw/extension-sdk": "0.1.5",
22
+ "@nextclaw/ncp-toolkit": "0.5.16",
23
+ "@nextclaw/ncp": "0.5.11"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^20.17.6",