Forum teuk.org

🔮 Mediabot v3 — The Horoscope Leaves the Crystal Ball

in Mediabot · started by TeuK · 2w ago

TeuK · 2w ago

Development chronicle — mb620 to mb622 · Real daily signs, UTF-8 wire safety and async parity

Mediabot’s horoscope command has moved beyond a purely local deterministic flavour text.

This round adds a real daily horoscope provider by zodiac sign, keeps the existing local horoscope as a safe fallback, fixes the UTF-8 problem that could turn perfectly good French into mojibake on IRC, and makes sure both horoscope and its short alias horo use the same asynchronous execution path.

No database schema change was required.


🔮 mb620 — A real horoscope joins the local one

The historical Mediabot horoscope remains in place:

  • deterministic for a given user and day;
  • generated locally;
  • independent from any external service;
  • available even when every API is down.

The new layer enriches it with a real daily horoscope when Mediabot knows the zodiac sign.

Examples:

m horoscope lion
m horoscope bélier
m horoscope SaYa
m horoscope

A sign supplied explicitly wins over the birthday stored for the target user.

If no sign is supplied, Mediabot can still derive it from the existing USER.birthday value.


♌ Human sign names are accepted

The horoscope module understands zodiac signs in several useful forms:

Lion
Leo
Bélier
Belier
Gémeaux
Gemini
Cáncer
Scorpio
♌

Internally everything is normalized to the canonical English slug expected by the provider:

Bélier   ─► aries
Gémeaux  ─► gemini
Lion     ─► leo
Poissons ─► pisces

The normalization accepts both already-decoded IRC strings and raw UTF-8 byte input.


🛡️ The remote horoscope is strictly best-effort

External data never replaces Mediabot’s local horoscope.

The flow is:

local horoscope
      │
      ├── always available
      │
      └── known zodiac sign
              │
              ▼
        remote daily horoscope
              │
              ├── valid response ─► extra line
              │
              └── failure ───────► local result only

Network failure, bad JSON, missing text or an unusable provider response does not produce an ugly error in the channel.

The user simply receives the local horoscope.


🌍 Channel language still wins

The remote provider speaks English.

On an English channel, its response can be used directly.

On French or Spanish channels, Mediabot asks the existing Claude layer for a short translation into the channel language.

If translation is unavailable, Mediabot prefers silence for that optional line rather than mixing an English sentence into an otherwise French or Spanish horoscope.


🧙 The requested sign participates in the deterministic seed

The local horoscope already uses a private deterministic PRNG rather than modifying Perl’s global random generator.

With explicit signs, the sign now also participates in the local seed.

That means:

m horoscope lion
m horoscope vierge

do not receive the exact same local flavour merely because they were requested by the same user on the same date.

The historical no-sign behaviour remains stable.


✨ mb621 — Fix the UTF-8 class, not one broken sentence

The visible bug was classic mojibake:

humeur électrique

The deeper cause was not the horoscope API.

Incoming IRC text is decoded into Perl character strings. Some modules, however, still contained non-ASCII source literals without:

use utf8;

Interpolating decoded IRC text into those byte literals could turn the whole outgoing line into mixed character/byte data and cause a second UTF-8 encoding step.

The repair was applied to the class of affected modules, not only to one horoscope string.

Modules that can emit non-ASCII literals now explicitly treat their source as UTF-8 characters.


🧪 The wire itself is tested

The UTF-8 regression test does more than search source code.

It recreates Mediabot’s real outbound rule:

Perl character string
       │
       ▼
UTF-8 encode once
       │
       ▼
IRC bytes
       │
       ▼
strict UTF-8 decode in the test

The guard checks that:

  • emitted bytes are valid UTF-8;
  • French accents survive;
  • zodiac glyphs survive;
  • decoded IRC nicknames do not trigger double encoding;
  • raw-byte internal callers still behave correctly;
  • signatures such as é do not reappear.

The horoscope round is now numbered mb620, with test 803.

The wire-encoding round is mb621, with test 804.

This avoids colliding with the already published news rounds mb616 and mb617.


⚡ mb622 — horo cannot block the IRC loop

The final review found one runtime asymmetry.

The long command had correctly moved to the async worker:

m horoscope lion
       │
       ▼
CommandAsync
       │
       ▼
HTTP / optional translation

But the historical short alias still called the implementation directly:

m horo lion
       │
       ▼
synchronous call

Since the command can now perform network requests, the alias could block the IRC event loop while the long form did not.

That path is now closed.

Both forms use the same canonical worker label:

horoscope ─┐
           ├──► CommandAsync("horoscope") ─► mbHoroscope_ctx
horo ──────┘

They therefore share the same worker behaviour, channel lock, timeout handling and output facade.

The help text also now documents the same contract for both commands:

horoscope [nick|signe]
horo [nick|signe]

A dedicated test 805 locks this parity.


🧪 Focused validation

The final pre-commit validation targets the affected contracts:

803_mb620_horoscope_sign_api
804_mb621_wire_encoding
805_mb622_horoscope_alias_async

Test 805 passes independently with all six async/help assertions green.

The complete project suite does not need to be repeated merely for the renumbering and alias-parity cleanup; the server-side targeted tests are sufficient for this final pass.


🏰 Final parchment

The crystal ball now has two layers.

One is carved into the castle itself and never stops working.

The other looks outside for today’s stars when the sky is clear.

Whether a student asks for Lion, Leo or , whether they call the spell horoscope or simply horo, and whether the Common Room speaks English or French, the same guarded path now leads to the answer.

And the accents finally arrive exactly once.

You must be logged in to reply.