lib/discorb/emoji.rb
# frozen_string_literal: true
require "uri"
module Discorb
# Represents a Discord emoji.
# @abstract
class Emoji
def eql?(other)
other.is_a?(self.class) && other.to_uri == to_uri
end
def ==(other)
eql?(other)
end
def inspect
"#<#{self.class}>"
end
# @return [Hash] The hash represents the emoji.
def to_hash
raise NotImplementedError
end
end
# Represents a custom emoji in discord.
class CustomEmoji < Emoji
# @return [Discorb::Snowflake] The ID of the emoji.
attr_reader :id
# @return [String] The name of the emoji.
attr_reader :name
# @return [Array<Discorb::Role>] The roles that can use this emoji.
attr_reader :roles
# @return [Discorb::User] The user that created this emoji.
attr_reader :user
# @return [Discorb::Guild] The guild that owns this emoji.
attr_reader :guild
# @return [Boolean] whether the emoji is managed by integration (ex: Twitch).
attr_reader :managed
alias managed? managed
# @return [Boolean] whether the emoji requires colons.
attr_reader :require_colons
alias require_colons? require_colons
# @return [Boolean] whether the emoji is available.
attr_reader :available
alias available? available
# @!attribute [r] roles?
# @return [Boolean] whether or not this emoji is restricted to certain roles.
#
# Initialize a new custom emoji.
# @private
#
# @param [Discorb::Client] client The client that owns this emoji.
# @param [Discorb::Guild] guild The guild that owns this emoji.
# @param [Hash] data The data of the emoji.
#
def initialize(client, guild, data)
@client = client
@guild = guild
@data = {}
_set_data(data)
end
#
# Format the emoji for sending.
#
# @return [String] the formatted emoji.
#
def to_s
"<#{@animated ? "a" : ""}:#{@name}:#{id}>"
end
#
# Format the emoji for URI.
#
# @return [String] the formatted emoji.
#
def to_uri
"#{@name}:#{@id}"
end
def roles?
@roles != []
end
alias role? roles?
def inspect
"#<#{self.class} id=#{@id} :#{@name}:>"
end
#
# Edit the emoji.
# @async
# @macro edit
#
# @param [String] name The new name of the emoji.
# @param [Array<Discorb::Role>] roles The new roles that can use this emoji.
# @param [String] reason The reason for editing the emoji.
#
# @return [Async::Task<self>] The edited emoji.
#
def edit(name: Discorb::Unset, roles: Discorb::Unset, reason: nil)
Async do
payload = {}
payload[:name] = name if name != Discorb::Unset
payload[:roles] = roles.map do |r|
Discorb::Utils.try(r, :id)
end if roles != Discorb::Unset
@client.http.request(
Route.new(
"/guilds/#{@guild.id}/emojis/#{@id}",
"//guilds/:guild_id/emojis/:emoji_id",
:patch
),
payload,
audit_log_reason: reason
)
self
end
end
alias modify edit
#
# Delete the emoji.
# @async
#
# @param [String] reason The reason for deleting the emoji.
#
# @return [Async::Task<self>] The deleted emoji.
#
def delete(reason: nil)
Async do
@client
.http
.request(
Route.new(
"/guilds/#{@guild.id}/emojis/#{@id}",
"//guilds/:guild_id/emojis/:emoji_id",
:delete
),
{},
audit_log_reason: reason
)
.wait
@available = false
self
end
end
alias destroy delete
#
# Converts the object to a hash.
# @private
#
# @return [Hash] The hash represents the object.
#
def to_hash
{ name: @name, id: @id, animated: @animated }
end
private
def _set_data(data)
@id = Snowflake.new(data[:id])
@name = data[:name]
@roles =
data[:role] ? data[:role].map { |r| Role.new(@client, guild, r) } : []
@user = User.new(@client, data[:user]) if data[:user]
@require_colons = data[:require_colons]
@managed = data[:managed]
@animated = data[:animated]
@available = data[:available]
@guild.emojis[@id] = self unless data[:no_cache]
@data.update(data)
end
end
#
# Represents a partial custom emoji in discord.
#
class PartialEmoji < DiscordModel
# @return [Discorb::Snowflake] The ID of the emoji.
attr_reader :id
# @return [String] The name of the emoji.
attr_reader :name
# @return [Boolean] Whether the emoji is deleted.
attr_reader :deleted
alias deleted? deleted
#
# Initialize a new partial custom emoji.
# @private
#
# @param [Hash] data The data of the emoji.
#
def initialize(data)
@id = Snowflake.new(data[:id])
@name = data[:name]
@animated = data[:animated]
@deleted = @name.nil?
end
#
# Format the emoji for URI.
#
# @return [String] the formatted emoji.
#
def to_uri
"#{@name}:#{@id}"
end
def inspect
"#<#{self.class} id=#{@id} :#{@name}:>"
end
#
# Format the emoji for sending.
#
# @return [String] the formatted emoji.
#
def to_s
"<#{@animated ? "a" : ""}:#{@name}:#{@id}>"
end
end
#
# Represents a unicode emoji (default emoji) in discord.
#
class UnicodeEmoji < Emoji
# @return [String] The name of the emoji. (e.g. :grinning:)
attr_reader :name
# @return [String] The unicode value of the emoji. (e.g. U+1F600)
attr_reader :value
# @return [Integer] The skin tone of the emoji.
attr_reader :skin_tone
#
# Initialize a new unicode emoji.
#
# @param [String] name The name of the emoji.
# @param [Integer] tone The skin tone of the emoji.
#
def initialize(name, tone: 0)
if EmojiTable::DISCORD_TO_UNICODE.key?(name)
@name = name
@value = EmojiTable::DISCORD_TO_UNICODE[name]
elsif EmojiTable::UNICODE_TO_DISCORD.key?(name)
@name = EmojiTable::UNICODE_TO_DISCORD[name][0]
@value = name
elsif EmojiTable::SKIN_TONES.any? { |t| name.include?(t) }
name2 = name.dup
EmojiTable::SKIN_TONES.each.with_index do |t, i|
next unless name2.include?(t)
@skin_tone = i
name2.sub!(t, "")
break
end
raise ArgumentError, "Invalid skin tone: #{tone}" unless @skin_tone
@name = EmojiTable::UNICODE_TO_DISCORD[name2].first
@value = name
else
raise ArgumentError, "No such emoji: #{name}"
end
if tone.positive?
unless @value = EmojiTable::DISCORD_TO_UNICODE["#{name}_tone#{tone}"]
raise ArgumentError, "Invalid skin tone for emoji: #{name}"
end
@name = "#{name}_tone#{tone}"
@skin_tone = tone
end
end
# @return [String] The unicode string of the emoji.
def to_s
@value
end
#
# Format the emoji for URI.
#
# @return [String] the formatted emoji.
#
def to_uri
URI.encode_www_form_component(@value)
end
def inspect
"#<#{self.class} :#{@name}:>"
end
#
# Converts the object to a hash.
# @private
#
# @return [Hash] The hash represents the object.
#
def to_hash
{ name: @value, id: nil, animated: false }
end
class << self
alias [] new
end
end
end