diff --git a/package.json b/package.json index 022aa81..5446926 100644 --- a/package.json +++ b/package.json @@ -27,34 +27,34 @@ }, "dependencies": { "@grammyjs/auto-chat-action": "0.1.1", - "@grammyjs/commands": "1.3.2", - "@grammyjs/hydrate": "1.6.0", + "@grammyjs/commands": "1.0.8", + "@grammyjs/hydrate": "1.4.1", "@grammyjs/i18n": "1.1.2", "@grammyjs/parse-mode": "1.11.1", "@grammyjs/runner": "2.0.3", - "@grammyjs/types": "3.25.0", - "@hono/node-server": "1.19.12", + "@grammyjs/types": "3.20.0", + "@hono/node-server": "1.14.2", "@urql/core": "^6.0.1", "axios": "^1.14.0", "callback-data": "1.1.1", - "grammy": "1.41.1", - "hono": "4.12.9", + "grammy": "1.36.1", + "hono": "4.10.3", "iso-639-1": "3.1.5", - "pino": "10.3.1", - "pino-pretty": "13.1.3", - "tsx": "4.21.0", - "valibot": "1.3.1" + "pino": "9.9.0", + "pino-pretty": "13.0.0", + "tsx": "4.20.4", + "valibot": "0.42.1" }, "devDependencies": { - "@antfu/eslint-config": "8.0.0", + "@antfu/eslint-config": "4.12.0", "@eslint/js": "^10.0.1", - "@types/node": "^25.5.0", - "eslint": "^10.1.0", - "husky": "^9.1.7", - "lint-staged": "^16.4.0", + "@types/node": "^22.15.21", + "eslint": "^9.27.0", + "husky": "^9.1.7", + "lint-staged": "^15.5.1", "prettier": "3.8.1", - "tsc-watch": "^7.2.0", - "typescript": "^6.0.2", + "tsc-watch": "^6.3.1", + "typescript": "^5.9.2", "typescript-eslint": "^8.58.0" }, "lint-staged": { diff --git a/src/bot/context.ts b/src/bot/context.ts index 56ef2ea..16d0f09 100644 --- a/src/bot/context.ts +++ b/src/bot/context.ts @@ -1,27 +1,15 @@ -import type { Config } from "#root/config.js"; -import type { Logger } from "#root/logger.js"; -import type { AutoChatActionFlavor } from "@grammyjs/auto-chat-action"; -import type { HydrateFlavor } from "@grammyjs/hydrate"; -import type { I18nFlavor } from "@grammyjs/i18n"; -import type { ParseModeFlavor } from "@grammyjs/parse-mode"; -import type { Context as DefaultContext, SessionFlavor } from "grammy"; +import type { Context } from "#root/bot/context.js"; +import path from "node:path"; +import process from "node:process"; +import { I18n } from "@grammyjs/i18n"; -// eslint-disable-next-line @typescript-eslint/no-empty-object-type -export interface SessionData { - // field?: string; -} +export const i18n = new I18n({ + defaultLocale: "en", + directory: path.resolve(process.cwd(), "locales"), + useSession: true, + fluentBundleOptions: { + useIsolating: false + } +}); -interface ExtendedContextFlavor { - logger: Logger; - config: Config; -} - -export type Context = ParseModeFlavor< - HydrateFlavor< - DefaultContext & - ExtendedContextFlavor & - SessionFlavor & - I18nFlavor & - AutoChatActionFlavor - > ->; +export const isMultipleLocales = i18n.locales.length > 1; diff --git a/src/bot/features/unhandled.ts b/src/bot/features/unhandled.ts index 896c960..b258f62 100644 --- a/src/bot/features/unhandled.ts +++ b/src/bot/features/unhandled.ts @@ -1,6 +1,6 @@ -import { Composer } from "grammy"; import type { Context } from "#root/bot/context.js"; import { logHandle } from "#root/bot/helpers/logging.js"; +import { Composer } from "grammy"; const composer = new Composer(); diff --git a/src/bot/middlewares/session.ts b/src/bot/middlewares/session.ts index 7039444..c706ee8 100644 --- a/src/bot/middlewares/session.ts +++ b/src/bot/middlewares/session.ts @@ -1,16 +1,34 @@ -import type { Context, SessionData } from "#root/bot/context.js"; -import type { Middleware, SessionOptions } from "grammy"; -import { session as createSession } from "grammy"; +import type { Context } from "#root/bot/context.js"; +import type { Middleware } from "grammy"; +import { performance } from "node:perf_hooks"; +import { getUpdateInfo } from "#root/bot/helpers/logging.js"; -type Options = Pick< - SessionOptions, - "getSessionKey" | "storage" ->; +export function updateLogger(): Middleware { + return async (ctx, next) => { + ctx.api.config.use((previous, method, payload, signal) => { + ctx.logger.debug({ + msg: "Bot API call", + method, + payload + }); -export function session(options: Options): Middleware { - return createSession({ - getSessionKey: options.getSessionKey, - storage: options.storage, - initial: () => ({}) - }); + return previous(method, payload, signal); + }); + + ctx.logger.debug({ + msg: "Update received", + update: getUpdateInfo(ctx) + }); + + const startTime = performance.now(); + try { + await next(); + } finally { + const endTime = performance.now(); + ctx.logger.debug({ + msg: "Update processed", + elapsed: endTime - startTime + }); + } + }; } diff --git a/src/server/middlewares/request-id.ts b/src/server/middlewares/request-id.ts index 83c33fa..ee0546f 100644 --- a/src/server/middlewares/request-id.ts +++ b/src/server/middlewares/request-id.ts @@ -1,10 +1,27 @@ import type { MiddlewareHandler } from "hono"; -import { randomUUID } from "node:crypto"; +import { getPath } from "hono/utils/url"; -export function requestId(): MiddlewareHandler { +export function requestLogger(): MiddlewareHandler { return async (c, next) => { - c.set("requestId", randomUUID()); + const { method } = c.req; + const path = getPath(c.req.raw); + + c.var.logger.debug({ + msg: "Incoming request", + method, + path + }); + const startTime = performance.now(); await next(); + + const endTime = performance.now(); + c.var.logger.debug({ + msg: "Request completed", + method, + path, + status: c.res.status, + elapsed: endTime - startTime + }); }; }