@fragno-dev/telegram-fragment 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/browser/client/react.d.ts +129 -3
  2. package/dist/browser/client/react.d.ts.map +1 -1
  3. package/dist/browser/client/react.js +32 -22
  4. package/dist/browser/client/react.js.map +1 -1
  5. package/dist/browser/client/solid.d.ts +129 -3
  6. package/dist/browser/client/solid.d.ts.map +1 -1
  7. package/dist/browser/client/solid.js +2 -2
  8. package/dist/browser/client/solid.js.map +1 -1
  9. package/dist/browser/client/svelte.d.ts +129 -3
  10. package/dist/browser/client/svelte.d.ts.map +1 -1
  11. package/dist/browser/client/svelte.js +1 -1
  12. package/dist/browser/client/vanilla.d.ts +129 -3
  13. package/dist/browser/client/vanilla.d.ts.map +1 -1
  14. package/dist/browser/client/vanilla.js +1 -1
  15. package/dist/browser/client/vue.d.ts +129 -3
  16. package/dist/browser/client/vue.d.ts.map +1 -1
  17. package/dist/browser/client/vue.js +1 -1
  18. package/dist/browser/{client-Bk-J98pf.d.ts → client-BumUy6cu.d.ts} +87 -83
  19. package/dist/browser/client-BumUy6cu.d.ts.map +1 -0
  20. package/dist/browser/index.d.ts +697 -48
  21. package/dist/browser/index.d.ts.map +1 -1
  22. package/dist/browser/index.js +2 -2
  23. package/dist/browser/index.js.map +1 -1
  24. package/dist/browser/{schema-QDMf15Vn.js → schema-IrJsGm4M.js} +264 -41
  25. package/dist/browser/schema-IrJsGm4M.js.map +1 -0
  26. package/dist/node/definition.d.ts.map +1 -1
  27. package/dist/node/index.d.ts +260 -13
  28. package/dist/node/index.d.ts.map +1 -1
  29. package/dist/node/index.js +2 -2
  30. package/dist/node/index.js.map +1 -1
  31. package/dist/node/routes.d.ts +130 -9
  32. package/dist/node/routes.d.ts.map +1 -1
  33. package/dist/node/routes.js +25 -15
  34. package/dist/node/routes.js.map +1 -1
  35. package/dist/node/services.js +56 -53
  36. package/dist/node/services.js.map +1 -1
  37. package/dist/node/telegram-api.js +80 -16
  38. package/dist/node/telegram-api.js.map +1 -1
  39. package/dist/node/telegram-utils.js +284 -9
  40. package/dist/node/telegram-utils.js.map +1 -1
  41. package/dist/node/types.d.ts +308 -27
  42. package/dist/node/types.d.ts.map +1 -1
  43. package/dist/node/types.js +232 -27
  44. package/dist/node/types.js.map +1 -1
  45. package/dist/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +13 -8
  47. package/dist/browser/client-Bk-J98pf.d.ts.map +0 -1
  48. package/dist/browser/schema-QDMf15Vn.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","names":[],"sources":["../../src/routes.ts"],"sourcesContent":["import { z } from \"zod\";\n\nimport { defineRoutes } from \"@fragno-dev/core\";\nimport { decodeCursor } from \"@fragno-dev/db\";\nimport { ExponentialBackoffRetryPolicy } from \"@fragno-dev/db\";\n\nimport { telegramFragmentDefinition } from \"./definition\";\nimport { telegramSchema } from \"./schema\";\nimport { createTelegramApi } from \"./telegram-api\";\nimport { DEFAULT_COMMAND_SCOPES, parseCommandBindings } from \"./telegram-utils\";\nimport {\n telegramChatTypeSchema,\n telegramCommandBindingsSchema,\n telegramUpdateSchema,\n} from \"./types\";\nimport type { TelegramCommandScope, TelegramHooksMap } from \"./types\";\n\nconst chatSummarySchema = z.object({\n id: z.string(),\n type: telegramChatTypeSchema,\n title: z.string().nullable(),\n username: z.string().nullable(),\n isForum: z.boolean(),\n commandBindings: telegramCommandBindingsSchema.nullable(),\n createdAt: z.date(),\n updatedAt: z.date(),\n});\n\nconst userSummarySchema = z.object({\n id: z.string(),\n username: z.string().nullable(),\n firstName: z.string(),\n lastName: z.string().nullable(),\n isBot: z.boolean(),\n languageCode: z.string().nullable(),\n createdAt: z.date(),\n updatedAt: z.date(),\n});\n\nconst chatMemberSummarySchema = z.object({\n id: z.string(),\n chatId: z.string(),\n userId: z.string(),\n status: z.string(),\n joinedAt: z.date().nullable(),\n leftAt: z.date().nullable(),\n user: userSummarySchema.nullable(),\n createdAt: z.date(),\n updatedAt: z.date(),\n});\n\nconst messageSummarySchema = z.object({\n id: z.string(),\n chatId: z.string(),\n fromUserId: z.string().nullable(),\n senderChatId: z.string().nullable(),\n replyToMessageId: z.string().nullable(),\n messageType: z.enum([\"message\", \"edited_message\", \"channel_post\"]),\n text: z.string().nullable(),\n payload: z.unknown().nullable(),\n sentAt: z.date(),\n editedAt: z.date().nullable(),\n commandName: z.string().nullable(),\n fromUser: userSummarySchema.nullable(),\n});\n\nconst commandBindingInputSchema = z.object({\n chatId: z.string(),\n commandName: z.string(),\n enabled: z.boolean().optional().default(true),\n scopes: z.array(telegramChatTypeSchema).optional(),\n});\n\nconst commandBindingOutputSchema = z.object({\n chatId: z.string(),\n commandName: z.string(),\n enabled: z.boolean(),\n scopes: z.array(telegramChatTypeSchema).optional(),\n});\n\nconst commandOutputSchema = z.object({\n name: z.string(),\n description: z.string().optional(),\n scopes: z.array(telegramChatTypeSchema),\n enabled: z.boolean().optional(),\n effectiveScopes: z.array(telegramChatTypeSchema).optional(),\n binding: z\n .object({\n enabled: z.boolean().optional(),\n scopes: z.array(telegramChatTypeSchema).optional(),\n })\n .nullable()\n .optional(),\n});\n\nconst actionSchema = z.enum([\n \"typing\",\n \"upload_photo\",\n \"record_video\",\n \"upload_video\",\n \"record_voice\",\n \"upload_voice\",\n \"upload_document\",\n \"choose_sticker\",\n \"find_location\",\n \"record_video_note\",\n \"upload_video_note\",\n]);\n\nconst positiveIntSchema = z.coerce.number().int().positive();\n\nconst sendMessageSchema = z.object({\n text: z.string().min(1),\n parseMode: z.enum([\"MarkdownV2\", \"Markdown\", \"HTML\"]).optional(),\n disableWebPagePreview: z.boolean().optional(),\n replyToMessageId: positiveIntSchema.optional(),\n});\n\nconst editMessageSchema = z.object({\n text: z.string().min(1),\n parseMode: z.enum([\"MarkdownV2\", \"Markdown\", \"HTML\"]).optional(),\n disableWebPagePreview: z.boolean().optional(),\n});\n\nconst queuedMessageSchema = z.object({\n ok: z.boolean(),\n queued: z.boolean(),\n});\n\nconst webhookOutputSchema = z.object({\n ok: z.boolean(),\n duplicate: z.boolean().optional(),\n});\n\nconst isDuplicateHookError = (error: unknown): boolean => {\n if (!error || typeof error !== \"object\") {\n return false;\n }\n const code = \"code\" in error ? String(error.code) : \"\";\n const message = \"message\" in error ? String(error.message) : \"\";\n if (code === \"SQLITE_CONSTRAINT\" || code === \"23505\") {\n return message.includes(\"fragno_hooks\") || message.includes(\"hook\");\n }\n return message.toLowerCase().includes(\"duplicate\") || message.toLowerCase().includes(\"unique\");\n};\n\nconst filterUndefined = (payload: Record<string, unknown>) =>\n Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== undefined));\n\nexport const telegramRoutesFactory = defineRoutes(telegramFragmentDefinition).create(\n ({ defineRoute, services, config }) => {\n const api = createTelegramApi(config);\n\n return [\n defineRoute({\n method: \"POST\",\n path: \"/telegram/webhook\",\n inputSchema: telegramUpdateSchema,\n outputSchema: webhookOutputSchema,\n errorCodes: [\"UNAUTHORIZED\"] as const,\n handler: async function ({ headers, input }, { json, error }) {\n const secret = headers.get(\"x-telegram-bot-api-secret-token\");\n if (!secret || secret !== config.webhookSecretToken) {\n return error({ message: \"Unauthorized\", code: \"UNAUTHORIZED\" }, 401);\n }\n\n const update = await input.valid();\n\n try {\n await this.handlerTx()\n .mutate(({ forSchema }) => {\n const uow = forSchema(telegramSchema, {} as TelegramHooksMap);\n uow.triggerHook(\n \"internalProcessUpdate\",\n { update },\n { id: String(update.update_id) },\n );\n })\n .execute();\n\n return json({ ok: true });\n } catch (err) {\n console.error(\"telegram webhook hook insert error\", err);\n if (isDuplicateHookError(err)) {\n return json({ ok: true, duplicate: true });\n }\n throw err;\n }\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/commands/bind\",\n inputSchema: commandBindingInputSchema,\n outputSchema: commandBindingOutputSchema,\n errorCodes: [\"chat_not_found\", \"command_not_found\", \"invalid_scopes\"] as const,\n handler: async function ({ input }, { json, error }) {\n const payload = await input.valid();\n const command = (config.commands ?? {})[payload.commandName];\n\n if (!command) {\n return error({ message: \"Command not found\", code: \"command_not_found\" }, 404);\n }\n\n const allowedScopes = command.scopes ?? DEFAULT_COMMAND_SCOPES;\n if (\n payload.scopes &&\n !payload.scopes.every((scope: TelegramCommandScope) => allowedScopes.includes(scope))\n ) {\n return error({ message: \"Invalid scopes\", code: \"invalid_scopes\" }, 400);\n }\n\n const result = await this.handlerTx({\n retryPolicy: new ExponentialBackoffRetryPolicy({\n maxRetries: 5,\n initialDelayMs: 10,\n maxDelayMs: 250,\n }),\n })\n .withServiceCalls(\n () =>\n [\n services.bindCommand({\n chatId: payload.chatId,\n commandName: payload.commandName,\n enabled: payload.enabled,\n scopes: payload.scopes,\n }),\n ] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n if (!result.ok) {\n return error({ message: \"Chat not found\", code: \"chat_not_found\" }, 404);\n }\n\n return json(result.binding);\n },\n }),\n\n defineRoute({\n method: \"GET\",\n path: \"/commands\",\n queryParameters: [\"chatId\"],\n outputSchema: z.object({\n commands: z.array(commandOutputSchema),\n }),\n errorCodes: [\"chat_not_found\"] as const,\n handler: async function ({ query }, { json, error }) {\n const chatId = query.get(\"chatId\") ?? undefined;\n const commandDefinitions = Object.values(config.commands ?? {});\n\n if (!chatId) {\n return json({\n commands: commandDefinitions.map((command) => ({\n name: command.name,\n description: command.description,\n scopes: command.scopes ?? DEFAULT_COMMAND_SCOPES,\n })),\n });\n }\n\n const chat = await this.handlerTx()\n .withServiceCalls(() => [services.getChat(chatId)] as const)\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n if (!chat) {\n return error({ message: \"Chat not found\", code: \"chat_not_found\" }, 404);\n }\n\n const bindings = parseCommandBindings(chat.commandBindings);\n\n return json({\n commands: commandDefinitions.map((command) => {\n const binding = bindings[command.name] ?? null;\n const enabled = binding ? binding.enabled !== false : true;\n const defaultScopes = command.scopes ?? DEFAULT_COMMAND_SCOPES;\n const effectiveScopes = binding?.scopes ?? defaultScopes;\n\n return {\n name: command.name,\n description: command.description,\n scopes: defaultScopes,\n enabled,\n effectiveScopes,\n binding,\n };\n }),\n });\n },\n }),\n\n defineRoute({\n method: \"GET\",\n path: \"/chats\",\n queryParameters: [\"type\"],\n outputSchema: z.array(chatSummarySchema),\n handler: async function ({ query }, { json }) {\n const parsed = z\n .object({ type: telegramChatTypeSchema.optional() })\n .parse({ type: query.get(\"type\") ?? undefined });\n\n const chats = await this.handlerTx()\n .withServiceCalls(() => [services.listChats(parsed.type)] as const)\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n return json(chats);\n },\n }),\n\n defineRoute({\n method: \"GET\",\n path: \"/chats/:chatId\",\n outputSchema: z.object({\n chat: chatSummarySchema,\n members: z.array(chatMemberSummarySchema),\n }),\n errorCodes: [\"chat_not_found\"] as const,\n handler: async function ({ pathParams }, { json, error }) {\n const result = await this.handlerTx()\n .withServiceCalls(() => [services.getChatWithMembers(pathParams.chatId)] as const)\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n if (!result.chat) {\n return error({ message: \"Chat not found\", code: \"chat_not_found\" }, 404);\n }\n\n return json({\n chat: result.chat,\n members: result.members,\n });\n },\n }),\n\n defineRoute({\n method: \"GET\",\n path: \"/chats/:chatId/messages\",\n queryParameters: [\"cursor\", \"pageSize\", \"order\"],\n outputSchema: z.object({\n messages: z.array(messageSummarySchema),\n cursor: z.string().optional(),\n hasNextPage: z.boolean(),\n }),\n handler: async function ({ pathParams, query }, { json }) {\n const parsed = z\n .object({\n cursor: z.string().optional(),\n pageSize: z.coerce.number().min(1).max(100).catch(50),\n order: z.enum([\"asc\", \"desc\"]).catch(\"desc\"),\n })\n .parse({\n cursor: query.get(\"cursor\") ?? undefined,\n pageSize: query.get(\"pageSize\"),\n order: query.get(\"order\"),\n });\n\n const cursor = parsed.cursor\n ? (() => {\n try {\n return decodeCursor(parsed.cursor!);\n } catch {\n return undefined;\n }\n })()\n : undefined;\n\n const result = await this.handlerTx()\n .withServiceCalls(\n () =>\n [\n services.listMessages({\n chatId: pathParams.chatId,\n pageSize: parsed.pageSize,\n order: parsed.order,\n cursor,\n }),\n ] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n return json({\n messages: result.messages,\n cursor: result.cursor?.encode(),\n hasNextPage: result.hasNextPage,\n });\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/chats/:chatId/actions\",\n inputSchema: z.object({ action: actionSchema }),\n outputSchema: z.object({ ok: z.boolean() }),\n errorCodes: [\"TELEGRAM_API_ERROR\"] as const,\n handler: async function ({ pathParams, input }, { json, error }) {\n const { action } = await input.valid();\n const result = await api.sendChatAction({\n chat_id: pathParams.chatId,\n action,\n });\n\n if (!result.ok) {\n return error(\n { message: result.description ?? \"Telegram API error\", code: \"TELEGRAM_API_ERROR\" },\n 502,\n );\n }\n\n return json({ ok: true });\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/chats/:chatId/send\",\n inputSchema: sendMessageSchema,\n outputSchema: queuedMessageSchema,\n handler: async function ({ pathParams, input }, { json }) {\n const payload = await input.valid();\n\n await this.handlerTx()\n .mutate(({ forSchema }) => {\n const uow = forSchema(telegramSchema, {} as TelegramHooksMap);\n uow.triggerHook(\"internalOutgoingMessage\", {\n action: \"sendMessage\",\n payload: filterUndefined({\n chat_id: pathParams.chatId,\n text: payload.text,\n parse_mode: payload.parseMode,\n disable_web_page_preview: payload.disableWebPagePreview,\n reply_to_message_id: payload.replyToMessageId,\n }),\n });\n })\n .execute();\n\n return json({ ok: true, queued: true });\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/chats/:chatId/messages/:messageId/edit\",\n inputSchema: editMessageSchema,\n outputSchema: queuedMessageSchema,\n errorCodes: [\"INVALID_MESSAGE_ID\"] as const,\n handler: async function ({ pathParams, input }, { json, error }) {\n const payload = await input.valid();\n const messageIdResult = positiveIntSchema.safeParse(pathParams.messageId);\n if (!messageIdResult.success) {\n return error({ message: \"Invalid message id\", code: \"INVALID_MESSAGE_ID\" }, 400);\n }\n const messageId = messageIdResult.data;\n\n await this.handlerTx()\n .mutate(({ forSchema }) => {\n const uow = forSchema(telegramSchema, {} as TelegramHooksMap);\n uow.triggerHook(\"internalOutgoingMessage\", {\n action: \"editMessageText\",\n payload: filterUndefined({\n chat_id: pathParams.chatId,\n message_id: messageId,\n text: payload.text,\n parse_mode: payload.parseMode,\n disable_web_page_preview: payload.disableWebPagePreview,\n }),\n });\n })\n .execute();\n\n return json({ ok: true, queued: true });\n },\n }),\n ];\n },\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","names":[],"sources":["../../src/routes.ts"],"sourcesContent":["import { z } from \"zod\";\n\nimport { defineRoutes } from \"@fragno-dev/core\";\nimport { decodeCursor } from \"@fragno-dev/db\";\nimport { ExponentialBackoffRetryPolicy } from \"@fragno-dev/db\";\n\nimport { telegramFragmentDefinition } from \"./definition\";\nimport { telegramSchema } from \"./schema\";\nimport { createTelegramApi } from \"./telegram-api\";\nimport {\n DEFAULT_COMMAND_SCOPES,\n normalizeTelegramUpdate,\n parseCommandBindings,\n} from \"./telegram-utils\";\nimport {\n telegramAttachmentSchema,\n telegramChatTypeSchema,\n telegramCommandBindingsSchema,\n} from \"./types\";\nimport type { TelegramCommandScope, TelegramHooksMap } from \"./types\";\n\nconst chatSummarySchema = z.object({\n id: z.string(),\n type: telegramChatTypeSchema,\n title: z.string().nullable(),\n username: z.string().nullable(),\n isForum: z.boolean(),\n commandBindings: telegramCommandBindingsSchema.nullable(),\n createdAt: z.date(),\n updatedAt: z.date(),\n});\n\nconst userSummarySchema = z.object({\n id: z.string(),\n username: z.string().nullable(),\n firstName: z.string(),\n lastName: z.string().nullable(),\n isBot: z.boolean(),\n languageCode: z.string().nullable(),\n createdAt: z.date(),\n updatedAt: z.date(),\n});\n\nconst chatMemberSummarySchema = z.object({\n id: z.string(),\n chatId: z.string(),\n userId: z.string(),\n status: z.string(),\n joinedAt: z.date().nullable(),\n leftAt: z.date().nullable(),\n user: userSummarySchema.nullable(),\n createdAt: z.date(),\n updatedAt: z.date(),\n});\n\nconst messageSummarySchema = z.object({\n id: z.string(),\n chatId: z.string(),\n fromUserId: z.string().nullable(),\n senderChatId: z.string().nullable(),\n replyToMessageId: z.string().nullable(),\n messageType: z.enum([\"message\", \"edited_message\", \"channel_post\"]),\n text: z.string().nullable(),\n attachments: z.array(telegramAttachmentSchema),\n payload: z.unknown().nullable(),\n sentAt: z.date(),\n editedAt: z.date().nullable(),\n commandName: z.string().nullable(),\n fromUser: userSummarySchema.nullable(),\n});\n\nconst commandBindingInputSchema = z.object({\n chatId: z.string(),\n commandName: z.string(),\n enabled: z.boolean().optional().default(true),\n scopes: z.array(telegramChatTypeSchema).optional(),\n});\n\nconst commandBindingOutputSchema = z.object({\n chatId: z.string(),\n commandName: z.string(),\n enabled: z.boolean(),\n scopes: z.array(telegramChatTypeSchema).optional(),\n});\n\nconst commandOutputSchema = z.object({\n name: z.string(),\n description: z.string().optional(),\n scopes: z.array(telegramChatTypeSchema),\n enabled: z.boolean().optional(),\n effectiveScopes: z.array(telegramChatTypeSchema).optional(),\n binding: z\n .object({\n enabled: z.boolean().optional(),\n scopes: z.array(telegramChatTypeSchema).optional(),\n })\n .nullable()\n .optional(),\n});\n\nconst actionSchema = z.enum([\n \"typing\",\n \"upload_photo\",\n \"record_video\",\n \"upload_video\",\n \"record_voice\",\n \"upload_voice\",\n \"upload_document\",\n \"choose_sticker\",\n \"find_location\",\n \"record_video_note\",\n \"upload_video_note\",\n]);\n\nconst positiveIntSchema = z.coerce.number().int().positive();\n\nconst sendMessageSchema = z.object({\n text: z.string().min(1),\n parseMode: z.enum([\"MarkdownV2\", \"Markdown\", \"HTML\"]).optional(),\n disableWebPagePreview: z.boolean().optional(),\n replyToMessageId: positiveIntSchema.optional(),\n});\n\nconst editMessageSchema = z.object({\n text: z.string().min(1),\n parseMode: z.enum([\"MarkdownV2\", \"Markdown\", \"HTML\"]).optional(),\n disableWebPagePreview: z.boolean().optional(),\n});\n\nconst queuedMessageSchema = z.object({\n ok: z.boolean(),\n queued: z.boolean(),\n});\n\nconst webhookOutputSchema = z.object({\n ok: z.boolean(),\n duplicate: z.boolean().optional(),\n});\n\nconst isDuplicateHookError = (error: unknown): boolean => {\n if (!error || typeof error !== \"object\") {\n return false;\n }\n const code = \"code\" in error ? String(error.code) : \"\";\n const message = \"message\" in error ? String(error.message) : \"\";\n if (code === \"SQLITE_CONSTRAINT\" || code === \"23505\") {\n return message.includes(\"fragno_hooks\") || message.includes(\"hook\");\n }\n return message.toLowerCase().includes(\"duplicate\") || message.toLowerCase().includes(\"unique\");\n};\n\nconst filterUndefined = (payload: Record<string, unknown>) =>\n Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== undefined));\n\nexport const telegramRoutesFactory = defineRoutes(telegramFragmentDefinition).create(\n ({ defineRoute, services, config }) => {\n const api = createTelegramApi(config);\n\n return [\n defineRoute({\n method: \"POST\",\n path: \"/telegram/webhook\",\n inputSchema: z.unknown(),\n outputSchema: webhookOutputSchema,\n errorCodes: [\"UNAUTHORIZED\", \"INVALID_UPDATE\"] as const,\n handler: async function ({ headers, input }, { json, error }) {\n const secret = headers.get(\"x-telegram-bot-api-secret-token\");\n if (!secret || secret !== config.webhookSecretToken) {\n return error({ message: \"Unauthorized\", code: \"UNAUTHORIZED\" }, 401);\n }\n\n const rawUpdate = await input.valid();\n let update;\n try {\n update = normalizeTelegramUpdate(rawUpdate);\n } catch {\n return error({ message: \"Invalid Telegram update\", code: \"INVALID_UPDATE\" }, 400);\n }\n\n try {\n await this.handlerTx()\n .mutate(({ forSchema }) => {\n const uow = forSchema(telegramSchema, {} as TelegramHooksMap);\n uow.triggerHook(\n \"internalProcessUpdate\",\n { update },\n { id: String(update.updateId) },\n );\n })\n .execute();\n\n return json({ ok: true });\n } catch (err) {\n console.error(\"telegram webhook hook insert error\", err);\n if (isDuplicateHookError(err)) {\n return json({ ok: true, duplicate: true });\n }\n throw err;\n }\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/commands/bind\",\n inputSchema: commandBindingInputSchema,\n outputSchema: commandBindingOutputSchema,\n errorCodes: [\"chat_not_found\", \"command_not_found\", \"invalid_scopes\"] as const,\n handler: async function ({ input }, { json, error }) {\n const payload = await input.valid();\n const command = (config.commands ?? {})[payload.commandName];\n\n if (!command) {\n return error({ message: \"Command not found\", code: \"command_not_found\" }, 404);\n }\n\n const allowedScopes = command.scopes ?? DEFAULT_COMMAND_SCOPES;\n if (\n payload.scopes &&\n !payload.scopes.every((scope: TelegramCommandScope) => allowedScopes.includes(scope))\n ) {\n return error({ message: \"Invalid scopes\", code: \"invalid_scopes\" }, 400);\n }\n\n const result = await this.handlerTx({\n retryPolicy: new ExponentialBackoffRetryPolicy({\n maxRetries: 5,\n initialDelayMs: 10,\n maxDelayMs: 250,\n }),\n })\n .withServiceCalls(\n () =>\n [\n services.bindCommand({\n chatId: payload.chatId,\n commandName: payload.commandName,\n enabled: payload.enabled,\n scopes: payload.scopes,\n }),\n ] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n if (!result.ok) {\n return error({ message: \"Chat not found\", code: \"chat_not_found\" }, 404);\n }\n\n return json(result.binding);\n },\n }),\n\n defineRoute({\n method: \"GET\",\n path: \"/commands\",\n queryParameters: [\"chatId\"],\n outputSchema: z.object({\n commands: z.array(commandOutputSchema),\n }),\n errorCodes: [\"chat_not_found\"] as const,\n handler: async function ({ query }, { json, error }) {\n const chatId = query.get(\"chatId\") ?? undefined;\n const commandDefinitions = Object.values(config.commands ?? {});\n\n if (!chatId) {\n return json({\n commands: commandDefinitions.map((command) => ({\n name: command.name,\n description: command.description,\n scopes: command.scopes ?? DEFAULT_COMMAND_SCOPES,\n })),\n });\n }\n\n const chat = await this.handlerTx()\n .withServiceCalls(() => [services.getChat(chatId)] as const)\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n if (!chat) {\n return error({ message: \"Chat not found\", code: \"chat_not_found\" }, 404);\n }\n\n const bindings = parseCommandBindings(chat.commandBindings);\n\n return json({\n commands: commandDefinitions.map((command) => {\n const binding = bindings[command.name] ?? null;\n const enabled = binding ? binding.enabled !== false : true;\n const defaultScopes = command.scopes ?? DEFAULT_COMMAND_SCOPES;\n const effectiveScopes = binding?.scopes ?? defaultScopes;\n\n return {\n name: command.name,\n description: command.description,\n scopes: defaultScopes,\n enabled,\n effectiveScopes,\n binding,\n };\n }),\n });\n },\n }),\n\n defineRoute({\n method: \"GET\",\n path: \"/chats\",\n queryParameters: [\"type\"],\n outputSchema: z.array(chatSummarySchema),\n handler: async function ({ query }, { json }) {\n const parsed = z\n .object({ type: telegramChatTypeSchema.optional() })\n .parse({ type: query.get(\"type\") ?? undefined });\n\n const chats = await this.handlerTx()\n .withServiceCalls(() => [services.listChats(parsed.type)] as const)\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n return json(chats);\n },\n }),\n\n defineRoute({\n method: \"GET\",\n path: \"/chats/:chatId\",\n outputSchema: z.object({\n chat: chatSummarySchema,\n members: z.array(chatMemberSummarySchema),\n }),\n errorCodes: [\"chat_not_found\"] as const,\n handler: async function ({ pathParams }, { json, error }) {\n const result = await this.handlerTx()\n .withServiceCalls(() => [services.getChatWithMembers(pathParams.chatId)] as const)\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n if (!result.chat) {\n return error({ message: \"Chat not found\", code: \"chat_not_found\" }, 404);\n }\n\n return json({\n chat: result.chat,\n members: result.members,\n });\n },\n }),\n\n defineRoute({\n method: \"GET\",\n path: \"/chats/:chatId/messages\",\n queryParameters: [\"cursor\", \"pageSize\", \"order\"],\n outputSchema: z.object({\n messages: z.array(messageSummarySchema),\n cursor: z.string().optional(),\n hasNextPage: z.boolean(),\n }),\n handler: async function ({ pathParams, query }, { json }) {\n const parsed = z\n .object({\n cursor: z.string().optional(),\n pageSize: z.coerce.number().min(1).max(100).catch(50),\n order: z.enum([\"asc\", \"desc\"]).catch(\"desc\"),\n })\n .parse({\n cursor: query.get(\"cursor\") ?? undefined,\n pageSize: query.get(\"pageSize\"),\n order: query.get(\"order\"),\n });\n\n const cursor = parsed.cursor\n ? (() => {\n try {\n return decodeCursor(parsed.cursor!);\n } catch {\n return undefined;\n }\n })()\n : undefined;\n\n const result = await this.handlerTx()\n .withServiceCalls(\n () =>\n [\n services.listMessages({\n chatId: pathParams.chatId,\n pageSize: parsed.pageSize,\n order: parsed.order,\n cursor,\n }),\n ] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n\n return json({\n messages: result.messages,\n cursor: result.cursor?.encode(),\n hasNextPage: result.hasNextPage,\n });\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/chats/:chatId/actions\",\n inputSchema: z.object({ action: actionSchema }),\n outputSchema: z.object({ ok: z.boolean() }),\n errorCodes: [\"TELEGRAM_API_ERROR\"] as const,\n handler: async function ({ pathParams, input }, { json, error }) {\n const { action } = await input.valid();\n const result = await api.sendChatAction({\n chatId: pathParams.chatId,\n action,\n });\n\n if (!result.ok) {\n return error(\n { message: result.description ?? \"Telegram API error\", code: \"TELEGRAM_API_ERROR\" },\n 502,\n );\n }\n\n return json({ ok: true });\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/chats/:chatId/send\",\n inputSchema: sendMessageSchema,\n outputSchema: queuedMessageSchema,\n handler: async function ({ pathParams, input }, { json }) {\n const payload = await input.valid();\n\n await this.handlerTx()\n .mutate(({ forSchema }) => {\n const uow = forSchema(telegramSchema, {} as TelegramHooksMap);\n uow.triggerHook(\"internalOutgoingMessage\", {\n action: \"sendMessage\",\n payload: filterUndefined({\n chatId: pathParams.chatId,\n text: payload.text,\n parseMode: payload.parseMode,\n disableWebPagePreview: payload.disableWebPagePreview,\n replyToMessageId: payload.replyToMessageId,\n }),\n });\n })\n .execute();\n\n return json({ ok: true, queued: true });\n },\n }),\n\n defineRoute({\n method: \"POST\",\n path: \"/chats/:chatId/messages/:messageId/edit\",\n inputSchema: editMessageSchema,\n outputSchema: queuedMessageSchema,\n errorCodes: [\"INVALID_MESSAGE_ID\"] as const,\n handler: async function ({ pathParams, input }, { json, error }) {\n const payload = await input.valid();\n const messageIdResult = positiveIntSchema.safeParse(pathParams.messageId);\n if (!messageIdResult.success) {\n return error({ message: \"Invalid message id\", code: \"INVALID_MESSAGE_ID\" }, 400);\n }\n const messageId = messageIdResult.data;\n\n await this.handlerTx()\n .mutate(({ forSchema }) => {\n const uow = forSchema(telegramSchema, {} as TelegramHooksMap);\n uow.triggerHook(\"internalOutgoingMessage\", {\n action: \"editMessageText\",\n payload: filterUndefined({\n chatId: pathParams.chatId,\n messageId,\n text: payload.text,\n parseMode: payload.parseMode,\n disableWebPagePreview: payload.disableWebPagePreview,\n }),\n });\n })\n .execute();\n\n return json({ ok: true, queued: true });\n },\n }),\n ];\n },\n);\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { telegramSchema } from "./schema.js";
2
- import { telegramMessageSchema } from "./types.js";
3
- import { DEFAULT_COMMAND_SCOPES, buildChatMemberId, buildMessageId, parseCommand, parseCommandBindings, parseTelegramUpdate } from "./telegram-utils.js";
2
+ import "./types.js";
3
+ import { DEFAULT_COMMAND_SCOPES, buildChatMemberId, buildMessageId, extractTelegramAttachments, parseCommand, parseCommandBindings, parseTelegramUpdate, safeNormalizeTelegramMessage } from "./telegram-utils.js";
4
4
  import "@fragno-dev/db";
5
5
  import "@fragno-dev/db/schema";
6
6
 
@@ -28,10 +28,7 @@ const parseMessageCompositeId = (value) => {
28
28
  messageId: second ?? ""
29
29
  };
30
30
  };
