- Matrix: interactive device verification (SAS emoji flow), recovery-key
backup/restore, membership announcement moderation, member/room avatars,
power-level-aware moderation menu, and an encryption lock indicator on
the composer - verified live with two independent devices.
- Replace the single combined join panel with a dedicated page per
protocol (IrcJoinPanel/MatrixJoinPanel/DiscordJoinPanel), routed by
whichever account the "+" button was clicked for rather than listing
every account in one expandable page.
- Discord: friends list (Online/All, click-to-DM, add-friend, matches
READY's relationships + live PRESENCE_UPDATE), create-a-server, and
join-by-invite alongside the existing DM-by-ID flow.
- Discord: an expired attachment-link fallback ("Open in Discord") for
scrollback media whose signed cdn.discordapp.com URL has lapsed -
Discord's own /attachments/refresh-urls re-sign endpoint hard-rejects
this backend's user-token requests, confirmed live, so this opens the
real message instead of showing a broken embed.
- Discord: detect gateway close code 4004 (revoked token) as a distinct
ConnState::AuthFailed instead of retrying a dead credential forever;
the Accounts pane shows a clear "needs login" state that routes
straight to a fresh QR login, which upserts the same account id in
place (no lost buffers/scrollback).
- Fix the unread-DM pinned-buffer flash never reliably triggering: an
implicit property binding computed via nested closures over two
separate properties wasn't consistently re-evaluated by QML's
dependency tracker; replaced with an explicit recompute function called
at every actual mutation point.
- Fix ChatBarWidget.qml's pill double-padding against BasePill's own
padding.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
dms-irc (Dank Chat)
A Dank Material Shell plugin that brings a unified chat frontend to the shell: IRC, Discord, and Sneedchat (SockChat, the Tor-only XenForo chat feature) today, with XMPP/Matrix/Slack support landing protocol by protocol without frontend changes. The architecture is deliberately service-agnostic - the QML side only ever sees a unified account/buffer/message model, never a protocol-specific concept.
Why two parts
Quickshell's QML runtime only exposes Unix-domain sockets, not raw TCP/TLS, so the actual protocol work can't live in QML. Instead:
chatd(chatd-rs/, Rust) speaks each protocol directly and translates it into a small unified JSON model (accounts, buffers, messages) over a Unix socket. It never exposes protocol-specific concepts to the plugin; it speaks the unified model, and each backend module happens to be how that model gets populated for a given service.- The QML plugin (top-level
*.qml,plugin.json) spawns/superviseschatdand renders whatever it reports: a DankBar status pill, a slideout for quick access, and a floating window for a full chat session.
chatd-rs/ chatd - the Rust daemon, one crate per concern:
chatd/src/backend/ one module per protocol (irc, discord, sockchat, ...)
chatd/src/rpc/ the Unix-socket JSON-RPC server
chatd/src/store.rs SQLite-backed scrollback persistence
chatd/src/runtime.rs protocol-agnostic connection/buffer/message state
*.qml the DMS plugin surfaces plugin.json points at directly:
daemon supervisor, bar widget, startup check, settings entry
views/ main chat UI: slideout/popout window, accounts, buffers, messages
settings/ per-protocol settings panels loaded by views/ChatSettings.qml
forms/ per-protocol account-connect forms loaded by views/AccountsPanel.qml
plugin.json DMS plugin manifest
Building chatd
cd chatd-rs
cargo build --release
Enable the plugin in DMS (Settings → Plugins, or dms ipc call plugins enable dankChat). StartupCheck.qml verifies the built binary exists at
chatd-rs/target/release/chatd before allowing activation.
Opening the chat UI
Besides the DankBar pill (ChatBarWidget.qml), ChatDaemon.qml registers an
IpcHandler under the dankChat target for scripting/keybinds:
dms ipc call dankChat toggle # open/close the slideout (or the floating window, if that's what's open)
dms ipc call dankChat popout # open (or bring forward) the floating window directly
Protocol backends
- IRC - TLS with SASL PLAIN, NickServ auto-identify, autojoin.
- Discord - official cross-device QR login (the same one discord.com/app offers), a real-time gateway client, message edit/delete/reactions/replies sync.
- Sneedchat (SockChat) - the Tor-only chat built into Kiwi Farms. Runs over an
embedded Tor client (or an external SOCKS5 proxy), solves the site's own
proof-of-work anti-bot gate, and connects to every configured room simultaneously
(one persistent websocket per room, sharing a single login). Supports message
edit/delete and avatars (fetched through the same Tor session and cached locally,
since the frontend has no route to a
.onionhost on its own). - XMPP/Matrix/Slack - not implemented yet; see the project plan for the milestone list.
Wire protocol
Newline-delimited JSON over a Unix socket at $XDG_RUNTIME_DIR/dms-chat/chatd.sock:
request: {"id": 1, "method": "sendMessage", "params": {...}}
response: {"id": 1, "result": {...}} or {"id": 1, "error": "..."}
push: {"event": "message", "data": {...}}
Core methods: listAccounts, listBuffers, listProtocols, addAccount,
removeAccount, setAccountConnected, joinBuffer, partBuffer, sendMessage,
editMessage, deleteMessage, subscribe/unsubscribe, getBacklog (SQLite-backed,
persists across chatd restarts). Each protocol also has its own account-creation
method (addAccount for IRC, addDiscordAccount, addSockChatAccount, ...).
Push events: message, messageUpdated, messageDeleted, reactionsChanged,
presenceChange, bufferListChange, connectionState, notification, plus
per-protocol login-flow events (discordLoginQr/discordLoginScanned/
discordLoginResult, sockChatLoginStatus/sockChatLoginResult). message and
presenceChange are only delivered to clients that called subscribe for that buffer.
Status
Core loop is verified end to end through the actual DMS-spawned plugin: connect to
real IRC networks over TLS with SASL PLAIN auth, log into Discord via QR and Sneedchat
via username/password (+ optional TOTP 2FA), list/add accounts, join channels, send/
receive messages through a slideout or floating window, mentions flagged as highlights
and pushed as notification events, and scrollback that survives a chatd restart.
Not yet built: tab-complete, multi-account grouping in the buffer list, and XMPP/
Matrix/Slack backends. See the project plan for the full milestone list.