Development chronicle — mb620 to mb625 · Real horoscopes, UTF-8-safe IRC, stronger AI summaries and strict recap syntax
This development round started with a horoscope and ended up hardening two of Mediabot’s most useful conversational tools.
The visible additions are easy to describe: a real daily zodiac forecast, better ai summary periods, typo hints, hour windows and a stricter recap.
The more important work happened underneath: one UTF-8 rule for IRC output, one async path for every horoscope alias, one edit-distance helper for typo detection, index-friendly period queries and parsers that stop guessing when the user wrote something ambiguous or invalid.
No database schema change was required.
Mediabot’s historical local horoscope stays intact.
It remains:
The new layer can add a real daily horoscope when a zodiac sign is known.
Examples:
m horoscope lion
m horoscope bélier
m horoscope SaYa
m horoscope
An explicitly supplied sign takes priority over a birthday stored for the target user.
If no sign is written, the existing birthday can still be used to derive it.
Useful French, English and symbolic forms are accepted:
Lion
Leo
Bélier
Belier
Gémeaux
Gemini
Cáncer
Scorpio
♌
They resolve to canonical provider slugs such as:
Bélier -> aries
Gémeaux -> gemini
Lion -> leo
Poissons -> pisces
The normalization accepts both normal decoded Perl strings and raw UTF-8 byte input.
The chosen sign also contributes to the deterministic local seed, so two different explicit signs do not receive identical local flavour text merely because they were requested by the same person on the same day.
The remote horoscope is deliberately best-effort.
local horoscope
|
+---- always available
|
+---- zodiac sign known
|
v
remote daily forecast
|
+---- valid ----> extra line
|
+---- failure --> local result only
Bad HTTP, malformed JSON, missing forecast text or translation trouble does not turn into noise on the channel.
The local horoscope simply remains the answer.
For French and Spanish channels, the optional remote forecast can use the existing Claude layer for a short translation. English channels can use the original forecast directly.
The visible symptom was familiar:
humeur électrique
The real issue was broader than the horoscope.
IRC input is decoded into Perl character strings, but some source modules still contained non-ASCII literals without explicitly declaring their source encoding.
Combining both forms could create mixed byte/character strings and later encode text twice on the wire.
The affected modules now declare:
use utf8;
where required.
The audit also checked the wider codebase: modules without use utf8 no longer emit non-ASCII source literals.
That closes the bug class rather than hiding one occurrence.
The UTF-8 tests reproduce the important boundary:
Perl characters
|
v
UTF-8 encode once
|
v
IRC bytes
|
v
strict UTF-8 decode
They verify that:
é do not come back.horo and horoscope use the same workerOnce the horoscope gained HTTP and optional translation, its short alias exposed an old asymmetry.
The long command was asynchronous:
m horoscope lion
|
v
CommandAsync
while:
m horo lion
still called the implementation directly.
That meant the alias could block the IRC loop even though the full command could not.
Both commands now enter the same canonical worker:
horoscope --\
+--> CommandAsync("horoscope") --> horoscope implementation
horo -------/
They therefore share timeout behaviour, worker locking and output capture.
The help contract is aligned too:
horoscope [nick|signe]
horo [nick|signe]
ai summary gets a real parserThe old syntax had several traps.
A typo such as:
m ai summary todya
could be interpreted as a nickname filter.
Instead of saying the option was wrong, Mediabot could answer that no messages existed for that apparent nickname.
The new parser is strict, order-independent and explicit.
Its syntax is:
ai summary [period] [nick] [options]
Periods:
today
yesterday
week
last
<N>d 1-30 days
<N>h 1-72 hours
Options include:
<N> number of messages without a period
<N>l requested summary lines
public
en | fr | es
lang=fr
nick=<nick>
Order is free:
m ai summary today teuk
m ai summary teuk 7d
m ai summary 6h public
m ai summary week 3l fr
Mediabot can now recognize a likely one-edit typo:
m ai summary todya
and explain that today was probably intended.
It does not silently activate the suggested option.
That distinction matters: an invisible typo should not be replaced by an invisible guess.
Because IRC nicknames can legitimately resemble ordinary words, nick= remains the explicit escape hatch:
nick=todya
today now means the day, not the last few minutesThe previous implementation could use a fixed recent-message limit even when the user asked for a whole period.
On a busy channel, that meant:
today
might really represent only the latest slice of conversation.
Period summaries can now read much more of the requested window.
When the material is too large for one useful AI prompt, Mediabot takes a distributed sample across the period rather than simply chopping off the beginning.
The user is told when this happens.
Example:
1842 messages in that window - summarising a spread sample of 400.
The sample includes early, middle and recent material, with extra weight toward the recent part of the discussion.
The new <N>h syntax is useful for questions such as:
m ai summary 2h
m ai summary 6h public
m ai summary 12h SaYa
It covers the practical space between “last N messages” and calendar periods such as today.
The human-facing period labels are localized too, so the bot can say that the summary covers the last six hours rather than silently using an hour range underneath.
The final pre-commit audit found several values that were accepted and silently clamped.
That is now forbidden.
The real bounds are:
days 1..30
hours 1..72
messages 5..50
lines 1..10
So values such as:
31d
73h
51
11l
are rejected rather than quietly transformed into different requests.
Likewise, a bare message count combined with an explicit period is rejected because the count would otherwise be accepted but meaningless.
Duplicate selectors are also refused instead of being resolved by accident:
today yesterday
fr es
nick=teuk SaYa
A parser should describe one request, not choose between conflicting ones.
recap gets the same disciplineThe sister command had the same class of silent failure.
For example:
m recap 2h ia
could return ordinary statistics because the misspelled ai token was ignored.
The answer still looked plausible, which made the bug worse.
Now:
m recap 2h ia
reports the unknown option and suggests ai.
Similarly:
m recap 30min
can suggest the supported form instead of silently falling back to the default window.
The syntax is order-independent:
recap [window] [ai] [en|fr|es]
so forms such as these work naturally:
m recap ai 2h
m recap 45m ai lang=en
During development, the typo-distance algorithm briefly existed in more than one place.
That was rejected.
The edit-distance helper now lives in Helpers and is reused by both ai summary and recap.
Likewise, recap continues to delegate language extraction to the shared AI-language API instead of introducing a second interpretation of:
en
fr
es
lang=xx
The goal is simple: commands that share a contract should share its implementation too.
The final review also removed an avoidable database performance trap.
Calendar summaries previously used expressions equivalent to applying DATE() to the channel-log timestamp.
That makes it harder for MariaDB to use the existing (id_channel, ts) index efficiently.
today and yesterday now use timestamp ranges:
start <= ts < end
This becomes more important precisely because period summaries can now read far more messages from busy channels.
No schema change was necessary.
The last pre-commit round also tightened details that matter in daily use.
A safety cap is now presented naturally:
1500+ messages
rather than attaching + awkwardly to the period wording.
An invalid language such as:
m recap lang=de
fails even when ai was not requested.
Strict syntax should not become permissive merely because one execution path would have ignored the option.
The current round is kept separate from the previously committed news work.
mb620 real horoscope
mb621 UTF-8 wire safety
mb622 horoscope/horo async parity
mb623 strict ai summary parser
mb624 strict recap parser
mb625 final pre-commit truthfulness
Tests:
803 mb620 horoscope sign/provider
804 mb621 wire encoding
805 mb622 horoscope alias async
806 mb623 ai summary syntax
807 mb624 recap strict syntax
808 mb625 summary/recap final guards
The pre-existing news tests 801 and 802 keep their original identities.
The final targeted checks also retain older AI-summary and recap regression tests so the new parsers do not break the shared language and public-output behaviour established in earlier rounds.
Two different rooms of the castle were improved in this round.
In the astronomy tower, the crystal ball can finally look at today’s sky without forgetting the dependable spell engraved beneath it.
In the library, the talking grimoire has learned something equally important: when a student asks for six hours, it reads six hours; when they misspell a spell, it says so; and when a request contradicts itself, it refuses to pretend it understood.
The result is not merely more functionality.
It is a Mediabot that is more precise about what it heard, what it read and what it is willing to claim.
You must be logged in to reply.