mij-discord 1.0.0

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