discorb 0.4.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e88541a1d18a1ebd262bae5d91bb38c02d91350e1b2cf698c3f491bf58bb5cfa
4
- data.tar.gz: dc55e68bea5b74105eb941c8ec50bec6fa6f4bf3dd716a1e505ea6c3ab598725
3
+ metadata.gz: 3d17006d6ca103247dcf304581c61104054722b1af32bc8b3ec3d23b7f5e8680
4
+ data.tar.gz: 92c481e5532d3e990ca73b891c71a87f5a4a2e6217ff7d7c5677519dbeaf4259
5
5
  SHA512:
6
- metadata.gz: 5c5d884643656bca43b2ee1e448c0860f0235f439875cbad59a89c79b5d98c71d7b3f539ab6150a051b262ebb09551cf8f943c090676f0751d5533596bc5aea8
7
- data.tar.gz: d4ddfe0faa3c4cdcbc14293216c13017d4c64b7c0f4594e44d5ddb9ab7b9b60ddb4f5acc8a4d06eaec8d1f5e504b13fd43c4f3bc898d787976c617c7509a15dc
6
+ metadata.gz: b305d2f608c0506b7c84f53d16e4384710d454d5deacc69d51a55dbf81fe25d80ab7226717cb12480e07ba25f069a59a49a50608826d711c2a84edb4ec67258c
7
+ data.tar.gz: c40281fb460527163504b8fe999f02a57db3190532a47c78ee25d71871372ff4c720a7ec0e17c70853083ad7327ae574e418b080d7379c98d683fcc1ccb0169e
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
@@ -94,4 +94,26 @@
94
94
 
95
95
  ## 0.4.1
96
96
 
97
- - Add: Add `-s` option to `discorb run`
97
+ - Add: Add `-s` option to `discorb run`
98
+
99
+ ## 0.4.2
100
+
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
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- discorb (0.4.1)
4
+ discorb (0.5.2)
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!
@@ -49,6 +49,8 @@ module Discorb
49
49
  attr_reader :ping
50
50
  # @return [:initialized, :running, :closed] The status of the client.
51
51
  attr_reader :status
52
+ # @private
53
+ attr_reader :bottom_commands
52
54
 
53
55
  #
54
56
  # Initializes a new client.
@@ -85,6 +87,7 @@ module Discorb
85
87
  @tasks = []
86
88
  @conditions = {}
87
89
  @commands = []
90
+ @bottom_commands = []
88
91
  @status = :initialized
89
92
  end
90
93
 
@@ -372,6 +375,14 @@ module Discorb
372
375
  @events[name] << event
373
376
  end
374
377
  end
378
+ @commands.delete_if do |cmd|
379
+ cmd.respond_to? :extension and cmd.extension == mod.name
380
+ end
381
+ mod.commands.each do |cmd|
382
+ cmd.define_singleton_method(:extension) { mod.name }
383
+ @commands << cmd
384
+ end
385
+ @bottom_commands += mod.bottom_commands
375
386
  mod.client = self
376
387
  end
377
388
  super(mod)
@@ -406,7 +417,7 @@ module Discorb
406
417
  when "stdout"
407
418
  $stdout
408
419
  else
409
- File.open(options[:log_file], "a")
420
+ ::File.open(options[:log_file], "a")
410
421
  end
411
422
  @log.level = options[:log_level].to_sym
412
423
  @log.colorize_log = case options[:log_color]
@@ -422,7 +433,7 @@ module Discorb
422
433
  end
423
434
  end
424
435
  start_client(token)
425
- when "setup_command"
436
+ when "setup"
426
437
  setup_commands(token)
427
438
  end
428
439
  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.1"
7
+ VERSION = "0.5.2"
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]))
@@ -973,5 +981,25 @@ module Discorb
973
981
  end
974
982
  end
975
983
  end
984
+
985
+ #
986
+ # A class for connecting websocket with raw bytes data.
987
+ # @private
988
+ #
989
+ class RawConnection < Async::WebSocket::Connection
990
+ def initialize(...)
991
+ super
992
+ end
993
+
994
+ def parse(buffer)
995
+ # noop
996
+ buffer.to_s
997
+ end
998
+
999
+ def dump(object)
1000
+ # noop
1001
+ object.to_s
1002
+ end
1003
+ end
976
1004
  end
977
1005
  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
@@ -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.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.1
4
+ version: 0.5.2
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-10 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