export type InputRecord = Record export function inputRecord(value: unknown): InputRecord { if (value === null || typeof value !== 'object' || Array.isArray(value)) { throw new Error('Input must be an object.') } return value as InputRecord } export function requiredString(input: InputRecord, key: string): string { const value = input[key] if (typeof value !== 'string' || value.trim().length === 0) { throw new Error(key + ' is required.') } return value } export function optionalString(input: InputRecord, key: string): string | undefined { const value = input[key] if (value === undefined || value === null || value === '') return undefined if (typeof value !== 'string') throw new Error(key + ' must be a string.') return value } export function optionalBoolean(input: InputRecord, key: string): boolean | undefined { const value = input[key] if (value === undefined || value === null) return undefined if (typeof value !== 'boolean') throw new Error(key + ' must be a boolean.') return value } export function optionalInteger(input: InputRecord, key: string): number | undefined { const value = input[key] if (value === undefined || value === null || value === '') return undefined if (typeof value !== 'number' || !Number.isInteger(value)) { throw new Error(key + ' must be an integer.') } return value } export function boundedLimit(value: unknown, fallback = 100, max = 100): number { if (value === undefined || value === null) return fallback if (!Number.isInteger(value) || Number(value) < 1 || Number(value) > max) { throw new Error('limit must be an integer from 1 through ' + max + '.') } return Number(value) } export function requiredChatId(input: InputRecord): string | number { const value = input.chatId ?? input.chat_id if (typeof value === 'number' && Number.isFinite(value)) return value if (typeof value === 'string' && value.trim().length > 0) return value.trim() throw new Error('chatId is required (numeric Telegram chat id or @username).') } export function optionalRecord(input: InputRecord, key: string): InputRecord | undefined { const value = input[key] if (value === undefined || value === null) return undefined if (typeof value !== 'object' || Array.isArray(value)) { throw new Error(key + ' must be an object when provided.') } return value as InputRecord } const DEFAULT_SECRET_NAME = 'telegramBotToken' /** telegramBotToken, or telegramBotToken- for additional bots. */ export function secretNameFrom(input: InputRecord = {}): string { const value = optionalString(input, 'secretName') ?? DEFAULT_SECRET_NAME if (!/^telegramBotToken(?:-[a-z0-9]+)?$/.test(value)) { throw new Error( 'secretName must be telegramBotToken or telegramBotToken- (lowercase letters and digits).', ) } return value } export function secretPlaceholder(secretName: string): string { return '{{secret:' + secretName + '|scope=user}}' } export function secretSetupUrl(secretName: string): string { return ( 'https://kody.codes/account/secrets/new?name=' + encodeURIComponent(secretName) + '&description=' + encodeURIComponent('Telegram Bot API token from @BotFather') + '&allowedHosts=api.telegram.org&scope=user' ) }