discorb-lib/discorb

View on GitHub
lib/discorb/embed.rb

Summary

Maintainability
B
4 hrs
Test Coverage
# frozen_string_literal: true

module Discorb
  #
  # Represents an embed of discord.
  #
  class Embed
    # @return [String, nil] The title of embed.
    attr_accessor :title
    # @return [String, nil] The description of embed.
    attr_accessor :description
    # @return [String, nil] The url of embed.
    attr_accessor :url
    # @return [Time, nil] The timestamp of embed.
    attr_accessor :timestamp
    # @return [Discorb::Color, nil] The color of embed.
    attr_accessor :color
    # @return [Discorb::Embed::Author, nil] The author of embed.
    attr_accessor :author
    # @return [Array<Discorb::Embed::Field>] The fields of embed.
    attr_accessor :fields
    # @return [Discorb::Embed::Footer, nil] The footer of embed.
    attr_accessor :footer
    # @return [Symbol] The type of embed.
    attr_reader :type
    attr_reader :image, :thumbnail

    # @!attribute [rw] image
    #   @return [Discorb::Embed::Image] The image of embed.
    # @!attribute [rw] thumbnail
    #   @return [Discorb::Embed::Thumbnail] The thumbnail of embed.

    #
    # Initialize a new Embed object.
    #
    # @param [String] title The title of embed.
    # @param [String] description The description of embed.
    # @param [Discorb::Color, Integer] color The color of embed.
    # @param [String] url The url of embed.
    # @param [Time] timestamp The timestamp of embed.
    # @param [Discorb::Embed::Author] author The author field of embed.
    # @param [Array<Discorb::Embed::Field>] fields The fields of embed.
    # @param [Discorb::Embed::Footer] footer The footer of embed.
    # @param [Discorb::Embed::Image, String] image The image of embed.
    # @param [Discorb::Embed::Thumbnail, String] thumbnail The thumbnail of embed.
    #
    def initialize(
      title = nil,
      description = nil,
      color: nil,
      url: nil,
      timestamp: nil,
      author: nil,
      fields: nil,
      footer: nil,
      image: nil,
      thumbnail: nil
    )
      @title = title
      @description = description
      @url = url
      @timestamp = timestamp
      @color = color && (color.is_a?(Color) ? color : Color.new(color))
      @author = author
      @fields = fields || []
      @footer = footer
      @image = image && (image.is_a?(String) ? Image.new(image) : image)
      @thumbnail =
        thumbnail &&
          (thumbnail.is_a?(String) ? Thumbnail.new(thumbnail) : thumbnail)
      @type = :rich
    end

    #
    # Initialize embed from hash.
    # @private
    #
    # @param [Hash] data The hash data to initialize embed.
    #
    def initialize_hash(data)
      @title = data[:title]
      @description = data[:description]
      @url = data[:url]
      @timestamp = data[:timestamp] && Time.iso8601(data[:timestamp])
      @type = data[:type]
      @color = data[:color] && Color.new(data[:color])
      @footer =
        data[:footer] &&
          Footer.new(data[:footer][:text], icon: data[:footer][:icon_url])
      @author =
        if data[:author]
          Author.new(
            data[:author][:name],
            icon: data[:author][:icon_url],
            url: data[:author][:url]
          )
        end
      @thumbnail = data[:thumbnail] && Thumbnail.new(data[:thumbnail])
      @image = data[:image] && Image.new(data[:image])
      @video = data[:video] && Video.new(data[:video])
      @provider = data[:provider] && Provider.new(data[:provider])
      @fields =
        (
          if data[:fields]
            data[:fields].map do |f|
              Field.new(f[:name], f[:value], inline: f[:inline])
            end
          else
            []
          end
        )
    end

    def image=(value)
      @image = value.is_a?(String) ? Image.new(value) : value
    end

    def thumbnail=(value)
      @thumbnail = value.is_a?(String) ? Thumbnail.new(value) : value
    end

    def inspect
      "#<#{self.class} \"#{@title}\">"
    end

    #
    # Convert embed to hash.
    #
    # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-structure Offical Discord API Docs
    # @return [Hash] Converted embed.
    #
    def to_hash
      # @type var ret: Hash[untyped, untyped]
      ret = { type: "rich" }
      ret[:title] = @title if @title
      ret[:description] = @description if @description
      ret[:url] = @url if @url
      ret[:timestamp] = @timestamp&.iso8601 if @timestamp
      ret[:color] = @color&.to_i if @color
      ret[:footer] = @footer&.to_hash if @footer
      ret[:image] = @image&.to_hash if @image
      ret[:thumbnail] = @thumbnail&.to_hash if @thumbnail
      ret[:author] = @author&.to_hash if @author
      ret[:fields] = @fields&.map(&:to_hash) if @fields.any?
      ret
    end

    def self.from_hash(data)
      inst = allocate
      inst.initialize_hash(data)
      inst
    end

    #
    # Represents an entry in embed.
    # @abstract
    # @private
    #
    class Entry
      def inspect
        "#<#{self.class}>"
      end
    end

    #
    # Represents an author of embed.
    #
    class Author < Entry
      # @return [String] The name of author.
      attr_accessor :name
      # @return [String, nil] The url of author.
      attr_accessor :url
      # @return [String, nil] The icon url of author.
      attr_accessor :icon

      #
      # Initialize a new Author object.
      #
      # @param [String] name The name of author.
      # @param [String] url The url of author.
      # @param [String] icon The icon url of author.
      #
      def initialize(name, url: nil, icon: nil)
        @name = name
        @url = url
        @icon = icon
      end

      #
      # Convert author to hash.
      #
      # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure
      #   Offical Discord API Docs
      # @return [Hash] Converted author.
      #
      def to_hash
        { name: @name, url: @url, icon_url: @icon }
      end
    end

    #
    # Represemts a footer of embed.
    #
    class Footer < Entry
      attr_accessor :text, :icon

      #
      # Initialize a new Footer object.
      #
      # @param [String] text The text of footer.
      # @param [String] icon The icon url of footer.
      #
      def initialize(text, icon: nil)
        @text = text
        @icon = icon
      end

      #
      # Convert footer to hash.
      #
      # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure
      #   Offical Discord API Docs
      # @return [Hash] Converted footer.
      #
      def to_hash
        { text: @text, icon_url: @icon }
      end
    end

    #
    # Represents a field of embed.
    #
    class Field < Entry
      # @return [String] The name of field.
      attr_accessor :name
      # @return [String] The value of field.
      attr_accessor :value
      # @return [Boolean] Whether the field is inline.
      attr_accessor :inline

      #
      # Initialize a new Field object.
      #
      # @param [String] name The name of field.
      # @param [String] value The value of field.
      # @param [Boolean] inline Whether the field is inline.
      #
      def initialize(name, value, inline: true)
        @name = name
        @value = value
        @inline = inline
      end

      #
      # Convert field to hash.
      #
      # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure
      #   Offical Discord API Docs
      # @return [Hash] Converted field.
      #
      def to_hash
        { name: @name, value: @value, inline: @inline }
      end
    end

    #
    # Represents an image of embed.
    #
    class Image < Entry
      # @return [String] The url of image.
      attr_accessor :url
      # @return [String] The proxy url of image.
      # @return [nil] The Image object wasn't created from gateway.
      attr_reader :proxy_url
      # @return [Integer] The height of image.
      # @return [nil] The Image object wasn't created from gateway.
      attr_reader :height
      # @return [Integer] The width of image.
      # @return [nil] The Image object wasn't created from gateway.
      attr_reader :width

      #
      # Initialize a new Image object.
      #
      # @param [String] url URL of image.
      #
      def initialize(url)
        data = url
        if data.is_a? String
          @url = data
        else
          @url = data[:url]
          @proxy_url = data[:proxy_url]
          @height = data[:height]
          @width = data[:width]
        end
      end

      #
      # Convert image to hash for sending.
      #
      # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure
      #   Offical Discord API Docs
      # @return [Hash] Converted image.
      #
      def to_hash
        { url: @url }
      end
    end

    #
    # Represents a thumbnail of embed.
    #
    class Thumbnail < Entry
      # @return [String] The url of thumbnail.
      attr_accessor :url
      # @return [String] The proxy url of thumbnail.
      # @return [nil] The Thumbnail object wasn't created from gateway.
      attr_reader :proxy_url
      # @return [Integer] The height of thumbnail.
      # @return [nil] The Thumbnail object wasn't created from gateway.
      attr_reader :height
      # @return [Integer] The width of thumbnail.
      # @return [nil] The Thumbnail object wasn't created from gateway.
      attr_reader :width

      #
      # Initialize a new Thumbnail object.
      #
      # @param [String] url URL of thumbnail.
      #
      def initialize(url)
        data = url
        if data.is_a? String
          @url = data
        else
          @url = data[:url]
          @proxy_url = data[:proxy_url]
          @height = data[:height]
          @width = data[:width]
        end
      end

      #
      # Convert thumbnail to hash for sending.
      #
      # @see https://discord.com/developers/docs/resources/channel#embed-object-embed-thumbnail-structure
      #   Offical Discord API Docs
      # @return [Hash] Converted thumbnail.
      #
      def to_hash
        { url: @url }
      end
    end

    #
    # Represents a video of embed.
    #
    class Video < Entry
      # @return [String] The url of video.
      attr_reader :url
      # @return [String] The proxy url of video.
      attr_reader :proxy_url
      # @return [Integer] The height of video.
      attr_reader :height
      # @return [Integer] The width of video.
      attr_reader :width

      #
      # Initialize a new Video object.
      # @private
      #
      # @param [Hash] data The data of video.
      #
      def initialize(data)
        @url = data[:url]
        @proxy_url = data[:proxy_url]
        @height = data[:height]
        @width = data[:width]
      end
    end

    #
    # Represents a provider of embed.
    #
    class Provider < Entry
      # @return [String] The name of provider.
      attr_reader :name
      # @return [String] The url of provider.
      attr_reader :url

      #
      # Initialize a new Provider object.
      # @private
      #
      # @param [Hash] data The data of provider.
      #
      def initialize(data)
        @name = data[:name]
        @url = data[:url]
      end
    end
  end
end