妖火网增强插件

妖火网回复增强

  1. // ==UserScript==
  2. // @name 妖火网增强插件
  3. // @namespace https://yaohuo.me/
  4. // @version 0.22
  5. // @description 妖火网回复增强
  6. // @author 外卖不用券(id:23825)
  7. // @match https://yaohuo.me/*
  8. // @icon https://yaohuo.me/css/favicon.ico
  9. // @grant unsafeWindow
  10. // @license MIT
  11. // @2022/3/11 增加无跳转回复帖子
  12. // @2022/3/11 去除jQuery,使用原生方式获取元素,支持非油猴手机浏览器
  13. // @2022/3/12 回帖增加图床,自动插入图片UBB
  14. // @2022/3/12 增加论坛表情包展开可视化选择
  15. // @2022/3/12 增加文字颜色UBB快捷选择
  16. // @2022/3/12 网址链接自动替换成UBB格式
  17. // @2022/3/12 修复图片UBB与提取网址UBB冲突
  18. // @2022/3/13 增加无感深入查看下一页回帖
  19. // @2022/3/13 Alook安卓&iOS测试通过
  20. // @2022/3/13 增加无跳转回复任意楼层
  21.  
  22. // @202003132004在外卖佬0.20基础添加了UBB快捷功能
  23. // @author 北行(id:3321)
  24.  
  25. // @202003132234在0.21基础添加了将普通文本替换成链接的功能(使用了一个其他作者的脚本)
  26. // @author 北行(id:3321)
  27.  
  28. // ==/UserScript==
  29. ;
  30. console.log("妖火网分享你我!");
  31. const FORE_COLORS = {
  32. '会员红': '#ff00c0',
  33. '妖火蓝': '#3d68a8',
  34. '论坛绿': '#21b273'
  35. };
  36. const ADD_UBB = {
  37. '超链接': '[url=] [/url]',
  38. '加粗':'[b] [/b]',
  39. '斜体':'[i] [/i]',
  40. '下划线':'[u] [/u]',
  41. '删除线':'[strike] [/strike]'
  42. };
  43. /* 表单对象序列化 */
  44. function stringify(obj, sep, eq) {
  45. sep = sep || '&';
  46. eq = eq || '=';
  47. let str = "";
  48. for (var k in obj) {
  49. str += k + eq + unescape(obj[k]) + sep;
  50. }
  51. return str.slice(0, -1);
  52. };
  53.  
  54. /* POST表单封装 */
  55. async function postData(url = '', data = {}) {
  56. const response = await fetch(url, {
  57. method: 'POST',
  58. mode: 'cors',
  59. cache: 'no-cache',
  60. headers: {
  61. 'Content-Type': 'application/x-www-form-urlencoded',
  62. },
  63. redirect: 'follow',
  64. referrerPolicy: 'no-referrer',
  65. body: stringify(data)
  66. });
  67. return response;
  68. }
  69.  
  70. /* GET简单封装 */
  71. async function getData(url = '') {
  72. const response = await fetch(url, {
  73. method: 'GET',
  74. mode: 'cors',
  75. cache: 'no-cache',
  76. redirect: 'follow',
  77. referrerPolicy: 'no-referrer',
  78. });
  79. return response.text();
  80. }
  81.  
  82. /**
  83. * 内容ubb检测处理
  84. */
  85. function ubbProcess(content) {
  86. /* 匹配内容网址,替换为[url=https://xxx][/url]链接UBB */
  87. const urlReg = new RegExp("((http|ftp|https):\/\/[\\w\-_]+(\.[\\w\-_]+)+([\\w\-\.,@?^=%&:/~\+#]*[\\w\-\\@?^=%&/~\+#])?)");
  88. /* const urlReg = /((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?)/; */
  89. const urlMatchResult = content.match(urlReg);
  90. if (urlMatchResult != null && content.indexOf('[img]') == -1) {
  91. /* TODO简单排除图片UBB内链接 */
  92. const urlString = urlMatchResult[0];
  93. content = content.replace(urlString, '[url]' + urlString + '[/url]');
  94. }
  95. /* 添加[forecolor=#000000][/forecolor]文本颜色UBB */
  96. let foreColor = document.getElementsByName("forecolor")[0];
  97. if (foreColor && foreColor.value != '文字颜色') {
  98. let chooseColor = FORE_COLORS[foreColor.value];
  99. content = '[forecolor=' + chooseColor + ']' + content + '[/forecolor]';
  100. }
  101. return content;
  102. }
  103.  
  104. /*
  105. * 无跳转回帖
  106. */
  107. let replyButton = document.getElementsByName("g")[0];
  108. if (replyButton && replyButton.value == "快速回复") {
  109. replyButton.addEventListener('click',
  110. function (event) {
  111. event.preventDefault();
  112. let content = document.getElementsByName("content")[0].value;
  113. if (content.length) {
  114. content = ubbProcess(content);
  115. /* 获取form表单参数 */
  116. var face = document.getElementsByName("face")[0].value;
  117. var sendmsg = document.getElementsByName("sendmsg")[0].value;
  118. var action = document.getElementsByName("action")[0].value;
  119. var id = document.getElementsByName("id")[0].value;
  120. var siteid = document.getElementsByName("siteid")[0].value;
  121. var lpage = document.getElementsByName("lpage")[0].value;
  122. var classid = document.getElementsByName("classid")[0].value;
  123. var sid = document.getElementsByName("sid")[0].value;
  124. var g = document.getElementsByName("g")[0].value;
  125. /* 发表回复 */
  126. postData('/bbs/book_re.aspx', {
  127. face: face,
  128. sendmsg: sendmsg,
  129. content: content,
  130. action: action,
  131. id: id,
  132. siteid: siteid,
  133. lpage: lpage,
  134. classid: classid,
  135. sid: sid,
  136. g: g
  137. }).then(function (data) {
  138. /* console.log(data) 回复成功!</b> 获得妖晶:30,获得经验:10<br/> 跳转中... */
  139. location.reload();
  140. /* 直接刷新页面,没有优化处理 */
  141. })
  142. }
  143. })
  144. }
  145.  
  146. /*
  147. * 图床传图
  148. */
  149. let PIC_UPLOAD_API = "https://kf.dianping.com/api/file/burstUploadFile";
  150. function uploadImage() {
  151. console.log("上传图片");
  152. let uploadimg = document.getElementsByName("upload-image")[0];
  153. /* 构造表单 */
  154. const formData = new FormData();
  155. formData.append('files', uploadimg.files[0]);
  156. formData.append('fileName', uploadimg.files[0].name);
  157. formData.append('part', '0');
  158. formData.append('partSize', '1');
  159. formData.append('fileID', new Date().getTime());
  160. /* 请求头 */
  161. let headers = {
  162. 'Host': 'kf.dianping.com',
  163. 'CSC-VisitId': 'access-0ef0c9ff-03d9-42b9-952a-0a221e9c0e3a',
  164. 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'
  165. };
  166. postImage(PIC_UPLOAD_API, formData, headers).then(function (data) {
  167. if (data.code == 200) {
  168. let uploadPath = data.data.uploadPath;
  169. /* 粘贴UBB代码到文本框 */
  170. let content = document.getElementsByName("content")[0];
  171. content.value += '\r\n[img]' + uploadPath + '[/img]';
  172. }
  173. }).
  174. catch(function (error) {
  175. console.log(error);
  176. })
  177. }
  178.  
  179. /* POST图片封装 */
  180. async function postImage(url = '', data = {},
  181. headers = {}) {
  182. const response = await fetch(url, {
  183. method: 'POST',
  184. headers: headers,
  185. body: data
  186. });
  187. return response.json();
  188. }
  189.  
  190. /* 创建上传文件按钮、自动上传 */
  191. let replyForm = document.getElementsByName("f")[0];
  192. if (replyForm) {
  193. let sendmsg = document.getElementsByName("sendmsg")[0];
  194. sendmsg.insertAdjacentHTML('afterend', '<input type="file" style="width: 40%" name="upload-image" accept="image/*" value="">');
  195. let uploadimg = document.getElementsByName("upload-image")[0];
  196. uploadimg.addEventListener('change',
  197. function () {
  198. uploadImage();
  199. })
  200. }
  201.  
  202. /*
  203. * 表情展开
  204. */
  205. let faceSelect = document.getElementsByName("face")[0];
  206. if (faceSelect) {
  207. /* 隐藏表情下拉组件 */
  208. faceSelect.style.cssText = 'display: none;';
  209. /* 创建新的表情选框 */
  210. faceSelect.insertAdjacentHTML('afterend', '<input type="button" value="表情" name="new-face" style="padding: 0px 20px;margin-right: 3px;height: 19px;border-radius: 2px;border-color: rgb(133, 133, 133);">');
  211. let newface = document.getElementsByName("new-face")[0];
  212. newface.addEventListener('click',
  213. function () {
  214. /* console.log('展开新表情'); */
  215. let newFaceBox = document.getElementsByName('new-face-box')[0];
  216. if (newFaceBox.style.display == "none") {
  217. newFaceBox.style.display = "block";
  218. } else newFaceBox.style.display = "none";
  219. });
  220. /* 创建表情图标 */
  221. createNewFace();
  222. }
  223. function createNewFace() {
  224. let content = document.getElementsByName("content")[0];
  225. if (content) {
  226. let faceHtml = '<div style="display:none" name="new-face-box">';
  227. const faces = ["踩.gif", "狂踩.gif", "淡定.gif", "囧.gif", "不要.gif", "重拳出击.gif", "砳砳.gif", "滑稽砳砳.gif", "沙发.gif", "汗.gif", "亲亲.gif", "太开心.gif", "酷.gif", "思考.gif", "发呆.gif", "得瑟.gif", "哈哈.gif", "泪流满面.gif", "放电.gif", "困.gif", "超人.gif", "害羞.gif", "呃.gif", "哇哦.gif", "要死了.gif", "谢谢.gif", "抓狂.gif", "无奈.gif", "不好笑.gif", "呦呵.gif", "感动.gif", "喜欢.gif", "疑问.gif", "委屈.gif", "你不行.gif", "流口水.gif", "潜水.gif", "咒骂.gif", "耶耶.gif", "被揍.gif", "抱走.gif",];
  228. for (const face of faces) {
  229. faceHtml += '<img name="face-icon" alt="' + face + '" src="/face/' + face + '" style="width: 30px; height: 30px">';
  230. }
  231. faceHtml += '</div>';
  232. content.insertAdjacentHTML('beforebegin', faceHtml);
  233. /* 注册(不可用)点击事件 */
  234. let faceIcons = document.getElementsByName("face-icon");
  235. let newFaceBox = document.getElementsByName('new-face-box')[0];
  236. let newface = document.getElementsByName("new-face")[0];
  237. let face = document.getElementsByName("face")[0];
  238. for (const faceIcon of faceIcons) {
  239. faceIcon.addEventListener('click',
  240. function () {
  241. let faceName = faceIcon.alt;
  242. if (newFaceBox.style.display == "none") {
  243. newFaceBox.style.display = "block";
  244. } else newFaceBox.style.display = "none";
  245. face.value = newface.value = faceName;
  246. })
  247. }
  248. };
  249. }
  250.  
  251. /**
  252. * 字体颜色
  253. */
  254. replyButton = document.getElementsByName("g")[0];
  255. if (replyButton) {
  256. replyButton.insertAdjacentHTML('afterend', '<input type="button" value="文字颜色" name="forecolor">');
  257. let foreColor = document.getElementsByName("forecolor")[0];
  258. foreColor.addEventListener('click',
  259. function () {
  260. let forecolorBox = document.getElementsByName('forecolor-box')[0];
  261. if (forecolorBox.style.display == "none") {
  262. forecolorBox.style.display = "inline-block";
  263. } else forecolorBox.style.display = "none";
  264. });
  265. createForeColorBox();
  266. }
  267. function createForeColorBox() {
  268. let forecolor = document.getElementsByName("forecolor")[0];
  269. if (forecolor) {
  270. let forecolorBoxHtml = '<span style="display:none" name="forecolor-box">';
  271. const colors = FORE_COLORS;
  272. for (const color in colors) {
  273. forecolorBoxHtml += '&nbsp;<span name="forecolor-span" style="color:' + colors[color] + '">' + color + '</span>';
  274. }
  275. forecolorBoxHtml += '</span>';
  276. forecolor.insertAdjacentHTML('afterend', forecolorBoxHtml);
  277. /* 注册(不可用)颜色选择事件 */
  278. let forecolorSpans = document.getElementsByName("forecolor-span");
  279. let forecolorBox = document.getElementsByName('forecolor-box')[0];
  280. for (const forecolorSpan of forecolorSpans) {
  281. forecolorSpan.addEventListener('click',
  282. function () {
  283. let chooseColor = colors[forecolorSpan.innerHTML];
  284. if (forecolorBox.style.display == "inline-block") {
  285. forecolorBox.style.display = "none";
  286. } else forecolorBox.style.display = "inline-block";
  287. forecolor.style.color = chooseColor;
  288. forecolor.value = forecolorSpan.innerHTML;
  289. })
  290. }
  291. }
  292. }
  293.  
  294. /**
  295. * 加载下一页
  296. */
  297. let moreBtn = document.getElementsByClassName('more')[0];
  298. if (moreBtn) {
  299. moreBtn.addEventListener('click', asyncPage);
  300. function asyncPage(e) {
  301. e.preventDefault();
  302. const moreLink = moreBtn.firstChild.href;
  303. console.log(moreLink);
  304. getData(moreLink).then(function (html) {
  305. /* 提取楼层回复 */
  306. const replyReg = new RegExp('<div class="line.">(.*?)<\/div>', "g");
  307. let nextReply = html.match(replyReg);
  308. for (const reply of nextReply) {
  309. moreBtn.insertAdjacentHTML('beforebegin', reply);
  310. }
  311. /* 提取下一页页码 */
  312. const linkReg = new RegExp('bt2"><a.href="(.*)">下一页', "g");
  313. let nextLink = html.match(linkReg)[1].replaceAll('\&amp;', '&');
  314. console.log(nextLink);
  315. /* 为新的下一页注册(不可用)事件 */
  316. const moreNextBtn = document.getElementsByClassName('more')[0];
  317. moreNextBtn.firstChild.href = nextLink;
  318. moreNextBtn.addEventListener('click', asyncPage);
  319. })
  320. };
  321. }
  322.  
  323. /**
  324. * 无跳转回复楼层
  325. */
  326. let replyLines = document.getElementsByClassName("reline");
  327. if (replyLines.length) {
  328. for (const replyLine of replyLines) {
  329. const replyLink = replyLine.children[0];
  330. replyLink.addEventListener('click', function (e) {
  331. /* 阻止默认跳转页面 */
  332. e.preventDefault();
  333. const replyNth = replyLink.href.match(new RegExp("reply=(\\d+)&"), "g")[1];
  334. /* 添加:回复N楼,通知对方 */
  335. let form = document.getElementsByName('f')[0];
  336. if (form.firstChild.tagName == 'B') {
  337. form.removeChild(form.firstChild);
  338. form.removeChild(form.firstChild);
  339. form.removeChild(form.firstChild);
  340. }
  341. form.insertAdjacentHTML('afterbegin', '<b name="reply-to" value="' + replyLink + '">回复' + replyNth + '楼 </b><select name="sendmsg2"><option value="1">通知对方</option><option value="0">不予通知</option></select><br>');
  342. /* 获取输入框焦点 */
  343. document.getElementsByName('content')[0].focus();
  344. /* 修改提交按钮文字 */
  345. let replyButton = document.getElementsByName("g")[0];
  346. replyButton.value = '发表回复';
  347. /* 监听点击事件 */
  348. replyButton.addEventListener('click',
  349. function (event) {
  350. event.preventDefault();
  351. let content = document.getElementsByName("content")[0].value;
  352. if (content.length) {
  353. content = ubbProcess(content);
  354. /* 获取form表单参数 */
  355. var sendmsg2 = document.getElementsByName("sendmsg2")[0].value;
  356. let replyLink = document.getElementsByName('reply-to')[0].getAttributeNode('value').value;
  357. var id = replyLink.match(new RegExp("&id=(\\d+)&"), "g")[1];
  358. var siteid = replyLink.match(new RegExp("siteid=(\\d+)&"), "g")[1];
  359. var lpage = replyLink.match(new RegExp("lpage=(\\d+)&"), "g")[1];
  360. var classid = replyLink.match(new RegExp("classid=(\\d+)&"), "g")[1];
  361. var reply = replyLink.match(new RegExp("reply=(\\d+)&"), "g")[1];
  362. var touserid = replyLink.match(new RegExp("touserid=(\\d+)$"), "g")[1];;
  363. var face = document.getElementsByName("face")[0].value;
  364. var sendmsg = document.getElementsByName("sendmsg")[0].value;
  365. var action = document.getElementsByName("action")[0].value;
  366. var sid = document.getElementsByName("sid")[0].value;
  367. var g = '发表回复';
  368. console.log(touserid);
  369. /* 发表回复 */
  370. postData('/bbs/book_re.aspx', {
  371. sendmsg2: sendmsg2,
  372. face: face,
  373. sendmsg: sendmsg,
  374. content: content,
  375. action: action,
  376. id: id,
  377. siteid: siteid,
  378. lpage: lpage,
  379. classid: classid,
  380. reply: reply,
  381. touserid: touserid,
  382. sid: sid,
  383. g: g
  384. }).then(function (data) {
  385. /* console.log(data) 回复成功!</b> 获得妖晶:30,获得经验:10<br/> 跳转中... */
  386. location.reload();
  387. /* 直接刷新页面,没有优化处理 */
  388. })
  389. }
  390. })
  391. })
  392. }
  393. }
  394.  
  395. //添加UBB
  396. replyButton = document.getElementsByName("g")[0];
  397. if (replyButton) {
  398. replyButton.insertAdjacentHTML('afterend', '<input type="button" value="添加UBB" name="addUbb">');
  399. let addUbb = document.getElementsByName("addUbb")[0];
  400. addUbb.addEventListener('click', function () {
  401. //alert("hello");
  402. let addUbbBox = document.getElementsByName('addUbb-box')[0];
  403. if (addUbbBox.style.display == "none") {
  404. addUbbBox.style.display = "inline-block";
  405. } else addUbbBox.style.display = "none";
  406. });
  407. createUbbBox();
  408. }
  409.  
  410. function createUbbBox(){
  411. let addUbb = document.getElementsByName("addUbb")[0];
  412. if (addUbb) {
  413. let addUbbBoxHtml = '<span style="display:none" name="addUbb-box">';
  414. const ubbs = ADD_UBB;
  415. for (const ubb in ubbs) {
  416. addUbbBoxHtml += '&nbsp;<span name="addUbb-span">' + ubb + '</span>';
  417. // alert(ubb);
  418. }
  419. addUbbBoxHtml += '</span>';
  420. addUbb.insertAdjacentHTML('afterend', addUbbBoxHtml);
  421. //添加Ubb到文本框
  422. let addUbbSpans = document.getElementsByName("addUbb-span");
  423. let addUbbBox = document.getElementsByName('addUbb-box')[0];
  424. for (const addUbbSpan of addUbbSpans) {
  425. addUbbSpan.addEventListener('click', function () {
  426. let chooseUbb = ubbs[addUbbSpan.innerHTML];
  427. if (addUbbBox.style.display == "inline-block") {
  428. addUbbBox.style.display = "none";
  429. } else addUbbBox.style.display = "inline-block";
  430. //forecolor.style.color = chooseColor;
  431. //forecolor.value = forecolorSpan.innerHTML;
  432. let content = document.getElementsByName("content")[0];
  433. content.value +=chooseUbb;
  434. })
  435. }
  436. }
  437. }
  438.  
  439. //替换链接
  440. /*TEXT link to Clickable Hyperlink*/
  441. let clearLink, excludedTags, filter, linkMixInit, linkPack, linkify, observePage, observer, setLink, url_regexp, xpath;
  442. url_regexp = /((https?:\/\/|www\.)[\x21-\x7e]+[\w\/]|(\w[\w._-]+\.(com|cn|org|net|info|tv|cc))(\/[\x21-\x7e]*[\w\/])?|ed2k:\/\/[\x21-\x7e]+\|\/|thunder:\/\/[\x21-\x7e]+=)/gi;
  443. clearLink = function(a) {
  444. let b;
  445. a = null != (b = a.originalTarget) ? b : a.target;
  446. if (null != a && "a" === a.localName && -1 !== a.className.indexOf("texttolink") && (b = a.getAttribute("href"), 0 !== b.indexOf("http") && 0 !== b.indexOf("ed2k://") && 0 !== b.indexOf("thunder://"))) return a.setAttribute("href", "http://" + b)
  447. };
  448. document.addEventListener("mouseover", clearLink);
  449. setLink = function(a) {
  450. if (null != a && -1 === a.parentNode.className.indexOf("texttolink") && "#cdata-section" !== a.nodeName) {
  451. let b = a.textContent.replace(url_regexp, '<a href="$1" target="_blank" class="texttolink">$1</a>');
  452. if (a.textContent.length !== b.length) {
  453. let c = document.createElement("span");
  454. c.innerHTML = b;
  455. return a.parentNode.replaceChild(c, a)
  456. }
  457. }
  458. };
  459. excludedTags = "a svg canvas applet input button area pre embed frame frameset head iframe img option map meta noscript object script style textarea code".split(" ");
  460. xpath = "//text()[not(ancestor::" + excludedTags.join(") and not(ancestor::") + ")]";
  461. filter = new RegExp("^(" + excludedTags.join("|") + ")$", "i");
  462. linkPack = function(a, b) {
  463. let c, d, e;
  464. if (b + 1E4 < a.snapshotLength) {
  465. e= c = b;
  466. for (d = b + 1E4; b <= d ? c <= d : c >= d; e = b <= d ? ++c : --c) setLink(a.snapshotItem(e));
  467. setTimeout(function() {
  468. return linkPack(a, b + 1E4)
  469. }, 15)
  470. } else{
  471. for (e = c = b, d = a.snapshotLength; b <= d ? c <= d : c >= d; e = b <= d ? ++c : --c) setLink(a.snapshotItem(e));
  472. }
  473. };
  474. linkify = function(a) {
  475. a = document.evaluate(xpath, a, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  476. return linkPack(a, 0)
  477. };
  478. observePage = function(a) {
  479. for (a = document.createTreeWalker(a, NodeFilter.SHOW_TEXT, {
  480. acceptNode: function(a) {
  481. if (!filter.test(a.parentNode.localName)) return NodeFilter.FILTER_ACCEPT
  482. }
  483. }, !1); a.nextNode();) setLink(a.currentNode)
  484. };
  485. observer = new window.MutationObserver(function(a) {
  486. let b, c;
  487. let d = 0;
  488. for (b = a.length; d < b; d++) {
  489. let e = a[d];
  490. if ("childList" === e.type) {
  491. let g = e.addedNodes;
  492. let f = 0;
  493. for (c = g.length; f < c; f++) e = g[f], observePage(e)
  494. }
  495. }
  496. });
  497. linkMixInit = function() {
  498. if (window === window.top && "" !== window.document.title) return linkify(document.body), observer.observe(document.body, {
  499. childList: !0,
  500. subtree: !0
  501. })
  502. };
  503. let clearlinkF = function(a) {
  504. let url = a.getAttribute("href");
  505. if (0 !== url.indexOf("http") && 0 !== url.indexOf("ed2k://") && 0 !== url.indexOf("thunder://")) return a.setAttribute("href", "http://" + url)
  506. },
  507. clearlinkE = function() {
  508. for (let a = document.getElementsByClassName("texttolink"), b = 0; b < a.length; b++) clearlinkF(a[b])
  509. };
  510. setTimeout(clearlinkE, 1500);
  511. setTimeout(linkMixInit, 100);

QingJ © 2025

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