2025-03-25 13:15:37 +05:00

161 lines
6.2 KiB
C#

using System.IO;
using System.Net.Http;
using CompatApiClient.Compression;
using CompatBot.Commands.Converters;
using DSharpPlus.Commands.Processors.TextCommands;
using DSharpPlus.Commands.Processors.TextCommands.Parsing;
using Microsoft.Extensions.DependencyInjection;
namespace CompatBot.Commands;
[Command("sudo"), RequiresBotSudoerRole, AllowDMUsage]
[Description("Used to manage bot moderators and sudoers")]
internal static partial class Sudo
{
[Command("say"), RequiresDm]
[Description("Make bot say things. Specify #channel or put message link in the beginning to specify where to reply")]
public static async ValueTask Say(
TextCommandContext ctx,
[Description("Message text to send"), RemainingText] string message
)
{
var channel = ctx.Channel;
DiscordMessage? ogMsg = null;
if (message.Split(' ', 2, StringSplitOptions.TrimEntries) is [{Length: >0} chOrLink, {Length: >0} msg])
{
if (await ctx.GetMessageAsync(chOrLink).ConfigureAwait(false) is DiscordMessage lnk)
{
ogMsg = lnk;
channel = ogMsg.Channel;
message = msg;
}
else
{
if (await ctx.ParseChannelNameAsync(chOrLink).ConfigureAwait(false) is {} ch)
{
channel = ch;
message = msg;
}
}
}
if (channel is null)
return;
var typingTask = channel.TriggerTypingAsync();
// simulate bot typing the message at 300 cps
await Task.Delay(message.Length * 10 / 3).ConfigureAwait(false);
var msgBuilder = new DiscordMessageBuilder().WithContent(message);
if (ogMsg is not null)
msgBuilder.WithReply(ogMsg.Id);
if (ctx.Message.Attachments.Count > 0)
{
try
{
await using var memStream = Config.MemoryStreamManager.GetStream();
using var client = HttpClientFactory.Create(new CompressionMessageHandler());
await using var requestStream = await client.GetStreamAsync(ctx.Message.Attachments[0].Url!).ConfigureAwait(false);
await requestStream.CopyToAsync(memStream).ConfigureAwait(false);
memStream.Seek(0, SeekOrigin.Begin);
msgBuilder.AddFile(ctx.Message.Attachments[0].FileName!, memStream);
await channel.SendMessageAsync(msgBuilder).ConfigureAwait(false);
}
catch { }
}
else
await channel.SendMessageAsync(msgBuilder).ConfigureAwait(false);
await typingTask.ConfigureAwait(false);
}
[Command("react"), RequiresDm]
[Description("Add reactions to the specified message")]
public static async ValueTask React(
TextCommandContext ctx,
[Description("Message link")] string messageLink,
[Description("List of reactions to add"), RemainingText]string emojis
)
{
try
{
var message = await ctx.GetMessageAsync(messageLink).ConfigureAwait(false);
if (message is null)
{
await ctx.ReactWithAsync(Config.Reactions.Failure, "Couldn't find the message").ConfigureAwait(false);
return;
}
string emoji = "";
for (var i = 0; i < emojis.Length; i++)
{
try
{
var c = emojis[i];
if (char.IsHighSurrogate(c))
emoji += c;
else
{
DiscordEmoji de;
if (c == '<')
{
var endIdx = emojis.IndexOf('>', i);
if (endIdx < i)
endIdx = emojis.Length;
emoji = emojis[i..endIdx];
i = endIdx - 1;
var emojiId = ulong.Parse(emoji[(emoji.LastIndexOf(':') + 1)..]);
de = DiscordEmoji.FromGuildEmote(ctx.Client, emojiId);
}
else
de = DiscordEmoji.FromUnicode(emoji + c);
emoji = "";
await message.ReactWithAsync(de).ConfigureAwait(false);
}
}
catch { }
}
}
catch (Exception e)
{
Config.Log.Debug(e);
}
}
[Command("salt")]
[Description("Regenerate salt for data anonymization. This WILL affect Hardware DB deduplication.")]
public static async ValueTask ResetCryptoSalt(
SlashCommandContext ctx,
[Description("Should be `I understand this will break hardware survey deduplication`")]
string confirmation
)
{
if (confirmation is not "I understand this will break hardware survey deduplication")
{
await ctx.RespondAsync($"{Config.Reactions.Failure} Operation cancelled.", ephemeral: true).ConfigureAwait(false);
return;
}
var salt = new byte[256 / 8];
System.Security.Cryptography.RandomNumberGenerator.Fill(salt);
await Bot.Configuration.Set(ctx, nameof(Config.CryptoSalt), Convert.ToBase64String(salt)).ConfigureAwait(false);
}
private static async ValueTask<DiscordChannel?> ParseChannelNameAsync(this TextCommandContext ctx, string channelName)
{
await using var scope = ctx.Extension.ServiceProvider.CreateAsyncScope();
if (await TextOnlyDiscordChannelConverter.ConvertAsync(new TextConverterContext()
{
User = ctx.User,
Channel = ctx.Channel,
Message = ctx.Message,
Command = ctx.Command,
RawArguments = channelName,
PrefixLength = ctx.Prefix?.Length ?? 0,
Splicer = DefaultTextArgumentSplicer.Splice,
Extension = ctx.Extension,
ServiceScope = scope,
}).ConfigureAwait(false) is { HasValue: true } ch)
return ch.Value;
return null;
}
}