Async::DiscordSourceAsyncDiscordGatewayConnection

class GatewayConnection

Represents a gateway connection to Discord, which can be used to send and receive messages via a WebSocket connection.

Nested

Definitions

DISPATCH = 0

Gateway Opcodes:

ERROR_CODES = {...}

Gateway Error Codes.

Implementation

ERROR_CODES = {
	4000 => "UNKNOWN_ERROR",
	4001 => "UNKNOWN_OPCODE",
	4002 => "DECODE_ERROR",
	4003 => "NOT_AUTHENTICATED",
	4004 => "AUTHENTICATION_FAILED",
	4005 => "ALREADY_AUTHENTICATED",
	4007 => "INVALID_SEQUENCE",
	4008 => "RATE_LIMITED",
	4009 => "SESSION_TIMEOUT",
	4010 => "INVALID_SHARD",
	4011 => "SHARDING_REQUIRED",
	4012 => "INVALID_VERSION",
	4013 => "INVALID_INTENT",
	4014 => "DISALLOWED_INTENT"
}

DEFAULT_INTENT = Intent::GUILDS | Intent::GUILD_MESSAGES | Intent::DIRECT_MESSAGES

Default intent for a bot.

DEFAULT_PROPERTIES = {...}

Default properties for a bot.

Implementation

DEFAULT_PROPERTIES = {
	os: RUBY_PLATFORM,
	browser: Async::Discord.name,
	device: Async::Discord.name,
}

DEFAULT_PRESENCE = {...}

Default presence for a bot.

Implementation

DEFAULT_PRESENCE = {
	status: "online",
	afk: false,
	activities: [],
}

def initialize(...)

Initialize the gateway connection.

Implementation

def initialize(...)
	super
	
	@heartbeat_task = nil
	@sequence = nil
end

def close(...)

Close the gateway connection, including the heartbeat task.

Implementation

def close(...)
	if heartbeat_task = @heartbeat_task
		@heartbeat_task = nil
		heartbeat_task.stop
	end
	
	super
end

def identify(**identity)

Identify the bot with the given identity.

Signature

returns Hash

the payload from the READY event.

Implementation

def identify(**identity)
	while message = self.read
		payload = message.parse
		
		case payload[:op]
		when HELLO
			@heartbeat_task ||= self.run_heartbeat(payload[:d][:heartbeat_interval])
			break
		else
			Console.warn(self, "Unexpected payload during identify: #{payload}")
		end
	end
	
	identity[:intents] ||= DEFAULT_INTENT
	identity[:properties] ||= DEFAULT_PROPERTIES
	identity[:presence] ||= DEFAULT_PRESENCE
	
	Console.debug(self, "Identifying...", identity: identity)
	::Protocol::WebSocket::TextMessage.generate(op: IDENTIFY, d: identity).send(self)
	
	while message = self.read
		payload = message.parse
		
		if payload[:op] == DISPATCH && payload[:t] == "READY"
			Console.info(self, "Identified successfully.")
			
			# Store the sequence number for future heartbeats:
			@sequence = payload[:s]
			
			return payload[:d]
		elsif payload[:op] == INVALID_SESSION
			Console.warn(self, "Invalid session.")
			break
		else
			Console.warn(self, "Unexpected payload during identify: #{payload}")
		end
	end
end

def listen

Listen for events from the gateway.

Signature

yields {|payload| ...}
parameter payload Hash

The parsed payload.

Implementation

def listen
	while message = self.read
		payload = message.parse
		
		case payload[:op]
		when DISPATCH
			@sequence = payload[:s]
			yield payload
		when HEARTBEAT
			Console.debug(self, "Received heartbeat request.", payload: payload)
			heartbeat_message = ::Protocol::WebSocket::TextMessage.generate(op: HEARTBEAT_ACK, d: @sequence)
			heartbeat_message.send(self)
		when HEARTBEAT_ACK
			Console.debug(self, "Received heartbeat ACK.", payload: payload)
		else
			yield payload
		end
	end
end

def run_heartbeat(duration_ms)

Run a heartbeat task at the given interval.

Implementation

def run_heartbeat(duration_ms)
	duration = duration_ms / 1000.0
	Console.debug(self, "Running heartbeat every #{duration} seconds.")
	
	Async do |task|
		sleep(duration * rand)
		
		while !self.closed?
			Console.debug(self, "Sending heartbeat.", sequence: @sequence)
			heartbeat_message = ::Protocol::WebSocket::TextMessage.generate(op: HEARTBEAT, d: @sequence)
			heartbeat_message.send(self)
			self.flush
			
			sleep(duration)
		end
	end
end