Skip to content
Artists4Palestine Bot

Artists4Palestine Bot

Artists4Palestine Bot (hereinafter the “Application”) is a Discord bot designed to facilitate the Art for Palestine charity event. The Application serves as an automated bridge between Airtable, Trello, and Discord to manage art commission requests, artist assignments, delivery tracking, and support tickets for the charity event.

This section is for those interacting with a live instance of the Application.

The Art for Palestine Bot is available on the Art for Palestine Discord server. Once you join the server, you will automatically be assigned the member role and can begin interacting with the bot’s features.

Art commission requests are submitted through an Airtable form (external to Discord). Once submitted, your request will automatically appear in the designated commission channel where artists can view and claim it. Your request should include:

  • Your name
  • Preferred contact method (Email, Discord, or Twitter)
  • Your contact handle/username
  • Description of the art you would like drawn
  • Reference images (optional)
  • Any additional notes or requirements

As an artist, you can claim art commissions by reacting to commission posts in the art commission channel (ID: 1172568787330019340).

Important Notes:

  • You can only claim up to 2 commissions at a time
  • Claiming a commission creates a private thread for coordination
  • The thread will include your mention and the requester’s information

Once you’ve completed the artwork:

  1. Post the completed artwork as an image attachment in the private thread
  2. The bot will automatically forward your submission to the distribution channel
  3. Your thread will be archived
  4. The commission will be removed from your active count

The bot sends automated reminders to help manage workload:

  • Unclaimed Art Reminders: Sent Monday, Wednesday, and Friday at 9:00 AM
  • Unfinished Art Reminders: Sent Saturday at 9:00 AM

Distribution team members can claim distribution tasks by reacting to posts in the distribution channel (ID: 1173061747737903315). Similar to art commissions:

  • Maximum 2 concurrent distributions per member
  • Creates a private coordination thread
  • Includes recipient and artwork details

To confirm a distribution:

  1. Deliver the artwork to the recipient
  2. Post a confirmation image in the private thread
  3. The bot will close and archive the thread
  • Unclaimed Distribution Reminders: Sent Monday, Wednesday, and Friday at 9:00 AM
  • Unfinished Distribution Reminders: Sent Saturday at 9:00 AM

Users can open support tickets using the ticket button in the designated ticket channel:

  1. Click the “Open Ticket” button
  2. Fill out the modal form with your issue description
  3. A private thread will be created for your ticket
  4. Support team members will be notified
  • For Users: Describe your issue in the private thread and wait for a support team member to claim it
  • For Support Team: Click “Claim Ticket” to add your mention and take responsibility for the issue
  • Conversations in the ticket thread are automatically logged

Support team members can close tickets using the “Close Ticket” button:

  • A log file of the entire conversation will be generated
  • The log is sent to the ticket archive channel
  • The thread is closed and archived

Moderation team members with the support role (ID: 1173582640843063366) have access to these commands:

Temporarily timeout a user.

Parameters:

  • user_id: The Discord user ID to mute
  • duration: Duration with unit (e.g., 30m, 2h, 1d)
    • Supported units: s (seconds), m (minutes), h (hours), d (days)
  • reason: Explanation for the timeout

Example:

!mute 123456789012345678 1h Spam in general channel

Remove a timeout from a user.

Parameters:

  • user_id: The Discord user ID to unmute
  • reason: Explanation for removing the timeout

Example:

!unmute 123456789012345678 Appeal accepted

The bot automatically fetches and posts news updates from Al Jazeera’s Palestine live blog:

  • Updates are checked every 10 minutes
  • New articles are posted to the news channel
  • Prevents duplicate posts using article ID tracking

If you encounter issues or have questions:

  • Open a support ticket in the Discord server
  • Report bugs in the #bug-reports forum channel on our Discord community
  • Contact via the chat server or email at [email protected]

This section is for those interested in running their own instance of the Application.

Before running your own instance, ensure you have:

  • Node.js: Version 22 (specified in package.json)
  • pnpm: Version 10 (package manager)
  • Discord Bot Account: With a bot token and appropriate permissions
  • Airtable Account: With API access
  • Discord Webhooks: Multiple webhooks configured for different channels

Your Discord bot requires the following Gateway Intents:

  • GuildMessages: Access to guild message events
  • Guilds: Access to guild/server data
  • GuildMessageReactions: Access to message reaction events
  • MessageContent: Privileged intent for full message content access
  • GuildMembers: Access to member data

Create a .env file in the project root with the following variables:

Terminal window
# Discord Bot Configuration
TOKEN=your_discord_bot_token
# Discord Webhooks
DEBUG=https://discord.com/api/webhooks/... # Debug/error logging
COMM=https://discord.com/api/webhooks/... # Commission notifications
DIST=https://discord.com/api/webhooks/... # Distribution notifications
NEWS=https://discord.com/api/webhooks/... # Palestine news updates
TICKET=https://discord.com/api/webhooks/... # Ticket logs
# Airtable Configuration
AIRTABLE_KEY=your_airtable_api_key
AIRTABLE_BASE_ID=your_base_id
AIRTABLE_TABLE_ID=your_table_name
# Server Configuration (optional)
NODE_ENV=production # Set to enable HTTPS server on port 10443

Several IDs are hardcoded in the src/config/ directory and must be updated for your server:

File: src/config/Tickets.ts

  • guildId: Your Discord server ID
  • supportRole: Your support team role ID

File: src/config/Webhooks.ts

  • artChannelId: Channel for art commission posts
  • distributionChannelId: Channel for distribution posts

File: src/config/Trello.ts

  • Trello board, list, and label IDs (if using Trello integration)
  1. Clone the repository:
Terminal window
git clone https://github.com/spellware/a4p-bot.git
cd a4p-bot
  1. Install dependencies:
Terminal window
pnpm install
  1. Configure environment variables (see section 2.3)

  2. Build the TypeScript code:

Terminal window
pnpm build

Start the bot in production mode:

Terminal window
pnpm start

This will:

  • Load environment variables from .env
  • Execute the compiled JavaScript from prod/index.js
  • Connect to Discord
  • Initialise event listeners and scheduled tasks
  • Start the HTTP server on port 10080 (HTTP) or 10443 (HTTPS if NODE_ENV=production)

For development and testing:

Linting:

Terminal window
pnpm lint

Runs ESLint and Prettier to check code style and formatting.

Testing:

Terminal window
pnpm test

Runs Mocha test suite with TypeScript support (timeout: 10 seconds).

Building:

Terminal window
pnpm build

Compiles TypeScript files from src/ to JavaScript in prod/.

a4p-bot/
├── src/
│ ├── index.ts # Main entry point, bot initialization
│ ├── config/ # Configuration files (channels, webhooks, Trello)
│ ├── events/ # Discord event handlers
│ ├── modules/ # Core business logic
│ │ ├── buttons/ # Button interaction handlers
│ │ ├── messages/ # Message processing logic
│ │ ├── modals/ # Modal form handlers
│ │ └── reminders/ # Scheduled reminder tasks
│ ├── interface/ # TypeScript interfaces and types
│ ├── server/ # Express HTTP server
│ └── utils/ # Utility functions
├── prod/ # Compiled JavaScript output
├── logs/ # Ticket conversation logs (runtime)
└── .env # Environment variables (not committed)

Discord Bot (src/index.ts:1)

  • Initialises Discord.js client with required intents
  • Registers event listeners for messages, reactions, interactions, and member joins
  • Creates webhook clients for different notification types
  • Starts HTTP server for monitoring

Event Handlers (src/events/)

  • onReady.ts:1: Initialises scheduled jobs, caches messages, sends startup notification
  • onMessageCreate.ts:1: Handles commands (!mute, !unmute) and ticket message logging
  • onInteractionCreate.ts:1: Routes button clicks and modal submissions
  • onReactionAdd.ts:1: Manages art commission and distribution claims
  • onMemberAdd.ts:1: Auto-assigns member role to new joiners

Scheduled Tasks (node-schedule)

  • Runs every 60 minutes: Check Airtable for new form submissions
  • Runs every 10 minutes: Fetch and post Palestine news updates
  • Monday/Wednesday/Friday 9 AM: Send unclaimed art and distribution reminders
  • Saturday 9 AM: Send unfinished art and distribution reminders

Ticket System (src/modules/)

  • buttons/ticketOpen.ts:1: Opens modal form for ticket creation
  • modals/handleTicketModal.ts:1: Creates private thread and initialises log file
  • buttons/ticketClaim.ts:1: Adds support member’s mention to thread
  • buttons/ticketClose.ts:1: Generates log file, sends to archive, closes thread
  • logTicketMessage.ts:1: Appends messages to log file in real-time
  • generateLogs.ts:1: Reads log file and creates Discord attachment

