feat: better error messaging and trying to fix teacher schedule

This commit is contained in:
kilyabin
2026-01-30 00:54:35 +04:00
parent a930dcfa4e
commit 47b8bc7dad
6 changed files with 189 additions and 62 deletions

View File

@@ -115,6 +115,23 @@ export async function getSchedule(groupID: number, groupName: string, wk?: numbe
networkErrorCode: networkError.code,
networkErrorMessage: networkError.message
})
} else if (networkError.code === 'DEPTH_ZERO_SELF_SIGNED_CERT' || networkError.message?.includes('self-signed certificate') || networkError.message?.includes('certificate')) {
// Ошибка SSL сертификата
console.error(`SSL certificate error while fetching ${PROXY_URL}:`, {
code: networkError.code,
message: networkError.message,
url
})
const sslError = new Error(`В колледже что-то сломалось (проблема с сертификатом безопасности). Здесь я бессилен, проблема не на моей стороне.`)
logErrorToFile(sslError, {
type: 'ssl_certificate_error',
groupName,
url,
groupID,
networkErrorCode: networkError.code,
networkErrorMessage: networkError.message
})
throw sslError
} else {
// Логируем другие ошибки тоже
logErrorToFile(errorObj, {
@@ -125,6 +142,18 @@ export async function getSchedule(groupID: number, groupName: string, wk?: numbe
})
}
} else {
// Проверяем сообщение об ошибке на наличие упоминания сертификата
if (errorObj.message?.includes('self-signed certificate') || errorObj.message?.includes('certificate')) {
const sslError = new Error(`В колледже что-то сломалось (проблема с сертификатом безопасности). Здесь я бессилен, проблема не на моей стороне.`)
logErrorToFile(sslError, {
type: 'ssl_certificate_error',
groupName,
url,
groupID,
errorMessage: errorObj.message
})
throw sslError
}
// Логируем ошибки без cause
logErrorToFile(errorObj, {
type: 'unknown_error',
@@ -234,6 +263,23 @@ export async function getTeacherSchedule(teacherID: number, teacherName: string,
networkErrorCode: networkError.code,
networkErrorMessage: networkError.message
})
} else if (networkError.code === 'DEPTH_ZERO_SELF_SIGNED_CERT' || networkError.message?.includes('self-signed certificate') || networkError.message?.includes('certificate')) {
// Ошибка SSL сертификата
console.error(`SSL certificate error while fetching ${PROXY_URL}:`, {
code: networkError.code,
message: networkError.message,
url
})
const sslError = new Error(`В колледже что-то сломалось (проблема с сертификатом безопасности). Здесь я бессилен, проблема не на моей стороне.`)
logErrorToFile(sslError, {
type: 'ssl_certificate_error',
teacherName,
url,
teacherID,
networkErrorCode: networkError.code,
networkErrorMessage: networkError.message
})
throw sslError
} else {
// Логируем другие ошибки тоже
logErrorToFile(errorObj, {
@@ -244,6 +290,18 @@ export async function getTeacherSchedule(teacherID: number, teacherName: string,
})
}
} else {
// Проверяем сообщение об ошибке на наличие упоминания сертификата
if (errorObj.message?.includes('self-signed certificate') || errorObj.message?.includes('certificate')) {
const sslError = new Error(`В колледже что-то сломалось (проблема с сертификатом безопасности). Здесь я бессилен, проблема не на моей стороне.`)
logErrorToFile(sslError, {
type: 'ssl_certificate_error',
teacherName,
url,
teacherID,
errorMessage: errorObj.message
})
throw sslError
}
// Логируем ошибки без cause
logErrorToFile(errorObj, {
type: 'unknown_error',

View File

@@ -624,19 +624,37 @@ const parseLesson = (row: Element, isTeacherSchedule: boolean = false): Lesson |
}
} else if (isTeacherSchedule) {
// Для преподавателей место может быть в другом формате в тексте ячейки
// Формат: "ПредметГруппа(Аудитория)Адрес"
const fullText = disciplineCell.textContent?.trim() || ''
if (fullText) {
// Ищем паттерн: группа в скобках и адрес после
// Например: "(ИКС-8)Московское шоссе, 120" или "(ССА-15к)Моск"
const placeMatch = fullText.match(/\(([^)]+)\)([^(]+?)(?:\d+|$)/)
// Формат: "ПредметГруппа(Аудитория)Адрес" или в отдельной ячейке
// Сначала проверяем наличие отдельной ячейки с местом (как для групп)
const placeCellIndex = cells.length >= 6 ? 5 : (cells.length >= 5 ? 4 : -1)
if (placeCellIndex >= 0 && cells[placeCellIndex]) {
const placeCell = cells[placeCellIndex]
const placeText = placeCell.textContent?.trim() || ''
// Ищем адрес и кабинет в формате "адрес\nКабинет: номер"
const placeMatch = placeText.match(/([^\n]+)\n.*?Кабинет:\s*([^\s\n]+)/i)
if (placeMatch) {
const classroom = placeMatch[1].trim()
const address = placeMatch[2].trim()
if (classroom && address) {
lesson.place = {
address,
classroom
lesson.place = {
address: placeMatch[1].trim(),
classroom: placeMatch[2].trim()
}
}
}
// Если не нашли в отдельной ячейке, ищем в тексте ячейки с предметом
if (!lesson.place) {
const fullText = disciplineCell.textContent?.trim() || ''
if (fullText) {
// Ищем паттерн: группа в скобках и адрес после
// Например: "(ИКС-8)Московское шоссе, 120" или "(ССА-15к)Моск"
const placeMatch = fullText.match(/\(([^)]+)\)([^(]+?)(?:\d+|$)/)
if (placeMatch) {
const classroom = placeMatch[1].trim()
const address = placeMatch[2].trim()
if (classroom && address && address.length > 3) {
lesson.place = {
address,
classroom
}
}
}
}
@@ -698,8 +716,21 @@ export function parsePage(document: Document, groupName: string, url?: string, s
// Способ 2: Если не нашли, ищем таблицу по имени в первой строке (может быть заголовок)
if (!table) {
table = tables.find(table => {
const firstRow = table.querySelector(':scope > tbody > tr:first-child')
return firstRow?.textContent?.trim() === groupName
const firstRow = table.querySelector(':scope > tbody > tr:first-child') || table.querySelector(':scope > tr:first-child')
const firstRowText = firstRow?.textContent?.trim() || ''
// Проверяем точное совпадение
return firstRowText === groupName
})
}
// Способ 2.5: Ищем таблицу, которая содержит имя где-то в первых строках (только если имя длинное)
if (!table && groupName.length > 10) {
table = tables.find(table => {
const rows = Array.from(table.querySelectorAll('tr')).slice(0, 3)
return rows.some(row => {
const rowText = row.textContent?.trim() || ''
return rowText.includes(groupName)
})
})
}
} else {