예제 #1
0
        /// <summary>
        /// Runs the <see cref="PassiveHandler"/>s for the server that <paramref name="originalMessage"/> was sent in based on the content from the message.
        /// </summary>
        /// <param name="originalMessage">The chat message.</param>
        public static async Task RunPassiveHandlersForMessage(DiscordMessage originalMessage)
        {
            BotContext   targetContext = BotContextRegistry.GetContext(originalMessage.Channel.Guild);
            XanBotMember sender        = XanBotMember.GetMemberFromUser(targetContext, originalMessage.Author);

            if (!targetContext.Virtual)
            {
                foreach (PassiveHandler handler in targetContext.Handlers)
                {
                    XanBotLogger.WriteDebugLine(handler.Name);
                    if (await handler.RunHandlerAsync(targetContext, sender, originalMessage))
                    {
                        XanBotLogger.WriteDebugLine("... returned true");
                        return;
                    }
                    XanBotLogger.WriteDebugLine("... returned false");
                }
            }
            else
            {
                XanBotLogger.WriteDebugLine("Target context is virtual. Skipping passive handlers.");
            }
        }
예제 #2
0
        /// <summary>
        /// Strictly gets a user from their user ID. Unlike <see cref="GetMemberFromDataAsync(DiscordGuild, string)"/>, this code will return null if a name or discriminator is passed in.
        /// </summary>
        /// <param name="data">The query to get a XanBotMember from. This can either be a ulong as a string or a user ping (&lt;@ulong&gt;)</param>
        /// <returns></returns>
        public static async Task <XanBotMember> GetMemberFromDataIDStrictAsync(DiscordGuild server, string data)
        {
            string newdata = data;

            if (data.StartsWith("<@") && data.EndsWith(">"))
            {
                newdata = data.Substring(2, data.Length - 3);
            }
            if (ulong.TryParse(newdata, out ulong uuid))
            {
                DiscordUser user = await XanBotCoreSystem.Client.GetUserAsync(uuid);

                // Catch case: Someone's username is a bunch of numbers OR they link a user ID that isn't in the server.
                if (user != null)
                {
                    XanBotMember member = XanBotMember.GetMemberFromUser(server, user);
                    if (member != null)
                    {
                        return(member);
                    }
                }
            }
            return(null);
        }
