@gakr-gakr/line 0.1.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/api.ts +11 -0
- package/autobot.plugin.json +15 -0
- package/channel-plugin-api.ts +1 -0
- package/contract-api.ts +5 -0
- package/index.ts +54 -0
- package/package.json +60 -0
- package/runtime-api.ts +182 -0
- package/secret-contract-api.ts +4 -0
- package/setup-api.ts +2 -0
- package/setup-entry.ts +9 -0
- package/src/account-helpers.ts +16 -0
- package/src/accounts.ts +187 -0
- package/src/actions.ts +61 -0
- package/src/auto-reply-delivery.ts +200 -0
- package/src/bindings.ts +65 -0
- package/src/bot-access.ts +30 -0
- package/src/bot-handlers.ts +620 -0
- package/src/bot-message-context.ts +586 -0
- package/src/bot.ts +70 -0
- package/src/card-command.ts +347 -0
- package/src/channel-access-token.ts +14 -0
- package/src/channel-api.ts +17 -0
- package/src/channel-shared.ts +48 -0
- package/src/channel.runtime.ts +3 -0
- package/src/channel.setup.ts +11 -0
- package/src/channel.ts +155 -0
- package/src/config-adapter.ts +29 -0
- package/src/config-schema.ts +81 -0
- package/src/download.ts +34 -0
- package/src/flex-templates/basic-cards.ts +395 -0
- package/src/flex-templates/common.ts +20 -0
- package/src/flex-templates/media-control-cards.ts +555 -0
- package/src/flex-templates/message.ts +13 -0
- package/src/flex-templates/schedule-cards.ts +467 -0
- package/src/flex-templates/types.ts +22 -0
- package/src/flex-templates.ts +32 -0
- package/src/gateway.ts +129 -0
- package/src/group-keys.ts +65 -0
- package/src/group-policy.ts +22 -0
- package/src/markdown-to-line.ts +416 -0
- package/src/monitor-durable.ts +37 -0
- package/src/monitor.runtime.ts +1 -0
- package/src/monitor.ts +507 -0
- package/src/outbound-media.ts +120 -0
- package/src/outbound.runtime.ts +12 -0
- package/src/outbound.ts +427 -0
- package/src/probe.runtime.ts +1 -0
- package/src/probe.ts +34 -0
- package/src/quick-reply-fallback.ts +10 -0
- package/src/reply-chunks.ts +110 -0
- package/src/reply-payload-transform.ts +317 -0
- package/src/rich-menu.ts +326 -0
- package/src/runtime.ts +32 -0
- package/src/send-receipt.ts +32 -0
- package/src/send.ts +531 -0
- package/src/setup-core.ts +149 -0
- package/src/setup-runtime-api.ts +9 -0
- package/src/setup-surface.ts +229 -0
- package/src/signature.ts +24 -0
- package/src/status.ts +37 -0
- package/src/template-messages.ts +333 -0
- package/src/types.ts +130 -0
- package/src/webhook-node.ts +155 -0
- package/src/webhook-utils.ts +10 -0
- package/src/webhook.ts +135 -0
- package/tsconfig.json +16 -0
package/api.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
ChannelAccountSnapshot,
|
|
3
|
+
ChannelPlugin,
|
|
4
|
+
AutoBotConfig,
|
|
5
|
+
AutoBotPluginApi,
|
|
6
|
+
PluginRuntime,
|
|
7
|
+
} from "autobot/plugin-sdk/core";
|
|
8
|
+
export type { ReplyPayload } from "autobot/plugin-sdk/reply-runtime";
|
|
9
|
+
export type { ResolvedLineAccount } from "./runtime-api.js";
|
|
10
|
+
export { linePlugin } from "./src/channel.js";
|
|
11
|
+
export { lineSetupPlugin } from "./src/channel.setup.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "line",
|
|
3
|
+
"activation": {
|
|
4
|
+
"onStartup": false
|
|
5
|
+
},
|
|
6
|
+
"channels": ["line"],
|
|
7
|
+
"channelEnvVars": {
|
|
8
|
+
"line": ["LINE_CHANNEL_ACCESS_TOKEN", "LINE_CHANNEL_SECRET"]
|
|
9
|
+
},
|
|
10
|
+
"configSchema": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"properties": {}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { linePlugin } from "./src/channel.js";
|
package/contract-api.ts
ADDED
package/index.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineBundledChannelEntry,
|
|
3
|
+
type AutoBotPluginCommandDefinition,
|
|
4
|
+
type AutoBotPluginApi,
|
|
5
|
+
} from "autobot/plugin-sdk/channel-entry-contract";
|
|
6
|
+
|
|
7
|
+
type RegisteredLineCardCommand = AutoBotPluginCommandDefinition;
|
|
8
|
+
|
|
9
|
+
let lineCardCommandPromise: Promise<RegisteredLineCardCommand> | null = null;
|
|
10
|
+
|
|
11
|
+
async function loadLineCardCommand(api: AutoBotPluginApi): Promise<RegisteredLineCardCommand> {
|
|
12
|
+
lineCardCommandPromise ??= (async () => {
|
|
13
|
+
let registered: RegisteredLineCardCommand | null = null;
|
|
14
|
+
const { registerLineCardCommand } = await import("./src/card-command.js");
|
|
15
|
+
registerLineCardCommand({
|
|
16
|
+
...api,
|
|
17
|
+
registerCommand(command: RegisteredLineCardCommand) {
|
|
18
|
+
registered = command;
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
if (!registered) {
|
|
22
|
+
throw new Error("LINE card command registration unavailable");
|
|
23
|
+
}
|
|
24
|
+
return registered;
|
|
25
|
+
})();
|
|
26
|
+
return await lineCardCommandPromise;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default defineBundledChannelEntry({
|
|
30
|
+
id: "line",
|
|
31
|
+
name: "LINE",
|
|
32
|
+
description: "LINE Messaging API channel plugin",
|
|
33
|
+
importMetaUrl: import.meta.url,
|
|
34
|
+
plugin: {
|
|
35
|
+
specifier: "./channel-plugin-api.js",
|
|
36
|
+
exportName: "linePlugin",
|
|
37
|
+
},
|
|
38
|
+
runtime: {
|
|
39
|
+
specifier: "./runtime-api.js",
|
|
40
|
+
exportName: "setLineRuntime",
|
|
41
|
+
},
|
|
42
|
+
registerFull(api) {
|
|
43
|
+
api.registerCommand({
|
|
44
|
+
name: "card",
|
|
45
|
+
description: "Send a rich card message (LINE).",
|
|
46
|
+
acceptsArgs: true,
|
|
47
|
+
requireAuth: false,
|
|
48
|
+
async handler(ctx) {
|
|
49
|
+
const command = await loadLineCardCommand(api);
|
|
50
|
+
return await command.handler(ctx);
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gakr-gakr/line",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AutoBot LINE channel plugin",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/autobot/autobot"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@line/bot-sdk": "11.0.0",
|
|
12
|
+
"zod": "4.4.3"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@gakr-gakr/plugin-sdk": "workspace:*",
|
|
16
|
+
"@gakr-gakr/autobot": "workspace:*",
|
|
17
|
+
"autobot": "workspace:@gakr-gakr/autobot@*"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@gakr-gakr/autobot": ">=0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependenciesMeta": {
|
|
23
|
+
"@gakr-gakr/autobot": {
|
|
24
|
+
"optional": true
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"autobot": {
|
|
28
|
+
"extensions": [
|
|
29
|
+
"./index.ts"
|
|
30
|
+
],
|
|
31
|
+
"setupEntry": "./setup-entry.ts",
|
|
32
|
+
"channel": {
|
|
33
|
+
"id": "line",
|
|
34
|
+
"label": "LINE",
|
|
35
|
+
"selectionLabel": "LINE (Messaging API)",
|
|
36
|
+
"detailLabel": "LINE Bot",
|
|
37
|
+
"docsPath": "/channels/line",
|
|
38
|
+
"docsLabel": "line",
|
|
39
|
+
"blurb": "LINE Messaging API webhook bot.",
|
|
40
|
+
"systemImage": "message",
|
|
41
|
+
"order": 75,
|
|
42
|
+
"quickstartAllowFrom": true
|
|
43
|
+
},
|
|
44
|
+
"install": {
|
|
45
|
+
"npmSpec": "@gakr-gakr/line",
|
|
46
|
+
"defaultChoice": "npm",
|
|
47
|
+
"minHostVersion": ">=2026.4.10"
|
|
48
|
+
},
|
|
49
|
+
"compat": {
|
|
50
|
+
"pluginApi": ">=2026.5.19"
|
|
51
|
+
},
|
|
52
|
+
"build": {
|
|
53
|
+
"autobotVersion": "2026.5.19"
|
|
54
|
+
},
|
|
55
|
+
"release": {
|
|
56
|
+
"publishToClawHub": true,
|
|
57
|
+
"publishToNpm": true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
package/runtime-api.ts
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Private runtime barrel for the bundled LINE extension.
|
|
2
|
+
// Keep this barrel thin and aligned with the local extension surface.
|
|
3
|
+
|
|
4
|
+
export type {
|
|
5
|
+
ChannelAccountSnapshot,
|
|
6
|
+
ChannelPlugin,
|
|
7
|
+
AutoBotConfig,
|
|
8
|
+
AutoBotPluginApi,
|
|
9
|
+
PluginRuntime,
|
|
10
|
+
} from "autobot/plugin-sdk/core";
|
|
11
|
+
export type {
|
|
12
|
+
ChannelGatewayContext,
|
|
13
|
+
ChannelStatusIssue,
|
|
14
|
+
} from "autobot/plugin-sdk/channel-contract";
|
|
15
|
+
export { clearAccountEntryFields } from "autobot/plugin-sdk/core";
|
|
16
|
+
export { buildChannelConfigSchema } from "autobot/plugin-sdk/channel-config-schema";
|
|
17
|
+
export type { ReplyPayload } from "autobot/plugin-sdk/reply-runtime";
|
|
18
|
+
export type { ChannelSetupDmPolicy, ChannelSetupWizard } from "autobot/plugin-sdk/setup";
|
|
19
|
+
export {
|
|
20
|
+
buildComputedAccountStatusSnapshot,
|
|
21
|
+
buildTokenChannelStatusSummary,
|
|
22
|
+
} from "autobot/plugin-sdk/status-helpers";
|
|
23
|
+
export {
|
|
24
|
+
DEFAULT_ACCOUNT_ID,
|
|
25
|
+
formatDocsLink,
|
|
26
|
+
setSetupChannelEnabled,
|
|
27
|
+
splitSetupEntries,
|
|
28
|
+
} from "autobot/plugin-sdk/setup";
|
|
29
|
+
export { setLineRuntime } from "./src/runtime.js";
|
|
30
|
+
export { firstDefined, normalizeAllowFrom } from "./src/bot-access.js";
|
|
31
|
+
export { downloadLineMedia } from "./src/download.js";
|
|
32
|
+
export { probeLineBot } from "./src/probe.js";
|
|
33
|
+
export { buildTemplateMessageFromPayload } from "./src/template-messages.js";
|
|
34
|
+
export {
|
|
35
|
+
createQuickReplyItems,
|
|
36
|
+
pushFlexMessage,
|
|
37
|
+
pushLocationMessage,
|
|
38
|
+
pushMessageLine,
|
|
39
|
+
pushMessagesLine,
|
|
40
|
+
pushTemplateMessage,
|
|
41
|
+
pushTextMessageWithQuickReplies,
|
|
42
|
+
sendMessageLine,
|
|
43
|
+
} from "./src/send.js";
|
|
44
|
+
export { monitorLineProvider } from "./src/monitor.js";
|
|
45
|
+
export { hasLineDirectives, parseLineDirectives } from "./src/reply-payload-transform.js";
|
|
46
|
+
export {
|
|
47
|
+
listLineAccountIds,
|
|
48
|
+
normalizeAccountId,
|
|
49
|
+
resolveDefaultLineAccountId,
|
|
50
|
+
resolveLineAccount,
|
|
51
|
+
} from "./src/accounts.js";
|
|
52
|
+
export { type NormalizedAllowFrom } from "./src/bot-access.js";
|
|
53
|
+
export { resolveLineChannelAccessToken } from "./src/channel-access-token.js";
|
|
54
|
+
export {
|
|
55
|
+
LineChannelConfigSchema,
|
|
56
|
+
LineConfigSchema,
|
|
57
|
+
type LineConfigSchemaType,
|
|
58
|
+
} from "./src/config-schema.js";
|
|
59
|
+
export {
|
|
60
|
+
resolveExactLineGroupConfigKey,
|
|
61
|
+
resolveLineGroupConfigEntry,
|
|
62
|
+
resolveLineGroupLookupIds,
|
|
63
|
+
resolveLineGroupsConfig,
|
|
64
|
+
} from "./src/group-keys.js";
|
|
65
|
+
export {
|
|
66
|
+
type CodeBlock,
|
|
67
|
+
convertCodeBlockToFlexBubble,
|
|
68
|
+
convertLinksToFlexBubble,
|
|
69
|
+
convertTableToFlexBubble,
|
|
70
|
+
extractCodeBlocks,
|
|
71
|
+
extractLinks,
|
|
72
|
+
extractMarkdownTables,
|
|
73
|
+
hasMarkdownToConvert,
|
|
74
|
+
type MarkdownLink,
|
|
75
|
+
type MarkdownTable,
|
|
76
|
+
type ProcessedLineMessage,
|
|
77
|
+
processLineMessage,
|
|
78
|
+
stripMarkdown,
|
|
79
|
+
} from "./src/markdown-to-line.js";
|
|
80
|
+
export {
|
|
81
|
+
createAudioMessage,
|
|
82
|
+
createFlexMessage,
|
|
83
|
+
createImageMessage,
|
|
84
|
+
createLocationMessage,
|
|
85
|
+
createTextMessageWithQuickReplies,
|
|
86
|
+
createVideoMessage,
|
|
87
|
+
getUserDisplayName,
|
|
88
|
+
getUserProfile,
|
|
89
|
+
pushImageMessage,
|
|
90
|
+
replyMessageLine,
|
|
91
|
+
showLoadingAnimation,
|
|
92
|
+
} from "./src/send.js";
|
|
93
|
+
export { validateLineSignature } from "./src/signature.js";
|
|
94
|
+
export {
|
|
95
|
+
type ButtonsTemplate,
|
|
96
|
+
type CarouselColumn,
|
|
97
|
+
type CarouselTemplate,
|
|
98
|
+
type ConfirmTemplate,
|
|
99
|
+
createButtonMenu,
|
|
100
|
+
createButtonTemplate,
|
|
101
|
+
createCarouselColumn,
|
|
102
|
+
createConfirmTemplate,
|
|
103
|
+
createImageCarousel,
|
|
104
|
+
createImageCarouselColumn,
|
|
105
|
+
createLinkMenu,
|
|
106
|
+
createProductCarousel,
|
|
107
|
+
createTemplateCarousel,
|
|
108
|
+
createYesNoConfirm,
|
|
109
|
+
type ImageCarouselColumn,
|
|
110
|
+
type ImageCarouselTemplate,
|
|
111
|
+
type TemplateMessage,
|
|
112
|
+
} from "./src/template-messages.js";
|
|
113
|
+
export type {
|
|
114
|
+
LineChannelData,
|
|
115
|
+
LineConfig,
|
|
116
|
+
LineProbeResult,
|
|
117
|
+
ResolvedLineAccount,
|
|
118
|
+
} from "./src/types.js";
|
|
119
|
+
export { createLineNodeWebhookHandler, readLineWebhookRequestBody } from "./src/webhook-node.js";
|
|
120
|
+
export {
|
|
121
|
+
createLineWebhookMiddleware,
|
|
122
|
+
type LineWebhookOptions,
|
|
123
|
+
startLineWebhook,
|
|
124
|
+
type StartLineWebhookOptions,
|
|
125
|
+
} from "./src/webhook.js";
|
|
126
|
+
export { parseLineWebhookBody } from "./src/webhook-utils.js";
|
|
127
|
+
export { datetimePickerAction, messageAction, postbackAction, uriAction } from "./src/actions.js";
|
|
128
|
+
export type { Action } from "./src/actions.js";
|
|
129
|
+
export {
|
|
130
|
+
createActionCard,
|
|
131
|
+
createAgendaCard,
|
|
132
|
+
createAppleTvRemoteCard,
|
|
133
|
+
createCarousel,
|
|
134
|
+
createDeviceControlCard,
|
|
135
|
+
createEventCard,
|
|
136
|
+
createImageCard,
|
|
137
|
+
createInfoCard,
|
|
138
|
+
createListCard,
|
|
139
|
+
createMediaPlayerCard,
|
|
140
|
+
createNotificationBubble,
|
|
141
|
+
createReceiptCard,
|
|
142
|
+
toFlexMessage,
|
|
143
|
+
} from "./src/flex-templates.js";
|
|
144
|
+
export type {
|
|
145
|
+
CardAction,
|
|
146
|
+
FlexBox,
|
|
147
|
+
FlexBubble,
|
|
148
|
+
FlexButton,
|
|
149
|
+
FlexCarousel,
|
|
150
|
+
FlexComponent,
|
|
151
|
+
FlexContainer,
|
|
152
|
+
FlexImage,
|
|
153
|
+
FlexText,
|
|
154
|
+
ListItem,
|
|
155
|
+
} from "./src/flex-templates.js";
|
|
156
|
+
export {
|
|
157
|
+
cancelDefaultRichMenu,
|
|
158
|
+
createDefaultMenuConfig,
|
|
159
|
+
createGridLayout,
|
|
160
|
+
createRichMenu,
|
|
161
|
+
createRichMenuAlias,
|
|
162
|
+
deleteRichMenu,
|
|
163
|
+
deleteRichMenuAlias,
|
|
164
|
+
getDefaultRichMenuId,
|
|
165
|
+
getRichMenu,
|
|
166
|
+
getRichMenuIdOfUser,
|
|
167
|
+
getRichMenuList,
|
|
168
|
+
linkRichMenuToUser,
|
|
169
|
+
linkRichMenuToUsers,
|
|
170
|
+
setDefaultRichMenu,
|
|
171
|
+
unlinkRichMenuFromUser,
|
|
172
|
+
unlinkRichMenuFromUsers,
|
|
173
|
+
uploadRichMenuImage,
|
|
174
|
+
} from "./src/rich-menu.js";
|
|
175
|
+
export type {
|
|
176
|
+
CreateRichMenuParams,
|
|
177
|
+
RichMenuArea,
|
|
178
|
+
RichMenuAreaRequest,
|
|
179
|
+
RichMenuRequest,
|
|
180
|
+
RichMenuResponse,
|
|
181
|
+
RichMenuSize,
|
|
182
|
+
} from "./src/rich-menu.js";
|
package/setup-api.ts
ADDED
package/setup-entry.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type LineCredentialAccount = {
|
|
2
|
+
channelAccessToken?: string;
|
|
3
|
+
channelSecret?: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export function hasLineCredentials(account: LineCredentialAccount): boolean {
|
|
7
|
+
return Boolean(account.channelAccessToken?.trim() && account.channelSecret?.trim());
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function parseLineAllowFromId(raw: string): string | null {
|
|
11
|
+
const trimmed = raw.trim().replace(/^line:(?:user:)?/i, "");
|
|
12
|
+
if (!/^U[a-f0-9]{32}$/i.test(trimmed)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return trimmed;
|
|
16
|
+
}
|
package/src/accounts.ts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_ACCOUNT_ID,
|
|
3
|
+
normalizeAccountId as normalizeSharedAccountId,
|
|
4
|
+
normalizeOptionalAccountId,
|
|
5
|
+
} from "autobot/plugin-sdk/account-id";
|
|
6
|
+
import type { AutoBotConfig } from "autobot/plugin-sdk/account-resolution";
|
|
7
|
+
import { resolveAccountEntry } from "autobot/plugin-sdk/account-resolution";
|
|
8
|
+
import { tryReadSecretFileSync } from "autobot/plugin-sdk/core";
|
|
9
|
+
import type {
|
|
10
|
+
LineAccountConfig,
|
|
11
|
+
LineConfig,
|
|
12
|
+
LineTokenSource,
|
|
13
|
+
ResolvedLineAccount,
|
|
14
|
+
} from "./types.js";
|
|
15
|
+
|
|
16
|
+
export { DEFAULT_ACCOUNT_ID } from "autobot/plugin-sdk/account-id";
|
|
17
|
+
|
|
18
|
+
function readFileIfExists(filePath: string | undefined): string | undefined {
|
|
19
|
+
return tryReadSecretFileSync(filePath, "LINE credential file", { rejectSymlink: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resolveToken(params: {
|
|
23
|
+
accountId: string;
|
|
24
|
+
baseConfig?: LineConfig;
|
|
25
|
+
accountConfig?: LineAccountConfig;
|
|
26
|
+
}): { token: string; tokenSource: LineTokenSource } {
|
|
27
|
+
const { accountId, baseConfig, accountConfig } = params;
|
|
28
|
+
|
|
29
|
+
if (accountConfig?.channelAccessToken?.trim()) {
|
|
30
|
+
return { token: accountConfig.channelAccessToken.trim(), tokenSource: "config" };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const accountFileToken = readFileIfExists(accountConfig?.tokenFile);
|
|
34
|
+
if (accountFileToken) {
|
|
35
|
+
return { token: accountFileToken, tokenSource: "file" };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
39
|
+
if (baseConfig?.channelAccessToken?.trim()) {
|
|
40
|
+
return { token: baseConfig.channelAccessToken.trim(), tokenSource: "config" };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const baseFileToken = readFileIfExists(baseConfig?.tokenFile);
|
|
44
|
+
if (baseFileToken) {
|
|
45
|
+
return { token: baseFileToken, tokenSource: "file" };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const envToken = process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim();
|
|
49
|
+
if (envToken) {
|
|
50
|
+
return { token: envToken, tokenSource: "env" };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { token: "", tokenSource: "none" };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveSecret(params: {
|
|
58
|
+
accountId: string;
|
|
59
|
+
baseConfig?: LineConfig;
|
|
60
|
+
accountConfig?: LineAccountConfig;
|
|
61
|
+
}): string {
|
|
62
|
+
const { accountId, baseConfig, accountConfig } = params;
|
|
63
|
+
|
|
64
|
+
if (accountConfig?.channelSecret?.trim()) {
|
|
65
|
+
return accountConfig.channelSecret.trim();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const accountFileSecret = readFileIfExists(accountConfig?.secretFile);
|
|
69
|
+
if (accountFileSecret) {
|
|
70
|
+
return accountFileSecret;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
74
|
+
if (baseConfig?.channelSecret?.trim()) {
|
|
75
|
+
return baseConfig.channelSecret.trim();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const baseFileSecret = readFileIfExists(baseConfig?.secretFile);
|
|
79
|
+
if (baseFileSecret) {
|
|
80
|
+
return baseFileSecret;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const envSecret = process.env.LINE_CHANNEL_SECRET?.trim();
|
|
84
|
+
if (envSecret) {
|
|
85
|
+
return envSecret;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function resolveLineAccount(params: {
|
|
93
|
+
cfg: AutoBotConfig;
|
|
94
|
+
accountId?: string;
|
|
95
|
+
}): ResolvedLineAccount {
|
|
96
|
+
const cfg = params.cfg;
|
|
97
|
+
const accountId = normalizeSharedAccountId(params.accountId ?? resolveDefaultLineAccountId(cfg));
|
|
98
|
+
const lineConfig = cfg.channels?.line as LineConfig | undefined;
|
|
99
|
+
const accounts = lineConfig?.accounts;
|
|
100
|
+
const accountConfig =
|
|
101
|
+
accountId !== DEFAULT_ACCOUNT_ID ? resolveAccountEntry(accounts, accountId) : undefined;
|
|
102
|
+
|
|
103
|
+
const { token, tokenSource } = resolveToken({
|
|
104
|
+
accountId,
|
|
105
|
+
baseConfig: lineConfig,
|
|
106
|
+
accountConfig,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const secret = resolveSecret({
|
|
110
|
+
accountId,
|
|
111
|
+
baseConfig: lineConfig,
|
|
112
|
+
accountConfig,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const {
|
|
116
|
+
accounts: _ignoredAccounts,
|
|
117
|
+
defaultAccount: _ignoredDefaultAccount,
|
|
118
|
+
...lineBase
|
|
119
|
+
} = (lineConfig ?? {}) as LineConfig & {
|
|
120
|
+
accounts?: unknown;
|
|
121
|
+
defaultAccount?: unknown;
|
|
122
|
+
};
|
|
123
|
+
const mergedConfig: LineConfig & LineAccountConfig = {
|
|
124
|
+
...lineBase,
|
|
125
|
+
...accountConfig,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const enabled =
|
|
129
|
+
accountConfig?.enabled ??
|
|
130
|
+
(accountId === DEFAULT_ACCOUNT_ID ? (lineConfig?.enabled ?? true) : false);
|
|
131
|
+
|
|
132
|
+
const name =
|
|
133
|
+
accountConfig?.name ?? (accountId === DEFAULT_ACCOUNT_ID ? lineConfig?.name : undefined);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
accountId,
|
|
137
|
+
name,
|
|
138
|
+
enabled,
|
|
139
|
+
channelAccessToken: token,
|
|
140
|
+
channelSecret: secret,
|
|
141
|
+
tokenSource,
|
|
142
|
+
config: mergedConfig,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function listLineAccountIds(cfg: AutoBotConfig): string[] {
|
|
147
|
+
const lineConfig = cfg.channels?.line as LineConfig | undefined;
|
|
148
|
+
const accounts = lineConfig?.accounts;
|
|
149
|
+
const ids = new Set<string>();
|
|
150
|
+
|
|
151
|
+
if (
|
|
152
|
+
lineConfig?.channelAccessToken?.trim() ||
|
|
153
|
+
lineConfig?.tokenFile ||
|
|
154
|
+
process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim()
|
|
155
|
+
) {
|
|
156
|
+
ids.add(DEFAULT_ACCOUNT_ID);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (accounts) {
|
|
160
|
+
for (const id of Object.keys(accounts)) {
|
|
161
|
+
ids.add(id);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return Array.from(ids);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function resolveDefaultLineAccountId(cfg: AutoBotConfig): string {
|
|
169
|
+
const preferred = normalizeOptionalAccountId(
|
|
170
|
+
(cfg.channels?.line as LineConfig | undefined)?.defaultAccount,
|
|
171
|
+
);
|
|
172
|
+
if (
|
|
173
|
+
preferred &&
|
|
174
|
+
listLineAccountIds(cfg).some((accountId) => normalizeSharedAccountId(accountId) === preferred)
|
|
175
|
+
) {
|
|
176
|
+
return preferred;
|
|
177
|
+
}
|
|
178
|
+
const ids = listLineAccountIds(cfg);
|
|
179
|
+
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
|
|
180
|
+
return DEFAULT_ACCOUNT_ID;
|
|
181
|
+
}
|
|
182
|
+
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function normalizeAccountId(accountId: string | undefined): string {
|
|
186
|
+
return normalizeSharedAccountId(accountId);
|
|
187
|
+
}
|
package/src/actions.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { messagingApi } from "@line/bot-sdk";
|
|
2
|
+
|
|
3
|
+
export type Action = messagingApi.Action;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create a message action (sends text when tapped)
|
|
7
|
+
*/
|
|
8
|
+
export function messageAction(label: string, text?: string): Action {
|
|
9
|
+
return {
|
|
10
|
+
type: "message",
|
|
11
|
+
label: label.slice(0, 20),
|
|
12
|
+
text: text ?? label,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a URI action (opens a URL when tapped)
|
|
18
|
+
*/
|
|
19
|
+
export function uriAction(label: string, uri: string): Action {
|
|
20
|
+
return {
|
|
21
|
+
type: "uri",
|
|
22
|
+
label: label.slice(0, 20),
|
|
23
|
+
uri,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create a postback action (sends data to webhook when tapped)
|
|
29
|
+
*/
|
|
30
|
+
export function postbackAction(label: string, data: string, displayText?: string): Action {
|
|
31
|
+
return {
|
|
32
|
+
type: "postback",
|
|
33
|
+
label: label.slice(0, 20),
|
|
34
|
+
data: data.slice(0, 300),
|
|
35
|
+
displayText: displayText?.slice(0, 300),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a datetime picker action
|
|
41
|
+
*/
|
|
42
|
+
export function datetimePickerAction(
|
|
43
|
+
label: string,
|
|
44
|
+
data: string,
|
|
45
|
+
mode: "date" | "time" | "datetime",
|
|
46
|
+
options?: {
|
|
47
|
+
initial?: string;
|
|
48
|
+
max?: string;
|
|
49
|
+
min?: string;
|
|
50
|
+
},
|
|
51
|
+
): Action {
|
|
52
|
+
return {
|
|
53
|
+
type: "datetimepicker",
|
|
54
|
+
label: label.slice(0, 20),
|
|
55
|
+
data: data.slice(0, 300),
|
|
56
|
+
mode,
|
|
57
|
+
initial: options?.initial,
|
|
58
|
+
max: options?.max,
|
|
59
|
+
min: options?.min,
|
|
60
|
+
};
|
|
61
|
+
}
|