剧本杀活动通知生成器

用于获取本周剧本杀活动信息并生成 Markdown 代码

// ==UserScript==
// @name         剧本杀活动通知生成器
// @namespace    https://github.com/heiyexing
// @version      2024-07-17
// @description  用于获取本周剧本杀活动信息并生成 Markdown 代码
// @author       炎熊
// @match        https://yuque.antfin-inc.com/yuhmb7/pksdw8/**
// @match        https://yuque.antfin.com/yuhmb7/pksdw8/**
// @icon         https://www.google.com/s2/favicons?sz=64&domain=antfin-inc.com
// @require      https://registry.npmmirror.com/dayjs/1.11.9/files/dayjs.min.js
// @require      https://registry.npmmirror.com/dayjs/1.11.9/files/plugin/isSameOrAfter.js
// @require      https://registry.npmmirror.com/dayjs/1.11.9/files/plugin/isSameOrBefore.js
// @require      https://registry.npmmirror.com/dayjs/1.11.9/files/locale/zh-cn.js
// @require      https://www.layuicdn.com/layui-v2.8.0/layui.js
// @run-at       document-end
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    "use strict";

    dayjs.locale(dayjs_locale_zh_cn);
    dayjs.extend(dayjs_plugin_isSameOrAfter);
    dayjs.extend(dayjs_plugin_isSameOrBefore);

    const BTN_ID = "murder-mystery-btn";
    const USER_LIST_CLASS_NAME = "murder-user-list";
    const USER_ITEM_CLASS_NAME = "murder-user-item";

    let timeRange = [dayjs().startOf("week"), dayjs().endOf("week")];

    function initStyle() {
        const style = document.createElement("style");
        style.innerHTML = `
                      #${BTN_ID} {
                          position: fixed;
                          bottom: 25px;
                          right: 80px;
                          width: 40px;
                          height: 40px;
                          background-color: #fff;
                          border-radius: 50%;
                          box-shadow: 0 0 10px rgba(0, 0, 0, .2);
                          cursor: pointer;
                          display: inline-flex;
                          justify-content: center;
                          align-items: center;
                          z-index: 2;
                      }
                      #${BTN_ID} img {
                          width: 20px;
                      }
                      .${USER_LIST_CLASS_NAME} {
                        display: flex;
                        flex-wrap: wrap;
                      }
                      .${USER_ITEM_CLASS_NAME} {
                        margin-right: 12px;
                        margin-bottom: 12px;
                        display: flex;
                        justify-content: space-between;
                        align-items: center;
                        flex-wrap: wrap;
                        line-height: 14px;
                        border-radius: 6px;
                        padding: 6px;
                        border: 1px solid #E7E9E8;
                      }
                      .${USER_ITEM_CLASS_NAME}.unchecked {
                        border-color: #ff0000;
                      }
                      .${USER_ITEM_CLASS_NAME} span {
                        white-space: nowrap;
                      }
                      .${USER_ITEM_CLASS_NAME} img {
                        width: 30px;
                        height: 30px;
                        border-radius: 30px;
                        margin-right: 6px;
                      }
                      .layui-card-body {
                        width: 100%;
                      }
                      .layui-card-footer {
                        display: flex;
                        justify-content: space-between;
                        align-items: center;
                      }
                      `;
      const link = document.createElement("link");
      link.setAttribute("rel", "stylesheet");
      link.setAttribute("type", "text/css");
      link.href =
          "https://cdn.bootcdn.net/ajax/libs/layui/2.8.17/css/layui.min.css";
      document.head.appendChild(style);
      document.head.appendChild(link);
      return style;
  }

    function initBtn() {
        const btn = document.createElement("div");
        btn.id = BTN_ID;
        const logo = document.createElement("img");
        logo.src =
            "https://mdn.alipayobjects.com/huamei_baaa7a/afts/img/A*f8MvQYdbHPoAAAAAAAAAAAAADqSCAQ/original";
        btn.appendChild(logo);
        document.body.appendChild(btn);
        return btn;
    }

    function getTitleInfo(title) {
        const month = title.match(/\d+(?=\s*月)/)?.[0];
        const date = title.match(/\d+(?=\s*日)/)?.[0];
        const name = title.match(/(?<=《).*?(?=》)/)?.[0];
        if (!month || !date || !name) {
            return null;
        }
        return {
            month: +month,
            date: +date,
            name,
        };
    }

    function getRegExpStr(strList, regexp) {
        for (const str of strList) {
            const result = str.match(regexp);
            if (result) {
                return result[0].trim();
            }
        }
        return "";
    }

    function downloadFile(content, fileName) {
        const url = `data:text/csv;charset=utf-8,\ufeff${encodeURIComponent(
            content
        )}`;
        // 创建a标签
        const link = document.createElement("a");
        link.href = url;
        link.download = fileName;
        link.click();
    }

    function exeCommandCopyText(text) {
        try {
            const t = document.createElement("textarea");
            t.nodeValue = text;
            t.value = text;
            document.body.appendChild(t);
            t.select();
            document.execCommand("copy");
            document.body.removeChild(t);
            return true;
        } catch (e) {
            console.log(e);
            return false;
        }
    }

    function getInnerText(content) {
        const div = document.createElement("div");
        div.style = "height: 0px; overflow: hidden;";
        div.innerHTML = content;
        document.body.appendChild(div);
        return div.innerText;
    }

    function chineseToArabic(chineseNum) {
        let num = chineseNum
        .replace(/零/g, "0")
        .replace(/一/g, "1")
        .replace(/二/g, "2")
        .replace(/三/g, "3")
        .replace(/四/g, "4")
        .replace(/五/g, "5")
        .replace(/六/g, "6")
        .replace(/七/g, "7")
        .replace(/八/g, "8")
        .replace(/九/g, "9");
        num = num
            .replace(/十/g, "10")
            .replace(/百/g, "100")
            .replace(/千/g, "1000")
            .replace(/万/g, "10000");
        return num;
    }

    async function getAllActivesInfo() {
        if (!window.appData || !Array.isArray(window.appData?.book.toc)) {
            return;
        }
        const tocList = window.appData?.book.toc.filter((item) =>
                                                        ["BkpJsZ1b7Xm9MB8p", "_yvlr38511LXSB_-"].includes(item.parent_uuid)
                                                       );

        return tocList;
    }

    async function getActivesInfo(start, end) {
        if (!window.appData || !Array.isArray(window.appData?.book.toc)) {
            return;
        }
        const tocList = window.appData?.book.toc;
        const pathList = location.pathname.split("/");
        if (pathList.length <= 0) {
            return;
        }
        const docUrl = pathList[pathList.length - 1];
        const currentToc = tocList.find((item) => item.url === docUrl);
        if (!currentToc) {
            return;
        }
        const parentToc = tocList.find(
            (item) => item.uuid === currentToc.parent_uuid
        );
        if (!parentToc) {
            return;
        }
        const targetTocList = tocList.filter(
            (item) => item.parent_uuid === parentToc.uuid
        );

        const targetTimeRangeList = targetTocList
        .map((item) => {
            const titleInfo = getTitleInfo(item.title);
            if (!titleInfo) {
                return item;
            }
            return {
                ...item,
                ...titleInfo,
                dayjs: dayjs()
                .set("month", titleInfo.month - 1)
                .set("date", titleInfo.date),
            };
        })
        .filter((item) => {
            return (
                item.dayjs.isSameOrAfter(start, "date") &&
                item.dayjs.isSameOrBefore(end, "date")
            );
        })
        .sort((a, b) => a.dayjs - b.dayjs);

        return await Promise.all(
            targetTimeRangeList.map((item) => {
                return fetch(
                    `${location.origin}/api/docs/${item.url}?book_id=${window.appData?.book.id}&include_contributors=true&include_like=true&include_hits=true&merge_dynamic_data=false`
        )
            .then((res) => res.json())
            .then((res) => {
            const rowList = getInnerText(res.data.content).split("\n");

            const tag = getRegExpStr(rowList, /(?<=类型\s*[::]\s*).+/)
            ?.split(/[/||]/)
            .join("/");

            const level = getRegExpStr(
                rowList,
                /(?<=(难度|适合)\s*[::\s*]).+/
            );

            const dm = getRegExpStr(rowList, /(?<=(dm|DM)\s*[::]\s*).+/);

            let place = getRegExpStr(rowList, /(?<=(地点|场地)\s*[::]\s*).+/);

            if (/[Aa]\s?空间/.test(place)) {
                place = "A空间";
            }
            if (/元空间/.test(place)) {
                place = "元空间";
            }

            const persons = getRegExpStr(rowList, /(?<=(人数)\s*[::]\s*).+/)
            .split(/[,,\(\)()「」]/)
            .map((item) => item.replace(/(回复报名|注明男女|及人数)/, ""))
            .filter((item) => item.trim())
            .join("·");

            const manCount = +persons.match(/(\d+)\s?男/)?.[1] || undefined;
            const womanCount = +persons.match(/(\d+)\s?女/)?.[1] || undefined;
            const personCount = (() => {
                if (manCount && womanCount) {
                    return manCount + womanCount;
                }
                if (/(\d+)[~~到-](\d+)/.test(persons.replace(/\s/g, ""))) {
                    return +/(\d+)[~~到-](\d+)/.exec(
                        persons.replaceAll(" ", "")
                    )[1];
                }
                if (/(\d+)人?/.test(persons.replaceAll(/\s/g, ""))) {
                    return +/(\d+)人?/.exec(persons.replaceAll(" ", ""))[1];
                }
                return undefined;
            })();

            const reversable = !/不[^反]*反串/.test(persons);

            const week =
                  getRegExpStr(rowList, /周[一二三四五六日]/) ||
                  `周${
                ["日", "一", "二", "三", "四", "五", "六"][item.dayjs.day()]
            }`;

            const time = getRegExpStr(rowList, /\d{1,2}[::]\d{2}/);

            const [hour = "", minute = ""] = time.split(/[::]/);

            const duration = getRegExpStr(
                rowList,
                /(?<=(预计时.|时长)\s*[::]\s*).+/
            ).replace(/(h|小时)/, "H");

            const url = `https://yuque.antfin.com/yuhmb7/pksdw8/${item.url}?singleDoc#`;

            return {
                ...item,
                tag,
                level,
                dm,
                week,
                hour,
                minute,
                place,
                persons,
                duration,
                url,
                manCount,
                womanCount,
                personCount,
                reversable,
            };
        });
      })
    );
  }

    async function copyMarkdownInfo(list) {
        const text = `
# 📢 剧本杀活动通知

---
${list
    .map((item) => {
        return `
🎬 《${item.name}》${item.tag}${item.level ? `/${item.level}` : ""}

🕙  ${item.month}.${item.date} ${item.week} ${item.hour}:${item.minute} 📍${
          item.place
      }

💎  DM ${item.dm}【${item.persons}·${item.duration}】[报名](${item.url})

---
`;
      })
    .join("")}

🔺 入门:新手友好,10推理本以内经验的玩家

🔺 进阶:中等难度,20推理本以内经验的玩家

🔺 烧脑:积极推理、全程在线、20推理本以上

🔍 务必结合自身经验和剧本难度充分评估后报名

🙋‍ [【活动须知】](https://yuque.antfin.com/yuhmb7/pksdw8/hyv3ir5v5gplvvgl?singleDoc#)[【报名规则】](https://yuque.antfin.com/yuhmb7/pksdw8/igri3gwp127v3v32?singleDoc#)[【情感本注意事项】](https://yuque.antfin.com/yuhmb7/pksdw8/sxs3yz5y5b00f65w?singleDoc#)
`;

      exeCommandCopyText(text);
      window.layui?.layer?.msg("已复制到剪贴板");
  }

    async function getCommentsList(list) {
        return Promise.all(
            list.map((item) => {
                return fetch(
                    `https://yuque.antfin-inc.com/api/comments/floor?commentable_type=Doc&commentable_id=${item.id}&include_section=true&include_to_user=true&include_reactions=true`,
                    {
                        headers: {
                            accept: "application/json",
                            "accept-language": "zh-CN,zh;q=0.9",
                            "content-type": "application/json",
                            "sec-ch-ua":
                            '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"',
                            "sec-ch-ua-mobile": "?0",
                            "sec-ch-ua-platform": '"macOS"',
                            "sec-fetch-dest": "empty",
                            "sec-fetch-mode": "cors",
                            "sec-fetch-site": "same-origin",
                            "x-csrf-token": "7g3LVrMMDcljwFdl3GBLLIRy",
                            "x-requested-with": "XMLHttpRequest",
                        },
                        referrerPolicy: "strict-origin-when-cross-origin",
                        body: null,
                        method: "GET",
                        mode: "cors",
                        credentials: "include",
                    }
                )
                    .then((res) => res.json())
                    .then((res) => {
                    return {
                        ...item,
                        comments: res.data.comments,
                    };
                });
            })
        );
    }

    function openActivityModal(list) {
        requestAnimationFrame(() => {
            document
                .querySelector("#murder-activity-btn")
                ?.addEventListener("click", () => {
                const fullList = list.filter((item) => item.isFull);
                const unFullList = list.filter((item) => !item.isFull);
                if (fullList.length === list.length) {
                    window.layui?.layer?.msg("所有活动已满人,无需生成 Markdown");
                    return;
                }
                const text = `
# 📢 剧本杀活动通知

---
    ${unFullList
          .map((item) => {
              return `
🎬 《${item.name}》${item.tag}${item.level ? `/${item.level}` : ""}

🕙  ${item.month}.${item.date} ${item.week} ${item.hour}:${item.minute} 📍${
          item.place
      }

💎  DM ${item.dm}【${item.persons}·${item.inputValue ?? ""}·${
          item.duration
      }】[报名](${item.url})

---

`;
      })
          .join("")}

    ${
      fullList.length
          ? `
📎  本周其他剧本活动信息
    ${list
          .filter((item) => item.isFull)
          .map((item) => {
              return `
${item.month}月${item.date}日《${item.name}》【满】
    `;
      })
          .join("")}
---

`
        : ""
          }

🔺 入门:新手友好,10推理本以内经验的玩家

🔺 进阶:中等难度,20推理本以内经验的玩家

🔺 烧脑:积极推理、全程在线、20推理本以上

🔍 务必结合自身经验和剧本难度充分评估后报名

🙋‍ [【活动须知】](https://yuque.antfin.com/yuhmb7/pksdw8/hyv3ir5v5gplvvgl?singleDoc#)[【报名规则】](https://yuque.antfin.com/yuhmb7/pksdw8/igri3gwp127v3v32?singleDoc#)[【情感本注意事项】](https://yuque.antfin.com/yuhmb7/pksdw8/sxs3yz5y5b00f65w?singleDoc#)

`;

          exeCommandCopyText(text);
          window.layui?.layer?.msg("已复制到剪贴板");
      });
    });
      layui.layer.open(
          {
              type: 1, // page 层类型
              area: ["800px", "500px"],
              title: "活动报名情况",
              shade: 0.6, // 遮罩透明度
              shadeClose: true, // 点击遮罩区域,关闭弹层
              maxmin: true, // 允许全屏最小化
              anim: 0, // 0-6 的动画形式,-1 不开启
              content: `
                    <div style="padding: 12px; height: 400px; overflow: auto;">
                      ${list
          .map((item) => {
              let manCount = 0;
              let womanCount = 0;
              let unknownCount = 0;

              item.comments.forEach((comment) => {
                  const content = chineseToArabic(
                      getInnerText(comment.body) ?? ""
                  );

                  comment.checked = true;
                  if (/[=等]/.test(content)) {
                      comment.checked = false;
                  } else if (
                      /(\d+)\s*男\s*(\d+)\s*女/.test(content)
                  ) {
                      const result = /(\d+)\s*男\s*(\d+)\s*女/.exec(
                          content
                      );
                      manCount += +result[1];
                      womanCount += +result[2];
                      console.log(result);
                  } else if (/(\d+)\s?男/.test(content)) {
                      manCount += +/(\d+)\s?男/.exec(content)[1];
                  } else if (/男[\s+]*(\d+)/.test(content)) {
                      manCount += +/男[\s+]*(\d+)/.exec(content)[1];
                  } else if (/^\+?男$/.test(content)) {
                      manCount += 1;
                  } else if (/(\d+)\s?女/.test(content)) {
                      womanCount += +/(\d+)\s?女/.exec(content)[1];
                  } else if (/女[\s+]*(\d+)/.test(content)) {
                      womanCount += +/女[\s+]*(\d+)/.exec(content)[1];
                  } else if (/^\+?女$/.test(content)) {
                      womanCount += 1;
                  } else if (/\+(\d+)/.test(content)) {
                      unknownCount += +/\+(\d+)/.exec(content)[1];
                  } else if (content === "+") {
                      unknownCount += 1;
                  } else if (/\d+/.test(content)) {
                      unknownCount += +/\d+/.exec(content)[0];
                  } else {
                      comment.checked = false;
                  }
              });

              const listHTML = item.comments
              .map((comment) => {
                  const content = getInnerText(comment.body);
                  return `<a class="${USER_ITEM_CLASS_NAME} ${
                                !comment.checked ? "unchecked" : ""
                            }" href="https://yuque.antfin-inc.com/${
                                comment.user.login
                            }" target="_blank">
                            <img src="${comment.user.avatar_url}"/>
                            <div>
                              <div>${comment.user.name}</div>
                              <div style="font-size: 12px; color: gray; margin-top: 4px;">${content}</div>
                            </div>
                          </a>`;
                            })
                          .join("");

                            const personCount =
                                  manCount + womanCount + unknownCount;
                            const status = (() => {
                                if (
                                    item.manCount &&
                                    item.womanCount &&
                                    !item.reversable
                                ) {
                                    if (
                                        manCount >= item.manCount &&
                                        womanCount >= item.womanCount
                                    ) {
                                        return `<span class="layui-badge layui-bg-green">已满人</span>`;
                                    }
                                    if (
                                        personCount >=
                                        item.manCount + item.womanCount
                                    ) {
                                        return `<span class="layui-badge layui-bg-orange">满人,但男女未满</span>`;
                                    }
                                    return `<span class="layui-badge layui-bg-red">未满人</span>`;
                                }
                                if (item.personCount) {
                                    if (personCount >= item.personCount) {
                                        return `<span class="layui-badge layui-bg-green">已满人</span>`;
                                    }
                                    return `<span class="layui-badge layui-bg-red">未满人</span>`;
                                }
                                return "";
                            })();

                            item.isFull = status.indexOf("已满人") > -1;
                            item.inputValue = (() => {
                                if (
                                    item.personCount &&
                                    personCount < item.personCount
                                ) {
                                    return `=${item.personCount - personCount}`;
                                }
                                if (
                                    item.manCount &&
                                    item.womanCount &&
                                    !item.reversable
                                ) {
                                    let result = "=";
                                    if (manCount < item.manCount) {
                                        result += `${item.manCount - manCount}男`;
                                    }
                                    if (womanCount < item.womanCount) {
                                        result += `${item.womanCount - womanCount}女`;
                                    }
                                    if (result.length > 1) {
                                        return result;
                                    }
                                }
                                return "";
                            })();

                            const operation = document.createElement("div");
                            operation.style.width = "120px";
                            const operationId = `murder-operation-${item.uuid}`;
                            operation.id = operationId;

                            operation.style =
                                "display: flex; align-items: center;text-wrap: nowrap;";

                            const updateOperation = () => {
                                const checkboxId = `murder-checkbox-${item.uuid}`;
                                const inputId = `murder-input-${item.uuid}`;
                                let innerHTML = "";
                                if (!item.isFull) {
                                    innerHTML += `<input value="${item.inputValue}" type="text" id="${inputId}" class="layui-input" style="margin-right: 6px; width: 80px;" />`;
                                }
                                innerHTML += `<input type="checkbox" id="${checkboxId}" ${
                              item.isFull ? "checked" : ""
                          } /> 满人`;
                              const target =
                                    document.querySelector(`#${operationId}`) ??
                                    operation;
                              target.innerHTML = innerHTML;
                              requestAnimationFrame(() => {
                                  document
                                      .querySelector(`#${checkboxId}`)
                                      ?.addEventListener(
                                      "change",
                                      (e) => {
                                          item.isFull = !!e.target.checked;
                                          updateOperation();
                                      },
                                      {
                                          once: true,
                                      }
                                  );

                                  document
                                      .querySelector(`#${inputId}`)
                                      ?.addEventListener("change", (e) => {
                                      item.inputValue = e.target.value;
                                      console.log("chagne", item.inputValue);
                                  });
                              });
                          };

                            updateOperation();

                            return `
                          <div class="layui-card">
                            <div class="layui-card-header" style="display: flex; justify-content: space-between;">
                              <a href="${item.url}" target="_blank">🔗 ${
                            item.title
                        }</a>
                            </div>
                            <div class="layui-card-body">
                              <div class="${USER_LIST_CLASS_NAME}">
                                ${listHTML}
                              </div>
                              <div class="layui-card-footer">
                                <span>要求:${item.persons}</span>
                                <span>当前:${manCount}男${womanCount}女${
                            unknownCount ? `${unknownCount}未知` : ""
                        },共${manCount + womanCount + unknownCount}人</span>
                                ${operation.outerHTML}
                              </div>
                            </div>
                          </div>
                        `;
                        })
          .join("")}
                    </div>
                    <div style="padding: 4px 12px; position: absolute; width: 100%; bottom: 0; left: 0; text-align: right;">
                        <button type="button" class="layui-btn" id="murder-activity-btn">生成 Markdown</button>
                    </div>
                  `,
      },
        2000
    );
  }

    function openDatePickerModal([start, end]) {
        const modalIndex = layui.layer.open(
            {
                type: 1, // page 层类型
                title: "请选择日期范围",
                shade: 0.6, // 遮罩透明度
                area: ["655px", "400px"],
                shadeClose: true, // 点击遮罩区域,关闭弹层
                maxmin: true, // 允许全屏最小化
                anim: 0, // 0-6 的动画形式,-1 不开启
                content: `
                      <div style="padding: 12px">
                          <div id="date"></div>
                      </div>
                  `,
      },
        2000
    );
      layui.laydate.render({
          elem: "#date",
          range: true,
          type: "date",
          rangeLinked: true,
          weekStart: 1,
          show: true,
          theme: "#0271BD",
          position: "static",
          value: `${start.format("YYYY-MM-DD")} - ${end.format("YYYY-MM-DD")}`,
          mark: {
              [dayjs().format("YYYY-MM-DD")]: "今天",
          },
          shortcuts: [
              {
                  text: "本周",
                  value: [
                      new Date(+dayjs().startOf("week")),
                      new Date(+dayjs().endOf("week")),
                  ],
              },
              {
                  text: "上周",
                  value: [
                      new Date(+dayjs().startOf("week").subtract(1, "week")),
                      new Date(+dayjs().endOf("week").subtract(1, "week")),
                  ],
              },
              {
                  text: "下周",
                  value: [
                      new Date(+dayjs().startOf("week").add(1, "week")),
                      new Date(+dayjs().endOf("week").add(1, "week")),
                  ],
              },
              {
                  text: "本月",
                  value: [
                      new Date(+dayjs().startOf("month")),
                      new Date(+dayjs().endOf("month")),
                  ],
              },
              // 更多选项 …
          ],
          done: function (value, startDate, endDate) {
              const [startStr, endStr] = value.split(" - ");
              timeRange = [
                  dayjs(startStr, "YYYY-MM-DD"),
                  dayjs(endStr, "YYYY-MM-DD"),
              ];
              layui.dropdown.reload(BTN_ID, {
                  data: getDropdownItems(),
              });
              layui.layer.close(modalIndex);
          },
      });
  }

    initStyle();
    initBtn();

    function getDropdownItems() {
        return [
            {
                title: "导出所有参与人员报名结果",
                id: "export all user activity",
            },
            {
                title: `日期范围:${timeRange[0].format("M-D")} - ${timeRange[1].format(
                    "M-D"
                )}`,
                disabled: true,
            },
            {
                title: `更改日期范围`,
                id: "edit date range",
            },
            {
                title: "复制活动信息 Markdown",
                id: "copy week markdown",
            },
            {
                title: "查看活动报名情况",
                id: "check sign up",
            },
        ];
    }

    layui.dropdown.render({
        elem: `#${BTN_ID}`,
        data: getDropdownItems(),
        click: async function ({ id }) {
            if (id === "export all user activity") {
                const list = await getCommentsList(await getAllActivesInfo());
                const userMap = new Map();
                list.forEach((item) => {
                    item.comments.forEach((comment) => {
                        const userName = comment.user.name;
                        userMap.set(userName, (userMap.get(userName) ?? 0) + 1);
                    });
                });

                const result = Array.from(userMap.entries()).sort(
                    (a, b) => b[1] - a[1]
                );
                const csv = `name,count\n${result
                .map((item) => `${item[0]},${item[1]}`)
                .join("\n")}`;

                downloadFile(csv, "result.csv");

                return;
            }
            let list = await getActivesInfo(...timeRange);
            if (id === "edit date range") {
                openDatePickerModal(timeRange);
            }
            if (id === "copy week markdown") {
                copyMarkdownInfo(list);
            }
            if (id === "check sign up") {
                list = await getCommentsList(list);
                openActivityModal(list);
            }
        },
    });
})();