mij-discord 1.0.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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE +21 -0
  6. data/README.md +35 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +7 -0
  9. data/bin/setup +6 -0
  10. data/lib/mij-discord.rb +56 -0
  11. data/lib/mij-discord/bot.rb +579 -0
  12. data/lib/mij-discord/cache.rb +298 -0
  13. data/lib/mij-discord/core/api.rb +228 -0
  14. data/lib/mij-discord/core/api/channel.rb +416 -0
  15. data/lib/mij-discord/core/api/invite.rb +43 -0
  16. data/lib/mij-discord/core/api/server.rb +465 -0
  17. data/lib/mij-discord/core/api/user.rb +144 -0
  18. data/lib/mij-discord/core/errors.rb +106 -0
  19. data/lib/mij-discord/core/gateway.rb +505 -0
  20. data/lib/mij-discord/data.rb +65 -0
  21. data/lib/mij-discord/data/application.rb +38 -0
  22. data/lib/mij-discord/data/channel.rb +404 -0
  23. data/lib/mij-discord/data/embed.rb +115 -0
  24. data/lib/mij-discord/data/emoji.rb +62 -0
  25. data/lib/mij-discord/data/invite.rb +87 -0
  26. data/lib/mij-discord/data/member.rb +174 -0
  27. data/lib/mij-discord/data/message.rb +206 -0
  28. data/lib/mij-discord/data/permissions.rb +121 -0
  29. data/lib/mij-discord/data/role.rb +99 -0
  30. data/lib/mij-discord/data/server.rb +359 -0
  31. data/lib/mij-discord/data/user.rb +173 -0
  32. data/lib/mij-discord/data/voice.rb +68 -0
  33. data/lib/mij-discord/events.rb +133 -0
  34. data/lib/mij-discord/events/basic.rb +80 -0
  35. data/lib/mij-discord/events/channel.rb +50 -0
  36. data/lib/mij-discord/events/member.rb +66 -0
  37. data/lib/mij-discord/events/message.rb +150 -0
  38. data/lib/mij-discord/events/server.rb +102 -0
  39. data/lib/mij-discord/logger.rb +20 -0
  40. data/lib/mij-discord/version.rb +5 -0
  41. data/mij-discord.gemspec +31 -0
  42. metadata +154 -0
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MijDiscord::Data
4
+ DISCORD_EPOCH = 1_420_070_400_000
5
+
6
+ CHARACTER_LIMIT = 2000
7
+
8
+ module IDObject
9
+ attr_reader :id
10
+ alias_method :to_id, :id
11
+
12
+ def hash
13
+ @id.hash
14
+ end
15
+
16
+ def ==(other)
17
+ @id == (other.respond_to?(:to_id) ? other.to_id : other)
18
+ end
19
+
20
+ alias_method :eql?, :==
21
+
22
+ def creation_time
23
+ ms = (@id >> 22) + DISCORD_EPOCH
24
+ Time.at(ms / 1000.0).utc
25
+ end
26
+
27
+ def self.synthesize(time)
28
+ ms = (time.to_f * 1000).to_i
29
+ (ms - DISCORD_EPOCH) << 22
30
+ end
31
+ end
32
+
33
+ module PermissionObject
34
+
35
+ end
36
+
37
+ class ColorRGB
38
+ attr_reader :red
39
+ alias_method :r, :red
40
+
41
+ attr_reader :green
42
+ alias_method :g, :green
43
+
44
+ attr_reader :blue
45
+ alias_method :b, :blue
46
+
47
+ attr_reader :value
48
+ alias_method :to_i, :value
49
+
50
+ def initialize(color = nil)
51
+ self.value = color || 0xFFFFFF
52
+ end
53
+
54
+ def value=(color)
55
+ @value = color.is_a?(String) ? color.to_i(16) : color
56
+ @red = (@value >> 16) & 0xFF
57
+ @green = (@value >> 8) & 0xFF
58
+ @blue = (@value >> 0) & 0xFF
59
+ end
60
+
61
+ def to_hex
62
+ '%06x' % @value
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MijDiscord::Data
4
+ class Application
5
+ include IDObject
6
+
7
+ attr_reader :name
8
+
9
+ attr_reader :description
10
+
11
+ attr_reader :rpc_origins
12
+
13
+ attr_reader :flags
14
+
15
+ attr_reader :owner
16
+
17
+ def initialize(data, bot)
18
+ @bot = bot
19
+
20
+ @id = data['id'].to_i
21
+ @name = data['name']
22
+ @description = data['description']
23
+ @icon_id = data['icon']
24
+ @rpc_origins = data['rpc_origins']
25
+ @flags = data['flags']
26
+ @owner = @bot.cache.put_user(data['owner'])
27
+ end
28
+
29
+ def icon_url(format = nil)
30
+ return nil if @icon_id.nil?
31
+ MijDiscord::Core::API.app_icon_url(@id, @icon_id, format)
32
+ end
33
+
34
+ def inspect
35
+ %(<Application id=#{@id} name="#{@name}">)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,404 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MijDiscord::Data
4
+ class Channel
5
+ TYPES = {
6
+ 0 => :text,
7
+ 1 => :pm,
8
+ 2 => :voice,
9
+ 3 => :group,
10
+ 4 => :category,
11
+ }.freeze
12
+
13
+ include IDObject
14
+
15
+ attr_reader :bot
16
+
17
+ attr_reader :name
18
+
19
+ attr_reader :server
20
+
21
+ attr_reader :type
22
+
23
+ attr_reader :position
24
+
25
+ attr_reader :parent_id
26
+
27
+ attr_reader :permission_overwrites
28
+ alias_method :overwrites, :permission_overwrites
29
+
30
+ attr_reader :cache
31
+
32
+ def initialize(data, bot, server)
33
+ @bot = bot
34
+ @cache = MijDiscord::Cache::ChannelCache.new(self, @bot)
35
+
36
+ data = data[-1] if data.is_a?(Array)
37
+
38
+ @id = data['id'].to_i
39
+ update_data(data)
40
+
41
+ @server = server || @bot.server(data['guild_id']) unless private?
42
+ end
43
+
44
+ def self.create(data, bot, server)
45
+ case TYPES[data['type']]
46
+ when :text, :pm, :group
47
+ TextChannel.new(data, bot, server)
48
+ when :voice
49
+ VoiceChannel.new(data, bot, server)
50
+ when :category
51
+ ChannelCategory.new(data, bot, server)
52
+ else
53
+ raise RuntimeError, 'Broken channel object!'
54
+ end
55
+ end
56
+
57
+ def update_data(data)
58
+ @name = data.fetch('name', @name) unless pm?
59
+ @type_id = data.fetch('type', @type_id || 0)
60
+ @type = TYPES[@type_id]
61
+
62
+ @position = data.fetch('position', @position)
63
+ @parent_id = data.fetch('parent_id', @parent_id).to_i
64
+
65
+ if (perms = data['permission_overwrites'])
66
+ @permission_overwrites = {}
67
+
68
+ perms.each do |elem|
69
+ id = elem['id'].to_i
70
+ @permission_overwrites[id] = Overwrite.from_hash(elem)
71
+ end
72
+ end
73
+ end
74
+
75
+ def mention
76
+ "<##{@id}>"
77
+ end
78
+
79
+ alias_method :to_s, :mention
80
+
81
+ def text?
82
+ @type == :text
83
+ end
84
+
85
+ def pm?
86
+ @type == :pm
87
+ end
88
+
89
+ alias_method :dm?, :pm?
90
+
91
+ def voice?
92
+ @type == :voice
93
+ end
94
+
95
+ def group?
96
+ @type == :group
97
+ end
98
+
99
+ def category?
100
+ @type == :category
101
+ end
102
+
103
+ def private?
104
+ pm? || group?
105
+ end
106
+
107
+ def default_channel?
108
+ @server.default_channel == self
109
+ end
110
+
111
+ alias_method :default?, :default_channel?
112
+
113
+ def parent
114
+ @parent_id ? @server.cache.get_channel(@parent_id) : nil
115
+ end
116
+
117
+ def member_overwrites
118
+ @permission_overwrites.values.select {|v| v.type == :member }
119
+ end
120
+
121
+ def role_overwrites
122
+ @permission_overwrites.values.select {|v| v.type == :role }
123
+ end
124
+
125
+ def set_name(name, reason = nil)
126
+ return if private?
127
+
128
+ set_options(reason, name: name)
129
+ end
130
+
131
+ alias_method :name=, :set_name
132
+
133
+ def set_position(position, reason = nil)
134
+ set_options(reason, position: position)
135
+ end
136
+
137
+ alias_method :position=, :set_position
138
+
139
+ def set_parent(parent, reason = nil)
140
+ set_options(reason, parent: parent)
141
+ end
142
+
143
+ def set_options(reason = nil, name: nil, topic: nil, nsfw: nil,
144
+ parent: nil, position: nil, bitrate: nil, user_limit: nil)
145
+ response = MijDiscord::Core::API::Channel.update(@bot.token, @id,
146
+ name, topic, nsfw,parent&.to_id, position, bitrate, user_limit, reason)
147
+ @server.cache.put_channel(JSON.parse(response), update: true)
148
+ end
149
+
150
+ def define_overwrite(object, reason = nil, allow: 0, deny: 0)
151
+ unless object.is_a?(Overwrite)
152
+ allow_bits = allow.respond_to?(:bits) ? allow.bits : allow
153
+ deny_bits = deny.respond_to?(:bits) ? deny.bits : deny
154
+
155
+ object = Overwrite.new(object, allow: allow_bits, deny: deny_bits)
156
+ end
157
+
158
+ MijDiscord::Core::API::Channel.update_permission(@bot.token, @id,
159
+ object.id, object.allow.bits, object.deny.bits, object.type, reason)
160
+ nil
161
+ end
162
+
163
+ def delete_overwrite(object, reason = nil)
164
+ raise ArgumentError, 'Invalid overwrite target' unless object.respond_to?(:to_id)
165
+ MijDiscord::Core::API::Channel.delete_permission(@bot.token, @id,
166
+ object.to_id, reason)
167
+ nil
168
+ end
169
+
170
+ def delete(reason = nil)
171
+ MijDiscord::Core::API::Channel.delete(@bot.token, @id, reason)
172
+ @server.cache.remove_channel(@id)
173
+ end
174
+
175
+ def inspect
176
+ %(<Channel id=#{@id} server=#{@server.id} name="#{@name}" type=#{@type}>)
177
+ end
178
+ end
179
+
180
+ class TextChannel < Channel
181
+ attr_reader :topic
182
+
183
+ attr_reader :owner_id
184
+
185
+ attr_reader :nsfw
186
+ alias_method :nsfw?, :nsfw
187
+
188
+ attr_reader :recipients
189
+
190
+ def initialize(data, bot, server)
191
+ super(data, bot, server)
192
+
193
+ if private?
194
+ @recipients = []
195
+ if data['recipients']
196
+ data['recipients'].each do |rd|
197
+ user = @bot.cache.put_user(rd)
198
+ @recipients << Recipient.new(user, self, @bot)
199
+ end
200
+ end
201
+
202
+ if pm?
203
+ @name = @recipients.first.username
204
+ else
205
+ @owner_id = data['owner_id'].to_i
206
+ end
207
+ end
208
+ end
209
+
210
+ def update_data(data)
211
+ super(data)
212
+
213
+ @topic = data.fetch('topic', @topic)
214
+ @nsfw = data.fetch('nsfw', @nsfw)
215
+ end
216
+
217
+ def update_recipient(add: nil, remove: nil)
218
+ return unless group?
219
+
220
+ unless add.nil?
221
+ user = @bot.cache.put_user(add)
222
+ recipient = Recipient.new(user, self, @bot)
223
+ @recipients << recipient
224
+ return recipient
225
+ end
226
+
227
+ unless remove.nil?
228
+ id = remove['id'].to_i
229
+ recipient = @recipients.find {|x| x.id == id }
230
+ return @recipients.delete(recipient)
231
+ end
232
+ end
233
+
234
+ def owner
235
+ @owner_id ? @bot.cache.get_user(@owner_id) : nil
236
+ end
237
+
238
+ def set_topic(topic, reason = nil)
239
+ return unless text?
240
+
241
+ set_options(reason, topic: topic)
242
+ end
243
+
244
+ alias_method :topic=, :set_topic
245
+
246
+ def set_nsfw(nsfw, reason = nil)
247
+ return unless text?
248
+
249
+ set_options(reason, nsfw: nsfw)
250
+ end
251
+
252
+ alias_method :nsfw=, :set_nsfw
253
+
254
+ def send_message(text: '', embed: nil, tts: false)
255
+ raise MijDiscord::Core::Errors::MessageTooLong if text.length > 2000
256
+
257
+ response = MijDiscord::Core::API::Channel.create_message(@bot.token, @id,
258
+ text, tts, embed&.to_h)
259
+ @cache.put_message(JSON.parse(response))
260
+ end
261
+
262
+ def send_file(file, caption: '', tts: false)
263
+ raise MijDiscord::Core::Errors::MessageTooLong if caption.length > 2000
264
+
265
+ response = MijDiscord::Core::API::Channel.upload_file(@bot.token, @id, file, caption, tts)
266
+ @cache.put_message(JSON.parse(response))
267
+ end
268
+
269
+ def delete_message(message)
270
+ MijDiscord::Core::API::Channel.delete_message(@bot.token, @id, message.to_id)
271
+ @cache.remove_message(message)
272
+ end
273
+
274
+ def message(id)
275
+ @cache.get_message(id)
276
+ end
277
+
278
+ def message_history(amount, before: nil, after: nil, around: nil)
279
+ response = MijDiscord::Core::API::Channel.messages(@bot.token, @id,
280
+ amount, before&.to_id, after&.to_id, around&.to_id)
281
+ JSON.parse(response).map {|m| Message.new(m, @bot) }
282
+ end
283
+
284
+ alias_method :history, :message_history
285
+
286
+ def pinned_messages
287
+ response = MijDiscord::Core::API::Channel.pinned_messages(@bot.token, @id)
288
+ JSON.parse(response).map {|m| Message.new(m, @bot) }
289
+ end
290
+
291
+ alias_method :pinned, :pinned_messages
292
+
293
+ def delete_messages(messages)
294
+ two_weeks = Time.now - (14 * 86_400)
295
+ min_snowflake = IDObject.synthesize(two_weeks)
296
+ ids = messages.map(&:to_id).delete_if {|m| m < min_snowflake }
297
+
298
+ MijDiscord::Core::API::Channel.bulk_delete_messages(@bot.token, @id, ids)
299
+ ids.each {|m| @cache.remove_message(m) }
300
+ end
301
+
302
+ def invites
303
+ response = MijDiscord::Core::API::Channel.invites(@bot.token, @id)
304
+ JSON.parse(response).map {|x| Invite.new(x, @bot) }
305
+ end
306
+
307
+ def make_invite(reason = nil, max_age: 0, max_uses: 0, temporary: false, unique: false)
308
+ response = MijDiscord::Core::API::Channel.create_invite(@bot.token, @id,
309
+ max_age, max_uses, temporary, unique, reason)
310
+ Invite.new(JSON.parse(response), @bot)
311
+ end
312
+
313
+ alias_method :invite, :make_invite
314
+
315
+ def start_typing
316
+ MijDiscord::Core::API::Channel.start_typing(@bot.token, @id)
317
+ nil
318
+ end
319
+
320
+ def create_group(users)
321
+ raise 'Attempted to create group channel on a non-pm channel' unless pm?
322
+
323
+ ids = users.map(&:to_id)
324
+ response = MijDiscord::Core::API::Channel.create_group(@bot.token, @id, ids.shift)
325
+ channel = @bot.cache.put_channel(JSON.parse(response))
326
+ channel.add_group_users(ids)
327
+
328
+ channel
329
+ end
330
+
331
+ def add_group_users(users)
332
+ raise 'Attempted to add a user to a non-group channel' unless group?
333
+
334
+ users.each do |u|
335
+ MijDiscord::Core::API::Channel.add_group_user(@bot.token, @id, u.to_id)
336
+ end
337
+ nil
338
+ end
339
+
340
+ def remove_group_users(users)
341
+ raise 'Attempted to remove a user to a non-group channel' unless group?
342
+
343
+ users.each do |u|
344
+ MijDiscord::Core::API::Channel.remove_group_user(@bot.token, @id, u.to_id)
345
+ end
346
+ nil
347
+ end
348
+
349
+ def leave_group
350
+ raise 'Attempoted to leave a non-group channel' unless group?
351
+
352
+ MijDiscord::Core::API::Channel.leave_group(@bot.token, @id)
353
+ nil
354
+ end
355
+ end
356
+
357
+ class VoiceChannel < Channel
358
+ attr_reader :bitrate
359
+
360
+ attr_reader :user_limit
361
+ alias_method :limit, :user_limit
362
+
363
+ def initialize(data, bot, server)
364
+ super(data, bot, server)
365
+
366
+
367
+ end
368
+
369
+ def update_data(data)
370
+ super(data)
371
+
372
+ @bitrate = data.fetch('bitrate', @bitrate)
373
+ @user_limit = data.fetch('user_limit', @user_limit)
374
+ end
375
+
376
+ def set_bitrate(rate, reason = nil)
377
+ set_options(reason, bitrate: rate)
378
+ end
379
+
380
+ alias_method :bitrate=, :set_bitrate
381
+
382
+ def set_user_limit(limit, reason = nil)
383
+ set_options(reason, user_limit: limit)
384
+ end
385
+
386
+ alias_method :user_limit=, :set_user_limit
387
+ alias_method :set_limit, :set_user_limit
388
+ alias_method :limit=, :set_user_limit
389
+ end
390
+
391
+ class ChannelCategory < Channel
392
+ def initialize(data, bot, server)
393
+ super(data, bot, server)
394
+ end
395
+
396
+ def update_data(data)
397
+ super(data)
398
+ end
399
+
400
+ def channels
401
+ @server.channels.select! {|x| x.parent_id == @id }
402
+ end
403
+ end
404
+ end