rubord 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +39 -0
  4. data/lib/rubord/components/actionRow.rb +93 -0
  5. data/lib/rubord/components/button.rb +125 -0
  6. data/lib/rubord/components/componentsV2.rb +4 -0
  7. data/lib/rubord/components/containers/base.rb +15 -0
  8. data/lib/rubord/components/containers/container.rb +39 -0
  9. data/lib/rubord/components/containers/section.rb +26 -0
  10. data/lib/rubord/components/containers/separator.rb +51 -0
  11. data/lib/rubord/components/containers/text.rb +23 -0
  12. data/lib/rubord/components/modal.rb +134 -0
  13. data/lib/rubord/components/select_menu.rb +147 -0
  14. data/lib/rubord/models/channel.rb +50 -0
  15. data/lib/rubord/models/collection.rb +70 -0
  16. data/lib/rubord/models/commands/base.rb +111 -0
  17. data/lib/rubord/models/commands/command.rb +3 -0
  18. data/lib/rubord/models/commands/loader.rb +36 -0
  19. data/lib/rubord/models/commands/registry.rb +26 -0
  20. data/lib/rubord/models/components.rb +5 -0
  21. data/lib/rubord/models/embed.rb +87 -0
  22. data/lib/rubord/models/flags.rb +249 -0
  23. data/lib/rubord/models/guild.rb +78 -0
  24. data/lib/rubord/models/interaction.rb +136 -0
  25. data/lib/rubord/models/member.rb +63 -0
  26. data/lib/rubord/models/mention.rb +47 -0
  27. data/lib/rubord/models/message.rb +88 -0
  28. data/lib/rubord/models/role.rb +15 -0
  29. data/lib/rubord/models/user.rb +21 -0
  30. data/lib/rubord/structs/client.rb +364 -0
  31. data/lib/rubord/structs/gateway.rb +363 -0
  32. data/lib/rubord/structs/logger.rb +19 -0
  33. data/lib/rubord/structs/models.rb +19 -0
  34. data/lib/rubord/structs/parser.rb +68 -0
  35. data/lib/rubord/structs/rate_limiter.rb +163 -0
  36. data/lib/rubord/structs/rest.rb +353 -0
  37. data/lib/rubord.rb +8 -0
  38. metadata +105 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 73d40380336ec0be54afd05fa19a6177fcfc25d16d335c7748da02de95da4538
