frontAnswerer/lib/api/quizRelase.ts
2025-05-01 16:15:54 +03:00

376 lines
9.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { GetQuizDataResponse, parseQuizData } from "@model/api/getQuizData";
import axios from "axios";
import MobileDetect from "mobile-detect";
import device from "current-device";
import type { AxiosError } from "axios";
import { replaceSpacesToEmptyLines } from "../components/ViewPublicationPage/tools/replaceSpacesToEmptyLines";
import { QuizSettings } from "@model/settingsData";
import * as Bowser from "bowser";
import { domain } from "../utils/defineDomain";
let SESSIONS = "";
const md = new MobileDetect(window.navigator.userAgent);
const userAgent = navigator.userAgent;
//операционная система
let OSDevice: string | undefined;
if (userAgent.toLowerCase().includes("linux")) {
OSDevice = "Linux";
}
if (userAgent.toLowerCase().includes("windows")) {
OSDevice = "Windows";
}
if (/iPad|iPhone|iPod/.test(userAgent)) {
OSDevice = "IOS";
}
if (userAgent.toLowerCase().includes("macintosh")) {
OSDevice = "Mac OS";
}
if (OSDevice === undefined) {
OSDevice = userAgent;
}
//браузер
let browserUser: string;
if (Bowser.name === "Chrome") {
browserUser = "Chrome";
} else if (Bowser.name === "Firefox") {
browserUser = "Firefox";
} else if (Bowser.name === "Safari") {
browserUser = "Safari";
} else if (Bowser.name === "Yandex Browser") {
browserUser = "Yandex Browser";
} else {
browserUser = userAgent;
}
const DeviceType = device.type;
let Device = md.mobile();
if (Device === null) {
Device = userAgent;
}
type PublicationMakeRequestParams = {
url: string;
body: FormData;
method: "POST";
};
export const publicationMakeRequest = ({ url, body }: PublicationMakeRequestParams) => {
return axios(url, {
data: body,
headers: {
"X-Sessionkey": SESSIONS,
"Content-Type": "multipart/form-data",
DeviceType: DeviceType,
Device: Device,
OS: OSDevice,
Browser: browserUser,
},
method: "POST",
});
};
export async function getData({ quizId, page, status }: { quizId: string; page?: number; status?: string }): Promise<{
data: GetQuizDataResponse | null;
isRecentlyCompleted: boolean;
error?: AxiosError;
}> {
try {
const { data, headers } = await axios<GetQuizDataResponse>(
domain + `/answer/v1.0.0/settings${window.location.search}`,
{
method: "POST",
headers: {
"X-Sessionkey": SESSIONS,
"Content-Type": "application/json",
DeviceType: DeviceType,
Device: Device,
OS: OSDevice,
Browser: userAgent,
},
data: {
quiz_id: quizId,
limit: status === "ai" ? 1 : 100,
page: status === "ai" ? page : 0,
need_config: true,
},
}
);
const sessions = JSON.parse(localStorage.getItem("sessions") || "{}");
//Тут ещё проверка на антифрод без парса конфига. Нам не интересно время если не нужно запрещать проходить чаще чем в сутки
if (typeof sessions[quizId] === "number" && data.settings.cfg.includes('antifraud":true')) {
// unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше
if (Date.now() - sessions[quizId] < 86400000) {
return { data, isRecentlyCompleted: true };
}
}
SESSIONS = headers["x-sessionkey"] ? headers["x-sessionkey"] : SESSIONS;
return { data, isRecentlyCompleted: false };
} catch (nativeError) {
const error = nativeError as AxiosError;
return { data: null, isRecentlyCompleted: false, error: error };
}
}
export async function getQuizData(quizId: string) {
if (!quizId) throw new Error("No quiz id");
const response = await getData({ quizId });
const quizDataResponse = response.data;
if (response.error) {
throw response.error;
}
if (!quizDataResponse) {
throw new Error("Quiz not found");
}
const quizSettings = replaceSpacesToEmptyLines(parseQuizData(quizDataResponse));
const res = JSON.parse(
JSON.stringify({ data: quizSettings })
.replaceAll(/\\" \\"/g, '""')
.replaceAll(/" "/g, '""')
).data as QuizSettings;
res.recentlyCompleted = response.isRecentlyCompleted;
return res;
}
let page = 1;
export async function getQuizDataAI(quizId: string) {
let maxRetries = 50;
if (!quizId) throw new Error("No quiz id");
let lastError: Error | null = null;
let responseData: any = null;
// Первый цикл - обработка result вопросов с увеличением page
let resultRetryCount = 0;
while (resultRetryCount < maxRetries) {
try {
const response = await getData({ quizId, page, status: "ai" });
console.log("ф-я аналитики", response);
if (response.error) throw response.error;
if (!response.data) throw new Error("Quiz not found");
const hasAiResult = response.data.items.some((item) => item.typ === "result");
if (hasAiResult) {
page++; // Увеличиваем страницу
resultRetryCount++;
continue;
}
// Если result вопросов нет, сохраняем данные для второго цикла
responseData = response;
break;
} catch (error) {
lastError = error as Error;
resultRetryCount++;
if (resultRetryCount >= maxRetries) break;
await new Promise((resolve) => setTimeout(resolve, 1500 * resultRetryCount));
}
}
if (!responseData) {
throw lastError || new Error("Failed to get quiz data after result retries");
}
// Второй цикл - обработка пустого массива items (без изменения page)
let isEmpty = !responseData.data?.items.length;
let emptyRetryCount = 0;
while (isEmpty && emptyRetryCount < maxRetries) {
try {
await new Promise((resolve) => setTimeout(resolve, 1000));
const response = await getData({ quizId, page, status: "ai" });
if (response.error) throw response.error;
if (!response.data) throw new Error("Quiz not found");
isEmpty = !response.data.items.length;
if (!isEmpty) {
responseData = response; // Обновляем данные
}
emptyRetryCount++;
} catch (error) {
lastError = error as Error;
emptyRetryCount++;
if (emptyRetryCount >= maxRetries) break;
}
}
if (isEmpty) {
throw new Error("Items array is empty after maximum retries");
}
// Финальная обработка успешного ответа
const quizSettings = replaceSpacesToEmptyLines(parseQuizData(responseData.data));
const res = JSON.parse(
JSON.stringify({ data: quizSettings })
.replaceAll(/\\" \\"/g, '""')
.replaceAll(/" "/g, '""')
).data as QuizSettings;
res.recentlyCompleted = responseData.isRecentlyCompleted;
return res;
}
type SendAnswerProps = {
questionId: string;
body: string | string[];
qid: string;
preview?: boolean;
};
export function sendAnswer({ questionId, body, qid, preview = false }: SendAnswerProps) {
console.log("qid");
console.log(qid);
if (preview) return;
const formData = new FormData();
const answers = [
{
question_id: questionId,
content: body, //тут массив с ответом
},
];
formData.append("answers", JSON.stringify(answers));
formData.append("qid", qid);
return publicationMakeRequest({
url: domain + `/answer/v1.0.0/answer`,
body: formData,
method: "POST",
});
}
//body ={file, filename}
type SendFileParams = {
questionId: string;
body: {
name: string;
file: File;
preview: boolean;
};
qid: string;
};
type Answer = {
question_id: string;
content: string;
};
export function sendFile({ questionId, body, qid }: SendFileParams) {
if (body.preview) return;
const formData = new FormData();
const file = new File([body.file], body.file.name.replace(/\s/g, "_"));
const nameImage = body.name.replace(/\s/g, "_");
const answers: Answer[] = [
{
question_id: questionId,
content: "file:" + nameImage,
},
];
formData.append("answers", JSON.stringify(answers));
formData.append(nameImage, file);
formData.append("qid", qid);
return publicationMakeRequest({
url: domain + `/answer/v1.0.0/answer`,
body: formData,
method: "POST",
});
}
//форма контактов
export type SendFCParams = {
questionId: string;
body: {
name?: string;
email?: string;
phone?: string;
address?: string;
customs?: Record<string, string>;
};
qid: string;
preview: boolean;
};
export function sendFC({ questionId, body, qid, preview }: SendFCParams) {
if (preview) return;
const formData = new FormData();
// const keysBody = Object.keys(body)
// const content:any = {}
// fields.forEach((key) => {
// if (keysBody.includes(key)) content[key] = body.key
// })
const answers = [
{
question_id: questionId,
content: JSON.stringify(body),
result: true,
qid,
},
];
formData.append("answers", JSON.stringify(answers));
formData.append("qid", qid);
return publicationMakeRequest({
url: domain + `/answer/v1.0.0/answer`,
body: formData,
method: "POST",
});
}
//форма контактов
export type SendResultParams = {
questionId: string;
pointsSum: number;
qid: string;
preview: boolean;
};
export function sendResult({ questionId, pointsSum, qid, preview }: SendResultParams) {
if (preview) return;
const formData = new FormData();
// const keysBody = Object.keys(body)
// const content:any = {}
// fields.forEach((key) => {
// if (keysBody.includes(key)) content[key] = body.key
// })
const answers = [
{
question_id: questionId,
content: pointsSum.toString(),
result: false,
qid,
},
];
formData.append("answers", JSON.stringify(answers));
formData.append("qid", qid);
return publicationMakeRequest({
url: domain + `/answer/v1.0.0/answer`,
body: formData,
method: "POST",
});
}