2022-12-05 17:04:27 +03:00
|
|
|
|
using System.Text;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
using Boyfriend.Commands;
|
2023-01-18 17:39:24 +03:00
|
|
|
|
using Boyfriend.Data;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
using Discord;
|
|
|
|
|
using Discord.Commands;
|
|
|
|
|
using Discord.WebSocket;
|
|
|
|
|
|
|
|
|
|
namespace Boyfriend;
|
|
|
|
|
|
2022-09-18 17:41:29 +03:00
|
|
|
|
public sealed class CommandProcessor {
|
2022-12-05 17:04:27 +03:00
|
|
|
|
private static readonly string Mention = $"<@{Boyfriend.Client.CurrentUser.Id}>";
|
2022-10-21 09:12:43 +03:00
|
|
|
|
|
2022-09-18 17:41:29 +03:00
|
|
|
|
public static readonly ICommand[] Commands = {
|
2022-08-30 18:15:01 +03:00
|
|
|
|
new BanCommand(), new ClearCommand(), new HelpCommand(),
|
|
|
|
|
new KickCommand(), new MuteCommand(), new PingCommand(),
|
2023-01-18 17:39:24 +03:00
|
|
|
|
new SettingsCommand(), new UnbanCommand(), new UnmuteCommand(),
|
|
|
|
|
new RemindCommand()
|
2022-08-30 18:15:01 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private readonly StringBuilder _stackedPrivateFeedback = new();
|
|
|
|
|
private readonly StringBuilder _stackedPublicFeedback = new();
|
|
|
|
|
private readonly StringBuilder _stackedReplyMessage = new();
|
|
|
|
|
private readonly List<Task> _tasks = new();
|
|
|
|
|
|
|
|
|
|
public readonly SocketCommandContext Context;
|
|
|
|
|
|
|
|
|
|
public bool ConfigWriteScheduled = false;
|
|
|
|
|
|
|
|
|
|
public CommandProcessor(SocketUserMessage message) {
|
|
|
|
|
Context = new SocketCommandContext(Boyfriend.Client, message);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-18 17:41:29 +03:00
|
|
|
|
public async Task HandleCommandAsync() {
|
2022-08-30 18:15:01 +03:00
|
|
|
|
var guild = Context.Guild;
|
2023-01-18 17:39:24 +03:00
|
|
|
|
var data = GuildData.Get(guild);
|
|
|
|
|
Utils.SetCurrentLanguage(guild);
|
2022-08-30 18:15:01 +03:00
|
|
|
|
|
2023-01-18 17:39:24 +03:00
|
|
|
|
if (GetMember().Roles.Contains(data.MuteRole)) {
|
|
|
|
|
_ = Context.Message.DeleteAsync();
|
2022-08-30 18:15:01 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var list = Context.Message.Content.Split("\n");
|
2022-09-18 17:41:29 +03:00
|
|
|
|
var cleanList = Context.Message.CleanContent.Split("\n");
|
2022-11-16 21:27:10 +03:00
|
|
|
|
for (var i = 0; i < list.Length; i++)
|
2023-01-18 17:39:24 +03:00
|
|
|
|
_tasks.Add(RunCommandOnLine(list[i], cleanList[i], data.Preferences["Prefix"]));
|
2022-10-18 20:55:16 +03:00
|
|
|
|
|
2022-11-16 21:27:10 +03:00
|
|
|
|
try { Task.WaitAll(_tasks.ToArray()); } catch (AggregateException e) {
|
|
|
|
|
foreach (var ex in e.InnerExceptions)
|
|
|
|
|
await Boyfriend.Log(new LogMessage(LogSeverity.Error, nameof(CommandProcessor),
|
|
|
|
|
"Exception while executing commands", ex));
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-18 17:41:29 +03:00
|
|
|
|
_tasks.Clear();
|
2022-08-30 18:15:01 +03:00
|
|
|
|
|
2023-01-18 17:39:24 +03:00
|
|
|
|
if (ConfigWriteScheduled) await data.Save(true);
|
2022-08-30 18:15:01 +03:00
|
|
|
|
|
2022-09-18 17:41:29 +03:00
|
|
|
|
SendFeedbacks();
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-16 21:27:10 +03:00
|
|
|
|
private async Task RunCommandOnLine(string line, string cleanLine, string prefix) {
|
2022-11-12 09:02:44 +03:00
|
|
|
|
var prefixed = line.StartsWith(prefix);
|
|
|
|
|
if (!prefixed && !line.StartsWith(Mention)) return;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
foreach (var command in Commands) {
|
2022-10-21 09:12:43 +03:00
|
|
|
|
var lineNoMention = line.Remove(0, prefixed ? prefix.Length : Mention.Length);
|
|
|
|
|
if (!command.Aliases.Contains(lineNoMention.Trim().Split()[0])) continue;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
|
2022-10-21 09:12:43 +03:00
|
|
|
|
var args = lineNoMention.Trim().Split().Skip(1).ToArray();
|
2022-09-18 17:41:29 +03:00
|
|
|
|
var cleanArgs = cleanLine.Split().Skip(lineNoMention.StartsWith(" ") ? 2 : 1).ToArray();
|
2022-11-16 21:27:10 +03:00
|
|
|
|
await command.RunAsync(this, args, cleanArgs);
|
|
|
|
|
if (_stackedReplyMessage.Length > 0) _ = Context.Channel.TriggerTypingAsync();
|
2022-10-21 09:12:43 +03:00
|
|
|
|
return;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Reply(string response, string? customEmoji = null) {
|
2022-12-09 14:47:11 +03:00
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{customEmoji ?? ReplyEmojis.Success} {response}",
|
|
|
|
|
Context.Message);
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Audit(string action, bool isPublic = true) {
|
2022-11-11 22:59:11 +03:00
|
|
|
|
var format = $"*[{Context.User.Mention}: {action}]*";
|
2023-01-18 17:39:24 +03:00
|
|
|
|
var data = GuildData.Get(Context.Guild);
|
|
|
|
|
if (isPublic) Utils.SafeAppendToBuilder(_stackedPublicFeedback, format, data.PublicFeedbackChannel);
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedPrivateFeedback, format, data.PrivateFeedbackChannel);
|
2022-11-11 22:59:11 +03:00
|
|
|
|
if (_tasks.Count is 0) SendFeedbacks(false);
|
2022-09-18 17:41:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SendFeedbacks(bool reply = true) {
|
|
|
|
|
if (reply && _stackedReplyMessage.Length > 0)
|
|
|
|
|
_ = Context.Message.ReplyAsync(_stackedReplyMessage.ToString(), false, null, AllowedMentions.None);
|
|
|
|
|
|
2023-01-18 17:39:24 +03:00
|
|
|
|
var data = GuildData.Get(Context.Guild);
|
|
|
|
|
var adminChannel = data.PublicFeedbackChannel;
|
|
|
|
|
var systemChannel = data.PrivateFeedbackChannel;
|
2022-11-11 22:59:11 +03:00
|
|
|
|
if (_stackedPrivateFeedback.Length > 0 && adminChannel is not null &&
|
2022-09-18 17:41:29 +03:00
|
|
|
|
adminChannel.Id != Context.Message.Channel.Id) {
|
|
|
|
|
_ = Utils.SilentSendAsync(adminChannel, _stackedPrivateFeedback.ToString());
|
|
|
|
|
_stackedPrivateFeedback.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-11 22:59:11 +03:00
|
|
|
|
if (_stackedPublicFeedback.Length > 0 && systemChannel is not null && systemChannel.Id != adminChannel?.Id
|
2022-09-18 17:41:29 +03:00
|
|
|
|
&& systemChannel.Id != Context.Message.Channel.Id) {
|
|
|
|
|
_ = Utils.SilentSendAsync(systemChannel, _stackedPublicFeedback.ToString());
|
|
|
|
|
_stackedPublicFeedback.Clear();
|
|
|
|
|
}
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string? GetRemaining(string[] from, int startIndex, string? argument) {
|
2022-11-11 22:59:11 +03:00
|
|
|
|
if (startIndex >= from.Length && argument is not null)
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2022-12-06 18:33:46 +03:00
|
|
|
|
$"{ReplyEmojis.MissingArgument} {Utils.GetMessage($"Missing{argument}")}", Context.Message);
|
2022-11-11 21:17:44 +03:00
|
|
|
|
else return string.Join(" ", from, startIndex, from.Length - startIndex);
|
2022-08-30 18:15:01 +03:00
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-18 17:39:24 +03:00
|
|
|
|
public Tuple<ulong, SocketUser?>? GetUser(string[] args, string[] cleanArgs, int index) {
|
2022-08-30 18:15:01 +03:00
|
|
|
|
if (index >= args.Length) {
|
2022-12-06 18:33:46 +03:00
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingUser}",
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Context.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-18 17:39:24 +03:00
|
|
|
|
var mention = Utils.ParseMention(args[index]);
|
|
|
|
|
if (mention is 0) {
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2022-12-06 18:33:46 +03:00
|
|
|
|
$"{ReplyEmojis.InvalidArgument} {string.Format(Messages.InvalidUser, Utils.Wrap(cleanArgs[index]))}",
|
2022-09-18 17:41:29 +03:00
|
|
|
|
Context.Message);
|
2023-01-18 17:39:24 +03:00
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var exists = Utils.UserExists(mention);
|
|
|
|
|
if (!exists) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
|
|
|
|
$"{ReplyEmojis.Error} {string.Format(Messages.UserNotFound, Utils.Wrap(cleanArgs[index]))}",
|
|
|
|
|
Context.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Tuple.Create(mention, Boyfriend.Client.GetUser(mention))!;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool HasPermission(GuildPermission permission) {
|
|
|
|
|
if (!Context.Guild.CurrentUser.GuildPermissions.Has(permission)) {
|
2022-12-09 14:47:11 +03:00
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
|
|
|
|
$"{ReplyEmojis.NoPermission} {Utils.GetMessage($"BotCannot{permission}")}",
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Context.Message);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-18 17:39:24 +03:00
|
|
|
|
if (!GetMember().GuildPermissions.Has(permission)
|
2022-12-09 14:47:11 +03:00
|
|
|
|
&& Context.Guild.OwnerId != Context.User.Id) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
|
|
|
|
$"{ReplyEmojis.NoPermission} {Utils.GetMessage($"UserCannot{permission}")}",
|
|
|
|
|
Context.Message);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-08-30 18:15:01 +03:00
|
|
|
|
|
2022-12-09 14:47:11 +03:00
|
|
|
|
return true;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-18 17:39:24 +03:00
|
|
|
|
private SocketGuildUser GetMember() {
|
|
|
|
|
return GetMember(Context.User.Id)!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SocketGuildUser? GetMember(ulong id) {
|
|
|
|
|
return Context.Guild.GetUser(id);
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-18 17:39:24 +03:00
|
|
|
|
public SocketGuildUser? GetMember(string[] args, int index) {
|
2022-08-30 18:15:01 +03:00
|
|
|
|
if (index >= args.Length) {
|
2022-12-06 18:33:46 +03:00
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingMember}",
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Context.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var member = Context.Guild.GetUser(Utils.ParseMention(args[index]));
|
2023-01-18 17:39:24 +03:00
|
|
|
|
if (member is null)
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2023-01-18 17:39:24 +03:00
|
|
|
|
$"{ReplyEmojis.InvalidArgument} {Messages.InvalidMember}",
|
2022-09-18 17:41:29 +03:00
|
|
|
|
Context.Message);
|
2022-08-30 18:15:01 +03:00
|
|
|
|
return member;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ulong? GetBan(string[] args, int index) {
|
|
|
|
|
if (index >= args.Length) {
|
2022-12-06 18:33:46 +03:00
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingUser}",
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Context.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var id = Utils.ParseMention(args[index]);
|
2022-11-11 22:59:11 +03:00
|
|
|
|
if (Context.Guild.GetBanAsync(id) is null) {
|
2022-09-18 17:41:29 +03:00
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage, Messages.UserNotBanned, Context.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return id;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? GetNumberRange(string[] args, int index, int min, int max, string? argument) {
|
|
|
|
|
if (index >= args.Length) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2022-12-06 18:33:46 +03:00
|
|
|
|
$"{ReplyEmojis.MissingArgument} {string.Format(Messages.MissingNumber, min.ToString(), max.ToString())}",
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Context.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!int.TryParse(args[index], out var i)) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2022-12-06 18:33:46 +03:00
|
|
|
|
$"{ReplyEmojis.InvalidArgument} {string.Format(Utils.GetMessage($"{argument}Invalid"), min.ToString(), max.ToString(), Utils.Wrap(args[index]))}",
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Context.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-11 22:59:11 +03:00
|
|
|
|
if (argument is null) return i;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
if (i < min) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2022-12-06 18:33:46 +03:00
|
|
|
|
$"{ReplyEmojis.InvalidArgument} {string.Format(Utils.GetMessage($"{argument}TooSmall"), min.ToString())}",
|
2022-08-30 18:15:01 +03:00
|
|
|
|
Context.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-09 14:47:11 +03:00
|
|
|
|
if (i > max) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
|
|
|
|
$"{ReplyEmojis.InvalidArgument} {string.Format(Utils.GetMessage($"{argument}TooLarge"), max.ToString())}",
|
|
|
|
|
Context.Message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return i;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static TimeSpan GetTimeSpan(string[] args, int index) {
|
|
|
|
|
var infinity = TimeSpan.FromMilliseconds(-1);
|
2022-11-11 21:17:44 +03:00
|
|
|
|
if (index >= args.Length) return infinity;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
var chars = args[index].AsSpan();
|
|
|
|
|
var numberBuilder = Boyfriend.StringBuilder;
|
|
|
|
|
int days = 0, hours = 0, minutes = 0, seconds = 0;
|
|
|
|
|
foreach (var c in chars)
|
|
|
|
|
if (char.IsDigit(c)) { numberBuilder.Append(c); } else {
|
2022-11-11 22:59:11 +03:00
|
|
|
|
if (numberBuilder.Length is 0) return infinity;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
switch (c) {
|
|
|
|
|
case 'd' or 'D' or 'д' or 'Д':
|
|
|
|
|
days += int.Parse(numberBuilder.ToString());
|
|
|
|
|
numberBuilder.Clear();
|
|
|
|
|
break;
|
|
|
|
|
case 'h' or 'H' or 'ч' or 'Ч':
|
|
|
|
|
hours += int.Parse(numberBuilder.ToString());
|
|
|
|
|
numberBuilder.Clear();
|
|
|
|
|
break;
|
|
|
|
|
case 'm' or 'M' or 'м' or 'М':
|
|
|
|
|
minutes += int.Parse(numberBuilder.ToString());
|
|
|
|
|
numberBuilder.Clear();
|
|
|
|
|
break;
|
|
|
|
|
case 's' or 'S' or 'с' or 'С':
|
|
|
|
|
seconds += int.Parse(numberBuilder.ToString());
|
|
|
|
|
numberBuilder.Clear();
|
|
|
|
|
break;
|
|
|
|
|
default: return infinity;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
numberBuilder.Clear();
|
|
|
|
|
return new TimeSpan(days, hours, minutes, seconds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool CanInteractWith(SocketGuildUser user, string action) {
|
|
|
|
|
if (Context.User.Id == user.Id) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2022-12-06 18:33:46 +03:00
|
|
|
|
$"{ReplyEmojis.CantInteract} {Utils.GetMessage($"UserCannot{action}Themselves")}", Context.Message);
|
2022-08-30 18:15:01 +03:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Context.Guild.CurrentUser.Id == user.Id) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2022-12-06 18:33:46 +03:00
|
|
|
|
$"{ReplyEmojis.CantInteract} {Utils.GetMessage($"UserCannot{action}Bot")}", Context.Message);
|
2022-08-30 18:15:01 +03:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-18 21:46:43 +03:00
|
|
|
|
if (Context.Guild.Owner.Id == user.Id) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2022-12-06 18:33:46 +03:00
|
|
|
|
$"{ReplyEmojis.CantInteract} {Utils.GetMessage($"UserCannot{action}Owner")}", Context.Message);
|
2022-10-18 21:46:43 +03:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 18:15:01 +03:00
|
|
|
|
if (Context.Guild.CurrentUser.Hierarchy <= user.Hierarchy) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
2022-12-06 18:33:46 +03:00
|
|
|
|
$"{ReplyEmojis.CantInteract} {Utils.GetMessage($"BotCannot{action}Target")}", Context.Message);
|
2022-08-30 18:15:01 +03:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-09 14:47:11 +03:00
|
|
|
|
if (Context.Guild.Owner.Id != Context.User.Id && GetMember().Hierarchy <= user.Hierarchy) {
|
|
|
|
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
|
|
|
|
$"{ReplyEmojis.CantInteract} {Utils.GetMessage($"UserCannot{action}Target")}", Context.Message);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2022-08-30 18:15:01 +03:00
|
|
|
|
}
|
|
|
|
|
}
|