예제 #3
0
        /// <summary>
        /// Attempts to get a member via three distinct methods: <para/>
        /// First, it will attempt to cast <paramref name="data"/> into a ulong and see if it is a user GUID (this includes processing pings, like &lt;@GUID_HERE&gt;)<para/>
        /// Second, it will attempt to see if <paramref name="data"/> is a discriminator (only if data is formatted as #XXXX where XXXX is four digits)<para/>
        /// Finally, and only if the second method had no results or wasn't used, it will attempt to find <paramref name="data"/> as a nickname or username.<para/>
        /// This will throw a NonSingularResultException if the query can return more than one user.
        /// </summary>
        /// <param name="server">The Discord server to target.</param>
        /// <param name="data">The query to get a XanBotMember from. This can be a ulong as a string, a user ping (&lt;@ulong&gt;), a server nickname, a username (or optionally username#discriminator)</param>
        /// <exception cref="NonSingularResultException"/>
        /// <returns></returns>
        public static async Task <XanBotMember> GetMemberFromDataAsync(DiscordGuild server, string data)
        {
            // Wait! If it's a ping, it will start with <@ and end with >
            string newdata = data;

            if (data.StartsWith("<@") && data.EndsWith(">"))
            {
                newdata = data.Substring(2, data.Length - 3);
            }

            // I don't know if this method is used but it's one Discord supports so I have to support it too.
            if (data.StartsWith("<@!") && data.EndsWith(">"))
            {
                newdata = data.Substring(3, data.Length - 4);
            }

            if (ulong.TryParse(newdata, out ulong uuid))
            {
                try
                {
                    DiscordUser user = await XanBotCoreSystem.Client.GetUserAsync(uuid);

                    // Catch case: Someone's username is a bunch of numbers OR they link a user ID that isn't in the server.
                    if (user != null)
                    {
                        XanBotMember member = XanBotMember.GetMemberFromUser(server, user);
                        if (member != null)
                        {
                            return(member);
                        }
                    }
                }
                catch (NotFoundException)
                {
                    // The person typed in a number, it doesn't correspond to a discord GUID, so we'll catch the NotFoundException and let the code continue.
                }
            }
            List <XanBotMember> potentialReturns = new List <XanBotMember>();
            string dataLower = data.ToLower();

            // Discriminator searching:
            if (dataLower.Length == 5 && dataLower.First() == '#' && ushort.TryParse(dataLower.Substring(1), out ushort _))
            {
                // This is a discriminator -- Length is 5, starts with #, and the last 4 chars are numbers.
                foreach (DiscordUser user in server.Members.Values)
                {
                    string ud = "#" + user.Discriminator;
                    if (dataLower == ud)
                    {
                        potentialReturns.Add(XanBotMember.GetMemberFromUser(server, user));
                    }
                }
            }

            if (potentialReturns.Count == 0)
            {
                // ONLY if discriminator searching found nothing will we search by display name or username.
                if (NeedsNewMemberCache)
                {
                    LastCheckTime = DateTime.Now;
                    Members       = await server.GetAllMembersAsync();
                }
                foreach (DiscordMember member in Members)
                {
                    //foreach (DiscordMember member in server.Members.Values) { // DO NOT USE THIS. It's not fully populated in large servers.
                    string fullName = member.Username + "#" + member.Discriminator;
                    string nickName = member.Nickname ?? "";

                    fullName = fullName.ToLower();
                    nickName = nickName.ToLower();
                    if (nickName.Length >= dataLower.Length && dataLower == nickName.Substring(0, dataLower.Length))
                    {
                        potentialReturns.Add(XanBotMember.GetMemberFromUser(server, member));
                    }
                    else if (fullName.Length >= dataLower.Length && dataLower == fullName.Substring(0, dataLower.Length))
                    {
                        potentialReturns.Add(XanBotMember.GetMemberFromUser(server, member));
                    }

                    // Do NOT break if there are multiple. This is necessary for the values in a potential NonSingularResultException
                }
            }

            XanBotMember[] potentialReturnsArray = potentialReturns.ToArray();
            if (potentialReturnsArray.Length == 0)
            {
                return(null);
            }
            else if (potentialReturnsArray.Length == 1)
            {
                return(potentialReturnsArray[0]);
            }
            else
            {
                throw new NonSingularResultException(string.Format("More than one member of the server was found with the search query `{0}`!", data), potentialReturnsArray);
            }
        }
