> ## Documentation Index
> Fetch the complete documentation index at: https://metacognition-fdc534de-master.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Slack bot with channel memory

> Build a Slack Bolt app that remembers channel messages and answers mentions with Tex recall.

Give each Slack channel its own Tex **`session_id`**. Save normal messages with **`remember`**. When someone mentions the bot, use **`recall`** to answer from that channel's memory.

This uses the same isolation pattern as [Scopes and multi-tenancy](/concepts/scopes).

<Steps>
  <Step title="Install Bolt + Tex">
    ```bash theme={null}
    pip install tex-sdk slack-bolt python-dotenv
    ```
  </Step>

  <Step title="Export tokens">
    Load the bot and app tokens. This example uses socket mode:

    ```bash theme={null}
    SLACK_BOT_TOKEN=xoxb-...
    SLACK_APP_TOKEN=xapp-...
    TEX_API_KEY=tex_live_...
    TEX_BASE_URL=https://api.getmetacognition.com
    ```
  </Step>

  <Step title="Wire Slack events">
    Ignore bot messages, remember human text, and answer mentions with recall:

    ```python bot.py theme={null}
    import os
    from datetime import datetime, timezone
    from slack_bolt import App
    from slack_bolt.adapter.socket_mode import SocketModeHandler
    from tex import Tex

    tex = Tex(api_key=os.environ["TEX_API_KEY"], base_url=os.environ["TEX_BASE_URL"])
    app = App(token=os.environ["SLACK_BOT_TOKEN"])

    def now_iso() -> str:
        return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")

    def session_for(channel: str) -> str:
        return f"slack-{channel}"

    @app.event("message")
    def remember_message(event, say):
        if event.get("subtype") or event.get("bot_id"):
            return
        tex.conversations.remember(
            session_id=session_for(event["channel"]),
            turns=[{
                "role": "user",
                "text": f"<@{event['user']}>: {event['text']}",
                "timestamp": now_iso(),
            }],
        )

    @app.event("app_mention")
    def answer(event, say):
        query = event["text"].split(">", 1)[-1].strip()
        if not query:
            say("Ask me something and I'll search this channel's memory.")
            return

        hits = tex.recall(q=query, session_id=session_for(event["channel"]), top_k=5)

        if not hits.hits.turns or hits.confidence < 0.2:
            say(f"<@{event['user']}> I don't have anything relevant in memory yet.")
            return

        body = "\n".join(f"• {h.text}" for h in hits.hits.turns[:3])
        say(f"<@{event['user']}> here's what I remember:\n{body}")

    if __name__ == "__main__":
        SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()
    ```
  </Step>

  <Step title="Run the bot">
    ```bash theme={null}
    python bot.py
    ```

    Type in a channel, then ask `@YourBot what did we decide?` to verify recall.
  </Step>
</Steps>

## Slack tweaks

| Topic             | What you do                                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| **Channel vs DM** | You keep `slack-{channel}` for shared rooms; you append `-{user}` if you need private scratch space.          |
| **Noise**         | You filter joins, uploads, reactions **before** `remember` so you do not pay tokens for junk.                 |
| **Latency**       | React with `:thinking_face:` right away. `recall` can take 1-3s. Post the final answer after recall finishes. |
