haijiao-vip: 海角社区 解锁收费视频,VIP,去广告

解锁 海角社区(haijiao.com) 收费视频,VIP,并去除网站广告, TG频道:@svip_nav.本插件完全免费,请注意甄别,避免上当受骗.

Versione datata 01/07/2024. Vedi la nuova versione l'ultima versione.

  1. // ==UserScript==
  2. // @name haijiao-vip: 海角社区 解锁收费视频,VIP,去广告
  3. // @namespace https://github.com/sex4096/haijiao_vip
  4. // @version 1.1.1
  5. // @author forgetme8
  6. // @description 解锁 海角社区(haijiao.com) 收费视频,VIP,并去除网站广告, TG频道:@svip_nav.本插件完全免费,请注意甄别,避免上当受骗.
  7. // @homepage https://github.com/sex4096/haijiao_vip#readme
  8. // @supportURL https://github.com/sex4096/haijiao_vip/issue
  9. // @run-at document-idle
  10. // @match https://www.hjcx.org/*
  11. // @match https://hjcx.org/*
  12. // @match https://www.haijiao.com/*
  13. // @match https://haijiao.com/*
  14. // @match https://*.top/home
  15. // @match */post/details?pid=*
  16. // @license MIT
  17. // @connect cdn.jsdelivr.net
  18. // @require https://cdn.jsdelivr.net/npm/react@18.3.0/umd/react.production.min.js
  19. // @require https://cdn.jsdelivr.net/npm/react-dom@18.3.0/umd/react-dom.production.min.js
  20. // @require https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js
  21. // @require https://cdn.jsdelivr.net/npm/antd@5.16.4/dist/antd.min.js
  22. // @require https://cdn.jsdelivr.net/npm/@ant-design/icons@5.3.6/dist/index.umd.min.js
  23. // ==/UserScript==
  24. (function (React$1, ReactDOM, antd, icons) {
  25. 'use strict';
  26.  
  27. function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
  28.  
  29. var React__default = /*#__PURE__*/_interopDefaultLegacy(React$1);
  30. var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
  31.  
  32. var __webpack_require__ = undefined;
  33. var VUE = undefined;
  34. var STORE = undefined;
  35. var AXIOS = undefined;
  36. var callback = undefined;
  37. function initHookWebpack(initialed) {
  38. callback = initialed;
  39. let originCall = Function.prototype.call;
  40. Function.prototype.call = function () {
  41. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  42. args[_key] = arguments[_key];
  43. }
  44. const result = originCall.apply(this, args);
  45. if (args.length === 4 && args[1] && args[1].exports && args[0] === args[2] && __webpack_require__ === undefined) {
  46. __webpack_require__ = args[3];
  47. Function.prototype.call = originCall;
  48. importModules();
  49. }
  50. return result;
  51. };
  52. }
  53. function importModules() {
  54. VUE = __webpack_require__("2b0e").default;
  55. STORE = __webpack_require__("4360").a;
  56. AXIOS = __webpack_require__("bc3a");
  57. AXIOS = getObject(AXIOS);
  58. AXIOS = AXIOS.a;
  59. callback(VUE, STORE, AXIOS);
  60. }
  61. function getObject(module) {
  62. const t = module && module.__esModule ? function () {
  63. return module.default;
  64. } : function () {
  65. return module;
  66. };
  67. defineObject(t, "a", t);
  68. return t;
  69. }
  70. function defineObject(module, key, value) {
  71. Object.prototype.hasOwnProperty.call(module, key) || Object.defineProperty(module, key, {
  72. enumerable: true,
  73. get: value
  74. });
  75. }
  76.  
  77. /**
  78. * 加载模块
  79. * @param module
  80. */
  81. function getModule(module) {
  82. if (!__webpack_require__) return null;
  83. return __webpack_require__(module);
  84. }
  85.  
  86. const Settings = _ref => {
  87. let {
  88. initialSettings,
  89. onFormInstanceReady
  90. } = _ref;
  91. const [form] = antd.Form.useForm();
  92. React$1.useEffect(() => {
  93. onFormInstanceReady(form);
  94. }, []);
  95. initialSettings.viewBanUser = false;
  96. return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(antd.Form, {
  97. form: form,
  98. name: "settings",
  99. labelAlign: "right",
  100. labelCol: {
  101. span: 8
  102. },
  103. wrapperCol: {
  104. span: 16
  105. },
  106. initialValues: initialSettings
  107. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  108. label: "\u53BB\u5E7F\u544A"
  109. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  110. name: "removeAds",
  111. noStyle: true
  112. }, /*#__PURE__*/React.createElement(antd.Switch, null)), /*#__PURE__*/React.createElement("span", {
  113. style: {
  114. marginLeft: 10
  115. }
  116. }, "\u53BB\u9664\u7F51\u7AD9\u5E7F\u544A")), /*#__PURE__*/React.createElement(antd.Form.Item, {
  117. label: "\u5C4F\u853D\u7F6E\u9876"
  118. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  119. name: "removeTops",
  120. noStyle: true
  121. }, /*#__PURE__*/React.createElement(antd.Switch, null)), /*#__PURE__*/React.createElement("span", {
  122. style: {
  123. marginLeft: 10
  124. }
  125. }, "\u5C4F\u853D\u5168\u5C40\u7F6E\u9876\u5E16")), /*#__PURE__*/React.createElement(antd.Form.Item, {
  126. label: "\u67E5\u770B\u5C01\u7981\u7528\u6237"
  127. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  128. name: "viewBanUser",
  129. noStyle: true
  130. }, /*#__PURE__*/React.createElement(antd.Switch, {
  131. disabled: true
  132. })), /*#__PURE__*/React.createElement("span", {
  133. style: {
  134. marginLeft: 10
  135. }
  136. }, "\u67E5\u770B\u88AB\u5C01\u7981\u7684\u7528\u6237\u4FE1\u606F(\u5347\u7EA7\u4E2D)")), /*#__PURE__*/React.createElement(antd.Form.Item, {
  137. label: "\u89E3\u9501VIP"
  138. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  139. name: "unlockVip",
  140. noStyle: true
  141. }, /*#__PURE__*/React.createElement(antd.Switch, null)), /*#__PURE__*/React.createElement("span", {
  142. style: {
  143. marginLeft: 10
  144. }
  145. }, "\u53EF\u89C2\u770Bvip\u533A\u7684\u5E16\u5B50\u53CA\u89C6\u9891")), /*#__PURE__*/React.createElement(antd.Form.Item, {
  146. label: "\u89E3\u9501\u6536\u8D39\u89C6\u9891"
  147. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  148. name: "unlockBuy",
  149. noStyle: true
  150. }, /*#__PURE__*/React.createElement(antd.Switch, null)), /*#__PURE__*/React.createElement("span", {
  151. style: {
  152. marginLeft: 10
  153. }
  154. }, "\u53EF\u89C2\u770B\u9700\u8981\u8D2D\u4E70\u7684\u89C6\u9891")), /*#__PURE__*/React.createElement(antd.Form.Item, {
  155. label: "\u670D\u52A1\u5730\u5740"
  156. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  157. name: "host",
  158. noStyle: true,
  159. rules: [{
  160. required: true,
  161. message: "请输入服务器地址"
  162. }]
  163. }, /*#__PURE__*/React.createElement(antd.Input, null)), /*#__PURE__*/React.createElement("h2", {
  164. style: {
  165. color: "red",
  166. marginTop: 5,
  167. textAlign: "center"
  168. }
  169. }, "\u8BF7\u4ED4\u7EC6\u9605\u8BFB!!!!!"), /*#__PURE__*/React.createElement("div", {
  170. style: {
  171. color: "red",
  172. marginTop: 5
  173. }
  174. }, /*#__PURE__*/React.createElement("div", null, "\u670D\u52A1\u5668\u5730\u5740\u4E0D\u5B9A\u65F6\u66F4\u6362"), /*#__PURE__*/React.createElement("div", null, "\u60A8\u53EF\u4EE5\u901A\u8FC7\u4EE5\u4E0B\u65B9\u5F0F\u83B7\u53D6\u670D\u52A1\u5730\u5740:"), /*#__PURE__*/React.createElement("div", null, "1. \u8BF7\u8BA2\u9605TG\u9891\u9053:", /*#__PURE__*/React.createElement("a", {
  175. href: "https://t.me/svip_nav",
  176. target: "_blank"
  177. }, "@svip_nav"), "\u83B7\u53D6\u6700\u65B0\u5730\u5740"), /*#__PURE__*/React.createElement("div", null, "2. \u79C1\u4FE1\u6211\u83B7\u53D6\u670D\u52A1\u5730\u5740,\u4E0D\u5B9A\u671F\u665A\u4E0A\u7EDF\u4E00\u56DE\u590D."), /*#__PURE__*/React.createElement("div", null, "3. \u901A\u8FC7", /*#__PURE__*/React.createElement("a", {
  178. href: "https://hjcx.org",
  179. target: "_blank"
  180. }, "https://hjcx.org"), "\u89C2\u770B,\u8BE5\u5730\u5740\u4E0D\u9700\u8981\u586B\u5199\u670D\u52A1\u5730\u5740\u5373\u53EF\u89C2\u770B.")))));
  181. };
  182.  
  183. class PluginStore {
  184. static get(key, defaultValue) {
  185. const value = localStorage.getItem(key);
  186. if (value === null) {
  187. return defaultValue;
  188. }
  189. if (typeof defaultValue === "number") {
  190. return parseInt(value);
  191. }
  192. if (typeof defaultValue === "boolean") {
  193. return value === "true";
  194. }
  195. return value;
  196. }
  197. static set(key, value) {
  198. localStorage.setItem(key, value.toString());
  199. }
  200. }
  201.  
  202. const App = () => {
  203. const [isModalOpen, setIsModalOpen] = React$1.useState(false);
  204. const [formInstance, setFormInstance] = React$1.useState();
  205. const showModal = () => {
  206. setIsModalOpen(true);
  207. };
  208. const handleOk = async () => {
  209. const values = await formInstance?.validateFields();
  210. onCreate(values);
  211. setIsModalOpen(false);
  212. };
  213. const handleCancel = () => {
  214. setIsModalOpen(false);
  215. };
  216. const onCreate = values => {
  217. console.log("Received values of form: ", values);
  218. PluginStore.set("removeAds", values.removeAds);
  219. PluginStore.set("removeTops", values.removeTops);
  220. PluginStore.set("unlockVip", values.unlockVip);
  221. PluginStore.set("unlockBuy", values.unlockBuy);
  222. PluginStore.set("viewBanUser", values.viewBanUser);
  223. PluginStore.set("host", values.host);
  224. };
  225. return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(antd.FloatButton, {
  226. type: "primary",
  227. tooltip: "\u6D77\u89D2VIP\u8BBE\u7F6E",
  228. style: {
  229. left: 16
  230. },
  231. icon: /*#__PURE__*/React__default["default"].createElement(icons.SettingOutlined, null),
  232. onClick: showModal
  233. }), /*#__PURE__*/React__default["default"].createElement(antd.Modal, {
  234. title: "\u8BBE\u7F6E",
  235. open: isModalOpen,
  236. onOk: handleOk,
  237. onCancel: handleCancel,
  238. destroyOnClose: true,
  239. okButtonProps: {
  240. autoFocus: true
  241. }
  242. }, /*#__PURE__*/React__default["default"].createElement(Settings, {
  243. initialSettings: {
  244. removeAds: PluginStore.get("removeAds", true),
  245. removeTops: PluginStore.get("removeTops", true),
  246. viewBanUser: PluginStore.get("viewBanUser", true),
  247. unlockVip: PluginStore.get("unlockVip", true),
  248. unlockBuy: PluginStore.get("unlockBuy", true),
  249. host: PluginStore.get("host", "https://haijiao.ku-cloud.com")
  250. },
  251. onFormInstanceReady: instance => {
  252. setFormInstance(instance);
  253. }
  254. })));
  255. };
  256.  
  257. /**
  258. * 自定义拦截器
  259. */
  260. class Interceptor {
  261. // axios模块
  262.  
  263. constructor(axios) {
  264. this.axios = axios;
  265. }
  266.  
  267. /**
  268. * 初始化请求拦截器
  269. */
  270. initRequestInterceptor() {
  271. this.axios.interceptors.request.use(this.requestInterceptor.bind(this));
  272. }
  273.  
  274. /**
  275. * 初始化返回请求拦截器
  276. */
  277. initResponseInterceptor() {
  278. if (this.axios.interceptors.response.handlers.length != 1) {
  279. return;
  280. }
  281. // 因为返回处理会处理掉config,而我们需要config中的url,所以需要在返回处理之前处理
  282. if (this.axios.interceptors.response.handlers?.[0].fulfilled) {
  283. const origin = this.axios.interceptors.response.handlers?.[0].fulfilled;
  284. this.axios.interceptors.response.handlers[0].fulfilled = async response => {
  285. const data = await origin(response);
  286. response = {
  287. data: data,
  288. config: response.config
  289. };
  290. return response;
  291. };
  292. }
  293. this.axios.interceptors.response.use(this.responseDecodeInterceptor.bind(this));
  294. this.axios.interceptors.response.use(this.responseInterceptor.bind(this));
  295. this.axios.interceptors.response.use(this.responseEncodeInterceptor.bind(this));
  296. }
  297.  
  298. /**
  299. * 请求拦截器
  300. * @param request
  301. * @returns
  302. */
  303. async requestInterceptor(request) {
  304. request = await this.requestUnlockBuyInterceptor(request);
  305. // request = await this.requestUnlockBanUserInterceptor(request);
  306. // request = await this.requestSearchInterceptor(request);
  307. return request;
  308. }
  309.  
  310. /**
  311. * 解锁购买
  312. * @param request
  313. * @returns
  314. */
  315. async requestUnlockBuyInterceptor(request) {
  316. if (PluginStore.get("unlockBuy", false) === true && PluginStore.get("host", "").length > 0) {
  317. if (/\/api\/attachment/g.test(request.url)) {
  318. console.log("转发请求", request.url, request);
  319. var host = PluginStore.get("host", "");
  320. request.baseURL = host;
  321. request.crossDomain = true;
  322. }
  323. }
  324. return request;
  325. }
  326.  
  327. /**
  328. * 查看被ban的用户信息
  329. * @param request
  330. */
  331. async requestUnlockBanUserInterceptor(request) {
  332. if (PluginStore.get("unlockBanUser", true) === true && PluginStore.get("host", "").length > 0) {
  333. if (/\/api\/user\/info\/\d+/g.test(request.url) || /\/api\/user\/news\/other_news_list/g.test(request.url) || /\/api\/topic\/node\/topics/g.test(request.url) && request.url.includes("userId")) {
  334. console.log("查看被ban的用户信息", request.url);
  335. var host = PluginStore.get("host", "");
  336. request.baseURL = host;
  337. request.crossDomain = true;
  338. }
  339. }
  340. return request;
  341. }
  342.  
  343. /**
  344. * 解锁搜索功能
  345. * @param request
  346. */
  347. async requestSearchInterceptor(request) {
  348. if (PluginStore.get("unlockSearch", true) === true && PluginStore.get("host", "").length > 0) {
  349. if (/\/api\/user\/search/g.test(request.url)) {
  350. console.log("搜索", request.url);
  351. var host = PluginStore.get("host", "");
  352. request.baseURL = host;
  353. request.crossDomain = true;
  354. }
  355. }
  356. return request;
  357. }
  358.  
  359. /**
  360. * 对返回数据进行解码
  361. * @param response
  362. */
  363. async responseDecodeInterceptor(response) {
  364. if (response.data.status === 200) {
  365. const origin_response = JSON.parse(JSON.stringify(response.data.data));
  366. var enc_data = response.data.data.data;
  367. if (enc_data && typeof enc_data === "string" && enc_data.length > 0) {
  368. const Base64 = getModule("e762").a;
  369. enc_data = JSON.parse(Base64.decode(Base64.decode(Base64.decode(enc_data))));
  370. }
  371. response = {
  372. item: enc_data,
  373. url: response.config.url,
  374. mobile: true,
  375. origin_response: origin_response
  376. };
  377. } else {
  378. // 克隆一个原始请求
  379. const origin_response = JSON.parse(JSON.stringify(response.data));
  380. const item = JSON.parse(JSON.stringify(response.data.data));
  381. response = {
  382. item: item,
  383. url: response.config.url,
  384. mobile: false,
  385. origin_response: origin_response
  386. };
  387. }
  388. return response;
  389. }
  390.  
  391. /**
  392. * 对reponse重新编码
  393. * @param response
  394. * @returns
  395. */
  396. async responseEncodeInterceptor(response) {
  397. if (response.mobile === true) {
  398. var dec = response.item;
  399. if (response.origin_response.isEncrypted === true) {
  400. const Base64 = getModule("e762").a;
  401. dec = Base64.encode(Base64.encode(Base64.encode(JSON.stringify(response.item))));
  402. }
  403. return {
  404. data: {
  405. ...response.origin_response,
  406. data: dec
  407. }
  408. };
  409. } else {
  410. return {
  411. ...response.origin_response,
  412. data: response.item
  413. };
  414. }
  415. }
  416. async responseInterceptor(response) {
  417. const url = response.url.toLowerCase();
  418. var item = response.item;
  419. console.log("拦截器返回数据", url, item);
  420. if (/^\/api\/topic\/\d+/g.test(url) && PluginStore.get("unlockVip", true) === true) {
  421. item = await this.fixTopic(item);
  422. }
  423. // 去广告
  424. else if (/banner\/banner_list/g.test(url) && PluginStore.get("removeAds", true) === true) {
  425. item = await this.fixAds(item);
  426. }
  427. // 屏蔽置顶帖
  428. else if (/^\/api\/topic\/global\/topics/g.test(url) && PluginStore.get("removeTops", true) === true) {
  429. item = await this.fixTops(item);
  430. }
  431. response.item = item;
  432. return response;
  433. }
  434. /**
  435. * 修复帖子内容
  436. * @param {*} data
  437. * @returns
  438. */
  439. async fixTopic(data) {
  440. console.log("修复帖子内容", data);
  441. if (data.node?.vipLimit > 0) {
  442. data.node.vipLimit = 0;
  443. }
  444. var content = data.content;
  445. if (content && !content.startsWith("<html><head></head><body>")) {
  446. // 删除掉[]标签
  447. content = content.replace(/\[视频\]/g, "");
  448. content = content.replace(/\[图片\]/g, "");
  449. content = content.replace(/此处内容售价\d+金币.*请购买后查看./g, "");
  450. content = content.replace(/\[sell.*\/sell]/g, "");
  451. data.attachments?.forEach(attachment => {
  452. var hasImage,
  453. hasVideo = false;
  454. // 处理图片
  455. if (attachment.category === "images" && attachment.remoteUrl) {
  456. content = content += `<p><img src="${attachment.remoteUrl}" data-id="${attachment.id}"/>`;
  457. hasImage = true;
  458. }
  459. if (hasImage === true) {
  460. content = `<p>${content}</p>`;
  461. }
  462. if (attachment.category === "video") {
  463. hasVideo = true;
  464. content += `<p><video src="${attachment.remoteUrl}" data-id="${attachment.id}"></video></p>`;
  465. }
  466. if (hasVideo === true) {
  467. content = `<p>${content}</p>`;
  468. }
  469. });
  470. content = `<html><head></head><body>${content}</body></html>`;
  471. } else {
  472. // 处理html内容
  473.  
  474. var parser = new DOMParser();
  475. var doc = parser.parseFromString(content, "text/html");
  476. const videoNodes = doc.querySelectorAll("video");
  477. videoNodes.forEach(videoNode => {
  478. videoNode.remove();
  479. });
  480. const sellContainer = doc.querySelector(".sell-btn");
  481. if (sellContainer) {
  482. sellContainer.remove();
  483. }
  484. if (Object.hasOwnProperty.call(data, "attachments") && data.attachments.length > 0) {
  485. data.attachments.forEach(attachment => {
  486. if (attachment.category === "video") {
  487. // 创建一个新tag加入到body中
  488. const video = doc.createElement("video");
  489. video.src = attachment.remoteUrl;
  490. video.setAttribute("data-id", attachment.id);
  491. doc.body.appendChild(video);
  492. }
  493. });
  494. }
  495. const serializer = new XMLSerializer();
  496. const serializedHTML = serializer.serializeToString(doc);
  497. content = serializedHTML;
  498. }
  499. data.content = content;
  500. return data;
  501. }
  502.  
  503. /**
  504. * 去广告
  505. * @param data
  506. */
  507. async fixAds(data) {
  508. return null;
  509. }
  510. async fixTops(data) {
  511. return [];
  512. }
  513. }
  514.  
  515. function addStyle() {
  516. let script = document.createElement("link");
  517. script.setAttribute("rel", "stylesheet");
  518. script.setAttribute("type", "text/css");
  519. script.href = "https://cdn.jsdelivr.net/npm/antd@5.16.4/dist/reset.min.css";
  520. document.documentElement.appendChild(script);
  521. }
  522. function setCookie(name, value) {
  523. document.cookie = name + "=" + value + ";path=/;expires=" + new Date(Date.now() + 864e5).toUTCString() + ";";
  524. }
  525.  
  526. function initialed(vue, store, axios) {
  527. const interceptor = new Interceptor(axios);
  528. interceptor.initRequestInterceptor();
  529. interceptor.initResponseInterceptor();
  530. }
  531. function initSetting() {
  532. const myButton = /*#__PURE__*/React__default["default"].createElement(App, null);
  533. const pluginDiv = document.createElement("div");
  534. pluginDiv.id = "haijiao-vip-plugin";
  535. document.body.appendChild(pluginDiv);
  536. ReactDOM__default["default"].render(myButton, pluginDiv);
  537. }
  538. sessionStorage.setItem("pageOpen", "1");
  539. if (/hjcx.org/.test(window.location.href)) {
  540. setCookie("is_vip", "1");
  541. } else {
  542. addStyle();
  543. initSetting();
  544. initHookWebpack(initialed);
  545. }
  546. console.log("haijiao-vip-plugin init success");
  547.  
  548. })(React, ReactDOM, antd, icons);