discorb 0.4.2 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7322e6131e0e5d070df790e4a4b7ea6ea6ea7a286c975b0732c0c7eff2e9903d
4
- data.tar.gz: 045ef3ab63628e0e0fe867de4aef8b1f872076cd74912b38e035c8cb6907c731
3
+ metadata.gz: 5ec867460217dad0b51aff3a636a564a2f2bfc881300861551b18c6bb249c4d9
4
+ data.tar.gz: 823fbab066d668f642e76c526c432fef93d3906e6f9934eaededd3b759cb299b
5
5
  SHA512:
6
- metadata.gz: f2b045e2b31b6a8fb95d74f87de931b30e67c8f34f647a0994b4feb4f3c3d8c270f2fddab03e995cce4eaf2f770d2698ae537e0e5d328152dc0244f3e6f5c59d
7
- data.tar.gz: 3582014d604c85091c3c6180a13766ccab7dce8e0df0319278eb0d89e7cd0d0df2d236b3a58763cc55e5ccf80ef6c46317ad62028c30f0d89002bf48f1bc4b8b
6
+ metadata.gz: 75a6ba486bb056d1ae2fc526ac24934b53b81b05bc10f7c435d87db74479490b342b9f4a38fd7e36cd78a3e89931637960702fd728835f30660b732c3645c5ca
7
+ data.tar.gz: 39233f9f24c1753f33a4a7ff866c3e64370809e1d40e228112286cbe1c3d19a7bd6915503206f4b5eca18935f8c1a4ebeff20991d62321243ae5a6b79c9a9b57
data/.yardopts CHANGED
@@ -3,6 +3,7 @@
3
3
  --markup markdown
4
4
  --tag flags:"Flags"
5
5
  --verbose
6
+ --asset docs/assets:assets
6
7
  -
7
8
  docs/**/*.md
8
9
  Changelog.md
data/Changelog.md CHANGED
@@ -98,4 +98,29 @@
98
98
 
99
99
  ## 0.4.2
100
100
 
101
- - Fix: Fix error in `discorb run`
101
+ - Fix: Fix error in `discorb run`
102
+
103
+ ## 0.5.0
104
+
105
+ - Change: Use zlib stream instead
106
+ - Add: Add tutorials
107
+ - Add: Add ratelimit handler
108
+ - Change: Make `--git` option in `discorb init` false
109
+
110
+ ## 0.5.1
111
+
112
+ - Add: Can use block for defining group commands
113
+ - Fix: Fix bug in subcommands
114
+ - Fix: Fix bug in receiving commands
115
+
116
+ ## 0.5.2
117
+
118
+ - Fix: Fix bug of registering commands
119
+ - Add: Add way to register commands in Extension
120
+
121
+ ## 0.5.3
122
+
123
+ - Add: Add way to handle raw events with `event_xxx`
124
+ - Add: Add `Client#session_id`
125
+ - Add: Add `Connectable`
126
+ - Fix: Fix error by sending DM
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- discorb (0.4.2)
4
+ discorb (0.5.3)
5
5
  async (~> 1.30.1)
6
6
  async-http (~> 0.56.5)
7
7
  async-websocket (~> 0.19.0)
