@zhin.js/adapter-line 4.0.13 → 5.0.0
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/CHANGELOG.md +45 -0
- package/adapters/line.js +2 -1
- package/adapters/line.ts +2 -1
- package/lib/endpoint.d.ts +2 -1
- package/lib/endpoint.js +6 -1
- package/lib/protocol.d.ts +1 -0
- package/lib/protocol.js +6 -0
- package/lib/side-event-dispatch.d.ts +4 -0
- package/lib/side-event-dispatch.js +42 -0
- package/package.json +13 -13
- package/src/endpoint.ts +14 -1
- package/src/protocol.ts +7 -0
- package/src/side-event-dispatch.ts +54 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @zhin.js/adapter-line
|
|
2
2
|
|
|
3
|
+
## 5.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [b10d058]
|
|
8
|
+
- Updated dependencies [f2c532f]
|
|
9
|
+
- Updated dependencies [3dbf990]
|
|
10
|
+
- @zhin.js/host-http@1.0.12
|
|
11
|
+
- @zhin.js/adapter@1.2.0
|
|
12
|
+
- @zhin.js/core@1.5.13
|
|
13
|
+
- @zhin.js/agent@1.1.15
|
|
14
|
+
- zhin.js@6.0.13
|
|
15
|
+
|
|
16
|
+
## 4.0.14
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- 5969c5b: Add SideEventGateway so adapters forward notice/request/system into HandlerIndex. HandlerContext now exposes only generation-safe capabilities and prompt ports; live Endpoint escape hatches are removed.
|
|
21
|
+
- Updated dependencies [5969c5b]
|
|
22
|
+
- Updated dependencies [d336a3f]
|
|
23
|
+
- Updated dependencies [0c82a7e]
|
|
24
|
+
- Updated dependencies [b9217e4]
|
|
25
|
+
- Updated dependencies [5969c5b]
|
|
26
|
+
- Updated dependencies [5969c5b]
|
|
27
|
+
- Updated dependencies [974772e]
|
|
28
|
+
- Updated dependencies [5969c5b]
|
|
29
|
+
- Updated dependencies [5969c5b]
|
|
30
|
+
- Updated dependencies [2f786bd]
|
|
31
|
+
- Updated dependencies [63d89f9]
|
|
32
|
+
- Updated dependencies [71c7cdd]
|
|
33
|
+
- Updated dependencies [3cca0ea]
|
|
34
|
+
- Updated dependencies [1312ca0]
|
|
35
|
+
- Updated dependencies [985fa22]
|
|
36
|
+
- Updated dependencies [04b861d]
|
|
37
|
+
- Updated dependencies [a23d544]
|
|
38
|
+
- Updated dependencies [8cddabf]
|
|
39
|
+
- Updated dependencies [dbe5081]
|
|
40
|
+
- @zhin.js/im-contract@1.0.4
|
|
41
|
+
- @zhin.js/core@1.5.12
|
|
42
|
+
- @zhin.js/adapter@1.1.11
|
|
43
|
+
- @zhin.js/agent@1.1.14
|
|
44
|
+
- @zhin.js/host-http@1.0.11
|
|
45
|
+
- @zhin.js/command@1.0.15
|
|
46
|
+
- zhin.js@6.0.12
|
|
47
|
+
|
|
3
48
|
## 4.0.13
|
|
4
49
|
|
|
5
50
|
### Patch Changes
|
package/adapters/line.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Convention entry: discover `adapters/line.ts` → defineAdapter.
|
|
4
4
|
*/
|
|
5
5
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
-
import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
6
|
+
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
7
7
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
8
8
|
import { LineEndpoint } from "../lib/endpoint.js";
|
|
9
9
|
import { resolveLineConfig, } from "../lib/protocol.js";
|
|
@@ -26,6 +26,7 @@ export default defineAdapter({
|
|
|
26
26
|
return new LineEndpoint({
|
|
27
27
|
id: context.id,
|
|
28
28
|
gateway: context.use(messageGatewayToken),
|
|
29
|
+
sideEvents: context.use(sideEventGatewayToken),
|
|
29
30
|
http: context.use(httpHostToken),
|
|
30
31
|
config,
|
|
31
32
|
});
|
package/adapters/line.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Convention entry: discover `adapters/line.ts` → defineAdapter.
|
|
3
3
|
*/
|
|
4
4
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
5
|
-
import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
5
|
+
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
6
6
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
7
7
|
import { LineEndpoint } from '../src/endpoint.js';
|
|
8
8
|
import {
|
|
@@ -31,6 +31,7 @@ export default defineAdapter<LineAdapterConfig>({
|
|
|
31
31
|
return new LineEndpoint({
|
|
32
32
|
id: context.id,
|
|
33
33
|
gateway: context.use(messageGatewayToken),
|
|
34
|
+
sideEvents: context.use(sideEventGatewayToken),
|
|
34
35
|
http: context.use(httpHostToken),
|
|
35
36
|
config,
|
|
36
37
|
});
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* LineEndpoint — lifecycle, outbound, admit, OpenAPI helpers for agent tools.
|
|
3
3
|
*/
|
|
4
4
|
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
|
|
5
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
5
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
6
6
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
7
7
|
import type { CapabilityId } from 'zhin.js';
|
|
8
8
|
import { type LineEvent, type ResolvedLineConfig } from './protocol.js';
|
|
@@ -20,6 +20,7 @@ export type LineFetch = (url: string, init?: {
|
|
|
20
20
|
export interface LineEndpointOptions {
|
|
21
21
|
readonly id: CapabilityId;
|
|
22
22
|
readonly gateway: MessageGateway;
|
|
23
|
+
readonly sideEvents?: SideEventGateway;
|
|
23
24
|
readonly http: HttpHost;
|
|
24
25
|
readonly config: ResolvedLineConfig;
|
|
25
26
|
readonly fetch?: LineFetch;
|
package/lib/endpoint.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
2
2
|
import { registerLineAgentEndpoint } from './line-agent-deps.js';
|
|
3
|
-
import { formatInboundContent, formatOutboundMessages, generateMessageId, isMessageEvent, isValidLineRecipientId, lineInboundConversation, } from './protocol.js';
|
|
3
|
+
import { formatInboundContent, formatOutboundMessages, generateMessageId, isLineLifecycleEvent, isMessageEvent, isValidLineRecipientId, lineInboundConversation, } from './protocol.js';
|
|
4
4
|
import { registerLineWebhookRoutes } from './webhook.js';
|
|
5
|
+
import { receiveLineSideEvent } from './side-event-dispatch.js';
|
|
5
6
|
/** LINE replyToken 有效期短,过期后 reply 必 400;缓存带时间戳,超时弃用改走 push。 */
|
|
6
7
|
const REPLY_TOKEN_TTL_MS = 60_000;
|
|
7
8
|
/** 出站 HTTP 调用统一 30s 超时。 */
|
|
@@ -104,6 +105,10 @@ export class LineEndpoint {
|
|
|
104
105
|
admit(event) {
|
|
105
106
|
if (!this.#open)
|
|
106
107
|
return;
|
|
108
|
+
if (isLineLifecycleEvent(event)) {
|
|
109
|
+
receiveLineSideEvent(this.#options.sideEvents, String(this.#options.id), this.#options.config.id, event, this.#logger);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
107
112
|
const conversation = lineInboundConversation(String(this.#options.id), event.source);
|
|
108
113
|
if ('replyToken' in event && typeof event.replyToken === 'string') {
|
|
109
114
|
this.#replyTokenCache.set(conversation.id, { token: event.replyToken, timestamp: Date.now() });
|
package/lib/protocol.d.ts
CHANGED
|
@@ -135,6 +135,7 @@ export interface LineChannel {
|
|
|
135
135
|
export declare function resolveLineConfig(config?: LineAdapterConfig): ResolvedLineConfig;
|
|
136
136
|
export declare function normalizeWebhookPath(path: string): string;
|
|
137
137
|
export declare function isMessageEvent(event: LineEvent): event is LineMessageEvent;
|
|
138
|
+
export declare function isLineLifecycleEvent(event: LineEvent): boolean;
|
|
138
139
|
export declare function isPostbackEvent(event: LineEvent): event is LinePostbackEvent;
|
|
139
140
|
export declare function resolveChannel(source: LineSource): LineChannel;
|
|
140
141
|
/**
|
package/lib/protocol.js
CHANGED
|
@@ -39,6 +39,12 @@ export function normalizeWebhookPath(path) {
|
|
|
39
39
|
export function isMessageEvent(event) {
|
|
40
40
|
return event.type === 'message' && 'message' in event && event.message != null;
|
|
41
41
|
}
|
|
42
|
+
export function isLineLifecycleEvent(event) {
|
|
43
|
+
return event.type === 'follow'
|
|
44
|
+
|| event.type === 'unfollow'
|
|
45
|
+
|| event.type === 'join'
|
|
46
|
+
|| event.type === 'leave';
|
|
47
|
+
}
|
|
42
48
|
export function isPostbackEvent(event) {
|
|
43
49
|
return event.type === 'postback' && 'postback' in event;
|
|
44
50
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
2
|
+
import { type getAdapterLogger } from '@zhin.js/logger';
|
|
3
|
+
import { type LineEvent } from './protocol.js';
|
|
4
|
+
export declare function receiveLineSideEvent(sideEvents: SideEventGateway | undefined, endpointKey: string, configId: string, event: LineEvent, logger: ReturnType<typeof getAdapterLogger>): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { buildNotice, senderFromId } from '@zhin.js/core';
|
|
2
|
+
import { formatCompact } from '@zhin.js/logger';
|
|
3
|
+
import { isLineLifecycleEvent, lineInboundConversation, } from './protocol.js';
|
|
4
|
+
function mapLineLifecycleParts(type) {
|
|
5
|
+
switch (type) {
|
|
6
|
+
case 'follow':
|
|
7
|
+
return { scene_type: 'friend', sub_type: 'increase' };
|
|
8
|
+
case 'unfollow':
|
|
9
|
+
return { scene_type: 'friend', sub_type: 'decrease' };
|
|
10
|
+
case 'join':
|
|
11
|
+
return { scene_type: 'group', sub_type: 'member_increase' };
|
|
12
|
+
case 'leave':
|
|
13
|
+
return { scene_type: 'group', sub_type: 'member_decrease' };
|
|
14
|
+
default:
|
|
15
|
+
return { scene_type: 'line', sub_type: type };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function receiveLineSideEvent(sideEvents, endpointKey, configId, event, logger) {
|
|
19
|
+
if (!sideEvents || !isLineLifecycleEvent(event))
|
|
20
|
+
return;
|
|
21
|
+
const parts = mapLineLifecycleParts(event.type);
|
|
22
|
+
const conversation = lineInboundConversation(endpointKey, event.source);
|
|
23
|
+
const userId = event.source.userId || conversation.id;
|
|
24
|
+
void sideEvents.receiveNotice(buildNotice(event, {
|
|
25
|
+
$id: `line:${event.type}:${event.timestamp}:${userId}`,
|
|
26
|
+
$adapter: 'line',
|
|
27
|
+
$endpoint: configId,
|
|
28
|
+
$type: 'notice',
|
|
29
|
+
$scene_id: conversation.id,
|
|
30
|
+
$scene_type: parts.scene_type,
|
|
31
|
+
$sub_type: parts.sub_type,
|
|
32
|
+
$actor: senderFromId(userId),
|
|
33
|
+
$timestamp: event.timestamp,
|
|
34
|
+
})).catch((err) => {
|
|
35
|
+
logger.warn(formatCompact({
|
|
36
|
+
op: 'line_side_event_failed',
|
|
37
|
+
endpoint: configId,
|
|
38
|
+
event: event.type,
|
|
39
|
+
error: err instanceof Error ? err.message : String(err),
|
|
40
|
+
}));
|
|
41
|
+
});
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-line",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Zhin.js LINE Messaging API adapter for Plugin Runtime (HTTP webhook)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -32,20 +32,20 @@
|
|
|
32
32
|
"directory": "plugins/adapters/line"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@zhin.js/adapter": "1.
|
|
36
|
-
"@zhin.js/core": "1.5.
|
|
37
|
-
"@zhin.js/host-http": "1.0.
|
|
38
|
-
"@zhin.js/im-contract": "1.0.
|
|
35
|
+
"@zhin.js/adapter": "1.2.0",
|
|
36
|
+
"@zhin.js/core": "1.5.13",
|
|
37
|
+
"@zhin.js/host-http": "1.0.12",
|
|
38
|
+
"@zhin.js/im-contract": "1.0.4",
|
|
39
39
|
"@zhin.js/logger": "1.0.76"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"zod": "^4.0.0",
|
|
43
|
-
"@zhin.js/adapter": "1.
|
|
44
|
-
"@zhin.js/agent": "1.1.
|
|
45
|
-
"@zhin.js/command": "1.0.
|
|
46
|
-
"@zhin.js/core": "1.5.
|
|
47
|
-
"@zhin.js/host-http": "1.0.
|
|
48
|
-
"zhin.js": "6.0.
|
|
43
|
+
"@zhin.js/adapter": "1.2.0",
|
|
44
|
+
"@zhin.js/agent": "1.1.15",
|
|
45
|
+
"@zhin.js/command": "1.0.15",
|
|
46
|
+
"@zhin.js/core": "1.5.13",
|
|
47
|
+
"@zhin.js/host-http": "1.0.12",
|
|
48
|
+
"zhin.js": "6.0.13"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
51
|
"@zhin.js/agent": {
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
"typescript": "^6.0.3",
|
|
67
67
|
"vitest": "^4.1.10",
|
|
68
68
|
"zod": "^4.4.3",
|
|
69
|
-
"@zhin.js/agent": "1.1.
|
|
70
|
-
"zhin.js": "6.0.
|
|
69
|
+
"@zhin.js/agent": "1.1.15",
|
|
70
|
+
"zhin.js": "6.0.13"
|
|
71
71
|
},
|
|
72
72
|
"files": [
|
|
73
73
|
"adapters",
|
package/src/endpoint.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* LineEndpoint — lifecycle, outbound, admit, OpenAPI helpers for agent tools.
|
|
3
3
|
*/
|
|
4
4
|
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
|
|
5
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
5
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
6
6
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
7
7
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
8
8
|
import type { CapabilityId } from 'zhin.js';
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
formatInboundContent,
|
|
12
12
|
formatOutboundMessages,
|
|
13
13
|
generateMessageId,
|
|
14
|
+
isLineLifecycleEvent,
|
|
14
15
|
isMessageEvent,
|
|
15
16
|
isValidLineRecipientId,
|
|
16
17
|
lineInboundConversation,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
type ResolvedLineConfig,
|
|
20
21
|
} from './protocol.js';
|
|
21
22
|
import { registerLineWebhookRoutes } from './webhook.js';
|
|
23
|
+
import { receiveLineSideEvent } from './side-event-dispatch.js';
|
|
22
24
|
|
|
23
25
|
/** LINE replyToken 有效期短,过期后 reply 必 400;缓存带时间戳,超时弃用改走 push。 */
|
|
24
26
|
const REPLY_TOKEN_TTL_MS = 60_000;
|
|
@@ -43,6 +45,7 @@ export type LineFetch = (
|
|
|
43
45
|
export interface LineEndpointOptions {
|
|
44
46
|
readonly id: CapabilityId;
|
|
45
47
|
readonly gateway: MessageGateway;
|
|
48
|
+
readonly sideEvents?: SideEventGateway;
|
|
46
49
|
readonly http: HttpHost;
|
|
47
50
|
readonly config: ResolvedLineConfig;
|
|
48
51
|
readonly fetch?: LineFetch;
|
|
@@ -155,6 +158,16 @@ export class LineEndpoint implements EndpointInstance {
|
|
|
155
158
|
/** Test / internal: admit a parsed event when open (non-webhook path). */
|
|
156
159
|
admit(event: LineEvent): void {
|
|
157
160
|
if (!this.#open) return;
|
|
161
|
+
if (isLineLifecycleEvent(event)) {
|
|
162
|
+
receiveLineSideEvent(
|
|
163
|
+
this.#options.sideEvents,
|
|
164
|
+
String(this.#options.id),
|
|
165
|
+
this.#options.config.id,
|
|
166
|
+
event,
|
|
167
|
+
this.#logger,
|
|
168
|
+
);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
158
171
|
const conversation = lineInboundConversation(String(this.#options.id), event.source);
|
|
159
172
|
if ('replyToken' in event && typeof event.replyToken === 'string') {
|
|
160
173
|
this.#replyTokenCache.set(conversation.id, { token: event.replyToken, timestamp: Date.now() });
|
package/src/protocol.ts
CHANGED
|
@@ -201,6 +201,13 @@ export function isMessageEvent(event: LineEvent): event is LineMessageEvent {
|
|
|
201
201
|
return event.type === 'message' && 'message' in event && (event as LineMessageEvent).message != null;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
export function isLineLifecycleEvent(event: LineEvent): boolean {
|
|
205
|
+
return event.type === 'follow'
|
|
206
|
+
|| event.type === 'unfollow'
|
|
207
|
+
|| event.type === 'join'
|
|
208
|
+
|| event.type === 'leave';
|
|
209
|
+
}
|
|
210
|
+
|
|
204
211
|
export function isPostbackEvent(event: LineEvent): event is LinePostbackEvent {
|
|
205
212
|
return event.type === 'postback' && 'postback' in event;
|
|
206
213
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { buildNotice, senderFromId } from '@zhin.js/core';
|
|
2
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
3
|
+
import { formatCompact, type getAdapterLogger } from '@zhin.js/logger';
|
|
4
|
+
import {
|
|
5
|
+
isLineLifecycleEvent,
|
|
6
|
+
lineInboundConversation,
|
|
7
|
+
type LineEvent,
|
|
8
|
+
} from './protocol.js';
|
|
9
|
+
|
|
10
|
+
function mapLineLifecycleParts(type: LineEvent['type']): { scene_type: string; sub_type: string } {
|
|
11
|
+
switch (type) {
|
|
12
|
+
case 'follow':
|
|
13
|
+
return { scene_type: 'friend', sub_type: 'increase' };
|
|
14
|
+
case 'unfollow':
|
|
15
|
+
return { scene_type: 'friend', sub_type: 'decrease' };
|
|
16
|
+
case 'join':
|
|
17
|
+
return { scene_type: 'group', sub_type: 'member_increase' };
|
|
18
|
+
case 'leave':
|
|
19
|
+
return { scene_type: 'group', sub_type: 'member_decrease' };
|
|
20
|
+
default:
|
|
21
|
+
return { scene_type: 'line', sub_type: type };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function receiveLineSideEvent(
|
|
26
|
+
sideEvents: SideEventGateway | undefined,
|
|
27
|
+
endpointKey: string,
|
|
28
|
+
configId: string,
|
|
29
|
+
event: LineEvent,
|
|
30
|
+
logger: ReturnType<typeof getAdapterLogger>,
|
|
31
|
+
): void {
|
|
32
|
+
if (!sideEvents || !isLineLifecycleEvent(event)) return;
|
|
33
|
+
const parts = mapLineLifecycleParts(event.type);
|
|
34
|
+
const conversation = lineInboundConversation(endpointKey, event.source);
|
|
35
|
+
const userId = event.source.userId || conversation.id;
|
|
36
|
+
void sideEvents.receiveNotice(buildNotice(event, {
|
|
37
|
+
$id: `line:${event.type}:${event.timestamp}:${userId}`,
|
|
38
|
+
$adapter: 'line' as never,
|
|
39
|
+
$endpoint: configId,
|
|
40
|
+
$type: 'notice',
|
|
41
|
+
$scene_id: conversation.id,
|
|
42
|
+
$scene_type: parts.scene_type,
|
|
43
|
+
$sub_type: parts.sub_type,
|
|
44
|
+
$actor: senderFromId(userId),
|
|
45
|
+
$timestamp: event.timestamp,
|
|
46
|
+
})).catch((err) => {
|
|
47
|
+
logger.warn(formatCompact({
|
|
48
|
+
op: 'line_side_event_failed',
|
|
49
|
+
endpoint: configId,
|
|
50
|
+
event: event.type,
|
|
51
|
+
error: err instanceof Error ? err.message : String(err),
|
|
52
|
+
}));
|
|
53
|
+
});
|
|
54
|
+
}
|