竹白工具箱 🧰

为竹白创作者的工具箱,包括订阅者信息导出,Markdown 编辑器等功能。更多功能欢迎交流。

当前为 2023-04-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Zhubai Toolbox 🧰
  3. // @name:zh-CN 竹白工具箱 🧰
  4. // @namespace https://github.com/utags/zhubai-toolbox
  5. // @homepage https://github.com/utags/zhubai-toolbox#readme
  6. // @supportURL https://github.com/utags/zhubai-toolbox/issues
  7. // @version 0.0.2
  8. // @description Tools for Zhubai creators.
  9. // @description:zh-CN 为竹白创作者的工具箱,包括订阅者信息导出,Markdown 编辑器等功能。更多功能欢迎交流。
  10. // @icon https://zhubai.love/favicon.png
  11. // @author Pipecraft
  12. // @license MIT
  13. // @match https://zhubai.love/*
  14. // @connect zhubai.love
  15. // @grant GM_addElement
  16. // ==/UserScript==
  17. //
  18. ;(() => {
  19. "use strict"
  20. var doc = document
  21. var createElement = (tagName, attributes) =>
  22. setAttributes(doc.createElement(tagName), attributes)
  23. var addEventListener = (element, type, listener, options) => {
  24. if (!element) {
  25. return
  26. }
  27. if (typeof type === "object") {
  28. for (const type1 in type) {
  29. if (Object.hasOwn(type, type1)) {
  30. element.addEventListener(type1, type[type1])
  31. }
  32. }
  33. } else if (typeof type === "string" && typeof listener === "function") {
  34. element.addEventListener(type, listener, options)
  35. }
  36. }
  37. var setAttribute = (element, name, value) =>
  38. element ? element.setAttribute(name, value) : void 0
  39. var setAttributes = (element, attributes) => {
  40. if (element && attributes) {
  41. for (const name in attributes) {
  42. if (Object.hasOwn(attributes, name)) {
  43. const value = attributes[name]
  44. if (name === "textContent") {
  45. element[name] = value
  46. } else if (name === "style") {
  47. setStyle(element, value, true)
  48. } else if (/on\w+/.test(name)) {
  49. const type = name.slice(2)
  50. addEventListener(element, type, value)
  51. } else {
  52. setAttribute(element, name, value)
  53. }
  54. }
  55. }
  56. }
  57. return element
  58. }
  59. var setStyle = (element, values, overwrite) => {
  60. if (!element) {
  61. return
  62. }
  63. const style = element.style
  64. if (typeof values === "string") {
  65. style.cssText = overwrite ? values : style.cssText + ";" + values
  66. return
  67. }
  68. if (overwrite) {
  69. style.cssText = ""
  70. }
  71. for (const key in values) {
  72. if (Object.hasOwn(values, key)) {
  73. style[key] = values[key].replace("!important", "")
  74. }
  75. }
  76. }
  77. if (typeof Object.hasOwn !== "function") {
  78. Object.hasOwn = (instance, prop) =>
  79. Object.prototype.hasOwnProperty.call(instance, prop)
  80. }
  81. var addElement = (parentNode, tagName, attributes) => {
  82. if (typeof parentNode === "string" || typeof tagName === "string") {
  83. const element = GM_addElement(parentNode, tagName, attributes)
  84. setAttributes(element, attributes)
  85. return element
  86. }
  87. setAttributes(tagName, attributes)
  88. parentNode.append(tagName)
  89. return tagName
  90. }
  91. async function fetchZhubaiSubscriptions(
  92. page,
  93. subscriberEmailSet,
  94. subscribers
  95. ) {
  96. page = page || 1
  97. const url = `https://zhubai.love/api/dashboard/subscriptions?limit=20&page=${page}`
  98. const response = await fetch(url, {})
  99. console.log("Fetched page", page)
  100. if (response.status !== 200) {
  101. console.warn(response)
  102. return
  103. }
  104. const data = await response.json()
  105. for (const subscriber of data.data) {
  106. subscribers.push(subscriber)
  107. if (subscriber.subscriber_email) {
  108. subscriberEmailSet.add(subscriber.subscriber_email)
  109. }
  110. }
  111. const limit = data.pagination.limit
  112. const total = data.pagination.total_count
  113. return { limit, total }
  114. }
  115. async function main() {
  116. const modal = addElement(document.body, "div", {
  117. style:
  118. "width: 100%; height: 100%; position: fixed; top: 72px; left: 30px; padding: 10px; background-color: #fff;",
  119. })
  120. const message = addElement(modal, "p", {
  121. style: "font-weight: 600; font-size: 16px; color: #060e4b;",
  122. textContent: "\u{1F680} \u6B63\u5728\u5BFC\u51FA\u6570\u636E ...",
  123. })
  124. addElement(modal, "p", {
  125. style: "font-weight: 600; font-size: 16px; color: #060e4b;",
  126. textContent: "\u{1F4EE} \u90AE\u7BB1\u8BA2\u9605\u5217\u8868",
  127. })
  128. const textarea = addElement(modal, "textarea", {
  129. style: "width: 90%; height: 40%; padding: 5px;",
  130. })
  131. addElement(modal, "p", {
  132. style: "font-weight: 600; font-size: 16px; color: #060e4b;",
  133. textContent:
  134. "\u{1F4D6} \u8BE6\u7EC6\u8BA2\u9605\u7528\u6237\u6570\u636E\uFF0C\u5305\u542B\u90AE\u7BB1\u8BA2\u9605\u548C\u5FAE\u4FE1\u8BA2\u9605",
  135. })
  136. const textarea2 = addElement(modal, "textarea", {
  137. style: "width: 90%; height: 40%; padding: 5px;",
  138. })
  139. const subscriberEmailSet = /* @__PURE__ */ new Set()
  140. const subscribers = []
  141. let page = 1
  142. try {
  143. while (true) {
  144. const result = await fetchZhubaiSubscriptions(
  145. page,
  146. subscriberEmailSet,
  147. subscribers
  148. )
  149. const total = result.total
  150. const limit = result.limit
  151. message.textContent = `\u{1F697} \u6B63\u5728\u83B7\u53D6\u6570\u636E: ${page} / ${Math.round(
  152. total / 20
  153. )}`
  154. if (subscribers.length > 0) {
  155. textarea.value = JSON.stringify([...subscriberEmailSet], null, 2)
  156. textarea2.value = JSON.stringify(subscribers, null, 2)
  157. }
  158. if (limit * page < total) {
  159. page++
  160. } else {
  161. break
  162. }
  163. }
  164. 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`
  165. } catch (error) {
  166. console.error(error)
  167. 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`
  168. }
  169. }
  170. var intervalId = setInterval(() => {
  171. const button = document.querySelector("th:nth-of-type(6) button")
  172. if (button && button.textContent === "\u5BFC\u5165/\u5BFC\u51FA") {
  173. const newButton = createElement("button", {
  174. textContent: "\u5BFC\u5165/\u5BFC\u51FA",
  175. class: "Button_button__2Ce79 Button_defaultButton__1bbUl",
  176. })
  177. button.after(newButton)
  178. button.remove()
  179. newButton.addEventListener("click", (event) => {
  180. main()
  181. event.preventDefault()
  182. })
  183. clearInterval(intervalId)
  184. }
  185. }, 1e3)
  186. })()