示例#1
0
 private void OnUserSentMessage(User user, EvtUserMessageArgs e)
 {
     if (user.OptedOut == true)
     {
         return;
     }
 }
        private void ReadWaitInputs()
        {
            while (true)
            {
                if (StopConsoleThread == true)
                {
                    break;
                }

                //if (Console.KeyAvailable == false)
                //    continue;

                string line = Console.ReadLine();

                User user = UserData;

                //Send message event
                EvtUserMessageArgs umArgs = new EvtUserMessageArgs()
                {
                    UsrMessage = new EvtUserMsgData(user.Name, user.Name,
                                                    user.Name, string.Empty, line)
                };

                UserSentMessageEvent?.Invoke(user, umArgs);

                //Attempt to parse the message as an input
                ProcessMsgAsInput(user, umArgs);

                //Check for a command
                if (line.Length > 0 && line[0] == Globals.CommandIdentifier)
                {
                    //Build args list
                    List <string> argsList  = new List <string>(line.Split(' '));
                    string        argsAsStr = string.Empty;

                    //Remove the command itself and the space from the string
                    if (argsList.Count > 1)
                    {
                        argsAsStr = line.Remove(0, argsList[0].Length + 1);
                    }

                    //Remove command identifier
                    string cmdText = argsList[0].Remove(0, 1);

                    //Now remove the first entry from the list, which is the command, retaining only the arguments
                    argsList.RemoveAt(0);

                    EvtChatCommandArgs chatcmdArgs = new EvtChatCommandArgs();
                    EvtUserMsgData     msgData     = new EvtUserMsgData(user.Name, user.Name, user.Name, string.Empty, line);

                    chatcmdArgs.Command = new EvtChatCommandData(argsList, argsAsStr, msgData, Globals.CommandIdentifier, cmdText);

                    ChatCommandReceivedEvent?.Invoke(UserData, chatcmdArgs);
                }
            }
        }
示例#3
0
        //Break up much of the message handling by sending events
        private void OnMessageReceived(object sender, OnMessageReceivedArgs e)
        {
            EvtUserMessageArgs umArgs = new EvtUserMessageArgs()
            {
                //UserData = user,
                UsrMessage = new EvtUserMsgData(e.ChatMessage.UserId, e.ChatMessage.Username,
                                                e.ChatMessage.DisplayName, e.ChatMessage.Channel, e.ChatMessage.Message)
            };

            UserSentMessageEvent?.Invoke(umArgs);
        }
示例#4
0
        private void OnUserSentMessage(User user, EvtUserMessageArgs e)
        {
            if (user.OptedOut == false)
            {
                user.IncrementMsgCount();
            }

            string possibleMeme = e.UsrMessage.Message.ToLower();

            if (BotProgram.BotData.Memes.TryGetValue(possibleMeme, out string meme) == true)
            {
                BotProgram.MsgHandler.QueueMessage(meme);
            }
        }
        private void ReadWaitInputs()
        {
            while (true)
            {
                if (StopConsoleThread == true)
                {
                    break;
                }

                string line = Console.ReadLine();

                //Send message event
                EvtUserMessageArgs umArgs = new EvtUserMessageArgs()
                {
                    UsrMessage = new EvtUserMsgData(TerminalUsername, TerminalUsername, TerminalUsername,
                                                    string.Empty, line)
                };

                UserSentMessageEvent?.Invoke(umArgs);

                //Check for a command
                if (line.Length > 0 && line[0] == CommandIdentifier)
                {
                    //Build args list
                    List <string> argsList  = new List <string>(line.Split(' '));
                    string        argsAsStr = string.Empty;

                    //Remove the command itself and the space from the string
                    if (argsList.Count > 1)
                    {
                        argsAsStr = line.Remove(0, argsList[0].Length + 1);
                    }

                    //Remove command identifier
                    string cmdText = argsList[0].Remove(0, 1);

                    //Now remove the first entry from the list, which is the command, retaining only the arguments
                    argsList.RemoveAt(0);

                    EvtChatCommandArgs chatcmdArgs = new EvtChatCommandArgs();
                    EvtUserMsgData     msgData     = new EvtUserMsgData(TerminalUsername, TerminalUsername, TerminalUsername,
                                                                        string.Empty, line);

                    chatcmdArgs.Command = new EvtChatCommandData(argsList, argsAsStr, msgData, CommandIdentifier, cmdText);

                    ChatCommandReceivedEvent?.Invoke(chatcmdArgs);
                }
            }
        }
