A畜3畜野狗大杂烩指示器

自动标注成分,初版默认包括a畜、3畜、4畜、e畜、OP、农狗、粥批、塔畜、罕见(東雪蓮)、T畜(星瞳)

  1. // ==UserScript==
  2. // @name A畜3畜野狗大杂烩指示器
  3. // @version 2.57
  4. // @author trychen,miayoshi
  5. // @namespace ACG3CGTCG
  6. // @license GPLv3
  7. // @description 自动标注成分,初版默认包括a畜、3畜、4畜、e畜、OP、农狗、粥批、塔畜、罕见(東雪蓮)、T畜(星瞳)
  8. // @match https://space.bilibili.com/*
  9. // @match https://www.bilibili.com/read/*
  10. // @match https://www.bilibili.com/video/*
  11. // @match https://t.bilibili.com/*
  12. // @icon https://static.hdslb.com/images/favicon.ico
  13. // @connect bilibili.com
  14. // @grant GM_xmlhttpRequest
  15. // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js
  16. // ==/UserScript==
  17.  
  18. const blog = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?&host_mid='
  19. const followapi = 'https://api.bilibili.com/x/relation/followings?vmid='
  20.  
  21. $(function () {
  22. 'use strict';
  23. const checkers = [
  24. {
  25. displayName: "A畜",
  26. displayIcon: "https://i2.hdslb.com/bfs/face/43b21998da8e7e210340333f46d4e2ae7ec046eb.jpg@240w_240h_1c_1s.jpg",
  27. keywords: ["想到晚的瞬间","晚晚","嘉晚饭","乃贝","贝极星空间站","乃琳夸夸群","顶碗人","皇珈骑士","贝极星","乃宝","嘉心糖的手账本","嘉心糖","拉姐","然然","asoul","A-SOUL","水母","来点然能量","奶淇琳","珈乐","贝拉拉的717片星空"],
  28. followings: [703007996,672342685,672328094,672353429,672346917,351609538]
  29. }
  30. ,
  31. {
  32. displayName: "4畜",
  33. displayIcon: "https://i2.hdslb.com/bfs/face/27258e94f32b724821ee16c4d020fa7b2042d489.jpg@240w_240h_1c_1s.jpg",
  34. keywords: ["啵刚","谭🐷","谭猪","衫之恶魔","枝江杀猪","9分美女","三畜","野狗","3畜","谭德安","孤珈者","一等骑士","谭女士",""],
  35. followings: [1529814632,17771572]
  36. }
  37. ,
  38. {
  39. displayName: "3畜",
  40. displayIcon: "https://i2.hdslb.com/bfs/face/26ad353c5dfa2319417e5bac84f876b9bd1b54a6.jpg@240w_240h_1c_1s.jpg",
  41. keywords: ["小狗说","玉桂幺幺340","三宝","3宝","巢友","巢畜","4畜","小狗生病","Pomelo不加糖","黛露党","啵啵伯仁","學無止境","藤枝薰official","小谷桔","巢楚","大事不好_Official","水无月雅Official","黛露党","王力口富贵","咩罗斯","量子观测Official","玉桂狗美图分享bot","锯沫","锯元素"]
  42. }
  43. ,
  44. {
  45. displayName: "塔畜",
  46. displayIcon: "https://i1.hdslb.com/bfs/face/4907464999fbf2f2a6f9cc8b7352fceb6b3bfec3.jpg@240w_240h_1c_1s.jpg",
  47. keywords: ["谢谢喵","taffy","雏草姬"],
  48. followings: [1265680561]
  49. }
  50. ,
  51. {
  52. displayName: "罕见",
  53. displayIcon: "https://i0.hdslb.com/bfs/face/ced15dc126348dc42bd5c8eefdd1de5e48bdd8e6.jpg@240w_240h_1c_1s.jpg",
  54. keywords: ["東雪蓮Official","东雪莲","莲宝"],
  55. followings: [1437582453]
  56. }
  57. ,
  58. {
  59. displayName: "T畜",
  60. displayIcon: "https://i0.hdslb.com/bfs/face/6be92dec2240b0593a40d2c696b37aa75c704ff6.jpg@240w_240h_1c_1s.jpg",
  61. keywords: ["小星星","瞳宝","瞳子","瞳瞳","瞳星结"],
  62. followings: [2122506217]
  63. }
  64. ,
  65. {
  66. displayName: "E畜",
  67. displayIcon: "https://i0.hdslb.com/bfs/face/f0ac506bbfa4e4ce09729d424d28d2383e721ade.jpg@240w_240h_1c_1s.jpg",
  68. keywords: ["虞莫","柚恩","露早","莞儿","米诺"],
  69. followings: [2018113152]
  70. }
  71. ,
  72. {
  73. displayName: "梓畜",
  74. displayIcon: "https://i2.hdslb.com/bfs/face/ba9ce36ef60a53e24a97f54429e62bdb951530a0.jpg@240w_240h_1c_1s.jpg",
  75. keywords: ["阿梓从小就很可爱","阿梓","小孩梓","达达","AME"],
  76. followings: [7706705]
  77. }
  78. ,
  79. {
  80. displayName: "量畜",
  81. displayIcon: "https://i1.hdslb.com/bfs/face/2f745d6ad1b703f9d972c6e628ad6bc5c756e94d.jpg@240w_240h_1c_1s.jpg",
  82. keywords: ["量子少年","慕宇","泽一","祥太","楚枫"],
  83. followings: [1895683714,1535525542,1461176910,1757836012,1230039261]
  84. }
  85. ,
  86. {
  87. displayName: "鲨畜",
  88. displayIcon: "https://i2.hdslb.com/bfs/face/254aedbf9dad0ed5e1117c2e435a6f36ed70c64d.jpg@240w_240h_1c_1s.jpg",
  89. keywords: ["脆鲨","娜娜米","海子姐"],
  90. followings: [434334701]
  91. }
  92. ,
  93. {
  94. displayName: "OP",
  95. displayIcon: "https://i2.hdslb.com/bfs/face/d2a95376140fb1e5efbcbed70ef62891a3e5284f.jpg@240w_240h_1c_1s.jpg",
  96. keywords: ["互动抽奖 #原神", "米哈游", "#米哈游#", "#miHoYo#","原神"],
  97. followings: [401742377]
  98. }
  99. ,
  100. {
  101. displayName: "农狗",
  102. displayIcon: "https://i2.hdslb.com/bfs/face/effbafff589a27f02148d15bca7e97031a31d772.jpg@240w_240h_1c_1s.jpg",
  103. keywords: ["互动抽奖 #王者荣耀","王者荣耀"]
  104. }
  105. ,
  106. {
  107. displayName: "粥批",
  108. displayIcon: "https://i0.hdslb.com/bfs/face/89154378c06a5ed332c40c2ca56f50cd641c0c90.jpg@240w_240h_1c_1s.jpg",
  109. keywords: ["互动抽奖 #明日方舟","危机合约","《明日方舟》"],
  110. followings: [161775300]
  111. }
  112. ]
  113. const checked = {}
  114. const checking = {}
  115. var printed = false
  116.  
  117. // 监听用户ID元素出现
  118. listenKey(".user-name", addButton);
  119. listenKey(".sub-user-name", addButton);
  120. listenKey(".user .name", addButton);
  121.  
  122.  
  123. // 添加查成分按钮
  124. function addButton(element) {
  125. let node = $(`<div style="display: inline;" class="composition-checkable"><div class="iBadge">
  126. <a class="iName">查成分</a>
  127. </div></div>`)
  128.  
  129. node.on('click', function () {
  130. node.find(".iName").text("检查中...")
  131. checktag(element, node.find(".iName"))
  132. })
  133.  
  134. element.after(node)
  135. }
  136.  
  137. // 添加标签
  138. function addtag(id, element, setting) {
  139. let node = $(`<div style="display: inline;"><div class="iBadge">
  140. <a class="iName">${setting.displayName}</a>
  141. <img src="${setting.displayIcon}" class="iIcon">
  142. </div></div>`)
  143.  
  144. element.after(node)
  145. }
  146.  
  147. // 检查标签
  148. function checktag(element, loadingElement) {
  149. // 用户ID
  150. let UID = element.attr("data-user-id") || element.attr("data-usercard-mid")
  151. // 用户名
  152. let name = element.text().charAt(0) == "@" ? element.text().substring(1) : element.text()
  153.  
  154. if (checked[UID]) {
  155. // 已经缓存过了
  156. for(let setting of checked[UID]) {
  157. addtag(UID, element, setting)
  158. }
  159. } else if (checking[UID] != undefined) {
  160. // 检查中
  161. if (checking[UID].indexOf(element) < 0)
  162. checking[UID].push(element)
  163. } else {
  164. checking[UID] = [element]
  165.  
  166. // 获取最近动态
  167. GM_xmlhttpRequest({
  168. method: "get",
  169. url: blog + UID,
  170. data: '',
  171. headers: {
  172. 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
  173. },
  174. onload: res => {
  175. if(res.status === 200) {
  176. // 获取关注列表
  177. GM_xmlhttpRequest({
  178. method: "get",
  179. url: followapi + UID,
  180. data: '',
  181. headers: {
  182. 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
  183. },
  184. onload: followingRes => {
  185. if(followingRes.status === 200) {
  186. // 查询关注列表
  187. let followingData = JSON.parse(followingRes.response)
  188. // 可能无权限
  189. let following = followingData.code == 0 ? followingData.data.list.map(it => it.mid) : []
  190.  
  191. // 查询并拼接动态数据
  192. let st = JSON.stringify(JSON.parse(res.response).data.items)
  193.  
  194. // 找到的匹配内容
  195. let found = []
  196. for(let setting of checkers) {
  197. // 检查动态内容
  198. if (setting.keywords)
  199. if (setting.keywords.find(keyword => st.includes(keyword))) {
  200. if (found.indexOf(setting) < 0)
  201. found.push(setting)
  202. continue;
  203. }
  204.  
  205. // 检查关注列表
  206. if (setting.followings)
  207. for(let mid of setting.followings) {
  208. if (following.indexOf(mid) >= 0) {
  209. if (found.indexOf(setting) < 0)
  210. found.push(setting)
  211. continue;
  212. }
  213. }
  214. }
  215.  
  216. // 添加标签
  217. if (found.length > 0) {
  218. if (!printed) {
  219. console.log(JSON.parse(res.response).data)
  220. printed = true
  221. }
  222. checked[UID] = found
  223.  
  224. // 给所有用到的地方添加标签
  225. for (let element of checking[UID]) {
  226. for(let setting of found) {
  227. addtag(UID, element, setting)
  228. }
  229. }
  230. loadingElement.parent().remove()
  231. } else {
  232. loadingElement.text('无')
  233. }
  234.  
  235. } else {
  236. loadingElement.text('失败')
  237. }
  238.  
  239. delete checking[UID]
  240. },
  241. onerror: err => {
  242. loadingElement.text('失败')
  243. delete checking[UID]
  244. },
  245. })
  246.  
  247.  
  248. } else {
  249. loadingElement.text('失败')
  250. delete checking[UID]
  251. }
  252. },
  253. onerror: err => {
  254. loadingElement.text('失败')
  255. delete checking[UID]
  256. },
  257. });
  258. }
  259. }
  260.  
  261. addGlobalStyle(`
  262. .iBadge {
  263. display: inline-flex;
  264. justify-content: center;
  265. align-items: center;
  266. width: fit-content;
  267. background: #07beff26;
  268. border-radius: 10px;
  269. margin: -6px 0;
  270. margin: 0 5px;
  271. font-family: PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei, sans-serif;
  272. }
  273. .iName {
  274. line-height: 13px;
  275. font-size: 13px;
  276. color: #07beff;
  277. padding: 2px 8px;
  278. }
  279. .iIcon {
  280. width: 25px;
  281. height: 25px;
  282. border-radius: 50%;
  283. border: 2px solid white;
  284. margin: -6px;
  285. margin-right: 5px;
  286. }
  287. `)
  288.  
  289. function addGlobalStyle(css) {
  290. var head, style;
  291. head = document.getElementsByTagName('head')[0];
  292. if (!head) { return; }
  293. style = document.createElement('style');
  294. style.type = 'text/css';
  295. style.innerHTML = css;
  296. head.appendChild(style);
  297. }
  298.  
  299. function listenKey(selectorTxt, actionFunction, bWaitOnce, iframeSelector) {
  300. var targetNodes, btargetsFound;
  301.  
  302. if (typeof iframeSelector == "undefined")
  303. targetNodes = $(selectorTxt);
  304. else
  305. targetNodes = $(iframeSelector).contents ()
  306. .find (selectorTxt);
  307.  
  308. if (targetNodes && targetNodes.length > 0) {
  309. btargetsFound = true;
  310. targetNodes.each ( function () {
  311. var jThis = $(this);
  312. var alreadyFound = jThis.data ('alreadyFound') || false;
  313.  
  314. if (!alreadyFound) {
  315. //--- Call the payload function.
  316. var cancelFound = actionFunction (jThis);
  317. if (cancelFound) btargetsFound = false;
  318. else jThis.data ('alreadyFound', true);
  319. }
  320. } );
  321. } else {
  322. btargetsFound = false;
  323. }
  324.  
  325. var controlObj = listenKey.controlObj || {};
  326. var controlKey = selectorTxt.replace (/[^\w]/g, "_");
  327. var timeControl = controlObj [controlKey];
  328.  
  329. //--- Now set or clear the timer as appropriate.
  330. if (btargetsFound && bWaitOnce && timeControl) {
  331. clearInterval (timeControl);
  332. delete controlObj [controlKey]
  333. } else {
  334. //设置定时器
  335. if ( ! timeControl) {
  336. timeControl = setInterval ( function () {
  337. listenKey(selectorTxt,actionFunction,bWaitOnce,iframeSelector);
  338. }, 300);
  339. controlObj [controlKey] = timeControl;
  340. }
  341. }
  342. listenKey.controlObj = controlObj;
  343. }
  344. })