Skip to content

Add Telegram integration#2

Merged
valuecodes merged 6 commits into
mainfrom
telegram-integration
Mar 29, 2026
Merged

Add Telegram integration#2
valuecodes merged 6 commits into
mainfrom
telegram-integration

Conversation

@valuecodes
Copy link
Copy Markdown
Owner

What

This update introduces Telegram integration along with a structured logging middleware and error handling for improved debugging. The Telegram service allows sending messages and setting webhooks, while the logger middleware captures request details and errors. The changes enhance the application's robustness and maintainability.

  • Implemented Telegram service for API interactions.

  • Added logger middleware for structured logging of requests and errors.

  • Integrated error handling middleware for unhandled errors.

  • Updated environment validation to utilize the logger.

How to test

  • Run pnpm start to start the application and test the Telegram integration.

  • Use the Telegram bot to send messages and verify the logging output.

  • Execute pnpm test to ensure all tests pass.

Security review

  • Secrets / env vars: changed. (New environment variables for Telegram integration.)

  • Auth / session: not changed.

  • Network / API calls: changed. (New API calls to Telegram.)

  • Data handling / PII: changed. (Logging request and error details.)

  • Dependencies: added. (New dependencies for Telegram and logging functionality.)

Justification:

  • Introduced new features that require additional environment variables.

  • Enhanced logging and error handling improves overall security posture.

- Add logger middleware for structured logging
- Introduce error handling middleware for unhandled errors
- Update environment validation to use logger
- Create logger package with tests and configuration
- Add HttpClient for API requests with error handling
- Implement Logger for better debugging and error tracking
- Update error handlers to utilize new logging mechanism
Copilot AI review requested due to automatic review settings March 29, 2026 19:13
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc977b926e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +20 to +23
await next();

// Set the request ID after downstream middleware so cache snapshots never persist a stale ID.
c.header("X-Request-Id", requestId);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve request-finalization in error paths

If any downstream handler throws, execution exits at await next() and the middleware never sets X-Request-Id or emits the request completed log. In those failure scenarios, clients lose the request ID needed for incident correlation and observability misses completion records for errored requests. Move finalization work behind a try/finally so it runs for both success and error responses.

Useful? React with 👍 / 👎.

Comment thread packages/http-client/src/http-client.ts Outdated

const response = await fetch(url, init);

const json: unknown = await response.json();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard JSON parsing for non-JSON error responses

The client parses the body with response.json() before checking response.ok, so upstream 4xx/5xx responses that are empty or non-JSON throw a SyntaxError instead of HttpClientError. This breaks callers that intentionally catch HttpClientError (for example scripts/set-webhook.ts) and prevents graceful error handling/reporting in those failure modes. Parse defensively (or fall back to text) and still raise HttpClientError with status information.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Telegram integration to the operator Cloudflare Worker, including shared workspace packages for structured logging and a small Zod-validated HTTP client, plus middleware for request logging and centralized error handling.

Changes:

  • Added new workspace packages: @repo/logger (structured JSON logs) and @repo/http-client (fetch wrapper with Zod parsing + logging).
  • Implemented Telegram webhook route/controller/service in apps/operator, plus request logging and error-handling middleware.
  • Added a set-webhook script and updated workspace/docs/gitignore to support new env vars and local/prod vars files.

Reviewed changes

