1
0
Fork 1
mirror of https://github.com/TeamOctolings/Octobot.git synced 2025-01-31 09:09:00 +03:00

more stuff. yeah

This commit is contained in:
l1ttleO 2021-12-15 11:19:14 +05:00
parent 382add19a3
commit 270fba5c3c
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
13 changed files with 301 additions and 116 deletions

View file

@ -1,8 +1,10 @@
using Discord;
using System.Text.Json;
using Discord;
using Discord.WebSocket;
namespace Boyfriend;
public static class Boyfriend {
public static class Boyfriend {
public static void Main()
=> Init().GetAwaiter().GetResult();
@ -11,8 +13,11 @@ namespace Boyfriend;
MessageCacheSize = 250,
GatewayIntents = GatewayIntents.All
};
public static readonly DiscordSocketClient Client = new(Config);
private static readonly Dictionary<ulong, GuildConfig> GuildConfigDictionary = new();
private static async Task Init() {
Client.Log += Log;
var token = (await File.ReadAllTextAsync("token.txt")).Trim();
@ -30,4 +35,28 @@ namespace Boyfriend;
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
}
public static async Task SetupGuildConfigs() {
foreach (var guild in Client.Guilds) {
var path = "config_" + guild.Id + ".json";
var openStream = !File.Exists(path) ? File.Create(path) : File.OpenRead(path);
GuildConfig config;
try {
config = await JsonSerializer.DeserializeAsync<GuildConfig>(openStream) ?? throw new Exception();
} catch (JsonException) {
config = new GuildConfig(guild.Id, "ru", "!", false);
}
GuildConfigDictionary.Add(guild.Id, config);
}
}
public static GuildConfig GetGuildConfig(IGuild guild) {
GuildConfig toReturn;
toReturn = GuildConfigDictionary.ContainsKey(guild.Id) ? GuildConfigDictionary[guild.Id]
: new GuildConfig(guild.Id, "ru", "!", false);
if (toReturn.Id != guild.Id) throw new Exception();
return toReturn;
}
}

View file

@ -0,0 +1,74 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace Boyfriend;
public static class CommandHandler {
public static async Task HandleCommand(SocketUserMessage message, int argPos) {
var context = new SocketCommandContext(Boyfriend.Client, message);
var result = await EventHandler.Commands.ExecuteAsync(context, argPos, null);
await HandleErrors(context, result);
}
private static async Task HandleErrors(SocketCommandContext context, IResult result) {
var channel = context.Channel;
var reason = Utils.WrapInline(result.ErrorReason);
switch (result.Error) {
case CommandError.Exception:
await channel.SendMessageAsync(reason);
break;
case CommandError.Unsuccessful:
await channel.SendMessageAsync($"Выполнение команды завершилось неудачей: {reason}");
break;
case CommandError.MultipleMatches:
await channel.SendMessageAsync($"Обнаружены повторяющиеся типы аргументов! {reason}");
break;
case CommandError.ParseFailed:
await channel.SendMessageAsync($"Не удалось обработать команду: {reason}");
break;
case CommandError.UnknownCommand:
await channel.SendMessageAsync($"Неизвестная команда! {reason}");
break;
case CommandError.UnmetPrecondition:
await channel.SendMessageAsync($"У тебя недостаточно прав для выполнения этой к: {reason}");
break;
case CommandError.BadArgCount:
await channel.SendMessageAsync($"Неверное количество аргументов! {reason}");
break;
case CommandError.ObjectNotFound:
await channel.SendMessageAsync($"Нету нужных аргументов! {reason}");
break;
case null:
break;
default:
throw new Exception("CommandError");
}
}
public static async Task CheckPermissions(IGuildUser user, GuildPermission toCheck,
GuildPermission forBot = GuildPermission.StartEmbeddedActivities) {
if (forBot == GuildPermission.StartEmbeddedActivities) forBot = toCheck;
if (!(await user.Guild.GetCurrentUserAsync()).GuildPermissions.Has(forBot))
throw new Exception("У меня недостаточно прав для выполнения этой команды!");
if (!user.GuildPermissions.Has(toCheck))
throw new Exception("У тебя недостаточно прав для выполнения этой команды!");
}
public static async Task CheckInteractions(IGuildUser actor, IGuildUser target) {
if (actor.Guild != target.Guild)
throw new Exception("Участники находятся в разных гильдиях!");
var me = await target.Guild.GetCurrentUserAsync();
if (actor.Id == actor.Guild.OwnerId) return;
if (target.Id == target.Guild.OwnerId)
throw new Exception("Ты не можешь взаимодействовать с владельцем сервера!");
if (actor == target)
throw new Exception("Ты не можешь взаимодействовать с самим собой!");
if (target == me)
throw new Exception("Ты не можешь со мной взаимодействовать!");
if (actor.Hierarchy <= target.Hierarchy)
throw new Exception("Ты не можешь взаимодействовать с этим участником!");
if (me.Hierarchy <= target.Hierarchy)
throw new Exception("Я не могу взаимодействовать с этим участником!");
}
}