示例#6
0
        //Break up much of the message handling by sending events
        private void OnMessageReceived(object sender, OnMessageReceivedArgs e)
        {
            User user = BotProgram.GetOrAddUser(e.ChatMessage.DisplayName, false);

            EvtUserMessageArgs umArgs = new EvtUserMessageArgs()
            {
                UsrMessage = new EvtUserMsgData(e.ChatMessage.UserId, e.ChatMessage.Username,
                                                e.ChatMessage.DisplayName, e.ChatMessage.Channel, e.ChatMessage.Message)
            };

            UserSentMessageEvent?.Invoke(user, umArgs);

            //Attempt to parse the message as an input
            ProcessMsgAsInput(user, umArgs);
        }
示例#7
0
        private void OnUserSentMessage(EvtUserMessageArgs e)
        {
            //Look for a user with this name
            string userName = e.UsrMessage.Username;

            if (string.IsNullOrEmpty(userName) == false)
            {
                User user = DataHelper.GetOrAddUser(userName, out bool added);

                if (added == true)
                {
                    //Get the new user message and replace the variable with their name
                    string newUserMessage = DataHelper.GetSettingString(SettingsConstants.NEW_USER_MESSAGE, $"Welcome to the stream, {userName}!");
                    newUserMessage = newUserMessage.Replace("{0}", userName);

                    MsgHandler.QueueMessage(newUserMessage);
                }

                using (BotDBContext context = DatabaseManager.OpenContext())
                {
                    user = DataHelper.GetUserNoOpen(userName, context);

                    //Increment message count and save
                    if (user.IsOptedOut == false)
                    {
                        user.Stats.TotalMessageCount++;
                        context.SaveChanges();
                    }

                    //Check for memes if the user isn't ignoring them
                    if (user.Stats.IgnoreMemes == 0)
                    {
                        string possibleMeme = e.UsrMessage.Message.ToLower();
                        Meme   meme         = context.Memes.FirstOrDefault((m) => m.MemeName == possibleMeme);
                        if (meme != null)
                        {
                            MsgHandler.QueueMessage(meme.MemeValue);
                        }
                    }
                }
            }

            ProcessMsgAsInput(e);
        }
示例#8
0
 private void OnUserMadeInput(User user, EvtUserMessageArgs e, in Parser.InputSequence validInputSeq)
