Browse Source

Initial Commit - V0.1.0

tags/docs-0.9
Brandon Smith 9 years ago
parent
commit
7b29abc2bf
21 changed files with 1731 additions and 0 deletions
  1. +28
    -0
      Discord.Net.sln
  2. +61
    -0
      Discord.Net/API/DiscordAPI.cs
  3. +30
    -0
      Discord.Net/API/Endpoints.cs
  4. +66
    -0
      Discord.Net/API/Models/ApiRequests.cs
  5. +131
    -0
      Discord.Net/API/Models/General.cs
  6. +41
    -0
      Discord.Net/API/Models/WebSocketCommands.cs
  7. +120
    -0
      Discord.Net/API/Models/WebSocketEvents.cs
  8. +78
    -0
      Discord.Net/Discord.Net.csproj
  9. +145
    -0
      Discord.Net/DiscordClient.Events.cs
  10. +400
    -0
      Discord.Net/DiscordClient.cs
  11. +46
    -0
      Discord.Net/DiscordWebSocket.Events.cs
  12. +189
    -0
      Discord.Net/DiscordWebSocket.cs
  13. +142
    -0
      Discord.Net/Helpers/Http.cs
  14. +41
    -0
      Discord.Net/Models/Channel.cs
  15. +31
    -0
      Discord.Net/Models/ChatMessage.cs
  16. +17
    -0
      Discord.Net/Models/ChatMessageReference.cs
  17. +53
    -0
      Discord.Net/Models/Server.cs
  18. +40
    -0
      Discord.Net/Models/User.cs
  19. +36
    -0
      Discord.Net/Properties/AssemblyInfo.cs
  20. +32
    -0
      Discord.Net/Region.cs
  21. +4
    -0
      Discord.Net/packages.config

+ 28
- 0
Discord.Net.sln View File

@@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net", "Discord.Net\Discord.Net.csproj", "{8D23F61B-723C-4966-859D-1119B28BCF19}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1DDC89B5-2A88-45E5-A743-7A43E6B5C4B3}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
LICENSE = LICENSE
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8D23F61B-723C-4966-859D-1119B28BCF19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D23F61B-723C-4966-859D-1119B28BCF19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D23F61B-723C-4966-859D-1119B28BCF19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D23F61B-723C-4966-859D-1119B28BCF19}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

+ 61
- 0
Discord.Net/API/DiscordAPI.cs View File

@@ -0,0 +1,61 @@
using Discord.API.Models;
using Discord.Helpers;
using System.Threading.Tasks;

namespace Discord.API
{
internal static class DiscordAPI
{
public static async Task<AuthRegisterResponse> LoginAnonymous(string username, HttpOptions options)
{
var fingerprintResponse = await Http.Post<AuthFingerprintResponse>(Endpoints.AuthFingerprint, options);
var registerRequest = new AuthRegisterRequest { Fingerprint = fingerprintResponse.Fingerprint, Username = username };
var registerResponse = await Http.Post<AuthRegisterResponse>(Endpoints.AuthRegister, registerRequest, options);
return registerResponse;
}
public static async Task<AuthLoginResponse> Login(string email, string password, HttpOptions options)
{
var request = new AuthLoginRequest { Email = email, Password = password };
var response = await Http.Post<AuthLoginResponse>(Endpoints.AuthLogin, request, options);
options.Token = response.Token;
return response;
}
public static Task Logout(HttpOptions options)
{
return Http.Post(Endpoints.AuthLogout, options);
}

public static Task CreateServer(string name, Region region, HttpOptions options)
{
var request = new CreateServerRequest { Name = name, Region = RegionConverter.Convert(region) };
return Http.Post(Endpoints.Servers, request, options);
}
public static Task DeleteServer(string id, HttpOptions options)
{
return Http.Delete(Endpoints.Server(id), options);
}

public static Task<GetInviteResponse> GetInvite(string id, HttpOptions options)
{
return Http.Get<GetInviteResponse>(Endpoints.Invite(id), options);
}
public static Task AcceptInvite(string id, HttpOptions options)
{
return Http.Post(Endpoints.Invite(id), options);
}
public static Task DeleteInvite(string id, HttpOptions options)
{
return Http.Delete(Endpoints.Invite(id), options);
}

public static Task Typing(string channelId, HttpOptions options)
{
return Http.Post(Endpoints.ChannelTyping(channelId), options);
}
public static Task SendMessage(string channelId, string message, string[] mentions, HttpOptions options)
{
var request = new SendMessageRequest { Content = message, Mentions = mentions };
return Http.Post(Endpoints.ChannelMessages(channelId), request, options);
}
}
}

+ 30
- 0
Discord.Net/API/Endpoints.cs View File

@@ -0,0 +1,30 @@
namespace Discord.API
{
internal static class Endpoints
{
public static readonly string BaseUrl = "discordapp.com/";
public static readonly string BaseHttps = "https://" + BaseUrl;
public static readonly string BaseWss = "wss://" + BaseUrl;

public static readonly string Auth = $"{BaseHttps}/api/auth";
public static readonly string AuthFingerprint = $"{Auth}fingerprint";
public static readonly string AuthRegister = $"{Auth}/register";
public static readonly string AuthLogin = $"{Auth}/login";
public static readonly string AuthLogout = $"{Auth}/logout";

public static readonly string Servers = $"{BaseHttps}/api/guilds";
public static string Server(string id) { return $"{Servers}/{id}"; }
public static string ServerMessages(string id) { return $"{Servers}/{id}/messages?limit=50"; }

public static readonly string Invites = $"{BaseHttps}/api/invite";
public static string Invite(string id) { return $"{Invites}/{id}"; }

public static readonly string Channels = $"{BaseHttps}/api/channels";
public static string Channel(string id) { return $"{Channels}/{id}"; }
public static string ChannelTyping(string id) { return $"{Channels}/{id}/typing"; }
public static string ChannelMessages(string id) { return $"{Channels}/{id}/messages"; }

public static readonly string WebSocket_Hub = BaseWss + "hub";
}
}

+ 66
- 0
Discord.Net/API/Models/ApiRequests.cs View File

@@ -0,0 +1,66 @@
//Ignore unused/unassigned variable warnings
#pragma warning disable CS0649
#pragma warning disable CS0169

using Newtonsoft.Json;

namespace Discord.API.Models
{
public class AuthFingerprintResponse
{
[JsonProperty(PropertyName = "fingerprint")]
public string Fingerprint;
}

public class AuthRegisterRequest
{
[JsonProperty(PropertyName = "fingerprint")]
public string Fingerprint;
[JsonProperty(PropertyName = "username")]
public string Username;
}
public class AuthRegisterResponse : AuthLoginResponse { }

public class AuthLoginRequest
{
[JsonProperty(PropertyName = "email")]
public string Email;
[JsonProperty(PropertyName = "password")]
public string Password;
}
public class AuthLoginResponse
{
[JsonProperty(PropertyName = "token")]
public string Token;
}

public class CreateServerRequest
{
[JsonProperty(PropertyName = "name")]
public string Name;
[JsonProperty(PropertyName = "region")]
public string Region;
}

public class GetInviteResponse
{
[JsonProperty(PropertyName = "inviter")]
public UserInfo Inviter;
[JsonProperty(PropertyName = "guild")]
public ServerInfo Server;
[JsonProperty(PropertyName = "channel")]
public ChannelInfo Channel;
[JsonProperty(PropertyName = "code")]
public string Code;
[JsonProperty(PropertyName = "xkcdpass")]
public string XkcdPass;
}

public class SendMessageRequest
{
[JsonProperty(PropertyName = "content")]
public string Content;
[JsonProperty(PropertyName = "mentions")]
public string[] Mentions;
}
}

+ 131
- 0
Discord.Net/API/Models/General.cs View File

@@ -0,0 +1,131 @@
//Ignore unused/unassigned variable warnings
#pragma warning disable CS0649
#pragma warning disable CS0169

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;

namespace Discord.API.Models
{
internal class WebSocketMessage
{
[JsonProperty(PropertyName = "op")]
public int Operation;
[JsonProperty(PropertyName = "t")]
public string Type;
[JsonProperty(PropertyName = "d")]
public object Payload;
}
internal abstract class WebSocketMessage<T> : WebSocketMessage
where T : new()
{
public WebSocketMessage() { Payload = new T(); }
public WebSocketMessage(int op) { Operation = op; Payload = new T(); }
public WebSocketMessage(int op, T payload) { Operation = op; Payload = payload; }

[JsonIgnore]
public new T Payload
{
get { if (base.Payload is JToken) { base.Payload = (base.Payload as JToken).ToObject<T>(); } return (T)base.Payload; }
set { base.Payload = value; }
}
}

public class UserInfo
{
[JsonProperty(PropertyName = "username")]
public string Username;
[JsonProperty(PropertyName = "id")]
public string Id;
[JsonProperty(PropertyName = "discriminator")]
public string Discriminator;
[JsonProperty(PropertyName = "avatar")]
public string Avatar;
}
public class SelfUserInfo : UserInfo
{
[JsonProperty(PropertyName = "email")]
public string Email;
[JsonProperty(PropertyName = "verified")]
public bool IsVerified;
}
public class PresenceUserInfo : UserInfo
{
[JsonProperty(PropertyName = "game_id")]
public string GameId;
[JsonProperty(PropertyName = "status")]
public string Status;
}

public class MembershipInfo
{
[JsonProperty(PropertyName = "roles")]
public object[] Roles;
[JsonProperty(PropertyName = "mute")]
public bool IsMuted;
[JsonProperty(PropertyName = "deaf")]
public bool IsDeaf;
[JsonProperty(PropertyName = "joined_at")]
public DateTime JoinedAt;
[JsonProperty(PropertyName = "user")]
public UserInfo User;
}

public class ChannelInfo
{
[JsonProperty(PropertyName = "id")]
public string Id;
[JsonProperty(PropertyName = "name")]
public string Name;
[JsonProperty(PropertyName = "last_message_id")]
public string LastMessageId;
[JsonProperty(PropertyName = "is_private")]
public bool IsPrivate;
[JsonProperty(PropertyName = "type")]
public string Type;
[JsonProperty(PropertyName = "permission_overwrites")]
public object[] PermissionOverwrites;
[JsonProperty(PropertyName = "recipient")]
public UserInfo Recipient;
}

public class ServerInfo
{
[JsonProperty(PropertyName = "id")]
public string Id;
[JsonProperty(PropertyName = "name")]
public string Name;
}
public class ExtendedServerInfo : ServerInfo
{
[JsonProperty(PropertyName = "afk_channel_id")]
public string AFKChannelId;
[JsonProperty(PropertyName = "afk_timeout")]
public int AFKTimeout;
[JsonProperty(PropertyName = "channels")]
public ChannelInfo[] Channels;
[JsonProperty(PropertyName = "joined_at")]
public DateTime JoinedAt;
[JsonProperty(PropertyName = "members")]
public MembershipInfo[] Members;
[JsonProperty(PropertyName = "owner_id")]
public string OwnerId;
[JsonProperty(PropertyName = "presence")]
public object[] Presence;
[JsonProperty(PropertyName = "region")]
public string Region;
[JsonProperty(PropertyName = "roles")]
public object[] Roles;
[JsonProperty(PropertyName = "voice_states")]
public object[] VoiceStates;
}

internal class MessageReference
{
[JsonProperty(PropertyName = "message_id")]
public string MessageId;
[JsonProperty(PropertyName = "channel_id")]
public string ChannelId;
}
}

+ 41
- 0
Discord.Net/API/Models/WebSocketCommands.cs View File

@@ -0,0 +1,41 @@
//Ignore unused/unassigned variable warnings
#pragma warning disable CS0649
#pragma warning disable CS0169

using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace Discord.API.Models
{
internal static class WebSocketCommands
{
internal sealed class KeepAlive : WebSocketMessage<int>
{
private static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public KeepAlive() : base(1, (int)(DateTime.UtcNow - epoch).TotalMilliseconds) { }
}
internal sealed class Login : WebSocketMessage<Login.Data>
{
public Login() : base(2) { }
public class Data
{
[JsonProperty(PropertyName = "token")]
public string Token;
[JsonProperty(PropertyName = "properties")]
public Dictionary<string, string> Properties = new Dictionary<string, string>();
}
}
internal sealed class UpdateStatus : WebSocketMessage<UpdateStatus.Data>
{
public UpdateStatus() : base(3) { }
public class Data
{
[JsonProperty(PropertyName = "idle_since")]
public string IdleSince;
[JsonProperty(PropertyName = "game_id")]
public string GameId;
}
}
}
}

