Spaces:
Paused
Paused
| import { | |
| ensureRecord, | |
| getRecord, | |
| isRecord, | |
| type LegacyConfigMigration, | |
| mergeMissing, | |
| } from "./legacy.shared.js"; | |
| export const LEGACY_CONFIG_MIGRATIONS_PART_1: LegacyConfigMigration[] = [ | |
| { | |
| id: "bindings.match.provider->bindings.match.channel", | |
| describe: "Move bindings[].match.provider to bindings[].match.channel", | |
| apply: (raw, changes) => { | |
| const bindings = Array.isArray(raw.bindings) ? raw.bindings : null; | |
| if (!bindings) { | |
| return; | |
| } | |
| let touched = false; | |
| for (const entry of bindings) { | |
| if (!isRecord(entry)) { | |
| continue; | |
| } | |
| const match = getRecord(entry.match); | |
| if (!match) { | |
| continue; | |
| } | |
| if (typeof match.channel === "string" && match.channel.trim()) { | |
| continue; | |
| } | |
| const provider = typeof match.provider === "string" ? match.provider.trim() : ""; | |
| if (!provider) { | |
| continue; | |
| } | |
| match.channel = provider; | |
| delete match.provider; | |
| entry.match = match; | |
| touched = true; | |
| } | |
| if (touched) { | |
| raw.bindings = bindings; | |
| changes.push("Moved bindings[].match.provider → bindings[].match.channel."); | |
| } | |
| }, | |
| }, | |
| { | |
| id: "bindings.match.accountID->bindings.match.accountId", | |
| describe: "Move bindings[].match.accountID to bindings[].match.accountId", | |
| apply: (raw, changes) => { | |
| const bindings = Array.isArray(raw.bindings) ? raw.bindings : null; | |
| if (!bindings) { | |
| return; | |
| } | |
| let touched = false; | |
| for (const entry of bindings) { | |
| if (!isRecord(entry)) { | |
| continue; | |
| } | |
| const match = getRecord(entry.match); | |
| if (!match) { | |
| continue; | |
| } | |
| if (match.accountId !== undefined) { | |
| continue; | |
| } | |
| const accountID = | |
| typeof match.accountID === "string" ? match.accountID.trim() : match.accountID; | |
| if (!accountID) { | |
| continue; | |
| } | |
| match.accountId = accountID; | |
| delete match.accountID; | |
| entry.match = match; | |
| touched = true; | |
| } | |
| if (touched) { | |
| raw.bindings = bindings; | |
| changes.push("Moved bindings[].match.accountID → bindings[].match.accountId."); | |
| } | |
| }, | |
| }, | |
| { | |
| id: "session.sendPolicy.rules.match.provider->match.channel", | |
| describe: "Move session.sendPolicy.rules[].match.provider to match.channel", | |
| apply: (raw, changes) => { | |
| const session = getRecord(raw.session); | |
| if (!session) { | |
| return; | |
| } | |
| const sendPolicy = getRecord(session.sendPolicy); | |
| if (!sendPolicy) { | |
| return; | |
| } | |
| const rules = Array.isArray(sendPolicy.rules) ? sendPolicy.rules : null; | |
| if (!rules) { | |
| return; | |
| } | |
| let touched = false; | |
| for (const rule of rules) { | |
| if (!isRecord(rule)) { | |
| continue; | |
| } | |
| const match = getRecord(rule.match); | |
| if (!match) { | |
| continue; | |
| } | |
| if (typeof match.channel === "string" && match.channel.trim()) { | |
| continue; | |
| } | |
| const provider = typeof match.provider === "string" ? match.provider.trim() : ""; | |
| if (!provider) { | |
| continue; | |
| } | |
| match.channel = provider; | |
| delete match.provider; | |
| rule.match = match; | |
| touched = true; | |
| } | |
| if (touched) { | |
| sendPolicy.rules = rules; | |
| session.sendPolicy = sendPolicy; | |
| raw.session = session; | |
| changes.push("Moved session.sendPolicy.rules[].match.provider → match.channel."); | |
| } | |
| }, | |
| }, | |
| { | |
| id: "messages.queue.byProvider->byChannel", | |
| describe: "Move messages.queue.byProvider to messages.queue.byChannel", | |
| apply: (raw, changes) => { | |
| const messages = getRecord(raw.messages); | |
| if (!messages) { | |
| return; | |
| } | |
| const queue = getRecord(messages.queue); | |
| if (!queue) { | |
| return; | |
| } | |
| if (queue.byProvider === undefined) { | |
| return; | |
| } | |
| if (queue.byChannel === undefined) { | |
| queue.byChannel = queue.byProvider; | |
| changes.push("Moved messages.queue.byProvider → messages.queue.byChannel."); | |
| } else { | |
| changes.push("Removed messages.queue.byProvider (messages.queue.byChannel already set)."); | |
| } | |
| delete queue.byProvider; | |
| messages.queue = queue; | |
| raw.messages = messages; | |
| }, | |
| }, | |
| { | |
| id: "providers->channels", | |
| describe: "Move provider config sections to channels.*", | |
| apply: (raw, changes) => { | |
| const legacyKeys = [ | |
| "whatsapp", | |
| "telegram", | |
| "discord", | |
| "slack", | |
| "signal", | |
| "imessage", | |
| "msteams", | |
| ]; | |
| const legacyEntries = legacyKeys.filter((key) => isRecord(raw[key])); | |
| if (legacyEntries.length === 0) { | |
| return; | |
| } | |
| const channels = ensureRecord(raw, "channels"); | |
| for (const key of legacyEntries) { | |
| const legacy = getRecord(raw[key]); | |
| if (!legacy) { | |
| continue; | |
| } | |
| const channelEntry = ensureRecord(channels, key); | |
| const hadEntries = Object.keys(channelEntry).length > 0; | |
| mergeMissing(channelEntry, legacy); | |
| channels[key] = channelEntry; | |
| delete raw[key]; | |
| changes.push( | |
| hadEntries ? `Merged ${key} → channels.${key}.` : `Moved ${key} → channels.${key}.`, | |
| ); | |
| } | |
| raw.channels = channels; | |
| }, | |
| }, | |
| { | |
| id: "routing.allowFrom->channels.whatsapp.allowFrom", | |
| describe: "Move routing.allowFrom to channels.whatsapp.allowFrom", | |
| apply: (raw, changes) => { | |
| const routing = raw.routing; | |
| if (!routing || typeof routing !== "object") { | |
| return; | |
| } | |
| const allowFrom = (routing as Record<string, unknown>).allowFrom; | |
| if (allowFrom === undefined) { | |
| return; | |
| } | |
| const channels = getRecord(raw.channels); | |
| const whatsapp = channels ? getRecord(channels.whatsapp) : null; | |
| if (!whatsapp) { | |
| delete (routing as Record<string, unknown>).allowFrom; | |
| if (Object.keys(routing as Record<string, unknown>).length === 0) { | |
| delete raw.routing; | |
| } | |
| changes.push("Removed routing.allowFrom (channels.whatsapp not configured)."); | |
| return; | |
| } | |
| if (whatsapp.allowFrom === undefined) { | |
| whatsapp.allowFrom = allowFrom; | |
| changes.push("Moved routing.allowFrom → channels.whatsapp.allowFrom."); | |
| } else { | |
| changes.push("Removed routing.allowFrom (channels.whatsapp.allowFrom already set)."); | |
| } | |
| delete (routing as Record<string, unknown>).allowFrom; | |
| if (Object.keys(routing as Record<string, unknown>).length === 0) { | |
| delete raw.routing; | |
| } | |
| channels!.whatsapp = whatsapp; | |
| raw.channels = channels!; | |
| }, | |
| }, | |
| { | |
| id: "routing.groupChat.requireMention->groups.*.requireMention", | |
| describe: "Move routing.groupChat.requireMention to channels.whatsapp/telegram/imessage groups", | |
| apply: (raw, changes) => { | |
| const routing = raw.routing; | |
| if (!routing || typeof routing !== "object") { | |
| return; | |
| } | |
| const groupChat = | |
| (routing as Record<string, unknown>).groupChat && | |
| typeof (routing as Record<string, unknown>).groupChat === "object" | |
| ? ((routing as Record<string, unknown>).groupChat as Record<string, unknown>) | |
| : null; | |
| if (!groupChat) { | |
| return; | |
| } | |
| const requireMention = groupChat.requireMention; | |
| if (requireMention === undefined) { | |
| return; | |
| } | |
| const channels = ensureRecord(raw, "channels"); | |
| const applyTo = ( | |
| key: "whatsapp" | "telegram" | "imessage", | |
| options?: { requireExisting?: boolean }, | |
| ) => { | |
| if (options?.requireExisting && !isRecord(channels[key])) { | |
| return; | |
| } | |
| const section = | |
| channels[key] && typeof channels[key] === "object" | |
| ? (channels[key] as Record<string, unknown>) | |
| : {}; | |
| const groups = | |
| section.groups && typeof section.groups === "object" | |
| ? (section.groups as Record<string, unknown>) | |
| : {}; | |
| const defaultKey = "*"; | |
| const entry = | |
| groups[defaultKey] && typeof groups[defaultKey] === "object" | |
| ? (groups[defaultKey] as Record<string, unknown>) | |
| : {}; | |
| if (entry.requireMention === undefined) { | |
| entry.requireMention = requireMention; | |
| groups[defaultKey] = entry; | |
| section.groups = groups; | |
| channels[key] = section; | |
| changes.push( | |
| `Moved routing.groupChat.requireMention → channels.${key}.groups."*".requireMention.`, | |
| ); | |
| } else { | |
| changes.push( | |
| `Removed routing.groupChat.requireMention (channels.${key}.groups."*" already set).`, | |
| ); | |
| } | |
| }; | |
| applyTo("whatsapp", { requireExisting: true }); | |
| applyTo("telegram"); | |
| applyTo("imessage"); | |
| delete groupChat.requireMention; | |
| if (Object.keys(groupChat).length === 0) { | |
| delete (routing as Record<string, unknown>).groupChat; | |
| } | |
| if (Object.keys(routing as Record<string, unknown>).length === 0) { | |
| delete raw.routing; | |
| } | |
| raw.channels = channels; | |
| }, | |
| }, | |
| { | |
| id: "gateway.token->gateway.auth.token", | |
| describe: "Move gateway.token to gateway.auth.token", | |
| apply: (raw, changes) => { | |
| const gateway = raw.gateway; | |
| if (!gateway || typeof gateway !== "object") { | |
| return; | |
| } | |
| const token = (gateway as Record<string, unknown>).token; | |
| if (token === undefined) { | |
| return; | |
| } | |
| const gatewayObj = gateway as Record<string, unknown>; | |
| const auth = | |
| gatewayObj.auth && typeof gatewayObj.auth === "object" | |
| ? (gatewayObj.auth as Record<string, unknown>) | |
| : {}; | |
| if (auth.token === undefined) { | |
| auth.token = token; | |
| if (!auth.mode) { | |
| auth.mode = "token"; | |
| } | |
| changes.push("Moved gateway.token → gateway.auth.token."); | |
| } else { | |
| changes.push("Removed gateway.token (gateway.auth.token already set)."); | |
| } | |
| delete gatewayObj.token; | |
| if (Object.keys(auth).length > 0) { | |
| gatewayObj.auth = auth; | |
| } | |
| raw.gateway = gatewayObj; | |
| }, | |
| }, | |
| { | |
| id: "telegram.requireMention->channels.telegram.groups.*.requireMention", | |
| describe: "Move telegram.requireMention to channels.telegram.groups.*.requireMention", | |
| apply: (raw, changes) => { | |
| const channels = ensureRecord(raw, "channels"); | |
| const telegram = channels.telegram; | |
| if (!telegram || typeof telegram !== "object") { | |
| return; | |
| } | |
| const requireMention = (telegram as Record<string, unknown>).requireMention; | |
| if (requireMention === undefined) { | |
| return; | |
| } | |
| const groups = | |
| (telegram as Record<string, unknown>).groups && | |
| typeof (telegram as Record<string, unknown>).groups === "object" | |
| ? ((telegram as Record<string, unknown>).groups as Record<string, unknown>) | |
| : {}; | |
| const defaultKey = "*"; | |
| const entry = | |
| groups[defaultKey] && typeof groups[defaultKey] === "object" | |
| ? (groups[defaultKey] as Record<string, unknown>) | |
| : {}; | |
| if (entry.requireMention === undefined) { | |
| entry.requireMention = requireMention; | |
| groups[defaultKey] = entry; | |
| (telegram as Record<string, unknown>).groups = groups; | |
| changes.push( | |
| 'Moved telegram.requireMention → channels.telegram.groups."*".requireMention.', | |
| ); | |
| } else { | |
| changes.push('Removed telegram.requireMention (channels.telegram.groups."*" already set).'); | |
| } | |
| delete (telegram as Record<string, unknown>).requireMention; | |
| channels.telegram = telegram as Record<string, unknown>; | |
| raw.channels = channels; | |
| }, | |
| }, | |
| ]; | |