mij-discord 1.0.0
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 +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
|