data/README.md CHANGED
@@ -2,7 +2,8 @@
2
2
  [![Document](https://img.shields.io/badge/Document-discord--lib.github.io-blue.svg)](https://discorb-lib.github.io/)
3
3
  [![Gem](https://img.shields.io/gem/dt/discorb?logo=rubygems&logoColor=fff)](https://rubygems.org/gems/discorb)
4
4
  [![Gem](https://img.shields.io/gem/v/discorb?logo=rubygems&logoColor=fff)](https://rubygems.org/gems/discorb)
5
- [![Discord](https://img.shields.io/discord/863581274916913193?logo=discord&logoColor=fff&color=5865f2&label=Discord)](https://discord.gg/hCP6zq8Vpj)
5
+ [![Discord](https://img.shields.io/discord/863581274916913193?logo=discord&logoColor=fff&color=5865f2&label=Discord)](https://discord.gg/hCP6zq8Vpj)
6
+ [![GitHub](https://img.shields.io/github/stars/discorb-lib/discorb?color=24292e&label=Stars&logo=GitHub&logoColor=fff)](https://github.com/discorb-lib/discorb)
6
7
 
7
8
  discorb is a Discord API wrapper for Ruby.
8
9
 
data/Rakefile CHANGED
@@ -117,6 +117,7 @@ namespace :document do
117
117
  iputs "Replacing CRLF with LF"
118
118
  Dir.glob("doc/**/*.*") do |file|
119
119
  next unless File.file?(file)
120
+ next unless %w[html css js].include? file.split(".").last
120
121
 
121
122
  content = ""
122
123
  File.open(file, "rb") do |f|
@@ -162,13 +163,9 @@ namespace :document do
162
163
  FileUtils.cp_r("./doc/.", "./tmp-doc")
163
164
  FileUtils.rm_rf("doc")
164
165
  end
165
- version = "."
166
- Rake::Task["document:yard"].execute
167
- Rake::Task["document:replace:html"].execute
168
- Rake::Task["document:replace:css"].execute
169
- Rake::Task["document:replace:eol"].execute
170
166
  sh "git switch main -f"
171
167
  FileUtils.cp_r("./tmp-doc/.", "./doc")
168
+ FileUtils.cp_r("./doc/#{tags.last.delete_prefix("v")}/.", "./doc")
172
169
  sputs "Successfully built all versions"
173
170
  rescue => e
174
171
  sh "git switch main -f"
@@ -53,11 +53,11 @@ In `options`, hash should be like this:
53
53
 
54
54
  ```ruby
55
55
  {
56
- "Name" => {
57
- type: :string,
58
- required: true,
59
- description: "The description of the command."
60
- }
56
+ "Name" => {
57
+ type: :string,
58
+ required: true,
59
+ description: "The description of the command."
60
+ }
61
61
  }
62
62
  ```
63
63
 
@@ -73,19 +73,19 @@ In `options`, hash should be like this:
73
73
 
74
74
  ```ruby
75
75
  {
76
- "vocaloid" => {
77
- required: true,
78
- description: "The vocaloid which you like.",
79
- type: :string,
80
- choices: {
81
- "Hatsune Miku" => "miku",
82
- "Kagamine Rin" => "rin",
83
- "Kagamine Len" => "len",
84
- "Megurine Luka" => "luka",
85
- "MEIKO" => "meiko",
86
- "KAITO" => "kaito",
87
- }
76
+ "vocaloid" => {
77
+ required: true,
78
+ description: "The vocaloid which you like.",
79
+ type: :string,
80
+ choices: {
81
+ "Hatsune Miku" => "miku",
82
+ "Kagamine Rin" => "rin",
83
+ "Kagamine Len" => "len",
84
+ "Megurine Luka" => "luka",
85
+ "MEIKO" => "meiko",
86
+ "KAITO" => "kaito",
88
87
  }
88
+ }
89
89
  }
90
90
 
91
91
  # Note: This aritcle is written in 8/31.
@@ -113,22 +113,47 @@ To register a group of slash commands, use {Discorb::Command::Handler#slash_grou
113
113
  group = client.slash_group("settings", "Set settings of bot.")
114
114
 
115
115
  group.slash("message_expand", "Whether bot should expand message.", {
116
- "enabled" => {
117
- type: :boolean,
118
- description: "Whether bot should expand message."
119
- }
116
+ "enabled" => {
117
+ type: :boolean,
118
+ description: "Whether bot should expand message."
119
+ }
120
120
  }) do |interaction|
121
121
  # ...
122
122
  end
123
123
 
124
124
  group.slash("bump_alert", "Whether bot should notify DISBOARD bump.", {
125
- "enabled" => {
126
- type: :boolean,
127
- description: "Whether bot should notify DISBOARD bump."
128
- }
125
+ "enabled" => {
126
+ type: :boolean,
127
+ description: "Whether bot should notify DISBOARD bump."
128
+ }
129
129
  }) do |interaction|
130
130
  # ...
131
131
  end
132
+
133
+ ```
134
+
135
+ Since v0.5.1, You can use block for register commands.
136
+
137
+ ```ruby
138
+
139
+ client.slash_group("settings", "Set settings of bot.") do
140
+ slash("message_expand", "Whether bot should expand message.", {
141
+ "enabled" => {
142
+ type: :boolean,
143
+ description: "Whether bot should expand message."
144
+ }
145
+ }) do |interaction|
146
+ # ...
147
+ end
148
+ slash("bump_alert", "Whether bot should notify DISBOARD bump.", {
149
+ "enabled" => {
150
+ type: :boolean,
151
+ description: "Whether bot should notify DISBOARD bump."
152
+ }
153
+ }) do |interaction|
154
+ # ...
155
+ end
156
+ end
132
157
  ```
133
158
 
134
159
  You can make subcommand group by using {Discorb::Command::Command::GroupCommand#group}.
@@ -210,6 +235,8 @@ end
210
235
 
211
236
  ```
212
237
 
238
+ Same as above, you can use block for register commands since v0.5.1.
239
+
213
240
  ### Register User Context Menu Command
214
241
 
215
242
  ```ruby
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/docs/cli/init.md CHANGED
@@ -30,7 +30,7 @@ Default to true.
30
30
  Whether to initialize git.
31
31
  If true, the command will initialize git and commit the initial files with commit message `Initial commit`.
32
32
  Use `git commit --amend -m "..."` to change the commit message.
33
- Default to true.
33
+ Default to false.
34
34
 
35
35
  ### `-t`, `--token`
36
36
 
data/docs/cli.md CHANGED
@@ -21,5 +21,6 @@ Currently, discorb has the following commands:
21
21
  | {file:docs/cli/run.md `run`} | Run a client. |
22
22
  | {file:docs/cli/setup.md `setup`} | Setup application commands. |
23
23
  | `show` | Show your environment. (No document) |
24
+ | `about` | Show discorb's information. (No document) |
24
25
 
25
26
  Click the command name to see the document.
data/docs/events.md CHANGED
@@ -219,14 +219,13 @@ Fires when a guild integration is created.
219
219
  | ---------- | ----- | ----------- |
220
220
  |`integration`| {Discorb::Integration}| The created integration. |
221
221
 
222
- #### `integration_update(before, after)`
222
+ #### `integration_update(after)`
223
223
 
224
224
  Fires when a guild integration is updated.
225
225
 
226
226
 
227
227
  | Parameter | Type | Description |
228
228
  | ---------- | ----- | ----------- |
229
- |`before` | {Discorb::Integration}| The integration before the update. |
230
229
  |`after` | {Discorb::Integration}| The integration after the update. |
231
230
 
232
231
  #### `integration_delete(integration)`
data/docs/extension.md CHANGED
@@ -34,6 +34,33 @@ module MyExtension
34
34
  end
35
35
  ```
36
36
 
37
+ ## Register Command
38
+
39
+ Since v0.5.2, {Discorb::Extension} includes {Discorb::Command::Handler} module, so you can register command with {Discorb::Command::Handler#slash} and {Discorb::Command::Handler#slash_group}.
40
+
41
+ ```ruby
42
+ module MyExtension
43
+ extend Discorb::Extension
44
+
45
+ slash("command", "Command") do |interaction|
46
+ # ...
47
+ end
48
+
49
+ slash_group("group", "Group") do
50
+ slash("subcommand", "Subcommand") do |interaction|
51
+ # ...
52
+ end
53
+
54
+ group("subgroup", "Subcommand group") do
55
+ slash("group_subcommand", "Command in Subcommand group") do |interaction|
56
+ # ...
57
+ end
58
+ end
59
+ end
60
+ end
61
+ ```
62
+
63
+
37
64
  ## Load extension
38
65
 
39
66
  Use {Discorb::Client#extend} to load extension.
data/docs/tutorial.md ADDED
@@ -0,0 +1,194 @@
1
+ # @title Tutorial
2
+
3
+ # Tutorial
4
+
5
+ Welcome to discorb! This lib allows you to create a discord bot with ease. So, let's get started!
6
+
7
+ ## Requirements
8
+
9
+ - Ruby 3.0.0+
10
+ - Basic knowledge of ruby
11
+ These documents will help you:
12
+ - [Ruby in Twenty Minutes](https://www.ruby-lang.org/en/documentation/quickstart/)
13
+ - [Ruby from other languages](https://www.ruby-lang.org/en/documentation/ruby-from-other-languages/)
14
+ - [Try ruby!](https://try.ruby-lang.org/)
15
+
16
+ ### Recommended
17
+
18
+ - Good editor
19
+ They are recommended:
20
+ - [VSCode](https://code.visualstudio.com/)
21
+ - [Atom](https://atom.io/)
22
+ - [Sublime Text](https://www.sublimetext.com/)
23
+ - [Brackets](https://brackets.io/)
24
+ - [Notepad++](https://notepad-plus-plus.org/)
25
+ - Git
26
+ - Bundler
27
+
28
+ Once you have all of these, you can start coding!
29
+
30
+ ## Start creating your bot
31
+
32
+ ### Create a Bot account
33
+
34
+ You must have a bot account to use this lib. First, go to [Discord Developer Portal](https://discord.com/developers/applications) and click on `New Application`.
35
+ ![](./assets/01_new_app.png)
36
+
37
+ And then type a name for your bot, and click `Create`.
38
+ ![](./assets/02_bot_name.png)
39
+
40
+ You will be redirected to the `General Information` page.
41
+ Then, click `Bot` and then `Add Bot` and then `Yes, do it!`.
42
+ ![](./assets/03_add_bot.png)
43
+
44
+ You will see bot information, and click `Copy` button in TOKEN section.
45
+ ![](./assets/04_token_copy.png)
46
+
47
+ **DO NOT SHARE THIS TOKEN, OR YOUR BOT BAN EVERYONE IN EVERY SERVER!**
48
+ This is serious security risk.
49
+
50
+ Click `Regenerate` button to regenerate your token. Do this immediately when you accidentally share your token.
51
+
52
+ You did it! Now, you have a bot account.
53
+
54
+ #### Invite your bot to a server
55
+
56
+ Go to `OAuth2` page and scroll down, and check `bot` and `applications.commands` permissions.
57
+ ![](./assets/05_oauth.png)
58
+ Then, click `Copy` button and paste it to your browser.
59
+ Choose a server you want to invite your bot to, and follow the instructions.
60
+
61
+ ### Code your bot
62
+
63
+ #### Install gems
64
+
65
+ Open terminal and type:
66
+
67
+ ```
68
+ gem install bundler discorb
69
+ ```
70
+
71
+ #### Setup files
72
+
73
+ Create a new directory and go to it.
74
+ Open terminal and type:
75
+
76
+ ```
77
+ discorb init
78
+ ```
79
+
80
+ Specify `--git` if you want to use git.
81
+
82
+ You will get some files in your directory.
83
+
84
+ - `main.rb`: The main file of your bot.
85
+ - `.env`: The environment variables of your bot. **You must keep this file secret!**
86
+ - `Gemfile`: Gemfile for bundler.
87
+ - `Gemfile.lock`: Gemfile.lock for bundler.
88
+
89
+ You will get other files if you specify `--git`.
90
+ {file:docs/cli/init.md Learn more here}.
91
+
92
+ #### Start your bot
93
+
94
+ Open `main.rb`, you will see the following code:
95
+
96
+ ```ruby
97
+ require "discorb"
98
+ require "dotenv"
99
+
100
+ Dotenv.load # Loads .env file
101
+
102
+ client = Discorb::Client.new # Create client for connecting to Discord
103
+
104
+ client.once :ready do
105
+ puts "Logged in as #{client.user}" # Prints username of logged in user
106
+ end
107
+
108
+ client.run ENV["TOKEN"] # Starts client
109
+ ```
110
+
111
+ Open `.env`, you will see:
112
+
113
+ ```
114
+ TOKEN=Y0urB0tT0k3nHer3.Th1sT0ken.W0ntWorkB3c4useItH4sM34n1ng
115
+ ```
116
+
117
+ Replace `Y0urB0tT0k3nHer3.Th1sT0ken.W0ntWorkB3c4useItH4sM34n1ng` with your bot token.
118
+ Remember to keep this file secret!
119
+
120
+ Open terminal and type:
121
+
122
+ ```sh
123
+ bundle exec ruby main.rb
124
+ # or
125
+ bundle exec discorb run main.rb
126
+ ```
127
+
128
+ Yay! Your bot is online!
129
+ ![](./assets/06_online.png)
130
+
131
+ But your bot won't do anything.
132
+ So add your bot some greetings!
133
+ `Ctrl + C` to stop your bot.
134
+
135
+ #### Add a greeting
136
+
137
+ You can do some action on message by typing like this:
138
+
139
+ ```ruby
140
+ client.on :message do |message|
141
+ # ...
142
+ end
143
+ ```
144
+
145
+ `message` is a {Discorb::Message} object. It contains information about the message.
146
+ You can get the message content by {Discorb::Message#content}.
147
+ Add `if` statement, and reply to the message with {Discorb::Message#reply}.
148
+
149
+ ```ruby
150
+ client.on :message do |message|
151
+ if message.content.downcase.include? "hello"
152
+ message.reply "Hello!"
153
+ end
154
+ end
155
+ ```
156
+
157
+ Save your bot and restart it.
158
+
159
+ You can see your bot's response by typing `hello` in your server...
160
+
161
+ ![](./assets/07_hello_infinite.png)
162
+
163
+ Oh no! Your bot is responding to bot's messages, and it doesn't stop!
164
+
165
+ Terminate your bot by typing `Ctrl + C` in terminal.
166
+
167
+ #### Ignore bot's messages
168
+
169
+ You can access author information by {Discorb::Message#author}, and it has {Discorb::User#bot?}.
170
+ So, you can ignore bot's messages by adding `if` statement:
171
+
172
+ ```ruby
173
+ client.on :message do |message|
174
+ next if message.author.bot?
175
+
176
+ # ...
177
+ end
178
+ ```
179
+
180
+ Note you must use `next` to exit the block.
181
+
182
+ Save your bot and start it.
183
+
184
+ ![](./assets/08_hello_once.png)
185
+
186
+ You did it! Your bot won't respond to bot's messages anymore.
187
+
188
+ ## Finally
189
+
190
+ This is the end of tutorial.
191
+
192
+ To learn more, check out the [documentation](https://discorb-lib.github.io/).
193
+
194
+ We hope you enjoy this lib! Thanks for reading!
@@ -636,7 +636,6 @@ module Discorb
636
636
 
637
637
  #
638
638
  # Represents a voice channel.
639
- # @todo Implement connecting to voice channel.
640
639
  #
641
640
  class VoiceChannel < GuildChannel
642
641
  # @return [Integer] The bitrate of the voice channel.
@@ -645,6 +644,8 @@ module Discorb
645
644
  # @return [nil] If the user limit is not set.
646
645
  attr_reader :user_limit
647
646
 
647
+ include Connectable
648
+
648
649
  @channel_type = 2
649
650
  #
650
651
  # Edit the voice channel.
@@ -699,6 +700,8 @@ module Discorb
699
700
  # @!visibility private
700
701
  attr_reader :stage_instances
701
702
 
703
+ include Connectable
704
+
702
705
  # @!attribute [r] stage_instance
703
706
  # @return [Discorb::StageInstance] The stage instance of the channel.
704
707
 
@@ -49,6 +49,10 @@ module Discorb
49
49
  attr_reader :ping
50
50
  # @return [:initialized, :running, :closed] The status of the client.
51
51
  attr_reader :status
52
+ # @return [Integer] The session ID of connection.
53
+ attr_reader :session_id
54
+ # @private
55
+ attr_reader :bottom_commands
52
56
 
53
57
  #
54
58
  # Initializes a new client.
@@ -85,6 +89,7 @@ module Discorb
85
89
  @tasks = []
86
90
  @conditions = {}
87
91
  @commands = []
92
+ @bottom_commands = []
88
93
  @status = :initialized
89
94
  end
90
95
 
@@ -372,6 +377,14 @@ module Discorb
372
377
  @events[name] << event
373
378
  end
374
379
  end
380
+ @commands.delete_if do |cmd|
381
+ cmd.respond_to? :extension and cmd.extension == mod.name
382
+ end
383
+ mod.commands.each do |cmd|
384
+ cmd.define_singleton_method(:extension) { mod.name }
385
+ @commands << cmd
386
+ end
387
+ @bottom_commands += mod.bottom_commands
375
388
  mod.client = self
376
389
  end
377
390
  super(mod)
@@ -422,7 +435,7 @@ module Discorb
422
435
  end
423
436
  end
424
437
  start_client(token)
425
- when "setup_command"
438
+ when "setup"
426
439
  setup_commands(token)
427
440
  end
428
441
  end
@@ -34,6 +34,7 @@ module Discorb
34
34
  def slash(command_name, description, options = {}, guild_ids: [], &block)
35
35
  command = Discorb::Command::Command::SlashCommand.new(command_name, description, options, guild_ids, block, 1, "")
36
36
  @commands << command
37
+ @bottom_commands << command
37
38
  command
38
39
  end
39
40
 
@@ -44,12 +45,16 @@ module Discorb
44
45
  # @param [String] description Command description.
45
46
  # @param [Array<#to_s>] guild_ids Guild IDs to restrict the command to.
46
47
  #
48
+ # @yield Block to execute as the command. It can be used to define sub-commands.
49
+ # @yieldself [Discorb::Command::Command::GroupCommand] Group command.
50
+ #
47
51
  # @return [Discorb::Command::Command::GroupCommand] Command object.
48
52
  #
49
53
  # @see file:docs/slash_command.md
50
54
  #
51
- def slash_group(command_name, description, guild_ids: [])
52
- command = Discorb::Command::Command::GroupCommand.new(command_name, description, guild_ids, nil)
55
+ def slash_group(command_name, description, guild_ids: [], &block)
56
+ command = Discorb::Command::Command::GroupCommand.new(command_name, description, guild_ids, nil, self)
57
+ command.instance_eval(&block) if block_given?
53
58
  @commands << command
54
59
  command
55
60
  end
@@ -174,6 +179,7 @@ module Discorb
174
179
  @guild_ids = guild_ids.map(&:to_s)
175
180
  @block = block
176
181
  @type = Discorb::Command::Command.types[type]
182
+ @type_raw = 1
177
183
  @options = options
178
184
  @id = nil
179
185
  @parent = parent
@@ -241,10 +247,11 @@ module Discorb
241
247
  attr_reader :description
242
248
 
243
249
  # @!visibility private
244
- def initialize(name, description, guild_ids, type)
250
+ def initialize(name, description, guild_ids, type, client)
245
251
  super(name, guild_ids, block, type)
246
252
  @description = description
247
253
  @commands = []
254
+ @client = client
248
255
  @id_map = Discorb::Dictionary.new
249
256
  end
250
257
 
@@ -256,42 +263,7 @@ module Discorb
256
263
  #
257
264
  def slash(command_name, description, options = {}, &block)
258
265
  command = Discorb::Command::Command::SlashCommand.new(command_name, description, options, [], block, 1, @name)
259
- options_payload = options.map do |name, value|
260
- ret = {
261
- type: case (value[:type].is_a?(Array) ? value[:type].first : value[:type])
262
- when String, :string
263
- 3
264
- when Integer
265
- 4
266
- when TrueClass, FalseClass, :boolean
267
- 5
268
- when Discorb::User, Discorb::Member, :user, :member
269
- 6
270
- when Discorb::Channel, :channel
271
- 7
272
- when Discorb::Role, :role
273
- 8
274
- when :mentionable
275
- 9
276
- when Float
277
- 10
278
- end,
279
- name: name,
280
- description: value[:description],
281
- required: !value[:optional],
282
- }
283
- if value[:type].is_a?(Array)
284
- ret[:choices] = value[:type]
285
- end
286
-
287
- ret
288
- end
289
- {
290
- name: @name,
291
- default_permission: true,
292
- description: @description,
293
- options: options_payload,
294
- }
266
+ @client.bottom_commands << command
295
267
  @commands << command
296
268
  command
297
269
  end
@@ -302,12 +274,16 @@ module Discorb
302
274
  # @param [String] command_name Group name.
303
275
  # @param [String] description Group description.
304
276
  #
277
+ # @yield Block to execute as the command. It can be used to define sub-commands.
278
+ # @yieldself [Discorb::Command::Command::SubcommandGroup] Group command.
279
+ #
305
280
  # @return [Discorb::Command::Command::SubcommandGroup] Command object.
306
281
  #
307
282
  # @see file:docs/slash_command.md
308
283
  #
309
- def group(command_name, description)
310
- command = Discorb::Command::Command::SubcommandGroup.new(command_name, description, @name)
284
+ def group(command_name, description, &block)
285
+ command = Discorb::Command::Command::SubcommandGroup.new(command_name, description, @name, @client)
286
+ command.instance_eval(&block) if block_given?
311
287
  @commands << command
312
288
  command
313
289
  end
@@ -328,7 +304,7 @@ module Discorb
328
304
  {
329
305
  name: command.name,
330
306
  description: command.description,
331
- default_permission: command.enabled,
307
+ default_permission: true,
332
308
  type: 1,
333
309
  options: command.to_hash[:options],
334
310
  }
@@ -336,7 +312,7 @@ module Discorb
336
312
  {
337
313
  name: command.name,
338
314
  description: command.description,
339
- default_permission: command.enabled,
315
+ default_permission: true,
340
316
  type: 2,
341
317
  options: command.commands.map { |c| c.to_hash.merge(type: 1) },
342
318
  }
@@ -360,8 +336,8 @@ module Discorb
360
336
  attr_reader :commands
361
337
 
362
338
  # @!visibility private
363
- def initialize(name, description, enabled, parent)
364
- super(name, description, [], enabled, 1)
339
+ def initialize(name, description, parent, client)
340
+ super(name, description, [], 1, client)
365
341
 
366
342
  @commands = []
367
343
  @parent = parent
@@ -376,9 +352,10 @@ module Discorb
376
352
  # @param (see Discorb::Command::Handler#slash)
377
353
  # @return [Discorb::Command::Command::SlashCommand] The added subcommand.
378
354
  #
379
- def slash(command_name, description, options = {}, enabled: true, &block)
380
- command = Discorb::Command::Command::SlashCommand.new(command_name, description, options, [], enabled, block, 1, @parent + " " + @name)
355
+ def slash(command_name, description, options = {}, &block)
356
+ command = Discorb::Command::Command::SlashCommand.new(command_name, description, options, [], block, 1, @parent + " " + @name)
381
357
  @commands << command
358
+ @client.bottom_commands << command
382
359
  command
383
360
  end
384
361
  end
@@ -4,7 +4,7 @@ module Discorb
4
4
  # @return [String] The API base URL.
5
5
  API_BASE_URL = "https://discord.com/api/v9"
6
6
  # @return [String] The version of discorb.
7
- VERSION = "0.4.2"
7
+ VERSION = "0.5.3"
8
8
  # @return [String] The user agent for the bot.
9
9
  USER_AGENT = "DiscordBot (https://github.com/discorb-lib/discorb #{VERSION}) Ruby/#{RUBY_VERSION}"
10
10
 
@@ -7,6 +7,7 @@ informations = {
7
7
  "Documentation" => "https://discorb-lib.github.io",
8
8
  "RubyGems" => "https://rubygems.org/gems/discorb",
9
9
  "Changelog" => "https://discorb-lib.github.io/file.Changelog.html",
10
+ "License" => "MIT License",
10
11
  }
11
12
 
12
13
  informations.each do |key, value|
@@ -1,6 +1,7 @@
1
1
  # description: Make files for the discorb project.
2
2
 
3
3
  require "optparse"
4
+ require "discorb"
4
5
  require_relative "../utils/colored_puts"
5
6
 
6
7
  $path = Dir.pwd
@@ -11,15 +12,15 @@ FILES = {
11
12
  require "discorb"
12
13
  require "dotenv"
13
14
 
14
- Dotenv.load
15
+ Dotenv.load # Loads .env file
15
16
 
16
- client = Discorb::Client.new
17
+ client = Discorb::Client.new # Create client for connecting to Discord
17
18
 
18
19
  client.once :ready do
19
- puts "Logged in as #{client.user}"
20
+ puts "Logged in as #{client.user}" # Prints username of logged in user
20
21
  end
21
22
 
22
- client.run ENV["%<token>s"]
23
+ client.run ENV["%<token>s"] # Starts client
23
24
  RUBY
24
25
  ".env" => <<~BASH,
25
26
  %<token>s=Y0urB0tT0k3nHer3.Th1sT0ken.W0ntWorkB3c4useItH4sM34n1ng
@@ -90,9 +91,9 @@ FILES = {
90
91
 
91
92
  source "https://rubygems.org"
92
93
 
93
- git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
94
+ git_source(:github) { |repo_name| "https://github.com/\#{repo_name}" }
94
95
 
95
- gem "discorb", "~> 0.2.5"
96
+ gem "discorb", "~> #{Discorb::VERSION}"
96
97
  gem "dotenv", "~> 2.7"
97
98
  RUBY
98
99
  }
@@ -141,7 +142,7 @@ opt = OptionParser.new <<~BANNER
141
142
 
142
143
  $values = {
143
144
  bundle: true,
144
- git: true,
145
+ git: false,
145
146
  force: false,
146
147
  token: "TOKEN",
147
148
  }
@@ -150,7 +151,7 @@ opt.on("--[no-]bundle", "Whether to use bundle. Default to true.") do |v|
150
151
  $values[:bundle] = v
151
152
  end
152
153
 
153
- opt.on("--[no-]git", "Whether to initialize git. Default to true.") do |v|
154
+ opt.on("--[no-]git", "Whether to initialize git. Default to false.") do |v|
154
155
  $values[:git] = v
155
156
  end
156
157
 
@@ -8,6 +8,9 @@ module Discorb
8
8
  # @abstract
9
9
  #
10
10
  module Extension
11
+ include Discorb::Command::Handler
12
+ undef setup_commands
13
+
11
14
  @events = {}
12
15
  @client = nil
13
16
 
@@ -46,12 +49,18 @@ module Discorb
46
49
 
47
50
  # @return [Hash{Symbol => Array<Discorb::Event>}] The events of the extension.
48
51
  attr_reader :events
52
+ # @return [Array<Discorb::Command::Command] The commands of the extension.
53
+ attr_reader :commands
54
+ # @private
55
+ attr_reader :bottom_commands
49
56
 
50
57
  # @!visibility private
51
58
  attr_accessor :client
52
59
 
53
60
  def self.extended(obj)
54
61
  obj.instance_variable_set(:@events, {})
62
+ obj.instance_variable_set(:@commands, [])
63
+ obj.instance_variable_set(:@bottom_commands, [])
55
64
  end
56
65
  end
57
66
  end
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "async/http"
4
+ require "async/websocket"
5
+ require "json"
6
+ require "zlib"
4
7
 
5
8
  module Discorb
6
9
  #
@@ -464,13 +467,20 @@ module Discorb
464
467
  @first = first
465
468
  _, gateway_response = @http.get("/gateway").wait
466
469
  gateway_url = gateway_response[:url]
467
- endpoint = Async::HTTP::Endpoint.parse("#{gateway_url}?v=9&encoding=json",
470
+ endpoint = Async::HTTP::Endpoint.parse("#{gateway_url}?v=9&encoding=json&compress=zlib-stream",
468
471
  alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
469
472
  begin
470
- Async::WebSocket::Client.connect(endpoint, headers: [["User-Agent", Discorb::USER_AGENT]]) do |connection|
473
+ Async::WebSocket::Client.connect(endpoint, headers: [["User-Agent", Discorb::USER_AGENT]], handler: RawConnection) do |connection|
471
474
  @connection = connection
475
+ @zlib_stream = Zlib::Inflate.new(Zlib::MAX_WBITS)
476
+ @buffer = +""
472
477
  while (message = @connection.read)
473
- handle_gateway(message)
478
+ @buffer << message
479
+ if message.end_with?((+"\x00\x00\xff\xff").force_encoding("ASCII-8BIT"))
480
+ message = JSON.parse(@zlib_stream.inflate(@buffer), symbolize_names: true)
481
+ @buffer = +""
482
+ handle_gateway(message)
483
+ end
474
484
  end
475
485
  end
476
486
  rescue Protocol::WebSocket::ClosedError => e
@@ -490,7 +500,7 @@ module Discorb
490
500
  end
491
501
 
492
502
  def send_gateway(opcode, **value)
493
- @connection.write({ op: opcode, d: value })
503
+ @connection.write({ op: opcode, d: value }.to_json)
494
504
  @connection.flush
495
505
  @log.debug "Sent message with opcode #{opcode}: #{value.to_json.gsub(@token, "[Token]")}"
496
506
  end
@@ -535,7 +545,7 @@ module Discorb
535
545
  connect_gateway(true)
536
546
  end
537
547
  when 11
538
- @log.info "Received opcode 11"
548
+ @log.debug "Received opcode 11"
539
549
  @ping = Time.now.to_f - @heartbeat_before
540
550
  when 0
541
551
  handle_event(payload[:t], data)
@@ -548,7 +558,7 @@ module Discorb
548
558
  task.sleep((interval / 1000.0 - 1) * rand)
549
559
  loop do
550
560
  @heartbeat_before = Time.now.to_f
551
- @connection.write({ op: 1, d: @last_s })
561
+ @connection.write({ op: 1, d: @last_s }.to_json)
552
562
  @connection.flush
553
563
  @log.debug "Sent opcode 1."
554
564
  @log.debug "Waiting for heartbeat."
@@ -768,11 +778,9 @@ module Discorb
768
778
  dispatch(:integration_create, Integration.new(self, data, data[:guild_id]))
769
779
  when "INTEGRATION_UPDATE"
770
780
  return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
771
- return @log.warn "Unknown integration id #{data[:id]}, ignoring" unless (integration = guild.integrations[data[:id]])
772
781
 
773
- before = Integration.new(self, integration.instance_variable_get(:@data), data[:guild_id], no_cache: true)
774
- integration.send(:_set_data, data)
775
- dispatch(:integration_update, before, integration)
782
+ before = Integration.new(self, data, data[:guild_id])
783
+ dispatch(:integration_update, integration)
776
784
  when "INTEGRATION_DELETE"
777
785
  return @log.warn "Unknown guild id #{data[:guild_id]}, ignoring" unless (guild = @guilds[data[:guild_id]])
778
786
  return @log.warn "Unknown integration id #{data[:id]}, ignoring" unless (integration = guild.integrations.delete(data[:id]))
@@ -969,9 +977,33 @@ module Discorb
969
977
  @log.info("Successfully resumed connection")
970
978
  dispatch(:resumed)
971
979
  else
972
- @log.warn "Unknown event: #{event_name}\n#{data.inspect}"
980
+ if respond_to?("event_" + event_name.downcase)
981
+ __send__("event_" + event_name.downcase, data)
982
+ else
983
+ @log.debug "Received unknown event: #{event_name}\n#{data.inspect}"
984
+ end
973
985
  end
974
986
  end
975
987
  end
988
+
989
+ #
990
+ # A class for connecting websocket with raw bytes data.
991
+ # @private
992
+ #
993
+ class RawConnection < Async::WebSocket::Connection
994
+ def initialize(...)
995
+ super
996
+ end
997
+
998
+ def parse(buffer)
999
+ # noop
1000
+ buffer.to_s
1001
+ end
1002
+
1003
+ def dump(object)
1004
+ # noop
1005
+ object.to_s
1006
+ end
1007
+ end
976
1008
  end
977
1009
  end
data/lib/discorb/http.rb CHANGED
@@ -5,7 +5,7 @@ require "net/https"
5
5
  module Discorb
6
6
  #
7
7
  # A class to handle http requests.
8
- # This class is internal use only.
8
+ # @private
9
9
  #
10
10
  class HTTP
11
11
  @nil_body = nil
@@ -13,6 +13,7 @@ module Discorb
13
13
  # @!visibility private
14
14
  def initialize(client)
15
15
  @client = client
16
+ @ratelimit_handler = RatelimitHandler.new(client)
16
17
  end
17
18
 
18
19
  #
@@ -31,8 +32,10 @@ module Discorb
31
32
  #
32
33
  def get(path, headers: nil, audit_log_reason: nil, **kwargs)
33
34
  Async do |task|
35
+ @ratelimit_handler.wait("GET", path)
34
36
  resp = http.get(get_path(path), get_headers(headers, "", audit_log_reason), **kwargs)
35
37
  data = get_response_data(resp)
38
+ @ratelimit_handler.save("GET", path, resp)
36
39
  test_error(if resp.code == "429"
37
40
  @client.log.warn "Ratelimit exceeded for #{path}, trying again in #{data[:retry_after]} seconds."
38
41
  task.sleep(data[:retry_after])
@@ -60,8 +63,10 @@ module Discorb
60
63
  #
61
64
  def post(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
62
65
  Async do |task|
66
+ @ratelimit_handler.wait("POST", path)
63
67
  resp = http.post(get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
64
68
  data = get_response_data(resp)
69
+ @ratelimit_handler.save("POST", path, resp)
65
70
  test_error(if resp.code == "429"
66
71
  task.sleep(data[:retry_after])
67
72
  post(path, body, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
@@ -88,13 +93,10 @@ module Discorb
88
93
  #
89
94
  def patch(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
90
95
  Async do |task|
96
+ @ratelimit_handler.wait("PATCH", path)
91
97
  resp = http.patch(get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
92
- rd = resp.body
93
- data = if rd.nil? || rd.empty?
94
- nil
95
- else
96
- JSON.parse(rd, symbolize_names: true)
97
- end
98
+ data = get_response_data(resp)
99
+ @ratelimit_handler.save("PATCH", path, resp)
98
100
  test_error(if resp.code == "429"
99
101
  task.sleep(data[:retry_after])
100
102
  patch(path, body, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
@@ -121,8 +123,10 @@ module Discorb
121
123
  #
122
124
  def put(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
123
125
  Async do |task|
126
+ @ratelimit_handler.wait("PUT", path)
124
127
  resp = http.put(get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
125
128
  data = get_response_data(resp)
129
+ @ratelimit_handler.save("PUT", path, resp)
126
130
  test_error(if resp.code == "429"
127
131
  task.sleep(data[:retry_after])
128
132
  put(path, body, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
@@ -148,8 +152,10 @@ module Discorb
148
152
  #
149
153
  def delete(path, headers: nil, audit_log_reason: nil, **kwargs)
150
154
  Async do |task|
155
+ @ratelimit_handler.wait("DELETE", path)
151
156
  resp = http.delete(get_path(path), get_headers(headers, "", audit_log_reason))
152
157
  data = get_response_data(resp)
158
+ @ratelimit_handler.save("DELETE", path, resp)
153
159
  test_error(if resp.code == "429"
154
160
  task.sleep(data[:retry_after])
155
161
  delete(path, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
@@ -297,7 +297,7 @@ module Discorb
297
297
  end
298
298
  end
299
299
 
300
- unless (command = @client.commands.find { |c| c.to_s == name })
300
+ unless (command = @client.bottom_commands.find { |c| c.to_s == name && c.type_raw == 1 })
301
301
  @client.log.warn "Unknown command name #{name}, ignoreing"
302
302
  next
303
303
  end
@@ -208,4 +208,14 @@ module Discorb
208
208
  end
209
209
  end
210
210
  end
211
+
212
+ #
213
+ # Module for connecting to a voice channel.
214
+ # This will be discord-voice gem.
215
+ #
216
+ module Connectable
217
+ def connect
218
+ raise NotImplementedError, "This method is implemented by discord-voice gem."
219
+ end
220
+ end
211
221
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Class to handle rate limiting.
6
+ # @private
7
+ #
8
+ class RatelimitHandler
9
+ # @!visibility private
10
+ def initialize(client)
11
+ @client = client
12
+ @ratelimit_hash = {}
13
+ @path_ratelimit_hash = {}
14
+ end
15
+
16
+ #
17
+ # Wait for the rate limit to reset.
18
+ #
19
+ # @param [String] method The HTTP method.
20
+ # @param [String] path The path.
21
+ #
22
+ def wait(method, path)
23
+ return if path.start_with?("https://")
24
+
25
+ return unless hash = @path_ratelimit_hash[method + path]
26
+
27
+ return unless b = @ratelimit_hash[hash]
28
+
29
+ if b[:reset_at] < Time.now.to_i
30
+ @ratelimit_hash.delete(hash)
31
+ return
32
+ end
33
+ return if b[:remaining] > 0
34
+
35
+ @client.log.info("Ratelimit reached, waiting for #{b[:reset_at] - Time.now.to_i} seconds")
36
+ sleep(b[:reset_at] - Time.now.to_i)
37
+ end
38
+
39
+ #
40
+ # Save the rate limit.
41
+ #
42
+ # @param [String] method The HTTP method.
43
+ # @param [String] path The path.
44
+ # @param [Net::HTTPResponse] resp The response.
45
+ #
46
+ def save(method, path, resp)
47
+ return unless resp["X-RateLimit-Remaining"]
48
+
49
+ @path_ratelimit_hash[method + path] = resp["X-RateLimit-Bucket"]
50
+ @ratelimit_hash[resp["X-RateLimit-Bucket"]] = {
51
+ remaining: resp["X-RateLimit-Remaining"].to_i,
52
+ reset_at: resp["X-RateLimit-Reset"].to_i,
53
+ }
54
+ end
55
+ end
56
+ end
data/lib/discorb/user.rb CHANGED
@@ -78,7 +78,7 @@ module Discorb
78
78
  Async do
79
79
  next @dm_channel_id if @dm_channel_id
80
80
 
81
- dm_channel = @client.http.post("/users/#{@id}/channels", { recipient_id: @client.user.id }).wait
81
+ _resp, dm_channel = @client.http.post("/users/@me/channels", { recipient_id: @id }).wait
82
82
  @dm_channel_id = dm_channel[:id]
83
83
  @dm_channel_id
84
84
  end
data/lib/discorb.rb CHANGED
@@ -39,13 +39,13 @@ module Discorb
39
39
  end
40
40
  end
41
41
 
42
- require_order = %w[common flag dictionary error http intents emoji_table modules] +
42
+ require_order = %w[common flag dictionary error rate_limit http intents emoji_table modules] +
43
43
  %w[user member guild emoji channel embed message] +
44
- %w[application audit_logs color components event extension] +
44
+ %w[application audit_logs color components event] +
45
45
  %w[file guild_template image integration interaction invite log permission] +
46
46
  %w[presence reaction role sticker utils voice_state webhook] +
47
47
  %w[gateway_requests gateway command] +
48
- %w[asset client extend]
48
+ %w[asset extension client extend]
49
49
  require_order.each do |name|
50
50
  require_relative "discorb/#{name}.rb"
51
51
  end
@@ -421,7 +421,7 @@ code[class=""] {
421
421
 
422
422
  .docstring .abstract {
423
423
  --dstr-back: var(--background-modifier-hover);
424
- --dstr-fore: var(--interactive-hover);
424
+ --dstr-fore: var(--interactive-muted);
425
425
  }
426
426
 
427
427
  .docstring .note {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: discorb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - sevenc-nanashi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-08 00:00:00.000000000 Z
11
+ date: 2021-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -89,6 +89,15 @@ files:
89
89
  - discorb.gemspec
90
90
  - docs/Examples.md
91
91
  - docs/application_command.md
92
+ - docs/assets/01_new_app.png
93
+ - docs/assets/02_bot_name.png
94
+ - docs/assets/03_add_bot.png
95
+ - docs/assets/04_token_copy.png
96
+ - docs/assets/05_oauth.png
97
+ - docs/assets/06_online.png
98
+ - docs/assets/07_hello_infinite.png
99
+ - docs/assets/08_hello_once.png
100
+ - docs/assets/unused_ping_pong.png
92
101
  - docs/cli.md
93
102
  - docs/cli/init.md
94
103
  - docs/cli/irb.md
@@ -96,6 +105,7 @@ files:
96
105
  - docs/cli/setup.md
97
106
  - docs/events.md
98
107
  - docs/extension.md
108
+ - docs/tutorial.md
99
109
  - docs/voice_events.md
100
110
  - examples/commands/bookmarker.rb
101
111
  - examples/commands/hello.rb
@@ -151,6 +161,7 @@ files:
151
161
  - lib/discorb/modules.rb
152
162
  - lib/discorb/permission.rb
153
163
  - lib/discorb/presence.rb
164
+ - lib/discorb/rate_limit.rb
154
165
  - lib/discorb/reaction.rb
155
166
  - lib/discorb/role.rb
156
167
  - lib/discorb/sticker.rb