mij-discord 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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