feat: tolerate network failures during IRC connection #8

Merged
alex merged 1 commit from pi/issue7-1784383560700 into master 2026-07-18 14:16:23 +00:00
Contributor

Summary

Fixes #7.

The bot previously crashed whenever the initial IRC connection failed (e.g. ConnectionRefused, a dropped TLS handshake, or a temporarily unreachable server), because client.connect() was awaited directly in main() and any error propagated all the way up, killing the process.

This wraps the connection in an infinite retry loop with a fixed 10-second backoff, so transient network issues no longer crash the bot.

Changes

  • retryForever (src/main.ts): a small, generic helper that calls an operation repeatedly until it succeeds, waiting a fixed delayMs between failed attempts. It never rejects — failures are reported via an injected onError callback and the loop keeps going. The sleeper and error callback are injected dependencies so the behaviour is fully unit-testable without real timers.
  • createClient (src/main.ts): the previous inline client construction + event-listener wiring was extracted into a helper. A fresh client is built on every connection attempt, so a half-open socket from a previous failed attempt can never leak into the next one.
  • main() now resolves its client via retryForever, with each attempt logging the connection target, and failures logging the error plus the attempt number and the upcoming backoff delay.
  • Tests (src/main_test.ts): three new tests covering retryForever — success on first try (no sleep / no error reported), retry-until-success (verifies backoff delay, attempt numbering, and error forwarding), and never-gives-up (verifies it keeps retrying indefinitely).
  • README: a short "Network resilience" section documenting the behaviour.

Verification

  • deno task check
  • deno lint
  • deno task test → 54 passed
  • deno fmt --check
## Summary Fixes #7. The bot previously crashed whenever the initial IRC connection failed (e.g. `ConnectionRefused`, a dropped TLS handshake, or a temporarily unreachable server), because `client.connect()` was awaited directly in `main()` and any error propagated all the way up, killing the process. This wraps the connection in an infinite retry loop with a fixed 10-second backoff, so transient network issues no longer crash the bot. ## Changes - **`retryForever`** (`src/main.ts`): a small, generic helper that calls an operation repeatedly until it succeeds, waiting a fixed `delayMs` between failed attempts. It never rejects — failures are reported via an injected `onError` callback and the loop keeps going. The sleeper and error callback are injected dependencies so the behaviour is fully unit-testable without real timers. - **`createClient`** (`src/main.ts`): the previous inline client construction + event-listener wiring was extracted into a helper. A **fresh** client is built on every connection attempt, so a half-open socket from a previous failed attempt can never leak into the next one. - **`main()`** now resolves its client via `retryForever`, with each attempt logging the connection target, and failures logging the error plus the attempt number and the upcoming backoff delay. - **Tests** (`src/main_test.ts`): three new tests covering `retryForever` — success on first try (no sleep / no error reported), retry-until-success (verifies backoff delay, attempt numbering, and error forwarding), and never-gives-up (verifies it keeps retrying indefinitely). - **README**: a short "Network resilience" section documenting the behaviour. ## Verification - `deno task check` ✅ - `deno lint` ✅ - `deno task test` → 54 passed ✅ - `deno fmt --check` ✅
feat: tolerate network failures during IRC connection
Some checks failed
PR / deno (pull_request) Successful in 19s
PR / fallow (pull_request) Failing after 29s
Build and Push Container Image / build-and-push-1 (push) Successful in 50s
Build and Push Container Image / build-and-push (push) Successful in 0s
Build and Push Container Image / deploy-1 (push) Successful in 4s
Build and Push Container Image / deploy (push) Successful in 0s
36f8d9fdee
Co-authored-by: alex <alex@forge.l3x.in>
alex merged commit 36f8d9fdee into master 2026-07-18 14:16:23 +00:00
alex deleted branch pi/issue7-1784383560700 2026-07-18 14:16:23 +00:00
Owner

/pi fallow checks are failing, fix them in a new pr

/pi fallow checks are failing, fix them in a new pr
Author
Contributor

