mij-discord 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +35 -0
- data/Rakefile +10 -0
- data/bin/console +7 -0
- data/bin/setup +6 -0
- data/lib/mij-discord.rb +56 -0
- data/lib/mij-discord/bot.rb +579 -0
- data/lib/mij-discord/cache.rb +298 -0
- data/lib/mij-discord/core/api.rb +228 -0
- data/lib/mij-discord/core/api/channel.rb +416 -0
- data/lib/mij-discord/core/api/invite.rb +43 -0
- data/lib/mij-discord/core/api/server.rb +465 -0
- data/lib/mij-discord/core/api/user.rb +144 -0
- data/lib/mij-discord/core/errors.rb +106 -0
- data/lib/mij-discord/core/gateway.rb +505 -0
- data/lib/mij-discord/data.rb +65 -0
- data/lib/mij-discord/data/application.rb +38 -0
- data/lib/mij-discord/data/channel.rb +404 -0
- data/lib/mij-discord/data/embed.rb +115 -0
- data/lib/mij-discord/data/emoji.rb +62 -0
- data/lib/mij-discord/data/invite.rb +87 -0
- data/lib/mij-discord/data/member.rb +174 -0
- data/lib/mij-discord/data/message.rb +206 -0
- data/lib/mij-discord/data/permissions.rb +121 -0
- data/lib/mij-discord/data/role.rb +99 -0
- data/lib/mij-discord/data/server.rb +359 -0
- data/lib/mij-discord/data/user.rb +173 -0
- data/lib/mij-discord/data/voice.rb +68 -0
- data/lib/mij-discord/events.rb +133 -0
- data/lib/mij-discord/events/basic.rb +80 -0
- data/lib/mij-discord/events/channel.rb +50 -0
- data/lib/mij-discord/events/member.rb +66 -0
- data/lib/mij-discord/events/message.rb +150 -0
- data/lib/mij-discord/events/server.rb +102 -0
- data/lib/mij-discord/logger.rb +20 -0
- data/lib/mij-discord/version.rb +5 -0
- data/mij-discord.gemspec +31 -0
- metadata +154 -0
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MijDiscord::Data
|
4
|
+
class User
|
5
|
+
include IDObject
|
6
|
+
|
7
|
+
attr_reader :bot
|
8
|
+
|
9
|
+
attr_reader :username
|
10
|
+
alias_method :name, :username
|
11
|
+
|
12
|
+
attr_reader :discriminator
|
13
|
+
alias_method :tag, :discriminator
|
14
|
+
|
15
|
+
attr_reader :bot_account
|
16
|
+
alias_method :bot_account?, :bot_account
|
17
|
+
|
18
|
+
attr_reader :avatar_id
|
19
|
+
|
20
|
+
attr_reader :status
|
21
|
+
|
22
|
+
attr_reader :game
|
23
|
+
|
24
|
+
attr_reader :stream_url
|
25
|
+
|
26
|
+
attr_reader :stream_type
|
27
|
+
|
28
|
+
def initialize(data, bot)
|
29
|
+
@bot = bot
|
30
|
+
|
31
|
+
@id = data['id'].to_i
|
32
|
+
@bot_account = !!data['bot']
|
33
|
+
update_data(data)
|
34
|
+
|
35
|
+
@status = :offline
|
36
|
+
|
37
|
+
@roles = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def update_data(data)
|
41
|
+
@username = data.fetch('username', @username)
|
42
|
+
@discriminator = data.fetch('discriminator', @discriminator)
|
43
|
+
@avatar_id = data.fetch('avatar', @avatar_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def update_presence(presence)
|
47
|
+
@status = presence['status'].to_sym
|
48
|
+
|
49
|
+
if (game = presence['game'])
|
50
|
+
@game = game['name']
|
51
|
+
@stream_url = game['url']
|
52
|
+
@stream_type = game['type']
|
53
|
+
else
|
54
|
+
@game = @stream_url = @stream_type = nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def mention
|
59
|
+
"<@#{@id}>"
|
60
|
+
end
|
61
|
+
|
62
|
+
alias_method :to_s, :mention
|
63
|
+
|
64
|
+
def distinct
|
65
|
+
"#{@username}##{@discriminator}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def pm(text: nil, embed: nil)
|
69
|
+
if text || embed
|
70
|
+
pm.send_message(text: text || '', embed: embed)
|
71
|
+
else
|
72
|
+
@bot.pm_channel(@id)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
alias_method :dm, :pm
|
77
|
+
|
78
|
+
def send_file(file, caption: nil)
|
79
|
+
pm.send_file(file, caption: caption)
|
80
|
+
end
|
81
|
+
|
82
|
+
def on(server)
|
83
|
+
id = server.to_id
|
84
|
+
@bot.server(id).member(@id)
|
85
|
+
end
|
86
|
+
|
87
|
+
def webhook?
|
88
|
+
@discriminator == '0000'
|
89
|
+
end
|
90
|
+
|
91
|
+
def current_bot?
|
92
|
+
@bot.profile == self
|
93
|
+
end
|
94
|
+
|
95
|
+
def online?
|
96
|
+
@status == :online?
|
97
|
+
end
|
98
|
+
|
99
|
+
def idle?
|
100
|
+
@status == :idle
|
101
|
+
end
|
102
|
+
|
103
|
+
alias_method :away?, :idle?
|
104
|
+
|
105
|
+
def dnd?
|
106
|
+
@status == :dnd
|
107
|
+
end
|
108
|
+
|
109
|
+
alias_method :busy?, :dnd?
|
110
|
+
|
111
|
+
def invisible?
|
112
|
+
@status == :invisible
|
113
|
+
end
|
114
|
+
|
115
|
+
alias_method :hidden?, :invisible?
|
116
|
+
|
117
|
+
def offline?
|
118
|
+
@status == :offline
|
119
|
+
end
|
120
|
+
|
121
|
+
def member?
|
122
|
+
false
|
123
|
+
end
|
124
|
+
|
125
|
+
def avatar_url(format = nil)
|
126
|
+
return MijDiscord::Core::API::User.default_avatar(@discriminator) unless @avatar_id
|
127
|
+
MijDiscord::Core::API::User.avatar_url(@id, @avatar_id, format)
|
128
|
+
end
|
129
|
+
|
130
|
+
def inspect
|
131
|
+
%(<User id=#{@id} name="#{@username}" tag=#{@discriminator}>)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class Profile < User
|
136
|
+
attr_reader :mfa_enabled
|
137
|
+
alias_method :mfa_enabled?, :mfa_enabled
|
138
|
+
|
139
|
+
def update_data(data)
|
140
|
+
super(data)
|
141
|
+
|
142
|
+
@mfa_enabled = !!data['mfa_enabled']
|
143
|
+
end
|
144
|
+
|
145
|
+
def set_username(name)
|
146
|
+
response = MijDiscord::Core::API::User.update_profile(@bot.token, name, nil)
|
147
|
+
update_data(JSON.parse(response))
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
|
151
|
+
alias_method :username=, :set_username
|
152
|
+
alias_method :set_name, :set_username
|
153
|
+
alias_method :name=, :set_username
|
154
|
+
|
155
|
+
def set_avatar(data, format = :png)
|
156
|
+
if data.is_a?(String)
|
157
|
+
data = "data:image/#{format};base64,#{data}"
|
158
|
+
elsif data.respond_to?(:read)
|
159
|
+
data.binmode if data.respond_to?(:binmode)
|
160
|
+
data = Base64.strict_encode64(data.read)
|
161
|
+
data = "data:image/#{format};base64,#{data}"
|
162
|
+
else
|
163
|
+
raise ArgumentError, 'Invalid avatar data provided'
|
164
|
+
end
|
165
|
+
|
166
|
+
response = MijDiscord::Core::API::User.update_profile(@bot.token, @username, data)
|
167
|
+
update_data(JSON.parse(response))
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
|
171
|
+
alias_method :avatar=, :set_avatar
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MijDiscord::Data
|
4
|
+
class VoiceState
|
5
|
+
attr_reader :user
|
6
|
+
|
7
|
+
attr_reader :mute
|
8
|
+
alias_method :mute?, :mute
|
9
|
+
|
10
|
+
attr_reader :deaf
|
11
|
+
alias_method :deaf?, :deaf
|
12
|
+
|
13
|
+
attr_reader :self_mute
|
14
|
+
alias_method :self_mute?, :self_mute
|
15
|
+
|
16
|
+
attr_reader :self_deaf
|
17
|
+
alias_method :self_deaf?, :self_deaf
|
18
|
+
|
19
|
+
attr_reader :voice_channel
|
20
|
+
alias_method :channel, :voice_channel
|
21
|
+
|
22
|
+
def initialize(user)
|
23
|
+
@user = user
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_data(channel, data)
|
27
|
+
@voice_channel = channel
|
28
|
+
|
29
|
+
@mute = data.fetch('mute', @mute)
|
30
|
+
@deaf = data.fetch('deaf', @deaf)
|
31
|
+
@self_mute = data.fetch('self_mute', @self_mute)
|
32
|
+
@self_deaf = data.fetch('self_deaf', @self_deaf)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class VoiceRegion
|
37
|
+
attr_reader :id
|
38
|
+
alias_method :to_s, :id
|
39
|
+
|
40
|
+
attr_reader :name
|
41
|
+
|
42
|
+
attr_reader :sample_hostname
|
43
|
+
|
44
|
+
attr_reader :sample_port
|
45
|
+
|
46
|
+
attr_reader :vip
|
47
|
+
|
48
|
+
attr_reader :optimal
|
49
|
+
|
50
|
+
attr_reader :deprecated
|
51
|
+
|
52
|
+
attr_reader :custom
|
53
|
+
|
54
|
+
def initialize(data)
|
55
|
+
@id = data['id']
|
56
|
+
|
57
|
+
@name = data['name']
|
58
|
+
|
59
|
+
@sample_hostname = data['sample_hostname']
|
60
|
+
@sample_port = data['sample_port']
|
61
|
+
|
62
|
+
@vip = data['vip']
|
63
|
+
@optimal = data['optimal']
|
64
|
+
@deprecated = data['deprecated']
|
65
|
+
@custom = data['custom']
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MijDiscord::Events
|
4
|
+
class EventBase
|
5
|
+
FilterMatch = Struct.new(:field, :on, :cmp)
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
# Nothing
|
9
|
+
end
|
10
|
+
|
11
|
+
def trigger?(params)
|
12
|
+
filters = self.class.event_filters
|
13
|
+
|
14
|
+
result = params.map do |key, param|
|
15
|
+
next true unless filters.has_key?(key)
|
16
|
+
|
17
|
+
check = filters[key].map do |match|
|
18
|
+
on, field, cmp = match.on, match.field, match.cmp
|
19
|
+
|
20
|
+
is_match = case on
|
21
|
+
when Array
|
22
|
+
on.reduce(false) {|a,x| a || trigger_match?(x, param) }
|
23
|
+
else
|
24
|
+
trigger_match?(on, param)
|
25
|
+
end
|
26
|
+
|
27
|
+
next false unless is_match
|
28
|
+
|
29
|
+
value = case field
|
30
|
+
when Array
|
31
|
+
field.reduce(self) {|a,x| a.respond_to?(x) ? a.send(x) : nil }
|
32
|
+
else
|
33
|
+
respond_to?(field) ? send(field) : nil
|
34
|
+
end
|
35
|
+
|
36
|
+
case cmp
|
37
|
+
when :eql?
|
38
|
+
value == param
|
39
|
+
when :neq?
|
40
|
+
value != param
|
41
|
+
when :case
|
42
|
+
param === value
|
43
|
+
when Proc
|
44
|
+
cmp.call(value, param)
|
45
|
+
else
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
check.reduce(false, &:|)
|
51
|
+
end
|
52
|
+
|
53
|
+
result.reduce(true, &:&)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def trigger_match?(match, key)
|
59
|
+
case match
|
60
|
+
when :any, :all
|
61
|
+
true
|
62
|
+
when :id_obj
|
63
|
+
key.respond_to?(:to_id)
|
64
|
+
when Class
|
65
|
+
match === key
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class << self
|
70
|
+
attr_reader :event_filters
|
71
|
+
|
72
|
+
def filter_match(key, field: key, on: :any, cmp: nil, &block)
|
73
|
+
raise ArgumentError, 'No comparison function provided' unless cmp || block
|
74
|
+
|
75
|
+
# @event_filters ||= superclass&.event_filters&.dup || {}
|
76
|
+
filter = (@event_filters[key] ||= [])
|
77
|
+
filter << FilterMatch.new(field, on, block || cmp)
|
78
|
+
end
|
79
|
+
|
80
|
+
def delegate_method(*names, to:)
|
81
|
+
names.each do |name|
|
82
|
+
define_method(name) do |*arg|
|
83
|
+
send(to).send(name, *arg)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def inherited(sc)
|
89
|
+
filters = @event_filters&.dup || {}
|
90
|
+
sc.instance_variable_set(:@event_filters, filters)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class DispatcherBase
|
96
|
+
Callback = Struct.new(:key, :block, :filter)
|
97
|
+
|
98
|
+
def initialize(klass)
|
99
|
+
raise ArgumentError, 'Class must inherit from EventBase' unless klass < EventBase
|
100
|
+
|
101
|
+
@klass, @callbacks = klass, {}
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_callback(key = nil, **filter, &block)
|
105
|
+
raise ArgumentError, 'No callback block provided' if block.nil?
|
106
|
+
|
107
|
+
key = block.object_id if key.nil?
|
108
|
+
@callbacks[key] = Callback.new(key, block, filter)
|
109
|
+
key
|
110
|
+
end
|
111
|
+
|
112
|
+
def remove_callback(key)
|
113
|
+
@callbacks.delete(key)
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
def callbacks
|
118
|
+
@callbacks.values
|
119
|
+
end
|
120
|
+
|
121
|
+
def trigger(event_args, block_args = nil)
|
122
|
+
event = @klass.new(*event_args)
|
123
|
+
|
124
|
+
@callbacks.each do |_, cb|
|
125
|
+
execute_callback(cb, event, block_args) if event.trigger?(cb.filter)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
alias_method :raise, :trigger
|
130
|
+
|
131
|
+
# Must implement execute_callback
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MijDiscord::Events
|
4
|
+
class Generic < EventBase
|
5
|
+
attr_reader :bot
|
6
|
+
|
7
|
+
def initialize(bot)
|
8
|
+
@bot = bot
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Ready < Generic; end
|
13
|
+
|
14
|
+
class Heartbeat < Generic; end
|
15
|
+
|
16
|
+
class Connect < Generic; end
|
17
|
+
|
18
|
+
class Disconnect < Generic; end
|
19
|
+
|
20
|
+
class Exception < Generic
|
21
|
+
attr_reader :type
|
22
|
+
|
23
|
+
attr_reader :payload
|
24
|
+
|
25
|
+
attr_reader :exception
|
26
|
+
|
27
|
+
filter_match(:type, on: Symbol, cmp: :eql?)
|
28
|
+
|
29
|
+
def initialize(bot, type, exception, payload = nil)
|
30
|
+
super(bot)
|
31
|
+
|
32
|
+
@type, @exception, @payload = type, exception, payload
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class UpdateUser < Generic
|
37
|
+
attr_reader :user
|
38
|
+
|
39
|
+
filter_match(:user, field: [:user, :name], on: [String, Regexp], cmp: :case)
|
40
|
+
filter_match(:user, on: :id_obj, cmp: :eql?)
|
41
|
+
|
42
|
+
def initialize(bot, user)
|
43
|
+
super(bot)
|
44
|
+
|
45
|
+
@user = user
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class EventDispatcher < MijDiscord::Events::DispatcherBase
|
50
|
+
attr_reader :threads
|
51
|
+
|
52
|
+
def initialize(klass, bot)
|
53
|
+
super(klass)
|
54
|
+
|
55
|
+
@bot, @threads = bot, []
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute_callback(callback, event, _)
|
59
|
+
Thread.new do
|
60
|
+
thread = Thread.current
|
61
|
+
|
62
|
+
@threads << thread
|
63
|
+
thread[:mij_discord] = "event-#{callback.key}"
|
64
|
+
|
65
|
+
begin
|
66
|
+
callback.block.call(event, callback.key)
|
67
|
+
rescue LocalJumpError
|
68
|
+
# Allow premature return from callback block
|
69
|
+
rescue => exc
|
70
|
+
@bot.handle_exception(:event, exc, event)
|
71
|
+
|
72
|
+
MijDiscord::LOGGER.error('Events') { 'An error occurred in event callback' }
|
73
|
+
MijDiscord::LOGGER.error('Events') { exc }
|
74
|
+
ensure
|
75
|
+
@threads.delete(thread)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|