@@ -0,0 +1,48 @@ | |||||
using Discord.Models; | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Discord.Net.Tests | |||||
{ | |||||
[TestClass] | |||||
public class ChannelTests | |||||
{ | |||||
private DiscordClient _bot1, _bot2; | |||||
[TestInitialize] | |||||
public void Initialize() | |||||
{ | |||||
_bot1 = new DiscordClient(); | |||||
_bot2 = new DiscordClient(); | |||||
_bot1.Connect(Settings.Test1_Username, Settings.Test1_Password).Wait(); | |||||
_bot2.Connect(Settings.Test2_Username, Settings.Test2_Password).Wait(); | |||||
//Cleanup existing servers | |||||
Task.WaitAll(_bot1.Servers.Select(x => _bot1.LeaveServer(x)).ToArray()); | |||||
Task.WaitAll(_bot2.Servers.Select(x => _bot2.LeaveServer(x)).ToArray()); | |||||
} | |||||
[TestMethod] | |||||
public async Task DoNothing() | |||||
{ | |||||
Server server = await _bot1.CreateServer("Discord.Net Testbed", Region.US_East); | |||||
Invite invite = await _bot1.CreateInvite(server, 60, 1, false, false); | |||||
await _bot2.AcceptInvite(invite); | |||||
await _bot2.LeaveServer(server); | |||||
} | |||||
[TestCleanup] | |||||
public void Cleanup() | |||||
{ | |||||
if (_bot1.IsConnected) | |||||
Task.WaitAll(_bot1.Servers.Select(x => _bot1.LeaveServer(x)).ToArray()); | |||||
if (_bot2.IsConnected) | |||||
Task.WaitAll(_bot2.Servers.Select(x => _bot2.LeaveServer(x)).ToArray()); | |||||
_bot1.Disconnect().Wait(); | |||||
_bot2.Disconnect().Wait(); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,90 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
<PropertyGroup> | |||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||||
<ProjectGuid>{855D6B1D-847B-42DA-BE6A-23683EA89511}</ProjectGuid> | |||||
<OutputType>Library</OutputType> | |||||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||||
<RootNamespace>Discord.Net.Tests</RootNamespace> | |||||
<AssemblyName>Discord.Net.Tests</AssemblyName> | |||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> | |||||
<FileAlignment>512</FileAlignment> | |||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | |||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> | |||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | |||||
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> | |||||
<IsCodedUITest>False</IsCodedUITest> | |||||
<TestProjectType>UnitTest</TestProjectType> | |||||
</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="System" /> | |||||
</ItemGroup> | |||||
<Choose> | |||||
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> | |||||
<ItemGroup> | |||||
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> | |||||
</ItemGroup> | |||||
</When> | |||||
<Otherwise> | |||||
<ItemGroup> | |||||
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" /> | |||||
</ItemGroup> | |||||
</Otherwise> | |||||
</Choose> | |||||
<ItemGroup> | |||||
<Compile Include="ChannelTests.cs" /> | |||||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||||
<Compile Include="Settings.cs" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\Discord.Net\Discord.Net.csproj"> | |||||
<Project>{8d23f61b-723c-4966-859d-1119b28bcf19}</Project> | |||||
<Name>Discord.Net</Name> | |||||
</ProjectReference> | |||||
</ItemGroup> | |||||
<Choose> | |||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> | |||||
<ItemGroup> | |||||
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<Private>False</Private> | |||||
</Reference> | |||||
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<Private>False</Private> | |||||
</Reference> | |||||
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<Private>False</Private> | |||||
</Reference> | |||||
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<Private>False</Private> | |||||
</Reference> | |||||
</ItemGroup> | |||||
</When> | |||||
</Choose> | |||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> | |||||
<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> |
@@ -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("Discord.Net.Tests")] | |||||
[assembly: AssemblyDescription("")] | |||||
[assembly: AssemblyConfiguration("")] | |||||
[assembly: AssemblyCompany("")] | |||||
[assembly: AssemblyProduct("Discord.Net.Tests")] | |||||
[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("855d6b1d-847b-42da-be6a-23683ea89511")] | |||||
// 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")] |
@@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution | |||||
LICENSE = LICENSE | LICENSE = LICENSE | ||||
EndProjectSection | EndProjectSection | ||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.Tests", "Discord.Net.Tests\Discord.Net.Tests.csproj", "{855D6B1D-847B-42DA-BE6A-23683EA89511}" | |||||
EndProject | |||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
@@ -21,6 +23,10 @@ Global | |||||
{8D23F61B-723C-4966-859D-1119B28BCF19}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU | ||||
{8D23F61B-723C-4966-859D-1119B28BCF19}.Release|Any CPU.Build.0 = Release|Any CPU | {8D23F61B-723C-4966-859D-1119B28BCF19}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{855D6B1D-847B-42DA-BE6A-23683EA89511}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{855D6B1D-847B-42DA-BE6A-23683EA89511}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{855D6B1D-847B-42DA-BE6A-23683EA89511}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{855D6B1D-847B-42DA-BE6A-23683EA89511}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
@@ -6,17 +6,18 @@ namespace Discord.API | |||||
{ | { | ||||
internal static class DiscordAPI | internal static class DiscordAPI | ||||
{ | { | ||||
public static async Task<AuthRegisterResponse> LoginAnonymous(string username, HttpOptions options) | |||||
//Auth | |||||
public static async Task<APIResponses.AuthRegister> 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); | |||||
var fingerprintResponse = await Http.Post<APIResponses.AuthFingerprint>(Endpoints.AuthFingerprint, options); | |||||
var registerRequest = new APIRequests.AuthRegisterRequest { Fingerprint = fingerprintResponse.Fingerprint, Username = username }; | |||||
var registerResponse = await Http.Post<APIResponses.AuthRegister>(Endpoints.AuthRegister, registerRequest, options); | |||||
return registerResponse; | return registerResponse; | ||||
} | } | ||||
public static async Task<AuthLoginResponse> Login(string email, string password, HttpOptions options) | |||||
public static async Task<APIResponses.AuthLogin> 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); | |||||
var request = new APIRequests.AuthLogin { Email = email, Password = password }; | |||||
var response = await Http.Post<APIResponses.AuthLogin>(Endpoints.AuthLogin, request, options); | |||||
options.Token = response.Token; | options.Token = response.Token; | ||||
return response; | return response; | ||||
} | } | ||||
@@ -25,37 +26,61 @@ namespace Discord.API | |||||
return Http.Post(Endpoints.AuthLogout, options); | return Http.Post(Endpoints.AuthLogout, options); | ||||
} | } | ||||
public static Task CreateServer(string name, string region, HttpOptions options) | |||||
//Servers | |||||
public static Task<APIResponses.CreateServer> CreateServer(string name, string region, HttpOptions options) | |||||
{ | { | ||||
var request = new CreateServerRequest { Name = name, Region = region }; | |||||
return Http.Post(Endpoints.Servers, request, options); | |||||
var request = new APIRequests.CreateServer { Name = name, Region = region }; | |||||
return Http.Post<APIResponses.CreateServer>(Endpoints.Servers, request, options); | |||||
} | |||||
public static Task LeaveServer(string id, HttpOptions options) | |||||
{ | |||||
return Http.Delete<APIResponses.DeleteServer>(Endpoints.Server(id), options); | |||||
} | } | ||||
public static Task DeleteServer(string id, HttpOptions options) | |||||
//Channels | |||||
public static Task<APIResponses.GetMessages[]> GetMessages(string channelId, HttpOptions options) | |||||
{ | { | ||||
return Http.Delete(Endpoints.Server(id), options); | |||||
} | |||||
return Http.Get<APIResponses.GetMessages[]>(Endpoints.ChannelMessages(channelId, 50), options); | |||||
} | |||||
public static Task<GetInviteResponse> GetInvite(string id, HttpOptions options) | |||||
//Invites | |||||
public static Task<APIResponses.CreateInvite> CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass, HttpOptions options) | |||||
{ | { | ||||
return Http.Get<GetInviteResponse>(Endpoints.Invite(id), options); | |||||
var request = new APIRequests.CreateInvite { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = isTemporary, HasXkcdPass = hasXkcdPass }; | |||||
return Http.Post<APIResponses.CreateInvite>(Endpoints.ChannelInvites(channelId), request, options); | |||||
} | |||||
public static Task<APIResponses.GetInvite> GetInvite(string id, HttpOptions options) | |||||
{ | |||||
return Http.Get<APIResponses.GetInvite>(Endpoints.Invite(id), options); | |||||
} | } | ||||
public static Task AcceptInvite(string id, HttpOptions options) | public static Task AcceptInvite(string id, HttpOptions options) | ||||
{ | { | ||||
return Http.Post(Endpoints.Invite(id), options); | |||||
return Http.Post<APIResponses.AcceptInvite>(Endpoints.Invite(id), options); | |||||
} | } | ||||
public static Task DeleteInvite(string id, HttpOptions options) | public static Task DeleteInvite(string id, HttpOptions options) | ||||
{ | { | ||||
return Http.Delete(Endpoints.Invite(id), options); | return Http.Delete(Endpoints.Invite(id), options); | ||||
} | } | ||||
public static Task Typing(string channelId, HttpOptions options) | |||||
//Chat | |||||
public static Task SendMessage(string channelId, string message, string[] mentions, HttpOptions options) | |||||
{ | |||||
var request = new APIRequests.SendMessage { Content = message, Mentions = mentions }; | |||||
return Http.Post(Endpoints.ChannelMessages(channelId), request, options); | |||||
} | |||||
public static Task SendIsTyping(string channelId, HttpOptions options) | |||||
{ | { | ||||
return Http.Post(Endpoints.ChannelTyping(channelId), options); | return Http.Post(Endpoints.ChannelTyping(channelId), options); | ||||
} | } | ||||
public static Task SendMessage(string channelId, string message, string[] mentions, HttpOptions options) | |||||
//Voice | |||||
public static Task<APIResponses.GetRegions[]> GetVoiceRegions(HttpOptions options) | |||||
{ | { | ||||
var request = new SendMessageRequest { Content = message, Mentions = mentions }; | |||||
return Http.Post(Endpoints.ChannelMessages(channelId), request, options); | |||||
return Http.Get<APIResponses.GetRegions[]>(Endpoints.VoiceRegions, options); | |||||
} | |||||
public static Task<APIResponses.GetIce> GetVoiceIce(HttpOptions options) | |||||
{ | |||||
return Http.Get<APIResponses.GetIce>(Endpoints.VoiceIce, options); | |||||
} | } | ||||
} | |||||
} | |||||
} | } |
@@ -2,29 +2,43 @@ | |||||
{ | { | ||||
internal static class Endpoints | 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 BaseUrl = "discordapp.com"; | |||||
public static readonly string BaseHttps = $"https://{BaseUrl}"; | |||||
// /api | |||||
public static readonly string BaseApi = $"{BaseHttps}/api"; | |||||
public static readonly string Track = $"{BaseApi}/track"; | |||||
public static readonly string Auth = $"{BaseHttps}/api/auth"; | |||||
// /api/auth | |||||
public static readonly string Auth = $"{BaseApi}/auth"; | |||||
public static readonly string AuthFingerprint = $"{Auth}fingerprint"; | public static readonly string AuthFingerprint = $"{Auth}fingerprint"; | ||||
public static readonly string AuthRegister = $"{Auth}/register"; | public static readonly string AuthRegister = $"{Auth}/register"; | ||||
public static readonly string AuthLogin = $"{Auth}/login"; | public static readonly string AuthLogin = $"{Auth}/login"; | ||||
public static readonly string AuthLogout = $"{Auth}/logout"; | public static readonly string AuthLogout = $"{Auth}/logout"; | ||||
public static readonly string Servers = $"{BaseHttps}/api/guilds"; | |||||
// /api/guilds | |||||
public static readonly string Servers = $"{BaseApi}/guilds"; | |||||
public static string Server(string id) { return $"{Servers}/{id}"; } | 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"; | |||||
// /api/guilds | |||||
public static readonly string Invites = $"{BaseApi}/invite"; | |||||
public static string Invite(string id) { return $"{Invites}/{id}"; } | public static string Invite(string id) { return $"{Invites}/{id}"; } | ||||
public static readonly string Channels = $"{BaseHttps}/api/channels"; | |||||
// /api/channels | |||||
public static readonly string Channels = $"{BaseApi}/channels"; | |||||
public static string Channel(string id) { return $"{Channels}/{id}"; } | public static string Channel(string id) { return $"{Channels}/{id}"; } | ||||
public static string ChannelTyping(string id) { return $"{Channels}/{id}/typing"; } | public static string ChannelTyping(string id) { return $"{Channels}/{id}/typing"; } | ||||
public static string ChannelMessages(string id) { return $"{Channels}/{id}/messages"; } | public static string ChannelMessages(string id) { return $"{Channels}/{id}/messages"; } | ||||
public static string ChannelMessages(string id, int limit) { return $"{Channels}/{id}/messages?limit={limit}"; } | |||||
public static string ChannelInvites(string id) { return $"{Channels}/{id}/invites"; } | |||||
public static readonly string WebSocket_Hub = BaseWss + "hub"; | |||||
// /api/voice | |||||
public static readonly string Voice = $"{BaseApi}/voice"; | |||||
public static readonly string VoiceRegions = $"{Voice}/regions"; | |||||
public static readonly string VoiceIce = $"{Voice}/ice"; | |||||
//Web Sockets | |||||
public static readonly string BaseWss = "wss://" + BaseUrl; | |||||
public static readonly string WebSocket_Hub = $"{BaseWss}/hub"; | |||||
} | } | ||||
} | } |
@@ -0,0 +1,91 @@ | |||||
//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 APIResponses | |||||
{ | |||||
public class AuthFingerprint | |||||
{ | |||||
[JsonProperty(PropertyName = "fingerprint")] | |||||
public string Fingerprint; | |||||
} | |||||
public class AuthRegister : AuthLogin { } | |||||
public class AuthLogin | |||||
{ | |||||
[JsonProperty(PropertyName = "token")] | |||||
public string Token; | |||||
} | |||||
public class CreateServer : ServerInfo { } | |||||
public class DeleteServer : ServerInfo { } | |||||
public class CreateInvite : GetInvite | |||||
{ | |||||
[JsonProperty(PropertyName = "max_age")] | |||||
public int MaxAge; | |||||
[JsonProperty(PropertyName = "max_uses")] | |||||
public int MaxUses; | |||||
[JsonProperty(PropertyName = "revoked")] | |||||
public bool IsRevoked; | |||||
[JsonProperty(PropertyName = "temporary")] | |||||
public bool IsTemporary; | |||||
[JsonProperty(PropertyName = "uses")] | |||||
public int Uses; | |||||
[JsonProperty(PropertyName = "created_at")] | |||||
public DateTime CreatedAt; | |||||
} | |||||
public class GetInvite | |||||
{ | |||||
[JsonProperty(PropertyName = "inviter")] | |||||
public UserReference Inviter; | |||||
[JsonProperty(PropertyName = "guild")] | |||||
public ServerReference Server; | |||||
[JsonProperty(PropertyName = "channel")] | |||||
public ChannelReference Channel; | |||||
[JsonProperty(PropertyName = "code")] | |||||
public string Code; | |||||
[JsonProperty(PropertyName = "xkcdpass")] | |||||
public string XkcdPass; | |||||
} | |||||
public class AcceptInvite : GetInvite { } | |||||
public class GetMessages : Message { } | |||||
public class GetRegions | |||||
{ | |||||
[JsonProperty(PropertyName = "sample_hostname")] | |||||
public string Hostname; | |||||
[JsonProperty(PropertyName = "sample_port")] | |||||
public int Port; | |||||
[JsonProperty(PropertyName = "id")] | |||||
public string Id; | |||||
[JsonProperty(PropertyName = "name")] | |||||
public string Name; | |||||
} | |||||
public class GetIce | |||||
{ | |||||
[JsonProperty(PropertyName = "ttl")] | |||||
public string TTL; | |||||
[JsonProperty(PropertyName = "servers")] | |||||
public Server[] Servers; | |||||
public class Server | |||||
{ | |||||
[JsonProperty(PropertyName = "url")] | |||||
public string URL; | |||||
[JsonProperty(PropertyName = "username")] | |||||
public string Username; | |||||
[JsonProperty(PropertyName = "credential")] | |||||
public string Credential; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -6,61 +6,49 @@ using Newtonsoft.Json; | |||||
namespace Discord.API.Models | namespace Discord.API.Models | ||||
{ | { | ||||
public class AuthFingerprintResponse | |||||
internal static class APIRequests | |||||
{ | { | ||||
[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 AuthRegisterRequest | |||||
{ | |||||
[JsonProperty(PropertyName = "fingerprint")] | |||||
public string Fingerprint; | |||||
[JsonProperty(PropertyName = "username")] | |||||
public string Username; | |||||
} | |||||
public class AuthLogin | |||||
{ | |||||
[JsonProperty(PropertyName = "email")] | |||||
public string Email; | |||||
[JsonProperty(PropertyName = "password")] | |||||
public string Password; | |||||
} | |||||
public class CreateServerRequest | |||||
{ | |||||
[JsonProperty(PropertyName = "name")] | |||||
public string Name; | |||||
[JsonProperty(PropertyName = "region")] | |||||
public string Region; | |||||
} | |||||
public class CreateServer | |||||
{ | |||||
[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 CreateInvite | |||||
{ | |||||
[JsonProperty(PropertyName = "max_age")] | |||||
public int MaxAge; | |||||
[JsonProperty(PropertyName = "max_uses")] | |||||
public int MaxUses; | |||||
[JsonProperty(PropertyName = "temporary")] | |||||
public bool IsTemporary; | |||||
[JsonProperty(PropertyName = "xkcdpass")] | |||||
public bool HasXkcdPass; | |||||
} | |||||
public class SendMessageRequest | |||||
{ | |||||
[JsonProperty(PropertyName = "content")] | |||||
public string Content; | |||||
[JsonProperty(PropertyName = "mentions")] | |||||
public string[] Mentions; | |||||
public class SendMessage | |||||
{ | |||||
[JsonProperty(PropertyName = "content")] | |||||
public string Content; | |||||
[JsonProperty(PropertyName = "mentions")] | |||||
public string[] Mentions; | |||||
} | |||||
} | } | ||||
} | } |
@@ -32,7 +32,8 @@ namespace Discord.API.Models | |||||
} | } | ||||
} | } | ||||
public class UserInfo | |||||
//Users | |||||
internal class UserReference | |||||
{ | { | ||||
[JsonProperty(PropertyName = "username")] | [JsonProperty(PropertyName = "username")] | ||||
public string Username; | public string Username; | ||||
@@ -43,14 +44,14 @@ namespace Discord.API.Models | |||||
[JsonProperty(PropertyName = "avatar")] | [JsonProperty(PropertyName = "avatar")] | ||||
public string Avatar; | public string Avatar; | ||||
} | } | ||||
public class SelfUserInfo : UserInfo | |||||
internal class SelfUserInfo : UserReference | |||||
{ | { | ||||
[JsonProperty(PropertyName = "email")] | [JsonProperty(PropertyName = "email")] | ||||
public string Email; | public string Email; | ||||
[JsonProperty(PropertyName = "verified")] | [JsonProperty(PropertyName = "verified")] | ||||
public bool IsVerified; | public bool IsVerified; | ||||
} | } | ||||
public class PresenceUserInfo : UserInfo | |||||
internal class PresenceUserInfo : UserReference | |||||
{ | { | ||||
[JsonProperty(PropertyName = "game_id")] | [JsonProperty(PropertyName = "game_id")] | ||||
public string GameId; | public string GameId; | ||||
@@ -58,78 +59,115 @@ namespace Discord.API.Models | |||||
public string 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 | |||||
//Channels | |||||
internal class ChannelReference | |||||
{ | { | ||||
[JsonProperty(PropertyName = "id")] | [JsonProperty(PropertyName = "id")] | ||||
public string Id; | public string Id; | ||||
[JsonProperty(PropertyName = "guild_id")] | |||||
public string GuildId; | |||||
[JsonProperty(PropertyName = "name")] | [JsonProperty(PropertyName = "name")] | ||||
public string Name; | public string Name; | ||||
[JsonProperty(PropertyName = "type")] | |||||
public string Type; | |||||
} | |||||
internal class ChannelInfo : ChannelReference | |||||
{ | |||||
[JsonProperty(PropertyName = "last_message_id")] | [JsonProperty(PropertyName = "last_message_id")] | ||||
public string LastMessageId; | public string LastMessageId; | ||||
[JsonProperty(PropertyName = "is_private")] | [JsonProperty(PropertyName = "is_private")] | ||||
public bool IsPrivate; | public bool IsPrivate; | ||||
[JsonProperty(PropertyName = "type")] | |||||
public string Type; | |||||
[JsonProperty(PropertyName = "permission_overwrites")] | [JsonProperty(PropertyName = "permission_overwrites")] | ||||
public object[] PermissionOverwrites; | public object[] PermissionOverwrites; | ||||
[JsonProperty(PropertyName = "recipient")] | [JsonProperty(PropertyName = "recipient")] | ||||
public UserInfo Recipient; | |||||
public UserReference Recipient; | |||||
} | } | ||||
public class ServerInfo | |||||
//Servers | |||||
internal class ServerReference | |||||
{ | { | ||||
[JsonProperty(PropertyName = "id")] | [JsonProperty(PropertyName = "id")] | ||||
public string Id; | public string Id; | ||||
[JsonProperty(PropertyName = "name")] | [JsonProperty(PropertyName = "name")] | ||||
public string Name; | public string Name; | ||||
} | } | ||||
public class ExtendedServerInfo : ServerInfo | |||||
internal class ServerInfo : ServerReference | |||||
{ | { | ||||
[JsonProperty(PropertyName = "afk_channel_id")] | [JsonProperty(PropertyName = "afk_channel_id")] | ||||
public string AFKChannelId; | public string AFKChannelId; | ||||
[JsonProperty(PropertyName = "afk_timeout")] | [JsonProperty(PropertyName = "afk_timeout")] | ||||
public int AFKTimeout; | public int AFKTimeout; | ||||
[JsonProperty(PropertyName = "channels")] | |||||
public ChannelInfo[] Channels; | |||||
[JsonProperty(PropertyName = "embed_channel_id")] | |||||
public string EmbedChannelId; | |||||
[JsonProperty(PropertyName = "embed_enabled")] | |||||
public bool EmbedEnabled; | |||||
[JsonProperty(PropertyName = "joined_at")] | [JsonProperty(PropertyName = "joined_at")] | ||||
public DateTime JoinedAt; | |||||
[JsonProperty(PropertyName = "members")] | |||||
public MembershipInfo[] Members; | |||||
public DateTime? JoinedAt; | |||||
[JsonProperty(PropertyName = "owner_id")] | [JsonProperty(PropertyName = "owner_id")] | ||||
public string OwnerId; | public string OwnerId; | ||||
[JsonProperty(PropertyName = "presence")] | |||||
public object[] Presence; | |||||
[JsonProperty(PropertyName = "region")] | [JsonProperty(PropertyName = "region")] | ||||
public string Region; | public string Region; | ||||
[JsonProperty(PropertyName = "roles")] | [JsonProperty(PropertyName = "roles")] | ||||
public object[] Roles; | |||||
public Role[] Roles; | |||||
} | |||||
internal class ExtendedServerInfo : ServerInfo | |||||
{ | |||||
public class Membership | |||||
{ | |||||
[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 UserReference User; | |||||
} | |||||
[JsonProperty(PropertyName = "channels")] | |||||
public ChannelInfo[] Channels; | |||||
[JsonProperty(PropertyName = "members")] | |||||
public Membership[] Members; | |||||
[JsonProperty(PropertyName = "presence")] | |||||
public object[] Presence; | |||||
[JsonProperty(PropertyName = "voice_states")] | [JsonProperty(PropertyName = "voice_states")] | ||||
public object[] VoiceStates; | public object[] VoiceStates; | ||||
} | } | ||||
//Messages | |||||
internal class MessageReference | internal class MessageReference | ||||
{ | { | ||||
[JsonProperty(PropertyName = "message_id")] | |||||
public string MessageId; | |||||
[JsonProperty(PropertyName = "id")] | |||||
public string Id; | |||||
[JsonProperty(PropertyName = "channel_id")] | [JsonProperty(PropertyName = "channel_id")] | ||||
public string ChannelId; | public string ChannelId; | ||||
[JsonProperty(PropertyName = "message_id")] | |||||
public string MessageId { get { return Id; } set { Id = value; } } | |||||
} | |||||
internal class Message : MessageReference | |||||
{ | |||||
[JsonProperty(PropertyName = "tts")] | |||||
public bool IsTextToSpeech; | |||||
[JsonProperty(PropertyName = "mention_everyone")] | |||||
public bool IsMentioningEveryone; | |||||
[JsonProperty(PropertyName = "timestamp")] | |||||
public DateTime Timestamp; | |||||
[JsonProperty(PropertyName = "mentions")] | |||||
public UserReference[] Mentions; | |||||
[JsonProperty(PropertyName = "embeds")] | |||||
public object[] Embeds; | |||||
[JsonProperty(PropertyName = "attachments")] | |||||
public object[] Attachments; | |||||
[JsonProperty(PropertyName = "content")] | |||||
public string Content; | |||||
[JsonProperty(PropertyName = "author")] | |||||
public UserReference Author; | |||||
} | } | ||||
internal class Role | |||||
//Roles | |||||
internal class Role | |||||
{ | { | ||||
[JsonProperty(PropertyName = "permissions")] | [JsonProperty(PropertyName = "permissions")] | ||||
public int Permissions; | public int Permissions; |
@@ -25,65 +25,72 @@ namespace Discord.API.Models | |||||
public int HeartbeatInterval; | public int HeartbeatInterval; | ||||
} | } | ||||
//Servers | |||||
internal sealed class GuildCreate : ExtendedServerInfo { } | internal sealed class GuildCreate : ExtendedServerInfo { } | ||||
internal sealed class GuildDelete : ExtendedServerInfo { } | internal sealed class GuildDelete : ExtendedServerInfo { } | ||||
//Channels | |||||
internal sealed class ChannelCreate : ChannelInfo { } | internal sealed class ChannelCreate : ChannelInfo { } | ||||
internal sealed class ChannelDelete : ChannelInfo { } | internal sealed class ChannelDelete : ChannelInfo { } | ||||
internal sealed class ChannelUpdate : ChannelInfo { } | internal sealed class ChannelUpdate : ChannelInfo { } | ||||
internal sealed class GuildMemberAdd : GuildMemberUpdate | |||||
//Memberships | |||||
internal abstract class GuildMemberEvent | |||||
{ | |||||
[JsonProperty(PropertyName = "user")] | |||||
public UserReference User; | |||||
[JsonProperty(PropertyName = "guild_id")] | |||||
public string GuildId; | |||||
} | |||||
internal sealed class GuildMemberAdd : GuildMemberEvent | |||||
{ | { | ||||
[JsonProperty(PropertyName = "joined_at")] | [JsonProperty(PropertyName = "joined_at")] | ||||
public DateTime JoinedAt; | public DateTime JoinedAt; | ||||
[JsonProperty(PropertyName = "roles")] | |||||
public object[] Roles; | |||||
} | } | ||||
internal class GuildMemberUpdate | |||||
internal sealed class GuildMemberUpdate : GuildMemberEvent | |||||
{ | { | ||||
[JsonProperty(PropertyName = "user")] | |||||
public UserInfo User; | |||||
[JsonProperty(PropertyName = "roles")] | [JsonProperty(PropertyName = "roles")] | ||||
public object[] Roles; | public object[] Roles; | ||||
[JsonProperty(PropertyName = "guild_id")] | |||||
public string GuildId; | |||||
} | } | ||||
internal sealed class GuildMemberRemove | |||||
internal sealed class GuildMemberRemove : GuildMemberEvent { } | |||||
//Roles | |||||
internal abstract class GuildRoleEvent | |||||
{ | { | ||||
[JsonProperty(PropertyName = "user")] | |||||
public UserInfo User; | |||||
[JsonProperty(PropertyName = "guild_id")] | [JsonProperty(PropertyName = "guild_id")] | ||||
public string GuildId; | public string GuildId; | ||||
} | } | ||||
internal sealed class GuildRoleCreateUpdate | |||||
internal sealed class GuildRoleCreateUpdate : GuildRoleEvent | |||||
{ | { | ||||
[JsonProperty(PropertyName = "role")] | [JsonProperty(PropertyName = "role")] | ||||
public Role Role; | public Role Role; | ||||
[JsonProperty(PropertyName = "guild_id")] | |||||
public string GuildId; | |||||
} | } | ||||
internal sealed class GuildRoleDelete | |||||
internal sealed class GuildRoleDelete : GuildRoleEvent | |||||
{ | { | ||||
[JsonProperty(PropertyName = "role_id")] | [JsonProperty(PropertyName = "role_id")] | ||||
public string RoleId; | public string RoleId; | ||||
[JsonProperty(PropertyName = "guild_id")] | |||||
public string GuildId; | |||||
} | } | ||||
internal sealed class GuildBanAddRemove | |||||
//Bans | |||||
internal abstract class GuildBanEvent | |||||
{ | { | ||||
[JsonProperty(PropertyName = "user")] | |||||
public UserInfo User; | |||||
[JsonProperty(PropertyName = "guild_id")] | [JsonProperty(PropertyName = "guild_id")] | ||||
public string GuildId; | public string GuildId; | ||||
} | } | ||||
internal sealed class GuildBanRemove | |||||
internal sealed class GuildBanAddRemove : GuildBanEvent | |||||
{ | |||||
[JsonProperty(PropertyName = "user")] | |||||
public UserReference User; | |||||
} | |||||
internal sealed class GuildBanRemove : GuildBanEvent | |||||
{ | { | ||||
[JsonProperty(PropertyName = "user_id")] | [JsonProperty(PropertyName = "user_id")] | ||||
public string UserId; | public string UserId; | ||||
[JsonProperty(PropertyName = "guild_id")] | |||||
public string GuildId; | |||||
} | } | ||||
//User | |||||
internal sealed class UserUpdate : SelfUserInfo { } | internal sealed class UserUpdate : SelfUserInfo { } | ||||
internal sealed class PresenceUpdate : PresenceUserInfo { } | internal sealed class PresenceUpdate : PresenceUserInfo { } | ||||
internal sealed class VoiceStateUpdate | internal sealed class VoiceStateUpdate | ||||
@@ -107,35 +114,11 @@ namespace Discord.API.Models | |||||
[JsonProperty(PropertyName = "deaf")] | [JsonProperty(PropertyName = "deaf")] | ||||
public bool IsDeafened; | 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 | |||||
//Chat | |||||
internal sealed class MessageCreate : Message { } | |||||
internal sealed class MessageUpdate : MessageReference | |||||
{ | { | ||||
[JsonProperty(PropertyName = "id")] | |||||
public string Id; | |||||
[JsonProperty(PropertyName = "channel_id")] | |||||
public string ChannelId; | |||||
[JsonProperty(PropertyName = "embeds")] | [JsonProperty(PropertyName = "embeds")] | ||||
public object[] Embeds; | public object[] Embeds; | ||||
} | } | ||||
@@ -150,5 +133,14 @@ namespace Discord.API.Models | |||||
[JsonProperty(PropertyName = "timestamp")] | [JsonProperty(PropertyName = "timestamp")] | ||||
public int Timestamp; | public int Timestamp; | ||||
} | } | ||||
//Voice | |||||
internal sealed class VoiceServerUpdate | |||||
{ | |||||
[JsonProperty(PropertyName = "guild_id")] | |||||
public string ServerId; | |||||
[JsonProperty(PropertyName = "endpoint")] | |||||
public string Endpoint; | |||||
} | |||||
} | } | ||||
} | } |
@@ -45,13 +45,15 @@ | |||||
<Reference Include="System.Xml" /> | <Reference Include="System.Xml" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Compile Include="API\Models\General.cs" /> | |||||
<Compile Include="API\Models\ApiRequests.cs" /> | |||||
<Compile Include="API\Models\Common.cs" /> | |||||
<Compile Include="API\Models\APIRequests.cs" /> | |||||
<Compile Include="API\Endpoints.cs" /> | <Compile Include="API\Endpoints.cs" /> | ||||
<Compile Include="API\Models\APIResponses.cs" /> | |||||
<Compile Include="API\Models\WebSocketCommands.cs" /> | <Compile Include="API\Models\WebSocketCommands.cs" /> | ||||
<Compile Include="Helpers\AsyncCache.cs" /> | |||||
<Compile Include="Models\Invite.cs" /> | |||||
<Compile Include="Models\Role.cs" /> | <Compile Include="Models\Role.cs" /> | ||||
<Compile Include="Models\ChatMessageReference.cs" /> | |||||
<Compile Include="Models\ChatMessage.cs" /> | |||||
<Compile Include="Models\Message.cs" /> | |||||
<Compile Include="Models\Channel.cs" /> | <Compile Include="Models\Channel.cs" /> | ||||
<Compile Include="DiscordWebSocket.Events.cs" /> | <Compile Include="DiscordWebSocket.Events.cs" /> | ||||
<Compile Include="Helpers\Http.cs" /> | <Compile Include="Helpers\Http.cs" /> | ||||
@@ -11,7 +11,6 @@ namespace Discord | |||||
public readonly string Message; | public readonly string Message; | ||||
internal LogMessageEventArgs(string msg) { Message = msg; } | internal LogMessageEventArgs(string msg) { Message = msg; } | ||||
} | } | ||||
public event EventHandler<LogMessageEventArgs> DebugMessage; | public event EventHandler<LogMessageEventArgs> DebugMessage; | ||||
private void RaiseOnDebugMessage(string message) | private void RaiseOnDebugMessage(string message) | ||||
{ | { | ||||
@@ -26,7 +25,6 @@ namespace Discord | |||||
if (Connected != null) | if (Connected != null) | ||||
Connected(this, EventArgs.Empty); | Connected(this, EventArgs.Empty); | ||||
} | } | ||||
public event EventHandler Disconnected; | public event EventHandler Disconnected; | ||||
private void RaiseDisconnected() | private void RaiseDisconnected() | ||||
{ | { | ||||
@@ -34,12 +32,12 @@ namespace Discord | |||||
Disconnected(this, EventArgs.Empty); | Disconnected(this, EventArgs.Empty); | ||||
} | } | ||||
public event EventHandler LoggedIn; | |||||
/*public event EventHandler LoggedIn; | |||||
private void RaiseLoggedIn() | private void RaiseLoggedIn() | ||||
{ | { | ||||
if (LoggedIn != null) | if (LoggedIn != null) | ||||
LoggedIn(this, EventArgs.Empty); | LoggedIn(this, EventArgs.Empty); | ||||
} | |||||
}*/ | |||||
//Server | //Server | ||||
public sealed class ServerEventArgs : EventArgs | public sealed class ServerEventArgs : EventArgs | ||||
@@ -54,7 +52,6 @@ namespace Discord | |||||
if (ServerCreated != null) | if (ServerCreated != null) | ||||
ServerCreated(this, new ServerEventArgs(server)); | ServerCreated(this, new ServerEventArgs(server)); | ||||
} | } | ||||
public event EventHandler<ServerEventArgs> ServerDestroyed; | public event EventHandler<ServerEventArgs> ServerDestroyed; | ||||
private void RaiseServerDestroyed(Server server) | private void RaiseServerDestroyed(Server server) | ||||
{ | { | ||||
@@ -75,14 +72,12 @@ namespace Discord | |||||
if (ChannelCreated != null) | if (ChannelCreated != null) | ||||
ChannelCreated(this, new ChannelEventArgs(channel)); | ChannelCreated(this, new ChannelEventArgs(channel)); | ||||
} | } | ||||
public event EventHandler<ChannelEventArgs> ChannelDestroyed; | public event EventHandler<ChannelEventArgs> ChannelDestroyed; | ||||
private void RaiseChannelDestroyed(Channel channel) | private void RaiseChannelDestroyed(Channel channel) | ||||
{ | { | ||||
if (ChannelDestroyed != null) | if (ChannelDestroyed != null) | ||||
ChannelDestroyed(this, new ChannelEventArgs(channel)); | ChannelDestroyed(this, new ChannelEventArgs(channel)); | ||||
} | } | ||||
public event EventHandler<ChannelEventArgs> ChannelUpdated; | public event EventHandler<ChannelEventArgs> ChannelUpdated; | ||||
private void RaiseChannelUpdated(Channel channel) | private void RaiseChannelUpdated(Channel channel) | ||||
{ | { | ||||
@@ -98,40 +93,32 @@ namespace Discord | |||||
} | } | ||||
//Message | //Message | ||||
public sealed class MessageCreateEventArgs : EventArgs | |||||
{ | |||||
public readonly ChatMessage Message; | |||||
internal MessageCreateEventArgs(ChatMessage msg) { Message = msg; } | |||||
} | |||||
public sealed class MessageEventArgs : EventArgs | public sealed class MessageEventArgs : EventArgs | ||||
{ | { | ||||
public readonly ChatMessageReference Message; | |||||
internal MessageEventArgs(ChatMessageReference msg) { Message = msg; } | |||||
public readonly Message Message; | |||||
internal MessageEventArgs(Message msg) { Message = msg; } | |||||
} | } | ||||
public event EventHandler<MessageCreateEventArgs> MessageCreated; | |||||
private void RaiseMessageCreated(ChatMessage msg) | |||||
public event EventHandler<MessageEventArgs> MessageCreated; | |||||
private void RaiseMessageCreated(Message msg) | |||||
{ | { | ||||
if (MessageCreated != null) | if (MessageCreated != null) | ||||
MessageCreated(this, new MessageCreateEventArgs(msg)); | |||||
MessageCreated(this, new MessageEventArgs(msg)); | |||||
} | } | ||||
public event EventHandler<MessageEventArgs> MessageDeleted; | public event EventHandler<MessageEventArgs> MessageDeleted; | ||||
private void RaiseMessageDeleted(ChatMessageReference msg) | |||||
private void RaiseMessageDeleted(Message msg) | |||||
{ | { | ||||
if (MessageDeleted != null) | if (MessageDeleted != null) | ||||
MessageDeleted(this, new MessageEventArgs(msg)); | MessageDeleted(this, new MessageEventArgs(msg)); | ||||
} | } | ||||
public event EventHandler<MessageEventArgs> MessageUpdated; | public event EventHandler<MessageEventArgs> MessageUpdated; | ||||
private void RaiseMessageUpdated(ChatMessageReference msg) | |||||
private void RaiseMessageUpdated(Message msg) | |||||
{ | { | ||||
if (MessageUpdated != null) | if (MessageUpdated != null) | ||||
MessageUpdated(this, new MessageEventArgs(msg)); | MessageUpdated(this, new MessageEventArgs(msg)); | ||||
} | } | ||||
public event EventHandler<MessageEventArgs> MessageAcknowledged; | public event EventHandler<MessageEventArgs> MessageAcknowledged; | ||||
private void RaiseMessageAcknowledged(ChatMessageReference msg) | |||||
private void RaiseMessageAcknowledged(Message msg) | |||||
{ | { | ||||
if (MessageAcknowledged != null) | if (MessageAcknowledged != null) | ||||
MessageAcknowledged(this, new MessageEventArgs(msg)); | MessageAcknowledged(this, new MessageEventArgs(msg)); | ||||
@@ -150,14 +137,12 @@ namespace Discord | |||||
if (RoleCreated != null) | if (RoleCreated != null) | ||||
RoleCreated(this, new RoleEventArgs(role)); | RoleCreated(this, new RoleEventArgs(role)); | ||||
} | } | ||||
public event EventHandler<RoleEventArgs> RoleUpdated; | public event EventHandler<RoleEventArgs> RoleUpdated; | ||||
private void RaiseRoleDeleted(Role role) | private void RaiseRoleDeleted(Role role) | ||||
{ | { | ||||
if (RoleDeleted != null) | if (RoleDeleted != null) | ||||
RoleDeleted(this, new RoleEventArgs(role)); | RoleDeleted(this, new RoleEventArgs(role)); | ||||
} | } | ||||
public event EventHandler<RoleEventArgs> RoleDeleted; | public event EventHandler<RoleEventArgs> RoleDeleted; | ||||
private void RaiseRoleUpdated(Role role) | private void RaiseRoleUpdated(Role role) | ||||
{ | { | ||||
@@ -183,7 +168,6 @@ namespace Discord | |||||
if (BanAdded != null) | if (BanAdded != null) | ||||
BanAdded(this, new BanEventArgs(user, server)); | BanAdded(this, new BanEventArgs(user, server)); | ||||
} | } | ||||
public event EventHandler<BanEventArgs> BanRemoved; | public event EventHandler<BanEventArgs> BanRemoved; | ||||
private void RaiseBanRemoved(User user, Server server) | private void RaiseBanRemoved(User user, Server server) | ||||
{ | { | ||||
@@ -209,14 +193,12 @@ namespace Discord | |||||
if (MemberAdded != null) | if (MemberAdded != null) | ||||
MemberAdded(this, new MemberEventArgs(user, server)); | MemberAdded(this, new MemberEventArgs(user, server)); | ||||
} | } | ||||
public event EventHandler<MemberEventArgs> MemberRemoved; | public event EventHandler<MemberEventArgs> MemberRemoved; | ||||
private void RaiseMemberRemoved(User user, Server server) | private void RaiseMemberRemoved(User user, Server server) | ||||
{ | { | ||||
if (MemberRemoved != null) | if (MemberRemoved != null) | ||||
MemberRemoved(this, new MemberEventArgs(user, server)); | MemberRemoved(this, new MemberEventArgs(user, server)); | ||||
} | } | ||||
public event EventHandler<MemberEventArgs> MemberUpdated; | public event EventHandler<MemberEventArgs> MemberUpdated; | ||||
private void RaiseMemberUpdated(User user, Server server) | private void RaiseMemberUpdated(User user, Server server) | ||||
{ | { | ||||
@@ -242,19 +224,36 @@ namespace Discord | |||||
if (PresenceUpdated != null) | if (PresenceUpdated != null) | ||||
PresenceUpdated(this, new UserEventArgs(user)); | PresenceUpdated(this, new UserEventArgs(user)); | ||||
} | } | ||||
public event EventHandler<UserEventArgs> VoiceStateUpdated; | public event EventHandler<UserEventArgs> VoiceStateUpdated; | ||||
private void RaiseVoiceStateUpdated(User user) | private void RaiseVoiceStateUpdated(User user) | ||||
{ | { | ||||
if (VoiceStateUpdated != null) | if (VoiceStateUpdated != null) | ||||
VoiceStateUpdated(this, new UserEventArgs(user)); | VoiceStateUpdated(this, new UserEventArgs(user)); | ||||
} | } | ||||
public event EventHandler<UserTypingEventArgs> UserTyping; | public event EventHandler<UserTypingEventArgs> UserTyping; | ||||
private void RaiseUserTyping(User user, Channel channel) | private void RaiseUserTyping(User user, Channel channel) | ||||
{ | { | ||||
if (UserTyping != null) | if (UserTyping != null) | ||||
UserTyping(this, new UserTypingEventArgs(user, channel)); | UserTyping(this, new UserTypingEventArgs(user, channel)); | ||||
} | } | ||||
//Voice | |||||
public sealed class VoiceServerUpdatedEventArgs : EventArgs | |||||
{ | |||||
public readonly Server Server; | |||||
public readonly string Endpoint; | |||||
internal VoiceServerUpdatedEventArgs(Server server, string endpoint) | |||||
{ | |||||
Server = server; | |||||
Endpoint = endpoint; | |||||
} | |||||
} | |||||
public event EventHandler<VoiceServerUpdatedEventArgs> VoiceServerUpdated; | |||||
private void RaiseVoiceServerUpdated(Server server, string endpoint) | |||||
{ | |||||
if (VoiceServerUpdated != null) | |||||
VoiceServerUpdated(this, new VoiceServerUpdatedEventArgs(server, endpoint)); | |||||
} | |||||
} | } | ||||
} | } |
@@ -3,10 +3,12 @@ using Discord.API.Models; | |||||
using Discord.Helpers; | using Discord.Helpers; | ||||
using Discord.Models; | using Discord.Models; | ||||
using System; | using System; | ||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | |||||
using System.Reflection; | using System.Reflection; | ||||
using System.Threading; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Message = Discord.Models.Message; | |||||
using Role = Discord.Models.Role; | using Role = Discord.Models.Role; | ||||
namespace Discord | namespace Discord | ||||
@@ -19,26 +21,144 @@ namespace Discord | |||||
private HttpOptions _httpOptions; | private HttpOptions _httpOptions; | ||||
private bool _isClosing, _isReady; | private bool _isClosing, _isReady; | ||||
public string SelfId { get; private set; } | |||||
public User Self { get { return GetUser(SelfId); } } | |||||
public string UserId { get; private set; } | |||||
public User User { get { return _users[UserId]; } } | |||||
public IEnumerable<User> Users { get { return _users.Values; } } | |||||
private ConcurrentDictionary<string, User> _users; | |||||
public IEnumerable<User> Users { get { return _users; } } | |||||
private AsyncCache<User, API.Models.UserReference> _users; | |||||
public IEnumerable<Server> Servers { get { return _servers.Values; } } | |||||
private ConcurrentDictionary<string, Server> _servers; | |||||
public IEnumerable<Server> Servers { get { return _servers; } } | |||||
private AsyncCache<Server, API.Models.ServerReference> _servers; | |||||
public IEnumerable<Channel> Channels { get { return _channels.Values; } } | |||||
private ConcurrentDictionary<string, Channel> _channels; | |||||
public IEnumerable<Channel> Channels { get { return _channels; } } | |||||
private AsyncCache<Channel, API.Models.ChannelReference> _channels; | |||||
public IEnumerable<Message> Messages { get { return _messages; } } | |||||
private AsyncCache<Message, API.Models.MessageReference> _messages; | |||||
public IEnumerable<Role> Roles { get { return _roles; } } | |||||
private AsyncCache<Role, API.Models.Role> _roles; | |||||
public bool IsConnected { get { return _isReady; } } | |||||
public DiscordClient() | public DiscordClient() | ||||
{ | { | ||||
string version = typeof(DiscordClient).GetTypeInfo().Assembly.GetName().Version.ToString(2); | string version = typeof(DiscordClient).GetTypeInfo().Assembly.GetName().Version.ToString(2); | ||||
_httpOptions = new HttpOptions { UserAgent = $"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)" }; | _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>(); | |||||
_servers = new AsyncCache<Server, API.Models.ServerReference>( | |||||
(key, parentKey) => new Server(key, this), | |||||
(server, model) => | |||||
{ | |||||
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 ?? DateTime.MinValue; | |||||
server.OwnerId = extendedModel.OwnerId; | |||||
server.Presence = extendedModel.Presence; | |||||
server.Region = extendedModel.Region; | |||||
server.VoiceStates = extendedModel.VoiceStates; | |||||
foreach (var role in extendedModel.Roles) | |||||
_roles.Update(role.Id, model.Id, role); | |||||
foreach (var channel in extendedModel.Channels) | |||||
{ | |||||
_channels.Update(channel.Id, model.Id, channel); | |||||
if (channel.Type == "text") | |||||
{ | |||||
try | |||||
{ | |||||
var messages = DiscordAPI.GetMessages(channel.Id, _httpOptions).Result.OrderBy(x => x.Timestamp); | |||||
foreach (var message in messages) | |||||
{ | |||||
var msg = _messages.Update(message.Id, message.ChannelId, message); | |||||
if (msg.User != null) | |||||
msg.User.UpdateActivity(message.Timestamp); | |||||
} | |||||
} | |||||
catch { } //Bad Permissions? | |||||
} | |||||
} | |||||
foreach (var membership in extendedModel.Members) | |||||
{ | |||||
_users.Update(membership.User.Id, membership.User); | |||||
server.AddMember(membership.User.Id); | |||||
} | |||||
} | |||||
}, | |||||
server => { } | |||||
); | |||||
_channels = new AsyncCache<Channel, API.Models.ChannelReference>( | |||||
(key, parentKey) => new Channel(key, parentKey, this), | |||||
(channel, model) => | |||||
{ | |||||
channel.Name = model.Name; | |||||
channel.Type = model.Type; | |||||
if (model is ChannelInfo) | |||||
{ | |||||
var extendedModel = model as ChannelInfo; | |||||
channel.PermissionOverwrites = extendedModel.PermissionOverwrites; | |||||
channel.RecipientId = extendedModel.Recipient?.Id; | |||||
} | |||||
}, | |||||
channel => { }); | |||||
_messages = new AsyncCache<Message, API.Models.MessageReference>( | |||||
(key, parentKey) => new Message(key, parentKey, this), | |||||
(message, model) => | |||||
{ | |||||
if (model is API.Models.Message) | |||||
{ | |||||
var extendedModel = model as API.Models.Message; | |||||
message.Attachments = extendedModel.Attachments; | |||||
message.Text = extendedModel.Content; | |||||
message.Embeds = extendedModel.Embeds; | |||||
message.IsMentioningEveryone = extendedModel.IsMentioningEveryone; | |||||
message.IsTTS = extendedModel.IsTextToSpeech; | |||||
message.UserId = extendedModel.Author.Id; | |||||
message.Timestamp = extendedModel.Timestamp; | |||||
} | |||||
if (model is WebSocketEvents.MessageUpdate) | |||||
{ | |||||
var extendedModel = model as WebSocketEvents.MessageUpdate; | |||||
message.Embeds = extendedModel.Embeds; | |||||
} | |||||
}, | |||||
message => { } | |||||
); | |||||
_roles = new AsyncCache<Role, API.Models.Role>( | |||||
(key, parentKey) => new Role(key, parentKey, this), | |||||
(role, model) => | |||||
{ | |||||
role.Permissions = model.Permissions; | |||||
}, | |||||
role => { } | |||||
); | |||||
_users = new AsyncCache<User, API.Models.UserReference>( | |||||
(key, parentKey) => new User(key, this), | |||||
(user, model) => | |||||
{ | |||||
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; | |||||
} | |||||
}, | |||||
user => { } | |||||
); | |||||
_webSocket = new DiscordWebSocket(); | _webSocket = new DiscordWebSocket(); | ||||
_webSocket.Connected += (s,e) => RaiseConnected(); | _webSocket.Connected += (s,e) => RaiseConnected(); | ||||
@@ -65,14 +185,12 @@ namespace Discord | |||||
_channels.Clear(); | _channels.Clear(); | ||||
_users.Clear(); | _users.Clear(); | ||||
SelfId = data.User.Id; | |||||
UpdateUser(data.User); | |||||
UserId = data.User.Id; | |||||
_users.Update(data.User.Id, data.User); | |||||
foreach (var server in data.Guilds) | foreach (var server in data.Guilds) | ||||
UpdateServer(server); | |||||
_servers.Update(server.Id, server); | |||||
foreach (var channel in data.PrivateChannels) | foreach (var channel in data.PrivateChannels) | ||||
UpdateChannel(channel as ChannelInfo, null); | |||||
RaiseLoggedIn(); | |||||
_channels.Update(channel.Id, null, channel); | |||||
} | } | ||||
break; | break; | ||||
@@ -80,15 +198,15 @@ namespace Discord | |||||
case "GUILD_CREATE": | case "GUILD_CREATE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.GuildCreate>(); | var data = e.Event.ToObject<WebSocketEvents.GuildCreate>(); | ||||
var server = UpdateServer(data); | |||||
var server = _servers.Update(data.Id, data); | |||||
RaiseServerCreated(server); | RaiseServerCreated(server); | ||||
} | } | ||||
break; | break; | ||||
case "GUILD_DELETE": | case "GUILD_DELETE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.GuildDelete>(); | var data = e.Event.ToObject<WebSocketEvents.GuildDelete>(); | ||||
Server server; | |||||
if (_servers.TryRemove(data.Id, out server)) | |||||
var server = _servers.Remove(data.Id); | |||||
if (server != null) | |||||
RaiseServerDestroyed(server); | RaiseServerDestroyed(server); | ||||
} | } | ||||
break; | break; | ||||
@@ -97,52 +215,53 @@ namespace Discord | |||||
case "CHANNEL_CREATE": | case "CHANNEL_CREATE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.ChannelCreate>(); | var data = e.Event.ToObject<WebSocketEvents.ChannelCreate>(); | ||||
var channel = UpdateChannel(data, null); | |||||
var channel = _channels.Update(data.Id, data.GuildId, data); | |||||
RaiseChannelCreated(channel); | RaiseChannelCreated(channel); | ||||
} | } | ||||
break; | break; | ||||
case "CHANNEL_DELETE": | |||||
{ | |||||
var data = e.Event.ToObject<WebSocketEvents.ChannelDelete>(); | |||||
var channel = DeleteChannel(data.Id); | |||||
RaiseChannelDestroyed(channel); | |||||
} | |||||
break; | |||||
case "CHANNEL_UPDATE": | case "CHANNEL_UPDATE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.ChannelUpdate>(); | var data = e.Event.ToObject<WebSocketEvents.ChannelUpdate>(); | ||||
var channel = DeleteChannel(data.Id); | |||||
var channel = _channels.Update(data.Id, data.GuildId, data); | |||||
RaiseChannelUpdated(channel); | RaiseChannelUpdated(channel); | ||||
} | } | ||||
break; | break; | ||||
case "CHANNEL_DELETE": | |||||
{ | |||||
var data = e.Event.ToObject<WebSocketEvents.ChannelDelete>(); | |||||
var channel = _channels.Remove(data.Id); | |||||
if (channel != null) | |||||
RaiseChannelDestroyed(channel); | |||||
} | |||||
break; | |||||
//Members | //Members | ||||
case "GUILD_MEMBER_ADD": | case "GUILD_MEMBER_ADD": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberAdd>(); | var data = e.Event.ToObject<WebSocketEvents.GuildMemberAdd>(); | ||||
var user = UpdateUser(data.User); | |||||
var server = GetServer(data.GuildId); | |||||
var user = _users.Update(data.User.Id, data.User); | |||||
var server = _servers[data.GuildId]; | |||||
server._members[user.Id] = true; | server._members[user.Id] = true; | ||||
RaiseMemberAdded(user, server); | RaiseMemberAdded(user, server); | ||||
} | } | ||||
break; | 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; | |||||
RaiseMemberRemoved(user, server); | |||||
} | |||||
break; | |||||
case "GUILD_MEMBER_UPDATE": | case "GUILD_MEMBER_UPDATE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberUpdate>(); | var data = e.Event.ToObject<WebSocketEvents.GuildMemberUpdate>(); | ||||
var user = UpdateUser(data.User); | |||||
var server = GetServer(data.GuildId); | |||||
var user = _users.Update(data.User.Id, data.User); | |||||
var server = _servers[data.GuildId]; | |||||
RaiseMemberUpdated(user, server); | RaiseMemberUpdated(user, server); | ||||
} | } | ||||
break; | break; | ||||
case "GUILD_MEMBER_REMOVE": | |||||
{ | |||||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberRemove>(); | |||||
var user = _users.Update(data.User.Id, data.User); | |||||
var server = _servers[data.GuildId]; | |||||
if (server != null && server.RemoveMember(user.Id)) | |||||
RaiseMemberRemoved(user, server); | |||||
} | |||||
break; | |||||
//Roles | //Roles | ||||
case "GUILD_ROLE_CREATE": | case "GUILD_ROLE_CREATE": | ||||
@@ -152,13 +271,6 @@ namespace Discord | |||||
RaiseRoleCreated(role); | RaiseRoleCreated(role); | ||||
} | } | ||||
break; | break; | ||||
case "GUILD_ROLE_DELETE": | |||||
{ | |||||
var data = e.Event.ToObject<WebSocketEvents.GuildRoleDelete>(); | |||||
var role = GetRole(data.RoleId, data.GuildId); | |||||
RaiseRoleDeleted(role); | |||||
} | |||||
break; | |||||
case "GUILD_ROLE_UPDATE": | case "GUILD_ROLE_UPDATE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(); | var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(); | ||||
@@ -166,22 +278,31 @@ namespace Discord | |||||
RaiseRoleUpdated(role); | RaiseRoleUpdated(role); | ||||
} | } | ||||
break; | break; | ||||
case "GUILD_ROLE_DELETE": | |||||
{ | |||||
var data = e.Event.ToObject<WebSocketEvents.GuildRoleDelete>(); | |||||
var role = _roles.Remove(data.RoleId); | |||||
if (role != null) | |||||
RaiseRoleDeleted(role); | |||||
} | |||||
break; | |||||
//Roles | |||||
//Bans | |||||
case "GUILD_BAN_ADD": | case "GUILD_BAN_ADD": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.GuildBanAddRemove>(); | var data = e.Event.ToObject<WebSocketEvents.GuildBanAddRemove>(); | ||||
var user = UpdateUser(data.User); | |||||
var server = GetServer(data.GuildId); | |||||
var user = _users.Update(data.User.Id, data.User); | |||||
var server = _servers[data.GuildId]; | |||||
RaiseBanAdded(user, server); | RaiseBanAdded(user, server); | ||||
} | } | ||||
break; | break; | ||||
case "GUILD_BAN_REMOVE": | case "GUILD_BAN_REMOVE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.GuildBanAddRemove>(); | var data = e.Event.ToObject<WebSocketEvents.GuildBanAddRemove>(); | ||||
var user = UpdateUser(data.User); | |||||
var server = GetServer(data.GuildId); | |||||
RaiseBanRemoved(user, server); | |||||
var user = _users.Update(data.User.Id, data.User); | |||||
var server = _servers[data.GuildId]; | |||||
if (server != null && server.RemoveBan(user.Id)) | |||||
RaiseBanRemoved(user, server); | |||||
} | } | ||||
break; | break; | ||||
@@ -189,7 +310,7 @@ namespace Discord | |||||
case "MESSAGE_CREATE": | case "MESSAGE_CREATE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.MessageCreate>(); | var data = e.Event.ToObject<WebSocketEvents.MessageCreate>(); | ||||
var msg = UpdateMessage(data); | |||||
var msg = _messages.Update(data.Id, data.ChannelId, data); | |||||
msg.User.UpdateActivity(data.Timestamp); | msg.User.UpdateActivity(data.Timestamp); | ||||
RaiseMessageCreated(msg); | RaiseMessageCreated(msg); | ||||
} | } | ||||
@@ -197,21 +318,21 @@ namespace Discord | |||||
case "MESSAGE_UPDATE": | case "MESSAGE_UPDATE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.MessageUpdate>(); | var data = e.Event.ToObject<WebSocketEvents.MessageUpdate>(); | ||||
var msg = GetMessage(data.Id, data.ChannelId); | |||||
var msg = _messages.Update(data.Id, data); | |||||
RaiseMessageUpdated(msg); | RaiseMessageUpdated(msg); | ||||
} | } | ||||
break; | break; | ||||
case "MESSAGE_DELETE": | case "MESSAGE_DELETE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.MessageDelete>(); | var data = e.Event.ToObject<WebSocketEvents.MessageDelete>(); | ||||
var msg = GetMessage(data.MessageId, data.ChannelId); | |||||
RaiseMessageDeleted(msg); | |||||
var msg = GetMessage(data.MessageId); | |||||
_messages.Remove(msg.Id); | |||||
} | } | ||||
break; | break; | ||||
case "MESSAGE_ACK": | case "MESSAGE_ACK": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.MessageAck>(); | var data = e.Event.ToObject<WebSocketEvents.MessageAck>(); | ||||
var msg = GetMessage(data.MessageId, data.ChannelId); | |||||
var msg = GetMessage(data.MessageId); | |||||
RaiseMessageAcknowledged(msg); | RaiseMessageAcknowledged(msg); | ||||
} | } | ||||
break; | break; | ||||
@@ -220,25 +341,36 @@ namespace Discord | |||||
case "PRESENCE_UPDATE": | case "PRESENCE_UPDATE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.PresenceUpdate>(); | var data = e.Event.ToObject<WebSocketEvents.PresenceUpdate>(); | ||||
var user = UpdateUser(data); | |||||
var user = _users.Update(data.Id, data); | |||||
RaisePresenceUpdated(user); | RaisePresenceUpdated(user); | ||||
} | } | ||||
break; | break; | ||||
case "VOICE_STATE_UPDATE": | case "VOICE_STATE_UPDATE": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.VoiceStateUpdate>(); | var data = e.Event.ToObject<WebSocketEvents.VoiceStateUpdate>(); | ||||
var user = GetUser(data.UserId); //TODO: Don't ignore this | |||||
var user = _users[data.UserId]; //TODO: Don't ignore this | |||||
RaiseVoiceStateUpdated(user); | RaiseVoiceStateUpdated(user); | ||||
} | } | ||||
break; | break; | ||||
case "TYPING_START": | case "TYPING_START": | ||||
{ | { | ||||
var data = e.Event.ToObject<WebSocketEvents.TypingStart>(); | var data = e.Event.ToObject<WebSocketEvents.TypingStart>(); | ||||
var channel = GetChannel(data.ChannelId); | |||||
var user = GetUser(data.UserId); | |||||
var channel = _channels[data.ChannelId]; | |||||
var user = _users[data.UserId]; | |||||
RaiseUserTyping(user, channel); | RaiseUserTyping(user, channel); | ||||
} | } | ||||
break; | break; | ||||
//Voice | |||||
case "VOICE_SERVER_UPDATE": | |||||
{ | |||||
var data = e.Event.ToObject<WebSocketEvents.VoiceServerUpdate>(); | |||||
var server = _servers[data.ServerId]; | |||||
RaiseVoiceServerUpdated(server, data.Endpoint); | |||||
} | |||||
break; | |||||
//Others | |||||
default: | default: | ||||
RaiseOnDebugMessage("Unknown WebSocket message type: " + e.Type); | RaiseOnDebugMessage("Unknown WebSocket message type: " + e.Type); | ||||
break; | break; | ||||
@@ -247,6 +379,7 @@ namespace Discord | |||||
_webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Message); | _webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Message); | ||||
} | } | ||||
//Auth | |||||
public async Task Connect(string email, string password) | public async Task Connect(string email, string password) | ||||
{ | { | ||||
_isClosing = false; | _isClosing = false; | ||||
@@ -271,21 +404,67 @@ namespace Discord | |||||
_isClosing = false; | _isClosing = false; | ||||
} | } | ||||
public Task CreateServer(string name, string region) | |||||
//Servers | |||||
public async Task<Server> CreateServer(string name, string region) | |||||
{ | { | ||||
CheckReady(); | CheckReady(); | ||||
return DiscordAPI.CreateServer(name, region, _httpOptions); | |||||
var response = await DiscordAPI.CreateServer(name, region, _httpOptions); | |||||
return _servers.Update(response.Id, response); | |||||
} | |||||
public Task<Server> LeaveServer(Server server) | |||||
{ | |||||
return LeaveServer(server.Id); | |||||
} | } | ||||
public Task DeleteServer(string id) | |||||
public async Task<Server> LeaveServer(string id) | |||||
{ | { | ||||
CheckReady(); | CheckReady(); | ||||
return DiscordAPI.DeleteServer(id, _httpOptions); | |||||
await DiscordAPI.LeaveServer(id, _httpOptions); | |||||
return _servers.Remove(id); | |||||
} | } | ||||
public Task<GetInviteResponse> GetInvite(string id) | |||||
//Invites | |||||
public Task<Invite> CreateInvite(Server server, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass) | |||||
{ | |||||
return CreateInvite(server.DefaultChannelId, maxAge, maxUses, isTemporary, hasXkcdPass); | |||||
} | |||||
public Task<Invite> CreateInvite(Channel channel, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass) | |||||
{ | |||||
return CreateInvite(channel, maxAge, maxUses, isTemporary, hasXkcdPass); | |||||
} | |||||
public async Task<Invite> CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass) | |||||
{ | |||||
CheckReady(); | |||||
var response = await DiscordAPI.CreateInvite(channelId, maxAge, maxUses, isTemporary, hasXkcdPass, _httpOptions); | |||||
_channels.Update(response.Channel.Id, response.Server.Id, response.Channel); | |||||
_servers.Update(response.Server.Id, response.Server); | |||||
_users.Update(response.Inviter.Id, response.Inviter); | |||||
return new Invite(response.Code, response.XkcdPass, this) | |||||
{ | |||||
ChannelId = response.Channel.Id, | |||||
InviterId = response.Inviter.Id, | |||||
ServerId = response.Server.Id, | |||||
IsRevoked = response.IsRevoked, | |||||
IsTemporary = response.IsTemporary, | |||||
MaxAge = response.MaxAge, | |||||
MaxUses = response.MaxUses, | |||||
Uses = response.Uses | |||||
}; | |||||
} | |||||
public async Task<Invite> GetInvite(string id) | |||||
{ | |||||
CheckReady(); | |||||
var response = await DiscordAPI.GetInvite(id, _httpOptions); | |||||
return new Invite(response.Code, response.XkcdPass, this) | |||||
{ | |||||
ChannelId = response.Channel.Id, | |||||
InviterId = response.Inviter.Id, | |||||
ServerId = response.Server.Id | |||||
}; | |||||
} | |||||
public Task AcceptInvite(Invite invite) | |||||
{ | { | ||||
CheckReady(); | CheckReady(); | ||||
return DiscordAPI.GetInvite(id, _httpOptions); | |||||
return DiscordAPI.AcceptInvite(invite.Code, _httpOptions); | |||||
} | } | ||||
public async Task AcceptInvite(string id) | public async Task AcceptInvite(string id) | ||||
{ | { | ||||
@@ -302,6 +481,7 @@ namespace Discord | |||||
await DiscordAPI.DeleteInvite(response.Code, _httpOptions); | await DiscordAPI.DeleteInvite(response.Code, _httpOptions); | ||||
} | } | ||||
//Chat | |||||
public Task SendMessage(string channelId, string text) | public Task SendMessage(string channelId, string text) | ||||
{ | { | ||||
return SendMessage(channelId, text, new string[0]); | return SendMessage(channelId, text, new string[0]); | ||||
@@ -323,136 +503,27 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
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, bool addNew = true) | |||||
{ | |||||
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; | |||||
} | |||||
if (addNew) | |||||
_users[model.Id] = user; | |||||
return user; | |||||
} | |||||
public Server GetServer(string id) | public Server GetServer(string id) | ||||
{ | { | ||||
if (id == null) return null; | |||||
Server server = null; | |||||
_servers.TryGetValue(id, out server); | |||||
return server; | |||||
return _servers[id]; | |||||
} | } | ||||
private Server UpdateServer(ServerInfo model, bool addNew = true) | |||||
{ | |||||
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, addNew); | |||||
server._channels[channel.Id] = true; | |||||
} | |||||
foreach (var membership in extendedModel.Members) | |||||
{ | |||||
UpdateUser(membership.User, addNew); | |||||
server._members[membership.User.Id] = true; | |||||
} | |||||
} | |||||
if (addNew) | |||||
_servers[model.Id] = server; | |||||
return server; | |||||
} | |||||
public Channel GetChannel(string id) | public Channel GetChannel(string id) | ||||
{ | { | ||||
if (id == null) return null; | |||||
Channel channel = null; | |||||
_channels.TryGetValue(id, out channel); | |||||
return channel; | |||||
return _channels[id]; | |||||
} | } | ||||
private Channel UpdateChannel(ChannelInfo model, string serverId, bool addNew = true) | |||||
{ | |||||
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; | |||||
if (addNew) | |||||
_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) | |||||
public User GetUser(string id) | |||||
{ | { | ||||
if (id == null || channelId == null) return null; | |||||
return new ChatMessageReference(id, channelId, this); | |||||
return _users[id]; | |||||
} | } | ||||
private ChatMessage UpdateMessage(WebSocketEvents.MessageCreate model, bool addNew = true) | |||||
public Models.Message GetMessage(string id) | |||||
{ | { | ||||
return new ChatMessage(model.Id, model.ChannelId, this) | |||||
{ | |||||
Attachments = model.Attachments, | |||||
Text = model.Content, | |||||
Embeds = model.Embeds, | |||||
IsMentioningEveryone = model.IsMentioningEveryone, | |||||
IsTTS = model.IsTextToSpeech, | |||||
UserId = model.Author.Id, | |||||
Timestamp = model.Timestamp | |||||
}; | |||||
return _messages[id]; | |||||
} | } | ||||
private Role GetRole(string id, string serverId) | |||||
public Role GetRole(string id) | |||||
{ | { | ||||
if (id == null || serverId == null) return null; | |||||
return new Role(id, serverId, this); | |||||
return _roles[id]; | |||||
} | } | ||||
private Role UpdateRole(WebSocketEvents.GuildRoleCreateUpdate role, bool addNew = true) | private Role UpdateRole(WebSocketEvents.GuildRoleCreateUpdate role, bool addNew = true) | ||||
{ | { | ||||
return new Role(role.Role.Id, role.GuildId, this) | return new Role(role.Role.Id, role.GuildId, this) | ||||
@@ -467,5 +538,12 @@ namespace Discord | |||||
if (!_isReady) | if (!_isReady) | ||||
throw new InvalidOperationException("The client is not currently connected to Discord"); | throw new InvalidOperationException("The client is not currently connected to Discord"); | ||||
} | } | ||||
public void Block() | |||||
{ | |||||
//Blocking call for console apps | |||||
//TODO: Improve this | |||||
while (!_isClosing) | |||||
Thread.Sleep(1000); | |||||
} | |||||
} | } | ||||
} | } |
@@ -22,11 +22,13 @@ namespace Discord | |||||
private ConcurrentQueue<byte[]> _sendQueue; | private ConcurrentQueue<byte[]> _sendQueue; | ||||
private int _heartbeatInterval; | private int _heartbeatInterval; | ||||
private DateTime _lastHeartbeat; | private DateTime _lastHeartbeat; | ||||
private AutoResetEvent _connectWaitOnLogin; | |||||
public async Task ConnectAsync(string url, HttpOptions options) | public async Task ConnectAsync(string url, HttpOptions options) | ||||
{ | { | ||||
await DisconnectAsync(); | await DisconnectAsync(); | ||||
_connectWaitOnLogin = new AutoResetEvent(false); | |||||
_sendQueue = new ConcurrentQueue<byte[]>(); | _sendQueue = new ConcurrentQueue<byte[]>(); | ||||
_webSocket = new ClientWebSocket(); | _webSocket = new ClientWebSocket(); | ||||
@@ -62,7 +64,9 @@ namespace Discord | |||||
msg.Payload.Properties["$referrer"] = ""; | msg.Payload.Properties["$referrer"] = ""; | ||||
msg.Payload.Properties["$referring_domain"] = ""; | msg.Payload.Properties["$referring_domain"] = ""; | ||||
SendMessage(msg, cancelToken); | SendMessage(msg, cancelToken); | ||||
} | |||||
_connectWaitOnLogin.WaitOne(); | |||||
} | |||||
public async Task DisconnectAsync() | public async Task DisconnectAsync() | ||||
{ | { | ||||
if (_webSocket != null) | if (_webSocket != null) | ||||
@@ -112,6 +116,8 @@ namespace Discord | |||||
SendMessage(new WebSocketCommands.KeepAlive(), cancelToken); | SendMessage(new WebSocketCommands.KeepAlive(), cancelToken); | ||||
} | } | ||||
RaiseGotEvent(msg.Type, msg.Payload as JToken); | RaiseGotEvent(msg.Type, msg.Payload as JToken); | ||||
if (msg.Type == "READY") | |||||
_connectWaitOnLogin.Set(); | |||||
break; | break; | ||||
default: | default: | ||||
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation); | RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation); | ||||
@@ -0,0 +1,90 @@ | |||||
using System; | |||||
using System.Collections; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
namespace Discord.Helpers | |||||
{ | |||||
public class AsyncCache<TValue, TModel> : IEnumerable<TValue> | |||||
where TValue : class | |||||
where TModel : class | |||||
{ | |||||
protected readonly ConcurrentDictionary<string, TValue> _dictionary; | |||||
private readonly Func<string, string, TValue> _onCreate; | |||||
private readonly Action<TValue, TModel> _onUpdate; | |||||
private readonly Action<TValue> _onRemove; | |||||
public AsyncCache(Func<string, string, TValue> onCreate, Action<TValue, TModel> onUpdate, Action<TValue> onRemove) | |||||
{ | |||||
_dictionary = new ConcurrentDictionary<string, TValue>(); | |||||
_onCreate = onCreate; | |||||
_onUpdate = onUpdate; | |||||
_onRemove = onRemove; | |||||
} | |||||
public TValue this[string key] | |||||
{ | |||||
get | |||||
{ | |||||
if (key == null) | |||||
return null; | |||||
TValue value = null; | |||||
_dictionary.TryGetValue(key, out value); | |||||
return value; | |||||
} | |||||
} | |||||
public TValue Update(string key, TModel model) | |||||
{ | |||||
return Update(key, null, model); | |||||
} | |||||
public TValue Update(string key, string parentKey, TModel model) | |||||
{ | |||||
if (key == null) | |||||
return null; | |||||
while (true) | |||||
{ | |||||
bool isNew; | |||||
TValue value; | |||||
isNew = !_dictionary.TryGetValue(key, out value); | |||||
if (isNew) | |||||
value = _onCreate(key, parentKey); | |||||
_onUpdate(value, model); | |||||
if (isNew) | |||||
{ | |||||
//If this fails, repeat as an update instead of an add | |||||
if (_dictionary.TryAdd(key, value)) | |||||
return value; | |||||
} | |||||
else | |||||
{ | |||||
_dictionary[key] = value; | |||||
return value; | |||||
} | |||||
} | |||||
} | |||||
public TValue Remove(string key) | |||||
{ | |||||
TValue value = null; | |||||
if (_dictionary.TryRemove(key, out value)) | |||||
return value; | |||||
else | |||||
return null; | |||||
} | |||||
public void Clear() | |||||
{ | |||||
_dictionary.Clear(); | |||||
} | |||||
public IEnumerator<TValue> GetEnumerator() | |||||
{ | |||||
return _dictionary.Values.GetEnumerator(); | |||||
} | |||||
IEnumerator IEnumerable.GetEnumerator() | |||||
{ | |||||
return _dictionary.Values.GetEnumerator(); | |||||
} | |||||
} | |||||
} |
@@ -1,4 +1,5 @@ | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using Newtonsoft.Json.Linq; | |||||
using System; | using System; | ||||
using System.IO; | using System.IO; | ||||
using System.IO.Compression; | using System.IO.Compression; | ||||
@@ -24,47 +25,93 @@ namespace Discord.Helpers | |||||
internal static class Http | internal static class Http | ||||
{ | { | ||||
private static readonly RequestCachePolicy _cachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); | private static readonly RequestCachePolicy _cachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); | ||||
#if DEBUG | |||||
private const bool _isDebug = true; | |||||
#else | |||||
private const bool _isDebug = false; | |||||
#endif | |||||
//GET | |||||
internal static async Task<ResponseT> Get<ResponseT>(string path, object data, HttpOptions options) | internal static async Task<ResponseT> Get<ResponseT>(string path, object data, HttpOptions options) | ||||
where ResponseT : class | where ResponseT : class | ||||
{ | { | ||||
string requestJson = JsonConvert.SerializeObject(data); | string requestJson = JsonConvert.SerializeObject(data); | ||||
string responseJson = await SendRequest("GET", path, requestJson, options, true); | string responseJson = await SendRequest("GET", path, requestJson, options, true); | ||||
return JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||||
var response = JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||||
#if DEBUG | |||||
CheckResponse(responseJson, response); | |||||
#endif | |||||
return response; | |||||
} | } | ||||
internal static async Task<ResponseT> Get<ResponseT>(string path, HttpOptions options) | internal static async Task<ResponseT> Get<ResponseT>(string path, HttpOptions options) | ||||
where ResponseT : class | where ResponseT : class | ||||
{ | { | ||||
string responseJson = await SendRequest("GET", path, null, options, true); | string responseJson = await SendRequest("GET", path, null, options, true); | ||||
return JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||||
var response = JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||||
#if DEBUG | |||||
CheckResponse(responseJson, response); | |||||
#endif | |||||
return response; | |||||
} | } | ||||
//POST | |||||
internal static async Task<ResponseT> Post<ResponseT>(string path, object data, HttpOptions options) | internal static async Task<ResponseT> Post<ResponseT>(string path, object data, HttpOptions options) | ||||
where ResponseT : class | where ResponseT : class | ||||
{ | { | ||||
string requestJson = JsonConvert.SerializeObject(data); | string requestJson = JsonConvert.SerializeObject(data); | ||||
string responseJson = await SendRequest("POST", path, requestJson, options, true); | string responseJson = await SendRequest("POST", path, requestJson, options, true); | ||||
return JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||||
var response = JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||||
#if DEBUG | |||||
CheckResponse(responseJson, response); | |||||
#endif | |||||
return response; | |||||
} | } | ||||
internal static Task Post(string path, object data, HttpOptions options) | |||||
internal static async Task<string> Post(string path, object data, HttpOptions options) | |||||
{ | { | ||||
string requestJson = JsonConvert.SerializeObject(data); | string requestJson = JsonConvert.SerializeObject(data); | ||||
return SendRequest("POST", path, requestJson, options, false); | |||||
string responseJson = await SendRequest("POST", path, requestJson, options, _isDebug); | |||||
#if DEBUG | |||||
CheckEmptyResponse(responseJson); | |||||
#endif | |||||
return responseJson; | |||||
} | } | ||||
internal static async Task<ResponseT> Post<ResponseT>(string path, HttpOptions options) | internal static async Task<ResponseT> Post<ResponseT>(string path, HttpOptions options) | ||||
where ResponseT : class | where ResponseT : class | ||||
{ | { | ||||
string responseJson = await SendRequest("POST", path, null, options, true); | string responseJson = await SendRequest("POST", path, null, options, true); | ||||
return JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||||
var response = JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||||
#if DEBUG | |||||
CheckResponse(responseJson, response); | |||||
#endif | |||||
return response; | |||||
} | } | ||||
internal static Task Post(string path, HttpOptions options) | |||||
internal static async Task<string> Post(string path, HttpOptions options) | |||||
{ | { | ||||
return SendRequest("POST", path, null, options, false); | |||||
string responseJson = await SendRequest("POST", path, null, options, _isDebug); | |||||
#if DEBUG | |||||
CheckEmptyResponse(responseJson); | |||||
#endif | |||||
return responseJson; | |||||
} | } | ||||
internal static Task Delete(string path, HttpOptions options) | |||||
//DELETE | |||||
internal static async Task<ResponseT> Delete<ResponseT>(string path, HttpOptions options) | |||||
where ResponseT : class | |||||
{ | |||||
string responseJson = await SendRequest("DELETE", path, null, options, true); | |||||
var response = JsonConvert.DeserializeObject<ResponseT>(responseJson); | |||||
#if DEBUG | |||||
CheckResponse(responseJson, response); | |||||
#endif | |||||
return response; | |||||
} | |||||
internal static async Task<string> Delete(string path, HttpOptions options) | |||||
{ | { | ||||
return SendRequest("DELETE", path, null, options, false); | |||||
string responseJson = await SendRequest("DELETE", path, null, options, _isDebug); | |||||
#if DEBUG | |||||
CheckEmptyResponse(responseJson); | |||||
#endif | |||||
return responseJson; | |||||
} | } | ||||
private static async Task<string> SendRequest(string method, string path, string data, HttpOptions options, bool hasResponse) | private static async Task<string> SendRequest(string method, string path, string data, HttpOptions options, bool hasResponse) | ||||
@@ -124,6 +171,7 @@ namespace Discord.Helpers | |||||
else | else | ||||
return null; | return null; | ||||
} | } | ||||
} | } | ||||
private static Stream GetDecoder(string contentEncoding, MemoryStream encodedStream) | private static Stream GetDecoder(string contentEncoding, MemoryStream encodedStream) | ||||
@@ -138,5 +186,21 @@ namespace Discord.Helpers | |||||
throw new ArgumentOutOfRangeException("Unknown encoding: " + contentEncoding); | throw new ArgumentOutOfRangeException("Unknown encoding: " + contentEncoding); | ||||
} | } | ||||
} | } | ||||
#if DEBUG | |||||
private static void CheckResponse<T>(string json, T obj) | |||||
{ | |||||
/*JToken token = JToken.Parse(json); | |||||
JToken token2 = JToken.FromObject(obj); | |||||
if (!JToken.DeepEquals(token, token2)) | |||||
throw new Exception("API check failed: Objects do not match.");*/ | |||||
} | |||||
private static void CheckEmptyResponse(string json) | |||||
{ | |||||
if (!string.IsNullOrEmpty(json)) | |||||
throw new Exception("API check failed: Response is not empty."); | |||||
} | |||||
#endif | |||||
} | } | ||||
} | } |
@@ -1,16 +1,19 @@ | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
namespace Discord.Models | namespace Discord.Models | ||||
{ | { | ||||
public sealed class Channel | public sealed class Channel | ||||
{ | { | ||||
private readonly DiscordClient _client; | private readonly DiscordClient _client; | ||||
private string _name; | |||||
public string Id { get; } | public string Id { get; } | ||||
private string _name; | |||||
public string Name { get { return !IsPrivate ? _name : '@' + Recipient.Name; } internal set { _name = value; } } | public string Name { get { return !IsPrivate ? _name : '@' + Recipient.Name; } internal set { _name = value; } } | ||||
public bool IsPrivate { get; internal set; } | |||||
public bool IsPrivate { get; } | |||||
public string Type { get; internal set; } | public string Type { get; internal set; } | ||||
public string ServerId { get; } | public string ServerId { get; } | ||||
@@ -21,6 +24,8 @@ namespace Discord.Models | |||||
public string RecipientId { get; internal set; } | public string RecipientId { get; internal set; } | ||||
public User Recipient { get { return _client.GetUser(RecipientId); } } | public User Recipient { get { return _client.GetUser(RecipientId); } } | ||||
public IEnumerable<Message> Messages { get { return _client.Messages.Where(x => x.ChannelId == Id); } } | |||||
//Not Implemented | //Not Implemented | ||||
public object[] PermissionOverwrites { get; internal set; } | public object[] PermissionOverwrites { get; internal set; } | ||||
@@ -28,6 +33,7 @@ namespace Discord.Models | |||||
{ | { | ||||
Id = id; | Id = id; | ||||
ServerId = serverId; | ServerId = serverId; | ||||
IsPrivate = serverId == null; | |||||
_client = client; | _client = client; | ||||
} | } | ||||
@@ -1,22 +0,0 @@ | |||||
using Newtonsoft.Json; | |||||
namespace Discord.Models | |||||
{ | |||||
public class ChatMessageReference | |||||
{ | |||||
protected readonly DiscordClient _client; | |||||
public string Id { get; } | |||||
public string ChannelId { get; } | |||||
[JsonIgnore] | |||||
public Channel Channel { get { return _client.GetChannel(ChannelId); } } | |||||
internal ChatMessageReference(string id, string channelId, DiscordClient client) | |||||
{ | |||||
Id = id; | |||||
ChannelId = channelId; | |||||
_client = client; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,32 @@ | |||||
using Newtonsoft.Json; | |||||
namespace Discord.Models | |||||
{ | |||||
public sealed class Invite | |||||
{ | |||||
private readonly DiscordClient _client; | |||||
public int MaxAge, Uses, MaxUses; | |||||
public bool IsRevoked, IsTemporary; | |||||
public readonly string Code, XkcdPass; | |||||
public string InviterId { get; internal set; } | |||||
[JsonIgnore] | |||||
public User Inviter { get { return _client.GetUser(InviterId); } } | |||||
public string ServerId { get; internal set; } | |||||
[JsonIgnore] | |||||
public Server Server { get { return _client.GetServer(ServerId); } } | |||||
public string ChannelId { get; internal set; } | |||||
[JsonIgnore] | |||||
public Channel Channel { get { return _client.GetChannel(ChannelId); } } | |||||
internal Invite(string code, string xkcdPass, DiscordClient client) | |||||
{ | |||||
Code = code; | |||||
XkcdPass = xkcdPass; | |||||
_client = client; | |||||
} | |||||
} | |||||
} |
@@ -3,8 +3,13 @@ using System; | |||||
namespace Discord.Models | namespace Discord.Models | ||||
{ | { | ||||
public sealed class ChatMessage : ChatMessageReference | |||||
public sealed class Message | |||||
{ | { | ||||
private readonly DiscordClient _client; | |||||
public string Id { get; } | |||||
public string ChannelId { get; } | |||||
public bool IsMentioningEveryone { get; internal set; } | public bool IsMentioningEveryone { get; internal set; } | ||||
public bool IsTTS { get; internal set; } | public bool IsTTS { get; internal set; } | ||||
public string Text { get; internal set; } | public string Text { get; internal set; } | ||||
@@ -18,10 +23,12 @@ namespace Discord.Models | |||||
public object[] Attachments { get; internal set; } | public object[] Attachments { get; internal set; } | ||||
public object[] Embeds { get; internal set; } | public object[] Embeds { get; internal set; } | ||||
internal ChatMessage(string id, string channelId, DiscordClient client) | |||||
: base(id, channelId, client) | |||||
internal Message(string id, string channelId, DiscordClient client) | |||||
{ | { | ||||
} | |||||
Id = id; | |||||
ChannelId = channelId; | |||||
_client = client; | |||||
} | |||||
public override string ToString() | public override string ToString() | ||||
{ | { |
@@ -1,4 +1,5 @@ | |||||
using Newtonsoft.Json; | |||||
using Discord.Helpers; | |||||
using Newtonsoft.Json; | |||||
using System; | using System; | ||||
using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
@@ -20,20 +21,22 @@ namespace Discord.Models | |||||
public string OwnerId { get; internal set; } | public string OwnerId { get; internal set; } | ||||
public User Owner { get { return _client.GetUser(OwnerId); } } | public User Owner { get { return _client.GetUser(OwnerId); } } | ||||
public bool IsOwner { get { return _client.UserId == OwnerId; } } | |||||
public string DefaultChannelId { get { return Id; } } | |||||
public Channel DefaultChannel { get { return _client.GetChannel(DefaultChannelId); } } | |||||
internal ConcurrentDictionary<string, bool> _members; | internal ConcurrentDictionary<string, bool> _members; | ||||
public IEnumerable<string> MemberIds { get { return _members.Keys; } } | |||||
[JsonIgnore] | |||||
public IEnumerable<User> Members { get { return _members.Keys.Select(x => _client.GetUser(x)); } } | public IEnumerable<User> Members { get { return _members.Keys.Select(x => _client.GetUser(x)); } } | ||||
internal ConcurrentDictionary<string, bool> _channels; | |||||
public IEnumerable<string> ChannelIds { get { return _channels.Keys; } } | |||||
[JsonIgnore] | |||||
public IEnumerable<Channel> Channels { get { return _channels.Keys.Select(x => _client.GetChannel(x)); } } | |||||
internal ConcurrentDictionary<string, bool> _bans; | |||||
public IEnumerable<User> Bans { get { return _bans.Keys.Select(x => _client.GetUser(x)); } } | |||||
public IEnumerable<Channel> Channels { get { return _client.Channels.Where(x => x.ServerId == Id); } } | |||||
public IEnumerable<Role> Roles { get { return _client.Roles.Where(x => x.ServerId == Id); } } | |||||
//Not Implemented | //Not Implemented | ||||
public object Presence { get; internal set; } | public object Presence { get; internal set; } | ||||
public object[] Roles { get; internal set; } | |||||
public object[] VoiceStates { get; internal set; } | public object[] VoiceStates { get; internal set; } | ||||
internal Server(string id, DiscordClient client) | internal Server(string id, DiscordClient client) | ||||
@@ -41,12 +44,32 @@ namespace Discord.Models | |||||
Id = id; | Id = id; | ||||
_client = client; | _client = client; | ||||
_members = new ConcurrentDictionary<string, bool>(); | _members = new ConcurrentDictionary<string, bool>(); | ||||
_channels = new ConcurrentDictionary<string, bool>(); | |||||
_bans = new ConcurrentDictionary<string, bool>(); | |||||
} | } | ||||
public override string ToString() | public override string ToString() | ||||
{ | { | ||||
return Name; | return Name; | ||||
} | } | ||||
internal void AddMember(string id) | |||||
{ | |||||
_members.TryAdd(id, true); | |||||
} | |||||
internal bool RemoveMember(string id) | |||||
{ | |||||
bool ignored; | |||||
return _members.TryRemove(id, out ignored); | |||||
} | |||||
internal void AddBan(string id) | |||||
{ | |||||
_bans.TryAdd(id, true); | |||||
} | |||||
internal bool RemoveBan(string id) | |||||
{ | |||||
bool ignored; | |||||
return _bans.TryRemove(id, out ignored); | |||||
} | |||||
} | } | ||||
} | } |
@@ -1,7 +1,7 @@ | |||||
using System.Reflection; | using System.Reflection; | ||||
[assembly: AssemblyTitle("Discord.Net")] | [assembly: AssemblyTitle("Discord.Net")] | ||||
[assembly: AssemblyDescription("A .Net API Wrapper for the Discord client")] | |||||
[assembly: AssemblyDescription("A .Net API wrapper for the Discord client")] | |||||
[assembly: AssemblyConfiguration("")] | [assembly: AssemblyConfiguration("")] | ||||
[assembly: AssemblyCompany("RogueException")] | [assembly: AssemblyCompany("RogueException")] | ||||
[assembly: AssemblyProduct("Discord.Net")] | [assembly: AssemblyProduct("Discord.Net")] | ||||
@@ -9,5 +9,5 @@ | |||||
[assembly: AssemblyTrademark("")] | [assembly: AssemblyTrademark("")] | ||||
[assembly: AssemblyCulture("")] | [assembly: AssemblyCulture("")] | ||||
[assembly: AssemblyVersion("0.1.0.0")] | |||||
[assembly: AssemblyFileVersion("0.1.0.0")] | |||||
[assembly: AssemblyVersion("0.2.0.0")] | |||||
[assembly: AssemblyFileVersion("0.2.0.0")] |