微博粉丝变化监测

监测微博粉丝变化并发送通知

  1. // ==UserScript==
  2. // @name 微博粉丝变化监测
  3. // @namespace Spuddy
  4. // @version 1.2.3
  5. // @description 监测微博粉丝变化并发送通知
  6. // @author Spuddy
  7. // @match https://weibo.com/*
  8. // @grant GM_xmlhttpRequest
  9. // @grant GM_notification
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // @grant GM_registerMenuCommand
  13. // @run-at document-idle
  14. // @noframes
  15. // @license MIT
  16. // ==/UserScript==
  17.  
  18. 'use strict';
  19.  
  20. if (document.querySelector('.loginBtn')) {
  21. console.log('User is not logged in');
  22. return;
  23. }
  24.  
  25. // 配置参数
  26. const config = {
  27. uid: unsafeWindow.$CONFIG.uid
  28. };
  29.  
  30. // 获取当前用户的 UID
  31. const uid = config.uid;
  32. console.log(`Current UID: ${uid}`);
  33.  
  34. // 初始化
  35. let isRunning = false;
  36. let userMap = new Map(JSON.parse(GM_getValue(`userMap_${uid}`, '[]')));
  37. console.log('Loaded userMap from storage:', userMap);
  38.  
  39. // 获取当前时间戳
  40. const currentTime = Date.now();
  41. const lastRunTime = GM_getValue(`lastRunTime_${uid}`, 0);
  42. const twentyFourHours = 24 * 60 * 60 * 1000;
  43. console.log(`Current time: ${currentTime}, Last run time: ${lastRunTime}`);
  44.  
  45. // 判断是否超过24小时
  46. if (currentTime - lastRunTime > twentyFourHours) {
  47. console.log('More than 24 hours since last run, starting main loop');
  48. mainLoop();
  49. } else {
  50. console.log('Less than 24 hours since last run, skipping main loop');
  51. }
  52.  
  53. // 注册(不可用)菜单命令
  54. GM_registerMenuCommand('立即执行检测', () => {
  55. if (isRunning) {
  56. alert('检测正在进行中,请稍后再试。');
  57. return;
  58. }
  59. mainLoop();
  60. });
  61.  
  62. async function mainLoop() {
  63. if (isRunning) {
  64. console.log('检测正在进行中,跳过此次调用');
  65. return;
  66. }
  67. isRunning = true;
  68. // 更新缓存的时间戳
  69. GM_setValue(`lastRunTime_${uid}`, Date.now());
  70. try {
  71. let currentPage = 1;
  72. let hasNextPage = true;
  73. const newUserMap = new Map();
  74. console.log('Starting main loop');
  75.  
  76. while (hasNextPage) {
  77. console.log(`Fetching page ${currentPage}`);
  78. const response = await fetchFansPage(currentPage);
  79. hasNextPage = processResponse(response, newUserMap);
  80. currentPage++;
  81. }
  82.  
  83. const differences = compareUsers(userMap, newUserMap);
  84. if (differences.length > 0) {
  85. console.log('Differences found:', differences);
  86. sendNotification(differences);
  87. userMap = newUserMap;
  88. GM_setValue(`userMap_${uid}`, JSON.stringify([...userMap]));
  89. console.log('Updated userMap in storage');
  90. } else {
  91. console.log('No differences found');
  92. GM_notification({
  93. title: '微博粉丝变化监测',
  94. text: '已执行检测,没有检测到变化',
  95. timeout: 5000
  96. });
  97. }
  98. } catch (error) {
  99. handleError(error);
  100. } finally {
  101. isRunning = false;
  102. }
  103. }
  104.  
  105. async function fetchFansPage(page) {
  106. return new Promise((resolve, reject) => {
  107. GM_xmlhttpRequest({
  108. method: "GET",
  109. url: `https://weibo.com/ajax/friendships/friends?uid=${config.uid}&relate=fans&type=fans&page=${page}`,
  110. onload: (response) => {
  111. if (response.status === 200) {
  112. console.log(`Page ${page} fetched successfully`);
  113. resolve(JSON.parse(response.responseText));
  114. } else {
  115. console.error(`Failed to fetch page ${page}: ${response.status}`);
  116. reject(new Error(`请求失败: ${response.status}`));
  117. }
  118. },
  119. onerror: (error) => {
  120. console.error(`Error fetching page ${page}:`, error);
  121. reject(error);
  122. }
  123. });
  124. });
  125. }
  126.  
  127. function processResponse(response, userMap) {
  128. const users = response.users || [];
  129. console.log(`Processing response, found ${users.length} users`);
  130. users.forEach(user => {
  131. userMap.set(user.id, user.name);
  132. });
  133. return response.next_cursor != 0 && users.length > 0;
  134. }
  135.  
  136. function compareUsers(oldMap, newMap) {
  137. const changes = [];
  138. // 检测新增
  139. for (const id of newMap.keys()) {
  140. if (!oldMap.has(id)) {
  141. const name = newMap.get(id);
  142. changes.push(`➕ 新增: ${name} (${id})`);
  143. }
  144. }
  145. // 检测移除
  146. for (const id of oldMap.keys()) {
  147. if (!newMap.has(id)) {
  148. const name = oldMap.get(id);
  149. changes.push(`➖ 移除: ${name} (${id})`);
  150. }
  151. }
  152. // 检测改名
  153. for (const id of oldMap.keys()) {
  154. if (newMap.has(id) && oldMap.get(id) !== newMap.get(id)) {
  155. const oldName = oldMap.get(id);
  156. const newName = newMap.get(id);
  157. changes.push(`🔄 改名: ${oldName} ${newName} (${id})`);
  158. }
  159. }
  160. console.log('Comparison complete, changes:', changes);
  161. return changes;
  162. }
  163.  
  164. function sendNotification(messages) {
  165. const content = messages.join('<br>');
  166. console.log('Sending notification:', content);
  167. // 创建一个弹窗元素
  168. const notificationDiv = document.createElement('div');
  169. notificationDiv.style.position = 'fixed';
  170. notificationDiv.style.top = '20px';
  171. notificationDiv.style.right = '20px';
  172. notificationDiv.style.backgroundColor = 'white';
  173. notificationDiv.style.border = '1px solid black';
  174. notificationDiv.style.padding = '10px';
  175. notificationDiv.style.zIndex = '10000';
  176. notificationDiv.style.minWidth = '300px';
  177. notificationDiv.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
  178. notificationDiv.style.whiteSpace = 'nowrap';
  179. notificationDiv.style.maxHeight = '80vh';
  180. notificationDiv.style.overflowY = 'auto';
  181. notificationDiv.innerHTML = `<strong>粉丝变化通知:</strong><br>${content}<br>`;
  182.  
  183. // 添加关闭按钮
  184. const closeButton = document.createElement('button');
  185. closeButton.textContent = '关闭';
  186. closeButton.style.marginTop = '10px';
  187. closeButton.onclick = () => {
  188. document.body.removeChild(notificationDiv);
  189. };
  190. notificationDiv.appendChild(closeButton);
  191.  
  192. // 将弹窗添加到文档中
  193. document.body.appendChild(notificationDiv);
  194.  
  195. alert(`粉丝变化通知(完整见网页右上角):\n\n${messages.join('\n')}`);
  196. }
  197.  
  198. function handleError(error) {
  199. console.error('发生错误:', error);
  200. GM_notification({
  201. title: '微博粉丝变化监测',
  202. text: '发生错误:' + error.message.substring(0, 100),
  203. timeout: 5000
  204. });
  205. }
  206.  
  207. // 修改初始化提示
  208. if (!GM_getValue('init', false)) {
  209. console.log('First time initialization');
  210. GM_notification({
  211. title: '微博粉丝变化监测',
  212. text: '监测服务已启动',
  213. timeout: 5000
  214. });
  215. GM_setValue('init', true);
  216. }

QingJ © 2025

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