Иконка ресурса

Самообучающийся алгоритм для чат-викторин "вопрос-ответ" 2021-05-23

Нет прав для скачивания
Версия(и) Minecraft
Не важна
Мини-бот, запускаемый как отдельный процесс, для решения в полу-автоматическом и автоматическом режиме чат-игр "Реши пример", "Напиши быстрее", "Вопрос-ответ" с возможностью автоматического составления базы ответов.

Возможные плюсы:
  • Алгоритм использует как источник информации лог-файл игры, что исключает необходимость внедряться в процесс игры, следовательно, его возможно использовать на проектах с лаунчером и анти-читом;
  • (Не проверено) Используется библиотека User32.dll для обхода CREATE_ROBOT_PERMISSION;
  • Переключение между полу-автоматическим режимом (ответ копируется в буфер обмена) и автоматическим (бот сам отправляет сообщение с ответом) с помощью комбинации клавиш;
  • Звуковая индикация в полу-автоматическом режиме, чтобы точно не пропустить викторину в чате;
  • Гибкая подстройка триггеров и границ сообщений;
Возможные минусы:
  • Некоторые проекты могут просто не давать сохранять лог-файлы клиентам вообще или иметь мод, который фильтрует запись логов на клиенте;
  • Если игра запущена от имени администратора, то и программу тоже необходимо запускать от имени администратора.
  • Для автоматического режима необходимы библиотеки JNA и JNA-Platform.
  • В особо запущенных случаях администраторы блочат не только использование роботов, но и буфера обмена, так что в данном случае придется эмулировать нажатие каждой клавиши.
ВНИМАНИЕ! Использование данного алгоритма производится на ВАШ страх и риск. За любые последствия, в том числе получение бана, мута и иных мер наказания, отвечаете лично ВЫ.

Ну а теперь приступим:
Test.java:
@SuppressWarnings({"ResultOfMethodCallIgnored", "InfiniteLoopStatement", "BusyWait"})
public class Test {

    static final ScriptEngineManager mgr = new ScriptEngineManager();
    //Стандартное решение для решения примеров из строки
    static final ScriptEngine engine= mgr.getEngineByName("JavaScript");
    //Путь к файлу с ответами
    static final File answ = new File("answers.txt");
    //Хранение ответов в памяти
    static final HashMap<String, String> map = new HashMap<>();
    //Доступ к буферу обмена
    static final Clipboard clipboard = getSystemClipboard();
    static final Random random = new Random();
    static WinUser.INPUT winInput;
    //Пути к звуковым файлам
    static final File s1 = new File("A.wav"),s2 = new File("NA.wav");
    static Clip c1, c2;
    //Разделитель для хранения пары вопрос-ответ в файле
    static final String split = ":=:";

    //Объект для хранения двух объектов
    static class Vec2Obj<T>{
        T a; T b;
        Vec2Obj(T a, T b){
            this.a=a; this.b=b;
        }
    }

    static String[]
            //Строки первичной фильтрации
            trigger1 = {": [CHAT] [Чат-Игра]", ": [CHAT] [Чат-игра]"};
    @SuppressWarnings("unchecked")
    static Vec2Obj<String>[]
            //Строки, характерезующие сообщение с правильным ответом
            trigger2 = new Vec2Obj[]{
                    new Vec2Obj<>("Правильным ответом было: ", ""),
                    new Vec2Obj<>("Ответом было: ", "")
            };

    static String
            mathPre = "[Чат-игра] Реши пример ", mathPost = " быстрее остальных и получи награду!",
            copyPastePre = "[Чат-игра] Напишите \"", copyPastePost = "\" быстрее остальных и получи награду!",
            quizPre = "[CHAT] [Чат-Игра] ", quizPost = "";

    //Ожидание правиильного ответа от чат-игры
    static boolean waiting = false, AFKMode = false, useAudio = true;
    static volatile boolean delayCheck = false;
    //Хранение неизвестного вопроса в ожидании ответа
    static String buffered;

