Auto Like Specific User base on activity

自动点赞特定用户,适用于discourse

  1. // ==UserScript==
  2. // @name Auto Like Specific User base on activity
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1.2
  5. // @description 自动点赞特定用户,适用于discourse
  6. // @author liuweiqing
  7. // @match https://meta.discourse.org/*
  8. // @match https://linux.do/*
  9. // @match https://meta.appinn.net/*
  10. // @match https://community.openai.com/
  11. // @grant none
  12. // @license MIT
  13. // @icon https://www.google.com/s2/favicons?domain=linux.do
  14. // @run-at document-end
  15. // ==/UserScript==
  16.  
  17. (function () {
  18. ("use strict");
  19. // 定义可能的基本URL
  20. const possibleBaseURLs = [
  21. "https://meta.discourse.org",
  22. "https://linux.do",
  23. "https://meta.appinn.net",
  24. "https://community.openai.com",
  25. ];
  26. const commentLimit = 1000;
  27. const specificUserPostListLimit = 100;
  28. const currentURL = window.location.href;
  29. let specificUser = localStorage.getItem("specificUser") || "14790897";
  30. let likeLimit = parseInt(localStorage.getItem("likeLimit") || 200, 10);
  31. let BASE_URL = possibleBaseURLs.find((url) => currentURL.startsWith(url));
  32.  
  33. // 环境变量:阅读网址,如果没有找到匹配的URL,则默认为第一个
  34. if (!BASE_URL) {
  35. BASE_URL = possibleBaseURLs[0];
  36. console.log("当前BASE_URL设置为(默认): " + BASE_URL);
  37. } else {
  38. console.log("当前BASE_URL是: " + BASE_URL);
  39. }
  40. // 获取当前时间戳
  41. const currentTime = Date.now();
  42. // 获取存储的时间戳
  43. const defaultTimestamp = new Date("1999-01-01T00:00:00Z").getTime(); //默认值为1999年
  44. const storedTime = parseInt(
  45. localStorage.getItem("clickCounterTimestamp") ||
  46. defaultTimestamp.toString(),
  47. 10
  48. );
  49.  
  50. // 获取当前的点击计数,如果不存在则初始化为0
  51. let clickCounter = parseInt(localStorage.getItem("clickCounter") || "0", 10);
  52. // 检查是否超过12小时(12小时 = 12 * 60 * 60 * 1000 毫秒)
  53. if (currentTime - storedTime > 12 * 60 * 60 * 1000) {
  54. // 超过24小时,清空点击计数器并更新时间戳
  55. clickCounter = 0;
  56. localStorage.setItem("clickCounter", "0");
  57. localStorage.setItem("clickCounterTimestamp", currentTime.toString());
  58. }
  59.  
  60. console.log(`Initial clickCounter: ${clickCounter}`);
  61. // 入口函数
  62. window.addEventListener("load", () => {
  63. console.log("autoRead", localStorage.getItem("read"));
  64. checkFirstRun();
  65. if (localStorage.getItem("read") === "true") {
  66. console.log("点赞开始");
  67. setTimeout(() => {
  68. likeSpecificPost();
  69. }, 2000);
  70. setTimeout(() => {
  71. openSpecificUserPost();
  72. }, 4000);
  73. }
  74. });
  75. function checkFirstRun() {
  76. if (localStorage.getItem("isFirstRun") === null) {
  77. console.log("脚本第一次运行,执行初始化操作...");
  78. updateInitialData();
  79. localStorage.setItem("isFirstRun", "false");
  80. } else {
  81. console.log("脚本非第一次运行");
  82. }
  83. }
  84.  
  85. function updateInitialData() {
  86. localStorage.setItem("read", "false"); // 开始时自动滚动关闭
  87. console.log("执行了初始数据更新操作");
  88. }
  89.  
  90. function getLatestTopic() {
  91. let lastOffset = Number(localStorage.getItem("lastOffset")) || 0;
  92. let specificUserPostList = [];
  93. let isDataSufficient = false;
  94.  
  95. while (!isDataSufficient) {
  96. lastOffset += 20;
  97. // lastOffset += 1; //对于page来说
  98. // 举例:https://linux.do/user_actions.json?offset=0&username=14790897&filter=5
  99. const url = `${BASE_URL}/user_actions.json?offset=${lastOffset}&username=${specificUser}&filter=5`;
  100. //举例:https://linux.do/search?q=%4014790897%20in%3Aunseen
  101. // const url = `${BASE_URL}/search?q=%40${specificUser}%20in%3Aunseen`; //&page=${lastOffset}
  102. $.ajax({
  103. url: url,
  104. async: false,
  105. headers: {
  106. Accept: "application/json",
  107. },
  108. success: function (result) {
  109. if (result && result.user_actions && result.user_actions.length > 0) {
  110. result.user_actions.forEach((action) => {
  111. // if (result && result.posts && result.posts.length > 0) {
  112. // result.posts.forEach((action) => {
  113. const topicId = action.topic_id;
  114. // const postId = action.post_id;
  115. const postNumber = action.post_number;
  116. specificUserPostList.push({
  117. topic_id: topicId,
  118. // post_id: postId,
  119. post_number: postNumber,
  120. });
  121. });
  122.  
  123. // 检查是否已获得足够的 Posts
  124. if (specificUserPostList.length >= specificUserPostListLimit) {
  125. isDataSufficient = true;
  126. }
  127. } else {
  128. isDataSufficient = true; // 没有更多内容时停止请求
  129. }
  130. },
  131. error: function (XMLHttpRequest, textStatus, errorThrown) {
  132. console.error(XMLHttpRequest, textStatus, errorThrown);
  133. isDataSufficient = true; // 遇到错误时也停止请求
  134. },
  135. });
  136. }
  137.  
  138. // 如果列表超出限制,则截断
  139. if (specificUserPostList.length > specificUserPostListLimit) {
  140. specificUserPostList = specificUserPostList.slice(
  141. 0,
  142. specificUserPostListLimit
  143. );
  144. }
  145.  
  146. // 存储 lastOffset 和 specificUserPostList 到 localStorage
  147. localStorage.setItem("lastOffset", lastOffset);
  148. localStorage.setItem(
  149. "specificUserPostList",
  150. JSON.stringify(specificUserPostList)
  151. );
  152. }
  153.  
  154. function openSpecificUserPost() {
  155. let specificUserPostListStr = localStorage.getItem("specificUserPostList");
  156. let specificUserPostList = specificUserPostListStr
  157. ? JSON.parse(specificUserPostListStr)
  158. : [];
  159.  
  160. // 如果列表为空,则获取最新文章
  161. if (specificUserPostList.length === 0) {
  162. getLatestTopic();
  163. specificUserPostListStr = localStorage.getItem("specificUserPostList");
  164. specificUserPostList = specificUserPostListStr
  165. ? JSON.parse(specificUserPostListStr)
  166. : [];
  167. }
  168.  
  169. // 如果获取到新文章,打开第一个
  170. if (specificUserPostList.length > 0) {
  171. const post = specificUserPostList.shift(); // 获取列表中的第一个对象
  172. localStorage.setItem(
  173. "specificUserPostList",
  174. JSON.stringify(specificUserPostList)
  175. );
  176.  
  177. window.location.href = `${BASE_URL}/t/topic/${post.topic_id}/${post.post_number}`;
  178. } else {
  179. console.error("未能获取到新的帖子数据。");
  180. }
  181. }
  182.  
  183. // 检查是否点赞
  184. // const postId = data.post_id;
  185.  
  186. // const targetId = `discourse-reactions-counter-${postId}-right`;
  187.  
  188. // const element = document.getElementById(targetId);
  189. function likeSpecificPost() {
  190. const urlParts = window.location.pathname.split("/");
  191. const lastPart = urlParts[urlParts.length - 1]; // 获取最后一部分
  192. let buttons, reactionButton;
  193. console.log("post number:", lastPart);
  194. if (lastPart < 10000) {
  195. buttons = document.querySelectorAll(
  196. "button[aria-label]" //[class*='reply']
  197. );
  198.  
  199. let targetButton = null;
  200. buttons.forEach((button) => {
  201. const ariaLabel = button.getAttribute("aria-label");
  202. if (ariaLabel && ariaLabel.includes(`#${lastPart}`)) {
  203. targetButton = button;
  204. console.log("找到post number按钮:", targetButton);
  205. return;
  206. }
  207. });
  208.  
  209. if (targetButton) {
  210. // 找到按钮后,获取其父级元素
  211. const parentElement = targetButton.parentElement;
  212. console.log("父级元素:", parentElement);
  213. reactionButton = parentElement.querySelector(
  214. ".discourse-reactions-reaction-button"
  215. );
  216. } else {
  217. console.log(`未找到包含 #${lastPart} 的按钮`);
  218. }
  219. } else {
  220. //大于10000说明是主题帖,选择第一个
  221. reactionButton = document.querySelectorAll(
  222. ".discourse-reactions-reaction-button"
  223. )[0];
  224. }
  225. if (
  226. reactionButton.title !== "点赞此帖子" &&
  227. reactionButton.title !== "Like this post"
  228. ) {
  229. console.log("已经点赞过");
  230. return "already liked";
  231. } else if (clickCounter >= likeLimit) {
  232. console.log("已经达到点赞上限");
  233. localStorage.setItem("read", false);
  234. return;
  235. }
  236. triggerClick(reactionButton);
  237. clickCounter++;
  238. console.log(
  239. `Clicked like button ${clickCounter},已点赞用户${specificUser}`
  240. );
  241. localStorage.setItem("clickCounter", clickCounter.toString());
  242. // 如果点击次数达到likeLimit次,则设置点赞变量为false
  243. if (clickCounter === likeLimit) {
  244. console.log(
  245. `Reached ${likeLimit} likes, setting the like variable to false.`
  246. );
  247. localStorage.setItem("read", false);
  248. } else {
  249. console.log("clickCounter:", clickCounter);
  250. }
  251. }
  252.  
  253. function triggerClick(button) {
  254. const event = new MouseEvent("click", {
  255. bubbles: true,
  256. cancelable: true,
  257. view: window,
  258. });
  259. button.dispatchEvent(event);
  260. }
  261.  
  262. const button = document.createElement("button");
  263. button.textContent =
  264. localStorage.getItem("read") === "true" ? "停止阅读" : "开始阅读";
  265. button.style.position = "fixed";
  266. button.style.bottom = "20px";
  267. button.style.left = "20px";
  268. button.style.zIndex = 1000;
  269. button.style.backgroundColor = "#e0e0e0";
  270. button.style.color = "#333";
  271. button.style.border = "1px solid #aaa";
  272. button.style.padding = "8px 16px";
  273. button.style.borderRadius = "8px";
  274. document.body.appendChild(button);
  275.  
  276. button.onclick = function () {
  277. const currentlyReading = localStorage.getItem("read") === "true";
  278. const newReadState = !currentlyReading;
  279. localStorage.setItem("read", newReadState.toString());
  280. button.textContent = newReadState ? "停止阅读" : "开始阅读";
  281. if (newReadState) {
  282. if (BASE_URL == "https://linux.do") {
  283. const maxPostNumber = 600;
  284. const randomPostNumber = Math.floor(Math.random() * maxPostNumber) + 1;
  285. const newUrl = `https://linux.do/t/topic/13716/${randomPostNumber}`;
  286. window.location.href = newUrl;
  287. } else {
  288. window.location.href = `${BASE_URL}/t/topic/1`;
  289. }
  290. }
  291. };
  292.  
  293. const userInput = document.createElement("input");
  294. userInput.type = "text";
  295. userInput.placeholder = "输入要点赞的用户ID";
  296. userInput.style.position = "fixed";
  297. userInput.style.bottom = "90px";
  298. userInput.style.left = "20px";
  299. userInput.style.zIndex = "1000";
  300. userInput.style.padding = "6px";
  301. userInput.style.border = "1px solid #aaa";
  302. userInput.style.borderRadius = "8px";
  303. userInput.style.backgroundColor = "#e0e0e0";
  304. userInput.style.width = "100px";
  305. userInput.value = localStorage.getItem("specificUser") || "14790897";
  306.  
  307. document.body.appendChild(userInput);
  308.  
  309. const saveUserButton = document.createElement("button");
  310. saveUserButton.textContent = "保存用户ID";
  311. saveUserButton.style.position = "fixed";
  312. saveUserButton.style.bottom = "60px";
  313. saveUserButton.style.left = "20px";
  314. saveUserButton.style.zIndex = "1000";
  315. saveUserButton.style.backgroundColor = "#e0e0e0";
  316. saveUserButton.style.color = "#333";
  317. saveUserButton.style.border = "1px solid #aaa";
  318. saveUserButton.style.padding = "8px 16px";
  319. saveUserButton.style.borderRadius = "8px";
  320. document.body.appendChild(saveUserButton);
  321.  
  322. saveUserButton.onclick = function () {
  323. const newSpecificUser = userInput.value.trim();
  324. if (newSpecificUser) {
  325. localStorage.setItem("specificUser", newSpecificUser);
  326. localStorage.removeItem("specificUserPostList");
  327. localStorage.removeItem("lastOffset");
  328. specificUser = newSpecificUser;
  329. console.log(
  330. `新的specificUser已保存: ${specificUser},specificUserPostList已重置`
  331. );
  332. }
  333. };
  334.  
  335. const likeLimitInput = document.createElement("input");
  336. likeLimitInput.type = "number";
  337. likeLimitInput.placeholder = "输入点赞数量";
  338. likeLimitInput.style.position = "fixed";
  339. likeLimitInput.style.bottom = "180px";
  340. likeLimitInput.style.left = "20px";
  341. likeLimitInput.style.zIndex = "1000";
  342. likeLimitInput.style.padding = "6px";
  343. likeLimitInput.style.border = "1px solid #aaa";
  344. likeLimitInput.style.borderRadius = "8px";
  345. likeLimitInput.style.backgroundColor = "#e0e0e0";
  346. likeLimitInput.style.width = "100px";
  347. likeLimitInput.value = localStorage.getItem("likeLimit") || 200;
  348. document.body.appendChild(likeLimitInput);
  349.  
  350. const saveLikeLimitButton = document.createElement("button");
  351. saveLikeLimitButton.textContent = "保存点赞数量";
  352. saveLikeLimitButton.style.position = "fixed";
  353. saveLikeLimitButton.style.bottom = "140px";
  354. saveLikeLimitButton.style.left = "20px";
  355. saveLikeLimitButton.style.zIndex = "1000";
  356. saveLikeLimitButton.style.backgroundColor = "#e0e0e0";
  357. saveLikeLimitButton.style.color = "#333";
  358. saveLikeLimitButton.style.border = "1px solid #aaa";
  359. saveLikeLimitButton.style.padding = "8px 16px";
  360. saveLikeLimitButton.style.borderRadius = "8px";
  361. document.body.appendChild(saveLikeLimitButton);
  362.  
  363. saveLikeLimitButton.onclick = function () {
  364. const newLikeLimit = parseInt(likeLimitInput.value.trim(), 10);
  365. if (newLikeLimit && newLikeLimit > 0) {
  366. localStorage.setItem("likeLimit", newLikeLimit);
  367. likeLimit = newLikeLimit;
  368. console.log(`新的likeLimit已保存: ${likeLimit}`);
  369. }
  370. };
  371.  
  372. // 增加清除数据的按钮
  373. const clearDataButton = document.createElement("button");
  374. clearDataButton.textContent = "清除所有数据";
  375. clearDataButton.style.position = "fixed";
  376. clearDataButton.style.bottom = "20px";
  377. clearDataButton.style.left = "140px";
  378. clearDataButton.style.zIndex = "1000";
  379. clearDataButton.style.backgroundColor = "#ff6666"; // 红色背景,提示删除操作
  380. clearDataButton.style.color = "#fff"; // 白色文本
  381. clearDataButton.style.border = "1px solid #ff3333"; // 深红色边框
  382. clearDataButton.style.padding = "8px 16px";
  383. clearDataButton.style.borderRadius = "8px";
  384. document.body.appendChild(clearDataButton);
  385.  
  386. clearDataButton.onclick = function () {
  387. localStorage.removeItem("lastOffset");
  388. localStorage.removeItem("clickCounter");
  389. localStorage.removeItem("clickCounterTimestamp");
  390. localStorage.removeItem("specificUserPostList");
  391. console.log("所有数据已清除,除了 specificUser 和 specificUserPostList");
  392. };
  393. })();