discordrb 3.1.1 → 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 (91) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +126 -0
  3. data/.codeclimate.yml +16 -0
  4. data/.github/CONTRIBUTING.md +13 -0
  5. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  6. data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
  7. data/.github/pull_request_template.md +37 -0
  8. data/.gitignore +5 -0
  9. data/.rubocop.yml +39 -33
  10. data/.travis.yml +27 -2
  11. data/.yardopts +1 -1
  12. data/CHANGELOG.md +808 -208
  13. data/Gemfile +4 -1
  14. data/LICENSE.txt +1 -1
  15. data/README.md +108 -53
  16. data/Rakefile +14 -1
  17. data/bin/console +1 -0
  18. data/bin/travis_build_docs.sh +17 -0
  19. data/discordrb-webhooks.gemspec +26 -0
  20. data/discordrb.gemspec +24 -15
  21. data/lib/discordrb.rb +75 -2
  22. data/lib/discordrb/allowed_mentions.rb +36 -0
  23. data/lib/discordrb/api.rb +126 -27
  24. data/lib/discordrb/api/channel.rb +165 -43
  25. data/lib/discordrb/api/invite.rb +10 -7
  26. data/lib/discordrb/api/server.rb +240 -61
  27. data/lib/discordrb/api/user.rb +26 -24
  28. data/lib/discordrb/api/webhook.rb +83 -0
  29. data/lib/discordrb/await.rb +1 -2
  30. data/lib/discordrb/bot.rb +417 -149
  31. data/lib/discordrb/cache.rb +42 -10
  32. data/lib/discordrb/colour_rgb.rb +43 -0
  33. data/lib/discordrb/commands/command_bot.rb +186 -31
  34. data/lib/discordrb/commands/container.rb +30 -16
  35. data/lib/discordrb/commands/parser.rb +102 -47
  36. data/lib/discordrb/commands/rate_limiter.rb +18 -17
  37. data/lib/discordrb/container.rb +245 -41
  38. data/lib/discordrb/data.rb +27 -2511
  39. data/lib/discordrb/data/activity.rb +264 -0
  40. data/lib/discordrb/data/application.rb +50 -0
  41. data/lib/discordrb/data/attachment.rb +56 -0
  42. data/lib/discordrb/data/audit_logs.rb +345 -0
  43. data/lib/discordrb/data/channel.rb +849 -0
  44. data/lib/discordrb/data/embed.rb +251 -0
  45. data/lib/discordrb/data/emoji.rb +82 -0
  46. data/lib/discordrb/data/integration.rb +83 -0
  47. data/lib/discordrb/data/invite.rb +137 -0
  48. data/lib/discordrb/data/member.rb +297 -0
  49. data/lib/discordrb/data/message.rb +334 -0
  50. data/lib/discordrb/data/overwrite.rb +102 -0
  51. data/lib/discordrb/data/profile.rb +91 -0
  52. data/lib/discordrb/data/reaction.rb +33 -0
  53. data/lib/discordrb/data/recipient.rb +34 -0
  54. data/lib/discordrb/data/role.rb +191 -0
  55. data/lib/discordrb/data/server.rb +1002 -0
  56. data/lib/discordrb/data/user.rb +204 -0
  57. data/lib/discordrb/data/voice_region.rb +45 -0
  58. data/lib/discordrb/data/voice_state.rb +41 -0
  59. data/lib/discordrb/data/webhook.rb +145 -0
  60. data/lib/discordrb/errors.rb +36 -2
  61. data/lib/discordrb/events/bans.rb +7 -5
  62. data/lib/discordrb/events/channels.rb +2 -0
  63. data/lib/discordrb/events/generic.rb +19 -3
  64. data/lib/discordrb/events/guilds.rb +129 -6
  65. data/lib/discordrb/events/invites.rb +125 -0
  66. data/lib/discordrb/events/members.rb +6 -2
  67. data/lib/discordrb/events/message.rb +86 -36
  68. data/lib/discordrb/events/presence.rb +23 -16
  69. data/lib/discordrb/events/raw.rb +47 -0
  70. data/lib/discordrb/events/reactions.rb +159 -0
  71. data/lib/discordrb/events/roles.rb +7 -6
  72. data/lib/discordrb/events/typing.rb +9 -5
  73. data/lib/discordrb/events/voice_server_update.rb +47 -0
  74. data/lib/discordrb/events/voice_state_update.rb +29 -9
  75. data/lib/discordrb/events/webhooks.rb +64 -0
  76. data/lib/discordrb/gateway.rb +219 -88
  77. data/lib/discordrb/id_object.rb +39 -0
  78. data/lib/discordrb/light.rb +1 -1
  79. data/lib/discordrb/light/integrations.rb +1 -1
  80. data/lib/discordrb/light/light_bot.rb +1 -1
  81. data/lib/discordrb/logger.rb +12 -11
  82. data/lib/discordrb/paginator.rb +57 -0
  83. data/lib/discordrb/permissions.rb +148 -14
  84. data/lib/discordrb/version.rb +1 -1
  85. data/lib/discordrb/voice/encoder.rb +14 -15
  86. data/lib/discordrb/voice/network.rb +86 -45
  87. data/lib/discordrb/voice/sodium.rb +96 -0
  88. data/lib/discordrb/voice/voice_bot.rb +52 -40
  89. data/lib/discordrb/webhooks.rb +12 -0
  90. data/lib/discordrb/websocket.rb +2 -2
  91. metadata +137 -34
