Zhubai Toolbox 🧰

Tools for Zhubai creators.

目前為 2023-04-17 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name                 Zhubai Toolbox 🧰
// @name:zh-CN           竹白工具箱 🧰
// @namespace            https://github.com/utags/zhubai-toolbox
// @homepage             https://github.com/utags/zhubai-toolbox#readme
// @supportURL           https://github.com/utags/zhubai-toolbox/issues
// @version              0.0.3
// @description          Tools for Zhubai creators.
// @description:zh-CN    为竹白创作者的工具箱,包括订阅者信息导出,Markdown 编辑器等功能。更多功能欢迎交流。
// @icon                 https://zhubai.love/favicon.png
// @author               Pipecraft
// @license              MIT
// @match                https://zhubai.love/*
// @connect              zhubai.love
// @grant                GM_addElement
// @grant                GM_addStyle
// ==/UserScript==
//
;(() => {
  "use strict"
  var doc = document
  var createElement = (tagName, attributes) =>
    setAttributes(doc.createElement(tagName), attributes)
  var addEventListener = (element, type, listener, options) => {
    if (!element) {
      return
    }
    if (typeof type === "object") {
      for (const type1 in type) {
        if (Object.hasOwn(type, type1)) {
          element.addEventListener(type1, type[type1])
        }
      }
    } else if (typeof type === "string" && typeof listener === "function") {
      element.addEventListener(type, listener, options)
    }
  }
  var setAttribute = (element, name, value) =>
    element ? element.setAttribute(name, value) : void 0
  var setAttributes = (element, attributes) => {
    if (element && attributes) {
      for (const name in attributes) {
        if (Object.hasOwn(attributes, name)) {
          const value = attributes[name]
          if (name === "textContent") {
            element[name] = value
          } else if (name === "style") {
            setStyle(element, value, true)
          } else if (/on\w+/.test(name)) {
            const type = name.slice(2)
            addEventListener(element, type, value)
          } else {
            setAttribute(element, name, value)
          }
        }
      }
    }
    return element
  }
  var setStyle = (element, values, overwrite) => {
    if (!element) {
      return
    }
    const style = element.style
    if (typeof values === "string") {
      style.cssText = overwrite ? values : style.cssText + ";" + values
      return
    }
    if (overwrite) {
      style.cssText = ""
    }
    for (const key in values) {
      if (Object.hasOwn(values, key)) {
        style[key] = values[key].replace("!important", "")
      }
    }
  }
  if (typeof Object.hasOwn !== "function") {
    Object.hasOwn = (instance, prop) =>
      Object.prototype.hasOwnProperty.call(instance, prop)
  }
  var addElement = (parentNode, tagName, attributes) => {
    if (typeof parentNode === "string" || typeof tagName === "string") {
      const element = GM_addElement(parentNode, tagName, attributes)
      setAttributes(element, attributes)
      return element
    }
    setAttributes(tagName, attributes)
    parentNode.append(tagName)
    return tagName
  }
  var addStyle = (styleText) => GM_addStyle(styleText)
  var content_default =
    ".zbtb{width:100%;height:100%;position:fixed;top:72px;left:30px;padding:10px;background-color:#fff;font-weight:600;font-size:16px;color:#060e4b}.zbtb button{font-weight:600;font-size:16px;color:#060e4b}.zbtb button:disabled{opacity:40%}.zbtb textarea{width:90%;height:40%;padding:5px}"
  async function fetchZhubaiSubscriptions(
    page,
    subscriberEmailSet,
    subscribers
  ) {
    page = page || 1
    const url = `https://zhubai.love/api/dashboard/subscriptions?limit=20&page=${page}`
    const response = await fetch(url, {})
    console.log("Fetched page", page)
    if (response.status !== 200) {
      console.warn(response)
      return
    }
    const data = await response.json()
    for (const subscriber of data.data) {
      subscribers.push(subscriber)
      if (subscriber.subscriber_email) {
        subscriberEmailSet.add(subscriber.subscriber_email)
      }
    }
    const limit = data.pagination.limit
    const total = data.pagination.total_count
    return { limit, total }
  }
  async function main() {
    addStyle(content_default)
    const subscriberEmailSet = /* @__PURE__ */ new Set()
    const subscribers = []
    let page = 1
    let isRunning = false
    let paused = false
    const modal = addElement(document.body, "div", {
      class: "zbtb",
    })
    const toolbar = addElement(modal, "div", {
      style: "display: flex;",
    })
    const pauseButton = addElement(toolbar, "button", {
      textContent: "\u6682\u505C \u23F8\uFE0F",
      async onclick(event) {
        paused = !paused
        event.target.textContent = paused
          ? "\u7EE7\u7EED \u25B6\uFE0F"
          : "\u6682\u505C \u23F8\uFE0F"
        if (!paused) {
          await run()
        }
      },
    })
    const message = addElement(modal, "p", {
      textContent: "\u{1F680} \u6B63\u5728\u5BFC\u51FA\u6570\u636E ...",
    })
    addElement(modal, "p", {
      textContent: "\u{1F4EE} \u90AE\u7BB1\u8BA2\u9605\u5217\u8868",
    })
    const textarea = addElement(modal, "textarea")
    addElement(modal, "p", {
      textContent:
        "\u{1F4D6} \u8BE6\u7EC6\u8BA2\u9605\u7528\u6237\u6570\u636E\uFF0C\u5305\u542B\u90AE\u7BB1\u8BA2\u9605\u548C\u5FAE\u4FE1\u8BA2\u9605",
    })
    const textarea2 = addElement(modal, "textarea")
    const run = async () => {
      if (isRunning) {
        return
      }
      try {
        isRunning = true
        while (isRunning) {
          const result = await fetchZhubaiSubscriptions(
            page,
            subscriberEmailSet,
            subscribers
          )
          const total = result.total
          const limit = result.limit
          message.textContent = `\u{1F697} \u6B63\u5728\u83B7\u53D6\u6570\u636E: ${page} / ${Math.round(
            total / 20
          )}`
          if (subscribers.length > 0) {
            textarea.value = JSON.stringify([...subscriberEmailSet], null, 2)
            textarea2.value = JSON.stringify(subscribers, null, 2)
          }
          if (limit * page < total) {
            page++
            if (paused) {
              message.textContent = `\u23F8\uFE0F \u6682\u505C\u4E2D\u3002\u5DF2\u83B7\u53D6\u6570\u636E: ${
                page - 1
              } / ${Math.round(total / 20)}`
              isRunning = false
            }
          } else {
            message.textContent = `\u{1F389} \u6570\u636E\u5BFC\u51FA\u5B8C\u6BD5\u3002\u8BF7\u590D\u5236\u4E0B\u9762\u7684\u6570\u636E\uFF0C\u505A\u597D\u5907\u4EFD\u3002`
            isRunning = false
            pauseButton.disabled = true
          }
        }
      } catch (error) {
        console.error(error)
        message.innerHTML = `\u6570\u636E\u5BFC\u51FA\u5931\u8D25\uFF0C\u6709\u95EE\u9898\u8BF7\u5728 <a href="https://github.com/utags/zhubai-toolbox/issues" target="_blank">GitHub</a> \u6216 <a href="https://greasyfork.org/scripts/463934" target="_blank">Greasy Fork</a> \u53CD\u9988\u3002`
      }
    }
    await run()
  }
  var intervalId = setInterval(() => {
    const button = document.querySelector("th:nth-of-type(6) button")
    if (button && button.textContent === "\u5BFC\u5165/\u5BFC\u51FA") {
      const newButton = createElement("button", {
        textContent: "\u5BFC\u5165/\u5BFC\u51FA",
        class: "Button_button__2Ce79 Button_defaultButton__1bbUl",
      })
      button.after(newButton)
      button.remove()
      newButton.addEventListener("click", async (event) => {
        await main()
        event.preventDefault()
      })
      clearInterval(intervalId)
    }
  }, 1e3)
})()