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
@@ -0,0 +1,147 @@
1
+ # components/select_menu.rb
2
+
3
+ module Rubord
4
+ # Represents a Discord select menu component.
5
+ #
6
+ # Select menus are dropdown components that allow users to select
7
+ # one or multiple options from a list.
8
+ #
9
+ # @example Creating a single-select menu
10
+ # menu = Rubord::SelectMenu.new(
11
+ # custom_id: "game_selection",
12
+ # placeholder: "Choose a game..."
13
+ # )
14
+ # menu.add_option(label: "Minecraft", value: "minecraft")
15
+ # menu.add_option(label: "Terraria", value: "terraria")
16
+ #
17
+ # @example Creating a multi-select menu
18
+ # menu = Rubord::SelectMenu.new(
19
+ # custom_id: "hobbies",
20
+ # placeholder: "Select your hobbies",
21
+ # min_values: 1,
22
+ # max_values: 3
23
+ # )
24
+ #
25
+ # @since 1.0.0
26
+ # @see https://discord.com/developers/docs/interactions/message-components#select-menus
27
+ class SelectMenu
28
+ # @return [Integer] Component type (always 3 for select menus).
29
+ attr_accessor :type
30
+
31
+ # @return [String] Developer-defined identifier.
32
+ attr_accessor :custom_id
33
+
34
+ # @return [Array<Hash>] List of available options.
35
+ attr_accessor :options
36
+
37
+ # @return [String, nil] Placeholder text displayed when no option is selected.
38
+ attr_accessor :placeholder
39
+
40
+ # @return [Integer] Minimum number of options that must be selected.
41
+ # Defaults to 1.
42
+ attr_accessor :min_values
43
+
44
+ # @return [Integer] Maximum number of options that can be selected.
45
+ # Defaults to 1.
46
+ attr_accessor :max_values
47
+
48
+ # @return [Boolean] Whether the select menu is disabled.
49
+ attr_accessor :disabled
50
+
51
+ # Creates a new select menu component.
52
+ #
53
+ # @param custom_id [String] Developer-defined identifier.
54
+ # @param placeholder [String, nil] Placeholder text.
55
+ # @param min_values [Integer] Minimum selectable options.
56
+ # Defaults to 1.
57
+ # @param max_values [Integer] Maximum selectable options.
58
+ # Defaults to 1.
59
+ # @param disabled [Boolean] Whether the menu is disabled.
60
+ # Defaults to false.
61
+ #
62
+ # @example Basic select menu
63
+ # SelectMenu.new(
64
+ # custom_id: "color_picker",
65
+ # placeholder: "Choose a color",
66
+ # min_values: 1,
67
+ # max_values: 1
68
+ # )
69
+ #
70
+ # @example Multi-select menu
71
+ # SelectMenu.new(
72
+ # custom_id: "tags",
73
+ # placeholder: "Select up to 3 tags",
74
+ # min_values: 0,
75
+ # max_values: 3
76
+ # )
77
+ def initialize(custom_id:, placeholder: nil, min_values: 1, max_values: 1, disabled: false)
78
+ @type = 3
79
+ @custom_id = custom_id
80
+ @options = []
81
+ @placeholder = placeholder
82
+ @min_values = min_values
83
+ @max_values = max_values
84
+ @disabled = disabled
85
+ end
86
+
87
+ # Adds an option to the select menu.
88
+ #
89
+ # @param label [String] The user-facing name of the option (max 100 chars).
90
+ # @param value [String] The developer-defined value of the option (max 100 chars).
91
+ # @param description [String, nil] Additional description (max 100 chars).
92
+ # @param emoji [Hash, nil] Emoji to display with the option.
93
+ # @param default [Boolean] Whether this option is selected by default.
94
+ # Defaults to false.
95
+ #
96
+ # @return [Rubord::SelectMenu] Self for method chaining.
97
+ #
98
+ # @example Adding an option with description
99
+ # menu.add_option(
100
+ # label: "Ruby",
101
+ # value: "ruby_lang",
102
+ # description: "Dynamic, object-oriented programming language",
103
+ # emoji: { name: "💎" }
104
+ # )
105
+ def add_option(label:, value:, description: nil, emoji: nil, default: false)
106
+ @options << {
107
+ label: label,
108
+ value: value,
109
+ description: description,
110
+ emoji: emoji,
111
+ default: default
112
+ }.compact
113
+ self
114
+ end
115
+
116
+ # Converts the select menu to a Discord API-compatible hash.
117
+ #
118
+ # @return [Hash] Select menu data in Discord API format.
119
+ #
120
+ # @example
121
+ # menu = SelectMenu.new(custom_id: "test")
122
+ # menu.to_h
123
+ # # => {type: 3, custom_id: "test", options: [], min_values: 1, max_values: 1, disabled: false}
124
+ def to_h
125
+ {
126
+ type: @type,
127
+ custom_id: @custom_id,
128
+ options: @options,
129
+ placeholder: @placeholder,
130
+ min_values: @min_values,
131
+ max_values: @max_values,
132
+ disabled: @disabled
133
+ }.compact
134
+ end
135
+ end
136
+
137
+ # Factory method for creating SelectMenu instances.
138
+ #
139
+ # @param args [Hash] Select menu initialization parameters.
140
+ # @return [Rubord::SelectMenu] New select menu instance.
141
+ #
142
+ # @example
143
+ # menu = Rubord.SelectMenu(custom_id: "choices", placeholder: "Make a choice")
144
+ def self.SelectMenu(**args)
145
+ SelectMenu.new(**args)
146
+ end
147
+ end
@@ -0,0 +1,50 @@
1
+ module Rubord
2
+ class Channel
3
+ attr_reader :id,
4
+ :name,
5
+ :type,
6
+ :client
7
+
8
+ def initialize(data, client)
9
+ @id = data["id"]
10
+ @name = data["name"]
11
+ @type = data["type"]
12
+ @client = client
13
+
14
+ client&.channels&.set(@id, self)
15
+ end
16
+
17
+ def post(content = nil, embeds: nil, components: nil, flags: nil)
18
+ ensure_client!
19
+
20
+ client.rest.send_message(
21
+ @id,
22
+ content: content,
23
+ embeds: embeds,
24
+ components: components,
25
+ flags: flags
26
+ )
27
+ end
28
+
29
+ def fetch_message(message_id)
30
+ client.rest.get_message(@id, message_id)
31
+ end
32
+
33
+ def guild
34
+ return nil unless client && client.respond_to?(:guilds)
35
+ client.guilds.get(guild_id)
36
+ end
37
+
38
+ def text?
39
+ type == 0
40
+ end
41
+
42
+ def voice?
43
+ type == 2
44
+ end
45
+
46
+ def inspect
47
+ "#<Rubord::Channel id=#{id} name=#{name.inspect} type=#{type}>"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,70 @@
1
+ module Rubord
2
+ class Collection
3
+ include Enumerable
4
+
5
+ def initialize
6
+ @store = {}
7
+ end
8
+
9
+ def [](key)
10
+ @store[key]
11
+ end
12
+
13
+ def []=(key, value)
14
+ @store[key] = value
15
+ end
16
+
17
+ def get(key)
18
+ @store[key]
19
+ end
20
+
21
+ def set(key, value)
22
+ @store[key] = value
23
+ self
24
+ end
25
+
26
+ def delete(key)
27
+ @store.delete(key)
28
+ end
29
+
30
+ def each(&block)
31
+ @store.each_value(&block)
32
+ end
33
+
34
+ def map(&block)
35
+ @store.values.map(&block)
36
+ end
37
+
38
+ def filter(&block)
39
+ self.class.new.tap do |col|
40
+ @store.each do |k, v|
41
+ col.set(k, v) if block.call(v)
42
+ end
43
+ end
44
+ end
45
+
46
+ def find(&block)
47
+ @store.values.find(&block)
48
+ end
49
+
50
+ def values
51
+ @store.values
52
+ end
53
+
54
+ def keys
55
+ @store.keys
56
+ end
57
+
58
+ def size
59
+ @store.size
60
+ end
61
+
62
+ def empty?
63
+ @store.empty?
64
+ end
65
+
66
+ def to_h
67
+ @store.dup
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,111 @@
1
+ module Rubord
2
+ # Base class for creating commands.
3
+ #
4
+ # @example Creating a simple command
5
+ # class PingCommand < Rubord::CommandBase
6
+ # name "ping"
7
+ # description "Responds with pong"
8
+ #
9
+ # # @yieldparam message [Rubord::Message] The message that triggered the command
10
+ # def run(message)
11
+ # message.reply("Pong!")
12
+ # end
13
+ # end
14
+ #
15
+ # @abstract
16
+ class CommandBase
17
+ class << self
18
+ attr_reader :command_name,
19
+ :description_text,
20
+ :command_usage,
21
+ :aliases_list,
22
+ :cooldown_seconds,
23
+ :guild_only_flag,
24
+ :dm_only_flag
25
+
26
+ def inherited(subclass)
27
+ super
28
+ subclass.instance_variable_set(:@aliases_list, [])
29
+ subclass.instance_variable_set(:@cooldown_seconds, 0)
30
+ subclass.instance_variable_set(:@guild_only_flag, false)
31
+ subclass.instance_variable_set(:@dm_only_flag, false)
32
+ end
33
+
34
+ def name(value)
35
+ @command_name = value.to_s
36
+ end
37
+
38
+ def description(value)
39
+ @description_text = value.to_s
40
+ end
41
+
42
+ def aliases(*values)
43
+ @aliases_list.concat(values.map(&:to_s))
44
+ end
45
+
46
+ def cooldown(seconds)
47
+ @cooldown_seconds = seconds.to_i
48
+ end
49
+
50
+ def guild_only(value = true)
51
+ @guild_only_flag = value
52
+ end
53
+
54
+ def dm_only(value = true)
55
+ @dm_only_flag = value
56
+ end
57
+
58
+ def validate!
59
+ raise ArgumentError, "Command #{self} is missing a name" unless @command_name
60
+ end
61
+ end
62
+
63
+ attr_reader :client
64
+
65
+ def initialize(client)
66
+ @client = client
67
+ self.class.validate!
68
+ end
69
+
70
+ # @return [String]
71
+ def name
72
+ self.class.command_name
73
+ end
74
+
75
+ # @return [String]
76
+ def description
77
+ self.class.description_text
78
+ end
79
+
80
+ # @return [Array<String>]
81
+ def aliases
82
+ self.class.aliases_list
83
+ end
84
+
85
+ # @return [Integer]
86
+ def cooldown
87
+ self.class.cooldown_seconds
88
+ end
89
+
90
+ def guild_only?
91
+ self.class.guild_only_flag
92
+ end
93
+
94
+ def dm_only?
95
+ self.class.dm_only_flag
96
+ end
97
+
98
+ # Executes the command.
99
+ #
100
+ # @param message [Rubord::Message]
101
+ # @param args [Array<String>]
102
+ # @abstract
103
+ def run(_message, _args = [])
104
+ raise NotImplementedError, "#{self.class} must implement #run(message)"
105
+ end
106
+
107
+ def inspect
108
+ "#<#{self.class} name=#{command_name.inspect}>"
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ require_relative "base.rb"
2
+ require_relative "loader.rb"
3
+ require_relative "registry.rb"
@@ -0,0 +1,36 @@
1
+ # loader.rb
2
+ module Rubord
3
+ class CommandLoader
4
+ def self.load(path, client, registry)
5
+ full_path = File.expand_path(path)
6
+ files = Dir["#{full_path}/**/*.rb"]
7
+
8
+ files.each do |file|
9
+ begin
10
+ Kernel.load(file)
11
+ rescue => e
12
+ puts "[Rubord:Commands] ERRO ao carregar #{file}:"
13
+ puts " #{e.class}: #{e.message}"
14
+ end
15
+ end
16
+
17
+ found_classes = []
18
+ ObjectSpace.each_object(Class) do |klass|
19
+ next if klass == Rubord::CommandBase
20
+
21
+ if klass.ancestors.include?(Rubord::CommandBase)
22
+ found_classes << klass
23
+ end
24
+ end
25
+
26
+ found_classes.each do |klass|
27
+ begin
28
+ registry.register(klass, client)
29
+ rescue => e
30
+ puts "[Rubord:Commands] ERRO ao registrar #{klass}:"
31
+ puts " #{e.class}: #{e.message}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ module Rubord
2
+ class CommandRegistry
3
+ def initialize
4
+ @commands = {}
5
+ @command_classes = {}
6
+ end
7
+
8
+ def register(klass, client)
9
+ name = klass.command_name
10
+ raise "Command missing name" unless name
11
+
12
+ @command_classes[name] = klass
13
+ @commands[name] = klass.new(client)
14
+
15
+ Rubord::Logger.success "[Rubord:Commands] Registered command: #{name}"
16
+ end
17
+
18
+ def get(name)
19
+ @commands[name]
20
+ end
21
+
22
+ def all
23
+ @commands.values.freeze
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "../components/button"
2
+ require_relative "../components/select_menu"
3
+ require_relative "../components/modal"
4
+ require_relative "../components/actionRow.rb"
5
+ require_relative "../components/componentsV2.rb"
@@ -0,0 +1,87 @@
1
+ module Rubord
2
+ class Embed
3
+ attr_accessor :title,
4
+ :description,
5
+ :url,
6
+ :timestamp,
7
+ :color,
8
+ :footer,
9
+ :image,
10
+ :thumbnail,
11
+ :author,
12
+ :fields
13
+
14
+ def initialize
15
+ @fields = []
16
+ end
17
+
18
+ def title(text)
19
+ @title = text
20
+ self
21
+ end
22
+
23
+ def description(text)
24
+ @description = text
25
+ self
26
+ end
27
+
28
+ def url(link)
29
+ @url = link
30
+ self
31
+ end
32
+
33
+ def timestamp(time = Time.now)
34
+ @timestamp = time.utc.iso8601
35
+ self
36
+ end
37
+
38
+ def color(hex)
39
+ @color = Rubord.Parser.color(hex)
40
+ self
41
+ end
42
+
43
+ def footer(text:, icon_url: nil)
44
+ @footer = { text: text, icon_url: icon_url }
45
+ self
46
+ end
47
+
48
+ def image(url)
49
+ @image = { url: url }
50
+ self
51
+ end
52
+
53
+ def thumbnail(url)
54
+ @thumbnail = { url: url }
55
+ self
56
+ end
57
+
58
+ def author(name:, url: nil, icon_url: nil)
59
+ @author = { name: name, url: url, icon_url: icon_url }
60
+ self
61
+ end
62
+
63
+ def add_field(name, value, inline = false)
64
+ @fields << { name: name, value: value, inline: inline }
65
+ self
66
+ end
67
+
68
+ def to_h
69
+ {
70
+ title: @title,
71
+ description: @description,
72
+ url: @url,
73
+ timestamp: @timestamp,
74
+ color: @color,
75
+ footer: @footer,
76
+ image: @image,
77
+ thumbnail: @thumbnail,
78
+ author: @author,
79
+ fields: @fields.empty? ? nil : @fields
80
+ }.compact
81
+ end
82
+ end
83
+
84
+ def self.Embed
85
+ Embed.new
86
+ end
87
+ end