When2Meet CSV Exporter

Add a button to export When2Meet availability data as CSV

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

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

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

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

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

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         When2Meet CSV Exporter
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Add a button to export When2Meet availability data as CSV
// @author       Tyler Bletsch
// @match        https://www.when2meet.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

/* reverse engineering notes:

Here are the important global variables kept by when2meet:

The strings of people names:
  PeopleNames (Array): [ "Abdul", … ]

An array of slots, where each entry is an array of people IDs:
  AvailableAtSlot (Array): [ [ 115436591, 115438675, 115448517, … ], … ]

The actual time of each timeslot reffered to in AvailableAtSlot, as unix timestamps:
  TimeOfSlot (Array):  [ 1736780400, 1736781300, … ]

An array of the people ID numbers used in AvailableAtSlot, ordered in a manner correlated to PeopleNames:
  PeopleIDs (Array): [ 115660081, 115436591, … ]

The currently selected timezone:
  timezone (string): "America/New_York"

These are consumed by make_csv() below.
*/

(function () {
    'use strict';

    // Wait until the page is fully loaded
    window.addEventListener('load', () => {
        // Check if the make_csv function exists
        console.log("Export CSV button is being added...");
        function make_csv() {
            let csv = "time ("+timezone+")," + PeopleNames.join(",") + "\n";

            // Iterate through each time slot
            for (let i = 0; i < TimeOfSlot.length; i++) {
                //const time = new Date(TimeOfSlot[i] * 1000).toISOString(); // Convert UNIX timestamp to ISO string
                //const time = new Date(TimeOfSlot[i] * 1000).toISOString().replace("T", " ").split(".")[0];
                const time = new Date(TimeOfSlot[i] * 1000).toLocaleString("en-US", {
                    timeZone: timezone, // timezone is a global from when2meet
                    year: "numeric",
                    month: "2-digit",
                    day: "2-digit",
                    hour: "2-digit",
                    minute: "2-digit",
                    second: "2-digit",
                    hour12: false,
                }).replace(",", "");
                const availablePeople = new Set(AvailableAtSlot[i]); // Set of people IDs available at this time slot

                // Create a row with "o" for available people
                const row = [time];
                for (let personID of PeopleIDs) {
                    row.push(availablePeople.has(personID) ? "o" : "");
                }

                csv += row.join(",") + "\n";
            }
            return csv;
        }

        // Create the "Export CSV" button floating in the lower right of the screen
        const button = document.createElement('button');
        button.innerText = 'Tampermonkey: Export CSV';
        button.style.position = 'fixed';
        button.style.bottom = '20px';
        button.style.right = '20px';
        button.style.padding = '12px 24px';
        button.style.fontSize = '16px';
        button.style.fontWeight = 'bold';
        button.style.backgroundColor = '#007bff';
        button.style.color = '#fff';
        button.style.border = '2px solid #0056b3';
        button.style.borderRadius = '8px';
        button.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.5)';
        button.style.cursor = 'pointer';
        button.style.transition = 'all 0.2s ease';
        button.style.zIndex = '1000';

        // Add a click event to generate and download the CSV
        button.addEventListener('click', () => {
            try {
                const csv = make_csv(); // Call the make_csv() function
                const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
                const url = URL.createObjectURL(blob);

                // Create a temporary <a> element to trigger the download
                const link = document.createElement('a');
                link.href = url;
                link.download = 'when2meet_export.csv';
                link.style.display = 'none';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);

                // Revoke the blob URL
                URL.revokeObjectURL(url);
            } catch (error) {
                console.error("Error generating CSV:", error);
                alert("Failed to generate CSV. Check the console for details.");
            }
        });

        // Add the button to the page
        document.body.appendChild(button);
    });
})();