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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE +21 -0
  6. data/README.md +35 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +7 -0
  9. data/bin/setup +6 -0
  10. data/lib/mij-discord.rb +56 -0
  11. data/lib/mij-discord/bot.rb +579 -0
  12. data/lib/mij-discord/cache.rb +298 -0
  13. data/lib/mij-discord/core/api.rb +228 -0
  14. data/lib/mij-discord/core/api/channel.rb +416 -0
  15. data/lib/mij-discord/core/api/invite.rb +43 -0
  16. data/lib/mij-discord/core/api/server.rb +465 -0
  17. data/lib/mij-discord/core/api/user.rb +144 -0
  18. data/lib/mij-discord/core/errors.rb +106 -0
  19. data/lib/mij-discord/core/gateway.rb +505 -0
  20. data/lib/mij-discord/data.rb +65 -0
  21. data/lib/mij-discord/data/application.rb +38 -0
  22. data/lib/mij-discord/data/channel.rb +404 -0
  23. data/lib/mij-discord/data/embed.rb +115 -0
  24. data/lib/mij-discord/data/emoji.rb +62 -0
  25. data/lib/mij-discord/data/invite.rb +87 -0
  26. data/lib/mij-discord/data/member.rb +174 -0
  27. data/lib/mij-discord/data/message.rb +206 -0
  28. data/lib/mij-discord/data/permissions.rb +121 -0
  29. data/lib/mij-discord/data/role.rb +99 -0
  30. data/lib/mij-discord/data/server.rb +359 -0
  31. data/lib/mij-discord/data/user.rb +173 -0
  32. data/lib/mij-discord/data/voice.rb +68 -0
  33. data/lib/mij-discord/events.rb +133 -0
  34. data/lib/mij-discord/events/basic.rb +80 -0
  35. data/lib/mij-discord/events/channel.rb +50 -0
  36. data/lib/mij-discord/events/member.rb +66 -0
  37. data/lib/mij-discord/events/message.rb +150 -0
  38. data/lib/mij-discord/events/server.rb +102 -0
  39. data/lib/mij-discord/logger.rb +20 -0
  40. data/lib/mij-discord/version.rb +5 -0
  41. data/mij-discord.gemspec +31 -0
  42. metadata +154 -0
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MijDiscord::Data
4
+ class Embed
5
+ attr_reader :type
6
+
7
+ attr_reader :title
8
+
9
+ attr_reader :description
10
+
11
+ attr_reader :url
12
+
13
+ attr_reader :timestamp
14
+
15
+ attr_reader :color
16
+
17
+ attr_reader :footer
18
+
19
+ attr_reader :thumbnail
20
+
21
+ attr_reader :image
22
+
23
+ attr_reader :video
24
+
25
+ attr_reader :provider
26
+
27
+ attr_reader :author
28
+
29
+ attr_reader :fields
30
+
31
+ def initialize(data)
32
+ @type, @url = data['type'], data['url']
33
+ @title, @description = data['title'], data['description']
34
+
35
+ @color = ColorRGB.new(data['color']) if data['color']
36
+ @timestamp = Time.parse(data['timestamp']).utc if data['timestamp']
37
+
38
+ @footer = EmbedFooter.new(data['footer']) if data['footer']
39
+ @thumbnail = EmbedMedia.new(data['thumbnail']) if data['thumbnail']
40
+
41
+ @image = EmbedMedia.new(data['image']) if data['image']
42
+ @video = EmbedMedia.new(data['video']) if data['video']
43
+
44
+ @author = EmbedAuthor.new(data['author']) if data['author']
45
+ @provider = EmbedProvider.new(data['provider']) if data['provider']
46
+
47
+ @fields = data['fields']&.map {|x| EmbedField.new(x) }
48
+ end
49
+ end
50
+
51
+ class EmbedFooter
52
+ attr_reader :text
53
+
54
+ attr_reader :icon_url
55
+
56
+ attr_reader :proxy_icon_url
57
+
58
+ def initialize(data)
59
+ @text, @icon_url = data['text'], data['icon_url']
60
+ @proxy_icon_url = data['proxy_icon_url']
61
+ end
62
+ end
63
+
64
+ class EmbedMedia
65
+ attr_reader :url
66
+
67
+ attr_reader :proxy_url
68
+
69
+ attr_reader :width
70
+
71
+ attr_reader :height
72
+
73
+ def initialize(data)
74
+ @url, @width, @height = data['url'], data['width'], data['height']
75
+ @proxy_url = data['proxy_url']
76
+ end
77
+ end
78
+
79
+ class EmbedAuthor
80
+ attr_reader :name
81
+
82
+ attr_reader :url
83
+
84
+ attr_reader :icon_url
85
+
86
+ attr_reader :proxy_icon_url
87
+
88
+ def initialize(data)
89
+ @name, @url, @icon_url = data['name'], data['url'], data['icon_url']
90
+ @proxy_icon_url = data['proxy_icon_url']
91
+ end
92
+ end
93
+
94
+ class EmbedProvider
95
+ attr_reader :name
96
+
97
+ attr_reader :url
98
+
99
+ def initialize(data)
100
+ @name, @url = data['name'], data['url']
101
+ end
102
+ end
103
+
104
+ class EmbedField
105
+ attr_reader :name
106
+
107
+ attr_reader :value
108
+
109
+ attr_reader :inline
110
+
111
+ def initialize(data)
112
+ @name, @value, @inline = data['name'], data['value'], data['inline']
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MijDiscord::Data
4
+ class Emoji
5
+ include IDObject
6
+
7
+ attr_reader :name
8
+
9
+ attr_reader :server
10
+
11
+ attr_reader :roles
12
+
13
+ def initialize(data, bot, server)
14
+ @bot, @server = bot, server
15
+
16
+ @id = data['id'].to_i
17
+ @name = data['name']
18
+
19
+ @roles = []
20
+ if @server && (roles = data['roles'])
21
+ @roles = roles.map {|ro| @server.role(ro) }
22
+ end
23
+ end
24
+
25
+ def mention
26
+ "<:#{@name}:#{@id}>"
27
+ end
28
+
29
+ alias_method :to_s, :mention
30
+
31
+ def reaction
32
+ @id.zero? ? @name : "#{@name}:#{@id}"
33
+ end
34
+
35
+ def icon_url(format = :png)
36
+ MijDiscord::Core::API.emoji_icon_url(@id, format)
37
+ end
38
+
39
+ def inspect
40
+ %(<Emoji id=#{@id} name=#{@name}>)
41
+ end
42
+ end
43
+
44
+ class Reaction
45
+ attr_reader :id
46
+
47
+ attr_reader :name
48
+
49
+ attr_reader :count
50
+
51
+ attr_reader :me
52
+ alias_method :me?, :me
53
+
54
+ def initialize(data)
55
+ @me = !!data['me']
56
+ @count = data['count'] || 1
57
+
58
+ @id = data['emoji']['id']&.to_i
59
+ @name = data['emoji']['name']
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MijDiscord::Data
4
+ class InviteChannel
5
+ include IDObject
6
+
7
+ attr_reader :name
8
+
9
+ attr_reader :type
10
+
11
+ def initialize(data, bot)
12
+ @bot = bot
13
+
14
+ @id = data['id'].to_i
15
+ @name = data['name']
16
+ @type = data['type']
17
+ end
18
+ end
19
+
20
+ class InviteServer
21
+ include IDObject
22
+
23
+ attr_reader :name
24
+
25
+ attr_reader :splash_hash
26
+
27
+ def initialize(data, bot)
28
+ @bot = bot
29
+
30
+ @id = data['id'].to_i
31
+ @name = data['name']
32
+ @splash_hash = data['splash_hash']
33
+ end
34
+ end
35
+
36
+ class Invite
37
+ attr_reader :channel
38
+
39
+ attr_reader :server
40
+
41
+ attr_reader :code
42
+
43
+ attr_reader :max_uses
44
+ alias_method :uses, :max_uses
45
+
46
+ attr_reader :inviter
47
+ alias_method :user, :inviter
48
+
49
+ attr_reader :temporary
50
+ alias_method :temporary?, :temporary
51
+
52
+ attr_reader :revoked
53
+ alias_method :revoked?, :revoked
54
+
55
+ def initialize(data, bot)
56
+ @bot = bot
57
+
58
+ @channel = InviteChannel.new(data['channel'], bot)
59
+ @server = InviteServer.new(data['guild'], bot)
60
+
61
+ @code, @max_uses = data['code'], data['uses']
62
+ @temporary, @revoked = data['temporary'], data['revoked']
63
+
64
+ @inviter = data['inviter'] ? @bot.cache.put_user(data['inviter']) : nil
65
+ end
66
+
67
+ def ==(other)
68
+ @code == (other.respond_to?(:code) ? other.code : other)
69
+ end
70
+
71
+ alias_method :eql?, :==
72
+
73
+ def delete(reason = nil)
74
+ MijDiscord::Core::API::Invite.delete(@bot.token, @code, reason)
75
+ end
76
+
77
+ alias_method :revoke, :delete
78
+
79
+ def invite_url
80
+ "https://discord.gg/#{@code}"
81
+ end
82
+
83
+ def inspect
84
+ %(<Invite code=#{@code} uses=#{@max_uses} temporary=#{@temporary} revoked=#{@revoked}>)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MijDiscord::Data
4
+ class Member < DelegateClass(User)
5
+ include PermissionObject
6
+
7
+ attr_reader :bot
8
+
9
+ attr_reader :joined_at
10
+
11
+ attr_reader :nickname
12
+ alias_method :nick, :nickname
13
+
14
+ attr_reader :roles
15
+
16
+ attr_reader :server
17
+
18
+ def mute?
19
+ voice_state_attribute(:mute)
20
+ end
21
+
22
+ def deaf?
23
+ voice_state_attribute(:deaf)
24
+ end
25
+
26
+ def self_mute?
27
+ voice_state_attribute(:self_mute)
28
+ end
29
+
30
+ def self_deaf?
31
+ voice_state_attribute(:self_deaf)
32
+ end
33
+
34
+ def voice_channel
35
+ voice_state_attribute(:voice_channel)
36
+ end
37
+
38
+ def initialize(data, server, bot)
39
+ @bot = bot
40
+
41
+ @user = @bot.cache.put_user(data['user'])
42
+ super @user
43
+
44
+ raise ArgumentError, 'Cannot create member with no server' unless server || data['guild_id']
45
+ @server = server || @bot.servers(data['guild_id'])
46
+
47
+ @joined_at = Time.parse(data['joined_at']).utc if data['joined_at']
48
+
49
+ update_data(data)
50
+ end
51
+
52
+ def update_data(data)
53
+ @user.update_data(data['user'])
54
+
55
+ @nickname = data.fetch('nick', @nickname || '')
56
+
57
+ if (roles = data['roles'])
58
+ @roles = roles.map {|x| @server.role(x) }
59
+ end
60
+ end
61
+
62
+ def member?
63
+ true
64
+ end
65
+
66
+ def owner?
67
+ @server.owner == self
68
+ end
69
+
70
+ def role?(role)
71
+ role = role.to_id
72
+ @roles.any? {|x| x.id == role }
73
+ end
74
+
75
+ def set_roles(roles, reason = nil)
76
+ roles = roles.map(&:to_id)
77
+ MijDiscord::Core::API::Server.update_member(@bot.token, @server.id, @user.id, reason, roles: roles)
78
+ end
79
+
80
+ def modify_roles(add, remove, reason = nil)
81
+ add, remove = add.map(&:to_id), remove.map(&:to_id)
82
+ roles = (@roles.map(&:id) - remove + add).uniq
83
+ MijDiscord::Core::API::Server.update_member(@bot.tolen, @server.id, @user.id, reason, roles: roles)
84
+ end
85
+
86
+ def add_role(role, reason = nil)
87
+ if role.is_a?(Array)
88
+ modify_roles(role, [], reason)
89
+ else
90
+ role = role.to_id
91
+ MijDiscord::Core::API::Server.add_member_role(@bot.token, @server.id, @user.id, role, reason)
92
+ end
93
+ end
94
+
95
+ def remove_role(role, reason = nil)
96
+ if role.is_a?(Array)
97
+ modify_roles([], role, reason)
98
+ else
99
+ role = role.to_id
100
+ MijDiscord::Core::API::Server.remove_member_role(@bot.token, @server.id, @user.id, role, reason)
101
+ end
102
+ end
103
+
104
+ def highest_role
105
+ @roles.sort_by(&:position).last
106
+ end
107
+
108
+ def hoist_role
109
+ roles = @roles.select(&:hoist)
110
+ roles.sort_by(&:position).last
111
+ end
112
+
113
+ def color_role
114
+ roles = @roles.select {|x| x.color.to_i.nonzero? }
115
+ roles.sort_by(&:position).last
116
+ end
117
+
118
+ def color
119
+ color_role&.color
120
+ end
121
+
122
+ def set_nickname(nick, reason = nil)
123
+ nick ||= ''
124
+
125
+ if @user.current_bot?
126
+ MijDiscord::Core::API::User.change_own_nickname(@bot.token, @server.id, nick, reason)
127
+ else
128
+ MijDiscord::Core::API::Server.update_member(@bot.token, @server.id, @user.id, reason, nick: nick)
129
+ end
130
+ end
131
+
132
+ alias_method :nickname=, :set_nickname
133
+
134
+ def display_name
135
+ nickname.empty? ? username : nickname
136
+ end
137
+
138
+ def inspect
139
+ %(<Member user=#{@user.inspect} server=#{@server.id}>)
140
+ end
141
+
142
+ private
143
+
144
+ def voice_state_attribute(key)
145
+ @server.voice_states[@user.id]&.send(key)
146
+ end
147
+ end
148
+
149
+ class Recipient < DelegateClass(User)
150
+ attr_reader :joined_at
151
+
152
+ attr_reader :nickname
153
+ alias_method :nick, :nickname
154
+
155
+ attr_reader :roles
156
+
157
+ attr_reader :server
158
+
159
+ def initialize(user, channel, bot)
160
+ @bot, @channel, @user = bot, channel, user
161
+ raise ArgumentError, 'Recipient for public channel' unless channel.private?
162
+
163
+ super @user
164
+
165
+ @mute = @deaf = @self_mute = @self_deaf = false
166
+ @voice_channel, @server, @roles = nil, nil, []
167
+ @nickname, @joined_at = '', @channel.creation_time
168
+ end
169
+
170
+ def inspect
171
+ %(<Recipient user=#{@user.inspect} channel=#{@channel.inspect}>)
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MijDiscord::Data
4
+ class Message
5
+ include IDObject
6
+
7
+ attr_reader :bot
8
+
9
+ attr_reader :content
10
+ alias_method :text, :content
11
+ alias_method :to_s, :content
12
+
13
+ attr_reader :author
14
+ alias_method :user, :author
15
+
16
+ attr_reader :channel
17
+
18
+ attr_reader :timestamp
19
+
20
+ attr_reader :edited_timestamp
21
+ alias_method :edit_timestamp, :edited_timestamp
22
+
23
+ attr_reader :user_mentions
24
+
25
+ attr_reader :role_mentions
26
+
27
+ attr_reader :mention_everyone
28
+
29
+ attr_reader :attachments
30
+
31
+ attr_reader :embeds
32
+
33
+ # attr_reader :reactions
34
+
35
+ attr_reader :tts
36
+ alias_method :tts?, :tts
37
+
38
+ # attr_reader :nonce
39
+
40
+ attr_reader :edited
41
+ alias_method :edited?, :edited
42
+
43
+ attr_reader :pinned
44
+ alias_method :pinned?, :pinned
45
+
46
+ attr_reader :webhook_id
47
+
48
+ def initialize(data, bot)
49
+ @bot = bot
50
+
51
+ @id = data['id'].to_i
52
+ @channel = @bot.channel(data['channel_id'])
53
+
54
+ # @nonce = data['nonce']
55
+ @webhook_id = data['webhook_id']&.to_i
56
+
57
+ if (author = data['author'])
58
+ if author['discriminator'] == '0000'
59
+ @author = @bot.cache.put_user(author)
60
+ elsif @channel.private?
61
+ @author = @channel.recipient
62
+ else
63
+ member = @channel.server.member(author['id'])
64
+ @author = member || @bot.user(author['id'])
65
+ end
66
+ end
67
+
68
+ update_data(data)
69
+ end
70
+
71
+ def update_data(data)
72
+ @content = data.fetch('content', @content)
73
+ @pinned = data.fetch('pinned', @pinned)
74
+ @tts = data.fetch('tts', @tts)
75
+
76
+ @timestamp = Time.parse(data['timestamp']).utc if data['timestamp']
77
+ @edited_timestamp = Time.parse(data['edited_timestamp']).utc if data['edited_timestamp']
78
+ @edited = !!@edited_timestamp
79
+
80
+ @mention_everyone = !!data['mention_everyone']
81
+
82
+ # @reactions = []
83
+ # if (reactions = data['reactions'])
84
+ # reactions.each {|x| @reactions << Reaction.new(x) }
85
+ # end
86
+
87
+ @user_mentions = []
88
+ if (mentions = data['mentions'])
89
+ mentions.each {|x| @user_mentions << @bot.cache.put_user(x) }
90
+ end
91
+
92
+ @role_mentions = []
93
+ if @channel.text? && (mentions = data['mention_roles'])
94
+ mentions.each {|x| @role_mentions << @channel.server.role(x) }
95
+ end
96
+
97
+ @attachments = []
98
+ if (attachments = data['attachments'])
99
+ attachments.each {|x| @attachments << Attachment.new(x, self) }
100
+ end
101
+
102
+ @embeds = []
103
+ if (embeds = data['embeds'])
104
+ embeds.each {|x| @embeds << Embed.new(x) }
105
+ end
106
+ end
107
+
108
+ def reply(text: '', embed: nil, tts: false)
109
+ @channel.send_message(text: text, embed: embed, tts: tts)
110
+ end
111
+
112
+ def reply_file(file, caption: '', tts: false)
113
+ @channel.send_file(file, caption: caption, tts: tts)
114
+ end
115
+
116
+ def edit(text: '', embed: nil)
117
+ response = MijDiscord::Core::API::Channel.edit_message(@bot.token, @channel.id, @id,
118
+ text, [], embed&.to_h)
119
+ @channel.cache.put_message(JSON.parse(response), update: true)
120
+ end
121
+
122
+ def pin
123
+ MijDiscord::Core::API::Channel.pin_message(@bot.token, @channel.id, @id)
124
+ nil
125
+ end
126
+
127
+ def unpin
128
+ MijDiscord::Core::API::Channel.unpin_message(@bot.token, @channel.id, @id)
129
+ nil
130
+ end
131
+
132
+ def webhook?
133
+ !@webhook_id.nil?
134
+ end
135
+
136
+ # def my_reactions
137
+ # @reactions.select(&:me)
138
+ # end
139
+
140
+ def create_reaction(reaction)
141
+ emoji = reaction.respond_to?(:reaction) ? reaction.reaction : reaction
142
+ MijDiscord::Core::API::Channel.create_reaction(@bot.token, @channel.id, @id, emoji)
143
+ nil
144
+ end
145
+
146
+ alias_method :add_reaction, :create_reaction
147
+
148
+ def reacted_with(reaction)
149
+ emoji = reaction.respond_to?(:reaction) ? reaction.reaction : reaction
150
+ response = MijDiscord::Core::API::Channel.get_reactions(@bot.token, @channel.id, @id, emoji)
151
+ JSON.parse(response).map {|x| @bot.cache.put_user(x) }
152
+ end
153
+
154
+ def delete_reaction(reaction, user: nil)
155
+ emoji = reaction.respond_to?(:reaction) ? reaction.reaction : reaction
156
+ if user.nil?
157
+ MijDiscord::Core::API::Channel.delete_own_reaction(@bot.token, @channel.id, @id, emoji)
158
+ else
159
+ MijDiscord::Core::API::Channel.delete_user_reaction(@bot.token, @channel.id, @id, emoji, user.to_id)
160
+ end
161
+ nil
162
+ end
163
+
164
+ def clear_reactions
165
+ MijDiscord::Core::API::Channel.delete_all_reactions(@bot.token, @channel.id, @id)
166
+ nil
167
+ end
168
+
169
+ def delete
170
+ MijDiscord::Core::API::Channel.delete_message(@bot.token, @channel.id, @id)
171
+ nil
172
+ end
173
+
174
+ def inspect
175
+ %(<Message id=#{@id} timestamp=#{@timestamp} content="#{@content}" author=#{@author.inspect} channel=#{@channel.inspect}>)
176
+ end
177
+ end
178
+
179
+ class Attachment
180
+ attr_reader :message
181
+
182
+ attr_reader :url
183
+
184
+ attr_reader :proxy_url
185
+
186
+ attr_reader :filename
187
+
188
+ attr_reader :size
189
+
190
+ attr_reader :width
191
+
192
+ attr_reader :height
193
+
194
+ def initialize(data, message)
195
+ @message = message
196
+
197
+ @url, @proxy_url = data['url'], data['proxy_url']
198
+ @filename, @size = data['filename'], data['size']
199
+ @width, @height = data['width'], data['height']
200
+ end
201
+
202
+ def image?
203
+ !@width.nil? && !@height.nil?
204
+ end
205
+ end
206
+ end