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