Skip to content
Callista

Callista

Callista (hereinafter the “Application”) is a Discord bot that allows users to bookmark messages so they can find them later. The Application provides a simple interface for saving message links via DM, with optional note-taking capabilities to help organise and remember why messages were bookmarked.

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

Callista is available as a Discord user-installable application. You can add Callista to your Discord account by visiting: https://discord.com/oauth2/authorize?client_id=1391494389477412906

To bookmark a message:

  1. Right-click (or long-press on mobile) any message in a guild or private channel
  2. Navigate to Apps → Bookmark
  3. Callista will send you a DM containing the message link

Note: You must have Direct Messages enabled to receive bookmarks. If you receive an error, check your Discord privacy settings.

Each bookmark can have an associated note to help you remember context:

  1. When you receive a bookmark DM, click the Add Note button
  2. A modal will appear asking “What would you like to note down?”
  3. Enter your note (1-1000 characters)
  4. Your note will be appended to the bookmark message

To edit an existing note on a bookmark:

  1. Click the Edit Note button on the bookmark message
  2. Update the note text in the modal
  3. The bookmark message will be updated with your new note

To delete a bookmark from your DMs:

  1. Right-click the bookmark message in your DM with Callista
  2. Navigate to Apps → Delete
  3. The bookmark message will be deleted

Note: The Delete command only works in DMs with Callista (bot DM context).

Each bookmark message includes convenient buttons:

  • Add Note/Edit Note: Add or modify notes on your bookmarks
  • Join our Discord: Link to the support server (https://chat.spellware.dev)
  • Donate: Premium button for supporting the bot developer
CommandTypeContextDescription
BookmarkMessage Context MenuGuilds, Private ChannelsSends you a DM with the message link
DeleteMessage Context MenuBot DMsDeletes the bookmark message

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

  • Node.js (compatible with version specified in package.json)
  • pnpm 10.17.1 or higher (specified in packageManager field)
  • A Discord Bot Token
  • 1Password CLI (op) for production environment variables (or modify start script)

Runtime Dependencies:

  • discord.js (14.22.1): Discord API wrapper
  • @nhcarrigan/logger (1.0.0): Logging utility
  • fastify (5.6.1): Web server for health monitoring

Development Dependencies:

  • TypeScript (5.9.2)
  • ESLint with @nhcarrigan configuration
  • tsx for development execution
  1. Clone the repository:

    Terminal window
    git clone <repository-url>
    cd callista
  2. Install dependencies:

    Terminal window
    pnpm install
  3. Create a production environment file (prod.env) with the following:

    BOT_TOKEN=your_discord_bot_token
  4. Register Discord commands by running:

    Terminal window
    node commandData.js

    Copy the output JSON and register it via the Discord Developer Portal or API.

Compile TypeScript to JavaScript:

Terminal window
pnpm run build

Output is generated in the prod/ directory.

Production:

Terminal window
pnpm start

This uses 1Password CLI to inject environment variables. Modify the start script in package.json if using a different secrets management solution.

Development:

Terminal window
tsx src/index.ts

Project Structure:

src/
├── index.ts # Main entry point, Discord client setup
├── server/
│ └── serve.ts # Fastify web server for health checks (port 6111)
├── interactions/
│ ├── bookmark.ts # Bookmark command handler
│ └── deleteCmd.ts # Delete command handler
├── buttons/
│ ├── addNote.ts # Add note button definition
│ ├── editNote.ts # Edit note button definition
│ ├── discord.ts # Discord invite button
│ └── donate.ts # Premium donation button
├── modals/
│ └── note.ts # Note input modal definition
└── utils/
└── logger.ts # Logger configuration

Component Interactions:

  1. Discord Client (index.ts:22-24): Initialised with no intents (interaction-only bot)
  2. Interaction Handler (index.ts:27-81): Routes interactions to appropriate handlers
  3. Web Server (server/serve.ts:54-79): Runs on port 6111 for health monitoring
  4. Message Context Commands: Registered via commandData.js with user-install integration

The bot requires the following Discord application settings:

Intents: None (the bot uses no gateway intents)

Integration Types:

  • User Install (ApplicationIntegrationType.UserInstall)

Interaction Contexts:

  • Bookmark: Guilds and Private Channels
  • Delete: Bot DMs only

Commands to Register:

[
{
"type": 3,
"name": "Bookmark",
"contexts": [0, 2],
"integration_types": [1]
},
{
"type": 3,
"name": "Delete",
"contexts": [1],
"integration_types": [1]
}
]
VariableRequiredDescription
BOT_TOKENYesDiscord bot token for authentication

The Application exposes a web server on port 6111:

  • Endpoint: GET /
  • Response: HTML page with bot information
  • Purpose: Health monitoring and bot information display

Logging is handled via @nhcarrigan/logger with the following log levels:

  • debug: Informational messages (bot online, server started)
  • error: Error conditions (server failures, etc.)

Logs are written from utils/logger.ts:1.

The Application implements error handling for:

  • Failed DM delivery (user has DMs disabled) - interactions/bookmark.ts:43-46
  • Missing message context for modals - index.ts:46-52
  • Server startup failures - server/serve.ts:66-77
  1. Port Configuration: The web server runs on port 6111 by default
  2. Secrets Management: The default start script uses 1Password CLI
  3. Build Output: Compiled JavaScript is output to prod/ directory
  4. Process Management: Consider using PM2, systemd, or Docker for production

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

This Application is licensed under Naomi’s Public Licence as indicated in the source file headers.

Copyright held by SpellWare / Naomi Carrigan.

Full licence text available at: https://docs.spellware.dev/#/license

Privacy policy document: PRIVACY.md in the repository root.

Data Collection: The Application processes the following user data:

  • Discord User IDs (for DM delivery)
  • Message URLs (bookmarked messages)
  • User-provided notes (stored in Discord messages)

Data Storage: All data is stored within Discord’s infrastructure via message content. The Application does not maintain a separate database.

Data Retention: Data persists as long as users keep their DM messages. Users can delete bookmarks at any time using the Delete command.

Terms of service document: TERMS.md in the repository root.

Security policy document: SECURITY.md in the repository root.

For security concerns, please follow the reporting procedures outlined in the security policy.

Code of conduct document: CODE_OF_CONDUCT.md in the repository root.

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

Contributing guidelines document: CONTRIBUTING.md in the repository root.

Before contributing, please review:

  1. The Code of Conduct (CODE_OF_CONDUCT.md)
  2. The Contributing Guidelines (CONTRIBUTING.md)
  3. The License terms (LICENSE.md)
  1. Fork the repository
  2. Clone your fork
  3. Install dependencies: pnpm install
  4. Create a Discord application and bot at https://discord.com/developers/applications
  5. Copy your bot token to a prod.env file (or use environment variables directly)
  6. Run the development server: tsx src/index.ts

The project uses:

  • TypeScript (5.9.2) with strict type checking
  • ESLint with @nhcarrigan/eslint-config (5.2.0)
  • TypeScript Config from @nhcarrigan/typescript-config (4.0.0)

Run linting with:

Terminal window
pnpm run lint

Note: The lint script uses --max-warnings 0, so warnings are treated as errors.

  • All source files include copyright headers with @copyright, @license, and @author tags
  • Some ESLint rules are selectively disabled with comments (e.g., index.ts:26)
  • The project uses ESM (module) syntax ("type": "module" in package.json)

Currently, the project has a placeholder test script:

Terminal window
pnpm test

This exits with code 0 (no tests configured yet).

  1. Create a feature branch from main
  2. Make your changes following the code standards
  3. Run pnpm run lint to ensure code quality
  4. Run pnpm run build to verify the build succeeds
  5. Submit a pull request with a clear description of changes
  6. Address any review feedback

Adding a new button:

  1. Create a new file in src/buttons/ exporting a ButtonBuilder
  2. Import and add the button to the relevant ActionRowBuilder in index.ts
  3. Add handler logic in handleInteraction if the button requires custom behaviour

Adding a new command:

  1. Create a handler in src/interactions/
  2. Add the command definition to commandData.js
  3. Run node commandData.js and register the output with Discord
  4. Add the command routing logic to handleInteraction in index.ts

Adding a new modal:

  1. Create modal definition in src/modals/
  2. Add modal handling logic in the isModalSubmit section of handleInteraction
  3. Link the modal to a button or command that calls showModal()
  • Main Branch: main
  • Package Manager: pnpm (lockfile version: 10.17.1)
  • CI/CD: Configuration available in .github/ directory
  • Editor Configuration: VSCode settings in .vscode/

For questions or support:

Please report bugs in the #bug-reports forum channel and request features in the #feature-requests forum channel of our Discord community.

Include:

  • Clear description of the issue or feature
  • Steps to reproduce (for bugs)
  • Expected vs actual behaviour
  • Environment details (Discord client version, etc.)