mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-04-20 00:43:36 +03:00
Fix issues reported by ReSharper, implement GuildData (only GuildConfiguration)
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
parent
3e9940f0ca
commit
cca2965205
10 changed files with 201 additions and 116 deletions
3
.github/workflows/resharper.yml
vendored
3
.github/workflows/resharper.yml
vendored
|
@ -18,9 +18,6 @@ jobs:
|
||||||
contents: read
|
contents: read
|
||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
40
Boyfriend.cs
40
Boyfriend.cs
|
@ -1,3 +1,4 @@
|
||||||
|
using Boyfriend.Data.Services;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
@ -17,20 +18,11 @@ using Remora.Rest.Core;
|
||||||
namespace Boyfriend;
|
namespace Boyfriend;
|
||||||
|
|
||||||
public class Boyfriend {
|
public class Boyfriend {
|
||||||
public static ILogger<Boyfriend> Logger = null!;
|
|
||||||
public static IConfiguration GuildConfiguration = null!;
|
|
||||||
|
|
||||||
public static readonly AllowedMentions NoMentions = new(
|
public static readonly AllowedMentions NoMentions = new(
|
||||||
Array.Empty<MentionType>(), Array.Empty<Snowflake>(), Array.Empty<Snowflake>());
|
Array.Empty<MentionType>(), Array.Empty<Snowflake>(), Array.Empty<Snowflake>());
|
||||||
|
|
||||||
public static async Task Main(string[] args) {
|
public static async Task Main(string[] args) {
|
||||||
var host = CreateHostBuilder(args).UseConsoleLifetime().Build();
|
var host = CreateHostBuilder(args).UseConsoleLifetime().Build();
|
||||||
|
|
||||||
var services = host.Services;
|
|
||||||
Logger = services.GetRequiredService<ILogger<Boyfriend>>();
|
|
||||||
GuildConfiguration = services.GetRequiredService<IConfigurationBuilder>().AddJsonFile("guild_configs.json")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
await host.RunAsync();
|
await host.RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +39,10 @@ public class Boyfriend {
|
||||||
}
|
}
|
||||||
).ConfigureServices(
|
).ConfigureServices(
|
||||||
(_, services) => {
|
(_, services) => {
|
||||||
var responderTypes = typeof(Boyfriend).Assembly
|
services.Configure<DiscordGatewayClientOptions>(
|
||||||
.GetExportedTypes()
|
options => options.Intents |= GatewayIntents.MessageContents
|
||||||
.Where(t => t.IsResponder());
|
| GatewayIntents.GuildMembers
|
||||||
foreach (var responderType in responderTypes) services.AddResponder(responderType);
|
| GatewayIntents.GuildScheduledEvents);
|
||||||
|
|
||||||
services.AddDiscordCaching();
|
|
||||||
services.Configure<CacheSettings>(
|
services.Configure<CacheSettings>(
|
||||||
settings => {
|
settings => {
|
||||||
settings.SetDefaultAbsoluteExpiration(TimeSpan.FromHours(1));
|
settings.SetDefaultAbsoluteExpiration(TimeSpan.FromHours(1));
|
||||||
|
@ -61,16 +51,16 @@ public class Boyfriend {
|
||||||
settings.SetSlidingExpiration<IMessage>(TimeSpan.FromDays(7));
|
settings.SetSlidingExpiration<IMessage>(TimeSpan.FromDays(7));
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddTransient<IConfigurationBuilder, ConfigurationBuilder>();
|
services.AddTransient<IConfigurationBuilder, ConfigurationBuilder>()
|
||||||
|
.AddDiscordCaching()
|
||||||
services.Configure<DiscordGatewayClientOptions>(
|
.AddDiscordCommands()
|
||||||
options => options.Intents |= GatewayIntents.MessageContents
|
.AddInteractivity()
|
||||||
| GatewayIntents.GuildMembers
|
.AddInteractionGroup<InteractionResponders>()
|
||||||
| GatewayIntents.GuildScheduledEvents);
|
.AddSingleton<GuildDataService>();
|
||||||
|
var responderTypes = typeof(Boyfriend).Assembly
|
||||||
services.AddDiscordCommands();
|
.GetExportedTypes()
|
||||||
services.AddInteractivity();
|
.Where(t => t.IsResponder());
|
||||||
services.AddInteractionGroup<InteractionResponders>();
|
foreach (var responderType in responderTypes) services.AddResponder(responderType);
|
||||||
}
|
}
|
||||||
).ConfigureLogging(
|
).ConfigureLogging(
|
||||||
c => c.AddConsole()
|
c => c.AddConsole()
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>1.0.0</Version>
|
<Version>2.0.0</Version>
|
||||||
<Title>Boyfriend</Title>
|
<Title>Boyfriend</Title>
|
||||||
<Authors>Octol1ttle, mctaylors</Authors>
|
<Authors>Octol1ttle, mctaylors</Authors>
|
||||||
<Copyright>AGPLv3</Copyright>
|
<Copyright>AGPLv3</Copyright>
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DiffPlex" Version="1.7.1"/>
|
<PackageReference Include="DiffPlex" Version="1.7.1"/>
|
||||||
<PackageReference Include="Humanizer.Core.ru" Version="2.14.1"/>
|
<PackageReference Include="Humanizer.Core.ru" Version="2.14.1"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0-preview.3.23174.8"/>
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1"/>
|
||||||
<PackageReference Include="Remora.Discord" Version="2023.3.0"/>
|
<PackageReference Include="Remora.Discord" Version="2023.3.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
32
Data/GuildConfiguration.cs
Normal file
32
Data/GuildConfiguration.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Boyfriend.Data;
|
||||||
|
|
||||||
|
public class GuildConfiguration {
|
||||||
|
private static readonly Dictionary<string, CultureInfo> CultureInfoCache = new() {
|
||||||
|
{ "en", new CultureInfo("en-US") },
|
||||||
|
{ "ru", new CultureInfo("ru-RU") },
|
||||||
|
{ "mctaylors-ru", new CultureInfo("tt-RU") }
|
||||||
|
};
|
||||||
|
|
||||||
|
public string Prefix { get; set; } = "!";
|
||||||
|
public string Language { get; set; } = "en";
|
||||||
|
public string? WelcomeMessage { get; set; }
|
||||||
|
public bool ReceiveStartupMessages { get; set; }
|
||||||
|
public bool RemoveRolesOnMute { get; set; }
|
||||||
|
public bool ReturnRolesOnRejoin { get; set; }
|
||||||
|
public bool AutoStartEvents { get; set; }
|
||||||
|
public ulong? PublicFeedbackChannel { get; set; }
|
||||||
|
public ulong? PrivateFeedbackChannel { get; set; }
|
||||||
|
public ulong? EventNotificationChannel { get; set; }
|
||||||
|
public ulong? StarterRole { get; set; }
|
||||||
|
public ulong? MuteRole { get; set; }
|
||||||
|
public ulong? EventNotificationRole { get; set; }
|
||||||
|
|
||||||
|
public List<NotificationReceiver> EventStartedReceivers { get; set; }
|
||||||
|
= new() { NotificationReceiver.Interested, NotificationReceiver.Role };
|
||||||
|
|
||||||
|
public TimeSpan EventEarlyNotificationOffset { get; set; } = TimeSpan.Zero;
|
||||||
|
|
||||||
|
public CultureInfo Culture => CultureInfoCache[Language];
|
||||||
|
}
|
11
Data/GuildData.cs
Normal file
11
Data/GuildData.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Boyfriend.Data;
|
||||||
|
|
||||||
|
public class GuildData {
|
||||||
|
public readonly GuildConfiguration Configuration;
|
||||||
|
public readonly string ConfigurationPath;
|
||||||
|
|
||||||
|
public GuildData(GuildConfiguration configuration, string configurationPath) {
|
||||||
|
Configuration = configuration;
|
||||||
|
ConfigurationPath = configurationPath;
|
||||||
|
}
|
||||||
|
}
|
6
Data/NotificationReceiver.cs
Normal file
6
Data/NotificationReceiver.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace Boyfriend.Data;
|
||||||
|
|
||||||
|
public enum NotificationReceiver {
|
||||||
|
Interested,
|
||||||
|
Role
|
||||||
|
}
|
49
Data/Services/GuildDataService.cs
Normal file
49
Data/Services/GuildDataService.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
|
namespace Boyfriend.Data.Services;
|
||||||
|
|
||||||
|
public class GuildDataService : IHostedService {
|
||||||
|
private readonly Dictionary<Snowflake, GuildData> _datas = new();
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken ct) {
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAsync(CancellationToken ct) {
|
||||||
|
var tasks = new List<Task>();
|
||||||
|
foreach (var data in _datas.Values) {
|
||||||
|
await using var stream = File.OpenWrite(data.ConfigurationPath);
|
||||||
|
tasks.Add(JsonSerializer.SerializeAsync(stream, data.Configuration, cancellationToken: ct));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<GuildData> GetData(Snowflake guildId, CancellationToken ct = default) {
|
||||||
|
return _datas.TryGetValue(guildId, out var data) ? data : await InitializeData(guildId, ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<GuildData> InitializeData(Snowflake guildId, CancellationToken ct = default) {
|
||||||
|
var idString = $"{guildId}";
|
||||||
|
var memberDataDir = $"{guildId}/MemberData";
|
||||||
|
var configurationPath = $"{guildId}/Configuration.json";
|
||||||
|
if (!Directory.Exists(idString)) Directory.CreateDirectory(idString);
|
||||||
|
if (!Directory.Exists(memberDataDir)) Directory.CreateDirectory(memberDataDir);
|
||||||
|
if (!File.Exists(configurationPath)) await File.WriteAllTextAsync(configurationPath, "{}", ct);
|
||||||
|
|
||||||
|
await using var stream = File.OpenRead(configurationPath);
|
||||||
|
var configuration
|
||||||
|
= JsonSerializer.DeserializeAsync<GuildConfiguration>(
|
||||||
|
stream, cancellationToken: ct);
|
||||||
|
|
||||||
|
var data = new GuildData(await configuration ?? new GuildConfiguration(), configurationPath);
|
||||||
|
_datas.Add(guildId, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GuildConfiguration> GetConfiguration(Snowflake guildId, CancellationToken ct = default) {
|
||||||
|
return (await GetData(guildId, ct)).Configuration;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using Boyfriend.Data.Services;
|
||||||
using DiffPlex;
|
using DiffPlex;
|
||||||
using DiffPlex.DiffBuilder;
|
using DiffPlex.DiffBuilder;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -21,29 +22,35 @@ namespace Boyfriend;
|
||||||
|
|
||||||
public class GuildCreateResponder : IResponder<IGuildCreate> {
|
public class GuildCreateResponder : IResponder<IGuildCreate> {
|
||||||
private readonly IDiscordRestChannelAPI _channelApi;
|
private readonly IDiscordRestChannelAPI _channelApi;
|
||||||
|
private readonly GuildDataService _dataService;
|
||||||
|
private readonly ILogger<GuildCreateResponder> _logger;
|
||||||
private readonly IDiscordRestUserAPI _userApi;
|
private readonly IDiscordRestUserAPI _userApi;
|
||||||
|
|
||||||
public GuildCreateResponder(IDiscordRestChannelAPI channelApi, IDiscordRestUserAPI userApi) {
|
public GuildCreateResponder(
|
||||||
|
IDiscordRestChannelAPI channelApi, GuildDataService dataService, IDiscordRestUserAPI userApi,
|
||||||
|
ILogger<GuildCreateResponder> logger) {
|
||||||
_channelApi = channelApi;
|
_channelApi = channelApi;
|
||||||
|
_dataService = dataService;
|
||||||
_userApi = userApi;
|
_userApi = userApi;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> RespondAsync(IGuildCreate gatewayEvent, CancellationToken ct = default) {
|
public async Task<Result> RespondAsync(IGuildCreate gatewayEvent, CancellationToken ct = default) {
|
||||||
if (!gatewayEvent.Guild.IsT0) return Result.FromSuccess(); // is IAvailableGuild
|
if (!gatewayEvent.Guild.IsT0) return Result.FromSuccess(); // is IAvailableGuild
|
||||||
|
|
||||||
var guild = gatewayEvent.Guild.AsT0;
|
var guild = gatewayEvent.Guild.AsT0;
|
||||||
Boyfriend.Logger.LogInformation("Joined guild \"{Name}\"", guild.Name);
|
_logger.LogInformation("Joined guild \"{Name}\"", guild.Name);
|
||||||
|
|
||||||
var channelResult = guild.ID.GetConfigChannel("PrivateFeedbackChannel");
|
var guildConfig = await _dataService.GetConfiguration(guild.ID, ct);
|
||||||
if (!channelResult.IsDefined(out var channel)) return Result.FromSuccess();
|
if (!guildConfig.ReceiveStartupMessages)
|
||||||
|
return Result.FromSuccess();
|
||||||
|
if (guildConfig.PrivateFeedbackChannel is null)
|
||||||
|
return Result.FromSuccess();
|
||||||
|
|
||||||
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
|
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
|
||||||
if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult);
|
if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult);
|
||||||
|
|
||||||
if (!guild.GetConfigBool("ReceiveStartupMessages").IsDefined(out var shouldSendStartupMessage)
|
Messages.Culture = guildConfig.Culture;
|
||||||
|| !shouldSendStartupMessage) return Result.FromSuccess();
|
|
||||||
|
|
||||||
Messages.Culture = guild.ID.GetGuildCulture();
|
|
||||||
var i = Random.Shared.Next(1, 4);
|
var i = Random.Shared.Next(1, 4);
|
||||||
|
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
|
@ -56,7 +63,7 @@ public class GuildCreateResponder : IResponder<IGuildCreate> {
|
||||||
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
||||||
|
|
||||||
return (Result)await _channelApi.CreateMessageAsync(
|
return (Result)await _channelApi.CreateMessageAsync(
|
||||||
channel, embeds: new[] { built }, ct: ct);
|
guildConfig.PrivateFeedbackChannel.ToDiscordSnowflake(), embeds: new[] { built }, ct: ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,22 +71,24 @@ public class MessageDeletedResponder : IResponder<IMessageDelete> {
|
||||||
private readonly IDiscordRestAuditLogAPI _auditLogApi;
|
private readonly IDiscordRestAuditLogAPI _auditLogApi;
|
||||||
private readonly CacheService _cacheService;
|
private readonly CacheService _cacheService;
|
||||||
private readonly IDiscordRestChannelAPI _channelApi;
|
private readonly IDiscordRestChannelAPI _channelApi;
|
||||||
|
private readonly GuildDataService _dataService;
|
||||||
private readonly IDiscordRestUserAPI _userApi;
|
private readonly IDiscordRestUserAPI _userApi;
|
||||||
|
|
||||||
public MessageDeletedResponder(
|
public MessageDeletedResponder(
|
||||||
IDiscordRestAuditLogAPI auditLogApi, CacheService cacheService, IDiscordRestChannelAPI channelApi,
|
IDiscordRestAuditLogAPI auditLogApi, CacheService cacheService, IDiscordRestChannelAPI channelApi,
|
||||||
IDiscordRestUserAPI userApi) {
|
GuildDataService dataService, IDiscordRestUserAPI userApi) {
|
||||||
_auditLogApi = auditLogApi;
|
_auditLogApi = auditLogApi;
|
||||||
_cacheService = cacheService;
|
_cacheService = cacheService;
|
||||||
_channelApi = channelApi;
|
_channelApi = channelApi;
|
||||||
|
_dataService = dataService;
|
||||||
_userApi = userApi;
|
_userApi = userApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> RespondAsync(IMessageDelete gatewayEvent, CancellationToken ct = default) {
|
public async Task<Result> RespondAsync(IMessageDelete gatewayEvent, CancellationToken ct = default) {
|
||||||
if (!gatewayEvent.GuildID.IsDefined(out var guildId)) return Result.FromSuccess();
|
if (!gatewayEvent.GuildID.IsDefined(out var guildId)) return Result.FromSuccess();
|
||||||
|
|
||||||
var channelResult = guildId.GetConfigChannel("PrivateFeedbackChannel");
|
var guildConfiguration = await _dataService.GetConfiguration(guildId, ct);
|
||||||
if (!channelResult.IsDefined(out var logChannel)) return Result.FromSuccess();
|
if (guildConfiguration.PrivateFeedbackChannel is null) return Result.FromSuccess();
|
||||||
|
|
||||||
var messageResult = await _cacheService.TryGetValueAsync<IMessage>(
|
var messageResult = await _cacheService.TryGetValueAsync<IMessage>(
|
||||||
new KeyHelpers.MessageCacheKey(gatewayEvent.ChannelID, gatewayEvent.ID), ct);
|
new KeyHelpers.MessageCacheKey(gatewayEvent.ChannelID, gatewayEvent.ID), ct);
|
||||||
|
@ -101,7 +110,7 @@ public class MessageDeletedResponder : IResponder<IMessageDelete> {
|
||||||
if (!userResult.IsDefined(out user)) return Result.FromError(userResult);
|
if (!userResult.IsDefined(out user)) return Result.FromError(userResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
Messages.Culture = guildId.GetGuildCulture();
|
Messages.Culture = guildConfiguration.Culture;
|
||||||
|
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
.WithSmallTitle(
|
.WithSmallTitle(
|
||||||
|
@ -118,22 +127,29 @@ public class MessageDeletedResponder : IResponder<IMessageDelete> {
|
||||||
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
||||||
|
|
||||||
return (Result)await _channelApi.CreateMessageAsync(
|
return (Result)await _channelApi.CreateMessageAsync(
|
||||||
logChannel, embeds: new[] { built }, allowedMentions: Boyfriend.NoMentions, ct: ct);
|
guildConfiguration.PrivateFeedbackChannel.ToDiscordSnowflake(), embeds: new[] { built },
|
||||||
|
allowedMentions: Boyfriend.NoMentions, ct: ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MessageEditedResponder : IResponder<IMessageUpdate> {
|
public class MessageEditedResponder : IResponder<IMessageUpdate> {
|
||||||
private readonly CacheService _cacheService;
|
private readonly CacheService _cacheService;
|
||||||
private readonly IDiscordRestChannelAPI _channelApi;
|
private readonly IDiscordRestChannelAPI _channelApi;
|
||||||
|
private readonly GuildDataService _dataService;
|
||||||
|
|
||||||
public MessageEditedResponder(CacheService cacheService, IDiscordRestChannelAPI channelApi) {
|
public MessageEditedResponder(
|
||||||
|
CacheService cacheService, IDiscordRestChannelAPI channelApi, GuildDataService dataService) {
|
||||||
_cacheService = cacheService;
|
_cacheService = cacheService;
|
||||||
_channelApi = channelApi;
|
_channelApi = channelApi;
|
||||||
|
_dataService = dataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> RespondAsync(IMessageUpdate gatewayEvent, CancellationToken ct = default) {
|
public async Task<Result> RespondAsync(IMessageUpdate gatewayEvent, CancellationToken ct = default) {
|
||||||
if (!gatewayEvent.GuildID.IsDefined(out var guildId))
|
if (!gatewayEvent.GuildID.IsDefined(out var guildId))
|
||||||
return Result.FromSuccess();
|
return Result.FromSuccess();
|
||||||
|
var guildConfiguration = await _dataService.GetConfiguration(guildId, ct);
|
||||||
|
if (guildConfiguration.PrivateFeedbackChannel is null)
|
||||||
|
return Result.FromSuccess();
|
||||||
if (!gatewayEvent.Content.IsDefined(out var newContent))
|
if (!gatewayEvent.Content.IsDefined(out var newContent))
|
||||||
return Result.FromSuccess();
|
return Result.FromSuccess();
|
||||||
|
|
||||||
|
@ -148,18 +164,12 @@ public class MessageEditedResponder : IResponder<IMessageUpdate> {
|
||||||
var messageResult = await _cacheService.TryGetValueAsync<IMessage>(
|
var messageResult = await _cacheService.TryGetValueAsync<IMessage>(
|
||||||
cacheKey, ct);
|
cacheKey, ct);
|
||||||
if (!messageResult.IsDefined(out var message)) return Result.FromError(messageResult);
|
if (!messageResult.IsDefined(out var message)) return Result.FromError(messageResult);
|
||||||
if (string.IsNullOrWhiteSpace(message.Content)
|
if (message.Content == newContent) return Result.FromSuccess();
|
||||||
|| string.IsNullOrWhiteSpace(newContent)
|
|
||||||
|| message.Content == newContent) return Result.FromSuccess();
|
|
||||||
|
|
||||||
await _cacheService.EvictAsync<IMessage>(cacheKey, ct);
|
await _cacheService.EvictAsync<IMessage>(cacheKey, ct);
|
||||||
var newMessageResult = await _channelApi.GetChannelMessageAsync(channelId, messageId, ct);
|
var newMessageResult = await _channelApi.GetChannelMessageAsync(channelId, messageId, ct);
|
||||||
if (!newMessageResult.IsDefined(out var newMessage)) return Result.FromError(newMessageResult);
|
if (!newMessageResult.IsDefined(out var newMessage)) return Result.FromError(newMessageResult);
|
||||||
// No need to await the recache since we don't depend on it
|
await _cacheService.CacheAsync(cacheKey, newMessage, ct);
|
||||||
_ = _cacheService.CacheAsync(cacheKey, newMessage, ct);
|
|
||||||
|
|
||||||
var logChannelResult = guildId.GetConfigChannel("PrivateFeedbackChannel");
|
|
||||||
if (!logChannelResult.IsDefined(out var logChannel)) return Result.FromSuccess();
|
|
||||||
|
|
||||||
var currentUserResult = await _cacheService.TryGetValueAsync<IUser>(
|
var currentUserResult = await _cacheService.TryGetValueAsync<IUser>(
|
||||||
new KeyHelpers.CurrentUserCacheKey(), ct);
|
new KeyHelpers.CurrentUserCacheKey(), ct);
|
||||||
|
@ -167,7 +177,7 @@ public class MessageEditedResponder : IResponder<IMessageUpdate> {
|
||||||
|
|
||||||
var diff = new SideBySideDiffBuilder(Differ.Instance).BuildDiffModel(message.Content, newContent, true, true);
|
var diff = new SideBySideDiffBuilder(Differ.Instance).BuildDiffModel(message.Content, newContent, true, true);
|
||||||
|
|
||||||
Messages.Culture = guildId.GetGuildCulture();
|
Messages.Culture = guildConfiguration.Culture;
|
||||||
|
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
.WithSmallTitle(
|
.WithSmallTitle(
|
||||||
|
@ -181,30 +191,35 @@ public class MessageEditedResponder : IResponder<IMessageUpdate> {
|
||||||
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
||||||
|
|
||||||
return (Result)await _channelApi.CreateMessageAsync(
|
return (Result)await _channelApi.CreateMessageAsync(
|
||||||
logChannel, embeds: new[] { built }, allowedMentions: Boyfriend.NoMentions, ct: ct);
|
guildConfiguration.PrivateFeedbackChannel.ToDiscordSnowflake(), embeds: new[] { built },
|
||||||
|
allowedMentions: Boyfriend.NoMentions, ct: ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GuildMemberAddResponder : IResponder<IGuildMemberAdd> {
|
public class GuildMemberAddResponder : IResponder<IGuildMemberAdd> {
|
||||||
private readonly CacheService _cacheService;
|
private readonly CacheService _cacheService;
|
||||||
private readonly IDiscordRestChannelAPI _channelApi;
|
private readonly IDiscordRestChannelAPI _channelApi;
|
||||||
|
private readonly GuildDataService _dataService;
|
||||||
|
|
||||||
public GuildMemberAddResponder(CacheService cacheService, IDiscordRestChannelAPI channelApi) {
|
public GuildMemberAddResponder(
|
||||||
|
CacheService cacheService, IDiscordRestChannelAPI channelApi, GuildDataService dataService) {
|
||||||
_cacheService = cacheService;
|
_cacheService = cacheService;
|
||||||
_channelApi = channelApi;
|
_channelApi = channelApi;
|
||||||
|
_dataService = dataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> RespondAsync(IGuildMemberAdd gatewayEvent, CancellationToken ct = default) {
|
public async Task<Result> RespondAsync(IGuildMemberAdd gatewayEvent, CancellationToken ct = default) {
|
||||||
if (!gatewayEvent.GuildID.GetConfigString("WelcomeMessage").IsDefined(out var welcomeMessage)
|
var guildConfiguration = await _dataService.GetConfiguration(gatewayEvent.GuildID, ct);
|
||||||
|| welcomeMessage is "off" or "disable" or "disabled")
|
if (guildConfiguration.PublicFeedbackChannel is null)
|
||||||
|
return Result.FromSuccess();
|
||||||
|
if (guildConfiguration.WelcomeMessage is null or "off" or "disable" or "disabled")
|
||||||
return Result.FromSuccess();
|
return Result.FromSuccess();
|
||||||
if (welcomeMessage is "default" or "reset") {
|
|
||||||
Messages.Culture = gatewayEvent.GuildID.GetGuildCulture();
|
|
||||||
welcomeMessage = Messages.DefaultWelcomeMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gatewayEvent.GuildID.GetConfigChannel("PublicFeedbackChannel").IsDefined(out var channel))
|
Messages.Culture = guildConfiguration.Culture;
|
||||||
return Result.FromSuccess();
|
var welcomeMessage = guildConfiguration.WelcomeMessage is "default" or "reset"
|
||||||
|
? Messages.DefaultWelcomeMessage
|
||||||
|
: guildConfiguration.WelcomeMessage;
|
||||||
|
|
||||||
if (!gatewayEvent.User.IsDefined(out var user))
|
if (!gatewayEvent.User.IsDefined(out var user))
|
||||||
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.User)));
|
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.User)));
|
||||||
|
|
||||||
|
@ -221,25 +236,30 @@ public class GuildMemberAddResponder : IResponder<IGuildMemberAdd> {
|
||||||
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
||||||
|
|
||||||
return (Result)await _channelApi.CreateMessageAsync(
|
return (Result)await _channelApi.CreateMessageAsync(
|
||||||
channel, embeds: new[] { built }, allowedMentions: Boyfriend.NoMentions, ct: ct);
|
guildConfiguration.PublicFeedbackChannel.ToDiscordSnowflake(), embeds: new[] { built },
|
||||||
|
allowedMentions: Boyfriend.NoMentions, ct: ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GuildScheduledEventCreateResponder : IResponder<IGuildScheduledEventCreate> {
|
public class GuildScheduledEventCreateResponder : IResponder<IGuildScheduledEventCreate> {
|
||||||
private readonly CacheService _cacheService;
|
private readonly CacheService _cacheService;
|
||||||
private readonly IDiscordRestChannelAPI _channelApi;
|
private readonly IDiscordRestChannelAPI _channelApi;
|
||||||
|
private readonly GuildDataService _dataService;
|
||||||
private readonly IDiscordRestUserAPI _userApi;
|
private readonly IDiscordRestUserAPI _userApi;
|
||||||
|
|
||||||
public GuildScheduledEventCreateResponder(
|
public GuildScheduledEventCreateResponder(
|
||||||
CacheService cacheService, IDiscordRestChannelAPI channelApi, IDiscordRestUserAPI userApi) {
|
CacheService cacheService, IDiscordRestChannelAPI channelApi, GuildDataService dataService,
|
||||||
|
IDiscordRestUserAPI userApi) {
|
||||||
_cacheService = cacheService;
|
_cacheService = cacheService;
|
||||||
_channelApi = channelApi;
|
_channelApi = channelApi;
|
||||||
|
_dataService = dataService;
|
||||||
_userApi = userApi;
|
_userApi = userApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> RespondAsync(IGuildScheduledEventCreate gatewayEvent, CancellationToken ct = default) {
|
public async Task<Result> RespondAsync(IGuildScheduledEventCreate gatewayEvent, CancellationToken ct = default) {
|
||||||
var channelResult = gatewayEvent.GuildID.GetConfigChannel("EventNotificationChannel");
|
var guildConfiguration = await _dataService.GetConfiguration(gatewayEvent.GuildID, ct);
|
||||||
if (!channelResult.IsDefined(out var channel)) return Result.FromSuccess();
|
if (guildConfiguration.EventNotificationChannel is null)
|
||||||
|
return Result.FromSuccess();
|
||||||
|
|
||||||
var currentUserResult = await _cacheService.TryGetValueAsync<IUser>(
|
var currentUserResult = await _cacheService.TryGetValueAsync<IUser>(
|
||||||
new KeyHelpers.CurrentUserCacheKey(), ct);
|
new KeyHelpers.CurrentUserCacheKey(), ct);
|
||||||
|
@ -250,7 +270,7 @@ public class GuildScheduledEventCreateResponder : IResponder<IGuildScheduledEven
|
||||||
var creatorResult = await creatorId.Value.TryGetUserAsync(_cacheService, _userApi, ct);
|
var creatorResult = await creatorId.Value.TryGetUserAsync(_cacheService, _userApi, ct);
|
||||||
if (!creatorResult.IsDefined(out var creator)) return Result.FromError(creatorResult);
|
if (!creatorResult.IsDefined(out var creator)) return Result.FromError(creatorResult);
|
||||||
|
|
||||||
Messages.Culture = gatewayEvent.GuildID.GetGuildCulture();
|
Messages.Culture = guildConfiguration.Culture;
|
||||||
|
|
||||||
string embedDescription;
|
string embedDescription;
|
||||||
var eventDescription = gatewayEvent.Description is { HasValue: true, Value: not null }
|
var eventDescription = gatewayEvent.Description is { HasValue: true, Value: not null }
|
||||||
|
@ -308,6 +328,7 @@ public class GuildScheduledEventCreateResponder : IResponder<IGuildScheduledEven
|
||||||
);
|
);
|
||||||
|
|
||||||
return (Result)await _channelApi.CreateMessageAsync(
|
return (Result)await _channelApi.CreateMessageAsync(
|
||||||
channel, embeds: new[] { built }, components: new[] { new ActionRowComponent(new[] { button }) }, ct: ct);
|
guildConfiguration.EventNotificationChannel.ToDiscordSnowflake(), embeds: new[] { built },
|
||||||
|
components: new[] { new ActionRowComponent(new[] { button }) }, ct: ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
using System.Globalization;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using DiffPlex.DiffBuilder.Model;
|
using DiffPlex.DiffBuilder.Model;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
using Remora.Discord.API.Abstractions.Rest;
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
|
@ -16,34 +14,6 @@ using Remora.Results;
|
||||||
namespace Boyfriend;
|
namespace Boyfriend;
|
||||||
|
|
||||||
public static class Extensions {
|
public static class Extensions {
|
||||||
private static readonly Dictionary<string, CultureInfo> CultureInfoCache = new() {
|
|
||||||
{ "en", new CultureInfo("en-US") },
|
|
||||||
{ "ru", new CultureInfo("ru-RU") },
|
|
||||||
{ "mctaylors-ru", new CultureInfo("tt-RU") }
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Result<bool> GetConfigBool(this IGuild guild, string key) {
|
|
||||||
var value = Boyfriend.GuildConfiguration.GetValue<bool?>($"GuildConfigs:{guild.ID}:{key}");
|
|
||||||
return value is not null ? Result<bool>.FromSuccess(value.Value) : Result<bool>.FromError(new NotFoundError());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Result<Snowflake> GetConfigChannel(this Snowflake guildId, string key) {
|
|
||||||
var value = Boyfriend.GuildConfiguration.GetValue<ulong?>($"GuildConfigs:{guildId}:{key}");
|
|
||||||
return value is not null
|
|
||||||
? Result<Snowflake>.FromSuccess(DiscordSnowflake.New(value.Value))
|
|
||||||
: Result<Snowflake>.FromError(new NotFoundError());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Result<string> GetConfigString(this Snowflake guildId, string key) {
|
|
||||||
var value = Boyfriend.GuildConfiguration.GetValue<string?>($"GuildConfigs:{guildId}:{key}");
|
|
||||||
return value is not null ? Result<string>.FromSuccess(value) : Result<string>.FromError(new NotFoundError());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CultureInfo GetGuildCulture(this Snowflake guildId) {
|
|
||||||
var value = Boyfriend.GuildConfiguration.GetValue<string?>($"GuildConfigs:{guildId}:Language");
|
|
||||||
return value is not null ? CultureInfoCache[value] : CultureInfoCache["en"];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<Result<IUser>> TryGetUserAsync(
|
public static async Task<Result<IUser>> TryGetUserAsync(
|
||||||
this Snowflake userId, CacheService cacheService, IDiscordRestUserAPI userApi, CancellationToken ct) {
|
this Snowflake userId, CacheService cacheService, IDiscordRestUserAPI userApi, CancellationToken ct) {
|
||||||
var cachedUserResult = await cacheService.TryGetValueAsync<IUser>(
|
var cachedUserResult = await cacheService.TryGetValueAsync<IUser>(
|
||||||
|
@ -118,4 +88,8 @@ public static class Extensions {
|
||||||
public static string GetTag(this IUser user) {
|
public static string GetTag(this IUser user) {
|
||||||
return $"{user.Username}#{user.Discriminator:0000}";
|
return $"{user.Username}#{user.Discriminator:0000}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Snowflake ToDiscordSnowflake(this ulong? id) {
|
||||||
|
return DiscordSnowflake.New(id ?? 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
35
Messages.Designer.cs
generated
35
Messages.Designer.cs
generated
|
@ -7,36 +7,41 @@
|
||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Resources;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Boyfriend {
|
namespace Boyfriend {
|
||||||
using System;
|
[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[DebuggerNonUserCode()]
|
||||||
|
[CompilerGenerated()]
|
||||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
|
||||||
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
|
||||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
|
||||||
internal class Messages {
|
internal class Messages {
|
||||||
|
|
||||||
private static System.Resources.ResourceManager resourceMan;
|
private static ResourceManager resourceMan;
|
||||||
|
|
||||||
private static System.Globalization.CultureInfo resourceCulture;
|
private static CultureInfo resourceCulture;
|
||||||
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
internal Messages() {
|
internal Messages() {
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||||
internal static System.Resources.ResourceManager ResourceManager {
|
internal static ResourceManager ResourceManager {
|
||||||
get {
|
get {
|
||||||
if (object.Equals(null, resourceMan)) {
|
if (Equals(null, resourceMan)) {
|
||||||
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Boyfriend.Messages", typeof(Messages).Assembly);
|
ResourceManager temp = new ResourceManager("Boyfriend.Messages", typeof(Messages).Assembly);
|
||||||
resourceMan = temp;
|
resourceMan = temp;
|
||||||
}
|
}
|
||||||
return resourceMan;
|
return resourceMan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||||
internal static System.Globalization.CultureInfo Culture {
|
internal static CultureInfo Culture {
|
||||||
get {
|
get {
|
||||||
return resourceCulture;
|
return resourceCulture;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue