DiscordRDA

Modern, scalable Ruby library for Discord bot development with full Slash Commands and Component V2 support

License: MIT Ruby Version

DiscordRDA (Ruby Development API) is a high-performance Ruby library for building Discord bots with modern async patterns, comprehensive Slash Command support, Component V2 architecture, and enterprise-grade scalability features.

The library now exposes a much broader Discord REST surface than the original quick-start examples show, including thread lifecycle helpers, webhook message management, invite creation, guild widget and onboarding endpoints, and current-user OAuth2 convenience methods.

Features

Core Capabilities

  • ⚑ Async Runtime: Built on Ruby 3.0+ Fiber scheduler for true concurrency
  • 🏭 Factory Pattern: Clean entity creation with EntityFactory
  • πŸ“‘ Auto Sharding: Automatic and manual sharding with zero-downtime resharding
  • πŸ’Ύ Pluggable Cache: Memory or Redis backends with pattern-based invalidation
  • πŸ”Œ Plugin System: Extensible architecture for commands and features
  • πŸ“Š Rate Limiting: Advanced Discord API rate limit handling with queue management
  • 🎯 Full Documentation: Every API is fully documented

Slash Commands & Interactions

  • Full Slash Command API: Create, edit, delete global and guild commands
  • Context Menu Commands: User and Message context menu support
  • Autocomplete: Real-time autocomplete with dynamic choices
  • Modals: Custom modal forms with text inputs
  • Component V2: Latest Discord components (buttons, selects, containers)

Enterprise Features

  • Instant Restarts: Exec-based restart flow that preserves gateway resume state
  • Zero-Downtime Resharding: Add shards without stopping the bot
  • Hot Reload: File system event-based code reloading
  • Session Transfer: Migrate guilds between shards seamlessly
  • REST Proxy Support: Horizontal scaling with proxy servers
  • State Preservation: Maintain sessions across reloads
  • Execution Supervision: Command timeouts, concurrency limits, and failure circuit breaking

Extended REST Coverage

  • Threads: Create, join, leave, archive, and manage thread members
  • Webhooks: Fetch, mutate, and edit webhook-owned messages
  • Guild Admin: Invites, prune, widget, vanity URL, welcome screen, onboarding
  • Current User: DM creation, guild listing, connections, role connections
  • Persistence: Built-in ActiveRecord bootstrap and migration helpers

Installation

Add this line to your application's Gemfile:

gem 'discord_rda'

And then execute:

bundle install

Or install it yourself as:

gem install discord_rda

Quick Start

require 'discord_rda'

bot = DiscordRDA::Bot.new(
  token: ENV['DISCORD_TOKEN'],
  intents: [:guilds, :guild_messages, :message_content]
)

bot.on(:message_create) do |event|
  if event.content == '!ping'
    event.message.respond(content: 'Pong!')
  end
end

bot.run

Table of Contents

Slash Commands

DiscordRDA provides a comprehensive DSL for building Slash Commands:

Basic Slash Command

bot.slash('hello', 'Say hello') do |cmd|
  cmd.string('name', 'Your name', required: true)
  cmd.handler do |interaction|
    name = interaction.option('name')
    interaction.respond(content: "Hello, #{name}!")
  end
end

Command with Multiple Options

bot.slash('ban', 'Ban a user from the server') do |cmd|
  cmd.user('user', 'User to ban', required: true)
  cmd.string('reason', 'Reason for ban')
  cmd.integer('days', 'Days of messages to delete')
  cmd.default_permissions(:ban_members)

  cmd.handler do |interaction|
    user = interaction.option('user')
    reason = interaction.option('reason') || 'No reason provided'
    interaction.respond(content: "Banned #{user.username}", ephemeral: true)
  end
end

Guild-Specific Commands

bot.slash('admin', 'Admin only command', guild_id: '123456789') do |cmd|
  cmd.default_permissions(:administrator)
  cmd.handler do |interaction|
    interaction.respond(content: 'Admin command executed!', ephemeral: true)
  end
end

Context Menu Commands

# User context menu
bot.context_menu(type: :user, name: 'High Five') do |interaction|
  user = interaction.target_user
  interaction.respond(content: "High-fived #{user.username}!")
end

REST Helpers

# Create a thread from a message
thread = bot.start_thread_from_message(
  channel_id,
  message_id,
  name: 'incident-review',
  auto_archive_duration: 1440
)

# Create a channel invite with the DSL builder
invite = bot.create_channel_invite(channel_id) do |builder|
  builder.max_age(3600).max_uses(5).temporary
end

# Edit a webhook message
bot.edit_webhook_message(webhook_id, webhook_token, message_id, content: 'Updated from DiscordRDA')

Resilience

bot.slash('rebuild', 'Run a risky rebuild task') do |cmd|
  cmd.timeout(10)
  cmd.max_concurrency(1)
  cmd.circuit_breaker(failures: 3, cooldown: 120)

  cmd.handler do |interaction|
    interaction.respond(content: 'Started safely', ephemeral: true)
  end
end

# For memory-bound or untrusted work, use an isolated subprocess:
result = bot.supervisor.run_isolated(
  ruby_code: "puts({ ok: true }.to_json)",
  timeout_seconds: 5,
  memory_limit_mb: 128
)

ActiveRecord

bot.enable_active_record(database_url: ENV['DATABASE_URL'])

class GuildSetting < DiscordRDA::Record
end

bot.active_record.migrate

Components

Button Components

