Initial Commit - V0.1.0
This commit is contained in:
28
Discord.Net.sln
Normal file
28
Discord.Net.sln
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net", "Discord.Net\Discord.Net.csproj", "{8D23F61B-723C-4966-859D-1119B28BCF19}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1DDC89B5-2A88-45E5-A743-7A43E6B5C4B3}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.gitignore = .gitignore
|
||||
LICENSE = LICENSE
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8D23F61B-723C-4966-859D-1119B28BCF19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8D23F61B-723C-4966-859D-1119B28BCF19}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8D23F61B-723C-4966-859D-1119B28BCF19}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8D23F61B-723C-4966-859D-1119B28BCF19}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
61
Discord.Net/API/DiscordAPI.cs
Normal file
61
Discord.Net/API/DiscordAPI.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Discord.API.Models;
|
||||
using Discord.Helpers;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.API
|
||||
{
|
||||
internal static class DiscordAPI
|
||||
{
|
||||
public static async Task<AuthRegisterResponse> LoginAnonymous(string username, HttpOptions options)
|
||||
{
|
||||
var fingerprintResponse = await Http.Post<AuthFingerprintResponse>(Endpoints.AuthFingerprint, options);
|
||||
var registerRequest = new AuthRegisterRequest { Fingerprint = fingerprintResponse.Fingerprint, Username = username };
|
||||
var registerResponse = await Http.Post<AuthRegisterResponse>(Endpoints.AuthRegister, registerRequest, options);
|
||||
return registerResponse;
|
||||
}
|
||||
public static async Task<AuthLoginResponse> Login(string email, string password, HttpOptions options)
|
||||
{
|
||||
var request = new AuthLoginRequest { Email = email, Password = password };
|
||||
var response = await Http.Post<AuthLoginResponse>(Endpoints.AuthLogin, request, options);
|
||||
options.Token = response.Token;
|
||||
return response;
|
||||
}
|
||||
public static Task Logout(HttpOptions options)
|
||||
{
|
||||
return Http.Post(Endpoints.AuthLogout, options);
|
||||
}
|
||||
|
||||
public static Task CreateServer(string name, Region region, HttpOptions options)
|
||||
{
|
||||
var request = new CreateServerRequest { Name = name, Region = RegionConverter.Convert(region) };
|
||||
return Http.Post(Endpoints.Servers, request, options);
|
||||
}
|
||||
public static Task DeleteServer(string id, HttpOptions options)
|
||||
{
|
||||
return Http.Delete(Endpoints.Server(id), options);
|
||||
}
|
||||
|
||||
public static Task<GetInviteResponse> GetInvite(string id, HttpOptions options)
|
||||
{
|
||||
return Http.Get<GetInviteResponse>(Endpoints.Invite(id), options);
|
||||
}
|
||||
public static Task AcceptInvite(string id, HttpOptions options)
|
||||
{
|
||||
return Http.Post(Endpoints.Invite(id), options);
|
||||
}
|
||||
public static Task DeleteInvite(string id, HttpOptions options)
|
||||
{
|
||||
return Http.Delete(Endpoints.Invite(id), options);
|
||||
}
|
||||
|
||||
public static Task Typing(string channelId, HttpOptions options)
|
||||
{
|
||||
return Http.Post(Endpoints.ChannelTyping(channelId), options);
|
||||
}
|
||||
public static Task SendMessage(string channelId, string message, string[] mentions, HttpOptions options)
|
||||
{
|
||||
var request = new SendMessageRequest { Content = message, Mentions = mentions };
|
||||
return Http.Post(Endpoints.ChannelMessages(channelId), request, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Discord.Net/API/Endpoints.cs
Normal file
30
Discord.Net/API/Endpoints.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace Discord.API
|
||||
{
|
||||
internal static class Endpoints
|
||||
{
|
||||
public static readonly string BaseUrl = "discordapp.com/";
|
||||
public static readonly string BaseHttps = "https://" + BaseUrl;
|
||||
public static readonly string BaseWss = "wss://" + BaseUrl;
|
||||
|
||||
public static readonly string Auth = $"{BaseHttps}/api/auth";
|
||||
public static readonly string AuthFingerprint = $"{Auth}fingerprint";
|
||||
public static readonly string AuthRegister = $"{Auth}/register";
|
||||
public static readonly string AuthLogin = $"{Auth}/login";
|
||||
public static readonly string AuthLogout = $"{Auth}/logout";
|
||||
|
||||
public static readonly string Servers = $"{BaseHttps}/api/guilds";
|
||||
public static string Server(string id) { return $"{Servers}/{id}"; }
|
||||
public static string ServerMessages(string id) { return $"{Servers}/{id}/messages?limit=50"; }
|
||||
|
||||
public static readonly string Invites = $"{BaseHttps}/api/invite";
|
||||
public static string Invite(string id) { return $"{Invites}/{id}"; }
|
||||
|
||||
public static readonly string Channels = $"{BaseHttps}/api/channels";
|
||||
public static string Channel(string id) { return $"{Channels}/{id}"; }
|
||||
public static string ChannelTyping(string id) { return $"{Channels}/{id}/typing"; }
|
||||
public static string ChannelMessages(string id) { return $"{Channels}/{id}/messages"; }
|
||||
|
||||
public static readonly string WebSocket_Hub = BaseWss + "hub";
|
||||
|
||||
}
|
||||
}
|
||||
66
Discord.Net/API/Models/ApiRequests.cs
Normal file
66
Discord.Net/API/Models/ApiRequests.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
//Ignore unused/unassigned variable warnings
|
||||
#pragma warning disable CS0649
|
||||
#pragma warning disable CS0169
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.API.Models
|
||||
{
|
||||
public class AuthFingerprintResponse
|
||||
{
|
||||
[JsonProperty(PropertyName = "fingerprint")]
|
||||
public string Fingerprint;
|
||||
}
|
||||
|
||||
public class AuthRegisterRequest
|
||||
{
|
||||
[JsonProperty(PropertyName = "fingerprint")]
|
||||
public string Fingerprint;
|
||||
[JsonProperty(PropertyName = "username")]
|
||||
public string Username;
|
||||
}
|
||||
public class AuthRegisterResponse : AuthLoginResponse { }
|
||||
|
||||
public class AuthLoginRequest
|
||||
{
|
||||
[JsonProperty(PropertyName = "email")]
|
||||
public string Email;
|
||||
[JsonProperty(PropertyName = "password")]
|
||||
public string Password;
|
||||
}
|
||||
public class AuthLoginResponse
|
||||
{
|
||||
[JsonProperty(PropertyName = "token")]
|
||||
public string Token;
|
||||
}
|
||||
|
||||
public class CreateServerRequest
|
||||
{
|
||||
[JsonProperty(PropertyName = "name")]
|
||||
public string Name;
|
||||
[JsonProperty(PropertyName = "region")]
|
||||
public string Region;
|
||||
}
|
||||
|
||||
public class GetInviteResponse
|
||||
{
|
||||
[JsonProperty(PropertyName = "inviter")]
|
||||
public UserInfo Inviter;
|
||||
[JsonProperty(PropertyName = "guild")]
|
||||
public ServerInfo Server;
|
||||
[JsonProperty(PropertyName = "channel")]
|
||||
public ChannelInfo Channel;
|
||||
[JsonProperty(PropertyName = "code")]
|
||||
public string Code;
|
||||
[JsonProperty(PropertyName = "xkcdpass")]
|
||||
public string XkcdPass;
|
||||
}
|
||||
|
||||
public class SendMessageRequest
|
||||
{
|
||||
[JsonProperty(PropertyName = "content")]
|
||||
public string Content;
|
||||
[JsonProperty(PropertyName = "mentions")]
|
||||
public string[] Mentions;
|
||||
}
|
||||
}
|
||||
131
Discord.Net/API/Models/General.cs
Normal file
131
Discord.Net/API/Models/General.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
//Ignore unused/unassigned variable warnings
|
||||
#pragma warning disable CS0649
|
||||
#pragma warning disable CS0169
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
|
||||
namespace Discord.API.Models
|
||||
{
|
||||
internal class WebSocketMessage
|
||||
{
|
||||
[JsonProperty(PropertyName = "op")]
|
||||
public int Operation;
|
||||
[JsonProperty(PropertyName = "t")]
|
||||
public string Type;
|
||||
[JsonProperty(PropertyName = "d")]
|
||||
public object Payload;
|
||||
}
|
||||
internal abstract class WebSocketMessage<T> : WebSocketMessage
|
||||
where T : new()
|
||||
{
|
||||
public WebSocketMessage() { Payload = new T(); }
|
||||
public WebSocketMessage(int op) { Operation = op; Payload = new T(); }
|
||||
public WebSocketMessage(int op, T payload) { Operation = op; Payload = payload; }
|
||||
|
||||
[JsonIgnore]
|
||||
public new T Payload
|
||||
{
|
||||
get { if (base.Payload is JToken) { base.Payload = (base.Payload as JToken).ToObject<T>(); } return (T)base.Payload; }
|
||||
set { base.Payload = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class UserInfo
|
||||
{
|
||||
[JsonProperty(PropertyName = "username")]
|
||||
public string Username;
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public string Id;
|
||||
[JsonProperty(PropertyName = "discriminator")]
|
||||
public string Discriminator;
|
||||
[JsonProperty(PropertyName = "avatar")]
|
||||
public string Avatar;
|
||||
}
|
||||
public class SelfUserInfo : UserInfo
|
||||
{
|
||||
[JsonProperty(PropertyName = "email")]
|
||||
public string Email;
|
||||
[JsonProperty(PropertyName = "verified")]
|
||||
public bool IsVerified;
|
||||
}
|
||||
public class PresenceUserInfo : UserInfo
|
||||
{
|
||||
[JsonProperty(PropertyName = "game_id")]
|
||||
public string GameId;
|
||||
[JsonProperty(PropertyName = "status")]
|
||||
public string Status;
|
||||
}
|
||||
|
||||
public class MembershipInfo
|
||||
{
|
||||
[JsonProperty(PropertyName = "roles")]
|
||||
public object[] Roles;
|
||||
[JsonProperty(PropertyName = "mute")]
|
||||
public bool IsMuted;
|
||||
[JsonProperty(PropertyName = "deaf")]
|
||||
public bool IsDeaf;
|
||||
[JsonProperty(PropertyName = "joined_at")]
|
||||
public DateTime JoinedAt;
|
||||
[JsonProperty(PropertyName = "user")]
|
||||
public UserInfo User;
|
||||
}
|
||||
|
||||
public class ChannelInfo
|
||||
{
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public string Id;
|
||||
[JsonProperty(PropertyName = "name")]
|
||||
public string Name;
|
||||
[JsonProperty(PropertyName = "last_message_id")]
|
||||
public string LastMessageId;
|
||||
[JsonProperty(PropertyName = "is_private")]
|
||||
public bool IsPrivate;
|
||||
[JsonProperty(PropertyName = "type")]
|
||||
public string Type;
|
||||
[JsonProperty(PropertyName = "permission_overwrites")]
|
||||
public object[] PermissionOverwrites;
|
||||
[JsonProperty(PropertyName = "recipient")]
|
||||
public UserInfo Recipient;
|
||||
}
|
||||
|
||||
public class ServerInfo
|
||||
{
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public string Id;
|
||||
[JsonProperty(PropertyName = "name")]
|
||||
public string Name;
|
||||
}
|
||||
public class ExtendedServerInfo : ServerInfo
|
||||
{
|
||||
[JsonProperty(PropertyName = "afk_channel_id")]
|
||||
public string AFKChannelId;
|
||||
[JsonProperty(PropertyName = "afk_timeout")]
|
||||
public int AFKTimeout;
|
||||
[JsonProperty(PropertyName = "channels")]
|
||||
public ChannelInfo[] Channels;
|
||||
[JsonProperty(PropertyName = "joined_at")]
|
||||
public DateTime JoinedAt;
|
||||
[JsonProperty(PropertyName = "members")]
|
||||
public MembershipInfo[] Members;
|
||||
[JsonProperty(PropertyName = "owner_id")]
|
||||
public string OwnerId;
|
||||
[JsonProperty(PropertyName = "presence")]
|
||||
public object[] Presence;
|
||||
[JsonProperty(PropertyName = "region")]
|
||||
public string Region;
|
||||
[JsonProperty(PropertyName = "roles")]
|
||||
public object[] Roles;
|
||||
[JsonProperty(PropertyName = "voice_states")]
|
||||
public object[] VoiceStates;
|
||||
}
|
||||
|
||||
internal class MessageReference
|
||||
{
|
||||
[JsonProperty(PropertyName = "message_id")]
|
||||
public string MessageId;
|
||||
[JsonProperty(PropertyName = "channel_id")]
|
||||
public string ChannelId;
|
||||
}
|
||||
}
|
||||
41
Discord.Net/API/Models/WebSocketCommands.cs
Normal file
41
Discord.Net/API/Models/WebSocketCommands.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
//Ignore unused/unassigned variable warnings
|
||||
#pragma warning disable CS0649
|
||||
#pragma warning disable CS0169
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Discord.API.Models
|
||||
{
|
||||
internal static class WebSocketCommands
|
||||
{
|
||||
internal sealed class KeepAlive : WebSocketMessage<int>
|
||||
{
|
||||
private static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
public KeepAlive() : base(1, (int)(DateTime.UtcNow - epoch).TotalMilliseconds) { }
|
||||
}
|
||||
internal sealed class Login : WebSocketMessage<Login.Data>
|
||||
{
|
||||
public Login() : base(2) { }
|
||||
public class Data
|
||||
{
|
||||
[JsonProperty(PropertyName = "token")]
|
||||
public string Token;
|
||||
[JsonProperty(PropertyName = "properties")]
|
||||
public Dictionary<string, string> Properties = new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
internal sealed class UpdateStatus : WebSocketMessage<UpdateStatus.Data>
|
||||
{
|
||||
public UpdateStatus() : base(3) { }
|
||||
public class Data
|
||||
{
|
||||
[JsonProperty(PropertyName = "idle_since")]
|
||||
public string IdleSince;
|
||||
[JsonProperty(PropertyName = "game_id")]
|
||||
public string GameId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
120
Discord.Net/API/Models/WebSocketEvents.cs
Normal file
120
Discord.Net/API/Models/WebSocketEvents.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
//Ignore unused/unassigned variable warnings
|
||||
#pragma warning disable CS0649
|
||||
#pragma warning disable CS0169
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
|
||||
namespace Discord.API.Models
|
||||
{
|
||||
internal static class WebSocketEvents
|
||||
{
|
||||
internal sealed class Ready
|
||||
{
|
||||
[JsonProperty(PropertyName = "user")]
|
||||
public SelfUserInfo User;
|
||||
[JsonProperty(PropertyName = "session_id")]
|
||||
public string SessionId;
|
||||
[JsonProperty(PropertyName = "read_state")]
|
||||
public object[] ReadState;
|
||||
[JsonProperty(PropertyName = "guilds")]
|
||||
public ExtendedServerInfo[] Guilds;
|
||||
[JsonProperty(PropertyName = "private_channels")]
|
||||
public ChannelInfo[] PrivateChannels;
|
||||
[JsonProperty(PropertyName = "heartbeat_interval")]
|
||||
public int HeartbeatInterval;
|
||||
}
|
||||
|
||||
internal sealed class GuildCreate : ExtendedServerInfo { }
|
||||
internal sealed class GuildDelete : ExtendedServerInfo { }
|
||||
|
||||
internal sealed class ChannelCreate : ChannelInfo { }
|
||||
internal sealed class ChannelDelete : ChannelInfo { }
|
||||
|
||||
internal sealed class GuildMemberAdd
|
||||
{
|
||||
[JsonProperty(PropertyName = "user")]
|
||||
public UserInfo User;
|
||||
[JsonProperty(PropertyName = "roles")]
|
||||
public object[] Roles;
|
||||
[JsonProperty(PropertyName = "joined_at")]
|
||||
public DateTime JoinedAt;
|
||||
[JsonProperty(PropertyName = "guild_id")]
|
||||
public string GuildId;
|
||||
}
|
||||
|
||||
internal sealed class GuildMemberRemove
|
||||
{
|
||||
[JsonProperty(PropertyName = "user")]
|
||||
public UserInfo User;
|
||||
[JsonProperty(PropertyName = "guild_id")]
|
||||
public string GuildId;
|
||||
}
|
||||
internal sealed class UserUpdate : SelfUserInfo { }
|
||||
internal sealed class PresenceUpdate : PresenceUserInfo { }
|
||||
internal sealed class VoiceStateUpdate
|
||||
{
|
||||
[JsonProperty(PropertyName = "user_id")]
|
||||
public string UserId;
|
||||
[JsonProperty(PropertyName = "guild_id")]
|
||||
public string GuildId;
|
||||
[JsonProperty(PropertyName = "channel_id")]
|
||||
public string ChannelId;
|
||||
[JsonProperty(PropertyName = "suppress")]
|
||||
public bool IsSuppressed;
|
||||
[JsonProperty(PropertyName = "session_id")]
|
||||
public string SessionId;
|
||||
[JsonProperty(PropertyName = "self_mute")]
|
||||
public bool IsSelfMuted;
|
||||
[JsonProperty(PropertyName = "self_deaf")]
|
||||
public bool IsSelfDeafened;
|
||||
[JsonProperty(PropertyName = "mute")]
|
||||
public bool IsMuted;
|
||||
[JsonProperty(PropertyName = "deaf")]
|
||||
public bool IsDeafened;
|
||||
}
|
||||
internal sealed class MessageCreate
|
||||
{
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public string Id;
|
||||
[JsonProperty(PropertyName = "channel_id")]
|
||||
public string ChannelId;
|
||||
[JsonProperty(PropertyName = "tts")]
|
||||
public bool IsTextToSpeech;
|
||||
[JsonProperty(PropertyName = "mention_everyone")]
|
||||
public bool IsMentioningEveryone;
|
||||
[JsonProperty(PropertyName = "timestamp")]
|
||||
public DateTime Timestamp;
|
||||
[JsonProperty(PropertyName = "mentions")]
|
||||
public UserInfo[] Mentions;
|
||||
[JsonProperty(PropertyName = "embeds")]
|
||||
public object[] Embeds;
|
||||
[JsonProperty(PropertyName = "attachments")]
|
||||
public object[] Attachments;
|
||||
[JsonProperty(PropertyName = "content")]
|
||||
public string Content;
|
||||
[JsonProperty(PropertyName = "author")]
|
||||
public UserInfo Author;
|
||||
}
|
||||
internal sealed class MessageUpdate
|
||||
{
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public string Id;
|
||||
[JsonProperty(PropertyName = "channel_id")]
|
||||
public string ChannelId;
|
||||
[JsonProperty(PropertyName = "embeds")]
|
||||
public object[] Embeds;
|
||||
}
|
||||
internal sealed class MessageDelete : MessageReference { }
|
||||
internal sealed class MessageAck : MessageReference { }
|
||||
internal sealed class TypingStart
|
||||
{
|
||||
[JsonProperty(PropertyName = "user_id")]
|
||||
public string UserId;
|
||||
[JsonProperty(PropertyName = "channel_id")]
|
||||
public string ChannelId;
|
||||
[JsonProperty(PropertyName = "timestamp")]
|
||||
public int Timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Discord.Net/Discord.Net.csproj
Normal file
78
Discord.Net/Discord.Net.csproj
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{8D23F61B-723C-4966-859D-1119B28BCF19}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Discord</RootNamespace>
|
||||
<AssemblyName>Discord.Net</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="API\Models\General.cs" />
|
||||
<Compile Include="API\Models\ApiRequests.cs" />
|
||||
<Compile Include="API\Endpoints.cs" />
|
||||
<Compile Include="API\Models\WebSocketCommands.cs" />
|
||||
<Compile Include="Models\ChatMessageReference.cs" />
|
||||
<Compile Include="Models\ChatMessage.cs" />
|
||||
<Compile Include="Models\Channel.cs" />
|
||||
<Compile Include="DiscordWebSocket.Events.cs" />
|
||||
<Compile Include="Helpers\Http.cs" />
|
||||
<Compile Include="API\DiscordAPI.cs" />
|
||||
<Compile Include="API\Models\WebSocketEvents.cs" />
|
||||
<Compile Include="DiscordClient.cs" />
|
||||
<Compile Include="Region.cs" />
|
||||
<Compile Include="DiscordClient.Events.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="DiscordWebSocket.cs" />
|
||||
<Compile Include="Models\Server.cs" />
|
||||
<Compile Include="Models\User.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
145
Discord.Net/DiscordClient.Events.cs
Normal file
145
Discord.Net/DiscordClient.Events.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using Discord.Models;
|
||||
using System;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public partial class DiscordClient
|
||||
{
|
||||
public sealed class ServerEventArgs : EventArgs
|
||||
{
|
||||
public readonly Server Server;
|
||||
internal ServerEventArgs(Server server) { Server = server; }
|
||||
}
|
||||
public sealed class ChannelEventArgs : EventArgs
|
||||
{
|
||||
public readonly Channel Channel;
|
||||
internal ChannelEventArgs(Channel channel) { Channel = channel; }
|
||||
}
|
||||
public sealed class UserEventArgs : EventArgs
|
||||
{
|
||||
public readonly User User;
|
||||
internal UserEventArgs(User user) { User = user; }
|
||||
}
|
||||
public sealed class MessageCreateEventArgs : EventArgs
|
||||
{
|
||||
public readonly ChatMessage Message;
|
||||
internal MessageCreateEventArgs(ChatMessage msg) { Message = msg; }
|
||||
}
|
||||
public sealed class MessageEventArgs : EventArgs
|
||||
{
|
||||
public readonly ChatMessageReference Message;
|
||||
internal MessageEventArgs(ChatMessageReference msg) { Message = msg; }
|
||||
}
|
||||
public sealed class LogMessageEventArgs : EventArgs
|
||||
{
|
||||
public readonly string Message;
|
||||
internal LogMessageEventArgs(string msg) { Message = msg; }
|
||||
}
|
||||
public sealed class UserTypingEventArgs : EventArgs
|
||||
{
|
||||
public readonly User User;
|
||||
public readonly Channel Channel;
|
||||
internal UserTypingEventArgs(User user, Channel channel)
|
||||
{
|
||||
User = user;
|
||||
Channel = channel;
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<LogMessageEventArgs> DebugMessage;
|
||||
private void RaiseOnDebugMessage(string message)
|
||||
{
|
||||
if (DebugMessage != null)
|
||||
DebugMessage(this, new LogMessageEventArgs(message));
|
||||
}
|
||||
|
||||
public event EventHandler Connected;
|
||||
private void RaiseConnected()
|
||||
{
|
||||
if (Connected != null)
|
||||
Connected(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler Disconnected;
|
||||
private void RaiseDisconnected()
|
||||
{
|
||||
if (Disconnected != null)
|
||||
Disconnected(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler LoggedIn;
|
||||
private void RaiseLoggedIn()
|
||||
{
|
||||
if (LoggedIn != null)
|
||||
LoggedIn(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler<ServerEventArgs> ServerCreated, ServerDestroyed;
|
||||
private void RaiseServerCreated(Server server)
|
||||
{
|
||||
if (ServerCreated != null)
|
||||
ServerCreated(this, new ServerEventArgs(server));
|
||||
}
|
||||
private void RaiseServerDestroyed(Server server)
|
||||
{
|
||||
if (ServerDestroyed != null)
|
||||
ServerDestroyed(this, new ServerEventArgs(server));
|
||||
}
|
||||
|
||||
public event EventHandler<ChannelEventArgs> ChannelCreated, ChannelDestroyed;
|
||||
private void RaiseChannelCreated(Channel channel)
|
||||
{
|
||||
if (ChannelCreated != null)
|
||||
ChannelCreated(this, new ChannelEventArgs(channel));
|
||||
}
|
||||
private void RaiseChannelDestroyed(Channel channel)
|
||||
{
|
||||
if (ChannelDestroyed != null)
|
||||
ChannelDestroyed(this, new ChannelEventArgs(channel));
|
||||
}
|
||||
|
||||
public event EventHandler<MessageCreateEventArgs> MessageCreated;
|
||||
public event EventHandler<MessageEventArgs> MessageDeleted, MessageUpdated, MessageAcknowledged;
|
||||
private void RaiseMessageCreated(ChatMessage msg)
|
||||
{
|
||||
if (MessageCreated != null)
|
||||
MessageCreated(this, new MessageCreateEventArgs(msg));
|
||||
}
|
||||
private void RaiseMessageDeleted(ChatMessageReference msg)
|
||||
{
|
||||
if (MessageDeleted != null)
|
||||
MessageDeleted(this, new MessageEventArgs(msg));
|
||||
}
|
||||
private void RaiseMessageUpdated(ChatMessageReference msg)
|
||||
{
|
||||
if (MessageUpdated != null)
|
||||
MessageUpdated(this, new MessageEventArgs(msg));
|
||||
}
|
||||
private void RaiseMessageAcknowledged(ChatMessageReference msg)
|
||||
{
|
||||
if (MessageAcknowledged != null)
|
||||
MessageAcknowledged(this, new MessageEventArgs(msg));
|
||||
}
|
||||
|
||||
public event EventHandler<UserEventArgs> PresenceUpdated;
|
||||
private void RaisePresenceUpdated(User user)
|
||||
{
|
||||
if (PresenceUpdated != null)
|
||||
PresenceUpdated(this, new UserEventArgs(user));
|
||||
}
|
||||
|
||||
public event EventHandler<UserEventArgs> VoiceStateUpdated;
|
||||
private void RaiseVoiceStateUpdated(User user)
|
||||
{
|
||||
if (VoiceStateUpdated != null)
|
||||
VoiceStateUpdated(this, new UserEventArgs(user));
|
||||
}
|
||||
|
||||
public event EventHandler<UserTypingEventArgs> UserTyping;
|
||||
private void RaiseUserTyping(User user, Channel channel)
|
||||
{
|
||||
if (UserTyping != null)
|
||||
UserTyping(this, new UserTypingEventArgs(user, channel));
|
||||
}
|
||||
}
|
||||
}
|
||||
400
Discord.Net/DiscordClient.cs
Normal file
400
Discord.Net/DiscordClient.cs
Normal file
@@ -0,0 +1,400 @@
|
||||
using Discord.API;
|
||||
using Discord.API.Models;
|
||||
using Discord.Helpers;
|
||||
using Discord.Models;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public partial class DiscordClient
|
||||
{
|
||||
private const int MaxMessageSize = 2000;
|
||||
|
||||
private DiscordWebSocket _webSocket;
|
||||
private HttpOptions _httpOptions;
|
||||
private bool _isClosing, _isReady;
|
||||
|
||||
public string SelfId { get; private set; }
|
||||
public User Self { get { return GetUser(SelfId); } }
|
||||
|
||||
public IEnumerable<User> Users { get { return _users.Values; } }
|
||||
private ConcurrentDictionary<string, User> _users;
|
||||
|
||||
public IEnumerable<Server> Servers { get { return _servers.Values; } }
|
||||
private ConcurrentDictionary<string, Server> _servers;
|
||||
|
||||
public IEnumerable<Channel> Channels { get { return _channels.Values; } }
|
||||
private ConcurrentDictionary<string, Channel> _channels;
|
||||
|
||||
public DiscordClient()
|
||||
{
|
||||
string version = typeof(DiscordClient).GetTypeInfo().Assembly.GetName().Version.ToString(2);
|
||||
_httpOptions = new HttpOptions { UserAgent = $"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)" };
|
||||
|
||||
_users = new ConcurrentDictionary<string, User>();
|
||||
_servers = new ConcurrentDictionary<string, Server>();
|
||||
_channels = new ConcurrentDictionary<string, Channel>();
|
||||
|
||||
_webSocket = new DiscordWebSocket();
|
||||
_webSocket.Connected += (s,e) => RaiseConnected();
|
||||
_webSocket.Disconnected += async (s,e) =>
|
||||
{
|
||||
//Reconnect if we didn't cause the disconnect
|
||||
RaiseDisconnected();
|
||||
if (!_isClosing)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
await _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, _httpOptions);
|
||||
}
|
||||
};
|
||||
_webSocket.GotEvent += (s, e) =>
|
||||
{
|
||||
switch (e.Type)
|
||||
{
|
||||
//Global
|
||||
case "READY": //Resync
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.Ready>();
|
||||
|
||||
_servers.Clear();
|
||||
_channels.Clear();
|
||||
_users.Clear();
|
||||
|
||||
SelfId = data.User.Id;
|
||||
UpdateUser(data.User);
|
||||
foreach (var server in data.Guilds)
|
||||
UpdateServer(server);
|
||||
foreach (var channel in data.PrivateChannels)
|
||||
UpdateChannel(channel as ChannelInfo, null);
|
||||
|
||||
RaiseLoggedIn();
|
||||
}
|
||||
break;
|
||||
|
||||
//Servers
|
||||
case "GUILD_CREATE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.GuildCreate>();
|
||||
var server = UpdateServer(data);
|
||||
RaiseServerCreated(server);
|
||||
}
|
||||
break;
|
||||
case "GUILD_DELETE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.GuildDelete>();
|
||||
Server server;
|
||||
if (_servers.TryRemove(data.Id, out server))
|
||||
RaiseServerDestroyed(server);
|
||||
}
|
||||
break;
|
||||
|
||||
//Channels
|
||||
case "CHANNEL_CREATE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.ChannelCreate>();
|
||||
var channel = UpdateChannel(data, null);
|
||||
RaiseChannelCreated(channel);
|
||||
}
|
||||
break;
|
||||
case "CHANNEL_DELETE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.ChannelDelete>();
|
||||
var channel = DeleteChannel(data.Id);
|
||||
RaiseChannelDestroyed(channel);
|
||||
}
|
||||
break;
|
||||
|
||||
//Members
|
||||
case "GUILD_MEMBER_ADD":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberAdd>();
|
||||
var user = UpdateUser(data.User);
|
||||
var server = GetServer(data.GuildId);
|
||||
server._members[user.Id] = true;
|
||||
}
|
||||
break;
|
||||
case "GUILD_MEMBER_REMOVE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberRemove>();
|
||||
var user = UpdateUser(data.User);
|
||||
var server = GetServer(data.GuildId);
|
||||
server._members[user.Id] = true;
|
||||
}
|
||||
break;
|
||||
|
||||
//Users
|
||||
case "PRESENCE_UPDATE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.PresenceUpdate>();
|
||||
var user = UpdateUser(data);
|
||||
RaisePresenceUpdated(user);
|
||||
}
|
||||
break;
|
||||
case "VOICE_STATE_UPDATE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.VoiceStateUpdate>();
|
||||
var user = GetUser(data.UserId); //TODO: Don't ignore this
|
||||
RaiseVoiceStateUpdated(user);
|
||||
}
|
||||
break;
|
||||
|
||||
//Messages
|
||||
case "MESSAGE_CREATE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.MessageCreate>();
|
||||
var msg = UpdateMessage(data);
|
||||
msg.User.UpdateActivity(data.Timestamp);
|
||||
RaiseMessageCreated(msg);
|
||||
}
|
||||
break;
|
||||
case "MESSAGE_UPDATE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.MessageUpdate>();
|
||||
var msg = GetMessage(data.Id, data.ChannelId);
|
||||
RaiseMessageUpdated(msg);
|
||||
}
|
||||
break;
|
||||
case "MESSAGE_DELETE":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.MessageDelete>();
|
||||
var msg = GetMessage(data.MessageId, data.ChannelId);
|
||||
RaiseMessageDeleted(msg);
|
||||
}
|
||||
break;
|
||||
case "MESSAGE_ACK":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.MessageAck>();
|
||||
var msg = GetMessage(data.MessageId, data.ChannelId);
|
||||
RaiseMessageAcknowledged(msg);
|
||||
}
|
||||
break;
|
||||
case "TYPING_START":
|
||||
{
|
||||
var data = e.Event.ToObject<WebSocketEvents.TypingStart>();
|
||||
var channel = GetChannel(data.ChannelId);
|
||||
var user = GetUser(data.UserId);
|
||||
RaiseUserTyping(user, channel);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
RaiseOnDebugMessage("Unknown WebSocket message type: " + e.Type);
|
||||
break;
|
||||
}
|
||||
};
|
||||
_webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Message);
|
||||
}
|
||||
|
||||
public async Task Connect(string email, string password)
|
||||
{
|
||||
_isClosing = false;
|
||||
var response = await DiscordAPI.Login(email, password, _httpOptions);
|
||||
_httpOptions.Token = response.Token;
|
||||
await _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, _httpOptions);
|
||||
_isReady = true;
|
||||
}
|
||||
public async Task ConnectAnonymous(string username)
|
||||
{
|
||||
_isClosing = false;
|
||||
var response = await DiscordAPI.LoginAnonymous(username, _httpOptions);
|
||||
_httpOptions.Token = response.Token;
|
||||
await _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, _httpOptions);
|
||||
_isReady = true;
|
||||
}
|
||||
public async Task Disconnect()
|
||||
{
|
||||
_isReady = false;
|
||||
_isClosing = true;
|
||||
await _webSocket.DisconnectAsync();
|
||||
_isClosing = false;
|
||||
}
|
||||
|
||||
public Task CreateServer(string name, Region region)
|
||||
{
|
||||
CheckReady();
|
||||
return DiscordAPI.CreateServer(name, region, _httpOptions);
|
||||
}
|
||||
public Task DeleteServer(string id)
|
||||
{
|
||||
CheckReady();
|
||||
return DiscordAPI.DeleteServer(id, _httpOptions);
|
||||
}
|
||||
|
||||
public Task<GetInviteResponse> GetInvite(string id)
|
||||
{
|
||||
CheckReady();
|
||||
return DiscordAPI.GetInvite(id, _httpOptions);
|
||||
}
|
||||
public async Task AcceptInvite(string id)
|
||||
{
|
||||
CheckReady();
|
||||
//Check if this is a human-readable link and get its ID
|
||||
var response = await DiscordAPI.GetInvite(id, _httpOptions);
|
||||
await DiscordAPI.AcceptInvite(response.Code, _httpOptions);
|
||||
}
|
||||
public async Task DeleteInvite(string id)
|
||||
{
|
||||
CheckReady();
|
||||
//Check if this is a human-readable link and get its ID
|
||||
var response = await DiscordAPI.GetInvite(id, _httpOptions);
|
||||
await DiscordAPI.DeleteInvite(response.Code, _httpOptions);
|
||||
}
|
||||
|
||||
public Task SendMessage(string channelId, string text)
|
||||
{
|
||||
return SendMessage(channelId, text, new string[0]);
|
||||
}
|
||||
public async Task SendMessage(string channelId, string text, string[] mentions)
|
||||
{
|
||||
CheckReady();
|
||||
if (text.Length <= 2000)
|
||||
await DiscordAPI.SendMessage(channelId, text, mentions, _httpOptions);
|
||||
else
|
||||
{
|
||||
int blockCount = (int)Math.Ceiling(text.Length / (double)MaxMessageSize);
|
||||
for (int i = 0; i < blockCount; i++)
|
||||
{
|
||||
int index = i * MaxMessageSize;
|
||||
await DiscordAPI.SendMessage(channelId, text.Substring(index, Math.Min(2000, text.Length - index)), mentions, _httpOptions);
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public User GetUser(string id)
|
||||
{
|
||||
if (id == null) return null;
|
||||
User user = null;
|
||||
_users.TryGetValue(id, out user);
|
||||
return user;
|
||||
}
|
||||
private User UpdateUser(UserInfo model)
|
||||
{
|
||||
var user = GetUser(model.Id) ?? new User(model.Id, this);
|
||||
|
||||
user.Avatar = model.Avatar;
|
||||
user.Discriminator = model.Discriminator;
|
||||
user.Name = model.Username;
|
||||
if (model is SelfUserInfo)
|
||||
{
|
||||
var extendedModel = model as SelfUserInfo;
|
||||
user.Email = extendedModel.Email;
|
||||
user.IsVerified = extendedModel.IsVerified;
|
||||
}
|
||||
if (model is PresenceUserInfo)
|
||||
{
|
||||
var extendedModel = model as PresenceUserInfo;
|
||||
user.GameId = extendedModel.GameId;
|
||||
user.Status = extendedModel.Status;
|
||||
}
|
||||
|
||||
_users[model.Id] = user;
|
||||
return user;
|
||||
}
|
||||
|
||||
public Server GetServer(string id)
|
||||
{
|
||||
if (id == null) return null;
|
||||
Server server = null;
|
||||
_servers.TryGetValue(id, out server);
|
||||
return server;
|
||||
}
|
||||
private Server UpdateServer(ServerInfo model)
|
||||
{
|
||||
var server = GetServer(model.Id) ?? new Server(model.Id, this);
|
||||
|
||||
server.Name = model.Name;
|
||||
if (model is ExtendedServerInfo)
|
||||
{
|
||||
var extendedModel = model as ExtendedServerInfo;
|
||||
server.AFKChannelId = extendedModel.AFKChannelId;
|
||||
server.AFKTimeout = extendedModel.AFKTimeout;
|
||||
server.JoinedAt = extendedModel.JoinedAt;
|
||||
server.OwnerId = extendedModel.OwnerId;
|
||||
server.Presence = extendedModel.Presence;
|
||||
server.Region = extendedModel.Region;
|
||||
server.Roles = extendedModel.Roles;
|
||||
server.VoiceStates = extendedModel.VoiceStates;
|
||||
|
||||
foreach (var channel in extendedModel.Channels)
|
||||
{
|
||||
UpdateChannel(channel, model.Id);
|
||||
server._channels[channel.Id] = true;
|
||||
}
|
||||
foreach (var membership in extendedModel.Members)
|
||||
{
|
||||
UpdateUser(membership.User);
|
||||
server._members[membership.User.Id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
_servers[model.Id] = server;
|
||||
return server;
|
||||
}
|
||||
|
||||
public Channel GetChannel(string id)
|
||||
{
|
||||
if (id == null) return null;
|
||||
Channel channel = null;
|
||||
_channels.TryGetValue(id, out channel);
|
||||
return channel;
|
||||
}
|
||||
private Channel UpdateChannel(ChannelInfo model, string serverId)
|
||||
{
|
||||
var channel = GetChannel(model.Id) ?? new Channel(model.Id, serverId, this);
|
||||
|
||||
channel.Name = model.Name;
|
||||
channel.IsPrivate = model.IsPrivate;
|
||||
channel.PermissionOverwrites = model.PermissionOverwrites;
|
||||
channel.RecipientId = model.Recipient?.Id;
|
||||
channel.Type = model.Type;
|
||||
|
||||
_channels[model.Id] = channel;
|
||||
return channel;
|
||||
}
|
||||
private Channel DeleteChannel(string id)
|
||||
{
|
||||
Channel channel = null;
|
||||
if (_channels.TryRemove(id, out channel))
|
||||
{
|
||||
bool ignored;
|
||||
channel.Server._channels.TryRemove(id, out ignored);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
//TODO: Temporary measure, unsure if we want to store these or not.
|
||||
private ChatMessageReference GetMessage(string id, string channelId)
|
||||
{
|
||||
if (id == null || channelId == null) return null;
|
||||
var msg = new ChatMessageReference(id, this);
|
||||
|
||||
msg.ChannelId = channelId;
|
||||
|
||||
return msg;
|
||||
}
|
||||
private ChatMessage UpdateMessage(WebSocketEvents.MessageCreate model)
|
||||
{
|
||||
return new ChatMessage(model.Id, this)
|
||||
{
|
||||
Attachments = model.Attachments,
|
||||
ChannelId = model.ChannelId,
|
||||
Text = model.Content,
|
||||
Embeds = model.Embeds,
|
||||
IsMentioningEveryone = model.IsMentioningEveryone,
|
||||
IsTTS = model.IsTextToSpeech,
|
||||
UserId = model.Author.Id,
|
||||
Timestamp = model.Timestamp
|
||||
};
|
||||
}
|
||||
|
||||
private void CheckReady()
|
||||
{
|
||||
if (!_isReady)
|
||||
throw new InvalidOperationException("The client is not currently connected to Discord");
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Discord.Net/DiscordWebSocket.Events.cs
Normal file
46
Discord.Net/DiscordWebSocket.Events.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
internal partial class DiscordWebSocket
|
||||
{
|
||||
public event EventHandler Connected;
|
||||
private void RaiseConnected()
|
||||
{
|
||||
if (Connected != null)
|
||||
Connected(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler Disconnected;
|
||||
private void RaiseDisconnected()
|
||||
{
|
||||
if (Disconnected != null)
|
||||
Disconnected(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler<MessageEventArgs> GotEvent;
|
||||
public sealed class MessageEventArgs : EventArgs
|
||||
{
|
||||
public readonly string Type;
|
||||
public readonly JToken Event;
|
||||
internal MessageEventArgs(string type, JToken data)
|
||||
{
|
||||
Type = type;
|
||||
Event = data;
|
||||
}
|
||||
}
|
||||
private void RaiseGotEvent(string type, JToken payload)
|
||||
{
|
||||
if (GotEvent != null)
|
||||
GotEvent(this, new MessageEventArgs(type, payload));
|
||||
}
|
||||
|
||||
public event EventHandler<DiscordClient.LogMessageEventArgs> OnDebugMessage;
|
||||
private void RaiseOnDebugMessage(string message)
|
||||
{
|
||||
if (OnDebugMessage != null)
|
||||
OnDebugMessage(this, new DiscordClient.LogMessageEventArgs(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
189
Discord.Net/DiscordWebSocket.cs
Normal file
189
Discord.Net/DiscordWebSocket.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using Discord.API.Models;
|
||||
using Discord.Helpers;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
internal sealed partial class DiscordWebSocket : IDisposable
|
||||
{
|
||||
private const int ReceiveChunkSize = 4096;
|
||||
private const int SendChunkSize = 4096;
|
||||
|
||||
private volatile ClientWebSocket _webSocket;
|
||||
private volatile CancellationTokenSource _cancelToken;
|
||||
private volatile Task _tasks;
|
||||
private ConcurrentQueue<byte[]> _sendQueue;
|
||||
private int _heartbeatInterval;
|
||||
private DateTime _lastHeartbeat;
|
||||
|
||||
public async Task ConnectAsync(string url, HttpOptions options)
|
||||
{
|
||||
await DisconnectAsync();
|
||||
|
||||
_sendQueue = new ConcurrentQueue<byte[]>();
|
||||
|
||||
_webSocket = new ClientWebSocket();
|
||||
_webSocket.Options.Cookies = options.Cookies;
|
||||
_webSocket.Options.KeepAliveInterval = TimeSpan.Zero;
|
||||
|
||||
_cancelToken = new CancellationTokenSource();
|
||||
var cancelToken = _cancelToken.Token;
|
||||
|
||||
await _webSocket.ConnectAsync(new Uri(url), cancelToken);
|
||||
_tasks = Task.WhenAll(
|
||||
await Task.Factory.StartNew(ReceiveAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default),
|
||||
await Task.Factory.StartNew(SendAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)
|
||||
).ContinueWith(x =>
|
||||
{
|
||||
//Do not clean up until both tasks have ended
|
||||
_heartbeatInterval = 0;
|
||||
_lastHeartbeat = DateTime.MinValue;
|
||||
_webSocket.Dispose();
|
||||
_webSocket = null;
|
||||
_cancelToken.Dispose();
|
||||
_cancelToken = null;
|
||||
_tasks = null;
|
||||
|
||||
RaiseDisconnected();
|
||||
});
|
||||
|
||||
WebSocketCommands.Login msg = new WebSocketCommands.Login();
|
||||
msg.Payload.Token = options.Token;
|
||||
msg.Payload.Properties["$os"] = "";
|
||||
msg.Payload.Properties["$browser"] = "";
|
||||
msg.Payload.Properties["$device"] = "Discord.Net";
|
||||
msg.Payload.Properties["$referrer"] = "";
|
||||
msg.Payload.Properties["$referring_domain"] = "";
|
||||
SendMessage(msg, cancelToken);
|
||||
}
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
if (_webSocket != null)
|
||||
{
|
||||
_cancelToken.Cancel();
|
||||
await _tasks;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReceiveAsync()
|
||||
{
|
||||
RaiseConnected();
|
||||
|
||||
var cancelToken = _cancelToken.Token;
|
||||
var buffer = new byte[ReceiveChunkSize];
|
||||
var builder = new StringBuilder();
|
||||
|
||||
try
|
||||
{
|
||||
while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
|
||||
{
|
||||
WebSocketReceiveResult result;
|
||||
do
|
||||
{
|
||||
result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), _cancelToken.Token);
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
|
||||
return;
|
||||
}
|
||||
else
|
||||
builder.Append(Encoding.UTF8.GetString(buffer, 0, result.Count));
|
||||
|
||||
}
|
||||
while (!result.EndOfMessage);
|
||||
|
||||
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(builder.ToString());
|
||||
switch (msg.Operation)
|
||||
{
|
||||
case 0:
|
||||
if (msg.Type == "READY")
|
||||
{
|
||||
var payload = (msg.Payload as JToken).ToObject<WebSocketEvents.Ready>();
|
||||
_heartbeatInterval = payload.HeartbeatInterval;
|
||||
SendMessage(new WebSocketCommands.UpdateStatus(), cancelToken);
|
||||
SendMessage(new WebSocketCommands.KeepAlive(), cancelToken);
|
||||
}
|
||||
RaiseGotEvent(msg.Type, msg.Payload as JToken);
|
||||
break;
|
||||
default:
|
||||
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation);
|
||||
break;
|
||||
}
|
||||
|
||||
builder.Clear();
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
finally { _cancelToken.Cancel(); }
|
||||
}
|
||||
|
||||
private async Task SendAsync()
|
||||
{
|
||||
var cancelToken = _cancelToken.Token;
|
||||
try
|
||||
{
|
||||
byte[] bytes;
|
||||
while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
|
||||
{
|
||||
if (_heartbeatInterval > 0)
|
||||
{
|
||||
DateTime now = DateTime.UtcNow;
|
||||
if ((now - _lastHeartbeat).TotalMilliseconds > _heartbeatInterval)
|
||||
{
|
||||
SendMessage(new WebSocketCommands.KeepAlive(), cancelToken);
|
||||
_lastHeartbeat = now;
|
||||
}
|
||||
}
|
||||
while (_sendQueue.TryDequeue(out bytes))
|
||||
{
|
||||
var frameCount = (int)Math.Ceiling((double)bytes.Length / SendChunkSize);
|
||||
|
||||
int offset = 0;
|
||||
for (var i = 0; i < frameCount; i++, offset += SendChunkSize)
|
||||
{
|
||||
bool isLast = i == (frameCount - 1);
|
||||
|
||||
int count;
|
||||
if (isLast)
|
||||
count = bytes.Length - (i * SendChunkSize);
|
||||
else
|
||||
count = SendChunkSize;
|
||||
|
||||
await _webSocket.SendAsync(new ArraySegment<byte>(bytes, offset, count), WebSocketMessageType.Text, isLast, cancelToken);
|
||||
}
|
||||
}
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
finally { _cancelToken.Cancel(); }
|
||||
}
|
||||
|
||||
private void SendMessage(object frame, CancellationToken token)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(frame));
|
||||
_sendQueue.Enqueue(bytes);
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool _isDisposed = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
DisconnectAsync().Wait();
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
142
Discord.Net/Helpers/Http.cs
Normal file
142
Discord.Net/Helpers/Http.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Net.Cache;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Helpers
|
||||
{
|
||||
internal class HttpOptions
|
||||
{
|
||||
public string UserAgent, Token;
|
||||
public CookieContainer Cookies;
|
||||
|
||||
public HttpOptions(string userAgent = null)
|
||||
{
|
||||
UserAgent = userAgent ?? "DiscordAPI";
|
||||
Cookies = new CookieContainer(1);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Http
|
||||
{
|
||||
private static readonly RequestCachePolicy _cachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
|
||||
|
||||
internal static async Task<ResponseT> Get<ResponseT>(string path, object data, HttpOptions options)
|
||||
where ResponseT : class
|
||||
{
|
||||
string requestJson = JsonConvert.SerializeObject(data);
|
||||
string responseJson = await SendRequest("GET", path, requestJson, options, true);
|
||||
return JsonConvert.DeserializeObject<ResponseT>(responseJson);
|
||||
}
|
||||
internal static async Task<ResponseT> Get<ResponseT>(string path, HttpOptions options)
|
||||
where ResponseT : class
|
||||
{
|
||||
string responseJson = await SendRequest("GET", path, null, options, true);
|
||||
return JsonConvert.DeserializeObject<ResponseT>(responseJson);
|
||||
}
|
||||
|
||||
internal static async Task<ResponseT> Post<ResponseT>(string path, object data, HttpOptions options)
|
||||
where ResponseT : class
|
||||
{
|
||||
string requestJson = JsonConvert.SerializeObject(data);
|
||||
string responseJson = await SendRequest("POST", path, requestJson, options, true);
|
||||
return JsonConvert.DeserializeObject<ResponseT>(responseJson);
|
||||
}
|
||||
internal static Task Post(string path, object data, HttpOptions options)
|
||||
{
|
||||
string requestJson = JsonConvert.SerializeObject(data);
|
||||
return SendRequest("POST", path, requestJson, options, false);
|
||||
}
|
||||
internal static async Task<ResponseT> Post<ResponseT>(string path, HttpOptions options)
|
||||
where ResponseT : class
|
||||
{
|
||||
string responseJson = await SendRequest("POST", path, null, options, true);
|
||||
return JsonConvert.DeserializeObject<ResponseT>(responseJson);
|
||||
}
|
||||
internal static Task Post(string path, HttpOptions options)
|
||||
{
|
||||
return SendRequest("POST", path, null, options, false);
|
||||
}
|
||||
|
||||
internal static Task Delete(string path, HttpOptions options)
|
||||
{
|
||||
return SendRequest("DELETE", path, null, options, false);
|
||||
}
|
||||
|
||||
private static async Task<string> SendRequest(string method, string path, string data, HttpOptions options, bool hasResponse)
|
||||
{
|
||||
options = options ?? new HttpOptions();
|
||||
|
||||
//Create Request
|
||||
HttpWebRequest request = WebRequest.CreateHttp(path);
|
||||
request.Accept = "*/*";
|
||||
request.Headers[HttpRequestHeader.AcceptLanguage] = "en-US;q=0.8";
|
||||
request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
|
||||
request.CachePolicy = _cachePolicy;
|
||||
request.CookieContainer = options.Cookies;
|
||||
request.Method = method;
|
||||
request.UserAgent = options.UserAgent;
|
||||
|
||||
//Add Payload
|
||||
if (data != null)
|
||||
{
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(data);
|
||||
using (var payload = await request.GetRequestStreamAsync())
|
||||
payload.Write(buffer, 0, buffer.Length);
|
||||
request.ContentType = "application/json";
|
||||
}
|
||||
|
||||
//Get Response
|
||||
using (var response = (HttpWebResponse)(await request.GetResponseAsync()))
|
||||
{
|
||||
if (hasResponse)
|
||||
{
|
||||
using (var stream = response.GetResponseStream())
|
||||
using (var reader = new BinaryReader(stream))
|
||||
using (var largeBuffer = new MemoryStream())
|
||||
{
|
||||
//Read the response in small chunks and add them to a larger buffer.
|
||||
//ContentLength isn't always provided, so this is safer.
|
||||
int bytesRead = 0;
|
||||
byte[] smallBuffer = new byte[4096];
|
||||
while ((bytesRead = reader.Read(smallBuffer, 0, smallBuffer.Length)) > 0)
|
||||
largeBuffer.Write(smallBuffer, 0, bytesRead);
|
||||
|
||||
//Do we need to decompress?
|
||||
if (!string.IsNullOrEmpty(response.ContentEncoding))
|
||||
{
|
||||
largeBuffer.Position = 0;
|
||||
using (var decoder = GetDecoder(response.ContentEncoding, largeBuffer))
|
||||
using (var decodedStream = new MemoryStream())
|
||||
{
|
||||
decoder.CopyTo(decodedStream);
|
||||
return Encoding.UTF8.GetString(decodedStream.GetBuffer(), 0, (int)decodedStream.Length);
|
||||
}
|
||||
}
|
||||
else
|
||||
return Encoding.UTF8.GetString(largeBuffer.GetBuffer(), 0, (int)largeBuffer.Length);
|
||||
}
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream GetDecoder(string contentEncoding, MemoryStream encodedStream)
|
||||
{
|
||||
switch (contentEncoding)
|
||||
{
|
||||
case "gzip":
|
||||
return new GZipStream(encodedStream, CompressionMode.Decompress, true);
|
||||
case "deflate":
|
||||
return new DeflateStream(encodedStream, CompressionMode.Decompress, true);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("Unknown encoding: " + contentEncoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Discord.Net/Models/Channel.cs
Normal file
41
Discord.Net/Models/Channel.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord.Models
|
||||
{
|
||||
public class Channel
|
||||
{
|
||||
protected readonly DiscordClient _client;
|
||||
private string _name;
|
||||
|
||||
public string Id { get; }
|
||||
public string Name { get { return !IsPrivate ? _name : '@' + Recipient.Name; } internal set { _name = value; } }
|
||||
|
||||
public bool IsPrivate { get; internal set; }
|
||||
public string Type { get; internal set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string ServerId { get; }
|
||||
[JsonIgnore]
|
||||
public Server Server { get { return ServerId != null ? _client.GetServer(ServerId) : null; } }
|
||||
|
||||
[JsonIgnore]
|
||||
public string RecipientId { get; internal set; }
|
||||
public User Recipient { get { return _client.GetUser(RecipientId); } }
|
||||
|
||||
//Not Implemented
|
||||
public object[] PermissionOverwrites { get; internal set; }
|
||||
|
||||
internal Channel(string id, string serverId, DiscordClient client)
|
||||
{
|
||||
Id = id;
|
||||
ServerId = serverId;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
//return Name + " (" + Id + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Discord.Net/Models/ChatMessage.cs
Normal file
31
Discord.Net/Models/ChatMessage.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
|
||||
namespace Discord.Models
|
||||
{
|
||||
public class ChatMessage : ChatMessageReference
|
||||
{
|
||||
public bool IsMentioningEveryone { get; internal set; }
|
||||
public bool IsTTS { get; internal set; }
|
||||
public string Text { get; internal set; }
|
||||
public DateTime Timestamp { get; internal set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string UserId { get; internal set; }
|
||||
public User User { get { return _client.GetUser(UserId); } }
|
||||
|
||||
//Not Implemented
|
||||
public object[] Attachments { get; internal set; }
|
||||
public object[] Embeds { get; internal set; }
|
||||
|
||||
internal ChatMessage(string id, DiscordClient client)
|
||||
: base(id, client)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return User.ToString() + ": " + Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Discord.Net/Models/ChatMessageReference.cs
Normal file
17
Discord.Net/Models/ChatMessageReference.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Discord.Models
|
||||
{
|
||||
public class ChatMessageReference
|
||||
{
|
||||
protected readonly DiscordClient _client;
|
||||
|
||||
public string Id { get; }
|
||||
public string ChannelId { get; internal set; }
|
||||
public Channel Channel { get { return _client.GetChannel(ChannelId); } }
|
||||
|
||||
internal ChatMessageReference(string id, DiscordClient client)
|
||||
{
|
||||
Id = id;
|
||||
_client = client;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Discord.Net/Models/Server.cs
Normal file
53
Discord.Net/Models/Server.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Discord.Models
|
||||
{
|
||||
public class Server
|
||||
{
|
||||
protected readonly DiscordClient _client;
|
||||
|
||||
public string Id { get; }
|
||||
public string Name { get; internal set; }
|
||||
|
||||
public string AFKChannelId { get; internal set; }
|
||||
public int AFKTimeout { get; internal set; }
|
||||
public DateTime JoinedAt { get; internal set; }
|
||||
public string Region { get; internal set; }
|
||||
|
||||
public string OwnerId { get; internal set; }
|
||||
public User Owner { get { return _client.GetUser(OwnerId); } }
|
||||
|
||||
internal ConcurrentDictionary<string, bool> _members;
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> MemberIds { get { return _members.Keys; } }
|
||||
public IEnumerable<User> Members { get { return _members.Keys.Select(x => _client.GetUser(x)); } }
|
||||
|
||||
internal ConcurrentDictionary<string, bool> _channels;
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> ChannelIds { get { return _channels.Keys; } }
|
||||
public IEnumerable<Channel> Channels { get { return _channels.Keys.Select(x => _client.GetChannel(x)); } }
|
||||
|
||||
//Not Implemented
|
||||
public object Presence { get; internal set; }
|
||||
public object[] Roles { get; internal set; }
|
||||
public object[] VoiceStates { get; internal set; }
|
||||
|
||||
internal Server(string id, DiscordClient client)
|
||||
{
|
||||
Id = id;
|
||||
_client = client;
|
||||
_members = new ConcurrentDictionary<string, bool>();
|
||||
_channels = new ConcurrentDictionary<string, bool>();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
//return Name + " (" + Id + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Discord.Net/Models/User.cs
Normal file
40
Discord.Net/Models/User.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
namespace Discord.Models
|
||||
{
|
||||
public class User
|
||||
{
|
||||
protected readonly DiscordClient _client;
|
||||
|
||||
public string Id { get; }
|
||||
public string Name { get; internal set; }
|
||||
|
||||
public string Avatar { get; internal set; }
|
||||
public string Discriminator { get; internal set; }
|
||||
public string Email { get; internal set; }
|
||||
public bool IsVerified { get; internal set; } = true;
|
||||
public string GameId { get; internal set; }
|
||||
public string Status { get; internal set; }
|
||||
|
||||
public DateTime LastActivity { get; private set; }
|
||||
|
||||
internal User(string id, DiscordClient client)
|
||||
{
|
||||
Id = id;
|
||||
_client = client;
|
||||
LastActivity = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
internal void UpdateActivity(DateTime activity)
|
||||
{
|
||||
if (activity > LastActivity)
|
||||
LastActivity = activity;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
//return Name + " (" + Id + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Discord.Net/Properties/AssemblyInfo.cs
Normal file
36
Discord.Net/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("DiscordAPI")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("DiscordAPI")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("8d23f61b-723c-4966-859d-1119b28bcf19")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
32
Discord.Net/Region.cs
Normal file
32
Discord.Net/Region.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public enum Region
|
||||
{
|
||||
US_West,
|
||||
US_East,
|
||||
Singapore,
|
||||
London,
|
||||
Sydney,
|
||||
Amsterdam
|
||||
}
|
||||
|
||||
internal static class RegionConverter
|
||||
{
|
||||
public static string Convert(Region region)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case Region.US_West: return "us-west";
|
||||
case Region.US_East: return "us-east";
|
||||
case Region.Singapore: return "singapore";
|
||||
case Region.London: return "london";
|
||||
case Region.Sydney: return "sydney";
|
||||
case Region.Amsterdam: return "amsterdam";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("Unknown server region");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
Discord.Net/packages.config
Normal file
4
Discord.Net/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user