discordrb 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of discordrb might be problematic. Click here for more details.

Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +126 -0
  3. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
  6. data/.github/pull_request_template.md +37 -0
  7. data/.rubocop.yml +34 -37
  8. data/.travis.yml +5 -6
  9. data/CHANGELOG.md +472 -347
  10. data/Gemfile +2 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +61 -79
  13. data/Rakefile +2 -0
  14. data/bin/console +1 -0
  15. data/discordrb-webhooks.gemspec +6 -6
  16. data/discordrb.gemspec +17 -17
  17. data/lib/discordrb.rb +73 -0
  18. data/lib/discordrb/allowed_mentions.rb +36 -0
  19. data/lib/discordrb/api.rb +40 -15
  20. data/lib/discordrb/api/channel.rb +57 -39
  21. data/lib/discordrb/api/invite.rb +3 -3
  22. data/lib/discordrb/api/server.rb +55 -50
  23. data/lib/discordrb/api/user.rb +8 -8
  24. data/lib/discordrb/api/webhook.rb +6 -6
  25. data/lib/discordrb/await.rb +0 -1
  26. data/lib/discordrb/bot.rb +164 -72
  27. data/lib/discordrb/cache.rb +4 -2
  28. data/lib/discordrb/colour_rgb.rb +43 -0
  29. data/lib/discordrb/commands/command_bot.rb +22 -6
  30. data/lib/discordrb/commands/container.rb +20 -23
  31. data/lib/discordrb/commands/parser.rb +18 -18
  32. data/lib/discordrb/commands/rate_limiter.rb +3 -2
  33. data/lib/discordrb/container.rb +77 -17
  34. data/lib/discordrb/data.rb +25 -4180
  35. data/lib/discordrb/data/activity.rb +264 -0
  36. data/lib/discordrb/data/application.rb +50 -0
  37. data/lib/discordrb/data/attachment.rb +56 -0
  38. data/lib/discordrb/data/audit_logs.rb +345 -0
  39. data/lib/discordrb/data/channel.rb +849 -0
  40. data/lib/discordrb/data/embed.rb +251 -0
  41. data/lib/discordrb/data/emoji.rb +82 -0
  42. data/lib/discordrb/data/integration.rb +83 -0
  43. data/lib/discordrb/data/invite.rb +137 -0
  44. data/lib/discordrb/data/member.rb +297 -0
  45. data/lib/discordrb/data/message.rb +334 -0
  46. data/lib/discordrb/data/overwrite.rb +102 -0
  47. data/lib/discordrb/data/profile.rb +91 -0
  48. data/lib/discordrb/data/reaction.rb +33 -0
  49. data/lib/discordrb/data/recipient.rb +34 -0
  50. data/lib/discordrb/data/role.rb +191 -0
  51. data/lib/discordrb/data/server.rb +1002 -0
  52. data/lib/discordrb/data/user.rb +204 -0
  53. data/lib/discordrb/data/voice_region.rb +45 -0
  54. data/lib/discordrb/data/voice_state.rb +41 -0
  55. data/lib/discordrb/data/webhook.rb +145 -0
  56. data/lib/discordrb/errors.rb +2 -1
  57. data/lib/discordrb/events/bans.rb +7 -5
  58. data/lib/discordrb/events/channels.rb +2 -0
  59. data/lib/discordrb/events/guilds.rb +16 -9
  60. data/lib/discordrb/events/invites.rb +125 -0
  61. data/lib/discordrb/events/members.rb +6 -2
  62. data/lib/discordrb/events/message.rb +69 -27
  63. data/lib/discordrb/events/presence.rb +14 -4
  64. data/lib/discordrb/events/raw.rb +1 -3
  65. data/lib/discordrb/events/reactions.rb +49 -3
  66. data/lib/discordrb/events/typing.rb +6 -4
  67. data/lib/discordrb/events/voice_server_update.rb +47 -0
  68. data/lib/discordrb/events/voice_state_update.rb +15 -10
  69. data/lib/discordrb/events/webhooks.rb +9 -6
  70. data/lib/discordrb/gateway.rb +72 -57
  71. data/lib/discordrb/id_object.rb +39 -0
  72. data/lib/discordrb/light/integrations.rb +1 -1
  73. data/lib/discordrb/light/light_bot.rb +1 -1
  74. data/lib/discordrb/logger.rb +4 -4
  75. data/lib/discordrb/paginator.rb +57 -0
  76. data/lib/discordrb/permissions.rb +103 -8
  77. data/lib/discordrb/version.rb +1 -1
  78. data/lib/discordrb/voice/encoder.rb +3 -3
  79. data/lib/discordrb/voice/network.rb +84 -43
  80. data/lib/discordrb/voice/sodium.rb +96 -0
  81. data/lib/discordrb/voice/voice_bot.rb +34 -26
  82. metadata +93 -55
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # A permissions overwrite, when applied to channels describes additional
5
+ # permissions a member needs to perform certain actions in context.
6
+ class Overwrite
7
+ # @return [Integer] ID of the thing associated with this overwrite type
8
+ attr_accessor :id
9
+
10
+ # @return [Symbol] either :role or :member
11
+ attr_accessor :type
12
+
13
+ # @return [Permissions] allowed permissions for this overwrite type
14
+ attr_accessor :allow
15
+
16
+ # @return [Permissions] denied permissions for this overwrite type
17
+ attr_accessor :deny
18
+
19
+ # Creates a new Overwrite object
20
+ # @example Create an overwrite for a role that can mention everyone, send TTS messages, but can't create instant invites
21
+ # allow = Discordrb::Permissions.new
22
+ # allow.can_mention_everyone = true
23
+ # allow.can_send_tts_messages = true
24
+ #
25
+ # deny = Discordrb::Permissions.new
26
+ # deny.can_create_instant_invite = true
27
+ #
28
+ # # Find some role by name
29
+ # role = server.roles.find { |r| r.name == 'some role' }
30
+ #
31
+ # Overwrite.new(role, allow: allow, deny: deny)
32
+ # @example Create an overwrite by ID and permissions bits
33
+ # Overwrite.new(120571255635181568, type: 'member', allow: 1024, deny: 0)
34
+ # @param object [Integer, #id] the ID or object this overwrite is for
35
+ # @param type [String] the type of object this overwrite is for (only required if object is an Integer)
36
+ # @param allow [Integer, Permissions] allowed permissions for this overwrite, by bits or a Permissions object
37
+ # @param deny [Integer, Permissions] denied permissions for this overwrite, by bits or a Permissions object
38
+ # @raise [ArgumentError] if type is not :member or :role
39
+ def initialize(object = nil, type: nil, allow: 0, deny: 0)
40
+ if type
41
+ type = type.to_sym
42
+ raise ArgumentError, 'Overwrite type must be :member or :role' unless (type != :member) || (type != :role)
43
+ end
44
+
45
+ @id = object.respond_to?(:id) ? object.id : object
46
+
47
+ @type = case object
48
+ when User, Member, Recipient, Profile
49
+ :member
50
+ when Role
51
+ :role
52
+ else
53
+ type
54
+ end
55
+
56
+ @allow = allow.is_a?(Permissions) ? allow : Permissions.new(allow)
57
+ @deny = deny.is_a?(Permissions) ? deny : Permissions.new(deny)
58
+ end
59
+
60
+ # Comparison by attributes [:id, :type, :allow, :deny]
61
+ def ==(other)
62
+ false unless other.is_a? Discordrb::Overwrite
63
+ id == other.id &&
64
+ type == other.type &&
65
+ allow == other.allow &&
66
+ deny == other.deny
67
+ end
68
+
69
+ # @return [Overwrite] create an overwrite from a hash payload
70
+ # @!visibility private
71
+ def self.from_hash(data)
72
+ new(
73
+ data['id'].to_i,
74
+ type: data['type'],
75
+ allow: Permissions.new(data['allow']),
76
+ deny: Permissions.new(data['deny'])
77
+ )
78
+ end
79
+
80
+ # @return [Overwrite] copies an overwrite from another Overwrite
81
+ # @!visibility private
82
+ def self.from_other(other)
83
+ new(
84
+ other.id,
85
+ type: other.type,
86
+ allow: Permissions.new(other.allow.bits),
87
+ deny: Permissions.new(other.deny.bits)
88
+ )
89
+ end
90
+
91
+ # @return [Hash] hash representation of an overwrite
92
+ # @!visibility private
93
+ def to_hash
94
+ {
95
+ id: id,
96
+ type: type,
97
+ allow: allow.bits,
98
+ deny: deny.bits
99
+ }
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # This class is a special variant of User that represents the bot's user profile (things like own username and the avatar).
5
+ # It can be accessed using {Bot#profile}.
6
+ class Profile < User
7
+ # Whether or not the user is the bot. The Profile can only ever be the bot user, so this always returns true.
8
+ # @return [true]
9
+ def current_bot?
10
+ true
11
+ end
12
+
13
+ # Sets the bot's username.
14
+ # @param username [String] The new username.
15
+ def username=(username)
16
+ update_profile_data(username: username)
17
+ end
18
+
19
+ alias_method :name=, :username=
20
+
21
+ # Changes the bot's avatar.
22
+ # @param avatar [String, #read] A JPG file to be used as the avatar, either
23
+ # something readable (e.g. File Object) or as a data URL.
24
+ def avatar=(avatar)
25
+ if avatar.respond_to? :read
26
+ # Set the file to binary mode if supported, so we don't get problems with Windows
27
+ avatar.binmode if avatar.respond_to?(:binmode)
28
+
29
+ avatar_string = 'data:image/jpg;base64,'
30
+ avatar_string += Base64.strict_encode64(avatar.read)
31
+ update_profile_data(avatar: avatar_string)
32
+ else
33
+ update_profile_data(avatar: avatar)
34
+ end
35
+ end
36
+
37
+ # Updates the cached profile data with the new one.
38
+ # @note For internal use only.
39
+ # @!visibility private
40
+ def update_data(new_data)
41
+ @username = new_data[:username] || @username
42
+ @avatar_id = new_data[:avatar_id] || @avatar_id
43
+ end
44
+
45
+ # Sets the user status setting to Online.
46
+ # @note Only usable on User accounts.
47
+ def online
48
+ update_profile_status_setting('online')
49
+ end
50
+
51
+ # Sets the user status setting to Idle.
52
+ # @note Only usable on User accounts.
53
+ def idle
54
+ update_profile_status_setting('idle')
55
+ end
56
+
57
+ # Sets the user status setting to Do Not Disturb.
58
+ # @note Only usable on User accounts.
59
+ def dnd
60
+ update_profile_status_setting('dnd')
61
+ end
62
+
63
+ alias_method(:busy, :dnd)
64
+
65
+ # Sets the user status setting to Invisible.
66
+ # @note Only usable on User accounts.
67
+ def invisible
68
+ update_profile_status_setting('invisible')
69
+ end
70
+
71
+ # The inspect method is overwritten to give more useful output
72
+ def inspect
73
+ "<Profile user=#{super}>"
74
+ end
75
+
76
+ private
77
+
78
+ # Internal handler for updating the user's status setting
79
+ def update_profile_status_setting(status)
80
+ API::User.change_status_setting(@bot.token, status)
81
+ end
82
+
83
+ def update_profile_data(new_data)
84
+ API::User.update_profile(@bot.token,
85
+ nil, nil,
86
+ new_data[:username] || @username,
87
+ new_data.key?(:avatar) ? new_data[:avatar] : @avatar_id)
88
+ update_data(new_data)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # A reaction to a message.
5
+ class Reaction
6
+ # @return [Integer] the amount of users who have reacted with this reaction
7
+ attr_reader :count
8
+
9
+ # @return [true, false] whether the current bot or user used this reaction
10
+ attr_reader :me
11
+ alias_method :me?, :me
12
+
13
+ # @return [Integer] the ID of the emoji, if it was custom
14
+ attr_reader :id
15
+
16
+ # @return [String] the name or unicode representation of the emoji
17
+ attr_reader :name
18
+
19
+ def initialize(data)
20
+ @count = data['count']
21
+ @me = data['me']
22
+ @id = data['emoji']['id'].nil? ? nil : data['emoji']['id'].to_i
23
+ @name = data['emoji']['name']
24
+ end
25
+
26
+ # Converts this Reaction into a string that can be sent back to Discord in other reaction endpoints.
27
+ # If ID is present, it will be rendered into the form of `name:id`.
28
+ # @return [String] the name of this reaction, including the ID if it is a custom emoji
29
+ def to_s
30
+ id.nil? ? name : "#{name}:#{id}"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # Recipients are members on private channels - they exist for completeness purposes, but all
5
+ # the attributes will be empty.
6
+ class Recipient < DelegateClass(User)
7
+ include MemberAttributes
8
+
9
+ # @return [Channel] the private channel this recipient is the recipient of.
10
+ attr_reader :channel
11
+
12
+ # @!visibility private
13
+ def initialize(user, channel, bot)
14
+ @bot = bot
15
+ @channel = channel
16
+ raise ArgumentError, 'Tried to create a recipient for a public channel!' unless @channel.private?
17
+
18
+ @user = user
19
+ super @user
20
+
21
+ # Member attributes
22
+ @mute = @deaf = @self_mute = @self_deaf = false
23
+ @voice_channel = nil
24
+ @server = nil
25
+ @roles = []
26
+ @joined_at = @channel.creation_time
27
+ end
28
+
29
+ # Overwriting inspect for debug purposes
30
+ def inspect
31
+ "<Recipient user=#{@user.inspect} channel=#{@channel.inspect}>"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # A Discord role that contains permissions and applies to certain users
5
+ class Role
6
+ include IDObject
7
+
8
+ # @return [Permissions] this role's permissions.
9
+ attr_reader :permissions
10
+
11
+ # @return [String] this role's name ("new role" if it hasn't been changed)
12
+ attr_reader :name
13
+
14
+ # @return [Server] the server this role belongs to
15
+ attr_reader :server
16
+
17
+ # @return [true, false] whether or not this role should be displayed separately from other users
18
+ attr_reader :hoist
19
+
20
+ # @return [true, false] whether or not this role is managed by an integration or a bot
21
+ attr_reader :managed
22
+ alias_method :managed?, :managed
23
+
24
+ # @return [true, false] whether this role can be mentioned using a role mention
25
+ attr_reader :mentionable
26
+ alias_method :mentionable?, :mentionable
27
+
28
+ # @return [ColourRGB] the role colour
29
+ attr_reader :colour
30
+ alias_method :color, :colour
31
+
32
+ # @return [Integer] the position of this role in the hierarchy
33
+ attr_reader :position
34
+
35
+ # This class is used internally as a wrapper to a Role object that allows easy writing of permission data.
36
+ class RoleWriter
37
+ # @!visibility private
38
+ def initialize(role, token)
39
+ @role = role
40
+ @token = token
41
+ end
42
+
43
+ # Write the specified permission data to the role, without updating the permission cache
44
+ # @param bits [Integer] The packed permissions to write.
45
+ def write(bits)
46
+ @role.send(:packed=, bits, false)
47
+ end
48
+
49
+ # The inspect method is overridden, in this case to prevent the token being leaked
50
+ def inspect
51
+ "<RoleWriter role=#{@role} token=...>"
52
+ end
53
+ end
54
+
55
+ # @!visibility private
56
+ def initialize(data, bot, server = nil)
57
+ @bot = bot
58
+ @server = server
59
+ @permissions = Permissions.new(data['permissions'], RoleWriter.new(self, @bot.token))
60
+ @name = data['name']
61
+ @id = data['id'].to_i
62
+
63
+ @position = data['position']
64
+
65
+ @hoist = data['hoist']
66
+ @mentionable = data['mentionable']
67
+ @managed = data['managed']
68
+
69
+ @colour = ColourRGB.new(data['color'])
70
+ end
71
+
72
+ # @return [String] a string that will mention this role, if it is mentionable.
73
+ def mention
74
+ "<@&#{@id}>"
75
+ end
76
+
77
+ # @return [Array<Member>] an array of members who have this role.
78
+ # @note This requests a member chunk if it hasn't for the server before, which may be slow initially
79
+ def members
80
+ @server.members.select { |m| m.role? self }
81
+ end
82
+
83
+ alias_method :users, :members
84
+
85
+ # Updates the data cache from another Role object
86
+ # @note For internal use only
87
+ # @!visibility private
88
+ def update_from(other)
89
+ @permissions = other.permissions
90
+ @name = other.name
91
+ @hoist = other.hoist
92
+ @colour = other.colour
93
+ @position = other.position
94
+ @managed = other.managed
95
+ end
96
+
97
+ # Updates the data cache from a hash containing data
98
+ # @note For internal use only
99
+ # @!visibility private
100
+ def update_data(new_data)
101
+ @name = new_data[:name] || new_data['name'] || @name
102
+ @hoist = new_data['hoist'] unless new_data['hoist'].nil?
103
+ @hoist = new_data[:hoist] unless new_data[:hoist].nil?
104
+ @colour = new_data[:colour] || (new_data['color'] ? ColourRGB.new(new_data['color']) : @colour)
105
+ end
106
+
107
+ # Sets the role name to something new
108
+ # @param name [String] The name that should be set
109
+ def name=(name)
110
+ update_role_data(name: name)
111
+ end
112
+
113
+ # Changes whether or not this role is displayed at the top of the user list
114
+ # @param hoist [true, false] The value it should be changed to
115
+ def hoist=(hoist)
116
+ update_role_data(hoist: hoist)
117
+ end
118
+
119
+ # Changes whether or not this role can be mentioned
120
+ # @param mentionable [true, false] The value it should be changed to
121
+ def mentionable=(mentionable)
122
+ update_role_data(mentionable: mentionable)
123
+ end
124
+
125
+ # Sets the role colour to something new
126
+ # @param colour [ColourRGB] The new colour
127
+ def colour=(colour)
128
+ update_role_data(colour: colour)
129
+ end
130
+
131
+ alias_method :color=, :colour=
132
+
133
+ # Changes this role's permissions to a fixed bitfield. This allows setting multiple permissions at once with just
134
+ # one API call.
135
+ #
136
+ # Information on how this bitfield is structured can be found at
137
+ # https://discord.com/developers/docs/topics/permissions.
138
+ # @example Remove all permissions from a role
139
+ # role.packed = 0
140
+ # @param packed [Integer] A bitfield with the desired permissions value.
141
+ # @param update_perms [true, false] Whether the internal data should also be updated. This should always be true
142
+ # when calling externally.
143
+ def packed=(packed, update_perms = true)
144
+ update_role_data(permissions: packed)
145
+ @permissions.bits = packed if update_perms
146
+ end
147
+
148
+ # Moves this role above another role in the list.
149
+ # @param other [Role, String, Integer, nil] The role, or its ID, above which this role should be moved. If it is `nil`,
150
+ # the role will be moved above the @everyone role.
151
+ # @return [Integer] the new position of this role
152
+ def sort_above(other = nil)
153
+ other = @server.role(other.resolve_id) if other
154
+ roles = @server.roles.sort_by(&:position)
155
+ roles.delete_at(@position)
156
+
157
+ index = other ? roles.index { |role| role.id == other.id } + 1 : 1
158
+ roles.insert(index, self)
159
+
160
+ updated_roles = roles.map.with_index { |role, position| { id: role.id, position: position } }
161
+ @server.update_role_positions(updated_roles)
162
+ index
163
+ end
164
+
165
+ alias_method :move_above, :sort_above
166
+
167
+ # Deletes this role. This cannot be undone without recreating the role!
168
+ # @param reason [String] the reason for this role's deletion
169
+ def delete(reason = nil)
170
+ API::Server.delete_role(@bot.token, @server.id, @id, reason)
171
+ @server.delete_role(@id)
172
+ end
173
+
174
+ # The inspect method is overwritten to give more useful output
175
+ def inspect
176
+ "<Role name=#{@name} permissions=#{@permissions.inspect} hoist=#{@hoist} colour=#{@colour.inspect} server=#{@server.inspect} position=#{@position} mentionable=#{@mentionable}>"
177
+ end
178
+
179
+ private
180
+
181
+ def update_role_data(new_data)
182
+ API::Server.update_role(@bot.token, @server.id, @id,
183
+ new_data[:name] || @name,
184
+ (new_data[:colour] || @colour).combined,
185
+ new_data[:hoist].nil? ? @hoist : new_data[:hoist],
186
+ new_data[:mentionable].nil? ? @mentionable : new_data[:mentionable],
187
+ new_data[:permissions] || @permissions.bits)
188
+ update_data(new_data)
189
+ end
190
+ end
191
+ end