Copilot reviewed 37 out of 39 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Expands workspace to include packages/*.
pnpm-lock.yaml Adds deps for new packages, Vitest, tsx, validator, and Zod.
packages/logger/vitest.config.ts Adds Vitest config for logger package.
packages/logger/tsconfig.json Adds package TS config (incl. vitest globals).
packages/logger/src/types.ts Defines logger types (levels, config, entries).
packages/logger/src/logger.ts Implements structured JSON logger.
packages/logger/src/tests/logger.test.ts Adds unit tests for logger behavior and filtering.
packages/logger/package.json Declares @repo/logger workspace package and scripts.
packages/logger/eslint.config.ts Enables shared ESLint config for logger package.
packages/http-client/vitest.config.ts Adds Vitest config for http-client package.
packages/http-client/tsconfig.json Adds package TS config (incl. vitest globals).
packages/http-client/src/types.ts Defines http-client config and request option types.
packages/http-client/src/http-client.ts Implements fetch wrapper + Zod parsing + logging + error type.
packages/http-client/src/tests/http-client.test.ts Adds unit tests for http-client requests/errors/logging.
packages/http-client/package.json Declares @repo/http-client workspace package and deps.
packages/http-client/eslint.config.ts Enables shared ESLint config for http-client package.
docs/plan.md Adds implementation plan/architecture documentation.
apps/operator/src/types/telegram.ts Adds Zod schemas/types for Telegram payloads and API responses.
apps/operator/src/types/env.ts Adds env schema and typed Hono environment bindings/variables.
apps/operator/src/services/telegram.ts Adds Telegram API service built on @repo/http-client.
apps/operator/src/services/telegram.test.ts Adds tests for Telegram service requests and error handling.
apps/operator/src/modules/telegram/routes.ts Adds /webhook/telegram route with Zod validation.
apps/operator/src/modules/telegram/routes.test.ts Adds route-level tests for auth/validation/echo behavior.
apps/operator/src/modules/telegram/controller.ts Implements webhook auth, allowlist, and echo behavior.
apps/operator/src/modules/health/routes.ts Moves health endpoint into module route.
apps/operator/src/modules/health/controller.ts Implements health controller.
apps/operator/src/middleware/logger.ts Adds request-scoped logger + requestId + request logs.
apps/operator/src/middleware/error-handlers.ts Adds centralized error handler + not-found handler.
apps/operator/src/middleware/env.ts Adds runtime env validation middleware with logging.
apps/operator/src/middleware/tests/logger.test.ts Adds tests for logger middleware and onError handler.
apps/operator/src/index.ts Wires middleware, routes, and handlers into the Hono app.
apps/operator/scripts/tsconfig.json Adds TS config for Node scripts.
apps/operator/scripts/set-webhook.ts Adds CLI script to register Telegram webhook using local vars files.
apps/operator/package.json Switches tests to Vitest; adds deps and set-webhook script.
apps/operator/eslint.config.ts Wraps base ESLint config and ignores scripts/**.
apps/operator/README.md Adds operator setup/dev/prod docs and usage instructions.
README.md Expands root README with stack/structure and links to operator docs.
.vscode/settings.json Updates Copilot enablement settings for dotenv/vars files.
.gitignore Ignores .prod.vars, .env, and .wrangler in addition to .dev.vars.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +34

await next();

// Set the request ID after downstream middleware so cache snapshots never persist a stale ID.
c.header("X-Request-Id", requestId);

const duration = Date.now() - start;
const status = c.res.status;

logger.info("request completed", {
requestId,
method,
path,
status,
duration,
});
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If downstream handlers throw (or an HTTPException is raised), await next() will reject and the middleware won’t reach the X-Request-Id header assignment or the "request completed" log. Wrapping await next() in a try/finally (and optionally logging completion in the finally) will ensure the request ID is always returned and timing/status are logged consistently, including error paths.

Suggested change
await next();
// Set the request ID after downstream middleware so cache snapshots never persist a stale ID.
c.header("X-Request-Id", requestId);
const duration = Date.now() - start;
const status = c.res.status;
logger.info("request completed", {
requestId,
method,
path,
status,
duration,
});
let status: number | undefined;
try {
await next();
status = c.res.status;
} finally {
// Set the request ID after downstream middleware so cache snapshots never persist a stale ID.
c.header("X-Request-Id", requestId);
const duration = Date.now() - start;
if (status === undefined) {
status = c.res.status;
}
logger.info("request completed", {
requestId,
method,
path,
status,
duration,
});
}

Copilot uses AI. Check for mistakes.

type WebhookInput = {
in: { json: TelegramUpdate };
out: { json: TelegramUpdate };
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WebhookInput declares out: { json: TelegramUpdate }, but this handler returns { ok: true } and { error: "Unauthorized" } JSON responses. This output typing is misleading and can break type inference for callers; consider removing the out type entirely or updating it to match the actual response shapes.

Suggested change
out: { json: TelegramUpdate };
out: { json: { ok: true } | { error: "Unauthorized" } };

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +34
logger.debug("incoming update", { update });
logger.debug("chat authorization check", {
chatId: message?.chat.id,
allowedChatId: c.env.ALLOWED_CHAT_ID,
});

if (!message?.text) {
return c.json({ ok: true });
}

const chatId = message.chat.id;
logger.info("message received", { text: message.text, chatId });

Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current logging captures the full Telegram update object and the raw message text. Since the logger defaults to debug level, this will be emitted by default and can leak user-provided content/PII into logs. Consider logging only minimal identifiers (e.g., update_id/chatId) and/or redacting text, and making the minimum log level configurable (e.g., via an env var) so debug payload logs aren’t enabled in production by default.

Copilot uses AI. Check for mistakes.
Comment thread packages/http-client/src/http-client.ts Outdated
Comment on lines +62 to +63
const json: unknown = await response.json();

Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response.json() is called unconditionally. This will throw for empty bodies (e.g., 204) or non-JSON responses, which means callers may get a generic parsing error instead of an HttpClientError with status/body context. Consider parsing defensively (e.g., read text first and JSON-parse conditionally/with try-catch) and include the raw response content in the thrown error/logs when JSON parsing fails.

Suggested change
const json: unknown = await response.json();
const text = await response.text();
let json: unknown = null;
if (text !== "") {
try {
json = JSON.parse(text);
} catch (error) {
this.logger.error("failed to parse JSON response", {
method,
path,
status: response.status,
body: text,
error:
error instanceof Error ? error.message : String(error),
});
throw new HttpClientError(response.status, text);
}
}

Copilot uses AI. Check for mistakes.
- Update pnpm action to v5 in GitHub Actions setup
- Improve logging in Telegram webhook handler
- Add tests for non-JSON error responses in HttpClient
- Clarify package workspaces in AGENTS.md
- Enhance README.md with setup instructions and command details
…bhook

- Implement verifyTelegramSecret middleware to check secret token
- Add enforceTelegramBodyLimit middleware to restrict payload size
- Update tests to cover new middleware functionality
@valuecodes valuecodes merged commit 0125989 into main Mar 29, 2026
7 checks passed
@valuecodes valuecodes deleted the telegram-integration branch March 29, 2026 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants