剧本杀活动通知生成器

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

目前为 2024-07-15 提交的版本。查看 最新版本

// ==UserScript==
// @name         剧本杀活动通知生成器
// @namespace    https://github.com/heiyexing
// @version      2024-07-16
// @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);
      }
    },
  });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址