Metadata-Version: 2.4
Name: waken-discord
Version: 0.1.0
Summary: Discord Source/Output for Waken -- gateway events in, REST message send out.
Project-URL: Homepage, https://github.com/WakenHQ/waken-discord
Project-URL: Repository, https://github.com/WakenHQ/waken-discord
Project-URL: Issues, https://github.com/WakenHQ/waken-discord/issues
Project-URL: Documentation, https://github.com/WakenHQ/waken-discord/tree/main/README.md
Author: Waken contributors
License: MIT
License-File: LICENSE
Keywords: agents,ai,discord,waken
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.12
Requires-Dist: discord-py>=2.7
Requires-Dist: waken>=0.1.1
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# waken-discord

[![CI](https://github.com/WakenHQ/waken-discord/actions/workflows/ci.yml/badge.svg)](https://github.com/WakenHQ/waken-discord/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-lightgrey)](https://github.com/WakenHQ/waken-discord/blob/main/LICENSE)

Discord `Source`/`Output` for [Waken](https://github.com/WakenHQ/waken) --
"nginx for AI agents." Unlike a `Target` adapter (`waken-claude`,
`waken-gemini`, ...), Discord here is a *channel*: `DiscordSource` turns
incoming Discord messages into waken `Event`s, and `DiscordOutput` posts a
`Target`'s `Response` back to the channel it came from.

## Install

```bash
pip install waken-discord
```

Needs a Discord bot application (Discord Developer Portal -> New
Application -> Bot):

- `DISCORD_BOT_TOKEN` -- the bot token from the Bot page.
- The **Message Content** privileged gateway intent, enabled on that same
  Bot page (under "Privileged Gateway Intents"). This is a real, commonly
  missed step -- without it, Discord silently delivers every message with
  `content` as an empty string (except direct @mentions of the bot), no
  matter what the bot process itself requests.
- The bot invited to your server with, at minimum, the `bot` scope and the
  **Send Messages**/**Read Message History** permissions.

## Usage

```python
from waken import Runtime
from waken_claude import ClaudeAdapter
from waken_discord import DiscordOutput, DiscordSource

runtime = Runtime()
runtime.target("claude", ClaudeAdapter())
runtime.source("discord", DiscordSource(target="claude"))
runtime.output("discord", DiscordOutput())
runtime.run()
```

`DiscordSource`/`DiscordOutput` both resolve `DISCORD_BOT_TOKEN` from the
environment by default, or accept it as a constructor argument. Every
incoming message becomes an `Event(source="discord", target="claude",
payload={"prompt": text}, session_id=runtime.session("discord",
external_key=str(channel_id)), metadata={"channel_id": channel_id})` --
follow-up messages in the same channel resume the same waken session.
`DiscordOutput` reads `event.metadata["channel_id"]` back off to know where
to send the reply.

Messages from bots -- including `DiscordOutput`'s own replies -- are
filtered out before dispatch, otherwise a reply posted back into the
channel would re-trigger `DiscordSource` and loop forever.

## Design notes

**A gateway connection for `DiscordSource`, REST-only for
`DiscordOutput`.** Discord bots receive messages over a persistent gateway
WebSocket -- there's no webhook equivalent of "a message was posted" a bot
can subscribe to instead, so `DiscordSource` holds that connection open,
the same shape as `waken-slack`'s Socket Mode connection: a background task
started in `start()`, closed in `stop()`.

Sending a message, however, is a plain REST call authenticated by the bot
token -- it needs no gateway connection at all. `DiscordOutput` doesn't
spin up a second `discord.Client` (which would mean logging in twice and
paying for a connection it'd never use) to get one. Instead it talks
directly to `discord.http.HTTPClient`, the lightweight, gateway-free HTTP
client that `discord.py` itself builds internally and hangs every
`Client`'s own REST calls off of. It isn't re-exported from
`discord/__init__.py` or documented as public API the way `discord.Client`
is, but it's the only thing the library ships that fits "REST send, no
gateway" -- there's no separate public `discord.RESTClient`. It ships under
the same `py.typed` package as everything else, so it stays fully
type-checked here regardless.

**`discord.Client.start()`, not `.run()`.** `Client.run()` is documented as
"a blocking call that abstracts away the event loop initialisation from
you" and calls `asyncio.run(...)` itself -- it wants to own the whole
process's event loop, the same way `Client.run()`/`SlackSource`'s
`SocketModeHandler.start_async()` would. `waken`'s `Runtime` already owns
the loop this `Source` runs inside, so `DiscordSource.start()` instead
schedules the lower-level `client.start(token)` coroutine (a shorthand for
`login()` + `connect()`, and just a plain coroutine with no loop management
of its own) as a background `asyncio.Task`, and awaits `client.close()` +
that task on `stop()` -- the recipe discord.py itself documents for running
inside an already-running event loop.

**Why `discord.py`, not one of its forks.** `discord.py` went through a
period of being unmaintained a few years back, which is what spawned
`pycord`/`nextcord`/`disnake` in the first place -- but it later resumed
active maintenance, and remains, by a wide margin, the most widely used
library in this space (checked directly against all three forks' PyPI
release cadence and GitHub activity before picking it, not from memory).

## Development

```bash
git clone https://github.com/WakenHQ/waken-discord
cd waken-discord
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
```

Tests mock `discord.py`'s `Client` and `discord.http.HTTPClient` entirely
-- no network access, real gateway connection, or Discord bot token needed
to run the suite.

## License

[MIT](https://github.com/WakenHQ/waken-discord/blob/main/LICENSE)