interaction.respond(content: 'Click the button!') do |builder|
  builder.components do |row|
    row.button(style: :primary, label: 'Click Me', custom_id: 'click_button')
    row.button(style: :danger, label: 'Delete', custom_id: 'delete_button')
    row.button(style: :link, label: 'Docs', url: 'https://example.com')
  end
end

# Handle button clicks
bot.on(:button_click) do |interaction|
  interaction.respond(content: 'Button clicked!', ephemeral: true)
end

Select Menus

interaction.respond(content: 'Select your roles:') do |builder|
  builder.components do |row|
    row.string_select(
      custom_id: 'role_select',
      placeholder: 'Choose roles',
      options: [
        { label: 'Admin', value: 'admin' },
        { label: 'Mod', value: 'mod' }
      ]
    )
  end
end

Interactions

Deferred Responses

bot.slash('slow', 'A slow command') do |cmd|
  cmd.handler do |interaction|
    interaction.defer(ephemeral: true)
    # Do slow work
    sleep(5)
    interaction.edit_original(content: 'Done!')
  end
end

Modals

bot.slash('feedback', 'Submit feedback') do |cmd|
  cmd.handler do |interaction|
    interaction.modal(custom_id: 'feedback_modal', title: 'Send Feedback') do |modal|
      modal.short(custom_id: 'subject', label: 'Subject', required: true)
      modal.paragraph(custom_id: 'message', label: 'Your feedback', required: true)
    end
  end
end

# Handle modal submission
bot.on(:modal_submit) do |interaction|
  subject = interaction.modal_value('subject')
  message = interaction.modal_value('message')
  interaction.respond(content: 'Thank you!', ephemeral: true)
end

Documentation

Examples

See the examples directory:

  • basic_bot.rb - Simple echo bot
  • sharded_bot.rb - Multi-shard example
  • plugin_bot.rb - Custom plugin demonstration
  • slash_command_bot.rb - Slash command handling

Architecture

DiscordRDA follows a layered architecture designed for scalability:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Application Layer                       β”‚
β”‚  (Your bot code, commands, event handlers, plugins)         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                      Interaction Layer                       β”‚
β”‚  (Slash commands, components, modals, autocomplete)         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                      Entity Layer                            β”‚
β”‚  (User, Message, Guild, Channel - Factory Pattern)         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                      Event System                            β”‚
β”‚  (EventBus, subscriptions, middleware chain)               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                    Connection Layer                          β”‚
β”‚  (Gateway WebSocket, REST API client)                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                     Scalability Layer                        β”‚
β”‚  (Rate limiting, sharding, hot reload, caching)            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                      Core Runtime                            β”‚
β”‚  (Async scheduler, configuration, logging)                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Design Principles

  1. Immutable by Default: Entities are frozen after creation for thread safety
  2. Async-First: All I/O operations are non-blocking using Ruby's Fiber scheduler
  3. Type Safety: Full type coercion with attributes system
  4. Zero-Cost Abstractions: No unnecessary object allocations
  5. Extensibility: Plugin system for modular functionality

Configuration

Basic Configuration

bot = DiscordRDA::Bot.new(
  token: ENV['DISCORD_TOKEN'],
  application_id: ENV['DISCORD_APP_ID'],
  shards: :auto,
  cache: :redis,
  intents: [:guilds, :guild_messages, :message_content],
  log_level: :info,
  log_format: :json
)

Advanced Configuration

bot = DiscordRDA::Bot.new(
  token: ENV['DISCORD_TOKEN'],
  shards: [[0, 4], [1, 4]],
  cache: :redis,
  redis_config: { host: 'localhost', port: 6379 },
  enable_scalable_rest: true,
  intents: [:guilds, :guild_members, :guild_messages, :message_content]
)

Sharding

Automatic Sharding

bot = DiscordRDA::Bot.new(token: token, shards: :auto)

Zero-Downtime Resharding

# Enable auto-resharding
bot.enable_auto_reshard(max_guilds_per_shard: 1000)

# Manual resharding
bot.reshard_to(8)

Caching

Memory Cache (Default)

bot = DiscordRDA::Bot.new(token: token, cache: :memory, max_cache_size: 10000)

Redis Cache

bot = DiscordRDA::Bot.new(
  token: token,
  cache: :redis,
  redis_config: { host: 'localhost', port: 6379 }
)

Cache Invalidation

bot.cache.invalidate(:user, user_id)
bot.cache.invalidate_guild(guild_id)
bot.cache.clear

Rate Limiting

DiscordRDA includes advanced rate limit management:

# Enable scalable REST (recommended for production)
bot.enable_scalable_rest

# Check invalid request bucket status
status = bot.invalid_bucket_status

Plugin System

Creating a Plugin

class MusicPlugin < DiscordRDA::Plugin
  def setup(bot)
    @bot = bot
  end

  def ready(bot)
    bot.logger.info('Music plugin ready')
  end
end

bot.register_plugin(MusicPlugin.new)

Development

Hot Reload

bot = DiscordRDA::Bot.new(token: token)
bot.enable_hot_reload(watch_dir: 'lib')

Running Tests

bundle exec rspec

Contributing

Bug reports and pull requests are welcome on GitHub.

License

Licensed under the MIT License.

Copyright (c) 2026 JΓΊlia Klee

Acknowledgments

Created by JΓΊlia Klee. Inspired by DiscordJDA and other Discord libraries. Special thanks to the Ruby async community and Discord API documentation contributors.