Keylol Helper

Keylol Helper 提供其乐论坛多便捷功能支持,包括自动检测是否有其乐消息,

  1. // ==UserScript==
  2. // @name Keylol Helper
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2.8
  5. // @description Keylol Helper 提供其乐论坛多便捷功能支持,包括自动检测是否有其乐消息,
  6. // @author shiquda
  7. // @namespace https://github.com/shiquda/shiquda_UserScript
  8. // @supportURL https://github.com/shiquda/shiquda_UserScript/issues
  9. // @match *://*/*
  10. // @icon https://keylol.com/favicon.ico
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_xmlhttpRequest
  13. // @grant GM_notification
  14. // @grant GM_addStyle
  15. // @grant GM_setValue
  16. // @grant GM_getValue
  17. // @license AGPL-3.0
  18. // ==/UserScript==
  19.  
  20. (function () {
  21. 'use strict';
  22.  
  23. // 用户配置
  24. const botID = "" // 指定的ASF bot名字
  25. const message = "{:17_1010:}"// 自动回复要发送的信息,默认为阿鲁点赞
  26. const checkInterval = 5; // 检测消息间隔,单位是分钟
  27. const noticeTimeout = 10; // Win10/11通知显示时间,单位是秒
  28.  
  29.  
  30.  
  31.  
  32.  
  33. const featureInfo = [
  34. '检测是否有其乐消息',
  35. '添加自动回复功能',
  36. '抽奖自动加愿望单',
  37. '快速跳转激活key',
  38. '检查是否已回贴'
  39. ]
  40.  
  41. for (var i = 0; i < featureInfo.length; i++) {
  42. setMenu(featureInfo[i])
  43. }
  44. checkNotice()
  45. if (isInKeylol()) {
  46. noticeAlert()
  47. }
  48. if (isInThread()) {
  49. addCSS()
  50. amIReplied()
  51. autoReply()
  52. keyGiving()
  53. addList()
  54. }
  55. if (isNotice()) {
  56. addNotice()
  57. }
  58.  
  59. function setMenu(name) {
  60. const status = (GM_getValue(name) !== undefined) ? GM_getValue(name) : initialize(name);
  61. GM_registerMenuCommand(`${name}: ${status}`, () => {
  62. GM_setValue(name, !status);
  63. window.location.reload();
  64. });
  65. function initialize(name) {
  66. GM_setValue(name, true)
  67. return true
  68. }
  69. }
  70.  
  71.  
  72. // 判断是否在帖子页面
  73. function isInThread() {
  74. const url = window.location.href;
  75. return (url.includes('https://keylol.com/t') || url.includes('https://keylol.com/forum.php?mod=viewthread'))
  76. }
  77.  
  78. function isNotice() {
  79. const url = window.location.href
  80. return (url.includes('https://keylol.com/home.php?mod=space&do=notice'))
  81. }
  82.  
  83. function isInKeylol() {
  84. const url = window.location.href
  85. return (url.includes('https://keylol.com'))
  86. }
  87.  
  88. // 添加样式
  89. function addCSS() {
  90. GM_addStyle(`
  91. .s_btn {
  92. display: inline-block;
  93. padding: 9px 8px;
  94. font-size: 20px;
  95. text-align: center;
  96. text-decoration: none;
  97. border-radius: 5px;
  98. background-color: #57b9e7;
  99. color: #FFFFFF;
  100. cursor: pointer;
  101. }
  102. .s_btn:hover {
  103. background-color: #0056b3;
  104. }
  105. .s_btn:active {
  106. background-color: #003d80;
  107. }
  108. .s_btn:focus {
  109. outline: none;
  110. box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.5);
  111. }
  112. }
  113. `)
  114. }
  115. // 添加自动回复功能
  116. function autoReply() {
  117. if (!GM_getValue('添加自动回复功能')) return
  118.  
  119.  
  120. createUI1()
  121.  
  122.  
  123. function post() {
  124. document.querySelector("#post_reply").click()
  125. setTimeout(() => { document.querySelector("#postmessage").value = message }, 1000)
  126. setTimeout(() => { document.querySelector("#postsubmit").click(); }, 1050)
  127. }
  128. function createUI1() {
  129. var tab = document.querySelector("#pgt")
  130. var btn = document.createElement("button")
  131. btn.className = "s_btn"
  132. btn.textContent = "自动回帖"
  133. btn.title = `自动回复:${message}`
  134. btn.addEventListener("click", start)
  135. tab.appendChild(btn)
  136. }
  137. function start() {
  138. console.log("start")
  139. post()
  140. }
  141. }
  142.  
  143. // 快速跳转激活key
  144. function keyGiving() {
  145. if (!GM_getValue('快速跳转激活key')) return
  146. if (document.querySelector(".subforum_left_title_left_up").innerText.indexOf("[明Key]") > -1) {
  147. const url = 'https://store.steampowered.com/account/registerkey'
  148. const tab = document.querySelector("#pgt")
  149. const container = document.createElement('button');
  150. container.classList.add('s_btn');
  151. container.textContent = '点击跳转到 Steam 激活页面';
  152. container.addEventListener("click", () => { window.open(url) })
  153. tab.appendChild(container)
  154. }
  155. }
  156.  
  157. // 抽奖自动加愿望单
  158. function addList() {
  159.  
  160. if (!GM_getValue('抽奖自动加愿望单')) return
  161.  
  162. if (judge()) {
  163. createUI2()
  164. }
  165.  
  166. function judge() {
  167. return (document.querySelector(".subforum_left_title_left_up").innerText.indexOf("[活动推广]") > -1)
  168. }
  169. function getAppID(urls) {
  170. var AppIDs = []
  171. if (urls.length === 0) {
  172. return AppIDs; // 如果urls数组为空,直接返回空数组
  173. }
  174. for (var i = 0; i < urls.length; i++) {
  175. var AppID = urls[i].src.match(/^https:\/\/store\.steampowered\.com\/widget\/(\d+)/)
  176. if (AppID && AppID[1]) {
  177. AppIDs.push(AppID[1])
  178. }
  179. }
  180. AppIDs = [...new Set(AppIDs)]
  181. return AppIDs
  182. }
  183.  
  184. function createUI2() {
  185. let urls = document.querySelectorAll("iframe")
  186. const IDList = getAppID(urls)
  187. console.log(IDList)
  188. if (IDList.length === 0) { return }
  189. var txt = `ADDWISHLIST ${botID} `;
  190. for (var i = 0; i < IDList.length; i++) {
  191. txt += (IDList[i] + ",")
  192. }
  193. txt = txt.slice(0, -1)
  194. const tab = document.querySelector("#pgt")
  195. const container = document.createElement('button');
  196. container.classList.add('s_btn');
  197. container.textContent = '复制愿望单ASF代码';
  198. container.addEventListener("click", () => { setCopy(txt, `ASF代码复制成功:${txt}`) })
  199. tab.appendChild(container)
  200. }
  201. }
  202.  
  203.  
  204. // 检测是否有其乐消息
  205. function checkNotice() {
  206. if (!GM_getValue('检测是否有其乐消息')) return
  207. let t = getBriefTime()
  208. var lastExecutionTimestamp = GM_getValue("lastExecutionTimestamp");
  209. var currentTimestamp = new Date().getTime();
  210. if (
  211. !lastExecutionTimestamp ||
  212. currentTimestamp - lastExecutionTimestamp > checkInterval * 60 * 1000
  213. ) check()
  214. else {
  215. console.log(`${t} 时间间隔在${checkInterval}分钟内.`);
  216. return;
  217. }
  218. setTimeout(check, (checkInterval * 60 + 1) * 1000)
  219. function check() {
  220. let t = getBriefTime()
  221. GM_xmlhttpRequest({
  222. method: "GET",
  223. url: "https://keylol.com/home.php?mod=space&do=notice",
  224. onload: function (response) {
  225. // 在响应中查找具有"btn-user-action-highlight"类的元素
  226. var responseHTML = response.responseText;
  227. var parser = new DOMParser();
  228. var doc = parser.parseFromString(responseHTML, "text/html");
  229. var elements = doc.getElementsByClassName(
  230. "btn-user-action-highlight"
  231. );
  232. if (elements.length > 0) {
  233. var newNotices = doc.querySelectorAll('[style="color:#000;font-weight:bold;"]')
  234. var noticeID = GM_getValue("noticeID") ? GM_getValue("noticeID") : []
  235. for (var i = 0; i < newNotices.length; i++) {
  236. noticeID.push(newNotices[i].parentElement.getAttribute("notice"))
  237. }
  238. GM_setValue("noticeID", noticeID)
  239. GM_notification({
  240. text: "点击前往", // 通知的文本内容
  241. title: `Keylol有消息!`, // 通知的标题(可选)
  242. timeout: 1000 * noticeTimeout, // 通知显示的时间(毫秒),过了时间后通知会自动关闭(可选)
  243. onclick: function () {
  244. window.open(
  245. "https://keylol.com/home.php?mod=space&do=notice"
  246. );
  247. },
  248. });
  249. } else {
  250. console.log(`${t} keylol没有通知.`);
  251. }
  252. },
  253. onerror: function (error) {
  254. console.error(error); // 处理错误
  255. },
  256. });
  257. GM_setValue("lastExecutionTimestamp", currentTimestamp);
  258. }
  259. function getBriefTime(d) {
  260. d = d ? new Date() : new Date();
  261. // 获取小时、分钟和秒
  262. let hours = d.getHours();
  263. let minutes = d.getMinutes();
  264. let seconds = d.getSeconds();
  265. // 格式化小时、分钟和秒,确保它们始终是两位数
  266. hours = (hours < 10 ? "0" : "") + hours;
  267. minutes = (minutes < 10 ? "0" : "") + minutes;
  268. seconds = (seconds < 10 ? "0" : "") + seconds;
  269. const currentTime = hours + ":" + minutes + ":" + seconds;
  270. // 返回当前时间
  271. return currentTime;
  272. }
  273. }
  274.  
  275. function noticeAlert() {
  276. if (!GM_getValue('检测是否有其乐消息')) return
  277. if (GM_getValue("noticeID")?.length > 0) {
  278. // console.log(GM_getValue("noticeID"))
  279. // 寻找提醒元素
  280. const elements = document.querySelectorAll(".btn-user-action");
  281. for (let i = 0; i < elements.length; i++) {
  282. const element = elements[i];
  283. if (element.textContent.trim() === "提醒") {
  284. element.classList.add("btn-user-action-highlight")
  285. break
  286. }
  287. }
  288. }
  289. }
  290.  
  291. function addNotice() {
  292. if (!GM_getValue('检测是否有其乐消息')) return
  293. const list = GM_getValue("noticeID")
  294. console.log(list);
  295. for (var i = 0; i < list.length; i++) {
  296. document.querySelector(`[notice="${list[i]}"]`).querySelector(".ntc_body").style = "color:#000;font-weight:bold;"
  297. }
  298. GM_setValue("noticeID", [])
  299. }
  300.  
  301. // 检测帖子是否已回复 感谢@chr_
  302. async function amIReplied() {
  303. "use strict";
  304. if (!GM_getValue('检查是否已回贴')) return
  305.  
  306. if ((location.pathname === "/forum.php" && !location.search.includes("tid")) || location.search.includes("authorid")) {
  307. return;
  308. }
  309.  
  310. const inlineMode = window.localStorage.getItem("air_inline") ?? "关";
  311.  
  312. const isDiscuz = typeof discuz_uid != "undefined";
  313.  
  314. const userId = isDiscuz ? discuz_uid : __CURRENT_UID;
  315.  
  316. const testUrl = location.href + (location.search ? `&authorid=${userId}` : `?authorid=${userId}`);
  317.  
  318. fetch(testUrl)
  319. .then((res) => res.text())
  320. .then((html) => {
  321. const replied = !(html.includes("未定义操作") || html.includes("ERROR:"));
  322.  
  323. const text = replied ? "✅已经回过贴了" : "❌还没回过贴子";
  324.  
  325. const tips = document.createElement("a");
  326. tips.textContent = text;
  327. if (replied) {
  328. tips.href = testUrl;
  329. } else {
  330. tips.addEventListener("click", () => {
  331. if (isDiscuz) {
  332. showError("❌还没回过贴子");
  333. }
  334. else {
  335. alert("❌还没回过贴子");
  336. }
  337. });
  338. }
  339.  
  340. if (isDiscuz) {
  341. const btnArea = inlineMode !== "开" ?
  342. document.querySelector("#pgt") :
  343. document.querySelector("#postlist td.plc div.authi>span.none") ??
  344. document.querySelector("#postlist td.plc div.authi>span.pipe");
  345.  
  346. if (btnArea === null) {
  347. return;
  348. }
  349.  
  350. if (btnArea.tagName === "SPAN") {
  351. const span = document.createElement("span");
  352. span.textContent = "|";
  353. span.className = "pipe";
  354. const bar = btnArea.parentNode;
  355. bar.insertBefore(span, btnArea);
  356. bar.insertBefore(tips, btnArea);
  357. } else {
  358. btnArea.appendChild(tips);
  359. }
  360. } else {
  361. const btnArea = document.querySelector("#m_nav>.nav");
  362. const anchor = btnArea.querySelector("div.clear");
  363.  
  364. if (btnArea === null || anchor === null) {
  365. return;
  366. }
  367.  
  368. tips.className = "nav_link";
  369. btnArea.insertBefore(tips, anchor);
  370. }
  371.  
  372. });
  373. }
  374. }
  375. )();

QingJ © 2025

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