mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 23:49:02 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,782 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.Extensions;
|
||||
using RadicadeServer.RecNet.Config;
|
||||
using RadicadeServer.RecNet.Auth;
|
||||
using RadicadeServer.RecNet.CDN;
|
||||
using RadicadeServer.RecNet.Enums;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace RadicadeServer.Clubs;
|
||||
|
||||
public sealed class ClubRecord
|
||||
{
|
||||
public long clubId { get; set; }
|
||||
public string name { get; set; } = "Radicade Club";
|
||||
public string description { get; set; } = "";
|
||||
public string primaryTag { get; set; } = "Social";
|
||||
public string imageName { get; set; } = "default";
|
||||
public string bannerImageName { get; set; } = "default";
|
||||
public long ownerPlayerId { get; set; }
|
||||
public DateTime createdAt { get; set; } = DateTime.UtcNow;
|
||||
public long? clubhouseRoomId { get; set; }
|
||||
public int visibility { get; set; } = 1;
|
||||
public int joinability { get; set; } = 0;
|
||||
public bool allowJuniors { get; set; } = true;
|
||||
public int minLevel { get; set; }
|
||||
public string[] customTags { get; set; } = Array.Empty<string>();
|
||||
public string[] additionalImages { get; set; } = Array.Empty<string>();
|
||||
public List<ClubMemberRecord> members { get; set; } = new();
|
||||
}
|
||||
|
||||
public sealed class ClubMemberRecord
|
||||
{
|
||||
public long accountId { get; set; }
|
||||
public int membershipType { get; set; }
|
||||
public int pendingMembershipType { get; set; }
|
||||
public long inviterAccountId { get; set; }
|
||||
public DateTime createdAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public static class ClubMembershipTypes
|
||||
{
|
||||
public const int Banned = -1;
|
||||
public const int None = 0;
|
||||
public const int PendingRequested = 1;
|
||||
public const int PendingInvited = 2;
|
||||
public const int PendingDenied = 3;
|
||||
public const int Member = 10;
|
||||
public const int Moderator = 20;
|
||||
public const int Coowner = 30;
|
||||
public const int Creator = 100;
|
||||
}
|
||||
|
||||
public sealed record ClubMemberView(long AccountId, int MembershipType, long InviterAccountId, DateTime CreatedAt);
|
||||
|
||||
public static class ClubStore
|
||||
{
|
||||
private const string ConfigKey = "Clubs";
|
||||
|
||||
public const string DefaultImageName = "DefaultImgPurple";
|
||||
private const int ActiveMemberThreshold = ClubMembershipTypes.Member;
|
||||
|
||||
private static readonly string[] CategoryQueryKeys =
|
||||
{
|
||||
"category", "Category", "categoryTag", "CategoryTag", "categoryName", "CategoryName",
|
||||
"primaryTag", "PrimaryTag", "tag", "Tag", "tags", "Tags"
|
||||
};
|
||||
|
||||
public static string? CategoryFilter(IQueryCollection query)
|
||||
{
|
||||
foreach (var key in CategoryQueryKeys)
|
||||
{
|
||||
var value = query[key].FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(value)) return value.Trim().TrimStart('#');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool MatchesCategory(ClubRecord? club, string? category)
|
||||
{
|
||||
if (club == null) return false;
|
||||
|
||||
var wanted = category?.Trim().TrimStart('#');
|
||||
if (string.IsNullOrWhiteSpace(wanted)) return true;
|
||||
|
||||
return string.Equals(club.primaryTag?.Trim(), wanted, StringComparison.OrdinalIgnoreCase)
|
||||
|| (club.customTags ?? Array.Empty<string>())
|
||||
.Any(tag => string.Equals(tag?.Trim(), wanted, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public static List<ClubRecord> FilterByCategory(List<ClubRecord> clubs, string? category)
|
||||
=> string.IsNullOrWhiteSpace(category)
|
||||
? clubs
|
||||
: clubs.Where(club => MatchesCategory(club, category)).ToList();
|
||||
|
||||
public static async Task<List<ClubRecord>> LoadAsync(RecDBContext db)
|
||||
{
|
||||
var config = await db.ServerConfig.AsNoTracking().FirstOrDefaultAsync(x => x.Key == ConfigKey);
|
||||
if (config == null || string.IsNullOrWhiteSpace(config.Value)) return new List<ClubRecord>();
|
||||
|
||||
try
|
||||
{
|
||||
var clubs = config.Value.FromJson<List<ClubRecord>>() ?? new List<ClubRecord>();
|
||||
foreach (var club in clubs)
|
||||
{
|
||||
club.members ??= new List<ClubMemberRecord>();
|
||||
club.customTags ??= Array.Empty<string>();
|
||||
club.additionalImages ??= Array.Empty<string>();
|
||||
}
|
||||
return clubs;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new List<ClubRecord>();
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string[] HomeClubConfigPrefixes = ["ClubHome:", "ClubHome.", "HomeClub:"];
|
||||
|
||||
private static string HomeClubConfigKey(long accountId) => $"ClubHome:{accountId}";
|
||||
|
||||
private static async Task<long?> GetHomeClubIdAsync(RecDBContext db, long accountId)
|
||||
{
|
||||
var keys = HomeClubConfigPrefixes.Select(prefix => prefix + accountId.ToString(System.Globalization.CultureInfo.InvariantCulture)).ToArray();
|
||||
var value = await db.ServerConfig.AsNoTracking()
|
||||
.Where(config => keys.Contains(config.Key))
|
||||
.Select(config => config.Value)
|
||||
.FirstOrDefaultAsync();
|
||||
return long.TryParse(value, out var clubId) && clubId > 0 ? clubId : null;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> GetHomeClubAsync(RecDBContext db, long accountId)
|
||||
{
|
||||
var clubId = await GetHomeClubIdAsync(db, accountId);
|
||||
if (!clubId.HasValue) return null;
|
||||
var club = await GetAsync(db, clubId.Value);
|
||||
return club?.clubhouseRoomId is > 0 ? club : null;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> SetHomeClubAsync(RecDBContext db, long accountId, long? clubId)
|
||||
{
|
||||
var configKey = HomeClubConfigKey(accountId);
|
||||
var config = await db.ServerConfig.FirstOrDefaultAsync(row => row.Key == configKey);
|
||||
if (clubId is > 0)
|
||||
{
|
||||
if (config == null)
|
||||
{
|
||||
config = new ServerConfigVal(configKey, clubId.Value.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
await db.ServerConfig.AddAsync(config);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.Value = clubId.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
else if (config != null)
|
||||
{
|
||||
db.ServerConfig.Remove(config);
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
return clubId is > 0 ? await GetAsync(db, clubId.Value) : null;
|
||||
}
|
||||
|
||||
public static async Task ClearHomeClubsAsync(RecDBContext db, long clubId)
|
||||
{
|
||||
var clubIdValue = clubId.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
var matching = await db.ServerConfig.Where(config => config.Value == clubIdValue).ToListAsync();
|
||||
matching.RemoveAll(config => !HomeClubConfigPrefixes.Any(prefix => config.Key.StartsWith(prefix, StringComparison.Ordinal)));
|
||||
if (matching.Count == 0) return;
|
||||
db.ServerConfig.RemoveRange(matching);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public static async Task<bool> DeleteAsync(RecDBContext db, long clubId)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
if (clubs.RemoveAll(club => club.clubId == clubId) == 0) return false;
|
||||
await SaveAsync(db, clubs);
|
||||
await ClearHomeClubsAsync(db, clubId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> UpdateAsync(RecDBContext db, long clubId, JsonObject body)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
var club = clubs.FirstOrDefault(item => item.clubId == clubId);
|
||||
if (club == null) return null;
|
||||
|
||||
if (GetNonEmptyString(body, "name") is { } name) club.name = name.Trim();
|
||||
if (GetNonEmptyString(body, "description") is { } description) club.description = description;
|
||||
if (GetNonEmptyString(body, "category", "primaryTag") is { } category) club.primaryTag = category.Trim();
|
||||
if (GetNonEmptyString(body, "mainImageName") is { } imageName) club.imageName = imageName.Trim();
|
||||
if (GetNonEmptyString(body, "bannerImageName") is { } bannerName) club.bannerImageName = bannerName.Trim();
|
||||
if (GetInt(body, "visibility") is { } visibility) club.visibility = visibility;
|
||||
if (GetInt(body, "joinability") is { } joinability) club.joinability = joinability;
|
||||
if (GetInt(body, "minLevel") is { } minLevel && minLevel >= 0) club.minLevel = minLevel;
|
||||
if (GetBool(body, "allowJuniors") is { } allowJuniors) club.allowJuniors = allowJuniors;
|
||||
if (GetStringArray(body, "customTags") is { } customTags) club.customTags = NormalizeTags(customTags);
|
||||
|
||||
await SaveAsync(db, clubs);
|
||||
return club;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> SetClubhouseAsync(RecDBContext db, long clubId, long? roomId)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
var club = clubs.FirstOrDefault(item => item.clubId == clubId);
|
||||
if (club == null) return null;
|
||||
club.clubhouseRoomId = roomId;
|
||||
await SaveAsync(db, clubs);
|
||||
return club;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> SetAdditionalImageAsync(RecDBContext db, long clubId, int index, string imageName)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
var club = clubs.FirstOrDefault(item => item.clubId == clubId);
|
||||
if (club == null) return null;
|
||||
|
||||
var images = (club.additionalImages ?? Array.Empty<string>()).ToList();
|
||||
if (string.IsNullOrWhiteSpace(imageName))
|
||||
{
|
||||
if (index < images.Count) images.RemoveAt(index);
|
||||
}
|
||||
else if (index < images.Count)
|
||||
{
|
||||
images[index] = imageName.Trim();
|
||||
}
|
||||
else if (images.Count < 3)
|
||||
{
|
||||
images.Add(imageName.Trim());
|
||||
}
|
||||
club.additionalImages = images.ToArray();
|
||||
await SaveAsync(db, clubs);
|
||||
return club;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> InviteMemberAsync(RecDBContext db, long clubId, long accountId, int membershipType, long inviterAccountId)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
var club = clubs.FirstOrDefault(item => item.clubId == clubId);
|
||||
if (club == null || club.ownerPlayerId == accountId) return null;
|
||||
|
||||
club.members ??= new List<ClubMemberRecord>();
|
||||
var membership = club.members.FirstOrDefault(item => item.accountId == accountId);
|
||||
if (membership == null)
|
||||
{
|
||||
membership = new ClubMemberRecord { accountId = accountId, createdAt = DateTime.UtcNow };
|
||||
club.members.Add(membership);
|
||||
}
|
||||
|
||||
membership.membershipType = ClubMembershipTypes.PendingInvited;
|
||||
membership.pendingMembershipType = membershipType;
|
||||
membership.inviterAccountId = inviterAccountId;
|
||||
await SaveAsync(db, clubs);
|
||||
return club;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> AcceptInvitationAsync(RecDBContext db, long clubId, long accountId)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
var club = clubs.FirstOrDefault(item => item.clubId == clubId);
|
||||
var membership = club?.members?.FirstOrDefault(item => item.accountId == accountId);
|
||||
if (club == null || membership?.membershipType != ClubMembershipTypes.PendingInvited) return null;
|
||||
|
||||
var grantedType = membership.pendingMembershipType is ClubMembershipTypes.Member or ClubMembershipTypes.Moderator or ClubMembershipTypes.Coowner
|
||||
? membership.pendingMembershipType
|
||||
: ClubMembershipTypes.Member;
|
||||
membership.membershipType = grantedType;
|
||||
membership.pendingMembershipType = 0;
|
||||
membership.createdAt = DateTime.UtcNow;
|
||||
await SaveAsync(db, clubs);
|
||||
return club;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> RequestToJoinAsync(RecDBContext db, long clubId, long accountId)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
var club = clubs.FirstOrDefault(item => item.clubId == clubId);
|
||||
if (club == null) return null;
|
||||
var current = GetMembershipType(club, accountId);
|
||||
if (current == ClubMembershipTypes.Banned || current >= ClubMembershipTypes.Member || current == ClubMembershipTypes.PendingRequested)
|
||||
return club;
|
||||
if (club.joinability == 1) return club;
|
||||
return await SetMemberTypeAsync(db, clubId, accountId,
|
||||
club.joinability == 0 ? ClubMembershipTypes.Member : ClubMembershipTypes.PendingRequested,
|
||||
accountId);
|
||||
}
|
||||
|
||||
private static string? GetString(JsonObject body, params string[] keys)
|
||||
{
|
||||
foreach (var pair in body)
|
||||
{
|
||||
if (!keys.Any(key => pair.Key.Equals(key, StringComparison.OrdinalIgnoreCase)) || pair.Value == null) continue;
|
||||
if (pair.Value is JsonValue jsonValue && jsonValue.TryGetValue<string>(out var value)) return value;
|
||||
return pair.Value.ToString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? GetNonEmptyString(JsonObject body, params string[] keys)
|
||||
{
|
||||
var value = GetString(body, keys);
|
||||
return string.IsNullOrWhiteSpace(value) ? null : value;
|
||||
}
|
||||
|
||||
private static int? GetInt(JsonObject body, params string[] keys)
|
||||
{
|
||||
var raw = GetString(body, keys)?.Trim();
|
||||
if (int.TryParse(raw, out var value)) return value;
|
||||
return raw?.ToLowerInvariant() switch
|
||||
{
|
||||
"private" => 0,
|
||||
"public" => 1,
|
||||
"open" => 0,
|
||||
"inviteonly" or "invite_only" => 1,
|
||||
"asktojoin" or "requesttojoin" or "ask_to_join" => 2,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private static bool? GetBool(JsonObject body, params string[] keys)
|
||||
{
|
||||
var raw = GetString(body, keys)?.Trim();
|
||||
if (bool.TryParse(raw, out var value)) return value;
|
||||
return raw?.ToLowerInvariant() switch
|
||||
{
|
||||
"1" or "yes" => true,
|
||||
"0" or "no" => false,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private static string[]? GetStringArray(JsonObject body, params string[] keys)
|
||||
{
|
||||
foreach (var pair in body)
|
||||
{
|
||||
if (!keys.Any(key => pair.Key.Equals(key, StringComparison.OrdinalIgnoreCase))) continue;
|
||||
if (pair.Value is JsonArray array)
|
||||
return NormalizeTags(array.Select(item => item?.ToString() ?? string.Empty));
|
||||
return NormalizeTags(new[] { pair.Value?.ToString() ?? string.Empty });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string[] NormalizeTags(IEnumerable<string> tags)
|
||||
{
|
||||
var output = new List<string>();
|
||||
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var raw in tags)
|
||||
{
|
||||
var tag = raw.Trim();
|
||||
if (tag.Length > 0 && seen.Add(tag)) output.Add(tag);
|
||||
}
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
public static async Task SaveAsync(RecDBContext db, List<ClubRecord> clubs)
|
||||
{
|
||||
var config = await db.ServerConfig.FirstOrDefaultAsync(x => x.Key == ConfigKey);
|
||||
if (config == null)
|
||||
{
|
||||
config = new ServerConfigVal(ConfigKey, "[]");
|
||||
await db.ServerConfig.AddAsync(config);
|
||||
}
|
||||
|
||||
config.Value = clubs.ToJson();
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> GetAsync(RecDBContext db, long clubId)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
return clubs.FirstOrDefault(x => x.clubId == clubId);
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord> CreateAsync(
|
||||
RecDBContext db,
|
||||
long ownerPlayerId,
|
||||
string? name,
|
||||
string? description,
|
||||
string? primaryTag = null,
|
||||
int visibility = 1,
|
||||
int joinability = 0,
|
||||
bool allowJuniors = true,
|
||||
int minLevel = 0,
|
||||
string? mainImageName = null)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
var clubName = string.IsNullOrWhiteSpace(name) ? "Radicade Club" : name.Trim();
|
||||
var tag = string.IsNullOrWhiteSpace(primaryTag) ? "Social" : primaryTag.Trim();
|
||||
var now = DateTime.UtcNow;
|
||||
var clubId = Math.Abs((long)HashCode.Combine(ownerPlayerId, clubName, now.Ticks));
|
||||
|
||||
while (clubId == 0 || clubs.Any(x => x.clubId == clubId))
|
||||
{
|
||||
clubId = Math.Abs((long)HashCode.Combine(ownerPlayerId, clubName, Guid.NewGuid()));
|
||||
}
|
||||
|
||||
var club = new ClubRecord
|
||||
{
|
||||
clubId = clubId,
|
||||
name = clubName,
|
||||
description = description?.Trim() ?? "",
|
||||
primaryTag = tag,
|
||||
imageName = string.IsNullOrWhiteSpace(mainImageName) ? DefaultImageName : mainImageName.Trim(),
|
||||
visibility = visibility,
|
||||
joinability = joinability,
|
||||
allowJuniors = allowJuniors,
|
||||
minLevel = minLevel,
|
||||
ownerPlayerId = ownerPlayerId,
|
||||
createdAt = now
|
||||
};
|
||||
club.members.Add(new ClubMemberRecord
|
||||
{
|
||||
accountId = ownerPlayerId,
|
||||
membershipType = ClubMembershipTypes.Creator,
|
||||
inviterAccountId = ownerPlayerId,
|
||||
createdAt = now
|
||||
});
|
||||
|
||||
clubs.Add(club);
|
||||
await SaveAsync(db, clubs);
|
||||
return club;
|
||||
}
|
||||
|
||||
public static int GetMembershipType(ClubRecord? club, long accountId)
|
||||
{
|
||||
if (club == null || club.clubId <= 0 || accountId <= 0) return ClubMembershipTypes.None;
|
||||
if (club.ownerPlayerId == accountId) return ClubMembershipTypes.Creator;
|
||||
return club.members?.FirstOrDefault(x => x.accountId == accountId)?.membershipType ?? ClubMembershipTypes.None;
|
||||
}
|
||||
|
||||
public static int GetMemberCount(ClubRecord? club)
|
||||
{
|
||||
if (club == null || club.clubId <= 0) return 0;
|
||||
return 1 + (club.members ?? new List<ClubMemberRecord>())
|
||||
.Where(x => x.accountId != club.ownerPlayerId && x.membershipType >= ActiveMemberThreshold)
|
||||
.Select(x => x.accountId)
|
||||
.Distinct()
|
||||
.Count();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<ClubMemberView> GetMembers(ClubRecord? club)
|
||||
{
|
||||
if (club == null || club.clubId <= 0) return Array.Empty<ClubMemberView>();
|
||||
|
||||
var members = new List<ClubMemberView>
|
||||
{
|
||||
new(club.ownerPlayerId, ClubMembershipTypes.Creator, club.ownerPlayerId, club.createdAt)
|
||||
};
|
||||
members.AddRange((club.members ?? new List<ClubMemberRecord>())
|
||||
.Where(x => x.accountId != club.ownerPlayerId)
|
||||
.GroupBy(x => x.accountId)
|
||||
.Select(group => group.Last())
|
||||
.Select(x => new ClubMemberView(
|
||||
x.accountId,
|
||||
x.membershipType,
|
||||
x.inviterAccountId,
|
||||
NormalizeMembershipDate(x.createdAt, club.createdAt))));
|
||||
return members;
|
||||
}
|
||||
|
||||
private static DateTime NormalizeMembershipDate(DateTime memberCreatedAt, DateTime clubCreatedAt)
|
||||
{
|
||||
if (memberCreatedAt > DateTime.MinValue) return memberCreatedAt;
|
||||
if (clubCreatedAt > DateTime.MinValue) return clubCreatedAt;
|
||||
return DateTime.UnixEpoch;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> SetMemberTypeAsync(
|
||||
RecDBContext db,
|
||||
long clubId,
|
||||
long accountId,
|
||||
int membershipType,
|
||||
long inviterAccountId)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
var club = clubs.FirstOrDefault(x => x.clubId == clubId);
|
||||
if (club == null || club.ownerPlayerId == accountId) return null;
|
||||
|
||||
club.members ??= new List<ClubMemberRecord>();
|
||||
var membership = club.members.FirstOrDefault(x => x.accountId == accountId);
|
||||
if (membership == null)
|
||||
{
|
||||
club.members.Add(new ClubMemberRecord
|
||||
{
|
||||
accountId = accountId,
|
||||
membershipType = membershipType,
|
||||
inviterAccountId = inviterAccountId,
|
||||
createdAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (membership.membershipType < ClubMembershipTypes.Member && membershipType >= ClubMembershipTypes.Member)
|
||||
membership.createdAt = DateTime.UtcNow;
|
||||
membership.membershipType = membershipType;
|
||||
membership.inviterAccountId = inviterAccountId;
|
||||
}
|
||||
|
||||
await SaveAsync(db, clubs);
|
||||
return club;
|
||||
}
|
||||
|
||||
public static async Task<ClubRecord?> RemoveMemberAsync(RecDBContext db, long clubId, long accountId)
|
||||
{
|
||||
var clubs = await LoadAsync(db);
|
||||
var club = clubs.FirstOrDefault(x => x.clubId == clubId);
|
||||
if (club == null || club.ownerPlayerId == accountId) return null;
|
||||
|
||||
club.members ??= new List<ClubMemberRecord>();
|
||||
club.members.RemoveAll(member => member.accountId == accountId && member.membershipType != ClubMembershipTypes.Banned);
|
||||
await SaveAsync(db, clubs);
|
||||
return club;
|
||||
}
|
||||
|
||||
private static long ToUnix(DateTime value) => new DateTimeOffset(value).ToUnixTimeSeconds();
|
||||
|
||||
public static object BuildClub(ClubRecord? club, long viewerId = 0)
|
||||
{
|
||||
club ??= new ClubRecord { clubId = 0 };
|
||||
var memberCount = GetMemberCount(club);
|
||||
|
||||
return new Dictionary<string, object?>
|
||||
{
|
||||
["ClubId"] = club.clubId,
|
||||
["Name"] = club.name,
|
||||
["Description"] = club.description,
|
||||
["MainImageName"] = club.imageName,
|
||||
["State"] = 0,
|
||||
["CreatorAccountId"] = club.ownerPlayerId,
|
||||
["Category"] = club.primaryTag,
|
||||
["Visibility"] = club.visibility,
|
||||
["Joinability"] = club.joinability,
|
||||
["AllowJuniors"] = club.allowJuniors,
|
||||
["MinLevel"] = club.minLevel,
|
||||
["MemberCount"] = memberCount,
|
||||
["IsRRO"] = false,
|
||||
["ClubhouseRoomId"] = club.clubhouseRoomId,
|
||||
["ClubType"] = 0
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task<object> BuildClubDetailsAsync(RecDBContext db, ClubRecord? club, long viewerId = 0)
|
||||
{
|
||||
club ??= new ClubRecord { clubId = 0 };
|
||||
var details = (Dictionary<string, object?>)BuildClubDetails(club, viewerId);
|
||||
details["AdditionalImages"] = await GetAdditionalImagesAsync(db, club.additionalImages ?? Array.Empty<string>());
|
||||
return details;
|
||||
}
|
||||
|
||||
public static object BuildClubDetails(ClubRecord? club, long viewerId = 0)
|
||||
{
|
||||
club ??= new ClubRecord { clubId = 0 };
|
||||
return new Dictionary<string, object?>
|
||||
{
|
||||
["Club"] = BuildClub(club, viewerId),
|
||||
["ClubId"] = club.clubId,
|
||||
["CustomTags"] = club.customTags ?? Array.Empty<string>(),
|
||||
["AdditionalImages"] = Array.Empty<object>(),
|
||||
["CoownerPermissions"] = BuildPermissions(club.clubId, 0),
|
||||
["ModeratorPermissions"] = BuildPermissions(club.clubId, 1),
|
||||
["MemberPermissions"] = BuildPermissions(club.clubId, 2),
|
||||
["MyMembershipType"] = GetMembershipType(club, viewerId)
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task<object[]> GetAdditionalImagesAsync(RecDBContext db, string[] imageNames)
|
||||
{
|
||||
if (imageNames.Length == 0) return Array.Empty<object>();
|
||||
var records = await db.Images.AsNoTracking()
|
||||
.Where(image => imageNames.Contains(image.Name))
|
||||
.ToDictionaryAsync(image => image.Name);
|
||||
var imageIds = records.Values.Select(image => image.Id).ToList();
|
||||
var cheers = await db.ImageInteractions.AsNoTracking()
|
||||
.Where(interaction => interaction.IsCheered && imageIds.Contains(interaction.SavedImageId))
|
||||
.GroupBy(interaction => interaction.SavedImageId)
|
||||
.Select(group => new { ImageId = group.Key, Count = group.Count() })
|
||||
.ToDictionaryAsync(group => group.ImageId, group => group.Count);
|
||||
|
||||
return imageNames.Select(name =>
|
||||
{
|
||||
if (!records.TryGetValue(name, out var image))
|
||||
return (object)new Dictionary<string, object?>
|
||||
{
|
||||
["Id"] = 0L, ["Type"] = 1,
|
||||
["Accessibility"] = (int)SavedImageAccessibility.Public,
|
||||
["AccessibilityLocked"] = false, ["ImageName"] = name,
|
||||
["Description"] = null, ["PlayerId"] = 0L,
|
||||
["TaggedPlayerIds"] = Array.Empty<long>(), ["RoomId"] = null,
|
||||
["PlayerEventId"] = null, ["CreatedAt"] = DateTime.UnixEpoch,
|
||||
["CheerCount"] = 0, ["CommentCount"] = 0
|
||||
};
|
||||
return (object)new Dictionary<string, object?>
|
||||
{
|
||||
["Id"] = image.Id, ["Type"] = (int)image.ImageType,
|
||||
["Accessibility"] = (int)image.Accessibility, ["AccessibilityLocked"] = false,
|
||||
["ImageName"] = image.Name, ["Description"] = null, ["PlayerId"] = image.OwnerId,
|
||||
["TaggedPlayerIds"] = ParseImagePlayerIds(image.PlayerIds), ["RoomId"] = image.RoomId,
|
||||
["PlayerEventId"] = image.EventId, ["CreatedAt"] = image.CreatedAt,
|
||||
["CheerCount"] = cheers.GetValueOrDefault(image.Id), ["CommentCount"] = 0
|
||||
};
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private static long[] ParseImagePlayerIds(string? playerIds)
|
||||
{
|
||||
try { return JsonSerializer.Deserialize<long[]>(playerIds ?? "[]") ?? Array.Empty<long>(); }
|
||||
catch { return Array.Empty<long>(); }
|
||||
}
|
||||
|
||||
public static object BuildPermissions(long clubId, int type)
|
||||
{
|
||||
var isCoowner = type == 0;
|
||||
var isModerator = type == 1;
|
||||
return new Dictionary<string, object?>
|
||||
{
|
||||
["Type"] = type switch
|
||||
{
|
||||
0 => ClubMembershipTypes.Coowner,
|
||||
1 => ClubMembershipTypes.Moderator,
|
||||
_ => ClubMembershipTypes.Member
|
||||
},
|
||||
["ClubId"] = clubId,
|
||||
["ApproveMember"] = isCoowner || isModerator,
|
||||
["BanUnban"] = isCoowner || isModerator,
|
||||
["CreateEvent"] = isCoowner,
|
||||
["EditDetails"] = isCoowner,
|
||||
["EditPermissionSettings"] = isCoowner,
|
||||
["PostAnnouncement"] = isCoowner
|
||||
};
|
||||
}
|
||||
|
||||
public static object BuildMemberListItem(long playerId, int role, bool isOwner, long clubId = 0, long created = 0)
|
||||
{
|
||||
var type = isOwner ? ClubMembershipTypes.Creator : role;
|
||||
var createdAt = created > 0 ? DateTimeOffset.FromUnixTimeSeconds(created).UtcDateTime.ToString("O") : null;
|
||||
return new Dictionary<string, object?>
|
||||
{
|
||||
["ClubMemberId"] = playerId,
|
||||
["ClubId"] = clubId,
|
||||
["AccountId"] = playerId,
|
||||
["MembershipType"] = type,
|
||||
["CreatedAt"] = createdAt
|
||||
};
|
||||
}
|
||||
|
||||
public static object BuildMemberListItem(ClubMemberView member, long clubId) =>
|
||||
BuildMemberListItem(
|
||||
member.AccountId,
|
||||
member.MembershipType,
|
||||
member.MembershipType == ClubMembershipTypes.Creator,
|
||||
clubId,
|
||||
ToUnix(member.CreatedAt));
|
||||
|
||||
public static object BuildMember(long playerId, int role, bool isOwner, long clubId = 0, long created = 0) => new Dictionary<string, object?>
|
||||
{
|
||||
["AccountId"] = playerId,
|
||||
["ClubId"] = clubId,
|
||||
["CreatedAt"] = created > 0 ? DateTimeOffset.FromUnixTimeSeconds(created).UtcDateTime.ToString("O") : null,
|
||||
["MembershipType"] = isOwner ? ClubMembershipTypes.Creator : role,
|
||||
["membershipRequestId"] = 0L,
|
||||
["inviterAccountId"] = 0L,
|
||||
["subscriptionId"] = clubId,
|
||||
["playerId"] = playerId,
|
||||
["username"] = string.Empty,
|
||||
["displayName"] = string.Empty,
|
||||
["level"] = 0,
|
||||
["role"] = isOwner ? ClubMembershipTypes.Creator : role,
|
||||
["membershipStatus"] = role >= ActiveMemberThreshold || isOwner ? 1 : 0,
|
||||
["isOwner"] = isOwner,
|
||||
["created"] = created,
|
||||
["createdAt"] = created
|
||||
};
|
||||
|
||||
public static object BuildMember(ClubMemberView membership, ClubRecord? club, Profile? profile)
|
||||
{
|
||||
var role = membership.MembershipType;
|
||||
var created = ToUnix(membership.CreatedAt);
|
||||
var isOwner = role == ClubMembershipTypes.Creator;
|
||||
return new Dictionary<string, object?>
|
||||
{
|
||||
["AccountId"] = membership.AccountId,
|
||||
["ClubId"] = club?.clubId ?? 0L,
|
||||
["CreatedAt"] = membership.CreatedAt.ToString("O"),
|
||||
["MembershipType"] = role,
|
||||
["membershipRequestId"] = 0L,
|
||||
["inviterAccountId"] = membership.InviterAccountId,
|
||||
["subscriptionId"] = club?.clubId ?? 0L,
|
||||
["playerId"] = membership.AccountId,
|
||||
["username"] = profile?.username ?? string.Empty,
|
||||
["displayName"] = profile?.displayName ?? string.Empty,
|
||||
["level"] = 0,
|
||||
["role"] = role,
|
||||
["membershipStatus"] = role >= ActiveMemberThreshold || isOwner ? 1 : 0,
|
||||
["isOwner"] = isOwner,
|
||||
["created"] = created,
|
||||
["createdAt"] = created
|
||||
};
|
||||
}
|
||||
|
||||
public static object BuildMembership(ClubRecord? club, long viewerId = 0)
|
||||
{
|
||||
club ??= new ClubRecord { clubId = 0 };
|
||||
var role = GetMembershipType(club, viewerId);
|
||||
var isMember = role >= ActiveMemberThreshold;
|
||||
|
||||
return new Dictionary<string, object?>
|
||||
{
|
||||
["accountId"] = viewerId,
|
||||
["clubId"] = club.clubId,
|
||||
["creatorAccountId"] = club.ownerPlayerId,
|
||||
["membershipType"] = role,
|
||||
["LastAnnouncementId"] = 0L,
|
||||
["LastReadAnnouncementId"] = 0L,
|
||||
["announcements"] = Array.Empty<object>(),
|
||||
["subscriptionId"] = club.clubId
|
||||
};
|
||||
}
|
||||
|
||||
public static object BuildSubscriptionEnvelope(ClubRecord? club, long viewerId = 0) => new Dictionary<string, object?>
|
||||
{
|
||||
["success"] = club != null,
|
||||
["error"] = string.Empty,
|
||||
["value"] = club == null ? null : BuildMembership(club, viewerId)
|
||||
};
|
||||
|
||||
public static object BuildSearchResponse(object[] clubs, int skip, int take) => new Dictionary<string, object?>
|
||||
{
|
||||
["Clubs"] = clubs,
|
||||
["TotalClubs"] = clubs.Length,
|
||||
["ContinuationToken"] = null
|
||||
};
|
||||
|
||||
public static object BuildHome(ClubRecord? homeClub, long viewerId, IReadOnlyList<ClubRecord> allClubs, IReadOnlyList<ClubRecord> mine)
|
||||
{
|
||||
return BuildClub(homeClub, viewerId);
|
||||
}
|
||||
|
||||
public static object BuildEmptyHome(long viewerId)
|
||||
{
|
||||
return BuildClub(null, viewerId);
|
||||
}
|
||||
|
||||
public static object BuildModerationBlockDetails() => new Dictionary<string, object?>
|
||||
{
|
||||
["Duration"] = 0L,
|
||||
["GameSessionId"] = 0L,
|
||||
["PlayerIdReporter"] = 0L,
|
||||
["IsBan"] = false,
|
||||
["IsHostKick"] = false,
|
||||
["Message"] = string.Empty,
|
||||
["ReportCategory"] = 0,
|
||||
["VoteKickReason"] = string.Empty,
|
||||
["bannedUntil"] = string.Empty,
|
||||
["displayReason"] = string.Empty,
|
||||
["banType"] = string.Empty,
|
||||
["banAppealCode"] = string.Empty,
|
||||
["banAppealText"] = string.Empty
|
||||
};
|
||||
|
||||
public static object BuildEligibility() => new Dictionary<string, object?>
|
||||
{
|
||||
["canCreateClub"] = true,
|
||||
["canCreate"] = true,
|
||||
["creationAllowed"] = true,
|
||||
["isAllowed"] = true,
|
||||
["isCreationRestricted"] = false,
|
||||
["creationRestricted"] = false,
|
||||
["moderationRestricted"] = false,
|
||||
["moderationIncidentCount"] = 0,
|
||||
["myCreatedClubsCount"] = 0,
|
||||
["maxCreatedClubs"] = 100,
|
||||
["cooldownSeconds"] = 0,
|
||||
["restrictionReason"] = string.Empty
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user