frontAnswerer/lib/api/quizRelase.ts
Nastya 17013922df
Some checks failed
Deploy / CreateImage (push) Successful in 4m10s
Deploy / DeployService (push) Failing after 24s
Merge branch 'newai'
2025-05-01 19:01:40 +03:00

438 lines
11 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",
});
};
// Глобальные переменные для хранения состояния между вызовами
let globalStatus: string | null = null;
let isFirstRequest = true;
export async function getData({ quizId, page }: { quizId: string; page?: number }): Promise<{
data: GetQuizDataResponse | null;
isRecentlyCompleted: boolean;
error?: AxiosError;
}> {
try {
// Первый запрос: 1 вопрос + конфиг
if (isFirstRequest) {
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: 1,
page: 0,
need_config: true,
},
}
);
globalStatus = data.settings.status;
isFirstRequest = false;
SESSIONS = headers["x-sessionkey"] || SESSIONS;
// Проверка антифрода
const sessions = JSON.parse(localStorage.getItem("sessions") || "{}");
if (typeof sessions[quizId] === "number" && data.settings.cfg.includes('antifraud":true')) {
if (Date.now() - sessions[quizId] < 86400000) {
return { data, isRecentlyCompleted: true };
}
}
// Если статус не AI - сразу делаем запрос за всеми вопросами
if (globalStatus !== "ai") {
const secondResponse = 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: 100,
page: 0,
need_config: false,
},
}
);
return {
data: { ...data, items: secondResponse.data.items },
isRecentlyCompleted: false,
};
}
return { data, isRecentlyCompleted: false };
}
// Последующие запросы
const response = 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: 1,
page: page,
need_config: false,
},
});
return {
data: response.data,
isRecentlyCompleted: false,
};
} catch (error) {
return {
data: null,
isRecentlyCompleted: false,
error: error as AxiosError,
};
}
}
export async function getQuizData({ quizId, status = "" }: { quizId: string; status?: 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 });
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 });
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;
console.log("я собираюсь вернуть вопрос");
console.log(res);
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",
});
}