Concrete class prototype
This commit is contained in:
246
src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs
Normal file
246
src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
#pragma warning disable CS1591
|
||||
using Discord.API.Gateway;
|
||||
using Discord.API.Rest;
|
||||
using Discord.Net.Queue;
|
||||
using Discord.Net.Rest;
|
||||
using Discord.Net.WebSockets;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.API
|
||||
{
|
||||
public class DiscordSocketApiClient : DiscordRestApiClient
|
||||
{
|
||||
public event Func<GatewayOpCode, Task> SentGatewayMessage { add { _sentGatewayMessageEvent.Add(value); } remove { _sentGatewayMessageEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<GatewayOpCode, Task>> _sentGatewayMessageEvent = new AsyncEvent<Func<GatewayOpCode, Task>>();
|
||||
public event Func<GatewayOpCode, int?, string, object, Task> ReceivedGatewayEvent { add { _receivedGatewayEvent.Add(value); } remove { _receivedGatewayEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<GatewayOpCode, int?, string, object, Task>> _receivedGatewayEvent = new AsyncEvent<Func<GatewayOpCode, int?, string, object, Task>>();
|
||||
|
||||
public event Func<Exception, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>();
|
||||
|
||||
private readonly IWebSocketClient _gatewayClient;
|
||||
private CancellationTokenSource _connectCancelToken;
|
||||
private string _gatewayUrl;
|
||||
|
||||
public ConnectionState ConnectionState { get; private set; }
|
||||
|
||||
public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, JsonSerializer serializer = null, RequestQueue requestQueue = null)
|
||||
: base(restClientProvider, serializer, requestQueue)
|
||||
{
|
||||
_gatewayClient = webSocketProvider();
|
||||
//_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+)
|
||||
_gatewayClient.BinaryMessage += async (data, index, count) =>
|
||||
{
|
||||
using (var compressed = new MemoryStream(data, index + 2, count - 2))
|
||||
using (var decompressed = new MemoryStream())
|
||||
{
|
||||
using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress))
|
||||
zlib.CopyTo(decompressed);
|
||||
decompressed.Position = 0;
|
||||
using (var reader = new StreamReader(decompressed))
|
||||
using (var jsonReader = new JsonTextReader(reader))
|
||||
{
|
||||
var msg = _serializer.Deserialize<WebSocketMessage>(jsonReader);
|
||||
await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
_gatewayClient.TextMessage += async text =>
|
||||
{
|
||||
using (var reader = new StringReader(text))
|
||||
using (var jsonReader = new JsonTextReader(reader))
|
||||
{
|
||||
var msg = _serializer.Deserialize<WebSocketMessage>(jsonReader);
|
||||
await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
|
||||
}
|
||||
};
|
||||
_gatewayClient.Closed += async ex =>
|
||||
{
|
||||
await DisconnectAsync().ConfigureAwait(false);
|
||||
await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
|
||||
};
|
||||
}
|
||||
internal override void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_connectCancelToken?.Dispose();
|
||||
(_gatewayClient as IDisposable)?.Dispose();
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ConnectAsync()
|
||||
{
|
||||
await _stateLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await ConnectInternalAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally { _stateLock.Release(); }
|
||||
}
|
||||
internal override async Task ConnectInternalAsync()
|
||||
{
|
||||
if (LoginState != LoginState.LoggedIn)
|
||||
throw new InvalidOperationException("You must log in before connecting.");
|
||||
if (_gatewayClient == null)
|
||||
throw new NotSupportedException("This client is not configured with websocket support.");
|
||||
|
||||
ConnectionState = ConnectionState.Connecting;
|
||||
try
|
||||
{
|
||||
_connectCancelToken = new CancellationTokenSource();
|
||||
if (_gatewayClient != null)
|
||||
_gatewayClient.SetCancelToken(_connectCancelToken.Token);
|
||||
|
||||
if (_gatewayUrl == null)
|
||||
{
|
||||
var gatewayResponse = await GetGatewayAsync().ConfigureAwait(false);
|
||||
_gatewayUrl = $"{gatewayResponse.Url}?v={DiscordConfig.APIVersion}&encoding={DiscordSocketConfig.GatewayEncoding}";
|
||||
}
|
||||
await _gatewayClient.ConnectAsync(_gatewayUrl).ConfigureAwait(false);
|
||||
|
||||
ConnectionState = ConnectionState.Connected;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_gatewayUrl = null; //Uncache in case the gateway url changed
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
await _stateLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally { _stateLock.Release(); }
|
||||
}
|
||||
public async Task DisconnectAsync(Exception ex)
|
||||
{
|
||||
await _stateLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally { _stateLock.Release(); }
|
||||
}
|
||||
internal override async Task DisconnectInternalAsync()
|
||||
{
|
||||
if (_gatewayClient == null)
|
||||
throw new NotSupportedException("This client is not configured with websocket support.");
|
||||
|
||||
if (ConnectionState == ConnectionState.Disconnected) return;
|
||||
ConnectionState = ConnectionState.Disconnecting;
|
||||
|
||||
try { _connectCancelToken?.Cancel(false); }
|
||||
catch { }
|
||||
|
||||
await _gatewayClient.DisconnectAsync().ConfigureAwait(false);
|
||||
|
||||
ConnectionState = ConnectionState.Disconnected;
|
||||
}
|
||||
|
||||
//Core
|
||||
private async Task SendGatewayInternalAsync(GatewayOpCode opCode, object payload,
|
||||
BucketGroup group, int bucketId, ulong guildId, RequestOptions options)
|
||||
{
|
||||
CheckState();
|
||||
|
||||
//TODO: Add ETF
|
||||
byte[] bytes = null;
|
||||
payload = new WebSocketMessage { Operation = (int)opCode, Payload = payload };
|
||||
if (payload != null)
|
||||
bytes = Encoding.UTF8.GetBytes(SerializeJson(payload));
|
||||
await RequestQueue.SendAsync(new WebSocketRequest(_gatewayClient, bytes, true, options), group, bucketId, guildId).ConfigureAwait(false);
|
||||
await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//Gateway
|
||||
public Task SendGatewayAsync(GatewayOpCode opCode, object payload,
|
||||
GlobalBucket bucket = GlobalBucket.GeneralGateway, RequestOptions options = null)
|
||||
=> SendGatewayInternalAsync(opCode, payload, BucketGroup.Global, (int)bucket, 0, options);
|
||||
|
||||
public Task SendGatewayAsync(GatewayOpCode opCode, object payload,
|
||||
GuildBucket bucket, ulong guildId, RequestOptions options = null)
|
||||
=> SendGatewayInternalAsync(opCode, payload, BucketGroup.Guild, (int)bucket, guildId, options);
|
||||
|
||||
public async Task<GetGatewayResponse> GetGatewayAsync(RequestOptions options = null)
|
||||
{
|
||||
return await SendAsync<GetGatewayResponse>("GET", "gateway", options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendIdentifyAsync(int largeThreshold = 100, bool useCompression = true, int shardID = 0, int totalShards = 1, RequestOptions options = null)
|
||||
{
|
||||
var props = new Dictionary<string, string>
|
||||
{
|
||||
["$device"] = "Discord.Net"
|
||||
};
|
||||
var msg = new IdentifyParams()
|
||||
{
|
||||
Token = _authToken,
|
||||
Properties = props,
|
||||
LargeThreshold = largeThreshold,
|
||||
UseCompression = useCompression,
|
||||
};
|
||||
if (totalShards > 1)
|
||||
msg.ShardingParams = new int[] { shardID, totalShards };
|
||||
|
||||
await SendGatewayAsync(GatewayOpCode.Identify, msg, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null)
|
||||
{
|
||||
var msg = new ResumeParams()
|
||||
{
|
||||
Token = _authToken,
|
||||
SessionId = sessionId,
|
||||
Sequence = lastSeq
|
||||
};
|
||||
await SendGatewayAsync(GatewayOpCode.Resume, msg, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendHeartbeatAsync(int lastSeq, RequestOptions options = null)
|
||||
{
|
||||
await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendStatusUpdateAsync(long? idleSince, Game game, RequestOptions options = null)
|
||||
{
|
||||
var args = new StatusUpdateParams
|
||||
{
|
||||
IdleSince = idleSince,
|
||||
Game = game
|
||||
};
|
||||
await SendGatewayAsync(GatewayOpCode.StatusUpdate, args, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendRequestMembersAsync(IEnumerable<ulong> guildIds, RequestOptions options = null)
|
||||
{
|
||||
await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null)
|
||||
{
|
||||
var payload = new VoiceStateUpdateParams
|
||||
{
|
||||
GuildId = guildId,
|
||||
ChannelId = channelId,
|
||||
SelfDeaf = selfDeaf,
|
||||
SelfMute = selfMute
|
||||
};
|
||||
await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, payload, options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendGuildSyncAsync(IEnumerable<ulong> guildIds, RequestOptions options = null)
|
||||
{
|
||||
await SendGatewayAsync(GatewayOpCode.GuildSync, guildIds, options: options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
259
src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs
Normal file
259
src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
#pragma warning disable CS1591
|
||||
using Discord.API;
|
||||
using Discord.API.Voice;
|
||||
using Discord.Net.Converters;
|
||||
using Discord.Net.WebSockets;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
|
||||
namespace Discord.Audio
|
||||
{
|
||||
public class DiscordVoiceAPIClient
|
||||
{
|
||||
public const int MaxBitrate = 128;
|
||||
public const string Mode = "xsalsa20_poly1305";
|
||||
|
||||
public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<string, string, double, Task>> _sentRequestEvent = new AsyncEvent<Func<string, string, double, Task>>();
|
||||
public event Func<VoiceOpCode, Task> SentGatewayMessage { add { _sentGatewayMessageEvent.Add(value); } remove { _sentGatewayMessageEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<VoiceOpCode, Task>> _sentGatewayMessageEvent = new AsyncEvent<Func<VoiceOpCode, Task>>();
|
||||
public event Func<Task> SentDiscovery { add { _sentDiscoveryEvent.Add(value); } remove { _sentDiscoveryEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<Task>> _sentDiscoveryEvent = new AsyncEvent<Func<Task>>();
|
||||
public event Func<int, Task> SentData { add { _sentDataEvent.Add(value); } remove { _sentDataEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<int, Task>> _sentDataEvent = new AsyncEvent<Func<int, Task>>();
|
||||
|
||||
public event Func<VoiceOpCode, object, Task> ReceivedEvent { add { _receivedEvent.Add(value); } remove { _receivedEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<VoiceOpCode, object, Task>> _receivedEvent = new AsyncEvent<Func<VoiceOpCode, object, Task>>();
|
||||
public event Func<byte[], Task> ReceivedPacket { add { _receivedPacketEvent.Add(value); } remove { _receivedPacketEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<byte[], Task>> _receivedPacketEvent = new AsyncEvent<Func<byte[], Task>>();
|
||||
public event Func<Exception, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>();
|
||||
|
||||
private readonly JsonSerializer _serializer;
|
||||
private readonly IWebSocketClient _webSocketClient;
|
||||
private readonly SemaphoreSlim _connectionLock;
|
||||
private CancellationTokenSource _connectCancelToken;
|
||||
private UdpClient _udp;
|
||||
private IPEndPoint _udpEndpoint;
|
||||
private Task _udpRecieveTask;
|
||||
private bool _isDisposed;
|
||||
|
||||
public ulong GuildId { get; }
|
||||
public ConnectionState ConnectionState { get; private set; }
|
||||
|
||||
internal DiscordVoiceAPIClient(ulong guildId, WebSocketProvider webSocketProvider, JsonSerializer serializer = null)
|
||||
{
|
||||
GuildId = guildId;
|
||||
_connectionLock = new SemaphoreSlim(1, 1);
|
||||
_udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
|
||||
|
||||
_webSocketClient = webSocketProvider();
|
||||
//_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+)
|
||||
_webSocketClient.BinaryMessage += async (data, index, count) =>
|
||||
{
|
||||
using (var compressed = new MemoryStream(data, index + 2, count - 2))
|
||||
using (var decompressed = new MemoryStream())
|
||||
{
|
||||
using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress))
|
||||
zlib.CopyTo(decompressed);
|
||||
decompressed.Position = 0;
|
||||
using (var reader = new StreamReader(decompressed))
|
||||
{
|
||||
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(reader.ReadToEnd());
|
||||
await _receivedEvent.InvokeAsync((VoiceOpCode)msg.Operation, msg.Payload).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
_webSocketClient.TextMessage += async text =>
|
||||
{
|
||||
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(text);
|
||||
await _receivedEvent.InvokeAsync((VoiceOpCode)msg.Operation, msg.Payload).ConfigureAwait(false);
|
||||
};
|
||||
_webSocketClient.Closed += async ex =>
|
||||
{
|
||||
await DisconnectAsync().ConfigureAwait(false);
|
||||
await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
|
||||
};
|
||||
|
||||
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
|
||||
}
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_connectCancelToken?.Dispose();
|
||||
(_webSocketClient as IDisposable)?.Dispose();
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
public async Task SendAsync(VoiceOpCode opCode, object payload, RequestOptions options = null)
|
||||
{
|
||||
byte[] bytes = null;
|
||||
payload = new WebSocketMessage { Operation = (int)opCode, Payload = payload };
|
||||
if (payload != null)
|
||||
bytes = Encoding.UTF8.GetBytes(SerializeJson(payload));
|
||||
await _webSocketClient.SendAsync(bytes, 0, bytes.Length, true).ConfigureAwait(false);
|
||||
await _sentGatewayMessageEvent.InvokeAsync(opCode);
|
||||
}
|
||||
public async Task SendAsync(byte[] data, int bytes)
|
||||
{
|
||||
if (_udpEndpoint != null)
|
||||
{
|
||||
await _udp.SendAsync(data, bytes, _udpEndpoint).ConfigureAwait(false);
|
||||
await _sentDataEvent.InvokeAsync(bytes).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//WebSocket
|
||||
public async Task SendHeartbeatAsync(RequestOptions options = null)
|
||||
{
|
||||
await SendAsync(VoiceOpCode.Heartbeat, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), options: options).ConfigureAwait(false);
|
||||
}
|
||||
public async Task SendIdentityAsync(ulong userId, string sessionId, string token)
|
||||
{
|
||||
await SendAsync(VoiceOpCode.Identify, new IdentifyParams
|
||||
{
|
||||
GuildId = GuildId,
|
||||
UserId = userId,
|
||||
SessionId = sessionId,
|
||||
Token = token
|
||||
});
|
||||
}
|
||||
public async Task SendSelectProtocol(string externalIp, int externalPort)
|
||||
{
|
||||
await SendAsync(VoiceOpCode.SelectProtocol, new SelectProtocolParams
|
||||
{
|
||||
Protocol = "udp",
|
||||
Data = new UdpProtocolInfo
|
||||
{
|
||||
Address = externalIp,
|
||||
Port = externalPort,
|
||||
Mode = Mode
|
||||
}
|
||||
});
|
||||
}
|
||||
public async Task SendSetSpeaking(bool value)
|
||||
{
|
||||
await SendAsync(VoiceOpCode.Speaking, new SpeakingParams
|
||||
{
|
||||
IsSpeaking = value,
|
||||
Delay = 0
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ConnectAsync(string url)
|
||||
{
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await ConnectInternalAsync(url).ConfigureAwait(false);
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
}
|
||||
private async Task ConnectInternalAsync(string url)
|
||||
{
|
||||
ConnectionState = ConnectionState.Connecting;
|
||||
try
|
||||
{
|
||||
_connectCancelToken = new CancellationTokenSource();
|
||||
_webSocketClient.SetCancelToken(_connectCancelToken.Token);
|
||||
await _webSocketClient.ConnectAsync(url).ConfigureAwait(false);
|
||||
_udpRecieveTask = ReceiveAsync(_connectCancelToken.Token);
|
||||
|
||||
ConnectionState = ConnectionState.Connected;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
}
|
||||
private async Task DisconnectInternalAsync()
|
||||
{
|
||||
if (ConnectionState == ConnectionState.Disconnected) return;
|
||||
ConnectionState = ConnectionState.Disconnecting;
|
||||
|
||||
try { _connectCancelToken?.Cancel(false); }
|
||||
catch { }
|
||||
|
||||
//Wait for tasks to complete
|
||||
await _udpRecieveTask.ConfigureAwait(false);
|
||||
|
||||
await _webSocketClient.DisconnectAsync().ConfigureAwait(false);
|
||||
|
||||
ConnectionState = ConnectionState.Disconnected;
|
||||
}
|
||||
|
||||
//Udp
|
||||
public async Task SendDiscoveryAsync(uint ssrc)
|
||||
{
|
||||
var packet = new byte[70];
|
||||
packet[0] = (byte)(ssrc >> 24);
|
||||
packet[1] = (byte)(ssrc >> 16);
|
||||
packet[2] = (byte)(ssrc >> 8);
|
||||
packet[3] = (byte)(ssrc >> 0);
|
||||
await SendAsync(packet, 70).ConfigureAwait(false);
|
||||
await _sentDiscoveryEvent.InvokeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void SetUdpEndpoint(IPEndPoint endpoint)
|
||||
{
|
||||
_udpEndpoint = endpoint;
|
||||
}
|
||||
private async Task ReceiveAsync(CancellationToken cancelToken)
|
||||
{
|
||||
var closeTask = Task.Delay(-1, cancelToken);
|
||||
while (!cancelToken.IsCancellationRequested)
|
||||
{
|
||||
var receiveTask = _udp.ReceiveAsync();
|
||||
var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false);
|
||||
if (task == closeTask)
|
||||
break;
|
||||
|
||||
await _receivedPacketEvent.InvokeAsync(receiveTask.Result.Buffer).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
//Helpers
|
||||
private static double ToMilliseconds(Stopwatch stopwatch) => Math.Round((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0, 2);
|
||||
private string SerializeJson(object value)
|
||||
{
|
||||
var sb = new StringBuilder(256);
|
||||
using (TextWriter text = new StringWriter(sb, CultureInfo.InvariantCulture))
|
||||
using (JsonWriter writer = new JsonTextWriter(text))
|
||||
_serializer.Serialize(writer, value);
|
||||
return sb.ToString();
|
||||
}
|
||||
private T DeserializeJson<T>(Stream jsonStream)
|
||||
{
|
||||
using (TextReader text = new StreamReader(jsonStream))
|
||||
using (JsonReader reader = new JsonTextReader(text))
|
||||
return _serializer.Deserialize<T>(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/Discord.Net.WebSocket/API/Gateway/ExtendedGuild.cs
Normal file
25
src/Discord.Net.WebSocket/API/Gateway/ExtendedGuild.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class ExtendedGuild : Guild
|
||||
{
|
||||
[JsonProperty("unavailable")]
|
||||
public bool? Unavailable { get; set; }
|
||||
[JsonProperty("member_count")]
|
||||
public int MemberCount { get; set; }
|
||||
[JsonProperty("large")]
|
||||
public bool Large { get; set; }
|
||||
|
||||
[JsonProperty("presences")]
|
||||
public Presence[] Presences { get; set; }
|
||||
[JsonProperty("members")]
|
||||
public GuildMember[] Members { get; set; }
|
||||
[JsonProperty("channels")]
|
||||
public Channel[] Channels { get; set; }
|
||||
[JsonProperty("joined_at")]
|
||||
public DateTimeOffset JoinedAt { get; set; }
|
||||
}
|
||||
}
|
||||
33
src/Discord.Net.WebSocket/API/Gateway/GatewayOpCode.cs
Normal file
33
src/Discord.Net.WebSocket/API/Gateway/GatewayOpCode.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma warning disable CS1591
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public enum GatewayOpCode : byte
|
||||
{
|
||||
/// <summary> C←S - Used to send most events. </summary>
|
||||
Dispatch = 0,
|
||||
/// <summary> C↔S - Used to keep the connection alive and measure latency. </summary>
|
||||
Heartbeat = 1,
|
||||
/// <summary> C→S - Used to associate a connection with a token and specify configuration. </summary>
|
||||
Identify = 2,
|
||||
/// <summary> C→S - Used to update client's status and current game id. </summary>
|
||||
StatusUpdate = 3,
|
||||
/// <summary> C→S - Used to join a particular voice channel. </summary>
|
||||
VoiceStateUpdate = 4,
|
||||
/// <summary> C→S - Used to ensure the guild's voice server is alive. </summary>
|
||||
VoiceServerPing = 5,
|
||||
/// <summary> C→S - Used to resume a connection after a redirect occurs. </summary>
|
||||
Resume = 6,
|
||||
/// <summary> C←S - Used to notify a client that they must reconnect to another gateway. </summary>
|
||||
Reconnect = 7,
|
||||
/// <summary> C→S - Used to request members that were withheld by large_threshold </summary>
|
||||
RequestGuildMembers = 8,
|
||||
/// <summary> C←S - Used to notify the client that their session has expired and cannot be resumed. </summary>
|
||||
InvalidSession = 9,
|
||||
/// <summary> C←S - Used to provide information to the client immediately on connection. </summary>
|
||||
Hello = 10,
|
||||
/// <summary> C←S - Used to reply to a client's heartbeat. </summary>
|
||||
HeartbeatAck = 11,
|
||||
/// <summary> C→S - Used to request presence updates from particular guilds. </summary>
|
||||
GuildSync = 12
|
||||
}
|
||||
}
|
||||
13
src/Discord.Net.WebSocket/API/Gateway/GuildBanEvent.cs
Normal file
13
src/Discord.Net.WebSocket/API/Gateway/GuildBanEvent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildBanEvent
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildEmojiUpdateEvent
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
[JsonProperty("emojis")]
|
||||
public Emoji[] Emojis { get; set; }
|
||||
}
|
||||
}
|
||||
11
src/Discord.Net.WebSocket/API/Gateway/GuildMemberAddEvent.cs
Normal file
11
src/Discord.Net.WebSocket/API/Gateway/GuildMemberAddEvent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildMemberAddEvent : GuildMember
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildMemberRemoveEvent
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildMemberUpdateEvent : GuildMember
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildMembersChunkEvent
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
[JsonProperty("members")]
|
||||
public GuildMember[] Members { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildRoleCreateEvent
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
[JsonProperty("role")]
|
||||
public Role Role { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildRoleDeleteEvent
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
[JsonProperty("role_id")]
|
||||
public ulong RoleId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildRoleUpdateEvent
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
[JsonProperty("role")]
|
||||
public Role Role { get; set; }
|
||||
}
|
||||
}
|
||||
18
src/Discord.Net.WebSocket/API/Gateway/GuildSyncEvent.cs
Normal file
18
src/Discord.Net.WebSocket/API/Gateway/GuildSyncEvent.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class GuildSyncEvent
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public ulong Id { get; set; }
|
||||
[JsonProperty("large")]
|
||||
public bool Large { get; set; }
|
||||
|
||||
[JsonProperty("presences")]
|
||||
public Presence[] Presences { get; set; }
|
||||
[JsonProperty("members")]
|
||||
public GuildMember[] Members { get; set; }
|
||||
}
|
||||
}
|
||||
11
src/Discord.Net.WebSocket/API/Gateway/HelloEvent.cs
Normal file
11
src/Discord.Net.WebSocket/API/Gateway/HelloEvent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class HelloEvent
|
||||
{
|
||||
[JsonProperty("heartbeat_interval")]
|
||||
public int HeartbeatInterval { get; set; }
|
||||
}
|
||||
}
|
||||
21
src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
Normal file
21
src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
|
||||
public class IdentifyParams
|
||||
{
|
||||
[JsonProperty("token")]
|
||||
public string Token { get; set; }
|
||||
[JsonProperty("properties")]
|
||||
public IDictionary<string, string> Properties { get; set; }
|
||||
[JsonProperty("large_threshold")]
|
||||
public int LargeThreshold { get; set; }
|
||||
[JsonProperty("compress")]
|
||||
public bool UseCompression { get; set; }
|
||||
[JsonProperty("shard")]
|
||||
public Optional<int[]> ShardingParams { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class MessageDeleteBulkEvent
|
||||
{
|
||||
[JsonProperty("channel_id")]
|
||||
public ulong ChannelId { get; set; }
|
||||
[JsonProperty("ids")]
|
||||
public IEnumerable<ulong> Ids { get; set; }
|
||||
}
|
||||
}
|
||||
38
src/Discord.Net.WebSocket/API/Gateway/ReadyEvent.cs
Normal file
38
src/Discord.Net.WebSocket/API/Gateway/ReadyEvent.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class ReadyEvent
|
||||
{
|
||||
public class ReadState
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string ChannelId { get; set; }
|
||||
[JsonProperty("mention_count")]
|
||||
public int MentionCount { get; set; }
|
||||
[JsonProperty("last_message_id")]
|
||||
public string LastMessageId { get; set; }
|
||||
}
|
||||
|
||||
[JsonProperty("v")]
|
||||
public int Version { get; set; }
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
[JsonProperty("session_id")]
|
||||
public string SessionId { get; set; }
|
||||
[JsonProperty("read_state")]
|
||||
public ReadState[] ReadStates { get; set; }
|
||||
[JsonProperty("guilds")]
|
||||
public ExtendedGuild[] Guilds { get; set; }
|
||||
[JsonProperty("private_channels")]
|
||||
public Channel[] PrivateChannels { get; set; }
|
||||
[JsonProperty("relationships")]
|
||||
public Relationship[] Relationships { get; set; }
|
||||
|
||||
//Ignored
|
||||
/*[JsonProperty("user_settings")]
|
||||
[JsonProperty("user_guild_settings")]
|
||||
[JsonProperty("tutorial")]*/
|
||||
}
|
||||
}
|
||||
13
src/Discord.Net.WebSocket/API/Gateway/RecipientEvent.cs
Normal file
13
src/Discord.Net.WebSocket/API/Gateway/RecipientEvent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class RecipientEvent
|
||||
{
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
[JsonProperty("channel_id")]
|
||||
public ulong ChannelId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
|
||||
public class RequestMembersParams
|
||||
{
|
||||
[JsonProperty("query")]
|
||||
public string Query { get; set; }
|
||||
[JsonProperty("limit")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
[JsonProperty("guild_id")]
|
||||
private ulong[] _guildIds { get; set; }
|
||||
public IEnumerable<ulong> GuildIds { set { _guildIds = value.ToArray(); } }
|
||||
public IEnumerable<IGuild> Guilds { set { _guildIds = value.Select(x => x.Id).ToArray(); } }
|
||||
}
|
||||
}
|
||||
16
src/Discord.Net.WebSocket/API/Gateway/ResumeParams.cs
Normal file
16
src/Discord.Net.WebSocket/API/Gateway/ResumeParams.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
|
||||
public class ResumeParams
|
||||
{
|
||||
[JsonProperty("token")]
|
||||
public string Token { get; set; }
|
||||
[JsonProperty("session_id")]
|
||||
public string SessionId { get; set; }
|
||||
[JsonProperty("seq")]
|
||||
public int Sequence { get; set; }
|
||||
}
|
||||
}
|
||||
11
src/Discord.Net.WebSocket/API/Gateway/ResumedEvent.cs
Normal file
11
src/Discord.Net.WebSocket/API/Gateway/ResumedEvent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class ResumedEvent
|
||||
{
|
||||
[JsonProperty("heartbeat_interval")]
|
||||
public int HeartbeatInterval { get; set; }
|
||||
}
|
||||
}
|
||||
14
src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs
Normal file
14
src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
|
||||
public class StatusUpdateParams
|
||||
{
|
||||
[JsonProperty("idle_since"), Int53]
|
||||
public long? IdleSince { get; set; }
|
||||
[JsonProperty("game")]
|
||||
public Game Game { get; set; }
|
||||
}
|
||||
}
|
||||
15
src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs
Normal file
15
src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class TypingStartEvent
|
||||
{
|
||||
[JsonProperty("user_id")]
|
||||
public ulong UserId { get; set; }
|
||||
[JsonProperty("channel_id")]
|
||||
public ulong ChannelId { get; set; }
|
||||
[JsonProperty("timestamp")]
|
||||
public int Timestamp { get; set; }
|
||||
}
|
||||
}
|
||||
14
src/Discord.Net.WebSocket/API/Gateway/UpdateStatusParams.cs
Normal file
14
src/Discord.Net.WebSocket/API/Gateway/UpdateStatusParams.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
|
||||
public class UpdateStatusParams
|
||||
{
|
||||
[JsonProperty("idle_since")]
|
||||
public long? IdleSince { get; set; }
|
||||
[JsonProperty("game")]
|
||||
public Game Game { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
public class VoiceServerUpdateEvent
|
||||
{
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
[JsonProperty("endpoint")]
|
||||
public string Endpoint { get; set; }
|
||||
[JsonProperty("token")]
|
||||
public string Token { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Gateway
|
||||
{
|
||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
|
||||
public class VoiceStateUpdateParams
|
||||
{
|
||||
[JsonProperty("self_mute")]
|
||||
public bool SelfMute { get; set; }
|
||||
[JsonProperty("self_deaf")]
|
||||
public bool SelfDeaf { get; set; }
|
||||
|
||||
[JsonProperty("guild_id")]
|
||||
public ulong? GuildId { get; set; }
|
||||
public IGuild Guild { set { GuildId = value?.Id; } }
|
||||
|
||||
[JsonProperty("channel_id")]
|
||||
public ulong? ChannelId { get; set; }
|
||||
public IChannel Channel { set { ChannelId = value?.Id; } }
|
||||
}
|
||||
}
|
||||
17
src/Discord.Net.WebSocket/API/Voice/IdentifyParams.cs
Normal file
17
src/Discord.Net.WebSocket/API/Voice/IdentifyParams.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Voice
|
||||
{
|
||||
public class IdentifyParams
|
||||
{
|
||||
[JsonProperty("server_id")]
|
||||
public ulong GuildId { get; set; }
|
||||
[JsonProperty("user_id")]
|
||||
public ulong UserId { get; set; }
|
||||
[JsonProperty("session_id")]
|
||||
public string SessionId { get; set; }
|
||||
[JsonProperty("token")]
|
||||
public string Token { get; set; }
|
||||
}
|
||||
}
|
||||
17
src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs
Normal file
17
src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Voice
|
||||
{
|
||||
public class ReadyEvent
|
||||
{
|
||||
[JsonProperty("ssrc")]
|
||||
public uint SSRC { get; set; }
|
||||
[JsonProperty("port")]
|
||||
public ushort Port { get; set; }
|
||||
[JsonProperty("modes")]
|
||||
public string[] Modes { get; set; }
|
||||
[JsonProperty("heartbeat_interval")]
|
||||
public int HeartbeatInterval { get; set; }
|
||||
}
|
||||
}
|
||||
13
src/Discord.Net.WebSocket/API/Voice/SelectProtocolParams.cs
Normal file
13
src/Discord.Net.WebSocket/API/Voice/SelectProtocolParams.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Voice
|
||||
{
|
||||
public class SelectProtocolParams
|
||||
{
|
||||
[JsonProperty("protocol")]
|
||||
public string Protocol { get; set; }
|
||||
[JsonProperty("data")]
|
||||
public UdpProtocolInfo Data { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Voice
|
||||
{
|
||||
public class SessionDescriptionEvent
|
||||
{
|
||||
[JsonProperty("secret_key")]
|
||||
public byte[] SecretKey { get; set; }
|
||||
[JsonProperty("mode")]
|
||||
public string Mode { get; set; }
|
||||
}
|
||||
}
|
||||
13
src/Discord.Net.WebSocket/API/Voice/SpeakingParams.cs
Normal file
13
src/Discord.Net.WebSocket/API/Voice/SpeakingParams.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Voice
|
||||
{
|
||||
public class SpeakingParams
|
||||
{
|
||||
[JsonProperty("speaking")]
|
||||
public bool IsSpeaking { get; set; }
|
||||
[JsonProperty("delay")]
|
||||
public int Delay { get; set; }
|
||||
}
|
||||
}
|
||||
15
src/Discord.Net.WebSocket/API/Voice/UdpProtocolInfo.cs
Normal file
15
src/Discord.Net.WebSocket/API/Voice/UdpProtocolInfo.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma warning disable CS1591
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Voice
|
||||
{
|
||||
public class UdpProtocolInfo
|
||||
{
|
||||
[JsonProperty("address")]
|
||||
public string Address { get; set; }
|
||||
[JsonProperty("port")]
|
||||
public int Port { get; set; }
|
||||
[JsonProperty("mode")]
|
||||
public string Mode { get; set; }
|
||||
}
|
||||
}
|
||||
21
src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs
Normal file
21
src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma warning disable CS1591
|
||||
namespace Discord.API.Voice
|
||||
{
|
||||
public enum VoiceOpCode : byte
|
||||
{
|
||||
/// <summary> C→S - Used to associate a connection with a token. </summary>
|
||||
Identify = 0,
|
||||
/// <summary> C→S - Used to specify configuration. </summary>
|
||||
SelectProtocol = 1,
|
||||
/// <summary> C←S - Used to notify that the voice connection was successful and informs the client of available protocols. </summary>
|
||||
Ready = 2,
|
||||
/// <summary> C→S - Used to keep the connection alive and measure latency. </summary>
|
||||
Heartbeat = 3,
|
||||
/// <summary> C←S - Used to reply to a client's heartbeat. </summary>
|
||||
HeartbeatAck = 3,
|
||||
/// <summary> C←S - Used to provide an encryption key to the client. </summary>
|
||||
SessionDescription = 4,
|
||||
/// <summary> C↔S - Used to inform that a certain user is speaking. </summary>
|
||||
Speaking = 5
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user