onyxcord 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/Dockerfile +13 -0
  3. data/.devcontainer/devcontainer.json +29 -0
  4. data/.devcontainer/postcreate.sh +4 -0
  5. data/.github/CONTRIBUTING.md +13 -0
  6. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  8. data/.github/pull_request_template.md +37 -0
  9. data/.github/workflows/ci.yml +78 -0
  10. data/.github/workflows/codeql.yml +65 -0
  11. data/.github/workflows/deploy.yml +54 -0
  12. data/.github/workflows/release.yml +51 -0
  13. data/.gitignore +16 -0
  14. data/.markdownlint.json +4 -0
  15. data/.overcommit.yml +7 -0
  16. data/.rspec +2 -0
  17. data/.rubocop.yml +129 -0
  18. data/.yardopts +1 -0
  19. data/CHANGELOG.md +0 -0
  20. data/Gemfile +7 -0
  21. data/LICENSE.txt +21 -0
  22. data/README.md +305 -0
  23. data/Rakefile +17 -0
  24. data/bin/console +15 -0
  25. data/bin/setup +7 -0
  26. data/lib/onyxcord/allowed_mentions.rb +43 -0
  27. data/lib/onyxcord/api/application.rb +316 -0
  28. data/lib/onyxcord/api/channel.rb +700 -0
  29. data/lib/onyxcord/api/interaction.rb +67 -0
  30. data/lib/onyxcord/api/invite.rb +44 -0
  31. data/lib/onyxcord/api/server.rb +775 -0
  32. data/lib/onyxcord/api/user.rb +158 -0
  33. data/lib/onyxcord/api/webhook.rb +163 -0
  34. data/lib/onyxcord/api.rb +335 -0
  35. data/lib/onyxcord/await.rb +51 -0
  36. data/lib/onyxcord/bot.rb +1971 -0
  37. data/lib/onyxcord/cache.rb +326 -0
  38. data/lib/onyxcord/colour_rgb.rb +43 -0
  39. data/lib/onyxcord/commands/command_bot.rb +511 -0
  40. data/lib/onyxcord/commands/container.rb +112 -0
  41. data/lib/onyxcord/commands/events.rb +11 -0
  42. data/lib/onyxcord/commands/parser.rb +327 -0
  43. data/lib/onyxcord/commands/rate_limiter.rb +144 -0
  44. data/lib/onyxcord/configuration.rb +125 -0
  45. data/lib/onyxcord/container.rb +988 -0
  46. data/lib/onyxcord/data/activity.rb +271 -0
  47. data/lib/onyxcord/data/application.rb +341 -0
  48. data/lib/onyxcord/data/attachment.rb +91 -0
  49. data/lib/onyxcord/data/audit_logs.rb +438 -0
  50. data/lib/onyxcord/data/avatar_decoration.rb +26 -0
  51. data/lib/onyxcord/data/call.rb +22 -0
  52. data/lib/onyxcord/data/channel.rb +1355 -0
  53. data/lib/onyxcord/data/channel_tag.rb +69 -0
  54. data/lib/onyxcord/data/collectibles.rb +47 -0
  55. data/lib/onyxcord/data/component.rb +583 -0
  56. data/lib/onyxcord/data/embed.rb +258 -0
  57. data/lib/onyxcord/data/emoji.rb +123 -0
  58. data/lib/onyxcord/data/install_params.rb +24 -0
  59. data/lib/onyxcord/data/integration.rb +144 -0
  60. data/lib/onyxcord/data/interaction.rb +1141 -0
  61. data/lib/onyxcord/data/invite.rb +137 -0
  62. data/lib/onyxcord/data/member.rb +528 -0
  63. data/lib/onyxcord/data/message.rb +612 -0
  64. data/lib/onyxcord/data/message_activity.rb +41 -0
  65. data/lib/onyxcord/data/overwrite.rb +109 -0
  66. data/lib/onyxcord/data/poll.rb +365 -0
  67. data/lib/onyxcord/data/primary_server.rb +60 -0
  68. data/lib/onyxcord/data/profile.rb +79 -0
  69. data/lib/onyxcord/data/reaction.rb +64 -0
  70. data/lib/onyxcord/data/recipient.rb +34 -0
  71. data/lib/onyxcord/data/role.rb +449 -0
  72. data/lib/onyxcord/data/role_connection_data.rb +69 -0
  73. data/lib/onyxcord/data/role_subscription.rb +41 -0
  74. data/lib/onyxcord/data/scheduled_event.rb +513 -0
  75. data/lib/onyxcord/data/server.rb +1614 -0
  76. data/lib/onyxcord/data/server_preview.rb +68 -0
  77. data/lib/onyxcord/data/snapshot.rb +112 -0
  78. data/lib/onyxcord/data/team.rb +98 -0
  79. data/lib/onyxcord/data/timestamp.rb +69 -0
  80. data/lib/onyxcord/data/user.rb +324 -0
  81. data/lib/onyxcord/data/voice_region.rb +46 -0
  82. data/lib/onyxcord/data/voice_state.rb +41 -0
  83. data/lib/onyxcord/data/webhook.rb +238 -0
  84. data/lib/onyxcord/data.rb +57 -0
  85. data/lib/onyxcord/errors.rb +246 -0
  86. data/lib/onyxcord/event_executor.rb +80 -0
  87. data/lib/onyxcord/events/await.rb +48 -0
  88. data/lib/onyxcord/events/bans.rb +60 -0
  89. data/lib/onyxcord/events/channels.rb +225 -0
  90. data/lib/onyxcord/events/generic.rb +129 -0
  91. data/lib/onyxcord/events/guilds.rb +269 -0
  92. data/lib/onyxcord/events/integrations.rb +100 -0
  93. data/lib/onyxcord/events/interactions.rb +624 -0
  94. data/lib/onyxcord/events/invites.rb +127 -0
  95. data/lib/onyxcord/events/lifetime.rb +31 -0
  96. data/lib/onyxcord/events/members.rb +110 -0
  97. data/lib/onyxcord/events/message.rb +399 -0
  98. data/lib/onyxcord/events/polls.rb +118 -0
  99. data/lib/onyxcord/events/presence.rb +131 -0
  100. data/lib/onyxcord/events/raw.rb +74 -0
  101. data/lib/onyxcord/events/reactions.rb +218 -0
  102. data/lib/onyxcord/events/roles.rb +87 -0
  103. data/lib/onyxcord/events/scheduled_events.rb +171 -0
  104. data/lib/onyxcord/events/threads.rb +100 -0
  105. data/lib/onyxcord/events/typing.rb +73 -0
  106. data/lib/onyxcord/events/voice_server_update.rb +48 -0
  107. data/lib/onyxcord/events/voice_state_update.rb +106 -0
  108. data/lib/onyxcord/events/webhooks.rb +65 -0
  109. data/lib/onyxcord/gateway.rb +890 -0
  110. data/lib/onyxcord/id_object.rb +39 -0
  111. data/lib/onyxcord/light/data.rb +62 -0
  112. data/lib/onyxcord/light/integrations.rb +73 -0
  113. data/lib/onyxcord/light/light_bot.rb +58 -0
  114. data/lib/onyxcord/light.rb +8 -0
  115. data/lib/onyxcord/logger.rb +120 -0
  116. data/lib/onyxcord/message_components.rb +70 -0
  117. data/lib/onyxcord/paginator.rb +60 -0
  118. data/lib/onyxcord/permissions.rb +255 -0
  119. data/lib/onyxcord/rate_limiter/gateway.rb +42 -0
  120. data/lib/onyxcord/rate_limiter/rest.rb +89 -0
  121. data/lib/onyxcord/version.rb +7 -0
  122. data/lib/onyxcord/voice/encoder.rb +115 -0
  123. data/lib/onyxcord/voice/network.rb +380 -0
  124. data/lib/onyxcord/voice/opcodes.rb +29 -0
  125. data/lib/onyxcord/voice/sodium.rb +157 -0
  126. data/lib/onyxcord/voice/timer.rb +19 -0
  127. data/lib/onyxcord/voice/voice_bot.rb +386 -0
  128. data/lib/onyxcord/webhooks.rb +14 -0
  129. data/lib/onyxcord/websocket.rb +62 -0
  130. data/lib/onyxcord.rb +180 -0
  131. data/onyxcord-webhooks.gemspec +30 -0
  132. data/onyxcord.gemspec +50 -0
  133. metadata +421 -0
@@ -0,0 +1,513 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnyxCord
4
+ # A scheduled event for an occurrence on a server.
5
+ class ScheduledEvent
6
+ include IDObject
7
+
8
+ # Map of status types.
9
+ STATUSES = {
10
+ scheduled: 1,
11
+ active: 2,
12
+ completed: 3,
13
+ canceled: 4
14
+ }.freeze
15
+
16
+ # Map of entity types.
17
+ ENTITY_TYPES = {
18
+ stage: 1,
19
+ voice: 2,
20
+ external: 3
21
+ }.freeze
22
+
23
+ # @return [String] the name of the scheduled event.
24
+ attr_reader :name
25
+
26
+ # @return [Integer] the current status of the scheduled event.
27
+ attr_reader :status
28
+
29
+ # @return [Time, nil] the time at when the scheduled event will end.
30
+ attr_reader :end_time
31
+
32
+ # @return [String, nil] the external location of the scheduled event.
33
+ attr_reader :location
34
+
35
+ # @return [String, nil] the image hash of the scheduled event's cover image.
36
+ attr_reader :cover_id
37
+
38
+ # @return [Integer, nil] the ID of the entity associated with the scheduled event.
39
+ attr_reader :entity_id
40
+
41
+ # @return [Time] the time at when the scheduled event has been scheduled to start.
42
+ attr_reader :start_time
43
+
44
+ # @return [Integer] the type of the entity that is assoicated with the scheduled event.
45
+ attr_reader :entity_type
46
+
47
+ # @return [String, nil] the description of the scheduled event. Between 1-1000 characters.
48
+ attr_reader :description
49
+
50
+ # @return [RecurrenceRule, nil] the definition for how often this scheduled event should repeat.
51
+ attr_reader :recurrence_rule
52
+
53
+ # @!visibility private
54
+ def initialize(data, server, bot)
55
+ @bot = bot
56
+ @server = server
57
+ @id = data['id'].to_i
58
+ @server_id = data['guild_id']&.to_i
59
+ @user_count = data['user_count']
60
+ @creator_id = data['creator_id']&.to_i
61
+ bot.ensure_user(data['creator']) if data['creator']
62
+
63
+ # Set the rest of the mutable attributes in the method.
64
+ update_data(data)
65
+ end
66
+
67
+ # Get the server that the scheduled event originates from.
68
+ # @return [Server] The server that the scheduled event originates from.
69
+ def server
70
+ @server ||= @bot.server(@server_id)
71
+ end
72
+
73
+ # Get the user who was responsible for the creation of the scheduled event.
74
+ # @return [User, nil] The user who was responsible for the creation of the scheduled event.
75
+ def creator
76
+ @bot.user(@creator_id) if @creator_id
77
+ end
78
+
79
+ # Get the channel in which the scheduled event will be hosted. This can be `nil` if the type is external.
80
+ # @return [Channel, nil] The channel where the scheduled event will take place, or `nil` if there isn't one.
81
+ def channel
82
+ @bot.channel(@channel_id) if @channel_id
83
+ end
84
+
85
+ # Get a URL that will display an embed in the Discord client containing information about the scheduled event.
86
+ # @return [String] A URL that will display an embed containing a brief overview about the scheduled event's information.
87
+ def url
88
+ "https://discord.com/events/#{@server_id}/#{@id}"
89
+ end
90
+
91
+ # Utility method to get a scheduled event's cover image URL.
92
+ # @param format [String] The URL will default to `webp`. You can otherwise specify one of `jpg` or `png` to override this.
93
+ # @param size [Integer, nil] The URL will default to `4096`. You can otherwise specify any number that's a power of two to override this.
94
+ # @return [String, nil] The URL to the scheduled event's cover image, or `nil` if the scheduled event doesn't have a cover image set.
95
+ def cover_url(format: 'webp', size: 4096)
96
+ API.scheduled_event_cover_url(@id, @cover_id, format, size) if @cover_id
97
+ end
98
+
99
+ # @!method scheduled?
100
+ # @return [true, false] whether the scheduled event has been scheduled to take place.
101
+ # @!method active?
102
+ # @return [true, false] whether the scheduled event is currently taking place.
103
+ # @!method completed?
104
+ # @return [true, false] whether the scheduled event has finished taking place.
105
+ # @!method canceled?
106
+ # @return [true, false] whether the scheduled event has been canceled.
107
+ STATUSES.each do |name, value|
108
+ define_method("#{name}?") do
109
+ @status == value
110
+ end
111
+ end
112
+
113
+ # @!method stage?
114
+ # @return [true, false] whether the scheduled event will take place in a stage channel.
115
+ # @!method voice?
116
+ # @return [true, false] whether the scheduled event will take place in a voice channel.
117
+ # @!method external?
118
+ # @return [true, false] whether the scheduled event will take place in an external location.
119
+ ENTITY_TYPES.each do |name, value|
120
+ define_method("#{name}?") do
121
+ @entity_type == value
122
+ end
123
+ end
124
+
125
+ # Start the scheduled event.
126
+ # @param reason [String, nil] The reason for starting the event.
127
+ # @return [nil]
128
+ def start(reason: nil)
129
+ raise 'cannot start this event' unless scheduled?
130
+
131
+ modify(status: STATUSES[:active], reason: reason)
132
+ end
133
+
134
+ # Cancel the scheduled event. This cannot be undone.
135
+ # @param reason [String, nil] The reason for cancelling the event.
136
+ # @return [nil]
137
+ def cancel(reason: nil)
138
+ raise 'cannot cancel this event' unless scheduled?
139
+
140
+ modify(status: STATUSES[:canceled], reason: reason)
141
+ end
142
+
143
+ # End the scheduled event. This cannot be undone.
144
+ # @param reason [String, nil] The reason for ending the event.
145
+ # @return [nil]
146
+ def end(reason: nil)
147
+ raise 'cannot end this event' unless active?
148
+
149
+ modify(status: STATUSES[:completed], reason: reason)
150
+ end
151
+
152
+ # Edit the properties of the scheduled event.
153
+ # @param name [String] The new 1-100 character name of the scheduled event.
154
+ # @param channel [Integer, Channel, String, nil] The new channel of the scheduled event.
155
+ # @param location [String, nil] The new location of the scheduled event.
156
+ # @param start_time [Time] The new start time of the scheduled event.
157
+ # @param end_time [Time] The new end time of the scheduled event.
158
+ # @param description [String, nil] The new 1-100 character description of the scheduled event.
159
+ # @param entity_type [Integer, Symbol] The new entity type of the scheduled event.
160
+ # @param status [Integer, Symbol] The new status of the scheduled event.
161
+ # @param cover [File, #read] The new cover image of the scheduled event.
162
+ # @param recurrence_rule [#to_h, nil] The new recurrence rule of the scheduled event.
163
+ # @param reason [String, nil] The audit log reason for updating the scheduled event.
164
+ # @yieldparam builder [RecurrenceRule::Builder] An optional reccurence rule builder.
165
+ # @return [nil]
166
+ def modify(
167
+ name: :undef, channel: :undef, location: :undef, start_time: :undef, end_time: :undef,
168
+ description: :undef, entity_type: :undef, status: :undef, cover: :undef,
169
+ recurrence_rule: :undef, reason: nil
170
+ )
171
+ data = {
172
+ name: name,
173
+ channel_id: channel == :undef ? channel : channel&.resolve_id,
174
+ entity_metadata: location == :undef ? location : { location: },
175
+ scheduled_end_time: end_time == :undef ? end_time : end_time&.iso8601,
176
+ scheduled_start_time: start_time == :undef ? start_time : start_time&.iso8601,
177
+ description: description,
178
+ entity_type: entity_type == :undef ? entity_type : ENTITY_TYPES[type] || type,
179
+ status: status == :undef ? status : STATUSES[status] || status,
180
+ image: cover.respond_to?(:read) ? OnyxCord.encode64(cover) : cover,
181
+ recurrence_rule: recurrence_rule == :undef ? recurrence_rule : recurrence_rule&.to_h,
182
+ reason: reason
183
+ }
184
+
185
+ if block_given?
186
+ yield((builder = RecurrenceRule::Builder.new))
187
+ raise 'An `interval` must be provided' unless builder.interval?
188
+ raise 'A `frequency` must be provided' unless builder.frequency?
189
+ raise 'A `start_time` must be provided' unless builder.start_time?
190
+
191
+ builder[:recurrence_rule] = builder.to_h
192
+ end
193
+
194
+ update_data(JSON.parse(API::Server.update_scheduled_event(@bot.token, @server_id, @id, **data)))
195
+ nil
196
+ end
197
+
198
+ # Delete the scheduled event. Use this with caution, as it cannot be undone.
199
+ # @param reason [String, nil] The reason to show in the audit log for deleting the scheduled event.
200
+ # @return [nil]
201
+ def delete(reason: nil)
202
+ API::Server.delete_scheduled_event(@bot.token, @server_id, @id, reason: reason)
203
+ @server&.delete_scheduled_event(@id)
204
+ nil
205
+ end
206
+
207
+ # Get the total amount of users who are subscribed to the scheduled event.
208
+ # @return [Integer] The total amount of users who are currently subscribed to the scheduled event.
209
+ def user_count
210
+ @user_count ||= JSON.parse(API::Server.get_scheduled_event(@bot.token, @server_id, @id, with_user_count: true))['user_count']
211
+ end
212
+
213
+ alias_method :subscriber_count, :user_count
214
+
215
+ # Get the users who are subscribed to the scheduled event.
216
+ # @param limit [Integer, nil] The limit (`nil` for no limit) of how many subscribers to return.
217
+ # @param member [true, false] Whether to return subscribers as server members, when applicable.
218
+ # @return [Array<User, Member>] the users or members that have subscribed to the scheduled event.
219
+ def users(limit: 100, member: false)
220
+ get_users = proc do |fetch_limit, after = nil|
221
+ response = JSON.parse(API::Server.get_scheduled_event_users(@bot.token, @server_id, @id, limit: fetch_limit, with_member: member, after: after))
222
+ response.map { |data| data['member'] ? Member.new(data['member'], server, @bot).tap { |member| server&.cache_member(member) } : User.new(data['user'], @bot) }
223
+ end
224
+
225
+ # Can be done without pagination.
226
+ return get_users.call(limit) if limit && limit <= 100
227
+
228
+ paginator = Paginator.new(limit, :down) do |last_page|
229
+ if last_page && last_page.count < 100
230
+ []
231
+ else
232
+ get_users.call(100, last_page&.last&.id)
233
+ end
234
+ end
235
+
236
+ paginator.to_a
237
+ end
238
+
239
+ alias_method :subscribers, :users
240
+
241
+ # @!visibility private
242
+ def increment_user_count
243
+ @user_count += 1 if @user_count
244
+ end
245
+
246
+ # @!visibility private
247
+ def deincrement_user_count
248
+ @user_count -= 1 if @user_count
249
+ end
250
+
251
+ # @!visibility private
252
+ def inspect
253
+ "<ScheduledEvent id=#{@id} name=\"#{@name}\" start_time=#{@start_time.inspect} end_time=#{@end_time.inspect}>"
254
+ end
255
+
256
+ # @!visibility private
257
+ def update_data(new_data)
258
+ @name = new_data['name']
259
+ @status = new_data['status']
260
+ @cover_id = new_data['image']
261
+ @entity_type = new_data['entity_type']
262
+ @description = new_data['description']
263
+ @entity_id = new_data['entity_id']&.to_i
264
+ @channel_id = new_data['channel_id']&.to_i
265
+ @start_time = Time.iso8601(new_data['scheduled_start_time'])
266
+ @location = new_data['entity_metadata'] ? new_data['entity_metadata']['location'] : nil
267
+ @end_time = new_data['scheduled_end_time'] ? Time.iso8601(new_data['scheduled_end_time']) : nil
268
+ @recurrence_rule = new_data['recurrence_rule'] ? RecurrenceRule.new(new_data['recurrence_rule'], @bot) : nil
269
+ end
270
+
271
+ # Represents how frequently a scheduled event will repeat.
272
+ class RecurrenceRule
273
+ # Map of weekdays.
274
+ WEEKDAYS = {
275
+ monday: 0,
276
+ tuesday: 1,
277
+ wednesday: 2,
278
+ thursday: 3,
279
+ friday: 4,
280
+ saturday: 5,
281
+ sunday: 6
282
+ }.freeze
283
+
284
+ # Map of frequencies.
285
+ FREQUENCIES = {
286
+ yearly: 0,
287
+ monthly: 1,
288
+ weekly: 2,
289
+ daily: 3
290
+ }.freeze
291
+
292
+ # Map of months.
293
+ MONTHS = {
294
+ january: 1,
295
+ february: 2,
296
+ march: 3,
297
+ april: 4,
298
+ may: 5,
299
+ june: 6,
300
+ july: 7,
301
+ august: 8,
302
+ september: 9,
303
+ october: 10,
304
+ november: 11,
305
+ december: 12
306
+ }.freeze
307
+
308
+ # @return [Integer, nil] the amount of times that the event can recur before stopping.
309
+ attr_reader :count
310
+
311
+ # @return [Array<Integer>] the specific months the event can recur on.
312
+ attr_reader :by_month
313
+
314
+ # @return [Time, nil] the time at when the reccurence interval will end.
315
+ attr_reader :end_time
316
+
317
+ # @return [Time] the time at when the reccurence interval will start.
318
+ attr_reader :start_time
319
+
320
+ # @return [Array<Integer>] the specific days of the week the event can recur on.
321
+ attr_reader :by_weekday
322
+
323
+ # @return [Integer] The spacing between the events, defined by the frequency.
324
+ attr_reader :interval
325
+
326
+ # @return [Integer] how often the reccurence interval will occur, e.g. yearly, monthly.
327
+ attr_reader :frequency
328
+
329
+ # @return [Array<Integer>] the specific days within the year (1-364) to recur on.
330
+ attr_reader :by_year_day
331
+
332
+ # @return [Array<WeeklyDay>] the specific days within a specific week to recur on.
333
+ attr_reader :by_n_weekday
334
+
335
+ # @return [Array<Integer>] the specific dates within a month to recur on.
336
+ attr_reader :by_month_day
337
+
338
+ # @!visibility private
339
+ def initialize(data, bot)
340
+ @bot = bot
341
+ @count = data['count']
342
+ @by_month = data['by_month'] || []
343
+ @end_time = Time.iso8601(data['end']) if data['end']
344
+ @start_time = Time.iso8601(data['start']) if data['start']
345
+ @by_weekday = data['by_weekday'] || []
346
+ @interval = data['interval']
347
+ @frequency = data['frequency']
348
+ @by_year_day = data['by_year_day'] || []
349
+ @by_n_weekday = data['by_n_weekday']&.map { |day| WeeklyDay.new(day, @bot) } || []
350
+ @by_month_day = data['by_month_day'] || []
351
+ end
352
+
353
+ # @!visibility private
354
+ def to_h
355
+ {
356
+ count: @count,
357
+ interval: @interval,
358
+ frequency: @frequency,
359
+ end: @end_time&.iso8601,
360
+ start: @start_time&.iso8601,
361
+ by_month: @by_month.any? ? @by_month : nil,
362
+ by_weekday: @by_weekday.any? ? @by_weekday : nil,
363
+ by_year_day: @by_year_day.any? ? @by_year_day : nil,
364
+ by_month_day: @by_month_day.any? ? @by_month_day : nil,
365
+ by_n_weekday: @by_n_weekday.any? ? @by_n_weekday.map(&:to_h) : nil
366
+ }
367
+ end
368
+
369
+ # @!method yearly?
370
+ # @return [true, false] whether the event repeat on a yearly basis.
371
+ # @!method monthly?
372
+ # @return [true, false] whether the event repeat on a monthly basis.
373
+ # @!method weekly?
374
+ # @return [true, false] whether the event repeat on a weekly basis.
375
+ # @!method daily?
376
+ # @return [true, false] whether the event repeat on a daily basis.
377
+ FREQUENCIES.each do |name, value|
378
+ define_method("#{name}?") do
379
+ @frequency == value
380
+ end
381
+ end
382
+
383
+ # The specific day within a specific week to recur on.
384
+ class WeeklyDay
385
+ # @return [Integer] the day (0-6) of the week to recur on.
386
+ attr_reader :day
387
+
388
+ # @return [Integer] the week (1-5) to recur on in the month.
389
+ attr_reader :week
390
+
391
+ # @!visibility private
392
+ def initialize(data, bot)
393
+ @bot = bot
394
+ @week = data['n']
395
+ @day = data['day']
396
+ end
397
+
398
+ # @!visibility private
399
+ def to_h
400
+ { n: @week, day: @day }
401
+ end
402
+
403
+ # @!method monday?
404
+ # @return [true, false] whether the day within the week is a monday.
405
+ # @!method tuesday?
406
+ # @return [true, false] whether the day within the week is a tuesday.
407
+ # @!method wednesday?
408
+ # @return [true, false] whether the day within the week is a wednesday.
409
+ # @!method thursday?
410
+ # @return [true, false] whether the day within the week is a thursday.
411
+ # @!method friday?
412
+ # @return [true, false] whether the day within the week is a friday.
413
+ # @!method saturday?
414
+ # @return [true, false] whether the day within the week is a saturday.
415
+ # @!method sunday?
416
+ # @return [true, false] whether the day within the week is a sunday.
417
+ WEEKDAYS.each do |name, value|
418
+ define_method("#{name}?") do
419
+ @day == value
420
+ end
421
+ end
422
+ end
423
+
424
+ # Builder for the reccurence rule.
425
+ class Builder
426
+ # @overload interval=(value)
427
+ # @param value [Integer] the spacing between the events, defined by the frequency.
428
+ # @return [void]
429
+ attr_writer :interval
430
+
431
+ # @overload frequency=(value)
432
+ # @param value [Integer, String] how frequently the scheduled event should occur.
433
+ # @return [void]
434
+ attr_writer :frequency
435
+
436
+ # @overload start_time=(value)
437
+ # @param value [Time, #iso8601] the time at when the reccurence interval will begin.
438
+ # @return [void]
439
+ attr_writer :start_time
440
+
441
+ # @!visibility private
442
+ def initialize
443
+ @interval = nil
444
+ @frequency = nil
445
+ @start_time = nil
446
+ end
447
+
448
+ # Set the the specific days within the month to recur on.
449
+ # @param monthly_days [Array<Integer>] The speific days within
450
+ # the month to recur on.
451
+ # @return [void]
452
+ def by_month_day=(monthly_days)
453
+ @by_month_day = Array(monthly_days).map(&:to_i)
454
+ end
455
+
456
+ # Set the the specific months of the year to recur on.
457
+ # @param months [Array<Integer, Symbol>, Integer, Symbol] The specific months
458
+ # of the year to recur on, e.g. `:april`, `:july`, `:june`, etc.
459
+ # @return [void]
460
+ def by_month=(months)
461
+ @by_month = Array(months).map { |month| MONTHS[month] || month }
462
+ end
463
+
464
+ # Set the specific days of the week to recur on.
465
+ # @param weekdays [Array<Symbol, Integer>, Symbol, Integer] The specific days
466
+ # of the week to recur on, e.g. `:tuesday`, `:saturday`, etc.
467
+ # @return [void]
468
+ def by_weekday=(weekdays)
469
+ @by_weekday = Array(weekdays).map { |day| WEEKDAYS[day] || day }
470
+ end
471
+
472
+ # Set the specific days for a specific week to recur on.
473
+ # @param week [Integer] The week of the month (1-5) to recur on.
474
+ # @param day [Integer, Symbol] The specific day of the week to recur on, e.g. `:april`.
475
+ # @return [void]
476
+ def by_n_weekday(week:, day:)
477
+ (@by_n_weekday ||= []) << { n: week, day: WEEKDAYS[day] || day }
478
+ end
479
+
480
+ # @!visibility private
481
+ # @return [true, false]
482
+ def interval?
483
+ !@interval.nil?
484
+ end
485
+
486
+ # @!visibility private
487
+ # @return [true, false]
488
+ def frequency?
489
+ !@frequency.nil?
490
+ end
491
+
492
+ # @!visibility private
493
+ # @return [true, false]
494
+ def start_time?
495
+ !@start_time.nil?
496
+ end
497
+
498
+ # @!visibility private
499
+ def to_h
500
+ {
501
+ by_month: @by_month,
502
+ by_weekday: @by_weekday,
503
+ interval: @interval.to_i,
504
+ by_n_weekday: @by_n_weekday,
505
+ by_month_day: @by_month_day,
506
+ start: @start_time.utc.iso8601,
507
+ frequency: FREQUENCIES[@frequency] || @frequency
508
+ }
509
+ end
510
+ end
511
+ end
512
+ end
513
+ end