@elizaos/plugin-line 2.0.0-alpha.3 → 2.0.0-alpha.4

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.
Files changed (61) hide show
  1. package/dist/accounts.d.ts +152 -0
  2. package/dist/accounts.d.ts.map +1 -0
  3. package/dist/accounts.js +258 -0
  4. package/dist/accounts.js.map +1 -0
  5. package/dist/actions/index.d.ts +7 -0
  6. package/dist/actions/index.d.ts.map +1 -0
  7. package/{src/actions/index.ts → dist/actions/index.js} +1 -1
  8. package/dist/actions/index.js.map +1 -0
  9. package/dist/actions/sendFlexMessage.d.ts +6 -0
  10. package/dist/actions/sendFlexMessage.d.ts.map +1 -0
  11. package/dist/actions/sendFlexMessage.js +178 -0
  12. package/dist/actions/sendFlexMessage.js.map +1 -0
  13. package/dist/actions/sendLocation.d.ts +6 -0
  14. package/dist/actions/sendLocation.d.ts.map +1 -0
  15. package/dist/actions/sendLocation.js +155 -0
  16. package/dist/actions/sendLocation.js.map +1 -0
  17. package/dist/actions/sendMessage.d.ts +6 -0
  18. package/dist/actions/sendMessage.d.ts.map +1 -0
  19. package/dist/actions/sendMessage.js +146 -0
  20. package/dist/actions/sendMessage.js.map +1 -0
  21. package/dist/index.d.ts +22 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/messaging.d.ts +142 -0
  25. package/dist/messaging.d.ts.map +1 -0
  26. package/dist/messaging.js +348 -0
  27. package/dist/messaging.js.map +1 -0
  28. package/dist/providers/chatContext.d.ts +6 -0
  29. package/dist/providers/chatContext.d.ts.map +1 -0
  30. package/dist/providers/chatContext.js +85 -0
  31. package/dist/providers/chatContext.js.map +1 -0
  32. package/dist/providers/index.d.ts +6 -0
  33. package/dist/providers/index.d.ts.map +1 -0
  34. package/{src/providers/index.ts → dist/providers/index.js} +1 -1
  35. package/dist/providers/index.js.map +1 -0
  36. package/dist/providers/userContext.d.ts +6 -0
  37. package/dist/providers/userContext.d.ts.map +1 -0
  38. package/dist/providers/userContext.js +70 -0
  39. package/dist/providers/userContext.js.map +1 -0
  40. package/dist/service.d.ts +102 -0
  41. package/dist/service.d.ts.map +1 -0
  42. package/dist/service.js +433 -0
  43. package/dist/service.js.map +1 -0
  44. package/dist/types.d.ts +279 -0
  45. package/dist/types.d.ts.map +1 -0
  46. package/dist/types.js +106 -0
  47. package/dist/types.js.map +1 -0
  48. package/package.json +21 -6
  49. package/__tests__/integration.test.ts +0 -782
  50. package/build.ts +0 -16
  51. package/src/accounts.ts +0 -462
  52. package/src/actions/sendFlexMessage.ts +0 -243
  53. package/src/actions/sendLocation.ts +0 -223
  54. package/src/actions/sendMessage.ts +0 -202
  55. package/src/index.ts +0 -123
  56. package/src/messaging.ts +0 -507
  57. package/src/providers/chatContext.ts +0 -110
  58. package/src/providers/userContext.ts +0 -99
  59. package/src/service.ts +0 -578
  60. package/src/types.ts +0 -417
  61. package/tsconfig.json +0 -22
