Перехват скачиваемых скриптом данных другим скриптом

KtotoTama

Новичок
Автор темы
7
0
Версия MoonLoader
.025-beta
Приветствую. Можно ли как-то перехватить содержимое скачиваемого скриптом файла с помощь другого скрипта?

Lua:
asyncHttpRequest("GET", https://example.com/file.json, function(myvar)
        myvar.text
    end
end)

Вот содержимое myvar.text хотелось бы перехватить, не скачивая файл повторно (!). Как временный файл скрипт ничего не сохраняет.
 

baligins

Участник
40
12
Ответ от ChatGPT:
capture_async_http.lua:
-- capture_async_http.lua
-- Положи этот файл в папку скриптов и загрузи раньше остальных, чтобы подмена сработала.

-- Защита от повторной подмены
if rawget(_G, "__asyncHttpRequest_wrapped") then
    print("asyncHttpRequest уже обёрнут.")
    return
end

-- Сохранить оригинал
local orig_asyncHttpRequest = rawget(_G, "asyncHttpRequest")

if not orig_asyncHttpRequest then
    -- Если в текущей среде asyncHttpRequest нет — предупреждаем, но не падаем.
    print("ВНИМАНИЕ: asyncHttpRequest не найден в глобальной области. Перехват невозможен.")
    -- Поместим маркер, чтобы не пытаться снова
    _G["__asyncHttpRequest_wrapped"] = true
    -- Инициализируем пустой контейнер для совместимости API
    _G.__captured_http = _G.__captured_http or { by_url = {}, by_id = {} }
    -- публичные функции
    _G.get_captured = function(k)
        return _G.__captured_http.by_url[k] or _G.__captured_http.by_id[k]
    end
    _G.pop_captured = function(k)
        local v = _G.get_captured(k)
        if _G.__captured_http.by_url[k] then _G.__captured_http.by_url[k] = nil end
        if _G.__captured_http.by_id[k] then _G.__captured_http.by_id[k] = nil end
        return v
    end
    _G.clear_captured = function() _G.__captured_http.by_url = {}; _G.__captured_http.by_id = {} end
    return
end

-- инициализация контейнера хранения
_G.__captured_http = _G.__captured_http or { by_url = {}, by_id = {} }

-- Вспомогательная функция для сохранения (с ограничением размера списка)
local function save_response(key_table, key, response)
    if not key or key == "" then return end
    key_table[key] = key_table[key] or {}
    -- сохраняем объект (можно копировать поля, но сохраним ссылку)
    table.insert(key_table[key], response)
    -- ограничение: сохранять не более N записей на ключ
    local MAX_PER_KEY = 10
    if #key_table[key] > MAX_PER_KEY then
        -- удалить самый старый
        table.remove(key_table[key], 1)
    end
end

-- Создаём обёртку
local function make_wrapped(orig)
    return function(method, url, callback, headers, ...)
        -- Подготовка: допустим, аргументы совпадают с описанными (method, url, callback, headers)
        -- Создадим обёрнутый callback, который сохраняет response, потом вызывает оригинальный callback
        local function wrapped_callback(...)
            -- Попробуем получить response (в большинстве реализаций это один аргумент)
            local response = select(1, ...)
            -- Сохраняем копию: лучше хранить минимально необходимые поля
            local snapshot = nil
            if type(response) == "table" then
                snapshot = {
                    text = response.text,
                    status = response.status,
                    headers = response.headers,
                    url = response.url or url
                }
            else
                snapshot = { raw = response, url = url }
            end

            -- Сохраняем по URL
            pcall(save_response, _G.__captured_http.by_url, url, snapshot)

            -- Если response содержит id (или оригинал возвращает id), попробуем сохранить по id ниже.
            -- Но здесь id часто возвращается синхронно как return-value оригинальной функции.
            -- Дополнительно: если response.id существует, сохраним по нему.
            if type(response) == "table" and response.id ~= nil then
                pcall(save_response, _G.__captured_http.by_id, tostring(response.id), snapshot)
            end

            -- Вызвать исходный callback (если он был)
            if type(callback) == "function" then
                local ok, err = pcall(callback, ...)
                if not ok then
                    -- не ломаем цепочку — логируем ошибку, но продолжаем
                    print("Ошибка в оригинальном callback-е asyncHttpRequest:", tostring(err))
                end
            end
        end

        -- Вызов оригинала: может вернуть request-id или nil
        local ok, ret = pcall(orig, method, url, wrapped_callback, headers, ...)
        if not ok then
            print("Ошибка при вызове оригинального asyncHttpRequest:", tostring(ret))
            return nil
        end

        -- Если оригинал возвращает id (например строку/число) — сохраним привязку id -> url
        if ret ~= nil then
            local id_str = tostring(ret)
            -- пометим, что запрос с таким id ожидается; сохраним пустой список, он заполнится в wrapped_callback, если response содержит id
            _G.__captured_http.by_id[id_str] = _G.__captured_http.by_id[id_str] or {}
            -- сохраняем связь id->url (как дополнительный элемент)
            table.insert(_G.__captured_http.by_id[id_str], { meta = { url = url, returned_id = ret } })
        end

        return ret
    end
end

-- Подменяем только один раз
_G["__asyncHttpRequest_wrapped"] = true
_G["__orig_asyncHttpRequest"] = orig_asyncHttpRequest

-- Выполняем замену
asyncHttpRequest = make_wrapped(orig_asyncHttpRequest)

-- Публичный API для получения / очистки перехваченных данных
_G.get_captured = function(key)
    if key == nil then
        return nil
    end
    local by_url = _G.__captured_http.by_url[key]
    if by_url then return by_url end
    local by_id = _G.__captured_http.by_id[key]
    if by_id then return by_id end
    return nil
end

_G.pop_captured = function(key)
    if key == nil then return nil end
    local v = _G.get_captured(key)
    if v then
        if _G.__captured_http.by_url[key] then _G.__captured_http.by_url[key] = nil end
        if _G.__captured_http.by_id[key] then _G.__captured_http.by_id[key] = nil end
    end
    return v
end

_G.clear_captured = function()
    _G.__captured_http.by_url = {}
    _G.__captured_http.by_id = {}
end

-- Утилита печати перехваченных (для отладки)
_G.print_captured = function(key)
    local v = _G.get_captured(key)
    if not v then
        print("Нет захваченных данных для:", tostring(key))
        return
    end
    for i, item in ipairs(v) do
        if type(item) == "table" then
            print(string.format("[%d] url=%s status=%s text_len=%d", i, tostring(item.url), tostring(item.status), type(item.text)=="string" and #item.text or 0))
        else
            print(string.format("[%d] %s", i, tostring(item)))
        end
    end
end

print("asyncHttpRequest успешно обёрнут — ответы будут сохраняться в _G.__captured_http (by_url / by_id).")

Пример использования в другом скрипте (после того, как запрос уже выполнился):
lua:
-- в другом скрипте / позже
local res_list = _G.get_captured("https://example.com/file.json")
if res_list then
    -- берем самый последний
    local last = res_list[#res_list]
    if last and last.text then
        -- обработка без повторной загрузки
        print("Перехвачено тело:", last.text)
    else
        print("Перехваченные объекты не содержат поле text.")
    end
else
    print("Ничего не найдено по этому URL.")
end

Важные моменты (кратко):

  1. Этот код работает, только если оба скрипта исполняются в одном Lua-процессе / одном глобальном окружении.
  2. Скрипт-перехватчик должен быть загружен раньше скриптов, которые вызывают asyncHttpRequest. Если какой-то скрипт заранее сохранил ссылку на оригинальную функцию, перехват не сработает.
  3. Если хочешь — могу добавить: автоматическое логирование в файл, фильтрацию по статусу ответа, или хранение полного объекта response (с осторожностью насчёт памяти).

Ответ от ChatGPT:
capture_async_http.lua:
-- capture_async_http.lua
-- Положи этот файл в папку скриптов и загрузи раньше остальных, чтобы подмена сработала.

-- Защита от повторной подмены
if rawget(_G, "__asyncHttpRequest_wrapped") then
    print("asyncHttpRequest уже обёрнут.")
    return
end

-- Сохранить оригинал
local orig_asyncHttpRequest = rawget(_G, "asyncHttpRequest")

if not orig_asyncHttpRequest then
    -- Если в текущей среде asyncHttpRequest нет — предупреждаем, но не падаем.
    print("ВНИМАНИЕ: asyncHttpRequest не найден в глобальной области. Перехват невозможен.")
    -- Поместим маркер, чтобы не пытаться снова
    _G["__asyncHttpRequest_wrapped"] = true
    -- Инициализируем пустой контейнер для совместимости API
    _G.__captured_http = _G.__captured_http or { by_url = {}, by_id = {} }
    -- публичные функции
    _G.get_captured = function(k)
        return _G.__captured_http.by_url[k] or _G.__captured_http.by_id[k]
    end
    _G.pop_captured = function(k)
        local v = _G.get_captured(k)
        if _G.__captured_http.by_url[k] then _G.__captured_http.by_url[k] = nil end
        if _G.__captured_http.by_id[k] then _G.__captured_http.by_id[k] = nil end
        return v
    end
    _G.clear_captured = function() _G.__captured_http.by_url = {}; _G.__captured_http.by_id = {} end
    return
end

-- инициализация контейнера хранения
_G.__captured_http = _G.__captured_http or { by_url = {}, by_id = {} }

-- Вспомогательная функция для сохранения (с ограничением размера списка)
local function save_response(key_table, key, response)
    if not key or key == "" then return end
    key_table[key] = key_table[key] or {}
    -- сохраняем объект (можно копировать поля, но сохраним ссылку)
    table.insert(key_table[key], response)
    -- ограничение: сохранять не более N записей на ключ
    local MAX_PER_KEY = 10
    if #key_table[key] > MAX_PER_KEY then
        -- удалить самый старый
        table.remove(key_table[key], 1)
    end
end

-- Создаём обёртку
local function make_wrapped(orig)
    return function(method, url, callback, headers, ...)
        -- Подготовка: допустим, аргументы совпадают с описанными (method, url, callback, headers)
        -- Создадим обёрнутый callback, который сохраняет response, потом вызывает оригинальный callback
        local function wrapped_callback(...)
            -- Попробуем получить response (в большинстве реализаций это один аргумент)
            local response = select(1, ...)
            -- Сохраняем копию: лучше хранить минимально необходимые поля
            local snapshot = nil
            if type(response) == "table" then
                snapshot = {
                    text = response.text,
                    status = response.status,
                    headers = response.headers,
                    url = response.url or url
                }
            else
                snapshot = { raw = response, url = url }
            end

            -- Сохраняем по URL
            pcall(save_response, _G.__captured_http.by_url, url, snapshot)

            -- Если response содержит id (или оригинал возвращает id), попробуем сохранить по id ниже.
            -- Но здесь id часто возвращается синхронно как return-value оригинальной функции.
            -- Дополнительно: если response.id существует, сохраним по нему.
            if type(response) == "table" and response.id ~= nil then
                pcall(save_response, _G.__captured_http.by_id, tostring(response.id), snapshot)
            end

            -- Вызвать исходный callback (если он был)
            if type(callback) == "function" then
                local ok, err = pcall(callback, ...)
                if not ok then
                    -- не ломаем цепочку — логируем ошибку, но продолжаем
                    print("Ошибка в оригинальном callback-е asyncHttpRequest:", tostring(err))
                end
            end
        end

        -- Вызов оригинала: может вернуть request-id или nil
        local ok, ret = pcall(orig, method, url, wrapped_callback, headers, ...)
        if not ok then
            print("Ошибка при вызове оригинального asyncHttpRequest:", tostring(ret))
            return nil
        end

        -- Если оригинал возвращает id (например строку/число) — сохраним привязку id -> url
        if ret ~= nil then
            local id_str = tostring(ret)
            -- пометим, что запрос с таким id ожидается; сохраним пустой список, он заполнится в wrapped_callback, если response содержит id
            _G.__captured_http.by_id[id_str] = _G.__captured_http.by_id[id_str] or {}
            -- сохраняем связь id->url (как дополнительный элемент)
            table.insert(_G.__captured_http.by_id[id_str], { meta = { url = url, returned_id = ret } })
        end

        return ret
    end
end

-- Подменяем только один раз
_G["__asyncHttpRequest_wrapped"] = true
_G["__orig_asyncHttpRequest"] = orig_asyncHttpRequest

-- Выполняем замену
asyncHttpRequest = make_wrapped(orig_asyncHttpRequest)

-- Публичный API для получения / очистки перехваченных данных
_G.get_captured = function(key)
    if key == nil then
        return nil
    end
    local by_url = _G.__captured_http.by_url[key]
    if by_url then return by_url end
    local by_id = _G.__captured_http.by_id[key]
    if by_id then return by_id end
    return nil
end

_G.pop_captured = function(key)
    if key == nil then return nil end
    local v = _G.get_captured(key)
    if v then
        if _G.__captured_http.by_url[key] then _G.__captured_http.by_url[key] = nil end
        if _G.__captured_http.by_id[key] then _G.__captured_http.by_id[key] = nil end
    end
    return v
end

_G.clear_captured = function()
    _G.__captured_http.by_url = {}
    _G.__captured_http.by_id = {}
end

-- Утилита печати перехваченных (для отладки)
_G.print_captured = function(key)
    local v = _G.get_captured(key)
    if not v then
        print("Нет захваченных данных для:", tostring(key))
        return
    end
    for i, item in ipairs(v) do
        if type(item) == "table" then
            print(string.format("[%d] url=%s status=%s text_len=%d", i, tostring(item.url), tostring(item.status), type(item.text)=="string" and #item.text or 0))
        else
            print(string.format("[%d] %s", i, tostring(item)))
        end
    end
end

print("asyncHttpRequest успешно обёрнут — ответы будут сохраняться в _G.__captured_http (by_url / by_id).")

Пример использования в другом скрипте (после того, как запрос уже выполнился):
lua:
-- в другом скрипте / позже
local res_list = _G.get_captured("https://example.com/file.json")
if res_list then
    -- берем самый последний
    local last = res_list[#res_list]
    if last and last.text then
        -- обработка без повторной загрузки
        print("Перехвачено тело:", last.text)
    else
        print("Перехваченные объекты не содержат поле text.")
    end
else
    print("Ничего не найдено по этому URL.")
end

Важные моменты (кратко):

  1. Этот код работает, только если оба скрипта исполняются в одном Lua-процессе / одном глобальном окружении.
  2. Скрипт-перехватчик должен быть загружен раньше скриптов, которые вызывают asyncHttpRequest. Если какой-то скрипт заранее сохранил ссылку на оригинальную функцию, перехват не сработает.
  3. Если хочешь — могу добавить: автоматическое логирование в файл, фильтрацию по статусу ответа, или хранение полного объекта response (с осторожностью насчёт памяти).
развивай