After the Fawkes bugfix pass, Mediabot v3 did not need another dramatic phoenix flight.
This time, the work was quieter, more precise, and a little more temporal.
A Time‑Turner was taken from the drawer.
A Pensieve was placed on the desk.
And Mediabot went through a pass focused on time-aware reminders, smarter summaries, safer statistics, and heavier commands that now behave responsibly.
No database migration.
No schema change.
No giant rewrite.
Just careful magic in the places where time, memory, and IRC history meet.
Recurring reminders should respect time.
Summaries should remember what was already summarized.
Statistics should not freeze the bot.
Commands should fail cleanly when used in the wrong place.
That was the spirit of this pass.
Mediabot has grown a lot recently.
It now has richer radio tools, AI summaries, Partyline diagnostics, karma history, reminders, activity stats, YouTube helpers, URL handling, and many small IRC commands that people actually use.
That is good.
But once a bot starts dealing with time — daily reminders, weekly reminders, summaries since last call, “today”, “week”, “yesterday”, streaks, word counts — the risks change.
A small mistake can mean:
So this pass focused on making Mediabot’s sense of time more reliable.
A previous improvement introduced daily reminders.
That was useful, but a defensive edge case needed attention: a cancelled daily reminder must never be able to resurrect itself.
The reinsert logic now checks the reminder state carefully before scheduling the next occurrence.
In other words:
Cancelled means cancelled.
No Inferi reminders crawling out of the database the next morning.
Recurring reminders store metadata in the message field, for example:
[daily:09:00] [at:TS] drink coffee
A snooze operation used to remove only an [at:TS] tag at the very beginning of the message.
That was not enough.
For recurring reminders, the [at:TS] tag may appear after [daily:...] or [weekly:...].
The snooze logic now strips existing [at:TS] tags wherever they are before adding the new one.
Result:
[at:NEW_TS] [daily:09:00] drink coffee
instead of:
[at:NEW_TS] [daily:09:00] [at:OLD_TS] drink coffee
No more raw epoch timestamps sneaking into IRC like cursed runes.
The Time‑Turner work also added weekly reminders:
!remind weekly mon 09:00 gwen weekly meeting
Supported day names include English and French-style shortcuts such as:
mon/lun
tue/mar
wed/mer
thu/jeu
fri/ven
sat/sam
sun/dim
The design stays intentionally simple: no schema migration, no extra table.
The recurrence metadata lives inside the existing message field using a tag like:
[weekly:MON:09:00]
When delivered, the reminder is recreated for the following week.
This keeps the feature useful without turning the database into a Ministry paperwork dungeon.
Recurring reminder tags are useful internally, but they should not leak into user-facing output.
The list/show output now reformats reminder metadata into something readable:
#12 -> gwen [in 2h30m] [daily 09:00]: "bonjour !"
#15 -> teuk [in 3d] [weekly Mon 09:00]: "meeting"
That is much better than exposing raw tags like:
[daily:09:00] [at:TS]
A good spellbook should hide the plumbing.
Some reminder queries filtered by channel name even when the command was used in private message.
That meant private remind list / remind show could fail or behave inconsistently because there was no current channel.
Now the private-message path searches reminders across all relevant channels instead of applying a broken channel filter.
So this works properly:
/msg mediabot remind list
The bot should not forget how to read its own scrolls just because you asked quietly.
Before, a typo could create a reminder for a nick that did not exist.
That reminder could then sit in the database forever, waiting for a ghost.
Now !remind validates the target nick before accepting the reminder.
It checks:
USER_SEEN;USER table.If the nick is unknown, the reminder is refused:
Unknown nick 'maauvaissnick'. The remind was not created.
This is one of those small changes that prevents long-term mess.
A reminder should be a message to a person, not a letter to Azkaban.
!ai summary lastThe AI summary feature gained a very useful memory trick:
!ai summary last
Instead of summarizing the whole day again, Mediabot can summarize only the messages since the previous successful !ai summary on that channel.
Example flow:
!ai summary today
Two hours later:
!ai summary last
Mediabot summarizes only the recent discussion since the last summary.
The timestamp is stored in memory, not persisted across restarts. That is deliberate and lightweight.
It feels like a Pensieve: useful while the memory is fresh, not a permanent archive charm.
There was a subtle callback bug in the Claude summary path.
When a summary response was served from cache, it could ignore the caller’s output callback and send the response in the wrong place.
For example, a summary intended as a NOTICE could come back as a channel PRIVMSG on cache hit.
That is now corrected: cached responses use the same output path as fresh responses.
Same spell, same destination.
!top grows smarter!top can now focus on better time windows and bot filtering:
!top today
!top yesterday
!top week
!top nobots
!top bots
!top 10 week
The bot exclusion list can be configured through:
main.BOT_NICKS = mediabot,rssBotto,...
The bot’s own nick is always considered a bot.
This makes activity stats much more useful on channels where bots talk a lot.
Sometimes you want the living humans.
Sometimes you want to know how much the machines are shouting.
Now Mediabot can tell the difference.
!active understands real calendar periods!active also gained calendar-friendly periods:
!active today
!active yesterday
!active week
That is different from sliding windows like:
!active 3d
!active 12h
Both styles are useful.
Calendar periods answer human questions:
Who talked today?
Who was active yesterday?
Who has been around this week?
The castle clock matters.
!monthstats can compare two nicksMonthly stats gained a comparison mode:
!monthstats teuk vs menz
The output shows two normalized mini-bars per month, making it easy to compare activity over the last 12 months.
This is the kind of feature that looks small but is very handy for long-running IRC communities.
Mediabot has history.
Now it can compare some of it more clearly.
!karmadiff gets time windows and top givers!karmadiff became more useful too:
!karmadiff teuk 6h
!karmadiff teuk 12h
!karmadiff teuk 24h
!karmadiff teuk 7d
It now shows:
Example:
teuk: karma +3 in last 6h (5 vote(s), score: +42) | by: menz(3), gwen(1), poyan(1)
That turns karma from a raw number into a small story.
!calclast respects public/private context!calclast used to always answer by NOTICE, even when called publicly from a channel.
Now it behaves like the other history commands:
This keeps command behavior consistent.
No need for a private owl when the question was asked in the Great Hall.
!wordcount now has a safety limitThis was an important safety fix.
!wordcount could previously read every matching line from CHANNEL_LOG for a nick.
On old active channels, that can mean a very large query and a lot of memory.
Now the command limits itself to the most recent 50,000 messages:
ORDER BY cl.id DESC
LIMIT 50000
If the limit is reached, the output mentions it:
[last 50k msgs]
That is honest, fast, and much safer.
A statistics command should not be able to stun the bot like a badly aimed Stupefy.
!wordcount now refuses private contextSince wordcount is channel-scoped, it should not run from a private query where there is no channel.
The command now fails cleanly:
wordcount must be used from a channel.
That avoids undefined channel behavior and keeps the command semantics clear.
!streak rank query gets a TTL cacheThe rank calculation for !streak can be expensive because it has to group activity over a long period.
Now the rank result is cached for 5 minutes per:
channel + target + current streak
That means repeated calls do not hammer the database unnecessarily.
If the streak changes, the cache key changes naturally.
A neat little charm: simple, effective, and not over-engineered.
External.pm is still huge, but one knot was untangled.
The Spotify handler had several closures inside _handle_spotify, making the function hard to read and hard to test.
Those helpers are now private module functions:
_spotify_is_bad
_spotify_clean
_spotify_duration_from_ms
_spotify_duration_from_iso
_spotify_extract_meta
_spotify_extract_jsonish
The external behavior stays the same, but the code is less tangled.
A small step toward the larger future refactor of External.pm.
External.pm refactorThe plan is clear but intentionally not executed in this pass.
External.pm is still large and wants to become a façade over smaller modules:
Mediabot::External::Claude
Mediabot::External::Spotify
Mediabot::External::URL
Mediabot::External::YouTube
That is the right direction.
But it should be done later, on a dedicated snap, with focused tests.
This pass was about reminders, summaries, activity stats, and safety.
One chamber at a time.
This point matters:
No SQL migration required.
No new table.
No new column.
Weekly reminders, daily reminders, snooze timestamps, and recurrence metadata all continue to use the existing reminder structure.
The improvements are application-level.
That makes the deployment much safer.
m remind mauvaisnick bonjour
m remind weekly mon 09:00 teuk test weekly
m remind daily 09:00 teuk test daily
m remind list
m remindsnooze <id> 30m
m remind cancel <id>
m ai summary today
m ai summary today
m ai summary last
The second summary call should respect the same output target as the first one, even if it hits the cache.
m top today
m top yesterday
m top week
m top nobots
m top bots
m active today
m active yesterday
m active week
m monthstats teuk vs menz
m karmadiff teuk 6h
m wordcount teuk
m streak teuk
m streak teuk
In private message:
wordcount teuk
Expected:
wordcount must be used from a channel.
perl -I. -c Mediabot/UserCommands.pm
perl -I. -c Mediabot/External.pm
perl -I. -c Mediabot/Partyline.pm
perl -I. -c Mediabot/DBCommands.pm
perl -I. -c Mediabot/AdminCommands.pm
perl -I. -c Mediabot/Helpers.pm
perl -I. -c Mediabot/ChannelCommands.pm
perl -I. -c Mediabot/Hailo.pm
perl -I. -c Mediabot/LoginCommands.pm
perl -I. -c mediabot.pl
LC_ALL=C grep -P '\x00' -n Mediabot/*.pm Mediabot/*/*.pm || true
git diff --check
This was a Time‑Turner and Pensieve pass.
Mediabot now handles time and memory more carefully:
!ai summary last can summarize only recent discussion;!top and !active understand better time periods;!wordcount is bounded and channel-only;!streak rank has a TTL cache;You must be logged in to reply.