package/build.ts DELETED
@@ -1,16 +0,0 @@
1
- import { execSync } from "node:child_process";
2
- import { rmSync } from "node:fs";
3
- import { join } from "node:path";
4
-
5
- const distDir = join(import.meta.dirname, "dist");
6
-
7
- // Clean
8
- rmSync(distDir, { recursive: true, force: true });
9
-
10
- // Build
11
- execSync("npx tsc -p tsconfig.json", {
12
- cwd: import.meta.dirname,
13
- stdio: "inherit",
14
- });
15
-
16
- console.log("Build complete: plugin-line");
package/src/accounts.ts DELETED
@@ -1,462 +0,0 @@
1
- import type { IAgentRuntime } from "@elizaos/core";
2
-
3
- /**
4
- * Default account identifier used when no specific account is configured
5
- */
6
- export const DEFAULT_ACCOUNT_ID = "default";
7
-
8
- /**
9
- * Token source indicator
10
- */
11
- export type LineTokenSource = "config" | "env" | "character" | "none";
12
-
13
- /**
14
- * Group-specific configuration
15
- */
16
- export interface LineGroupConfig {
17
- /** If false, ignore messages from this group */
18
- enabled?: boolean;
19
- /** Allowlist for users in this group */
20
- allowFrom?: Array<string | number>;
21
- /** Require bot mention to respond */
22
- requireMention?: boolean;
23
- /** Custom system prompt for this group */
24
- systemPrompt?: string;
25
- /** Skills enabled for this group */
26
- skills?: string[];
27
- }
28
-
29
- /**
30
- * Configuration for a single LINE account
31
- */
32
- export interface LineAccountConfig {
33
- /** Optional display name for this account */
34
- name?: string;
35
- /** If false, do not start this LINE account */
36
- enabled?: boolean;
37
- /** Channel access token */
38
- channelAccessToken?: string;
39
- /** Channel secret */
40
- channelSecret?: string;
41
- /** Path to file containing channel access token */
42
- tokenFile?: string;
43
- /** Path to file containing channel secret */
44
- secretFile?: string;
45
- /** Allowlist for DM senders */
46
- allowFrom?: Array<string | number>;
47
- /** Allowlist for groups */
48
- groupAllowFrom?: Array<string | number>;
49
- /** DM access policy */
50
- dmPolicy?: "open" | "allowlist" | "pairing" | "disabled";
51
- /** Group message access policy */
52
- groupPolicy?: "open" | "allowlist" | "disabled";
53
- /** Max media size in MB */
54
- mediaMaxMb?: number;
55
- /** Custom webhook path */
56
- webhookPath?: string;
57
- /** Group-specific configurations */
58
- groups?: Record<string, LineGroupConfig>;
59
- }
60
-
61
- /**
62
- * Multi-account LINE configuration structure
63
- */
64
- export interface LineMultiAccountConfig {
65
- /** Default/base configuration applied to all accounts */
66
- enabled?: boolean;
67
- channelAccessToken?: string;
68
- channelSecret?: string;
69
- tokenFile?: string;
70
- secretFile?: string;
71
- dmPolicy?: "open" | "allowlist" | "pairing" | "disabled";
72
- groupPolicy?: "open" | "allowlist" | "disabled";
73
- mediaMaxMb?: number;
74
- webhookPath?: string;
75
- /** Per-account configuration overrides */
76
- accounts?: Record<string, LineAccountConfig>;
77
- /** Group configurations at base level */
78
- groups?: Record<string, LineGroupConfig>;
79
- }
80
-
81
- /**
82
- * Token resolution result
83
- */
84
- export interface LineTokenResolution {
85
- token: string;
86
- source: LineTokenSource;
87
- }
88
-
89
- /**
90
- * Resolved LINE account with all configuration merged
91
- */
92
- export interface ResolvedLineAccount {
93
- accountId: string;
94
- enabled: boolean;
95
- name?: string;
96
- channelAccessToken: string;
97
- channelSecret: string;
98
- tokenSource: LineTokenSource;
99
- configured: boolean;
100
- config: LineAccountConfig;
101
- }
102
-
103
- /**
104
- * Normalizes an account ID, returning the default if not provided
105
- */
106
- export function normalizeAccountId(accountId?: string | null): string {
107
- if (!accountId || typeof accountId !== "string") {
108
- return DEFAULT_ACCOUNT_ID;
109
- }
110
- const trimmed = accountId.trim().toLowerCase();
111
- if (!trimmed || trimmed === "default") {
112
- return DEFAULT_ACCOUNT_ID;
113
- }
114
- return trimmed;
115
- }
116
-
117
- /**
118
- * Gets the multi-account configuration from runtime settings
119
- */
120
- export function getMultiAccountConfig(
121
- runtime: IAgentRuntime,
122
- ): LineMultiAccountConfig {
123
- const characterLine = runtime.character?.settings?.line as
124
- | LineMultiAccountConfig
125
- | undefined;
126
-
127
- return {
128
- enabled: characterLine?.enabled,
129
- channelAccessToken: characterLine?.channelAccessToken,
130
- channelSecret: characterLine?.channelSecret,
131
- tokenFile: characterLine?.tokenFile,
132
- secretFile: characterLine?.secretFile,
133
- dmPolicy: characterLine?.dmPolicy,
134
- groupPolicy: characterLine?.groupPolicy,
135
- mediaMaxMb: characterLine?.mediaMaxMb,
136
- webhookPath: characterLine?.webhookPath,
137
- accounts: characterLine?.accounts,
138
- groups: characterLine?.groups,
139
- };
140
- }
141
-
142
- /**
143
- * Lists all configured account IDs
144
- */
145
- export function listLineAccountIds(runtime: IAgentRuntime): string[] {
146
- const config = getMultiAccountConfig(runtime);
147
- const accounts = config.accounts;
148
- const ids = new Set<string>();
149
-
150
- // Add default account if configured at base level
151
- const envToken = runtime.getSetting("LINE_CHANNEL_ACCESS_TOKEN") as
152
- | string
153
- | undefined;
154
- if (
155
- config.channelAccessToken?.trim() ||
156
- config.tokenFile ||
157
- envToken?.trim()
158
- ) {
159
- ids.add(DEFAULT_ACCOUNT_ID);
160
- }
161
-
162
- // Add named accounts
163
- if (accounts && typeof accounts === "object") {
164
- for (const id of Object.keys(accounts)) {
165
- if (id) {
166
- ids.add(id);
167
- }
168
- }
169
- }
170
-
171
- const result = Array.from(ids);
172
- if (result.length === 0) {
173
- return [DEFAULT_ACCOUNT_ID];
174
- }
175
-
176
- return result.slice().sort((a: string, b: string) => a.localeCompare(b));
177
- }
178
-
179
- /**
180
- * Resolves the default account ID to use
181
- */
182
- export function resolveDefaultLineAccountId(runtime: IAgentRuntime): string {
183
- const ids = listLineAccountIds(runtime);
184
- if (ids.includes(DEFAULT_ACCOUNT_ID)) {
185
- return DEFAULT_ACCOUNT_ID;
186
- }
187
- return ids[0] ?? DEFAULT_ACCOUNT_ID;
188
- }
189
-
190
- /**
191
- * Gets the account-specific configuration
192
- */
193
- function getAccountConfig(
194
- runtime: IAgentRuntime,
195
- accountId: string,
196
- ): LineAccountConfig | undefined {
197
- const config = getMultiAccountConfig(runtime);
198
- const accounts = config.accounts;
199
-
200
- if (!accounts || typeof accounts !== "object") {
201
- return undefined;
202
- }
203
-
204
- return accounts[accountId];
205
- }
206
-
207
- /**
208
- * Resolves the channel access token for a LINE account
209
- */
210
- export function resolveLineToken(
211
- runtime: IAgentRuntime,
212
- accountId: string,
213
- ): LineTokenResolution {
214
- const multiConfig = getMultiAccountConfig(runtime);
215
- const accountConfig = getAccountConfig(runtime, accountId);
216
-
217
- // Check account-level config first
218
- if (accountConfig?.channelAccessToken?.trim()) {
219
- return { token: accountConfig.channelAccessToken.trim(), source: "config" };
220
- }
221
-
222
- // For default account, check base config
223
- if (accountId === DEFAULT_ACCOUNT_ID) {
224
- if (multiConfig.channelAccessToken?.trim()) {
225
- return { token: multiConfig.channelAccessToken.trim(), source: "config" };
226
- }
227
-
228
- // Check environment/runtime settings
229
- const envToken = runtime.getSetting("LINE_CHANNEL_ACCESS_TOKEN") as
230
- | string
231
- | undefined;
232
- if (envToken?.trim()) {
233
- return { token: envToken.trim(), source: "env" };
234
- }
235
- }
236
-
237
- return { token: "", source: "none" };
238
- }
239
-
240
- /**
241
- * Resolves the channel secret for a LINE account
242
- */
243
- export function resolveLineSecret(
244
- runtime: IAgentRuntime,
245
- accountId: string,
246
- ): string {
247
- const multiConfig = getMultiAccountConfig(runtime);
248
- const accountConfig = getAccountConfig(runtime, accountId);
249
-
250
- // Check account-level config first
251
- if (accountConfig?.channelSecret?.trim()) {
252
- return accountConfig.channelSecret.trim();
253
- }
254
-
255
- // For default account, check base config and env
256
- if (accountId === DEFAULT_ACCOUNT_ID) {
257
- if (multiConfig.channelSecret?.trim()) {
258
- return multiConfig.channelSecret.trim();
259
- }
260
-
261
- const envSecret = runtime.getSetting("LINE_CHANNEL_SECRET") as
262
- | string
263
- | undefined;
264
- if (envSecret?.trim()) {
265
- return envSecret.trim();
266
- }
267
- }
268
-
269
- return "";
270
- }
271
-
272
- /**
273
- * Merges base configuration with account-specific overrides
274
- */
275
- /**
276
- * Removes undefined values from an object to prevent them from overwriting during spread
277
- */
278
- function filterDefined<T extends object>(obj: T): Partial<T> {
279
- return Object.fromEntries(
280
- Object.entries(obj).filter(([, v]) => v !== undefined),
281
- ) as Partial<T>;
282
- }
283
-
284
- function mergeLineAccountConfig(
285
- runtime: IAgentRuntime,
286
- accountId: string,
287
- ): LineAccountConfig {
288
- const multiConfig = getMultiAccountConfig(runtime);
289
- const { accounts: _ignored, ...baseConfig } = multiConfig;
290
- const accountConfig = getAccountConfig(runtime, accountId) ?? {};
291
-
292
- // Get environment/runtime settings for the base config
293
- const envDmPolicy = runtime.getSetting("LINE_DM_POLICY") as
294
- | string
295
- | undefined;
296
- const envGroupPolicy = runtime.getSetting("LINE_GROUP_POLICY") as
297
- | string
298
- | undefined;
299
-
300
- const envConfig: LineAccountConfig = {
301
- dmPolicy: envDmPolicy as LineAccountConfig["dmPolicy"] | undefined,
302
- groupPolicy: envGroupPolicy as LineAccountConfig["groupPolicy"] | undefined,
303
- };
304
-
305
- // Merge order: env defaults < base config < account config
306
- // Filter undefined values to prevent them from overwriting defined values
307
- return {
308
- ...filterDefined(envConfig),
309
- ...filterDefined(baseConfig),
310
- ...filterDefined(accountConfig),
311
- };
312
- }
313
-
314
- /**
315
- * Resolves a complete LINE account configuration
316
- */
317
- export function resolveLineAccount(
318
- runtime: IAgentRuntime,
319
- accountId?: string | null,
320
- ): ResolvedLineAccount {
321
- const normalizedAccountId = normalizeAccountId(accountId);
322
- const multiConfig = getMultiAccountConfig(runtime);
323
-
324
- const baseEnabled = multiConfig.enabled !== false;
325
- const merged = mergeLineAccountConfig(runtime, normalizedAccountId);
326
- const accountEnabled = merged.enabled !== false;
327
- const enabled = baseEnabled && accountEnabled;
328
-
329
- const { token, source: tokenSource } = resolveLineToken(
330
- runtime,
331
- normalizedAccountId,
332
- );
333
- const secret = resolveLineSecret(runtime, normalizedAccountId);
334
-
335
- // Determine if this account is actually configured
336
- const configured = Boolean(token || secret);
337
-
338
- return {
339
- accountId: normalizedAccountId,
340
- enabled,
341
- name: merged.name?.trim() || undefined,
342
- channelAccessToken: token,
343
- channelSecret: secret,
344
- tokenSource,
345
- configured,
346
- config: merged,
347
- };
348
- }
349
-
350
- /**
351
- * Lists all enabled LINE accounts
352
- */
353
- export function listEnabledLineAccounts(
354
- runtime: IAgentRuntime,
355
- ): ResolvedLineAccount[] {
356
- return listLineAccountIds(runtime)
357
- .map((accountId) => resolveLineAccount(runtime, accountId))
358
- .filter((account) => account.enabled && account.configured);
359
- }
360
-
361
- /**
362
- * Checks if multi-account mode is enabled
363
- */
364
- export function isMultiAccountEnabled(runtime: IAgentRuntime): boolean {
365
- const accounts = listEnabledLineAccounts(runtime);
366
- return accounts.length > 1;
367
- }
368
-
369
- /**
370
- * Resolves group configuration for a specific group
371
- */
372
- export function resolveLineGroupConfig(
373
- runtime: IAgentRuntime,
374
- accountId: string,
375
- groupId: string,
376
- ): LineGroupConfig | undefined {
377
- const multiConfig = getMultiAccountConfig(runtime);
378
- const accountConfig = getAccountConfig(runtime, accountId);
379
-
380
- // Check account-level groups first
381
- const accountGroup = accountConfig?.groups?.[groupId];
382
- if (accountGroup) {
383
- return accountGroup;
384
- }
385
-
386
- // Fall back to base-level groups
387
- return multiConfig.groups?.[groupId];
388
- }
389
-
390
- /**
391
- * Checks if a user is allowed based on policy and allowlist
392
- */
393
- export function isLineUserAllowed(params: {
394
- userId: string;
395
- accountConfig: LineAccountConfig;
396
- isGroup: boolean;
397
- groupId?: string;
398
- groupConfig?: LineGroupConfig;
399
- }): boolean {
400
- const { userId, accountConfig, isGroup, groupConfig } = params;
401
-
402
- if (isGroup) {
403
- const policy = accountConfig.groupPolicy ?? "allowlist";
404
- if (policy === "disabled") {
405
- return false;
406
- }
407
-
408
- if (policy === "open") {
409
- return true;
410
- }
411
-
412
- // Check group-specific allowlist first
413
- if (groupConfig?.allowFrom?.length) {
414
- return groupConfig.allowFrom.some(
415
- (allowed) => String(allowed) === userId,
416
- );
417
- }
418
-
419
- // Check account-level group allowlist
420
- if (accountConfig.groupAllowFrom?.length) {
421
- return accountConfig.groupAllowFrom.some(
422
- (allowed) => String(allowed) === userId,
423
- );
424
- }
425
-
426
- return policy !== "allowlist";
427
- }
428
-
429
- // DM handling
430
- const policy = accountConfig.dmPolicy ?? "pairing";
431
- if (policy === "disabled") {
432
- return false;
433
- }
434
-
435
- if (policy === "open") {
436
- return true;
437
- }
438
-
439
- if (policy === "pairing") {
440
- return true;
441
- }
442
-
443
- // Allowlist policy
444
- if (accountConfig.allowFrom?.length) {
445
- return accountConfig.allowFrom.some(
446
- (allowed) => String(allowed) === userId,
447
- );
448
- }
449
-
450
- return false;
451
- }
452
-
453
- /**
454
- * Checks if mention is required in a group
455
- */
456
- export function isLineMentionRequired(params: {
457
- accountConfig: LineAccountConfig;
458
- groupConfig?: LineGroupConfig;
459
- }): boolean {
460
- const { groupConfig } = params;
461
- return groupConfig?.requireMention ?? false;
462
- }
@@ -1,243 +0,0 @@
1
- /**
2
- * Send flex message action for the LINE plugin.
3
- */
4
-
5
- import type {
6
- Action,
7
- ActionResult,
8
- HandlerCallback,
9
- IAgentRuntime,
10
- Memory,
11
- State,
12
- } from "@elizaos/core";
13
- import {
14
- composePromptFromState,
15
- logger,
16
- ModelType,
17
- parseJSONObjectFromText,
18
- } from "@elizaos/core";
19
- import type { LineService } from "../service.js";
20
- import {
21
- isValidLineId,
22
- LINE_SERVICE_NAME,
23
- type LineFlexMessage,
24
- normalizeLineTarget,
25
- } from "../types.js";
26
-
27
- const SEND_FLEX_TEMPLATE = `# Task: Extract LINE Flex message parameters
28
-
29
- Based on the conversation, determine the flex message content to send.
30
-
31
- Recent conversation:
32
- {{recentMessages}}
33
-
34
- Extract the following:
35
- 1. altText: Alternative text for notifications (short summary)
36
- 2. title: Card title
37
- 3. body: Card body text
38
- 4. to: The target user/group/room ID (or "current" to reply to the current chat)
39
- 5. cardType: Type of card (info, image, action, list)
40
-
41
- Respond with a JSON object:
42
- \`\`\`json
43
- {
44
- "altText": "notification text",
45
- "title": "Card Title",
46
- "body": "Card body text",
47
- "to": "target ID or 'current'",
48
- "cardType": "info"
49
- }
50
- \`\`\`
51
- `;
52
-
53
- interface FlexMessageParams {
54
- altText: string;
55
- title: string;
56
- body: string;
57
- to: string;
58
- cardType: string;
59
- }
60
-
61
- /**
62
- * Create a simple info card bubble
63
- */
64
- function createInfoBubble(
65
- title: string,
66
- body: string,
67
- ): { type: string; [key: string]: unknown } {
68
- return {
69
- type: "bubble",
70
- body: {
71
- type: "box",
72
- layout: "vertical",
73
- contents: [
74
- {
75
- type: "text",
76
- text: title,
77
- weight: "bold",
78
- size: "xl",
79
- wrap: true,
80
- },
81
- {
82
- type: "text",
83
- text: body,
84
- margin: "md",
85
- wrap: true,
86
- },
87
- ],
88
- },
89
- };
90
- }
91
-
92
- export const sendFlexMessage: Action = {
93
- name: "LINE_SEND_FLEX_MESSAGE",
94
- similes: ["SEND_LINE_CARD", "LINE_FLEX", "LINE_CARD", "SEND_LINE_FLEX"],
95
- description: "Send a rich flex message/card via LINE",
96
-
97
- validate: async (
98
- _runtime: IAgentRuntime,
99
- message: Memory,
100
- _state?: State,
101
- ): Promise<boolean> => {
102
- return message.content.source === "line";
103
- },
104
-
105
- handler: async (
106
- runtime: IAgentRuntime,
107
- message: Memory,
108
- state: State | undefined,
109
- _options?: Record<string, unknown>,
110
- callback?: HandlerCallback,
111
- ): Promise<ActionResult> => {
112
- const lineService = runtime.getService(LINE_SERVICE_NAME) as unknown as
113
- | LineService
114
- | undefined;
115
-
116
- if (!lineService || !lineService.isConnected()) {
117
- if (callback) {
118
- callback({ text: "LINE service is not available.", source: "line" });
119
- }
120
- return { success: false, error: "LINE service not available" };
121
- }
122
-
123
- const currentState = state ?? (await runtime.composeState(message));
124
-
125
- const prompt = composePromptFromState({
126
- template: SEND_FLEX_TEMPLATE,
127
- state: currentState,
128
- });
129
-
130
- let flexInfo: FlexMessageParams | null = null;
131
-
132
- for (let attempt = 0; attempt < 3; attempt++) {
133
- const response = await runtime.useModel(ModelType.TEXT_SMALL, {
134
- prompt,
135
- });
136
-
137
- const parsed = parseJSONObjectFromText(response);
138
- if (parsed?.title && parsed?.body) {
139
- flexInfo = {
140
- altText: String(parsed.altText || `${parsed.title}: ${parsed.body}`),
141
- title: String(parsed.title),
142
- body: String(parsed.body),
143
- to: String(parsed.to || "current"),
144
- cardType: String(parsed.cardType || "info"),
145
- };
146
- break;
147
- }
148
- }
149
-
150
- if (!flexInfo || !flexInfo.title) {
151
- if (callback) {
152
- callback({
153
- text: "I couldn't understand the flex message content. Please try again.",
154
- source: "line",
155
- });
156
- }
157
- return {
158
- success: false,
159
- error: "Could not extract flex message parameters",
160
- };
161
- }
162
-
163
- // Determine target
164
- let targetId: string | undefined;
165
-
166
- if (flexInfo.to && flexInfo.to !== "current") {
167
- const normalized = normalizeLineTarget(flexInfo.to);
168
- if (normalized && isValidLineId(normalized)) {
169
- targetId = normalized;
170
- }
171
- }
172
-
173
- // Fall back to current chat
174
- if (!targetId) {
175
- const stateData = (currentState.data || {}) as Record<string, unknown>;
176
- targetId =
177
- (stateData.groupId as string) ||
178
- (stateData.roomId as string) ||
179
- (stateData.userId as string);
180
- }
181
-
182
- if (!targetId) {
183
- if (callback) {
184
- callback({
185
- text: "I couldn't determine where to send the message. Please specify a target.",
186
- source: "line",
187
- });
188
- }
189
- return { success: false, error: "Could not determine target" };
190
- }
191
-
192
- // Create flex message
193
- const flexMessage: LineFlexMessage = {
194
- altText: flexInfo.altText.slice(0, 400),
195
- contents: createInfoBubble(flexInfo.title, flexInfo.body),
196
- };
197
-
198
- // Send message
199
- const result = await lineService.sendFlexMessage(targetId, flexMessage);
200
-
201
- if (!result.success) {
202
- if (callback) {
203
- callback({
204
- text: `Failed to send flex message: ${result.error}`,
205
- source: "line",
206
- });
207
- }
208
- return { success: false, error: result.error };
209
- }
210
-
211
- logger.debug(`Sent LINE flex message to ${targetId}`);
212
-
213
- if (callback) {
214
- callback({
215
- text: "Card message sent successfully.",
216
- source: message.content.source as string,
217
- });
218
- }
219
-
220
- return {
221
- success: true,
222
- text: "Card message sent successfully",
223
- };
224
- },
225
-
226
- examples: [
227
- [
228
- {
229
- name: "{{user1}}",
230
- content: {
231
- text: "Send them an info card with title 'Update' and body 'New features are available'",
232
- },
233
- },
234
- {
235
- name: "{{agent}}",
236
- content: {
237
- text: "I'll send that as a card message.",
238
- actions: ["LINE_SEND_FLEX_MESSAGE"],
239
- },
240
- },
241
- ],
242
- ],
243
- };