|
| 1 | +using System; |
| 2 | +using System.Threading.Tasks; |
| 3 | +using DSharpPlus; |
| 4 | +using DSharpPlus.CommandsNext; |
| 5 | +using DSharpPlus.CommandsNext.Exceptions; |
| 6 | +using DSharpPlus.Entities; |
| 7 | +using DSharpPlus.EventArgs; |
| 8 | +using DSharpPlusDocs.Controllers; |
| 9 | +using DSharpPlusDocs.EmbedExtension; |
| 10 | +using DSharpPlusDocs.Modules; |
| 11 | +using DSharpPlusDocs.Paginator; |
| 12 | +using Microsoft.Extensions.Caching.Memory; |
| 13 | +using Microsoft.Extensions.DependencyInjection; |
| 14 | + |
| 15 | +namespace DSharpPlusDocs.Handlers |
| 16 | +{ |
| 17 | + public class CommandHandler |
| 18 | + { |
| 19 | + private CommandsNextExtension _commands; |
| 20 | + private DiscordClient _client; |
| 21 | + private MainHandler _mainHandler; |
| 22 | + private IServiceProvider _services; |
| 23 | + private MemoryCache cache = new MemoryCache(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(3) }); |
| 24 | + |
| 25 | + public Task InitializeAsync(MainHandler mainHandler) |
| 26 | + { |
| 27 | + _mainHandler = mainHandler; |
| 28 | + _client = mainHandler.Client; |
| 29 | + var services = new ServiceCollection(); |
| 30 | + services.AddSingleton(mainHandler); |
| 31 | + services.AddSingleton(new PaginationService(_client)); |
| 32 | + _services = services.BuildServiceProvider(); |
| 33 | + |
| 34 | + _commands = _client.UseCommandsNext(new CommandsNextConfiguration |
| 35 | + { |
| 36 | + EnableDefaultHelp = true, |
| 37 | + Services = _services, |
| 38 | + EnableMentionPrefix = false, |
| 39 | + PrefixResolver = (x) => HandleCommand(x) |
| 40 | + }); |
| 41 | + |
| 42 | + _commands.SetHelpFormatter<HelpFormatter>(); |
| 43 | + _commands.RegisterCommands<GeneralCommands>(); |
| 44 | + |
| 45 | + _client.MessageUpdated += HandleUpdate; |
| 46 | + _commands.CommandErrored += _commands_CommandErrored; |
| 47 | + |
| 48 | + return Task.CompletedTask; |
| 49 | + } |
| 50 | + |
| 51 | + private async Task _commands_CommandErrored(CommandsNextExtension commandsNext, CommandErrorEventArgs e) |
| 52 | + { |
| 53 | + if (e.Exception is CommandNotFoundException) |
| 54 | + { |
| 55 | + var reply = await BuildReply(e.Context.Message, e.Context.Message.Content.Substring(e.Context.Message.GetMentionPrefixLength(_client.CurrentUser))); |
| 56 | + if (reply.Item1 == null && reply.Item2 == null && reply.Item3 == null) |
| 57 | + return; |
| 58 | + DiscordMessage message; |
| 59 | + if (reply.Item3 != null) |
| 60 | + message = await e.Context.Services.GetService<PaginationService>().SendPaginatedMessageAsync(e.Context.Channel, reply.Item3); |
| 61 | + else |
| 62 | + message = await e.Context.RespondAsync(reply.Item1, embed: reply.Item2); |
| 63 | + AddCache(e.Context.Message.Id, message.Id); |
| 64 | + } |
| 65 | + else |
| 66 | + Console.WriteLine(e.Exception); |
| 67 | + } |
| 68 | + |
| 69 | + public Task<int> HandleCommand(DiscordMessage msg) |
| 70 | + { |
| 71 | + if (!msg.Channel.IsPrivate && msg.Channel.Guild.Id == 81384788765712384) |
| 72 | + if (msg.Channel.Name != "dotnet_dsharpplus" && msg.Channel.Name != "testing" && msg.Channel.Name != "playground") return Task.FromResult(-1); |
| 73 | + return Task.FromResult(msg.GetMentionPrefixLength(_client.CurrentUser)); |
| 74 | + } |
| 75 | + |
| 76 | + private Task HandleUpdate(DiscordClient client, MessageUpdateEventArgs e) |
| 77 | + { |
| 78 | + _ = Task.Run(async () => |
| 79 | + { |
| 80 | + ulong? id; |
| 81 | + if ((id = GetOurMessageIdFromCache(e.Message.Id)) != null) |
| 82 | + { |
| 83 | + var botMessage = await e.Channel.GetMessageAsync(id.Value); |
| 84 | + if (botMessage == null) |
| 85 | + return; |
| 86 | + int argPos = 0; |
| 87 | + if ((argPos = await HandleCommand(e.Message)) == -1) return; |
| 88 | + var reply = await BuildReply(e.Message, e.Message.Content.Substring(argPos)); |
| 89 | + |
| 90 | + if (reply.Item1 == null && reply.Item2 == null && reply.Item3 == null) |
| 91 | + return; |
| 92 | + var pagination = _services.GetService<PaginationService>(); |
| 93 | + var isPaginatedMessage = pagination.IsPaginatedMessage(id.Value); |
| 94 | + if (reply.Item3 != null) |
| 95 | + { |
| 96 | + if (isPaginatedMessage) |
| 97 | + await pagination.UpdatePaginatedMessageAsync(botMessage, reply.Item3); |
| 98 | + else |
| 99 | + await pagination.EditMessageToPaginatedMessageAsync(botMessage, reply.Item3); |
| 100 | + } |
| 101 | + else |
| 102 | + { |
| 103 | + if (isPaginatedMessage) |
| 104 | + { |
| 105 | + pagination.StopTrackingPaginatedMessage(id.Value); |
| 106 | + _ = botMessage.DeleteAllReactionsAsync(); //TODO: Should await, but D#+ is broken |
| 107 | + } |
| 108 | + await botMessage.ModifyAsync(reply.Item1, reply.Item2.Build()); |
| 109 | + } |
| 110 | + } |
| 111 | + }); |
| 112 | + return Task.CompletedTask; |
| 113 | + } |
| 114 | + |
| 115 | + private async Task<(string, DiscordEmbedBuilder, PaginatedMessage)> BuildReply(DiscordMessage msg, string message) |
| 116 | + { |
| 117 | + if (!_mainHandler.QueryHandler.IsReady()) |
| 118 | + { |
| 119 | + return ("Loading cache...", null, null); //TODO: Change message |
| 120 | + } |
| 121 | + else |
| 122 | + { |
| 123 | + try |
| 124 | + { |
| 125 | + var tuple = await _mainHandler.QueryHandler.RunAsync(message); |
| 126 | + if (tuple.Item2 is PaginatorBuilder pag) |
| 127 | + { |
| 128 | + var paginated = new PaginatedMessage(pag.Pages, "Results", user: msg.Author, options: new AppearanceOptions { Timeout = TimeSpan.FromMinutes(10) }); |
| 129 | + return (null, null, paginated); |
| 130 | + } |
| 131 | + else |
| 132 | + return (tuple.Item1, tuple.Item2 as DiscordEmbedBuilder, null); |
| 133 | + } |
| 134 | + catch (Exception e) |
| 135 | + { |
| 136 | + Console.WriteLine(e.ToString()); |
| 137 | + return ("Uh-oh... I think some pipes have broken...", null, null); |
| 138 | + } |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + public void AddCache(ulong userMessageId, ulong ourMessageId) |
| 143 | + { |
| 144 | + cache.Set(userMessageId, ourMessageId, new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) }); |
| 145 | + } |
| 146 | + |
| 147 | + public ulong? GetOurMessageIdFromCache(ulong messageId) |
| 148 | + { |
| 149 | + if (cache.TryGetValue(messageId, out ulong id)) |
| 150 | + return id; |
| 151 | + return null; |
| 152 | + } |
| 153 | + |
| 154 | + /*public async Task<DiscordEmbed> HelpEmbedBuilderAsync(CommandContext context, string command = null) |
| 155 | + { |
| 156 | + DiscordEmbed eb = new DiscordEmbed(); |
| 157 | + eb.Author = new DiscordEmbedAuthor().WithName("Help:").WithIconUrl("http://i.imgur.com/VzDRjUn.png"); |
| 158 | + StringBuilder sb = new StringBuilder(); |
| 159 | + if (command == null) |
| 160 | + { |
| 161 | + foreach (ModuleInfo mi in _commands.Modules.OrderBy(x => x.Name)) |
| 162 | + if (!mi.IsSubmodule) |
| 163 | + if (mi.Name != "Help") |
| 164 | + { |
| 165 | + bool ok = true; |
| 166 | + foreach (PreconditionAttribute precondition in mi.Preconditions) |
| 167 | + if (!(await precondition.CheckPermissions(context, null, _services)).IsSuccess) |
| 168 | + { |
| 169 | + ok = false; |
| 170 | + break; |
| 171 | + } |
| 172 | + if (ok) |
| 173 | + { |
| 174 | + var cmds = mi.Commands.ToList<object>(); |
| 175 | + cmds.AddRange(mi.Submodules); |
| 176 | + for (int i = cmds.Count - 1; i >= 0; i--) |
| 177 | + { |
| 178 | + object o = cmds[i]; |
| 179 | + foreach (PreconditionAttribute precondition in ((o as CommandInfo)?.Preconditions ?? (o as ModuleInfo)?.Preconditions)) |
| 180 | + if (!(await precondition.CheckPermissions(context, o as CommandInfo, _services)).IsSuccess) |
| 181 | + cmds.Remove(o); |
| 182 | + } |
| 183 | + if (cmds.Count != 0) |
| 184 | + { |
| 185 | + var list = cmds.Select(x => $"{((x as CommandInfo)?.Name ?? (x as ModuleInfo)?.Name)}").OrderBy(x => x); |
| 186 | + sb.AppendLine($"**{mi.Name}:** {String.Join(", ", list)}"); |
| 187 | + } |
| 188 | + } |
| 189 | + } |
| 190 | +
|
| 191 | + eb.AddField((x) => |
| 192 | + { |
| 193 | + x.IsInline = false; |
| 194 | + x.Name = "Query help"; |
| 195 | + x.Value = $"Usage: {context.Client.CurrentUser.Mention} [query]"; |
| 196 | + }); |
| 197 | + eb.AddField((x) => |
| 198 | + { |
| 199 | + x.IsInline = true; |
| 200 | + x.Name = "Keywords"; |
| 201 | + x.Value = "method, type, property,\nevent, in, list"; |
| 202 | + }); |
| 203 | + eb.AddField((x) => |
| 204 | + { |
| 205 | + x.IsInline = true; |
| 206 | + x.Name = "Examples"; |
| 207 | + x.Value = "EmbedBuilder\n" + |
| 208 | + "IGuildUser.Nickname\n" + |
| 209 | + "ModifyAsync in IRole\n" + |
| 210 | + "send message\n" + |
| 211 | + "type Emote"; |
| 212 | + }); |
| 213 | + eb.Footer = new EmbedFooterBuilder().WithText("Note: (i) = Inherited"); |
| 214 | + eb.Description = sb.ToString(); |
| 215 | + } |
| 216 | + else |
| 217 | + { |
| 218 | + SearchResult sr = _commands.Search(context, command); |
| 219 | + if (sr.IsSuccess) |
| 220 | + { |
| 221 | + Nullable<CommandMatch> cmd = null; |
| 222 | + if (sr.Commands.Count == 1) |
| 223 | + cmd = sr.Commands.First(); |
| 224 | + else |
| 225 | + { |
| 226 | + int lastIndex; |
| 227 | + var find = sr.Commands.Where(x => x.Command.Aliases.First().Equals(command, StringComparison.OrdinalIgnoreCase)); |
| 228 | + if (find.Any()) |
| 229 | + cmd = find.First(); |
| 230 | + while (cmd == null && (lastIndex = command.LastIndexOf(' ')) != -1) //TODO: Maybe remove and say command not found? |
| 231 | + { |
| 232 | + find = sr.Commands.Where(x => x.Command.Aliases.First().Equals(command.Substring(0, lastIndex), StringComparison.OrdinalIgnoreCase)); |
| 233 | + if (find.Any()) |
| 234 | + cmd = find.First(); |
| 235 | + command = command.Substring(0, lastIndex); |
| 236 | + } |
| 237 | + } |
| 238 | + if (cmd != null && (await cmd.Value.CheckPreconditionsAsync(context, _services)).IsSuccess) |
| 239 | + { |
| 240 | + eb.Author.Name = $"Help: {cmd.Value.Command.Aliases.First()}"; |
| 241 | + sb.Append($"Usage: {_mainHandler.Prefix}{cmd.Value.Command.Aliases.First()}"); |
| 242 | + if (cmd.Value.Command.Parameters.Count != 0) |
| 243 | + sb.Append($" [{String.Join("] [", cmd.Value.Command.Parameters.Select(x => x.Name))}]"); |
| 244 | + if (!String.IsNullOrEmpty(cmd.Value.Command.Summary)) |
| 245 | + sb.Append($"\nSummary: {cmd.Value.Command.Summary}"); |
| 246 | + if (!String.IsNullOrEmpty(cmd.Value.Command.Remarks)) |
| 247 | + sb.Append($"\nRemarks: {cmd.Value.Command.Remarks}"); |
| 248 | + if (cmd.Value.Command.Aliases.Count != 1) |
| 249 | + sb.Append($"\nAliases: {String.Join(", ", cmd.Value.Command.Aliases.Where(x => x != cmd.Value.Command.Aliases.First()))}"); |
| 250 | + eb.Description = sb.ToString(); |
| 251 | + } |
| 252 | + else |
| 253 | + eb.Description = $"Command '{command}' not found."; |
| 254 | + } |
| 255 | + else |
| 256 | + eb.Description = $"Command '{command}' not found."; |
| 257 | + } |
| 258 | + return eb; |
| 259 | + }*/ |
| 260 | + } |
| 261 | +} |
0 commit comments