mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
feat: Enhance API endpoints with new methods, improved response structures, and added Photon access token endpoint
This commit is contained in:
@@ -60,16 +60,34 @@ public class CompatibilityFeatureController(DorkNetDbContext db) : ControllerBas
|
||||
.ExecuteDeleteAsync();
|
||||
}
|
||||
|
||||
// 2020.12 deserializer for this response expects `subscription` to be a
|
||||
// nested OBJECT (Subscription DTO with subscriptionId/recNetPlayerId/...
|
||||
// fields), NOT a boolean. Sending a bool throws InvalidCastException
|
||||
// on the client ("Unable to cast object of type 'Boolean' to type
|
||||
// 'Dictionary`2'"). Send null when there's no active subscription —
|
||||
// the client's generic-cast helper returns default(T) which is fine.
|
||||
return Ok(new
|
||||
var active = targetId == pid || await db.Subscriptions.AnyAsync(s =>
|
||||
s.SubscriberPlayerId == pid && s.TargetPlayerId == targetId);
|
||||
var now = DateTime.UtcNow;
|
||||
var subscriptionId = unchecked((pid * 397L) ^ targetId);
|
||||
var subscription = new Dictionary<string, object?>
|
||||
{
|
||||
subscription = (object?)null,
|
||||
platformAccountSubscribedPlayerId = targetId,
|
||||
["subscriptionId"] = subscriptionId,
|
||||
["recNetPlayerId"] = pid,
|
||||
["platformAccountSubscribedPlayerId"] = targetId,
|
||||
["isActive"] = active,
|
||||
["startedAt"] = now,
|
||||
["currentPeriodEnd"] = active ? now.AddYears(10) : now,
|
||||
["renewalDate"] = active ? now.AddYears(10) : now,
|
||||
["SubscriptionId"] = subscriptionId,
|
||||
["RecNetPlayerId"] = pid,
|
||||
["PlatformAccountSubscribedPlayerId"] = targetId,
|
||||
["IsActive"] = active,
|
||||
["StartedAt"] = now,
|
||||
["CurrentPeriodEnd"] = active ? now.AddYears(10) : now,
|
||||
["RenewalDate"] = active ? now.AddYears(10) : now,
|
||||
};
|
||||
|
||||
return Ok(new Dictionary<string, object?>
|
||||
{
|
||||
["subscription"] = subscription,
|
||||
["platformAccountSubscribedPlayerId"] = targetId,
|
||||
["Subscription"] = subscription,
|
||||
["PlatformAccountSubscribedPlayerId"] = targetId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ public class ExternalFriendInviteController(DorkNetDbContext db) : ControllerBas
|
||||
}
|
||||
|
||||
[HttpGet("api/externalfriendinvite/v1/getplatformreferrers")]
|
||||
[HttpPost("api/externalfriendinvite/v1/getplatformreferrers")]
|
||||
public async Task<IActionResult> GetPlatformReferrers()
|
||||
{
|
||||
var rows = await db.PlayerSettings
|
||||
@@ -54,6 +55,7 @@ public class ExternalFriendInviteController(DorkNetDbContext db) : ControllerBas
|
||||
}
|
||||
|
||||
[HttpGet("api/externalfriendinvite/v1/gettextmessagereferrers")]
|
||||
[HttpPost("api/externalfriendinvite/v1/gettextmessagereferrers")]
|
||||
public async Task<IActionResult> GetTextMessageReferrers()
|
||||
{
|
||||
var rows = await db.PlayerSettings
|
||||
|
||||
+12
-6
@@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DorkNet.Models.Auth;
|
||||
using DorkNet.Server.Auth;
|
||||
using DorkNet.Server.Data;
|
||||
using DorkNet.Server.Data.Entities;
|
||||
@@ -25,7 +26,14 @@ public class InfluencerPartnerProgramController(DorkNetDbContext db) : Controlle
|
||||
.Where(p => creatorIds.Contains(p.Id))
|
||||
.OrderBy(p => p.Username)
|
||||
.ToListAsync();
|
||||
return Ok(players.Select(ToInfluencerWire));
|
||||
var influencers = players.Select(ToInfluencerWire).ToList();
|
||||
return Ok(new Dictionary<string, object>
|
||||
{
|
||||
["Influencers"] = influencers,
|
||||
["influencers"] = influencers,
|
||||
["Accounts"] = influencers,
|
||||
["accounts"] = influencers,
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("api/influencerpartnerprogram/influencer")]
|
||||
@@ -92,18 +100,16 @@ public class InfluencerPartnerProgramController(DorkNetDbContext db) : Controlle
|
||||
return Ok(new { Success = true });
|
||||
}
|
||||
|
||||
private static object ToInfluencerWire(PlayerEntity player) => new
|
||||
private static RecNetAccount ToInfluencerWire(PlayerEntity player) => new()
|
||||
{
|
||||
AccountId = (int)player.Id,
|
||||
RawUsername = player.Username,
|
||||
player.Username,
|
||||
Username = player.Username,
|
||||
DisplayName = player.DisplayName ?? player.Username,
|
||||
ProfileImage = player.ProfileImageName ?? string.Empty,
|
||||
TreatAsJunior = false,
|
||||
HasBirthday = true,
|
||||
Platforms = 1,
|
||||
IsInfluencer = true,
|
||||
SupportedInfluencerId = (int?)null,
|
||||
LocalSupportedInfluencerId = (int?)null,
|
||||
CreatedAt = player.CreatedAt == default ? DateTime.UtcNow : player.CreatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using DorkNet.Server.Auth;
|
||||
using DorkNet.Server.Data;
|
||||
@@ -29,12 +31,20 @@ public class KeepsakesController(DorkNetDbContext db, DomainConfig domain) : Con
|
||||
|
||||
[HttpGet("categories")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Categories() => Ok(new[]
|
||||
public IActionResult Categories()
|
||||
{
|
||||
new { Category = "account", DisplayName = "Account" },
|
||||
new { Category = "event", DisplayName = "Events" },
|
||||
new { Category = "room", DisplayName = "Rooms" },
|
||||
});
|
||||
var results = new[]
|
||||
{
|
||||
new { KeepsakeCategoryId = 0, VisualId = "account", LimitPerRoom = 0, XpValue = 0, IconOutlineImageName = string.Empty, IconFilledImageName = string.Empty },
|
||||
new { KeepsakeCategoryId = 1, VisualId = "event", LimitPerRoom = 0, XpValue = 0, IconOutlineImageName = string.Empty, IconFilledImageName = string.Empty },
|
||||
new { KeepsakeCategoryId = 2, VisualId = "room", LimitPerRoom = 64, XpValue = 0, IconOutlineImageName = string.Empty, IconFilledImageName = string.Empty },
|
||||
};
|
||||
return Ok(new
|
||||
{
|
||||
Results = results,
|
||||
TotalResults = results.Length,
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("events")]
|
||||
public async Task<IActionResult> Events()
|
||||
@@ -62,16 +72,22 @@ public class KeepsakesController(DorkNetDbContext db, DomainConfig domain) : Con
|
||||
|| EF.Functions.Like(k.EventKey, $"room/{roomIdText}/%")))
|
||||
.OrderByDescending(k => k.EarnedAt)
|
||||
.ToListAsync();
|
||||
return Ok(rows.Select(ToWire));
|
||||
var instances = rows.Select(row => ToRoomInstanceWire(row, roomId)).ToList();
|
||||
return Ok(new
|
||||
{
|
||||
Instances = instances,
|
||||
CollectionRecords = Array.Empty<object>(),
|
||||
KeepsakeProgressionEventIds = Array.Empty<long>(),
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("globalconfig")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult GlobalConfig() => Ok(new
|
||||
{
|
||||
Enabled = true,
|
||||
Categories = new[] { "account", "event", "room" },
|
||||
CdnBaseUrl = $"https://{domain.Sub("cdn")}/",
|
||||
KeepsakeFeatureEnabled = true,
|
||||
KeepsakeRoomLimit = 64,
|
||||
SocialXpBoostEnabled = false,
|
||||
});
|
||||
|
||||
[HttpPost]
|
||||
@@ -162,6 +178,32 @@ public class KeepsakesController(DorkNetDbContext db, DomainConfig domain) : Con
|
||||
row.EarnedAt,
|
||||
};
|
||||
|
||||
private static object ToRoomInstanceWire(KeepsakeEntity row, long roomId)
|
||||
{
|
||||
var placedByAccountId = row.PlayerId is > 0 and <= int.MaxValue
|
||||
? (int)row.PlayerId
|
||||
: 0;
|
||||
return new
|
||||
{
|
||||
KeepsakeInstanceId = StableKeepsakeInstanceId(row.Id).ToString("D"),
|
||||
KeepsakeCategoryConfigId = CategoryId(row.Category),
|
||||
PlacedByAccountId = placedByAccountId,
|
||||
RoomId = roomId,
|
||||
SubRoomId = (long?)null,
|
||||
};
|
||||
}
|
||||
|
||||
private static int CategoryId(string? category) =>
|
||||
string.Equals(category, "event", StringComparison.OrdinalIgnoreCase) ? 1 :
|
||||
string.Equals(category, "room", StringComparison.OrdinalIgnoreCase) ? 2 :
|
||||
0;
|
||||
|
||||
private static Guid StableKeepsakeInstanceId(long id)
|
||||
{
|
||||
var hash = MD5.HashData(Encoding.UTF8.GetBytes($"keepsakeInstance:{id}"));
|
||||
return new Guid(hash);
|
||||
}
|
||||
|
||||
private static string Trim(string? value, int max)
|
||||
{
|
||||
var s = (value ?? string.Empty).Trim();
|
||||
|
||||
@@ -3,11 +3,12 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DorkNet.Server.Auth;
|
||||
using DorkNet.Server.Data;
|
||||
using DorkNet.Server.Services;
|
||||
|
||||
namespace DorkNet.Server.Controllers.API.ProgressionEvents;
|
||||
|
||||
[ApiController]
|
||||
public class ProgressionEventsController(DorkNetDbContext db) : ControllerBase
|
||||
public class ProgressionEventsController(DorkNetDbContext db, ServerSettingsService serverSettings) : ControllerBase
|
||||
{
|
||||
[HttpGet("api/progressionEvents")]
|
||||
[Authorize]
|
||||
@@ -28,19 +29,60 @@ public class ProgressionEventsController(DorkNetDbContext db) : ControllerBase
|
||||
return Ok(rows);
|
||||
}
|
||||
|
||||
[HttpGet("api/progressionEvents/active")]
|
||||
[HttpGet("api/progressionEvents/event/{eventId:long}")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Active()
|
||||
public async Task<IActionResult> Event(long eventId)
|
||||
{
|
||||
var start = DateTime.UtcNow.Date.AddDays(-(int)DateTime.UtcNow.DayOfWeek);
|
||||
var end = start.AddDays(7);
|
||||
var activeId = CurrentProgressionEventId();
|
||||
if (eventId != activeId)
|
||||
return NotFound();
|
||||
|
||||
var weekly = await serverSettings.GetWeeklyChallengesAsync();
|
||||
var (start, end) = CurrentWeekWindow();
|
||||
return Ok(new
|
||||
{
|
||||
EventKey = $"weekly:{start:yyyyMMdd}",
|
||||
ProgressionEventId = activeId,
|
||||
Name = "Weekly Progression",
|
||||
StartAt = start,
|
||||
EndAt = end,
|
||||
Active = true,
|
||||
Rewards = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
ProgressionEventRewardId = activeId * 1000L,
|
||||
GiftDropId = weekly.Reward.GiftDropId != 0 ? weekly.Reward.GiftDropId : activeId,
|
||||
ImageName = string.Empty,
|
||||
Xp = weekly.Reward.Xp,
|
||||
RewardIndex = 0,
|
||||
IsBonus = false,
|
||||
},
|
||||
},
|
||||
KeepsakeRoomLists = Array.Empty<object>(),
|
||||
StartTime = start,
|
||||
EndTime = end,
|
||||
CollectionEndTime = end,
|
||||
UsesBoost = false,
|
||||
BoostDailyGameplayMinutesLimit = 0,
|
||||
BoostXpMultiplier = 1.0,
|
||||
PurchasableXpBoostId = (Guid?)null,
|
||||
ActiveExperiment = string.Empty,
|
||||
ChallengesIconImageName = string.Empty,
|
||||
RewardsPipImageName = string.Empty,
|
||||
EventInfoImageName = string.Empty,
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("api/progressionEvents/active")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Active() => Ok(CurrentProgressionEventId());
|
||||
|
||||
private static long CurrentProgressionEventId()
|
||||
{
|
||||
var (start, _) = CurrentWeekWindow();
|
||||
return long.Parse(start.ToString("yyyyMMdd"));
|
||||
}
|
||||
|
||||
private static (DateTime Start, DateTime End) CurrentWeekWindow()
|
||||
{
|
||||
var start = DateTime.UtcNow.Date.AddDays(-(int)DateTime.UtcNow.DayOfWeek);
|
||||
return (start, start.AddDays(7));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,15 +25,29 @@ public class QuickPlayController(DorkNetDbContext db) : ControllerBase
|
||||
/// immediately teleports — that's WHY users were randomly
|
||||
/// spawning in Paddleball etc.
|
||||
///
|
||||
/// Correct behaviour: return empty {} unless we actually have a
|
||||
/// queued match for the caller. The /set endpoint is what
|
||||
/// queues; getandclear is the consume.
|
||||
/// Correct behaviour: return the full no-target launch DTO unless
|
||||
/// we actually have a queued match for the caller. The /set endpoint
|
||||
/// is what queues; getandclear is the consume.
|
||||
///
|
||||
/// We don't persist queue state on a single-region private
|
||||
/// server (no real matchmaking pool), so always return empty.</summary>
|
||||
[HttpGet("api/quickPlay/v1/getandclear")]
|
||||
[HttpGet("api/quickPlay/v2/getandclear")]
|
||||
public IActionResult GetAndClear() => Ok(new { });
|
||||
public IActionResult GetAndClear() => Ok(QuickPlayLaunchTarget.Empty);
|
||||
|
||||
private sealed class QuickPlayLaunchTarget
|
||||
{
|
||||
public static readonly QuickPlayLaunchTarget Empty = new();
|
||||
|
||||
public bool OKCGKFJELAC { get; init; }
|
||||
public string INMKAFBAOPC { get; init; } = string.Empty;
|
||||
public int? FIOPJIIOCGA { get; init; }
|
||||
public string ICKOEFDNOPM { get; init; } = string.Empty;
|
||||
public int? KLDCJBLLDKN { get; init; }
|
||||
public bool OFBMKDAONNE { get; init; }
|
||||
public object? FMKHKNCEBCN { get; init; }
|
||||
public long? EEFNKAFLPLG { get; init; }
|
||||
}
|
||||
|
||||
public sealed class SetQuickPlayRequest
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using DorkNet.Server.Auth;
|
||||
using DorkNet.Server.Data;
|
||||
using DorkNet.Server.Data.Entities;
|
||||
@@ -13,8 +15,9 @@ public class RoomConsumablesController(DorkNetDbContext db, LevelService level)
|
||||
{
|
||||
[HttpGet("api/roomconsumables")]
|
||||
[HttpGet("api/roomconsumables/v1/roomConsumable")]
|
||||
[HttpGet("api/roomconsumables/v1/roomConsumable/room/{roomId:long}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> List([FromQuery] long? id = null)
|
||||
public async Task<IActionResult> List([FromRoute] long? roomId = null, [FromQuery] long? id = null)
|
||||
{
|
||||
var query = db.StoreItems
|
||||
.Where(i => i.IsActive && i.Category.ToLower() == "consumable");
|
||||
@@ -27,6 +30,24 @@ public class RoomConsumablesController(DorkNetDbContext db, LevelService level)
|
||||
return Ok(rows.Select(ToWire));
|
||||
}
|
||||
|
||||
[HttpGet("api/roomconsumables/v1/roomConsumable/room/{roomId:long}/me")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> MineForRoom(long roomId)
|
||||
{
|
||||
var me = this.RequireCurrentPlayerId();
|
||||
var rows = await (from inventory in db.PlayerInventory
|
||||
join item in db.StoreItems on inventory.ItemSlug equals item.Slug
|
||||
where inventory.PlayerId == me
|
||||
&& inventory.Quantity > 0
|
||||
&& item.IsActive
|
||||
&& item.Category.ToLower() == "consumable"
|
||||
orderby item.DisplayName
|
||||
select new { Inventory = inventory, Item = item })
|
||||
.Take(200)
|
||||
.ToListAsync();
|
||||
return Ok(rows.Select(row => ToInventoryWire(row.Item, row.Inventory)));
|
||||
}
|
||||
|
||||
[HttpPost("api/roomconsumables/v1/roomconsumable/{itemId:long}/purchase/currency")]
|
||||
[Authorize]
|
||||
public Task<IActionResult> PurchaseCurrency(long itemId, [FromQuery] int? currencyType = null) =>
|
||||
@@ -73,16 +94,46 @@ public class RoomConsumablesController(DorkNetDbContext db, LevelService level)
|
||||
});
|
||||
}
|
||||
|
||||
private static object ToWire(StoreItemEntity item) => new
|
||||
private static object ToWire(StoreItemEntity item)
|
||||
{
|
||||
RoomConsumableId = item.Id,
|
||||
ItemId = item.Id,
|
||||
item.Slug,
|
||||
Name = item.DisplayName,
|
||||
item.Description,
|
||||
item.ImageName,
|
||||
item.Price,
|
||||
item.CurrencyType,
|
||||
item.Category,
|
||||
};
|
||||
var roomConsumableId = StableRoomConsumableId(item.Id).ToString("D");
|
||||
return new
|
||||
{
|
||||
RoomConsumableId = roomConsumableId,
|
||||
Name = item.DisplayName,
|
||||
item.ImageName,
|
||||
item.Description,
|
||||
item.Price,
|
||||
CurrencyId = (Guid?)null,
|
||||
CreatedAt = DateTimeOffset.UnixEpoch,
|
||||
ItemId = item.Id,
|
||||
item.Slug,
|
||||
item.CurrencyType,
|
||||
item.Category,
|
||||
};
|
||||
}
|
||||
|
||||
private static object ToInventoryWire(StoreItemEntity item, PlayerInventoryEntity inventory)
|
||||
{
|
||||
var createdAt = inventory.AcquiredAt == default
|
||||
? DateTimeOffset.UnixEpoch
|
||||
: new DateTimeOffset(DateTime.SpecifyKind(inventory.AcquiredAt, DateTimeKind.Utc));
|
||||
return new
|
||||
{
|
||||
Id = StableRoomConsumableId(item.Id).ToString("D"),
|
||||
ConsumableItemDesc = ToWire(item),
|
||||
Count = Math.Max(0, inventory.Quantity),
|
||||
InitialCount = Math.Max(0, inventory.Quantity),
|
||||
CreatedAt = createdAt,
|
||||
ActiveDurationMinutes = 0,
|
||||
IsActive = inventory.IsActive,
|
||||
IsTransferable = false,
|
||||
};
|
||||
}
|
||||
|
||||
private static Guid StableRoomConsumableId(long itemId)
|
||||
{
|
||||
var hash = MD5.HashData(Encoding.UTF8.GetBytes($"roomConsumable:{itemId}"));
|
||||
return new Guid(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,12 +48,32 @@ public class RoomsController(
|
||||
/// TagsCsv column (e.g. `#community`, `#recroomoriginal`).
|
||||
/// </summary>
|
||||
[HttpGet("api/rooms/v1/hot")]
|
||||
[HttpGet("rooms/hot")]
|
||||
public async Task<IActionResult> Hot(
|
||||
public async Task<IActionResult> HotV1(
|
||||
[FromQuery] string? roomScoreType,
|
||||
[FromQuery] string? tags)
|
||||
=> Ok((await rooms.HotAsync(tags)).Select(RoomService.ToWireRoom).ToList());
|
||||
|
||||
[HttpGet("rooms/hot")]
|
||||
public async Task<IActionResult> HotRoomServer(
|
||||
[FromQuery] string? tag,
|
||||
[FromQuery] string? tags,
|
||||
[FromQuery] int skip = 0,
|
||||
[FromQuery] int take = 100)
|
||||
{
|
||||
var all = await rooms.HotAsync(tag ?? tags, take: 200);
|
||||
var page = all
|
||||
.Skip(Math.Max(skip, 0))
|
||||
.Take(Math.Clamp(take, 1, 100))
|
||||
.Select(RoomService.ToWireRoom)
|
||||
.ToList();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Results = page,
|
||||
TotalResults = all.Count,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET <c>/roomserver/rooms/recommendations</c> — the home-tab
|
||||
/// "Recommended" carousel. <b>Wire shape is NOT a flat
|
||||
@@ -1543,7 +1563,6 @@ public class RoomsController(
|
||||
.Where(r => r.Id == roomId)
|
||||
.Select(r => new { r.Id, r.VisitCount, r.CheerCount, r.FavoriteCount })
|
||||
.FirstOrDefaultAsync();
|
||||
if (room is null) return NotFound();
|
||||
|
||||
var favorited = await db.RoomBookmarks
|
||||
.AnyAsync(b => b.RoomId == roomId && b.PlayerId == pid.Value);
|
||||
@@ -1556,7 +1575,7 @@ public class RoomsController(
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
RoomId = room.Id,
|
||||
RoomId = room?.Id ?? roomId,
|
||||
PlayerId = pid.Value,
|
||||
Favorite = favorited,
|
||||
Favorited = favorited,
|
||||
@@ -1569,9 +1588,9 @@ public class RoomsController(
|
||||
VisitCount = visit?.VisitCount ?? 0,
|
||||
FirstVisitedAt = visit?.FirstVisitAt,
|
||||
LastVisitedAt = visit?.LastVisitAt,
|
||||
RoomVisitCount = room.VisitCount,
|
||||
RoomCheerCount = room.CheerCount,
|
||||
RoomFavoriteCount = room.FavoriteCount,
|
||||
RoomVisitCount = room?.VisitCount ?? 0,
|
||||
RoomCheerCount = room?.CheerCount ?? 0,
|
||||
RoomFavoriteCount = room?.FavoriteCount ?? 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1613,7 +1632,7 @@ public class RoomsController(
|
||||
public async Task<IActionResult> CuratedPlaylistsCompat()
|
||||
{
|
||||
var curated = await playlists.CuratedAsync();
|
||||
return Ok(curated.Select(BuildPlaylistUnionEntry).ToList());
|
||||
return Ok(curated.Select(p => p.Id).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("clubhousesearch/mostactivenow")]
|
||||
@@ -1725,17 +1744,25 @@ public class RoomsController(
|
||||
/// <see cref="Featured"/> shape here throws KeyNotFoundException on
|
||||
/// the watch's group-id lookup.</summary>
|
||||
[HttpGet("featuredrooms/current")]
|
||||
public async Task<IActionResult> FeaturedRoomsCurrent() => Ok(new
|
||||
public async Task<IActionResult> FeaturedRoomsCurrent()
|
||||
{
|
||||
FeaturedRoomGroupId = 1L,
|
||||
Name = "Featured",
|
||||
// NMPFCIJPODA.PPGFHEDFBEA (NMPFCIJPODA.txt:100-125) reads
|
||||
// 3 strict keys, not 2 — FeaturedRoomGroupId, Name, AND
|
||||
// Rooms (List<PPKJFAAAGDO>). Returning without the third
|
||||
// key throws KeyNotFoundException on the watch and the
|
||||
// Featured carousel stays empty.
|
||||
Rooms = await rooms.FeaturedAgRoomIdsAsync(12),
|
||||
});
|
||||
var ids = await rooms.FeaturedAgRoomIdsAsync(12);
|
||||
var featuredRooms = await db.Rooms
|
||||
.Where(r => ids.Contains(r.Id))
|
||||
.ToListAsync();
|
||||
var byId = featuredRooms.ToDictionary(r => r.Id);
|
||||
var wireRooms = ids
|
||||
.Select(id => byId.TryGetValue(id, out var room) ? RoomService.ToWireRoom(room) : null)
|
||||
.Where(room => room is not null)
|
||||
.Cast<object>()
|
||||
.ToList();
|
||||
return Ok(new
|
||||
{
|
||||
FeaturedRoomGroupId = 1L,
|
||||
Name = "Featured",
|
||||
Rooms = wireRooms,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>POST <c>api/rooms/v1/bookmark</c> — toggle bookmark
|
||||
/// state for a room. Body: <c>RoomId</c> + optional <c>Bookmark</c>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DorkNet.Server.Controllers.Commerce;
|
||||
|
||||
[ApiController]
|
||||
public class PurchaseCampaignController : ControllerBase
|
||||
{
|
||||
[HttpGet("/purchasecampaign/allcurrent/v2")]
|
||||
[HttpGet("/purchasecampaign/allcurrent")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult AllCurrent() => Ok(Array.Empty<object>());
|
||||
}
|
||||
@@ -103,6 +103,28 @@ public class MatchController(
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("/photon_access_token")]
|
||||
public IActionResult PhotonAccessToken()
|
||||
{
|
||||
var pid = this.RequireCurrentPlayerId();
|
||||
var appId = config["Photon:AppId"] ?? string.Empty;
|
||||
var voiceAppId = config["Photon:VoiceAppId"] ?? string.Empty;
|
||||
var region = (config["Photon:CloudRegion"] ?? "us").ToLowerInvariant();
|
||||
return Ok(new Dictionary<string, object>
|
||||
{
|
||||
["ResultCode"] = 1,
|
||||
["Message"] = string.Empty,
|
||||
["UserId"] = pid.ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||||
["PhotonAppId"] = appId,
|
||||
["PhotonVoiceAppId"] = voiceAppId,
|
||||
["AppId"] = appId,
|
||||
["VoiceAppId"] = voiceAppId,
|
||||
["CloudRegion"] = region,
|
||||
["Region"] = region,
|
||||
["Token"] = string.Empty,
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("/matchmake/dorm")]
|
||||
public async Task<IActionResult> MatchmakeDorm()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user