Загрузка...

Events JS tutorial. How to break the competition from Scooter and Aviasales with a small script.

Thread in Web vulnerabilities created by Savlavish Feb 19, 2023. 597 views

  1. Savlavish
    Savlavish Topic starter Feb 19, 2023 4 Feb 17, 2023
    Как сломать конкурс от Самоката и Aviasales маленьким скриптом.
    (На момент написания этой статьи проходит конкурс от
    Aviasales и Самокат который закончится 14.03.2023,
    розыгрыш на промокоды для самоката от 1000р
    https://samokat.aviasales.ru/)


    Суть игры - как можно лучше ставить ящики
    друг на друга

    (на картинке ниже можно увидеть интерфейс игры)
    [IMG][IMG]


    Шаг 1. Используем баг верстки
    Открываем в chrome панель разработчиков на F12,
    переходим в панель инструментов устройств Ctr + Shift + M,
    попутно сразу открываем консоль

    [IMG]

    Выставляем адаптивный размер окна, так чтобы верхний бокс почти соприкасался с нижним,
    как показано на картинке ниже

    [IMG]

    Таким образом мы добились обюза верстки в этой игре, в таком сетапе ставить контейнеры руками в 100 раз проще, а во вторых это сводит нашу задачу к очень простому решению.
    Все что нам осталось - отследить момент, когда верхний бокс будет точно под нижним и вызвать функцию которая опустит контейнер.


    Шаг 2. Пишем JS EventListeners

    Но для начала мы должны найти в DOM дереве (то есть в верстке), нужные нам контейнеры самоката,
    что мы и успешно делаем, помечены они CSS классом 'Bag_root__3bHAW' - этим и воспользуемся.

    [IMG]

    У нас есть план и все ингредиенты, осталось приготовить соус
    Немного потыкав мышкой по экрану - понимаем, что бокс падает на событие отпускания лкм,
    он же 'mouseup' Event в js. Заводим переменную, которую будет имитировать 'mouseup'.
    JS
    const event = new MouseEvent("mouseup", {
    view: window,
    bubbles: true,
    cancelable: true,
    });
    Следующим шагом создаем цикл и условие, которое будет спрашивать,
    'Контейнеры находятся друг над другом?'
    JS

    while(true) { //бесконечный цикл
    let boxArray = this.document.getElementsByClassName('Bag_root__3bHAW'); //берем список всех коробок по имени классу, скриншот выше
    if (boxArray[2].getBoundingClientRect().left.toFixed(0) ===
    boxArray[0].getBoundingClientRect().left.toFixed(0)) { //сравниваем координаты контейнеров параллельно округляя до целых значений
    console.log('Drop box') //если контейнеры находятся на одном расстоянии от левой части экрана выводим сообщение
    }
    }
    Тот кто шарит, поймет, что этот код не работает. Цикл wile заблокирует основной поток и дальше игра просто остановится, так как JavaScript однопоточный язык, мы не можем просто создать второй поток исполнения и написать WHILE (TRUE) в который вставим условие 'если контейнеры друг над другом вызови event mouseup'. Но мы можем использовать инструменты JS, взять асинхронную функцию и Promise, оно же обещание. Работает это так, что мы на время уходим из исполняемого цикла, и даем основному потоку заниматься своими обычными делами, но берем с него обещание вернутся обратно и поставить наш контейнер на место.​
    JS
    async function play(){
    while(true) {
    let boxArray = this.document.getElementsByClassName('Bag_root__3bHAW');
    if (boxArray[2].getBoundingClientRect().left.toFixed(0)
    === boxArray[0].getBoundingClientRect().left.toFixed(0)) {
    console.log('Drop box') //печатает в консоль сообщение
    }
    await new Promise(resolve => setTimeout(resolve, 2)); // то самое обещание которое мы ждем, и если что спросим за него
    }
    }
    Осталось скомпоновать весь код воедино, заменить console.log('Drop box') на вызов event​
    JS

    const event = new MouseEvent("mouseup", { //создали ивент
    view: window,
    bubbles: true,
    cancelable: true,
    });

    async function play(){ //создали асинхронную функцию
    while(true) { //бесконечный цикл
    let boxArray = this.document.getElementsByClassName('Bag_root__3bHAW');
    if (boxArray[2].getBoundingClientRect().left.toFixed(0)
    === boxArray[0].getBoundingClientRect().left.toFixed(0)) { //проверили находятся ли контейнеры на одном уровне
    document.getElementsByTagName('body')[0].dispatchEvent(event); //вызвали ивент нажатия
    }
    await new Promise(resolve => setTimeout(resolve, 2)); //отдали исполнения основному потоку и взяли с него обещание вернуться
    }
    }

    this.play()
    PS. Все хорошо за исключением одной маленькой вещи из "ШАГ 1". Мы поменяли размеры на мобильное отображение, а это значит, что у нас нет Event 'mouseup' (1 строчка кода), вместо это мы должны создавать const event = new TouchEvent("touchend", {...})

    Всем новых рекордов!
    [IMG]


     
Loading...
Top