Auto Read

自动刷linuxdo文章

  1. // ==UserScript==
  2. // @name Auto Read
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.4.2
  5. // @description 自动刷linuxdo文章
  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. // ==/UserScript==
  15.  
  16. (function () {
  17. ("use strict");
  18. // 定义可能的基本URL
  19. const possibleBaseURLs = [
  20. "https://linux.do",
  21. "https://meta.discourse.org",
  22. "https://meta.appinn.net",
  23. "https://community.openai.com",
  24. ];
  25. const commentLimit = 1000;
  26. const topicListLimit = 100;
  27. const likeLimit = 50;
  28. // 获取当前页面的URL
  29. const currentURL = window.location.href;
  30.  
  31. // 确定当前页面对应的BASE_URL
  32. let BASE_URL = possibleBaseURLs.find((url) => currentURL.startsWith(url));
  33. console.log("currentURL:", currentURL);
  34. // 环境变量:阅读网址,如果没有找到匹配的URL,则默认为第一个
  35. if (!BASE_URL) {
  36. BASE_URL = possibleBaseURLs[0];
  37. console.log("默认BASE_URL设置为: " + BASE_URL);
  38. } else {
  39. console.log("当前BASE_URL是: " + BASE_URL);
  40. }
  41.  
  42. console.log("脚本正在运行在: " + BASE_URL);
  43. //1.进入网页 https://linux.do/t/topic/数字(1,2,3,4)
  44. //2.使滚轮均衡的往下移动模拟刷文章
  45. // 检查是否是第一次运行脚本
  46. function checkFirstRun() {
  47. if (localStorage.getItem("isFirstRun") === null) {
  48. console.log("脚本第一次运行,执行初始化操作...");
  49. updateInitialData();
  50. localStorage.setItem("isFirstRun", "false");
  51. } else {
  52. console.log("脚本非第一次运行");
  53. }
  54. }
  55.  
  56. // 更新初始数据的函数
  57. function updateInitialData() {
  58. localStorage.setItem("read", "false"); // 开始时自动滚动关闭
  59. localStorage.setItem("autoLikeEnabled", "false"); //默认关闭自动点赞
  60. console.log("执行了初始数据更新操作");
  61. }
  62. const delay = 2000; // 滚动检查的间隔(毫秒)
  63. let scrollInterval = null;
  64. let checkScrollTimeout = null;
  65. let autoLikeInterval = null;
  66.  
  67. function scrollToBottomSlowly(distancePerStep = 20, delayPerStep = 50) {
  68. if (scrollInterval !== null) {
  69. clearInterval(scrollInterval);
  70. }
  71. scrollInterval = setInterval(() => {
  72. window.scrollBy(0, distancePerStep);
  73. }, delayPerStep); // 每50毫秒滚动20像素
  74. }
  75.  
  76. function getLatestTopic() {
  77. let latestPage = Number(localStorage.getItem("latestPage")) || 0;
  78. let topicList = [];
  79. let isDataSufficient = false;
  80.  
  81. while (!isDataSufficient) {
  82. latestPage++;
  83. const url = `${BASE_URL}/latest.json?no_definitions=true&page=${latestPage}`;
  84.  
  85. $.ajax({
  86. url: url,
  87. async: false,
  88. success: function (result) {
  89. if (
  90. result &&
  91. result.topic_list &&
  92. result.topic_list.topics.length > 0
  93. ) {
  94. result.topic_list.topics.forEach((topic) => {
  95. // 未读且评论数小于 commentLimit
  96. if (commentLimit > topic.posts_count) {
  97. //其实不需要 !topic.unseen &&
  98. topicList.push(topic);
  99. }
  100. });
  101.  
  102. // 检查是否已获得足够的 topics
  103. if (topicList.length >= topicListLimit) {
  104. isDataSufficient = true;
  105. }
  106. } else {
  107. isDataSufficient = true; // 没有更多内容时停止请求
  108. }
  109. },
  110. error: function (XMLHttpRequest, textStatus, errorThrown) {
  111. console.error(XMLHttpRequest, textStatus, errorThrown);
  112. isDataSufficient = true; // 遇到错误时也停止请求
  113. },
  114. });
  115. }
  116.  
  117. if (topicList.length > topicListLimit) {
  118. topicList = topicList.slice(0, topicListLimit);
  119. }
  120.  
  121. // 其实不需要对latestPage操作
  122. // localStorage.setItem("latestPage", latestPage);
  123. localStorage.setItem("topicList", JSON.stringify(topicList));
  124. }
  125.  
  126. function openNewTopic() {
  127. let topicListStr = localStorage.getItem("topicList");
  128. let topicList = topicListStr ? JSON.parse(topicListStr) : [];
  129.  
  130. // 如果列表为空,则获取最新文章
  131. if (topicList.length === 0) {
  132. getLatestTopic();
  133. topicListStr = localStorage.getItem("topicList");
  134. topicList = topicListStr ? JSON.parse(topicListStr) : [];
  135. }
  136.  
  137. // 如果获取到新文章,打开第一个
  138. if (topicList.length > 0) {
  139. const topic = topicList.shift();
  140. localStorage.setItem("topicList", JSON.stringify(topicList));
  141. if (topic.last_read_post_number) {
  142. window.location.href = `${BASE_URL}/t/topic/${topic.id}/${topic.last_read_post_number}`;
  143. } else {
  144. window.location.href = `${BASE_URL}/t/topic/${topic.id}`;
  145. }
  146. }
  147. }
  148.  
  149. // 检查是否已滚动到底部(不断重复执行),到底部时跳转到下一个话题
  150. function checkScroll() {
  151. if (localStorage.getItem("read")) {
  152. if (
  153. window.innerHeight + window.scrollY >=
  154. document.body.offsetHeight - 100
  155. ) {
  156. console.log("已滚动到底部");
  157. openNewTopic();
  158. } else {
  159. scrollToBottomSlowly();
  160. if (checkScrollTimeout !== null) {
  161. clearTimeout(checkScrollTimeout);
  162. }
  163. checkScrollTimeout = setTimeout(checkScroll, delay);
  164. }
  165. }
  166. }
  167.  
  168. // 入口函数
  169. window.addEventListener("load", () => {
  170. checkFirstRun();
  171. console.log(
  172. "autoRead",
  173. localStorage.getItem("read"),
  174. "autoLikeEnabled",
  175. localStorage.getItem("autoLikeEnabled")
  176. );
  177. if (localStorage.getItem("read") === "true") {
  178. console.log("执行正常的滚动和检查逻辑");
  179. checkScroll();
  180. if (isAutoLikeEnabled()) {
  181. autoLike();
  182. }
  183. }
  184. });
  185.  
  186. // 获取当前时间戳
  187. const currentTime = Date.now();
  188. // 获取存储的时间戳
  189. const defaultTimestamp = new Date("1999-01-01T00:00:00Z").getTime(); //默认值为1999年
  190. const storedTime = parseInt(
  191. localStorage.getItem("clickCounterTimestamp") ||
  192. defaultTimestamp.toString(),
  193. 10
  194. );
  195.  
  196. // 获取当前的点击计数,如果不存在则初始化为0
  197. let clickCounter = parseInt(localStorage.getItem("clickCounter") || "0", 10);
  198. // 检查是否超过24小时(24小时 = 24 * 60 * 60 * 1000 毫秒)
  199. if (currentTime - storedTime > 24 * 60 * 60 * 1000) {
  200. // 超过24小时,清空点击计数器并更新时间戳
  201. clickCounter = 0;
  202. localStorage.setItem("clickCounter", "0");
  203. localStorage.setItem("clickCounterTimestamp", currentTime.toString());
  204. }
  205.  
  206. console.log(`Initial clickCounter: ${clickCounter}`);
  207. function triggerClick(button) {
  208. const event = new MouseEvent("click", {
  209. bubbles: true,
  210. cancelable: true,
  211. view: window,
  212. });
  213. button.dispatchEvent(event);
  214. }
  215.  
  216. function autoLike() {
  217. console.log(`Initial clickCounter: ${clickCounter}`);
  218. // 寻找所有的discourse-reactions-reaction-button
  219. const buttons = document.querySelectorAll(
  220. ".discourse-reactions-reaction-button"
  221. );
  222. if (buttons.length === 0) {
  223. console.error(
  224. "No buttons found with the selector '.discourse-reactions-reaction-button'"
  225. );
  226. return;
  227. }
  228. console.log(`Found ${buttons.length} buttons.`); // 调试信息
  229.  
  230. // 逐个点击找到的按钮
  231. buttons.forEach((button, index) => {
  232. if (
  233. (button.title !== "点赞此帖子" && button.title !== "Like this post") ||
  234. clickCounter >= likeLimit
  235. ) {
  236. return;
  237. }
  238.  
  239. // 使用setTimeout来错开每次点击的时间,避免同时触发点击
  240. autoLikeInterval = setTimeout(() => {
  241. // 模拟点击
  242. triggerClick(button); // 使用自定义的触发点击方法
  243. console.log(`Clicked like button ${index + 1}`);
  244. clickCounter++; // 更新点击计数器
  245. // 将新的点击计数存储到localStorage
  246. localStorage.setItem("clickCounter", clickCounter.toString());
  247. // 如果点击次数达到likeLimit次,则设置点赞变量为false
  248. if (clickCounter === likeLimit) {
  249. console.log(
  250. `Reached ${likeLimit} likes, setting the like variable to false.`
  251. );
  252. localStorage.setItem("autoLikeEnabled", "false"); // 使用localStorage存储点赞变量状态
  253. } else {
  254. console.log("clickCounter:", clickCounter);
  255. }
  256. }, index * 3000); // 这里的1000毫秒是两次点击之间的间隔,可以根据需要调整
  257. });
  258. }
  259. const button = document.createElement("button");
  260. // 初始化按钮文本基于当前的阅读状态
  261. button.textContent =
  262. localStorage.getItem("read") === "true" ? "停止阅读" : "开始阅读";
  263. button.style.position = "fixed";
  264. button.style.bottom = "10px"; // 之前是 top
  265. button.style.left = "10px"; // 之前是 right
  266. button.style.zIndex = 1000;
  267. button.style.backgroundColor = "#f0f0f0"; // 浅灰色背景
  268. button.style.color = "#000"; // 黑色文本
  269. button.style.border = "1px solid #ddd"; // 浅灰色边框
  270. button.style.padding = "5px 10px"; // 内边距
  271. button.style.borderRadius = "5px"; // 圆角
  272. document.body.appendChild(button);
  273.  
  274. button.onclick = function () {
  275. const currentlyReading = localStorage.getItem("read") === "true";
  276. const newReadState = !currentlyReading;
  277. localStorage.setItem("read", newReadState.toString());
  278. button.textContent = newReadState ? "停止阅读" : "开始阅读";
  279. if (!newReadState) {
  280. if (scrollInterval !== null) {
  281. clearInterval(scrollInterval);
  282. scrollInterval = null;
  283. }
  284. if (checkScrollTimeout !== null) {
  285. clearTimeout(checkScrollTimeout);
  286. checkScrollTimeout = null;
  287. }
  288. localStorage.removeItem("navigatingToNextTopic");
  289. } else {
  290. // 如果是Linuxdo,就导航到我的帖子
  291. if (BASE_URL == "https://linux.do") {
  292. window.location.href = "https://linux.do/t/topic/13716/700";
  293. } else {
  294. window.location.href = `${BASE_URL}/t/topic/1`;
  295. }
  296. checkScroll();
  297. }
  298. };
  299.  
  300. //自动点赞按钮
  301. // 在页面上添加一个控制自动点赞的按钮
  302. const toggleAutoLikeButton = document.createElement("button");
  303. toggleAutoLikeButton.textContent = isAutoLikeEnabled()
  304. ? "禁用自动点赞"
  305. : "启用自动点赞";
  306. toggleAutoLikeButton.style.position = "fixed";
  307. toggleAutoLikeButton.style.bottom = "50px"; // 之前是 top,且与另一个按钮错开位置
  308. toggleAutoLikeButton.style.left = "10px"; // 之前是 right
  309. toggleAutoLikeButton.style.zIndex = "1000";
  310. toggleAutoLikeButton.style.backgroundColor = "#f0f0f0"; // 浅灰色背景
  311. toggleAutoLikeButton.style.color = "#000"; // 黑色文本
  312. toggleAutoLikeButton.style.border = "1px solid #ddd"; // 浅灰色边框
  313. toggleAutoLikeButton.style.padding = "5px 10px"; // 内边距
  314. toggleAutoLikeButton.style.borderRadius = "5px"; // 圆角
  315. document.body.appendChild(toggleAutoLikeButton);
  316.  
  317. // 为按钮添加点击事件处理函数
  318. toggleAutoLikeButton.addEventListener("click", () => {
  319. const isEnabled = !isAutoLikeEnabled();
  320. setAutoLikeEnabled(isEnabled);
  321. toggleAutoLikeButton.textContent = isEnabled
  322. ? "禁用自动点赞"
  323. : "启用自动点赞";
  324. });
  325. // 判断是否启用自动点赞
  326. function isAutoLikeEnabled() {
  327. // 从localStorage获取autoLikeEnabled的值,如果未设置,默认为"true"
  328. return localStorage.getItem("autoLikeEnabled") !== "false";
  329. }
  330.  
  331. // 设置自动点赞的启用状态
  332. function setAutoLikeEnabled(enabled) {
  333. localStorage.setItem("autoLikeEnabled", enabled ? "true" : "false");
  334. }
  335. })();