A Discord bot that automatically assigns a **Donator** role to users who have donated to the [RetroDECK OpenCollective](https://opencollective.com/retrodeck). The bot verifies donations by cross-referencing the user's email against the OpenCollective GraphQL API.
## How It Works
1. A user runs the `/claim-donation` slash command in any channel.
2. A private modal (form) opens, prompting them to enter the email they used on OpenCollective.
3. The bot queries the OpenCollective API to check if that email belongs to a backer of the collective.
4. If a match is found, the bot assigns the Donator role and confirms.
5. If no match is found, the bot suggests checking the email or contacting a moderator.
All interactions are **ephemeral** (only visible to the user), so the email address is never exposed to other members.
| `OC_REDIRECT_URI` | OAuth redirect URI (default: `http://localhost:3000/callback`) |
| `OC_COLLECTIVE_SLUG` | The OpenCollective collective slug (e.g. `retrodeck`) |
### 5. Authenticate with OpenCollective
Run the OAuth setup script to obtain an access token:
```bash
npm install
npm run setup-oauth
```
A browser window will open asking you to authorize the app on OpenCollective with `email` and `account` scopes. After approval, the token is saved to `data/oc-token.json`. This only needs to be done once. If the token expires, re-run the command.
### 6. Register the Slash Command
Register the `/claim-donation` slash command with Discord:
```bash
npm run deploy-commands
```
This registers the command as a **guild-specific** command for fast updates. You only need to re-run this if you change the command definition.
### 7. Start the Bot
#### With Docker (recommended)
```bash
docker compose up -d
```
To view logs:
```bash
docker compose logs -f donation-bot
```
To rebuild after code changes:
```bash
docker compose up -d --build
```
#### Without Docker
```bash
npm start
```
## OpenCollective API Details
The bot uses the [OpenCollective GraphQL API v2](https://docs.opencollective.com/help/contributing/development/api) to verify donations, authenticating with an OAuth Bearer token.
**Primary strategy:** Query the collective's members (backers) and match the provided email against each member's `emails` field. The query paginates through all members automatically.
**Fallback strategy:** If the members query fails (e.g. due to permissions), the bot falls back to querying credit transactions and matching against the `fromAccount.emails` field.
Both strategies perform **case-insensitive** email matching.
The OAuth app requires the `email` and `account` scopes.
## Edge Cases
| Scenario | Behavior |
|---|---|
| User already has the Donator role | Skips the API call and tells the user they already have it |
| Email not found | Suggests double-checking the email or contacting a moderator |
| Guest/anonymous donation | Cannot be verified automatically; the bot mentions this possibility |
| OpenCollective API error | Responds with a generic error and logs the details to the console |
| OAuth token expired/invalid | Bot logs a clear error; re-run `npm run setup-oauth` to re-authenticate |
| No token file present | Bot refuses to start with instructions to run `npm run setup-oauth` |
| Discord rate limits | Handled automatically by Discord.js |
## License
This project is licensed under the [MIT License](LICENSE).