View file

@ -12,18 +12,26 @@ public class BanModule : ModuleBase<SocketCommandContext> {
[Command("ban")]
[Summary("Банит пользователя")]
[Alias("бан")]
[RequireBotPermission(GuildPermission.BanMembers)]
[RequireUserPermission(GuildPermission.BanMembers)]
public Task Run(string user, TimeSpan duration, [Remainder]string reason) {
var toBan = Utils.ParseUser(user).Result;
BanUser(Context.Guild, Context.User, toBan, duration, reason);
return Task.CompletedTask;
public async Task Run(string user, string durationString, [Remainder]string reason) {
TimeSpan duration;
try {
duration = TimeSpan.Parse(durationString);
} catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
duration = TimeSpan.FromMilliseconds(-1);
reason = durationString + reason;
}
var author = Context.Guild.GetUser(Context.User.Id);
var toBan = await Utils.ParseUser(user);
await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
var memberToBan = Context.Guild.GetUser(toBan.Id);
if (memberToBan != null)
await CommandHandler.CheckInteractions(author, memberToBan);
BanUser(Context.Guild, Context.Guild.GetUser(Context.User.Id), toBan, duration, reason);
}
public static async void BanUser(IGuild guild, IUser author, IUser toBan, TimeSpan duration, string reason) {
public static async void BanUser(IGuild guild, IGuildUser author, IUser toBan, TimeSpan duration, string reason) {
var authorMention = author.Mention;
await Utils.SendDirectMessage(toBan, $"Тебя забанил {author.Mention} на сервере {guild.Name} за `{reason}`");
var guildBanMessage = $"({author.Username}#{author.Discriminator}) {reason}";
await guild.AddBanAsync(toBan, 0, guildBanMessage);
var notification = $"{authorMention} банит {toBan.Mention} за {Utils.WrapInline(reason)}";

View file

@ -12,15 +12,14 @@ public class ClearModule : ModuleBase<SocketCommandContext> {
[Command("clear")]
[Summary("Удаляет указанное количество сообщений")]
[Alias("очистить")]
[RequireBotPermission(GuildPermission.ManageMessages)]
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task Run(int toDelete) {
if (Context.Channel is not ITextChannel channel) return;
await CommandHandler.CheckPermissions(Context.Guild.GetUser(Context.User.Id), GuildPermission.ManageMessages);
switch (toDelete) {
case < 1:
throw new ArgumentException("toDelete is less than 1.");
throw new Exception( "Указано отрицательное количество сообщений!");
case > 200:
throw new ArgumentException("toDelete is more than 200.");
throw new Exception("Указано слишком много сообщений!");
default: {
var messages = await channel.GetMessagesAsync(toDelete + 1).FlattenAsync();
await channel.DeleteMessagesAsync(messages);

View file

@ -0,0 +1,25 @@
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands;
public class HelpModule : ModuleBase<SocketCommandContext> {
[Command("help")]
[Summary("Показывает эту справку")]
[Alias("помощь", "справка")]
public Task Run() {
var nl = Environment.NewLine;
var toSend = $"Справка по командам:{nl}";
var prefix = Boyfriend.GetGuildConfig(Context.Guild).Prefix;
foreach (var command in EventHandler.Commands.Commands) {
var aliases = command.Aliases.Aggregate("", (current, alias) =>
current + (current == "" ? "" : $", {prefix}") + alias);
toSend += $"`{prefix}{aliases}`: {command.Summary}{nl}";
}
Context.Channel.SendMessageAsync(toSend);
return Task.CompletedTask;
}
}

View file

@ -12,15 +12,15 @@ public class KickModule : ModuleBase<SocketCommandContext> {
[Command("kick")]
[Summary("Выгоняет пользователя")]
[Alias("кик")]
[RequireBotPermission(GuildPermission.KickMembers)]
[RequireUserPermission(GuildPermission.KickMembers)]
public Task Run(string user, [Remainder]string reason) {
public async Task Run(string user, [Remainder]string reason) {
var author = Context.Guild.GetUser(Context.User.Id);
var toKick = Utils.ParseMember(Context.Guild, user).Result;
KickMember(Context.Guild, Context.User, toKick, reason);
return Task.CompletedTask;
await CommandHandler.CheckPermissions(author, GuildPermission.KickMembers);
await CommandHandler.CheckInteractions(author, toKick);
KickMember(Context.Guild, Context.Guild.GetUser(Context.User.Id), toKick, reason);
}
private static async void KickMember(IGuild guild, IUser author, IGuildUser toKick, string reason) {
private static async void KickMember(IGuild guild, IGuildUser author, IGuildUser toKick, string reason) {
var authorMention = author.Mention;
await Utils.SendDirectMessage(toKick, $"Тебя кикнул {authorMention} на сервере {guild.Name} за " +
$"{Utils.WrapInline(reason)}");

View file

@ -12,18 +12,33 @@ public class MuteModule : ModuleBase<SocketCommandContext> {
[Command("mute")]
[Summary("Глушит пользователя")]
[Alias("мут")]
[RequireBotPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.ManageMessages)]
public Task Run(string user, TimeSpan duration, [Remainder]string reason) {
var toMute = Utils.ParseMember(Context.Guild, user).Result;
MuteMember(Context.Guild, Context.User, toMute, duration, reason);
return Task.CompletedTask;
public async Task Run(string user, string durationString, [Remainder]string reason) {
TimeSpan duration;
try {
duration = TimeSpan.Parse(durationString);
} catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
duration = TimeSpan.FromMilliseconds(-1);
reason = durationString + reason;
}
var author = Context.Guild.GetUser(Context.User.Id);
var toMute = await Utils.ParseMember(Context.Guild, user);
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
await CommandHandler.CheckInteractions(author, toMute);
MuteMember(Context.Guild, Context.Guild.GetUser(Context.User.Id), toMute, duration, reason);
}
private static async void MuteMember(IGuild guild, IMentionable author, IGuildUser toMute, TimeSpan duration,
private static async void MuteMember(IGuild guild, IGuildUser author, IGuildUser toMute, TimeSpan duration,
string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
var authorMention = author.Mention;
var role = Utils.GetMuteRole(guild);
if (Boyfriend.GetGuildConfig(guild).RemoveRolesOnMute) {
foreach (var roleId in toMute.RoleIds) {
await toMute.RemoveRoleAsync(roleId);
}
}
await toMute.AddRoleAsync(role);
var notification = $"{authorMention} глушит {toMute.Mention} за {Utils.WrapInline(reason)}";
await Utils.SilentSendAsync(guild.GetSystemChannelAsync().Result, notification);

View file

@ -0,0 +1,46 @@
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands;
public class SettingsModule : ModuleBase<SocketCommandContext> {
[Command("settings")]
[Summary("Настраивает бота")]
[Alias("config", "настройки", "конфиг")]
public async Task Run([Remainder] string s = "") {
var config = Boyfriend.GetGuildConfig(Context.Guild);
var sArray = s.Split(" ");
if (s == "") {
var nl = Environment.NewLine;
await Context.Channel.SendMessageAsync($"Текущие настройки:{nl}Язык: `{config.Lang}`" +
$"{nl}Префикс: `{config.Prefix}`" +
$"{nl}Удалять роли при муте: " +
$"{(config.RemoveRolesOnMute ? "Да" : "Нет")}");
return;
}
if (sArray[0].ToLower() == "lang") {
if (sArray[1].ToLower() != "ru") throw new Exception("Язык не поддерживается!");
config.Lang = sArray[1].ToLower();
}
if (sArray[0].ToLower() == "prefix")
config.Prefix = sArray[1];
if (sArray[0].ToLower() == "removerolesonmute") {
try {
config.RemoveRolesOnMute = bool.Parse(sArray[1].ToLower());
} catch (FormatException) {
await Context.Channel.SendMessageAsync("Неверный параметр! Требуется `true` или `false`");
return;
}
}
config.Save();
await Context.Channel.SendMessageAsync("Настройки успешно обновлены!");
}
}

View file

@ -11,15 +11,16 @@ public class UnbanModule : ModuleBase<SocketCommandContext> {
[Command("unban")]
[Summary("Возвращает пользователя из бана")]
[Alias("разбан")]
[RequireBotPermission(GuildPermission.BanMembers)]
[RequireUserPermission(GuildPermission.BanMembers)]
public Task Run(string user, [Remainder] string reason) {
var toBan = Utils.ParseUser(user).Result;
UnbanUser(Context.Guild, Context.User, toBan, reason);
var toUnban = Utils.ParseUser(user).Result;
if (Context.Guild.GetBanAsync(toUnban.Id) == null)
throw new Exception("Пользователь не забанен!");
UnbanUser(Context.Guild, Context.Guild.GetUser(Context.User.Id), toUnban, reason);
return Task.CompletedTask;
}
public static async void UnbanUser(IGuild guild, IUser author, IUser toUnban, string reason) {
public static async void UnbanUser(IGuild guild, IGuildUser author, IUser toUnban, string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
var authorMention = author.Mention;
var notification = $"{authorMention} возвращает из бана {toUnban.Mention} за {Utils.WrapInline(reason)}";
await guild.RemoveBanAsync(toUnban);

View file

@ -11,15 +11,18 @@ public class UnmuteModule : ModuleBase<SocketCommandContext> {
[Command("unmute")]
[Summary("Возвращает пользователя из мута")]
[Alias("размут")]
[RequireBotPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.ManageMessages)]
public Task Run(string user, [Remainder] string reason) {
public async Task Run(string user, [Remainder] string reason) {
var toUnmute = Utils.ParseMember(Context.Guild, user).Result;
UnmuteMember(Context.Guild, Context.User, toUnmute, reason);
return Task.CompletedTask;
var author = Context.Guild.GetUser(Context.User.Id);
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
await CommandHandler.CheckInteractions(author, toUnmute);
if (toUnmute.RoleIds.All(x => x != Utils.GetMuteRole(Context.Guild).Id))
throw new Exception("Пользователь не в муте!");
UnmuteMember(Context.Guild, Context.Guild.GetUser(Context.User.Id), toUnmute, reason);
}
public static async void UnmuteMember(IGuild guild, IUser author, IGuildUser toUnmute, string reason) {
public static async void UnmuteMember(IGuild guild, IGuildUser author, IGuildUser toUnmute, string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
var authorMention = author.Mention;
var notification = $"{authorMention} возвращает из мута {toUnmute.Mention} за {Utils.WrapInline(reason)}";
await toUnmute.RemoveRoleAsync(Utils.GetMuteRole(guild));

View file

@ -8,7 +8,7 @@ namespace Boyfriend;
public class EventHandler {
private readonly DiscordSocketClient _client = Boyfriend.Client;
private readonly CommandService _commands = new();
public static readonly CommandService Commands = new();
public async Task InitEvents() {
_client.Ready += ReadyEvent;
@ -16,50 +16,16 @@ public class EventHandler {
_client.MessageReceived += MessageReceivedEvent;
_client.MessageUpdated += MessageUpdatedEvent;
_client.UserJoined += UserJoinedEvent;
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), null);
}
private static async Task HandleErrors(SocketCommandContext context, IResult result) {
var channel = context.Channel;
var reason = Utils.WrapInline(result.ErrorReason);
switch (result.Error) {
case CommandError.Exception:
await channel.SendMessageAsync($"Произошла непредвиденная ошибка при выполнении команды: {reason}");
break;
case CommandError.Unsuccessful:
await channel.SendMessageAsync($"Выполнение команды завершилось неудачей: {reason}");
break;
case CommandError.MultipleMatches:
await channel.SendMessageAsync($"Обнаружены повторяющиеся типы аргументов! {reason}");
break;
case CommandError.ParseFailed:
await channel.SendMessageAsync($"Не удалось обработать команду: {reason}");
break;
case CommandError.UnknownCommand:
await channel.SendMessageAsync($"Неизвестная команда! {reason}");
break;
case CommandError.UnmetPrecondition:
await channel.SendMessageAsync($"У тебя недостаточно прав для выполнения этой команды! {reason}");
break;
case CommandError.BadArgCount:
await channel.SendMessageAsync($"Неверное количество аргументов! {reason}");
break;
case CommandError.ObjectNotFound:
await channel.SendMessageAsync($"Нету нужных аргументов! {reason}");
break;
case null:
break;
default:
throw new ArgumentException("CommandError");
}
await Commands.AddModulesAsync(Assembly.GetEntryAssembly(), null);
}
[Obsolete("Stop hard-coding things!")]
private async Task ReadyEvent() {
if (_client.GetChannel(618044439939645444) is not IMessageChannel botLogChannel)
throw new ArgumentException("Invalid bot log channel");
throw new Exception("Invalid bot log channel");
await botLogChannel.SendMessageAsync($"{Utils.GetBeep()}Я запустился! (C#)");
await Boyfriend.SetupGuildConfigs();
}
private static async Task MessageDeletedEvent(Cacheable<IMessage, ulong> message,
@ -72,34 +38,38 @@ public class EventHandler {
await Utils.SilentSendAsync(Utils.GetAdminLogChannel(), toSend);
}
private async Task MessageReceivedEvent(SocketMessage messageParam) {
if (messageParam is not SocketUserMessage {Author: IGuildUser user} message) return;
var argPos = 0;
private static async Task MessageReceivedEvent(SocketMessage messageParam) {
if (messageParam is not SocketUserMessage message) return;
var user = (IGuildUser) message.Author;
var guild = user.Guild;
var argPos = 0;
if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2)
&& !user.GuildPermissions.MentionEveryone)
BanModule.BanUser(guild, guild.GetCurrentUserAsync().Result, user, TimeSpan.FromMilliseconds(-1),
"Более 3-ёх упоминаний в одном сообщении");
if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos)) ||
message.Author.IsBot)
var prevs = await message.Channel.GetMessagesAsync(3).FlattenAsync();
var prevsArray = prevs as IMessage[] ?? prevs.ToArray();
var prev = prevsArray[1].Content;
var prevFailsafe = prevsArray[2].Content;
if (!(message.HasStringPrefix(Boyfriend.GetGuildConfig(guild).Prefix, ref argPos)
|| message.HasMentionPrefix(Boyfriend.Client.CurrentUser, ref argPos))
|| user.IsBot && message.Content.Contains(prev) || message.Content.Contains(prevFailsafe))
return;
var context = new SocketCommandContext(_client, message);
var result = await _commands.ExecuteAsync(context, argPos, null);
await HandleErrors(context, result);
await CommandHandler.HandleCommand(message, argPos);
}
private static async Task MessageUpdatedEvent(Cacheable<IMessage, ulong> messageCached, SocketMessage messageSocket,
ISocketMessageChannel channel) {
var msg = messageCached.Value;
var nl = Environment.NewLine;
if (msg.Content == messageSocket.Content) return;
var toSend = msg == null
? $"Отредактировано сообщение от {messageSocket.Author.Mention} в канале" +
$" {Utils.MentionChannel(channel.Id)}," + $" но я забыл что там было до редактирования: " +
$"{Utils.Wrap(messageSocket.Content)}"
$" {Utils.MentionChannel(channel.Id)}," + " но я забыл что там было до редактирования: " +
Utils.Wrap(messageSocket.Content)
: $"Отредактировано сообщение от {msg.Author.Mention} " +
$"в канале {Utils.MentionChannel(channel.Id)}." +
$"{nl}До:{nl}{Utils.Wrap(msg.Content)}{nl}После:{nl}{Utils.Wrap(messageSocket.Content)}";

22
Boyfriend/GuildConfig.cs Normal file
View file

@ -0,0 +1,22 @@
using System.Text.Json;
namespace Boyfriend;
public class GuildConfig {
public ulong Id { get; }
public string Lang { get; set; }
public string Prefix { get; set; }
public bool RemoveRolesOnMute { get; set; }
public GuildConfig(ulong id, string lang, string prefix, bool removeRolesOnMute) {
Id = id;
Lang = lang;
Prefix = prefix;
RemoveRolesOnMute = removeRolesOnMute;
}
public async void Save() {
await using var stream = File.OpenWrite("config_" + Id + ".json");
await JsonSerializer.SerializeAsync(stream, this);
}
}

View file

@ -13,7 +13,7 @@ public static class Utils {
[Obsolete("Stop hard-coding things!")]
public static ITextChannel GetAdminLogChannel() {
if (Boyfriend.Client.GetChannel(870929165141032971) is not ITextChannel adminLogChannel)
throw new ArgumentException("Invalid admin log channel");
throw new Exception("Invalid admin log channel");
return adminLogChannel;
}
@ -31,13 +31,6 @@ public static class Utils {
}
public static async Task StartDelayed(Task toRun, TimeSpan delay, Func<bool>? condition = null) {
switch (delay.TotalMilliseconds) {
case < -1:
throw new ArgumentOutOfRangeException(nameof(delay), "Указана отрицательная продолжительность!");
case > int.MaxValue:
throw new ArgumentOutOfRangeException(nameof(delay), "Указана слишком большая продолжительность!");
}
await Task.Delay(delay);
var conditionResult = condition?.Invoke() ?? true;
if (conditionResult)