Загрузка данных


#NoEnv
#SingleInstance Force
#Persistent
SendMode Input
SetWorkingDir %A_ScriptDir%
SetBatchLines, -1
CoordMode, Mouse, Screen
CoordMode, Pixel, Screen

; ============================================================
; ПОДКЛЮЧЕНИЕ GDI+
; ============================================================

#Include %A_ScriptDir%\Gdip_All.ahk

; ============================================================
; НАСТРОЙКИ
; ============================================================

; ---------- TESSERACT ----------

TesseractPath := "C:\Program Files\Tesseract-OCR\tesseract.exe"

; Язык OCR.
; eng = английский
; rus = русский
; Если установлен русский языковой пакет:
OCRLanguage := "rus+eng"

; ---------- ЦЕНА ----------

; Максимальная цена.
; Будут приниматься только цены НИЖЕ этого значения.
MaxPrice := 50000

; ============================================================
; КООРДИНАТЫ
; ============================================================

; ------------------------------------------------------------
; 1. Поле, куда вводится нужный текст
; ------------------------------------------------------------

InputX := 500
InputY := 300

; Текст, который необходимо вводить.
SearchText := "НУЖНЫЙ ТЕКСТ"

; ------------------------------------------------------------
; 2. Кнопка обновления списка
; ------------------------------------------------------------

RefreshX := 700
RefreshY := 300

; ------------------------------------------------------------
; 3. ОБЛАСТЬ OCR
;
; Именно эта область будет анализироваться Tesseract.
;
; X1/Y1 = левый верхний угол
; OCRWidth/OCRHeight = ширина и высота
; ------------------------------------------------------------

OCRX := 400
OCRY := 400

OCRWidth := 700
OCRHeight := 500

; ------------------------------------------------------------
; 4. КНОПКА ПОДТВЕРЖДЕНИЯ
;
; После нахождения подходящей цены:
;
; клик по найденной цене
; ↓
; ожидание
; ↓
; клик по этой кнопке
; ------------------------------------------------------------

ConfirmX := 1000
ConfirmY := 800

; ------------------------------------------------------------
; 5. ПОЛЗУНОК СПИСКА
;
; ScrollBarX/Y = положение ползунка
;
; ScrollPixels = насколько опускаем его за один шаг
; ------------------------------------------------------------

ScrollBarX := 1200
ScrollBarY := 500

ScrollPixels := 150

; Сколько раз можно опустить список
; на одной странице.
;
; Например:
; 5 шагов × 150 пикселей = 750 пикселей
;
MaxScrollSteps := 5

; ------------------------------------------------------------
; 6. КНОПКА ПЕРЕХОДА НА 2-Ю СТРАНИЦУ
; ------------------------------------------------------------

Page2X := 1100
Page2Y := 850

; ============================================================
; ЗАДЕРЖКИ
; ============================================================

; Небольшая задержка после движения мыши
MoveDelay := 100

; После клика
ClickDelay := 300

; После обновления списка
RefreshDelay := 700

; После ввода текста
InputDelay := 300

; После прокрутки
ScrollDelay := 400

; После перехода на другую страницу
PageDelay := 700

; После клика по найденному товару
SelectDelay := 400

; ============================================================
; СЛУЖЕБНЫЕ ПЕРЕМЕННЫЕ
; ============================================================

ScriptRunning := false
CurrentPage := 1
CurrentScrollStep := 0

; ============================================================
; F3 — ЗАПУСК
; ============================================================

F3::
    if (ScriptRunning)
        return

    ScriptRunning := true

    CurrentPage := 1
    CurrentScrollStep := 0

    ToolTip, СКРИПТ ЗАПУЩЕН
    Sleep, 700
    ToolTip

    SetTimer, MainLoop, -10
return

; ============================================================
; F6 — ОСТАНОВКА
; ============================================================

F6::
    ScriptRunning := false

    ToolTip, СКРИПТ ОСТАНОВЛЕН
    Sleep, 700
    ToolTip
return

; ============================================================
; ГЛАВНЫЙ ЦИКЛ
; ============================================================

MainLoop:

    if (!ScriptRunning)
        return

    ; ========================================================
    ; ПУНКТ 1
    ; Наводимся на поле, кликаем и вводим текст
    ; ========================================================

    MouseMove, %InputX%, %InputY%, 0
    Sleep, %MoveDelay%

    Click
    Sleep, %ClickDelay%

    ; Очищаем старый текст
    Send, ^a
    Sleep, 100

    ; Вводим нужный текст
    SendInput, %SearchText%

    Sleep, %InputDelay%

    ; ========================================================
    ; ПУНКТ 2
    ; Обновляем список
    ; ========================================================

    MouseMove, %RefreshX%, %RefreshY%, 0
    Sleep, %MoveDelay%

    Click
    Sleep, %RefreshDelay%

    ; Начинаем с верхней позиции
    CurrentScrollStep := 0

    ; ========================================================
    ; ПОИСК НА ПЕРВОЙ СТРАНИЦЕ
    ; ========================================================

