Часть 2. Разбор файла Service.js (QuillBot Translate)
Этот скрипт отвечает за инициацию процесса. Вместо прямого HTTP-запроса он запускает QuillBotBridge.ps1 через ActiveX. Данные передаются через временные переменные среды (Environment), чтобы избежать ограничений на длину командной строки Windows (где лимит равен 8191 символу).
Строки перевода кодируются в Base64 для безопасной передачи любых спецсимволов.
code JavaScript
// Глобальные конфигурационные переменные адресов сервиса QuillBot
var QUILLBOT_TRANSLATE_HOST = "https://quillbot.com";
var QUILLBOT_TRANSLATE_SITE_URL = QUILLBOT_TRANSLATE_HOST + "/translate";
var QUILLBOT_TRANSLATE_HOME_PATH = "/translate";
var QUILLBOT_TRANSLATE_MAX_SOURCE_LEN = 5E3; // Лимит QuillBot в 5000 символов
// Глобальные переменные состояния, куда сохраняются результаты выполнения внешнего моста
var QUILLBOT_BRIDGE_TRANSLATION = "";
var QUILLBOT_BRIDGE_SOURCE_LANGUAGE = UNKNOWN_LANGUAGE;
var QUILLBOT_BRIDGE_TARGET_LANGUAGE = ENGLISH_LANGUAGE;
var QUILLBOT_BRIDGE_ERROR = "";
/** Возвращает описание сервиса для QTranslate */
function serviceHeader() {
return new ServiceHeader(
113,
"QuillBot Translate",
"QuillBot web translator via portable WebSocket bridge." + Const.NL2 + quillbotSiteUrl(),
Capability.TRANSLATE | Capability.DETECT_LANGUAGE
);
}
function serviceHost() {
return quillbotHost();
}
/** Генерирует веб-ссылку на переводчик для открытия в браузере при клике на иконку сервиса */
function serviceLink(a, b, c) {
var d = quillbotSiteUrl() + "?sl=" + encodeGetParam(quillbotQuerySourceCode(b)) + "&tl=" + encodeGetParam(quillbotQueryTargetCode(c || ENGLISH_LANGUAGE)) + "&tone=auto";
a && (d += "&text=" + encodeGetParam(trimString(prepareSource(a))));
return d;
}
// Таблица поддерживаемых языков. Индекс соответствует внутреннему ID QTranslate, значение - код языка для QuillBot
SupportedLanguages = [-1, "auto", "af", -1, -1, "ar", -1, -1, -1, -1, "ca", "zh-CN", -1, "hr", "cs", "da", "nl", "en", -1, "fi", "tl", "fr", -1, "de", "el", -1, "iw", "hi", "hu", -1, "id", "it", -1, "ja", -1, "ko", -1, -1, -1, "ms", -1, "no", "fa", "pl", "pt", "ro", "ru", "sr", -1, "sl", "es", -1, "sv", "th", "tr", "uk", "ur", "vi", -1, -1, -1, -1, -1, -1, -1, -1, -1, "te", -1, -1, "kn", "ta", "mr", "bn", -1];
/**
* Точка входа QTranslate для перевода текста.
* Она не делает запрос в сеть сама, а вызывает мост.
*/
function serviceTranslateRequest(a, b, c) {
var d = quillbotSourceText(a), // Очистка и обрезка текста
// Вызов внешней функции запуска PowerShell скрипта:
e = parseJsonSafe(quillbotBridgeExecute("translate", d, quillbotRequestLanguageCode(b, "auto"), quillbotRequestLanguageCode(c || ENGLISH_LANGUAGE, "en-US")));
// Запись результатов парсинга в глобальные переменные состояния
quillbotApplyBridgeState(e, b, c || ENGLISH_LANGUAGE);
// Возвращаем фейковый локальный запрос. Он нужен QTranslate только для того,
// чтобы сработал таймер и вызвался метод 'serviceTranslateBridgeResponse'
return new RequestData(HttpMethod.GET, QUILLBOT_TRANSLATE_HOME_PATH, null, quillbotPageHeaders(), null, "serviceTranslateBridgeResponse");
}
/** Callback для получения результата перевода */
function serviceTranslateBridgeResponse(a, b, c, d) {
var e = QUILLBOT_BRIDGE_TRANSLATION,
f = QUILLBOT_BRIDGE_ERROR,
g = QUILLBOT_BRIDGE_SOURCE_LANGUAGE,
h = QUILLBOT_BRIDGE_TARGET_LANGUAGE;
resetQuillbotBridgeState(); // Очистка памяти
if (f) return new ResponseData(f, g, h); // Если была ошибка, отдаем её текст
return e ? new ResponseData(e, g, h) : new ResponseData("[E] QuillBot Translate: Empty translation", g, h);
}
/** Точка входа определения языка */
function serviceDetectLanguageRequest(a) {
var b = parseJsonSafe(quillbotBridgeExecute("detect", quillbotSourceText(a), "auto", "en-US"));
resetQuillbotBridgeState();
if (!b) {
QUILLBOT_BRIDGE_ERROR = "[E] QuillBot Translate: Invalid bridge response";
} else if (b.error) {
QUILLBOT_BRIDGE_ERROR = quillbotFormatBridgeError(b.error);
} else if (b.sourceLanguage) {
QUILLBOT_BRIDGE_SOURCE_LANGUAGE = quillbotLanguageFromCode(String(b.sourceLanguage));
}
return new RequestData(HttpMethod.GET, QUILLBOT_TRANSLATE_HOME_PATH, null, quillbotPageHeaders(), null, "serviceDetectBridgeResponse");
}
/** Callback для получения определенного языка */
function serviceDetectBridgeResponse(a) {
var b = QUILLBOT_BRIDGE_SOURCE_LANGUAGE,
c = QUILLBOT_BRIDGE_ERROR;
resetQuillbotBridgeState();
return c ? UNKNOWN_LANGUAGE : b;
}
/** Применение JSON-данных из моста в глобальные переменные JScript */
function quillbotApplyBridgeState(a, b, c) {
resetQuillbotBridgeState();
if (!a) {
QUILLBOT_BRIDGE_ERROR = "[E] QuillBot Translate: Invalid bridge response";
return;
}
if (a.error) {
QUILLBOT_BRIDGE_ERROR = quillbotFormatBridgeError(a.error);
return;
}
QUILLBOT_BRIDGE_TRANSLATION = trimString(String(a.translation || ""));
QUILLBOT_BRIDGE_SOURCE_LANGUAGE = quillbotLanguageFromCode(a.sourceLanguage || b);
QUILLBOT_BRIDGE_TARGET_LANGUAGE = quillbotLanguageFromCode(a.targetLanguage || c);
}
/**
* ГЛАВНАЯ ФУНКЦИЯ ЗАПУСКА МОСТА POWERSHELL.
* Она координирует создание файлов, вызов процесса и чтение ответов.
*/
function quillbotBridgeExecute(a, b, c, d) {
var e = new ActiveXObject("WScript.Shell"), // Работа с процессами ОС
f = new ActiveXObject("Scripting.FileSystemObject"), // Работа с файлами
g = e.Environment("Process"), // Переменные окружения процесса
h = quillbotResolveBridgePath(f, e), // Поиск пути к QuillBotBridge.ps1
i = quillbotResolveBridgeTempPath(f, e, h), // Поиск пути к папке _temp
j = quillbotBuildBridgeFilePath(f, i, ".json"), // Путь к будущему файлу ответа
k = quillbotBuildBridgeFilePath(f, i, ".err"), // Путь к файлу ошибок
l = "",
m = "",
n = 0,
o = {};
if (!h) return stringifyJSON({ error: "Bridge script not found" });
// Предварительная очистка мусорных файлов
quillbotCleanupBridgeFiles(f, j, k);
// Передача параметров через переменные окружения текущего процесса
// Дочерний процесс PowerShell унаследует их автоматически
g.Item("QTB_QUILLBOT_MODE") = a || "translate";
g.Item("QTB_QUILLBOT_TEXT_B64") = quillbotBytesToBase64(quillbotStringToBytes(b || "")); // Текст в Base64
g.Item("QTB_QUILLBOT_SOURCE") = c || "auto";
g.Item("QTB_QUILLBOT_TARGET") = d || "en-US";
g.Item("QTB_QUILLBOT_RESULT_FILE") = j;
g.Item("QTB_QUILLBOT_TIMEOUT_MS") = String(quillbotBridgeTimeoutMs());
try {
// Попытка 1: Прямой запуск PowerShell (без вызова CMD)
o = quillbotRunBridgeCommand(e, f, quillbotBuildDirectBridgeCommand(e, h), j, k);
n = o.exitCode;
l = o.resultText;
m = o.errorText;
// Попытка 2 (резервная): Если первый запуск не вернул данных, пробуем запуск через интерпретатор CMD (%ComSpec%)
if (!l && !m) {
o = quillbotRunBridgeCommand(e, f, quillbotBuildShellBridgeCommand(h, k), j, k);
n = o.exitCode;
l = o.resultText;
m = o.errorText;
}
} catch (p) {
m = trimString(String(p && p.message ? p.message : p || "Bridge execution failed"));
}
// Очистка за собой
quillbotCleanupBridgeFiles(f, j, k);
quillbotClearBridgeEnvironment(g);
l = trimString(l);
m = trimString(m);
// Если есть текст ответа - отдаем его, иначе возвращаем ошибку в JSON
return l || stringifyJSON({ error: m || "Bridge exit code " + n });
}
/** Запуск команды Windows и чтение результирующего файла */
function quillbotRunBridgeCommand(a, b, c, d, e) {
var f = 0;
quillbotCleanupBridgeFiles(b, d, e);
// Параметр '0' скрывает окно консоли, 'true' заставляет JScript ждать завершения команды
f = a.Run(c, 0, true);
return {
exitCode: f,
resultText: quillbotReadTextFile(b, d),
errorText: quillbotReadTextFile(b, e)
};
}
/** Строит команду прямого вызова PowerShell с обходом политики выполнения */
function quillbotBuildDirectBridgeCommand(a, b) {
return '"' + quillbotResolvePowerShellPath(a) + '" -NoLogo -NoProfile -ExecutionPolicy Bypass -File "' + b + '"';
}
/** Строит резервную команду вызова через cmd.exe с перенаправлением ошибок в файл */
function quillbotBuildShellBridgeCommand(a, b) {
return '%ComSpec% /d /c ""' + quillbotResolvePowerShellPath(new ActiveXObject("WScript.Shell")) + '" -NoLogo -NoProfile -ExecutionPolicy Bypass -File "' + a + '" 2> "' + b + '""';
}
/** Находит абсолютный путь к powershell.exe */
function quillbotResolvePowerShellPath(a) {
var b = a.ExpandEnvironmentStrings("%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe");
return trimString(b || "powershell.exe");
}
/** Сканирует папки для обнаружения QuillBotBridge.ps1 */
function quillbotResolveBridgePath(a, b) {
var c = quillbotCandidateServiceRoots(a, b), d, e;
for (d = 0; d < c.length; d++) {
e = a.BuildPath(c[d], "QuillBotBridge.ps1");
if (a.FileExists(e)) return quillbotAbsolutePath(a, e);
}
return a.FileExists("QuillBotBridge.ps1") ? quillbotAbsolutePath(a, "QuillBotBridge.ps1") : "";
}
/** Поиск или создание временной директории _temp для обмена JSON-файлами */
function quillbotResolveBridgeTempPath(a, b, c) {
var d = [], e, f;
c && d.push(a.BuildPath(a.GetParentFolderName(c), "_temp"));
e = quillbotCandidateServiceRoots(a, b);
for (f = 0; f < e.length; f++) d.push(a.BuildPath(e[f], "_temp"));
d.push("_temp");
for (f = 0; f < d.length; f++) {
try {
d[f] = quillbotAbsolutePath(a, d[f]);
a.FolderExists(d[f]) || a.CreateFolder(d[f]);
if (a.FolderExists(d[f])) return d[f];
} catch (g) {}
}
return quillbotAbsolutePath(a, ".");
}
/** Собирает список возможных директорий, где может лежать скрипт сервиса */
function quillbotCandidateServiceRoots(a, b) {
var c = [], d = "";
try {
d = b.CurrentDirectory || "";
} catch (f) {}
d && (
c.push(a.BuildPath(d, "Services\\QuillBot Translate")),
c.push(a.BuildPath(d, ".\\Services\\QuillBot Translate")),
c.push(a.BuildPath(d, "QuillBot Translate"))
);
c.push("Services\\QuillBot Translate");
c.push(".\\Services\\QuillBot Translate");
c.push("QuillBot Translate");
for (var e = 0; e < c.length; e++) c[e] = quillbotAbsolutePath(a, c[e]);
return quillbotUniquePaths(c);
}
/** Удаление дублирующихся путей */
function quillbotUniquePaths(a) {
var b = [], c = {}, d, e;
for (d = 0; d < a.length; d++) {
e = String(a[d] || "").toLowerCase();
e && !c[e] && (c[e] = true, b.push(a[d]));
}
return b;
}
/** Генерация имени уникального временного файла */
function quillbotBuildBridgeFilePath(a, b, c) {
return a.BuildPath(b, "qtranslate_quillbot_" + quillbotUuid() + c);
}
/**
* Чтение текстового файла в кодировке UTF-8.
* Пытается использовать объект ADODB.Stream, так как встроенный Scripting.FileSystemObject
* не умеет читать стандартный UTF-8 без BOM (он читает его как ANSI).
*/
function quillbotReadTextFile(a, b) {
var c, d;
try {
if (!a.FileExists(b)) return "";
c = new ActiveXObject("ADODB.Stream");
c.Type = 2; // Текстовый режим
c.Charset = "utf-8";
c.Open();
c.LoadFromFile(b);
d = c.ReadText();
c.Close();
return trimString(String(d || "").replace(/^\uFEFF/, "")); // Вырезаем BOM маркер если он есть
} catch (d) {
// Резервный метод чтения через FSO в формате UTF-16 (-1)
try {
if (!a.FileExists(b)) return "";
c = a.OpenTextFile(b, 1, false, -1);
b = c.ReadAll();
c.Close();
return trimString(String(b || "").replace(/^\uFEFF/, ""));
} catch (e) {
return "";
}
}
}
/** Удаление временных файлов обмена */
function quillbotCleanupBridgeFiles(a, b, c) {
try { a.FileExists(b) && a.DeleteFile(b, true); } catch (d) {}
try { a.FileExists(c) && a.DeleteFile(c, true); } catch (e) {}
}
/** Очистка переменных окружения Windows после выполнения операции */
function quillbotClearBridgeEnvironment(a) {
var envs = ["QTB_QUILLBOT_MODE", "QTB_QUILLBOT_TEXT_B64", "QTB_QUILLBOT_SOURCE", "QTB_QUILLBOT_TARGET", "QTB_QUILLBOT_RESULT_FILE", "QTB_QUILLBOT_TIMEOUT_MS"];
for (var i = 0; i < envs.length; i++) {
try { a.Remove(envs[i]); } catch (e) {}
}
}
function quillbotAbsolutePath(a, b) {
try { return a.GetAbsolutePathName(b); } catch (c) { return b; }
}
/** Генератор уникального ID запроса */
function quillbotUuid() {
return (new Date).getTime() + "_" + Math.floor(1E6 * Math.random());
}
function quillbotSourceText(a) {
return trimString(limitSource(prepareSource(a), QUILLBOT_TRANSLATE_MAX_SOURCE_LEN));
}
function quillbotBridgeTimeoutMs() {
var a = Number(Options.QuillBotTimeoutMs) || 30000;
return 1000 > a ? 1000 : a;
}
function quillbotPageHeaders() {
return getHeader() + Const.NL + "Referer: " + quillbotSiteUrl() + Const.NL + "Origin: " + quillbotHost() + Const.NL + "Connection: keep-alive";
}
function quillbotHost() {
return trimString(Options.QuillBotHost || QUILLBOT_TRANSLATE_HOST);
}
function quillbotSiteUrl() {
return trimString(Options.QuillBotSiteUrl || QUILLBOT_TRANSLATE_SITE_URL);
}
/** Нормализация кодов языков под требования API QuillBot */
function quillbotRequestLanguageCode(a, b) {
a = codeFromLanguage(a);
if (a == UNKNOWN_LANGUAGE_CODE || !a) return b || "en-US";
switch (a) {
case "auto": return "auto";
case "iw":
case "he": return "he";
case "zh-CN":
case "zh-TW":
case "zh-Hans":
case "zh-Hant": return "zh";
case "en": return "en-US";
case "de": return "de-DE";
case "pt":
case "pt-BR": return "pt-PT";
case "no":
case "nb": return "no";
case "tl":
case "fil": return "tl";
default: return a;
}
}
function quillbotQuerySourceCode(a) {
a = quillbotRequestLanguageCode(a, "auto");
return a == "auto" ? "auto" : a;
}
function quillbotQueryTargetCode(a) {
return quillbotRequestLanguageCode(a, "en-US");
}
/** Преобразование ответа Quillbot обратно в коды QTranslate */
function quillbotNormalizeDetectedCode(a) {
a = trimString(String(a || ""));
switch (a) {
case "he":
case "he-IL": return "iw";
case "zh":
case "zh-CN":
case "zh-Hans": return "zh-CN";
case "zh-TW":
case "zh-Hant": return "zh-TW";
case "en-US":
case "en-GB":
case "en": return "en";
case "de-DE":
case "de-CH":
case "de": return "de";
case "pt-PT":
case "pt-BR": return "pt";
case "nb": return "no";
case "fil": return "tl";
default: return a.indexOf("-") > 0 ? a.split("-")[0] : a;
}
}
function quillbotLanguageFromCode(a) {
var b = languageFromCode(quillbotNormalizeDetectedCode(String(a || "")));
return isLanguage(b) ? b : UNKNOWN_LANGUAGE;
}
function quillbotFormatBridgeError(a) {
a = trimString(String(a || ""));
return "[E] QuillBot Translate: " + (a || "Bridge execution failed");
}
function resetQuillbotBridgeState() {
QUILLBOT_BRIDGE_TRANSLATION = "";
QUILLBOT_BRIDGE_SOURCE_LANGUAGE = UNKNOWN_LANGUAGE;
QUILLBOT_BRIDGE_TARGET_LANGUAGE = ENGLISH_LANGUAGE;
QUILLBOT_BRIDGE_ERROR = "";
}
function parseJsonSafe(a) {
try { return parseJSON(a); } catch (b) { return null; }
}
/** Побайтовое преобразование строки в UTF-8 массив байт (для Base64) */
function quillbotStringToBytes(a) {
var b = [], c = 0, d, e;
a = a || "";
for (d = 0; d < a.length; d++) {
e = a.charCodeAt(d);
if (128 > e) {
b[c++] = e;
} else {
if (2048 > e) {
b[c++] = e >> 6 | 192;
} else {
if (55296 == (e & 64512) && d + 1 < a.length && 56320 == (a.charCodeAt(d + 1) & 64512)) {
e = 65536 + ((e & 1023) << 10) + (a.charCodeAt(++d) & 1023);
b[c++] = e >> 18 | 240;
b[c++] = e >> 12 & 63 | 128;
} else {
b[c++] = e >> 12 | 224;
}
b[c++] = e >> 6 & 63 | 128;
}
b[c++] = e & 63 | 128;
}
}
return b;
}
/** Стандартный алгоритм Base64-кодирования массива байт */
function quillbotBytesToBase64(a) {
var b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
c = "", d = 0, e, f, g;
for (d = 0; d < a.length; d += 3) {
e = a[d];
f = d + 1 < a.length ? a[d + 1] : NaN;
g = d + 2 < a.length ? a[d + 2] : NaN;
c += b.charAt(e >> 2);
c += b.charAt((e & 3) << 4 | (f || 0) >> 4);
c += isNaN(f) ? "=" : b.charAt((f & 15) << 2 | (g || 0) >> 6);
c += isNaN(g) ? "=" : b.charAt(g & 63);
}
return c;
}