示例#9
0
        //NOTE: This would result in lots of code duplication if other streaming services were integrated
        //Is there a better way to do this?

        private void ProcessMsgAsInput(User userData, EvtUserMessageArgs e)
        {
            //Don't process for inputs if a meme
            string possibleMeme = e.UsrMessage.Message.ToLower();

            if (BotProgram.BotData.Memes.TryGetValue(possibleMeme, out string meme) == true)
            {
                return;
            }

            //Ignore commands as inputs
            if (possibleMeme.StartsWith(Globals.CommandIdentifier) == true)
            {
                return;
            }

            //If there are no valid inputs, don't attempt to parse
            if (InputGlobals.CurrentConsole.ValidInputs == null || InputGlobals.CurrentConsole.ValidInputs.Length == 0)
            {
                return;
            }

            //Parser.InputSequence inputSequence = default;
            //(bool, List<List<Parser.Input>>, bool, int) parsedVal = default;
            Parser.InputSequence inputSequence = default;

            try
            {
                string parse_message = Parser.Expandify(Parser.PopulateMacros(e.UsrMessage.Message));
                inputSequence = Parser.ParseInputs(parse_message, userData.Team, true, true);
                //parsedVal = Parser.Parse(parse_message);
                //Console.WriteLine(inputSequence.ToString());
                //Console.WriteLine("\nReverse Parsed: " + ReverseParser.ReverseParse(inputSequence));
                //Console.WriteLine("\nReverse Parsed Natural:\n" + ReverseParser.ReverseParseNatural(inputSequence));
            }
            catch (Exception exception)
            {
                string excMsg = exception.Message;

                //Kimimaru: Sanitize parsing exceptions
                //Most of these are currently caused by differences in how C# and Python handle slicing strings (Substring() vs string[:])
                //One example that throws this that shouldn't is "#mash(w234"
                //BotProgram.MsgHandler.QueueMessage($"ERROR: {excMsg}");
                inputSequence.InputValidationType = Parser.InputValidationTypes.Invalid;
                //parsedVal.Item1 = false;
            }

            //Check for non-valid messages
            if (inputSequence.InputValidationType != Parser.InputValidationTypes.Valid)
            {
                //Display error message for invalid inputs
                if (inputSequence.InputValidationType == Parser.InputValidationTypes.Invalid)
                {
                    BotProgram.MsgHandler.QueueMessage(inputSequence.Error);
                }

                return;
            }

            //It's a valid message, so process it

            //Ignore if user is silenced
            if (userData.Silenced == true)
            {
                return;
            }

            //Ignore based on user level and permissions
            if (userData.Level < BotProgram.BotData.InputPermissions)
            {
                BotProgram.MsgHandler.QueueMessage($"Inputs are restricted to levels {(AccessLevels.Levels)BotProgram.BotData.InputPermissions} and above");
                return;
            }

            #region Parser Post-Process Validation

            /* All this validation is very slow
             * Find a way to speed it up, ideally without integrating it directly into the parser
             */

            //Check if the user has permission to perform all the inputs they attempted
            //Also validate that the controller ports they're inputting for are valid
            ParserPostProcess.InputValidation inputValidation = ParserPostProcess.CheckInputPermissionsAndPorts(userData.Level, inputSequence.Inputs,
                                                                                                                BotProgram.BotData.InputAccess.InputAccessDict);

            //If the input isn't valid, exit
            if (inputValidation.IsValid == false)
            {
                if (string.IsNullOrEmpty(inputValidation.Message) == false)
                {
                    BotProgram.MsgHandler.QueueMessage(inputValidation.Message);
                }
                return;
            }

            //Lastly, check for invalid button combos given the current console
            if (BotProgram.BotData.InvalidBtnCombos.InvalidCombos.TryGetValue((int)InputGlobals.CurrentConsoleVal, out List <string> invalidCombos) == true)
            {
                bool buttonCombosValidated = ParserPostProcess.ValidateButtonCombos(inputSequence.Inputs, invalidCombos);

                if (buttonCombosValidated == false)
                {
                    string msg    = "Invalid input: buttons ({0}) are not allowed to be pressed at the same time.";
                    string combos = string.Empty;

                    for (int i = 0; i < invalidCombos.Count; i++)
                    {
                        combos += "\"" + invalidCombos[i] + "\"";

                        if (i < (invalidCombos.Count - 1))
                        {
                            combos += ", ";
                        }
                    }

                    msg = string.Format(msg, combos);
                    BotProgram.MsgHandler.QueueMessage(msg);

                    return;
                }
            }

            #endregion

            if (InputHandler.StopRunningInputs == false)
            {
                //Invoke input event
                UserMadeInputEvent?.Invoke(userData, e, inputSequence);
            }
            else
            {
                BotProgram.MsgHandler.QueueMessage("New inputs cannot be processed until all other inputs have stopped.");
            }
        }
