Class: DiscordRDA::Bot
- Inherits:
-
Object
- Object
- DiscordRDA::Bot
- Defined in:
- lib/discord_rda/bot.rb
Overview
Main Bot class for DiscordRDA. Entry point for building Discord bots.
Instance Attribute Summary collapse
-
#cache ⇒ EntityCache
readonly
Entity cache.
-
#config ⇒ Configuration
readonly
Bot configuration.
-
#event_bus ⇒ EventBus
readonly
Event bus.
-
#hot_reload_manager ⇒ HotReloadManager
readonly
Hot reload manager.
-
#logger ⇒ Logger
readonly
Logger instance.
-
#plugins ⇒ PluginRegistry
readonly
Plugin registry.
-
#reshard_manager ⇒ ReshardManager
readonly
Reshard manager.
-
#rest ⇒ RestClient
readonly
REST client.
-
#running ⇒ Boolean
readonly
Whether bot is running.
-
#scalable_rest ⇒ ScalableRestClient
readonly
Scalable REST client (if enabled).
-
#shard_manager ⇒ ShardManager
readonly
Shard manager.
-
#slash_commands ⇒ Hash
readonly
Registered slash commands.
Instance Method Summary collapse
-
#add_guild_member_role(guild_id, user_id, role_id, reason: nil) ⇒ void
Add role to guild member.
-
#add_reaction(channel_id, message_id, emoji) ⇒ void
Add a reaction to a message.
-
#analytics ⇒ Hash
Get analytics data (if analytics plugin registered).
-
#bulk_delete_messages(channel_id, message_ids, reason: nil) ⇒ void
Bulk delete messages.
-
#bulk_register_commands(commands) ⇒ Array<ApplicationCommand>
Bulk register global commands (replaces existing).
-
#channel(channel_id) ⇒ Channel?
Get a channel by ID.
-
#channel_message(channel_id, message_id) ⇒ Message?
Get a single message from a channel.
-
#channel_messages(channel_id, limit: 50, before: nil, after: nil, around: nil) ⇒ Array<Message>
Get messages from a channel with pagination (simplified).
-
#channel_webhooks(channel_id) ⇒ Array<Hash>
Get channel webhooks.
-
#context_menu(type:, name:, **options) {|Interaction| ... } ⇒ ApplicationCommand
Register a context menu command (user or message).
-
#create_guild_ban(guild_id, user_id, delete_message_days: nil, reason: nil) ⇒ void
Create guild ban.
-
#create_guild_channel(guild_id, name:, type: 0, **options) ⇒ Channel
Create guild channel (simplified).
-
#create_guild_role(guild_id, name:, **options) ⇒ Role
Create guild role (simplified).
-
#create_webhook(channel_id, name:, avatar: nil) ⇒ Hash
Create a webhook.
-
#delete_channel(channel_id, reason: nil) ⇒ Channel
Delete channel.
-
#delete_global_command(command_id) ⇒ void
Delete a global command.
-
#delete_guild_command(guild_id, command_id) ⇒ void
Delete a guild command.
-
#delete_guild_role(guild_id, role_id, reason: nil) ⇒ void
Delete guild role.
-
#delete_webhook(webhook_id, token: nil) ⇒ void
Delete a webhook.
-
#enable_auto_reshard(max_guilds_per_shard: 1000) ⇒ void
Enable auto-resharding based on guild count.
-
#enable_hot_reload(watch_dir: 'lib') ⇒ void
Enable hot reload for development.
-
#enable_scalable_rest(proxy: nil) ⇒ void
Enable scalable REST client (queue-based rate limiting).
-
#execute_webhook(webhook_id, token, content = nil, **options) ⇒ void
Execute webhook (simplified).
-
#get_reactions(channel_id, message_id, emoji, limit: 25) ⇒ Array<User>
Get reactions for a message (simplified - no pagination).
-
#guild(guild_id) ⇒ Guild?
Get a guild by ID.
-
#guild_ban(guild_id, user_id) ⇒ Hash?
Get a specific guild ban.
-
#guild_bans(guild_id, limit: 100) ⇒ Array<Hash>
Get guild bans (simplified - no pagination).
-
#guild_channels(guild_id) ⇒ Array<Channel>
Get guild channels.
-
#guild_member(guild_id, user_id) ⇒ Member?
Get a guild member.
-
#guild_members(guild_id, limit: 100, after: nil) ⇒ Array<Member>
List guild members (simplified - basic pagination).
-
#guild_roles(guild_id) ⇒ Array<Role>
Get guild roles.
-
#guild_webhooks(guild_id) ⇒ Array<Hash>
Get guild webhooks.
- #handle_autocomplete(interaction) ⇒ Object
- #handle_component(interaction) ⇒ Object
- #handle_modal_submit(interaction) ⇒ Object
- #handle_slash_command(interaction) ⇒ Object
-
#initialize(token:, **options) ⇒ Bot
constructor
Initialize a new bot.
-
#invalid_bucket_status ⇒ Hash?
Get invalid request bucket status.
-
#me ⇒ User
Fetch current user.
-
#modify_channel(channel_id, **options) ⇒ Channel
Modify channel.
-
#modify_guild_member(guild_id, user_id, **options) ⇒ Member
Modify a guild member (simplified).
-
#modify_guild_role(guild_id, role_id, **options) ⇒ Role
Modify guild role.
-
#on(event) { ... } ⇒ Subscription
Register an event handler.
-
#once(event) { ... } ⇒ Subscription
Register a one-time event handler.
-
#register_command(name, description = '', options = []) { ... } ⇒ void
(also: #command)
Register a command.
-
#register_plugin(plugin) ⇒ Boolean
(also: #plugin)
Register a plugin.
-
#remove_all_reactions(channel_id, message_id) ⇒ void
Remove all reactions from a message.
-
#remove_guild_ban(guild_id, user_id, reason: nil) ⇒ void
Remove guild ban (unban).
-
#remove_guild_member(guild_id, user_id, reason: nil) ⇒ void
Remove guild member (kick).
-
#remove_guild_member_role(guild_id, user_id, role_id, reason: nil) ⇒ void
Remove role from guild member.
-
#remove_reaction(channel_id, message_id, emoji, user_id: '@me') ⇒ void
Remove a reaction from a message.
-
#reshard_to(new_shard_count) ⇒ void
Trigger zero-downtime resharding.
-
#run(async: false) ⇒ void
Run the bot.
-
#search_guild_members(guild_id, query, limit: 25) ⇒ Array<Member>
Search guild members by query (simplified).
-
#send_message(channel_id, content = nil, **options) ⇒ Message
Send a message to a channel.
- #setup_interaction_handlers ⇒ Object
-
#slash(name, description, **options) {|CommandBuilder| ... } ⇒ ApplicationCommand
Register a slash command (global or guild-specific).
-
#status ⇒ Hash
Get bot status.
-
#stop ⇒ void
Stop the bot.
-
#update_presence(status: 'online', activity: nil) ⇒ void
Update bot presence.
-
#use(middleware) ⇒ void
Use middleware.
-
#wait_for(event, timeout: nil) { ... } ⇒ Event?
Wait for an event.
Constructor Details
#initialize(token:, **options) ⇒ Bot
Initialize a new bot
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/discord_rda/bot.rb', line 52 def initialize(token:, **) @config = Configuration.new(.merge(token: token)) @logger = Logger.new(level: @config.log_level, format: @config.log_format) @event_bus = EventBus.new(logger: @logger) @cache = build_cache @shard_manager = ShardManager.new(@config, @event_bus, @logger) @rest = RestClient.new(@config, @logger) # Configure entity API clients Message.api = @rest Interaction.api = @rest setup_event_handlers setup_interaction_handlers # Initialize scalable components @scalable_rest = nil @reshard_manager = ReshardManager.new(self, @shard_manager, @logger) @hot_reload_manager = HotReloadManager.new(self, @logger) @plugins = PluginRegistry.new(logger: @logger) @slash_commands = {} @running = false @commands = {} setup_event_handlers end |
Instance Attribute Details
#cache ⇒ EntityCache (readonly)
Returns Entity cache.
23 24 25 |
# File 'lib/discord_rda/bot.rb', line 23 def cache @cache end |
#config ⇒ Configuration (readonly)
Returns Bot configuration.
14 15 16 |
# File 'lib/discord_rda/bot.rb', line 14 def config @config end |
#event_bus ⇒ EventBus (readonly)
Returns Event bus.
20 21 22 |
# File 'lib/discord_rda/bot.rb', line 20 def event_bus @event_bus end |
#hot_reload_manager ⇒ HotReloadManager (readonly)
Returns Hot reload manager.
38 39 40 |
# File 'lib/discord_rda/bot.rb', line 38 def hot_reload_manager @hot_reload_manager end |
#logger ⇒ Logger (readonly)
Returns Logger instance.
17 18 19 |
# File 'lib/discord_rda/bot.rb', line 17 def logger @logger end |
#plugins ⇒ PluginRegistry (readonly)
Returns Plugin registry.
41 42 43 |
# File 'lib/discord_rda/bot.rb', line 41 def plugins @plugins end |
#reshard_manager ⇒ ReshardManager (readonly)
Returns Reshard manager.
35 36 37 |
# File 'lib/discord_rda/bot.rb', line 35 def reshard_manager @reshard_manager end |
#rest ⇒ RestClient (readonly)
Returns REST client.
29 30 31 |
# File 'lib/discord_rda/bot.rb', line 29 def rest @rest end |
#running ⇒ Boolean (readonly)
Returns Whether bot is running.
44 45 46 |
# File 'lib/discord_rda/bot.rb', line 44 def running @running end |
#scalable_rest ⇒ ScalableRestClient (readonly)
Returns Scalable REST client (if enabled).
32 33 34 |
# File 'lib/discord_rda/bot.rb', line 32 def scalable_rest @scalable_rest end |
#shard_manager ⇒ ShardManager (readonly)
Returns Shard manager.
26 27 28 |
# File 'lib/discord_rda/bot.rb', line 26 def shard_manager @shard_manager end |
#slash_commands ⇒ Hash (readonly)
Returns Registered slash commands.
47 48 49 |
# File 'lib/discord_rda/bot.rb', line 47 def slash_commands @slash_commands end |
Instance Method Details
#add_guild_member_role(guild_id, user_id, role_id, reason: nil) ⇒ void
This method returns an undefined value.
Add role to guild member
493 494 495 496 |
# File 'lib/discord_rda/bot.rb', line 493 def add_guild_member_role(guild_id, user_id, role_id, reason: nil) headers = reason ? { 'X-Audit-Log-Reason' => CGI.escape(reason) } : {} @rest.put("/guilds/#{guild_id}/members/#{user_id}/roles/#{role_id}", headers: headers) end |
#add_reaction(channel_id, message_id, emoji) ⇒ void
This method returns an undefined value.
Add a reaction to a message
404 405 406 407 |
# File 'lib/discord_rda/bot.rb', line 404 def add_reaction(channel_id, , emoji) emoji_str = emoji.respond_to?(:id) ? "#{emoji.name}:#{emoji.id}" : emoji.to_s @rest.put("/channels/#{channel_id}/messages/#{}/reactions/#{CGI.escape(emoji_str)}/@me") end |
#analytics ⇒ Hash
Get analytics data (if analytics plugin registered)
392 393 394 395 |
# File 'lib/discord_rda/bot.rb', line 392 def analytics analytics_plugin = @plugins.get(:Analytics) analytics_plugin&.summary || {} end |
#bulk_delete_messages(channel_id, message_ids, reason: nil) ⇒ void
This method returns an undefined value.
Bulk delete messages
700 701 702 703 |
# File 'lib/discord_rda/bot.rb', line 700 def (channel_id, , reason: nil) headers = reason ? { 'X-Audit-Log-Reason' => CGI.escape(reason) } : {} @rest.post("/channels/#{channel_id}/messages/bulk-delete", body: { messages: .map(&:to_s) }, headers: headers) end |
#bulk_register_commands(commands) ⇒ Array<ApplicationCommand>
Bulk register global commands (replaces existing)
134 135 136 137 138 139 140 141 142 |
# File 'lib/discord_rda/bot.rb', line 134 def bulk_register_commands(commands) return [] unless me app_id = me.id.to_s payload = commands.map(&:to_h) data = @rest.put("/applications/#{app_id}/commands", body: payload) data.map { |cmd| ApplicationCommand.new(cmd) } end |
#channel(channel_id) ⇒ Channel?
Get a channel by ID
297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/discord_rda/bot.rb', line 297 def channel(channel_id) cached = @cache.channel(channel_id) return cached if cached data = @rest.get("/channels/#{channel_id}") channel = Channel.new(data) @cache.cache_channel(channel) channel rescue RestClient::NotFoundError nil end |
#channel_message(channel_id, message_id) ⇒ Message?
Get a single message from a channel
341 342 343 344 345 346 |
# File 'lib/discord_rda/bot.rb', line 341 def (channel_id, ) data = @rest.get("/channels/#{channel_id}/messages/#{}") Message.new(data) rescue RestClient::NotFoundError nil end |
#channel_messages(channel_id, limit: 50, before: nil, after: nil, around: nil) ⇒ Array<Message>
Get messages from a channel with pagination (simplified)
327 328 329 330 331 332 333 334 335 |
# File 'lib/discord_rda/bot.rb', line 327 def (channel_id, limit: 50, before: nil, after: nil, around: nil) params = { limit: limit } params[:before] = before.to_s if before params[:after] = after.to_s if after params[:around] = around.to_s if around data = @rest.get("/channels/#{channel_id}/messages", params: params) data.map { |msg| Message.new(msg) } end |
#channel_webhooks(channel_id) ⇒ Array<Hash>
Get channel webhooks
622 623 624 |
# File 'lib/discord_rda/bot.rb', line 622 def channel_webhooks(channel_id) @rest.get("/channels/#{channel_id}/webhooks") end |
#context_menu(type:, name:, **options) {|Interaction| ... } ⇒ ApplicationCommand
Register a context menu command (user or message)
123 124 125 126 127 128 129 |
# File 'lib/discord_rda/bot.rb', line 123 def (type:, name:, **, &block) cmd_type = type == :user ? 2 : 3 [:type] = cmd_type [:description] = '' # Context menus don't have descriptions slash(name, '', **, &block) end |
#create_guild_ban(guild_id, user_id, delete_message_days: nil, reason: nil) ⇒ void
This method returns an undefined value.
Create guild ban
589 590 591 592 593 594 |
# File 'lib/discord_rda/bot.rb', line 589 def create_guild_ban(guild_id, user_id, delete_message_days: nil, reason: nil) payload = {} payload[:delete_message_days] = if headers = reason ? { 'X-Audit-Log-Reason' => CGI.escape(reason) } : {} @rest.put("/guilds/#{guild_id}/bans/#{user_id}", body: payload, headers: headers) end |
#create_guild_channel(guild_id, name:, type: 0, **options) ⇒ Channel
Create guild channel (simplified)
669 670 671 672 673 |
# File 'lib/discord_rda/bot.rb', line 669 def create_guild_channel(guild_id, name:, type: 0, **) payload = { name: name, type: type }.merge(.slice(:topic, :bitrate, :user_limit, :parent_id, :nsfw, :permission_overwrites, :rate_limit_per_user)) data = @rest.post("/guilds/#{guild_id}/channels", body: payload) Channel.new(data) end |
#create_guild_role(guild_id, name:, **options) ⇒ Role
Create guild role (simplified)
534 535 536 537 538 |
# File 'lib/discord_rda/bot.rb', line 534 def create_guild_role(guild_id, name:, **) payload = { name: name }.merge(.slice(:permissions, :color, :hoist, :mentionable, :icon, :unicode_emoji)) data = @rest.post("/guilds/#{guild_id}/roles", body: payload) Role.new(data.merge('guild_id' => guild_id.to_s)) end |
#create_webhook(channel_id, name:, avatar: nil) ⇒ Hash
Create a webhook
613 614 615 616 617 |
# File 'lib/discord_rda/bot.rb', line 613 def create_webhook(channel_id, name:, avatar: nil) payload = { name: name } payload[:avatar] = avatar if avatar @rest.post("/channels/#{channel_id}/webhooks", body: payload) end |
#delete_channel(channel_id, reason: nil) ⇒ Channel
Delete channel
689 690 691 692 693 |
# File 'lib/discord_rda/bot.rb', line 689 def delete_channel(channel_id, reason: nil) headers = reason ? { 'X-Audit-Log-Reason' => CGI.escape(reason) } : {} data = @rest.delete("/channels/#{channel_id}", headers: headers) Channel.new(data) end |
#delete_global_command(command_id) ⇒ void
This method returns an undefined value.
Delete a global command
147 148 149 |
# File 'lib/discord_rda/bot.rb', line 147 def delete_global_command(command_id) @rest.delete("/applications/#{me.id}/commands/#{command_id}") if me end |
#delete_guild_command(guild_id, command_id) ⇒ void
This method returns an undefined value.
Delete a guild command
155 156 157 |
# File 'lib/discord_rda/bot.rb', line 155 def delete_guild_command(guild_id, command_id) @rest.delete("/applications/#{me.id}/guilds/#{guild_id}/commands/#{command_id}") if me end |
#delete_guild_role(guild_id, role_id, reason: nil) ⇒ void
This method returns an undefined value.
Delete guild role
556 557 558 559 |
# File 'lib/discord_rda/bot.rb', line 556 def delete_guild_role(guild_id, role_id, reason: nil) headers = reason ? { 'X-Audit-Log-Reason' => CGI.escape(reason) } : {} @rest.delete("/guilds/#{guild_id}/roles/#{role_id}", headers: headers) end |
#delete_webhook(webhook_id, token: nil) ⇒ void
This method returns an undefined value.
Delete a webhook
648 649 650 651 |
# File 'lib/discord_rda/bot.rb', line 648 def delete_webhook(webhook_id, token: nil) path = token ? "/webhooks/#{webhook_id}/#{token}" : "/webhooks/#{webhook_id}" @rest.delete(path) end |
#enable_auto_reshard(max_guilds_per_shard: 1000) ⇒ void
This method returns an undefined value.
Enable auto-resharding based on guild count
377 378 379 380 381 382 |
# File 'lib/discord_rda/bot.rb', line 377 def enable_auto_reshard(max_guilds_per_shard: 1000) @event_bus.on(:guild_create) do |_event| guild_count = @shard_manager.total_guilds || 0 @reshard_manager.auto_reshard_if_needed(guild_count, max_guilds_per_shard: max_guilds_per_shard) end end |
#enable_hot_reload(watch_dir: 'lib') ⇒ void
This method returns an undefined value.
Enable hot reload for development
360 361 362 363 364 |
# File 'lib/discord_rda/bot.rb', line 360 def enable_hot_reload(watch_dir: 'lib') @logger.info('Enabling hot reload', watch_dir: watch_dir) @hot_reload_manager = HotReloadManager.new(self, @logger, watch_dir: watch_dir) @hot_reload_manager.enable end |
#enable_scalable_rest(proxy: nil) ⇒ void
This method returns an undefined value.
Enable scalable REST client (queue-based rate limiting)
351 352 353 354 355 |
# File 'lib/discord_rda/bot.rb', line 351 def enable_scalable_rest(proxy: nil) @logger.info('Enabling scalable REST client') @scalable_rest = ScalableRestClient.new(@config, @logger, proxy: proxy) @scalable_rest.start end |
#execute_webhook(webhook_id, token, content = nil, **options) ⇒ void
This method returns an undefined value.
Execute webhook (simplified)
639 640 641 642 |
# File 'lib/discord_rda/bot.rb', line 639 def execute_webhook(webhook_id, token, content = nil, **) payload = { content: content }.merge(.slice(:username, :avatar_url, :embeds, :components, :allowed_mentions)) @rest.post("/webhooks/#{webhook_id}/#{token}", body: payload) end |
#get_reactions(channel_id, message_id, emoji, limit: 25) ⇒ Array<User>
Get reactions for a message (simplified - no pagination)
426 427 428 429 430 |
# File 'lib/discord_rda/bot.rb', line 426 def get_reactions(channel_id, , emoji, limit: 25) emoji_str = emoji.respond_to?(:id) ? "#{emoji.name}:#{emoji.id}" : emoji.to_s data = @rest.get("/channels/#{channel_id}/messages/#{}/reactions/#{CGI.escape(emoji_str)}", params: { limit: limit }) data.map { |u| User.new(u) } end |
#guild(guild_id) ⇒ Guild?
Get a guild by ID
282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/discord_rda/bot.rb', line 282 def guild(guild_id) cached = @cache.guild(guild_id) return cached if cached data = @rest.get("/guilds/#{guild_id}") guild = Guild.new(data) @cache.cache_guild(guild) guild rescue RestClient::NotFoundError nil end |
#guild_ban(guild_id, user_id) ⇒ Hash?
Get a specific guild ban
576 577 578 579 580 581 |
# File 'lib/discord_rda/bot.rb', line 576 def guild_ban(guild_id, user_id) data = @rest.get("/guilds/#{guild_id}/bans/#{user_id}") { user: User.new(data['user']), reason: data['reason'] } rescue RestClient::NotFoundError nil end |
#guild_bans(guild_id, limit: 100) ⇒ Array<Hash>
Get guild bans (simplified - no pagination)
567 568 569 570 |
# File 'lib/discord_rda/bot.rb', line 567 def guild_bans(guild_id, limit: 100) data = @rest.get("/guilds/#{guild_id}/bans", params: { limit: limit }) data.map { |b| { user: User.new(b['user']), reason: b['reason'] } } end |
#guild_channels(guild_id) ⇒ Array<Channel>
Get guild channels
658 659 660 661 |
# File 'lib/discord_rda/bot.rb', line 658 def guild_channels(guild_id) data = @rest.get("/guilds/#{guild_id}/channels") data.map { |c| Channel.new(c) } end |
#guild_member(guild_id, user_id) ⇒ Member?
Get a guild member
446 447 448 449 450 451 |
# File 'lib/discord_rda/bot.rb', line 446 def guild_member(guild_id, user_id) data = @rest.get("/guilds/#{guild_id}/members/#{user_id}") Member.new(data.merge('guild_id' => guild_id.to_s)) rescue RestClient::NotFoundError nil end |
#guild_members(guild_id, limit: 100, after: nil) ⇒ Array<Member>
List guild members (simplified - basic pagination)
458 459 460 461 462 463 |
# File 'lib/discord_rda/bot.rb', line 458 def guild_members(guild_id, limit: 100, after: nil) params = { limit: limit } params[:after] = after.to_s if after data = @rest.get("/guilds/#{guild_id}/members", params: params) data.map { |m| Member.new(m.merge('guild_id' => guild_id.to_s)) } end |
#guild_roles(guild_id) ⇒ Array<Role>
Get guild roles
524 525 526 527 |
# File 'lib/discord_rda/bot.rb', line 524 def guild_roles(guild_id) data = @rest.get("/guilds/#{guild_id}/roles") data.map { |r| Role.new(r.merge('guild_id' => guild_id.to_s)) } end |
#guild_webhooks(guild_id) ⇒ Array<Hash>
Get guild webhooks
629 630 631 |
# File 'lib/discord_rda/bot.rb', line 629 def guild_webhooks(guild_id) @rest.get("/guilds/#{guild_id}/webhooks") end |
#handle_autocomplete(interaction) ⇒ Object
764 765 766 767 768 769 770 771 772 773 |
# File 'lib/discord_rda/bot.rb', line 764 def handle_autocomplete(interaction) # Autocomplete needs to be handled by the command that registered it cmd_name = interaction.command_name focused = interaction.focused_option @logger.debug('Autocomplete', command: cmd_name, option: focused&.dig('name')) # Emit autocomplete event @event_bus.emit(:autocomplete, interaction) end |
#handle_component(interaction) ⇒ Object
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 |
# File 'lib/discord_rda/bot.rb', line 745 def handle_component(interaction) # Component interactions are handled by custom_id patterns or specific handlers custom_id = interaction.custom_id @logger.debug('Component interaction', custom_id: custom_id, user: interaction.user&.id) # Emit specific event for this component type event_type = case interaction.component_type when 2 then :button_click when 3 then :string_select when 5 then :user_select when 6 then :role_select when 7 then :mentionable_select when 8 then :channel_select else :component_interaction end @event_bus.emit(event_type, interaction) end |
#handle_modal_submit(interaction) ⇒ Object
775 776 777 778 779 780 781 782 783 |
# File 'lib/discord_rda/bot.rb', line 775 def handle_modal_submit(interaction) modal_id = interaction.custom_id values = interaction.modal_values @logger.debug('Modal submit', modal_id: modal_id, values: values.keys) # Emit modal submit event @event_bus.emit(:modal_submit, interaction) end |
#handle_slash_command(interaction) ⇒ Object
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 |
# File 'lib/discord_rda/bot.rb', line 722 def handle_slash_command(interaction) cmd_name = interaction.command_name guild_id = interaction.guild_id # Try guild-specific command first, then global key = guild_id ? "#{cmd_name}:#{guild_id}" : cmd_name cmd = @slash_commands[key] || @slash_commands[cmd_name] if cmd && cmd.handler @logger.debug('Executing slash command', name: cmd_name, user: interaction.user&.id) begin cmd.handler.call(interaction) rescue => e @logger.error('Slash command error', command: cmd_name, error: e) # Send error response interaction.respond(content: "An error occurred while executing this command.", ephemeral: true) rescue nil end else @logger.warn('Unknown slash command', name: cmd_name) interaction.respond(content: "Unknown command: #{cmd_name}", ephemeral: true) rescue nil end end |
#invalid_bucket_status ⇒ Hash?
Get invalid request bucket status
386 387 388 |
# File 'lib/discord_rda/bot.rb', line 386 def invalid_bucket_status @scalable_rest&.invalid_bucket&.status end |
#me ⇒ User
Fetch current user
274 275 276 277 |
# File 'lib/discord_rda/bot.rb', line 274 def me data = @rest.get('/users/@me') User.new(data) end |
#modify_channel(channel_id, **options) ⇒ Channel
Modify channel
679 680 681 682 683 |
# File 'lib/discord_rda/bot.rb', line 679 def modify_channel(channel_id, **) payload = .slice(:name, :type, :position, :topic, :nsfw, :rate_limit_per_user, :bitrate, :user_limit, :parent_id, :default_auto_archive_duration) data = @rest.patch("/channels/#{channel_id}", body: payload) Channel.new(data) end |
#modify_guild_member(guild_id, user_id, **options) ⇒ Member
Modify a guild member (simplified)
481 482 483 484 485 |
# File 'lib/discord_rda/bot.rb', line 481 def modify_guild_member(guild_id, user_id, **) payload = .slice(:nick, :roles, :mute, :deaf, :channel_id, :communication_disabled_until) data = @rest.patch("/guilds/#{guild_id}/members/#{user_id}", body: payload) Member.new(data.merge('guild_id' => guild_id.to_s)) end |
#modify_guild_role(guild_id, role_id, **options) ⇒ Role
Modify guild role
545 546 547 548 549 |
# File 'lib/discord_rda/bot.rb', line 545 def modify_guild_role(guild_id, role_id, **) payload = .slice(:name, :permissions, :color, :hoist, :mentionable, :icon, :unicode_emoji) data = @rest.patch("/guilds/#{guild_id}/roles/#{role_id}", body: payload) Role.new(data.merge('guild_id' => guild_id.to_s)) end |
#on(event) { ... } ⇒ Subscription
Register an event handler
163 164 165 |
# File 'lib/discord_rda/bot.rb', line 163 def on(event, &block) @event_bus.on(event, &block) end |
#once(event) { ... } ⇒ Subscription
Register a one-time event handler
171 172 173 |
# File 'lib/discord_rda/bot.rb', line 171 def once(event, &block) @event_bus.once(event, &block) end |
#register_command(name, description = '', options = []) { ... } ⇒ void Also known as: command
This method returns an undefined value.
Register a command
190 191 192 193 194 195 196 |
# File 'lib/discord_rda/bot.rb', line 190 def register_command(name, description = '', = [], &block) @commands[name.to_s] = { description: description, options: , handler: block } end |
#register_plugin(plugin) ⇒ Boolean Also known as: plugin
Register a plugin
202 203 204 |
# File 'lib/discord_rda/bot.rb', line 202 def register_plugin(plugin) @plugins.register(plugin, self) end |
#remove_all_reactions(channel_id, message_id) ⇒ void
This method returns an undefined value.
Remove all reactions from a message
436 437 438 |
# File 'lib/discord_rda/bot.rb', line 436 def remove_all_reactions(channel_id, ) @rest.delete("/channels/#{channel_id}/messages/#{}/reactions") end |
#remove_guild_ban(guild_id, user_id, reason: nil) ⇒ void
This method returns an undefined value.
Remove guild ban (unban)
601 602 603 604 |
# File 'lib/discord_rda/bot.rb', line 601 def remove_guild_ban(guild_id, user_id, reason: nil) headers = reason ? { 'X-Audit-Log-Reason' => CGI.escape(reason) } : {} @rest.delete("/guilds/#{guild_id}/bans/#{user_id}", headers: headers) end |
#remove_guild_member(guild_id, user_id, reason: nil) ⇒ void
This method returns an undefined value.
Remove guild member (kick)
514 515 516 517 |
# File 'lib/discord_rda/bot.rb', line 514 def remove_guild_member(guild_id, user_id, reason: nil) headers = reason ? { 'X-Audit-Log-Reason' => CGI.escape(reason) } : {} @rest.delete("/guilds/#{guild_id}/members/#{user_id}", headers: headers) end |
#remove_guild_member_role(guild_id, user_id, role_id, reason: nil) ⇒ void
This method returns an undefined value.
Remove role from guild member
504 505 506 507 |
# File 'lib/discord_rda/bot.rb', line 504 def remove_guild_member_role(guild_id, user_id, role_id, reason: nil) headers = reason ? { 'X-Audit-Log-Reason' => CGI.escape(reason) } : {} @rest.delete("/guilds/#{guild_id}/members/#{user_id}/roles/#{role_id}", headers: headers) end |
#remove_reaction(channel_id, message_id, emoji, user_id: '@me') ⇒ void
This method returns an undefined value.
Remove a reaction from a message
415 416 417 418 |
# File 'lib/discord_rda/bot.rb', line 415 def remove_reaction(channel_id, , emoji, user_id: '@me') emoji_str = emoji.respond_to?(:id) ? "#{emoji.name}:#{emoji.id}" : emoji.to_s @rest.delete("/channels/#{channel_id}/messages/#{}/reactions/#{CGI.escape(emoji_str)}/#{user_id}") end |
#reshard_to(new_shard_count) ⇒ void
This method returns an undefined value.
Trigger zero-downtime resharding
369 370 371 372 |
# File 'lib/discord_rda/bot.rb', line 369 def reshard_to(new_shard_count) @logger.info('Triggering resharding', new_count: new_shard_count) @reshard_manager.reshard_to(new_shard_count) end |
#run(async: false) ⇒ void
This method returns an undefined value.
Run the bot
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/discord_rda/bot.rb', line 217 def run(async: false) @running = true @logger.info('Starting DiscordRDA bot', version: VERSION, shards: @config.shards.length) # Start REST client @rest.start # Calculate shard count if auto shard_count = if @config.shards == [:auto] @shard_manager.calculate_shard_count(:auto, @rest) else @config.shards.length end @shard_manager.instance_variable_set(:@shard_count, shard_count) # Start shards if async Async { start_shards } else start_shards end end |
#search_guild_members(guild_id, query, limit: 25) ⇒ Array<Member>
Search guild members by query (simplified)
470 471 472 473 474 |
# File 'lib/discord_rda/bot.rb', line 470 def search_guild_members(guild_id, query, limit: 25) params = { query: query, limit: limit } data = @rest.get("/guilds/#{guild_id}/members/search", params: params) data.map { |m| Member.new(m.merge('guild_id' => guild_id.to_s)) } end |
#send_message(channel_id, content = nil, **options) ⇒ Message
Send a message to a channel
314 315 316 317 318 |
# File 'lib/discord_rda/bot.rb', line 314 def (channel_id, content = nil, **) payload = { content: content }.merge().compact data = @rest.post("/channels/#{channel_id}/messages", body: payload) Message.new(data) end |
#setup_interaction_handlers ⇒ Object
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 |
# File 'lib/discord_rda/bot.rb', line 705 def setup_interaction_handlers # Handle slash commands @event_bus.on(:interaction_create) do |event| interaction = event.interaction if interaction.command? handle_slash_command(interaction) elsif interaction.component? handle_component(interaction) elsif interaction.autocomplete? handle_autocomplete(interaction) elsif interaction.modal_submit? handle_modal_submit(interaction) end end end |
#slash(name, description, **options) {|CommandBuilder| ... } ⇒ ApplicationCommand
Register a slash command (global or guild-specific)
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/discord_rda/bot.rb', line 89 def slash(name, description, **, &block) builder = CommandBuilder.new(name, description) builder.dm_allowed([:dm_permission]) if .key?(:dm_permission) builder.([:default_member_permissions]) if [:default_member_permissions] builder.nsfw([:nsfw]) if [:nsfw] block.call(builder) if block cmd = builder.build cmd.instance_variable_set(:@application_id, me.id.to_s) rescue nil cmd.instance_variable_set(:@guild_id, [:guild_id].to_s) if [:guild_id] key = [:guild_id] ? "#{name}:#{[:guild_id]}" : name @slash_commands[key] = cmd # Register with Discord if we have application ID if cmd.application_id if [:guild_id] cmd.create_guild(self, [:guild_id]) else cmd.create_global(self) end end @logger.info('Registered slash command', name: name, guild: [:guild_id] || 'global') cmd end |
#status ⇒ Hash
Get bot status
263 264 265 266 267 268 269 270 |
# File 'lib/discord_rda/bot.rb', line 263 def status { running: @running, shards: @shard_manager.status, cache: @cache.stats, plugins: @plugins.stats } end |
#stop ⇒ void
This method returns an undefined value.
Stop the bot
244 245 246 247 248 249 |
# File 'lib/discord_rda/bot.rb', line 244 def stop @logger.info('Stopping bot') @running = false @shard_manager.stop @rest.stop end |
#update_presence(status: 'online', activity: nil) ⇒ void
This method returns an undefined value.
Update bot presence
255 256 257 258 259 |
# File 'lib/discord_rda/bot.rb', line 255 def update_presence(status: 'online', activity: nil) @shard_manager.shards.each do |shard| shard.update_presence(status: status, activity: activity) end end |
#use(middleware) ⇒ void
This method returns an undefined value.
Use middleware
210 211 212 |
# File 'lib/discord_rda/bot.rb', line 210 def use(middleware) @event_bus.use(middleware) end |
#wait_for(event, timeout: nil) { ... } ⇒ Event?
Wait for an event
180 181 182 |
# File 'lib/discord_rda/bot.rb', line 180 def wait_for(event, timeout: nil, &block) @event_bus.wait_for(event, timeout: timeout, &block) end |