mij-discord 1.0.6 → 1.0.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a986d989e1c82e40f9c1c5346b3496a402491f3e
4
- data.tar.gz: 8db9cd45703269a7c40e03beccc2c4dcf272b0d3
3
+ metadata.gz: aca2803ee72c1ab5229d6767b809ee5a3a1aa605
4
+ data.tar.gz: 037b4495e988f6c7c70e83801920fda475b9a262
5
5
  SHA512:
6
- metadata.gz: 31147c15e9b63573c41bba6b700b9be15002be8b7262ca42c2fc50a7fc0ed7486ca08e6833a98ee53f58a4cbbdc151d05b6a43ee0da399698f291b7925100e9c
7
- data.tar.gz: e5d0c9e7383b825b8bb513177361cb9dbd56650fd8f3563ca18c878ac3a9269f816f2b3d84e43c5d18b97ac15b41f43316e5831d69f920cf828781cb5c31686a
6
+ metadata.gz: 2ff78a29820234b6ff79a848ccb0fb9b77019d645ec18bb6040a3566923f3e775462f01224a8df803ac07d31f29d55d4f7eb50c7de3ae5e464eac19b05c7fb0d
7
+ data.tar.gz: 641bcc97f7a0039e655eb7ad4800a1dfcf0d62c9a5ff9333243204dc2eb9fbec24e897a4b54b40bf0288bece09cff9f740bc3a5308ade781647adb8b908fd0ac
@@ -1,14 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MijDiscord
4
- class Bot
5
- class AuthInfo
6
- attr_reader :token
4
+ class Bot
5
+ class AuthInfo
6
+ attr_reader :token
7
7
 
8
8
  attr_reader :type
9
9
 
10
- def initialize(token, type)
11
- @token, @type = token, type
10
+ attr_reader :name
11
+
12
+ def initialize(token, type, name)
13
+ @type, @name = type, name
14
+
15
+ @token = case type
16
+ when :bot then "Bot #{token}"
17
+ when :user then "#{token}"
18
+ else raise ArgumentError, 'Invalid token type'
19
+ end
20
+ end
21
+
22
+ def bot?
23
+ @type == :bot
24
+ end
25
+
26
+ def user?
27
+ @type == :user
12
28
  end
13
29
 
14
30
  alias_method :to_s, :token
@@ -60,8 +76,6 @@ module MijDiscord
60
76
 
61
77
  UNAVAILABLE_SERVER_TIMEOUT = 10
62
78
 
63
- USER_STATUS = [:online, :idle, :dnd, :invisible, :offline].freeze
64
-
65
79
  attr_reader :name
66
80
 
67
81
  attr_reader :client_id
@@ -78,15 +92,8 @@ module MijDiscord
78
92
 
79
93
  def initialize(client_id:, token:, type: :bot, name: nil,
80
94
  shard_id: nil, num_shards: nil, ignore_bots: false, ignore_self: true)
81
- @client_id, @name = client_id.to_id, name || ''
82
-
83
- token = case type
84
- when :bot then "Bot #{token}"
85
- when :user then "#{token}"
86
- else raise ArgumentError, 'Invalid bot type'
87
- end
88
-
89
- @auth = AuthInfo.new(token, type)
95
+ @client_id = client_id.to_id
96
+ @auth = AuthInfo.new(token, type, name)
90
97
 
91
98
  @cache = MijDiscord::Cache::BotCache.new(self)
92
99
 
@@ -296,26 +303,26 @@ module MijDiscord
296
303
  @ignore_self && user.to_id == @client_id || @ignored_ids.include?(user.to_id)
297
304
  end
298
305
 
299
- def change_status(status: nil, game: nil, url: nil)
306
+ def update_presence(status: nil, game: nil)
300
307
  gateway_check
301
308
 
302
- status = status.nil? ? @profile.status : USER_STATUS.find(status)
303
- raise ArgumentError, "Status '#{status}' is not valid" unless status
304
-
305
- game_obj = case game
306
- when false
307
- nil
308
- when nil
309
- {'name' => @profile.game, 'url' => @profile.stream_url, 'type' => @profile.stream_type}
310
- else
311
- {'name' => game, 'url' => url, 'type' => url ? 1 : 0}
309
+ status = case status
310
+ when nil then @profile.status
311
+ when :online, :idle, :dnd, :online then status
312
+ else raise ArgumentError, 'Invalid status'
312
313
  end
313
314
 
314
- game_obj&.reject! {|_,v| v.nil? }
315
- game_obj = nil if game_obj&.empty?
316
-
317
- @gateway.send_status_update(status, nil, game_obj, false)
318
- @profile.update_presence('status' => status, 'game' => game_obj)
315
+ game = case game
316
+ when nil then @profile.game
317
+ when false then nil
318
+ when String, Hash
319
+ MijDiscord::Data::Game.construct(game)
320
+ when MijDiscord::Data::Game then game
321
+ else raise ArgumentError, 'Invalid game'
322
+ end&.to_hash
323
+
324
+ @gateway.send_status_update(status, nil, game, false)
325
+ @profile.update_presence('status' => status, 'game' => game)
319
326
  nil
320
327
  end
321
328
 
@@ -372,6 +379,7 @@ module MijDiscord
372
379
  when :GUILD_MEMBERS_CHUNK
373
380
  server = @cache.get_server(data['guild_id'])
374
381
  server.update_members_chunk(data['members'])
382
+ puts "Chunk(#{server.id}+#{data['members'].length})"
375
383
 
376
384
  when :GUILD_CREATE
377
385
  server = @cache.put_server(data)
@@ -386,6 +394,10 @@ module MijDiscord
386
394
 
387
395
  trigger_event(:create_server, self, server)
388
396
 
397
+ when :GUILD_SYNC
398
+ server = @cache.get_server(data['id'])
399
+ server.update_synced_data(data)
400
+
389
401
  when :GUILD_UPDATE
390
402
  server = @cache.put_server(data, update: true)
391
403
  trigger_event(:update_server, self, server)
@@ -424,17 +436,17 @@ module MijDiscord
424
436
 
425
437
  when :GUILD_MEMBER_ADD
426
438
  server = @cache.get_server(data['guild_id'])
427
- member = server.cache.put_member(data)
439
+ member = server.update_member(data, :add)
428
440
  trigger_event(:create_member, self, member, server)
429
441
 
430
442
  when :GUILD_MEMBER_UPDATE
431
443
  server = @cache.get_server(data['guild_id'])
432
- member = server.cache.put_member(data, update: true)
444
+ member = server.update_member(data, :update)
433
445
  trigger_event(:update_member, self, member, server)
434
446
 
435
447
  when :GUILD_MEMBER_REMOVE
436
448
  server = @cache.get_server(data['guild_id'])
437
- member = server.cache.remove_member(data['user']['id'])
449
+ member = server.update_member(data, :remove)
438
450
  trigger_event(:delete_member, self, member, server)
439
451
 
440
452
  when :GUILD_ROLE_CREATE
@@ -573,6 +585,11 @@ module MijDiscord
573
585
  @gateway.notify_ready
574
586
 
575
587
  trigger_event(:ready, self)
588
+
589
+ if @auth.user?
590
+ guilds = @cache.list_servers.map(&:id)
591
+ @gateway.send_request_guild_sync(guilds)
592
+ end
576
593
  end
577
594
 
578
595
  def trigger_event(name, *args)
@@ -81,7 +81,10 @@ module MijDiscord::Cache
81
81
  return nil if local
82
82
 
83
83
  begin
84
- response = MijDiscord::Core::API::User.resolve(@bot.auth, id)
84
+ response = case @bot.auth.type
85
+ when :bot then MijDiscord::Core::API::User.resolve(@bot.auth, id)
86
+ when :user then MijDiscord::Core::API::User.resolve2(@bot.auth, id)
87
+ end
85
88
  rescue RestClient::ResourceNotFound
86
89
  return nil
87
90
  end
@@ -6,12 +6,10 @@ module MijDiscord::Core::API
6
6
  CDN_URL = 'https://cdn.discordapp.com'
7
7
 
8
8
  class << self
9
- attr_accessor :bot_name
10
-
11
9
  def user_agent(auth)
12
10
  case auth&.type
13
11
  when :bot
14
- bot_name = @bot_name || 'generic'
12
+ bot_name = auth.name || 'generic'
15
13
  ua_base = "DiscordBot (https://github.com/Mijyuoon/mij-discord, v#{MijDiscord::VERSION})"
16
14
  "#{ua_base} mij-discord/#{MijDiscord::VERSION} #{bot_name}"
17
15
 
@@ -14,7 +14,19 @@ module MijDiscord::Core::API::User
14
14
  )
15
15
  end
16
16
 
17
- # Get profile data
17
+ # Get profile data (for userbots only)
18
+ # Not officially documented, reverse engineered from tracking Discord's network activity
19
+ def resolve2(auth, user_id)
20
+ MijDiscord::Core::API.request(
21
+ :users_uid,
22
+ nil,
23
+ :get,
24
+ "#{MijDiscord::Core::API::APIBASE_URL}/users/#{user_id}/profile",
25
+ Authorization: auth
26
+ )
27
+ end
28
+
29
+ # Get current user data
18
30
  # https://discordapp.com/developers/docs/resources/user#get-current-user
19
31
  def profile(auth)
20
32
  MijDiscord::Core::API.request(
@@ -60,6 +60,10 @@ module MijDiscord::Core
60
60
  # **Received**: Returned after a heartbeat was sent to the server. This allows clients to identify and deal with
61
61
  # zombie connections that don't dispatch any events anymore.
62
62
  HEARTBEAT_ACK = 11
63
+
64
+ # **Sent**: [Undocumented] This opcode makes the gateway to send current state of a server. This includes a list
65
+ # of members and their presences, which mirror the format of GUILD_CREATE event.
66
+ GUILD_SYNC = 12
63
67
  end
64
68
 
65
69
  # @!visibility private
@@ -238,7 +242,7 @@ module MijDiscord::Core
238
242
  send_packet(Opcodes::RESUME, data)
239
243
  end
240
244
 
241
- def send_request_members(server_id, query, limit)
245
+ def send_request_members(server_id, query = '', limit = 0)
242
246
  data = {
243
247
  guild_id: server_id,
244
248
  query: query,
@@ -248,6 +252,10 @@ module MijDiscord::Core
248
252
  send_packet(Opcodes::REQUEST_MEMBERS, data)
249
253
  end
250
254
 
255
+ def send_request_guild_sync(guilds)
256
+ send_packet(Opcodes::GUILD_SYNC, guilds)
257
+ end
258
+
251
259
  def send_packet(opcode, packet)
252
260
  data = {
253
261
  op: opcode,
@@ -66,7 +66,7 @@ module MijDiscord::Data
66
66
 
67
67
  @id = data['id'].to_i
68
68
  @large = data['large']
69
- @members_init = data['member_count']
69
+ @member_count = data['member_count']
70
70
  @members_chunked = 0
71
71
 
72
72
  @cache = MijDiscord::Cache::ServerCache.new(self, @bot)
@@ -75,15 +75,7 @@ module MijDiscord::Data
75
75
 
76
76
  data['roles']&.each {|ro| @cache.put_role(ro) }
77
77
 
78
- data['members']&.each {|mb| @cache.put_member(mb) }
79
-
80
- data['presences']&.each do |pr|
81
- next unless pr['user']
82
-
83
- user_id = pr['user']['id'].to_i
84
- user = @cache.get_member(user_id, local: true)
85
- user.update_presence(pr) if user
86
- end
78
+ update_synced_data(data)
87
79
 
88
80
  @voice_states = {}
89
81
  data['voice_states']&.each {|vs| update_voice_state(vs) }
@@ -146,7 +138,33 @@ module MijDiscord::Data
146
138
  @members_chunked += data.length
147
139
  data.each {|mb| @cache.put_member(mb) }
148
140
 
149
- @members_chunked = nil if @members_chunked == @members_init
141
+ @members_chunked = nil if @members_chunked == @member_count
142
+ end
143
+
144
+ def update_synced_data(data)
145
+ data['members']&.each {|mb| @cache.put_member(mb, update: true) }
146
+
147
+ data['presences']&.each do |pr|
148
+ next unless pr['user']
149
+
150
+ user_id = pr['user']['id'].to_i
151
+ user = @cache.get_member(user_id, local: true)
152
+ user.update_presence(pr) if user
153
+ end
154
+ end
155
+
156
+ def update_member(data, mode)
157
+ case mode
158
+ when :add
159
+ @member_count += 1
160
+ @cache.put_member(data)
161
+ when :remove
162
+ @member_count -= 1
163
+ key = data['user']['id']
164
+ @cache.remove_member(key)
165
+ when :update
166
+ @cache.put_member(data, update: true)
167
+ end
150
168
  end
151
169
 
152
170
  def channels
@@ -1,6 +1,112 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MijDiscord::Data
4
+ class Game
5
+ PLAYING_TYPE = [
6
+ :playing,
7
+ :streaming,
8
+ :listening,
9
+ :watching,
10
+ ].freeze
11
+
12
+ attr_reader :name
13
+
14
+ attr_reader :url
15
+
16
+ attr_reader :details
17
+
18
+ attr_reader :state
19
+
20
+ attr_reader :start_time
21
+
22
+ attr_reader :end_time
23
+
24
+ attr_reader :application
25
+
26
+ attr_reader :large_image
27
+
28
+ attr_reader :large_text
29
+
30
+ attr_reader :small_image
31
+
32
+ attr_reader :small_text
33
+
34
+ def type
35
+ PLAYING_TYPE[@type]
36
+ end
37
+
38
+ def initialize(data)
39
+ @type = data['type']
40
+ @name = data['name']
41
+ @url = data['url']
42
+ @details = data['details']
43
+ @state = data['state']
44
+
45
+ if (start_time = data.dig('timestamps', 'start'))
46
+ @start_time = Time.at(start_time).utc
47
+ end
48
+ if (end_time = data.dig('timestamps', 'end'))
49
+ @end_time = Time.at(end_time).utc
50
+ end
51
+
52
+ if (assets = data['assets'])
53
+ @large_image = assets['large_image']
54
+ @large_text = assets['large_text']
55
+ @small_image = assets['small_image']
56
+ @small_text = assets['small_text']
57
+ end
58
+ end
59
+
60
+ def to_hash
61
+ self.class.construct({
62
+ start_time: @start_time,
63
+ end_time: @end_time,
64
+
65
+ large_image: @large_image,
66
+ large_text: @large_text,
67
+ small_image: @small_image,
68
+ small_text: @small_text,
69
+
70
+ type: @type,
71
+ name: @name,
72
+ url: @url,
73
+ details: @details,
74
+ state: @state,
75
+ })
76
+ end
77
+
78
+ def self.construct(data)
79
+ data = {name: data} if data.is_a?(String)
80
+
81
+ times = {
82
+ start: data[:start_time]&.to_i,
83
+ end: data[:end_time]&.to_i,
84
+ }.delete_if {|_,v| v.nil? }
85
+
86
+ assets = {
87
+ large_image: data[:large_image],
88
+ large_text: data[:large_text],
89
+ small_image: data[:small_image],
90
+ small_text: data[:small_text],
91
+ }.delete_if {|_,v| v.nil? }
92
+
93
+ type = PLAYING_TYPE.index(data[:type])
94
+
95
+ game = {
96
+ type: type || 0,
97
+ name: data[:name],
98
+ url: data[:url],
99
+ details: data[:details],
100
+ state: data[:state],
101
+
102
+ timestamps: times.empty? ? nil : times,
103
+ assets: assets.empty? ? nil : assets,
104
+ }.delete_if {|_,v| v.nil? }
105
+
106
+ game
107
+ end
108
+ end
109
+
4
110
  class User
5
111
  include IDObject
6
112
 
@@ -21,18 +127,19 @@ module MijDiscord::Data
21
127
 
22
128
  attr_reader :game
23
129
 
24
- attr_reader :stream_url
25
-
26
- attr_reader :stream_type
130
+ attr_reader :extra
27
131
 
28
132
  def initialize(data, bot)
29
133
  @bot = bot
30
134
 
135
+ # Kludge for User::resolve2 API call
136
+ data = data['user'] if data['user'].is_a?(Hash)
137
+
31
138
  @id = data['id'].to_i
32
139
  @bot_account = !!data['bot']
33
140
  update_data(data)
34
141
 
35
- @status = :offline
142
+ @status, @game = :offline, nil
36
143
 
37
144
  @roles = {}
38
145
  end
@@ -47,11 +154,9 @@ module MijDiscord::Data
47
154
  @status = presence['status'].to_sym
48
155
 
49
156
  if (game = presence['game'])
50
- @game = game['name']
51
- @stream_url = game['url']
52
- @stream_type = game['type']
157
+ @game = Game.new(game)
53
158
  else
54
- @game = @stream_url = @stream_type = nil
159
+ @game = nil
55
160
  end
56
161
  end
57
162
 
@@ -126,6 +231,8 @@ module MijDiscord::Data
126
231
  return MijDiscord::Core::API::User.default_avatar(@discriminator) unless @avatar_id
127
232
  MijDiscord::Core::API::User.avatar_url(@id, @avatar_id, format)
128
233
  end
234
+
235
+ alias_method :avatar, :avatar_url
129
236
  end
130
237
 
131
238
  class Profile < User
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MijDiscord
4
- VERSION = '1.0.6'
4
+ VERSION = '1.0.7'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mij-discord
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mijyuoon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-22 00:00:00.000000000 Z
11
+ date: 2017-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client