minimost.chat
minimost.chat
Core messaging routes: sending, receiving, editing, deleting, reacting, and serving uploaded files.
This is the largest module in MiniMost and contains all business logic for the chat interface, including:
Channel management helpers — resolving participants, validating access for public channels, DMs, and private channels.
Message CRUD — sending, fetching (with polling support), editing, and soft-deleting messages across every recipient’s database simultaneously.
Reactions — toggle emoji reactions stored atomically in the shared
presence.db.Read receipts — mark messages as read and query who has read them.
File serving — authenticated delivery of image attachments.
Search — full-text
LIKEsearch across a user’s message history.Link previews — delegate to
minimost.preview.Private channels — create invite-only channels, manage membership, and rename channels. Private channel state (membership, channel metadata) is stored in the shared
presence.dbso every member sees the same view. Private channels are identified throughout the app as"private:<id>"where<id>is the auto-increment primary key from theprivate_channelstable.
Module-level attributes
- chat_bpflask.Blueprint
The Flask Blueprint that groups all chat-related routes. Registered in
minimost.create_app().- ALLOWED_EXTENSIONSset
File extensions accepted for image uploads:
{".jpg", ".jpeg", ".png", ".gif", ".webp"}.- UPLOAD_DIRpathlib.Path
Absolute path to the
uploads/directory where image attachments are stored. Created at import time if it does not exist.- CHANNELSlist of str
Public channel names loaded from
channels.jsonat startup. Defaults to["general"]if the file is absent or malformed.- VALID_REACTIONSset of str
Set of valid reaction emoji names, derived from the SVG filenames in
static/reactions/. Only names in this set are accepted by the/react/<msg_id>endpoint.
- minimost.chat._max_upload_size_bytes() int[source]
Return the configured max upload size in bytes (default 25 MiB).
- minimost.chat._check_upload_sizes(files, max_bytes: int)[source]
Return an error tuple if any file exceeds max_bytes, else
None.
- minimost.chat._max_avatar_size_bytes() int[source]
Return the configured max avatar size in bytes (default 5 MiB).
- minimost.chat._load_channels() List[str][source]
Load the list of public channels from
settings.json.Reads and parses
settings.jsonfrom the project root and returns the value of the"channels"key. Falls back to a single"general"channel if the file is absent, malformed, or missing the key.
- minimost.chat.extract_mentions(text: str, channel: str) List[str][source]
Return the channel members that are
@-mentioned in text.Parses every
@nametoken out of text and keeps only those that resolve (case-insensitively) to an actual member of channel. The returned usernames use their canonical (stored) casing, in channel membership order. Returns[]when text is empty or contains no valid mentions.The reserved keyword
@everyonementions the whole channel; when it is present the single-element list[MENTION_EVERYONE]is returned regardless of any other tokens, since it already covers every member.
- minimost.chat._mentions_json(text: str, channel: str)[source]
Return a JSON-encoded mention list for text, or
Noneif empty.
- minimost.chat._opt_float(value)[source]
Parse value as a float, returning
Nonefor empty/invalid input.Used to coerce optional numeric query-string parameters (e.g. the search date bounds) without raising on absent or malformed values.
- Parameters:
value – The raw query-string value, or
None.- Returns:
The parsed float, or
Noneif value is missing or invalid.- Return type:
float or None
- minimost.chat._get_private_db()[source]
Return an open WAL connection to the shared
presence.db.Configures the connection with WAL journal mode and
sqlite3.Rowrow factory so columns are accessible by name. The caller is responsible for closing the returned connection.- Returns:
An open SQLite connection to
presence.db.- Return type:
- minimost.chat.get_private_channel_members(channel_id: int) List[str][source]
Return the list of usernames that are members of a private channel.
Queries the
private_channel_memberstable inpresence.dbfor all rows matching channel_id and returns the corresponding usernames. Returns an empty list if the channel does not exist or has no members.
- minimost.chat.get_db(username: str)[source]
Open and return a SQLite connection to a user’s message database.
The connection is configured with:
WAL journal mode — allows concurrent reads during writes, which is important when multiple Gunicorn workers serve different users simultaneously.
``row_factory = sqlite3.Row`` — rows can be accessed by column name (e.g.
row["sender"]) in addition to integer index.
The caller is responsible for closing the returned connection.
- Parameters:
username (str) – The account username whose database should be opened.
- Returns:
An open SQLite database connection.
- Return type:
Example:
db = get_db("alice") rows = db.execute("SELECT * FROM messages WHERE channel = ?", ("general",)).fetchall() db.close()
- minimost.chat.all_users() List[str][source]
Return a list of every registered username.
Queries the shared
auth.dbdatabase and returns every username in theuserstable. The order is undefined (SQLite row insertion order).This is used to determine the recipient list when sending a message to a public channel — every registered user receives a copy of every public channel message.
- minimost.chat.normalize_dm(users: List[str]) str[source]
Return the canonical DM channel identifier for a set of participants.
DM channels are identified by the string
"dm:"followed by the sorted, colon-separated participant usernames. Sorting ensures thatnormalize_dm(["bob", "alice"])andnormalize_dm(["alice", "bob"])both return the same string, preventing duplicate conversations from being created with different orderings.- Parameters:
users (list of str) – List of usernames that are participants in the DM. Duplicates are removed before sorting.
- Returns:
Canonical DM channel string, e.g.
"dm:alice:bob".- Return type:
Example:
normalize_dm(["charlie", "alice"]) # returns "dm:alice:charlie"
- minimost.chat.channel_users(channel: str) List[str][source]
Return the list of users who should receive messages on a channel.
For DM channels (
"dm:user1:user2:..."): the participants are parsed directly from the channel string.For private channels (
"private:<id>"): the member list is fetched from theprivate_channel_memberstable inpresence.dbviaget_private_channel_members(). Returns[]if the channel ID is invalid.For public channels: every registered user is a recipient, so
all_users()is called.
This list is used by the send, edit, delete, and react routes to determine which per-user databases must be updated.
- Parameters:
channel (str) – The channel name, DM identifier, or private channel identifier (
"private:<id>").- Returns:
List of usernames that are members of channel.
- Return type:
Example:
channel_users("dm:alice:bob") # ["alice", "bob"] channel_users("private:3") # members of private channel 3 channel_users("general") # all registered users
- minimost.chat.is_valid_channel(channel: str, user: str) bool[source]
Return
Trueonly if user is permitted to access channel.Three access rules apply:
DM channels — the channel string must have at least two participants (
len(parts) >= 3) and user must be one of them.Private channels — user must appear in the
private_channel_memberstable for the given channel ID (looked up viaget_private_channel_members()). ReturnsFalseif the ID is malformed or the channel does not exist.Public channels — channel must appear in the
CHANNELSlist loaded fromchannels.json.
This function is called by the
send()route before writing any data, and is the primary authorization check for channel access.- Parameters:
- Returns:
Trueif access is permitted,Falseotherwise.- Return type:
Example:
is_valid_channel("dm:alice:bob", "alice") # True is_valid_channel("dm:alice:bob", "charlie") # False is_valid_channel("private:3", "alice") # True if alice is a member is_valid_channel("general", "alice") # True (if "general" in CHANNELS) is_valid_channel("secret", "alice") # False (not in CHANNELS)
- minimost.chat.channels()[source]
Return the list of public channel names.
Route:
GET /channelsRequires authentication. Returns the
CHANNELSlist that was loaded fromchannels.jsonat application startup. The client uses this list to build the channel sidebar.- Returns:
JSON array of channel name strings, e.g.
["general", "software", "off-topic"].- Return type:
flask.Response (application/json)
- minimost.chat.channel_unreads()[source]
Return unread message counts for every public channel.
Route:
GET /channel_unreadsRequires authentication. Queries the current user’s database and counts messages that are unread (
read = 0), not sent by the user (sender != user), and not deleted (deleted = 0), grouped by public channel.All channels in
CHANNELSare included in the response — those with no unread messages are returned with a count of0.- Returns:
JSON object mapping each public channel name to its unread count, e.g.
{"general": 3, "software": 0, "off-topic": 1}.- Return type:
flask.Response (application/json)
- minimost.chat.unread_count()[source]
Return the total number of unread direct messages for the current user.
Route:
GET /unread_countRequires authentication. Counts all unread (
read = 0) messages in DM channels (channel LIKE 'dm:%') where the current user is a participant and the message was sent by someone else.The three
LIKEpatterns handle the three positions a username can occupy in a DM channel string:dm:<user>:<others>— user is the first participant.dm:<others>:<user>— user is the last participant.dm:<others>:<user>:<more>— user is a middle participant (group DM).
This count is displayed as a badge on the “DMs” section of the sidebar and drives the browser tab title badge.
- Returns:
JSON object
{"count": N}where N is the total unread DM message count.- Return type:
flask.Response (application/json)
- minimost.chat.dms()[source]
Return a summary of all DM conversations involving the current user.
Route:
GET /dmsRequires authentication. Queries the current user’s database for all DM channels, returning them sorted by most-recent message timestamp (
last_ts DESC) so the sidebar list reflects activity order.For each conversation, the response includes:
channel— the canonical DM channel identifier (e.g."dm:alice:bob").users— list of the other participants (the current user is excluded so the client can display their names).unread— count of unread messages from other users in this conversation.
- Returns:
JSON array of conversation objects, each with keys
channel,users(list of str), andunread(int), ordered by most recent activity descending.- Return type:
flask.Response (application/json)
- minimost.chat.close_dm()[source]
Hide a DM conversation from the current user’s sidebar.
Route:
POST /dms/closeJSON body:
{"channel": str}. Records ahidden_tstimestamp for the channel; the/dmsendpoint will exclude this conversation until a new message arrives after that timestamp.- Returns:
"ok"on success.- Return type:
- minimost.chat.user_colors()[source]
Return custom name colors for all users who have set one.
Route:
GET /user_colors- Returns:
JSON object mapping username to hex color string.
- Return type:
flask.Response (application/json)
- minimost.chat.get_settings()[source]
Return the current user’s settings.
Route:
GET /settings- Returns:
JSON object with user settings keys.
- Return type:
flask.Response (application/json)
- minimost.chat.save_settings()[source]
Save the current user’s settings.
Route:
POST /settingsJSON body:
{"name_color": str | null}. Passnullto reset to the default hash-derived color.- Returns:
"ok"on success.- Return type:
- minimost.chat.user_avatars()[source]
Return usernames of all users who have a custom avatar.
Route:
GET /user_avatars- Returns:
JSON array of usernames.
- Return type:
flask.Response (application/json)
- minimost.chat.get_avatar(username)[source]
Serve a user’s avatar image.
Route:
GET /avatar/<username>Returns 404 if the user has no custom avatar.
- Returns:
Image file response or 404.
- Return type:
- minimost.chat.upload_avatar()[source]
Upload and store the current user’s avatar.
Route:
POST /avatarExpects a multipart file named
avatar(pre-resized client-side). Deletes the previous avatar file if one existed.- Returns:
"ok"on success.- Return type:
- minimost.chat.delete_avatar()[source]
Remove the current user’s custom avatar.
Route:
DELETE /avatar- Returns:
"ok"on success.- Return type:
- minimost.chat.delete_account()[source]
Soft or hard delete the current user’s account.
Route:
POST /delete_accountJSON body:
{"type": "soft"|"hard", "password": "<current password>"}Soft delete — removes the user’s credentials and settings from
auth.db, renames their messages to"Deleted User"in every recipient’s database, and deletes their avatar file. Message history remains visible.Hard delete — performs the soft delete steps and additionally removes every message they sent from all recipient databases, deletes their own database file, and purges their records from
presence.db.In both cases the session is cleared and the client should redirect to the login page.
- Returns:
JSON
{"status": "ok"}on success, or an error response.- Return type:
flask.Response (application/json)
- minimost.chat.online_users()[source]
Return presence states for all recently active users.
Route:
GET /online_usersRequires authentication. Queries
presence.dbfor any user whoselast_seentimestamp is within the last 3600 seconds (one hour). Users who have not reported any presence update in that window are considered offline and excluded from the response.Possible state values returned:
"active","idle","hidden","offline". States are lowercased before being returned.The client polls this endpoint once per second to refresh the colored presence indicator (dot) displayed next to each username in the sidebar.
- Returns:
JSON object mapping username strings to their current presence state, e.g.
{"alice": "active", "bob": "idle"}.- Return type:
flask.Response (application/json)
- minimost.chat.messages(channel)[source]
Fetch messages for a channel since a given timestamp.
Route:
GET /messages/<channel>?after=<timestamp>Requires authentication. This is the core polling endpoint: the JavaScript client calls it every 500 ms, passing the timestamp of the last message it received as the
afterquery parameter. The server returns only rows that have changed since that point, keeping each response small.What is returned:
A message row is included in the response if any of the following conditions are true since after:
It is a new, non-deleted message (
ts > after).It has been edited since after (
edited_ts > after).Its reactions have been updated since after (
reactions_ts > after).It was deleted after after (
deleted = 1 AND deleted_ts > after). Deleted tombstones are returned so the client can remove the message from view.
Reactions enrichment:
After querying the user’s database, the function fetches the current reactions for all returned messages from
presence.db::message_reactionsand merges them into the response as a JSON string under thereactionskey. This overwrites the stalereactionscolumn from the user DB.``after`` parameter handling:
The string
"NaN"(case-insensitive) is treated as0.0to guard against clients passingNaNon first load.Non-numeric values are silently converted to
0.0.
- Parameters:
channel (str) – The channel name or DM identifier.
- Query parameters:
after (float, optional): Unix timestamp. Only messages modified after this point are returned. Defaults to
0(return all messages).
- Returns:
JSON array of message objects. Each object has keys:
id,channel,sender,content,filename,ts,edited,edited_ts,deleted,deleted_ts,reply_to_id,reactions(JSON string or null),reactions_ts.- Return type:
flask.Response (application/json)
- minimost.chat._try_append_message(channel, sender, text, ts, recipients, filenames, reply_to_id)[source]
Append text to the sender’s previous message if within the 300 s window.
Returns True and commits if appended; returns False if a new message is needed.
- minimost.chat.post_welcome_message(new_user: str) None[source]
Post a system welcome message greeting a new user.
Called from the signup flow when a new account is created. Inserts a
content_type = "system"message — rendered under the “MiniMost” identity, like other system notices — into the first public channel for every recipient (including the newcomer), so the whole community sees the new arrival being greeted.No-op if no public channels are configured.
- Parameters:
new_user (str) – The username of the newly registered account.
- Returns:
None
- minimost.chat.send(channel)[source]
Send a message (and/or image attachments) to a channel.
Route:
POST /send/<channel>Requires authentication. Validates the channel, processes any uploaded images, then inserts the message into every recipient’s database.
Form fields:
text(str, optional) — The message body. Trailing whitespace is stripped.reply_to_id(int, optional) — Theidof the message being replied to. Stored as a foreign key in thereply_to_idcolumn.files(multipart file list, optional) — One or more image files. Only files with extensions inALLOWED_EXTENSIONSare saved; others are silently skipped.
At least one of text or files must be non-empty; a request with neither returns
400 empty.Message propagation:
MiniMost does not use a shared messages table. Instead, each message is written into every recipient’s individual database by iterating over
channel_users(). This means:For a public channel with N users, N separate
INSERTstatements are executed.For each uploaded image, an additional
INSERTper recipient is executed (one row per file, withcontent = ''andfilenameset to the UUID-based filename).All inserts for a given recipient are committed in a single transaction.
The sender is always added to the recipient list so their own message appears in their database (marked
read = 0like everyone else’s).File naming:
Uploaded images are saved as
<uuid4hex><original_ext>inUPLOAD_DIRto prevent collisions and avoid directory traversal via crafted filenames.- Parameters:
channel (str) – Target channel name or DM identifier.
- Returns:
The string
"ok"on success.- Return type:
- Raises:
403 forbidden— if the user is not permitted to post to the channel.- Raises:
400 empty— if neither text nor valid files were provided.- Raises:
400 no recipients— if the recipient list is empty (should not happen in normal operation).
- minimost.chat.get_message(msg_id)[source]
Fetch a single message by its database ID.
Route:
GET /message/<msg_id>Requires authentication. Used by the client to load the quoted parent message when rendering a reply thread, since the
reply_to_idcolumn stores only the ID rather than the full message content.The lookup is performed against the current user’s database, which means the message must exist in that user’s records.
- Parameters:
msg_id (int) – The integer primary key of the message to retrieve.
- Returns:
JSON object with keys
id,sender,content,filename, anddeleted.- Return type:
flask.Response (application/json)
- Raises:
404 not found— if no message with msg_id exists in the current user’s database.
- minimost.chat.files(filename)[source]
Serve an uploaded file.
Route:
GET /files/<filename>Images are served inline; all other file types are served as attachments so the browser downloads rather than attempts to render them.
- minimost.chat.file_preview(filename)[source]
Return a code preview for an uploaded text file.
Route:
GET /file_preview/<filename>Reads the file from
UPLOAD_DIR, checks its extension against the known text-file extension set, and returns the same code-preview dict shape used by the Bitbucket preview routes. Returns{}for unrecognised extensions or unreadable files.
- minimost.chat.search_messages()[source]
Search the current user’s message history by keyword.
Route:
GET /search_messages?q=<query>Requires authentication. Performs a case-insensitive substring search (SQLite
LIKE %query%) across thecontentcolumn of the current user’smessagestable, excluding deleted messages.Results are returned in descending timestamp order (newest first) and limited to 50 rows to keep responses fast. The search scope is the current user’s database only, which means only messages that user has access to (including all public channels and their DMs) are searched.
- Query parameters (all optional, combined with
AND): q (str): Keyword matched as a case-insensitive
LIKEsubstring of the message content.from (str): Restrict to messages from this sender (matched case-insensitively).
channel (str): Restrict to a single channel identifier.
start / end (float): Inclusive lower / exclusive upper bound on the message timestamp, as epoch seconds. The client computes these from the picked dates using the viewer’s local timezone.
At least one filter must be supplied; a request with no filters returns
[]without hitting the database, so an empty search box never dumps the whole history.- Returns:
JSON array of matching message objects, each with keys
id,channel,sender,content, andts.- Return type:
flask.Response (application/json)
- Query parameters (all optional, combined with
- minimost.chat.edit(msg_id)[source]
Edit the text content of the current user’s message.
Route:
POST /edit/<msg_id>Requires authentication. Only the original sender may edit a message; any other user’s attempt returns
403 forbidden.Only text messages can be edited — rows with a non-null
filename(image attachments) are excluded from theUPDATEby theAND filename IS NULLclause.Propagation:
The edit is applied to every recipient’s copy of the message, matched by the combination of
(channel, sender, ts)rather than byidbecause row IDs differ between per-user databases. Theeditedflag is set to1andedited_tsrecords when the edit occurred so the polling query (messages()) picks it up for other users.- Form fields:
text (str): The new message content. Stripped of leading/trailing whitespace.
- Parameters:
msg_id (int) – The integer
idof the message to edit (in the current user’s database).- Returns:
The string
"ok"on success.- Return type:
- Raises:
403 forbidden— if the message does not belong to the current user or does not exist.
- minimost.chat.delete_message(msg_id)[source]
Soft-delete the current user’s message.
Route:
POST /delete/<msg_id>Requires authentication. Only the original sender may delete a message; other users receive
403 forbidden.Soft delete, not hard delete:
The message row is not removed from the database. Instead,
deletedis set to1anddeleted_tsrecords when the deletion occurred. This allows the polling query (messages()) to return a tombstone to any client that has already cached the message, so they can remove it from their view. The actual row is preserved for audit/admin purposes.Propagation:
Like edits, the soft-delete is applied to every recipient’s copy of the message, matched by
(channel, sender, ts).- Parameters:
msg_id (int) – The integer
idof the message to delete (in the current user’s database).- Returns:
The string
"ok"on success.- Return type:
- Raises:
403 forbidden— if the message was not sent by the current user or does not exist.
- minimost.chat._save_uploaded_files(files) List[str][source]
Save uploaded files of any type, returning their stored filenames.
Images are stored as
<uuid32hex><ext>. All other files are stored as<uuid32hex>_<sanitized_original_name>so the original name can be recovered by slicing off the first 33 chars.
- minimost.chat.react(msg_id)[source]
Toggle an emoji reaction on a message.
Route:
POST /react/<msg_id>Requires authentication. The reaction is toggled: if the current user has already reacted with this emoji, the reaction is removed; otherwise it is added.
Why the shared ``presence.db`` is used:
Reactions must be visible to all users instantly and without race conditions. If reactions were stored in per-user databases, a read-modify-write cycle would be needed on each user’s file — which creates TOCTOU races under concurrent requests. Instead, the
message_reactionstable inpresence.dbis used with a single atomicINSERT OR DELETEoperation.Propagation to user databases:
After updating
presence.db, the function bumps thereactions_tscolumn in every recipient’s copy of the message. This causes the polling query (messages()) to return the message row again for all users, and the client re-fetches the current reactions from the response.- Form fields:
reaction (str): The emoji name (SVG filename without extension) to toggle. Must be a member of
VALID_REACTIONS.
- Parameters:
msg_id (int) – The integer
idof the message to react to (in the current user’s database).- Returns:
JSON object mapping emoji names to lists of reactors, e.g.
{"thumbs_up": ["alice", "bob"], "heart": ["charlie"]}.- Return type:
flask.Response (application/json)
- Raises:
400 invalid reaction— if reaction is not inVALID_REACTIONS.- Raises:
404 not found— if msg_id does not exist in the current user’s database.
- minimost.chat.mark_read(channel)[source]
Mark all messages in a channel as read for the current user.
Route:
POST /mark_read/<channel>Requires authentication. Called by the client whenever the user switches to a channel or scrolls to the bottom of the message list.
Two-step operation:
Collects the timestamps of every currently-unread message in the channel (sent by other users) so they can be recorded as read receipts.
Sets
read = 1for all messages in the channel that were not sent by the current user.
Read receipts:
After marking messages as read in the user’s database, a row is inserted into
presence.db::read_receiptsfor each previously-unread message timestamp.INSERT OR IGNOREis used to avoid duplicate entries when the same message is read multiple times (e.g. the user switches to the channel, back, then returns).The client polls
read_receipts()to display✓checkmarks and tooltips showing who has read each message.- Parameters:
channel (str) – The channel name or DM identifier.
- Returns:
Empty body with HTTP 204 No Content.
- Return type:
- minimost.chat.read_receipts(channel)[source]
Return read receipts for all messages in a channel.
Route:
GET /read_receipts/<channel>Requires authentication. Queries
presence.db::read_receiptsand returns a mapping of message timestamps to the list of users who have read each message.The message timestamp (
msg_ts) is used as the key (as a string) rather than the messageidbecause IDs differ across per-user databases while timestamps are shared.The client polls this endpoint every 3 seconds when viewing a channel and renders
✓indicators with a tooltip listing the readers.- Parameters:
channel (str) – The channel name or DM identifier.
- Returns:
JSON object mapping message timestamp strings to lists of reader usernames, e.g.
{"1716000000.123": ["alice", "bob"], "1716000001.456": ["alice"]}.- Return type:
flask.Response (application/json)
- minimost.chat.users()[source]
Return a list of all registered users except the current user.
Route:
GET /usersRequires authentication. Used by the client to populate the “New DM” autocomplete modal — the list shows all other accounts the user can start a conversation with.
- Returns:
JSON array of username strings, excluding the currently logged-in user.
- Return type:
flask.Response (application/json)
- minimost.chat.channel_members(channel)[source]
Return the mentionable members of a channel (excluding the caller).
Route:
GET /channel_members/<channel>Requires authentication and that the caller is permitted to access the channel. Used by the client to populate the
@-mention autocomplete dropdown: for public channels this is every other registered user, for private channels the other members, and for DMs the other participants.- Parameters:
channel (str) – The channel name, DM identifier, or
"private:<id>".- Returns:
JSON array of usernames the caller may mention, excluding themselves.
- Return type:
flask.Response (application/json)
- Raises:
403 forbidden— if the caller may not access the channel.
- minimost.chat.link_preview()[source]
Fetch a link preview card for a URL.
Route:
GET /link_preview?url=<url>Requires authentication. Delegates to
minimost.preview.fetch_preview(), which supports Bitbucket Cloud, Bitbucket Server, and generic OpenGraph previews.An empty JSON object
{}is returned if:The
urlquery parameter is missing or blank.The URL is a private/internal IP address (SSRF protection).
The URL scheme is not
httporhttps.The request fails or times out.
No usable preview data can be extracted.
- Query parameters:
url (str): The fully-qualified URL to preview.
- Returns:
A JSON object describing the preview. Shape depends on the preview type:
Code preview (Bitbucket):
{"type": "code", "filename": ..., "filepath": ..., "language": ..., "first_line_num": ..., "highlight_start": ..., "highlight_end": ..., "code": ..., "total_lines": ..., "url": ...}OpenGraph preview:
{"type": "og", "title": ..., "description": ..., "image": ..., "domain": ..., "url": ...}No preview:
{}
- Return type:
flask.Response (application/json)
- minimost.chat.create_private_channel()[source]
Create a new private channel.
Route:
POST /private_channels/createJSON body:
{"name": str, "members": [str]}. The creator is always included as a member regardless of the list provided.- Returns:
JSON
{"id": int, "channel": str, "name": str}on success.- Return type:
flask.Response (application/json)
- minimost.chat.list_private_channels()[source]
List private channels the current user is a member of.
Route:
GET /private_channels- Returns:
JSON array of
{"id", "channel", "name", "unread"}objects.- Return type:
flask.Response (application/json)
- minimost.chat.rename_private_channel(channel_id)[source]
Rename a private channel. Any member may rename.
Route:
POST /private_channels/<channel_id>/renameJSON body:
{"name": str}.- Returns:
"ok"on success.- Return type:
- minimost.chat.add_private_channel_member(channel_id)[source]
Add a user to a private channel. Any existing member may add.
Route:
POST /private_channels/<channel_id>/add_memberJSON body:
{"username": str}. The new member starts fresh — no prior message history is shared. A system message is inserted into every member’s database announcing the addition.- Returns:
"ok"on success.- Return type:
- minimost.chat.leave_private_channel(channel_id)[source]
Remove the current user from a private channel.
Route:
POST /private_channels/<channel_id>/leaveA system message announcing the departure is inserted into every remaining member’s database.
- Returns:
"ok"on success.- Return type:
- minimost.chat.private_channel_members_route(channel_id)[source]
List members of a private channel.
Route:
GET /private_channels/<channel_id>/members- Returns:
JSON array of
{"username": str}objects.- Return type:
flask.Response (application/json)
- minimost.chat.index()[source]
Serve the main chat single-page application.
Route:
GET /Requires authentication (redirects to
/loginotherwise). Renders thechat.htmlJinja2 template, which contains the full client-side chat interface. All subsequent data is loaded by the JavaScript polling loop via the JSON API endpoints in this module.- Returns:
A rendered HTML response.
- Return type:
flask.Response (text/html)
Route Summary
Method |
Path |
Handler |
|---|---|---|
GET |
|
|
GET |
|
|
GET |
|
|
GET |
|
|
POST |
|
|
GET |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
POST |
|
|
GET |
|
|
GET |
|
|
GET |
|
|
GET |
|
|
GET |
|
|
GET |
|
|
GET |
|
|
GET |
|
|
GET |
|