    public static void main(String[] args) throws IOException, InterruptedException {
        //Настройка черной магии для обхода CREATE_ROBOT_PERMISSION
        WinUser.INPUT in = new WinUser.INPUT(  );
        winInput=in;
        in.type = new WinDef.DWORD( WinUser.INPUT.INPUT_KEYBOARD );
        in.input.setType("ki");
        in.input.ki.wScan = new WinDef.WORD( 0 );
        in.input.ki.time = new WinDef.DWORD( 0 );
        in.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR( 0 );

        if(useAudio){
            try {
                prepareAudio();
            } catch (UnsupportedAudioFileException | LineUnavailableException e) {
                e.printStackTrace();
                useAudio=false;
            }
        }

        //Путь к лог-файлу
        File f = new File("run/logs/latest.log");
        if(!f.exists()) System.exit(-1);

        try(InputStreamReader isr = new InputStreamReader(new FileInputStream(f));
            BufferedReader br = new BufferedReader(isr)){
            //Загрузка базы вопрос-ответ из файла
            readData();
            //Переход в конец файла
            br.skip(Long.MAX_VALUE);
            //Основной цикл чтения файла
            while(true) {
                //Простое переключение АФК-режима комбинацией клавиш
                if(User32.INSTANCE.GetAsyncKeyState(KeyEvent.VK_TAB)<0){
                    if(User32.INSTANCE.GetAsyncKeyState(KeyEvent.VK_1)<0){ AFKMode=true; }
                    else if(User32.INSTANCE.GetAsyncKeyState(KeyEvent.VK_SPACE)<0){ AFKMode=false; }
                }
                //Чтение всех новых сток
                while (br.ready()){
                    scanLine(br.readLine());
                }
                Thread.sleep(500L);
            }
        }
    }

    //Определение типа чат-игры из сообщения
    static void scanLine(String line){
        //Если строка содержит любую ключевую фразу - продолжить
        if(Arrays.stream(trigger1).anyMatch(line::contains)){
            //Если задача - решить пример быстрее всех
            if(line.contains(mathPre)){
                //Выделение части с примером
                line = extract(line, mathPre, mathPost);
                //Удаление пробелов
                line = line.trim();
                try {
                    output(engine.eval(line).toString());
                } catch (ScriptException e) {
                    e.printStackTrace();
                }
            } else
            //Если задача - написать набор символов быстрее всех
            if(line.contains(copyPastePre)){
                //Выделение копируемой части
                line = extract(line, copyPastePre, copyPastePost);
                output(line);
            } else {
                //Если задача - викторина вопрос-ответ
                final String finalLine = line;
                //Имеет ли сообщение ответ на вопрос
                boolean hasT2 = Arrays.stream(trigger2).anyMatch(s -> finalLine.contains(s.a));

                if(!waiting || !hasT2){
                    if(!waiting && !hasT2){
                        line = extract(line, quizPre, quizPost);
                        //Имеется ли ответ на данный вопрос в базе
                        if(map.containsKey(line)){
                            output(map.get(line));
                        } else {
                            //Переход в режим ожидания ответа
                            waiting = true;
                            buffered = line;
                            if(!AFKMode && useAudio){
                                c2.setFramePosition(0);
                                c2.start();
                            }
                        }
                    }
                } else {
                    //Определение типа ответа
                    for (Vec2Obj<String> get : trigger2) {
                        if(line.contains(get.a)){
                            line = extract(line, get.a, get.b);
                            break;
                        }
                    }
                    //Запись в базу и сохранения на диск
                    map.put(buffered, line);
                    writeData();
                    waiting = false;
                }
                //Предохранитель на случай, если кто-то ответит быстрее бота
                if(AFKMode && delayCheck && hasT2){
                    delayCheck=false;
                }
            }
        } else
            //Простое закрытие бота при закрытии игры
            if(line.contains("[net.minecraft.client.Minecraft]: Stopping!")){
            System.exit(1);
        }
    }

