V2EX增强插件

一些增强功能

目前為 2025-03-30 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name V2EX增强插件
  3. // @description 一些增强功能
  4. // @homepage https://greasyfork.org/zh-CN/scripts/3452
  5. // @namespace yfmx746qpx8vhhmrgzt9s4cijmejj3tn
  6. // @icon https://favicon.yandex.net/favicon/www.v2ex.com
  7. // @author me
  8. // @match https://*.v2ex.com/*
  9. // @match https://v2ex.com/*
  10. // @version 2025.03.30
  11. // @grant none
  12. // ==/UserScript==
  13. // 2025.03.30 原生代码实现签到功能,去除对jQuery库的依赖
  14. // 2024.03.08 新消息界面,回复提醒对比感谢提醒更加醒目
  15. // 2024.02.27 回复框增加快捷回复,建议广告贴发在推广节点
  16. // 2024.01.16 新消息界面,显示消息序号,页码链接显示序号范围
  17. // 2024.01.08 更新管理员列表,标记回帖中漏标记的管理员
  18. // 2023.12.27 避免链接转图片的大小超出布局
  19. // 2021.11.26 账户余额页面增加签到页面链接
  20. // 2020.10.19 首页增加全文搜索链接
  21. // 2020.10.10 解决由于改版导致的定位错误导致无法签到问题
  22. // 2019.10.25 解决Safari不能重加载新浪图片
  23. // 2019.08.01 修正帖子tag区域的链接转图片误判
  24. // 2019.05.12 新浪的图片反防盗链
  25. // 2017.05.16 由于存储数据出错,改变存储数据的方式
  26. // 2016.09.21 修复发帖页面判断用户名出错的情况
  27. // 2016.09.14 修正判断登录状态逻辑
  28. // 2016.05.25 链接自动转图片
  29. // 2016.05.21 新增召唤/呼叫管理员
  30. // 2016.05.09 Webkit内核允许修改回复框高度
  31. // 2016.04.12 在回复时可@所有人
  32. // 2015.10.16 新增在回复中标记楼主
  33. // 2015.03.22 尝试修正未知原因情况下导致的签到失败。
  34. // 2015.02.07 解决JQuery库在某种情况可能会无法载入
  35. // 2014.10.07 某种情况下会产生cookie重复赋值,增加清理补丁。
  36. // 2014.10.06 cookie信息过期时间改为3天
  37. setTimeout(function(){
  38. // 签到
  39. setTimeout(() => {
  40. if (document.querySelector('a.balance_area') && document.querySelector('a[href="/settings"]')) {//已登陆
  41. var username = document.querySelector('a[href^="/member/"]').innerHTML;
  42. var today=new Date().toISOString().split("T")[0];
  43. if(localStorage.signdate==today && localStorage.signuser==username){
  44. return;//已签到就结束
  45. }
  46. var infobar = document.querySelector('#search');
  47. if(!infobar){
  48. return;//没有搜索栏的页面不执行签到工作
  49. }
  50. var days=0;//连续登陆天数
  51. fetch("/")
  52. .then(()=>{
  53. infobar.value = "正在检测每日签到状态...";
  54. return fetch("/mission/daily");//继续继续,前往领取页面
  55. })
  56. .then(rsp => rsp.text())
  57. .then(data=>{
  58. var parser = new DOMParser();
  59. var doc = parser.parseFromString(data, "text/html");
  60. if(doc.querySelector('input[value^="领取"]')){//领取按钮存在,尝试领取
  61. infobar.value = "正在领取签到奖励..."
  62. var url=doc.querySelector('input[value^="领取"]').getAttribute('onclick').split("'")[1];
  63. //<input type="button" class="xxx" value="领取 X 铜币" onclick="location.href = '/mission/daily/redeem?once=89367';">
  64. return fetch(url)//继续继续,提交领取动作
  65. } else {//按钮不存在
  66. if (data.indexOf("已领取") != -1) {
  67. localStorage.signdate=today;
  68. localStorage.signuser=username;
  69. throw new Error(infobar.value = "今天已经签到了。");
  70. } else {
  71. throw new Error(infobar.value = "无法识别领取奖励按钮 :( ");
  72. }
  73. }
  74. })
  75. .then(rsp => rsp.text())
  76. .then(data=>{
  77. days=data.split("已连续登")[1].split(" ")[1];//连续登陆天数
  78. //若是首页,签到入口淡化消失
  79. if(document.querySelector('a[href="/mission/daily"]')){
  80. (function(ele=document.querySelector('a[href="/mission/daily"]').parentElement.parentElement){
  81. ele.style.opacity=1;//默认为""
  82. var Timeindex=setInterval(() => {
  83. ele.style.opacity -= 0.067;
  84. if(ele.style.opacity<0.01){
  85. ele.style.display="none";
  86. clearInterval(Timeindex);
  87. }
  88. }, 200);
  89. })()
  90. }
  91. return fetch("/balance");//继续继续,查看领取数量
  92. })
  93. .then(rsp => rsp.text())
  94. .then(data=>{
  95. var todaystr=new Date().toISOString().split("T")[0].replaceAll("-","");
  96. if (data.indexOf(todaystr + " 的每日登录奖励")!== -1){
  97. var money=data.match(/每日登录奖励 \d+ 铜币/)[0].match(/\d+/)[0];
  98. console.log(infobar.value = "已连续领取" + days + "天,本次领到" + money + "铜币");
  99. } else {
  100. console.log(infobar.value = "未能识别到领取");
  101. }
  102. })
  103. .catch(error => {
  104. console.error("Sign info:", error);
  105. if(typeof error=="string" && error.indexOf("已经签到") == -1) {
  106. infobar.value = "请手动领取今日的登录奖励!";
  107. }
  108. });//end fetch
  109. }//end 判断登陆状态
  110. }, 0);// end 签到
  111. //帖子标记个别没有自动标记的管理员,回复所有人
  112. if (location.href.indexOf("/t/") != -1) {
  113. (function (){
  114. var modarr=["Livid","Kai","Olivia","GordianZ","sparanoid","Tink","ano"];
  115. var modlist="@"+modarr.join(" @");//生成@所有管理员的列表
  116. var uname=document.getElementById("Rightbar").getElementsByTagName("a")[0].href.split("/member/")[1];//自己用户名
  117. //标记管理员,预存回复用户名列表
  118. var lzname=document.getElementById("Main").getElementsByClassName("avatar")[0].parentNode.href.split("/member/")[1];
  119. var allname='@'+lzname+' ';
  120. var all_elem = document.querySelectorAll('a[href^="/member"].dark');
  121. for(var i=0; i<all_elem.length; i++) {
  122. if (modlist.indexOf(all_elem[i].innerHTML)!= -1){
  123. if (document.getElementsByClassName("badges")[i].innerHTML.indexOf("mod") == -1){
  124. document.getElementsByClassName("badges")[i].innerHTML+='<div class="badge mod">MOD</div>';
  125. }
  126. }
  127. //为回复所有人做准备
  128. if ( uname != all_elem[i].innerHTML && all_elem[i].href.indexOf("/member/") != -1
  129. && all_elem[i].innerText == all_elem[i].innerHTML && allname.indexOf('@'+all_elem[i].innerHTML+' ') == -1 ) {
  130. allname+='@'+ all_elem[i].innerHTML+' ';
  131. }
  132. }
  133. if ( document.getElementById("reply_content") ) {
  134. document.getElementById("reply_content").parentNode.innerHTML
  135. +="&nbsp;&nbsp;&nbsp;&nbsp;<a href='javascript:;' onclick='if ( document.getElementById(\"reply_content\").value.indexOf(\""
  136. +allname+"\") == -1 ) {document.getElementById(\"reply_content\").value+=\"\\r\\n"+allname+"\"}'>@所有人</a>";
  137. if ( document.body.style.WebkitBoxShadow !== undefined ) {
  138. //允许调整回复框高度
  139. document.getElementById("reply_content").style.resize="vertical";
  140. }
  141. document.getElementById("reply_content").style.overflow="auto";
  142. document.getElementById("reply_content").parentNode.innerHTML
  143. +="&nbsp;&nbsp;&nbsp;&nbsp;<a href='javascript:;' onclick='if ( document.getElementById(\"reply_content\").value.indexOf(\""
  144. +modlist+"\") == -1 ) {document.getElementById(\"reply_content\").value+=\"\\r\\n"+modlist+"\"}'>@管理员</a>";
  145. }
  146. })();
  147. }// end 回复所有人,@管理员
  148. // 帖子回复框增加快捷回复,提示广告贴应发在推广节点
  149. if (location.href.indexOf("/t/") != -1) {
  150. (function(){
  151. document.getElementById("reply_content").parentNode.innerHTML
  152. +="&nbsp;&nbsp;&nbsp;&nbsp;<a href='javascript:;' onclick='document.getElementById(\"reply_content\").value+=\"\\r\\n"+"@Livid 这贴明显是推广贴,却没有发在推广节点。"+"\"'>报告广告贴</a>";
  153. })()
  154. }// end 举报广告贴链接
  155. // 图片链接自动转换成图片 参考caoyue@v2ex
  156. (function (){
  157. var links = document.links;
  158. for (var i=0;i<links.length;i++){
  159. var link = links[i];
  160. if (/^http.*\.(?:jpg|jpeg|jpe|bmp|png|gif)/i.test(link.href)
  161. && !/<img\s/i.test(link.innerHTML) && link.href.indexOf("v2ex.com/tag")==-1){
  162. link.innerHTML = "<img title='" + link.href + "' src='" + link.href + "' style='max-width:98%' decoding='async' loading='lazy' />";
  163. // decoding='async'异步解析图像,加快显示其他内容。loading='lazy'懒加载。
  164. }
  165. }
  166. })();// end 图片链接自动转换成图片
  167. //新浪图床的图片反防盗链
  168. (function (){
  169. Array.from(document.images).forEach(ele=>{
  170. if (ele.src.indexOf(".sinaimg.cn")!=-1) {
  171. ele.setAttribute("referrerPolicy","no-referrer");
  172. ele.src="https://image.baidu.com/search/down?thumburl=https://baidu.com&url="+ele.src;
  173. }
  174. })
  175. })();// end 新浪图床的图片反防盗链
  176. // 在账户余额界面/明细界面的上方增加签到页面链接
  177. if ( location.href.indexOf("/balance") != -1 && document.getElementsByClassName("tab").length > 0) {
  178. document.getElementsByClassName("tab")[0].parentNode.innerHTML+='<a class="tab" href="/mission/daily" >签到</a>'
  179. }
  180. //
  181. // 新消息界面,显示消息序号,页码链接显示序号范围
  182. if (location.href.indexOf("/notifications") != -1){
  183. (function(){
  184. var page_index=new URL(window.location.href).searchParams.get('p');
  185. var before_index=0;
  186. if(page_index!=null){
  187. before_index=(page_index-1)*50;
  188. }
  189. document.querySelectorAll("a[onclick^=delete]").forEach((ele,i)=>{
  190. var index_ele=document.createElement("span");
  191. index_ele.innerText=(i+1+before_index)+". ";
  192. ele.parentElement.insertBefore(index_ele,ele.parentElement.firstElementChild)
  193. })
  194. var allmsgcount=document.querySelectorAll(".header .gray")[0].innerText;//消息总数
  195. document.querySelectorAll(".page_current,.page_normal").forEach((ele)=>{
  196. var index_a=(ele.innerText-1)*50+1;
  197. var index_b=(ele.innerText-1)*50+50;
  198. var title_str=index_a+"-"+index_b;
  199. if(allmsgcount-index_a<50){
  200. title_str=index_a+"-"+allmsgcount;
  201. }
  202. ele.setAttribute("title",title_str)
  203. })
  204. })();
  205. }// end 新消息界面,序号和翻页按钮优化
  206. // 新消息界面,回复提醒对比感谢提醒更加醒目
  207. if (location.href.indexOf("/notifications") != -1){
  208. (function(){
  209. if(document.querySelectorAll(".payload").length > 0){
  210. document.querySelectorAll(".payload").forEach((ele) => {
  211. if(ele.parentElement.innerText.indexOf("时提到了你") != -1
  212. || ele.parentElement.innerText.indexOf("里回复了你") != -1 ){
  213. //1、被人@提醒。2、回复我的主题提醒。
  214. ele.style.backgroundColor="#F9EA9A";
  215. }
  216. })
  217. }
  218. })();
  219. }// end 新消息界面优化
  220. },0);// end