Schedule to ICS

Convert schedule to ICS

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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();
})();