4
+ data.tar.gz: ad7c6cb4f6ab3aae3019e213bda0d4dc0d5dc6307aaa89626f4e2e594daad7e3
5
+ SHA512:
6
+ metadata.gz: 893c48fbcba9541e21c95d47076c6e427d6191638b11064ae59d99f4b8ec82bb606bbcb8a396c5da62c517c60d2a4ac85dfd91540270d1621fc2150337675ce1
7
+ data.tar.gz: c6b3a468ebf4fa9e9631fb1459e618908102d4c1820c70142d73cc7d0bbda27c23f874db774fc3be973ae88d72b4743d936e19b9a6442913e6d5f4492970a455
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kauã Eduardo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Rubord
2
+
3
+ Rubord is a modern Ruby library for building Discord bots.
4
+
5
+ It provides:
6
+ - Gateway (WebSocket)
7
+ - REST API
8
+ - Interactions (Buttons, Modals, Select Menus)
9
+ - Components v2 support
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ gem install rubord
15
+ ```
16
+
17
+ Or in your Gemfile:
18
+
19
+ ```rb
20
+ gem "rubord"
21
+ ```
22
+
23
+ ## Basic Example
24
+
25
+ ```rb
26
+ client = Rubord::Client.new(intents: 3276799)
27
+
28
+ client.on(:ready) do |client|
29
+ puts "Logged in: " + client.username
30
+ end
31
+
32
+ client.on(:message_create) do |message|
33
+ if message.content == "!hello"
34
+ message.reply("Hello!")
35
+ end
36
+ end
37
+
38
+ client.login("Your discord bot token")
39
+ ```
@@ -0,0 +1,93 @@
1
+ # components/actionRow.rb
2
+
3
+ module Rubord
4
+ # Represents a Discord action row container.
5
+ #
6
+ # Action rows are containers for message components (buttons, select menus).
7
+ # They can hold up to 5 buttons or 1 select menu per row.
8
+ #
9
+ # @example Creating an action row with buttons
10
+ # row = Rubord::ActionRow.new(
11
+ # Rubord::Button.new(label: "Yes", custom_id: "yes"),
12
+ # Rubord::Button.new(label: "No", custom_id: "no")
13
+ # )
14
+ #
15
+ # @example Creating an empty row and adding components
16
+ # row = Rubord::ActionRow.new
17
+ # row.add(Rubord::Button.new(label: "Click", custom_id: "click"))
18
+ #
19
+ # @since 1.0.0
20
+ # @see https://discord.com/developers/docs/interactions/message-components#action-rows
21
+ class ActionRow
22
+ # @return [Integer] Component type (always 1 for action rows).
23
+ attr_accessor :type
24
+
25
+ # @return [Array<Button, SelectMenu>] Components in this action row.
26
+ attr_accessor :components
27
+
28
+ # Creates a new action row.
29
+ #
30
+ # @param components [Array<Button, SelectMenu>] Components to include in the row.
31
+ # Can be empty.
32
+ #
33
+ # @example Empty action row
34
+ # ActionRow.new
35
+ #
36
+ # @example With initial buttons
37
+ # ActionRow.new(
38
+ # Button.new(label: "Save", custom_id: "save"),
39
+ # Button.new(label: "Cancel", custom_id: "cancel")
40
+ # )
41
+ #
42
+ # @note Action rows can contain either:
43
+ # - Up to 5 buttons
44
+ # - OR 1 select menu
45
+ # Not a mixture of both.
46
+ def initialize(*components)
47
+ @type = 1
48
+ @components = components
49
+ end
50
+
51
+ # Adds a component to the action row.
52
+ #
53
+ # @param component [Button, SelectMenu] Component to add.
54
+ #
55
+ # @return [Rubord::ActionRow] Self for method chaining.
56
+ #
57
+ # @raise [ArgumentError] If adding component would violate Discord limits.
58
+ #
59
+ # @example
60
+ # row = ActionRow.new
61
+ # row.add(Button.new(label: "Test", custom_id: "test"))
62
+ def add(component)
63
+ @components << component
64
+ self
65
+ end
66
+
67
+ # Converts the action row to a Discord API-compatible hash.
68
+ #
69
+ # @return [Hash] Action row data in Discord API format.
70
+ #
71
+ # @example
72
+ # row = ActionRow.new(Button.new(label: "Btn", custom_id: "btn"))
73
+ # row.to_h
74
+ # # => {type: 1, components: [{type: 2, style: 1, label: "Btn", custom_id: "btn", disabled: false}]}
75
+ def to_h
76
+ {
77
+ type: @type,
78
+ components: @components.map(&:to_h)
79
+ }
80
+ end
81
+ end
82
+
83
+ # Factory method for creating ActionRow instances.
84
+ #
85
+ # @param args [Array<Button, SelectMenu>] Components to include.
86
+ # @return [Rubord::ActionRow] New action row instance.
87
+ #
88
+ # @example
89
+ # row = Rubord.ActionRow(button1, button2)
90
+ def self.ActionRow(*args)
91
+ ActionRow.new(*args)
92
+ end
93
+ end
@@ -0,0 +1,125 @@
1
+ # components/button.rb
2
+
3
+ module Rubord
4
+ # Represents a Discord interactive button component.
5
+ #
6
+ # Buttons are interactive elements that users can click to trigger actions.
7
+ # They can be used in messages, modals, and action rows.
8
+ #
9
+ # @example Creating a primary button
10
+ # button = Rubord::Button.new(
11
+ # label: "Click Me",
12
+ # style: 1,
13
+ # custom_id: "my_button"
14
+ # )
15
+ #
16
+ # @example Creating a link button
17
+ # link_button = Rubord::Button.new(
18
+ # label: "Visit Website",
19
+ # style: 5,
20
+ # url: "https://example.com"
21
+ # )
22
+ #
23
+ # @since 1.0.0
24
+ # @see https://discord.com/developers/docs/interactions/message-components#buttons
25
+ class Button
26
+ # @return [Integer] Component type (always 2 for buttons).
27
+ attr_accessor :type
28
+
29
+ # @return [Integer] Button style (1-5).
30
+ # - 1: Primary (blurple)
31
+ # - 2: Secondary (grey)
32
+ # - 3: Success (green)
33
+ # - 4: Danger (red)
34
+ # - 5: Link (grey, navigates to URL)
35
+ attr_accessor :style
36
+
37
+ # @return [String] Text displayed on the button (max 80 characters).
38
+ attr_accessor :label
39
+
40
+ # @return [String, nil] Developer-defined identifier for the button.
41
+ # Required for non-link buttons, must be unique per message.
42
+ attr_accessor :custom_id
43
+
44
+ # @return [String, nil] URL for link buttons (style 5).
45
+ attr_accessor :url
46
+
47
+ # @return [Boolean] Whether the button is disabled.
48
+ attr_accessor :disabled
49
+
50
+ # @return [Hash, nil] Emoji to display on the button.
51
+ # Format: `{name: "emoji_name", id: "emoji_id", animated: false}`
52
+ attr_accessor :emoji
53
+
54
+ # Creates a new button component.
55
+ #
56
+ # @param label [String] Text label displayed on the button.
57
+ # @param style [Integer] Button style (1-5).
58
+ # Defaults to 1 (primary).
59
+ # @param custom_id [String, nil] Developer-defined identifier.
60
+ # Required for non-link buttons.
61
+ # @param url [String, nil] URL for link buttons (style 5).
62
+ # @param disabled [Boolean] Whether the button is disabled.
63
+ # Defaults to false.
64
+ # @param emoji [Hash, nil] Emoji to display on the button.
65
+ #
66
+ # @raise [ArgumentError] If required parameters are missing.
67
+ #
68
+ # @example Primary button with emoji
69
+ # Button.new(
70
+ # label: "Submit",
71
+ # style: 1,
72
+ # custom_id: "submit_btn",
73
+ # emoji: { name: "✅" }
74
+ # )
75
+ #
76
+ # @example Disabled danger button
77
+ # Button.new(
78
+ # label: "Delete",
79
+ # style: 4,
80
+ # custom_id: "delete_btn",
81
+ # disabled: true
82
+ # )
83
+ def initialize(label:, style: 1, custom_id: nil, url: nil, disabled: false, emoji: nil)
84
+ @type = 2
85
+ @style = style
86
+ @label = label
87
+ @custom_id = custom_id
88
+ @url = url
89
+ @disabled = disabled
90
+ @emoji = emoji
91
+ end
92
+
93
+ # Converts the button to a Discord API-compatible hash.
94
+ #
95
+ # @return [Hash] Button data in Discord API format.
96
+ #
97
+ # @example
98
+ # button = Button.new(label: "Test", custom_id: "test")
99
+ # button.to_h
100
+ # # => {type: 2, style: 1, label: "Test", custom_id: "test", disabled: false}
101
+ def to_h
102
+ h = {
103
+ type: @type,
104
+ style: @style,
105
+ label: @label,
106
+ disabled: @disabled
107
+ }
108
+ h[:custom_id] = @custom_id if @custom_id
109
+ h[:url] = @url if @url
110
+ h[:emoji] = @emoji if @emoji
111
+ h
112
+ end
113
+ end
114
+
115
+ # Factory method for creating Button instances.
116
+ #
117
+ # @param args [Hash] Button initialization parameters.
118
+ # @return [Rubord::Button] New button instance.
119
+ #
120
+ # @example
121
+ # button = Rubord.Button(label: "Click", custom_id: "click")
122
+ def self.Button(**args)
123
+ Button.new(**args)
124
+ end
125
+ end
@@ -0,0 +1,4 @@
1
+ require_relative "containers/container.rb"
2
+ require_relative "containers/section.rb"
3
+ require_relative "containers/separator.rb"
4
+ require_relative "containers/text.rb"
@@ -0,0 +1,15 @@
1
+ module Rubord
2
+ module Components
3
+ class BaseComponent
4
+ attr_reader :type
5
+
6
+ def initialize(type)
7
+ @type = type
8
+ end
9
+
10
+ def to_h
11
+ raise NotImplementedError, "Implement to_h in subclass"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ require_relative "base"
2
+
3
+ module Rubord
4
+ module Components
5
+ class Container < BaseComponent
6
+ attr_reader :components, :color
7
+
8
+ def initialize(*components, color: nil)
9
+ super(17)
10
+ @color = Rubord.Parser.color(color)
11
+ @components = []
12
+ components.each { |c| add(c) }
13
+ end
14
+
15
+ def add(component)
16
+ unless component.respond_to?(:to_h)
17
+ raise ArgumentError, "Invalid component inside Container: #{component.inspect}"
18
+ end
19
+
20
+ @components << component
21
+ self
22
+ end
23
+
24
+ def to_h
25
+ payload = {
26
+ type: @type,
27
+ components: @components.map(&:to_h)
28
+ }
29
+
30
+ payload[:accent_color] = @color if @color
31
+ payload
32
+ end
33
+ end
34
+ end
35
+
36
+ def self.Container(*components, **opts)
37
+ Components::Container.new(*components, **opts)
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ require_relative "base"
2
+
3
+ module Rubord
4
+ module Components
5
+ class Section < BaseComponent
6
+ def initialize(text:, accessory: nil)
7
+ super(9)
8
+ @text = text
9
+ @accessory = accessory
10
+ end
11
+
12
+ def to_h
13
+ h = {
14
+ type: @type,
15
+ text: @text.is_a?(String) ? { type: 10, content: @text } : @text.to_h
16
+ }
17
+ h[:accessory] = @accessory.to_h if @accessory
18
+ h
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.Section(**args)
24
+ Components::Section.new(**args)
25
+ end
26
+ end
@@ -0,0 +1,51 @@
1
+ require_relative "base"
2
+
3
+ module Rubord
4
+ module Components
5
+ class Separator < BaseComponent
6
+ SPACING = {
7
+ small: 1,
8
+ large: 2
9
+ }.freeze
10
+
11
+ attr_reader :divider, :spacing
12
+
13
+ def initialize(divider: true, spacing: :small)
14
+ super(14)
15
+
16
+ @divider = !!divider
17
+ @spacing = parse_spacing(spacing)
18
+ end
19
+
20
+ def to_h
21
+ {
22
+ type: @type,
23
+ divider: @divider,
24
+ spacing: @spacing
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ def parse_spacing(value)
31
+ case value
32
+ when Integer
33
+ unless SPACING.value?(value)
34
+ raise ArgumentError, "Invalid spacing value: #{value}"
35
+ end
36
+ value
37
+ when Symbol, String
38
+ spacing = SPACING[value.to_sym]
39
+ raise ArgumentError, "Invalid spacing: #{value.inspect}" unless spacing
40
+ spacing
41
+ else
42
+ raise ArgumentError, "Invalid spacing type: #{value.class}"
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.Separator(**opts)
49
+ Components::Separator.new(**opts)
50
+ end
51
+ end
@@ -0,0 +1,23 @@
1
+ require_relative "base"
2
+
3
+ module Rubord
4
+ module Components
5
+ class Text < BaseComponent
6
+ def initialize(content)
7
+ super(10)
8
+ @content = content
9
+ end
10
+
11
+ def to_h
12
+ {
13
+ type: @type,
14
+ content: @content
15
+ }
16
+ end
17
+ end
18
+ end
19
+
20
+ def self.Text(content)
21
+ Components::Text.new(content)
22
+ end
23
+ end
@@ -0,0 +1,134 @@
1
+ # components/modal.rb
2
+
3
+ module Rubord
4
+ # Represents a Discord modal dialog component.
5
+ #
6
+ # Modals are pop-up dialog windows that can contain text inputs.
7
+ # They're triggered by interactions and allow for more complex user input.
8
+ #
9
+ # @example Creating a simple modal
10
+ # modal = Rubord::Modal.new(
11
+ # custom_id: "feedback_form",
12
+ # title: "Submit Feedback"
13
+ # )
14
+ # modal.add_text_input(
15
+ # custom_id: "feedback",
16
+ # label: "Your feedback",
17
+ # style: 2,
18
+ # placeholder: "Type your feedback here..."
19
+ # )
20
+ #
21
+ # @since 1.0.0
22
+ # @see https://discord.com/developers/docs/interactions/modals
23
+ class Modal
24
+ # @return [Integer] Component type (always 1 for modals).
25
+ attr_accessor :type
26
+
27
+ # @return [String] Developer-defined identifier.
28
+ attr_accessor :custom_id
29
+
30
+ # @return [String] Modal title (max 45 characters).
31
+ attr_accessor :title
32
+
33
+ # @return [Array<Hash>] List of text input components.
34
+ attr_accessor :components
35
+
36
+ # Creates a new modal dialog.
37
+ #
38
+ # @param custom_id [String] Developer-defined identifier.
39
+ # @param title [String] Modal title (max 45 characters).
40
+ #
41
+ # @example
42
+ # Modal.new(
43
+ # custom_id: "user_registration",
44
+ # title: "Register Account"
45
+ # )
46
+ def initialize(custom_id:, title:)
47
+ @type = 1
48
+ @custom_id = custom_id
49
+ @title = title
50
+ @components = []
51
+ end
52
+
53
+ # Adds a text input field to the modal.
54
+ #
55
+ # @param custom_id [String] Developer-defined identifier for the input.
56
+ # @param label [String] Label displayed above the input (max 45 characters).
57
+ # @param style [Integer] Input style (1=single-line, 2=multi-line).
58
+ # Defaults to 1.
59
+ # @param min_length [Integer, nil] Minimum input length.
60
+ # @param max_length [Integer, nil] Maximum input length.
61
+ # @param required [Boolean] Whether the input is required.
62
+ # Defaults to true.
63
+ # @param value [String, nil] Pre-filled value.
64
+ # @param placeholder [String, nil] Placeholder text.
65
+ #
66
+ # @return [Rubord::Modal] Self for method chaining.
67
+ #
68
+ # @example Single-line required input
69
+ # modal.add_text_input(
70
+ # custom_id: "username",
71
+ # label: "Username",
72
+ # required: true,
73
+ # min_length: 3,
74
+ # max_length: 20
75
+ # )
76
+ #
77
+ # @example Multi-line optional input
78
+ # modal.add_text_input(
79
+ # custom_id: "bio",
80
+ # label: "Biography",
81
+ # style: 2,
82
+ # required: false,
83
+ # placeholder: "Tell us about yourself...",
84
+ # max_length: 500
85
+ # )
86
+ def add_text_input(custom_id:, label:, style: 1, min_length: nil, max_length: nil, required: true, value: nil, placeholder: nil)
87
+ @components << {
88
+ type: 4,
89
+ custom_id: custom_id,
90
+ style: style,
91
+ label: label,
92
+ min_length: min_length,
93
+ max_length: max_length,
94
+ required: required,
95
+ value: value,
96
+ placeholder: placeholder
97
+ }.compact
98
+ self
99
+ end
100
+
101
+ # Converts the modal to a Discord API-compatible hash.
102
+ #
103
+ # @return [Hash] Modal data in Discord API format.
104
+ #
105
+ # @example
106
+ # modal = Modal.new(custom_id: "test", title: "Test Modal")
107
+ # modal.to_h
108
+ # # => {type: 1, custom_id: "test", title: "Test Modal", components: [{type: 1, components: []}]}
109
+ def to_h
110
+ {
111
+ type: @type,
112
+ custom_id: @custom_id,
113
+ title: @title,
114
+ components: [
115
+ {
116
+ type: 1,
117
+ components: @components
118
+ }
119
+ ]
120
+ }
121
+ end
122
+ end
123
+
124
+ # Factory method for creating Modal instances.
125
+ #
126
+ # @param args [Hash] Modal initialization parameters.
127
+ # @return [Rubord::Modal] New modal instance.
128
+ #
129
+ # @example
130
+ # modal = Rubord.Modal(custom_id: "form", title: "Feedback Form")
131
+ def self.Modal(**args)
132
+ Modal.new(**args)
133
+ end
134
+ end