-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTeamChatRelay.cs
More file actions
70 lines (63 loc) · 3.21 KB
/
Copy pathTeamChatRelay.cs
File metadata and controls
70 lines (63 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
using Microsoft.Extensions.DependencyInjection;
using RustPlusBot.Abstractions.Events;
using RustPlusBot.Features.Chat.Webhooks;
using RustPlusBot.Features.Connections.Listening;
using RustPlusBot.Features.Workspace.Locating;
using RustPlusBot.Persistence.Commands;
namespace RustPlusBot.Features.Chat.Relaying;
/// <summary>
/// Relays one received in-game team message into its Discord #teamchat channel, dropping our own
/// echoes and command invocations.
/// </summary>
/// <param name="locator">Resolves the target #teamchat channel.</param>
/// <param name="poster">Posts the line via webhook.</param>
/// <param name="dedup">Tracks lines the bridge relayed into the game so their echoes can be dropped.</param>
/// <param name="scopeFactory">Opens a scope to read the scoped <see cref="IMuteStore"/> command prefix.</param>
internal sealed class TeamChatRelay(
ITeamChatChannelLocator locator,
ITeamChatWebhookPoster poster,
RelayDedupBuffer dedup,
IServiceScopeFactory scopeFactory)
{
/// <summary>Handles one <see cref="TeamMessageReceivedEvent"/>.</summary>
/// <param name="evt">The received team message.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>A task that completes when the line has been relayed or dropped.</returns>
public async Task RelayAsync(TeamMessageReceivedEvent evt, CancellationToken cancellationToken)
{
if (evt.FromActivePlayer && evt.Message.StartsWith(BotTeamChat.Prefix, StringComparison.Ordinal))
{
return; // Bot-originated line echoing back; #teamchat carries only human discussion.
}
if (evt.FromActivePlayer && dedup.TryConsume((evt.GuildId, evt.ServerId), evt.Message))
{
return; // Our own relayed line echoing back; do not re-post.
}
// The locator is an in-memory cache, so resolve the channel first: unmapped servers exit
// before the per-message prefix query below.
var channelId = await locator.GetChannelIdAsync(evt.GuildId, evt.ServerId, cancellationToken)
.ConfigureAwait(false);
if (channelId is null)
{
return;
}
// A command invocation (e.g. "!pop") gets its reply in game; the bare trigger line is noise in Discord.
var prefix = await GetCommandPrefixAsync(evt.GuildId, evt.ServerId, cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(prefix) &&
evt.Message.TrimStart().StartsWith(prefix, StringComparison.Ordinal))
{
return;
}
await poster.PostAsync(channelId.Value, evt.SenderName, evt.Message, cancellationToken).ConfigureAwait(false);
}
private async Task<string> GetCommandPrefixAsync(ulong guildId, Guid serverId, CancellationToken cancellationToken)
{
// IMuteStore is scoped, so resolve it from a fresh scope rather than capturing it on this singleton.
var scope = scopeFactory.CreateAsyncScope();
await using (scope.ConfigureAwait(false))
{
var muteStore = scope.ServiceProvider.GetRequiredService<IMuteStore>();
return await muteStore.GetPrefixAsync(guildId, serverId, cancellationToken).ConfigureAwait(false);
}
}
}