- GuildChatService.EmitSystemEventAsync: replaces no-op stub with real chat-row insertion via _msgs.AppendAsync (per-guild monotonic id). - GuildChatService.GetWindowAsync: real implementation — verifies caller membership, delegates to IGuildChatMessageRepository.GetWindowAsync (direction 1=OLD/2=NEW/3=BOTH, limit 50), returns adaptive wait interval (ChatPollActiveSeconds when messages returned, ChatPollIdleSeconds otherwise). - Drop unused repo injections from GuildChatService (guilds, invites, joinRequests). - GuildChatController.Messages: wires the full response — chat_message[], users[] (deduplicated author profiles with Emblem+Degree nav includes), maintenance_card_list (empty until maintenance service lands), wait_interval. - GuildChatPollTests (7 integration tests): fresh-guild CreateGuild event, sequenced Create+Join ordering, direction=NEW filter, direction=BOTH window, users[] deduplication, empty result for viewer-not-in-guild, active vs idle interval comparison. All 7 pass; full suite 1515/1515. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
3.4 KiB
C#
88 lines
3.4 KiB
C#
using SVSim.Database.Entities.Guild;
|
|
using SVSim.Database.Models.Config;
|
|
using SVSim.Database.Repositories.Guild;
|
|
using SVSim.Database.Services;
|
|
|
|
namespace SVSim.Database.Services.Guild;
|
|
|
|
public sealed class GuildChatService : IGuildChatService
|
|
{
|
|
private readonly IGuildMemberRepository _members;
|
|
private readonly IGuildChatMessageRepository _msgs;
|
|
private readonly IGameConfigService _cfg;
|
|
|
|
public GuildChatService(
|
|
IGuildMemberRepository members,
|
|
IGuildChatMessageRepository msgs,
|
|
IGameConfigService cfg)
|
|
{
|
|
_members = members;
|
|
_msgs = msgs;
|
|
_cfg = cfg;
|
|
}
|
|
|
|
// ── System-event emission ────────────────────────────────────────────────
|
|
|
|
public async Task EmitSystemEventAsync(
|
|
int guildId,
|
|
long actorViewerId,
|
|
GuildChatMessageType type,
|
|
string? body = null,
|
|
CancellationToken ct = default)
|
|
{
|
|
var msg = new GuildChatMessage
|
|
{
|
|
GuildId = guildId,
|
|
AuthorViewerId = actorViewerId,
|
|
MessageType = type,
|
|
Body = body ?? "",
|
|
CreatedAt = DateTime.UtcNow,
|
|
};
|
|
await _msgs.AppendAsync(msg, ct);
|
|
}
|
|
|
|
// ── Window query ─────────────────────────────────────────────────────────
|
|
|
|
public async Task<ChatWindow> GetWindowAsync(
|
|
long viewerId,
|
|
int startMessageId,
|
|
int direction,
|
|
int waitIntervalHint,
|
|
CancellationToken ct = default)
|
|
{
|
|
var membership = await _members.GetMembershipAsync(viewerId, ct);
|
|
if (membership is null)
|
|
{
|
|
var idleInterval = _cfg.Get<GuildConfig>().ChatPollIdleSeconds;
|
|
return new(Array.Empty<GuildChatMessage>(), idleInterval);
|
|
}
|
|
|
|
const int limit = 50;
|
|
var msgs = await _msgs.GetWindowAsync(membership.GuildId, startMessageId, direction, limit, ct);
|
|
|
|
var cfg = _cfg.Get<GuildConfig>();
|
|
int wait = msgs.Count == 0 ? cfg.ChatPollIdleSeconds : cfg.ChatPollActiveSeconds;
|
|
return new(msgs, wait);
|
|
}
|
|
|
|
// ── Not-yet-implemented stubs ────────────────────────────────────────────
|
|
|
|
public Task<ChatPostResult> PostTextOrStampAsync(long viewerId, int type, string message, CancellationToken ct = default)
|
|
=> throw new NotImplementedException();
|
|
|
|
public Task<ChatPostResult> PostDeckAsync(long viewerId, string deckPayloadJson, CancellationToken ct = default)
|
|
=> throw new NotImplementedException();
|
|
|
|
public Task<bool> DeleteDeckAsync(long viewerId, int messageId, CancellationToken ct = default)
|
|
=> throw new NotImplementedException();
|
|
|
|
public Task<ChatPostResult> PostReplayAsync(long viewerId, string replayPayloadJson, CancellationToken ct = default)
|
|
=> throw new NotImplementedException();
|
|
|
|
public Task<string?> GetReplayDetailAsync(long viewerId, int messageId, CancellationToken ct = default)
|
|
=> throw new NotImplementedException();
|
|
|
|
public Task<string?> GetDeckLogAsync(long viewerId, int messageId, CancellationToken ct = default)
|
|
=> throw new NotImplementedException();
|
|
}
|