98 lines
3.8 KiB
C#
98 lines
3.8 KiB
C#
using LiteDB;
|
|
|
|
namespace Campus_live_rewrite.api.dbs
|
|
{
|
|
/// <summary>
|
|
/// Website support tickets. A signed-in player opens a ticket (from the Support page bot's
|
|
/// "talk to a real person" flow); staff (dev / mod / owner) see EVERY ticket and can reply,
|
|
/// close and reopen. Stored in its own LiteDB file; checkpointed by DbMaintenance.
|
|
/// </summary>
|
|
public static class SupportDB
|
|
{
|
|
private static readonly LiteDatabase DB =
|
|
new LiteDatabase(Environment.CurrentDirectory + "/Data/DBs/Support.db");
|
|
|
|
public static void Checkpoint() => DB.Checkpoint();
|
|
|
|
private static ILiteCollection<Ticket> Tickets => DB.GetCollection<Ticket>("tickets");
|
|
|
|
static SupportDB()
|
|
{
|
|
Tickets.EnsureIndex(x => x.AccountId);
|
|
Tickets.EnsureIndex(x => x.Status);
|
|
}
|
|
|
|
public sealed class TicketMessage
|
|
{
|
|
public ulong FromAccountId { get; set; }
|
|
public string FromName { get; set; } = "";
|
|
public bool Staff { get; set; }
|
|
public string Body { get; set; } = "";
|
|
public DateTime At { get; set; } = DateTime.UtcNow;
|
|
}
|
|
|
|
public sealed class Ticket
|
|
{
|
|
public int Id { get; set; } // LiteDB auto-increment PK
|
|
public ulong AccountId { get; set; }
|
|
public string Username { get; set; } = "";
|
|
public string Subject { get; set; } = "";
|
|
public string Status { get; set; } = "open"; // "open" | "closed"
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
|
public List<TicketMessage> Messages { get; set; } = new();
|
|
}
|
|
|
|
public static Ticket Create(ulong accountId, string username, string subject, string body)
|
|
{
|
|
var t = new Ticket
|
|
{
|
|
AccountId = accountId,
|
|
Username = username,
|
|
Subject = string.IsNullOrWhiteSpace(subject) ? "Support request" : subject,
|
|
Status = "open",
|
|
};
|
|
t.Messages.Add(new TicketMessage { FromAccountId = accountId, FromName = username, Staff = false, Body = body });
|
|
Tickets.Insert(t); // Id auto-assigned
|
|
return t;
|
|
}
|
|
|
|
public static Ticket? Get(int id) => Tickets.FindById(id);
|
|
|
|
public static List<Ticket> ForAccount(ulong accountId) =>
|
|
Tickets.Find(x => x.AccountId == accountId).OrderByDescending(x => x.UpdatedAt).ToList();
|
|
|
|
public static List<Ticket> All(string? status)
|
|
{
|
|
var rows = status == "open" ? Tickets.Find(x => x.Status == "open")
|
|
: status == "closed" ? Tickets.Find(x => x.Status == "closed")
|
|
: Tickets.FindAll();
|
|
return rows.OrderByDescending(x => x.UpdatedAt).ToList();
|
|
}
|
|
|
|
public static bool AddMessage(int id, ulong fromId, string fromName, bool staff, string body)
|
|
{
|
|
var t = Tickets.FindById(id);
|
|
if (t == null) return false;
|
|
t.Messages.Add(new TicketMessage { FromAccountId = fromId, FromName = fromName, Staff = staff, Body = body });
|
|
t.UpdatedAt = DateTime.UtcNow;
|
|
// A reply on a closed ticket reopens it, so nobody's follow-up is lost in a closed thread.
|
|
if (t.Status == "closed") t.Status = "open";
|
|
Tickets.Update(t);
|
|
return true;
|
|
}
|
|
|
|
public static bool SetStatus(int id, string status)
|
|
{
|
|
var t = Tickets.FindById(id);
|
|
if (t == null) return false;
|
|
t.Status = status;
|
|
t.UpdatedAt = DateTime.UtcNow;
|
|
Tickets.Update(t);
|
|
return true;
|
|
}
|
|
|
|
public static int OpenCount() => Tickets.Count(x => x.Status == "open");
|
|
}
|
|
}
|