mirror of https://github.com/oliverbooth/VPLink
feat: announce avatar events
This commit is contained in:
parent
69edcfe3f5
commit
7a6ae083da
|
@ -1,6 +1,6 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
|
@ -9,7 +9,6 @@ using Microsoft.Extensions.Configuration;
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using VPLink.Commands;
|
||||
using VpSharp;
|
||||
using VpSharp.Entities;
|
||||
|
||||
namespace VPLink.Services;
|
||||
|
@ -25,7 +24,6 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
|
|||
private readonly IConfiguration _configuration;
|
||||
private readonly InteractionService _interactionService;
|
||||
private readonly DiscordSocketClient _discordClient;
|
||||
private readonly VirtualParadiseClient _virtualParadiseClient;
|
||||
private readonly Subject<IUserMessage> _messageReceived = new();
|
||||
|
||||
/// <summary>
|
||||
|
@ -36,20 +34,17 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
|
|||
/// <param name="configuration">The configuration.</param>
|
||||
/// <param name="interactionService">The interaction service.</param>
|
||||
/// <param name="discordClient">The Discord client.</param>
|
||||
/// <param name="virtualParadiseClient">The Virtual Paradise client.</param>
|
||||
public DiscordService(ILogger<DiscordService> logger,
|
||||
IServiceProvider serviceProvider,
|
||||
IConfiguration configuration,
|
||||
InteractionService interactionService,
|
||||
DiscordSocketClient discordClient,
|
||||
VirtualParadiseClient virtualParadiseClient)
|
||||
DiscordSocketClient discordClient)
|
||||
{
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
_configuration = configuration;
|
||||
_interactionService = interactionService;
|
||||
_discordClient = discordClient;
|
||||
_virtualParadiseClient = virtualParadiseClient;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -114,6 +109,38 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
|
|||
return _interactionService.RegisterCommandsGloballyAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AnnounceArrival(VirtualParadiseAvatar avatar)
|
||||
{
|
||||
if (avatar is null) throw new ArgumentNullException(nameof(avatar));
|
||||
if (!TryGetRelayChannel(out ITextChannel? channel)) return Task.CompletedTask;
|
||||
|
||||
var embed = new EmbedBuilder();
|
||||
embed.WithColor(0x00FF00);
|
||||
embed.WithTitle("📥 Avatar Joined");
|
||||
embed.WithDescription(avatar.Name);
|
||||
embed.WithTimestamp(DateTimeOffset.UtcNow);
|
||||
embed.WithFooter($"Session {avatar.Session}");
|
||||
|
||||
return channel.SendMessageAsync(embed: embed.Build());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AnnounceDeparture(VirtualParadiseAvatar avatar)
|
||||
{
|
||||
if (avatar is null) throw new ArgumentNullException(nameof(avatar));
|
||||
if (!TryGetRelayChannel(out ITextChannel? channel)) return Task.CompletedTask;
|
||||
|
||||
var embed = new EmbedBuilder();
|
||||
embed.WithColor(0xFF0000);
|
||||
embed.WithTitle("📤 Avatar Left");
|
||||
embed.WithDescription(avatar.Name);
|
||||
embed.WithTimestamp(DateTimeOffset.UtcNow);
|
||||
embed.WithFooter($"Session {avatar.Session}");
|
||||
|
||||
return channel.SendMessageAsync(embed: embed.Build());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendMessageAsync(VirtualParadiseMessage message)
|
||||
{
|
||||
|
@ -134,12 +161,7 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
|
|||
|
||||
_logger.LogInformation("Message by {Author}: {Content}", author, message.Content);
|
||||
|
||||
var channelId = _configuration.GetSection("Discord:ChannelId").Get<ulong>();
|
||||
if (_discordClient.GetChannel(channelId) is not ITextChannel channel)
|
||||
{
|
||||
_logger.LogError("Channel {ChannelId} does not exist", channelId);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (!TryGetRelayChannel(out ITextChannel? channel)) return Task.CompletedTask;
|
||||
|
||||
string unescaped = UnescapeRegex.Replace(message.Content, "$1");
|
||||
string escaped = EscapeRegex.Replace(unescaped, "\\$1");
|
||||
|
@ -148,6 +170,20 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
|
|||
return channel.SendMessageAsync($"**{displayName}**: {escaped}");
|
||||
}
|
||||
|
||||
private bool TryGetRelayChannel([NotNullWhen(true)] out ITextChannel? channel)
|
||||
{
|
||||
var channelId = _configuration.GetValue<ulong>("Discord:ChannelId");
|
||||
if (_discordClient.GetChannel(channelId) is ITextChannel textChannel)
|
||||
{
|
||||
channel = textChannel;
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.LogError("Channel {ChannelId} does not exist", channelId);
|
||||
channel = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"\\(\*|_|`|~|\\)", RegexOptions.Compiled)]
|
||||
private static partial Regex GetUnescapeRegex();
|
||||
|
||||
|
|
|
@ -14,10 +14,24 @@ public interface IDiscordService
|
|||
/// <value>An observable that is triggered when a message is received from the Discord channel.</value>
|
||||
IObservable<IUserMessage> OnMessageReceived { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Announces the arrival of an avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar.</param>
|
||||
/// <returns>A <see cref="Task" /> representing the asynchronous operation.</returns>
|
||||
Task AnnounceArrival(VirtualParadiseAvatar avatar);
|
||||
|
||||
/// <summary>
|
||||
/// Announces the arrival of an avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar.</param>
|
||||
/// <returns>A <see cref="Task" /> representing the asynchronous operation.</returns>
|
||||
Task AnnounceDeparture(VirtualParadiseAvatar avatar);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message to the Discord channel.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to send.</param>
|
||||
/// <returns>A <see cref="Task" /> representing the asynchronous operation.</returns>
|
||||
Task SendMessageAsync(VirtualParadiseMessage message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,22 @@ namespace VPLink.Services;
|
|||
/// </summary>
|
||||
public interface IVirtualParadiseService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an observable that is triggered when an avatar enters the Virtual Paradise world.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An observable that is triggered when an avatar enters the Virtual Paradise world.
|
||||
/// </value>
|
||||
IObservable<VirtualParadiseAvatar> OnAvatarJoined { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an observable that is triggered when an avatar exits the Virtual Paradise world.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An observable that is triggered when an avatar exits the Virtual Paradise world.
|
||||
/// </value>
|
||||
IObservable<VirtualParadiseAvatar> OnAvatarLeft { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an observable that is triggered when a message is received from the Virtual Paradise world server.
|
||||
/// </summary>
|
||||
|
|
|
@ -45,6 +45,9 @@ internal sealed class RelayService : BackgroundService
|
|||
.Where(m => m.Author != _discordClient.CurrentUser)
|
||||
.SubscribeAsync(_virtualParadiseService.SendMessageAsync);
|
||||
|
||||
_virtualParadiseService.OnAvatarJoined.SubscribeAsync(_discordService.AnnounceArrival);
|
||||
_virtualParadiseService.OnAvatarLeft.SubscribeAsync(_discordService.AnnounceDeparture);
|
||||
|
||||
_virtualParadiseService.OnMessageReceived
|
||||
.Where(m => m.Author != _virtualParadiseClient.CurrentAvatar)
|
||||
.SubscribeAsync(_discordService.SendMessageAsync);
|
||||
|
|
|
@ -17,6 +17,8 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
|
|||
private readonly IConfiguration _configuration;
|
||||
private readonly VirtualParadiseClient _virtualParadiseClient;
|
||||
private readonly Subject<VirtualParadiseMessage> _messageReceived = new();
|
||||
private readonly Subject<VirtualParadiseAvatar> _avatarJoined = new();
|
||||
private readonly Subject<VirtualParadiseAvatar> _avatarLeft = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VirtualParadiseService" /> class.
|
||||
|
@ -33,6 +35,12 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
|
|||
_virtualParadiseClient = virtualParadiseClient;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IObservable<VirtualParadiseAvatar> OnAvatarJoined => _avatarJoined.AsObservable();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IObservable<VirtualParadiseAvatar> OnAvatarLeft => _avatarJoined.AsObservable();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IObservable<VirtualParadiseMessage> OnMessageReceived => _messageReceived.AsObservable();
|
||||
|
||||
|
@ -59,6 +67,8 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
|
|||
{
|
||||
_logger.LogInformation("Establishing relay");
|
||||
_virtualParadiseClient.MessageReceived.Subscribe(_messageReceived);
|
||||
_virtualParadiseClient.AvatarJoined.Subscribe(OnVirtualParadiseAvatarJoined);
|
||||
_virtualParadiseClient.AvatarLeft.Subscribe(OnVirtualParadiseAvatarLeft);
|
||||
|
||||
string username = _configuration.GetSection("VirtualParadise:Username").Value ??
|
||||
throw new InvalidOperationException("Username is not set.");
|
||||
|
@ -76,4 +86,38 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
|
|||
_logger.LogInformation("Entering world {World}", world);
|
||||
await _virtualParadiseClient.EnterAsync(world).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void OnVirtualParadiseAvatarJoined(VirtualParadiseAvatar avatar)
|
||||
{
|
||||
if (!_configuration.GetValue<bool>("Bot:AnnounceAvatarEvents"))
|
||||
{
|
||||
_logger.LogDebug("Join/leave events are disabled, ignoring event");
|
||||
return;
|
||||
}
|
||||
|
||||
if (avatar.IsBot && !_configuration.GetSection("Bot:AnnounceBots").Get<bool>())
|
||||
{
|
||||
_logger.LogDebug("Bot events are disabled, ignoring event");
|
||||
return;
|
||||
}
|
||||
|
||||
_avatarJoined.OnNext(avatar);
|
||||
}
|
||||
|
||||
private void OnVirtualParadiseAvatarLeft(VirtualParadiseAvatar avatar)
|
||||
{
|
||||
if (!_configuration.GetValue<bool>("Bot:AnnounceAvatarEvents"))
|
||||
{
|
||||
_logger.LogDebug("Join/leave events are disabled, ignoring event");
|
||||
return;
|
||||
}
|
||||
|
||||
if (avatar.IsBot && !_configuration.GetSection("Bot:AnnounceBots").Get<bool>())
|
||||
{
|
||||
_logger.LogDebug("Bot events are disabled, ignoring event");
|
||||
return;
|
||||
}
|
||||
|
||||
_avatarLeft.OnNext(avatar);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue