Convert schedule to ICS file
当前为
// ==UserScript==
// @name USTC Courses to ICS
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Convert schedule to ICS file
// @match https://jw.ustc.edu.cn/for-std/course-select/*/select
// @grant none
// @run-at document-idle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 提取页面信息的函数
function extractPageInfo() {
const pageHTML = document.body.innerHTML;
const extractValue = (regex) => {
const match = pageHTML.match(regex);
return match && match[1];
};
return {
studentId: extractValue(/studentId:\s*(\d+)/),
turnId: extractValue(/turnId:\s*(\d+)/),
fetchSelectedCoursesUrl: extractValue(/fetchSelectedCourses:\s*"([^"]*)"/)
};
}
// 获取选项的函数
async function getOptions() {
const pageInfo = extractPageInfo();
return {
studentId: pageInfo.studentId,
turnId: pageInfo.turnId,
url: {
fetchSelectedCourses: pageInfo.fetchSelectedCoursesUrl
}
};
}
// 获取已选课程的函数
async function fetchSelectedLessons(options) {
return new Promise((resolve, reject) => {
$.ajax({
url: options.url.fetchSelectedCourses,
type: 'post',
data: {
studentId: options.studentId,
turnId: options.turnId
},
success: resolve,
error: reject
});
});
}
// 获取课程表的函数
async function getSchedule() {
try {
const options = await getOptions();
const selectedLessons = await fetchSelectedLessons(options);
const data = {
lessonIds: selectedLessons.map(lesson => lesson.id),
studentId: options.studentId
};
return new Promise((resolve, reject) => {
$.ajax({
url: `${window.CONTEXT_PATH}/ws/schedule-table/datum`,
type: 'post',
contentType: 'application/json',
data: JSON.stringify(data),
success: (res) => resolve(res),
error: reject
});
});
} catch (error) {
console.error("Error fetching schedule:", error);
throw error;
}
}
function createICS(data) {
let icsContent = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//[email protected]//Schedule to ICS Converter//EN',
'CALSCALE:GREGORIAN',
'METHOD:PUBLISH',
'X-WR-TIMEZONE:Asia/Shanghai'
];
for (const schedule of data.result.scheduleList) {
const event = ['BEGIN:VEVENT'];
// Set event summary
const lesson = data.result.lessonList.find(l => l.id === schedule.lessonId);
if (lesson) {
event.push(`SUMMARY:${lesson.courseName} - ${schedule.personName}`);
} else {
event.push(`SUMMARY:课程 - ${schedule.personName}`);
}
// Set event location
if (schedule.room) { // Not online teaching
const location = schedule.room.nameZh;
const building = schedule.room.building.nameZh;
const campus = schedule.room.building.campus.nameZh;
event.push(`LOCATION:${campus} ${building} ${location}`);
} else { // Online teaching
event.push(`LOCATION:${schedule.customPlace}`);
}
// Set event start and end time
const date = new Date(schedule.date);
const startHour = Math.floor(parseInt(schedule.startTime) / 100);
const startMinute = parseInt(schedule.startTime) % 100;
const endHour = Math.floor(parseInt(schedule.endTime) / 100);
const endMinute = parseInt(schedule.endTime) % 100;
const startTime = new Date(date.setHours(startHour, startMinute));
const endTime = new Date(date.setHours(endHour, endMinute));
event.push(`DTSTART:${formatDate(startTime)}`);
event.push(`DTEND:${formatDate(endTime)}`);
// Add description
const description = `教师: ${schedule.personName}\\n课程ID: ${schedule.lessonId}`;
event.push(`DESCRIPTION:${description}`);
event.push('END:VEVENT');
icsContent = icsContent.concat(event);
}
icsContent.push('END:VCALENDAR');
return icsContent.join('\r\n');
}
function formatDate(date) {
return date.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
}
async function generateICS() {
try {
const schedule = await getSchedule();
const icsData = createICS(schedule);
if (!icsData) {
throw new Error("Failed to create ICS data");
}
const blob = new Blob([icsData], { type: 'text/calendar;charset=utf-8' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'schedule.ics';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.error("Error generating ICS file:", error);
}
}
// 创建一个按钮来触发 ICS 生成
function createButton() {
const button = document.createElement('button');
button.textContent = '导出ICS';
button.className = 'btn btn-primary'
button.addEventListener('click', generateICS);
// 创建一个观察器实例
const observer = new MutationObserver((mutations) => {
for (let mutation of mutations) {
if (mutation.type === 'childList') {
const container = document.querySelector('.col.col-sm-3.text-right');
if (container) {
container.appendChild(button);
observer.disconnect(); // 停止观察
return;
}
}
}
});
// 配置观察选项
const config = { childList: true, subtree: true };
// 开始观察目标节点的变化
observer.observe(document.body, config);
// 设置一个超时,以防容器never出现
setTimeout(() => {
observer.disconnect();
if (!button.parentNode) {
document.body.appendChild(button);
}
}, 10000); // 10秒后超时
}
window.addEventListener('load', createButton);
})();