+ 120
- 0
Discord.Net/API/Models/WebSocketEvents.cs View File

@@ -0,0 +1,120 @@
//Ignore unused/unassigned variable warnings
#pragma warning disable CS0649
#pragma warning disable CS0169

using Newtonsoft.Json;
using System;

namespace Discord.API.Models
{
internal static class WebSocketEvents
{
internal sealed class Ready
{
[JsonProperty(PropertyName = "user")]
public SelfUserInfo User;
[JsonProperty(PropertyName = "session_id")]
public string SessionId;
[JsonProperty(PropertyName = "read_state")]
public object[] ReadState;
[JsonProperty(PropertyName = "guilds")]
public ExtendedServerInfo[] Guilds;
[JsonProperty(PropertyName = "private_channels")]
public ChannelInfo[] PrivateChannels;
[JsonProperty(PropertyName = "heartbeat_interval")]
public int HeartbeatInterval;
}

internal sealed class GuildCreate : ExtendedServerInfo { }
internal sealed class GuildDelete : ExtendedServerInfo { }

internal sealed class ChannelCreate : ChannelInfo { }
internal sealed class ChannelDelete : ChannelInfo { }

internal sealed class GuildMemberAdd
{
[JsonProperty(PropertyName = "user")]
public UserInfo User;
[JsonProperty(PropertyName = "roles")]
public object[] Roles;
[JsonProperty(PropertyName = "joined_at")]
public DateTime JoinedAt;
[JsonProperty(PropertyName = "guild_id")]
public string GuildId;
}

internal sealed class GuildMemberRemove
{
[JsonProperty(PropertyName = "user")]
public UserInfo User;
[JsonProperty(PropertyName = "guild_id")]
public string GuildId;
}
internal sealed class UserUpdate : SelfUserInfo { }
internal sealed class PresenceUpdate : PresenceUserInfo { }
internal sealed class VoiceStateUpdate
{
[JsonProperty(PropertyName = "user_id")]
public string UserId;
[JsonProperty(PropertyName = "guild_id")]
public string GuildId;
[JsonProperty(PropertyName = "channel_id")]
public string ChannelId;
[JsonProperty(PropertyName = "suppress")]
public bool IsSuppressed;
[JsonProperty(PropertyName = "session_id")]
public string SessionId;
[JsonProperty(PropertyName = "self_mute")]
public bool IsSelfMuted;
[JsonProperty(PropertyName = "self_deaf")]
public bool IsSelfDeafened;
[JsonProperty(PropertyName = "mute")]
public bool IsMuted;
[JsonProperty(PropertyName = "deaf")]
public bool IsDeafened;
}
internal sealed class MessageCreate
{
[JsonProperty(PropertyName = "id")]
public string Id;
[JsonProperty(PropertyName = "channel_id")]
public string ChannelId;
[JsonProperty(PropertyName = "tts")]
public bool IsTextToSpeech;
[JsonProperty(PropertyName = "mention_everyone")]
public bool IsMentioningEveryone;
[JsonProperty(PropertyName = "timestamp")]
public DateTime Timestamp;
[JsonProperty(PropertyName = "mentions")]
public UserInfo[] Mentions;
[JsonProperty(PropertyName = "embeds")]
public object[] Embeds;
[JsonProperty(PropertyName = "attachments")]
public object[] Attachments;
[JsonProperty(PropertyName = "content")]
public string Content;
[JsonProperty(PropertyName = "author")]
public UserInfo Author;
}
internal sealed class MessageUpdate
{
[JsonProperty(PropertyName = "id")]
public string Id;
[JsonProperty(PropertyName = "channel_id")]
public string ChannelId;
[JsonProperty(PropertyName = "embeds")]
public object[] Embeds;
}
internal sealed class MessageDelete : MessageReference { }
internal sealed class MessageAck : MessageReference { }
internal sealed class TypingStart
{
[JsonProperty(PropertyName = "user_id")]
public string UserId;
[JsonProperty(PropertyName = "channel_id")]
public string ChannelId;
[JsonProperty(PropertyName = "timestamp")]
public int Timestamp;
}
}
}

+ 78
- 0
Discord.Net/Discord.Net.csproj View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8D23F61B-723C-4966-859D-1119B28BCF19}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Discord</RootNamespace>
<AssemblyName>Discord.Net</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="API\Models\General.cs" />
<Compile Include="API\Models\ApiRequests.cs" />
<Compile Include="API\Endpoints.cs" />
<Compile Include="API\Models\WebSocketCommands.cs" />
<Compile Include="Models\ChatMessageReference.cs" />
<Compile Include="Models\ChatMessage.cs" />
<Compile Include="Models\Channel.cs" />
<Compile Include="DiscordWebSocket.Events.cs" />
<Compile Include="Helpers\Http.cs" />
<Compile Include="API\DiscordAPI.cs" />
<Compile Include="API\Models\WebSocketEvents.cs" />
<Compile Include="DiscordClient.cs" />
<Compile Include="Region.cs" />
<Compile Include="DiscordClient.Events.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="DiscordWebSocket.cs" />
<Compile Include="Models\Server.cs" />
<Compile Include="Models\User.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

+ 145
- 0
Discord.Net/DiscordClient.Events.cs View File

@@ -0,0 +1,145 @@
using Discord.Models;
using System;