示例#10
0
        private void ProcessMsgAsInput(EvtUserMessageArgs e)
        {
            //Ignore commands as inputs
            if (e.UsrMessage.Message.StartsWith(DataConstants.COMMAND_IDENTIFIER) == true)
            {
                return;
            }

            GameConsole usedConsole = null;

            int lastConsoleID = 1;

            using (BotDBContext context = DatabaseManager.OpenContext())
            {
                lastConsoleID = (int)DataHelper.GetSettingIntNoOpen(SettingsConstants.LAST_CONSOLE, context, 1L);
                GameConsole lastConsole = context.Consoles.FirstOrDefault(c => c.ID == lastConsoleID);

                if (lastConsole != null)
                {
                    //Create a new console using data from the database
                    usedConsole = new GameConsole(lastConsole.Name, lastConsole.InputList, lastConsole.InvalidCombos);
                }
            }

            //If there are no valid inputs, don't attempt to parse
            if (usedConsole == null)
            {
                MsgHandler.QueueMessage($"The current console does not point to valid data. Please set a different console to use, or if none are available, add one.");
                return;
            }

            if (usedConsole.ConsoleInputs.Count == 0)
            {
                MsgHandler.QueueMessage($"The current console, \"{usedConsole.Name}\", does not have any available inputs.");
            }

            ParsedInputSequence inputSequence = default;
            string userName    = e.UsrMessage.Username;
            int    defaultDur  = 200;
            int    defaultPort = 0;

            try
            {
                int maxDur = 60000;

                string regexStr = usedConsole.InputRegex;

                string readyMessage = string.Empty;

                //Get default and max input durations
                //Use user overrides if they exist, otherwise use the global values
                User user = DataHelper.GetUser(userName);

                //Get default controller port
                defaultPort = (int)user.ControllerPort;

                defaultDur = (int)DataHelper.GetUserOrGlobalDefaultInputDur(userName);
                maxDur     = (int)DataHelper.GetUserOrGlobalMaxInputDur(userName);

                //TRBotLogger.Logger.Information($"Default dur: {defaultDur} | Max dur: {maxDur}");

                using (BotDBContext context = DatabaseManager.OpenContext())
                {
                    //Get input synonyms for this console
                    IQueryable <InputSynonym> synonyms = context.InputSynonyms.Where(syn => syn.ConsoleID == lastConsoleID);

                    //Prepare the message for parsing
                    readyMessage = InputParser.PrepParse(e.UsrMessage.Message, context.Macros, synonyms);
                }

                //Parse inputs to get our parsed input sequence
                inputSequence = InputParser.ParseInputs(readyMessage, regexStr, new ParserOptions(defaultPort, defaultDur, true, maxDur));
                TRBotLogger.Logger.Debug(inputSequence.ToString());
                TRBotLogger.Logger.Debug("Reverse Parsed (on parse): " + ReverseParser.ReverseParse(inputSequence, usedConsole,
                                                                                                    new ReverseParser.ReverseParserOptions(ReverseParser.ShowPortTypes.ShowNonDefaultPorts, defaultPort,
                                                                                                                                           ReverseParser.ShowDurationTypes.ShowNonDefaultDurations, defaultDur)));
            }
            catch (Exception exception)
            {
                string excMsg = exception.Message;

                //Handle parsing exceptions
                MsgHandler.QueueMessage($"ERROR PARSING: {excMsg} | {exception.StackTrace}", Serilog.Events.LogEventLevel.Warning);
                inputSequence.ParsedInputResult = ParsedInputResults.Invalid;
            }

            //Check for non-valid messages
            if (inputSequence.ParsedInputResult != ParsedInputResults.Valid)
            {
                //Display error message for invalid inputs
                if (inputSequence.ParsedInputResult == ParsedInputResults.Invalid)
                {
                    MsgHandler.QueueMessage(inputSequence.Error);
                }

                return;
            }

            #region Parser Post-Process Validation

            /* All this validation may be able to be performed faster.
             * Find a way to speed it up.
             */

            long globalInputPermLevel = DataHelper.GetSettingInt(SettingsConstants.GLOBAL_INPUT_LEVEL, 0L);
            int  userControllerPort   = 0;
            long userLevel            = 0;

            using (BotDBContext context = DatabaseManager.OpenContext())
            {
                User user = DataHelper.GetUserNoOpen(e.UsrMessage.Username, context);

                //Check if the user is silenced and ignore the message if so
                if (user.HasEnabledAbility(PermissionConstants.SILENCED_ABILITY) == true)
                {
                    return;
                }

                //Ignore based on user level and permissions
                if (user.Level < globalInputPermLevel)
                {
                    MsgHandler.QueueMessage($"Inputs are restricted to levels {(PermissionLevels)globalInputPermLevel} and above.");
                    return;
                }

                userControllerPort = (int)user.ControllerPort;
                userLevel          = user.Level;
            }

            //First, add delays between inputs if we should
            //We do this first so we can validate the inserted inputs later
            //The blank inputs can have a different permission level
            if (DataHelper.GetUserOrGlobalMidInputDelay(e.UsrMessage.Username, out long midInputDelay) == true)
            {
                MidInputDelayData midInputDelayData = ParserPostProcess.InsertMidInputDelays(inputSequence, userControllerPort, (int)midInputDelay, usedConsole);

                //If it's successful, replace the input list and duration
                if (midInputDelayData.Success == true)
                {
                    int oldDur = inputSequence.TotalDuration;
                    inputSequence.Inputs        = midInputDelayData.NewInputs;
                    inputSequence.TotalDuration = midInputDelayData.NewTotalDuration;

                    //TRBotLogger.Logger.Debug($"Mid input delay success. Message: {midInputDelayData.Message} | OldDur: {oldDur} | NewDur: {inputSequence.TotalDuration}\n{ReverseParser.ReverseParse(inputSequence, usedConsole, new ReverseParser.ReverseParserOptions(ReverseParser.ShowPortTypes.ShowAllPorts, 0))}");
                }
            }

            InputValidation validation = default;

            using (BotDBContext context = DatabaseManager.OpenContext())
            {
                User user = DataHelper.GetUserNoOpen(userName, context);

                //Check for restricted inputs on this user
                validation = ParserPostProcess.InputSequenceContainsRestrictedInputs(inputSequence, user.GetRestrictedInputs());

                if (validation.InputValidationType != InputValidationTypes.Valid)
                {
                    if (string.IsNullOrEmpty(validation.Message) == false)
                    {
                        MsgHandler.QueueMessage(validation.Message);
                    }
                    return;
                }

                //Check for invalid input combinations
                validation = ParserPostProcess.ValidateInputCombos(inputSequence, usedConsole.InvalidCombos,
                                                                   DataContainer.ControllerMngr, usedConsole);

                if (validation.InputValidationType != InputValidationTypes.Valid)
                {
                    if (string.IsNullOrEmpty(validation.Message) == false)
                    {
                        MsgHandler.QueueMessage(validation.Message);
                    }
                    return;
                }
            }

            //Check for level permissions and ports
            validation = ParserPostProcess.ValidateInputLvlPermsAndPorts(userLevel, inputSequence,
                                                                         DataContainer.ControllerMngr, usedConsole.ConsoleInputs);
            if (validation.InputValidationType != InputValidationTypes.Valid)
            {
                if (string.IsNullOrEmpty(validation.Message) == false)
                {
                    MsgHandler.QueueMessage(validation.Message, Serilog.Events.LogEventLevel.Warning);
                }
                return;
            }

            #endregion

            //Make sure inputs aren't stopped
            if (InputHandler.InputsHalted == true)
            {
                //We can't process inputs because they're currently stopped
                MsgHandler.QueueMessage("New inputs cannot be processed until all other inputs have stopped.", Serilog.Events.LogEventLevel.Warning);
                return;
            }

            //Fetch these values ahead of time to avoid passing the database context through so many methods
            long   autoPromoteEnabled  = DataHelper.GetSettingInt(SettingsConstants.AUTO_PROMOTE_ENABLED, 0L);
            long   autoPromoteInputReq = DataHelper.GetSettingInt(SettingsConstants.AUTO_PROMOTE_INPUT_REQ, long.MaxValue);
            long   autoPromoteLevel    = DataHelper.GetSettingInt(SettingsConstants.AUTO_PROMOTE_LEVEL, -1L);
            string autoPromoteMsg      = DataHelper.GetSettingString(SettingsConstants.AUTOPROMOTE_MESSAGE, string.Empty);

            bool addedInputCount = false;

            TRBotLogger.Logger.Debug($"Reverse Parsed (post-process): " + ReverseParser.ReverseParse(inputSequence, usedConsole,
                                                                                                     new ReverseParser.ReverseParserOptions(ReverseParser.ShowPortTypes.ShowNonDefaultPorts, defaultPort,
                                                                                                                                            ReverseParser.ShowDurationTypes.ShowNonDefaultDurations, defaultDur)));

            //Get the max recorded inputs per-user
            long maxUserRecInps = DataHelper.GetSettingInt(SettingsConstants.MAX_USER_RECENT_INPUTS, 0L);

            //It's a valid input - save it in the user's stats
            //Also record the input if we should
            using (BotDBContext context = DatabaseManager.OpenContext())
            {
                User user = DataHelper.GetUserNoOpen(e.UsrMessage.Username, context);

                //Ignore if the user is opted out
                if (user.IsOptedOut == false)
                {
                    user.Stats.ValidInputCount++;
                    addedInputCount = true;

                    context.SaveChanges();

                    //If we should store recent user inputs, do so
                    if (maxUserRecInps > 0)
                    {
                        //Get the input sequence - we may have added mid input delays between
                        //As a result, we'll need to reverse parse it
                        string message = ReverseParser.ReverseParse(inputSequence, usedConsole,
                                                                    new ReverseParser.ReverseParserOptions(ReverseParser.ShowPortTypes.ShowNonDefaultPorts, (int)user.ControllerPort,
                                                                                                           ReverseParser.ShowDurationTypes.ShowNonDefaultDurations, defaultDur));

                        //Add the recorded input
                        user.RecentInputs.Add(new RecentInput(message));
                        context.SaveChanges();

                        int diff = user.RecentInputs.Count - (int)maxUserRecInps;

                        //If we're over the max after adding, remove
                        if (diff > 0)
                        {
                            //Order by ascending ID and take the difference
                            //Lower IDs = older entries
                            IEnumerable <RecentInput> shouldRemove = user.RecentInputs.OrderBy(r => r.UserID).Take(diff);

                            foreach (RecentInput rec in shouldRemove)
                            {
                                user.RecentInputs.Remove(rec);
                                context.SaveChanges();
                            }
                        }
                    }
                }
            }

            bool autoPromoted = false;

            //Check if auto promote is enabled and auto promote the user if applicable
            if (addedInputCount == true)
            {
                using (BotDBContext context = DatabaseManager.OpenContext())
                {
                    User user = DataHelper.GetUserNoOpen(e.UsrMessage.Username, context);

                    //Check if the user was already autopromoted, autopromote is enabled,
                    //and if the user reached the autopromote input count requirement
                    if (user.Stats.AutoPromoted == 0 && autoPromoteEnabled > 0 &&
                        user.Stats.ValidInputCount >= autoPromoteInputReq)
                    {
                        //Only autopromote if this is a valid permission level
                        //We may not want to log or send a message for this, as it has potential to be very spammy,
                        //and it's not something the users can control
                        if (PermissionHelpers.IsValidPermissionValue(autoPromoteLevel) == true)
                        {
                            //Mark the user as autopromoted and save
                            user.Stats.AutoPromoted = 1;
                            autoPromoted            = true;

                            context.SaveChanges();
                        }
                    }
                }
            }

            if (autoPromoted == true)
            {
                //If the user is already at or above this level, don't set them to it
                //Only set if the user is below
                if (userLevel < autoPromoteLevel)
                {
                    //Adjust abilities and promote to the new level
                    DataHelper.AdjustUserLvlAndAbilitiesOnLevel(userName, autoPromoteLevel);

                    if (string.IsNullOrEmpty(autoPromoteMsg) == false)
                    {
                        PermissionLevels permLvl  = (PermissionLevels)autoPromoteLevel;
                        string           finalMsg = autoPromoteMsg.Replace("{0}", userName).Replace("{1}", permLvl.ToString());
                        MsgHandler.QueueMessage(finalMsg);
                    }
                }
            }

            InputModes inputMode = (InputModes)DataHelper.GetSettingInt(SettingsConstants.INPUT_MODE, 0L);

            //If the mode is Democracy, add it as a vote for this input
            if (inputMode == InputModes.Democracy)
            {
                //Set up the routine if it doesn't exist
                BaseRoutine      foundRoutine     = RoutineHandler.FindRoutine(RoutineConstants.DEMOCRACY_ROUTINE_ID, out int indexFound);
                DemocracyRoutine democracyRoutine = null;

                if (foundRoutine == null)
                {
                    long voteTime = DataHelper.GetSettingInt(SettingsConstants.DEMOCRACY_VOTE_TIME, 10000L);

                    democracyRoutine = new DemocracyRoutine(voteTime);
                    RoutineHandler.AddRoutine(democracyRoutine);
                }
                else
                {
                    democracyRoutine = (DemocracyRoutine)foundRoutine;
                }

                democracyRoutine.AddInputSequence(userName, inputSequence.Inputs);
            }
            //If it's Anarchy, carry out the input
            else
            {
                /************************************
                * Finally carry out the inputs now! *
                ************************************/

                InputHandler.CarryOutInput(inputSequence.Inputs, usedConsole, DataContainer.ControllerMngr);
            }
        }