Repository navigation
Slack as a connector: one flat DM that works like Telegram - #670
Open
woltspace-asharakeh-contrib[bot] wants to merge 12 commits into
Open
woltspace-asharakeh-contrib[bot] wants to merge 12 commits into
woltspace-asharakeh-contrib[bot] wants to merge 12 commits into
Conversation
woltspace-asharakeh-contrib
Bot
requested a review
from jerpint
as a code owner
November 7, 2026 03:06
Slack was the last chat app still outside the connector design: a 776-line adapter importing lodge code, a picker for every new DM, one thread per session, Agent View status, no commands, no dog, text only, and its own copies of the notify transport, launcher class and route spec. It now runs from its own folder, container/connectors/slack/ (connector.json and slack_connector.py), and only translates: the owner's DMs in, Block Kit replies out, over the lodge's connector socket. The shared chat code does the rest, as for Telegram: an active wolt, the eight commands, the dog, files and voice, reply routing, the inbox. - In: message.im from the one configured member ID in a D... channel, each Slack event once; shared files (files:read) saved in the connector's uploads dir; a thread reply carries its parent when this bot wrote it, so the lodge routes it to that message's session; slash commands and clicks, acknowledged before anything else. Slack runs no custom slash command in a thread, so commands may also be typed with "!" anywhere. - Out: say, edit and ack as Block Kit in the DM; notify as chat.postMessage or a file upload, with Slack's answer mapped to delivered, refused, failed or ambiguous. - Removed: bot/slack_adapter.py, Slack's sender and outbound entry in notify, its route spec in connector_registry, SlackConnector in the launcher, /sessions/new/slack. - Kept: the channels.slack block (and its env names); the route field is chat_id, so every stored Slack record resolves to its DM. `notify --slack C T` and `--to slack:C:T` still work, the thread ignored; only flat routes are written. A connector may declare a default route env: SLACK_NOTIFY_CHANNEL stays the default for a notification that names no chat, and one of the lodge's own chats. The setup skill, its app manifest (scopes chat:write, commands, files:read, files:write, im:history; the eight slash commands; no Agent View) and the Slack docs are rewritten. A 0.6 change: threads per session and Agent View are gone, and the app needs one reinstall.
A reply under one of this bot's messages carried the parent's text as Slack stores it, mrkdwn: the session link in its footer read <https://...> or <https://...|label>, and the session parsed from it had ">" or "|label>" glued on. The connector now gives the lodge the text as written (links unwrapped, < > & restored); only this bot's own messages still count. An enabled Slack without a usable owner member ID started anyway and ignored every message. A bundled connector may now declare its owner id as a pattern (owner_pattern); without an owner that matches, the launcher does not start it and names the setting to fix, as the Slack adapter did.
A shared file's download link came from the event, and the connector sent the bot token to whatever it named. It now downloads only from https://files.slack.com, where Slack's url_private points, and refuses any other link before a request is made.
Slack refuses to save an app manifest that registers one of its own built-in slash commands, and /apps is one. Drop it from the setup manifest and the docs; !apps works anywhere, as do the buttons.
Slack refuses an app manifest that registers one of its own built-in slash commands. Register the lodge's apps command as /wapps, and map it back to apps in the connector, so all eight commands stay slash commands; !apps keeps working too.
Slack hands the connector a message as it stores it, mrkdwn: "<" as <, a link as <https://...|label>, a mention as <@U...>. That text went to the wolt, and to the "!" command parser, as stored. It is now unwrapped first, as a thread parent's already was, for messages and file captions alike. The connector sent any photo path the lodge named, while every file a notification sends must come from the outbox. A dog's generated image lives in its wolt's images folder, so the lodge now stages it in the outbox before it says it, and the connector sends a photo only from there; the outbox sweep removes the copy later. The Slack doc names the release this lands in: 0.7.
…en resend A dog's image is staged in the outbox for its connector, and only notify's sweep removed old entries: a lodge whose wolts never send a file kept every copy. Staging an image now sweeps the outbox first. On its own, this connector answers a message sent with no wolt chosen with the picker and asks for it again; the setup skill and the doc no longer promise that the message is held.
The lodge staged a dog's image in the outbox under an id, and Slack showed every one as "image.png". It is now staged in a folder of its own in the outbox (image-<random>/<its name>), and the Slack connector uploads it under the file's name. Image folders past the outbox's age limit are removed when the next image is staged, as notify sweeps the outbox's files.
woltspace-asharakeh
Bot
force-pushed
the
slack-integration
branch
from
November 7, 2026 04:13
146b879 to
8013dea
Compare
On a Python that ships the standard library's own test package (as CI's does), "from test.x import" and pytest_plugins = ["test.x"] resolve to that package and collection fails. Use the plain module names the rest of the suite uses.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🦊 Bob the Planner (planner): ## TL;DR
Slack becomes a chat connector, the same kind as Telegram. It's one flat DM that works exactly like Telegram: commands, the dog, voice, images and files, reply routing and the inbox. Target: 0.7. The "hold your first message while you pick a wolt" change is now its own PR, stacked on this one.
For humans
The problem. Slack was the last chat app still wired into the lodge by hand, so it behaved differently from Telegram:
Every Slack fix was a second copy of something the shared chat code already did.
What changes.
/woltand/newswitch, at the top of the DM. Slack keeps/appsfor itself, so the apps list is/wapps.!wolt,!newand!helpwork everywhere, and buttons cover the rest.channels.slackconfig is unchanged. The Slack app gains thecommandsandfiles:readscopes and dropsassistant:write, then needs one reinstall.What goes away. Slack threads per session and Slack's "Agent View" processing status. This changes how an existing Slack setup behaves, so it targets 0.7.
For Woltspace contributors
Problem
container/bot/slack_adapter.py(776 lines) imported lodge code and kept its own routing, its own picker queue and its own outbound path. Slack also had special branches in:server/notify.pyandserver/notification_senders.py;container/lib/connector_registry.py;src/woltspace/channels.py.That was the opposite of the connector design (RFC #617: "the lodge decides, connectors translate").
Solution
container/connectors/slack/):connector_client.message.imonly, failing closed.chat_id= the DM channel, so old Slack session records resolve to the flat DM through the generic codec.<url|label>,<) before command parsing and routing.!prefix. Slash names that are Slack built-ins are mapped (/wapps→apps).reply_to.https://files.slack.com).notify --fileor a dog image) passes the outbox check, and a dog image keeps its own file name.owner_patternrefuses to start without a usable owner id, with the remedy.default_route_envkeepsSLACK_NOTIFY_CHANNELworking.notify --slack C [T]and--to slack:C:Tnormalize to the flat route;channels.slackconfig keys are unchanged.Where to focus
Validation
/woltand/wapps;!woltin a thread;Limits
@U…), not the display name. That's a possible follow-up./sessions/new/slackis removed.