namespace Discord
{
public partial class DiscordClient
{
public sealed class ServerEventArgs : EventArgs
{
public readonly Server Server;
internal ServerEventArgs(Server server) { Server = server; }
}
public sealed class ChannelEventArgs : EventArgs
{
public readonly Channel Channel;
internal ChannelEventArgs(Channel channel) { Channel = channel; }
}
public sealed class UserEventArgs : EventArgs
{
public readonly User User;
internal UserEventArgs(User user) { User = user; }
}
public sealed class MessageCreateEventArgs : EventArgs
{
public readonly ChatMessage Message;
internal MessageCreateEventArgs(ChatMessage msg) { Message = msg; }
}
public sealed class MessageEventArgs : EventArgs
{
public readonly ChatMessageReference Message;
internal MessageEventArgs(ChatMessageReference msg) { Message = msg; }
}
public sealed class LogMessageEventArgs : EventArgs
{
public readonly string Message;
internal LogMessageEventArgs(string msg) { Message = msg; }
}
public sealed class UserTypingEventArgs : EventArgs
{
public readonly User User;
public readonly Channel Channel;
internal UserTypingEventArgs(User user, Channel channel)
{
User = user;
Channel = channel;
}
}

public event EventHandler<LogMessageEventArgs> DebugMessage;
private void RaiseOnDebugMessage(string message)
{
if (DebugMessage != null)
DebugMessage(this, new LogMessageEventArgs(message));
}

public event EventHandler Connected;
private void RaiseConnected()
{
if (Connected != null)
Connected(this, EventArgs.Empty);
}

public event EventHandler Disconnected;
private void RaiseDisconnected()
{
if (Disconnected != null)
Disconnected(this, EventArgs.Empty);
}

public event EventHandler LoggedIn;
private void RaiseLoggedIn()
{
if (LoggedIn != null)
LoggedIn(this, EventArgs.Empty);
}
public event EventHandler<ServerEventArgs> ServerCreated, ServerDestroyed;
private void RaiseServerCreated(Server server)
{
if (ServerCreated != null)
ServerCreated(this, new ServerEventArgs(server));
}
private void RaiseServerDestroyed(Server server)
{
if (ServerDestroyed != null)
ServerDestroyed(this, new ServerEventArgs(server));
}

public event EventHandler<ChannelEventArgs> ChannelCreated, ChannelDestroyed;
private void RaiseChannelCreated(Channel channel)
{
if (ChannelCreated != null)
ChannelCreated(this, new ChannelEventArgs(channel));
}
private void RaiseChannelDestroyed(Channel channel)
{
if (ChannelDestroyed != null)
ChannelDestroyed(this, new ChannelEventArgs(channel));
}

public event EventHandler<MessageCreateEventArgs> MessageCreated;
public event EventHandler<MessageEventArgs> MessageDeleted, MessageUpdated, MessageAcknowledged;
private void RaiseMessageCreated(ChatMessage msg)
{
if (MessageCreated != null)
MessageCreated(this, new MessageCreateEventArgs(msg));
}
private void RaiseMessageDeleted(ChatMessageReference msg)
{
if (MessageDeleted != null)
MessageDeleted(this, new MessageEventArgs(msg));
}
private void RaiseMessageUpdated(ChatMessageReference msg)
{
if (MessageUpdated != null)
MessageUpdated(this, new MessageEventArgs(msg));
}
private void RaiseMessageAcknowledged(ChatMessageReference msg)
{
if (MessageAcknowledged != null)
MessageAcknowledged(this, new MessageEventArgs(msg));
}

public event EventHandler<UserEventArgs> PresenceUpdated;
private void RaisePresenceUpdated(User user)
{
if (PresenceUpdated != null)
PresenceUpdated(this, new UserEventArgs(user));
}

public event EventHandler<UserEventArgs> VoiceStateUpdated;
private void RaiseVoiceStateUpdated(User user)
{
if (VoiceStateUpdated != null)
VoiceStateUpdated(this, new UserEventArgs(user));
}

public event EventHandler<UserTypingEventArgs> UserTyping;
private void RaiseUserTyping(User user, Channel channel)
{
if (UserTyping != null)
UserTyping(this, new UserTypingEventArgs(user, channel));
}
}
}

+ 400
- 0
Discord.Net/DiscordClient.cs View File

@@ -0,0 +1,400 @@
using Discord.API;
using Discord.API.Models;
using Discord.Helpers;
using Discord.Models;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;

namespace Discord
{
public partial class DiscordClient
{
private const int MaxMessageSize = 2000;

private DiscordWebSocket _webSocket;
private HttpOptions _httpOptions;
private bool _isClosing, _isReady;

public string SelfId { get; private set; }
public User Self { get { return GetUser(SelfId); } }

public IEnumerable<User> Users { get { return _users.Values; } }
private ConcurrentDictionary<string, User> _users;

public IEnumerable<Server> Servers { get { return _servers.Values; } }
private ConcurrentDictionary<string, Server> _servers;

public IEnumerable<Channel> Channels { get { return _channels.Values; } }
private ConcurrentDictionary<string, Channel> _channels;

public DiscordClient()
{
string version = typeof(DiscordClient).GetTypeInfo().Assembly.GetName().Version.ToString(2);
_httpOptions = new HttpOptions { UserAgent = $"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)" };

_users = new ConcurrentDictionary<string, User>();
_servers = new ConcurrentDictionary<string, Server>();
_channels = new ConcurrentDictionary<string, Channel>();

_webSocket = new DiscordWebSocket();
_webSocket.Connected += (s,e) => RaiseConnected();
_webSocket.Disconnected += async (s,e) =>
{
//Reconnect if we didn't cause the disconnect
RaiseDisconnected();
if (!_isClosing)
{
await Task.Delay(1000);
await _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, _httpOptions);
}
};
_webSocket.GotEvent += (s, e) =>
{
switch (e.Type)
{
//Global
case "READY": //Resync
{
var data = e.Event.ToObject<WebSocketEvents.Ready>();

_servers.Clear();
_channels.Clear();
_users.Clear();

SelfId = data.User.Id;
UpdateUser(data.User);
foreach (var server in data.Guilds)
UpdateServer(server);
foreach (var channel in data.PrivateChannels)
UpdateChannel(channel as ChannelInfo, null);

RaiseLoggedIn();
}
break;

//Servers
case "GUILD_CREATE":
{
var data = e.Event.ToObject<WebSocketEvents.GuildCreate>();
var server = UpdateServer(data);
RaiseServerCreated(server);
}
break;
case "GUILD_DELETE":
{
var data = e.Event.ToObject<WebSocketEvents.GuildDelete>();
Server server;
if (_servers.TryRemove(data.Id, out server))
RaiseServerDestroyed(server);
}
break;

//Channels
case "CHANNEL_CREATE":
{
var data = e.Event.ToObject<WebSocketEvents.ChannelCreate>();
var channel = UpdateChannel(data, null);
RaiseChannelCreated(channel);
}
break;
case "CHANNEL_DELETE":
{
var data = e.Event.ToObject<WebSocketEvents.ChannelDelete>();
var channel = DeleteChannel(data.Id);
RaiseChannelDestroyed(channel);
}
break;

//Members
case "GUILD_MEMBER_ADD":
{
var data = e.Event.ToObject<WebSocketEvents.GuildMemberAdd>();
var user = UpdateUser(data.User);
var server = GetServer(data.GuildId);
server._members[user.Id] = true;
}
break;
case "GUILD_MEMBER_REMOVE":
{
var data = e.Event.ToObject<WebSocketEvents.GuildMemberRemove>();
var user = UpdateUser(data.User);
var server = GetServer(data.GuildId);
server._members[user.Id] = true;
}
break;

//Users
case "PRESENCE_UPDATE":
{
var data = e.Event.ToObject<WebSocketEvents.PresenceUpdate>();
var user = UpdateUser(data);
RaisePresenceUpdated(user);
}
break;
case "VOICE_STATE_UPDATE":
{
var data = e.Event.ToObject<WebSocketEvents.VoiceStateUpdate>();
var user = GetUser(data.UserId); //TODO: Don't ignore this
RaiseVoiceStateUpdated(user);
}
break;

//Messages
case "MESSAGE_CREATE":
{
var data = e.Event.ToObject<WebSocketEvents.MessageCreate>();
var msg = UpdateMessage(data);
msg.User.UpdateActivity(data.Timestamp);
RaiseMessageCreated(msg);
}
break;
case "MESSAGE_UPDATE":
{
var data = e.Event.ToObject<WebSocketEvents.MessageUpdate>();
var msg = GetMessage(data.Id, data.ChannelId);
RaiseMessageUpdated(msg);
}
break;
case "MESSAGE_DELETE":
{
var data = e.Event.ToObject<WebSocketEvents.MessageDelete>();
var msg = GetMessage(data.MessageId, data.ChannelId);
RaiseMessageDeleted(msg);
}
break;
case "MESSAGE_ACK":
{
var data = e.Event.ToObject<WebSocketEvents.MessageAck>();
var msg = GetMessage(data.MessageId, data.ChannelId);
RaiseMessageAcknowledged(msg);
}
break;
case "TYPING_START":
{
var data = e.Event.ToObject<WebSocketEvents.TypingStart>();
var channel = GetChannel(data.ChannelId);
var user = GetUser(data.UserId);
RaiseUserTyping(user, channel);
}
break;
default:
RaiseOnDebugMessage("Unknown WebSocket message type: " + e.Type);
break;
}
};
_webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Message);
}

public async Task Connect(string email, string password)
{
_isClosing = false;
var response = await DiscordAPI.Login(email, password, _httpOptions);
_httpOptions.Token = response.Token;
await _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, _httpOptions);
_isReady = true;
}
public async Task ConnectAnonymous(string username)
{
_isClosing = false;
var response = await DiscordAPI.LoginAnonymous(username, _httpOptions);
_httpOptions.Token = response.Token;
await _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, _httpOptions);
_isReady = true;
}
public async Task Disconnect()
{
_isReady = false;
_isClosing = true;
await _webSocket.DisconnectAsync();
_isClosing = false;
}

public Task CreateServer(string name, Region region)
{
CheckReady();
return DiscordAPI.CreateServer(name, region, _httpOptions);
}
public Task DeleteServer(string id)
{
CheckReady();
return DiscordAPI.DeleteServer(id, _httpOptions);
}

public Task<GetInviteResponse> GetInvite(string id)
{
CheckReady();
return DiscordAPI.GetInvite(id, _httpOptions);
}
public async Task AcceptInvite(string id)
{
CheckReady();
//Check if this is a human-readable link and get its ID
var response = await DiscordAPI.GetInvite(id, _httpOptions);
await DiscordAPI.AcceptInvite(response.Code, _httpOptions);
}
public async Task DeleteInvite(string id)
{
CheckReady();
//Check if this is a human-readable link and get its ID
var response = await DiscordAPI.GetInvite(id, _httpOptions);
await DiscordAPI.DeleteInvite(response.Code, _httpOptions);
}

public Task SendMessage(string channelId, string text)
{
return SendMessage(channelId, text, new string[0]);
}
public async Task SendMessage(string channelId, string text, string[] mentions)
{
CheckReady();
if (text.Length <= 2000)
await DiscordAPI.SendMessage(channelId, text, mentions, _httpOptions);
else
{
int blockCount = (int)Math.Ceiling(text.Length / (double)MaxMessageSize);
for (int i = 0; i < blockCount; i++)
{
int index = i * MaxMessageSize;
await DiscordAPI.SendMessage(channelId, text.Substring(index, Math.Min(2000, text.Length - index)), mentions, _httpOptions);
await Task.Delay(1000);
}
}
}

public User GetUser(string id)
{
if (id == null) return null;
User user = null;
_users.TryGetValue(id, out user);
return user;
}
private User UpdateUser(UserInfo model)
{
var user = GetUser(model.Id) ?? new User(model.Id, this);
user.Avatar = model.Avatar;
user.Discriminator = model.Discriminator;
user.Name = model.Username;
if (model is SelfUserInfo)
{
var extendedModel = model as SelfUserInfo;
user.Email = extendedModel.Email;
user.IsVerified = extendedModel.IsVerified;
}
if (model is PresenceUserInfo)
{
var extendedModel = model as PresenceUserInfo;
user.GameId = extendedModel.GameId;
user.Status = extendedModel.Status;
}

_users[model.Id] = user;
return user;
}

