Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
157 lines
6.4 KiB
TypeScript
157 lines
6.4 KiB
TypeScript
// Line plugin module implements channel behavior.
|
|
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
import { createRestrictSendersChannelSecurity } from "openclaw/plugin-sdk/channel-policy";
|
|
import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
|
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
import { resolveLineAccount } from "./accounts.js";
|
|
import { lineBindingsAdapter } from "./bindings.js";
|
|
import type { ChannelPlugin, ResolvedLineAccount } from "./channel-api.js";
|
|
import { lineChannelPluginCommon } from "./channel-shared.js";
|
|
import { lineGatewayAdapter } from "./gateway.js";
|
|
import { resolveLineGroupRequireMention } from "./group-policy.js";
|
|
import { lineMessageAdapter, lineOutboundAdapter } from "./outbound.js";
|
|
import { hasLineDirectives, parseLineDirectives } from "./reply-payload-transform.js";
|
|
import { getLineRuntime } from "./runtime.js";
|
|
import { lineSetupAdapter } from "./setup-core.js";
|
|
import { lineSetupWizard } from "./setup-surface.js";
|
|
import { lineStatusAdapter } from "./status.js";
|
|
|
|
const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime.js"));
|
|
|
|
const lineSecurityAdapter = createRestrictSendersChannelSecurity<ResolvedLineAccount>({
|
|
channelKey: "line",
|
|
resolveDmPolicy: (account) => account.config.dmPolicy,
|
|
resolveDmAllowFrom: (account) => account.config.allowFrom,
|
|
resolveGroupPolicy: (account) => account.config.groupPolicy,
|
|
surface: "LINE groups",
|
|
openScope: "any member in groups",
|
|
groupPolicyPath: "channels.line.groupPolicy",
|
|
groupAllowFromPath: "channels.line.groupAllowFrom",
|
|
mentionGated: false,
|
|
policyPathSuffix: "dmPolicy",
|
|
approveHint: "openclaw pairing approve line <code>",
|
|
normalizeDmEntry: (raw) => raw.replace(/^line:(?:user:)?/i, ""),
|
|
});
|
|
|
|
export const linePlugin: ChannelPlugin<ResolvedLineAccount> = createChatChannelPlugin({
|
|
base: {
|
|
id: "line",
|
|
...lineChannelPluginCommon,
|
|
setupWizard: lineSetupWizard,
|
|
groups: {
|
|
resolveRequireMention: resolveLineGroupRequireMention,
|
|
},
|
|
messaging: {
|
|
targetPrefixes: ["line"],
|
|
normalizeTarget: (target) => {
|
|
const trimmed = target.trim();
|
|
if (!trimmed) {
|
|
return undefined;
|
|
}
|
|
return trimmed.replace(/^line:(group|room|user):/i, "").replace(/^line:/i, "");
|
|
},
|
|
resolveInboundConversation: lineBindingsAdapter.resolveInboundConversation,
|
|
transformReplyPayload: ({ payload }) => {
|
|
if (!payload.text || !hasLineDirectives(payload.text)) {
|
|
return payload;
|
|
}
|
|
return parseLineDirectives(payload);
|
|
},
|
|
targetResolver: {
|
|
looksLikeId: (id) => {
|
|
const trimmed = id?.trim();
|
|
if (!trimmed) {
|
|
return false;
|
|
}
|
|
return /^[UCR][a-f0-9]{32}$/i.test(trimmed) || /^line:/i.test(trimmed);
|
|
},
|
|
hint: "<userId|groupId|roomId>",
|
|
},
|
|
},
|
|
directory: createEmptyChannelDirectoryAdapter(),
|
|
setup: lineSetupAdapter,
|
|
status: lineStatusAdapter,
|
|
gateway: lineGatewayAdapter,
|
|
message: lineMessageAdapter,
|
|
bindings: lineBindingsAdapter,
|
|
conversationBindings: {
|
|
defaultTopLevelPlacement: "current",
|
|
},
|
|
agentPrompt: {
|
|
messageToolHints: () => [
|
|
"",
|
|
"### LINE Rich Messages",
|
|
"LINE supports rich visual messages. Use these directives in your reply when appropriate:",
|
|
"",
|
|
"**Quick Replies** (bottom button suggestions):",
|
|
" [[quick_replies: Option 1, Option 2, Option 3]]",
|
|
"",
|
|
"**Location** (map pin):",
|
|
" [[location: Place Name | Address | latitude | longitude]]",
|
|
"",
|
|
"**Confirm Dialog** (yes/no prompt):",
|
|
" [[confirm: Question text? | Yes Label | No Label]]",
|
|
"",
|
|
"**Button Menu** (title + text + buttons):",
|
|
" [[buttons: Title | Description | Btn1:action1, Btn2:https://url.com]]",
|
|
"",
|
|
"**Media Player Card** (music status):",
|
|
" [[media_player: Song Title | Artist Name | Source | https://albumart.url | playing]]",
|
|
" - Status: 'playing' or 'paused' (optional)",
|
|
"",
|
|
"**Event Card** (calendar events, meetings):",
|
|
" [[event: Event Title | Date | Time | Location | Description]]",
|
|
" - Time, Location, Description are optional",
|
|
"",
|
|
"**Agenda Card** (multiple events/schedule):",
|
|
" [[agenda: Schedule Title | Event1:9:00 AM, Event2:12:00 PM, Event3:3:00 PM]]",
|
|
"",
|
|
"**Device Control Card** (smart devices, TVs, etc.):",
|
|
" [[device: Device Name | Device Type | Status | Control1:data1, Control2:data2]]",
|
|
"",
|
|
"**Apple TV Remote** (full D-pad + transport):",
|
|
" [[appletv_remote: Apple TV | Playing]]",
|
|
"",
|
|
"**Auto-converted**: Markdown tables become Flex cards, code blocks become styled cards.",
|
|
"",
|
|
"When to use rich messages:",
|
|
"- Use [[quick_replies:...]] when offering 2-4 clear options",
|
|
"- Use [[confirm:...]] for yes/no decisions",
|
|
"- Use [[buttons:...]] for menus with actions/links",
|
|
"- Use [[location:...]] when sharing a place",
|
|
"- Use [[media_player:...]] when showing what's playing",
|
|
"- Use [[event:...]] for calendar event details",
|
|
"- Use [[agenda:...]] for a day's schedule or event list",
|
|
"- Use [[device:...]] for smart device status/controls",
|
|
"- Tables/code in your response auto-convert to visual cards",
|
|
],
|
|
},
|
|
},
|
|
pairing: {
|
|
text: {
|
|
idLabel: "lineUserId",
|
|
message: "OpenClaw: your access has been approved.",
|
|
normalizeAllowEntry: createPairingPrefixStripper(/^line:(?:user:)?/i),
|
|
notify: async ({ cfg, id, message }) => {
|
|
const account = (getLineRuntime().channel.line?.resolveLineAccount ?? resolveLineAccount)({
|
|
cfg,
|
|
});
|
|
if (!account.channelAccessToken) {
|
|
throw new Error("LINE channel access token not configured");
|
|
}
|
|
const pushMessageLine =
|
|
getLineRuntime().channel.line?.pushMessageLine ??
|
|
(await loadLineChannelRuntime()).pushMessageLine;
|
|
await pushMessageLine(id, message, {
|
|
cfg,
|
|
accountId: account.accountId,
|
|
channelAccessToken: account.channelAccessToken,
|
|
});
|
|
},
|
|
},
|
|
},
|
|
security: lineSecurityAdapter,
|
|
outbound: lineOutboundAdapter,
|
|
});
|