diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 0a1ec81..bf444a9 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -18,3 +18,5 @@ P:System.DateTime.Now;Use System.DateTime.UtcNow instead. P:System.DateTimeOffset.Now;Use System.DateTimeOffset.UtcNow instead. P:System.DateTimeOffset.DateTime;Use System.DateTimeOffset.UtcDateTime instead. M:System.IO.File.OpenWrite(System.String);File.OpenWrite(string) does not clear the file before writing to it. Use File.Create(string) instead. +M:System.Threading.Thread.Sleep(System.Int32);Use Task.Delay(int, CancellationToken) instead. +M:System.Threading.Thread.Sleep(System.TimeSpan);Use Task.Delay(TimeSpan, CancellationToken) instead. diff --git a/Octobot.csproj b/Octobot.csproj index 5258c89..e8f0dfa 100644 --- a/Octobot.csproj +++ b/Octobot.csproj @@ -20,7 +20,7 @@ - + diff --git a/locale/Messages.resx b/locale/Messages.resx index b546531..b7421f6 100644 --- a/locale/Messages.resx +++ b/locale/Messages.resx @@ -400,7 +400,7 @@ Octobot's source code - About Octobot + About {0} developer & designer, Octobot's Wiki creator @@ -588,4 +588,7 @@ Incorrect time specified. + + Kicked + diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx index 0616f58..4b3541a 100644 --- a/locale/Messages.ru.resx +++ b/locale/Messages.ru.resx @@ -400,7 +400,7 @@ Исходный код Octobot - Об Octobot + О боте {0} разработчик @@ -588,4 +588,7 @@ Указано некорректное время. + + Выгнан + diff --git a/locale/Messages.tt-ru.resx b/locale/Messages.tt-ru.resx index 7c01950..ca3c19d 100644 --- a/locale/Messages.tt-ru.resx +++ b/locale/Messages.tt-ru.resx @@ -400,7 +400,7 @@ репа Octobot (тык) - немного об Octobot + немного об {0} скучный девелопер + дизайнер создавший Octobot's Wiki @@ -502,7 +502,7 @@ приколы полученные по заслугам - забанен + пермабан вышел из сервера @@ -588,4 +588,7 @@ ты там правильно напиши таймспан + + кикнут + diff --git a/src/Commands/AboutCommandGroup.cs b/src/Commands/AboutCommandGroup.cs index 2c1e770..4c396d9 100644 --- a/src/Commands/AboutCommandGroup.cs +++ b/src/Commands/AboutCommandGroup.cs @@ -15,6 +15,7 @@ using Remora.Discord.Commands.Contexts; using Remora.Discord.Commands.Feedback.Messages; using Remora.Discord.Commands.Feedback.Services; using Remora.Discord.Extensions.Embeds; +using Remora.Discord.Extensions.Formatting; using Remora.Rest.Core; using Remora.Results; @@ -27,11 +28,11 @@ namespace Octobot.Commands; public class AboutCommandGroup : CommandGroup { private static readonly (string Username, Snowflake Id)[] Developers = - { + [ ("Octol1ttle", new Snowflake(504343489664909322)), ("mctaylors", new Snowflake(326642240229474304)), ("neroduckale", new Snowflake(474943797063843851)) - }; + ]; private readonly ICommandContext _context; private readonly IFeedbackService _feedback; @@ -88,12 +89,15 @@ public class AboutCommandGroup : CommandGroup { var guildMemberResult = await _guildApi.GetGuildMemberAsync( guildId, dev.Id, ct); - var tag = guildMemberResult.IsSuccess ? $"<@{dev.Id}>" : $"@{dev.Username}"; + var tag = guildMemberResult.IsSuccess + ? $"<@{dev.Id}>" + : Markdown.Hyperlink($"@{dev.Username}", $"https://github.com/{dev.Username}"); builder.AppendBulletPointLine($"{tag} — {$"AboutDeveloper@{dev.Username}".Localized()}"); } - var embed = new EmbedBuilder().WithSmallTitle(Messages.AboutBot, bot) + var embed = new EmbedBuilder() + .WithSmallTitle(string.Format(Messages.AboutBot, bot.Username), bot) .WithDescription(builder.ToString()) .WithColour(ColorsList.Cyan) .WithImageUrl("https://cdn.mctaylors.ru/octobot-banner.png") diff --git a/src/Commands/BanCommandGroup.cs b/src/Commands/BanCommandGroup.cs index f0da978..bbcf459 100644 --- a/src/Commands/BanCommandGroup.cs +++ b/src/Commands/BanCommandGroup.cs @@ -33,12 +33,12 @@ public class BanCommandGroup : CommandGroup private readonly IDiscordRestGuildAPI _guildApi; private readonly GuildDataService _guildData; private readonly IDiscordRestUserAPI _userApi; - private readonly UtilityService _utility; + private readonly Utility _utility; public BanCommandGroup( ICommandContext context, IDiscordRestChannelAPI channelApi, GuildDataService guildData, IFeedbackService feedback, IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi, - UtilityService utility) + Utility utility) { _context = context; _channelApi = channelApi; diff --git a/src/Commands/ClearCommandGroup.cs b/src/Commands/ClearCommandGroup.cs index 7ebd4ea..1d0ad64 100644 --- a/src/Commands/ClearCommandGroup.cs +++ b/src/Commands/ClearCommandGroup.cs @@ -30,11 +30,11 @@ public class ClearCommandGroup : CommandGroup private readonly IFeedbackService _feedback; private readonly GuildDataService _guildData; private readonly IDiscordRestUserAPI _userApi; - private readonly UtilityService _utility; + private readonly Utility _utility; public ClearCommandGroup( IDiscordRestChannelAPI channelApi, ICommandContext context, GuildDataService guildData, - IFeedbackService feedback, IDiscordRestUserAPI userApi, UtilityService utility) + IFeedbackService feedback, IDiscordRestUserAPI userApi, Utility utility) { _channelApi = channelApi; _context = context; diff --git a/src/Commands/KickCommandGroup.cs b/src/Commands/KickCommandGroup.cs index cad8ea9..a278fb4 100644 --- a/src/Commands/KickCommandGroup.cs +++ b/src/Commands/KickCommandGroup.cs @@ -30,12 +30,12 @@ public class KickCommandGroup : CommandGroup private readonly IDiscordRestGuildAPI _guildApi; private readonly GuildDataService _guildData; private readonly IDiscordRestUserAPI _userApi; - private readonly UtilityService _utility; + private readonly Utility _utility; public KickCommandGroup( ICommandContext context, IDiscordRestChannelAPI channelApi, GuildDataService guildData, IFeedbackService feedback, IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi, - UtilityService utility) + Utility utility) { _context = context; _channelApi = channelApi; @@ -151,7 +151,9 @@ public class KickCommandGroup : CommandGroup return Result.FromError(kickResult.Error); } - data.GetOrCreateMemberData(target.ID).Roles.Clear(); + var memberData = data.GetOrCreateMemberData(target.ID); + memberData.Roles.Clear(); + memberData.Kicked = true; var title = string.Format(Messages.UserKicked, target.GetTag()); var description = MarkdownExtensions.BulletPoint(string.Format(Messages.DescriptionActionReason, reason)); diff --git a/src/Commands/MuteCommandGroup.cs b/src/Commands/MuteCommandGroup.cs index 6a28f38..c7b21f6 100644 --- a/src/Commands/MuteCommandGroup.cs +++ b/src/Commands/MuteCommandGroup.cs @@ -32,11 +32,11 @@ public class MuteCommandGroup : CommandGroup private readonly IDiscordRestGuildAPI _guildApi; private readonly GuildDataService _guildData; private readonly IDiscordRestUserAPI _userApi; - private readonly UtilityService _utility; + private readonly Utility _utility; public MuteCommandGroup( ICommandContext context, GuildDataService guildData, IFeedbackService feedback, - IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi, UtilityService utility) + IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi, Utility utility) { _context = context; _guildData = guildData; @@ -300,9 +300,9 @@ public class MuteCommandGroup : CommandGroup } var memberData = data.GetOrCreateMemberData(target.ID); - var isMuted = memberData.MutedUntil is not null || communicationDisabledUntil is not null; + var wasMuted = memberData.MutedUntil is not null || communicationDisabledUntil is not null; - if (!isMuted) + if (!wasMuted) { var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.UserNotMuted, bot) .WithColour(ColorsList.Red).Build(); diff --git a/src/Commands/SettingsCommandGroup.cs b/src/Commands/SettingsCommandGroup.cs index a8891bd..ce7472f 100644 --- a/src/Commands/SettingsCommandGroup.cs +++ b/src/Commands/SettingsCommandGroup.cs @@ -36,7 +36,7 @@ public class SettingsCommandGroup : CommandGroup /// that the orders match. /// private static readonly IOption[] AllOptions = - { + [ GuildSettings.Language, GuildSettings.WelcomeMessage, GuildSettings.ReceiveStartupMessages, @@ -51,17 +51,17 @@ public class SettingsCommandGroup : CommandGroup GuildSettings.MuteRole, GuildSettings.EventNotificationRole, GuildSettings.EventEarlyNotificationOffset - }; + ]; private readonly ICommandContext _context; private readonly IFeedbackService _feedback; private readonly GuildDataService _guildData; private readonly IDiscordRestUserAPI _userApi; - private readonly UtilityService _utility; + private readonly Utility _utility; public SettingsCommandGroup( ICommandContext context, GuildDataService guildData, - IFeedbackService feedback, IDiscordRestUserAPI userApi, UtilityService utility) + IFeedbackService feedback, IDiscordRestUserAPI userApi, Utility utility) { _context = context; _guildData = guildData; diff --git a/src/Commands/ToolsCommandGroup.cs b/src/Commands/ToolsCommandGroup.cs index 78058cb..1dbf72d 100644 --- a/src/Commands/ToolsCommandGroup.cs +++ b/src/Commands/ToolsCommandGroup.cs @@ -122,32 +122,21 @@ public class ToolsCommandGroup : CommandGroup embedColor = AppendGuildInformation(embedColor, guildMember, builder); } - var isMuted = (memberData.MutedUntil is not null && DateTimeOffset.UtcNow <= memberData.MutedUntil) || - communicationDisabledUntil is not null; + var wasMuted = (memberData.MutedUntil is not null && DateTimeOffset.UtcNow <= memberData.MutedUntil) || + communicationDisabledUntil is not null; + var wasBanned = memberData.BannedUntil is not null; + var wasKicked = memberData.Kicked; - var existingBanResult = await _guildApi.GetGuildBanAsync(guildId, target.ID, ct); - - if (isMuted || existingBanResult.IsDefined()) + if (wasMuted || wasBanned || wasKicked) { builder.Append("### ") .AppendLine(Markdown.Bold(Messages.UserInfoPunishments)); + + embedColor = AppendPunishmentsInformation(wasMuted, wasKicked, wasBanned, memberData, + builder, embedColor, communicationDisabledUntil); } - if (isMuted) - { - AppendMuteInformation(memberData, communicationDisabledUntil, builder); - - embedColor = ColorsList.Red; - } - - if (existingBanResult.IsDefined()) - { - AppendBanInformation(memberData, builder); - - embedColor = ColorsList.Black; - } - - if (!guildMemberResult.IsSuccess && !existingBanResult.IsDefined()) + if (!guildMemberResult.IsSuccess && !wasBanned) { builder.Append("### ") .AppendLine(Markdown.Bold(Messages.UserInfoNotOnGuild)); @@ -166,6 +155,29 @@ public class ToolsCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct); } + private static Color AppendPunishmentsInformation(bool wasMuted, bool wasKicked, bool wasBanned, + MemberData memberData, StringBuilder builder, Color embedColor, DateTimeOffset? communicationDisabledUntil) + { + if (wasMuted) + { + AppendMuteInformation(memberData, communicationDisabledUntil, builder); + embedColor = ColorsList.Red; + } + + if (wasKicked) + { + builder.AppendBulletPointLine(Messages.UserInfoKicked); + } + + if (wasBanned) + { + AppendBanInformation(memberData, builder); + embedColor = ColorsList.Black; + } + + return embedColor; + } + private static Color AppendGuildInformation(Color color, IGuildMember guildMember, StringBuilder builder) { if (guildMember.Nickname.IsDefined(out var nickname)) @@ -393,7 +405,7 @@ public class ToolsCommandGroup : CommandGroup } private static readonly TimestampStyle[] AllStyles = - { + [ TimestampStyle.ShortDate, TimestampStyle.LongDate, TimestampStyle.ShortTime, @@ -401,7 +413,7 @@ public class ToolsCommandGroup : CommandGroup TimestampStyle.ShortDateTime, TimestampStyle.LongDateTime, TimestampStyle.RelativeTime - }; + ]; /// /// A slash command that shows the current timestamp with an optional offset in all styles supported by Discord. diff --git a/src/Data/MemberData.cs b/src/Data/MemberData.cs index 0b0cfb2..8e23e54 100644 --- a/src/Data/MemberData.cs +++ b/src/Data/MemberData.cs @@ -18,6 +18,7 @@ public sealed class MemberData public ulong Id { get; } public DateTimeOffset? BannedUntil { get; set; } public DateTimeOffset? MutedUntil { get; set; } + public bool Kicked { get; set; } public List Roles { get; set; } = []; public List Reminders { get; } = []; } diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs index 682bc6b..f5a06c0 100644 --- a/src/Messages.Designer.cs +++ b/src/Messages.Designer.cs @@ -1044,5 +1044,13 @@ namespace Octobot { return ResourceManager.GetString("InvalidTimeSpan", resourceCulture); } } + + internal static string UserInfoKicked + { + get + { + return ResourceManager.GetString("UserInfoKicked", resourceCulture); + } + } } } diff --git a/src/Octobot.cs b/src/Octobot.cs index 1806330..063bd14 100644 --- a/src/Octobot.cs +++ b/src/Octobot.cs @@ -2,11 +2,10 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Octobot.Commands; using Octobot.Commands.Events; using Octobot.Services; +using Octobot.Services.Profiler; using Octobot.Services.Update; -using Remora.Commands.Extensions; using Remora.Discord.API.Abstractions.Gateway.Commands; using Remora.Discord.API.Abstractions.Objects; using Remora.Discord.API.Objects; @@ -14,8 +13,8 @@ using Remora.Discord.Caching.Extensions; using Remora.Discord.Caching.Services; using Remora.Discord.Commands.Extensions; using Remora.Discord.Commands.Services; +using Remora.Discord.Extensions.Extensions; using Remora.Discord.Gateway; -using Remora.Discord.Gateway.Extensions; using Remora.Discord.Hosting.Extensions; using Remora.Rest.Core; using Serilog.Extensions.Logging; @@ -24,12 +23,12 @@ namespace Octobot; public sealed class Octobot { - public static readonly AllowedMentions NoMentions = new( - Array.Empty(), Array.Empty(), Array.Empty()); - public const string RepositoryUrl = "https://github.com/LabsDevelopment/Octobot"; public const string IssuesUrl = $"{RepositoryUrl}/issues"; + public static readonly AllowedMentions NoMentions = new( + Array.Empty(), Array.Empty(), Array.Empty()); + public static async Task Main(string[] args) { var host = CreateHostBuilder(args).UseConsoleLifetime().Build(); @@ -82,34 +81,20 @@ public sealed class Octobot // Init .AddDiscordCaching() .AddDiscordCommands(true, false) + .AddRespondersFromAssembly(typeof(Octobot).Assembly) + .AddCommandGroupsFromAssembly(typeof(Octobot).Assembly) // Slash command event handlers .AddPreparationErrorEvent() .AddPostExecutionEvent() // Services + .AddTransient() + .AddSingleton() + .AddSingleton() .AddSingleton() - .AddSingleton() + .AddHostedService(provider => provider.GetRequiredService()) .AddHostedService() .AddHostedService() - .AddHostedService() - .AddHostedService() - // Slash commands - .AddCommandTree() - .WithCommandGroup() - .WithCommandGroup() - .WithCommandGroup() - .WithCommandGroup() - .WithCommandGroup() - .WithCommandGroup() - .WithCommandGroup() - .WithCommandGroup() - .WithCommandGroup(); - var responderTypes = typeof(Octobot).Assembly - .GetExportedTypes() - .Where(t => t.IsResponder()); - foreach (var responderType in responderTypes) - { - services.AddResponder(responderType); - } + .AddHostedService(); } ).ConfigureLogging( c => c.AddConsole() diff --git a/src/Responders/GuildLoadedResponder.cs b/src/Responders/GuildLoadedResponder.cs index 2d66a3b..a1e7d16 100644 --- a/src/Responders/GuildLoadedResponder.cs +++ b/src/Responders/GuildLoadedResponder.cs @@ -25,11 +25,11 @@ public class GuildLoadedResponder : IResponder private readonly GuildDataService _guildData; private readonly ILogger _logger; private readonly IDiscordRestUserAPI _userApi; - private readonly UtilityService _utility; + private readonly Utility _utility; public GuildLoadedResponder( IDiscordRestChannelAPI channelApi, GuildDataService guildData, ILogger logger, - IDiscordRestUserAPI userApi, UtilityService utility) + IDiscordRestUserAPI userApi, Utility utility) { _channelApi = channelApi; _guildData = guildData; diff --git a/src/Responders/GuildMemberJoinedResponder.cs b/src/Responders/GuildMemberJoinedResponder.cs index 66faa28..eee93b6 100644 --- a/src/Responders/GuildMemberJoinedResponder.cs +++ b/src/Responders/GuildMemberJoinedResponder.cs @@ -43,6 +43,8 @@ public class GuildMemberJoinedResponder : IResponder var cfg = data.Settings; var memberData = data.GetOrCreateMemberData(user.ID); + memberData.Kicked = false; + var returnRolesResult = await TryReturnRolesAsync(cfg, memberData, gatewayEvent.GuildID, user.ID, ct); if (!returnRolesResult.IsSuccess) { diff --git a/src/Services/BackgroundGuildDataSaverService.cs b/src/Services/BackgroundGuildDataSaverService.cs deleted file mode 100644 index 766ffe0..0000000 --- a/src/Services/BackgroundGuildDataSaverService.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace Octobot.Services; - -public sealed class BackgroundGuildDataSaverService : BackgroundService -{ - private readonly GuildDataService _guildData; - - public BackgroundGuildDataSaverService(GuildDataService guildData) - { - _guildData = guildData; - } - - protected override async Task ExecuteAsync(CancellationToken ct) - { - using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5)); - - while (await timer.WaitForNextTickAsync(ct)) - { - await _guildData.SaveAsync(ct); - } - } -} diff --git a/src/Services/GuildDataService.cs b/src/Services/GuildDataService.cs index 3cc8cea..c9458a0 100644 --- a/src/Services/GuildDataService.cs +++ b/src/Services/GuildDataService.cs @@ -11,35 +11,23 @@ namespace Octobot.Services; /// /// Handles saving, loading, initializing and providing . /// -public sealed class GuildDataService : IHostedService +public sealed class GuildDataService : BackgroundService { private readonly ConcurrentDictionary _datas = new(); private readonly ILogger _logger; - // https://github.com/dotnet/aspnetcore/issues/39139 - public GuildDataService( - IHostApplicationLifetime lifetime, ILogger logger) + public GuildDataService(ILogger logger) { _logger = logger; - lifetime.ApplicationStopping.Register(ApplicationStopping); } - public Task StartAsync(CancellationToken ct) + public override Task StopAsync(CancellationToken ct) { - return Task.CompletedTask; + base.StopAsync(ct); + return SaveAsync(ct); } - public Task StopAsync(CancellationToken ct) - { - return Task.CompletedTask; - } - - private void ApplicationStopping() - { - SaveAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - - public Task SaveAsync(CancellationToken ct) + private Task SaveAsync(CancellationToken ct) { var tasks = new List(); var datas = _datas.Values.ToArray(); @@ -68,6 +56,16 @@ public sealed class GuildDataService : IHostedService File.Delete(tempFilePath); } + protected override async Task ExecuteAsync(CancellationToken ct) + { + using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5)); + + while (await timer.WaitForNextTickAsync(ct)) + { + await SaveAsync(ct); + } + } + public async Task GetData(Snowflake guildId, CancellationToken ct = default) { return _datas.TryGetValue(guildId, out var data) ? data : await InitializeData(guildId, ct); diff --git a/src/Services/Profiler/Profiler.cs b/src/Services/Profiler/Profiler.cs new file mode 100644 index 0000000..8d4ca98 --- /dev/null +++ b/src/Services/Profiler/Profiler.cs @@ -0,0 +1,114 @@ +using System.Diagnostics; +using System.Text; +using Microsoft.Extensions.Logging; +using Remora.Results; + +// TODO: remove in future profiler PRs +// ReSharper disable All + +namespace Octobot.Services.Profiler; + +/// +/// Provides the ability to profile how long certain parts of code take to complete using es. +/// +/// Resolve instead in singletons. +public sealed class Profiler +{ + private const int MaxProfilerTime = 1000; // milliseconds + private readonly List _events = []; + private readonly ILogger _logger; + + public Profiler(ILogger logger) + { + _logger = logger; + } + + /// + /// Pushes an event to the profiler. + /// + /// The ID of the event. + public void Push(string id) + { + _events.Add(new ProfilerEvent + { + Id = id, + Stopwatch = Stopwatch.StartNew() + }); + } + + /// + /// Pops the last pushed event from the profiler. + /// + /// Thrown if the profiler contains no events. + public void Pop() + { + if (_events.Count is 0) + { + throw new InvalidOperationException("Nothing to pop"); + } + + _events.Last().Stopwatch.Stop(); + } + + /// + /// If the profiler took too long to execute, this will log a warning with per-event time usage + /// + /// + private void Report() + { + var main = _events[0]; + if (main.Stopwatch.ElapsedMilliseconds < MaxProfilerTime) + { + return; + } + + var unprofiled = main.Stopwatch.ElapsedMilliseconds; + var builder = new StringBuilder().AppendLine(); + for (var i = 1; i < _events.Count; i++) + { + var profilerEvent = _events[i]; + if (profilerEvent.Stopwatch.IsRunning) + { + throw new InvalidOperationException( + $"Tried to report on a profiler with running stopwatches: {profilerEvent.Id}"); + } + + builder.AppendLine($"{profilerEvent.Id}: {profilerEvent.Stopwatch.ElapsedMilliseconds}ms"); + unprofiled -= profilerEvent.Stopwatch.ElapsedMilliseconds; + } + + builder.AppendLine($": {unprofiled}ms"); + + _logger.LogWarning("Profiler {ID} took {Elapsed} milliseconds to execute (max: {Max}ms):{Events}", main.Id, + main.Stopwatch.ElapsedMilliseconds, MaxProfilerTime, builder.ToString()); + } + + /// + /// the profiler and on it afterwards. + /// + public void PopAndReport() + { + Pop(); + Report(); + } + + /// + /// on the profiler and return a . + /// + /// + /// + public Result ReportWithResult(Result result) + { + PopAndReport(); + return result; + } + + /// + /// Calls with + /// + /// A successful result. + public Result ReportWithSuccess() + { + return ReportWithResult(Result.FromSuccess()); + } +} diff --git a/src/Services/Profiler/ProfilerEvent.cs b/src/Services/Profiler/ProfilerEvent.cs new file mode 100644 index 0000000..f655fc4 --- /dev/null +++ b/src/Services/Profiler/ProfilerEvent.cs @@ -0,0 +1,9 @@ +using System.Diagnostics; + +namespace Octobot.Services.Profiler; + +public struct ProfilerEvent +{ + public string Id { get; init; } + public Stopwatch Stopwatch { get; init; } +} diff --git a/src/Services/Profiler/ProfilerFactory.cs b/src/Services/Profiler/ProfilerFactory.cs new file mode 100644 index 0000000..0135771 --- /dev/null +++ b/src/Services/Profiler/ProfilerFactory.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Octobot.Services.Profiler; + +/// +/// Provides a method to create a . Useful in singletons. +/// +public sealed class ProfilerFactory +{ + private readonly IServiceScopeFactory _scopeFactory; + + public ProfilerFactory(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + /// + /// Creates a new . + /// + /// A new . + // TODO: remove in future profiler PRs + // ReSharper disable once UnusedMember.Global + public Profiler Create() + { + return _scopeFactory.CreateScope().ServiceProvider.GetRequiredService(); + } +} diff --git a/src/Services/Update/MemberUpdateService.cs b/src/Services/Update/MemberUpdateService.cs index e7860ae..06e531f 100644 --- a/src/Services/Update/MemberUpdateService.cs +++ b/src/Services/Update/MemberUpdateService.cs @@ -16,7 +16,7 @@ namespace Octobot.Services.Update; public sealed partial class MemberUpdateService : BackgroundService { private static readonly string[] GenericNicknames = - { + [ "Albatross", "Alpha", "Anchor", "Banjo", "Bell", "Beta", "Blackbird", "Bulldog", "Canary", "Cat", "Calf", "Cyclone", "Daisy", "Dalmatian", "Dart", "Delta", "Diamond", "Donkey", "Duck", "Emu", "Eclipse", "Flamingo", "Flute", "Frog", "Goose", "Hatchet", "Heron", "Husky", "Hurricane", @@ -24,16 +24,16 @@ public sealed partial class MemberUpdateService : BackgroundService "Nautilus", "Ostrich", "Octopus", "Pelican", "Puffin", "Pyramid", "Rattle", "Robin", "Rose", "Salmon", "Seal", "Shark", "Sheep", "Snake", "Sonar", "Stump", "Sparrow", "Toaster", "Toucan", "Torus", "Violet", "Vortex", "Vulture", "Wagon", "Whale", "Woodpecker", "Zebra", "Zigzag" - }; + ]; private readonly IDiscordRestChannelAPI _channelApi; private readonly IDiscordRestGuildAPI _guildApi; private readonly GuildDataService _guildData; private readonly ILogger _logger; - private readonly UtilityService _utility; + private readonly Utility _utility; public MemberUpdateService(IDiscordRestChannelAPI channelApi, IDiscordRestGuildAPI guildApi, - GuildDataService guildData, ILogger logger, UtilityService utility) + GuildDataService guildData, ILogger logger, Utility utility) { _channelApi = channelApi; _guildApi = guildApi; diff --git a/src/Services/Update/ScheduledEventUpdateService.cs b/src/Services/Update/ScheduledEventUpdateService.cs index dd9be0d..ac5c109 100644 --- a/src/Services/Update/ScheduledEventUpdateService.cs +++ b/src/Services/Update/ScheduledEventUpdateService.cs @@ -19,10 +19,10 @@ public sealed class ScheduledEventUpdateService : BackgroundService private readonly IDiscordRestGuildScheduledEventAPI _eventApi; private readonly GuildDataService _guildData; private readonly ILogger _logger; - private readonly UtilityService _utility; + private readonly Utility _utility; public ScheduledEventUpdateService(IDiscordRestChannelAPI channelApi, IDiscordRestGuildScheduledEventAPI eventApi, - GuildDataService guildData, ILogger logger, UtilityService utility) + GuildDataService guildData, ILogger logger, Utility utility) { _channelApi = channelApi; _eventApi = eventApi; diff --git a/src/Services/Update/SongUpdateService.cs b/src/Services/Update/SongUpdateService.cs index 391c416..53cc59b 100644 --- a/src/Services/Update/SongUpdateService.cs +++ b/src/Services/Update/SongUpdateService.cs @@ -9,7 +9,7 @@ namespace Octobot.Services.Update; public sealed class SongUpdateService : BackgroundService { private static readonly (string Author, string Name, TimeSpan Duration)[] SongList = - { + [ ("Yoko & the Gold Bazookas", "Rockagilly Blues", new TimeSpan(0, 2, 52)), ("Deep Cut", "Big Betrayal", new TimeSpan(0, 5, 55)), ("Squid Sisters", "Tomorrow's Nostalgia Today", new TimeSpan(0, 3, 7)), @@ -30,7 +30,7 @@ public sealed class SongUpdateService : BackgroundService ("Turquoise October", "Octoling Rendezvous", new TimeSpan(0, 1, 57)), ("Damp Socks feat. Off the Hook", "Tentacle to the Metal", new TimeSpan(0, 2, 51)), ("Off the Hook", "Fly Octo Fly ~ Ebb & Flow (Octo)", new TimeSpan(0, 3, 5)) - }; + ]; private readonly List _activityList = [new Activity("with Remora.Discord", ActivityType.Game)]; diff --git a/src/Services/UtilityService.cs b/src/Services/Utility.cs similarity index 97% rename from src/Services/UtilityService.cs rename to src/Services/Utility.cs index 9ac481b..401b067 100644 --- a/src/Services/UtilityService.cs +++ b/src/Services/Utility.cs @@ -1,7 +1,6 @@ using System.Drawing; using System.Text; using System.Text.Json.Nodes; -using Microsoft.Extensions.Hosting; using Octobot.Data; using Octobot.Extensions; using Remora.Discord.API.Abstractions.Objects; @@ -17,14 +16,14 @@ namespace Octobot.Services; /// Provides utility methods that cannot be transformed to extension methods because they require usage /// of some Discord APIs. /// -public sealed class UtilityService : IHostedService +public sealed class Utility { private readonly IDiscordRestChannelAPI _channelApi; private readonly IDiscordRestGuildScheduledEventAPI _eventApi; private readonly IDiscordRestGuildAPI _guildApi; private readonly IDiscordRestUserAPI _userApi; - public UtilityService( + public Utility( IDiscordRestChannelAPI channelApi, IDiscordRestGuildScheduledEventAPI eventApi, IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi) { @@ -34,16 +33,6 @@ public sealed class UtilityService : IHostedService _userApi = userApi; } - public Task StartAsync(CancellationToken ct) - { - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken ct) - { - return Task.CompletedTask; - } - /// /// Checks whether or not a member can interact with another member ///