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 +4 -4
- data/lib/mij-discord/bot.rb +52 -35
- data/lib/mij-discord/cache.rb +4 -1
- data/lib/mij-discord/core/api.rb +1 -3
- data/lib/mij-discord/core/api/user.rb +13 -1
- data/lib/mij-discord/core/gateway.rb +9 -1
- data/lib/mij-discord/data/server.rb +29 -11
- data/lib/mij-discord/data/user.rb +115 -8
- data/lib/mij-discord/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aca2803ee72c1ab5229d6767b809ee5a3a1aa605
|
4
|
+
data.tar.gz: 037b4495e988f6c7c70e83801920fda475b9a262
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ff78a29820234b6ff79a848ccb0fb9b77019d645ec18bb6040a3566923f3e775462f01224a8df803ac07d31f29d55d4f7eb50c7de3ae5e464eac19b05c7fb0d
|
7
|
+
data.tar.gz: 641bcc97f7a0039e655eb7ad4800a1dfcf0d62c9a5ff9333243204dc2eb9fbec24e897a4b54b40bf0288bece09cff9f740bc3a5308ade781647adb8b908fd0ac
|
data/lib/mij-discord/bot.rb
CHANGED
@@ -1,14 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module MijDiscord
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
class Bot
|
5
|
+
class AuthInfo
|
6
|
+
attr_reader :token
|
7
7
|
|
8
8
|
attr_reader :type
|
9
9
|
|
10
|
-
|
11
|
-
|
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
|
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
|
306
|
+
def update_presence(status: nil, game: nil)
|
300
307
|
gateway_check
|
301
308
|
|
302
|
-
status =
|
303
|
-
|
304
|
-
|
305
|
-
|
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
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
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.
|
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.
|
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.
|
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)
|
data/lib/mij-discord/cache.rb
CHANGED
@@ -81,7 +81,10 @@ module MijDiscord::Cache
|
|
81
81
|
return nil if local
|
82
82
|
|
83
83
|
begin
|
84
|
-
response =
|
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
|
data/lib/mij-discord/core/api.rb
CHANGED
@@ -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 =
|
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
|
-
@
|
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
|
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 == @
|
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 :
|
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
|
51
|
-
@stream_url = game['url']
|
52
|
-
@stream_type = game['type']
|
157
|
+
@game = Game.new(game)
|
53
158
|
else
|
54
|
-
@game =
|
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
|
data/lib/mij-discord/version.rb
CHANGED
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.
|
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
|
+
date: 2017-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|