180 lines
4.6 KiB
TypeScript
180 lines
4.6 KiB
TypeScript
import fs from "node:fs";
|
|
import type { MoltbotConfig } from "../config/config.js";
|
|
import type {
|
|
LineConfig,
|
|
LineAccountConfig,
|
|
ResolvedLineAccount,
|
|
LineTokenSource,
|
|
} from "./types.js";
|
|
|
|
export const DEFAULT_ACCOUNT_ID = "default";
|
|
|
|
function readFileIfExists(filePath: string | undefined): string | undefined {
|
|
if (!filePath) return undefined;
|
|
try {
|
|
return fs.readFileSync(filePath, "utf-8").trim();
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function resolveToken(params: {
|
|
accountId: string;
|
|
baseConfig?: LineConfig;
|
|
accountConfig?: LineAccountConfig;
|
|
}): { token: string; tokenSource: LineTokenSource } {
|
|
const { accountId, baseConfig, accountConfig } = params;
|
|
|
|
// Check account-level config first
|
|
if (accountConfig?.channelAccessToken?.trim()) {
|
|
return { token: accountConfig.channelAccessToken.trim(), tokenSource: "config" };
|
|
}
|
|
|
|
// Check account-level token file
|
|
const accountFileToken = readFileIfExists(accountConfig?.tokenFile);
|
|
if (accountFileToken) {
|
|
return { token: accountFileToken, tokenSource: "file" };
|
|
}
|
|
|
|
// For default account, check base config and env
|
|
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
if (baseConfig?.channelAccessToken?.trim()) {
|
|
return { token: baseConfig.channelAccessToken.trim(), tokenSource: "config" };
|
|
}
|
|
|
|
const baseFileToken = readFileIfExists(baseConfig?.tokenFile);
|
|
if (baseFileToken) {
|
|
return { token: baseFileToken, tokenSource: "file" };
|
|
}
|
|
|
|
const envToken = process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim();
|
|
if (envToken) {
|
|
return { token: envToken, tokenSource: "env" };
|
|
}
|
|
}
|
|
|
|
return { token: "", tokenSource: "none" };
|
|
}
|
|
|
|
function resolveSecret(params: {
|
|
accountId: string;
|
|
baseConfig?: LineConfig;
|
|
accountConfig?: LineAccountConfig;
|
|
}): string {
|
|
const { accountId, baseConfig, accountConfig } = params;
|
|
|
|
// Check account-level config first
|
|
if (accountConfig?.channelSecret?.trim()) {
|
|
return accountConfig.channelSecret.trim();
|
|
}
|
|
|
|
// Check account-level secret file
|
|
const accountFileSecret = readFileIfExists(accountConfig?.secretFile);
|
|
if (accountFileSecret) {
|
|
return accountFileSecret;
|
|
}
|
|
|
|
// For default account, check base config and env
|
|
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
if (baseConfig?.channelSecret?.trim()) {
|
|
return baseConfig.channelSecret.trim();
|
|
}
|
|
|
|
const baseFileSecret = readFileIfExists(baseConfig?.secretFile);
|
|
if (baseFileSecret) {
|
|
return baseFileSecret;
|
|
}
|
|
|
|
const envSecret = process.env.LINE_CHANNEL_SECRET?.trim();
|
|
if (envSecret) {
|
|
return envSecret;
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
export function resolveLineAccount(params: {
|
|
cfg: MoltbotConfig;
|
|
accountId?: string;
|
|
}): ResolvedLineAccount {
|
|
const { cfg, accountId = DEFAULT_ACCOUNT_ID } = params;
|
|
const lineConfig = cfg.channels?.line as LineConfig | undefined;
|
|
const accounts = lineConfig?.accounts;
|
|
const accountConfig = accountId !== DEFAULT_ACCOUNT_ID ? accounts?.[accountId] : undefined;
|
|
|
|
const { token, tokenSource } = resolveToken({
|
|
accountId,
|
|
baseConfig: lineConfig,
|
|
accountConfig,
|
|
});
|
|
|
|
const secret = resolveSecret({
|
|
accountId,
|
|
baseConfig: lineConfig,
|
|
accountConfig,
|
|
});
|
|
|
|
const mergedConfig: LineConfig & LineAccountConfig = {
|
|
...lineConfig,
|
|
...accountConfig,
|
|
};
|
|
|
|
const enabled =
|
|
accountConfig?.enabled ??
|
|
(accountId === DEFAULT_ACCOUNT_ID ? (lineConfig?.enabled ?? true) : false);
|
|
|
|
const name =
|
|
accountConfig?.name ?? (accountId === DEFAULT_ACCOUNT_ID ? lineConfig?.name : undefined);
|
|
|
|
return {
|
|
accountId,
|
|
name,
|
|
enabled,
|
|
channelAccessToken: token,
|
|
channelSecret: secret,
|
|
tokenSource,
|
|
config: mergedConfig,
|
|
};
|
|
}
|
|
|
|
export function listLineAccountIds(cfg: MoltbotConfig): string[] {
|
|
const lineConfig = cfg.channels?.line as LineConfig | undefined;
|
|
const accounts = lineConfig?.accounts;
|
|
const ids = new Set<string>();
|
|
|
|
// Add default account if configured at base level
|
|
if (
|
|
lineConfig?.channelAccessToken?.trim() ||
|
|
lineConfig?.tokenFile ||
|
|
process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim()
|
|
) {
|
|
ids.add(DEFAULT_ACCOUNT_ID);
|
|
}
|
|
|
|
// Add named accounts
|
|
if (accounts) {
|
|
for (const id of Object.keys(accounts)) {
|
|
ids.add(id);
|
|
}
|
|
}
|
|
|
|
return Array.from(ids);
|
|
}
|
|
|
|
export function resolveDefaultLineAccountId(cfg: MoltbotConfig): string {
|
|
const ids = listLineAccountIds(cfg);
|
|
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
|
|
return DEFAULT_ACCOUNT_ID;
|
|
}
|
|
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
}
|
|
|
|
export function normalizeAccountId(accountId: string | undefined): string {
|
|
const trimmed = accountId?.trim().toLowerCase();
|
|
if (!trimmed || trimmed === "default") {
|
|
return DEFAULT_ACCOUNT_ID;
|
|
}
|
|
return trimmed;
|
|
}
|