working messages!!@!@
This commit is contained in:
parent
bcd0e2c0ec
commit
e33554f369
4 changed files with 238 additions and 45 deletions
|
|
@ -647,6 +647,12 @@ public static async Task Auth(HttpContext context, Func<Task> next, IQueryCollec
|
||||||
case "user/dm/create":
|
case "user/dm/create":
|
||||||
await context.Response.WriteAsync(Encryption.Encryption.EncryptString(await Room.Requests.DmCreate(id, serializedBody.string2), password));
|
await context.Response.WriteAsync(Encryption.Encryption.EncryptString(await Room.Requests.DmCreate(id, serializedBody.string2), password));
|
||||||
break;
|
break;
|
||||||
|
case "dm/message/send":
|
||||||
|
await context.Response.WriteAsync(
|
||||||
|
Encryption.Encryption.EncryptString(
|
||||||
|
await Room.Requests.DmMessageSend(id, serializedBody.string2)
|
||||||
|
, password));
|
||||||
|
break;
|
||||||
case "dm/messages/get":
|
case "dm/messages/get":
|
||||||
{
|
{
|
||||||
Universal3String msgGet = JsonSerializer.Deserialize(
|
Universal3String msgGet = JsonSerializer.Deserialize(
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,17 @@ public class Program
|
||||||
|
|
||||||
switch (path)
|
switch (path)
|
||||||
{
|
{
|
||||||
|
case "/_larpix/ws":
|
||||||
|
if (context.WebSockets.IsWebSocketRequest)
|
||||||
|
{
|
||||||
|
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
|
||||||
|
await WsHandler(context, webSocket, query);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
}
|
||||||
|
return;
|
||||||
case "/_larpix/serverinfo":
|
case "/_larpix/serverinfo":
|
||||||
ServerInfo serverInfo = new ServerInfo();
|
ServerInfo serverInfo = new ServerInfo();
|
||||||
serverInfo.domain = DOMAIN;
|
serverInfo.domain = DOMAIN;
|
||||||
|
|
|
||||||
|
|
@ -133,25 +133,28 @@ public class Requests
|
||||||
{
|
{
|
||||||
return "error:cant.invite.urself";
|
return "error:cant.invite.urself";
|
||||||
}
|
}
|
||||||
SemaphoreSlim userLock2 = Account.Utils.GetUserLock(id2);
|
string inviteFile = ACCOUNTS_DATA_DIR + $"/{id2}/dminvites/recv/{id}";
|
||||||
await userLock2.WaitAsync();
|
if (Fs.Exists(inviteFile))
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
string inviteFile = ACCOUNTS_DATA_DIR + $"/{id2}/dminvites/recv/{id}";
|
return "info:user.already.invited";
|
||||||
if (Fs.Exists(inviteFile))
|
}
|
||||||
{
|
|
||||||
return "info:user.already.invited";
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] timestamp = Encoding.UTF8.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString());
|
byte[] timestamp = Encoding.UTF8.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString());
|
||||||
await Fs.WriteFile(inviteFile, timestamp);
|
await Fs.WriteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/sent/{id2}", timestamp); //we should also save that this user invited someone, because listing sent invites is cool
|
||||||
await Fs.WriteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/sent/{id2}", timestamp); //we should also save that this user invited someone, because listing sent invites is cool
|
|
||||||
}
|
_ = Task.Run(async () =>
|
||||||
finally
|
|
||||||
{
|
{
|
||||||
userLock2.Release();
|
SemaphoreSlim userLock2 = Account.Utils.GetUserLock(id2);
|
||||||
}
|
await userLock2.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Fs.WriteFile(inviteFile, timestamp);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
userLock2.Release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return "success:user.invited";
|
return "success:user.invited";
|
||||||
}
|
}
|
||||||
|
|
@ -170,17 +173,21 @@ public class Requests
|
||||||
string id2 = tId;
|
string id2 = tId;
|
||||||
if (id2 == "0" || string.IsNullOrEmpty(id2)) return "error:user.not.found";
|
if (id2 == "0" || string.IsNullOrEmpty(id2)) return "error:user.not.found";
|
||||||
|
|
||||||
SemaphoreSlim userLock2 = Account.Utils.GetUserLock(id2);
|
Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/sent/{id2}");
|
||||||
await userLock2.WaitAsync();
|
|
||||||
try
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id2}/dminvites/recv/{id}");
|
SemaphoreSlim userLock2 = Account.Utils.GetUserLock(id2);
|
||||||
Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/sent/{id2}");
|
await userLock2.WaitAsync();
|
||||||
}
|
try
|
||||||
finally
|
{
|
||||||
{
|
Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id2}/dminvites/recv/{id}");
|
||||||
userLock2.Release();
|
}
|
||||||
}
|
finally
|
||||||
|
{
|
||||||
|
userLock2.Release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return "success:invite.revoked";
|
return "success:invite.revoked";
|
||||||
}
|
}
|
||||||
|
|
@ -199,17 +206,21 @@ public class Requests
|
||||||
string id2 = tId;
|
string id2 = tId;
|
||||||
if (id2 == "0" || string.IsNullOrEmpty(id2)) return "error:user.not.found";
|
if (id2 == "0" || string.IsNullOrEmpty(id2)) return "error:user.not.found";
|
||||||
|
|
||||||
SemaphoreSlim userLock2 = Account.Utils.GetUserLock(id2);
|
Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/recv/{id2}");
|
||||||
await userLock2.WaitAsync();
|
|
||||||
try
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/recv/{id2}");
|
SemaphoreSlim userLock2 = Account.Utils.GetUserLock(id2);
|
||||||
Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id2}/dminvites/sent/{id}");
|
await userLock2.WaitAsync();
|
||||||
}
|
try
|
||||||
finally
|
{
|
||||||
{
|
Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id2}/dminvites/sent/{id}");
|
||||||
userLock2.Release();
|
}
|
||||||
}
|
finally
|
||||||
|
{
|
||||||
|
userLock2.Release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return "success:invite.declined";
|
return "success:invite.declined";
|
||||||
}
|
}
|
||||||
|
|
@ -309,17 +320,20 @@ public class Requests
|
||||||
|
|
||||||
if (isUserLocal)
|
if (isUserLocal)
|
||||||
{
|
{
|
||||||
SemaphoreSlim userLock = Account.Utils.GetUserLock(id2);
|
_ = Task.Run(async () =>
|
||||||
await userLock.WaitAsync();
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
await Account.Utils.UpdateUserDm(id2, dmId, "false",
|
SemaphoreSlim userLock = Account.Utils.GetUserLock(id2);
|
||||||
startingMessage.timestamp);
|
await userLock.WaitAsync();
|
||||||
}
|
try
|
||||||
finally
|
{
|
||||||
{
|
await Account.Utils.UpdateUserDm(id2, dmId, "false",
|
||||||
userLock.Release();
|
startingMessage.timestamp);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
userLock.Release();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else //federacja
|
else //federacja
|
||||||
{
|
{
|
||||||
|
|
@ -344,4 +358,70 @@ public class Requests
|
||||||
}
|
}
|
||||||
return "error:cant.create.dm.without.invitation";
|
return "error:cant.create.dm.without.invitation";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<string> DmMessageSend(string id, string body)
|
||||||
|
{
|
||||||
|
Universal3String serializedBody = JsonSerializer.Deserialize(
|
||||||
|
body,
|
||||||
|
AppJsonSerializerContext.Default.Universal3String
|
||||||
|
);
|
||||||
|
string dmId = serializedBody.string1;
|
||||||
|
string content = serializedBody.string2;
|
||||||
|
string attachment = serializedBody.string3;
|
||||||
|
|
||||||
|
if (!await IsMemberOfDm(id, dmId)) return "error:forbidden";
|
||||||
|
|
||||||
|
string msgDir = $"{ROOMS_DIR}/dms/{DOMAIN}/{dmId}/messages";
|
||||||
|
long last = 0;
|
||||||
|
if (Fs.Exists($"{msgDir}/last"))
|
||||||
|
{
|
||||||
|
last = long.Parse(Encoding.UTF8.GetString(await Fs.ReadFile($"{msgDir}/last")));
|
||||||
|
}
|
||||||
|
long nextId = last + 1;
|
||||||
|
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.author = id;
|
||||||
|
msg.timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
|
||||||
|
msg.type = "larp.text";
|
||||||
|
msg.content = content;
|
||||||
|
msg.attachment = attachment;
|
||||||
|
msg.key = "0"; // Flag for encrypted content
|
||||||
|
msg.pervious = "";
|
||||||
|
|
||||||
|
await Fs.WriteFile($"{msgDir}/{nextId}", Encoding.UTF8.GetBytes(JsonSerializer.Serialize(msg, AppJsonSerializerContext.Default.Message)));
|
||||||
|
await Fs.WriteFile($"{msgDir}/last", Encoding.UTF8.GetBytes(nextId.ToString()));
|
||||||
|
|
||||||
|
string[] members = Fs.ReadDirectory($"{ROOMS_DIR}/dms/{DOMAIN}/{dmId}/members");
|
||||||
|
foreach(string memberWD in members)
|
||||||
|
{
|
||||||
|
bool isLocal = Account.Utils.IsUserLocal(memberWD, out string domain);
|
||||||
|
string memberId = Account.Utils.GetValidIdOrZero(memberWD);
|
||||||
|
if (isLocal)
|
||||||
|
{
|
||||||
|
if (memberId == id)
|
||||||
|
{
|
||||||
|
await Account.Utils.UpdateUserDm(memberId, dmId, "true", msg.timestamp);
|
||||||
|
await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
SemaphoreSlim userLock = Account.Utils.GetUserLock(memberId);
|
||||||
|
await userLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Account.Utils.UpdateUserDm(memberId, dmId, "false", msg.timestamp);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
userLock.Release();
|
||||||
|
}
|
||||||
|
await Utils.Utils.SendWsMessage(memberId, $"dm_message:{dmId}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "success:message.sent";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using LarpixServer.Utils.Jsons;
|
||||||
|
|
||||||
namespace LarpixServer.Utils;
|
namespace LarpixServer.Utils;
|
||||||
|
|
||||||
|
|
@ -95,6 +97,100 @@ public class Utils
|
||||||
["ppt"] = "application/vnd.ms-powerpoint",
|
["ppt"] = "application/vnd.ms-powerpoint",
|
||||||
["pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
["pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static ConcurrentDictionary<string, List<System.Net.WebSockets.WebSocket>> ActiveWebSockets = new();
|
||||||
|
|
||||||
|
public static async Task WsHandler(HttpContext context, System.Net.WebSockets.WebSocket webSocket, IQueryCollection query)
|
||||||
|
{
|
||||||
|
var buffer = new byte[8192];
|
||||||
|
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||||
|
if (result.MessageType == System.Net.WebSockets.WebSocketMessageType.Close)
|
||||||
|
{
|
||||||
|
await webSocket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string authMessage = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||||
|
Universal2String authParams;
|
||||||
|
try {
|
||||||
|
authParams = JsonSerializer.Deserialize<Universal2String>(authMessage, AppJsonSerializerContext.Default.Universal2String);
|
||||||
|
} catch {
|
||||||
|
await webSocket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.InvalidMessageType, "Invalid auth", CancellationToken.None);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string id = Account.Utils.GetValidIdOrZero(authParams.string1);
|
||||||
|
string password = await Account.Utils.GetPassword(id);
|
||||||
|
string secret = await Account.Utils.NonceDecryptBody(id, password, authParams.string2);
|
||||||
|
|
||||||
|
string auth = await Account.Utils.Auth(id, password, secret);
|
||||||
|
|
||||||
|
if (auth != Account.Utils.LOGIN_SUCCESS)
|
||||||
|
{
|
||||||
|
await webSocket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.PolicyViolation, "Unauthorized", CancellationToken.None);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveWebSockets.AddOrUpdate(id, new List<System.Net.WebSockets.WebSocket> { webSocket }, (k, list) => {
|
||||||
|
lock(list) {
|
||||||
|
list.Add(webSocket);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (webSocket.State == System.Net.WebSockets.WebSocketState.Open)
|
||||||
|
{
|
||||||
|
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||||
|
if (result.MessageType == System.Net.WebSockets.WebSocketMessageType.Close)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (ActiveWebSockets.TryGetValue(id, out var list))
|
||||||
|
{
|
||||||
|
lock(list) {
|
||||||
|
list.Remove(webSocket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (webSocket.State == System.Net.WebSockets.WebSocketState.Open)
|
||||||
|
{
|
||||||
|
await webSocket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task SendWsMessage(string id, string message)
|
||||||
|
{
|
||||||
|
if (ActiveWebSockets.TryGetValue(id, out var list))
|
||||||
|
{
|
||||||
|
List<System.Net.WebSockets.WebSocket> toRemove = null;
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(message);
|
||||||
|
lock(list) {
|
||||||
|
foreach(var ws in list)
|
||||||
|
{
|
||||||
|
if (ws.State == System.Net.WebSockets.WebSocketState.Open)
|
||||||
|
{
|
||||||
|
_ = ws.SendAsync(new ArraySegment<byte>(bytes), System.Net.WebSockets.WebSocketMessageType.Text, true, CancellationToken.None);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (toRemove == null) toRemove = new();
|
||||||
|
toRemove.Add(ws);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (toRemove != null)
|
||||||
|
{
|
||||||
|
foreach(var ws in toRemove) list.Remove(ws);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task<string> LoadBody(StreamReader reader)
|
public static async Task<string> LoadBody(StreamReader reader)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue