mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-01-31 00:19:00 +03:00
i think this should work
This commit is contained in:
commit
006f0888de
68 changed files with 617 additions and 627 deletions
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
|
@ -15,6 +15,10 @@ updates:
|
|||
labels:
|
||||
- "type: change"
|
||||
- "area: build/ci"
|
||||
# For all packages, ignore all patch updates
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: [ "version-update:semver-patch" ]
|
||||
|
||||
- package-ecosystem: "nuget" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
|
@ -30,3 +34,7 @@ updates:
|
|||
remora:
|
||||
patterns:
|
||||
- "Remora.Discord.*"
|
||||
# For all packages, ignore all patch updates
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: [ "version-update:semver-patch" ]
|
||||
|
|
2
.github/workflows/build-pr.yml
vendored
2
.github/workflows/build-pr.yml
vendored
|
@ -23,7 +23,7 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
|
||||
- name: ReSharper CLI InspectCode
|
||||
uses: muno92/resharper_inspectcode@1.11.8
|
||||
uses: muno92/resharper_inspectcode@1.11.10
|
||||
with:
|
||||
solutionPath: ./Octobot.sln
|
||||
ignoreIssueType: InvertIf, ConvertIfStatementToSwitchStatement, ConvertToPrimaryConstructor
|
||||
|
|
10
Octobot.sln
10
Octobot.sln
|
@ -1,6 +1,6 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Octobot", "Octobot.csproj", "{9CA7A44F-167C-46D4-923D-88CE71044144}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamOctolings.Octobot", "TeamOctolings.Octobot\TeamOctolings.Octobot.csproj", "{A1679BA2-3A36-4D98-80C0-EEE771398FBD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -8,9 +8,9 @@ Global
|
|||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9CA7A44F-167C-46D4-923D-88CE71044144}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9CA7A44F-167C-46D4-923D-88CE71044144}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9CA7A44F-167C-46D4-923D-88CE71044144}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9CA7A44F-167C-46D4-923D-88CE71044144}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A1679BA2-3A36-4D98-80C0-EEE771398FBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A1679BA2-3A36-4D98-80C0-EEE771398FBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A1679BA2-3A36-4D98-80C0-EEE771398FBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A1679BA2-3A36-4D98-80C0-EEE771398FBD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace Octobot.Attributes;
|
||||
namespace TeamOctolings.Octobot.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Any property marked with <see cref="StaticCallersOnlyAttribute"/> should only be accessed by static methods.
|
|
@ -1,4 +1,4 @@
|
|||
namespace Octobot;
|
||||
namespace TeamOctolings.Octobot;
|
||||
|
||||
public static class BuildInfo
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using System.Drawing;
|
||||
|
||||
namespace Octobot;
|
||||
namespace TeamOctolings.Octobot;
|
||||
|
||||
/// <summary>
|
||||
/// Contains all colors used in embeds.
|
|
@ -1,9 +1,6 @@
|
|||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -18,14 +15,17 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the command to show information about this bot: /about.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class AboutCommandGroup : CommandGroup
|
||||
public sealed class AboutCommandGroup : CommandGroup
|
||||
{
|
||||
private static readonly (string Username, Snowflake Id)[] Developers =
|
||||
[
|
||||
|
@ -36,9 +36,9 @@ public class AboutCommandGroup : CommandGroup
|
|||
|
||||
private readonly ICommandContext _context;
|
||||
private readonly IFeedbackService _feedback;
|
||||
private readonly IDiscordRestGuildAPI _guildApi;
|
||||
private readonly GuildDataService _guildData;
|
||||
private readonly IDiscordRestUserAPI _userApi;
|
||||
private readonly IDiscordRestGuildAPI _guildApi;
|
||||
|
||||
public AboutCommandGroup(
|
||||
ICommandContext context, GuildDataService guildData,
|
|
@ -2,11 +2,6 @@ using System.ComponentModel;
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Parsers;
|
||||
using Octobot.Services;
|
||||
using Octobot.Services.Update;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -19,14 +14,19 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Parsers;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
using TeamOctolings.Octobot.Services.Update;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles commands related to ban management: /ban and /unban.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class BanCommandGroup : CommandGroup
|
||||
public sealed class BanCommandGroup : CommandGroup
|
||||
{
|
||||
private readonly AccessControlService _access;
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
|
@ -1,9 +1,6 @@
|
|||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -16,14 +13,17 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the command to clear messages in a channel: /clear.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class ClearCommandGroup : CommandGroup
|
||||
public sealed class ClearCommandGroup : CommandGroup
|
||||
{
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
private readonly ICommandContext _context;
|
||||
|
@ -64,6 +64,7 @@ public class ClearCommandGroup : CommandGroup
|
|||
public async Task<Result> ExecuteClear(
|
||||
[Description("Number of messages to remove (2-100)")] [MinValue(2)] [MaxValue(100)]
|
||||
int amount,
|
||||
[Description("Ignore messages except from the specified author")]
|
||||
IUser? author = null)
|
||||
{
|
||||
if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var executorId))
|
|
@ -1,6 +1,5 @@
|
|||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octobot.Extensions;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.API.Objects;
|
||||
|
@ -11,14 +10,15 @@ using Remora.Discord.Commands.Services;
|
|||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
|
||||
namespace Octobot.Commands.Events;
|
||||
namespace TeamOctolings.Octobot.Commands.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Handles error logging for slash command groups.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class ErrorLoggingPostExecutionEvent : IPostExecutionEvent
|
||||
public sealed class ErrorLoggingPostExecutionEvent : IPostExecutionEvent
|
||||
{
|
||||
private readonly IFeedbackService _feedback;
|
||||
private readonly ILogger<ErrorLoggingPostExecutionEvent> _logger;
|
|
@ -1,17 +1,17 @@
|
|||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octobot.Extensions;
|
||||
using Remora.Discord.Commands.Contexts;
|
||||
using Remora.Discord.Commands.Services;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
|
||||
namespace Octobot.Commands.Events;
|
||||
namespace TeamOctolings.Octobot.Commands.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Handles error logging for slash commands that couldn't be successfully prepared.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class LoggingPreparationErrorEvent : IPreparationErrorEvent
|
||||
public sealed class LoggingPreparationErrorEvent : IPreparationErrorEvent
|
||||
{
|
||||
private readonly ILogger<LoggingPreparationErrorEvent> _logger;
|
||||
|
|
@ -2,10 +2,6 @@ using System.ComponentModel;
|
|||
using System.Drawing;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Parsers;
|
||||
using Octobot.Services;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -17,14 +13,17 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles tool commands: /userinfo, /guildinfo, /random, /timestamp, /8ball.
|
||||
/// Handles info commands: /userinfo, /guildinfo.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class ToolsCommandGroup : CommandGroup
|
||||
public sealed class InfoCommandGroup : CommandGroup
|
||||
{
|
||||
private readonly ICommandContext _context;
|
||||
private readonly IFeedbackService _feedback;
|
||||
|
@ -32,7 +31,7 @@ public class ToolsCommandGroup : CommandGroup
|
|||
private readonly GuildDataService _guildData;
|
||||
private readonly IDiscordRestUserAPI _userApi;
|
||||
|
||||
public ToolsCommandGroup(
|
||||
public InfoCommandGroup(
|
||||
ICommandContext context, IFeedbackService feedback,
|
||||
GuildDataService guildData, IDiscordRestGuildAPI guildApi,
|
||||
IDiscordRestUserAPI userApi)
|
||||
|
@ -327,235 +326,4 @@ public class ToolsCommandGroup : CommandGroup
|
|||
|
||||
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A slash command that generates a random number using maximum and minimum numbers.
|
||||
/// </summary>
|
||||
/// <param name="first">The first number used for randomization.</param>
|
||||
/// <param name="second">The second number used for randomization. Default value: 0</param>
|
||||
/// <returns>
|
||||
/// A feedback sending result which may or may not have succeeded.
|
||||
/// </returns>
|
||||
[Command("random")]
|
||||
[DiscordDefaultDMPermission(false)]
|
||||
[Description("Generates a random number")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> ExecuteRandomAsync(
|
||||
[Description("First number")] long first,
|
||||
[Description("Second number (Default: 0)")]
|
||||
long? second = null)
|
||||
{
|
||||
if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId))
|
||||
{
|
||||
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
||||
}
|
||||
|
||||
var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken);
|
||||
if (!executorResult.IsDefined(out var executor))
|
||||
{
|
||||
return ResultExtensions.FromError(executorResult);
|
||||
}
|
||||
|
||||
var data = await _guildData.GetData(guildId, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
return await SendRandomNumberAsync(first, second, executor, CancellationToken);
|
||||
}
|
||||
|
||||
private Task<Result> SendRandomNumberAsync(long first, long? secondNullable,
|
||||
IUser executor, CancellationToken ct)
|
||||
{
|
||||
const long secondDefault = 0;
|
||||
var second = secondNullable ?? secondDefault;
|
||||
|
||||
var min = Math.Min(first, second);
|
||||
var max = Math.Max(first, second);
|
||||
|
||||
var i = Random.Shared.NextInt64(min, max + 1);
|
||||
|
||||
var description = new StringBuilder().Append("# ").Append(i);
|
||||
|
||||
description.AppendLine().AppendBulletPoint(string.Format(
|
||||
Messages.RandomMin, Markdown.InlineCode(min.ToString())));
|
||||
if (secondNullable is null && first >= secondDefault)
|
||||
{
|
||||
description.Append(' ').Append(Messages.Default);
|
||||
}
|
||||
|
||||
description.AppendLine().AppendBulletPoint(string.Format(
|
||||
Messages.RandomMax, Markdown.InlineCode(max.ToString())));
|
||||
if (secondNullable is null && first < secondDefault)
|
||||
{
|
||||
description.Append(' ').Append(Messages.Default);
|
||||
}
|
||||
|
||||
var embedColor = ColorsList.Blue;
|
||||
if (secondNullable is not null && min == max)
|
||||
{
|
||||
description.AppendLine().Append(Markdown.Italicise(Messages.RandomMinMaxSame));
|
||||
embedColor = ColorsList.Red;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.RandomTitle, executor.GetTag()), executor)
|
||||
.WithDescription(description.ToString())
|
||||
.WithColour(embedColor)
|
||||
.Build();
|
||||
|
||||
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
||||
}
|
||||
|
||||
private static readonly TimestampStyle[] AllStyles =
|
||||
[
|
||||
TimestampStyle.ShortDate,
|
||||
TimestampStyle.LongDate,
|
||||
TimestampStyle.ShortTime,
|
||||
TimestampStyle.LongTime,
|
||||
TimestampStyle.ShortDateTime,
|
||||
TimestampStyle.LongDateTime,
|
||||
TimestampStyle.RelativeTime
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// A slash command that shows the current timestamp with an optional offset in all styles supported by Discord.
|
||||
/// </summary>
|
||||
/// <param name="stringOffset">The offset for the current timestamp.</param>
|
||||
/// <returns>
|
||||
/// A feedback sending result which may or may not have succeeded.
|
||||
/// </returns>
|
||||
[Command("timestamp")]
|
||||
[DiscordDefaultDMPermission(false)]
|
||||
[Description("Shows a timestamp in all styles")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> ExecuteTimestampAsync(
|
||||
[Description("Offset from current time")] [Option("offset")]
|
||||
string? stringOffset = null)
|
||||
{
|
||||
if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId))
|
||||
{
|
||||
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
||||
}
|
||||
|
||||
var botResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
||||
if (!botResult.IsDefined(out var bot))
|
||||
{
|
||||
return ResultExtensions.FromError(botResult);
|
||||
}
|
||||
|
||||
var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken);
|
||||
if (!executorResult.IsDefined(out var executor))
|
||||
{
|
||||
return ResultExtensions.FromError(executorResult);
|
||||
}
|
||||
|
||||
var data = await _guildData.GetData(guildId, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
if (stringOffset is null)
|
||||
{
|
||||
return await SendTimestampAsync(null, executor, CancellationToken);
|
||||
}
|
||||
|
||||
var parseResult = TimeSpanParser.TryParse(stringOffset);
|
||||
if (!parseResult.IsDefined(out var offset))
|
||||
{
|
||||
var failedEmbed = new EmbedBuilder()
|
||||
.WithSmallTitle(Messages.InvalidTimeSpan, bot)
|
||||
.WithDescription(Messages.TimeSpanExample)
|
||||
.WithColour(ColorsList.Red)
|
||||
.Build();
|
||||
|
||||
return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: CancellationToken);
|
||||
}
|
||||
|
||||
return await SendTimestampAsync(offset, executor, CancellationToken);
|
||||
}
|
||||
|
||||
private Task<Result> SendTimestampAsync(TimeSpan? offset, IUser executor, CancellationToken ct)
|
||||
{
|
||||
var timestamp = DateTimeOffset.UtcNow.Add(offset ?? TimeSpan.Zero).ToUnixTimeSeconds();
|
||||
|
||||
var description = new StringBuilder().Append("# ").AppendLine(timestamp.ToString());
|
||||
|
||||
if (offset is not null)
|
||||
{
|
||||
description.AppendLine(string.Format(
|
||||
Messages.TimestampOffset, Markdown.InlineCode(offset.ToString() ?? string.Empty))).AppendLine();
|
||||
}
|
||||
|
||||
foreach (var markdownTimestamp in AllStyles.Select(style => Markdown.Timestamp(timestamp, style)))
|
||||
{
|
||||
description.AppendBulletPoint(Markdown.InlineCode(markdownTimestamp))
|
||||
.Append(" → ").AppendLine(markdownTimestamp);
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.TimestampTitle, executor.GetTag()), executor)
|
||||
.WithDescription(description.ToString())
|
||||
.WithColour(ColorsList.Blue)
|
||||
.Build();
|
||||
|
||||
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A slash command that shows a random answer from the Magic 8-Ball.
|
||||
/// </summary>
|
||||
/// <param name="question">Unused input.</param>
|
||||
/// <remarks>
|
||||
/// The 8-Ball answers were taken from <a href="https://en.wikipedia.org/wiki/Magic_8_Ball#Possible_answers">Wikipedia</a>.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// A feedback sending result which may or may not have succeeded.
|
||||
/// </returns>
|
||||
[Command("8ball")]
|
||||
[DiscordDefaultDMPermission(false)]
|
||||
[Description("Ask the Magic 8-Ball a question")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> ExecuteEightBallAsync(
|
||||
// let the user think he's actually asking the ball a question
|
||||
[Description("Question to ask")] string question)
|
||||
{
|
||||
if (!_context.TryGetContextIDs(out var guildId, out _, out _))
|
||||
{
|
||||
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
||||
}
|
||||
|
||||
var botResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
||||
if (!botResult.IsDefined(out var bot))
|
||||
{
|
||||
return ResultExtensions.FromError(botResult);
|
||||
}
|
||||
|
||||
var data = await _guildData.GetData(guildId, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
return await AnswerEightBallAsync(bot, CancellationToken);
|
||||
}
|
||||
|
||||
private static readonly string[] AnswerTypes =
|
||||
[
|
||||
"Positive", "Questionable", "Neutral", "Negative"
|
||||
];
|
||||
|
||||
private Task<Result> AnswerEightBallAsync(IUser bot, CancellationToken ct)
|
||||
{
|
||||
var typeNumber = Random.Shared.Next(0, 4);
|
||||
var embedColor = typeNumber switch
|
||||
{
|
||||
0 => ColorsList.Blue,
|
||||
1 => ColorsList.Green,
|
||||
2 => ColorsList.Yellow,
|
||||
3 => ColorsList.Red,
|
||||
_ => throw new ArgumentOutOfRangeException(null, nameof(typeNumber))
|
||||
};
|
||||
|
||||
var answer = $"EightBall{AnswerTypes[typeNumber]}{Random.Shared.Next(1, 6)}".Localized();
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(answer, bot)
|
||||
.WithColour(embedColor)
|
||||
.Build();
|
||||
|
||||
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -15,14 +12,17 @@ using Remora.Discord.Commands.Feedback.Services;
|
|||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the command to kick members of a guild: /kick.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class KickCommandGroup : CommandGroup
|
||||
public sealed class KickCommandGroup : CommandGroup
|
||||
{
|
||||
private readonly AccessControlService _access;
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
|
@ -2,11 +2,6 @@ using System.ComponentModel;
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Parsers;
|
||||
using Octobot.Services;
|
||||
using Octobot.Services.Update;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -19,14 +14,19 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Parsers;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
using TeamOctolings.Octobot.Services.Update;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles commands related to mute management: /mute and /unmute.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class MuteCommandGroup : CommandGroup
|
||||
public sealed class MuteCommandGroup : CommandGroup
|
||||
{
|
||||
private readonly AccessControlService _access;
|
||||
private readonly ICommandContext _context;
|
|
@ -1,8 +1,5 @@
|
|||
using System.ComponentModel;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -15,14 +12,17 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Gateway;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the command to get the time taken for the gateway to respond to the last heartbeat: /ping
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class PingCommandGroup : CommandGroup
|
||||
public sealed class PingCommandGroup : CommandGroup
|
||||
{
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
private readonly DiscordGatewayClient _client;
|
|
@ -2,9 +2,6 @@ using System.ComponentModel;
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -17,21 +14,24 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using Octobot.Parsers;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Parsers;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles commands to manage reminders: /remind, /listremind, /delremind
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class RemindCommandGroup : CommandGroup
|
||||
public sealed class RemindCommandGroup : CommandGroup
|
||||
{
|
||||
private readonly IInteractionCommandContext _context;
|
||||
private readonly IFeedbackService _feedback;
|
||||
private readonly GuildDataService _guildData;
|
||||
private readonly IDiscordRestUserAPI _userApi;
|
||||
private readonly IDiscordRestInteractionAPI _interactionApi;
|
||||
private readonly IDiscordRestUserAPI _userApi;
|
||||
|
||||
public RemindCommandGroup(
|
||||
IInteractionCommandContext context, GuildDataService guildData, IFeedbackService feedback,
|
|
@ -3,10 +3,6 @@ using System.ComponentModel.DataAnnotations;
|
|||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Data.Options;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -19,23 +15,27 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Data.Options;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the commands to list and modify per-guild settings: /settings and /settings list.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class SettingsCommandGroup : CommandGroup
|
||||
public sealed class SettingsCommandGroup : CommandGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents all options as an array of objects implementing <see cref="IOption" />.
|
||||
/// Represents all options as an array of objects implementing <see cref="IGuildOption" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// WARNING: If you update this array in any way, you must also update <see cref="AllOptionsEnum" /> and make sure
|
||||
/// that the orders match.
|
||||
/// </remarks>
|
||||
private static readonly IOption[] AllOptions =
|
||||
private static readonly IGuildOption[] AllOptions =
|
||||
[
|
||||
GuildSettings.Language,
|
||||
GuildSettings.WarnPunishment,
|
||||
|
@ -202,7 +202,7 @@ public class SettingsCommandGroup : CommandGroup
|
|||
}
|
||||
|
||||
private async Task<Result> EditSettingAsync(
|
||||
IOption option, string value, GuildData data, Snowflake channelId, IUser executor, IUser bot,
|
||||
IGuildOption option, string value, GuildData data, Snowflake channelId, IUser executor, IUser bot,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var setResult = option.Set(data.Settings, value);
|
||||
|
@ -273,7 +273,7 @@ public class SettingsCommandGroup : CommandGroup
|
|||
}
|
||||
|
||||
private async Task<Result> ResetSingleSettingAsync(JsonNode cfg, IUser bot,
|
||||
IOption option, CancellationToken ct = default)
|
||||
IGuildOption option, CancellationToken ct = default)
|
||||
{
|
||||
var resetResult = option.Reset(cfg);
|
||||
if (!resetResult.IsSuccess)
|
272
TeamOctolings.Octobot/Commands/ToolsCommandGroup.cs
Normal file
272
TeamOctolings.Octobot/Commands/ToolsCommandGroup.cs
Normal file
|
@ -0,0 +1,272 @@
|
|||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.Commands.Attributes;
|
||||
using Remora.Discord.Commands.Contexts;
|
||||
using Remora.Discord.Commands.Feedback.Services;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Parsers;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Handles tool commands: /random, /timestamp, /8ball.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class ToolsCommandGroup : CommandGroup
|
||||
{
|
||||
private static readonly TimestampStyle[] AllStyles =
|
||||
[
|
||||
TimestampStyle.ShortDate,
|
||||
TimestampStyle.LongDate,
|
||||
TimestampStyle.ShortTime,
|
||||
TimestampStyle.LongTime,
|
||||
TimestampStyle.ShortDateTime,
|
||||
TimestampStyle.LongDateTime,
|
||||
TimestampStyle.RelativeTime
|
||||
];
|
||||
|
||||
private static readonly string[] AnswerTypes =
|
||||
[
|
||||
"Positive", "Questionable", "Neutral", "Negative"
|
||||
];
|
||||
|
||||
private readonly ICommandContext _context;
|
||||
private readonly IFeedbackService _feedback;
|
||||
private readonly GuildDataService _guildData;
|
||||
private readonly IDiscordRestUserAPI _userApi;
|
||||
|
||||
public ToolsCommandGroup(
|
||||
ICommandContext context, IFeedbackService feedback,
|
||||
GuildDataService guildData, IDiscordRestUserAPI userApi)
|
||||
{
|
||||
_context = context;
|
||||
_guildData = guildData;
|
||||
_feedback = feedback;
|
||||
_userApi = userApi;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A slash command that generates a random number using maximum and minimum numbers.
|
||||
/// </summary>
|
||||
/// <param name="first">The first number used for randomization.</param>
|
||||
/// <param name="second">The second number used for randomization. Default value: 0</param>
|
||||
/// <returns>
|
||||
/// A feedback sending result which may or may not have succeeded.
|
||||
/// </returns>
|
||||
[Command("random")]
|
||||
[DiscordDefaultDMPermission(false)]
|
||||
[Description("Generates a random number")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> ExecuteRandomAsync(
|
||||
[Description("First number")] long first,
|
||||
[Description("Second number (Default: 0)")]
|
||||
long? second = null)
|
||||
{
|
||||
if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId))
|
||||
{
|
||||
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
||||
}
|
||||
|
||||
var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken);
|
||||
if (!executorResult.IsDefined(out var executor))
|
||||
{
|
||||
return ResultExtensions.FromError(executorResult);
|
||||
}
|
||||
|
||||
var data = await _guildData.GetData(guildId, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
return await SendRandomNumberAsync(first, second, executor, CancellationToken);
|
||||
}
|
||||
|
||||
private Task<Result> SendRandomNumberAsync(long first, long? secondNullable,
|
||||
IUser executor, CancellationToken ct)
|
||||
{
|
||||
const long secondDefault = 0;
|
||||
var second = secondNullable ?? secondDefault;
|
||||
|
||||
var min = Math.Min(first, second);
|
||||
var max = Math.Max(first, second);
|
||||
|
||||
var i = Random.Shared.NextInt64(min, max + 1);
|
||||
|
||||
var description = new StringBuilder().Append("# ").Append(i);
|
||||
|
||||
description.AppendLine().AppendBulletPoint(string.Format(
|
||||
Messages.RandomMin, Markdown.InlineCode(min.ToString())));
|
||||
if (secondNullable is null && first >= secondDefault)
|
||||
{
|
||||
description.Append(' ').Append(Messages.Default);
|
||||
}
|
||||
|
||||
description.AppendLine().AppendBulletPoint(string.Format(
|
||||
Messages.RandomMax, Markdown.InlineCode(max.ToString())));
|
||||
if (secondNullable is null && first < secondDefault)
|
||||
{
|
||||
description.Append(' ').Append(Messages.Default);
|
||||
}
|
||||
|
||||
var embedColor = ColorsList.Blue;
|
||||
if (secondNullable is not null && min == max)
|
||||
{
|
||||
description.AppendLine().Append(Markdown.Italicise(Messages.RandomMinMaxSame));
|
||||
embedColor = ColorsList.Red;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.RandomTitle, executor.GetTag()), executor)
|
||||
.WithDescription(description.ToString())
|
||||
.WithColour(embedColor)
|
||||
.Build();
|
||||
|
||||
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A slash command that shows the current timestamp with an optional offset in all styles supported by Discord.
|
||||
/// </summary>
|
||||
/// <param name="stringOffset">The offset for the current timestamp.</param>
|
||||
/// <returns>
|
||||
/// A feedback sending result which may or may not have succeeded.
|
||||
/// </returns>
|
||||
[Command("timestamp")]
|
||||
[DiscordDefaultDMPermission(false)]
|
||||
[Description("Shows a timestamp in all styles")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> ExecuteTimestampAsync(
|
||||
[Description("Offset from current time")] [Option("offset")]
|
||||
string? stringOffset = null)
|
||||
{
|
||||
if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId))
|
||||
{
|
||||
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
||||
}
|
||||
|
||||
var botResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
||||
if (!botResult.IsDefined(out var bot))
|
||||
{
|
||||
return ResultExtensions.FromError(botResult);
|
||||
}
|
||||
|
||||
var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken);
|
||||
if (!executorResult.IsDefined(out var executor))
|
||||
{
|
||||
return ResultExtensions.FromError(executorResult);
|
||||
}
|
||||
|
||||
var data = await _guildData.GetData(guildId, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
if (stringOffset is null)
|
||||
{
|
||||
return await SendTimestampAsync(null, executor, CancellationToken);
|
||||
}
|
||||
|
||||
var parseResult = TimeSpanParser.TryParse(stringOffset);
|
||||
if (!parseResult.IsDefined(out var offset))
|
||||
{
|
||||
var failedEmbed = new EmbedBuilder()
|
||||
.WithSmallTitle(Messages.InvalidTimeSpan, bot)
|
||||
.WithDescription(Messages.TimeSpanExample)
|
||||
.WithColour(ColorsList.Red)
|
||||
.Build();
|
||||
|
||||
return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: CancellationToken);
|
||||
}
|
||||
|
||||
return await SendTimestampAsync(offset, executor, CancellationToken);
|
||||
}
|
||||
|
||||
private Task<Result> SendTimestampAsync(TimeSpan? offset, IUser executor, CancellationToken ct)
|
||||
{
|
||||
var timestamp = DateTimeOffset.UtcNow.Add(offset ?? TimeSpan.Zero).ToUnixTimeSeconds();
|
||||
|
||||
var description = new StringBuilder().Append("# ").AppendLine(timestamp.ToString());
|
||||
|
||||
if (offset is not null)
|
||||
{
|
||||
description.AppendLine(string.Format(
|
||||
Messages.TimestampOffset, Markdown.InlineCode(offset.ToString() ?? string.Empty))).AppendLine();
|
||||
}
|
||||
|
||||
foreach (var markdownTimestamp in AllStyles.Select(style => Markdown.Timestamp(timestamp, style)))
|
||||
{
|
||||
description.AppendBulletPoint(Markdown.InlineCode(markdownTimestamp))
|
||||
.Append(" → ").AppendLine(markdownTimestamp);
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.TimestampTitle, executor.GetTag()), executor)
|
||||
.WithDescription(description.ToString())
|
||||
.WithColour(ColorsList.Blue)
|
||||
.Build();
|
||||
|
||||
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A slash command that shows a random answer from the Magic 8-Ball.
|
||||
/// </summary>
|
||||
/// <param name="question">Unused input.</param>
|
||||
/// <remarks>
|
||||
/// The 8-Ball answers were taken from <a href="https://en.wikipedia.org/wiki/Magic_8_Ball#Possible_answers">Wikipedia</a>.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// A feedback sending result which may or may not have succeeded.
|
||||
/// </returns>
|
||||
[Command("8ball")]
|
||||
[DiscordDefaultDMPermission(false)]
|
||||
[Description("Ask the Magic 8-Ball a question")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> ExecuteEightBallAsync(
|
||||
// let the user think he's actually asking the ball a question
|
||||
[Description("Question to ask")] string question)
|
||||
{
|
||||
if (!_context.TryGetContextIDs(out var guildId, out _, out _))
|
||||
{
|
||||
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
|
||||
}
|
||||
|
||||
var botResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
||||
if (!botResult.IsDefined(out var bot))
|
||||
{
|
||||
return ResultExtensions.FromError(botResult);
|
||||
}
|
||||
|
||||
var data = await _guildData.GetData(guildId, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
return await AnswerEightBallAsync(bot, CancellationToken);
|
||||
}
|
||||
|
||||
private Task<Result> AnswerEightBallAsync(IUser bot, CancellationToken ct)
|
||||
{
|
||||
var typeNumber = Random.Shared.Next(0, 4);
|
||||
var embedColor = typeNumber switch
|
||||
{
|
||||
0 => ColorsList.Blue,
|
||||
1 => ColorsList.Green,
|
||||
2 => ColorsList.Yellow,
|
||||
3 => ColorsList.Red,
|
||||
_ => throw new ArgumentOutOfRangeException(null, nameof(typeNumber))
|
||||
};
|
||||
|
||||
var answer = $"EightBall{AnswerTypes[typeNumber]}{Random.Shared.Next(1, 6)}".Localized();
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(answer, bot)
|
||||
.WithColour(embedColor)
|
||||
.Build();
|
||||
|
||||
return _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
|
||||
}
|
||||
}
|
|
@ -4,9 +4,6 @@ using System.ComponentModel.DataAnnotations;
|
|||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
@ -19,9 +16,12 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
using static System.DateTimeOffset;
|
||||
|
||||
namespace Octobot.Commands;
|
||||
namespace TeamOctolings.Octobot.Commands;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class WarnCommandGroup : CommandGroup
|
|
@ -1,7 +1,7 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using Remora.Rest.Core;
|
||||
|
||||
namespace Octobot.Data;
|
||||
namespace TeamOctolings.Octobot.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Stores information about a guild. This information is not accessible via the Discord API.
|
|
@ -1,8 +1,8 @@
|
|||
using Octobot.Data.Options;
|
||||
using Octobot.Responders;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using TeamOctolings.Octobot.Data.Options;
|
||||
using TeamOctolings.Octobot.Responders;
|
||||
|
||||
namespace Octobot.Data;
|
||||
namespace TeamOctolings.Octobot.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Contains all per-guild settings that can be set by a member
|
||||
|
@ -24,7 +24,7 @@ public static class GuildSettings
|
|||
/// </list>
|
||||
/// </remarks>
|
||||
/// <seealso cref="GuildMemberJoinedResponder" />
|
||||
public static readonly Option<string> WelcomeMessage = new("WelcomeMessage", "default");
|
||||
public static readonly GuildOption<string> WelcomeMessage = new("WelcomeMessage", "default");
|
||||
|
||||
/// <summary>
|
||||
/// Controls what message should be sent in <see cref="PublicFeedbackChannel" /> when a member leaves the guild.
|
||||
|
@ -36,7 +36,7 @@ public static class GuildSettings
|
|||
/// </list>
|
||||
/// </remarks>
|
||||
/// <seealso cref="GuildMemberLeftResponder" />
|
||||
public static readonly Option<string> LeaveMessage = new("LeaveMessage", "default");
|
||||
public static readonly GuildOption<string> LeaveMessage = new("LeaveMessage", "default");
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether or not the <see cref="Messages.Ready" /> message should be sent
|
|
@ -1,4 +1,4 @@
|
|||
namespace Octobot.Data;
|
||||
namespace TeamOctolings.Octobot.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Stores information about a member
|
|
@ -1,7 +1,7 @@
|
|||
using JetBrains.Annotations;
|
||||
using Octobot.Commands;
|
||||
using TeamOctolings.Octobot.Commands;
|
||||
|
||||
namespace Octobot.Data.Options;
|
||||
namespace TeamOctolings.Octobot.Data.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Represents all options as enums.
|
|
@ -1,9 +1,9 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Data.Options;
|
||||
namespace TeamOctolings.Octobot.Data.Options;
|
||||
|
||||
public sealed class BoolOption : Option<bool>
|
||||
public sealed class BoolOption : GuildOption<bool>
|
||||
{
|
||||
public BoolOption(string name, bool defaultValue) : base(name, defaultValue) { }
|
||||
|
|
@ -2,18 +2,18 @@ using System.Text.Json.Nodes;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Data.Options;
|
||||
namespace TeamOctolings.Octobot.Data.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an per-guild option.
|
||||
/// Represents a per-guild option.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the option.</typeparam>
|
||||
public class Option<T> : IOption
|
||||
public class GuildOption<T> : IGuildOption
|
||||
where T : notnull
|
||||
{
|
||||
protected readonly T DefaultValue;
|
||||
|
||||
public Option(string name, T defaultValue)
|
||||
public GuildOption(string name, T defaultValue)
|
||||
{
|
||||
Name = name;
|
||||
DefaultValue = defaultValue;
|
|
@ -1,9 +1,9 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Data.Options;
|
||||
namespace TeamOctolings.Octobot.Data.Options;
|
||||
|
||||
public interface IOption
|
||||
public interface IGuildOption
|
||||
{
|
||||
string Name { get; }
|
||||
string Display(JsonNode settings);
|
|
@ -1,9 +1,9 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Data.Options;
|
||||
namespace TeamOctolings.Octobot.Data.Options;
|
||||
|
||||
public sealed class IntOption : Option<int>
|
||||
public sealed class IntOption : GuildOption<int>
|
||||
{
|
||||
public IntOption(string name, int defaultValue) : base(name, defaultValue) { }
|
||||
|
|
@ -3,10 +3,10 @@ using System.Text.Json.Nodes;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Data.Options;
|
||||
namespace TeamOctolings.Octobot.Data.Options;
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed class LanguageOption : Option<CultureInfo>
|
||||
public sealed class LanguageOption : GuildOption<CultureInfo>
|
||||
{
|
||||
private static readonly Dictionary<string, CultureInfo> CultureInfoCache = new()
|
||||
{
|
|
@ -1,10 +1,10 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Data.Options;
|
||||
namespace TeamOctolings.Octobot.Data.Options;
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed class PunishmentOption : Option<string>
|
||||
public sealed class PunishmentOption : GuildOption<string>
|
||||
{
|
||||
private static readonly List<string> AllowedValues =
|
||||
[
|
|
@ -1,13 +1,13 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using System.Text.RegularExpressions;
|
||||
using Octobot.Extensions;
|
||||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
|
||||
namespace Octobot.Data.Options;
|
||||
namespace TeamOctolings.Octobot.Data.Options;
|
||||
|
||||
public sealed partial class SnowflakeOption : Option<Snowflake>
|
||||
public sealed partial class SnowflakeOption : GuildOption<Snowflake>
|
||||
{
|
||||
public SnowflakeOption(string name) : base(name, 0UL.ToSnowflake()) { }
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using Octobot.Parsers;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Parsers;
|
||||
|
||||
namespace Octobot.Data.Options;
|
||||
namespace TeamOctolings.Octobot.Data.Options;
|
||||
|
||||
public sealed class TimeSpanOption : Option<TimeSpan>
|
||||
public sealed class TimeSpanOption : GuildOption<TimeSpan>
|
||||
{
|
||||
public TimeSpanOption(string name, TimeSpan defaultValue) : base(name, defaultValue) { }
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Octobot.Data;
|
||||
namespace TeamOctolings.Octobot.Data;
|
||||
|
||||
public struct Reminder
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
||||
namespace Octobot.Data;
|
||||
namespace TeamOctolings.Octobot.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Stores information about scheduled events. This information is not provided by the Discord API.
|
|
@ -1,4 +1,4 @@
|
|||
namespace Octobot.Data;
|
||||
namespace TeamOctolings.Octobot.Data;
|
||||
|
||||
public struct Warn
|
||||
{
|
|
@ -5,18 +5,19 @@ using Remora.Discord.API.Objects;
|
|||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class ChannelApiExtensions
|
||||
{
|
||||
public static async Task<Result> CreateMessageWithEmbedResultAsync(this IDiscordRestChannelAPI channelApi,
|
||||
Snowflake channelId, Optional<string> message = default, Optional<string> nonce = default,
|
||||
Optional<bool> isTextToSpeech = default, Optional<Result<Embed>> embedResult = default,
|
||||
Optional<IAllowedMentions> allowedMentions = default, Optional<IMessageReference> messageRefenence = default,
|
||||
Optional<IAllowedMentions> allowedMentions = default, Optional<IMessageReference> messageReference = default,
|
||||
Optional<IReadOnlyList<IMessageComponent>> components = default,
|
||||
Optional<IReadOnlyList<Snowflake>> stickerIds = default,
|
||||
Optional<IReadOnlyList<OneOf<FileData, IPartialAttachment>>> attachments = default,
|
||||
Optional<MessageFlags> flags = default, CancellationToken ct = default)
|
||||
Optional<MessageFlags> flags = default, Optional<bool> enforceNonce = default,
|
||||
Optional<IPollCreateRequest> poll = default, CancellationToken ct = default)
|
||||
{
|
||||
if (!embedResult.IsDefined() || !embedResult.Value.IsDefined(out var embed))
|
||||
{
|
||||
|
@ -24,6 +25,6 @@ public static class ChannelApiExtensions
|
|||
}
|
||||
|
||||
return (Result)await channelApi.CreateMessageAsync(channelId, message, nonce, isTextToSpeech, new[] { embed },
|
||||
allowedMentions, messageRefenence, components, stickerIds, attachments, flags, ct);
|
||||
allowedMentions, messageReference, components, stickerIds, attachments, flags, enforceNonce, poll, ct);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class CollectionExtensions
|
||||
{
|
|
@ -2,7 +2,7 @@
|
|||
using Remora.Discord.Commands.Extensions;
|
||||
using Remora.Rest.Core;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class CommandContextExtensions
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System.Text;
|
||||
using DiffPlex.DiffBuilder.Model;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class DiffPaneModelExtensions
|
||||
{
|
|
@ -4,7 +4,7 @@ using Remora.Discord.API.Objects;
|
|||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Rest.Core;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class EmbedBuilderExtensions
|
||||
{
|
|
@ -3,7 +3,7 @@ using Remora.Discord.Commands.Feedback.Messages;
|
|||
using Remora.Discord.Commands.Feedback.Services;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class FeedbackServiceExtensions
|
||||
{
|
|
@ -2,7 +2,7 @@
|
|||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class GuildScheduledEventExtensions
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class LoggerExtensions
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class MarkdownExtensions
|
||||
{
|
|
@ -2,7 +2,7 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class ResultExtensions
|
||||
{
|
||||
|
@ -21,21 +21,25 @@ public static class ResultExtensions
|
|||
return casted;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private static void LogResultStackTrace(Result result)
|
||||
{
|
||||
if (Octobot.StaticLogger is null || result.IsSuccess)
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Octobot.StaticLogger.LogError("{ErrorType}: {ErrorMessage}{NewLine}{StackTrace}",
|
||||
if (Utility.StaticLogger is null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Utility.StaticLogger.LogError("{ErrorType}: {ErrorMessage}{NewLine}{StackTrace}",
|
||||
result.Error.GetType().FullName, result.Error.Message, Environment.NewLine, ConstructStackTrace());
|
||||
|
||||
var inner = result.Inner;
|
||||
while (inner is { IsSuccess: false })
|
||||
{
|
||||
Octobot.StaticLogger.LogError("Caused by: {ResultType}: {ResultMessage}",
|
||||
Utility.StaticLogger.LogError("Caused by: {ResultType}: {ResultMessage}",
|
||||
inner.Error.GetType().FullName, inner.Error.Message);
|
||||
|
||||
inner = inner.Inner;
|
|
@ -1,6 +1,6 @@
|
|||
using Remora.Rest.Core;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class SnowflakeExtensions
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using System.Text;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class StringBuilderExtensions
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System.Net;
|
||||
using Remora.Discord.Extensions.Formatting;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using Remora.Discord.API;
|
||||
using Remora.Rest.Core;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class UInt64Extensions
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using Remora.Discord.API.Abstractions.Objects;
|
||||
|
||||
namespace Octobot.Extensions;
|
||||
namespace TeamOctolings.Octobot.Extensions;
|
||||
|
||||
public static class UserExtensions
|
||||
{
|
|
@ -7,7 +7,10 @@
|
|||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Octobot {
|
||||
namespace TeamOctolings.Octobot {
|
||||
using System;
|
||||
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
|
@ -25,7 +28,7 @@ namespace Octobot {
|
|||
internal static System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.Equals(null, resourceMan)) {
|
||||
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Octobot.locale.Messages", typeof(Messages).Assembly);
|
||||
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("TeamOctolings.Octobot.Messages", typeof(Messages).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
|
@ -120,9 +123,9 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string SettingsLang {
|
||||
internal static string SettingsLanguage {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingsLang", resourceCulture);
|
||||
return ResourceManager.GetString("SettingsLanguage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,18 +207,6 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string InvalidRole {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidRole", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string InvalidChannel {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidChannel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string DurationRequiredForTimeOuts {
|
||||
get {
|
||||
return ResourceManager.GetString("DurationRequiredForTimeOuts", resourceCulture);
|
||||
|
@ -282,6 +273,12 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string MissingUser {
|
||||
get {
|
||||
return ResourceManager.GetString("MissingUser", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string UserCannotBanMembers {
|
||||
get {
|
||||
return ResourceManager.GetString("UserCannotBanMembers", resourceCulture);
|
||||
|
@ -300,9 +297,15 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string UserCannotModerateMembers {
|
||||
internal static string UserCannotMuteMembers {
|
||||
get {
|
||||
return ResourceManager.GetString("UserCannotModerateMembers", resourceCulture);
|
||||
return ResourceManager.GetString("UserCannotMuteMembers", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string UserCannotUnmuteMembers {
|
||||
get {
|
||||
return ResourceManager.GetString("UserCannotUnmuteMembers", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -702,6 +705,12 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string SettingsRenameHoistedUsers {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingsRenameHoistedUsers", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Page {
|
||||
get {
|
||||
return ResourceManager.GetString("Page", resourceCulture);
|
||||
|
@ -798,21 +807,15 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string InformationAbout {
|
||||
get {
|
||||
return ResourceManager.GetString("InformationAbout", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string UserInfoDisplayName {
|
||||
get {
|
||||
return ResourceManager.GetString("UserInfoDisplayName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string UserInfoDiscordUserSince {
|
||||
internal static string InformationAbout {
|
||||
get {
|
||||
return ResourceManager.GetString("UserInfoDiscordUserSince", resourceCulture);
|
||||
return ResourceManager.GetString("InformationAbout", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -822,6 +825,12 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string UserInfoDiscordUserSince {
|
||||
get {
|
||||
return ResourceManager.GetString("UserInfoDiscordUserSince", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string UserInfoBanned {
|
||||
get {
|
||||
return ResourceManager.GetString("UserInfoBanned", resourceCulture);
|
||||
|
@ -882,157 +891,122 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string RandomTitle
|
||||
{
|
||||
internal static string RandomTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("RandomTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string RandomMinMaxSame
|
||||
{
|
||||
internal static string RandomMinMaxSame {
|
||||
get {
|
||||
return ResourceManager.GetString("RandomMinMaxSame", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string RandomMax
|
||||
{
|
||||
get {
|
||||
return ResourceManager.GetString("RandomMax", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string RandomMin
|
||||
{
|
||||
internal static string RandomMin {
|
||||
get {
|
||||
return ResourceManager.GetString("RandomMin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Default
|
||||
{
|
||||
internal static string RandomMax {
|
||||
get {
|
||||
return ResourceManager.GetString("RandomMax", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Default {
|
||||
get {
|
||||
return ResourceManager.GetString("Default", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string TimestampTitle
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string TimestampTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("TimestampTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string TimestampOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string TimestampOffset {
|
||||
get {
|
||||
return ResourceManager.GetString("TimestampOffset", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GuildInfoDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string GuildInfoDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("GuildInfoDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GuildInfoCreatedAt
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string GuildInfoCreatedAt {
|
||||
get {
|
||||
return ResourceManager.GetString("GuildInfoCreatedAt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GuildInfoOwner
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string GuildInfoOwner {
|
||||
get {
|
||||
return ResourceManager.GetString("GuildInfoOwner", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GuildInfoServerBoost
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string GuildInfoServerBoost {
|
||||
get {
|
||||
return ResourceManager.GetString("GuildInfoServerBoost", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GuildInfoBoostTier
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string GuildInfoBoostTier {
|
||||
get {
|
||||
return ResourceManager.GetString("GuildInfoBoostTier", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GuildInfoBoostCount
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string GuildInfoBoostCount {
|
||||
get {
|
||||
return ResourceManager.GetString("GuildInfoBoostCount", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string NoMessagesToClear
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string NoMessagesToClear {
|
||||
get {
|
||||
return ResourceManager.GetString("NoMessagesToClear", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string MessagesClearedFiltered
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string MessagesClearedFiltered {
|
||||
get {
|
||||
return ResourceManager.GetString("MessagesClearedFiltered", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string DataLoadFailedTitle
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string DataLoadFailedTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("DataLoadFailedTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string DataLoadFailedDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string DataLoadFailedDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("DataLoadFailedDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string CommandExecutionFailed
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string CommandExecutionFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("CommandExecutionFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ContactDevelopers
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string ContactDevelopers {
|
||||
get {
|
||||
return ResourceManager.GetString("ContactDevelopers", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ButtonReportIssue
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string ButtonReportIssue {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonReportIssue", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
@ -1205,62 +1179,53 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string UserWarned
|
||||
{
|
||||
internal static string UserWarned {
|
||||
get {
|
||||
return ResourceManager.GetString("UserWarned", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string UserWarnsRemoved
|
||||
{
|
||||
internal static string UserWarnsRemoved {
|
||||
get {
|
||||
return ResourceManager.GetString("UserWarnsRemoved", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string YouHaveBeenWarned
|
||||
{
|
||||
internal static string YouHaveBeenWarned {
|
||||
get {
|
||||
return ResourceManager.GetString("YouHaveBeenWarned", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string YourWarningsHaveBeenRevoked
|
||||
{
|
||||
internal static string YourWarningsHaveBeenRevoked {
|
||||
get {
|
||||
return ResourceManager.GetString("YourWarningsHaveBeenRevoked", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string DescriptionWarns
|
||||
{
|
||||
internal static string DescriptionWarns {
|
||||
get {
|
||||
return ResourceManager.GetString("DescriptionWarns", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string UserHasNoWarnings
|
||||
{
|
||||
internal static string UserHasNoWarnings {
|
||||
get {
|
||||
return ResourceManager.GetString("UserHasNoWarnings", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string YouHaveNoWarnings
|
||||
{
|
||||
internal static string YouHaveNoWarnings {
|
||||
get {
|
||||
return ResourceManager.GetString("YouHaveNoWarnings", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ReceivedTooManyWarnings
|
||||
{
|
||||
internal static string ReceivedTooManyWarnings {
|
||||
get {
|
||||
return ResourceManager.GetString("ReceivedTooManyWarnings", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ButtonDirty {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonDirty", resourceCulture);
|
||||
|
@ -1273,82 +1238,74 @@ namespace Octobot {
|
|||
}
|
||||
}
|
||||
|
||||
internal static string ListTargetWarnsTitle
|
||||
{
|
||||
internal static string SettingsModeratorRole {
|
||||
get {
|
||||
return ResourceManager.GetString("SettingsModeratorRole", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ListTargetWarnsTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ListTargetWarnsTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ReceivedOn
|
||||
{
|
||||
internal static string ReceivedOn {
|
||||
get {
|
||||
return ResourceManager.GetString("ReceivedOn", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string UserWarnRemoved
|
||||
{
|
||||
internal static string UserWarnRemoved {
|
||||
get {
|
||||
return ResourceManager.GetString("UserWarnRemoved", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string YourWarningHasBeenRevoked
|
||||
{
|
||||
internal static string YourWarningHasBeenRevoked {
|
||||
get {
|
||||
return ResourceManager.GetString("YourWarningHasBeenRevoked", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string WrongWarningNumberSelected
|
||||
{
|
||||
internal static string WrongWarningNumberSelected {
|
||||
get {
|
||||
return ResourceManager.GetString("WrongWarningNumberSelected", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string ListExecutorWarnsTitle
|
||||
{
|
||||
internal static string ListExecutorWarnsTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ListExecutorWarnsTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string DescriptionPunishmentType
|
||||
{
|
||||
internal static string DescriptionPunishmentType {
|
||||
get {
|
||||
return ResourceManager.GetString("DescriptionPunishmentType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string WarnThresholdExceeded
|
||||
{
|
||||
internal static string WarnThresholdExceeded {
|
||||
get {
|
||||
return ResourceManager.GetString("WarnThresholdExceeded", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string WarnPunishmentDurationNotSet
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string WarnPunishmentDurationNotSet {
|
||||
get {
|
||||
return ResourceManager.GetString("WarnPunishmentDurationNotSet", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string WarnThresholdExceededDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string WarnThresholdExceededDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("WarnThresholdExceededDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string InvalidWarnPunishment
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string InvalidWarnPunishment {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidWarnPunishment", resourceCulture);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ using JetBrains.Annotations;
|
|||
using Remora.Commands.Parsers;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Parsers;
|
||||
namespace TeamOctolings.Octobot.Parsers;
|
||||
|
||||
/// <summary>
|
||||
/// Parses <see cref="TimeSpan"/>s.
|
|
@ -2,13 +2,8 @@ using Microsoft.Extensions.Configuration;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octobot.Attributes;
|
||||
using Octobot.Commands.Events;
|
||||
using Octobot.Services;
|
||||
using Octobot.Services.Update;
|
||||
using Remora.Discord.API.Abstractions.Gateway.Commands;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Objects;
|
||||
using Remora.Discord.Caching.Extensions;
|
||||
using Remora.Discord.Caching.Services;
|
||||
using Remora.Discord.Commands.Extensions;
|
||||
|
@ -16,24 +11,20 @@ using Remora.Discord.Commands.Services;
|
|||
using Remora.Discord.Extensions.Extensions;
|
||||
using Remora.Discord.Gateway;
|
||||
using Remora.Discord.Hosting.Extensions;
|
||||
using Remora.Rest.Core;
|
||||
using Serilog.Extensions.Logging;
|
||||
using TeamOctolings.Octobot.Commands.Events;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
using TeamOctolings.Octobot.Services.Update;
|
||||
|
||||
namespace Octobot;
|
||||
namespace TeamOctolings.Octobot;
|
||||
|
||||
public sealed class Octobot
|
||||
public sealed class Program
|
||||
{
|
||||
public static readonly AllowedMentions NoMentions = new(
|
||||
Array.Empty<MentionType>(), Array.Empty<Snowflake>(), Array.Empty<Snowflake>());
|
||||
|
||||
[StaticCallersOnly]
|
||||
public static ILogger<Octobot>? StaticLogger { get; private set; }
|
||||
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
var host = CreateHostBuilder(args).UseConsoleLifetime().Build();
|
||||
var services = host.Services;
|
||||
StaticLogger = services.GetRequiredService<ILogger<Octobot>>();
|
||||
Utility.StaticLogger = services.GetRequiredService<ILogger<Program>>();
|
||||
|
||||
var slashService = services.GetRequiredService<SlashService>();
|
||||
// Providing a guild ID to this call will result in command duplicates!
|
||||
|
@ -82,8 +73,8 @@ public sealed class Octobot
|
|||
// Init
|
||||
.AddDiscordCaching()
|
||||
.AddDiscordCommands(true, false)
|
||||
.AddRespondersFromAssembly(typeof(Octobot).Assembly)
|
||||
.AddCommandGroupsFromAssembly(typeof(Octobot).Assembly)
|
||||
.AddRespondersFromAssembly(typeof(Program).Assembly)
|
||||
.AddCommandGroupsFromAssembly(typeof(Program).Assembly)
|
||||
// Slash command event handlers
|
||||
.AddPreparationErrorEvent<LoggingPreparationErrorEvent>()
|
||||
.AddPostExecutionEvent<ErrorLoggingPostExecutionEvent>()
|
|
@ -1,8 +1,5 @@
|
|||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
|
@ -11,15 +8,18 @@ using Remora.Discord.API.Objects;
|
|||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Gateway.Responders;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Responders;
|
||||
namespace TeamOctolings.Octobot.Responders;
|
||||
|
||||
/// <summary>
|
||||
/// Handles sending a <see cref="Ready" /> message to a guild that has just initialized if that guild
|
||||
/// has <see cref="GuildSettings.ReceiveStartupMessages" /> enabled
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class GuildLoadedResponder : IResponder<IGuildCreate>
|
||||
public sealed class GuildLoadedResponder : IResponder<IGuildCreate>
|
||||
{
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
private readonly GuildDataService _guildData;
|
|
@ -1,16 +1,16 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Gateway.Responders;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Responders;
|
||||
namespace TeamOctolings.Octobot.Responders;
|
||||
|
||||
/// <summary>
|
||||
/// Handles sending a guild's <see cref="GuildSettings.WelcomeMessage" /> if one is set.
|
||||
|
@ -18,7 +18,7 @@ namespace Octobot.Responders;
|
|||
/// </summary>
|
||||
/// <seealso cref="GuildSettings.WelcomeMessage" />
|
||||
[UsedImplicitly]
|
||||
public class GuildMemberJoinedResponder : IResponder<IGuildMemberAdd>
|
||||
public sealed class GuildMemberJoinedResponder : IResponder<IGuildMemberAdd>
|
||||
{
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
private readonly IDiscordRestGuildAPI _guildApi;
|
||||
|
@ -77,7 +77,7 @@ public class GuildMemberJoinedResponder : IResponder<IGuildMemberAdd>
|
|||
|
||||
return await _channelApi.CreateMessageWithEmbedResultAsync(
|
||||
GuildSettings.WelcomeMessagesChannel.Get(cfg), embedResult: embed,
|
||||
allowedMentions: Octobot.NoMentions, ct: ct);
|
||||
allowedMentions: Utility.NoMentions, ct: ct);
|
||||
}
|
||||
|
||||
private async Task<Result> TryReturnRolesAsync(
|
|
@ -1,21 +1,21 @@
|
|||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Gateway.Responders;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Responders;
|
||||
namespace TeamOctolings.Octobot.Responders;
|
||||
|
||||
/// <summary>
|
||||
/// Handles sending a guild's <see cref="GuildSettings.LeaveMessage" /> if one is set.
|
||||
/// </summary>
|
||||
/// <seealso cref="GuildSettings.LeaveMessage" />
|
||||
[UsedImplicitly]
|
||||
public class GuildMemberLeftResponder : IResponder<IGuildMemberRemove>
|
||||
public sealed class GuildMemberLeftResponder : IResponder<IGuildMemberRemove>
|
||||
{
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
private readonly IDiscordRestGuildAPI _guildApi;
|
||||
|
@ -67,6 +67,6 @@ public class GuildMemberLeftResponder : IResponder<IGuildMemberRemove>
|
|||
|
||||
return await _channelApi.CreateMessageWithEmbedResultAsync(
|
||||
GuildSettings.WelcomeMessagesChannel.Get(cfg), embedResult: embed,
|
||||
allowedMentions: Octobot.NoMentions, ct: ct);
|
||||
allowedMentions: Utility.NoMentions, ct: ct);
|
||||
}
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octobot.Data;
|
||||
using Octobot.Services;
|
||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||
using Remora.Discord.Gateway.Responders;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Responders;
|
||||
namespace TeamOctolings.Octobot.Responders;
|
||||
|
||||
/// <summary>
|
||||
/// Handles removing guild ID from <see cref="GuildData" /> if the guild becomes unavailable.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class GuildUnloadedResponder : IResponder<IGuildDelete>
|
||||
public sealed class GuildUnloadedResponder : IResponder<IGuildDelete>
|
||||
{
|
||||
private readonly GuildDataService _guildData;
|
||||
private readonly ILogger<GuildUnloadedResponder> _logger;
|
|
@ -1,8 +1,5 @@
|
|||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
|
@ -10,15 +7,18 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Discord.Gateway.Responders;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Responders;
|
||||
namespace TeamOctolings.Octobot.Responders;
|
||||
|
||||
/// <summary>
|
||||
/// Handles logging the contents of a deleted message and the user who deleted the message
|
||||
/// to a guild's <see cref="GuildSettings.PrivateFeedbackChannel" /> if one is set.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class MessageDeletedResponder : IResponder<IMessageDelete>
|
||||
public sealed class MessageDeletedResponder : IResponder<IMessageDelete>
|
||||
{
|
||||
private readonly IDiscordRestAuditLogAPI _auditLogApi;
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
|
@ -102,6 +102,6 @@ public class MessageDeletedResponder : IResponder<IMessageDelete>
|
|||
|
||||
return await _channelApi.CreateMessageWithEmbedResultAsync(
|
||||
GuildSettings.PrivateFeedbackChannel.Get(cfg), embedResult: embed,
|
||||
allowedMentions: Octobot.NoMentions, ct: ct);
|
||||
allowedMentions: Utility.NoMentions, ct: ct);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
using System.Text;
|
||||
using DiffPlex.DiffBuilder;
|
||||
using JetBrains.Annotations;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Octobot.Services;
|
||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
|
@ -12,15 +9,18 @@ using Remora.Discord.Caching.Services;
|
|||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Gateway.Responders;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
using TeamOctolings.Octobot.Services;
|
||||
|
||||
namespace Octobot.Responders;
|
||||
namespace TeamOctolings.Octobot.Responders;
|
||||
|
||||
/// <summary>
|
||||
/// Handles logging the difference between an edited message's old and new content
|
||||
/// to a guild's <see cref="GuildSettings.PrivateFeedbackChannel" /> if one is set.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class MessageEditedResponder : IResponder<IMessageUpdate>
|
||||
public sealed class MessageEditedResponder : IResponder<IMessageUpdate>
|
||||
{
|
||||
private readonly CacheService _cacheService;
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
|
@ -104,6 +104,6 @@ public class MessageEditedResponder : IResponder<IMessageUpdate>
|
|||
|
||||
return await _channelApi.CreateMessageWithEmbedResultAsync(
|
||||
GuildSettings.PrivateFeedbackChannel.Get(cfg), embedResult: embed,
|
||||
allowedMentions: Octobot.NoMentions, ct: ct);
|
||||
allowedMentions: Utility.NoMentions, ct: ct);
|
||||
}
|
||||
}
|
|
@ -5,13 +5,13 @@ using Remora.Discord.Gateway.Responders;
|
|||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Octobot.Responders;
|
||||
namespace TeamOctolings.Octobot.Responders;
|
||||
|
||||
/// <summary>
|
||||
/// Handles sending replies to easter egg messages.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class MessageCreateResponder : IResponder<IMessageCreate>
|
||||
public sealed class MessageCreateResponder : IResponder<IMessageCreate>
|
||||
{
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
|
||||
namespace Octobot.Services;
|
||||
namespace TeamOctolings.Octobot.Services;
|
||||
|
||||
public sealed class AccessControlService
|
||||
{
|
||||
|
@ -20,18 +20,17 @@ public sealed class AccessControlService
|
|||
_userApi = userApi;
|
||||
}
|
||||
|
||||
private static bool CheckPermission(IEnumerable<IRole> roles, GuildData data, Snowflake memberId,
|
||||
IGuildMember member,
|
||||
private static bool CheckPermission(IEnumerable<IRole> roles, GuildData data, MemberData memberData,
|
||||
DiscordPermission permission)
|
||||
{
|
||||
var moderatorRole = GuildSettings.ModeratorRole.Get(data.Settings);
|
||||
if (!moderatorRole.Empty() && data.GetOrCreateMemberData(memberId).Roles.Contains(moderatorRole.Value))
|
||||
if (!moderatorRole.Empty() && memberData.Roles.Contains(moderatorRole.Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return roles
|
||||
.Where(r => member.Roles.Contains(r.ID))
|
||||
.Where(r => memberData.Roles.Contains(r.ID.Value))
|
||||
.Any(r =>
|
||||
r.Permissions.HasPermission(permission)
|
||||
);
|
||||
|
@ -80,38 +79,23 @@ public sealed class AccessControlService
|
|||
return Result<string?>.FromError(botResult);
|
||||
}
|
||||
|
||||
var botMemberResult = await _guildApi.GetGuildMemberAsync(guildId, bot.ID, ct);
|
||||
if (!botMemberResult.IsDefined(out var botMember))
|
||||
{
|
||||
return Result<string?>.FromError(botMemberResult);
|
||||
}
|
||||
|
||||
var targetMemberResult = await _guildApi.GetGuildMemberAsync(guildId, targetId, ct);
|
||||
if (!targetMemberResult.IsDefined(out var targetMember))
|
||||
{
|
||||
return Result<string?>.FromSuccess(null);
|
||||
}
|
||||
|
||||
var rolesResult = await _guildApi.GetGuildRolesAsync(guildId, ct);
|
||||
if (!rolesResult.IsDefined(out var roles))
|
||||
{
|
||||
return Result<string?>.FromError(rolesResult);
|
||||
}
|
||||
|
||||
var data = await _data.GetData(guildId, ct);
|
||||
var targetData = data.GetOrCreateMemberData(targetId);
|
||||
var botData = data.GetOrCreateMemberData(bot.ID);
|
||||
|
||||
if (interacterId is null)
|
||||
{
|
||||
return CheckInteractions(action, guild, roles, targetMember, botMember, botMember);
|
||||
return CheckInteractions(action, guild, roles, targetData, botData, botData);
|
||||
}
|
||||
|
||||
var interacterResult = await _guildApi.GetGuildMemberAsync(guildId, interacterId.Value, ct);
|
||||
if (!interacterResult.IsDefined(out var interacter))
|
||||
{
|
||||
return Result<string?>.FromError(interacterResult);
|
||||
}
|
||||
|
||||
var data = await _data.GetData(guildId, ct);
|
||||
|
||||
var hasPermission = CheckPermission(roles, data, interacterId.Value, interacter,
|
||||
var interacterData = data.GetOrCreateMemberData(interacterId.Value);
|
||||
var hasPermission = CheckPermission(roles, data, interacterData,
|
||||
action switch
|
||||
{
|
||||
"Ban" => DiscordPermission.BanMembers,
|
||||
|
@ -122,31 +106,26 @@ public sealed class AccessControlService
|
|||
});
|
||||
|
||||
return hasPermission
|
||||
? CheckInteractions(action, guild, roles, targetMember, botMember, interacter)
|
||||
? CheckInteractions(action, guild, roles, targetData, botData, interacterData)
|
||||
: Result<string?>.FromSuccess($"UserCannot{action}Members".Localized());
|
||||
}
|
||||
|
||||
private static Result<string?> CheckInteractions(
|
||||
string action, IGuild guild, IReadOnlyList<IRole> roles, IGuildMember targetMember, IGuildMember botMember,
|
||||
IGuildMember interacter)
|
||||
string action, IGuild guild, IReadOnlyList<IRole> roles, MemberData targetData, MemberData botData,
|
||||
MemberData interacterData)
|
||||
{
|
||||
if (!targetMember.User.IsDefined(out var targetUser))
|
||||
{
|
||||
return new ArgumentNullError(nameof(targetMember.User));
|
||||
}
|
||||
|
||||
if (botMember.User == targetMember.User)
|
||||
if (botData.Id == targetData.Id)
|
||||
{
|
||||
return Result<string?>.FromSuccess($"UserCannot{action}Bot".Localized());
|
||||
}
|
||||
|
||||
if (targetUser.ID == guild.OwnerID)
|
||||
if (targetData.Id == guild.OwnerID)
|
||||
{
|
||||
return Result<string?>.FromSuccess($"UserCannot{action}Owner".Localized());
|
||||
}
|
||||
|
||||
var targetRoles = roles.Where(r => targetMember.Roles.Contains(r.ID)).ToList();
|
||||
var botRoles = roles.Where(r => botMember.Roles.Contains(r.ID));
|
||||
var targetRoles = roles.Where(r => targetData.Roles.Contains(r.ID.Value)).ToList();
|
||||
var botRoles = roles.Where(r => botData.Roles.Contains(r.ID.Value));
|
||||
|
||||
var targetBotRoleDiff = targetRoles.MaxOrDefault(r => r.Position) - botRoles.MaxOrDefault(r => r.Position);
|
||||
if (targetBotRoleDiff >= 0)
|
||||
|
@ -154,7 +133,7 @@ public sealed class AccessControlService
|
|||
return Result<string?>.FromSuccess($"BotCannot{action}Target".Localized());
|
||||
}
|
||||
|
||||
var interacterRoles = roles.Where(r => interacter.Roles.Contains(r.ID));
|
||||
var interacterRoles = roles.Where(r => interacterData.Roles.Contains(r.ID.Value));
|
||||
var targetInteracterRoleDiff
|
||||
= targetRoles.MaxOrDefault(r => r.Position) - interacterRoles.MaxOrDefault(r => r.Position);
|
||||
return targetInteracterRoleDiff < 0
|
|
@ -3,10 +3,10 @@ using System.Text.Json;
|
|||
using System.Text.Json.Nodes;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octobot.Data;
|
||||
using Remora.Rest.Core;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
|
||||
namespace Octobot.Services;
|
||||
namespace TeamOctolings.Octobot.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Handles saving, loading, initializing and providing <see cref="GuildData" />.
|
|
@ -2,16 +2,16 @@ using System.Text;
|
|||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
|
||||
namespace Octobot.Services.Update;
|
||||
namespace TeamOctolings.Octobot.Services.Update;
|
||||
|
||||
public sealed partial class MemberUpdateService : BackgroundService
|
||||
{
|
|
@ -1,8 +1,6 @@
|
|||
using System.Text.Json.Nodes;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.API.Objects;
|
||||
|
@ -10,8 +8,10 @@ using Remora.Discord.Extensions.Embeds;
|
|||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
|
||||
namespace Octobot.Services.Update;
|
||||
namespace TeamOctolings.Octobot.Services.Update;
|
||||
|
||||
public sealed class ScheduledEventUpdateService : BackgroundService
|
||||
{
|
|
@ -4,7 +4,7 @@ using Remora.Discord.API.Gateway.Commands;
|
|||
using Remora.Discord.API.Objects;
|
||||
using Remora.Discord.Gateway;
|
||||
|
||||
namespace Octobot.Services.Update;
|
||||
namespace TeamOctolings.Octobot.Services.Update;
|
||||
|
||||
public sealed class SongUpdateService : BackgroundService
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
|
@ -16,31 +16,31 @@
|
|||
<Company>TeamOctolings</Company>
|
||||
<NeutralLanguage>en</NeutralLanguage>
|
||||
<Description>A general-purpose Discord bot for moderation written in C#</Description>
|
||||
<ApplicationIcon>docs/octobot.ico</ApplicationIcon>
|
||||
<ApplicationIcon>../docs/octobot.ico</ApplicationIcon>
|
||||
<GitVersion>false</GitVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DiffPlex" Version="1.7.2" />
|
||||
<PackageReference Include="GitInfo" Version="3.3.4" />
|
||||
<PackageReference Include="GitInfo" Version="3.3.5" />
|
||||
<PackageReference Include="Humanizer.Core.ru" Version="2.14.1" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="Remora.Commands" Version="10.0.5" />
|
||||
<PackageReference Include="Remora.Discord.Caching" Version="38.0.1" />
|
||||
<PackageReference Include="Remora.Discord.Extensions" Version="5.3.4" />
|
||||
<PackageReference Include="Remora.Discord.Hosting" Version="6.0.9" />
|
||||
<PackageReference Include="Remora.Discord.Interactivity" Version="4.5.3" />
|
||||
<PackageReference Include="Remora.Discord.Caching" Version="39.0.0" />
|
||||
<PackageReference Include="Remora.Discord.Extensions" Version="5.3.5" />
|
||||
<PackageReference Include="Remora.Discord.Hosting" Version="6.0.10" />
|
||||
<PackageReference Include="Remora.Discord.Interactivity" Version="4.5.4" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="locale\Messages.resx">
|
||||
<EmbeddedResource Update="Messages.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Messages.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="CodeAnalysis\BannedSymbols.txt" />
|
||||
<AdditionalFiles Include="..\CodeAnalysis\BannedSymbols.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,16 +1,19 @@
|
|||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using Octobot.Data;
|
||||
using Octobot.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.API.Objects;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
using TeamOctolings.Octobot.Attributes;
|
||||
using TeamOctolings.Octobot.Data;
|
||||
using TeamOctolings.Octobot.Extensions;
|
||||
|
||||
namespace Octobot.Services;
|
||||
namespace TeamOctolings.Octobot;
|
||||
|
||||
/// <summary>
|
||||
/// Provides utility methods that cannot be transformed to extension methods because they require usage
|
||||
|
@ -18,6 +21,9 @@ namespace Octobot.Services;
|
|||
/// </summary>
|
||||
public sealed class Utility
|
||||
{
|
||||
public static readonly AllowedMentions NoMentions = new(
|
||||
Array.Empty<MentionType>(), Array.Empty<Snowflake>(), Array.Empty<Snowflake>());
|
||||
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
private readonly IDiscordRestGuildScheduledEventAPI _eventApi;
|
||||
private readonly IDiscordRestGuildAPI _guildApi;
|
||||
|
@ -30,6 +36,9 @@ public sealed class Utility
|
|||
_guildApi = guildApi;
|
||||
}
|
||||
|
||||
[StaticCallersOnly]
|
||||
public static ILogger<Program>? StaticLogger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the string mentioning the <see cref="GuildSettings.EventNotificationRole" /> and event subscribers related to
|
||||
/// a scheduled
|
Loading…
Reference in a new issue