@emiran/omu-ubys 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -143
- package/README.tr.md +110 -106
- package/dist/index.cjs +244 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +244 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1029,6 +1029,167 @@ function textOrUndefined3(value) {
|
|
|
1029
1029
|
function normalizeText2(value) {
|
|
1030
1030
|
return decodeHtmlEntities(value).replace(/\s+/g, " ").trim();
|
|
1031
1031
|
}
|
|
1032
|
+
function parseSurveyPage(html, surveyUrl) {
|
|
1033
|
+
if (html.includes("/Account/Login")) {
|
|
1034
|
+
throw new SessionExpiredError();
|
|
1035
|
+
}
|
|
1036
|
+
const $ = cheerio.load(html);
|
|
1037
|
+
const description = $(".panel-body p").first().text().trim();
|
|
1038
|
+
let surveyId = "";
|
|
1039
|
+
let surveyUserId = "";
|
|
1040
|
+
let redirectUrl = "";
|
|
1041
|
+
$("script").each((_, script) => {
|
|
1042
|
+
const content = $(script).html() ?? "";
|
|
1043
|
+
const base64Match = content.match(
|
|
1044
|
+
/var\s+_jsm\s*=\s*null;\s*\(function\(\)\s*\{\s*_jsm\s*=\s*JSON\.parse\(Base64\.decode\(["']([^"']+)["']\)/
|
|
1045
|
+
);
|
|
1046
|
+
if (base64Match) {
|
|
1047
|
+
try {
|
|
1048
|
+
const encoded = base64Match[1];
|
|
1049
|
+
if (!encoded) return;
|
|
1050
|
+
const decoded = Buffer.from(encoded, "base64").toString("utf-8");
|
|
1051
|
+
const jsmObj = JSON.parse(decoded);
|
|
1052
|
+
if (typeof jsmObj["SurveyId"] === "number" && !surveyId) {
|
|
1053
|
+
surveyId = String(jsmObj["SurveyId"]);
|
|
1054
|
+
}
|
|
1055
|
+
const survey = jsmObj["Survey"];
|
|
1056
|
+
if (typeof survey?.["Id"] === "number" && !surveyId) {
|
|
1057
|
+
surveyId = String(survey["Id"]);
|
|
1058
|
+
}
|
|
1059
|
+
const surveyUser = jsmObj["SurveyUser"];
|
|
1060
|
+
if (typeof surveyUser?.["Id"] === "number" && !surveyUserId) {
|
|
1061
|
+
surveyUserId = String(surveyUser["Id"]);
|
|
1062
|
+
}
|
|
1063
|
+
if (typeof jsmObj["RedirectPageUrl"] === "string" && !redirectUrl) {
|
|
1064
|
+
redirectUrl = jsmObj["RedirectPageUrl"];
|
|
1065
|
+
}
|
|
1066
|
+
} catch {
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
if (!surveyId) {
|
|
1070
|
+
const m = content.match(/Survey\s*:\s*\{\s*Id\s*:\s*(\d+)/);
|
|
1071
|
+
if (m) surveyId = m[1] ?? "";
|
|
1072
|
+
}
|
|
1073
|
+
if (!surveyUserId) {
|
|
1074
|
+
const m = content.match(/SurveyUser\s*:\s*\{\s*Id\s*:\s*(\d+)/);
|
|
1075
|
+
if (m) surveyUserId = m[1] ?? "";
|
|
1076
|
+
}
|
|
1077
|
+
if (!redirectUrl) {
|
|
1078
|
+
const m = content.match(/RedirectPageUrl\s*:\s*["']([^"']+)["']/);
|
|
1079
|
+
if (m) redirectUrl = m[1] ?? "";
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
if (!surveyId) {
|
|
1083
|
+
surveyId = $("#surveyParent").attr("data-survey-id") ?? "";
|
|
1084
|
+
}
|
|
1085
|
+
if (!surveyUserId) {
|
|
1086
|
+
const m = surveyUrl.match(/[?&]Id=(\d+)/i);
|
|
1087
|
+
if (m) surveyUserId = m[1] ?? "";
|
|
1088
|
+
}
|
|
1089
|
+
if (!redirectUrl) {
|
|
1090
|
+
try {
|
|
1091
|
+
const urlObj = new URL(surveyUrl);
|
|
1092
|
+
redirectUrl = urlObj.searchParams.get("RedirectPageUrl") ?? "";
|
|
1093
|
+
} catch {
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (!surveyId && !surveyUserId) {
|
|
1097
|
+
throw new ParseError("Could not extract surveyId or surveyUserId from survey page.", {
|
|
1098
|
+
details: { surveyUrl }
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
const pages = parseSurveyPages($);
|
|
1102
|
+
return {
|
|
1103
|
+
url: surveyUrl,
|
|
1104
|
+
title: "Ders De\u011Ferlendirme Anketi",
|
|
1105
|
+
description,
|
|
1106
|
+
surveyId,
|
|
1107
|
+
surveyUserId,
|
|
1108
|
+
...redirectUrl ? { redirectUrl } : {},
|
|
1109
|
+
pages
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
function parseSurveyPages($) {
|
|
1113
|
+
const pages = [];
|
|
1114
|
+
$(".survey-page").each((_, pageEl) => {
|
|
1115
|
+
const pageId = $(pageEl).attr("data-page-id") ?? "";
|
|
1116
|
+
if (!pageId) return;
|
|
1117
|
+
const pageTitleText = $(pageEl).text().trim();
|
|
1118
|
+
const pageBody = $(pageEl).closest(".panel").find(".panel-body");
|
|
1119
|
+
const questions = [];
|
|
1120
|
+
const choiceLabels = [];
|
|
1121
|
+
const table = pageBody.find("table");
|
|
1122
|
+
if (table.length > 0) {
|
|
1123
|
+
table.find("thead th").each((idx, th) => {
|
|
1124
|
+
const choiceText = $(th).text().trim();
|
|
1125
|
+
if (choiceText && idx > 0) {
|
|
1126
|
+
choiceLabels.push(choiceText);
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
table.find("tbody tr[data-question-id]").each((_2, row) => {
|
|
1130
|
+
const questionId = $(row).attr("data-question-id") ?? "";
|
|
1131
|
+
const questionText = $(row).find("td").first().text().trim();
|
|
1132
|
+
const isRequired = $(row).hasClass("forRequired");
|
|
1133
|
+
const questionChoices = [];
|
|
1134
|
+
$(row).find('input[type="radio"]').each((idx, input) => {
|
|
1135
|
+
const choiceId = $(input).attr("data-choise-id") ?? "";
|
|
1136
|
+
if (choiceId) {
|
|
1137
|
+
questionChoices.push({
|
|
1138
|
+
id: choiceId,
|
|
1139
|
+
text: choiceLabels[idx] ?? `Option ${idx + 1}`
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
if (questionId && questionText) {
|
|
1144
|
+
questions.push({
|
|
1145
|
+
id: questionId,
|
|
1146
|
+
text: questionText,
|
|
1147
|
+
required: isRequired,
|
|
1148
|
+
choices: questionChoices
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
pages.push({
|
|
1154
|
+
id: pageId,
|
|
1155
|
+
title: pageTitleText,
|
|
1156
|
+
questions,
|
|
1157
|
+
choiceLabels
|
|
1158
|
+
});
|
|
1159
|
+
});
|
|
1160
|
+
return pages;
|
|
1161
|
+
}
|
|
1162
|
+
function buildSurveySubmitBody(surveyData, answers) {
|
|
1163
|
+
const choises = Object.entries(answers).map(([questionId, choiceId]) => ({
|
|
1164
|
+
Id: 0,
|
|
1165
|
+
SelectedOrder: null,
|
|
1166
|
+
SurveyQuestionId: parseInt(questionId, 10),
|
|
1167
|
+
SurveyUserId: parseInt(surveyData.surveyUserId, 10),
|
|
1168
|
+
SurveyQuestionChoiseId: parseInt(choiceId, 10),
|
|
1169
|
+
Text: "",
|
|
1170
|
+
IsIrrevelant: false
|
|
1171
|
+
}));
|
|
1172
|
+
return {
|
|
1173
|
+
choises,
|
|
1174
|
+
RedirectPageUrl: surveyData.redirectUrl ?? "",
|
|
1175
|
+
surveyId: parseInt(surveyData.surveyId, 10)
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
function extractSurveyCsrfToken(html) {
|
|
1179
|
+
const $ = cheerio.load(html);
|
|
1180
|
+
const token = $('input[name="__RequestVerificationToken"]').val();
|
|
1181
|
+
return typeof token === "string" && token ? token : void 0;
|
|
1182
|
+
}
|
|
1183
|
+
function parseSurveyLinks(html, baseUrl) {
|
|
1184
|
+
const $ = cheerio.load(html);
|
|
1185
|
+
const links = [];
|
|
1186
|
+
$('a[href*="MES/Application/Public/Participant"]').each((_, el) => {
|
|
1187
|
+
const href = $(el).attr("href");
|
|
1188
|
+
if (!href) return;
|
|
1189
|
+
links.push(href.startsWith("http") ? href : `${baseUrl}${href}`);
|
|
1190
|
+
});
|
|
1191
|
+
return links;
|
|
1192
|
+
}
|
|
1032
1193
|
|
|
1033
1194
|
// src/client.ts
|
|
1034
1195
|
var UBYSClient = class {
|
|
@@ -1285,6 +1446,89 @@ var UBYSClient = class {
|
|
|
1285
1446
|
this.session.assertNotLoginRedirect(response);
|
|
1286
1447
|
return parseWeeklySchedule(response.body);
|
|
1287
1448
|
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Check whether there are any mandatory portal-level surveys blocking access.
|
|
1451
|
+
* Navigates to the student home page and detects PortalSurveyManagement redirects.
|
|
1452
|
+
* If surveys are found they are fetched and returned.
|
|
1453
|
+
*/
|
|
1454
|
+
async getPendingSurveys() {
|
|
1455
|
+
this.ensureAuthenticated();
|
|
1456
|
+
const homeResponse = await this.session.get("AIS/Student/Home/Index");
|
|
1457
|
+
this.session.assertOk(homeResponse);
|
|
1458
|
+
const responseUrl = homeResponse.url;
|
|
1459
|
+
const html = homeResponse.body;
|
|
1460
|
+
const redirectedToSurvey = responseUrl.includes("PortalSurveyManagement");
|
|
1461
|
+
const htmlMentionsSurvey = html.includes("Zorunlu olan anketi");
|
|
1462
|
+
if (!redirectedToSurvey && !htmlMentionsSurvey) {
|
|
1463
|
+
return { hasPendingSurvey: false };
|
|
1464
|
+
}
|
|
1465
|
+
if (redirectedToSurvey) {
|
|
1466
|
+
let requestedUrl = null;
|
|
1467
|
+
try {
|
|
1468
|
+
requestedUrl = new URL(responseUrl).searchParams.get("RequestedUrl");
|
|
1469
|
+
} catch {
|
|
1470
|
+
}
|
|
1471
|
+
if (requestedUrl) {
|
|
1472
|
+
const retryResponse = await this.session.get(requestedUrl);
|
|
1473
|
+
if (!retryResponse.url.includes("PortalSurveyManagement")) {
|
|
1474
|
+
return { hasPendingSurvey: false };
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
const surveyLinks = parseSurveyLinks(html, this.session.baseUrl);
|
|
1478
|
+
if (surveyLinks.length > 0) {
|
|
1479
|
+
const surveys = [];
|
|
1480
|
+
for (const link of surveyLinks) {
|
|
1481
|
+
try {
|
|
1482
|
+
const survey = await this.fetchSurvey(link);
|
|
1483
|
+
surveys.push(survey);
|
|
1484
|
+
} catch {
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
return { hasPendingSurvey: true, ...surveys.length > 0 ? { surveys } : {} };
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
return { hasPendingSurvey: true };
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Fetch and parse a single MES survey participant page.
|
|
1494
|
+
*
|
|
1495
|
+
* @param surveyUrl - Full URL (e.g. https://ubys.omu.edu.tr/MES/Application/Public/Participant?Id=…)
|
|
1496
|
+
*/
|
|
1497
|
+
async fetchSurvey(surveyUrl) {
|
|
1498
|
+
this.ensureAuthenticated();
|
|
1499
|
+
const response = await this.session.get(surveyUrl);
|
|
1500
|
+
this.session.assertOk(response);
|
|
1501
|
+
this.session.assertNotLoginRedirect(response);
|
|
1502
|
+
return parseSurveyPage(response.body, surveyUrl);
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1505
|
+
* Submit answers for a survey.
|
|
1506
|
+
*
|
|
1507
|
+
* @param surveyData - The SurveyData returned by `fetchSurvey()`
|
|
1508
|
+
* @param answers - Map of questionId → choiceId
|
|
1509
|
+
*/
|
|
1510
|
+
async submitSurvey(surveyData, answers) {
|
|
1511
|
+
this.ensureAuthenticated();
|
|
1512
|
+
const pageResponse = await this.session.get(surveyData.url);
|
|
1513
|
+
this.session.assertOk(pageResponse);
|
|
1514
|
+
this.session.assertNotLoginRedirect(pageResponse);
|
|
1515
|
+
const csrfToken = extractSurveyCsrfToken(pageResponse.body);
|
|
1516
|
+
if (!csrfToken) {
|
|
1517
|
+
throw new Error("CSRF token not found on survey page.");
|
|
1518
|
+
}
|
|
1519
|
+
const body = buildSurveySubmitBody(surveyData, answers);
|
|
1520
|
+
const finishUrl = `${this.session.baseUrl}/MES/Application/Public/FinishSurvey`;
|
|
1521
|
+
const submitResponse = await this.session.post(finishUrl, {
|
|
1522
|
+
body: JSON.stringify(body),
|
|
1523
|
+
headers: {
|
|
1524
|
+
"content-type": "application/json; charset=UTF-8",
|
|
1525
|
+
"x-requested-with": "XMLHttpRequest",
|
|
1526
|
+
"x-requestverificationtoken": csrfToken,
|
|
1527
|
+
referer: surveyData.url
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
this.session.assertOk(submitResponse);
|
|
1531
|
+
}
|
|
1288
1532
|
clearSession() {
|
|
1289
1533
|
this.ensureOpen();
|
|
1290
1534
|
this.state = {
|