diff --git a/src/Discord.Net/API/Responses.cs b/src/Discord.Net/API/Responses.cs new file mode 100644 index 000000000..d98ec2dc6 --- /dev/null +++ b/src/Discord.Net/API/Responses.cs @@ -0,0 +1,112 @@ +//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 +{ + //Auth + public sealed class GatewayResponse + { + [JsonProperty("url")] + public string Url; + } + public sealed class FingerprintResponse + { + [JsonProperty("fingerprint")] + public string Fingerprint; + } + public sealed class RegisterResponse + { + [JsonProperty("token")] + public string Token; + } + public sealed class LoginResponse + { + [JsonProperty("token")] + public string Token; + } + + //Channels + public sealed class CreateChannelResponse : ChannelInfo { } + public sealed class DestroyChannelResponse : ChannelInfo { } + public sealed class EditChannelResponse : ChannelInfo { } + + //Invites + public sealed class CreateInviteResponse : ExtendedInvite { } + public sealed class GetInviteResponse : Invite { } + public sealed class AcceptInviteResponse : Invite { } + + //Messages + public sealed class SendMessageResponse : Message { } + public sealed class EditMessageResponse : Message { } + public sealed class GetMessagesResponse : List { } + + //Profile + public sealed class EditProfileResponse : SelfUserInfo { } + + //Servers + public sealed class CreateServerResponse : GuildInfo { } + public sealed class DeleteServerResponse : GuildInfo { } + public sealed class EditServerResponse : GuildInfo { } + + //Voice + public sealed class GetRegionsResponse : List + { + public sealed class RegionData + { + [JsonProperty("sample_hostname")] + public string Hostname; + [JsonProperty("sample_port")] + public int Port; + [JsonProperty("id")] + public string Id; + [JsonProperty("name")] + public string Name; + } + } + public sealed class GetIceResponse + { + [JsonProperty("ttl")] + public string TTL; + [JsonProperty("servers")] + public ServerData[] Servers; + + public sealed class ServerData + { + [JsonProperty("url")] + public string URL; + [JsonProperty("username")] + public string Username; + [JsonProperty("credential")] + public string Credential; + } + } + + public sealed class GetIncidentsResponse + { + [JsonProperty("page")] + public PageData Page; + [JsonProperty("scheduled_maintenances")] + public MaintenanceData[] ScheduledMaintenances; + + public sealed class PageData + { + [JsonProperty("id")] + public string Id; + [JsonProperty("name")] + public string Name; + [JsonProperty("url")] + public string Url; + [JsonProperty("updated-at")] + public DateTime UpdatedAt; + } + + public sealed class MaintenanceData + { + } + } +} diff --git a/src/Discord.Net/DiscordAPIClient.cs b/src/Discord.Net/DiscordAPIClient.cs new file mode 100644 index 000000000..7a159bf83 --- /dev/null +++ b/src/Discord.Net/DiscordAPIClient.cs @@ -0,0 +1,293 @@ +using Discord.API; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord +{ + /// A lightweight wrapper around the Discord API. + public class DiscordAPIClient + { + internal RestClient RestClient => _rest; + private readonly RestClient _rest; + + public DiscordAPIClient(LogMessageSeverity logLevel, int timeout) + { + _rest = new RestClient(logLevel, timeout); + } + + private string _token; + public string Token + { + get { return _token; } + set { _token = value; _rest.SetToken(value); } + } + private CancellationToken _cancelToken; + public CancellationToken CancelToken + { + get { return _cancelToken; } + set { _cancelToken = value; _rest.SetCancelToken(value); } + } + + //Auth + public Task Gateway() + => _rest.Get(Endpoints.Gateway); + public Task Fingerprint() + => _rest.Post(Endpoints.AuthFingerprint); + public async Task LoginAnonymous(string username, string fingerprint) + { + if (username == null) throw new ArgumentNullException(nameof(username)); + if (fingerprint == null) throw new ArgumentNullException(nameof(fingerprint)); + + var request = new RegisterRequest { Fingerprint = fingerprint, Username = username }; + return await _rest.Post(Endpoints.AuthRegister, request).ConfigureAwait(false); + } + public async Task Login(string email, string password) + { + if (email == null) throw new ArgumentNullException(nameof(email)); + if (password == null) throw new ArgumentNullException(nameof(password)); + + var request = new LoginRequest { Email = email, Password = password }; + return await _rest.Post(Endpoints.AuthLogin, request).ConfigureAwait(false); + } + public Task Logout() + => _rest.Post(Endpoints.AuthLogout); + + //Channels + public Task CreateChannel(string serverId, string name, string channelType) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (channelType == null) throw new ArgumentNullException(nameof(channelType)); + + var request = new CreateChannelRequest { Name = name, Type = channelType }; + return _rest.Post(Endpoints.ServerChannels(serverId), request); + } + public Task CreatePMChannel(string myId, string recipientId) + { + if (myId == null) throw new ArgumentNullException(nameof(myId)); + if (recipientId == null) throw new ArgumentNullException(nameof(recipientId)); + + var request = new CreatePMChannelRequest { RecipientId = recipientId }; + return _rest.Post(Endpoints.UserChannels(myId), request); + } + public Task DestroyChannel(string channelId) + { + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + + return _rest.Delete(Endpoints.Channel(channelId)); + } + public Task EditChannel(string channelId, string name = null, string topic = null) + { + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + + var request = new EditChannelRequest { Name = name, Topic = topic }; + return _rest.Patch(Endpoints.Channel(channelId), request); + } + public Task GetMessages(string channelId, int count) + { + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + + return _rest.Get(Endpoints.ChannelMessages(channelId, count)); + } + + //Incidents + public Task GetUnresolvedIncidents() + { + return _rest.Get(Endpoints.StatusUnresolvedMaintenance); + } + public Task GetActiveIncidents() + { + return _rest.Get(Endpoints.StatusActiveMaintenance); + } + public Task GetUpcomingIncidents() + { + return _rest.Get(Endpoints.StatusUpcomingMaintenance); + } + + //Invites + public Task CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool withXkcdPass) + { + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + + var request = new CreateInviteRequest { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = isTemporary, WithXkcdPass = withXkcdPass }; + return _rest.Post(Endpoints.ChannelInvites(channelId), request); + } + public Task GetInvite(string inviteIdOrXkcd) + { + if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd)); + + return _rest.Get(Endpoints.Invite(inviteIdOrXkcd)); + } + public Task AcceptInvite(string inviteId) + { + if (inviteId == null) throw new ArgumentNullException(nameof(inviteId)); + + return _rest.Post(Endpoints.Invite(inviteId)); + } + public Task DeleteInvite(string inviteId) + { + if (inviteId == null) throw new ArgumentNullException(nameof(inviteId)); + + return _rest.Delete(Endpoints.Invite(inviteId)); + } + + //Members + public Task EditMember(string serverId, string userId, bool? mute = null, bool? deaf = null, string[] roles = null) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (userId == null) throw new ArgumentNullException(nameof(userId)); + + var request = new EditMemberRequest { Mute = mute, Deaf = deaf, Roles = roles }; + return _rest.Patch(Endpoints.ServerMember(serverId, userId)); + } + public Task Kick(string serverId, string userId) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (userId == null) throw new ArgumentNullException(nameof(userId)); + + return _rest.Delete(Endpoints.ServerMember(serverId, userId)); + } + public Task Ban(string serverId, string userId) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (userId == null) throw new ArgumentNullException(nameof(userId)); + + return _rest.Put(Endpoints.ServerBan(serverId, userId)); + } + public Task Unban(string serverId, string userId) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (userId == null) throw new ArgumentNullException(nameof(userId)); + + return _rest.Delete(Endpoints.ServerBan(serverId, userId)); + } + + //Messages + public Task SendMessage(string channelId, string message, string[] mentions = null, string nonce = null, bool isTTS = false) + { + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (message == null) throw new ArgumentNullException(nameof(message)); + + var request = new SendMessageRequest { Content = message, Mentions = mentions ?? new string[0], Nonce = nonce, IsTTS = isTTS ? true : false }; + return _rest.Post(Endpoints.ChannelMessages(channelId), request); + } + public Task SendFile(string channelId, string filePath) + { + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (filePath == null) throw new ArgumentNullException(nameof(filePath)); + + return _rest.PostFile(Endpoints.ChannelMessages(channelId), filePath); + } + public Task DeleteMessage(string messageId, string channelId) + { + if (messageId == null) throw new ArgumentNullException(nameof(messageId)); + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + + return _rest.Delete(Endpoints.ChannelMessage(channelId, messageId)); + } + public Task EditMessage(string messageId, string channelId, string message = null, string[] mentions = null) + { + if (messageId == null) throw new ArgumentNullException(nameof(messageId)); + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + + var request = new EditMessageRequest { Content = message, Mentions = mentions }; + return _rest.Patch(Endpoints.ChannelMessage(channelId, messageId), request); + } + public Task SendIsTyping(string channelId) + { + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + + return _rest.Post(Endpoints.ChannelTyping(channelId)); + } + + //Permissions + public Task SetChannelPermissions(string channelId, string userOrRoleId, string idType, uint allow = 0, uint deny = 0) + { + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (userOrRoleId == null) throw new ArgumentNullException(nameof(userOrRoleId)); + if (idType == null) throw new ArgumentNullException(nameof(idType)); + + var request = new SetChannelPermissionsRequest { Id = userOrRoleId, Type = idType, Allow = allow, Deny = deny }; + return _rest.Put(Endpoints.ChannelPermission(channelId, userOrRoleId), request); + } + public Task DeleteChannelPermissions(string channelId, string userOrRoleId) + { + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (userOrRoleId == null) throw new ArgumentNullException(nameof(userOrRoleId)); + + return _rest.Delete(Endpoints.ChannelPermission(channelId, userOrRoleId), null); + } + + //Profile + public Task EditProfile(string currentPassword, + string username = null, string email = null, string password = null, + AvatarImageType avatarType = AvatarImageType.Png, byte[] avatar = null) + { + if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); + + string avatarBase64 = null; + if (avatar != null) + { + string base64 = Convert.ToBase64String(avatar); + string type = avatarType == AvatarImageType.Jpeg ? "image/jpeg;base64" : "image/png;base64"; + avatarBase64 = $"data:{type},/9j/{base64}"; + } + var request = new EditProfileRequest { CurrentPassword = currentPassword, Username = username, Email = email, Password = password, Avatar = avatarBase64 }; + return _rest.Patch(Endpoints.UserMe, request); + } + + //Roles + public Task CreateRole(string serverId) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + + //TODO: Return a response when Discord starts giving us one + return _rest.Post(Endpoints.ServerRoles(serverId)); + } + public Task DeleteRole(string serverId, string roleId) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (roleId == null) throw new ArgumentNullException(nameof(roleId)); + + return _rest.Delete(Endpoints.ServerRole(serverId, roleId)); + } + public Task EditRole(string serverId, string roleId, string name = null, uint? permissions = null) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (roleId == null) throw new ArgumentNullException(nameof(roleId)); + + var request = new EditRoleRequest { Name = name, Permissions = permissions }; + return _rest.Patch(Endpoints.ServerRole(serverId, roleId), request); + } + + //Servers + public Task CreateServer(string name, string region) + { + if (name == null) throw new ArgumentNullException(nameof(name)); + if (region == null) throw new ArgumentNullException(nameof(region)); + + var request = new CreateServerRequest { Name = name, Region = region }; + return _rest.Post(Endpoints.Servers, request); + } + public Task LeaveServer(string serverId) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + + return _rest.Delete(Endpoints.Server(serverId)); + } + public Task EditServer(string serverId, string name = null, string region = null) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + + var request = new EditServerRequest { Name = name, Region = region }; + return _rest.Patch(Endpoints.Server(serverId), request); + } + + //Voice + public Task GetVoiceRegions() + => _rest.Get(Endpoints.VoiceRegions); + public Task GetVoiceIce() + => _rest.Get(Endpoints.VoiceIce); + } +}