Airtable Integration (src/modules/checkAirtableRecords.ts:1)

  • Polls Airtable API for new form submissions
  • Tracks latest record ID to avoid duplicate processing
  • Extracts form fields (name, contact info, request details, reference images)
  • Posts formatted messages to commission channel via webhook

News Feed (src/utils/getNewsFeed.ts:1)

  • Fetches latest articles from Al Jazeera Palestine live blog via GraphQL
  • Tracks last posted article ID to prevent duplicates
  • Sends new updates to news webhook channel
User Action (Discord) → Event Listener → Event Handler → Module Logic
↓
External API / Webhook Call
↓
Response/Notification
↓
Logging (Winston + Debug Webhook)
Scheduled Task → External API (Airtable/Al Jazeera) → Parse Response
↓
Send to Discord Webhook
↓
Update Cache/State

While Prisma ORM is installed as a dependency, the Application does not currently use a database. All state is managed in memory using cache objects attached to the extended Discord client:

  • bot.ticketLogs: Maps channel IDs to log file message IDs
  • bot.lastArticle: Tracks the most recent news article ID

Ticket conversation logs are temporarily stored as text files in the logs/ directory and deleted after being sent to the archive channel.

Endpoint: https://api.airtable.com/v0/{BASE_ID}/{TABLE_ID}

Method: GET

Headers:

Authorization: Bearer {AIRTABLE_KEY}

Query Parameters:

  • maxRecords=100: Limit results
  • sort[0][field]=Created: Sort by creation time
  • sort[0][direction]=desc: Newest first

Response Structure (src/interface/AirtableResponse.ts:1):

{
records: [{
id: string;
createdTime: string;
fields: {
Name: string;
"Contact Method": "Email" | "Discord" | "Twitter";
Handle: string;
"What would you like us to draw?": string;
"Anything Else?": string;
Reference?: { url: string }[];
}
}]
}

Endpoint: https://www.aljazeera.com/graphql (inferred from code)

Operations:

  • SingleLiveBlogChildrensQuery: Fetch live blog structure
  • LiveBlogUpdateQuery: Fetch latest blog updates

The bot extracts article content and posts to the news webhook channel.

The Application includes a minimal Express server (src/server/serve.ts:1):

Endpoints:

  • GET / - Returns HTML landing page with project information

Ports:

  • HTTP: 10080 (default)
  • HTTPS: 10443 (when NODE_ENV=production)

Note: The server file includes a TODO comment suggesting deletion after Trello integration is complete.

The Application uses Winston for structured logging (src/utils/logHandler.ts:1):

Log Levels:

  • info: General information (bot startup, scheduled task execution)
  • error: Error conditions (API failures, Discord errors)

Log Format:

[TIMESTAMP] LEVEL: MESSAGE

Log Destinations:

  • Console (stdout)
  • Debug webhook (errors only)

All errors are caught and:

  1. Logged to console via Winston
  2. Sent to the debug webhook with error details
  3. Presented to users with friendly messages (when applicable)

Example error flow (src/modules/checkAirtableRecords.ts):

try {
// API call
} catch (error) {
logHandler.error("Error message", error);
debugHook.send({ content: "Error details" });
}
  • Message Caching: Fetches messages in batches of 100 to reduce API calls
  • Duplicate Prevention: Tracks latest Airtable record ID and news article ID
  • Rate Limiting:
    • Airtable: 60-minute polling interval
    • News API: 10-minute polling interval
  • Concurrent Limits: Artists/distributors limited to 2 active tasks each
  • Environment Variables: Sensitive tokens stored in .env (not committed)
  • Role Verification: Commands require support role for execution (src/events/onMessageCreate.ts:1)
  • Webhook Validation: Checks webhook existence before use (src/utils/isValidWebhook.ts:1)
  • Privileged Intents: Requires approval from Discord for MessageContent intent

Bot doesn’t start:

  • Verify TOKEN is valid in .env
  • Check Node.js version is 22
  • Ensure all dependencies are installed: pnpm install

Webhooks not working:

  • Verify webhook URLs are valid and accessible
  • Check webhook permissions in Discord channel settings
  • Use isValidWebhook utility for validation

Airtable integration failing:

  • Verify AIRTABLE_KEY, AIRTABLE_BASE_ID, and AIRTABLE_TABLE_ID
  • Check Airtable API permissions
  • Review Airtable API rate limits

Scheduled tasks not running:

  • Check server timezone matches expected cron schedule
  • Review onReady.ts for cron expression syntax
  • Check debug webhook for error messages

