Skip to content
NewOne Slack channel for every deploy and alarm
Nov 2, 2026·4 MIN READ
Librus messages in a family Slack channel

Librus messages in a family Slack channel

A small Python bot checks Librus Synergia every 15 minutes and posts new school messages to our family Slack. The trick that made it readable: a short post in the channel, the full text in the thread.

Teachers write to parents in Librus Synergia. To read a message, you open the app, log in and dig through the inbox. In our house that meant messages were read late, or by one parent only. So I wrote a small bot that forwards every new Librus message to a private #librus channel in our family Slack.

It took one day, from the first commit on 27 October to a working timer on the server. Most of that day went into the Slack layout, not into Librus.

How it works

flowchart LR
    T["systemd timer<br/>every 15 min"] --> B[slack-librus]
    B --> L["Librus Synergia<br/>(librus-apix)"]
    B --> S["state/seen.json"]
    B --> C["#librus channel<br/>title, sender, date"]
    C --> R["Thread<br/>full text"]

The bot is a Python script built with Poetry. It logs in with the unofficial librus-apix library, reads the inbox and compares it with a list of messages it has already seen. New ones go to Slack, and the list is saved again.

The first run only saves the current inbox and posts nothing. Without that, the channel would get a year of old messages in one go.

librus-apix is not an official API. It reads the Librus website, so the bot can break any day Librus changes its pages.

The layout took three tries

The first version posted the whole message through an incoming webhook. It worked, but a long message from a teacher filled the whole screen, and the channel was hard to scan.

The second version used Block Kit: title, sender and date in the channel, with the full text as a reply in the thread. That needs a bot token with chat:write instead of a webhook. The third fix removed the text from the channel post, because it showed up twice.

Now the channel is a list of short posts, and you open a thread only when the title matters to you:

channel post
✉️ Re: Nieobecność, prośba o informację
👤 Nauczyciel · 2026-09-16 10:05
🧵 Treść w wątku

The webhook is still there as a fallback. If the bot token is not set, the whole message goes out as one post.

Small things that cost time

Synergia login, not email

LIBRUS_LOGIN is the Synergia login. The email you use on portal.librus.pl does not work here.

Private channels need an ID

For a private channel, the API wants the channel ID (C...), not #librus. The bot also has to be invited with /invite.

Reading marks as read

When the bot opens a message, Librus marks it as read. Unread counters in the app stop meaning anything.

Fail and retry

If Slack rejects one message, the bot skips it. A bad token or a network error stops the run, and the timer tries again 15 minutes later.

Deploy

The bot runs on a small DigitalOcean droplet that we use for home tools. It has its own user, a systemd timer, and secrets in a .env file with chmod 600. The server has no GitHub access, so make deploy copies the committed code over and installs the dependencies.

Add make dry-run from day one. It prints new messages without posting them, so you can test against the real inbox without spamming the family channel.

What it took

One day of work, and most of it was layout. The Librus part was a library call. What made the bot useful was the choice to keep the channel short and put the long text in a thread. Next time I would start with the thread layout and skip the webhook version.

Share
Mariusz Smenżyk
Written by

Mariusz Smenżyk

Builder for sport. Make Things Live.

Related notes