Schedule to ICS

Convert schedule to ICS

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @license MIT
// @name         Schedule to ICS
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Convert schedule to ICS
// @author       Nwakey
// @match        https://dutgs.dlut.edu.cn/pyxx/pygl/kbcx_xs.aspx?xh=*
// @match        https://dutgs.dlut.edu.cn/pyxx/default.aspx?u=cas
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const startDate = new Date('2025-02-24'); // 第一周的开始日期

    const timeMap = {
        '1': { start: '08:00', end: '08:45' },
        '2': { start: '08:50', end: '09:35' },
        '3': { start: '10:05', end: '10:50' },
        '4': { start: '10:55', end: '11:40' },
        '5': { start: '13:30', end: '14:15' },
        '6': { start: '14:20', end: '15:05' },
        '7': { start: '15:35', end: '16:20' },
        '8': { start: '16:25', end: '17:10' },
        '9': { start: '18:00', end: '18:45' },
        '10': { start: '18:55', end: '19:40' },
        '11': { start: '19:50', end: '20:35' },
        '12': { start: '20:45', end: '21:30' }
    };

    function getWeekdayOffset(day) {
        const dayMap = {
            '星期一': 0,
            '星期二': 1,
            '星期三': 2,
            '星期四': 3,
            '星期五': 4,
            '星期六': 5,
            '星期日': 6
        };
        return dayMap[day];
    }

    function getEventDate(week, day) {
        const offset = (week - 1) * 7 + getWeekdayOffset(day);
        const eventDate = new Date(startDate);
        eventDate.setDate(startDate.getDate() + offset);
        return eventDate;
    }

    function formatDate(date, time) {
        return `${date.getFullYear()}${(date.getMonth() + 1).toString().padStart(2, '0')}${date.getDate().toString().padStart(2, '0')}T${time.replace(':', '')}00`;
    }

    function extractSchedule() {
        console.log('Extracting schedule...');
        const schedule = [];
        const courseSet = new Set(); // 用于存储已添加的课程信息
        const rows = document.querySelectorAll('#MainWork_DataGrid1 tr');
        rows.forEach((row, rowIndex) => {
            if (rowIndex === 0) return; // 跳过表头行
            const cells = row.querySelectorAll('td');
            cells.forEach((cell, cellIndex) => {
                if (cellIndex < 2 || !cell.innerHTML.trim()) return; // 跳过非课表单元格
                const courseInfos = cell.innerHTML.split('<br><br>'); // 分割多个课程信息
                courseInfos.forEach(courseInfoStr => {
                    const courseInfo = courseInfoStr.split('<br>');
                    if (courseInfo.length < 5) return; // 确保包含足够的课程信息
                    const weeks = courseInfo[3].match(/(\d+)-(\d+)周/);
                    const dayTimeMatches = courseInfo[3].match(/星期(.) (上|下)(\d+),(\d+)/g);
                    const locationMatch = courseInfo[2].match(/\((.*)\)/);
                    if (!weeks || !dayTimeMatches || !locationMatch) return;
                    const startWeek = parseInt(weeks[1]);
                    const endWeek = parseInt(weeks[2]);
                    dayTimeMatches.forEach(dayTime => {
                        const dayTimeMatch = dayTime.match(/星期(.) (上|下)(\d+),(\d+)/);
                        const day = `星期${dayTimeMatch[1]}`;
                        const times = [dayTimeMatch[3], dayTimeMatch[4]];
                        const course = {
                            name: courseInfo[0].replace('课程:', '').trim(),
                            class: courseInfo[1].replace('班级:', '').trim(),
                            location: locationMatch[1].trim(),
                            teacher: courseInfo[4].replace('主讲教师:', '').trim(),
                            startWeek,
                            endWeek,
                            day,
                            times
                        };
                        const courseKey = `${course.name}-${course.class}-${course.location}-${course.teacher}-${course.startWeek}-${course.endWeek}-${course.day}-${course.times.join(',')}`;
                        if (!courseSet.has(courseKey)) {
                            console.log('Extracted course:', course);
                            schedule.push(course);
                            courseSet.add(courseKey);
                        }
                    });
                });
            });
        });
        console.log('Schedule extraction complete:', schedule);
        return schedule;
    }
    function convertToICS(schedule) {
        console.log('Converting schedule to ICS...');
        let icsContent = 'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Nwakey//Schedule to ICS//EN\n';
        schedule.forEach(event => {
            let replacedLocation = event.location.replace(/研教楼/g, '研究生教育大楼');
            for (let week = event.startWeek; week <= event.endWeek; week++) {
                const eventDate = getEventDate(week, event.day);
                const startTime = timeMap[event.times[0]].start;
                const endTime = timeMap[event.times[event.times.length - 1]].end;
                icsContent += 'BEGIN:VEVENT\n';
                icsContent += `SUMMARY:${event.name}\n`;
                icsContent += `LOCATION:大连理工大学\\,${replacedLocation}\n`;
                icsContent += `DESCRIPTION:Class ${event.class} Teacher: ${event.teacher}\n`;
                icsContent += `DTSTART:${formatDate(eventDate, startTime)}\n`;
                icsContent += `DTEND:${formatDate(eventDate, endTime)}\n`;
                icsContent += 'END:VEVENT\n';
                console.log('Added event to ICS:', event);
            }
        });
        icsContent += 'END:VCALENDAR';
        console.log('ICS conversion complete.');
        return icsContent;
    }

    function downloadICS(icsContent) {
        console.log('Downloading ICS file...');
        const blob = new Blob([icsContent], { type: 'text/calendar' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'schedule.ics';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        console.log('ICS file downloaded.');
    }

    function addButton() {
        const button = document.createElement('button');
        button.innerText = '下载 ICS 文件';
        button.style.position = 'fixed';
        button.style.top = '10px';
        button.style.right = '10px';
        button.style.zIndex = 1000;
        button.onclick = () => {
            console.log('Button clicked. Extracting schedule...');
            const schedule = extractSchedule();
            console.log('Converting schedule to ICS...');
            const icsContent = convertToICS(schedule);
            console.log('Downloading ICS file...');
            downloadICS(icsContent);
        };
        document.body.appendChild(button);
        console.log('Button added to the page.');
    }

    addButton();
})();