using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using JacobTechEncryption; using JacobTechEncryption.Enums; using Luski.net.Enums; using Luski.net.Interfaces; using Luski.net.JsonTypes; using Luski.net.JsonTypes.HTTP; using Luski.net.Structures; using Luski.net.Structures.Public; using Luski.Shared.PublicServers.V1.ClientToServer.HTTP; using Luski.Shared.PublicServers.V1.Enums; using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; using Luski.Shared.PublicServers.V1.Shared; using Role = Luski.net.Structures.Public.Role; using SocketChannelProfile = Luski.net.Structures.Public.SocketChannelProfile; using SocketUser = Luski.net.Structures.Public.SocketUser; namespace Luski.net; public partial class PublicServer : Server { public event Func? MessageReceived; public List chans { get; } = new(); public List cats { get; } = new(); public List roles { get; } = new(); public SocketAppUser User { get; private set; } = null!; private PublicServer(string Domain, string API_Version, bool Secure = true) : base(Domain, API_Version, Secure) { } internal static async Task GetServer(string Domain, string API_Version, bool Secure = true, bool GenerateEncryption = true, bool LogConsole = false) { DateTime dt = DateTime.UtcNow; Console.WriteLine("Connecting to public server '{0}' using API {1}.", Domain, API_Version); PublicServer s = new(Domain, API_Version, Secure); s.PrintServerMessages = LogConsole; if (GenerateEncryption) { Thread t = new(_ => { s.EncryptionHandler.GenerateKeys(); }); t.Start(); } ServerInfoSTC? si = null; try { si = await s.GetFromServer("socketserver", ServerInfoSTCContext.Default.ServerInfoSTC, CancellationToken.None); s.EncryptionHandler.ServerPublicKey = await (await new HttpClient() .GetAsync($"{(s.Secure ? "https" : "http")}://{s.Domain}/{s.ApiVersion}/Keys/PublicKey")) .Content .ReadAsStringAsync(); } catch (Exception e) { LocalServerInfo ServerListing = s.Storage.GetJson(StorageDirectory.ServerInfo, "Servers.json", true, LocalServerInfoContext.Default.LocalServerInfo); if (ServerListing.AlternateServers.Length > 0) { Console.WriteLine("Failed to connect to public server '{0}' using API {1}. Attempting to connect to alternate servers.", Domain, API_Version); foreach (ServerData Server in ServerListing.AlternateServers) { s.Secure = Server.Secure; s.Domain = Server.DomainAndPort; try { si = await s.GetFromServer("socketserver", ServerInfoSTCContext.Default.ServerInfoSTC, CancellationToken.None); s.EncryptionHandler.ServerPublicKey = await (await new HttpClient() .GetAsync($"{(s.Secure ? "https" : "http")}://{s.Domain}/{s.ApiVersion}/Keys/PublicKey")) .Content .ReadAsStringAsync(); Console.WriteLine("Public server '{0}' connection restored by alternate server '{1}' using API {2}.", Domain, s.Domain, API_Version); break; } catch { // ignored } } } if (si is null) throw; } s.Name = si.Name; s.Description = si.Description; s.wssurl = si.WSSv4Address; s.ServerType = ServerType.Public; s.OwnerID = si.Owner; Console.WriteLine("Connected to public server '{0}' using API {1} in {4}.\nServer Name: {2}\nServer Description: {3}", Domain, API_Version, s.Name, s.Description, DateTime.UtcNow.Subtract(dt).ToString("g")); return s; } public long OwnerID { get; private set; } = 0; public async Task GetCategory(long id, CancellationToken CancellationToken) where TCategory : SocketCategory, new() { CategorySTC request; if (cats.Count > 0 && cats.Any(s => s.ID == id)) { return (cats.Where(s => s is TCategory && s.ID == id).First() as TCategory)!; } while (true) { if (CanRequest) { request = await GetFromServer($"SocketCategory", CategorySTCContext.Default.CategorySTC, CancellationToken, new KeyValuePair("id", id.ToString())); break; } } if (request is null) throw new Exception("Something was wrong with the server responce"); if (request.Error is null) { if (cats.Count > 0 && cats.Any(s => s.ID == request.ID)) { foreach (SocketCategory? p in cats.Where(s => s.ID == request.ID)) { cats.Remove(p); } } LocalKeyInfo deckey; if (request.DescriptionEncryptionKey != 0) deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); else deckey = new() { EncryptionType = EncryptionType.None, Key = string.Empty }; string dec = deckey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Description), deckey.Key, request.DescriptionEncoderType), EncryptionType.AES => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString( Encryption.AES.Decrypt(Convert.FromBase64String(request.Description), deckey.Key)), _ => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString(Convert.FromBase64String(request.Description)) }; LocalKeyInfo nkey; if (request.TitleEncryptionKey != 0) nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); else nkey = new() { EncryptionType = EncryptionType.None, Key = string.Empty }; string n = nkey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Name), nkey.Key, request.TitleEncoderType), EncryptionType.AES => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString( Encryption.AES.Decrypt(Convert.FromBase64String(request.Name), nkey.Key)), _ => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString(Convert.FromBase64String(request.Name)) }; TCategory bob = new() { ID = request.ID, ParentID = request.Parent, Description = dec, DescriptionEncoderType = request.DescriptionEncoderType, DescriptionEncryptionKey = request.DescriptionEncryptionKey, Channels = request.Channels, Categories = request.InnerCategories, Name = n, RoleOverides = request.RoleOverrides, UserOverides = request.UserOverrides, TitleEncoderType = request.TitleEncoderType, TitleEncryptionKey = request.TitleEncryptionKey, Server = this, Color = new(request.Color) }; cats.Add(bob); return bob; } throw request.Error switch { ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), ErrorCode.Forbidden => new Exception("The server rejected your request"), ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage), ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage), _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), }; } public async Task GetRole(long id) { Role[] r = roles.Where(s => s.ID == id).ToArray(); if (r.Length > 0) return r[0]; RoleSTC s = await GetFromServer("SocketRole?id=" + id.ToString(), RoleSTCContext.Default.RoleSTC, CancellationToken.None); Role role = new() { Server = this, ID = s.ID, Color = new(s.Color), Description = s.Description, DisplayName = s.DisplayName, MembersListID = s.Members, Name = s.Name, Index = s.Index, ServerPermissions = s.ServerPermissions }; roles.Add(role); return role; } public async Task SendMessage(TChannel channel, string msg, SocketMessage? ReplyTo = null, SocketChannelProfile? FakeProfile = null) where TChannel : SocketChannel, new() { string bc = ""; if (channel.EncryptionKeys[0] == 0) { if (!string.IsNullOrEmpty(msg)) bc = Convert.ToBase64String(Encryption.Generic.Encoders[(int)channel.EncoderTypes[0]].GetBytes(msg)); } else { LocalKeyInfo key = channel.Server.EncryptionHandler.GetKey(channel.EncryptionKeys[0]); bc = Convert.ToBase64String(key.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Encrypt(msg, key.Key, channel.EncoderTypes[0]), _ => Encryption.AES.Encrypt(Encryption.Generic.Encoders[(int)channel.EncoderTypes[0]].GetBytes(msg), key.Key) }); } MessageCTS pcsm = new() { Files = Array.Empty(), ChannelID = channel.ID, EncryptionKey = channel.EncryptionKeys[0], Encoding = channel.EncoderTypes[0], Base64Context = bc }; if (FakeProfile is not null) { pcsm.Profile = FakeProfile.Id; } MessageSTC smsg = await channel.Server.SendServer("socketmessage", pcsm, MessageCTSContext.Default.MessageCTS, MessageSTCContext.Default.MessageSTC, CancellationToken.None); List fl = new(); foreach (var VARIABLE in smsg.Files) { fl.Add(VARIABLE.ID); } SocketMessage sm = new() { ID = smsg.ID, AuthorID = smsg.AuthorID, ChannelID = channel.ID, Context = msg, EncoderType = smsg.EncoderType, EncryptionKey = smsg.EncryptionKey, FileIDs = fl.ToArray(), Server = channel.Server, IsProfile = smsg.IsProfile }; return sm; } public async Task GetChannel(long id, CancellationToken CancellationToken) where TChannel : SocketChannel, new() { ChannelSTC request; if (chans.Count > 0 && chans.Any(s => s.ID == id)) { return (chans.Where(s => s is TChannel && s.ID == id).First() as TChannel)!; } while (true) { if (CanRequest) { request = await GetFromServer($"SocketChannel", ChannelSTCContext.Default.ChannelSTC, CancellationToken, new KeyValuePair("id", id.ToString())); break; } } if (request is null) throw new Exception("Something was wrong with the server responce"); if (request.Error is null) { if (chans.Count > 0 && chans.Any(s => s.ID == request.ID)) { foreach (SocketChannel? p in chans.Where(s => s.ID == request.ID)) { chans.Remove(p); } } LocalKeyInfo deckey; if (request.DescriptionEncryptionKey != 0) deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); else deckey = new() { EncryptionType = EncryptionType.None, Key = string.Empty }; string dec = deckey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Description), deckey.Key, request.DescriptionEncoderType), EncryptionType.AES => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString( Encryption.AES.Decrypt(Convert.FromBase64String(request.Description), deckey.Key)), _ => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString(Convert.FromBase64String(request.Description)) }; LocalKeyInfo nkey; if (request.TitleEncryptionKey != 0) nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); else nkey = new() { EncryptionType = EncryptionType.None, Key = string.Empty }; string n = nkey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Name), nkey.Key, request.TitleEncoderType), EncryptionType.AES => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString( Encryption.AES.Decrypt(Convert.FromBase64String(request.Name), nkey.Key)), _ => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString(Convert.FromBase64String(request.Name)) }; TChannel bob = new() { ID = request.ID, CategoryID = request.Parent, Description = dec, DescriptionEncoderType = request.DescriptionEncoderType, DescriptionEncryptionKey = request.DescriptionEncryptionKey, EncoderTypes = request.EncoderTypes, EncryptionKeys = request.EncryptionKeys, Epoch = request.Epoch, Name = n, RoleOverrides = request.RoleOverrides, UserOverrides = request.UserOverrides, Type = request.Type, TitleEncoderType = request.TitleEncoderType, TitleEncryptionKey = request.TitleEncryptionKey, PictureType = request.PictureType, Server = this, Color = new(request.Color) }; chans.Add(bob); return bob; } throw request.Error switch { ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), ErrorCode.Forbidden => new Exception("The server rejected your request"), ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage), ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage), _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), }; } public async Task MakeChannel(SocketCategory parent, string Name, string Decription) { ChannelSTC res = await SendServer( "SocketChannel", new ChannelPostCTS() { Name = Convert.ToBase64String(Encoding.UTF8.GetBytes(Name)), Description = Convert.ToBase64String(Encoding.UTF8.GetBytes(Description)), EncoderTypes = new[] { EncoderType.UTF16 }, EncryptionKeys = new long[] { 0 }, DescriptionEncoderType = EncoderType.UTF8, TitleEncoderType = EncoderType.UTF8, Parent = parent.ID, DescriptionEncryptionKey = 0, TitleEncryptionKey = 0, RoleOverrides = Array.Empty(), UserOverrides = Array.Empty(), Type = ChannelType.TextAndVoice, Color = "FFFFFFFF", PictureType = PictureType.none }, ChannelPostCTSContext.Default.ChannelPostCTS, ChannelSTCContext.Default.ChannelSTC, CancellationToken.None); return new SocketChannel() { ID = res.ID, CategoryID = res.Parent, Description = Description, DescriptionEncoderType = res.DescriptionEncoderType, DescriptionEncryptionKey = res.DescriptionEncryptionKey, EncoderTypes = res.EncoderTypes, EncryptionKeys = res.EncryptionKeys, Epoch = res.Epoch, Name = Name, RoleOverrides = res.RoleOverrides, UserOverrides = res.UserOverrides, Type = res.Type, TitleEncoderType = res.TitleEncoderType, TitleEncryptionKey = res.TitleEncryptionKey, PictureType = res.PictureType, Server = this, Color = new(res.Color) }; } public async Task GetUser(long UserId, CancellationToken CancellationToken) where Tuser : SocketUser, new() { SocketUserSTC user; if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) { Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; return temp; } while (true) { if (CanRequest) { user = await GetFromServer("socketuser", SocketUserSTCContext.Default.SocketUserSTC, CancellationToken, new KeyValuePair("id", UserId.ToString())); break; } } if (user is null) throw new Exception("Server did not return a user"); if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) { foreach (IUser? p in poeople.Where(s => s.Id == UserId)) { poeople.Remove(p); } } if (user is null || user.Error is not null) { string error = "User was null"; if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}"; throw new Exception($"Something went wrong getting your user information\n{error}"); } Tuser u = new(); if (u is SocketUser) { u = new() { Server = this, Id = user.ID, DisplayName = user.DisplayName, PictureType = user.PictureType, RoleIds = user.RoleIds, Status = user.Status }; } else { u = (new SocketAppUser() { Server = this, Id = user.ID, DisplayName = user.DisplayName, SelectedChannel = user.SelectedChannel, PictureType = user.PictureType, RoleIds = user.RoleIds, Status = user.Status, } as Tuser)!; } poeople.Add(u); return u; } public async Task GetChannelProfile(long ProfileId, CancellationToken CancellationToken) { ChannelProfileSTC user; if (profiles.Count > 0 && profiles.Any(s => s.Id == ProfileId)) { SocketChannelProfile temp = profiles.Where(s => s.Id == ProfileId).FirstOrDefault()!; return temp; } while (true) { if (CanRequest) { user = await GetFromServer("socketchannelprofile", ChannelProfileSTCContext.Default.ChannelProfileSTC, CancellationToken, new KeyValuePair("id", ProfileId.ToString())); break; } } if (user is null) throw new Exception("Server did not return a user"); if (profiles.Count > 0 && profiles.Any(s => s.Id == ProfileId)) { foreach (SocketChannelProfile? p in profiles.Where(s => s.Id == ProfileId)) { profiles.Remove(p); } } if (user is null || user.Error is not null) { string error = "User was null"; if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}"; throw new Exception($"Something went wrong getting your user information\n{error}"); } SocketChannelProfile u = new() { Server = this, Id = user.ID, DisplayName = user.DisplayName, PictureType = user.PictureType, Controllers = user.Controllers, Color = new(user.Color) }; poeople.Add(u); return u; } /// /// Sends the server a request to update the of you account /// /// The you want to set your status to /// public async Task UpdateStatus(UserStatus Status, CancellationToken CancellationToken) { STC? data = await SendServer("SocketUserProfile/Status", new StatusUpdateCTS() { Status = Status }, StatusUpdateCTSContext.Default.StatusUpdateCTS, STCContext.Default.STC, CancellationToken); if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) { if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); if (data?.Error is not null) throw new Exception(((int)data.Error).ToString()); else throw new Exception("Something went worng"); } User.Status = Status; return Task.CompletedTask; } public string Name { get; private set; } public string Description { get; private set; } }