SearchPage1:

    if (!ScriptRunning)
        return

    ; --------------------------------------------------------
    ; Делаем OCR выбранной области
    ; --------------------------------------------------------

    OCRResult := OCR_GetPrices(
        OCRX,
        OCRY,
        OCRWidth,
        OCRHeight,
        TesseractPath,
        OCRLanguage,
        MaxPrice
    )

    ; --------------------------------------------------------
    ; Если подходящая цена найдена
    ; --------------------------------------------------------

    if (OCRResult != "")
    {
        ; OCRResult возвращает:
        ; X|Y|Price

        StringSplit, Found, OCRResult, |

        FoundX := Found1
        FoundY := Found2
        FoundPrice := Found3

        ToolTip, НАЙДЕНА ЦЕНА: %FoundPrice%
        Sleep, 500
        ToolTip

        ; --------------------------------------------
        ; Кликаем по найденной цене
        ; --------------------------------------------

        MouseMove, %FoundX%, %FoundY%, 0
        Sleep, %MoveDelay%

        Click
        Sleep, %SelectDelay%

        ; --------------------------------------------
        ; Нажимаем кнопку подтверждения
        ; --------------------------------------------

        MouseMove, %ConfirmX%, %ConfirmY%, 0
        Sleep, %MoveDelay%

        Click
        Sleep, %RefreshDelay%

        ; --------------------------------------------
        ; После выбора начинаем сначала.
        ;
        ; То есть возвращаемся к пункту 1,
        ; затем пункт 2.
        ; --------------------------------------------

        CurrentPage := 1
        CurrentScrollStep := 0

        SetTimer, MainLoop, -10
        return
    }

    ; ========================================================
    ; ПОДХОДЯЩЕЙ ЦЕНЫ НЕТ
    ; ========================================================

    CurrentScrollStep++

    ; --------------------------------------------------------
    ; Если ещё не дошли до конца страницы —
    ; двигаем ползунок вниз
    ; --------------------------------------------------------

    if (CurrentScrollStep <= MaxScrollSteps)
    {
        ScrollListDown()

        Sleep, %ScrollDelay%

        Goto, SearchPage1
    }

    ; ========================================================
    ; ПУНКТ 5
    ; Дошли до конца первой страницы
    ; Переходим на вторую
    ; ========================================================

    if (CurrentPage = 1)
    {
        CurrentPage := 2
        CurrentScrollStep := 0

        ; Переход на вторую страницу
        MouseMove, %Page2X%, %Page2Y%, 0
        Sleep, %MoveDelay%

        Click
        Sleep, %PageDelay%

        ; Начинаем искать на второй странице
        Goto, SearchPage2
    }

    ; ========================================================
    ; Если мы уже были на второй странице и ничего не нашли
    ; ========================================================

    if (CurrentPage = 2)
    {
        ; Возвращаемся к пункту 2.
        ;
        ; То есть обновляем список и начинаем новый поиск.

        CurrentPage := 1
        CurrentScrollStep := 0

        MouseMove, %RefreshX%, %RefreshY%, 0
        Sleep, %MoveDelay%

        Click
        Sleep, %RefreshDelay%

        Goto, SearchPage1
    }

return

; ============================================================
; ПОИСК НА ВТОРОЙ СТРАНИЦЕ
; ============================================================

SearchPage2:

    if (!ScriptRunning)
        return

    ; --------------------------------------------------------
    ; OCR
    ; --------------------------------------------------------

    OCRResult := OCR_GetPrices(
        OCRX,
        OCRY,
        OCRWidth,
        OCRHeight,
        TesseractPath,
        OCRLanguage,
        MaxPrice
    )

    ; --------------------------------------------------------
    ; Если нашли подходящую цену
    ; --------------------------------------------------------

    if (OCRResult != "")
    {
        StringSplit, Found, OCRResult, |

        FoundX := Found1
        FoundY := Found2
        FoundPrice := Found3

        ToolTip, НАЙДЕНА ЦЕНА: %FoundPrice%
        Sleep, 500
        ToolTip

        ; Кликаем по найденному варианту
        MouseMove, %FoundX%, %FoundY%, 0
        Sleep, %MoveDelay%

        Click
        Sleep, %SelectDelay%

        ; Подтверждаем
        MouseMove, %ConfirmX%, %ConfirmY%, 0
        Sleep, %MoveDelay%

        Click
        Sleep, %RefreshDelay%

        ; Начинаем новый цикл
        CurrentPage := 1
        CurrentScrollStep := 0

        SetTimer, MainLoop, -10
        return
    }

    ; ========================================================
    ; Ничего не нашли — опускаем список
    ; ========================================================

    CurrentScrollStep++

    if (CurrentScrollStep <= MaxScrollSteps)
    {
        ScrollListDown()

        Sleep, %ScrollDelay%

        Goto, SearchPage2
    }

    ; ========================================================
    ; На второй странице тоже дошли до конца
    ; ========================================================

    CurrentPage := 1
    CurrentScrollStep := 0

    ; Возвращаемся к пункту 2 —
    ; обновляем список

    MouseMove, %RefreshX%, %RefreshY%, 0
    Sleep, %MoveDelay%

    Click
    Sleep, %RefreshDelay%

    Goto, SearchPage1

