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

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

Ajankohdalta 27.6.2024. Katso uusin versio.

  1. // ==UserScript==
  2. // @name haijiao-vip: 海角社区 解锁收费视频,VIP,去广告
  3. // @namespace https://github.com/sex4096/haijiao_vip
  4. // @version 1.0.7
  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. // @match */topic/*
  17. // @license MIT
  18. // @connect cdn.jsdelivr.net
  19. // @require https://cdn.jsdelivr.net/npm/react@18.3.0/umd/react.production.min.js
  20. // @require https://cdn.jsdelivr.net/npm/react-dom@18.3.0/umd/react-dom.production.min.js
  21. // @require https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js
  22. // @require https://cdn.jsdelivr.net/npm/antd@5.16.4/dist/antd.min.js
  23. // @require https://cdn.jsdelivr.net/npm/@ant-design/icons@5.3.6/dist/index.umd.min.js
  24. // ==/UserScript==
  25. (function (React$1, ReactDOM, antd, icons) {
  26. "use strict";
  27.  
  28. function _interopDefaultLegacy(e) {
  29. return e && typeof e === "object" && "default" in e ? e : { default: e };
  30. }
  31.  
  32. var React__default = /*#__PURE__*/ _interopDefaultLegacy(React$1);
  33. var ReactDOM__default = /*#__PURE__*/ _interopDefaultLegacy(ReactDOM);
  34.  
  35. var __webpack_require__ = undefined;
  36. var VUE = undefined;
  37. var STORE = undefined;
  38. var AXIOS = undefined;
  39. var callback = undefined;
  40. function initHookWebpack(initialed) {
  41. callback = initialed;
  42. let originCall = Function.prototype.call;
  43. Function.prototype.call = function () {
  44. for (
  45. var _len = arguments.length, args = new Array(_len), _key = 0;
  46. _key < _len;
  47. _key++
  48. ) {
  49. args[_key] = arguments[_key];
  50. }
  51. const result = originCall.apply(this, args);
  52. if (
  53. args.length === 4 &&
  54. args[1] &&
  55. args[1].exports &&
  56. args[0] === args[2] &&
  57. __webpack_require__ === undefined
  58. ) {
  59. __webpack_require__ = args[3];
  60. Function.prototype.call = originCall;
  61. importModules();
  62. }
  63. return result;
  64. };
  65. }
  66. function importModules() {
  67. VUE = __webpack_require__("2b0e").default;
  68. STORE = __webpack_require__("4360").a;
  69. AXIOS = __webpack_require__("bc3a");
  70. AXIOS = getObject(AXIOS);
  71. AXIOS = AXIOS.a;
  72. callback(VUE, STORE, AXIOS);
  73. }
  74. function getObject(module) {
  75. const t =
  76. module && module.__esModule
  77. ? function () {
  78. return module.default;
  79. }
  80. : function () {
  81. return module;
  82. };
  83. defineObject(t, "a", t);
  84. return t;
  85. }
  86. function defineObject(module, key, value) {
  87. Object.prototype.hasOwnProperty.call(module, key) ||
  88. Object.defineProperty(module, key, {
  89. enumerable: true,
  90. get: value,
  91. });
  92. }
  93.  
  94. /**
  95. * 加载模块
  96. * @param module
  97. */
  98. function getModule(module) {
  99. if (!__webpack_require__) return null;
  100. return __webpack_require__(module);
  101. }
  102.  
  103. const Settings = (_ref) => {
  104. let { initialSettings, onFormInstanceReady } = _ref;
  105. const [form] = antd.Form.useForm();
  106. React$1.useEffect(() => {
  107. onFormInstanceReady(form);
  108. }, []);
  109. return /*#__PURE__*/ React.createElement(
  110. React.Fragment,
  111. null,
  112. /*#__PURE__*/ React.createElement(
  113. antd.Form,
  114. {
  115. form: form,
  116. name: "settings",
  117. labelAlign: "right",
  118. labelCol: {
  119. span: 8,
  120. },
  121. wrapperCol: {
  122. span: 16,
  123. },
  124. initialValues: initialSettings,
  125. },
  126. /*#__PURE__*/ React.createElement(
  127. antd.Form.Item,
  128. {
  129. label: "\u53BB\u5E7F\u544A",
  130. },
  131. /*#__PURE__*/ React.createElement(
  132. antd.Form.Item,
  133. {
  134. name: "removeAds",
  135. noStyle: true,
  136. },
  137. /*#__PURE__*/ React.createElement(antd.Switch, null)
  138. ),
  139. /*#__PURE__*/ React.createElement(
  140. "span",
  141. {
  142. style: {
  143. marginLeft: 10,
  144. },
  145. },
  146. "\u53BB\u9664\u7F51\u7AD9\u5E7F\u544A"
  147. )
  148. ),
  149. /*#__PURE__*/ React.createElement(
  150. antd.Form.Item,
  151. {
  152. label: "\u5C4F\u853D\u7F6E\u9876",
  153. },
  154. /*#__PURE__*/ React.createElement(
  155. antd.Form.Item,
  156. {
  157. name: "removeTops",
  158. noStyle: true,
  159. },
  160. /*#__PURE__*/ React.createElement(antd.Switch, null)
  161. ),
  162. /*#__PURE__*/ React.createElement(
  163. "span",
  164. {
  165. style: {
  166. marginLeft: 10,
  167. },
  168. },
  169. "\u5C4F\u853D\u5168\u5C40\u7F6E\u9876\u5E16"
  170. )
  171. ),
  172. /*#__PURE__*/ React.createElement(
  173. antd.Form.Item,
  174. {
  175. label: "\u67E5\u770B\u5C01\u7981\u7528\u6237",
  176. },
  177. /*#__PURE__*/ React.createElement(
  178. antd.Form.Item,
  179. {
  180. name: "viewBanUser",
  181. noStyle: true,
  182. },
  183. /*#__PURE__*/ React.createElement(antd.Switch, null)
  184. ),
  185. /*#__PURE__*/ React.createElement(
  186. "span",
  187. {
  188. style: {
  189. marginLeft: 10,
  190. },
  191. },
  192. "\u67E5\u770B\u88AB\u5C01\u7981\u7684\u7528\u6237\u4FE1\u606F"
  193. )
  194. ),
  195. /*#__PURE__*/ React.createElement(
  196. antd.Form.Item,
  197. {
  198. label: "\u89E3\u9501VIP",
  199. },
  200. /*#__PURE__*/ React.createElement(
  201. antd.Form.Item,
  202. {
  203. name: "unlockVip",
  204. noStyle: true,
  205. },
  206. /*#__PURE__*/ React.createElement(antd.Switch, null)
  207. ),
  208. /*#__PURE__*/ React.createElement(
  209. "span",
  210. {
  211. style: {
  212. marginLeft: 10,
  213. },
  214. },
  215. "\u53EF\u89C2\u770Bvip\u533A\u7684\u5E16\u5B50\u53CA\u89C6\u9891"
  216. )
  217. ),
  218. /*#__PURE__*/ React.createElement(
  219. antd.Form.Item,
  220. {
  221. label: "\u89E3\u9501\u6536\u8D39\u89C6\u9891",
  222. },
  223. /*#__PURE__*/ React.createElement(
  224. antd.Form.Item,
  225. {
  226. name: "unlockBuy",
  227. noStyle: true,
  228. },
  229. /*#__PURE__*/ React.createElement(antd.Switch, null)
  230. ),
  231. /*#__PURE__*/ React.createElement(
  232. "span",
  233. {
  234. style: {
  235. marginLeft: 10,
  236. },
  237. },
  238. "\u53EF\u89C2\u770B\u9700\u8981\u8D2D\u4E70\u7684\u89C6\u9891"
  239. )
  240. ),
  241. /*#__PURE__*/ React.createElement(
  242. antd.Form.Item,
  243. {
  244. label: "\u670D\u52A1\u5730\u5740",
  245. },
  246. /*#__PURE__*/ React.createElement(
  247. antd.Form.Item,
  248. {
  249. name: "host",
  250. noStyle: true,
  251. rules: [
  252. {
  253. required: true,
  254. message: "请输入服务器地址",
  255. },
  256. ],
  257. },
  258. /*#__PURE__*/ React.createElement(antd.Input, null)
  259. ),
  260. /*#__PURE__*/ React.createElement(
  261. "div",
  262. {
  263. style: {
  264. color: "red",
  265. marginTop: 5,
  266. },
  267. },
  268. "\u670D\u52A1\u5668\u5730\u5740\u4E0D\u5B9A\u65F6\u66F4\u6362"
  269. ),
  270. /*#__PURE__*/ React.createElement(
  271. "div",
  272. null,
  273. "\u8BF7\u8BA2\u9605TG\u9891\u9053:@svip_nav\u83B7\u53D6\u6700\u65B0\u5730\u5740"
  274. )
  275. )
  276. )
  277. );
  278. };
  279.  
  280. class PluginStore {
  281. static get(key, defaultValue) {
  282. const value = localStorage.getItem(key);
  283. if (value === null) {
  284. return defaultValue;
  285. }
  286. if (typeof defaultValue === "number") {
  287. return parseInt(value);
  288. }
  289. if (typeof defaultValue === "boolean") {
  290. return value === "true";
  291. }
  292. return value;
  293. }
  294. static set(key, value) {
  295. localStorage.setItem(key, value.toString());
  296. }
  297. }
  298.  
  299. const App = () => {
  300. const [isModalOpen, setIsModalOpen] = React$1.useState(false);
  301. const [formInstance, setFormInstance] = React$1.useState();
  302. const showModal = () => {
  303. setIsModalOpen(true);
  304. };
  305. const handleOk = async () => {
  306. const values = await formInstance?.validateFields();
  307. onCreate(values);
  308. setIsModalOpen(false);
  309. };
  310. const handleCancel = () => {
  311. setIsModalOpen(false);
  312. };
  313. const onCreate = (values) => {
  314. console.log("Received values of form: ", values);
  315. PluginStore.set("removeAds", values.removeAds);
  316. PluginStore.set("removeTops", values.removeTops);
  317. PluginStore.set("unlockVip", values.unlockVip);
  318. PluginStore.set("unlockBuy", values.unlockBuy);
  319. PluginStore.set("viewBanUser", values.viewBanUser);
  320. PluginStore.set("host", values.host);
  321. };
  322. return /*#__PURE__*/ React__default["default"].createElement(
  323. React__default["default"].Fragment,
  324. null,
  325. /*#__PURE__*/ React__default["default"].createElement(antd.FloatButton, {
  326. type: "primary",
  327. tooltip: "\u6D77\u89D2VIP\u8BBE\u7F6E",
  328. style: {
  329. left: 16,
  330. },
  331. icon: /*#__PURE__*/ React__default["default"].createElement(
  332. icons.SettingOutlined,
  333. null
  334. ),
  335. onClick: showModal,
  336. }),
  337. /*#__PURE__*/ React__default["default"].createElement(
  338. antd.Modal,
  339. {
  340. title: "\u8BBE\u7F6E",
  341. open: isModalOpen,
  342. onOk: handleOk,
  343. onCancel: handleCancel,
  344. destroyOnClose: true,
  345. okButtonProps: {
  346. autoFocus: true,
  347. },
  348. },
  349. /*#__PURE__*/ React__default["default"].createElement(Settings, {
  350. initialSettings: {
  351. removeAds: PluginStore.get("removeAds", true),
  352. removeTops: PluginStore.get("removeTops", true),
  353. viewBanUser: PluginStore.get("viewBanUser", true),
  354. unlockVip: PluginStore.get("unlockVip", true),
  355. unlockBuy: PluginStore.get("unlockBuy", true),
  356. host: PluginStore.get("host", "https://haijiao.ku-cloud.com"),
  357. },
  358. onFormInstanceReady: (instance) => {
  359. setFormInstance(instance);
  360. },
  361. })
  362. )
  363. );
  364. };
  365.  
  366. /**
  367. * 自定义拦截器
  368. */
  369. class Interceptor {
  370. // axios模块
  371.  
  372. constructor(axios) {
  373. this.axios = axios;
  374. }
  375.  
  376. /**
  377. * 初始化请求拦截器
  378. */
  379. initRequestInterceptor() {
  380. this.axios.interceptors.request.use(this.requestInterceptor.bind(this));
  381. }
  382.  
  383. /**
  384. * 初始化返回请求拦截器
  385. */
  386. initResponseInterceptor() {
  387. if (this.axios.interceptors.response.handlers.length != 1) {
  388. return;
  389. }
  390. // 因为返回处理会处理掉config,而我们需要config中的url,所以需要在返回处理之前处理
  391. if (this.axios.interceptors.response.handlers?.[0].fulfilled) {
  392. const origin = this.axios.interceptors.response.handlers?.[0].fulfilled;
  393. this.axios.interceptors.response.handlers[0].fulfilled = async (
  394. response
  395. ) => {
  396. const data = await origin(response);
  397. response = {
  398. data: data,
  399. config: response.config,
  400. };
  401. return response;
  402. };
  403. }
  404. this.axios.interceptors.response.use(
  405. this.responseDecodeInterceptor.bind(this)
  406. );
  407. this.axios.interceptors.response.use(this.responseInterceptor.bind(this));
  408. this.axios.interceptors.response.use(
  409. this.responseEncodeInterceptor.bind(this)
  410. );
  411. }
  412.  
  413. /**
  414. * 请求拦截器
  415. * @param request
  416. * @returns
  417. */
  418. async requestInterceptor(request) {
  419. request = await this.requestUnlockBuyInterceptor(request);
  420. request = await this.requestUnlockBanUserInterceptor(request);
  421. request = await this.requestSearchInterceptor(request);
  422. return request;
  423. }
  424.  
  425. /**
  426. * 解锁购买
  427. * @param request
  428. * @returns
  429. */
  430. async requestUnlockBuyInterceptor(request) {
  431. if (
  432. PluginStore.get("unlockBuy", false) === true &&
  433. PluginStore.get("host", "").length > 0
  434. ) {
  435. if (
  436. /\/api\/attachment/g.test(request.url) ||
  437. /topic\/\d+/g.test(request.url)
  438. ) {
  439. console.log("转发请求", request.url, request);
  440. var host = PluginStore.get("host", "");
  441. request.baseURL = host;
  442. request.crossDomain = true;
  443. }
  444. }
  445. return request;
  446. }
  447.  
  448. /**
  449. * 查看被ban的用户信息
  450. * @param request
  451. */
  452. async requestUnlockBanUserInterceptor(request) {
  453. if (
  454. PluginStore.get("unlockBanUser", true) === true &&
  455. PluginStore.get("host", "").length > 0
  456. ) {
  457. if (
  458. /\/api\/user\/info\/\d+/g.test(request.url) ||
  459. /\/api\/user\/news\/other_news_list/g.test(request.url) ||
  460. (/\/api\/topic\/node\/topics/g.test(request.url) &&
  461. request.url.includes("userId"))
  462. ) {
  463. console.log("查看被ban的用户信息", request.url);
  464. var host = PluginStore.get("host", "");
  465. request.baseURL = host;
  466. request.crossDomain = true;
  467. }
  468. }
  469. return request;
  470. }
  471.  
  472. /**
  473. * 解锁搜索功能
  474. * @param request
  475. */
  476. async requestSearchInterceptor(request) {
  477. if (
  478. PluginStore.get("unlockSearch", true) === true &&
  479. PluginStore.get("host", "").length > 0
  480. ) {
  481. if (/\/api\/user\/search/g.test(request.url)) {
  482. console.log("搜索", request.url);
  483. var host = PluginStore.get("host", "");
  484. request.baseURL = host;
  485. request.crossDomain = true;
  486. }
  487. }
  488. return request;
  489. }
  490.  
  491. /**
  492. * 对返回数据进行解码
  493. * @param response
  494. */
  495. async responseDecodeInterceptor(response) {
  496. if (response.data.status === 200) {
  497. const origin_response = JSON.parse(JSON.stringify(response.data.data));
  498. var enc_data = response.data.data.data;
  499. if (enc_data && typeof enc_data === "string" && enc_data.length > 0) {
  500. const Base64 = getModule("e762").a;
  501. enc_data = JSON.parse(
  502. Base64.decode(Base64.decode(Base64.decode(enc_data)))
  503. );
  504. }
  505. response = {
  506. item: enc_data,
  507. url: response.config.url,
  508. mobile: true,
  509. origin_response: origin_response,
  510. };
  511. } else {
  512. // 克隆一个原始请求
  513. const origin_response = JSON.parse(JSON.stringify(response.data));
  514. const item = JSON.parse(JSON.stringify(response.data.data));
  515. response = {
  516. item: item,
  517. url: response.config.url,
  518. mobile: false,
  519. origin_response: origin_response,
  520. };
  521. }
  522. return response;
  523. }
  524.  
  525. /**
  526. * 对reponse重新编码
  527. * @param response
  528. * @returns
  529. */
  530. async responseEncodeInterceptor(response) {
  531. if (response.mobile === true) {
  532. var dec = response.item;
  533. if (response.origin_response.isEncrypted === true) {
  534. const Base64 = getModule("e762").a;
  535. dec = Base64.encode(
  536. Base64.encode(Base64.encode(JSON.stringify(response.item)))
  537. );
  538. }
  539. return {
  540. data: {
  541. ...response.origin_response,
  542. data: dec,
  543. },
  544. };
  545. } else {
  546. return {
  547. ...response.origin_response,
  548. data: response.item,
  549. };
  550. }
  551. }
  552. async responseInterceptor(response) {
  553. const url = response.url.toLowerCase();
  554. var item = response.item;
  555. console.log("拦截器返回数据", url, item);
  556. if (
  557. /^\/api\/topic\/\d+/g.test(url) &&
  558. PluginStore.get("unlockVip", true) === true
  559. ) {
  560. item = await this.fixTopic(item);
  561. }
  562. // 去广告
  563. else if (
  564. /banner\/banner_list/g.test(url) &&
  565. PluginStore.get("removeAds", true) === true
  566. ) {
  567. item = await this.fixAds(item);
  568. }
  569. // 屏蔽置顶帖
  570. else if (
  571. /^\/api\/topic\/global\/topics/g.test(url) &&
  572. PluginStore.get("removeTops", true) === true
  573. ) {
  574. item = await this.fixTops(item);
  575. }
  576. response.item = item;
  577. return response;
  578. }
  579. /**
  580. * 修复帖子内容
  581. * @param {*} data
  582. * @returns
  583. */
  584. async fixTopic(data) {
  585. console.log("修复帖子内容", data);
  586. if (data.node?.vipLimit > 0) {
  587. data.node.vipLimit = 0;
  588. }
  589. var content = data.content;
  590. if (content && !content.startsWith("<html><head></head><body>")) {
  591. // 删除掉[]标签
  592. content = content.replace(/\[视频\]/g, "");
  593. content = content.replace(/\[图片\]/g, "");
  594. content = content.replace(/此处内容售价\d+金币.*请购买后查看./g, "");
  595. content = content.replace(/\[sell.*\/sell]/g, "");
  596. data.attachments?.forEach((attachment) => {
  597. var hasImage,
  598. hasVideo = false;
  599. // 处理图片
  600. if (attachment.category === "images" && attachment.remoteUrl) {
  601. content =
  602. content += `<p><img src="${attachment.remoteUrl}" data-id="${attachment.id}"/>`;
  603. hasImage = true;
  604. }
  605. if (hasImage === true) {
  606. content = `<p>${content}</p>`;
  607. }
  608. if (attachment.category === "video") {
  609. // if (attachment.remoteUrl) {
  610. hasVideo = true;
  611. content += `<p><video src="${attachment.remoteUrl}" data-id="${attachment.id}"></video></p>`;
  612. // } else {
  613. // console.log("视频链接为空", attachment);
  614. // content += `<p><div style="color:red;text-decoration:line-through;">${attachment.error}</div></p>`;
  615. // }
  616. }
  617. if (hasVideo === true) {
  618. content = `<p>${content}</p>`;
  619. }
  620. });
  621. content = `<html><head></head><body>${content}</body></html>`;
  622. }
  623. data.content = content;
  624. return data;
  625. }
  626.  
  627. /**
  628. * 去广告
  629. * @param data
  630. */
  631. async fixAds(data) {
  632. return null;
  633. }
  634. async fixTops(data) {
  635. return [];
  636. }
  637. }
  638.  
  639. function addStyle() {
  640. let script = document.createElement("link");
  641. script.setAttribute("rel", "stylesheet");
  642. script.setAttribute("type", "text/css");
  643. script.href = "https://cdn.jsdelivr.net/npm/antd@5.16.4/dist/reset.min.css";
  644. document.documentElement.appendChild(script);
  645. }
  646. function addAnalytics() {
  647. const script = document.createElement("script");
  648. script.src = "https://www.googletagmanager.com/gtag/js?id=G-NQ08DH5N3T";
  649. script.async = true;
  650. document.head.appendChild(script);
  651. const script2 = document.createElement("script");
  652. script2.innerHTML = `
  653. window.dataLayer = window.dataLayer || [];
  654. function gtag(){dataLayer.push(arguments);}
  655. gtag('js', new Date());
  656. gtag('config', 'G-NQ08DH5N3T');
  657. `;
  658. document.head.appendChild(script2);
  659. }
  660. function setCookie(name, value) {
  661. document.cookie =
  662. name +
  663. "=" +
  664. value +
  665. ";path=/;expires=" +
  666. new Date(Date.now() + 864e5).toUTCString() +
  667. ";";
  668. }
  669.  
  670. function initialed(vue, store, axios) {
  671. const interceptor = new Interceptor(axios);
  672. interceptor.initRequestInterceptor();
  673. interceptor.initResponseInterceptor();
  674. }
  675. function initSetting() {
  676. const myButton = /*#__PURE__*/ React__default["default"].createElement(
  677. App,
  678. null
  679. );
  680. const pluginDiv = document.createElement("div");
  681. pluginDiv.id = "haijiao-vip-plugin";
  682. document.body.appendChild(pluginDiv);
  683. ReactDOM__default["default"].render(myButton, pluginDiv);
  684. }
  685. sessionStorage.setItem("pageOpen", "1");
  686. addAnalytics();
  687. addStyle();
  688. initSetting();
  689. initHookWebpack(initialed);
  690. setCookie("is_vip", "1");
  691. console.log("haijiao-vip-plugin init success");
  692. })(React, ReactDOM, antd, icons);