Ticket logs not saving:

  • Verify write permissions for logs/ directory
  • Check disk space availability
  • Review console for Winston logging errors

This section is for expansions to our legal policies specific to the Application.

The Application is subject to our global Terms of Service, which can be found at:

https://docs.spellware.dev/#/terms

The Application’s privacy practices are governed by our global Privacy Policy, which can be found at:

https://docs.spellware.dev/#/privacy

The Application collects and temporarily stores the following data:

Discord Data:

  • User IDs (for role assignment, moderation, and ticket management)
  • Message content in ticket threads (logged to text files)
  • Reaction events (for tracking commission/distribution claims)
  • Member join events (for auto-role assignment)

Airtable Data:

  • Form submission data (name, contact method, handle, art request details)
  • Reference images (URLs only)
  • Submission timestamps and record IDs

Temporary Storage:

  • Ticket conversation logs (stored as text files, deleted after archival)
  • In-memory cache for article IDs and ticket log mappings

Data Retention:

  • Ticket logs: Deleted immediately after being sent to the archive channel
  • Cached data: Cleared on bot restart
  • Airtable record IDs: Stored in memory to prevent duplicate processing

Third-Party Services: The Application integrates with:

  • Discord (message delivery and bot functionality)
  • Airtable (form submission retrieval)
  • Al Jazeera (public news feed)
  • Trello (configuration only, no active integration)

Users should review the privacy policies of these services for their data handling practices.

This software is licensed under our global software licence:

https://docs.spellware.dev/#/license

Copyright: Held by Naomi Carrigan

License Type: Proprietary (see link above for full terms)

All contributors and users must adhere to our Code of Conduct:

See: CODE_OF_CONDUCT.md in the repository root

Security vulnerabilities should be reported according to our Security Policy:

See: SECURITY.md in the repository root

For security concerns, please contact us through:

The Application is provided for the specific purpose of facilitating the Art for Palestine charity event. While we strive for reliability and accuracy, the Application is provided “as-is” without warranties of any kind.

Moderation Actions: The Application includes moderation features (mute/unmute). Server administrators and support team members are responsible for using these features in accordance with Discord’s Terms of Service and Community Guidelines.

External Content: The Application fetches and displays news content from Al Jazeera. We do not control or endorse this content and are not responsible for its accuracy or appropriateness.

Charity Event: This Application is specifically designed for the Art for Palestine charity event. For questions about the event itself, please visit https://art4palestine.org or contact the event organisers.


This section is for documentation related to contributing to the Application’s codebase.

Our complete contributing guidelines can be found at:

https://docs.spellware.dev/#/contributing

  1. Fork the Repository

    Terminal window
    # Visit GitHub and fork the repository to your account
  2. Clone Your Fork

    Terminal window
    git clone https://github.com/YOUR_USERNAME/a4p-bot.git
    cd a4p-bot
  3. Install Dependencies

    Terminal window
    pnpm install
  4. Set Up Environment

    • Create a .env file (see section 2.3)
    • Create a test Discord server and bot
    • Configure test webhooks
  5. Create a Branch

    Terminal window
    git checkout -b feature/your-feature-name

The project enforces code style through automated tooling:

ESLint Configuration:

  • Extends @nhcarrigan/eslint-config
  • Maximum warnings allowed: 0 (all warnings treated as errors)
  • Checks both src/ and test/ directories

Prettier Configuration:

  • Extends @nhcarrigan/prettier-config
  • Enforces consistent formatting

TypeScript Configuration:

  • Extends @nhcarrigan/typescript-config
  • Strict type checking enabled
  • Root directory: ./src
  • Output directory: ./prod

Running Linters:

Terminal window
pnpm lint

Auto-Fixing Issues:

Terminal window
# Fix ESLint issues
npx eslint src test --fix
# Fix Prettier issues
npx prettier src test --write

The project uses Mocha and Chai for testing:

Test Structure:

  • Test files: test/**/*.spec.ts
  • Test style: TDD (Test-Driven Development)
  • Timeout: 10 seconds per test
  • Type: TypeScript (via ts-mocha)

Running Tests:

Terminal window
pnpm test

Writing a Test:

import { expect } from "chai";
import { suite, test } from "mocha";
suite("Feature Name", () => {
test("should do something", () => {
// Arrange
const input = "test";
// Act
const result = myFunction(input);
// Assert
expect(result).to.equal("expected");
});
});

JSDoc Comments: All functions, classes, and complex logic should include JSDoc comments:

/**
* Brief description of the function.
*
* @param {Type} paramName - Parameter description
* @returns {Type} Return value description
*/
function myFunction(paramName: Type): Type {
// Implementation
}

Inline Comments: Use inline comments for complex logic:

// Explanation of why this approach is used
const result = complexOperation();

Commit Messages: Follow conventional commit format:

  • feat: Add new feature
  • fix: Resolve bug in ticket system
  • docs: Update README
  • chore: Update dependencies
  • refactor: Restructure event handlers
  • test: Add tests for webhook validation
  1. Ensure All Checks Pass:

    • Run pnpm lint (must pass with 0 warnings)
    • Run pnpm test (all tests must pass)
    • Run pnpm build (must compile without errors)
  2. Create Pull Request:

    • Use a descriptive title
    • Provide detailed description of changes
    • Reference any related issues
    • Include screenshots/videos for UI changes
  3. Code Review:

    • Address reviewer feedback
    • Make requested changes
    • Re-request review after updates
  4. Merge:

    • Maintainers will merge approved PRs
    • Squash and merge is preferred for clean history

The project welcomes contributions in these areas:

Feature Enhancements:

  • Trello integration completion (see TODO in src/server/serve.ts)
  • Database integration using Prisma (currently installed but unused)
  • Additional moderation commands
  • Enhanced reporting and analytics

Bug Fixes:

  • Report bugs in the #bug-reports forum channel on our Discord community
  • Include reproduction steps and environment details
  • PRs to fix bugs are highly appreciated

Documentation:

  • Improve code comments
  • Expand user guides
  • Add troubleshooting sections
  • Create video tutorials

Testing:

  • Increase test coverage
  • Add integration tests
  • Create test utilities

Performance:

  • Optimise message caching
  • Reduce webhook API calls
  • Improve scheduled task efficiency

Recommended Tools:

  • Editor: VS Code (or any TypeScript-compatible editor)
  • Extensions:
    • ESLint
    • Prettier
    • TypeScript and JavaScript Language Features
  • Node Version Manager: nvm (for managing Node.js versions)
  • Package Manager: pnpm 10

Discord Developer Portal Setup:

  1. Create a new application at https://discord.com/developers/applications
  2. Create a bot user
  3. Enable required privileged intents: Message Content, Server Members
  4. Generate and save bot token
  5. Invite bot to test server with required permissions

Webhook Setup:

  1. Create test channels in your Discord server
  2. Go to channel settings → Integrations → Webhooks
  3. Create webhooks for: debug, comm, dist, news, ticket
  4. Copy webhook URLs to .env

Console Logging: The project uses Winston for logging. To add debug logs:

import { logHandler } from "./utils/logHandler.js";
logHandler.info("Informational message");
logHandler.error("Error message", error);

Discord Debug Webhook: Error details are automatically sent to the debug webhook. Check this channel for runtime errors.

VS Code Launch Configuration: Create .vscode/launch.json:

{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Bot",
"runtimeArgs": ["-r", "dotenv/config"],
"program": "${workspaceFolder}/prod/index.js",
"preLaunchTask": "npm: build",
"outFiles": ["${workspaceFolder}/prod/**/*.js"]
}
]
}

Adding a New Command:

  1. Add handler logic to src/events/onMessageCreate.ts
  2. Implement command functionality in src/modules/
  3. Update user documentation (section 1)
  4. Add tests in test/

Adding a New Button:

  1. Create button handler in src/modules/buttons/
  2. Export handler in button module
  3. Register handler in src/events/onInteractionCreate.ts
  4. Update user documentation

Adding a New Event Listener:

  1. Create event handler in src/events/
  2. Register listener in src/index.ts
  3. Add error handling
  4. Update technical documentation

Modifying Configuration:

  1. Update relevant file in src/config/
  2. Document changes in section 2.4
  3. Update environment variable documentation if needed

(For maintainers)

  1. Version Bump:

    • Update version in package.json
    • Follow semantic versioning (MAJOR.MINOR.PATCH)
  2. Changelog:

    • Document all changes since last release
    • Include breaking changes, features, and bug fixes
  3. Testing:

    • Run full test suite
    • Test in staging environment
    • Verify all integrations work
  4. Deployment:

    • Build production bundle: pnpm build
    • Deploy to production server
    • Monitor logs for issues
  5. Tag Release:

    Terminal window
    git tag -a v2.0.0 -m "Release version 2.0.0"
    git push origin v2.0.0

If you have questions about contributing:

We review pull requests as soon as possible and appreciate all contributions to the project.