Skip to content

Commit

Permalink
New usernames system (#221)
Browse files Browse the repository at this point in the history
* Add global_name to User after new usernames system

  + Add User#display_name
  + Change User#distinct to use discriminator or not, depend to the context (Maybe need change method later, next API versions may remove the discriminator field)
  + Change Discordrb::API::User#default_avatar & User#avatar_url to use old method to calculate index or not, depend to legacy or not (Maybe need change method later, next API versions may remove the discriminator field)
  + Change Member#display_name to select global_name before username, but after server nickname
  + Change Webhook#avatar_url to use new method to calculate index (After testing the webhook creation, the ID already seems to be used for the default avatar instead of the discriminator at 0000)

* Modification to allow to determine if User is a Webhook

  + Without relying on a 0000 discrimator, since it is very likely that this field will disappear completely, sooner or later

* Add missing flag

  + As I saw that the 'ACTIVE_DEVELOPER' flag was missing in the documentation, I added it in the process.

* Rubocop friendly

* Addition of missing cache edit
  • Loading branch information
Dakurei committed Jul 5, 2023
1 parent b029e52 commit 9ca8ebe
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 23 deletions.
11 changes: 8 additions & 3 deletions lib/discordrb/api/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,14 @@ def change_status_setting(token, status)
)
end

# Returns one of the "default" discord avatars from the CDN given a discriminator
def default_avatar(discrim = 0)
index = discrim.to_i % 5
# Returns one of the "default" discord avatars from the CDN given a discriminator or id since new usernames
# TODO: Maybe change this method again after discriminator removal ?
def default_avatar(discrim_id = 0, legacy: false)
index = if legacy
discrim_id.to_i % 5
else
(discrim_id.to_i >> 22) % 5
end
"#{Discordrb::API.cdn_url}/embed/avatars/#{index}.png"
end

Expand Down
9 changes: 8 additions & 1 deletion lib/discordrb/bot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -946,10 +946,16 @@ def update_presence(data)

username = data['user']['username']
if username && !member_is_new # Don't set the username for newly-cached members
debug "Implicitly updating presence-obtained information for member #{user_id}"
debug "Implicitly updating presence-obtained information username for member #{user_id}"
member.update_username(username)
end

global_name = data['user']['global_name']
if global_name && !member_is_new # Don't set the global_name for newly-cached members
debug "Implicitly updating presence-obtained information global_name for member #{user_id}"
member.update_global_name(global_name)
end

member.update_presence(data)

member.avatar_id = data['user']['avatar'] if data['user']['avatar']
Expand Down Expand Up @@ -1088,6 +1094,7 @@ def update_guild_member(data)
member = server.member(data['user']['id'].to_i)
member.update_roles(data['roles'])
member.update_nick(data['nick'])
member.update_global_name(data['user']['global_name']) if data['user']['global_name']
member.update_boosting_since(data['premium_since'])
member.update_communication_disabled_until(data['communication_disabled_until'])
end
Expand Down
4 changes: 2 additions & 2 deletions lib/discordrb/data/member.rb
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,9 @@ def set_nick(nick, reason = nil)

alias_method :set_nickname, :set_nick

# @return [String] the name the user displays as (nickname if they have one, username otherwise)
# @return [String] the name the user displays as (nickname if they have one, global_name if they have one, username otherwise)
def display_name
nickname || username
nickname || global_name || username
end

# Update this member's roles
Expand Down
12 changes: 5 additions & 7 deletions lib/discordrb/data/message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ class Message
# @return [Array<Component>]
attr_reader :components

# The discriminator that webhook user accounts have.
ZERO_DISCRIM = '0000'

# @!visibility private
def initialize(data, bot)
@bot = bot
Expand All @@ -92,12 +89,14 @@ def initialize(data, bot)

@server = @channel.server

@webhook_id = data['webhook_id']&.to_i

@author = if data['author']
if data['author']['discriminator'] == ZERO_DISCRIM
if @webhook_id
# This is a webhook user! It would be pointless to try to resolve a member here, so we just create
# a User and return that instead.
Discordrb::LOGGER.debug("Webhook user: #{data['author']['id']}")
User.new(data['author'], @bot)
User.new(data['author'].merge({ '_webhook' => true }), @bot)
elsif @channel.private?
# Turn the message user into a recipient - we can't use the channel recipient
# directly because the bot may also send messages to the channel
Expand All @@ -107,6 +106,7 @@ def initialize(data, bot)

if member
member.update_data(data['member']) if data['member']
member.update_global_name(data['author']['global_name']) if data['author']['global_name']
else
Discordrb::LOGGER.debug("Member with ID #{data['author']['id']} not cached (possibly left the server).")
member = if data['member']
Expand All @@ -121,8 +121,6 @@ def initialize(data, bot)
end
end

@webhook_id = data['webhook_id'].to_i if data['webhook_id']

@timestamp = Time.parse(data['timestamp']) if data['timestamp']
@edited_timestamp = data['edited_timestamp'].nil? ? nil : Time.parse(data['edited_timestamp'])
@edited = !@edited_timestamp.nil?
Expand Down
49 changes: 40 additions & 9 deletions lib/discordrb/data/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ module UserAttributes
verified_bot: 1 << 16,
verified_developer: 1 << 17,
certified_moderator: 1 << 18,
bot_http_interactions: 1 << 19
bot_http_interactions: 1 << 19,
active_developer: 1 << 22
}.freeze
# rubocop:enable Naming/VariableNumber

# @return [String] this user's username
attr_reader :username
alias_method :name, :username

# @return [String, nil] this user's global name
attr_reader :global_name

# @return [String] this user's discriminator which is used internally to identify users with identical usernames.
attr_reader :discriminator
alias_method :discrim, :discriminator
Expand All @@ -36,10 +40,21 @@ module UserAttributes
attr_reader :bot_account
alias_method :bot_account?, :bot_account

# @return [true, false] whether this is fake user for a webhook message
attr_reader :webhook_account
alias_method :webhook_account?, :webhook_account
alias_method :webhook?, :webhook_account

# @return [String] the ID of this user's current avatar, can be used to generate an avatar URL.
# @see #avatar_url
attr_accessor :avatar_id

# Utility function to get Discord's display name of a user not in server
# @return [String] the name the user displays as (global_name if they have one, username otherwise)
def display_name
global_name || username
end

# Utility function to mention users in messages
# @return [String] the mention code in the form of <@id>
def mention
Expand All @@ -48,15 +63,25 @@ def mention

# Utility function to get Discord's distinct representation of a user, i.e. username + discriminator
# @return [String] distinct representation of user
# TODO: Maybe change this method again after discriminator removal ?
def distinct
"#{@username}##{@discriminator}"
if @discriminator && @discriminator != '0'
"#{@username}##{@discriminator}"
else
@username.to_s
end
end

# Utility function to get a user's avatar URL.
# @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.
# @return [String] the URL to the avatar image.
# TODO: Maybe change this method again after discriminator removal ?
def avatar_url(format = nil)
return API::User.default_avatar(@discriminator) unless @avatar_id
unless @avatar_id
return API::User.default_avatar(@discriminator, legacy: true) if @discriminator && @discriminator != '0'

return API::User.default_avatar(@id)
end

API::User.avatar_url(@id, @avatar_id, format)
end
Expand Down Expand Up @@ -91,6 +116,7 @@ def initialize(data, bot)
@bot = bot

@username = data['username']
@global_name = data['global_name']
@id = data['id'].to_i
@discriminator = data['discriminator']
@avatar_id = data['avatar']
Expand All @@ -101,6 +127,9 @@ def initialize(data, bot)
@bot_account = false
@bot_account = true if data['bot']

@webhook_account = false
@webhook_account = true if data['_webhook']

@status = :offline
@client_status = process_client_status(data['client_status'])
end
Expand Down Expand Up @@ -138,13 +167,20 @@ def send_file(file, caption = nil, filename: nil, spoiler: nil)
pm.send_file(file, caption: caption, filename: filename, spoiler: spoiler)
end

# Set the user's name
# Set the user's username
# @note for internal use only
# @!visibility private
def update_username(username)
@username = username
end

# Set the user's global_name
# @note For internal use only.
# @!visibility private
def update_global_name(global_name)
@global_name = global_name
end

# Set the user's presence data
# @note for internal use only
# @!visibility private
Expand Down Expand Up @@ -183,11 +219,6 @@ def current_bot?
@bot.profile.id == @id
end

# @return [true, false] whether this user is a fake user for a webhook message
def webhook?
@discriminator == Message::ZERO_DISCRIM
end

# @!visibility private
def process_client_status(client_status)
(client_status || {}).to_h { |k, v| [k.to_sym, v.to_sym] }
Expand Down
2 changes: 1 addition & 1 deletion lib/discordrb/data/webhook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def edit_message(message, content: nil, embeds: nil, allowed_mentions: nil, buil
# Utility function to get a webhook's avatar URL.
# @return [String] the URL to the avatar image
def avatar_url
return API::User.default_avatar unless @avatar
return API::User.default_avatar(@id) unless @avatar

API::User.avatar_url(@id, @avatar)
end
Expand Down

0 comments on commit 9ca8ebe

Please sign in to comment.