return

; ============================================================
; ФУНКЦИЯ ПРОКРУТКИ
; ============================================================

ScrollListDown()
{
    global ScrollBarX
    global ScrollBarY
    global ScrollPixels
    global MoveDelay

    ; Наводимся на ползунок
    MouseMove, %ScrollBarX%, %ScrollBarY%, 0
    Sleep, %MoveDelay%

    ; Зажимаем левую кнопку
    Click, Down

    Sleep, 100

    ; Опускаем ползунок вниз
    MouseMove, %ScrollBarX%, % ScrollBarY + ScrollPixels, 300

    ; Отпускаем кнопку
    Click, Up
}

; ============================================================
; OCR
; ============================================================
;
; Делает скриншот заданной области экрана,
; передаёт его Tesseract,
; получает TSV с координатами распознанного текста,
; ищет цену меньше MaxPrice.
;
; Возвращает:
;
; X|Y|PRICE
;
; либо пустую строку, если подходящей цены нет.
;
; ============================================================

OCR_GetPrices(X, Y, Width, Height, TesseractPath, Language, MaxPrice)
{
    ; --------------------------------------------------------
    ; Создаём временные файлы
    ; --------------------------------------------------------

    TempImage := A_Temp "\ahk_ocr_screen.png"
    TempBase := A_Temp "\ahk_ocr_result"

    ; --------------------------------------------------------
    ; Скриншот
    ; --------------------------------------------------------

    pToken := Gdip_Startup()

    if (!pToken)
        return ""

    pBitmap := Gdip_BitmapFromScreen(X "|" Y "|" Width "|" Height)

    if (!pBitmap)
    {
        Gdip_Shutdown(pToken)
        return ""
    }

    ; Сохраняем PNG
    Gdip_SaveBitmapToFile(pBitmap, TempImage)

    ; Освобождаем память
    Gdip_DisposeImage(pBitmap)
    Gdip_Shutdown(pToken)

    ; --------------------------------------------------------
    ; Запускаем Tesseract
    ;
    ; TSV нужен для получения координат текста.
    ; --------------------------------------------------------

    RunWait, "%TesseractPath%" "%TempImage%" "%TempBase%" -l %Language% --psm 6 tsv,, Hide

    TSVFile := TempBase ".tsv"

    ; Если файл не создан — OCR не сработал
    if (!FileExist(TSVFile))
        return ""

    ; --------------------------------------------------------
    ; Читаем результат
    ; --------------------------------------------------------

    FileRead, TSV, %TSVFile%

    if (ErrorLevel)
        return ""

    ; --------------------------------------------------------
    ; Обрабатываем строки TSV
    ; --------------------------------------------------------

    Loop, Parse, TSV, `n, `r
    {
        Line := A_LoopField

        ; Пропускаем заголовок
        if (InStr(Line, "level page_num"))
            continue

        ; Разбиваем строку TSV
        StringSplit, Col, Line, %A_Tab%

        ; В TSV Tesseract:
        ;
        ; Col6 = left
        ; Col7 = top
        ; Col8 = width
        ; Col9 = height
        ; Col12 = распознанный текст

        if (Col12 = "")
            continue

        Text := Trim(Col12)

        ; ----------------------------------------------------
        ; Чистим возможные символы цены
        ; ----------------------------------------------------

        CleanPrice := Text

        CleanPrice := StrReplace(CleanPrice, "₽", "")
        CleanPrice := StrReplace(CleanPrice, "$", "")
        CleanPrice := StrReplace(CleanPrice, "€", "")
        CleanPrice := StrReplace(CleanPrice, " ", "")
        CleanPrice := StrReplace(CleanPrice, ".", "")
        CleanPrice := StrReplace(CleanPrice, ",", "")

        ; Убираем всё, кроме цифр
        CleanPrice := RegExReplace(CleanPrice, "[^\d]")

        ; Если после очистки ничего нет
        if (CleanPrice = "")
            continue

        Price := CleanPrice + 0

        ; ----------------------------------------------------
        ; Проверяем цену
        ; ----------------------------------------------------

        if (Price > 0 && Price < MaxPrice)
        {
            ; Координаты относительно экрана
            FoundX := X + Col6 + (Col8 / 2)
            FoundY := Y + Col7 + (Col9 / 2)

            return FoundX "|" FoundY "|" Price
        }
    }

    return ""
}