    static String extract(String line, String pre, String post){
        //Раздельный substring необходим для корректной работы indexOf при коротких значениях post
        line = line.substring(line.indexOf(pre)+pre.length());
        //Проверка на случай, если сообщение чат-игры не имеет завершающей фразы
        if(post!=null&&!post.isEmpty()){
            line = line.substring(0, line.indexOf(post));
        }
        return line;
    }

    static void readData() throws IOException {
        if (!answ.exists()) {
            PrintWriter writer = new PrintWriter(answ, "UTF-8");
            writer.close();
            return;
        }
        try (InputStreamReader isr = new InputStreamReader(new FileInputStream(answ));
             BufferedReader br = new BufferedReader(isr)){
            for(String line = br.readLine(); line != null; line = br.readLine()) {
                String key = line.substring(0, line.indexOf(split));
                String value = line.substring(line.indexOf(split)+split.length());
                map.put(key, value);
            }
        }
    }

    @SuppressWarnings("ResultOfMethodCallIgnored")
    //Безопасное сохранение базы ответов
    static void writeData(){
        File temp = new File(answ+"_tmp");
        try(FileWriter fw = new FileWriter(temp)) {
            for (String key : map.keySet()) { fw.write(key + ":=:" + map.get(key) + "\n"); }
            fw.close();
            if(answ.exists()){ answ.delete(); }
            temp.renameTo(answ);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static void prepareAudio() throws IOException, UnsupportedAudioFileException, LineUnavailableException {
        AudioInputStream ais1 = AudioSystem.getAudioInputStream(s1);
        c1 = AudioSystem.getClip();
        c1.open(ais1);
        ais1.close();
        AudioInputStream ais2 = AudioSystem.getAudioInputStream(s2);
        c2 = AudioSystem.getClip();
        c2.open(ais2);
        ais2.close();
    }

    static Clipboard getSystemClipboard() {
        Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
        return defaultToolkit.getSystemClipboard();
    }
    //Действия с готовым ответом
    static void output(String answer){
        clipboard.setContents(new StringSelection(answer), null);
        if(AFKMode){
            delayCheck=true;
            Thread t = new Thread(()->{
                try {
                    //Случайная задержка от 2 до 7 секунд
                    Thread.sleep(random.nextInt(5000)+2000);
                    //Если до нас ещё никто не ответил
                    if(delayCheck){
                        delayCheck=false;
                        setButtonState('T', true);
                        Thread.sleep(50);
                        setButtonState('T', false);
                        Thread.sleep(50);
                        setButtonState(0x11, true);
                        Thread.sleep(50);
                        setButtonState('V', true);
                        Thread.sleep(50);
                        setButtonState('V', false);
                        Thread.sleep(50);
                        setButtonState(0x11, false);
                        Thread.sleep(50);
                        setButtonState(0x0D, true);
                        Thread.sleep(50);
                        setButtonState(0x0D, false);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            t.start();
        } else if(useAudio){
            c1.setFramePosition(0);
            c1.start();
        }
    }

    static void setButtonState(long button, boolean pressed){
        winInput.input.ki.wVk = new WinDef.WORD(button);
        winInput.input.ki.dwFlags = new WinDef.DWORD( pressed?0:2 );
        User32.INSTANCE.SendInput( new WinDef.DWORD( 1 ), ( WinUser.INPUT[] ) winInput.toArray( 1 ), winInput.size() );
    }
}
}

Православные звуковые файлы прилагаются.

Для администраторов проектов:
Как писалось выше, данный бот не будет работать, если в лог-файле не будут содержаться элементы викторины, а для этого наиболее эффективным решением будет мод на клиентской стороне, который фильтрует сообщения, заносимые в лог-файл. Другим, не менее эффективным, но более трудоёмким решением будет вывод викторины не в чате, а в оверлее клиента, но это совсем другая история...
  • Like
Реакции: bagomot и Plasticable
Автор
NotYuki
Скачивания
3
Просмотры
269
Первый выпуск
Обновление
Оценка
4.00 звёзд 1 оценок

Последние рецензии

Нормас тема.
Сверху