Files
CannedNet/CannedNet/Services/Controllers/APIController.cs
T

1329 lines
50 KiB
C#

using CannedNet.Data;
using Microsoft.EntityFrameworkCore;
using System.Text.Json;
using CannedNet.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers;
[ApiController, Route("api/api")]
public class APIController : ControllerBase
{
[HttpGet("config/v1/amplitude")]
public async Task<IResult> Amplitude()
{
return Results.Ok(new
{
AmplitudeKey = "a",
StatSigKey = "a",
RudderStackKey = "a",
UseRudderStack = false
});
}
[HttpGet("config/v1/azurespeech")]
public async Task<IResult> AzureSpeech()
{
return Results.Ok(new
{
Key = "dce8de5b297747d9b5bddcc7f19e8c5b",
Region = "eastus",
Enabled = false
});
}
[HttpGet("config/v2")]
public async Task<IResult> ConfigV2() => Results.Content(await System.IO.File.ReadAllTextAsync("JSON/configv2.json"), "application/json");
[HttpGet("gameconfigs/v1/all")]
public async Task<IResult> GameConfigsV1All() => Results.Content(await System.IO.File.ReadAllTextAsync("JSON/gameconfigs.json"), "application/json");
[HttpGet("versioncheck/v4")]
public async Task<IResult> VersionCheckV4() => Results.Ok(new
{
VersionStatus = 0,
UpdateNotificationStage = 0,
IsVersionIslanded = false,
IsCrossPlayDisabled = false
});
[HttpGet("relationships/v2/get")]
public async Task<IResult> GetRelationShipsV2() => Results.Content("[]", "application/json");
[HttpGet("messages/v2/get")]
public async Task<IResult> GetMessagesV2() => Results.Content("[]", "application/json");
[HttpGet("playerReputation/v1/{id}")]
public async Task<IResult> GetPlayersV1Reputation(string id) => Results.Content($"{{\"AccountId\":{id},\"Noteriety\":0,\"CheerGeneral\":0,\"CheerHelpful\":0,\"CheerCreative\":0,\"CheerGreatHost\":0,\"CheerSportsman\":0,\"CheerCredit\":20,\"SelectedCheer\":null}}", "application/json");
[HttpGet("players/v1/progression/{id}")]
public async Task<IResult> GetPlayersV1Progression(string id) => Results.Content($"{{\"PlayerId\":{id},\"Level\":1,\"XP\":0}}", "application/json");
[HttpGet("playerReputation/v2/bulk")]
public async Task<IResult> PostPlayerReputationV2Bulk(AppDbContext db)
{
/*var ids = await ParseFormIds(httpRequest);
if (!ids.Any())
return Results.Json(new List<object>());
var reputations = ids.Select(id => new
{
AccountId = id,
Noteriety = 0,
CheerGeneral = 0,
CheerHelpful = 0,
CheerCreative = 0,
CheerGreatHost = 0,
CheerSportsman = 0,
CheerCredit = 20,
SelectedCheer = (object?)null
}).ToList();
return Results.Json(reputations);*/
//TODO: implement real endpoint from grabbing from db
string json = await System.IO.File.ReadAllTextAsync("JSON/bulkprogression.json");
return Results.Content(json, "application/json");
}
[HttpGet("players/v2/progression/bulk")]
public async Task<IResult> PostProgressionBulkV2(AppDbContext db)
{
List<int> ids = await ParseFormIds(HttpContext.Request);
if (!ids.Any())
return Results.Json(new List<PlayerProgressionBulkResponse>());
List<PlayerProgressionBulkResponse> progressions = await db.PlayerProgressions
.Where(p => ids.Contains(p.PlayerId))
.Select(p => new PlayerProgressionBulkResponse { PlayerId = p.PlayerId, Level = p.Level, Xp = p.Xp })
.ToListAsync();
return Results.Json(progressions);
}
[HttpPost("v1/progression/bulk")]
public async Task<IResult> PostProgressionBulkV1(AppDbContext db)
{
List<int> ids = await ParseFormIds(HttpContext.Request);
if (!ids.Any())
return Results.Json(new List<PlayerProgressionBulkResponse>());
List<PlayerProgressionBulkResponse> progressions = await db.PlayerProgressions
.Where(p => ids.Contains(p.PlayerId))
.Select(p => new PlayerProgressionBulkResponse { PlayerId = p.PlayerId, Level = p.Level, Xp = p.Xp })
.ToListAsync();
return Results.Json(progressions);
}
[HttpPost("avatar/v2/set")]
[Authorize]
public async Task<IResult> SetAvatarV2(AppDbContext db, JwtTokenService jwtService)
{
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
HttpContext.Request.EnableBuffering();
var avatarUpdate = await System.Text.Json.JsonSerializer.DeserializeAsync<PlayerAvatar>(HttpContext.Request.Body);
if (avatarUpdate == null)
return Results.BadRequest();
var avatar = await db.PlayerAvatars
.FirstOrDefaultAsync(a => a.OwnerAccountId == id);
if (avatar == null)
{
avatar = new PlayerAvatar { OwnerAccountId = id };
db.PlayerAvatars.Add(avatar);
}
avatar.OutfitSelections = avatarUpdate.OutfitSelections;
avatar.FaceFeatures = avatarUpdate.FaceFeatures;
avatar.SkinColor = avatarUpdate.SkinColor;
avatar.HairColor = avatarUpdate.HairColor;
await db.SaveChangesAsync();
return Results.Ok(avatar);
}
[HttpGet("PlayerReporting/v1/moderationBlockDetails")]
public IResult GetModerationBlockDetails()
{
return Results.Content("{\"ReportCategory\":0,\"Duration\":0,\"GameSessionId\":0,\"IsHostKick\":false,\"Message\":\"\",\"PlayerIdReporter\":null,\"IsBan\":false}", "application/json");
}
[HttpGet("PlayerReporting/v1/voteToKickReasons")]
public IResult GetVoteToKickReasons()
{
var json = System.IO.File.ReadAllText("JSON/vtkreasons.json");
return Results.Content(json, "application/json");
}
[HttpGet("settings/v2")]
[Authorize]
public async Task<IResult> GetSettings(AppDbContext db, JwtTokenService jwtService)
{
if (User == null || User.Identity == null)
return Results.NotFound();
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
var settings = await db.PlayerSettings
.Where(s => s.PlayerId == id)
.ToListAsync();
if (settings.Count == 0)
{
var defaults = new List<PlayerSetting>
{
new() { PlayerId = id, Key = "Recroom.OOBE", Value = "77" },
new() { PlayerId = id, Key = "SplitTestAssignedSegments", Value = "1|{\"SplitTesting+PhotonMaxDatagrams_2021_01_11\":\"Off\",\"SplitTesting+Curated_Rooms_2020_08_06\":\"Off\",\"SplitTesting+RoomRecommendationsType_2020_08_14\":\"Aug14MinVisitors35000\"}" },
new() { PlayerId = id, Key = "PlayerSessionCount", Value = "13" },
new() { PlayerId = id, Key = "TUTORIAL_COMPLETE_MASK", Value = "11" },
new() { PlayerId = id, Key = "BACKPACK_FAVORITE_TOOL", Value = "1" },
new() { PlayerId = id, Key = "VoiceChat", Value = "2" },
new() { PlayerId = id, Key = "VRAUTOSPRINT", Value = "1" },
new() { PlayerId = id, Key = "VR_MOVEMENT_MODE", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_SPRINT", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_WALK", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_VEHICLES", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FLY", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_ROTATE", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FORCES", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_FALL", Value = "0" },
new() { PlayerId = id, Key = "COMFORT_TELEPORT", Value = "0" },
new() { PlayerId = id, Key = "ROTATE_IN_PLACE_ENABLED", Value = "1" },
new() { PlayerId = id, Key = "ROTATION_INCREMENT", Value = "2" },
new() { PlayerId = id, Key = "CONTINUOUS_ROTATION_MODE", Value = "1" },
new() { PlayerId = id, Key = "DONT_LOCK_TOOLS_TO_HAND", Value = "0" },
new() { PlayerId = id, Key = "QualitySettings", Value = "2" },
new() { PlayerId = id, Key = "TeleportBuffer", Value = "0" },
new() { PlayerId = id, Key = "IgnoreBuffer", Value = "1" },
new() { PlayerId = id, Key = "FIRST_TIME_IN_FLAGS", Value = "0" },
new() { PlayerId = id, Key = "ShowRoomCenter", Value = "1" },
new() { PlayerId = id, Key = "USER_TRACKING", Value = "1" },
new() { PlayerId = id, Key = "STABILIZE_HANDS", Value = "0" },
new() { PlayerId = id, Key = "MakerPen_SnappingMode", Value = "2" },
new() { PlayerId = id, Key = "Recroom.ChallengeMap", Value = "17" },
new() { PlayerId = id, Key = "VoiceFilter2", Value = "1" },
new() { PlayerId = id, Key = "SFX_VOLUME_PERCENT_PREF", Value = "1" },
};
db.PlayerSettings.AddRange(defaults);
await db.SaveChangesAsync();
settings = defaults;
}
return Results.Json(settings);
}
[HttpGet("accounts/v1/getBio")]
[Authorize]
public async Task<IResult> GetAccountBio(AppDbContext db)
{
int accountId = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
var bio = await db.PlayerBios
.Where(s => accountId == accountId)
.ToListAsync();
return Results.Json(bio);
}
[HttpGet("communityboard/v2/current")]
public async Task<IResult> CommunityBoardCurrent()
{
var json = System.IO.File.ReadAllText("JSON/communityboard.json");
return Results.Content(json, "application/json");
}
[HttpGet("playerevents/v1/all")]
public async Task<IResult> PlayerEventsAll()
{
return Results.Content("{\"Created\":[],\"Responses\":[]}", "application/json");
}
[HttpGet("storefronts/v1/p2p/betaEnabled")]
public IResult StorefrontsP2PBetaEnabled()
{
return Results.Content("false", "application/json");
}
[HttpGet("announcement/v1/get")]
public async Task<IResult> AnnouncementGet()
{
var json = System.IO.File.ReadAllText("JSON/announcements.json");
return Results.Content(json, "application/json");
}
[HttpGet("quickPlay/v1/getandclear")]
public async Task<IResult> QuickPlayGetAndClear()
{
return Results.Content("{\"RoomName\":null,\"ActionCode\":null,\"TargetPlayerId\":null}", "application/json");
}
[HttpGet("roomkeys/v1/room")]
public async Task<IResult> RoomKeysRoom()
{
var roomid = Request.Query["roomId"];
return Results.Content("[]", "application/json");
}
[HttpGet("/roomserver/rooms/bulk")]
public async Task<IResult> RoomserverRoomsBulk(AppDbContext db)
{
var idParam = Request.Query["id"].FirstOrDefault();
var nameParam = Request.Query["name"].FirstOrDefault();
if (string.IsNullOrEmpty(idParam) && string.IsNullOrEmpty(nameParam))
return Results.BadRequest("Either 'id' or 'name' query parameter is required");
List<Room> results = new();
if (!string.IsNullOrEmpty(idParam))
{
var ids = idParam.Split(',').Select(s => int.TryParse(s.Trim(), out var i) ? i : -1).Where(i => i != -1);
results.AddRange(db.Rooms.Where(r => ids.Contains(r.RoomId)).ToList());
}
if (!string.IsNullOrEmpty(nameParam))
{
var names = nameParam.Split(',').Select(s => s.Trim().ToLower()).Where(s => !string.IsNullOrEmpty(s));
results.AddRange(db.Rooms.Where(r => names.Any(n => r.Name.ToLower().Contains(n))).ToList());
}
var roomIds = results.Select(r => r.RoomId).ToList();
if (roomIds.Count == 0)
return Results.Json(new object[0]);
var subRooms = db.SubRooms.Where(s => roomIds.Contains(s.RoomId)).ToList();
var roles = db.RoomRoles.Where(r => roomIds.Contains(r.RoomId)).ToList();
var loadScreens = db.LoadScreens.Where(l => roomIds.Contains(l.RoomId)).ToList();
var promoImages = db.PromoImages.Where(p => roomIds.Contains(p.RoomId)).ToList();
var promoExternalContents = db.PromoExternalContents.Where(p => roomIds.Contains(p.RoomId)).ToList();
var response = results.Distinct().Select(r => new
{
r.RoomId,
r.Name,
r.Description,
r.CreatorAccountId,
r.ImageName,
r.State,
r.Accessibility,
r.SupportsLevelVoting,
r.IsRRO,
r.IsDorm,
r.CloningAllowed,
r.SupportsVRLow,
r.SupportsQuest2,
r.SupportsMobile,
r.SupportsScreens,
r.SupportsWalkVR,
r.SupportsTeleportVR,
r.SupportsJuniors,
r.MinLevel,
r.WarningMask,
r.CustomWarning,
r.DisableMicAutoMute,
r.DisableRoomComments,
r.EncryptVoiceChat,
r.CreatedAt,
Stats = new { CheerCount = 0, FavoriteCount = 0, VisitorCount = 0, VisitCount = 0 },
SubRooms = subRooms.Where(s => s.RoomId == r.RoomId).Select(s => new
{
s.SubRoomId,
s.Name,
s.DataBlob,
s.IsSandbox,
s.MaxPlayers,
s.Accessibility,
s.UnitySceneId,
DataSavedAt = s.DataSavedAt
}).ToList(),
Roles = roles.Where(ro => ro.RoomId == r.RoomId).Select(ro => new
{
ro.AccountId,
ro.Role,
ro.InvitedRole
}).ToList(),
LoadScreens = loadScreens.Where(l => l.RoomId == r.RoomId).Select(l => new
{
l.ImageUrl,
l.Tooltip,
l.IsThumbnail
}).ToList(),
PromoImages = promoImages.Where(p => p.RoomId == r.RoomId).Select(p => new
{
p.ImageUrl,
p.Tooltip,
p.SortOrder
}).ToList(),
PromoExternalContent = promoExternalContents.Where(p => p.RoomId == r.RoomId).Select(p => new
{
p.Type,
p.Url,
p.Tooltip
}).ToList(),
Tags = new object[0]
}).ToList();
return Results.Json(response);
}
[HttpGet("/roomserver/rooms/hot")]
public async Task<IResult> RoomserverRoomsHot(AppDbContext db)
{
var tagFilter = Request.Query["tag"].FirstOrDefault()?.ToLower();
var allRooms = await db.Rooms.ToListAsync();
if (!string.IsNullOrEmpty(tagFilter))
{
allRooms = allRooms.Where(r =>
{
var roomTags = TryDeserializeRoomTags(r.Tags);
if (roomTags == null || roomTags.Length == 0) return false;
return roomTags.Any(t => t.Tag.Equals(tagFilter, StringComparison.OrdinalIgnoreCase));
}).ToList();
}
var hotRooms = allRooms.OrderByDescending(r => r.Id).ToList();
var results = new List<object>();
foreach (var room in hotRooms)
{
var subRooms = await db.SubRooms.Where(x => x.RoomId == room.RoomId).ToListAsync();
var roles = await db.RoomRoles.Where(x => x.RoomId == room.RoomId).ToListAsync();
var loadScreens = await db.LoadScreens.Where(x => x.RoomId == room.RoomId).ToListAsync();
var promoImages = await db.PromoImages.Where(x => x.RoomId == room.RoomId).ToListAsync();
var external = await db.PromoExternalContents.Where(x => x.RoomId == room.RoomId).ToListAsync();
var tags = TryDeserializeTags(room.Tags) ?? new object[0];
results.Add(new
{
room.RoomId,
room.Name,
room.Description,
room.CreatorAccountId,
room.ImageName,
room.State,
room.Accessibility,
room.SupportsLevelVoting,
room.IsRRO,
room.IsDorm,
room.CloningAllowed,
room.SupportsVRLow,
room.SupportsQuest2,
room.SupportsMobile,
room.SupportsScreens,
room.SupportsWalkVR,
room.SupportsTeleportVR,
room.SupportsJuniors,
room.MinLevel,
room.WarningMask,
room.CustomWarning,
room.DisableMicAutoMute,
room.DisableRoomComments,
room.EncryptVoiceChat,
room.CreatedAt,
Stats = (object?)null,
SubRooms = subRooms.Select(s => new { s.SubRoomId, s.RoomId, s.Name, s.DataBlob, s.IsSandbox, s.MaxPlayers, s.Accessibility, s.UnitySceneId, s.DataSavedAt }),
Roles = roles.Select(r => new { r.AccountId, r.Role, r.InvitedRole }),
LoadScreens = loadScreens.Select(ls => new { ls.ImageUrl, ls.Tooltip, ls.IsThumbnail }),
PromoImages = promoImages.Select(pi => new { pi.ImageUrl, pi.Tooltip, pi.SortOrder }),
PromoExternalContent = external.Select(ec => new { ec.Type, ec.Url, ec.Tooltip }),
Tags = tags
});
}
return Results.Json(new { Results = results, TotalResults = results.Count });
}
[HttpGet("/roomserver/roomsandplaylists/hot")]
public async Task<IResult> RoomserverRoomsAndPlaylistsHot(AppDbContext db)
{
var allRooms = await db.Rooms.Where(r => !r.IsDorm).OrderByDescending(r => r.Id).ToListAsync();
var results = new List<object>();
foreach (var room in allRooms)
{
var subRooms = await db.SubRooms.Where(x => x.RoomId == room.RoomId).ToListAsync();
var roles = await db.RoomRoles.Where(x => x.RoomId == room.RoomId).ToListAsync();
var loadScreens = await db.LoadScreens.Where(x => x.RoomId == room.RoomId).ToListAsync();
var promoImages = await db.PromoImages.Where(x => x.RoomId == room.RoomId).ToListAsync();
var external = await db.PromoExternalContents.Where(x => x.RoomId == room.RoomId).ToListAsync();
var tags = TryDeserializeTags(room.Tags) ?? new object[0];
results.Add(new
{
room.RoomId,
room.Name,
room.Description,
room.CreatorAccountId,
room.ImageName,
room.State,
room.Accessibility,
room.SupportsLevelVoting,
room.IsRRO,
room.IsDorm,
room.CloningAllowed,
room.SupportsVRLow,
room.SupportsQuest2,
room.SupportsMobile,
room.SupportsScreens,
room.SupportsWalkVR,
room.SupportsTeleportVR,
room.SupportsJuniors,
room.MinLevel,
room.WarningMask,
room.CustomWarning,
room.DisableMicAutoMute,
room.DisableRoomComments,
room.EncryptVoiceChat,
room.CreatedAt,
Stats = (object?)null,
SubRooms = subRooms.Select(s => new { s.SubRoomId, s.RoomId, s.Name, s.DataBlob, s.IsSandbox, s.MaxPlayers, s.Accessibility, s.UnitySceneId, s.DataSavedAt }),
Roles = roles.Select(r => new { r.AccountId, r.Role, r.InvitedRole }),
LoadScreens = loadScreens.Select(ls => new { ls.ImageUrl, ls.Tooltip, ls.IsThumbnail }),
PromoImages = promoImages.Select(pi => new { pi.ImageUrl, pi.Tooltip, pi.SortOrder }),
PromoExternalContent = external.Select(ec => new { ec.Type, ec.Url, ec.Tooltip }),
Tags = tags
});
}
return Results.Json(new { Results = results, TotalResults = results.Count });
}
[HttpGet("/roomserver/rooms/{id}")]
public async Task<IResult> RoomserverRoomsId(string id, AppDbContext db)
{
if (!int.TryParse(id, out var roomId))
return Results.NotFound();
var room = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == roomId);
if (room == null)
return Results.NotFound();
var subRooms = await db.SubRooms.Where(x => x.RoomId == room.RoomId).ToListAsync();
var roles = await db.RoomRoles.Where(x => x.RoomId == room.RoomId).ToListAsync();
var loadScreens = await db.LoadScreens.Where(x => x.RoomId == room.RoomId).ToListAsync();
var promoImages = await db.PromoImages.Where(x => x.RoomId == room.RoomId).ToListAsync();
var external = await db.PromoExternalContents.Where(x => x.RoomId == room.RoomId).ToListAsync();
var tags = TryDeserializeTags(room.Tags) ?? new object[0];
return Results.Json(new
{
room.RoomId,
room.Name,
room.Description,
room.CreatorAccountId,
room.ImageName,
room.State,
room.Accessibility,
room.SupportsLevelVoting,
room.IsRRO,
room.IsDorm,
room.CloningAllowed,
room.SupportsVRLow,
room.SupportsQuest2,
room.SupportsMobile,
room.SupportsScreens,
room.SupportsWalkVR,
room.SupportsTeleportVR,
room.SupportsJuniors,
room.MinLevel,
room.WarningMask,
room.CustomWarning,
room.DisableMicAutoMute,
room.DisableRoomComments,
room.EncryptVoiceChat,
room.CreatedAt,
Stats = new RoomStats { CheerCount = 0, FavoriteCount = 0, VisitorCount = 1, VisitCount = 1 },
SubRooms = subRooms.Select(s => new { s.SubRoomId, s.RoomId, s.Name, s.DataBlob, s.IsSandbox, s.MaxPlayers, s.Accessibility, s.UnitySceneId, s.DataSavedAt }).ToList(),
Roles = roles.Select(r => new { r.AccountId, r.Role, r.InvitedRole }).ToList(),
LoadScreens = loadScreens.Select(l => new { l.ImageUrl, l.Tooltip, l.IsThumbnail }).ToList(),
PromoImages = promoImages.Select(p => new { p.ImageUrl, p.Tooltip, p.SortOrder }).ToList(),
PromoExternalContent = external.Select(e => new { e.Type, e.Url, e.Tooltip }).ToList(),
Tags = tags
});
}
[HttpGet("images/v2/named")]
public async Task<IResult> NamedImages()
{
var json = System.IO.File.ReadAllText("JSON/namedimages.json");
return Results.Content(json, "application/json");
}
[HttpPost("objectives/v1/updateobjective")]
public async Task<IResult> UpdateObjective()
{
return Results.Ok();
}
[HttpPost("PlayerReporting/v1/hile")]
public async Task<IResult> PlayerReportingHile()
{
return Results.Ok();
}
[HttpGet("storefronts/v3/giftdropstore/300")]
public async Task<IResult> GiftDropStore300(StorefrontFillService storefrontService)
{
var storefronts = await storefrontService.GetStorefrontsAsync();
var storefront = storefronts.FirstOrDefault(s => s.StorefrontType == 300 && s.Name == "rc_cafe_storefront");
if (storefront == null)
{
var json = System.IO.File.ReadAllText("JSON/cafestorefront.json");
return Results.Content(json, "application/json");
}
var storeItems = storefront.Items.Select(item => new
{
item.Id,
item.StorefrontId,
item.PurchasableItemId,
item.Type,
item.IsFeatured,
item.NewUntil,
GiftDrops = item.GiftDrops.Select(gd => new
{
gd.Id,
gd.StorefrontItemId,
gd.GiftDropId,
gd.FriendlyName,
gd.Tooltip,
gd.ConsumableItemDesc,
gd.AvatarItemDesc,
gd.AvatarItemType,
gd.EquipmentPrefabName,
gd.EquipmentModificationGuid,
gd.IsQuery,
gd.Unique,
gd.SubscribersOnly,
gd.Level,
gd.Rarity,
gd.CurrencyType,
gd.Currency,
gd.Context,
gd.ItemSetId,
gd.ItemSetFriendlyName
}).ToList(),
Prices = item.Prices.Select(p => new { p.Id, p.StorefrontItemId, p.CurrencyType, p.Price }).ToList()
}).ToList();
return Results.Ok(new { storefront.Id, storefront.Name, storefront.StorefrontType, storefront.NextUpdate, StoreItems = storeItems });
}
[HttpGet("storefronts/v3/giftdropstore/2")]
public async Task<IResult> GiftDropStore2(StorefrontFillService storefrontService)
{
var storefronts = await storefrontService.GetStorefrontsAsync();
var storefront = storefronts.FirstOrDefault(s => s.StorefrontType == 2 && s.Name == "rec_center_store");
if (storefront == null)
{
var json = System.IO.File.ReadAllText("JSON/storefront12.json");
return Results.Content(json, "application/json");
}
var storeItems = storefront.Items.Select(item => new
{
item.Id,
item.StorefrontId,
item.PurchasableItemId,
item.Type,
item.IsFeatured,
item.NewUntil,
GiftDrops = item.GiftDrops.Select(gd => new
{
gd.Id,
gd.StorefrontItemId,
gd.GiftDropId,
gd.FriendlyName,
gd.Tooltip,
gd.ConsumableItemDesc,
gd.AvatarItemDesc,
gd.AvatarItemType,
gd.EquipmentPrefabName,
gd.EquipmentModificationGuid,
gd.IsQuery,
gd.Unique,
gd.SubscribersOnly,
gd.Level,
gd.Rarity,
gd.CurrencyType,
gd.Currency,
gd.Context,
gd.ItemSetId,
gd.ItemSetFriendlyName
}).ToList(),
Prices = item.Prices.Select(p => new { p.Id, p.StorefrontItemId, p.CurrencyType, p.Price }).ToList()
}).ToList();
return Results.Ok(new { storefront.Id, storefront.Name, storefront.StorefrontType, storefront.NextUpdate, StoreItems = storeItems });
}
[HttpPost("storefronts/v2/buyItem")]
[Authorize]
public async Task<IResult> BuyItem(BuyItemRequest buyRequest, AppDbContext db, NotificationService notificationService)
{
try
{
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
var storefrontItem = await db.StorefrontItems
.Include(si => si.Prices)
.Include(si => si.GiftDrops)
.FirstOrDefaultAsync(si => si.PurchasableItemId == buyRequest.PurchasableItemId);
if (storefrontItem == null)
return Results.NotFound(new { error = "Item not found" });
var price = storefrontItem.Prices.FirstOrDefault(p => p.CurrencyType == buyRequest.CurrencyType);
if (price == null)
return Results.BadRequest(new { error = "Currency type not available for this item" });
var tokenBalance = await db.TokenBalances
.FirstOrDefaultAsync(tb => tb.Id == id && tb.CurrencyType == buyRequest.CurrencyType && tb.BalanceType == -1);
if (tokenBalance == null)
return Results.BadRequest(new { error = "Account has no token balance" });
if (tokenBalance.Balance < price.Price)
return Results.BadRequest(new { error = "Insufficient balance" });
tokenBalance.Balance -= price.Price;
db.TokenBalances.Update(tokenBalance);
await db.SaveChangesAsync();
var receiverAccountId = id;
if (buyRequest.Gift != null)
{
receiverAccountId = buyRequest.Gift.ToPlayerId;
}
var storedGifts = new List<ReceivedGift>();
foreach (var giftDrop in storefrontItem.GiftDrops)
{
var receivedGift = new ReceivedGift
{
ReceiverAccountId = receiverAccountId,
FromPlayerId = 1,
Message = buyRequest.Gift?.Message ?? "A gift for you <3",
ConsumableItemDesc = giftDrop.ConsumableItemDesc ?? string.Empty,
AvatarItemDesc = giftDrop.AvatarItemDesc ?? string.Empty,
AvatarItemType = giftDrop.AvatarItemType,
EquipmentPrefabName = giftDrop.EquipmentPrefabName ?? string.Empty,
EquipmentModificationGuid = giftDrop.EquipmentModificationGuid ?? string.Empty,
CurrencyType = giftDrop.CurrencyType,
Currency = giftDrop.Currency,
Xp = 0,
Level = giftDrop.Level,
Platform = -1,
PlatformsToSpawnOn = -1,
BalanceType = 0,
GiftContext = buyRequest.Gift?.GiftContext ?? giftDrop.Context,
GiftRarity = giftDrop.Rarity,
ReceivedAt = DateTime.UtcNow,
IsConsumed = false
};
db.ReceivedGifts.Add(receivedGift);
storedGifts.Add(receivedGift);
}
await db.SaveChangesAsync();
var giftData = storedGifts.Select(rg => new GiftData
{
Id = rg.Id,
FromPlayerId = null,
ConsumableItemDesc = rg.ConsumableItemDesc,
AvatarItemDesc = rg.AvatarItemDesc,
FriendlyName = rg.FriendlyName,
AvatarItemType = rg.AvatarItemType,
EquipmentPrefabName = rg.EquipmentPrefabName,
EquipmentModificationGuid = rg.EquipmentModificationGuid,
CurrencyType = rg.CurrencyType,
Currency = rg.Currency,
Xp = rg.Xp,
Level = rg.Level,
Platform = rg.Platform,
PlatformsToSpawnOn = rg.PlatformsToSpawnOn,
BalanceType = rg.BalanceType,
GiftContext = rg.GiftContext,
GiftRarity = rg.GiftRarity,
Message = rg.Message
}).ToList();
var balanceUpdate = new BalanceUpdate { UpdateResponse = 0, Data = giftData };
var response = new BuyItemResponse
{
BalanceUpdates = new List<BalanceUpdate> { balanceUpdate },
Balance = tokenBalance.Balance,
CurrencyType = buyRequest.CurrencyType,
BalanceType = -1,
Platform = -1
};
try
{
var purchaseNotification = notificationService.CreateNotification(
PushNotificationId.StorefrontBalancePurchase, id: storefrontItem.Id, toAccountId: id,
data: new Dictionary<string, object>
{
{ "BalanceAddType", 1400 }, { "Delta", -price.Price },
{ "Balance", tokenBalance.Balance }, { "CurrencyType", buyRequest.CurrencyType }
});
await notificationService.SendNotificationToPlayer(id, purchaseNotification);
}
catch (Exception notifEx)
{
Console.WriteLine($"Error sending purchase notification: {notifEx.Message}");
}
return Results.Ok(response);
}
catch (Exception ex)
{
return Results.Problem($"Error processing purchase: {ex.Message}");
}
}
[HttpPost("avatar/v2/gifts/generate")]
[Authorize]
public async Task<IResult> AvatarGiftsGenerate(AppDbContext db)
{
try
{
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
Request.EnableBuffering();
Request.Body.Position = 0;
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
int giftContext = 0;
bool isGameGift = false;
string message = "";
int xp = 0;
if (!string.IsNullOrWhiteSpace(body))
{
foreach (var pair in body.Split('&'))
{
var keyValue = pair.Split('=');
if (keyValue.Length == 2)
{
var key = Uri.UnescapeDataString(keyValue[0]);
var value = Uri.UnescapeDataString(keyValue[1]);
if (key == "GiftContext" && int.TryParse(value, out var context))
giftContext = context;
else if (key == "IsGameGift")
isGameGift = value.Equals("true", StringComparison.OrdinalIgnoreCase);
else if (key == "Message")
message = value;
else if (key == "Xp" && int.TryParse(value, out var xpVal))
xp = xpVal;
}
}
}
var earnableRewards = await db.EarnableRewards
.Where(er => er.RewardContext == giftContext)
.ToListAsync();
var random = new Random();
string avatarItemDesc = "";
string friendlyName = "";
int avatarItemType = 0;
int currencyType = 0;
int currency = 0;
int giftRarity = 0;
string consumableItemDesc = "";
bool isEarnableItem = random.Next(0, 100) < 60;
if (isEarnableItem && earnableRewards.Any())
{
var avatarRewards = earnableRewards.Where(er => !string.IsNullOrEmpty(er.AvatarItemDesc)).ToList();
if (avatarRewards.Any())
{
var ownedAvatarItems = await db.AvatarItems
.Where(ai => ai.OwnerAccountId == id)
.Select(ai => ai.AvatarItemDesc)
.ToListAsync();
var unownedRewards = avatarRewards.Where(ar => !ownedAvatarItems.Contains(ar.AvatarItemDesc)).ToList();
if (unownedRewards.Any())
{
var selectedReward = unownedRewards[random.Next(unownedRewards.Count)];
avatarItemDesc = selectedReward.AvatarItemDesc;
friendlyName = selectedReward.FriendlyName;
avatarItemType = selectedReward.AvatarItemType;
giftRarity = selectedReward.GiftRarity;
}
else { isEarnableItem = false; }
}
else { isEarnableItem = false; }
}
if (!isEarnableItem)
{
int[] tokenAmounts = { 10, 25, 50, 100, 250, 500 };
currency = tokenAmounts[random.Next(tokenAmounts.Length)];
currencyType = 2;
giftRarity = 20;
}
var receivedGift = new ReceivedGift
{
ReceiverAccountId = id,
FromPlayerId = 1,
Message = message,
ConsumableItemDesc = consumableItemDesc,
AvatarItemDesc = avatarItemDesc,
FriendlyName = friendlyName,
AvatarItemType = avatarItemType,
EquipmentPrefabName = "",
EquipmentModificationGuid = "",
CurrencyType = currencyType,
Currency = currency,
Xp = xp,
Level = 0,
Platform = -1,
PlatformsToSpawnOn = -1,
BalanceType = 0,
GiftContext = giftContext,
GiftRarity = giftRarity,
ReceivedAt = DateTime.UtcNow,
IsConsumed = false
};
db.ReceivedGifts.Add(receivedGift);
await db.SaveChangesAsync();
return Results.Ok(new
{
Id = receivedGift.Id,
FromPlayerId = 1,
ConsumableItemDesc = consumableItemDesc,
AvatarItemDesc = avatarItemDesc,
FriendlyName = friendlyName,
AvatarItemType = avatarItemType,
EquipmentPrefabName = "",
EquipmentModificationGuid = "",
CurrencyType = currencyType,
Currency = currency,
Xp = xp,
Level = 0,
Platform = -1,
PlatformsToSpawnOn = -1,
BalanceType = 0,
GiftContext = giftContext,
GiftRarity = giftRarity,
Message = message
});
}
catch (Exception ex)
{
return Results.Problem($"Error generating gift: {ex.Message}");
}
}
[HttpPost("avatar/v2/gifts/consume/")]
[Authorize]
public async Task<IResult> AvatarGiftsConsume(AppDbContext db)
{
try
{
int id = User.Identity.Name != null && int.TryParse(User.Identity.Name, out var parsedId) ? parsedId : -1;
Request.EnableBuffering();
Request.Body.Position = 0;
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
var giftId = 0;
var unlockedLevel = 0;
if (!string.IsNullOrWhiteSpace(body))
{
foreach (var pair in body.Split('&'))
{
var keyValue = pair.Split('=');
if (keyValue.Length == 2)
{
if (keyValue[0] == "Id")
int.TryParse(Uri.UnescapeDataString(keyValue[1]), out giftId);
else if (keyValue[0] == "UnlockedLevel")
int.TryParse(Uri.UnescapeDataString(keyValue[1]), out unlockedLevel);
}
}
}
if (giftId == 0)
return Results.BadRequest(new { success = false, error = "Invalid gift ID" });
var receivedGift = await db.ReceivedGifts
.FirstOrDefaultAsync(rg => rg.Id == giftId && rg.ReceiverAccountId == id);
if (receivedGift == null)
return Results.NotFound(new { success = false, error = "Gift not found" });
if (!string.IsNullOrEmpty(receivedGift.ConsumableItemDesc))
{
var existingConsumable = await db.ConsumableItems
.FirstOrDefaultAsync(c => c.OwnerAccountId == id && c.ConsumableItemDesc == receivedGift.ConsumableItemDesc);
if (existingConsumable == null)
{
db.ConsumableItems.Add(new ConsumableItem
{
OwnerAccountId = id,
Ids = new List<int> { giftId },
CreatedAts = new List<DateTime> { DateTime.UtcNow },
ConsumableItemDesc = receivedGift.ConsumableItemDesc,
Count = 1,
InitialCount = 1,
IsActive = false,
ActiveDurationMinutes = 0,
IsTransferable = false
});
}
else
{
existingConsumable.Ids.Add(giftId);
existingConsumable.CreatedAts.Add(DateTime.UtcNow);
existingConsumable.Count += 1;
db.ConsumableItems.Update(existingConsumable);
}
}
if (!string.IsNullOrEmpty(receivedGift.AvatarItemDesc))
{
db.AvatarItems.Add(new AvatarItem
{
OwnerAccountId = id,
AvatarItemDesc = receivedGift.AvatarItemDesc,
FriendlyName = receivedGift.FriendlyName ?? ""
});
}
if (!string.IsNullOrEmpty(receivedGift.Currency.ToString()) && receivedGift.CurrencyType == 2)
{
var tokenBalance = await db.TokenBalances
.FirstOrDefaultAsync(tb => tb.Id == id && tb.CurrencyType == receivedGift.CurrencyType && tb.BalanceType == -1);
if (tokenBalance == null)
{
db.TokenBalances.Add(new TokenBalance
{
Id = id,
CurrencyType = receivedGift.CurrencyType,
BalanceType = -1,
Balance = receivedGift.Currency
});
}
else
{
tokenBalance.Balance += receivedGift.Currency;
db.TokenBalances.Update(tokenBalance);
}
}
receivedGift.IsConsumed = true;
receivedGift.ConsumedAt = DateTime.UtcNow;
db.ReceivedGifts.Update(receivedGift);
await db.SaveChangesAsync();
return Results.Ok(RecNetResult.Ok());
}
catch (Exception ex)
{
return Results.Problem($"Error consuming gift: {ex.Message}");
}
}
[HttpGet("rooms/v1/filters")]
public async Task<IResult> RoomsFilters()
{
var json = System.IO.File.ReadAllText("JSON/roomfilters.json");
return Results.Content(json, "application/json");
}
[HttpGet("/roomserver/rooms/{id}/interactionby/me")]
public async Task<IResult> RoomInteractionByMe(string id)
{
return Results.Content("{\"Cheered\":false,\"Favorited\":false}", "application/json");
}
[HttpPost("images/v4/uploadsaved")]
public async Task<IResult> ImageUploadSaved()
{
try
{
var form = await HttpContext.Request.ReadFormAsync();
var file = form.Files.FirstOrDefault();
if (file == null)
return Results.BadRequest(new { error = "No file found in request" });
var imageId = Guid.NewGuid().ToString("N");
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
var validExtensions = new[] { ".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp" };
if (string.IsNullOrEmpty(extension) || !validExtensions.Contains(extension))
extension = ".png";
var savedFileName = imageId + extension;
var filePath = Path.Combine(ImagesDir, savedFileName);
using (var fileStream = System.IO.File.Create(filePath))
await file.CopyToAsync(fileStream);
return Results.Ok(new { ImageName = savedFileName });
}
catch (Exception ex)
{
return Results.Problem($"Error uploading image: {ex.Message}");
}
}
[HttpGet("config/v1/backtrace")]
public IResult BacktraceConfig()
{
return Results.Ok(new
{
ReportBudget = 125,
FilterType = 0,
SampleRate = 0.025,
LogLineCount = 50,
CaptureNativeCrashes = 1,
AMRThresholdMS = 0,
MessageCount = 1000,
MessageRegex = "^Cannot set the parent of the GameObject .* while its new parent|^\\\\>\\\\x2010x\\\\:\\\\x20|\\\\'LabelTheme\\\\' contains missing PaletteTheme reference on",
VersionRegex = ".*"
});
}
[HttpPost("settings/v2/set")]
[Authorize]
public async Task<IResult> SetSettings(AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
HttpContext.Request.EnableBuffering();
HttpContext.Request.Body.Position = 0;
using var reader = new StreamReader(HttpContext.Request.Body);
var body = await reader.ReadToEndAsync();
Console.WriteLine($"Settings request body: {body}");
var settings = new List<PlayerSetting>();
if (body.TrimStart().StartsWith("["))
{
settings = System.Text.Json.JsonSerializer.Deserialize<List<PlayerSetting>>(body) ?? [];
}
else
{
var single = System.Text.Json.JsonSerializer.Deserialize<PlayerSetting>(body);
if (single != null) settings.Add(single);
}
settings = settings.Where(s => !string.IsNullOrEmpty(s.Key)).ToList();
if (settings.Count == 0)
return Results.Ok();
db.PlayerSettings.RemoveRange(db.PlayerSettings.Where(s => s.PlayerId == id));
foreach (var setting in settings)
{
setting.PlayerId = id;
setting.Key = setting.Key ?? "";
setting.Value = setting.Value ?? "";
db.PlayerSettings.Add(setting);
}
await db.SaveChangesAsync();
return Results.Ok();
}
[HttpGet("equipment/v2/getUnlocked")]
public async Task<IResult> GetEquipmentUnlocked()
{
return Results.Content("[]", "application/json");
}
[HttpGet("consumables/v2/getUnlocked")]
[Authorize]
public async Task<IResult> GetConsumablesUnlocked(AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var consumables = await db.ConsumableItems
.Where(c => c.OwnerAccountId == id)
.ToListAsync();
return Results.Json(consumables);
}
[HttpGet("storefronts/v4/balance/2")]
[Authorize]
public async Task<IResult> GetStorefrontBalance(AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var balance = await db.TokenBalances
.Where(s => s.Id == id)
.ToListAsync();
return Results.Json(balance);
}
[HttpGet("challenge/v2/getCurrent")]
public async Task<IResult> GetCurrentChallenge()
{
var json = System.IO.File.ReadAllText("JSON/weeklychallenge.json");
return Results.Content(json, "application/json");
}
[HttpGet("avatar/v3/saved")]
[Authorize]
public async Task<IResult> GetSavedAvatars(AppDbContext db)
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var items = await db.SavedOutfits
.Where(i => i.OwnerAccountId == id)
.ToListAsync();
return Results.Json(items);
}
[HttpGet("avatar/v2/gifts")]
[Authorize]
public async Task<IResult> GetGifts(AppDbContext db)
{
try
{
if (!int.TryParse(User.Identity?.Name, out var id))
return Results.Unauthorized();
var pendingGifts = await db.ReceivedGifts
.Where(rg => rg.ReceiverAccountId == id && !rg.IsConsumed)
.ToListAsync();
return Results.Json(pendingGifts);
}
catch (Exception ex)
{
return Results.Problem($"Error retrieving gifts: {ex.Message}");
}
}
[HttpGet("gamerewards/v1/pending")]
public async Task<IResult> GetGameRewardsPending()
{
return Results.Content("[]", "application/json");
}
[HttpGet("roomkeys/v1/mine")]
public async Task<IResult> GetRoomKeysMine()
{
return Results.Content("[]", "application/json");
}
[HttpPost("CampusCard/v1/UpdateAndGetSubscription")]
public async Task<IResult> UpdateAndGetSubscription()
{
return Results.Json(new { subscription = (object?)null, platformAccountSubscribedPlayerId = (object?)null });
}
[Authorize]
[HttpGet("customAvatarItems/v1/isCreationAllowedForAccount")]
public async Task<IResult> IsCustomAvatarItemsCreationAllowedForAccount()
{
return Results.Json(new { isAllowed = true });
}
private static object[]? TryDeserializeTags(string json)
{
try
{
var tags = System.Text.Json.JsonSerializer.Deserialize<RoomTag[]>(json);
if (tags != null)
return tags.Cast<object>().ToArray();
return null;
}
catch
{
return null;
}
}
private static RoomTag[]? TryDeserializeRoomTags(string json)
{
try
{
return System.Text.Json.JsonSerializer.Deserialize<RoomTag[]>(json);
}
catch
{
return null;
}
}
private static async Task<List<int>> ParseFormIds(HttpRequest httpRequest)
{
var ids = new List<int>();
if (httpRequest.ContentLength.HasValue && httpRequest.ContentLength > 0)
{
httpRequest.EnableBuffering();
using var reader = new StreamReader(httpRequest.Body, leaveOpen: true);
var body = await reader.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(body))
{
foreach (var pair in body.Split('&'))
{
var keyValue = pair.Split('=');
if (keyValue.Length == 2 && keyValue[0] == "Ids")
{
var idString = Uri.UnescapeDataString(keyValue[1]);
foreach (var id in idString.Split(','))
if (int.TryParse(id, out var parsedId))
ids.Add(parsedId);
break;
}
}
}
httpRequest.Body.Position = 0;
}
return ids;
}
private static readonly string ImagesDir;
static APIController()
{
var dir = "Images";
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
ImagesDir = dir;
}
}