AcFun 自动换头像框

有很多头像框能用,所以就是想切着玩

  1. // ==UserScript==
  2. // @name AcFun 自动换头像框
  3. // @namespace https://github.com/DaddyTrap
  4. // @version 0.1
  5. // @description 有很多头像框能用,所以就是想切着玩
  6. // @author PlusC
  7. // @match https://*.acfun.cn/*
  8. // @icon https://cdn.aixifan.com/ico/favicon.ico
  9. // @connect www.acfun.cn
  10. // @grant GM_addStyle
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @license MIT
  15. // ==/UserScript==
  16.  
  17. // 脚本内出现的 "SAF" / "saf" 是 switch avatar frame 的意思,仅用于区分命名
  18.  
  19. (function() {
  20. 'use strict';
  21.  
  22. const NAV_ITEM_HTML = `<a href="javascript:void(0)" class="ac-member-navigation-item"><span class="ac-icon"><i class="iconfont"></i></span>切换头像框设置</a>`
  23.  
  24. // source: https://stackoverflow.com/a/35385518
  25. /**
  26. * @param {String} HTML representing a single element
  27. * @return {HTMLElement}
  28. */
  29. function htmlToElement(html) {
  30. var template = document.createElement('template');
  31. html = html.trim(); // Never return a text node of whitespace as the result
  32. template.innerHTML = html;
  33. return template.content.firstChild;
  34. }
  35.  
  36. const SETTING_DIALOG_HTML = `
  37. <div class="saf-modal">
  38. <div class="saf-modal-content">
  39. <form>
  40. <label>输入在哪些头像框id中轮换 (每行一个id)</label><br>
  41. <textarea name="frame_list" id="saf-input-frame-list" cols="30" rows="10"></textarea><br>
  42. <input type="checkbox" name="random_switch" id="saf-input-random-switch">
  43. <label>是否随机切换 (不勾选表示按顺序轮换)</label><br>
  44. <label>切换间隔 (分钟为单位)</label>
  45. <input type="text" name="switch_interval" id="saf-input-switch-interval"><br>
  46. </form>
  47. <button id="saf-confirm-setting">确定</button>
  48. </div>
  49. </div>
  50. `
  51.  
  52. const SETTING_STYLE = `
  53. /* The Modal (background) */
  54. .saf-modal {
  55. display: none; /* Hidden by default */
  56. position: fixed; /* Stay in place */
  57. z-index: 1; /* Sit on top */
  58. left: 0;
  59. top: 0;
  60. width: 100%; /* Full width */
  61. height: 100%; /* Full height */
  62. overflow: auto; /* Enable scroll if needed */
  63. background-color: rgb(0,0,0); /* Fallback color */
  64. background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
  65. }
  66.  
  67. /* Modal Content/Box */
  68. .saf-modal-content {
  69. background-color: #fefefe;
  70. margin: 15% auto; /* 15% from the top and centered */
  71. padding: 20px;
  72. border: 1px solid #888;
  73. width: 80%; /* Could be more or less, depending on screen size */
  74. }
  75. `
  76.  
  77. /**
  78. * Find setting in dialog and save
  79. * @param {HTMLElement} settingDialog The dialog element.
  80. */
  81. function confirmCurrentSetting(settingDialog) {
  82. const frameListStr = settingDialog.querySelector('#saf-input-frame-list').value
  83. const frameList = frameListStr.split(/\r?\n/).map((e)=>parseInt(e))
  84. const isRandom = settingDialog.querySelector("#saf-input-random-switch").checked
  85. const switchIntervalStr = settingDialog.querySelector("#saf-input-switch-interval").value
  86. const switchInterval = parseFloat(switchIntervalStr)
  87. if (frameList.length <= 0) {
  88. alert(`当前选用头像框列表为空!请检查后再确定`)
  89. return
  90. }
  91. if (switchInterval < 5) {
  92. alert(`间隔时间设置为5分钟以上吧~`)
  93. return
  94. }
  95. // 尽量恢复一下上次轮换的状态
  96. // 如果老的轮换到的头像框id能在新列表中找到了,则将新index定位到该位置
  97. const oldIndex = GM_getValue('currIndex', null)
  98. const oldFrameList = GM_getValue('frameList', null)
  99. let newCurrIndex = 0
  100. if (oldIndex !== null && oldFrameList !== null) {
  101. const oldFrameId = oldFrameList[oldIndex]
  102. const tempNewIndex = frameList.indexOf(oldFrameId)
  103. if (tempNewIndex >= 0) {
  104. newCurrIndex = tempNewIndex
  105. }
  106. }
  107. GM_setValue('currIndex', newCurrIndex)
  108. GM_setValue('frameList', frameList)
  109. GM_setValue('isRandom', isRandom)
  110. GM_setValue('switchInterval', switchInterval)
  111. settingDialog.style.display = 'none'
  112. alert('切头像框设置完成!')
  113. }
  114.  
  115. function loadSetttingToUI(settingDialog) {
  116. const frameList = GM_getValue('frameList', [])
  117. const isRandom = GM_getValue('isRandom', false)
  118. const switchInterval = GM_getValue('switchInterval', 60)
  119. settingDialog.querySelector('#saf-input-frame-list').value = frameList.join('\n')
  120. settingDialog.querySelector("#saf-input-random-switch").checked = isRandom
  121. settingDialog.querySelector("#saf-input-switch-interval").value = switchInterval
  122. }
  123.  
  124. function createSettingUI() {
  125. console.log('[SAF] createSettingUI')
  126. GM_addStyle(SETTING_STYLE)
  127. const navbar = document.querySelector('.ac-member-navigation')
  128. const navItem = htmlToElement(NAV_ITEM_HTML)
  129. navbar.appendChild(navItem)
  130. const dialog = htmlToElement(SETTING_DIALOG_HTML)
  131. loadSetttingToUI(dialog)
  132. document.body.appendChild(dialog)
  133. // 侧边栏按钮点击事件
  134. navItem.addEventListener('click', () => {
  135. dialog.style.display = 'block'
  136. })
  137. // dialog外被点击时关闭dialog
  138. // TODO: 二次确认防止手误
  139. window.addEventListener('click', (event) => {
  140. if (event.target == dialog) {
  141. dialog.style.display = 'none'
  142. }
  143. })
  144. // dialog确认按钮
  145. dialog.querySelector('#saf-confirm-setting').addEventListener('click', () => {
  146. confirmCurrentSetting(dialog)
  147. })
  148. }
  149.  
  150. function doSwitchFrame() {
  151. const frameList = GM_getValue('frameList', [])
  152. if (frameList.length <= 0) {
  153. console.log('[SAF] frameList is empty, skip switch frame')
  154. return
  155. }
  156. const isRandom = GM_getValue('isRandom', false)
  157. const currIndex = GM_getValue('currIndex', 0)
  158. let nextIndex = 0
  159. if (!isRandom) {
  160. // 非随机,则取下一个
  161. nextIndex = (currIndex + 1) % frameList.length
  162. } else {
  163. // 随机
  164. nextIndex = Math.floor(Math.random() * frameList.length)
  165. }
  166. const nextFrameId = frameList[nextIndex]
  167. const nowTs = Date.now()
  168. GM_setValue('lastSwitchTs', nowTs)
  169. console.log(`[SAF] Ready to send request !!! data: productId=${nextFrameId}`)
  170. // const formData = new FormData()
  171. // formData.append('productId', nextFrameId)
  172. GM_xmlhttpRequest({
  173. method: 'POST',
  174. headers: {
  175. 'Content-Type': 'application/x-www-form-urlencoded',
  176. 'Origin': 'https://www.acfun.cn',
  177. 'Referer': 'https://www.acfun.cn/member/mall?tab=items',
  178. },
  179. url: 'https://www.acfun.cn/rest/pc-direct/shop/useItem',
  180. data: `productId=${nextFrameId}`,
  181. onload: (response) => {
  182. console.debug(response)
  183. GM_setValue('currIndex', nextIndex)
  184. }
  185. })
  186. }
  187.  
  188. // 每次访问页面都调用,检查是否达到切换间隔,到时间了才会真正切头像
  189. function trySwitchFrame() {
  190. console.log('[SAF] trySwitchFrame')
  191. const lastSwitchTs = GM_getValue('lastSwitchTs', 0)
  192. const nowTs = Date.now()
  193. const switchInterval = GM_getValue('switchInterval', null)
  194. if (typeof(switchInterval) !== 'number') {
  195. console.log('[SAF] switchInterval not set, skip switch frame')
  196. return
  197. }
  198. if (nowTs - lastSwitchTs <= switchInterval * 60 * 1000) {
  199. // 间隔还没到
  200. console.log(`[SAF] interval: ${switchInterval} (${switchInterval * 60 * 1000}), delta: ${nowTs - lastSwitchTs}`)
  201. return
  202. }
  203. doSwitchFrame()
  204. }
  205.  
  206. function safOnload() {
  207. console.log('[SAF] Switch Avatar Frame Onload !!')
  208. trySwitchFrame()
  209. if (window.location.pathname.startsWith('/member')) {
  210. createSettingUI()
  211. }
  212. }
  213.  
  214. window.addEventListener('load', safOnload)
  215.  
  216. console.log(`[SAF] All set values:
  217. currIndex: ${GM_getValue('currIndex', null)}
  218. frameList: ${GM_getValue('frameList', null)}
  219. isRandom: ${GM_getValue('isRandom', null)}
  220. switchInterval: ${GM_getValue('switchInterval', null)}
  221. lastSwitchTs: ${GM_getValue('lastSwitchTs', null)}
  222. `)
  223.  
  224. })();

QingJ © 2025

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