Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
# Recycler

A free, modern Discord **server-management bot** -- backups, templates,
chatlogs, sync, import/export and settings -- plus the `.clank` account
**containment** subset. Everything is rendered in Discord's **Components V2**
UI, with **no premium tiers and no paywalls**. Built on the shared
[`bot framework`](https://github.com/hilleywyn/framework) and templated for the
[Sojourns](https://github.com/hilleywyn/sojourns) platform.
chatlogs, sync, import/export and settings -- rendered in Discord's
**Components V2** UI, with **no premium tiers and no paywalls**. Built on the
shared [`bot framework`](https://github.com/hilleywyn/framework) and templated
for the [Sojourns](https://github.com/hilleywyn/sojourns) platform.

> Inspired by what Xenon does for server management -- rebuilt on a modern,
> open stack with the premium gating removed.

Recycler is the **server-tools** half of the project. Moderation, audit logging
and the `.clank` account-containment system live in a separate bot,
**Clanksimus Prime** (`hilleywyn/clanksimus-prime`).

## Features

| Area | What it does | Commands |
Expand All @@ -19,8 +22,7 @@ UI, with **no premium tiers and no paywalls**. Built on the shared
| **Chatlog** | Archive a channel's messages and replay them via webhook | `.chatlog create` `.chatlog load` `.chatlog list` `.chatlog delete` |
| **Sync** | Mirror messages between channels and propagate bans between guilds | `.sync messages` `.sync bans` `.sync list` `.sync remove` |
| **Import/Export** | Move backups in and out as portable JSON files | `.export <id>` `.import` (attach a file) |
| **Settings** | Per-guild configuration in a Components V2 panel | `.settings` `.set prefix` `.set log` `.set containment` |
| **Containment** | The ported `.clank` subset: scam/bot-account containment, evidence, account-linking, escape room | `.clank add` `.clank list` `.clank scan` `.clank help` |
| **Settings** | Per-guild configuration in a Components V2 panel | `.settings` `.set prefix` `.set log` |
| **REST API** | Read backups/templates over HTTP | `GET /api/v2/...` (see docs) |

## Quick start
Expand All @@ -29,7 +31,7 @@ UI, with **no premium tiers and no paywalls**. Built on the shared
git clone https://github.com/hilleywyn/recycler
cd recycler
cp .env.example .env # fill in DISCORD_TOKEN + DATABASE_URL
# install the framework (private repo) + deps, then run:
# install the framework (public) + deps, then run:
pip install "bot-framework @ git+https://github.com/hilleywyn/framework.git@main"
pip install -r requirements.txt
python main.py
Expand Down
16 changes: 0 additions & 16 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,6 @@ server permission; the bot also needs the matching permission itself.
| `.settings` | Manage Server | Show this server's configuration. |
| `.set prefix <p>` | Manage Server | Set a per-guild prefix. |
| `.set log <#channel>` | Manage Server | Set the log channel (`none` to clear). |
| `.set containment <#channel>` | Manage Server | Set the containment channel. |
| `.set containmentlog <#channel>` | Manage Server | Set the containment log channel. |

## Containment (`.clank`, alias `.clanker`)

The full ported containment subset. Highlights:

| Command | What it does |
|---|---|
| `.clank add <@user> [reason]` | Contain an account. |
| `.clank remove <@user>` | Release an account. |
| `.clank list` | Active contained accounts. |
| `.clank info <@user>` | Record, score and evidence for an account. |
| `.clank scan` | Score active accounts / a guarded role band. |
| `.clank chart` | Containment analytics chart. |
| `.clank help` | Full containment help. |

## REST API

Expand Down
16 changes: 1 addition & 15 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ service variables, on Sojourns via the settings UI generated from

| Variable | Default | What changes if you flip it |
|---|---|---|
| `PREFIX` | `.` | Command prefix. Commands become `.backup`, `.clank`, ... A per-guild override can be set with `.set prefix`. |
| `PREFIX` | `.` | Command prefix. Commands become `.backup`, `.template`, ... A per-guild override can be set with `.set prefix`. |
| `API_PORT` | `8080` | Port for the embedded REST API + `/health`. |
| `DEBUG` | `false` | Verbose logging and relaxed production guards. |
| `DISCORD_CLIENT_ID` | -- | Used only to build the invite URL before the bot is logged in. |
Expand All @@ -32,20 +32,6 @@ service variables, on Sojourns via the settings UI generated from
|---|---|---|
| `BACKUP_MAX_PER_USER` | `50` | Soft cap on stored backups per user (abuse prevention, not a paywall). |

## Containment (`.clank`)

These mirror the ported Discoin behaviour; channel/role values are Discord ids.
Per-guild overrides for the containment channels can also be set with
`.set containment` / `.set containmentlog`.

| Variable | Default | What it is |
|---|---|---|
| `CLANKER_ROLE_ID` | -- | The role applied to contained accounts. |
| `CLANKTANK_CHANNEL_ID` | -- | The "tank" channel contained users are limited to. |
| `CLANKTANK_LOG_CHANNEL_ID` | -- | Mod log channel for containment events (optional). |
| `CLANK_ESCAPE_THREAD_ID` | -- | Shared escape-room thread (optional). Can be set live with `.clank er setthread`. |
| `CLANK_ESCAPE_WAIT_MINUTES` | `8` | Reflection wait before the escape room opens. |

## Optional

| Variable | Default | What it is |
Expand Down
50 changes: 19 additions & 31 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ covers all four pathways:
Recycler runs as **one process**: a Discord gateway client plus an
embedded HTTP server (REST API + `/health`). It needs exactly two external
things to run: a **PostgreSQL database** and a **Discord bot token**. Redis is
optional. It is built on the private `hilleywyn/framework` package, so every
optional. It is built on the public `hilleywyn/framework` package, so every
install pulls that package from git.

---
Expand All @@ -31,10 +31,9 @@ install pulls that package from git.
the bot.
3. Scroll to **Privileged Gateway Intents** and enable both that Recycler
relies on:
- **Server Members Intent** - needed for ban sync, member-scoped permission
overwrites in backups/restores, and containment tracking.
- **Message Content Intent** - needed for message sync, chatlog archiving,
and `.clank` enforcement.
- **Server Members Intent** - needed for ban sync and member-scoped
permission overwrites in backups/restores.
- **Message Content Intent** - needed for message sync and chatlog archiving.
Leave **Presence Intent** off (unused).
4. (Optional) On the **OAuth2** tab, copy the **Client ID** into
`DISCORD_CLIENT_ID`. It is only used to build a clean invite URL before the
Expand Down Expand Up @@ -67,13 +66,13 @@ you never apply SQL by hand. The data plane enables TLS automatically for
remote hosts (and trusts Railway's self-signed certs); set `DB_SSL_VERIFY=1` to
force full certificate verification.

### 1.4 Access to the framework package
### 1.4 The framework package

The runtime lives in the **private** `hilleywyn/framework` repo. Every install
path needs read access to it:
The runtime lives in the **public** `hilleywyn/framework` repo, so no token or
SSH key is required - every install path pulls it from git directly:

- **Local**: none -- the framework repo is public.
- **Docker / Railway**: none -- the image pulls the public framework automatically.
- **Local**: `pip install` resolves it over plain HTTPS.
- **Docker / Railway**: the image pulls it automatically at build time.

The `FRAMEWORK_REF` setting (default `main`) picks which git ref to install.

Expand All @@ -95,11 +94,7 @@ source .venv/bin/activate # Windows: .venv\Scripts\activate
### A.2 Install the framework, then the app deps

```bash
# HTTPS with a token:
pip install "bot-framework @ git+https://<TOKEN>@github.com/hilleywyn/framework.git@main"
# ...or over SSH if your key has access:
# pip install "bot-framework @ git+ssh://git@github.com/hilleywyn/framework.git@main"

pip install "bot-framework @ git+https://github.com/hilleywyn/framework.git@main"
pip install -r requirements.txt
```

Expand Down Expand Up @@ -271,12 +266,11 @@ On the bot service's **Variables** tab:
| `API_PORT` | `8080` |
| `CLANK_API_KEY` | optional - set to enable `/api/v2` |
| `BACKUP_MAX_PER_USER` | optional - default `50` |
| containment vars | `CLANKER_ROLE_ID`, `CLANKTANK_CHANNEL_ID`, ... if you use `.clank` |

### C.4 Set Build Variables (the private framework dep)
### C.4 Set Build Variables (the framework dep)

The image must install the framework from its private repo, so add **build**
variables (Railway passes matching variables as Docker build args):
The image installs the framework from its public repo at build time. The only
build variable you might set selects the ref:

| Build variable | Value |
|---|---|
Expand Down Expand Up @@ -306,9 +300,9 @@ contract Sojourns reads.
- **`features`** - the exact cog list to load (same list `main.py` boots from);
- **`credentials`** - the secrets to collect (`DISCORD_TOKEN`, marked secret);
- **`provision`** - that it needs a Postgres database;
- **`settings`** - grouped fields (prefix, backup cap, API key, containment
channels/roles) that Sojourns renders as a dynamic configuration UI and
pushes to the running bot via `bot.settings`.
- **`settings`** - grouped fields (prefix, API port, client id, backup cap, API
key) that Sojourns renders as a dynamic configuration UI and pushes to the
running bot via `bot.settings`.

Validate it any time:

Expand All @@ -324,10 +318,9 @@ python -m core.framework.manifest sojourns.json
2. When prompted, paste `DISCORD_TOKEN`. Sojourns stores it in its vault (the
field is declared `secret`), provisions Postgres
(`provision.database = "postgres"`) and injects `DATABASE_URL`.
3. Set any settings in the generated UI - prefix, `BACKUP_MAX_PER_USER`,
`CLANK_API_KEY`, and the containment channel/role fields. Each maps to a
control defined in the manifest and is delivered to the bot without a code
change.
3. Set any settings in the generated UI - prefix, `BACKUP_MAX_PER_USER` and
`CLANK_API_KEY`. Each maps to a control defined in the manifest and is
delivered to the bot without a code change.
4. Deploy. Sojourns runs the same Dockerfile/runtime; first boot applies
migrations.

Expand Down Expand Up @@ -402,11 +395,6 @@ the full command list see [commands.md](commands.md).
| `CLANK_API_KEY` | no | - | Enables `/api/v2`; sent as `X-API-Key`. |
| `BACKUP_MAX_PER_USER` | no | `50` | Per-user backup cap (anti-abuse). |
| `DISCORD_CLIENT_ID` | no | - | For the invite URL before login. |
| `CLANKER_ROLE_ID` | no | - | `.clank` containment role. |
| `CLANKTANK_CHANNEL_ID` | no | - | `.clank` tank channel. |
| `CLANKTANK_LOG_CHANNEL_ID` | no | - | `.clank` mod-log channel. |
| `CLANK_ESCAPE_THREAD_ID` | no | - | `.clank` escape-room thread. |
| `CLANK_ESCAPE_WAIT_MINUTES` | no | `8` | `.clank` reflection wait. |
| `REDIS_URL` | no | - | Enables framework Redis features. |
| `DB_SSL_VERIFY` | no | `0` | `1` forces full DB TLS verification. |
| `FRAMEWORK_REF` (build) | no | `main` | Framework git ref to install. |
32 changes: 13 additions & 19 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Recycler runs as a single process: a Discord bot plus an embedded REST
API / health endpoint. It needs **PostgreSQL** and a **Discord bot token**.
It is built on the private `hilleywyn/framework` package, so installs pull that
It is built on the public `hilleywyn/framework` package, so installs pull that
from git.

- [1. Create the Discord application](#1-create-the-discord-application)
Expand All @@ -19,10 +19,9 @@ from git.
-> **New Application** -> **Bot** -> **Reset Token** and copy it into
`DISCORD_TOKEN`.
2. Under **Bot -> Privileged Gateway Intents**, enable:
- **Server Members Intent** -- ban sync, member-scoped permission overwrites,
containment tracking.
- **Message Content Intent** -- message sync, chatlog archiving, containment
enforcement.
- **Server Members Intent** -- ban sync and member-scoped permission
overwrites in backups/restores.
- **Message Content Intent** -- message sync and chatlog archiving.
3. Invite the bot with the **Administrator** permission (it creates/deletes
roles, channels and webhooks). The `.about` / `.invite` commands print a
ready-made invite URL once it's running.
Expand All @@ -34,8 +33,8 @@ git clone https://github.com/hilleywyn/recycler
cd recycler
python -m venv .venv && source .venv/bin/activate

# Install the shared framework (private repo -- use a token or SSH):
pip install "bot-framework @ git+https://<TOKEN>@github.com/hilleywyn/framework.git@main"
# Install the shared framework (public repo -- no token needed):
pip install "bot-framework @ git+https://github.com/hilleywyn/framework.git@main"
pip install -r requirements.txt

cp .env.example .env # set DISCORD_TOKEN + DATABASE_URL at minimum
Expand All @@ -59,8 +58,8 @@ python -m core.framework.manifest sojourns.json

## 3. Docker

The image installs the framework at build time, so it needs a read token for
the private repo passed as a build arg:
The image installs the framework (public) from git at build time, so no token
or build secret is needed:

```bash
docker build \
Expand All @@ -83,15 +82,10 @@ the container healthcheck.
- `DISCORD_TOKEN` -- your bot token.
- `PREFIX` -- defaults to `.`.
- `CLANK_API_KEY` -- optional, enables `/api/v2`.
- Containment vars (`CLANKER_ROLE_ID`, `CLANKTANK_CHANNEL_ID`, ...) if you
use `.clank`.
4. Set **Build** variables for the private framework dependency:
- `FRAMEWORK_REF` -- branch/tag/commit to install (e.g. `main`).
4. (Optional) set the **Build** variable `FRAMEWORK_REF` to pick the framework
ref to install (branch/tag/commit; defaults to `main`).
5. Deploy. The healthcheck path is `/health`; first boot runs migrations.

> Until the framework's Components V2 changes are merged to `main`, point
> `FRAMEWORK_REF` at the feature branch that contains them.

## 5. Sojourns

Recycler ships a `sojourns.json` manifest, so the Sojourns control plane can
Expand All @@ -103,9 +97,9 @@ deploy and manage it like any other managed bot:
as a dynamic config UI.
2. Provide `DISCORD_TOKEN` when prompted; Sojourns provisions Postgres
(`provision.database = "postgres"`) and injects `DATABASE_URL`.
3. Adjust settings (prefix, backup cap, API key, containment channels) from the
Sojourns settings UI -- each field in the manifest maps to a control and is
pushed to the running bot via `bot.settings`.
3. Adjust settings (prefix, backup cap, API key) from the Sojourns settings UI
-- each field in the manifest maps to a control and is pushed to the running
bot via `bot.settings`.

The same manifest is what `main.py` boots from locally, so there is no drift
between a standalone run and a Sojourns-managed deployment.
Loading