🧱 Localization infrastructure

This commit is contained in:
2025-05-08 01:55:32 +08:00
parent ee7dc31b20
commit 891dbfb255
19 changed files with 815 additions and 9 deletions

View File

@@ -1,14 +1,23 @@
using DysonNetwork.Sphere.Activity;
using DysonNetwork.Sphere.Connection;
using DysonNetwork.Sphere.Resources;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Localization;
using NodaTime;
using NodaTime;
namespace DysonNetwork.Sphere.Account;
public class AccountEventService(AppDatabase db, ActivityService act, WebSocketService ws, IMemoryCache cache)
public class AccountEventService(
AppDatabase db,
AccountService acc,
ActivityService act,
WebSocketService ws,
IMemoryCache cache
)
{
private static readonly Random Random = new();
private const string StatusCacheKey = "account_status_";
public async Task<Status> GetStatus(long userId)
@@ -76,4 +85,84 @@ public class AccountEventService(AppDatabase db, ActivityService act, WebSocketS
db.Update(status);
await db.SaveChangesAsync();
}
private const int FortuneTipCount = 7; // This will be the max index for each type (positive/negative)
private const string CaptchaCacheKey = "checkin_captcha_";
private const int CaptchaProbabilityPercent = 20;
public bool CheckInDailyDoAskCaptcha(Account user)
{
var cacheKey = $"{CaptchaCacheKey}{user.Id}";
if (cache.TryGetValue(cacheKey, out bool? needsCaptcha))
return needsCaptcha!.Value;
var result = Random.Next(100) < CaptchaProbabilityPercent;
cache.Set(cacheKey, result, TimeSpan.FromHours(24));
return result;
}
public async Task<bool> CheckInDailyIsAvailable(Account user)
{
var now = SystemClock.Instance.GetCurrentInstant();
var lastCheckIn = await db.AccountCheckInResults
.Where(x => x.AccountId == user.Id)
.OrderByDescending(x => x.CreatedAt)
.FirstOrDefaultAsync();
if (lastCheckIn == null)
return true;
var lastDate = lastCheckIn.CreatedAt.InUtc().Date;
var currentDate = now.InUtc().Date;
return lastDate < currentDate;
}
public async Task<CheckInResult> CheckInDaily(Account user)
{
if (await CheckInDailyIsAvailable(user)) throw new InvalidOperationException("Check-in is not available");
var localizer = acc.GetEventLocalizer(user.Language);
// Generate 2 positive tips
var positiveIndices = Enumerable.Range(1, FortuneTipCount)
.OrderBy(_ => Random.Next())
.Take(2)
.ToList();
var tips = positiveIndices.Select(index => new FortuneTip
{
IsPositive = true, Title = localizer[$"FortuneTipPositiveTitle_{index}"].Value,
Content = localizer[$"FortuneTipPositiveContent_{index}"].Value
}).ToList();
// Generate 2 negative tips
var negativeIndices = Enumerable.Range(1, FortuneTipCount)
.OrderBy(_ => Random.Next())
.Take(2)
.ToList();
tips.AddRange(negativeIndices.Select(index => new FortuneTip
{
IsPositive = false, Title = localizer[$"FortuneTipNegativeTitle_{index}"].Value,
Content = localizer[$"FortuneTipNegativeContent_{index}"].Value
}));
var result = new CheckInResult
{
Tips = tips,
Level = (CheckInResultLevel)Random.Next(Enum.GetValues<CheckInResultLevel>().Length),
AccountId = user.Id
};
db.AccountCheckInResults.Add(result);
await db.SaveChangesAsync();
await act.CreateActivity(
user,
"accounts.check-in",
$"account.check-in/{result.Id}",
ActivityVisibility.Friends
);
return result;
}
}