Files
2026-10-16 23:33:13 -07:00

783 lines
26 KiB
C#

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
};
}