dev #1
@ -7,15 +7,15 @@ namespace Luski.net;
|
|||||||
|
|
||||||
public class API
|
public class API
|
||||||
{
|
{
|
||||||
public Server<MainSocketAppUser> MainServer { get; internal set; }
|
public MainServer MainServer { get; internal set; }
|
||||||
internal List<Server<PublicSocketAppUser>> InternalServers { get; } = new();
|
internal List<PublicServer> InternalServers { get; } = new();
|
||||||
public IReadOnlyList<Server<PublicSocketAppUser>> LoadedServers => InternalServers.AsReadOnly();
|
public IReadOnlyList<PublicServer> LoadedServers => InternalServers.AsReadOnly();
|
||||||
|
|
||||||
public Server<PublicSocketAppUser> GetPublicServer(string Domain, string Version = "v1")
|
public PublicServer GetPublicServer(string Domain, string Version = "v1")
|
||||||
{
|
{
|
||||||
IEnumerable<Server<PublicSocketAppUser>> isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version));
|
IEnumerable<PublicServer> isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version));
|
||||||
if (isl.Any()) return isl.First();
|
if (isl.Any()) return isl.First();
|
||||||
Server<PublicSocketAppUser> s = new()
|
PublicServer s = new()
|
||||||
{
|
{
|
||||||
Domain = Domain,
|
Domain = Domain,
|
||||||
ApiVersion = Version,
|
ApiVersion = Version,
|
||||||
@ -24,7 +24,7 @@ public class API
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Server<MainSocketAppUser> GetMainServer(string Domain, string Version = "v1")
|
public MainServer GetMainServer(string Domain, string Version = "v1")
|
||||||
{
|
{
|
||||||
MainServer = new()
|
MainServer = new()
|
||||||
{
|
{
|
||||||
|
8
Luski.net/Enums/ServerCacheMode.cs
Normal file
8
Luski.net/Enums/ServerCacheMode.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Luski.net.Enums;
|
||||||
|
|
||||||
|
public enum ServerCacheMode : byte
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Encrypted,
|
||||||
|
Unencrypted
|
||||||
|
}
|
@ -33,6 +33,5 @@ public interface IServer
|
|||||||
public Task<Tresult> SendServer<Tresult>(string Path, string File, JsonTypeInfo<Tresult> ReturnjsonTypeInfo,
|
public Task<Tresult> SendServer<Tresult>(string Path, string File, JsonTypeInfo<Tresult> ReturnjsonTypeInfo,
|
||||||
CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers)
|
CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers)
|
||||||
where Tresult : IncomingHTTP, new();
|
where Tresult : IncomingHTTP, new();
|
||||||
public Task<Tuser> GetUser<Tuser>(long UserID, CancellationToken CancellationToken) where Tuser : SocketUserBase, new();
|
|
||||||
|
|
||||||
}
|
}
|
@ -35,8 +35,5 @@ public interface IUser
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<long> GetUserKey(CancellationToken CancellationToken);
|
Task<long> GetUserKey(CancellationToken CancellationToken);
|
||||||
/// <summary>
|
|
||||||
/// The server that the user comes from
|
|
||||||
/// </summary>
|
|
||||||
IServer Server { get; }
|
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<RepositoryUrl>https://github.com/JacobTech-com/Luski.net</RepositoryUrl>
|
<RepositoryUrl>https://github.com/JacobTech-com/Luski.net</RepositoryUrl>
|
||||||
<IncludeSymbols>True</IncludeSymbols>
|
<IncludeSymbols>True</IncludeSymbols>
|
||||||
<FileVersion>1.0.0</FileVersion>
|
<FileVersion>1.0.0</FileVersion>
|
||||||
<Version>1.1.3-alpha23</Version>
|
<Version>1.1.3-alpha24</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
@ -14,23 +15,22 @@ using Luski.net.JsonTypes.WSS;
|
|||||||
using Luski.net.Structures.Main;
|
using Luski.net.Structures.Main;
|
||||||
using Luski.net.Structures.Public;
|
using Luski.net.Structures.Public;
|
||||||
using WebSocketSharp;
|
using WebSocketSharp;
|
||||||
using File = System.IO.File;
|
|
||||||
|
|
||||||
namespace Luski.net;
|
namespace Luski.net;
|
||||||
|
|
||||||
public partial class Server<TUser>
|
public partial class MainServer
|
||||||
{
|
{
|
||||||
public void Login(string Email, string Password, CancellationToken CancellationToken)
|
public void Login(string Email, string Password, System.Threading.CancellationToken CancellationToken)
|
||||||
{
|
{
|
||||||
Both(Email, Password, CancellationToken);
|
Both(Email, Password, CancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateAccount(string Email, string Password, string Username, string PFP, CancellationToken CancellationToken)
|
public void CreateAccount(string Email, string Password, string Username, string PFP, System.Threading.CancellationToken CancellationToken)
|
||||||
{
|
{
|
||||||
Both(Email, Password, CancellationToken, Username, PFP);
|
Both(Email, Password, CancellationToken, Username, PFP);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Both(string Email, string Password, CancellationToken CancellationToken, string? Username = null, string? pfp = null)
|
private void Both(string Email, string Password, System.Threading.CancellationToken CancellationToken, string? Username = null, string? pfp = null)
|
||||||
{
|
{
|
||||||
if (!ClientEncryption.Generating)
|
if (!ClientEncryption.Generating)
|
||||||
{
|
{
|
||||||
@ -95,15 +95,9 @@ public partial class Server<TUser>
|
|||||||
long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(
|
long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(
|
||||||
Token.Split('.')[0]
|
Token.Split('.')[0]
|
||||||
)));
|
)));
|
||||||
TUser t = new();
|
User = GetUser(id, MainSocketAppUserContext.Default.MainSocketAppUser,
|
||||||
User = t switch
|
CancellationToken)
|
||||||
{
|
.Result;
|
||||||
MainSocketAppUser => (GetUser(id, MainSocketAppUserContext.Default.MainSocketAppUser,
|
|
||||||
CancellationToken.None)
|
|
||||||
.Result as TUser)!,
|
|
||||||
_ => (GetUser(id, PublicSocketAppUserContext.Default.PublicSocketAppUser, CancellationToken.None)
|
|
||||||
.Result as TUser)!
|
|
||||||
};
|
|
||||||
if (User is null || User.Error is not null)
|
if (User is null || User.Error is not null)
|
||||||
{
|
{
|
||||||
string error = "User was null";
|
string error = "User was null";
|
15
Luski.net/MainServer.Events.cs
Normal file
15
Luski.net/MainServer.Events.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Luski.net.Structures;
|
||||||
|
using Luski.net.Structures.Main;
|
||||||
|
|
||||||
|
namespace Luski.net;
|
||||||
|
|
||||||
|
public partial class MainServer
|
||||||
|
{
|
||||||
|
public event Func<MainSocketMessage, Task>? MessageReceived;
|
||||||
|
|
||||||
|
public event Func<MainSocketRemoteUser, Task>? ReceivedFriendRequest;
|
||||||
|
|
||||||
|
public event Func<MainSocketRemoteUser, bool, Task>? FriendRequestResult;
|
||||||
|
}
|
116
Luski.net/MainServer.Incoming.cs
Normal file
116
Luski.net/MainServer.Incoming.cs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading;
|
||||||
|
using JacobTechEncryption;
|
||||||
|
using Luski.net.Enums;
|
||||||
|
using Luski.net.JsonTypes;
|
||||||
|
using Luski.net.JsonTypes.BaseTypes;
|
||||||
|
using Luski.net.JsonTypes.HTTP;
|
||||||
|
using Luski.net.JsonTypes.WSS;
|
||||||
|
using Luski.net.Structures;
|
||||||
|
using Luski.net.Structures.Main;
|
||||||
|
using WebSocketSharp;
|
||||||
|
|
||||||
|
namespace Luski.net;
|
||||||
|
|
||||||
|
public partial class MainServer
|
||||||
|
{
|
||||||
|
private void DataFromServer(object? sender, MessageEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.IsPing) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine("From Server: {0}", e.Data);
|
||||||
|
IncomingWSS? data = JsonSerializer.Deserialize(e.Data, IncomingWSSContext.Default.IncomingWSS);
|
||||||
|
switch (data?.Type)
|
||||||
|
{
|
||||||
|
case DataType.Login:
|
||||||
|
Console.WriteLine("Pre auth");
|
||||||
|
WSSLogin n = JsonSerializer.Deserialize(e.Data, WSSLoginContext.Default.WSSLogin)!;
|
||||||
|
Token = n.Token;
|
||||||
|
Console.WriteLine("Token: {0}",Token);
|
||||||
|
break;
|
||||||
|
case DataType.Error:
|
||||||
|
if (Token is null)
|
||||||
|
{
|
||||||
|
Error = data.Error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Exception(new Exception(data.Error));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DataType.Message_Create:
|
||||||
|
if (MessageReceived is not null)
|
||||||
|
{
|
||||||
|
MainSocketMessage? m = JsonSerializer.Deserialize<MainSocketMessage>(e.Data);
|
||||||
|
if (m is not null)
|
||||||
|
{
|
||||||
|
m.decrypt(ClientEncryption.File.Channels.GetKey(m.ChannelID), CancellationToken.None);
|
||||||
|
_ = MessageReceived.Invoke(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DataType.Status_Update:
|
||||||
|
StatusUpdate? SU = JsonSerializer.Deserialize<StatusUpdate>(e.Data);
|
||||||
|
if (SU is not null)
|
||||||
|
{
|
||||||
|
MainSocketRemoteUser after = GetUser(SU.id, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken.None).Result;
|
||||||
|
after.Status = SU.after;
|
||||||
|
MainSocketRemoteUser before = after.Clone();
|
||||||
|
before.Status = SU.before;
|
||||||
|
StatusUpdate(before, after);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DataType.Friend_Request:
|
||||||
|
if (ReceivedFriendRequest is not null)
|
||||||
|
{
|
||||||
|
FriendRequest? request =
|
||||||
|
JsonSerializer.Deserialize(e.Data, FriendRequestContext.Default.FriendRequest);
|
||||||
|
if (request is not null)
|
||||||
|
_ = ReceivedFriendRequest.Invoke(GetUser(request.Id,
|
||||||
|
MainSocketRemoteUserContext.Default.MainSocketRemoteUser,
|
||||||
|
CancellationToken.None).Result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DataType.Friend_Request_Result:
|
||||||
|
FriendRequestResult? FRR = JsonSerializer.Deserialize<FriendRequestResult>(e.Data);
|
||||||
|
if (FRR is not null && FRR.Channel is not null && FRR.Id is not null &&
|
||||||
|
FRR.Result is not null)
|
||||||
|
{
|
||||||
|
MainSocketDMChannel chan = GetChannel((long)FRR.Channel,
|
||||||
|
MainSocketDMChannelContext.Default.MainSocketDMChannel,
|
||||||
|
CancellationToken.None).Result;
|
||||||
|
chans.Add(chan);
|
||||||
|
MainSocketRemoteUser from1 = GetUser((long)FRR.Id,
|
||||||
|
MainSocketRemoteUserContext.Default.MainSocketRemoteUser,
|
||||||
|
CancellationToken.None).Result;
|
||||||
|
//from1.Channel = chan;
|
||||||
|
if (FriendRequestResult is not null) _ = FriendRequestResult.Invoke(from1, (bool)FRR.Result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DataType.Key_Exchange:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
KeyExchange? KE = JsonSerializer.Deserialize<KeyExchange>(e.Data);
|
||||||
|
if (KE is not null)
|
||||||
|
ClientEncryption.File.Channels.AddKey(KE.channel,
|
||||||
|
ClientEncryption.Encoder.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(KE.key), ClientEncryption.ofkey)));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Exception(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine("Unknown");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Exception(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
171
Luski.net/MainServer.cs
Normal file
171
Luski.net/MainServer.cs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization.Metadata;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Luski.net.Enums;
|
||||||
|
using Luski.net.Interfaces;
|
||||||
|
using Luski.net.JsonTypes.BaseTypes;
|
||||||
|
using Luski.net.JsonTypes.HTTP;
|
||||||
|
using Luski.net.Structures.Main;
|
||||||
|
using Luski.net.Structures.Public;
|
||||||
|
|
||||||
|
namespace Luski.net;
|
||||||
|
|
||||||
|
public partial class MainServer : Server
|
||||||
|
{
|
||||||
|
public MainSocketAppUser User { get; internal set; } = default!;
|
||||||
|
|
||||||
|
public async Task<TChannel> GetChannel<TChannel>(long Channel, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new()
|
||||||
|
{
|
||||||
|
TChannel Return = new();
|
||||||
|
switch (Return)
|
||||||
|
{
|
||||||
|
case MainSocketDMChannel:
|
||||||
|
Return = (await GetChannel(Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken) as TChannel)!;
|
||||||
|
break;
|
||||||
|
case MainSocketGroupChannel:
|
||||||
|
Return = (await GetChannel(Channel, MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken) as TChannel)!;
|
||||||
|
break;
|
||||||
|
case MainSocketTextChannel:
|
||||||
|
Return = (await GetChannel(Channel, MainSocketTextChannelContext.Default.MainSocketTextChannel, CancellationToken) as TChannel)!;
|
||||||
|
break;
|
||||||
|
case MainSocketChannel:
|
||||||
|
Return = (await GetChannel(Channel, MainSocketChannelContext.Default.MainSocketChannel, CancellationToken) as TChannel)!;
|
||||||
|
break;
|
||||||
|
case null:
|
||||||
|
throw new NullReferenceException(nameof(TChannel));
|
||||||
|
default:
|
||||||
|
throw new Exception("Unknown channel type");
|
||||||
|
}
|
||||||
|
return Return;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<TChannel> GetChannel<TChannel>(long id, JsonTypeInfo<TChannel> Json, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new()
|
||||||
|
{
|
||||||
|
TChannel request;
|
||||||
|
if (chans.Count > 0 && chans.Any(s => s.Id == id))
|
||||||
|
{
|
||||||
|
return chans.Where(s => s is TChannel && s.Id == id).Cast<TChannel>().FirstOrDefault()!;
|
||||||
|
}
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (CanRequest)
|
||||||
|
{
|
||||||
|
request = await GetFromServer($"SocketChannel/Get/{id}", Json, CancellationToken);
|
||||||
|
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 (MainSocketChannel? p in chans.Where(s => s.Id == request.Id))
|
||||||
|
{
|
||||||
|
chans.Remove(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chans.Add(request);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
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 Task<Tuser> GetUser<Tuser>(long UserID, CancellationToken CancellationToken) where Tuser : MainSocketUserBase, new()
|
||||||
|
{
|
||||||
|
Tuser user = new();
|
||||||
|
switch (user)
|
||||||
|
{
|
||||||
|
case MainSocketAppUser:
|
||||||
|
user = (GetUser(UserID, MainSocketAppUserContext.Default.MainSocketAppUser, CancellationToken).Result as Tuser)!;
|
||||||
|
break;
|
||||||
|
case MainSocketUserBase:
|
||||||
|
user = (GetUser(UserID, MainSocketUserBaseContext.Default.MainSocketUserBase, CancellationToken).Result as Tuser)!;
|
||||||
|
break;
|
||||||
|
case null:
|
||||||
|
throw new NullReferenceException(nameof(Tuser));
|
||||||
|
default:
|
||||||
|
throw new Exception("Unknown channel type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MainSocketMessage> GetMessage(long id, CancellationToken CancellationToken)
|
||||||
|
{
|
||||||
|
MainSocketMessage message;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (CanRequest)
|
||||||
|
{
|
||||||
|
message = await GetFromServer("socketmessage",
|
||||||
|
MainSocketMessageContext.Default.MainSocketMessage,
|
||||||
|
CancellationToken,
|
||||||
|
new System.Collections.Generic.KeyValuePair<string, string?>("msg_id", id.ToString()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message is not null) return message;
|
||||||
|
throw new Exception("Server did not return a message");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends the server a request to update the <paramref name="Status"/> of you account
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Status">The <see cref="UserStatus"/> you want to set your status to</param>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public async Task<Task> UpdateStatus(UserStatus Status, CancellationToken CancellationToken)
|
||||||
|
{
|
||||||
|
IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<Tuser> GetUser<Tuser>(long UserId, JsonTypeInfo<Tuser> Json, CancellationToken CancellationToken) where Tuser : MainSocketUserBase, new()
|
||||||
|
{
|
||||||
|
Tuser user;
|
||||||
|
if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId))
|
||||||
|
{
|
||||||
|
Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast<Tuser>().FirstOrDefault()!;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (CanRequest)
|
||||||
|
{
|
||||||
|
user = await GetFromServer("socketuser",
|
||||||
|
Json,
|
||||||
|
CancellationToken,
|
||||||
|
new KeyValuePair<string, string?>("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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
poeople.Add(user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
6
Luski.net/PublicServer.cs
Normal file
6
Luski.net/PublicServer.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Luski.net;
|
||||||
|
|
||||||
|
public class PublicServer : Server
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -1,20 +1,22 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Luski.net.Interfaces;
|
using Luski.net.Interfaces;
|
||||||
using Luski.net.JsonTypes;
|
|
||||||
using Luski.net.Structures;
|
|
||||||
|
|
||||||
namespace Luski.net;
|
namespace Luski.net;
|
||||||
|
|
||||||
public partial class Server<TUser>
|
public partial class Server
|
||||||
{
|
{
|
||||||
public event Func<SocketMessage, Task>? MessageReceived;
|
|
||||||
|
|
||||||
public event Func<IUser, IUser, Task>? UserStatusUpdate;
|
public event Func<IUser, IUser, Task>? UserStatusUpdate;
|
||||||
|
|
||||||
public event Func<MainSocketRemoteUser, Task>? ReceivedFriendRequest;
|
|
||||||
|
|
||||||
public event Func<MainSocketRemoteUser, bool, Task>? FriendRequestResult;
|
|
||||||
|
|
||||||
public event Func<Exception, Task>? OnError;
|
public event Func<Exception, Task>? OnError;
|
||||||
|
|
||||||
|
internal void Exception(Exception e)
|
||||||
|
{
|
||||||
|
if (OnError is not null) OnError.Invoke(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void StatusUpdate(IUser u1, IUser u2)
|
||||||
|
{
|
||||||
|
if (UserStatusUpdate is not null) UserStatusUpdate.Invoke(u1, u2);
|
||||||
|
}
|
||||||
}
|
}
|
@ -4,23 +4,24 @@ using System.IO;
|
|||||||
using Luski.net.Enums;
|
using Luski.net.Enums;
|
||||||
using Luski.net.Interfaces;
|
using Luski.net.Interfaces;
|
||||||
using Luski.net.JsonTypes;
|
using Luski.net.JsonTypes;
|
||||||
|
using Luski.net.Structures;
|
||||||
using Luski.net.Structures.Main;
|
using Luski.net.Structures.Main;
|
||||||
using Luski.net.Structures.Public;
|
using Luski.net.Structures.Public;
|
||||||
using WebSocketSharp;
|
using WebSocketSharp;
|
||||||
|
|
||||||
namespace Luski.net;
|
namespace Luski.net;
|
||||||
|
|
||||||
public partial class Server<TUser> where TUser : class, IAppUser, new()
|
public partial class Server
|
||||||
{
|
{
|
||||||
public ServerType ServerType { get; internal set; } = ServerType.Public;
|
public ServerType ServerType { get; internal set; } = ServerType.Public;
|
||||||
public string Domain { get; internal set; } = default!;
|
public string Domain { get; internal set; } = default!;
|
||||||
public IAppUser IAppUser => User;
|
public ServerCacheMode CacheMode { get; set; } = ServerCacheMode.None;
|
||||||
public TUser User { get; internal set; }
|
|
||||||
public string ApiVersion { get; internal set; } = "v1";
|
public string ApiVersion { get; internal set; } = "v1";
|
||||||
private WebSocket? ServerOut;
|
internal WebSocket? ServerOut;
|
||||||
internal string? Token = null, Error = null, gen = null;
|
internal string? Token = null, Error = null, gen = null;
|
||||||
internal bool CanRequest = false, login = false;
|
internal bool CanRequest = false, login = false;
|
||||||
internal List<IUser> poeople = new();
|
internal List<IUser> poeople = new();
|
||||||
|
public long UserID { get; internal set; } = 0;
|
||||||
internal List<MainSocketChannel> chans { get; set; } = new();
|
internal List<MainSocketChannel> chans { get; set; } = new();
|
||||||
public string Cache
|
public string Cache
|
||||||
{
|
{
|
||||||
@ -33,7 +34,7 @@ public partial class Server<TUser> where TUser : class, IAppUser, new()
|
|||||||
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
||||||
path += "Data/";
|
path += "Data/";
|
||||||
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
||||||
path += User.Id + "/";
|
path += UserID + "/";
|
||||||
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
||||||
path += "Cache/";
|
path += "Cache/";
|
||||||
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
||||||
|
@ -1,131 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading;
|
|
||||||
using JacobTechEncryption;
|
|
||||||
using Luski.net.Enums;
|
|
||||||
using Luski.net.JsonTypes;
|
|
||||||
using Luski.net.JsonTypes.BaseTypes;
|
|
||||||
using Luski.net.JsonTypes.HTTP;
|
|
||||||
using Luski.net.JsonTypes.WSS;
|
|
||||||
using Luski.net.Structures;
|
|
||||||
using Luski.net.Structures.Main;
|
|
||||||
using WebSocketSharp;
|
using WebSocketSharp;
|
||||||
|
|
||||||
namespace Luski.net;
|
namespace Luski.net;
|
||||||
|
|
||||||
public partial class Server<TUser>
|
public partial class Server
|
||||||
{
|
{
|
||||||
private void ServerOut_OnError(object? sender, WebSocketSharp.ErrorEventArgs e)
|
internal void ServerOut_OnError(object? sender, ErrorEventArgs e)
|
||||||
{
|
{
|
||||||
if (OnError is not null) OnError.Invoke(new Exception(e.Message));
|
this.Exception(new Exception(e.Message));
|
||||||
}
|
|
||||||
|
|
||||||
private void DataFromServer(object? sender, MessageEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.IsPing) return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Console.WriteLine("From Server: {0}", e.Data);
|
|
||||||
IncomingWSS? data = JsonSerializer.Deserialize(e.Data, IncomingWSSContext.Default.IncomingWSS);
|
|
||||||
switch (data?.Type)
|
|
||||||
{
|
|
||||||
case DataType.Login:
|
|
||||||
Console.WriteLine("Pre auth");
|
|
||||||
WSSLogin n = JsonSerializer.Deserialize(e.Data, WSSLoginContext.Default.WSSLogin)!;
|
|
||||||
Token = n.Token;
|
|
||||||
Console.WriteLine("Token: {0}",Token);
|
|
||||||
break;
|
|
||||||
case DataType.Error:
|
|
||||||
if (Token is null)
|
|
||||||
{
|
|
||||||
Error = data.Error;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (OnError is not null)
|
|
||||||
{
|
|
||||||
_ = OnError.Invoke(new Exception(data.Error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DataType.Message_Create:
|
|
||||||
if (MessageReceived is not null)
|
|
||||||
{
|
|
||||||
SocketMessage? m = JsonSerializer.Deserialize<SocketMessage>(e.Data);
|
|
||||||
if (m is not null)
|
|
||||||
{
|
|
||||||
m.decrypt(ClientEncryption.File.Channels.GetKey(m.ChannelID), CancellationToken.None);
|
|
||||||
_ = MessageReceived.Invoke(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DataType.Status_Update:
|
|
||||||
if (UserStatusUpdate is not null)
|
|
||||||
{
|
|
||||||
StatusUpdate? SU = JsonSerializer.Deserialize<StatusUpdate>(e.Data);
|
|
||||||
if (SU is not null)
|
|
||||||
{
|
|
||||||
MainSocketRemoteUser after = GetUser(SU.id, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken.None).Result;
|
|
||||||
after.Status = SU.after;
|
|
||||||
MainSocketRemoteUser before = after.Clone();
|
|
||||||
before.Status = SU.before;
|
|
||||||
_ = UserStatusUpdate.Invoke(before, after);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DataType.Friend_Request:
|
|
||||||
if (ReceivedFriendRequest is not null)
|
|
||||||
{
|
|
||||||
FriendRequest? request =
|
|
||||||
JsonSerializer.Deserialize(e.Data, FriendRequestContext.Default.FriendRequest);
|
|
||||||
if (request is not null)
|
|
||||||
_ = ReceivedFriendRequest.Invoke(GetUser(request.Id,
|
|
||||||
MainSocketRemoteUserContext.Default.MainSocketRemoteUser,
|
|
||||||
CancellationToken.None).Result);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DataType.Friend_Request_Result:
|
|
||||||
if (FriendRequestResult is not null)
|
|
||||||
{
|
|
||||||
FriendRequestResult? FRR = JsonSerializer.Deserialize<FriendRequestResult>(e.Data);
|
|
||||||
if (FRR is not null && FRR.Channel is not null && FRR.Id is not null &&
|
|
||||||
FRR.Result is not null)
|
|
||||||
{
|
|
||||||
MainSocketDMChannel chan = GetChannel((long)FRR.Channel,
|
|
||||||
MainSocketDMChannelContext.Default.MainSocketDMChannel,
|
|
||||||
CancellationToken.None).Result;
|
|
||||||
chans.Add(chan);
|
|
||||||
MainSocketRemoteUser from1 = GetUser((long)FRR.Id,
|
|
||||||
MainSocketRemoteUserContext.Default.MainSocketRemoteUser,
|
|
||||||
CancellationToken.None).Result;
|
|
||||||
//from1.Channel = chan;
|
|
||||||
_ = FriendRequestResult.Invoke(from1, (bool)FRR.Result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DataType.Key_Exchange:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
KeyExchange? KE = JsonSerializer.Deserialize<KeyExchange>(e.Data);
|
|
||||||
if (KE is not null)
|
|
||||||
ClientEncryption.File.Channels.AddKey(KE.channel,
|
|
||||||
ClientEncryption.Encoder.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(KE.key), ClientEncryption.ofkey)));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (OnError is not null) OnError.Invoke(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Console.WriteLine("Unknown");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (OnError is not null) _ = OnError.Invoke(exception);
|
|
||||||
else throw exception;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ using File = System.IO.File;
|
|||||||
|
|
||||||
namespace Luski.net;
|
namespace Luski.net;
|
||||||
|
|
||||||
public partial class Server<TUser> : IServer
|
public partial class Server
|
||||||
{
|
{
|
||||||
internal Server()
|
internal Server()
|
||||||
{ }
|
{ }
|
||||||
@ -35,161 +35,6 @@ public partial class Server<TUser> : IServer
|
|||||||
return File.ReadAllBytes($"{Cache}/servers/{Domain}");
|
return File.ReadAllBytes($"{Cache}/servers/{Domain}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Tuser> GetUser<Tuser>(long UserID, CancellationToken CancellationToken) where Tuser : SocketUserBase, new()
|
|
||||||
{
|
|
||||||
Tuser user = new();
|
|
||||||
switch (user)
|
|
||||||
{
|
|
||||||
case MainSocketAppUser:
|
|
||||||
user = (GetUser(UserID, MainSocketAppUserContext.Default.MainSocketAppUser, CancellationToken).Result as Tuser)!;
|
|
||||||
break;
|
|
||||||
case PublicSocketAppUser:
|
|
||||||
user = (GetUser(UserID, PublicSocketAppUserContext.Default.PublicSocketAppUser, CancellationToken).Result as Tuser)!;
|
|
||||||
break;
|
|
||||||
case SocketUserBase:
|
|
||||||
user = (GetUser(UserID, SocketUserBaseContext.Default.SocketUserBase, CancellationToken).Result as Tuser)!;
|
|
||||||
break;
|
|
||||||
case null:
|
|
||||||
throw new NullReferenceException(nameof(Tuser));
|
|
||||||
default:
|
|
||||||
throw new Exception("Unknown channel type");
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TChannel> GetChannel<TChannel>(long Channel, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new()
|
|
||||||
{
|
|
||||||
TChannel Return = new();
|
|
||||||
switch (Return)
|
|
||||||
{
|
|
||||||
case MainSocketDMChannel:
|
|
||||||
Return = (await GetChannel(Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken) as TChannel)!;
|
|
||||||
break;
|
|
||||||
case MainSocketGroupChannel:
|
|
||||||
Return = (await GetChannel(Channel, MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken) as TChannel)!;
|
|
||||||
break;
|
|
||||||
case MainSocketTextChannel:
|
|
||||||
Return = (await GetChannel(Channel, MainSocketTextChannelContext.Default.MainSocketTextChannel, CancellationToken) as TChannel)!;
|
|
||||||
break;
|
|
||||||
case MainSocketChannel:
|
|
||||||
Return = (await GetChannel(Channel, MainSocketChannelContext.Default.MainSocketChannel, CancellationToken) as TChannel)!;
|
|
||||||
break;
|
|
||||||
case null:
|
|
||||||
throw new NullReferenceException(nameof(TChannel));
|
|
||||||
default:
|
|
||||||
throw new Exception("Unknown channel type");
|
|
||||||
}
|
|
||||||
return Return;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<TChannel> GetChannel<TChannel>(long id, JsonTypeInfo<TChannel> Json, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new()
|
|
||||||
{
|
|
||||||
TChannel request;
|
|
||||||
if (chans.Count > 0 && chans.Any(s => s.Id == id))
|
|
||||||
{
|
|
||||||
return chans.Where(s => s is TChannel && s.Id == id).Cast<TChannel>().FirstOrDefault()!;
|
|
||||||
}
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (CanRequest)
|
|
||||||
{
|
|
||||||
request = await GetFromServer($"SocketChannel/Get/{id}", Json, CancellationToken);
|
|
||||||
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 (MainSocketChannel? p in chans.Where(s => s.Id == request.Id))
|
|
||||||
{
|
|
||||||
chans.Remove(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chans.Add(request);
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
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<SocketMessage> GetMessage(long id, CancellationToken CancellationToken)
|
|
||||||
{
|
|
||||||
SocketMessage message;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (CanRequest)
|
|
||||||
{
|
|
||||||
message = await GetFromServer("socketmessage",
|
|
||||||
SocketMessageContext.Default.SocketMessage,
|
|
||||||
CancellationToken,
|
|
||||||
new System.Collections.Generic.KeyValuePair<string, string?>("msg_id", id.ToString()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (message is not null) return message;
|
|
||||||
throw new Exception("Server did not return a message");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends the server a request to update the <paramref name="Status"/> of you account
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Status">The <see cref="UserStatus"/> you want to set your status to</param>
|
|
||||||
/// <exception cref="Exception"></exception>
|
|
||||||
public async Task<Task> UpdateStatus(UserStatus Status, CancellationToken CancellationToken)
|
|
||||||
{
|
|
||||||
IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, 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 as SocketUserBase)!.Status = Status;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<Tuser> GetUser<Tuser>(long UserId, JsonTypeInfo<Tuser> Json, CancellationToken CancellationToken) where Tuser : SocketUserBase, new()
|
|
||||||
{
|
|
||||||
Tuser user;
|
|
||||||
if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId))
|
|
||||||
{
|
|
||||||
Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast<Tuser>().FirstOrDefault()!;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (CanRequest)
|
|
||||||
{
|
|
||||||
user = await GetFromServer("socketuser",
|
|
||||||
Json,
|
|
||||||
CancellationToken,
|
|
||||||
new KeyValuePair<string, string?>("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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
poeople.Add(user);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SendServer<Tvalue>(Tvalue Payload, JsonTypeInfo<Tvalue> jsonTypeInfo) where Tvalue : IncomingWSS
|
public void SendServer<Tvalue>(Tvalue Payload, JsonTypeInfo<Tvalue> jsonTypeInfo) where Tvalue : IncomingWSS
|
||||||
{
|
{
|
||||||
ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo));
|
ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo));
|
||||||
|
@ -14,8 +14,11 @@ using Luski.net.JsonTypes;
|
|||||||
|
|
||||||
namespace Luski.net.Structures.Main;
|
namespace Luski.net.Structures.Main;
|
||||||
|
|
||||||
public class MainSocketAppUser : SocketUserBase, IAppUser
|
public class MainSocketAppUser : MainSocketUserBase, IAppUser
|
||||||
{
|
{
|
||||||
|
[JsonPropertyName("selected_channel")]
|
||||||
|
[JsonInclude]
|
||||||
|
public long SelectedChannel { get; internal set; } = default!;
|
||||||
[JsonPropertyName("username")]
|
[JsonPropertyName("username")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public string Username { get; internal set; } = default!;
|
public string Username { get; internal set; } = default!;
|
||||||
@ -23,6 +26,8 @@ public class MainSocketAppUser : SocketUserBase, IAppUser
|
|||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public UserFlag Flags { get; internal set; } = default!;
|
public UserFlag Flags { get; internal set; } = default!;
|
||||||
|
|
||||||
|
public MainServer Server { get; internal set; } = default!;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IReadOnlyList<MainSocketChannel> Channels
|
public IReadOnlyList<MainSocketChannel> Channels
|
||||||
{
|
{
|
||||||
@ -35,17 +40,17 @@ public class MainSocketAppUser : SocketUserBase, IAppUser
|
|||||||
_Channels = new List<MainSocketChannel>();
|
_Channels = new List<MainSocketChannel>();
|
||||||
foreach (long channel in ChannelIdList)
|
foreach (long channel in ChannelIdList)
|
||||||
{
|
{
|
||||||
MainSocketChannel s = (Server as Server<MainSocketAppUser>).GetChannel(channel,
|
MainSocketChannel s = Server.GetChannel(channel,
|
||||||
MainSocketChannelContext.Default.MainSocketChannel, CancellationToken.None).Result;
|
MainSocketChannelContext.Default.MainSocketChannel, CancellationToken.None).Result;
|
||||||
(Server as Server<MainSocketAppUser>)!.chans.Remove(s);
|
Server.chans.Remove(s);
|
||||||
switch (s.Type)
|
switch (s.Type)
|
||||||
{
|
{
|
||||||
case ChannelType.GROUP:
|
case ChannelType.GROUP:
|
||||||
_Channels.Add((Server as Server<MainSocketAppUser>).GetChannel(channel,
|
_Channels.Add(Server.GetChannel(channel,
|
||||||
MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken.None).Result);
|
MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken.None).Result);
|
||||||
break;
|
break;
|
||||||
case ChannelType.DM:
|
case ChannelType.DM:
|
||||||
_Channels.Add((Server as Server<MainSocketAppUser>).GetChannel(channel,
|
_Channels.Add(Server.GetChannel(channel,
|
||||||
MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken.None).Result);
|
MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken.None).Result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -100,9 +105,6 @@ public class MainSocketAppUser : SocketUserBase, IAppUser
|
|||||||
return _Friends.AsReadOnly();
|
return _Friends.AsReadOnly();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[JsonPropertyName("selected_channel")]
|
|
||||||
[JsonInclude]
|
|
||||||
public long SelectedChannel { get; internal set; } = default!;
|
|
||||||
[JsonPropertyName("channels")]
|
[JsonPropertyName("channels")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public long[] ChannelIdList { get; internal set; } = default!;
|
public long[] ChannelIdList { get; internal set; } = default!;
|
||||||
@ -127,33 +129,33 @@ public class MainSocketAppUser : SocketUserBase, IAppUser
|
|||||||
|
|
||||||
internal void AddFriend(MainSocketRemoteUser User)
|
internal void AddFriend(MainSocketRemoteUser User)
|
||||||
{
|
{
|
||||||
if ((Server as Server<MainSocketAppUser>)!.poeople.Any(s => s.Id == User.Id))
|
if (Server.poeople.Any(s => s.Id == User.Id))
|
||||||
{
|
{
|
||||||
IEnumerable<IUser> b = (Server as Server<MainSocketAppUser>)!.poeople.Where(s => s.Id == User.Id);
|
IEnumerable<IUser> b = Server.poeople.Where(s => s.Id == User.Id);
|
||||||
foreach (IUser item in b)
|
foreach (IUser item in b)
|
||||||
{
|
{
|
||||||
(Server as Server<MainSocketAppUser>)!.poeople.Remove(item);
|
Server.poeople.Remove(item);
|
||||||
}
|
}
|
||||||
(Server as Server<MainSocketAppUser>)!.poeople.Add(User);
|
Server.poeople.Add(User);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(Server as Server<MainSocketAppUser>)!.poeople.Add(User);
|
Server.poeople.Add(User);
|
||||||
}
|
}
|
||||||
_Friends.Add(User);
|
_Friends.Add(User);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveFriendRequest(MainSocketRemoteUser User)
|
internal void RemoveFriendRequest(MainSocketRemoteUser User)
|
||||||
{
|
{
|
||||||
if ((Server as Server<MainSocketAppUser>)!.poeople.Any(s => s.Id == User.Id))
|
if (Server.poeople.Any(s => s.Id == User.Id))
|
||||||
{
|
{
|
||||||
IEnumerable<IUser> b = (Server as Server<MainSocketAppUser>)!.poeople.Where(s => s.Id == User.Id);
|
IEnumerable<IUser> b = Server.poeople.Where(s => s.Id == User.Id);
|
||||||
foreach (IUser item in b)
|
foreach (IUser item in b)
|
||||||
{
|
{
|
||||||
(Server as Server<MainSocketAppUser>)!.poeople.Remove(item);
|
Server.poeople.Remove(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Server as Server<MainSocketAppUser>)!.poeople.Add(User);
|
Server.poeople.Add(User);
|
||||||
foreach (MainSocketRemoteUser user in _FriendRequests)
|
foreach (MainSocketRemoteUser user in _FriendRequests)
|
||||||
{
|
{
|
||||||
if (User.Id == user.Id)
|
if (User.Id == user.Id)
|
||||||
@ -165,18 +167,18 @@ public class MainSocketAppUser : SocketUserBase, IAppUser
|
|||||||
|
|
||||||
internal void AddFriendRequest(MainSocketRemoteUser User)
|
internal void AddFriendRequest(MainSocketRemoteUser User)
|
||||||
{
|
{
|
||||||
if ((Server as Server<MainSocketAppUser>)!.poeople.Any(s => s.Id == User.Id))
|
if (Server.poeople.Any(s => s.Id == User.Id))
|
||||||
{
|
{
|
||||||
IEnumerable<IUser> b = (Server as Server<MainSocketAppUser>)!.poeople.Where(s => s.Id == User.Id);
|
IEnumerable<IUser> b = Server.poeople.Where(s => s.Id == User.Id);
|
||||||
foreach (IUser item in b)
|
foreach (IUser item in b)
|
||||||
{
|
{
|
||||||
(Server as Server<MainSocketAppUser>)!.poeople.Remove(item);
|
Server.poeople.Remove(item);
|
||||||
}
|
}
|
||||||
(Server as Server<MainSocketAppUser>)!.poeople.Add(User);
|
Server.poeople.Add(User);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(Server as Server<MainSocketAppUser>)!.poeople.Add(User);
|
Server.poeople.Add(User);
|
||||||
}
|
}
|
||||||
_FriendRequests.Add(User);
|
_FriendRequests.Add(User);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,9 @@ public class MainSocketChannel : IncomingHTTP
|
|||||||
[JsonPropertyName("type")]
|
[JsonPropertyName("type")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public ChannelType Type { get; internal set; } = default!;
|
public ChannelType Type { get; internal set; } = default!;
|
||||||
public IServer Server { get; internal set; }
|
|
||||||
|
public MainServer Server { get; internal set; } = default!;
|
||||||
|
|
||||||
[JsonPropertyName("members")]
|
[JsonPropertyName("members")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public long[] MemberIdList { get; internal set; } = default!;
|
public long[] MemberIdList { get; internal set; } = default!;
|
||||||
@ -49,8 +51,8 @@ public class MainSocketChannel : IncomingHTTP
|
|||||||
_members = new();
|
_members = new();
|
||||||
foreach (long member in MemberIdList)
|
foreach (long member in MemberIdList)
|
||||||
{
|
{
|
||||||
if (member != Server.IAppUser!.Id) _members.Add(Server.GetUser<MainSocketRemoteUser>(member, CancellationToken.None).Result);
|
if (member != Server.User.Id) _members.Add(Server.GetUser<MainSocketRemoteUser>(member, CancellationToken.None).Result);
|
||||||
else _members.Add(Server.IAppUser);
|
else _members.Add(Server.User);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _members.AsReadOnly();
|
return _members.AsReadOnly();
|
||||||
@ -74,7 +76,7 @@ public class MainSocketChannel : IncomingHTTP
|
|||||||
MaxDegreeOfParallelism = num
|
MaxDegreeOfParallelism = num
|
||||||
}, i =>
|
}, i =>
|
||||||
{
|
{
|
||||||
if (i.Id != (Server as Server<MainSocketAppUser>).User?.Id)
|
if (i.Id != Server.User.Id)
|
||||||
{
|
{
|
||||||
long key = i.GetUserKey(CancellationToken).Result;
|
long key = i.GetUserKey(CancellationToken).Result;
|
||||||
if (true)
|
if (true)
|
||||||
|
@ -14,7 +14,7 @@ public class MainSocketDMChannel : MainSocketTextChannel
|
|||||||
if (_user is null)
|
if (_user is null)
|
||||||
{
|
{
|
||||||
var list = MemberIdList.ToList();
|
var list = MemberIdList.ToList();
|
||||||
list.Remove(Server.IAppUser.Id);
|
list.Remove(Server.User.Id);
|
||||||
_user = Server.GetUser<MainSocketRemoteUser>(list.FirstOrDefault(), CancellationToken.None).Result;
|
_user = Server.GetUser<MainSocketRemoteUser>(list.FirstOrDefault(), CancellationToken.None).Result;
|
||||||
}
|
}
|
||||||
return _user;
|
return _user;
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
using Luski.net.Interfaces;
|
using Luski.net.Interfaces;
|
||||||
using Luski.net.JsonTypes.BaseTypes;
|
using Luski.net.JsonTypes.BaseTypes;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JacobTechEncryption;
|
using JacobTechEncryption;
|
||||||
|
|
||||||
namespace Luski.net.Structures;
|
namespace Luski.net.Structures.Main;
|
||||||
|
|
||||||
public class SocketMessage : IncomingHTTP
|
public class MainSocketMessage : IncomingHTTP
|
||||||
{
|
{
|
||||||
public IServer Server { get; }
|
public MainServer Server { get; internal set; } = default!;
|
||||||
[JsonPropertyName("id")]
|
[JsonPropertyName("id")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public long Id { get; internal set; } = default!;
|
public long Id { get; internal set; } = default!;
|
||||||
@ -32,8 +29,8 @@ public class SocketMessage : IncomingHTTP
|
|||||||
public File[]? Files { get; internal set; } = default!;
|
public File[]? Files { get; internal set; } = default!;
|
||||||
public async Task<IUser> GetAuthor(CancellationToken CancellationToken)
|
public async Task<IUser> GetAuthor(CancellationToken CancellationToken)
|
||||||
{
|
{
|
||||||
if (Server.IAppUser!.Id != AuthorID) return await Server.GetUser<SocketUserBase>(AuthorID, CancellationToken);
|
if (Server.User.Id != AuthorID) return await Server.GetUser<MainSocketUserBase>(AuthorID, CancellationToken);
|
||||||
else return Server.IAppUser;
|
else return Server.User;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void decrypt(string? key, CancellationToken CancellationToken)
|
internal void decrypt(string? key, CancellationToken CancellationToken)
|
||||||
@ -51,12 +48,12 @@ public class SocketMessage : IncomingHTTP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonSerializable(typeof(SocketMessage))]
|
[JsonSerializable(typeof(MainSocketMessage))]
|
||||||
[JsonSourceGenerationOptions(
|
[JsonSourceGenerationOptions(
|
||||||
GenerationMode = JsonSourceGenerationMode.Default,
|
GenerationMode = JsonSourceGenerationMode.Default,
|
||||||
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
|
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
|
||||||
WriteIndented = false)]
|
WriteIndented = false)]
|
||||||
public partial class SocketMessageContext : JsonSerializerContext
|
public partial class MainSocketMessageContext : JsonSerializerContext
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
@ -9,10 +9,11 @@ using System.Text.Json.Serialization;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Luski.net.Enums.Main;
|
using Luski.net.Enums.Main;
|
||||||
using Luski.net.Structures;
|
using Luski.net.Structures;
|
||||||
|
using Luski.net.Structures.Main;
|
||||||
|
|
||||||
namespace Luski.net.Structures;
|
namespace Luski.net.Structures.Main;
|
||||||
|
|
||||||
public class MainSocketRemoteUser : SocketUserBase
|
public class MainSocketRemoteUser : MainSocketUserBase
|
||||||
{
|
{
|
||||||
[JsonPropertyName("friend_status")]
|
[JsonPropertyName("friend_status")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
|
@ -17,9 +17,9 @@ namespace Luski.net.Structures.Main;
|
|||||||
|
|
||||||
public class MainSocketTextChannel : MainSocketChannel
|
public class MainSocketTextChannel : MainSocketChannel
|
||||||
{
|
{
|
||||||
public async Task<SocketMessage> GetMessage(long ID, CancellationToken CancellationToken)
|
public async Task<MainSocketMessage> GetMessage(long ID, CancellationToken CancellationToken)
|
||||||
{
|
{
|
||||||
return await (Server as Server<MainSocketAppUser>)!.GetMessage(ID, CancellationToken);
|
return await Server.GetMessage(ID, CancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<byte[]> GetPicture(CancellationToken CancellationToken)
|
public async Task<byte[]> GetPicture(CancellationToken CancellationToken)
|
||||||
@ -36,7 +36,7 @@ public class MainSocketTextChannel : MainSocketChannel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IReadOnlyList<SocketMessage>> GetMessages(long Message_Id, CancellationToken CancellationToken, int count = 50)
|
public async Task<IReadOnlyList<MainSocketMessage>> GetMessages(long Message_Id, CancellationToken CancellationToken, int count = 50)
|
||||||
{
|
{
|
||||||
if (count > 200)
|
if (count > 200)
|
||||||
{
|
{
|
||||||
@ -61,7 +61,7 @@ public class MainSocketTextChannel : MainSocketChannel
|
|||||||
|
|
||||||
string? key = ClientEncryption.File.Channels.GetKey(Id);
|
string? key = ClientEncryption.File.Channels.GetKey(Id);
|
||||||
if (data is null) throw new Exception("Invalid data from server");
|
if (data is null) throw new Exception("Invalid data from server");
|
||||||
if (data.Messages is null) data.Messages = Array.Empty<SocketMessage>();
|
if (data.Messages is null) data.Messages = Array.Empty<MainSocketMessage>();
|
||||||
Parallel.ForEach(data.Messages, new ParallelOptions()
|
Parallel.ForEach(data.Messages, new ParallelOptions()
|
||||||
{
|
{
|
||||||
MaxDegreeOfParallelism = num
|
MaxDegreeOfParallelism = num
|
||||||
@ -70,7 +70,7 @@ public class MainSocketTextChannel : MainSocketChannel
|
|||||||
i.decrypt(key, CancellationToken);
|
i.decrypt(key, CancellationToken);
|
||||||
});
|
});
|
||||||
key = null;
|
key = null;
|
||||||
return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList<SocketMessage>);
|
return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList<MainSocketMessage>);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -79,7 +79,7 @@ public class MainSocketTextChannel : MainSocketChannel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IReadOnlyList<SocketMessage>> GetMessages(CancellationToken CancellationToken, int count = 50)
|
public async Task<IReadOnlyList<MainSocketMessage>> GetMessages(CancellationToken CancellationToken, int count = 50)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -106,7 +106,7 @@ public class MainSocketTextChannel : MainSocketChannel
|
|||||||
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0));
|
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0));
|
||||||
if (num == 0) num = 1;
|
if (num == 0) num = 1;
|
||||||
string? key = ClientEncryption.File.Channels.GetKey(Id);
|
string? key = ClientEncryption.File.Channels.GetKey(Id);
|
||||||
if (data.Messages is null) data.Messages = Array.Empty<SocketMessage>();
|
if (data.Messages is null) data.Messages = Array.Empty<MainSocketMessage>();
|
||||||
Parallel.ForEach(data.Messages, new ParallelOptions()
|
Parallel.ForEach(data.Messages, new ParallelOptions()
|
||||||
{
|
{
|
||||||
MaxDegreeOfParallelism = num
|
MaxDegreeOfParallelism = num
|
||||||
@ -116,7 +116,7 @@ public class MainSocketTextChannel : MainSocketChannel
|
|||||||
});
|
});
|
||||||
key = null;
|
key = null;
|
||||||
Console.WriteLine($"Messages decrypted in {(DateTime.Now - start).TotalSeconds}");
|
Console.WriteLine($"Messages decrypted in {(DateTime.Now - start).TotalSeconds}");
|
||||||
return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList<SocketMessage>);
|
return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList<MainSocketMessage>);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -5,10 +5,11 @@ using Luski.net.Enums;
|
|||||||
using Luski.net.Interfaces;
|
using Luski.net.Interfaces;
|
||||||
using Luski.net.JsonTypes.BaseTypes;
|
using Luski.net.JsonTypes.BaseTypes;
|
||||||
|
|
||||||
namespace Luski.net.Structures;
|
namespace Luski.net.Structures.Main;
|
||||||
|
|
||||||
public class SocketUserBase : IncomingHTTP, IUser
|
public class MainSocketUserBase : IncomingHTTP, IUser
|
||||||
{
|
{
|
||||||
|
public MainServer Server { get; internal set; } = default!;
|
||||||
[JsonPropertyName("id")]
|
[JsonPropertyName("id")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public long Id { get; internal set; } = default!;
|
public long Id { get; internal set; } = default!;
|
||||||
@ -21,9 +22,6 @@ public class SocketUserBase : IncomingHTTP, IUser
|
|||||||
[JsonPropertyName("picture_type")]
|
[JsonPropertyName("picture_type")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public PictureType PictureType { get; internal set; } = default!;
|
public PictureType PictureType { get; internal set; } = default!;
|
||||||
[JsonIgnore]
|
|
||||||
public IServer Server { get; internal set; } = default!;
|
|
||||||
|
|
||||||
public async Task<byte[]> GetAvatar(CancellationToken CancellationToken)
|
public async Task<byte[]> GetAvatar(CancellationToken CancellationToken)
|
||||||
{
|
{
|
||||||
if (Server.Cache != null)
|
if (Server.Cache != null)
|
||||||
@ -41,13 +39,13 @@ public class SocketUserBase : IncomingHTTP, IUser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonSerializable(typeof(SocketUserBase))]
|
[JsonSerializable(typeof(MainSocketUserBase))]
|
||||||
[JsonSourceGenerationOptions(
|
[JsonSourceGenerationOptions(
|
||||||
GenerationMode = JsonSourceGenerationMode.Default,
|
GenerationMode = JsonSourceGenerationMode.Default,
|
||||||
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified,
|
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified,
|
||||||
WriteIndented = false,
|
WriteIndented = false,
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never)]
|
DefaultIgnoreCondition = JsonIgnoreCondition.Never)]
|
||||||
internal partial class SocketUserBaseContext : JsonSerializerContext
|
internal partial class MainSocketUserBaseContext : JsonSerializerContext
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
@ -1,13 +1,13 @@
|
|||||||
using Luski.net.JsonTypes.BaseTypes;
|
using Luski.net.JsonTypes.BaseTypes;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Luski.net.Structures;
|
namespace Luski.net.Structures.Main;
|
||||||
|
|
||||||
internal class SocketBulkMessage : IncomingHTTP
|
internal class SocketBulkMessage : IncomingHTTP
|
||||||
{
|
{
|
||||||
[JsonPropertyName("messages")]
|
[JsonPropertyName("messages")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public SocketMessage[]? Messages { get; set; } = default!;
|
public MainSocketMessage[]? Messages { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonSerializable(typeof(SocketBulkMessage))]
|
[JsonSerializable(typeof(SocketBulkMessage))]
|
51
Luski.net/Structures/Public/MainSocketUserBase.cs
Normal file
51
Luski.net/Structures/Public/MainSocketUserBase.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Luski.net.Enums;
|
||||||
|
using Luski.net.Interfaces;
|
||||||
|
using Luski.net.JsonTypes.BaseTypes;
|
||||||
|
|
||||||
|
namespace Luski.net.Structures.Public;
|
||||||
|
|
||||||
|
public class PublicSocketUserBase : IncomingHTTP, IUser
|
||||||
|
{
|
||||||
|
public PublicServer Server { get; internal set; } = default!;
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
[JsonInclude]
|
||||||
|
public long Id { get; internal set; } = default!;
|
||||||
|
[JsonPropertyName("username")]
|
||||||
|
[JsonInclude]
|
||||||
|
public string DisplayName { get; internal set; } = default!;
|
||||||
|
[JsonPropertyName("status")]
|
||||||
|
[JsonInclude]
|
||||||
|
public UserStatus Status { get; internal set; } = default!;
|
||||||
|
[JsonPropertyName("picture_type")]
|
||||||
|
[JsonInclude]
|
||||||
|
public PictureType PictureType { get; internal set; } = default!;
|
||||||
|
public async Task<byte[]> GetAvatar(CancellationToken CancellationToken)
|
||||||
|
{
|
||||||
|
if (Server.Cache != null)
|
||||||
|
{
|
||||||
|
bool isc = System.IO.File.Exists($"{Server.Cache}/avatars/{Id}");
|
||||||
|
if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", $"{Server.Cache}/avatars/{Id}", CancellationToken);
|
||||||
|
}
|
||||||
|
return System.IO.File.ReadAllBytes($"{Server.Cache}/avatars/{Id}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<long> GetUserKey(CancellationToken CancellationToken)
|
||||||
|
{
|
||||||
|
string data = Server.GetFromServer($"Keys/GetUserKey/{Id}", CancellationToken).Content.ReadAsStringAsync().Result;
|
||||||
|
return Task.FromResult(long.Parse(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonSerializable(typeof(PublicSocketUserBase))]
|
||||||
|
[JsonSourceGenerationOptions(
|
||||||
|
GenerationMode = JsonSourceGenerationMode.Default,
|
||||||
|
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified,
|
||||||
|
WriteIndented = false,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.Never)]
|
||||||
|
internal partial class PublicSocketUserBaseContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -10,10 +10,11 @@ using System.Threading.Tasks;
|
|||||||
using JacobTechEncryption;
|
using JacobTechEncryption;
|
||||||
using Luski.net.Enums;
|
using Luski.net.Enums;
|
||||||
using Luski.net.Enums.Main;
|
using Luski.net.Enums.Main;
|
||||||
|
using Luski.net.Structures.Main;
|
||||||
|
|
||||||
namespace Luski.net.Structures.Public;
|
namespace Luski.net.Structures.Public;
|
||||||
|
|
||||||
public class PublicSocketAppUser : SocketUserBase, IAppUser
|
public class PublicSocketAppUser : PublicSocketUserBase, IAppUser
|
||||||
{
|
{
|
||||||
[JsonPropertyName("selected_channel")]
|
[JsonPropertyName("selected_channel")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
|
Loading…
Reference in New Issue
Block a user