@@ -15,11 +15,16 @@ module Discordrb::Events
15
15
  # @return [Symbol] the new status.
16
16
  attr_reader :status
17
17
 
18
+ # @return [Hash<Symbol, Symbol>] the current online status (`:online`, `:idle` or `:dnd`) of the user
19
+ # on various device types (`:desktop`, `:mobile`, or `:web`). The value will be `nil` if the user is offline or invisible.
20
+ attr_reader :client_status
21
+
18
22
  def initialize(data, bot)
19
23
  @bot = bot
20
24
 
21
25
  @user = bot.user(data['user']['id'].to_i)
22
26
  @status = data['status'].to_sym
27
+ @client_status = user.client_status
23
28
  @server = bot.server(data['guild_id'].to_i)
24
29
  end
25
30
  end
@@ -32,9 +37,10 @@ module Discordrb::Events
32
37
 
33
38
  [
34
39
  matches_all(@attributes[:from], event.user) do |a, e|
35
- a == if a.is_a? String
40
+ a == case a
41
+ when String
36
42
  e.name
37
- elsif a.is_a? Fixnum
43
+ when Integer
38
44
  e.id
39
45
  else
40
46
  e
@@ -65,17 +71,22 @@ module Discordrb::Events
65
71
  # @return [String] the URL to the stream
66
72
  attr_reader :url
67
73
 
74
+ # @return [String] what the player is currently doing (ex. game being streamed)
75
+ attr_reader :details
76
+
68
77
  # @return [Integer] the type of play. 0 = game, 1 = Twitch
69
78
  attr_reader :type
70
79
 
71
80
  def initialize(data, bot)
72
81
  @bot = bot
73
82
 
83
+ @server = bot.server(data['guild_id'].to_i)
74
84
  @user = bot.user(data['user']['id'].to_i)
75
85
  @game = data['game'] ? data['game']['name'] : nil
76
- @server = bot.server(data['guild_id'].to_i)
77
- @url = data['game'] ? data['game']['url'] : nil
78
86
  @type = data['game'] ? data['game']['type'].to_i : nil
87
+ # Handle optional 'game' fields safely
88
+ @url = data['game'] && data['game']['url'] ? data['game']['url'] : nil
89
+ @details = data['game'] && data['game']['details'] ? data['game']['details'] : nil
79
90
  end
80
91
  end
81
92
 
@@ -87,27 +98,23 @@ module Discordrb::Events
87
98
 
88
99
  [
89
100
  matches_all(@attributes[:from], event.user) do |a, e|
90
- a == if a.is_a? String
101
+ a == case a
102
+ when String
91
103
  e.name
92
- elsif a.is_a? Fixnum
104
+ when Integer
93
105
  e.id
94
106
  else
95
107
  e
96
108
  end
97
109
  end,
98
110
  matches_all(@attributes[:game], event.game) do |a, e|
99
- a == if a.is_a? String
100
- e.name
101
- else
102
- e
103
- end
111
+ a == e
104
112
  end,
105
113
  matches_all(@attributes[:type], event.type) do |a, e|
106
- a == if a.is_a? Integer
107
- e.type
108
- else
109
- e
110
- end
114
+ a == e
115
+ end,
116
+ matches_all(@attributes[:client_status], event.client_status) do |a, e|
117
+ e.slice(a.keys) == a
111
118
  end
112
119
  ].reduce(true, &:&)
113
120
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'discordrb/events/generic'
4
+
5
+ # Event classes and handlers
6
+ module Discordrb::Events
7
+ # Event raised when any dispatch is received
8
+ class RawEvent < Event
9
+ # @return [Symbol] the type of this dispatch.
10
+ attr_reader :type
11
+ alias_method :t, :type
12
+
13
+ # @return [Hash] the data of this dispatch.
14
+ attr_reader :data
15
+ alias_method :d, :data
16
+
17
+ def initialize(type, data, bot)
18
+ @type = type
19
+ @data = data
20
+ @bot = bot
21
+ end
22
+ end
23
+
24
+ # Event handler for {RawEvent}
25
+ class RawEventHandler < EventHandler
26
+ def matches?(event)
27
+ # Check for the proper event type
28
+ return false unless event.is_a? RawEvent
29
+
30
+ [
31
+ matches_all(@attributes[:type] || @attributes[:t], event.type) do |a, e|
32
+ if a.is_a? Regexp
33
+ a.match?(e)
34
+ else
35
+ e.to_s.casecmp(a.to_s).zero?
36
+ end
37
+ end
38
+ ].reduce(true, &:&)
39
+ end
40
+ end
41
+
42
+ # Event raised when an unknown dispatch is received
43
+ class UnknownEvent < RawEvent; end
44
+
45
+ # Event handler for {UnknownEvent}
46
+ class UnknownEventHandler < RawEventHandler; end
47
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'discordrb/events/generic'
4
+ require 'discordrb/data'
5
+
6
+ module Discordrb::Events
7
+ # Generic superclass for events about adding and removing reactions
8
+ class ReactionEvent < Event
9
+ include Respondable
10
+
11
+ # @return [Emoji] the emoji that was reacted with.
12
+ attr_reader :emoji
13
+
14
+ # @!visibility private
15
+ attr_reader :message_id
16
+
17
+ def initialize(data, bot)
18
+ @bot = bot
19
+
20
+ @emoji = Discordrb::Emoji.new(data['emoji'], bot, nil)
21
+ @user_id = data['user_id'].to_i
22
+ @message_id = data['message_id'].to_i
23
+ @channel_id = data['channel_id'].to_i
24
+ end
25
+
26
+ # @return [User, Member] the user that reacted to this message, or member if a server exists.
27
+ def user
28
+ # Cache the user so we don't do requests all the time
29
+ @user ||= if server
30
+ @server.member(@user_id)
31
+ else
32
+ @bot.user(@user_id)
33
+ end
34
+ end
35
+
36
+ # @return [Message] the message that was reacted to.
37
+ def message
38
+ @message ||= channel.load_message(@message_id)
39
+ end
40
+
41
+ # @return [Channel] the channel that was reacted in.
42
+ def channel
43
+ @channel ||= @bot.channel(@channel_id)
44
+ end
45
+
46
+ # @return [Server, nil] the server that was reacted in. If reacted in a PM channel, it will be nil.
47
+ def server
48
+ @server ||= channel.server
49
+ end
50
+ end
51
+
52
+ # Generic superclass for event handlers pertaining to adding and removing reactions
53
+ class ReactionEventHandler < EventHandler
54
+ def matches?(event)
55
+ # Check for the proper event type
56
+ return false unless event.is_a? ReactionEvent
57
+
58
+ [
59
+ matches_all(@attributes[:emoji], event.emoji) do |a, e|
60
+ case a
61
+ when Integer
62
+ e.id == a
63
+ when String
64
+ e.name == a || e.name == a.delete(':') || e.id == a.resolve_id
65
+ else
66
+ e == a
67
+ end
68
+ end,
69
+ matches_all(@attributes[:message], event.message_id) do |a, e|
70
+ a == e
71
+ end,
72
+ matches_all(@attributes[:in], event.channel) do |a, e|
73
+ case a
74
+ when String
75
+ # Make sure to remove the "#" from channel names in case it was specified
76
+ a.delete('#') == e.name
77
+ when Integer
78
+ a == e.id
79
+ else
80
+ a == e
81
+ end
82
+ end,
83
+ matches_all(@attributes[:from], event.user) do |a, e|
84
+ case a
85
+ when String
86
+ a == e.name
87
+ when :bot
88
+ e.current_bot?
89
+ else
90
+ a == e
91
+ end
92
+ end
93
+ ].reduce(true, &:&)
94
+ end
95
+ end
96
+
97
+ # Event raised when somebody reacts to a message
98
+ class ReactionAddEvent < ReactionEvent; end
99
+
100
+ # Event handler for {ReactionAddEvent}
101
+ class ReactionAddEventHandler < ReactionEventHandler; end
102
+
103
+ # Event raised when somebody removes a reaction to a message
104
+ class ReactionRemoveEvent < ReactionEvent; end
105
+
106
+ # Event handler for {ReactionRemoveEvent}
107
+ class ReactionRemoveEventHandler < ReactionEventHandler; end
108
+
109
+ # Event raised when somebody removes all reactions from a message
110
+ class ReactionRemoveAllEvent < Event
111
+ include Respondable
112
+
113
+ # @!visibility private
114
+ attr_reader :message_id
115
+
116
+ def initialize(data, bot)
117
+ @bot = bot
118
+
119
+ @message_id = data['message_id'].to_i
120
+ @channel_id = data['channel_id'].to_i
121
+ end
122
+
123
+ # @return [Channel] the channel where the removal occurred.
124
+ def channel
125
+ @channel ||= @bot.channel(@channel_id)
126
+ end
127
+
128
+ # @return [Message] the message all reactions were removed from.
129
+ def message
130
+ @message ||= channel.load_message(@message_id)
131
+ end
132
+ end
133
+
134
+ # Event handler for {ReactionRemoveAllEvent}
135
+ class ReactionRemoveAllEventHandler < EventHandler
136
+ def matches?(event)
137
+ # Check for the proper event type
138
+ return false unless event.is_a? ReactionRemoveAllEvent
139
+
140
+ # No attributes yet as there is no property available on the event that doesn't involve doing a resolution request
141
+ [
142
+ matches_all(@attributes[:message], event.message_id) do |a, e|
143
+ a == e
144
+ end,
145
+ matches_all(@attributes[:in], event.channel) do |a, e|
146
+ case a
147
+ when String
148
+ # Make sure to remove the "#" from channel names in case it was specified
149
+ a.delete('#') == e.name
150
+ when Integer
151
+ a == e.id
152
+ else
153
+ a == e
154
+ end
155
+ end
156
+ ].reduce(true, &:&)
157
+ end
158
+ end
159
+ end
@@ -12,6 +12,11 @@ module Discordrb::Events
12
12
  # @return [Server] the server on which a role got created
13
13
  attr_reader :server
14
14
 
15
+ # @!attribute [r] name
16
+ # @return [String] this role's name
17
+ # @see Role#name
18
+ delegate :name, to: :role
19
+
15
20
  def initialize(data, bot)
16
21
  @bot = bot
17
22
 
@@ -68,12 +73,8 @@ module Discordrb::Events
68
73
  return false unless event.is_a? ServerRoleDeleteEvent
69
74
 
70
75
  [
71
- matches_all(@attributes[:name], event.name) do |a, e|
72
- a == if a.is_a? String
73
- e.to_s
74
- else
75
- e
76
- end
76
+ matches_all(@attributes[:id], event.id) do |a, e|
77
+ a.resolve_id == e.resolve_id
77
78
  end
78
79
  ].reduce(true, &:&)
79
80
  end
@@ -5,10 +5,12 @@ require 'discordrb/events/generic'
5
5
  module Discordrb::Events
6
6
  # Event raised when a user starts typing
7
7
  class TypingEvent < Event
8
+ include Respondable
9
+
8
10
  # @return [Channel] the channel on which a user started typing.
9
11
  attr_reader :channel
10
12
 
11
- # @return [Member] the user that started typing.
13
+ # @return [User, Member, Recipient] the user that started typing.
12
14
  attr_reader :user
13
15
  alias_method :member, :user
14
16
 
@@ -43,18 +45,20 @@ module Discordrb::Events
43
45
 
44
46
  [
45
47
  matches_all(@attributes[:in], event.channel) do |a, e|
46
- if a.is_a? String
48
+ case a
49
+ when String
47
50
  a.delete('#') == e.name
48
- elsif a.is_a? Fixnum
51
+ when Integer
49
52
  a == e.id
50
53
  else
51
54
  a == e
52
55
  end
53
56
  end,
54
57
  matches_all(@attributes[:from], event.user) do |a, e|
55
- a == if a.is_a? String
58
+ a == case a
59
+ when String
56
60
  e.name
57
- elsif a.is_a? Fixnum
61
+ when Integer
58
62
  e.id
59
63
  else
60
64
  e
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'discordrb/events/generic'
4
+ require 'discordrb/data'
5
+
6
+ module Discordrb::Events
7
+ # Event raised when a server's voice server is updating.
8
+ # Sent when initially connecting to voice and when a voice instance fails
9
+ # over to a new server.
10
+ # This event is exposed for use with library agnostic interfaces like telecom and
11
+ # lavalink.
12
+ class VoiceServerUpdateEvent < Event
13
+ # @return [String] The voice connection token
14
+ attr_reader :token
15
+
16
+ # @return [Server] The server this update is for.
17
+ attr_reader :server
18
+
19
+ # @return [String] The voice server host.
20
+ attr_reader :endpoint
21
+
22
+ def initialize(data, bot)
23
+ @bot = bot
24
+
25
+ @token = data['token']
26
+ @endpoint = data['endpoint']
27
+ @server = bot.server(data['guild_id'])
28
+ end
29
+ end
30
+
31
+ # Event handler for VoiceServerUpdateEvent
32
+ class VoiceServerUpdateEventHandler < EventHandler
33
+ def matches?(event)
34
+ return false unless event.is_a? VoiceServerUpdateEvent
35
+
36
+ [
37
+ matches_all(@attributes[:from], event.server) do |a, e|
38
+ a == if a.is_a? String
39
+ e.name
40
+ else
41
+ e
42
+ end
43
+ end
44
+ ]
45
+ end
46
+ end
47
+ end
@@ -8,7 +8,10 @@ module Discordrb::Events
8
8
  class VoiceStateUpdateEvent < Event
9
9
  attr_reader :user, :token, :suppress, :session_id, :self_mute, :self_deaf, :mute, :deaf, :server, :channel
10
10
 
11
- def initialize(data, bot)
11
+ # @return [Channel, nil] the old channel this user was on, or nil if the user is newly joining voice.
12
+ attr_reader :old_channel
13
+
14
+ def initialize(data, old_channel_id, bot)
12
15
  @bot = bot
13
16
 
14
17
  @token = data['token']
@@ -22,6 +25,7 @@ module Discordrb::Events
22
25
  return unless @server
23
26
 
24
27
  @channel = bot.channel(data['channel_id'].to_i) if data['channel_id']
28
+ @old_channel = bot.channel(old_channel_id) if old_channel_id
25
29
  @user = bot.user(data['user_id'].to_i)
26
30
  end
27
31
  end
@@ -34,46 +38,62 @@ module Discordrb::Events
34
38
 
35
39
  [
36
40
  matches_all(@attributes[:from], event.user) do |a, e|
37
- a == if a.is_a? String
41
+ a == case a
42
+ when String
38
43
  e.name
39
- elsif a.is_a? Integer
44
+ when Integer
40
45
  e.id
41
46
  else
42
47
  e
43
48
  end
44
49
  end,
45
50
  matches_all(@attributes[:mute], event.mute) do |a, e|
46
- a == if a.is_a?(TrueClass) || a.is_a?(FalseClass)
51
+ a == if a.is_a? String
47
52
  e.to_s
48
53
  else
49
54
  e
50
55
  end
51
56
  end,
52
57
  matches_all(@attributes[:deaf], event.deaf) do |a, e|
53
- a == if a.is_a?(TrueClass) || a.is_a?(FalseClass)
58
+ a == if a.is_a? String
54
59
  e.to_s
55
60
  else
56
61
  e
57
62
  end
58
63
  end,
59
64
  matches_all(@attributes[:self_mute], event.self_mute) do |a, e|
60
- a == if a.is_a?(TrueClass) || a.is_a?(FalseClass)
65
+ a == if a.is_a? String
61
66
  e.to_s
62
67
  else
63
68
  e
64
69
  end
65
70
  end,
66
71
  matches_all(@attributes[:self_deaf], event.self_deaf) do |a, e|
67
- a == if a.is_a?(TrueClass) || a.is_a?(FalseClass)
72
+ a == if a.is_a? String
68
73
  e.to_s
69
74
  else
70
75
  e
71
76
  end
72
77
  end,
73
78
  matches_all(@attributes[:channel], event.channel) do |a, e|
74
- a == if a.is_a? String
79
+ next unless e # Don't bother if the channel is nil
80
+
81
+ a == case a
82
+ when String
83
+ e.name
84
+ when Integer
85
+ e.id
86
+ else
87
+ e
88
+ end
89
+ end,
90
+ matches_all(@attributes[:old_channel], event.old_channel) do |a, e|
91
+ next unless e # Don't bother if the channel is nil
92
+
93
+ a == case a
94
+ when String
75
95
  e.name
76
- elsif a.is_a? Fixnum
96
+ when Integer
77
97
  e.id
78
98
  else
79
99
  e