| @@ -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")] | |||||