31
- const parseTelegramMessagePayload = (payload) => {
32
- const result = telegramMessageSchema.safeParse(payload);
33
- return result.success ? result.data : null;
34
- };
31
+ const parseTelegramMessagePayload = (payload) => safeNormalizeTelegramMessage(payload);
35
32
  const toUserSummary = (user) => ({
36
33
  id: String(user.id.valueOf()),
37
34
  username: user.username,
@@ -72,8 +69,9 @@ const toMessageSummary = (message) => {
72
69
  const payload = parseTelegramMessagePayload(message.payload);
73
70
  const chatId = payload?.chat ? String(payload.chat.id) : composite.chatId;
74
71
  const fromUserId = payload?.from ? String(payload.from.id) : message.messageAuthor ? String(message.messageAuthor.id.valueOf()) : null;
75
- const senderChatId = payload?.sender_chat ? String(payload.sender_chat.id) : null;
76
- const replyToMessageId = payload?.reply_to_message ? buildMessageId(chatId, payload.reply_to_message.message_id) : null;
72
+ const senderChatId = payload?.senderChat ? String(payload.senderChat.id) : null;
73
+ const replyToMessageId = payload?.replyToMessage ? buildMessageId(chatId, payload.replyToMessage.messageId) : null;
74
+ const attachments = payload ? extractTelegramAttachments(payload) : [];
77
75
  return {
78
76
  id: String(message.id.valueOf()),
79
77
  chatId,
@@ -82,6 +80,7 @@ const toMessageSummary = (message) => {
82
80
  replyToMessageId,
83
81
  messageType: message.messageType,
84
82
  text: message.text,
83
+ attachments,
85
84
  payload: message.payload,
86
85
  sentAt: message.sentAt,
87
86
  editedAt: message.editedAt,
@@ -105,29 +104,30 @@ const createProcessIncomingUpdateOps = (config) => {
105
104
  const parsed = parseTelegramUpdate(update);
106
105
  if (!parsed) return {
107
106
  kind: "ignored",
108
- updateId: update.update_id
107
+ updateId: update.updateId
109
108
  };
110
109
  const { message, type, updateId } = parsed;
111
110
  const chatId = String(message.chat.id);
112
- const messageId = buildMessageId(chatId, message.message_id);
111
+ const messageId = buildMessageId(chatId, message.messageId);
113
112
  const sentAt = /* @__PURE__ */ new Date(message.date * 1e3);
114
- const editedAt = message.edit_date ? /* @__PURE__ */ new Date(message.edit_date * 1e3) : null;
113
+ const editedAt = message.editDate ? /* @__PURE__ */ new Date(message.editDate * 1e3) : null;
115
114
  const fromUser = message.from ?? null;
116
115
  const fromUserId = fromUser ? String(fromUser.id) : null;
117
- const senderChat = message.sender_chat ?? null;
116
+ const senderChat = message.senderChat ?? null;
118
117
  const senderChatId = senderChat ? String(senderChat.id) : null;
119
- const replyToMessageId = message.reply_to_message ? buildMessageId(chatId, message.reply_to_message.message_id) : null;
118
+ const replyToMessageId = message.replyToMessage ? buildMessageId(chatId, message.replyToMessage.messageId) : null;
120
119
  const commandMatch = parseCommand(message, botUsername);
121
120
  const commandName = commandMatch?.name ?? null;
121
+ const attachments = extractTelegramAttachments(message);
122
122
  const membershipMap = /* @__PURE__ */ new Map();
123
- const newMembers = message.new_chat_members ?? [];
123
+ const newMembers = message.newChatMembers ?? [];
124
124
  for (const member of newMembers) membershipMap.set(String(member.id), {
125
125
  status: "member",
126
126
  joinedAt: sentAt,
127
127
  leftAt: null,
128
128
  notify: true
129
129
  });
130
- if (message.left_chat_member) membershipMap.set(String(message.left_chat_member.id), {
130
+ if (message.leftChatMember) membershipMap.set(String(message.leftChatMember.id), {
131
131
  status: "left",
132
132
  joinedAt: null,
133
133
  leftAt: sentAt,
@@ -143,7 +143,7 @@ const createProcessIncomingUpdateOps = (config) => {
143
143
  const userIds = Array.from(new Set([
144
144
  fromUserId,
145
145
  ...newMembers.map((member) => String(member.id)),
146
- message.left_chat_member ? String(message.left_chat_member.id) : null
146
+ message.leftChatMember ? String(message.leftChatMember.id) : null
147
147
  ].filter(Boolean)));
148
148
  const memberIds = Array.from(new Set(Array.from(membershipMap.keys()).map((userId) => buildChatMemberId(chatId, userId))));
149
149
  const chatsForUpsert = [];
@@ -160,7 +160,7 @@ const createProcessIncomingUpdateOps = (config) => {
160
160
  for (const user of [
161
161
  fromUser,
162
162
  ...newMembers,
163
- message.left_chat_member
163
+ message.leftChatMember
164
164
  ]) {
165
165
  if (!user) continue;
166
166
  const id = String(user.id);
@@ -187,7 +187,7 @@ const createProcessIncomingUpdateOps = (config) => {
187
187
  type: chat.type,
188
188
  title: chat.title ?? null,
189
189
  username: chat.username ?? null,
190
- isForum: chat.is_forum ?? false,
190
+ isForum: chat.isForum ?? false,
191
191
  commandBindings: null,
192
192
  createdAt: now,
193
193
  updatedAt: now
@@ -197,7 +197,7 @@ const createProcessIncomingUpdateOps = (config) => {
197
197
  type: chat.type,
198
198
  title: chat.title ?? null,
199
199
  username: chat.username ?? null,
200
- isForum: chat.is_forum ?? false,
200
+ isForum: chat.isForum ?? false,
201
201
  commandBindings: null,
202
202
  createdAt: now,
203
203
  updatedAt: now
@@ -207,7 +207,7 @@ const createProcessIncomingUpdateOps = (config) => {
207
207
  type: chat.type,
208
208
  title: chat.title ?? null,
209
209
  username: chat.username ?? null,
210
- isForum: chat.is_forum ?? false,
210
+ isForum: chat.isForum ?? false,
211
211
  updatedAt: now
212
212
  }).check());
213
213
  chatSummaries.set(id, {
@@ -215,7 +215,7 @@ const createProcessIncomingUpdateOps = (config) => {
215
215
  type: chat.type,
216
216
  title: chat.title ?? null,
217
217
  username: chat.username ?? null,
218
- isForum: chat.is_forum ?? false,
218
+ isForum: chat.isForum ?? false,
219
219
  updatedAt: now
220
220
  });
221
221
  }
@@ -228,39 +228,39 @@ const createProcessIncomingUpdateOps = (config) => {
228
228
  uow.create("user", {
229
229
  id,
230
230
  username: user.username ?? null,
231
- firstName: user.first_name,
232
- lastName: user.last_name ?? null,
233
- isBot: user.is_bot ?? false,
234
- languageCode: user.language_code ?? null,
231
+ firstName: user.firstName,
232
+ lastName: user.lastName ?? null,
233
+ isBot: user.isBot ?? false,
234
+ languageCode: user.languageCode ?? null,
235
235
  createdAt: now,
236
236
  updatedAt: now
237
237
  });
238
238
  userSummaries.set(id, {
239
239
  id,
240
240
  username: user.username ?? null,
241
- firstName: user.first_name,
242
- lastName: user.last_name ?? null,
243
- isBot: user.is_bot ?? false,
244
- languageCode: user.language_code ?? null,
241
+ firstName: user.firstName,
242
+ lastName: user.lastName ?? null,
243
+ isBot: user.isBot ?? false,
244
+ languageCode: user.languageCode ?? null,
245
245
  createdAt: now,
246
246
  updatedAt: now
247
247
  });
248
248
  } else {
249
249
  uow.update("user", existing.id, (b) => b.set({
250
250
  username: user.username ?? null,
251
- firstName: user.first_name,
252
- lastName: user.last_name ?? null,
253
- isBot: user.is_bot ?? false,
254
- languageCode: user.language_code ?? null,
251
+ firstName: user.firstName,
252
+ lastName: user.lastName ?? null,
253
+ isBot: user.isBot ?? false,
254
+ languageCode: user.languageCode ?? null,
255
255
  updatedAt: now
256
256
  }).check());
257
257
  userSummaries.set(id, {
258
258
  ...toUserSummary(existing),
259
259
  username: user.username ?? null,
260
- firstName: user.first_name,
261
- lastName: user.last_name ?? null,
262
- isBot: user.is_bot ?? false,
263
- languageCode: user.language_code ?? null,
260
+ firstName: user.firstName,
261
+ lastName: user.lastName ?? null,
262
+ isBot: user.isBot ?? false,
263
+ languageCode: user.languageCode ?? null,
264
264
  updatedAt: now
265
265
  });
266
266
  }
@@ -334,6 +334,7 @@ const createProcessIncomingUpdateOps = (config) => {
334
334
  replyToMessageId,
335
335
  messageType: type,
336
336
  text: message.text ?? null,
337
+ attachments,
337
338
  payload: message,
338
339
  sentAt,
339
340
  editedAt,
@@ -361,6 +362,7 @@ const createProcessIncomingUpdateOps = (config) => {
361
362
  replyToMessageId: existingMessage.replyToMessageId ? String(existingMessage.replyToMessageId.valueOf()) : null,
362
363
  messageType: type,
363
364
  text: message.text ?? existingMessage.text,
365
+ attachments,
364
366
  payload: message,
365
367
  sentAt: existingMessage.sentAt,
366
368
  editedAt: editedAt ?? existingMessage.editedAt,
@@ -373,7 +375,7 @@ const createProcessIncomingUpdateOps = (config) => {
373
375
  type: message.chat.type,
374
376
  title: message.chat.title ?? null,
375
377
  username: message.chat.username ?? null,
376
- isForum: message.chat.is_forum ?? false,
378
+ isForum: message.chat.isForum ?? false,
377
379
  commandBindings: null,
378
380
  createdAt: now,
379
381
  updatedAt: now
@@ -409,6 +411,7 @@ const createProcessIncomingUpdateOps = (config) => {
409
411
  chatId,
410
412
  fromUserId,
411
413
  text: message.text ?? null,
414
+ attachments,
412
415
  commandName,
413
416
  sentAt: messageSummary.sentAt,
414
417
  editedAt: messageSummary.editedAt
@@ -432,13 +435,13 @@ const createProcessIncomingUpdateOps = (config) => {
432
435
  const createUpsertOutgoingMessageOps = (input) => {
433
436
  const { message, messageType } = input;
434
437
  const chatId = String(message.chat.id);
435
- const senderChatId = message.sender_chat ? String(message.sender_chat.id) : null;
438
+ const senderChatId = message.senderChat ? String(message.senderChat.id) : null;
436
439
  const fromUser = message.from ?? null;
437
440
  const fromUserId = fromUser ? String(fromUser.id) : null;
438
- const messageId = buildMessageId(chatId, message.message_id);
439
- const replyToMessageId = message.reply_to_message ? buildMessageId(chatId, message.reply_to_message.message_id) : null;
441
+ const messageId = buildMessageId(chatId, message.messageId);
442
+ const replyToMessageId = message.replyToMessage ? buildMessageId(chatId, message.replyToMessage.messageId) : null;
440
443
  const sentAt = /* @__PURE__ */ new Date(message.date * 1e3);
441
- const editedAt = message.edit_date ? /* @__PURE__ */ new Date(message.edit_date * 1e3) : null;
444
+ const editedAt = message.editDate ? /* @__PURE__ */ new Date(message.editDate * 1e3) : null;
442
445
  const chatIds = Array.from(new Set([chatId, senderChatId].filter(Boolean)));
443
446
  return {
444
447
  retrieve: (uow) => uow.find("chat", (b) => b.whereIndex("primary", (eb) => eb("id", "in", chatIds.length ? chatIds : [missingId]))).find("user", (b) => b.whereIndex("primary", (eb) => eb("id", "in", fromUserId ? [fromUserId] : [missingId]))).findFirst("message", (b) => b.whereIndex("primary", (eb) => eb("id", "=", messageId))),
@@ -446,7 +449,7 @@ const createUpsertOutgoingMessageOps = (input) => {
446
449
  const now = /* @__PURE__ */ new Date();
447
450
  const chatById = new Map(existingChats.map((chat) => [chat.id.valueOf(), chat]));
448
451
  const userById = new Map(existingUsers.map((user) => [user.id.valueOf(), user]));
449
- const chatsForUpsert = [message.chat, message.sender_chat].filter(Boolean);
452
+ const chatsForUpsert = [message.chat, message.senderChat].filter(Boolean);
450
453
  for (const chat of chatsForUpsert) {
451
454
  const id = String(chat.id);
452
455
  const existing = chatById.get(id);
@@ -455,7 +458,7 @@ const createUpsertOutgoingMessageOps = (input) => {
455
458
  type: chat.type,
456
459
  title: chat.title ?? null,
457
460
  username: chat.username ?? null,
458
- isForum: chat.is_forum ?? false,
461
+ isForum: chat.isForum ?? false,
459
462
  commandBindings: null,
460
463
  createdAt: now,
461
464
  updatedAt: now
@@ -464,7 +467,7 @@ const createUpsertOutgoingMessageOps = (input) => {
464
467
  type: chat.type,
465
468
  title: chat.title ?? null,
466
469
  username: chat.username ?? null,
467
- isForum: chat.is_forum ?? false,
470
+ isForum: chat.isForum ?? false,
468
471
  updatedAt: now
469
472
  }).check());
470
473
  }
@@ -474,19 +477,19 @@ const createUpsertOutgoingMessageOps = (input) => {
474
477
  if (!existing) uow.create("user", {
475
478
  id,
476
479
  username: fromUser.username ?? null,
477
- firstName: fromUser.first_name,
478
- lastName: fromUser.last_name ?? null,
479
- isBot: fromUser.is_bot ?? false,
480
- languageCode: fromUser.language_code ?? null,
480
+ firstName: fromUser.firstName,
481
+ lastName: fromUser.lastName ?? null,
482
+ isBot: fromUser.isBot ?? false,
483
+ languageCode: fromUser.languageCode ?? null,
481
484
  createdAt: now,
482
485
  updatedAt: now
483
486
  });
484
487
  else uow.update("user", existing.id, (b) => b.set({
485
488
  username: fromUser.username ?? null,
486
- firstName: fromUser.first_name,
487
- lastName: fromUser.last_name ?? null,
488
- isBot: fromUser.is_bot ?? false,
489
- languageCode: fromUser.language_code ?? null,
489
+ firstName: fromUser.firstName,
490
+ lastName: fromUser.lastName ?? null,
491
+ isBot: fromUser.isBot ?? false,
492
+ languageCode: fromUser.languageCode ?? null,
490
493
  updatedAt: now
491
494
  }).check());
492
495
  }