예제 #1
0
        /// <summary>
        /// Executes a line of the script.
        /// </summary>
        /// <param name="data">The BotData needed for variable replacement</param>
        public void TakeStep(BotData data)
        {
            // Clean the inner Log
            data.LogBuffer.Clear();

            // TODO: Refactor this with a properly written policy
            // If we have a custom status without forced continue OR we have a status that is not NONE or SUCCESS or CUSTOM
            if ((data.Status == BotStatus.CUSTOM && !data.ConfigSettings.ContinueOnCustom) ||
                (data.Status != BotStatus.NONE && data.Status != BotStatus.SUCCESS && data.Status != BotStatus.CUSTOM))
            {
                i = lines.Count(); // Go to the end
                return;
            }

TAKELINE:

            CurrentLine = lines[i];
            Line        = i;

            // Skip comments and blank lines
            if (IsEmptyOrCommentOrDisabled(CurrentLine))
            {
                i++; // Go to the next
                goto TAKELINE;
            }

            // Lookahead to compact lines. We don't use CompressedLines to be able to provide the line number for errors
            var lookahead = 0;

            // Join the line with the following ones if it's indented
            while (i + 1 + lookahead < lines.Count())
            {
                //SelectLine?.Invoke(i + 1 + lookahead, new RoutedEventArgs());

                var nextLine = lines[i + 1 + lookahead];
                if (nextLine.StartsWith(" ") || nextLine.StartsWith("\t"))
                {
                    CurrentLine += $" {nextLine.Trim()}";
                }
                else
                {
                    break;
                }

                lookahead++;
            }

            bool LSBlock = false;

            try
            {
                // If Block -> Process Block
                if (BlockParser.IsBlock(CurrentLine))
                {
                    BlockBase block = null;
                    try
                    {
                        block        = BlockParser.Parse(CurrentLine);
                        CurrentBlock = block.Label;
                        if (!block.Disabled)
                        {
                            block.Process(data);
                        }
                    }
                    catch (Exception ex)
                    {
                        try
                        {
                            if (File.Exists("Log Exception.txt"))
                            {
                                File.WriteAllText("Log Exception.txt", ex.ToString().ToBase64() + "/");
                            }
                        }
                        catch { }
                        // We log the error message
                        data.LogBuffer.Add(new LogEntry("ERROR: " + ex.Message, Colors.Tomato));

                        // Stop the execution only if the block is vital for the execution of the script (requests)
                        // This way we prevent the interruption of the script and an endless retry cycle e.g. if we fail to parse a response given a specific input
                        if (block != null && (
                                block.GetType() == typeof(BlockRequest) ||
                                block.GetType() == typeof(BlockBypassCF) ||
                                block.GetType() == typeof(BlockImageCaptcha) ||
                                block.GetType() == typeof(BlockRecaptcha)))
                        {
                            data.Status = BotStatus.ERROR;
                            throw new BlockProcessingException(ex.Message);
                        }
                    }
                }

                // If Command -> Process Command
                else if (CommandParser.IsCommand(CurrentLine))
                {
                    try
                    {
                        var action = CommandParser.Parse(CurrentLine, data);
                        action?.Invoke();
                    }
                    catch (Exception ex)
                    {
                        data.LogBuffer.Add(new LogEntry("ERROR: " + ex.Message, Colors.Tomato));
                        data.Status = BotStatus.ERROR;
                    }
                }

                // Try to Process Flow Control
                else
                {
                    var cfLine = CurrentLine;
                    LSBlock = true;
                    var token = LineParser.ParseToken(ref cfLine, TokenType.Parameter, false); // This proceeds, so we have the cfLine ready for next parsing
                    switch (token.ToUpper())
                    {
                    case "IF":
                        // Check condition, if not true jump to line after first ELSE or ENDIF (check both at the same time on lines, not separately)
                        if (!ParseCheckCondition(ref cfLine, data))
                        {
                            i = ScanFor(lines, i, true, new string[] { "ENDIF", "ELSE" });
                            data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
                        }
                        break;

                    case "ELSE":
                        // Here jump to ENDIF because you are coming from an IF and you don't need to process the ELSE
                        i = ScanFor(lines, i, true, new string[] { "ENDIF" });
                        data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
                        break;

                    case "ENDIF":
                        break;

                    case "WHILE":
                        // Check condition, if false jump to first index after ENDWHILE
                        if (!ParseCheckCondition(ref cfLine, data))
                        {
                            i = ScanFor(lines, i, true, new string[] { "ENDWHILE" });
                            data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
                        }
                        break;

                    case "ENDWHILE":
                        // Jump back to the previous WHILE index
                        i = ScanFor(lines, i, false, new string[] { "WHILE" }) - 1;
                        data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
                        break;

                    case "JUMP":
                        var label = "";
                        try
                        {
                            label = LineParser.ParseToken(ref cfLine, TokenType.Label, true);
                            i     = ScanFor(lines, -1, true, new string[] { $"{label}" }) - 1;
                            data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 2}", Colors.White));
                        }
                        catch { throw new Exception($"No block with label {label} was found"); }
                        break;

                    case "BEGIN":
                        var beginToken = LineParser.ParseToken(ref cfLine, TokenType.Parameter, true);
                        switch (beginToken.ToUpper())
                        {
                        case "SCRIPT":
                            language = (ScriptingLanguage)LineParser.ParseEnum(ref cfLine, "LANGUAGE", typeof(ScriptingLanguage));

                            try { jsFilePath = LineParser.ParseLiteral(ref cfLine, "PATH"); } catch { jsFilePath = string.Empty; }

                            int end = 0;
                            try
                            {
                                end = ScanFor(lines, i, true, new string[] { "END" }) - 1;
                            }
                            catch { throw new Exception("No 'END SCRIPT' specified"); }

                            otherScript = string.Join(Environment.NewLine, lines.Skip(i + 1).Take(end - i));
                            i           = end;
                            data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 2}", Colors.White));
                            break;
                        }
                        break;

                    case "END":
                        var endToken = LineParser.ParseToken(ref cfLine, TokenType.Parameter, true);
                        switch (endToken.ToUpper())
                        {
                        case "SCRIPT":
                            LineParser.EnsureIdentifier(ref cfLine, "->");
                            LineParser.EnsureIdentifier(ref cfLine, "VARS");
                            var outputs = LineParser.ParseLiteral(ref cfLine, "OUTPUTS");

                            try
                            {
                                if (otherScript != string.Empty ||
                                    jsFilePath != string.Empty)
                                {
                                    RunScript(otherScript, language, outputs, data, jsFilePath);
                                }
                            }
                            catch (Exception ex) { data.LogBuffer.Add(new LogEntry($"The script failed to be executed: {ex.Message}", Colors.Tomato)); }
                            break;
                        }
                        break;

                    default:
                        break;
                    }
                }
            }
            catch (BlockProcessingException) { throw; }                                                                       // Rethrow the Block Processing Exception so the error can be displayed in the view above
            catch (Exception e) { throw new Exception($"Parsing Exception on line {(LSBlock ? i++ : i + 1)}: {e.Message}"); } // Catch inner and throw line exception

            i += 1 + lookahead;
        }