minimost.presence
minimost.presence
Real-time presence tracking, typing indicators, read receipts, message
reactions, and private channel membership — all backed by the shared
presence.db SQLite database.
This module manages all transient shared state in MiniMost. Because MiniMost stores per-user message history in individual SQLite files, a separate shared database is needed for data that all users must see simultaneously: who is typing, who is online, who has read a message, what reactions a message has received, and which users belong to each private channel.
``presence.db`` tables:
Table |
Purpose |
|---|---|
|
One row per user: |
|
One row per (user, channel) pair. Timestamp is updated on each keystroke. Rows older than 5 s are considered stale. |
|
Permanent record of (channel, msg_ts, reader) triples written when a
user calls |
|
One row per (channel, msg_ts, emoji, reactor) combination. Toggled
atomically by |
|
One row per private channel: |
|
One row per (channel_id, username) pair. Records |
The tables are created at module import time by _init_tables().
Module-level attributes
- presence_bpflask.Blueprint
The Flask Blueprint for presence routes. Registered in
minimost.create_app().- PRESENCE_DBstr
Absolute path to the shared
presence.dbSQLite file.- _VALID_STATESset of str
Allowed presence state values:
{"active", "idle", "hidden", "offline"}.
- minimost.presence._init_tables()[source]
Create all required tables in
presence.dbif they do not exist.Called unconditionally at module import time. Uses
CREATE TABLE IF NOT EXISTSthroughout, so repeated calls are safe.Tables created:
presence— tracks each user’s last-seen timestamp and state.typing— records when a user was last observed typing in a channel.read_receipts— permanent log of which users have read which messages.message_reactions— stores each (channel, message, emoji, user) reaction tuple.private_channels— one row per private channel with name, creator, and creation timestamp.private_channel_members— one row per (channel_id, username) pair recording membership and join timestamp.
- Returns:
None
- minimost.presence.typing_start(channel)[source]
Record that the current user is typing in a channel.
Route:
POST /typing/<channel>Does not require the
@login_requireddecorator — if the session is missing, the request is silently dropped (204 No Content) rather than redirecting to the login page. This avoids a redirect loop when the client sends typing notifications for a brief period after a session expiry.The timestamp is written to the
typingtable usingINSERT OR REPLACE, so the row for(user, channel)is updated in place on each call.- Parameters:
channel (str) – The channel name or DM identifier.
- Returns:
Empty body with HTTP 204 No Content.
- Return type:
- minimost.presence.typing_get(channel)[source]
Return the list of users currently typing in a channel.
Route:
GET /typing/<channel>A user is considered to be “currently typing” if their
tsin thetypingtable is within the last 5 seconds. The current user is excluded from the result so they never see their own typing indicator.The client polls this endpoint every second and displays a
"<user> is typing…"banner in the chat area.- Parameters:
channel (str) – The channel name or DM identifier.
- Returns:
JSON array of usernames who are currently typing, e.g.
["alice", "bob"]. Returns[]if the session is missing or no one is typing.- Return type:
flask.Response (application/json)
- minimost.presence.presence()[source]
Update the current user’s presence state.
Route:
POST /presenceAccepts a JSON body with a
statekey. Valid values are defined by_VALID_STATES:"active","idle","hidden","offline".The client sends this request:
Immediately when the page’s
visibilitychangeevent fires ("hidden"when the tab goes to the background,"active"when it returns).After detecting 5 minutes of keyboard/mouse inactivity (
"idle").On a 30-second heartbeat to keep
last_seenfresh.
Silently returns
204if the session is missing or the state is invalid.- Request body (JSON):
state (str): One of
"active","idle","hidden","offline".
- Returns:
Empty body with HTTP 204 No Content.
- Return type:
- minimost.presence.presence_override()[source]
Set or clear the current user’s manual presence override.
Route:
POST /presence/overrideThe account modal lets a user pin how they appear to others regardless of their real activity. Accepts a JSON body with an
overridekey:"active"/"idle"/"offline"— appear Online / Away / Offline.null,""or"auto"— clear the override (Automatic), so the live activity-derived state is shown again.
Silently returns
204if the session is missing or the value is invalid.- Returns:
Empty body with HTTP 204 No Content.
- Return type:
- minimost.presence.presence_override_get()[source]
Return the current user’s manual presence override.
Route:
GET /presence/overrideLets the account modal pre-select the active choice after a reload, since overrides persist server-side in
presence.db.- Returns:
JSON object
{"override": <state-or-null>}where the value is"active","idle","offline"ornull(Automatic).- Return type:
flask.Response (application/json)
- minimost.presence.reset_all_offline() None[source]
Reset every user’s presence to
"offline"and clear manual overrides.Called once at application startup so that stale presence records from a previous server run (e.g. users who were
"active"or"hidden"when the server was stopped) do not mislead other users. Any manual presence overrides are cleared too, so a stale “appear online” override can’t keep a logged-out user looking active across a restart.- Returns:
None
- minimost.presence.update_presence(user: str, state) None[source]
Write a presence record directly to
presence.db.This function is the shared implementation used by both the
/presenceHTTP route and theminimost.auth.logout()view (which sets the user’s state to"offline"before clearing the session).If state is not in
_VALID_STATESthe function returns immediately without writing anything.
- minimost.presence.set_override(user: str, override) None[source]
Set or clear a user’s manual presence override in
presence.db.Used by the
/presence/overrideroute and byminimost.auth.logout()(which clears the override on sign-out so a logged-out user can’t keep an “appear online” override).Unlike
update_presence(), this only touches theoverridecolumn (andlast_seen); the live activity-derivedstateis left intact so that clearing the override falls straight back to the real state.If override is neither
Nonenor a member of_VALID_OVERRIDESthe function returns immediately without writing anything.- Parameters:
user (str) – The username whose override should be updated.
override –
"active","idle","offline"orNoneto clear the override (Automatic).
- Returns:
None
Route Summary
Method |
Path |
Handler |
|---|---|---|
POST |
|
|
GET |
|
|
POST |
|
presence.db Schema
Table |
Description |
|---|---|
|
|
|
|
|
|
|
|