// ==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.2
// @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
// ==/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
}
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() {
const modal = addElement(document.body, "div", {
style:
"width: 100%; height: 100%; position: fixed; top: 72px; left: 30px; padding: 10px; background-color: #fff;",
})
const message = addElement(modal, "p", {
style: "font-weight: 600; font-size: 16px; color: #060e4b;",
textContent: "\u{1F680} \u6B63\u5728\u5BFC\u51FA\u6570\u636E ...",
})
addElement(modal, "p", {
style: "font-weight: 600; font-size: 16px; color: #060e4b;",
textContent: "\u{1F4EE} \u90AE\u7BB1\u8BA2\u9605\u5217\u8868",
})
const textarea = addElement(modal, "textarea", {
style: "width: 90%; height: 40%; padding: 5px;",
})
addElement(modal, "p", {
style: "font-weight: 600; font-size: 16px; color: #060e4b;",
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", {
style: "width: 90%; height: 40%; padding: 5px;",
})
const subscriberEmailSet = /* @__PURE__ */ new Set()
const subscribers = []
let page = 1
try {
while (true) {
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++
} else {
break
}
}
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`
} 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`
}
}
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", (event) => {
main()
event.preventDefault()
})
clearInterval(intervalId)
}
}, 1e3)
})()