Now let me experiment with the fallow ignorePatterns to understand why scripts/ isn't being respected:scripts/ (trailing slash) isn't matched, but scripts/** works. Let me verify the minimal fix with the full original config:The tail masked the real exit code. Let me capture fallow's actual exit code:The duplicate ChattoConfig export also fails the check. Let me examine the full picture before fixing — checking config_test.ts's chatto usage and confirming all references:Now I have the full picture. Let me restore the original config file (modified during testing) and apply both fixes:Working tree is clean. Now applying both fixes. Fix 1: correct the scripts/ ignore pattern (trailing-slash form isn't matched by fallow; scripts/** is):Fix 2: resolve the duplicate ChattoConfig export. chatto.ts owns the API connection shape (baseUrl, token, roomId); config.ts will import it and define a richer ChattoSettings that adds the message template. This removes the name collision while keeping the correct dependency direction (config → chatto).Now update main.ts to use the renamed type:Now let me verify all checks pass — fallow, type-check, lint, format, and tests:Fallow now passes, plus check/lint/fmt are green. Now the test suite:All 54 tests pass. Let me review the final diff before opening the PR:Both fixes are clean and minimal, and all checks pass (fallow ✓, check ✓, lint ✓, fmt ✓, 54 tests ✓). Let me create the PR:PR #9 created: #9

The deno task fallow check was failing on master due to two issues, both now fixed:

  1. scripts/chatto-token.ts reported as an unused file.fallowrc.json already had "scripts/" in ignorePatterns, but fallow's glob matcher doesn't match the bare trailing-slash directory form. Changed it to "scripts/**", which fallow honours (matching the author's clear intent that the standalone token helper isn't dead code).

  2. Duplicate export ChattoConfig — it was exported from both src/chatto.ts and src/config.ts. Made chatto.ts the single canonical owner of ChattoConfig (the API connection shape), and config.ts now imports it and defines ChattoSettings extends ChattoConfig adding the message template. This keeps the correct dependency direction (configchatto), leaves chatto.ts/its tests untouched, and stays structurally compatible. main.ts was updated to use ChattoSettings.

All checks pass: fallow ✓ No issues found, check , lint , fmt --check , test → 54 passed .


View action run | Model: zai/glm-5.2 (thinking: max) | Time: 6m 23s | Tokens: 404.7K | Pi SDK v0.80.10 | Action v2.26.0-develop (develop @ c0cad3d)

Now let me experiment with the fallow `ignorePatterns` to understand why `scripts/` isn't being respected:`scripts/` (trailing slash) isn't matched, but `scripts/**` works. Let me verify the minimal fix with the full original config:The `tail` masked the real exit code. Let me capture fallow's actual exit code:The duplicate `ChattoConfig` export also fails the check. Let me examine the full picture before fixing — checking config_test.ts's chatto usage and confirming all references:Now I have the full picture. Let me restore the original config file (modified during testing) and apply both fixes:Working tree is clean. Now applying both fixes. **Fix 1:** correct the `scripts/` ignore pattern (trailing-slash form isn't matched by fallow; `scripts/**` is):**Fix 2:** resolve the duplicate `ChattoConfig` export. `chatto.ts` owns the API connection shape (`baseUrl`, `token`, `roomId`); `config.ts` will import it and define a richer `ChattoSettings` that adds the `message` template. This removes the name collision while keeping the correct dependency direction (config → chatto).Now update `main.ts` to use the renamed type:Now let me verify all checks pass — fallow, type-check, lint, format, and tests:Fallow now passes, plus check/lint/fmt are green. Now the test suite:All 54 tests pass. Let me review the final diff before opening the PR:Both fixes are clean and minimal, and all checks pass (fallow ✓, check ✓, lint ✓, fmt ✓, 54 tests ✓). Let me create the PR:PR #9 created: https://forge.l3x.in/alex/irc-greeter-bot/pulls/9 The `deno task fallow` check was failing on `master` due to two issues, both now fixed: 1. **`scripts/chatto-token.ts` reported as an unused file** — `.fallowrc.json` already had `"scripts/"` in `ignorePatterns`, but fallow's glob matcher doesn't match the bare trailing-slash directory form. Changed it to `"scripts/**"`, which fallow honours (matching the author's clear intent that the standalone token helper isn't dead code). 2. **Duplicate export `ChattoConfig`** — it was exported from both `src/chatto.ts` and `src/config.ts`. Made `chatto.ts` the single canonical owner of `ChattoConfig` (the API connection shape), and `config.ts` now imports it and defines `ChattoSettings extends ChattoConfig` adding the `message` template. This keeps the correct dependency direction (`config` → `chatto`), leaves `chatto.ts`/its tests untouched, and stays structurally compatible. `main.ts` was updated to use `ChattoSettings`. All checks pass: fallow ✓ No issues found, `check` ✅, `lint` ✅, `fmt --check` ✅, `test` → 54 passed ✅. --- [View action run](https://forge.l3x.in/alex/irc-greeter-bot/actions/runs/45) | Model: zai/glm-5.2 (thinking: max) | Time: 6m 23s | Tokens: 404.7K | Pi SDK v0.80.10 | Action v2.26.0-develop (develop @ c0cad3d)
Sign in to join this conversation.
No reviewers
No labels
dependencies
No milestone
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
alex/irc-greeter-bot!8
No description provided.