376 lines
9.9 KiB
TypeScript
376 lines
9.9 KiB
TypeScript
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",
|
||
});
|
||
}
|