forked from EllieBotDevs/elliebot
784 lines
No EOL
26 KiB
C#
784 lines
No EOL
26 KiB
C#
using EllieBot.Modules.Utility.Services;
|
|
using Newtonsoft.Json;
|
|
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
|
using Microsoft.CodeAnalysis.Scripting;
|
|
using EllieBot.Modules.Searches.Common;
|
|
|
|
namespace EllieBot.Modules.Utility;
|
|
|
|
public partial class Utility : EllieModule
|
|
{
|
|
public enum CreateInviteType
|
|
{
|
|
Any,
|
|
New
|
|
}
|
|
|
|
public enum MeOrBot
|
|
{
|
|
Me,
|
|
Bot
|
|
}
|
|
|
|
private static readonly JsonSerializerOptions _showEmbedSerializerOptions = new()
|
|
{
|
|
WriteIndented = true,
|
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
PropertyNamingPolicy = LowerCaseNamingPolicy.Default
|
|
};
|
|
|
|
private readonly DiscordSocketClient _client;
|
|
private readonly ICoordinator _coord;
|
|
private readonly IStatsService _stats;
|
|
private readonly IBotCredentials _creds;
|
|
private readonly DownloadTracker _tracker;
|
|
private readonly IHttpClientFactory _httpFactory;
|
|
private readonly VerboseErrorsService _veService;
|
|
private readonly IServiceProvider _services;
|
|
private readonly AfkService _afkService;
|
|
|
|
public Utility(
|
|
DiscordSocketClient client,
|
|
ICoordinator coord,
|
|
IStatsService stats,
|
|
IBotCredentials creds,
|
|
DownloadTracker tracker,
|
|
IHttpClientFactory httpFactory,
|
|
VerboseErrorsService veService,
|
|
IServiceProvider services,
|
|
AfkService afkService)
|
|
{
|
|
_client = client;
|
|
_coord = coord;
|
|
_stats = stats;
|
|
_creds = creds;
|
|
_tracker = tracker;
|
|
_httpFactory = httpFactory;
|
|
_veService = veService;
|
|
_services = services;
|
|
_afkService = afkService;
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[UserPerm(GuildPerm.ManageMessages)]
|
|
[Priority(1)]
|
|
public async Task Say(ITextChannel channel, [Leftover] SmartText message)
|
|
{
|
|
if (!((IGuildUser)ctx.User).GetPermissions(channel).SendMessages)
|
|
{
|
|
await Response().Error(strs.insuf_perms_u).SendAsync();
|
|
return;
|
|
}
|
|
|
|
if (!((ctx.Guild as SocketGuild)?.CurrentUser.GetPermissions(channel).SendMessages ?? false))
|
|
{
|
|
await Response().Error(strs.insuf_perms_i).SendAsync();
|
|
return;
|
|
}
|
|
|
|
var repCtx = new ReplacementContext(Context);
|
|
message = await repSvc.ReplaceAsync(message, repCtx);
|
|
|
|
await Response()
|
|
.Text(message)
|
|
.Channel(channel)
|
|
.UserBasedMentions()
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[UserPerm(GuildPerm.ManageMessages)]
|
|
[Priority(0)]
|
|
public Task Say([Leftover] SmartText message)
|
|
=> Say((ITextChannel)ctx.Channel, message);
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task WhosPlaying([Leftover] string? game)
|
|
{
|
|
game = game?.Trim().ToUpperInvariant();
|
|
if (string.IsNullOrWhiteSpace(game))
|
|
return;
|
|
|
|
if (ctx.Guild is not SocketGuild socketGuild)
|
|
{
|
|
Log.Warning("Can't cast guild to socket guild");
|
|
return;
|
|
}
|
|
|
|
var rng = new EllieRandom();
|
|
var arr = await Task.Run(() => socketGuild.Users
|
|
.Where(u => u.Activities.Any(x
|
|
=> x.Name is not null && x.Name.ToUpperInvariant() == game))
|
|
.Select(u => u.Username)
|
|
.OrderBy(_ => rng.Next())
|
|
.Take(60)
|
|
.ToArray());
|
|
|
|
var i = 0;
|
|
if (arr.Length == 0)
|
|
await Response().Error(strs.nobody_playing_game).SendAsync();
|
|
else
|
|
{
|
|
await Response()
|
|
.Confirm("```css\n"
|
|
+ string.Join("\n",
|
|
arr.GroupBy(_ => i++ / 2)
|
|
.Select(ig => string.Concat(ig.Select(el => $"• {el,-27}"))))
|
|
+ "\n```")
|
|
.SendAsync();
|
|
}
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[Priority(0)]
|
|
public async Task InRole(int page, [Leftover] IRole? role = null)
|
|
{
|
|
if (--page < 0)
|
|
return;
|
|
|
|
await ctx.Channel.TriggerTypingAsync();
|
|
await _tracker.EnsureUsersDownloadedAsync(ctx.Guild);
|
|
|
|
var users = await ctx.Guild.GetUsersAsync(
|
|
CacheMode.CacheOnly
|
|
);
|
|
|
|
users = role is null
|
|
? users
|
|
: users.Where(u => u.RoleIds.Contains(role.Id)).ToList();
|
|
|
|
|
|
var roleUsers = new List<string>(users.Count);
|
|
foreach (var u in users)
|
|
{
|
|
roleUsers.Add($"{u.Mention} {Format.Spoiler(Format.Code(u.Username))}");
|
|
}
|
|
|
|
await Response()
|
|
.Paginated()
|
|
.Items(roleUsers)
|
|
.PageSize(20)
|
|
.CurrentPage(page)
|
|
.Page((pageUsers, _) =>
|
|
{
|
|
if (pageUsers.Count == 0)
|
|
return _sender.CreateEmbed().WithOkColor().WithDescription(GetText(strs.no_user_on_this_page));
|
|
|
|
var roleName = Format.Bold(role?.Name ?? "No Role");
|
|
|
|
return _sender.CreateEmbed()
|
|
.WithOkColor()
|
|
.WithTitle(GetText(strs.inrole_list(roleName, roleUsers.Count)))
|
|
.WithDescription(string.Join("\n", pageUsers));
|
|
})
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[Priority(1)]
|
|
public Task InRole([Leftover] IRole? role = null)
|
|
=> InRole(1, role);
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task CheckPerms(MeOrBot who = MeOrBot.Me)
|
|
{
|
|
var user = who == MeOrBot.Me ? (IGuildUser)ctx.User : ((SocketGuild)ctx.Guild).CurrentUser;
|
|
var perms = user.GetPermissions((ITextChannel)ctx.Channel);
|
|
await SendPerms(perms);
|
|
}
|
|
|
|
private async Task SendPerms(ChannelPermissions perms)
|
|
{
|
|
var builder = new StringBuilder();
|
|
foreach (var p in perms.GetType()
|
|
.GetProperties()
|
|
.Where(static p =>
|
|
{
|
|
var method = p.GetGetMethod();
|
|
if (method is null)
|
|
return false;
|
|
return !method.GetParameters().Any();
|
|
}))
|
|
builder.AppendLine($"{p.Name} : {p.GetValue(perms, null)}");
|
|
await Response().Confirm(builder.ToString()).SendAsync();
|
|
}
|
|
|
|
// [Cmd]
|
|
// [RequireContext(ContextType.Guild)]
|
|
// [RequireUserPermission(GuildPermission.ManageRoles)]
|
|
// public async Task CheckPerms(SocketRole role, string perm = null)
|
|
// {
|
|
// ChannelPermissions.
|
|
// var perms = ((ITextChannel)ctx.Channel);
|
|
// await SendPerms(perms)
|
|
// }
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task UserId([Leftover] IGuildUser? target = null)
|
|
{
|
|
var usr = target ?? ctx.User;
|
|
await Response()
|
|
.Confirm(strs.userid("🆔",
|
|
Format.Bold(usr.ToString()),
|
|
Format.Code(usr.Id.ToString())))
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task RoleId([Leftover] IRole role)
|
|
=> await Response()
|
|
.Confirm(strs.roleid("🆔",
|
|
Format.Bold(role.ToString()),
|
|
Format.Code(role.Id.ToString())))
|
|
.SendAsync();
|
|
|
|
[Cmd]
|
|
public async Task ChannelId()
|
|
=> await Response().Confirm(strs.channelid("🆔", Format.Code(ctx.Channel.Id.ToString()))).SendAsync();
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task ServerId()
|
|
=> await Response().Confirm(strs.serverid("🆔", Format.Code(ctx.Guild.Id.ToString()))).SendAsync();
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task Roles(IGuildUser? target, int page = 1)
|
|
{
|
|
var guild = ctx.Guild;
|
|
|
|
const int rolesPerPage = 20;
|
|
|
|
if (page is < 1 or > 100)
|
|
return;
|
|
|
|
if (target is not null)
|
|
{
|
|
var roles = target.GetRoles()
|
|
.Except(new[] { guild.EveryoneRole })
|
|
.OrderBy(r => -r.Position)
|
|
.Skip((page - 1) * rolesPerPage)
|
|
.Take(rolesPerPage)
|
|
.ToArray();
|
|
if (!roles.Any())
|
|
await Response().Error(strs.no_roles_on_page).SendAsync();
|
|
else
|
|
{
|
|
await Response()
|
|
.Confirm(GetText(strs.roles_page(page, Format.Bold(target.ToString()))),
|
|
"\n• " + string.Join("\n• ", (IEnumerable<IRole>)roles))
|
|
.SendAsync();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var roles = guild.Roles.Except(new[] { guild.EveryoneRole })
|
|
.OrderBy(r => -r.Position)
|
|
.Skip((page - 1) * rolesPerPage)
|
|
.Take(rolesPerPage)
|
|
.ToArray();
|
|
if (!roles.Any())
|
|
await Response().Error(strs.no_roles_on_page).SendAsync();
|
|
else
|
|
{
|
|
await Response()
|
|
.Confirm(GetText(strs.roles_all_page(page)),
|
|
"\n• " + string.Join("\n• ", (IEnumerable<IRole>)roles).SanitizeMentions(true))
|
|
.SendAsync();
|
|
}
|
|
}
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public Task Roles(int page = 1)
|
|
=> Roles(null, page);
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task ChannelTopic([Leftover] ITextChannel? channel = null)
|
|
{
|
|
if (channel is null)
|
|
channel = (ITextChannel)ctx.Channel;
|
|
|
|
var topic = channel.Topic;
|
|
if (string.IsNullOrWhiteSpace(topic))
|
|
await Response().Error(strs.no_topic_set).SendAsync();
|
|
else
|
|
await Response().Confirm(GetText(strs.channel_topic), topic).SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
public async Task Stats()
|
|
{
|
|
var ownerIds = string.Join("\n", _creds.OwnerIds);
|
|
if (string.IsNullOrWhiteSpace(ownerIds))
|
|
ownerIds = "-";
|
|
|
|
await Response()
|
|
.Embed(_sender.CreateEmbed()
|
|
.WithOkColor()
|
|
.WithAuthor($"EllieBot v{StatsService.BotVersion}",
|
|
"https://cdn.elliebot.net/Ellie.png",
|
|
"https://docs.elliebot.net/")
|
|
.AddField(GetText(strs.author), _stats.Author, true)
|
|
.AddField(GetText(strs.botid), _client.CurrentUser.Id.ToString(), true)
|
|
.AddField(GetText(strs.shard),
|
|
$"#{_client.ShardId} / {_creds.TotalShards}",
|
|
true)
|
|
.AddField(GetText(strs.commands_ran), _stats.CommandsRan.ToString(), true)
|
|
.AddField(GetText(strs.messages),
|
|
$"{_stats.MessageCounter} ({_stats.MessagesPerSecond:F2}/sec)",
|
|
true)
|
|
.AddField(GetText(strs.memory),
|
|
FormattableString.Invariant($"{_stats.GetPrivateMemoryMegabytes():F2} MB"),
|
|
true)
|
|
.AddField(GetText(strs.owner_ids), ownerIds, true)
|
|
.AddField(GetText(strs.uptime), _stats.GetUptimeString("\n"), true)
|
|
.AddField(GetText(strs.presence),
|
|
GetText(strs.presence_txt(_coord.GetGuildCount(),
|
|
_stats.TextChannels,
|
|
_stats.VoiceChannels)),
|
|
true))
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
public async Task Showemojis([Leftover] string _)
|
|
{
|
|
var tags = ctx.Message.Tags.Where(t => t.Type == TagType.Emoji).Select(t => (Emote)t.Value);
|
|
|
|
var result = string.Join("\n", tags.Select(m => GetText(strs.showemojis(m, m.Url))));
|
|
|
|
if (string.IsNullOrWhiteSpace(result))
|
|
await Response().Error(strs.showemojis_none).SendAsync();
|
|
else
|
|
await Response().Text(result.TrimTo(2000)).SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[BotPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
[UserPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
[Priority(2)]
|
|
public Task EmojiAdd(string name, Emote emote)
|
|
=> EmojiAdd(name, emote.Url);
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[BotPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
[UserPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
[Priority(1)]
|
|
public Task EmojiAdd(Emote emote)
|
|
=> EmojiAdd(emote.Name, emote.Url);
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[BotPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
[UserPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
[Priority(0)]
|
|
public async Task EmojiAdd(string name, string? url = null)
|
|
{
|
|
name = name.Trim(':');
|
|
|
|
url ??= ctx.Message.Attachments.FirstOrDefault()?.Url;
|
|
|
|
if (url is null)
|
|
return;
|
|
|
|
using var http = _httpFactory.CreateClient();
|
|
using var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
|
|
if (!res.IsImage() || res.GetContentLength() > 262_144)
|
|
{
|
|
await Response().Error(strs.invalid_emoji_link).SendAsync();
|
|
return;
|
|
}
|
|
|
|
await using var imgStream = await res.Content.ReadAsStreamAsync();
|
|
Emote em;
|
|
try
|
|
{
|
|
em = await ctx.Guild.CreateEmoteAsync(name, new(imgStream));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Warning(ex, "Error adding emoji on server {GuildId}", ctx.Guild.Id);
|
|
|
|
await Response().Error(strs.emoji_add_error).SendAsync();
|
|
return;
|
|
}
|
|
|
|
await Response().Confirm(strs.emoji_added(em.ToString())).SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[BotPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
[UserPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
[Priority(0)]
|
|
public async Task EmojiRemove(params Emote[] emotes)
|
|
{
|
|
if (emotes.Length == 0)
|
|
return;
|
|
|
|
var g = (SocketGuild)ctx.Guild;
|
|
|
|
var fails = new List<Emote>();
|
|
foreach (var emote in emotes)
|
|
{
|
|
var guildEmote = g.Emotes.FirstOrDefault(x => x.Id == emote.Id);
|
|
if (guildEmote is null)
|
|
{
|
|
fails.Add(emote);
|
|
}
|
|
else
|
|
{
|
|
await ctx.Guild.DeleteEmoteAsync(guildEmote);
|
|
}
|
|
}
|
|
|
|
if (fails.Count > 0)
|
|
{
|
|
await Response().Pending(strs.emoji_not_removed(fails.Select(x => x.ToString()).Join(" "))).SendAsync();
|
|
return;
|
|
}
|
|
|
|
await ctx.OkAsync();
|
|
}
|
|
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[BotPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
[UserPerm(GuildPerm.ManageEmojisAndStickers)]
|
|
public async Task StickerAdd(string? name = null, string? description = null, params string[] tags)
|
|
{
|
|
string format;
|
|
Stream? stream = null;
|
|
|
|
try
|
|
{
|
|
if (ctx.Message.Stickers.Count is 1 && ctx.Message.Stickers.First() is SocketSticker ss)
|
|
{
|
|
name ??= ss.Name;
|
|
description = ss.Description;
|
|
tags = tags is null or { Length: 0 } ? ss.Tags.ToArray() : tags;
|
|
format = FormatToExtension(ss.Format);
|
|
|
|
using var http = _httpFactory.CreateClient();
|
|
stream = await http.GetStreamAsync(ss.GetStickerUrl());
|
|
}
|
|
else if (ctx.Message.Attachments.Count is 1 && name is not null)
|
|
{
|
|
if (tags.Length == 0)
|
|
tags = [name];
|
|
|
|
if (ctx.Message.Attachments.Count != 1)
|
|
{
|
|
await Response().Error(strs.sticker_error).SendAsync();
|
|
return;
|
|
}
|
|
|
|
var attach = ctx.Message.Attachments.First();
|
|
|
|
|
|
if (attach.Size > 512_000 || attach.Width != 300 || attach.Height != 300)
|
|
{
|
|
await Response().Error(strs.sticker_error).SendAsync();
|
|
return;
|
|
}
|
|
|
|
format = attach.Filename
|
|
.Split('.')
|
|
.Last()
|
|
.ToLowerInvariant();
|
|
|
|
if (string.IsNullOrWhiteSpace(format) || (format != "png" && format != "apng"))
|
|
{
|
|
await Response().Error(strs.sticker_error).SendAsync();
|
|
return;
|
|
}
|
|
|
|
using var http = _httpFactory.CreateClient();
|
|
stream = await http.GetStreamAsync(attach.Url);
|
|
}
|
|
else
|
|
{
|
|
await Response().Error(strs.sticker_error).SendAsync();
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
await ctx.Guild.CreateStickerAsync(
|
|
name,
|
|
stream,
|
|
$"{name}.{format}",
|
|
tags,
|
|
string.IsNullOrWhiteSpace(description) ? "Missing description" : description
|
|
);
|
|
|
|
await ctx.OkAsync();
|
|
}
|
|
catch
|
|
(Exception ex)
|
|
{
|
|
Log.Warning(ex, "Error occurred while adding a sticker: {Message}", ex.Message);
|
|
await Response().Error(strs.error_occured).SendAsync();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
await (stream?.DisposeAsync() ?? ValueTask.CompletedTask);
|
|
}
|
|
}
|
|
|
|
private static string FormatToExtension(StickerFormatType format)
|
|
{
|
|
switch (format)
|
|
{
|
|
case StickerFormatType.None:
|
|
case StickerFormatType.Png:
|
|
case StickerFormatType.Apng:
|
|
return "png";
|
|
case StickerFormatType.Lottie:
|
|
return "lottie";
|
|
default:
|
|
throw new ArgumentException(nameof(format));
|
|
}
|
|
}
|
|
|
|
[Cmd]
|
|
[OwnerOnly]
|
|
public async Task ServerList(int page = 1)
|
|
{
|
|
page -= 1;
|
|
|
|
if (page < 0)
|
|
return;
|
|
|
|
var allGuilds = _client.Guilds
|
|
.OrderBy(g => g.Name)
|
|
.ToList();
|
|
|
|
await Response()
|
|
.Paginated()
|
|
.Items(allGuilds)
|
|
.PageSize(9)
|
|
.Page((guilds, _) =>
|
|
{
|
|
if (!guilds.Any())
|
|
{
|
|
return _sender.CreateEmbed()
|
|
.WithDescription(GetText(strs.listservers_none))
|
|
.WithErrorColor();
|
|
}
|
|
|
|
var embed = _sender.CreateEmbed()
|
|
.WithOkColor();
|
|
foreach (var guild in guilds)
|
|
embed.AddField(guild.Name, GetText(strs.listservers(guild.Id, guild.MemberCount, guild.OwnerId)));
|
|
|
|
return embed;
|
|
})
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public Task ShowEmbed(ulong messageId)
|
|
=> ShowEmbed((ITextChannel)ctx.Channel, messageId);
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task ShowEmbed(ITextChannel ch, ulong messageId)
|
|
{
|
|
var user = (IGuildUser)ctx.User;
|
|
var perms = user.GetPermissions(ch);
|
|
if (!perms.ReadMessageHistory || !perms.ViewChannel)
|
|
{
|
|
await Response().Error(strs.insuf_perms_u).SendAsync();
|
|
return;
|
|
}
|
|
|
|
var msg = await ch.GetMessageAsync(messageId);
|
|
if (msg is null)
|
|
{
|
|
await Response().Error(strs.msg_not_found).SendAsync();
|
|
return;
|
|
}
|
|
|
|
if (!msg.Embeds.Any())
|
|
{
|
|
await Response().Error(strs.not_found).SendAsync();
|
|
return;
|
|
}
|
|
|
|
var json = new SmartEmbedTextArray()
|
|
{
|
|
Content = msg.Content,
|
|
Embeds = msg.Embeds
|
|
.Map(x => new SmartEmbedArrayElementText(x))
|
|
}.ToJson(_showEmbedSerializerOptions);
|
|
|
|
await Response().Confirm(Format.Code(json, "json").Replace("](", "]\\(")).SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[OwnerOnly]
|
|
public async Task SaveChat(int cnt)
|
|
{
|
|
var msgs = new List<IMessage>(cnt);
|
|
await ctx.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled));
|
|
|
|
var title = $"Chatlog-{ctx.Guild.Name}/#{ctx.Channel.Name}-{DateTime.Now}.txt";
|
|
var grouping = msgs.GroupBy(x => $"{x.CreatedAt.Date:dd.MM.yyyy}")
|
|
.Select(g => new
|
|
{
|
|
date = g.Key,
|
|
messages = g.OrderBy(x => x.CreatedAt)
|
|
.Select(s =>
|
|
{
|
|
var msg = $"【{s.Timestamp:HH:mm:ss}】{s.Author}:";
|
|
if (string.IsNullOrWhiteSpace(s.ToString()))
|
|
{
|
|
if (s.Attachments.Any())
|
|
{
|
|
msg += "FILES_UPLOADED: "
|
|
+ string.Join("\n", s.Attachments.Select(x => x.Url));
|
|
}
|
|
else if (s.Embeds.Any())
|
|
{
|
|
msg += "EMBEDS: "
|
|
+ string.Join("\n--------\n",
|
|
s.Embeds.Select(x
|
|
=> $"Description: {x.Description}"));
|
|
}
|
|
}
|
|
else
|
|
msg += s.ToString();
|
|
|
|
return msg;
|
|
})
|
|
});
|
|
await using var stream = await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream();
|
|
await ctx.User.SendFileAsync(stream, title, title);
|
|
}
|
|
|
|
[Cmd]
|
|
[Ratelimit(3)]
|
|
public async Task Ping()
|
|
{
|
|
var sw = Stopwatch.StartNew();
|
|
var msg = await Response().Text("🏓").SendAsync();
|
|
sw.Stop();
|
|
msg.DeleteAfter(0);
|
|
|
|
await Response()
|
|
.Confirm($"{Format.Bold(ctx.User.ToString())} 🏓 {(int)sw.Elapsed.TotalMilliseconds}ms")
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
[UserPerm(GuildPerm.ManageMessages)]
|
|
public async Task VerboseError(bool? newstate = null)
|
|
{
|
|
var state = _veService.ToggleVerboseErrors(ctx.Guild.Id, newstate);
|
|
|
|
if (state)
|
|
await Response().Confirm(strs.verbose_errors_enabled).SendAsync();
|
|
else
|
|
await Response().Confirm(strs.verbose_errors_disabled).SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
public async Task Afk([Leftover] string text = "No reason specified.")
|
|
{
|
|
var succ = await _afkService.SetAfkAsync(ctx.User.Id, text);
|
|
|
|
if (succ)
|
|
{
|
|
await Response()
|
|
.Confirm(strs.afk_set)
|
|
.SendAsync();
|
|
}
|
|
}
|
|
|
|
[Cmd]
|
|
[NoPublicBot]
|
|
[OwnerOnly]
|
|
public async Task Eval([Leftover] string scriptText)
|
|
{
|
|
_ = ctx.Channel.TriggerTypingAsync();
|
|
|
|
if (scriptText.StartsWith("```cs"))
|
|
scriptText = scriptText[5..];
|
|
else if (scriptText.StartsWith("```"))
|
|
scriptText = scriptText[3..];
|
|
|
|
if (scriptText.EndsWith("```"))
|
|
scriptText = scriptText[..^3];
|
|
|
|
var script = CSharpScript.Create(scriptText,
|
|
ScriptOptions.Default
|
|
.WithReferences(this.GetType().Assembly)
|
|
.WithImports(
|
|
"System",
|
|
"System.Collections.Generic",
|
|
"System.IO",
|
|
"System.Linq",
|
|
"System.Net.Http",
|
|
"System.Threading",
|
|
"System.Threading.Tasks",
|
|
"EllieBot",
|
|
"EllieBot.Extensions",
|
|
"Microsoft.Extensions.DependencyInjection",
|
|
"EllieBot.Common",
|
|
"EllieBot.Modules",
|
|
"System.Text",
|
|
"System.Text.Json"),
|
|
globalsType: typeof(EvalGlobals));
|
|
|
|
try
|
|
{
|
|
var result = await script.RunAsync(new EvalGlobals()
|
|
{
|
|
ctx = this.ctx,
|
|
guild = this.ctx.Guild,
|
|
channel = this.ctx.Channel,
|
|
user = this.ctx.User,
|
|
self = this,
|
|
services = _services
|
|
});
|
|
|
|
var output = result.ReturnValue?.ToString();
|
|
if (!string.IsNullOrWhiteSpace(output))
|
|
{
|
|
var eb = _sender.CreateEmbed()
|
|
.WithOkColor()
|
|
.AddField("Code", scriptText)
|
|
.AddField("Output", output.TrimTo(512)!);
|
|
|
|
_ = Response().Embed(eb).SendAsync();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await Response().Error(ex.Message).SendAsync();
|
|
}
|
|
}
|
|
} |