onyxcord 1.1.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 (133) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/Dockerfile +13 -0
  3. data/.devcontainer/devcontainer.json +29 -0
  4. data/.devcontainer/postcreate.sh +4 -0
  5. data/.github/CONTRIBUTING.md +13 -0
  6. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  8. data/.github/pull_request_template.md +37 -0
  9. data/.github/workflows/ci.yml +78 -0
  10. data/.github/workflows/codeql.yml +65 -0
  11. data/.github/workflows/deploy.yml +54 -0
  12. data/.github/workflows/release.yml +51 -0
  13. data/.gitignore +16 -0
  14. data/.markdownlint.json +4 -0
  15. data/.overcommit.yml +7 -0
  16. data/.rspec +2 -0
  17. data/.rubocop.yml +129 -0
  18. data/.yardopts +1 -0
  19. data/CHANGELOG.md +0 -0
  20. data/Gemfile +7 -0
  21. data/LICENSE.txt +21 -0
  22. data/README.md +305 -0
  23. data/Rakefile +17 -0
  24. data/bin/console +15 -0
  25. data/bin/setup +7 -0
  26. data/lib/onyxcord/allowed_mentions.rb +43 -0
  27. data/lib/onyxcord/api/application.rb +316 -0
  28. data/lib/onyxcord/api/channel.rb +700 -0
  29. data/lib/onyxcord/api/interaction.rb +67 -0
  30. data/lib/onyxcord/api/invite.rb +44 -0
  31. data/lib/onyxcord/api/server.rb +775 -0
  32. data/lib/onyxcord/api/user.rb +158 -0
  33. data/lib/onyxcord/api/webhook.rb +163 -0
  34. data/lib/onyxcord/api.rb +335 -0
  35. data/lib/onyxcord/await.rb +51 -0
  36. data/lib/onyxcord/bot.rb +1971 -0
  37. data/lib/onyxcord/cache.rb +326 -0
  38. data/lib/onyxcord/colour_rgb.rb +43 -0
  39. data/lib/onyxcord/commands/command_bot.rb +511 -0
  40. data/lib/onyxcord/commands/container.rb +112 -0
  41. data/lib/onyxcord/commands/events.rb +11 -0
  42. data/lib/onyxcord/commands/parser.rb +327 -0
  43. data/lib/onyxcord/commands/rate_limiter.rb +144 -0
  44. data/lib/onyxcord/configuration.rb +125 -0
  45. data/lib/onyxcord/container.rb +988 -0
  46. data/lib/onyxcord/data/activity.rb +271 -0
  47. data/lib/onyxcord/data/application.rb +341 -0
  48. data/lib/onyxcord/data/attachment.rb +91 -0
  49. data/lib/onyxcord/data/audit_logs.rb +438 -0
  50. data/lib/onyxcord/data/avatar_decoration.rb +26 -0
  51. data/lib/onyxcord/data/call.rb +22 -0
  52. data/lib/onyxcord/data/channel.rb +1355 -0
  53. data/lib/onyxcord/data/channel_tag.rb +69 -0
  54. data/lib/onyxcord/data/collectibles.rb +47 -0
  55. data/lib/onyxcord/data/component.rb +583 -0
  56. data/lib/onyxcord/data/embed.rb +258 -0
  57. data/lib/onyxcord/data/emoji.rb +123 -0
  58. data/lib/onyxcord/data/install_params.rb +24 -0
  59. data/lib/onyxcord/data/integration.rb +144 -0
  60. data/lib/onyxcord/data/interaction.rb +1141 -0
  61. data/lib/onyxcord/data/invite.rb +137 -0
  62. data/lib/onyxcord/data/member.rb +528 -0
  63. data/lib/onyxcord/data/message.rb +612 -0
  64. data/lib/onyxcord/data/message_activity.rb +41 -0
  65. data/lib/onyxcord/data/overwrite.rb +109 -0
  66. data/lib/onyxcord/data/poll.rb +365 -0
  67. data/lib/onyxcord/data/primary_server.rb +60 -0
  68. data/lib/onyxcord/data/profile.rb +79 -0
  69. data/lib/onyxcord/data/reaction.rb +64 -0
  70. data/lib/onyxcord/data/recipient.rb +34 -0
  71. data/lib/onyxcord/data/role.rb +449 -0
  72. data/lib/onyxcord/data/role_connection_data.rb +69 -0
  73. data/lib/onyxcord/data/role_subscription.rb +41 -0
  74. data/lib/onyxcord/data/scheduled_event.rb +513 -0
  75. data/lib/onyxcord/data/server.rb +1614 -0
  76. data/lib/onyxcord/data/server_preview.rb +68 -0
  77. data/lib/onyxcord/data/snapshot.rb +112 -0
  78. data/lib/onyxcord/data/team.rb +98 -0
  79. data/lib/onyxcord/data/timestamp.rb +69 -0
  80. data/lib/onyxcord/data/user.rb +324 -0
  81. data/lib/onyxcord/data/voice_region.rb +46 -0
  82. data/lib/onyxcord/data/voice_state.rb +41 -0
  83. data/lib/onyxcord/data/webhook.rb +238 -0
  84. data/lib/onyxcord/data.rb +57 -0
  85. data/lib/onyxcord/errors.rb +246 -0
  86. data/lib/onyxcord/event_executor.rb +80 -0
  87. data/lib/onyxcord/events/await.rb +48 -0
  88. data/lib/onyxcord/events/bans.rb +60 -0
  89. data/lib/onyxcord/events/channels.rb +225 -0
  90. data/lib/onyxcord/events/generic.rb +129 -0
  91. data/lib/onyxcord/events/guilds.rb +269 -0
  92. data/lib/onyxcord/events/integrations.rb +100 -0
  93. data/lib/onyxcord/events/interactions.rb +624 -0
  94. data/lib/onyxcord/events/invites.rb +127 -0
  95. data/lib/onyxcord/events/lifetime.rb +31 -0
  96. data/lib/onyxcord/events/members.rb +110 -0
  97. data/lib/onyxcord/events/message.rb +399 -0
  98. data/lib/onyxcord/events/polls.rb +118 -0
  99. data/lib/onyxcord/events/presence.rb +131 -0
  100. data/lib/onyxcord/events/raw.rb +74 -0
  101. data/lib/onyxcord/events/reactions.rb +218 -0
  102. data/lib/onyxcord/events/roles.rb +87 -0
  103. data/lib/onyxcord/events/scheduled_events.rb +171 -0
  104. data/lib/onyxcord/events/threads.rb +100 -0
  105. data/lib/onyxcord/events/typing.rb +73 -0
  106. data/lib/onyxcord/events/voice_server_update.rb +48 -0
  107. data/lib/onyxcord/events/voice_state_update.rb +106 -0
  108. data/lib/onyxcord/events/webhooks.rb +65 -0
  109. data/lib/onyxcord/gateway.rb +890 -0
  110. data/lib/onyxcord/id_object.rb +39 -0
  111. data/lib/onyxcord/light/data.rb +62 -0
  112. data/lib/onyxcord/light/integrations.rb +73 -0
  113. data/lib/onyxcord/light/light_bot.rb +58 -0
  114. data/lib/onyxcord/light.rb +8 -0
  115. data/lib/onyxcord/logger.rb +120 -0
  116. data/lib/onyxcord/message_components.rb +70 -0
  117. data/lib/onyxcord/paginator.rb +60 -0
  118. data/lib/onyxcord/permissions.rb +255 -0
  119. data/lib/onyxcord/rate_limiter/gateway.rb +42 -0
  120. data/lib/onyxcord/rate_limiter/rest.rb +89 -0
  121. data/lib/onyxcord/version.rb +7 -0
  122. data/lib/onyxcord/voice/encoder.rb +115 -0
  123. data/lib/onyxcord/voice/network.rb +380 -0
  124. data/lib/onyxcord/voice/opcodes.rb +29 -0
  125. data/lib/onyxcord/voice/sodium.rb +157 -0
  126. data/lib/onyxcord/voice/timer.rb +19 -0
  127. data/lib/onyxcord/voice/voice_bot.rb +386 -0
  128. data/lib/onyxcord/webhooks.rb +14 -0
  129. data/lib/onyxcord/websocket.rb +62 -0
  130. data/lib/onyxcord.rb +180 -0
  131. data/onyxcord-webhooks.gemspec +30 -0
  132. data/onyxcord.gemspec +50 -0
  133. metadata +421 -0
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnyxCord
4
+ # Publicly accessible information about a discoverable server.
5
+ class ServerPreview
6
+ include IDObject
7
+ include ServerAttributes
8
+
9
+ # @return [String, nil] the ID of the server's invite splash screen.
10
+ # @see #splash_url
11
+ attr_reader :splash_id
12
+
13
+ # @return [String, nil] the ID of the server's discovery splash screen.
14
+ # @see #discovery_splash_url
15
+ attr_reader :discovery_splash_id
16
+
17
+ # @return [Hash<Integer => Emoji>] a hash of all the emojis usable on this server.
18
+ attr_reader :emojis
19
+
20
+ # @return [Array<Symbol>] the features of this server, e.g. `:banner` or `:verified`.
21
+ attr_reader :features
22
+
23
+ # @return [Integer] the approximate number of members on this server, offline or not.
24
+ attr_reader :member_count
25
+
26
+ # @return [Integer] the approximate number of members that aren't offline on this server.
27
+ attr_reader :presence_count
28
+
29
+ # @return [String, nil] the description of this server that's shown in the discovery tab.
30
+ attr_reader :description
31
+
32
+ # @!visibility private
33
+ def initialize(data, bot)
34
+ @bot = bot
35
+ @id = data['id'].to_i
36
+ @name = data['name']
37
+ @icon_id = data['icon']
38
+ @splash_id = data['splash']
39
+ @description = data['description']
40
+ @discovery_splash_id = data['discovery_splash']
41
+ @member_count = data['approximate_member_count']
42
+ @presence_count = data['approximate_presence_count']
43
+ @features = data['features'].map { |feature| feature.downcase.to_sym }
44
+ @emojis = data['emojis'].to_h { |emoji| [emoji['id'].to_i, Emoji.new(emoji, bot)] }
45
+ end
46
+
47
+ # Get the server associated with this server preview.
48
+ # @return [Server] the server associated with this server preview.
49
+ # @raise [OnyxCord::Errors::NoPermission] this can happen when the bot is not in the associated server.
50
+ def server
51
+ @bot.server(@id)
52
+ end
53
+
54
+ # Utility method to get a server preview's splash URL.
55
+ # @param format [String] the URL will default to `webp`. You can otherwise specify one of `jpg` or `png` to override this.
56
+ # @return [String, nil] the URL to the server's splash image, or `nil` if the server doesn't have a splash image.
57
+ def splash_url(format = 'webp')
58
+ API.splash_url(@id, @splash_id, format) if @splash_id
59
+ end
60
+
61
+ # Utility method to get a server preview's discovery splash URL.
62
+ # @param format [String] the URL will default to `webp`. You can otherwise specify one of `jpg` or `png` to override this.
63
+ # @return [String, nil] the URL to the server's discovery splash image, or `nil` if the server doesn't have a discovery splash image.
64
+ def discovery_splash_url(format = 'webp')
65
+ API.discovery_splash_url(@id, @discovery_splash_id, format) if @discovery_splash_id
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnyxCord
4
+ # A partial and immutable copy of a message that has been forwarded.
5
+ class Snapshot
6
+ # @return [Integer] the message type of the message snapshot.
7
+ attr_reader :type
8
+
9
+ # @return [String] the text content of the message snapshot.
10
+ attr_reader :content
11
+
12
+ # @return [Array<Embed>] the embeds attached to the message snapshot.
13
+ attr_reader :embeds
14
+
15
+ # @return [Array<Attachment>] the files attached to the message snapshot.
16
+ attr_reader :attachments
17
+
18
+ # @return [Time] the time at which the message snapshot was created.
19
+ attr_reader :created_at
20
+
21
+ # @return [Time, nil] the time at which the message snapshot was edited.
22
+ attr_reader :edited_at
23
+
24
+ # @return [Integer] the flags that have been set on the message snapshot.
25
+ attr_reader :flags
26
+
27
+ # @return [Array<User>] the users that were mentioned in the message snapshot.
28
+ attr_reader :mentions
29
+
30
+ # @return [Array<Component>] the interaction components associated with the message snapshot.
31
+ attr_reader :components
32
+
33
+ # @!visibility private
34
+ def initialize(data, bot)
35
+ @bot = bot
36
+ @type = data['type']
37
+ @flags = data['flags'] || 0
38
+ @content = data['content']
39
+ @mention_roles = data['mention_roles']&.map(&:resolve_id) || []
40
+ @embeds = data['embeds']&.map { |embed| Embed.new(embed, self) } || []
41
+ @attachments = data['attachments']&.map { |file| Attachment.new(file, self, @bot) } || []
42
+ @created_at = data['timestamp'] ? Time.parse(data['timestamp']) : nil
43
+ @edited_at = data['edited_timestamp'] ? Time.parse(data['edited_timestamp']) : nil
44
+ @mentions = data['mentions']&.map { |mention| @bot.ensure_user(mention) } || []
45
+ @components = data['components']&.map { |component| Components.from_data(component, @bot) } || []
46
+ end
47
+
48
+ # Check whether the message snapshot has been edited.
49
+ # @return [true, false] whether the snapshot was edited or not.
50
+ def edited?
51
+ !@edited_at.nil?
52
+ end
53
+
54
+ # Check whether the message snapshot contains any custom emojis.
55
+ # @return [true, false] whether or not any emoji were used in the snapshot.
56
+ def emojis?
57
+ emojis.any?
58
+ end
59
+
60
+ # Get the custom emojis that were used in the message snapshot.
61
+ # @return [Array<Emoji>] the emojis used in the message snapshot.
62
+ def emojis
63
+ return [] if @content.nil? || @content.empty?
64
+
65
+ @emojis ||= @bot.parse_mentions(@content).select { |parsed| parsed.is_a?(Emoji) }
66
+ end
67
+
68
+ # Get the roles that were mentioned in the message snapshot.
69
+ # @return [Array<Role>] the roles that were mentioned in the message snapshot.
70
+ # @note this can only resolve roles in servers that the bot has access to via {Bot#servers}.
71
+ def role_mentions
72
+ return [] if @mention_roles.empty?
73
+
74
+ return @role_mentions if @role_mentions
75
+
76
+ roles = @bot.servers.values.flat_map(&:roles)
77
+
78
+ @role_mentions = @mention_roles.filter_map { |id| roles.find { |r| r.id == id } }
79
+ end
80
+
81
+ # Get the buttons that were used in the message snapshot.
82
+ # @return [Array<Components::Button>] the button components used in the message snapshot.
83
+ def buttons
84
+ buttons = @components.flat_map do |component|
85
+ case component
86
+ when Components::Button
87
+ component
88
+ when Components::Section
89
+ component.accessory if component.accessory.is_a?(Components::Button)
90
+ when Components::ActionRow, Components::Container
91
+ component.buttons
92
+ end
93
+ end
94
+
95
+ buttons.compact
96
+ end
97
+
98
+ # @see OnyxCord::Message::FLAGS
99
+ Message::FLAGS.each do |name, value|
100
+ define_method("#{name}?") do
101
+ @flags.anybits?(value)
102
+ end
103
+ end
104
+
105
+ # @see OnyxCord::Message::TYPES
106
+ Message::TYPES.each do |name, value|
107
+ define_method("#{name}?") do
108
+ @type == value
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnyxCord
4
+ # A group of users that can manage applications.
5
+ class Team
6
+ include IDObject
7
+
8
+ # @return [String] the name of this team.
9
+ attr_reader :name
10
+
11
+ # @return [Member] the owner of this team.
12
+ attr_reader :owner
13
+
14
+ # @return [String, nil] the ID of this team's icon.
15
+ # @see #icon_url
16
+ attr_reader :icon_id
17
+
18
+ # @return [Array<Member>] the members that are a part of this team.
19
+ attr_reader :members
20
+
21
+ # @!visibility private
22
+ def initialize(data, bot)
23
+ @bot = bot
24
+ @id = data['id'].to_i
25
+ @name = data['name']
26
+ @icon_id = data['icon']
27
+ @members = data['members'].map { |member| Member.new(member, self, bot) }
28
+ @owner = @members.find { |member| member.user.id == data['owner_user_id'].to_i }
29
+ end
30
+
31
+ # Utility method to get a team's icon URL.
32
+ # @param format [String] The URL will default to `webp`. You can otherwise specify one of `webp`, `jpg`, or `png` to override this.
33
+ # @return [String, nil] the URL to the icon image (nil if no image is set).
34
+ def icon_url(format = 'webp')
35
+ API.team_icon_url(@id, @icon_id, format) if @icon_id
36
+ end
37
+
38
+ # A member that has been invited to a team.
39
+ class Member
40
+ # @return [Symbol] the role of this team member.
41
+ attr_reader :role
42
+
43
+ # @return [Team] the team this member is a part of.
44
+ attr_reader :team
45
+
46
+ # @return [Integer] the membership state of this team member.
47
+ attr_reader :state
48
+
49
+ # @return [Integer] the user associated with this team member.
50
+ attr_reader :user
51
+
52
+ # @!visibility private
53
+ def initialize(data, team, bot)
54
+ @bot = bot
55
+ @team = team
56
+ @role = data['role'].to_sym
57
+ @state = data['membership_state']
58
+ @user = bot.ensure_user(data['user'])
59
+ end
60
+
61
+ # Whether this team member has been invited to the team, but hasn't accepted the invite yet.
62
+ # @return [true, false]
63
+ def pending?
64
+ @state == 1
65
+ end
66
+
67
+ # Whether this team member is the owner of the team.
68
+ # @return [true, false]
69
+ def owner?
70
+ @team.owner == self
71
+ end
72
+
73
+ # @!method admin?
74
+ # @return [true, false] whether this team member is an admin.
75
+ # @!method developer?
76
+ # @return [true, false] whether this team member is a developer.
77
+ # @!method read_only?
78
+ # @return [true, false] whether this team member is a read only developer.
79
+ %i[admin developer read_only].each do |role|
80
+ define_method("#{role}?") do
81
+ @role == role
82
+ end
83
+ end
84
+
85
+ # Comparison based off of user ID and team ID.
86
+ # @return [true, false] if the two objects are equal.
87
+ def ==(other)
88
+ return false unless other.is_a?(Member)
89
+
90
+ return false unless @team == other.team
91
+
92
+ OnyxCord.id_compare?(other.user.id, @user.id)
93
+ end
94
+
95
+ alias_method :eql?, :==
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnyxCord
4
+ # A timestamp referenced in a message via markdown.
5
+ class TimestampMarkdown
6
+ # Mapping of timestamp styles.
7
+ STYLES = {
8
+ short_time: 't', # 16:20
9
+ long_time: 'T', # 16:20:30
10
+ short_date: 'd', # 20/04/2021
11
+ long_date: 'D', # 20 April 2021
12
+ short_datetime: 'f', # 20 April 2021 16:20
13
+ long_datetime: 'F', # Tuesday, 20 April 2021 16:20
14
+ relative: 'R', # 2 months ago
15
+ simple_datetime: 's', # 20/04/2021, 16:20
16
+ medium_datetime: 'S' # 20/04/2021, 16:20:30
17
+ }.freeze
18
+
19
+ # @return [Time] the time that the timestamp is referencing.
20
+ attr_reader :time
21
+
22
+ # @!visibility private
23
+ def initialize(time, style)
24
+ @time = time
25
+ @style = style
26
+ end
27
+
28
+ # Get the specifier used to determine the style of the timestamp.
29
+ # @return [String] the formatting specifier used to display the timestamp.
30
+ def style
31
+ @style || 'f'
32
+ end
33
+
34
+ # Get a string that will allow you to display the time in the Discord client.
35
+ # @return [String] The timestamp serialized as a string for the Discord client.
36
+ def to_s
37
+ OnyxCord.timestamp(@time, @style)
38
+ end
39
+
40
+ # @!visibility private
41
+ def inspect
42
+ "<TimestampMarkdown time=#{@time.to_i} style=\"#{style}\">"
43
+ end
44
+
45
+ # @!method short_time?
46
+ # @return [true, false] whether or not the timestamp is displayed in a format such as `16:20`.
47
+ # @!method long_time?
48
+ # @return [true, false] whether or not the timestamp is displayed in a format such as `16:20:30`.
49
+ # @!method short_date?
50
+ # @return [true, false] whether or not the timestamp is displayed in a format such as `20/04/2021`.
51
+ # @!method long_date?
52
+ # @return [true, false] whether or not the timestamp is displayed in a format such as `20 April 2021`.
53
+ # @!method short_datetime?
54
+ # @return [true, false] whether or not the timestamp is displayed in a format such as `20 April 2021 16:20`.
55
+ # @!method long_datetime?
56
+ # @return [true, false] whether or not the timestamp is displayed in a format such as `Tuesday, 20 April 2021 16:20`.
57
+ # @!method relative?
58
+ # @return [true, false] whether or not the timestamp is displayed in a format such as `2 months ago`.
59
+ # @!method simple_datetime?
60
+ # @return [true, false] whether or not the timestamp is displayed in a format such as `20/04/2021, 16:20`.
61
+ # @!method medium_datetime?
62
+ # @return [true, false] whether or not the timestamp is displayed in a format such as ` 20/04/2021, 16:20:30`.
63
+ STYLES.each do |name, value|
64
+ define_method("#{name}?") do
65
+ style == value
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,324 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnyxCord
4
+ # Mixin for the attributes users should have
5
+ module UserAttributes
6
+ # rubocop:disable Naming/VariableNumber
7
+ FLAGS = {
8
+ staff: 1 << 0,
9
+ partner: 1 << 1,
10
+ hypesquad: 1 << 2,
11
+ bug_hunter_level_1: 1 << 3,
12
+ hypesquad_online_house_1: 1 << 6,
13
+ hypesquad_online_house_2: 1 << 7,
14
+ hypesquad_online_house_3: 1 << 8,
15
+ premium_early_supporter: 1 << 9,
16
+ team_pseudo_user: 1 << 10,
17
+ bug_hunter_level_2: 1 << 14,
18
+ verified_bot: 1 << 16,
19
+ verified_developer: 1 << 17,
20
+ certified_moderator: 1 << 18,
21
+ bot_http_interactions: 1 << 19,
22
+ active_developer: 1 << 22
23
+ }.freeze
24
+ # rubocop:enable Naming/VariableNumber
25
+
26
+ # @return [String] this user's username
27
+ attr_reader :username
28
+ alias_method :name, :username
29
+
30
+ # @return [String, nil] this user's global name
31
+ attr_reader :global_name
32
+
33
+ # @return [String] this user's discriminator which is used internally to identify users with identical usernames.
34
+ attr_reader :discriminator
35
+ alias_method :discrim, :discriminator
36
+ alias_method :tag, :discriminator
37
+ alias_method :discord_tag, :discriminator
38
+
39
+ # @return [true, false] whether this user is a Discord bot account
40
+ attr_reader :bot_account
41
+ alias_method :bot_account?, :bot_account
42
+
43
+ # @return [true, false] whether this is fake user for a webhook message
44
+ attr_reader :webhook_account
45
+ alias_method :webhook_account?, :webhook_account
46
+ alias_method :webhook?, :webhook_account
47
+
48
+ # @return [String] the ID of this user's current avatar, can be used to generate an avatar URL.
49
+ # @see #avatar_url
50
+ attr_accessor :avatar_id
51
+
52
+ # @return [true, false] whether the user is an offical Discord System user (part of the urgent message system).
53
+ attr_reader :system_account
54
+ alias_method :system_account?, :system_account
55
+
56
+ # @return [AvatarDecoration, nil] the current user's avatar decoration, or nil if the user doesn't have one.
57
+ attr_reader :avatar_decoration
58
+
59
+ # @return [Collectibles] the collectibles that this user has collected.
60
+ attr_reader :collectibles
61
+
62
+ # @return [PrimaryServer, nil] the server tag the user has adopted, or nil if the user doesn't have one displayed.
63
+ attr_reader :primary_server
64
+ alias_method :server_tag, :primary_server
65
+
66
+ # Utility function to get Discord's display name of a user not in server
67
+ # @return [String] the name the user displays as (global_name if they have one, username otherwise)
68
+ def display_name
69
+ global_name || username
70
+ end
71
+
72
+ # Utility function to mention users in messages
73
+ # @return [String] the mention code in the form of <@id>
74
+ def mention
75
+ "<@#{@id}>"
76
+ end
77
+
78
+ # Utility function to get Discord's distinct representation of a user, i.e. username + discriminator
79
+ # @return [String] distinct representation of user
80
+ # TODO: Maybe change this method again after discriminator removal ?
81
+ def distinct
82
+ if @discriminator && @discriminator != '0'
83
+ "#{@username}##{@discriminator}"
84
+ else
85
+ @username.to_s
86
+ end
87
+ end
88
+
89
+ # Utility function to get a user's avatar URL.
90
+ # @param format [String, nil] If `nil`, the URL will default to `webp` for static avatars, and will detect if the user has a `gif` avatar. You can otherwise specify one of `webp`, `jpg`, `png`, or `gif` to override this. Will always be PNG for default avatars.
91
+ # @return [String] the URL to the avatar image.
92
+ # TODO: Maybe change this method again after discriminator removal ?
93
+ def avatar_url(format = nil)
94
+ unless @avatar_id
95
+ return API::User.default_avatar(@discriminator, legacy: true) if @discriminator && @discriminator != '0'
96
+
97
+ return API::User.default_avatar(@id)
98
+ end
99
+
100
+ API::User.avatar_url(@id, @avatar_id, format)
101
+ end
102
+
103
+ # @return [Integer] the public flags on a user's account
104
+ attr_reader :public_flags
105
+
106
+ FLAGS.each do |name, value|
107
+ define_method("#{name}?") do
108
+ @public_flags.anybits?(value)
109
+ end
110
+ end
111
+
112
+ # Utility function to get a user's banner URL.
113
+ # @param format [String, nil] If `nil`, the URL will default to `png` for static banners and will detect if the user has a `gif` banner.
114
+ # You can otherwise specify one of `webp`, `jpg`, `png`, or `gif` to override this.
115
+ # @return [String, nil] the URL to the banner image or nil if the user doesn't have one.
116
+ def banner_url(format = nil)
117
+ API::User.banner_url(@id, banner_id, format) if banner_id
118
+ end
119
+ end
120
+
121
+ # User on Discord, including internal data like discriminators
122
+ class User
123
+ include IDObject
124
+ include UserAttributes
125
+
126
+ # @return [Symbol] the current online status of the user (`:online`, `:offline` or `:idle`)
127
+ attr_reader :status
128
+
129
+ # @return [ActivitySet] the activities of the user
130
+ attr_reader :activities
131
+
132
+ # @return [Hash<Symbol, Symbol>] the current online status (`:online`, `:idle` or `:dnd`) of the user
133
+ # on various device types (`:desktop`, `:mobile`, or `:web`). The value will be `nil` if the user is offline or invisible.
134
+ attr_reader :client_status
135
+
136
+ # @!visibility private
137
+ def initialize(data, bot)
138
+ @bot = bot
139
+
140
+ @username = data['username']
141
+ @global_name = data['global_name']
142
+ @id = data['id'].to_i
143
+ @discriminator = data['discriminator']
144
+ @avatar_id = data['avatar']
145
+ @activities = OnyxCord::ActivitySet.new
146
+ @public_flags = data['public_flags'] || 0
147
+ @bot_account = data['bot'] || false
148
+ @webhook_account = data['_webhook'] || false
149
+
150
+ @status = :offline
151
+ @client_status = process_client_status(data['client_status'])
152
+ @banner_id = data['banner']
153
+ @system_account = data['system'] || false
154
+ @avatar_decoration = process_avatar_decoration(data['avatar_decoration_data'])
155
+ @collectibles = Collectibles.new(data['collectibles'] || {}, bot)
156
+
157
+ @primary_server = process_primary_server(data['primary_guild'] || {})
158
+ end
159
+
160
+ # Get a user's PM channel or send them a PM
161
+ # @overload pm
162
+ # Creates a private message channel for this user or returns an existing one if it already exists
163
+ # @return [Channel] the PM channel to this user.
164
+ # @overload pm(content)
165
+ # Sends a private to this user.
166
+ # @param content [String] The content to send.
167
+ # @return [Message] the message sent to this user.
168
+ def pm(content = nil)
169
+ if content
170
+ # Recursively call pm to get the channel, then send a message to it
171
+ channel = pm
172
+ channel.send_message(content)
173
+ else
174
+ # If no message was specified, return the PM channel
175
+ @bot.pm_channel(@id)
176
+ end
177
+ end
178
+
179
+ alias_method :dm, :pm
180
+
181
+ # Send the user a file.
182
+ # @param file [File] The file to send to the user
183
+ # @param caption [String] The caption of the file being sent
184
+ # @param filename [String] Overrides the filename of the uploaded file
185
+ # @param spoiler [true, false] Whether or not this file should appear as a spoiler.
186
+ # @return [Message] the message sent to this user.
187
+ # @example Send a file from disk
188
+ # user.send_file(File.open('rubytaco.png', 'r'))
189
+ def send_file(file, caption = nil, filename: nil, spoiler: nil)
190
+ pm.send_file(file, caption: caption, filename: filename, spoiler: spoiler)
191
+ end
192
+
193
+ # @return [String, nil] the ID of this user's current banner, can be used to generate a banner URL.
194
+ # @see #banner_url
195
+ def banner_id
196
+ @banner_id ||= JSON.parse(API::User.resolve(@bot.token, @id))['banner']
197
+ end
198
+
199
+ # Set the user's username
200
+ # @note for internal use only
201
+ # @!visibility private
202
+ def update_username(username)
203
+ @username = username
204
+ end
205
+
206
+ # Set the user's global_name
207
+ # @note For internal use only.
208
+ # @!visibility private
209
+ def update_global_name(global_name)
210
+ @global_name = global_name
211
+ end
212
+
213
+ # Set the user's avatar_decoration
214
+ # @note For internal use only.
215
+ # @!visibility private
216
+ def update_avatar_decoration(decoration)
217
+ @avatar_decoration = process_avatar_decoration(decoration)
218
+ end
219
+
220
+ # Set the user's collectibles
221
+ # @note For internal use only.
222
+ # @!visibility private
223
+ def update_collectibles(collectibles)
224
+ @collectibles = Collectibles.new(collectibles || {}, @bot)
225
+ end
226
+
227
+ # Set the user's primary server
228
+ # @note For internal use only.
229
+ # @!visibility private
230
+ def update_primary_server(server)
231
+ @primary_server = process_primary_server(server || {})
232
+ end
233
+
234
+ # Set the user's presence data
235
+ # @note for internal use only
236
+ # @!visibility private
237
+ def update_presence(data)
238
+ @status = data['status'].to_sym
239
+ @client_status = process_client_status(data['client_status'])
240
+
241
+ @activities = OnyxCord::ActivitySet.new(data['activities'].map { |act| Activity.new(act, @bot) })
242
+ end
243
+
244
+ # Add an await for a message from this user. Specifically, this adds a global await for a MessageEvent with this
245
+ # user's ID as a :from attribute.
246
+ # @see Bot#add_await
247
+ def await(key, attributes = {}, &block)
248
+ @bot.add_await(key, OnyxCord::Events::MessageEvent, { from: @id }.merge(attributes), &block)
249
+ end
250
+
251
+ # Add a blocking await for a message from this user. Specifically, this adds a global await for a MessageEvent with this
252
+ # user's ID as a :from attribute.
253
+ # @see Bot#add_await!
254
+ def await!(attributes = {}, &block)
255
+ @bot.add_await!(OnyxCord::Events::MessageEvent, { from: @id }.merge(attributes), &block)
256
+ end
257
+
258
+ # Gets the member this user is on a server
259
+ # @param server [Server] The server to get the member for
260
+ # @return [Member] this user as a member on a particular server
261
+ def on(server)
262
+ id = server.resolve_id
263
+ @bot.server(id).member(@id)
264
+ end
265
+
266
+ # Is the user the bot?
267
+ # @return [true, false] whether this user is the bot
268
+ def current_bot?
269
+ @bot.profile.id == @id
270
+ end
271
+
272
+ # @!visibility private
273
+ def process_client_status(client_status)
274
+ (client_status || {}).to_h { |k, v| [k.to_sym, v.to_sym] }
275
+ end
276
+
277
+ # @!visibility private
278
+ def process_avatar_decoration(decoration)
279
+ decoration ? AvatarDecoration.new(decoration, @bot) : nil
280
+ end
281
+
282
+ # @!visibility private
283
+ def process_primary_server(server)
284
+ PrimaryServer.new(server, @bot) if server['identity_enabled']
285
+ end
286
+
287
+ # @!method offline?
288
+ # @return [true, false] whether this user is offline.
289
+ # @!method idle?
290
+ # @return [true, false] whether this user is idle.
291
+ # @!method online?
292
+ # @return [true, false] whether this user is online.
293
+ # @!method dnd?
294
+ # @return [true, false] whether this user is set to do not disturb.
295
+ %i[offline idle online dnd].each do |e|
296
+ define_method("#{e}?") do
297
+ @status.to_sym == e
298
+ end
299
+ end
300
+
301
+ # @return [String, nil] the game the user is currently playing, or `nil` if nothing is being played.
302
+ # @deprecated Please use {ActivitySet#games} for information about the user's game activity
303
+ def game
304
+ @activities.games.first&.name
305
+ end
306
+
307
+ # @return [Integer] returns 1 for twitch streams, or 0 for no stream.
308
+ # @deprecated Please use {ActivitySet#streaming} for information about the user's stream activity
309
+ def stream_type
310
+ @activities.streaming ? 1 : 0
311
+ end
312
+
313
+ # @return [String, nil] the URL to the stream, if the user is currently streaming something
314
+ # @deprecated Please use {ActivitySet#streaming} for information about the user's stream activity
315
+ def stream_url
316
+ @activities.streaming.first&.url
317
+ end
318
+
319
+ # The inspect method is overwritten to give more useful output
320
+ def inspect
321
+ "<User username=#{@username} id=#{@id} discriminator=#{@discriminator}>"
322
+ end
323
+ end
324
+ end