예제 #4
0
        /// <summary>
        /// Handles a command issued via a Discord message. This does not test if it is a command. Please test with <see cref="IsCommand(string)"/> before running this.
        /// </summary>
        /// <param name="originalMessage">The Discord message sent by the command.</param>
        public static async Task HandleMessageCommand(DiscordMessage originalMessage)
        {
            DiscordUser author         = originalMessage.Author;
            BotContext  commandContext = BotContextRegistry.GetContext(originalMessage.Channel.Guild);

            XanBotLogger.WriteDebugLine("Executing command in bot context. Info:");
            XanBotLogger.WriteLine(commandContext.ToConsoleString(), isDebugModeOnly: true);

            XanBotMember member = XanBotMember.GetMemberFromUser(commandContext, author);
            string       text   = originalMessage.Content;

            if (text.ToLower().StartsWith(CommandPrefix.ToLower()))
            {
                text = text.Substring(CommandPrefix.Length);
            }
            while (AllowSpacesAfterPrefix && text.StartsWith(" "))
            {
                text = text.Substring(1);
            }
            string[] allArgs = ArgumentSplitter.SplitArgs(text);
            string   command = allArgs[0];

            string[] args = new string[0];
            if (allArgs.Length > 1)
            {
                args = allArgs.Skip(1).ToArray();
            }

            // Catch case: Prevent excessive command length.
            if (command.Length > 32)
            {
                await ResponseUtil.RespondToAsync(originalMessage, "The command you input is too long.");

                XanBotLogger.WriteLine(string.Format("§aUser \"§6{0}§a\" issued a command that was considered too long to parse.", member.FullName));
                return;
            }
            // Catch case: Strip color formatting
            command.Replace(XanBotLogger.COLOR_CODE_SYM.ToString(), "");

            XanBotLogger.WriteDebugLine("Searching for command...");
            Command execCommand = null;
            string  cmdLower    = command.ToLower();

            // Search the context FIRST. That ensures that context commands can override stock commands.
            foreach (Command cmd in commandContext.Commands)
            {
                if (cmd.Name.ToLower() == cmdLower)
                {
                    execCommand = cmd;
                    XanBotLogger.WriteDebugLine($"Found command [{cmd.Name}] in context.");
                    break;
                }
                else
                {
                    if (cmd.AlternateNames != null)
                    {
                        foreach (string altName in cmd.AlternateNames)
                        {
                            if (altName.ToLower() == cmdLower)
                            {
                                execCommand = cmd;
                                break;
                            }
                        }
                    }
                }
            }

            if (execCommand == null)
            {
                foreach (Command cmd in Commands)
                {
                    if (cmd.Name.ToLower() == command.ToLower())
                    {
                        execCommand = cmd;
                        XanBotLogger.WriteDebugLine($"Found command [{cmd.Name}] globally.");
                        break;
                    }
                }
            }


            if (execCommand != null)
            {
                UsagePermissionPacket usagePerms = execCommand.CanUseCommand(member);
                if (usagePerms.CanUse)
                {
                    if (execCommand.CanUseCommandInThisChannel(member, originalMessage.Channel, out DiscordChannel optimalTargetChannel))
                    {
                        try
                        {
                            string allArgsText = "";
                            if (args.Length > 0)
                            {
                                allArgsText = text.Substring(command.Length + 1);
                            }
                            originalMessage.Channel.TriggerTypingAsync().GetAwaiter().GetResult();
                            await execCommand.ExecuteCommandAsync(commandContext, member, originalMessage, args, allArgsText);

                            XanBotLogger.WriteLine(string.Format("§aUser \"§6{0}§a\" issued command \"§6{1}§a\" with args {2}", member.FullName, command, ArrayToText(args)));
                        }
                        catch (CommandException commandErr)
                        {
                            string message = string.Format("§cFailed to issue command `{0}`: §1{1}", commandErr.Command.Name, commandErr.Message);
                            await ResponseUtil.RespondToAsync(originalMessage, message);

                            XanBotLogger.WriteLine(string.Format("§aUser \"§6{0}§a\" attempted to issue command \"§6{1}§a\" but it failed. The command gave the reason: §2{2}", member.FullName, commandErr.Command.Name, commandErr.Message));
                        }
                        catch (ArchonCommandException commandErr)
                        {
                            string message = string.Format("§cFailed to issue Archon Command `{0}`: §1{1}", commandErr.Command.Name, commandErr.Message);
                            await ResponseUtil.RespondToAsync(originalMessage, message);

                            XanBotLogger.WriteLine(string.Format("§aUser \"§6{0}§a\" attempted to issue Archon Command \"§6{1}§a\" but it failed. The command gave the reason: §2{2}", member.FullName, commandErr.Command.Name, commandErr.Message));
                        }
                        catch (TaskCanceledException taskCancel)
                        {
                            XanBotLogger.WriteException(taskCancel);
                        }
                    }
                    else
                    {
                        string message = string.Format($"You can't use this command here. Go to <#{optimalTargetChannel.Id}> instead.");
                        await ResponseUtil.RespondToAsync(originalMessage, message);
                    }
                }
                else
                {
                    //string message = string.Format("You are not authorized to use `{0}`. It is only available to permission level `{1}` and above (You are at `{2}`)", execCommand.Name, execCommand.RequiredPermissionLevel, member.PermissionLevel);
                    await ResponseUtil.RespondToAsync(originalMessage, usagePerms.ErrorMessage);

                    XanBotLogger.WriteLine(string.Format("§aUser \"§6{0}§a\" attempted to issue command \"§6{1}§a\" but it failed because they don't have a high enough permission level or because the command's CanUseCommand method returned false.", member.FullName, execCommand.Name));
                }
            }
            else
            {
                await ResponseUtil.RespondToAsync(originalMessage, "The command `" + command + "` does not exist.");

                XanBotLogger.WriteLine(string.Format("§aUser \"§6{0}§a\" attempted to issue command \"§6{1}§a\" but it failed because it doesn't exist.", member.FullName, command));
            }
        }