import { telegramRequest } from './request.ts'
import {
boundedLimit,
inputRecord,
optionalInteger,
secretNameFrom,
} from './validation.ts'
type TelegramChat = {
id?: number
type?: string
title?: string
username?: string
first_name?: string
[key: string]: unknown
}
type TelegramMessage = {
message_id?: number
date?: number
text?: string
caption?: string
chat?: TelegramChat
from?: { id?: number; username?: string; first_name?: string; is_bot?: boolean }
[key: string]: unknown
}
type TelegramUpdate = {
update_id?: number
message?: TelegramMessage
edited_message?: TelegramMessage
channel_post?: TelegramMessage
callback_query?: {
id?: string
data?: string
from?: { id?: number; username?: string }
message?: TelegramMessage
}
[key: string]: unknown
}
function summarizeChat(chat: TelegramChat | undefined) {
if (!chat || typeof chat !== 'object') return null
return {
id: typeof chat.id === 'number' ? chat.id : null,
type: typeof chat.type === 'string' ? chat.type : null,
title: typeof chat.title === 'string' ? chat.title : null,
username: typeof chat.username === 'string' ? chat.username : null,
firstName: typeof chat.first_name === 'string' ? chat.first_name : null,
}
}
function summarizeMessage(message: TelegramMessage | undefined) {
if (!message || typeof message !== 'object') return null
return {
messageId: typeof message.message_id === 'number' ? message.message_id : null,
date: typeof message.date === 'number' ? message.date : null,
text: typeof message.text === 'string' ? message.text : null,
caption: typeof message.caption === 'string' ? message.caption : null,
chat: summarizeChat(message.chat),
fromId: typeof message.from?.id === 'number' ? message.from.id : null,
fromUsername:
typeof message.from?.username === 'string' ? message.from.username : null,
}
}
function summarizeUpdate(update: TelegramUpdate) {
const kind = update.message
? 'message'
: update.edited_message
? 'edited_message'
: update.channel_post
? 'channel_post'
: update.callback_query
? 'callback_query'
: 'other'
return {
updateId: typeof update.update_id === 'number' ? update.update_id : null,
kind,
message: summarizeMessage(
update.message ?? update.edited_message ?? update.channel_post,
),
callbackQueryId:
typeof update.callback_query?.id === 'string'
? update.callback_query.id
: null,
callbackData:
typeof update.callback_query?.data === 'string'
? update.callback_query.data
: null,
}
}
/**
* Read pending Bot API updates (polling). Bots cannot fetch arbitrary chat
* history; they only see messages delivered via getUpdates or a webhook.
* Passing offset acknowledges earlier updates.
*/
export default async function getUpdates(params: Record<string, unknown> = {}) {
const input = inputRecord(params)
const secretName = secretNameFrom(input)
const allowedUpdates = input.allowedUpdates
if (
allowedUpdates !== undefined &&
(!Array.isArray(allowedUpdates) ||
allowedUpdates.some((item) => typeof item !== 'string'))
) {
throw new Error('allowedUpdates must be an array of strings when provided.')
}
const result = await telegramRequest<TelegramUpdate[]>({
method: 'getUpdates',
secretName,
params: {
offset: optionalInteger(input, 'offset'),
limit: boundedLimit(input.limit, 20, 100),
timeout: optionalInteger(input, 'timeout') ?? 0,
allowed_updates: allowedUpdates,
},
})
const updates = Array.isArray(result) ? result.map(summarizeUpdate) : []
const lastUpdateId = updates.reduce((max, update) => {
if (typeof update.updateId === 'number' && update.updateId > max) {
return update.updateId
}
return max
}, 0)
return {
ok: true,
count: updates.length,
nextOffset: lastUpdateId > 0 ? lastUpdateId + 1 : null,
updates,
}
}