discorb 0.7.0 → 0.7.6

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: 13326863238ba4e79e873947fe579da1027bfcfe7f6baed1bd9d0c74806e31a1
4
- data.tar.gz: c43a370b95eec06dcfa2ce0f627c8b9e14421464848c7170690ddc9527aff589
3
+ metadata.gz: a2d341757256d728ba6bb750b068a06f859e5d796d9e44bb1e4d616f4b439444
4
+ data.tar.gz: 3d4f6aaf740f8366a7f714011f60688871a9b4698bce9a37b6fd4c35cd31ab10
5
5
  SHA512:
6
- metadata.gz: bf804c54e218696a9ba5464af5e8c6d4d25132ac573413d0453502872e2dcc55ec49bfb4c571d05df4d489a49cef894ca139091e74fb41fb0d5f36c2786e5cdf
7
- data.tar.gz: 8fb94771a4ea9730962f084d40439383b412caa97674aa9d6edb38d01ebd10fb14ecc54a9bac24a067768bbe5866d18ddd6366449ba84355324af70839fce135
6
+ metadata.gz: dd2620d3de3cf9e1b024701a9312d63a0990ac5360c9b5e65d096cc3792dd81effb79b26577812e6ac229f155d53ccf4a8f5aa093103e56d05c677eb8904936b
7
+ data.tar.gz: a04ab3bb0982d21d0d40d132b0b217ee4b95b214c848d970229e8f66bf0a4d5e2d07dce2006822ab07954f5a0de343a7769b1e36181778ccd501e5a02b6456fb
data/Changelog.md CHANGED
@@ -157,6 +157,7 @@
157
157
  - Fix: Fix some issues with client without guild intent
158
158
  - Add: Add alias for `#fired_by`
159
159
  - Change!: Change block usage of `ApplicationCommand::Handler#group`
160
+
160
161
  ```ruby
161
162
  # before
162
163
  client.slash_group do
@@ -170,4 +171,35 @@ client.slash_group do |group|
170
171
  group.slash "help", "Help" do |interaction|
171
172
  # ...
172
173
  end
173
- end
174
+ end
175
+
176
+ ```
177
+
178
+ ## 0.7.1
179
+
180
+ - Fix: Fix error of responding to interaction
181
+
182
+ ## 0.7.2
183
+
184
+ - Add: Add `Member#owner?`
185
+ - Fix: Fix `Member#permissions`
186
+ - Add: Add `Member#guild_permissions` as alias of `Member#permissions`
187
+ - Add: Add default role to `Member#roles`
188
+ - Fix: Fix error in `Integration#_set_data`
189
+ - Change: Reverse `Member#roles`
190
+
191
+ ## 0.7.3
192
+
193
+ - Add: Improve `discorb init`
194
+
195
+ ## 0.7.4 (yanked)
196
+
197
+ - Fix: Fix disconnected client
198
+
199
+ ## 0.7.5 (yanked)
200
+
201
+ - Fix: Fix critical error
202
+
203
+ ## 0.7.6
204
+
205
+ - Fix: Fix heartbeating error
data/docs/cli/init.md CHANGED
@@ -32,6 +32,18 @@ If true, the command will initialize git and commit the initial files with commi
32
32
  Use `git commit --amend -m "..."` to change the commit message.
33
33
  Default to false.
34
34
 
35
+ ### `--[no-]desc`
36
+
37
+ Whether to create a description file.
38
+ If true, the command will create a `.env.sample` and `README.md` file.
39
+ Default to false.
40
+
41
+ ### `-n` `--name`
42
+
43
+ The name of the project.
44
+ It will be used in the `README.md` file.
45
+ Default to the directory name.
46
+
35
47
  ### `-t`, `--token`
36
48
 
37
49
  The name of token environment variable.
@@ -49,7 +61,9 @@ The following files will be created:
49
61
  | File | Description |
50
62
  | ---- | ----------- |
51
63
  | `.env` | Environment variables. |
64
+ | `main.rb` | Main script. |
52
65
  | `.gitignore` | Git ignore file. Won't be created if `--git` is false. |
53
66
  | `Gemfile` | Gemfile. Won't be created if `--bundle` is false. |
54
67
  | `Gemfile.lock` | Gemfile lock file. Won't be created if `--bundle` is false. |
55
- | `main.rb` | Main script. |
68
+ | `README.md` | Readme file. Won't be created if `--desc` is false. |
69
+ | `.env.sample` | Sample environment variables. Won't be created if `--desc` is false. |
@@ -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.7.0"
7
+ VERSION = "0.7.6"
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
 
@@ -96,11 +96,46 @@ FILES = {
96
96
  gem "discorb", "~> #{Discorb::VERSION}"
97
97
  gem "dotenv", "~> 2.7"
98
98
  RUBY
99
+ ".env.sample" => <<~BASH,
100
+ %<token>s=
101
+ BASH
102
+ "README.md" => <<~MARKDOWN,
103
+ # %<name>s
104
+
105
+ Welcome to your bot: %<name>s!
106
+
107
+ TODO: Write your bot's description here.
108
+
109
+ ## Usage
110
+
111
+ TODO: Write your bot's usage here.
112
+
113
+ ## Features
114
+
115
+ TODO: Write your bot's features here.
116
+
117
+ ## How to host
118
+
119
+ 1. Clone the repository.
120
+ 2. Run `bundle install`.
121
+ 3. Get your bot's token from the Discord developer portal.
122
+ 4. Copy `.env.sample` to `.env` and fill in the token.
123
+ 5. Run `bundle exec discorb run`.
124
+
125
+ TODO: Write your own customizations here.
126
+
127
+ ## License
128
+
129
+ TODO: Write your bot's license here.
130
+ See https://choosealicense.com/ for more information.
131
+ https://rubygems.org/gems/license-cli may be useful.
132
+
133
+ MARKDOWN
99
134
  }
100
135
 
101
136
  # @!visibility private
102
137
  def create_file(name)
103
- File.write($path + "/#{name}", format(FILES[name], token: $values[:token]), mode: "wb")
138
+ File.write($path + "/#{name}", format(FILES[name], token: $values[:token], name: $values[:name]), mode: "wb")
104
139
  end
105
140
 
106
141
  # @!visibility private
@@ -132,6 +167,14 @@ def git_init
132
167
  " to change commit message of initial commit.\n"
133
168
  end
134
169
 
170
+ # @!visibility private
171
+ def make_descs
172
+ iputs "Making descriptions..."
173
+ create_file(".env.sample")
174
+ create_file("README.md")
175
+ sputs "Made descriptions.\n"
176
+ end
177
+
135
178
  opt = OptionParser.new <<~BANNER
136
179
  A tool to make a new project.
137
180
 
@@ -145,6 +188,8 @@ $values = {
145
188
  git: false,
146
189
  force: false,
147
190
  token: "TOKEN",
191
+ descs: false,
192
+ name: nil,
148
193
  }
149
194
 
150
195
  opt.on("--[no-]bundle", "Whether to use bundle. Default to true.") do |v|
@@ -155,10 +200,18 @@ opt.on("--[no-]git", "Whether to initialize git. Default to false.") do |v|
155
200
  $values[:git] = v
156
201
  end
157
202
 
203
+ opt.on("--[no-]descs", "Whether to put some file for description. Default to false.") do |v|
204
+ $values[:descs] = v
205
+ end
206
+
158
207
  opt.on("-t NAME", "--token NAME", "The name of token environment variable. Default to TOKEN.") do |v|
159
208
  $values[:token] = v
160
209
  end
161
210
 
211
+ opt.on("-n NAME", "--name NAME", "The name of your project. Default to the directory name.") do |v|
212
+ $values[:name] = v
213
+ end
214
+
162
215
  opt.on("-f", "--force", "Whether to force use directory. Default to false.") do |v|
163
216
  $values[:force] = v
164
217
  end
@@ -187,10 +240,14 @@ if (dir = ARGV[0])
187
240
  Dir.chdir($path)
188
241
  end
189
242
 
243
+ $values[:name] ||= Dir.pwd.split("/").last
244
+
190
245
  bundle_init if $values[:bundle]
191
246
 
192
247
  make_files
193
248
 
194
249
  git_init if $values[:git]
195
250
 
251
+ make_descs if $values[:descs]
252
+
196
253
  sputs "\nSuccessfully made a new project at \e[32m#{$path}\e[92m."
@@ -488,7 +488,7 @@ module Discorb
488
488
  _, gateway_response = @http.get("/gateway").wait
489
489
  gateway_url = gateway_response[:url]
490
490
  endpoint = Async::HTTP::Endpoint.parse("#{gateway_url}?v=9&encoding=json&compress=zlib-stream",
491
- alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
491
+ alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
492
492
  begin
493
493
  Async::WebSocket::Client.connect(endpoint, headers: [["User-Agent", Discorb::USER_AGENT]], handler: RawConnection) do |connection|
494
494
  @connection = connection
@@ -510,7 +510,9 @@ module Discorb
510
510
  raise ClientError.new("Authentication failed."), cause: nil
511
511
  when "Discord WebSocket requesting client reconnect."
512
512
  @log.info "Discord WebSocket requesting client reconnect"
513
- @tasks.map(&:stop)
513
+ connect_gateway(false)
514
+ else
515
+ @log.error "Discord WebSocket closed: #{e.message}"
514
516
  connect_gateway(false)
515
517
  end
516
518
  rescue EOFError, Async::Wrapper::Cancelled
@@ -579,15 +581,18 @@ module Discorb
579
581
  end
580
582
  end
581
583
 
582
- def handle_heartbeat(interval)
584
+ def handle_heartbeat
583
585
  Async do |task|
586
+ interval = @heartbeat_interval
584
587
  sleep((interval / 1000.0 - 1) * rand)
585
588
  loop do
586
- @heartbeat_before = Time.now.to_f
587
- @connection.write({ op: 1, d: @last_s }.to_json)
588
- @connection.flush
589
- @log.debug "Sent opcode 1."
590
- @log.debug "Waiting for heartbeat."
589
+ unless @connection.closed?
590
+ @heartbeat_before = Time.now.to_f
591
+ @connection.write({ op: 1, d: @last_s }.to_json)
592
+ @connection.flush
593
+ @log.debug "Sent opcode 1."
594
+ @log.debug "Waiting for heartbeat."
595
+ end
591
596
  sleep(interval / 1000.0 - 1)
592
597
  end
593
598
  end
@@ -609,7 +614,7 @@ module Discorb
609
614
  dispatch(:ready)
610
615
  @log.info("Successfully connected to Discord.")
611
616
  end
612
- @tasks << handle_heartbeat(@heartbeat_interval)
617
+ @tasks << handle_heartbeat
613
618
  when "GUILD_CREATE"
614
619
  if @uncached_guilds.include?(data[:id])
615
620
  Guild.new(self, data, true)
@@ -839,12 +844,12 @@ module Discorb
839
844
  dispatch(:voice_state_update, old, current)
840
845
  if old&.channel != current&.channel
841
846
  dispatch(:voice_channel_update, old, current)
842
- case [old&.channel, current&.channel]
843
- in [nil, _]
847
+ case [old&.channel.nil?, current&.channel.nil?]
848
+ when [true, false]
844
849
  dispatch(:voice_channel_connect, current)
845
- in [_, nil]
850
+ when [false, true]
846
851
  dispatch(:voice_channel_disconnect, old)
847
- in _
852
+ when [false, false]
848
853
  dispatch(:voice_channel_move, old, current)
849
854
  end
850
855
  end
@@ -980,9 +985,9 @@ module Discorb
980
985
  dispatch(:reaction_add, ReactionEvent.new(self, data))
981
986
  when "MESSAGE_REACTION_REMOVE"
982
987
  if (target_message = @messages[data[:message_id]]) &&
983
- (target_reaction = target_message.reactions.find do |r|
984
- data[:emoji][:id].nil? ? r.emoji.name == data[:emoji][:name] : r.emoji.id == data[:emoji][:id]
985
- end)
988
+ (target_reaction = target_message.reactions.find do |r|
989
+ data[:emoji][:id].nil? ? r.emoji.name == data[:emoji][:name] : r.emoji.id == data[:emoji][:id]
990
+ end)
986
991
  target_reaction.instance_variable_set(:@count, target_reaction.count - 1)
987
992
  target_message.reactions.delete(target_reaction) if target_reaction.count.zero?
988
993
  end
@@ -994,7 +999,7 @@ module Discorb
994
999
  dispatch(:reaction_remove_all, ReactionRemoveAllEvent.new(self, data))
995
1000
  when "MESSAGE_REACTION_REMOVE_EMOJI"
996
1001
  if (target_message = @messages[data[:message_id]]) &&
997
- (target_reaction = target_message.reactions.find { |r| data[:emoji][:id].nil? ? r.name == data[:emoji][:name] : r.id == data[:emoji][:id] })
1002
+ (target_reaction = target_message.reactions.find { |r| data[:emoji][:id].nil? ? r.name == data[:emoji][:name] : r.id == data[:emoji][:id] })
998
1003
  target_message.reactions.delete(target_reaction)
999
1004
  end
1000
1005
  dispatch(:reaction_remove_emoji, ReactionRemoveEmojiEvent.new(data))
@@ -1017,25 +1022,35 @@ module Discorb
1017
1022
  end
1018
1023
  end
1019
1024
  end
1020
-
1025
+
1021
1026
  #
1022
1027
  # A class for connecting websocket with raw bytes data.
1023
1028
  # @private
1024
1029
  #
1025
- class RawConnection < Async::WebSocket::Connection
1026
- def initialize(...)
1030
+ class RawConnection < Async::WebSocket::Connection
1031
+ def initialize(*, **)
1027
1032
  super
1033
+ @closed = false
1028
1034
  end
1029
-
1030
- def parse(buffer)
1031
- # noop
1035
+
1036
+ def closed?
1037
+ @closed
1038
+ end
1039
+
1040
+ def close
1041
+ super
1042
+ @closed = true
1043
+ end
1044
+
1045
+ def parse(buffer)
1046
+ # noop
1032
1047
  buffer.to_s
1033
- end
1034
-
1035
- def dump(object)
1036
- # noop
1048
+ end
1049
+
1050
+ def dump(object)
1051
+ # noop
1037
1052
  object.to_s
1038
- end
1053
+ end
1039
1054
  end
1040
1055
  end
1041
- end
1056
+ end
@@ -80,7 +80,7 @@ module Discorb
80
80
  @enable_emoticons = data[:enable_emoticons]
81
81
  @expire_behavior = self.class.expire_behavior[data[:expire_behavior]]
82
82
  @expire_grace_period = data[:expire_grace_period]
83
- @user = client.users[data[:user].to_i]
83
+ @user = @client.users[data[:user].to_i]
84
84
  @account = Account.new(data[:account])
85
85
  @subscriber_count = data[:subscriber_count]
86
86
  @revoked = data[:revoked]
@@ -100,6 +100,9 @@ module Discorb
100
100
  #
101
101
  # Response with `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE`(`5`).
102
102
  #
103
+ # @macro async
104
+ # @macro http
105
+ #
103
106
  # @param [Boolean] ephemeral Whether to make the response ephemeral.
104
107
  #
105
108
  def defer_source(ephemeral: false)
@@ -117,6 +120,9 @@ module Discorb
117
120
  #
118
121
  # Response with `CHANNEL_MESSAGE_WITH_SOURCE`(`4`).
119
122
  #
123
+ # @macro async
124
+ # @macro http
125
+ #
120
126
  # @param [String] content The content of the response.
121
127
  # @param [Boolean] tts Whether to send the message as text-to-speech.
122
128
  # @param [Discorb::Embed] embed The embed to send.
@@ -126,45 +132,48 @@ module Discorb
126
132
  # @param [Boolean] ephemeral Whether to make the response ephemeral.
127
133
  #
128
134
  def post(content = nil, tts: false, embed: nil, embeds: nil, allowed_mentions: nil, components: nil, ephemeral: false)
129
- payload = {}
130
- payload[:content] = content if content
131
- payload[:tts] = tts
132
- tmp_embed = if embed
133
- [embed]
134
- elsif embeds
135
- embeds
136
- end
137
- payload[:embeds] = tmp_embed.map(&:to_hash) if tmp_embed
138
- payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
139
- if components
140
- tmp_components = []
141
- tmp_row = []
142
- components.each do |c|
143
- case c
144
- when Array
145
- tmp_components << tmp_row
146
- tmp_row = []
147
- tmp_components << c
148
- when SelectMenu
149
- tmp_components << tmp_row
150
- tmp_row = []
151
- tmp_components << [c]
152
- else
153
- tmp_row << c
135
+ Async do
136
+ payload = {}
137
+ payload[:content] = content if content
138
+ payload[:tts] = tts
139
+ tmp_embed = if embed
140
+ [embed]
141
+ elsif embeds
142
+ embeds
143
+ end
144
+ payload[:embeds] = tmp_embed.map(&:to_hash) if tmp_embed
145
+ payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
146
+ if components
147
+ tmp_components = []
148
+ tmp_row = []
149
+ components.each do |c|
150
+ case c
151
+ when Array
152
+ tmp_components << tmp_row
153
+ tmp_row = []
154
+ tmp_components << c
155
+ when SelectMenu
156
+ tmp_components << tmp_row
157
+ tmp_row = []
158
+ tmp_components << [c]
159
+ else
160
+ tmp_row << c
161
+ end
154
162
  end
163
+ tmp_components << tmp_row
164
+ payload[:components] = tmp_components.filter { |c| c.length.positive? }.map { |c| { type: 1, components: c.map(&:to_hash) } }
155
165
  end
156
- tmp_components << tmp_row
157
- payload[:components] = tmp_components.filter { |c| c.length.positive? }.map { |c| { type: 1, components: c.map(&:to_hash) } }
158
- end
159
- payload[:flags] = (ephemeral ? 1 << 6 : 0)
160
- if @responded
161
- @client.http.post("/webhooks/#{@id}/#{@token}", { type: 4, data: payload }).wait
162
- elsif @defered
163
- @client.http.post("/interactions/#{@id}/#{@token}/@original/edit", { type: 4, data: payload }).wait
164
- else
165
- @client.http.post("/interactions/#{@id}/#{@token}/callback", { type: 4, data: payload }).wait
166
+ payload[:flags] = (ephemeral ? 1 << 6 : 0)
167
+
168
+ if @responded
169
+ @client.http.post("/webhooks/#{@application_id}/#{@token}", payload).wait
170
+ elsif @defered
171
+ @client.http.patch("/webhooks/#{@application_id}/#{@token}/messages/@original", payload).wait
172
+ else
173
+ @client.http.post("/interactions/#{@id}/#{@token}/callback", { type: 4, data: payload }).wait
174
+ end
175
+ @responded = true
166
176
  end
167
- @responded = true
168
177
  end
169
178
  end
170
179
 
@@ -191,6 +200,9 @@ module Discorb
191
200
  #
192
201
  # Response with `UPDATE_MESSAGE`(`7`).
193
202
  #
203
+ # @macro async
204
+ # @macro http
205
+ #
194
206
  # @param [String] content The content of the response.
195
207
  # @param [Boolean] tts Whether to send the message as text-to-speech.
196
208
  # @param [Discorb::Embed] embed The embed to send.
@@ -200,38 +212,40 @@ module Discorb
200
212
  # @param [Boolean] ephemeral Whether to make the response ephemeral.
201
213
  #
202
214
  def edit(content, tts: false, embed: nil, embeds: nil, allowed_mentions: nil, components: nil, ephemeral: false)
203
- payload = {}
204
- payload[:content] = content if content
205
- payload[:tts] = tts
206
- tmp_embed = if embed
207
- [embed]
208
- elsif embeds
209
- embeds
210
- end
211
- payload[:embeds] = tmp_embed.map(&:to_hash) if tmp_embed
212
- payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
213
- if components
214
- tmp_components = []
215
- tmp_row = []
216
- components.each do |c|
217
- case c
218
- when Array
219
- tmp_components << tmp_row
220
- tmp_row = []
221
- tmp_components << c
222
- when SelectMenu
223
- tmp_components << tmp_row
224
- tmp_row = []
225
- tmp_components << [c]
226
- else
227
- tmp_row << c
215
+ Async do
216
+ payload = {}
217
+ payload[:content] = content if content
218
+ payload[:tts] = tts
219
+ tmp_embed = if embed
220
+ [embed]
221
+ elsif embeds
222
+ embeds
223
+ end
224
+ payload[:embeds] = tmp_embed.map(&:to_hash) if tmp_embed
225
+ payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
226
+ if components
227
+ tmp_components = []
228
+ tmp_row = []
229
+ components.each do |c|
230
+ case c
231
+ when Array
232
+ tmp_components << tmp_row
233
+ tmp_row = []
234
+ tmp_components << c
235
+ when SelectMenu
236
+ tmp_components << tmp_row
237
+ tmp_row = []
238
+ tmp_components << [c]
239
+ else
240
+ tmp_row << c
241
+ end
228
242
  end
243
+ tmp_components << tmp_row
244
+ payload[:components] = tmp_components.filter { |c| c.length.positive? }.map { |c| { type: 1, components: c.map(&:to_hash) } }
229
245
  end
230
- tmp_components << tmp_row
231
- payload[:components] = tmp_components.filter { |c| c.length.positive? }.map { |c| { type: 1, components: c.map(&:to_hash) } }
246
+ payload[:flags] = (ephemeral ? 1 << 6 : 0)
247
+ @client.http.post("/interactions/#{@id}/#{@token}/callback", { type: 6, data: payload }).wait
232
248
  end
233
- payload[:flags] = (ephemeral ? 1 << 6 : 0)
234
- @client.http.post("/interactions/#{@id}/#{@token}/callback", { type: 6, data: payload }).wait
235
249
  end
236
250
  end
237
251
 
@@ -59,6 +59,8 @@ module Discorb
59
59
  # @!attribute [r] status
60
60
  # @macro client_cache
61
61
  # @return [Symbol] The status of the member. It's from the {#presence}.
62
+ # @!attribute [r] owner?
63
+ # @return [Boolean] Whether the member is the owner of the guild.
62
64
 
63
65
  # @!visibility private
64
66
  def initialize(client, guild_id, user_data, member_data)
@@ -99,18 +101,27 @@ module Discorb
99
101
  guild.voice_states[@id]
100
102
  end
101
103
 
104
+ def owner?
105
+ guild.owner_id == @id
106
+ end
107
+
102
108
  def guild
103
109
  @client.guilds[@guild_id]
104
110
  end
105
111
 
106
112
  def roles
107
- @role_ids.map { |r| guild.roles[r] }.sort_by(&:position)
113
+ @role_ids.map { |r| guild.roles[r] }.sort_by(&:position).reverse + [guild.roles[guild.id]]
108
114
  end
109
115
 
110
116
  def permissions
117
+ if owner?
118
+ return Permission.new((1 << 38) - 1)
119
+ end
111
120
  roles.map(&:permissions).sum(Permission.new(0))
112
121
  end
113
122
 
123
+ alias guild_permissions permissions
124
+
114
125
  def hoisted_role
115
126
  @hoisted_role_id && guild.roles[@hoisted_role_id]
116
127
  end
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.7.0
4
+ version: 0.7.6
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-20 00:00:00.000000000 Z
11
+ date: 2021-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async