public Server GetServer(string id)
{
if (id == null) return null;
Server server = null;
_servers.TryGetValue(id, out server);
return server;
}
private Server UpdateServer(ServerInfo model)
{
var server = GetServer(model.Id) ?? new Server(model.Id, this);
server.Name = model.Name;
if (model is ExtendedServerInfo)
{
var extendedModel = model as ExtendedServerInfo;
server.AFKChannelId = extendedModel.AFKChannelId;
server.AFKTimeout = extendedModel.AFKTimeout;
server.JoinedAt = extendedModel.JoinedAt;
server.OwnerId = extendedModel.OwnerId;
server.Presence = extendedModel.Presence;
server.Region = extendedModel.Region;
server.Roles = extendedModel.Roles;
server.VoiceStates = extendedModel.VoiceStates;

foreach (var channel in extendedModel.Channels)
{
UpdateChannel(channel, model.Id);
server._channels[channel.Id] = true;
}
foreach (var membership in extendedModel.Members)
{
UpdateUser(membership.User);
server._members[membership.User.Id] = true;
}
}

_servers[model.Id] = server;
return server;
}

public Channel GetChannel(string id)
{
if (id == null) return null;
Channel channel = null;
_channels.TryGetValue(id, out channel);
return channel;
}
private Channel UpdateChannel(ChannelInfo model, string serverId)
{
var channel = GetChannel(model.Id) ?? new Channel(model.Id, serverId, this);

channel.Name = model.Name;
channel.IsPrivate = model.IsPrivate;
channel.PermissionOverwrites = model.PermissionOverwrites;
channel.RecipientId = model.Recipient?.Id;
channel.Type = model.Type;

_channels[model.Id] = channel;
return channel;
}
private Channel DeleteChannel(string id)
{
Channel channel = null;
if (_channels.TryRemove(id, out channel))
{
bool ignored;
channel.Server._channels.TryRemove(id, out ignored);
}
return channel;
}

//TODO: Temporary measure, unsure if we want to store these or not.
private ChatMessageReference GetMessage(string id, string channelId)
{
if (id == null || channelId == null) return null;
var msg = new ChatMessageReference(id, this);

msg.ChannelId = channelId;

return msg;
}
private ChatMessage UpdateMessage(WebSocketEvents.MessageCreate model)
{
return new ChatMessage(model.Id, this)
{
Attachments = model.Attachments,
ChannelId = model.ChannelId,
Text = model.Content,
Embeds = model.Embeds,
IsMentioningEveryone = model.IsMentioningEveryone,
IsTTS = model.IsTextToSpeech,
UserId = model.Author.Id,
Timestamp = model.Timestamp
};
}

private void CheckReady()
{
if (!_isReady)
throw new InvalidOperationException("The client is not currently connected to Discord");
}
}
}

+ 46
- 0
Discord.Net/DiscordWebSocket.Events.cs View File

@@ -0,0 +1,46 @@
using Newtonsoft.Json.Linq;
using System;

namespace Discord
{
internal partial class DiscordWebSocket
{
public event EventHandler Connected;
private void RaiseConnected()
{
if (Connected != null)
Connected(this, EventArgs.Empty);
}

public event EventHandler Disconnected;
private void RaiseDisconnected()
{
if (Disconnected != null)
Disconnected(this, EventArgs.Empty);
}

public event EventHandler<MessageEventArgs> GotEvent;
public sealed class MessageEventArgs : EventArgs
{
public readonly string Type;
public readonly JToken Event;
internal MessageEventArgs(string type, JToken data)
{
Type = type;
Event = data;
}
}
private void RaiseGotEvent(string type, JToken payload)
{
if (GotEvent != null)
GotEvent(this, new MessageEventArgs(type, payload));
}

public event EventHandler<DiscordClient.LogMessageEventArgs> OnDebugMessage;
private void RaiseOnDebugMessage(string message)
{
if (OnDebugMessage != null)
OnDebugMessage(this, new DiscordClient.LogMessageEventArgs(message));
}
}
}

+ 189
- 0
Discord.Net/DiscordWebSocket.cs View File

@@ -0,0 +1,189 @@
using Discord.API.Models;
using Discord.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Discord
{
internal sealed partial class DiscordWebSocket : IDisposable
{
private const int ReceiveChunkSize = 4096;
private const int SendChunkSize = 4096;

private volatile ClientWebSocket _webSocket;
private volatile CancellationTokenSource _cancelToken;
private volatile Task _tasks;
private ConcurrentQueue<byte[]> _sendQueue;
private int _heartbeatInterval;
private DateTime _lastHeartbeat;

public async Task ConnectAsync(string url, HttpOptions options)
{
await DisconnectAsync();

_sendQueue = new ConcurrentQueue<byte[]>();

_webSocket = new ClientWebSocket();
_webSocket.Options.Cookies = options.Cookies;
_webSocket.Options.KeepAliveInterval = TimeSpan.Zero;

_cancelToken = new CancellationTokenSource();
var cancelToken = _cancelToken.Token;

await _webSocket.ConnectAsync(new Uri(url), cancelToken);
_tasks = Task.WhenAll(
await Task.Factory.StartNew(ReceiveAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default),
await Task.Factory.StartNew(SendAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)
).ContinueWith(x =>
{
//Do not clean up until both tasks have ended
_heartbeatInterval = 0;
_lastHeartbeat = DateTime.MinValue;
_webSocket.Dispose();
_webSocket = null;
_cancelToken.Dispose();
_cancelToken = null;
_tasks = null;

RaiseDisconnected();
});

WebSocketCommands.Login msg = new WebSocketCommands.Login();
msg.Payload.Token = options.Token;
msg.Payload.Properties["$os"] = "";
msg.Payload.Properties["$browser"] = "";
msg.Payload.Properties["$device"] = "Discord.Net";
msg.Payload.Properties["$referrer"] = "";
msg.Payload.Properties["$referring_domain"] = "";
SendMessage(msg, cancelToken);
}
public async Task DisconnectAsync()
{
if (_webSocket != null)
{
_cancelToken.Cancel();
await _tasks;
}
}

private async Task ReceiveAsync()
{
RaiseConnected();

var cancelToken = _cancelToken.Token;
var buffer = new byte[ReceiveChunkSize];
var builder = new StringBuilder();

try
{
while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
{
WebSocketReceiveResult result;
do
{
result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), _cancelToken.Token);

if (result.MessageType == WebSocketMessageType.Close)
{
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
return;
}
else
builder.Append(Encoding.UTF8.GetString(buffer, 0, result.Count));

}
while (!result.EndOfMessage);

var msg = JsonConvert.DeserializeObject<WebSocketMessage>(builder.ToString());
switch (msg.Operation)
{
case 0:
if (msg.Type == "READY")
{
var payload = (msg.Payload as JToken).ToObject<WebSocketEvents.Ready>();
_heartbeatInterval = payload.HeartbeatInterval;
SendMessage(new WebSocketCommands.UpdateStatus(), cancelToken);
SendMessage(new WebSocketCommands.KeepAlive(), cancelToken);
}
RaiseGotEvent(msg.Type, msg.Payload as JToken);
break;
default:
RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation);
break;
}

builder.Clear();
}
}
catch { }
finally { _cancelToken.Cancel(); }
}

private async Task SendAsync()
{
var cancelToken = _cancelToken.Token;
try
{
byte[] bytes;
while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
{
if (_heartbeatInterval > 0)
{
DateTime now = DateTime.UtcNow;
if ((now - _lastHeartbeat).TotalMilliseconds > _heartbeatInterval)
{
SendMessage(new WebSocketCommands.KeepAlive(), cancelToken);
_lastHeartbeat = now;
}
}
while (_sendQueue.TryDequeue(out bytes))
{
var frameCount = (int)Math.Ceiling((double)bytes.Length / SendChunkSize);

int offset = 0;
for (var i = 0; i < frameCount; i++, offset += SendChunkSize)
{
bool isLast = i == (frameCount - 1);

int count;
if (isLast)
count = bytes.Length - (i * SendChunkSize);
else
count = SendChunkSize;

await _webSocket.SendAsync(new ArraySegment<byte>(bytes, offset, count), WebSocketMessageType.Text, isLast, cancelToken);
}
}
await Task.Delay(100);
}
}
catch { }
finally { _cancelToken.Cancel(); }
}

private void SendMessage(object frame, CancellationToken token)
{
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(frame));
_sendQueue.Enqueue(bytes);
}

#region IDisposable Support
private bool _isDisposed = false;

public void Dispose()
{
if (!_isDisposed)
{
DisconnectAsync().Wait();
_isDisposed = true;
}
}
#endregion
}
}

+ 142
- 0
Discord.Net/Helpers/Http.cs View File

@@ -0,0 +1,142 @@
using Newtonsoft.Json;
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Cache;
using System.Text;
using System.Threading.Tasks;

namespace Discord.Helpers
{
internal class HttpOptions
{
public string UserAgent, Token;
public CookieContainer Cookies;

public HttpOptions(string userAgent = null)
{
UserAgent = userAgent ?? "DiscordAPI";
Cookies = new CookieContainer(1);
}
}

internal static class Http
{
private static readonly RequestCachePolicy _cachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);

internal static async Task<ResponseT> Get<ResponseT>(string path, object data, HttpOptions options)
where ResponseT : class
{
string requestJson = JsonConvert.SerializeObject(data);
string responseJson = await SendRequest("GET", path, requestJson, options, true);
return JsonConvert.DeserializeObject<ResponseT>(responseJson);
}
internal static async Task<ResponseT> Get<ResponseT>(string path, HttpOptions options)
where ResponseT : class
{
string responseJson = await SendRequest("GET", path, null, options, true);
return JsonConvert.DeserializeObject<ResponseT>(responseJson);
}

internal static async Task<ResponseT> Post<ResponseT>(string path, object data, HttpOptions options)
where ResponseT : class
{
string requestJson = JsonConvert.SerializeObject(data);
string responseJson = await SendRequest("POST", path, requestJson, options, true);
return JsonConvert.DeserializeObject<ResponseT>(responseJson);
}
internal static Task Post(string path, object data, HttpOptions options)
{
string requestJson = JsonConvert.SerializeObject(data);
return SendRequest("POST", path, requestJson, options, false);
}
internal static async Task<ResponseT> Post<ResponseT>(string path, HttpOptions options)
where ResponseT : class
{
string responseJson = await SendRequest("POST", path, null, options, true);
return JsonConvert.DeserializeObject<ResponseT>(responseJson);
}
internal static Task Post(string path, HttpOptions options)
{
return SendRequest("POST", path, null, options, false);
}

internal static Task Delete(string path, HttpOptions options)
{
return SendRequest("DELETE", path, null, options, false);
}

private static async Task<string> SendRequest(string method, string path, string data, HttpOptions options, bool hasResponse)
{
options = options ?? new HttpOptions();

//Create Request
HttpWebRequest request = WebRequest.CreateHttp(path);
request.Accept = "*/*";
request.Headers[HttpRequestHeader.AcceptLanguage] = "en-US;q=0.8";
request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
request.CachePolicy = _cachePolicy;
request.CookieContainer = options.Cookies;
request.Method = method;
request.UserAgent = options.UserAgent;

//Add Payload
if (data != null)
{
byte[] buffer = Encoding.UTF8.GetBytes(data);
using (var payload = await request.GetRequestStreamAsync())
payload.Write(buffer, 0, buffer.Length);
request.ContentType = "application/json";
}

//Get Response
using (var response = (HttpWebResponse)(await request.GetResponseAsync()))
{
if (hasResponse)
{
using (var stream = response.GetResponseStream())
using (var reader = new BinaryReader(stream))
using (var largeBuffer = new MemoryStream())
{
//Read the response in small chunks and add them to a larger buffer.
//ContentLength isn't always provided, so this is safer.
int bytesRead = 0;
byte[] smallBuffer = new byte[4096];
while ((bytesRead = reader.Read(smallBuffer, 0, smallBuffer.Length)) > 0)
largeBuffer.Write(smallBuffer, 0, bytesRead);

//Do we need to decompress?
if (!string.IsNullOrEmpty(response.ContentEncoding))
{
largeBuffer.Position = 0;
using (var decoder = GetDecoder(response.ContentEncoding, largeBuffer))
using (var decodedStream = new MemoryStream())
{
decoder.CopyTo(decodedStream);
return Encoding.UTF8.GetString(decodedStream.GetBuffer(), 0, (int)decodedStream.Length);
}
}
else
return Encoding.UTF8.GetString(largeBuffer.GetBuffer(), 0, (int)largeBuffer.Length);
}
}
else
return null;
}
}

private static Stream GetDecoder(string contentEncoding, MemoryStream encodedStream)
{
switch (contentEncoding)
{
case "gzip":
return new GZipStream(encodedStream, CompressionMode.Decompress, true);
case "deflate":
return new DeflateStream(encodedStream, CompressionMode.Decompress, true);
default:
throw new ArgumentOutOfRangeException("Unknown encoding: " + contentEncoding);
}
}
}
}

+ 41
- 0
Discord.Net/Models/Channel.cs View File

@@ -0,0 +1,41 @@
using Newtonsoft.Json;

namespace Discord.Models
{
public class Channel
{
protected readonly DiscordClient _client;
private string _name;

public string Id { get; }
public string Name { get { return !IsPrivate ? _name : '@' + Recipient.Name; } internal set { _name = value; } }

public bool IsPrivate { get; internal set; }
public string Type { get; internal set; }

[JsonIgnore]
public string ServerId { get; }
[JsonIgnore]
public Server Server { get { return ServerId != null ? _client.GetServer(ServerId) : null; } }

[JsonIgnore]
public string RecipientId { get; internal set; }
public User Recipient { get { return _client.GetUser(RecipientId); } }

//Not Implemented
public object[] PermissionOverwrites { get; internal set; }

internal Channel(string id, string serverId, DiscordClient client)
{
Id = id;
ServerId = serverId;
_client = client;
}

public override string ToString()
{
return Name;
//return Name + " (" + Id + ")";
}
}
}

+ 31
- 0
Discord.Net/Models/ChatMessage.cs View File

@@ -0,0 +1,31 @@
using Newtonsoft.Json;
using System;

namespace Discord.Models
{
public class ChatMessage : ChatMessageReference
{
public bool IsMentioningEveryone { get; internal set; }
public bool IsTTS { get; internal set; }
public string Text { get; internal set; }
public DateTime Timestamp { get; internal set; }

[JsonIgnore]
public string UserId { get; internal set; }
public User User { get { return _client.GetUser(UserId); } }
//Not Implemented
public object[] Attachments { get; internal set; }
public object[] Embeds { get; internal set; }

internal ChatMessage(string id, DiscordClient client)
: base(id, client)
{
}

public override string ToString()
{
return User.ToString() + ": " + Text;
}
}
}

+ 17
- 0
Discord.Net/Models/ChatMessageReference.cs View File

@@ -0,0 +1,17 @@
namespace Discord.Models
{
public class ChatMessageReference
{
protected readonly DiscordClient _client;

public string Id { get; }
public string ChannelId { get; internal set; }
public Channel Channel { get { return _client.GetChannel(ChannelId); } }

internal ChatMessageReference(string id, DiscordClient client)
{
Id = id;
_client = client;
}
}
}

+ 53
- 0
Discord.Net/Models/Server.cs View File

@@ -0,0 +1,53 @@
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace Discord.Models
{
public class Server
{
protected readonly DiscordClient _client;

public string Id { get; }
public string Name { get; internal set; }

public string AFKChannelId { get; internal set; }
public int AFKTimeout { get; internal set; }
public DateTime JoinedAt { get; internal set; }
public string Region { get; internal set; }
public string OwnerId { get; internal set; }
public User Owner { get { return _client.GetUser(OwnerId); } }

internal ConcurrentDictionary<string, bool> _members;
[JsonIgnore]
public IEnumerable<string> MemberIds { get { return _members.Keys; } }
public IEnumerable<User> Members { get { return _members.Keys.Select(x => _client.GetUser(x)); } }

internal ConcurrentDictionary<string, bool> _channels;
[JsonIgnore]
public IEnumerable<string> ChannelIds { get { return _channels.Keys; } }
public IEnumerable<Channel> Channels { get { return _channels.Keys.Select(x => _client.GetChannel(x)); } }

//Not Implemented
public object Presence { get; internal set; }
public object[] Roles { get; internal set; }
public object[] VoiceStates { get; internal set; }

internal Server(string id, DiscordClient client)
{
Id = id;
_client = client;
_members = new ConcurrentDictionary<string, bool>();
_channels = new ConcurrentDictionary<string, bool>();
}

public override string ToString()
{
return Name;
//return Name + " (" + Id + ")";
}
}
}

+ 40
- 0
Discord.Net/Models/User.cs View File

@@ -0,0 +1,40 @@
using System;

namespace Discord.Models
{
public class User
{
protected readonly DiscordClient _client;

public string Id { get; }
public string Name { get; internal set; }

public string Avatar { get; internal set; }
public string Discriminator { get; internal set; }
public string Email { get; internal set; }
public bool IsVerified { get; internal set; } = true;
public string GameId { get; internal set; }
public string Status { get; internal set; }

public DateTime LastActivity { get; private set; }

internal User(string id, DiscordClient client)
{
Id = id;
_client = client;
LastActivity = DateTime.UtcNow;
}

internal void UpdateActivity(DateTime activity)
{
if (activity > LastActivity)
LastActivity = activity;
}

public override string ToString()
{
return Name;
//return Name + " (" + Id + ")";
}
}
}

+ 36
- 0
Discord.Net/Properties/AssemblyInfo.cs View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("DiscordAPI")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DiscordAPI")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("8d23f61b-723c-4966-859d-1119b28bcf19")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

+ 32
- 0
Discord.Net/Region.cs View File

@@ -0,0 +1,32 @@
using System;

namespace Discord
{
public enum Region
{
US_West,
US_East,
Singapore,
London,
Sydney,
Amsterdam
}

internal static class RegionConverter
{
public static string Convert(Region region)
{
switch (region)
{
case Region.US_West: return "us-west";
case Region.US_East: return "us-east";
case Region.Singapore: return "singapore";
case Region.London: return "london";
case Region.Sydney: return "sydney";
case Region.Amsterdam: return "amsterdam";
default:
throw new ArgumentOutOfRangeException("Unknown server region");
}
}
}
}

+ 4
- 0
Discord.Net/packages.config View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" />
</packages>

Loading…
Cancel
Save