From 42c50a784c3415f069dac414bb2f4fa1bbd5135d Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 26 Nov 2024 19:23:03 +1300 Subject: [PATCH] Documentation. --- lib/async/discord.rb | 15 +++++++++++++++ lib/async/discord/channels.rb | 20 ++++++++++++++++++++ lib/async/discord/client.rb | 12 ++++++++++++ lib/async/discord/gateway.rb | 27 +++++++++++++++++++++++++-- lib/async/discord/guilds.rb | 13 +++++++++++++ lib/async/discord/representation.rb | 2 ++ 6 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 lib/async/discord.rb diff --git a/lib/async/discord.rb b/lib/async/discord.rb new file mode 100644 index 0000000..3e50a9a --- /dev/null +++ b/lib/async/discord.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2024, by Samuel Williams. + +require_relative "discord/version" +require_relative "discord/client" + +# @namespace +module Async + # @namespace + module Discord + end +end + diff --git a/lib/async/discord/channels.rb b/lib/async/discord/channels.rb index 63d46be..fb93b8d 100644 --- a/lib/async/discord/channels.rb +++ b/lib/async/discord/channels.rb @@ -7,10 +7,15 @@ module Async module Discord + # Represents a message in a channel. class Message < Representation end + # Represents a channel in a guild. class Channel < Representation + # Send a message to this channel. + # + # @parameter content [String] The content of the message. def send_message(content) payload = { content: content @@ -19,20 +24,32 @@ def send_message(content) Message.post(@resource.with(path: "messages"), payload) end + # The unique identifier for this channel. def id self.value[:id] end + # Whether this channel is a text channel. + # + # @returns [Boolean] if this channel is a text channel. def text? self.value[:type] == 0 end + # Whether this channel is a voice channel. + # + # @returns [Boolean] if this channel is a voice channel. def voice? self.value[:type] == 2 end end + # Represents a collection of channels. class Channels < Representation + # Enumerate over each channel. + # + # @yields {|channel| ...} + # @parameter channel [Channel] The channel. def each(&block) return to_enum unless block_given? @@ -43,6 +60,9 @@ def each(&block) end end + # Convert this collection to an array. + # + # @returns [Array(Channel)] an array of channels. def to_a each.to_a end diff --git a/lib/async/discord/client.rb b/lib/async/discord/client.rb index 38896d5..81c8ef9 100644 --- a/lib/async/discord/client.rb +++ b/lib/async/discord/client.rb @@ -10,10 +10,19 @@ module Async module Discord + # A client for interacting with Discord. class Client < Async::REST::Resource + # The default endpoint for Discord. ENDPOINT = Async::HTTP::Endpoint.parse("https://discord.com/api/v10/") + + # The default user agent for this client. USER_AGENT = "#{self.name} (https://github.com/socketry/async-discord, v#{Async::Discord::VERSION})" + # Authenticate the client, either with a bot or bearer token. + # + # @parameter bot [String] The bot token. + # @parameter bearer [String] The bearer token. + # @returns [Client] a new client with the given authentication. def authenticated(bot: nil, bearer: nil) headers = {} @@ -30,14 +39,17 @@ def authenticated(bot: nil, bearer: nil) return self.with(headers: headers) end + # @returns [Guilds] a collection of guilds the bot is a member of. def guilds Guilds.new(self.with(path: "users/@me/guilds")) end + # @returns [Gateway] the gateway for the bot. def gateway Gateway.new(self.with(path: "gateway/bot")) end + # @returns [Channel] a channel by its unique identifier. def channel(id) Channel.new(self.with(path: "channels/#{id}")) end diff --git a/lib/async/discord/gateway.rb b/lib/async/discord/gateway.rb index 80860c7..b36306b 100644 --- a/lib/async/discord/gateway.rb +++ b/lib/async/discord/gateway.rb @@ -9,6 +9,7 @@ module Async module Discord + # Represents a gateway connection to Discord, which can be used to send and receive messages via a WebSocket connection. class GatewayConnection < Async::WebSocket::Connection # Gateway Opcodes: DISPATCH = 0 @@ -24,7 +25,7 @@ class GatewayConnection < Async::WebSocket::Connection HEARTBEAT_ACK = 11 REQUEST_SOUNDBOARD_SOUNDS = 31 - # Gateway Error Codes + # Gateway Error Codes. ERROR_CODES = { 4000 => "UNKNOWN_ERROR", 4001 => "UNKNOWN_OPCODE", @@ -42,7 +43,7 @@ class GatewayConnection < Async::WebSocket::Connection 4014 => "DISALLOWED_INTENT" } - # Guild Intents: + # Guild Intents. module Intent GUILDS = 1 << 0 GUILD_MEMBERS = 1 << 1 @@ -67,20 +68,24 @@ module Intent DIRECT_MESSAGE_POLLS = 1 << 25 end + # Default intent for a bot. DEFAULT_INTENT = Intent::GUILDS | Intent::GUILD_MESSAGES | Intent::DIRECT_MESSAGES + # Default properties for a bot. DEFAULT_PROPERTIES = { os: RUBY_PLATFORM, browser: Async::Discord.name, device: Async::Discord.name, } + # Default presence for a bot. DEFAULT_PRESENCE = { status: "online", afk: false, activities: [], } + # Initialize the gateway connection. def initialize(...) super @@ -88,6 +93,7 @@ def initialize(...) @sequence = nil end + # Close the gateway connection, including the heartbeat task. def close(...) if heartbeat_task = @heartbeat_task @heartbeat_task = nil @@ -97,6 +103,9 @@ def close(...) super end + # Identify the bot with the given identity. + # + # @returns [Hash] the payload from the READY event. def identify(**identity) while message = self.read payload = message.parse @@ -136,6 +145,10 @@ def identify(**identity) end end + # Listen for events from the gateway. + # + # @yields {|payload| ...} + # @parameter payload [Hash] The parsed payload. def listen while message = self.read payload = message.parse @@ -158,6 +171,7 @@ def listen private + # Run a heartbeat task at the given interval. def run_heartbeat(duration_ms) duration = duration_ms / 1000.0 Console.debug(self, "Running heartbeat every #{duration} seconds.") @@ -177,19 +191,28 @@ def run_heartbeat(duration_ms) end end + # Represents a gateway for the bot. class Gateway < Representation + # The URL of the gateway, used for connecting to the WebSocket server. def url self.value[:url] end + # The number of shards to use. def shards self.value[:shards] end + def session_start_limit self.value[:session_start_limit] end + # Connect to the gateway, yielding the connection. + # + # @yields {|connection| ...} if a block is given. + # @parameter connection [GatewayConnection] The connection to the gateway. + # @returns [GatewayConnection] the connection to the gateway. def connect(shard: nil, &block) endpoint = Async::HTTP::Endpoint.parse(self.url, alpn_protocols: Async::HTTP::Protocol::HTTP11.names) diff --git a/lib/async/discord/guilds.rb b/lib/async/discord/guilds.rb index a0e230a..bc6fc5c 100644 --- a/lib/async/discord/guilds.rb +++ b/lib/async/discord/guilds.rb @@ -8,17 +8,26 @@ module Async module Discord + # Represents a guild in Discord. class Guild < Representation + # @returns [Channels] a collection of channels in this guild. def channels Channels.new(@resource.with(path: "channels")) end + # The unique identifier for this guild. def id self.value[:id] end end + # Represents a collection of guilds. class Guilds < Representation + # Enumerate over each guild. + # + # @yields {|guild| ...} if a block is given. + # @parameter guild [Guild] The guild. + # @returns [Enumerator] if no block is given. def each(&block) return to_enum unless block_given? @@ -29,10 +38,14 @@ def each(&block) end end + # Convert the collection to an array. + # + # @returns [Array(Guild)] the collection as an array. def to_a each.to_a end + # @returns [Boolean] if the collection is empty. def empty? self.value.empty? end diff --git a/lib/async/discord/representation.rb b/lib/async/discord/representation.rb index fc2095f..e31cd42 100644 --- a/lib/async/discord/representation.rb +++ b/lib/async/discord/representation.rb @@ -8,9 +8,11 @@ module Async module Discord + # The default wrapper for Discord. class Wrapper < Async::REST::Wrapper::JSON end + # The default representation for Discord. class Representation < Async::REST::Representation[Wrapper] end end