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

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

À partir de 2024-04-29. Voir la dernière version.

  1. // ==UserScript==
  2. // @name haijiao-vip: 海角社区 解锁收费视频,VIP,去广告
  3. // @namespace https://github.com/sex4096/haijiao_vip
  4. // @version 0.0.14
  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.haijiao.com/*
  11. // @match https://haijiao.com/*
  12. // @license MIT
  13. // @connect cdn.jsdelivr.net
  14. // @require https://cdn.jsdelivr.net/npm/react@18.3.0/umd/react.production.min.js
  15. // @require https://cdn.jsdelivr.net/npm/react-dom@18.3.0/umd/react-dom.production.min.js
  16. // @require https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js
  17. // @require https://cdn.jsdelivr.net/npm/antd@5.16.4/dist/antd.min.js
  18. // @require https://cdn.jsdelivr.net/npm/@ant-design/icons@5.3.6/dist/index.umd.min.js
  19. // ==/UserScript==
  20. (function (React$1, ReactDOM, antd, icons) {
  21. 'use strict';
  22.  
  23. function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
  24.  
  25. var React__default = /*#__PURE__*/_interopDefaultLegacy(React$1);
  26. var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
  27.  
  28. var __webpack_require__ = undefined;
  29. var VUE = undefined;
  30. var STORE = undefined;
  31. var AXIOS = undefined;
  32. var callback = undefined;
  33. function initHookWebpack(initialed) {
  34. callback = initialed;
  35. let originCall = Function.prototype.call;
  36. Function.prototype.call = function () {
  37. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  38. args[_key] = arguments[_key];
  39. }
  40. const result = originCall.apply(this, args);
  41. if (args.length === 4 && args[1] && args[1].exports && args[0] === args[2] && __webpack_require__ === undefined) {
  42. __webpack_require__ = args[3];
  43. Function.prototype.call = originCall;
  44. importModules();
  45. }
  46. return result;
  47. };
  48. }
  49. function importModules() {
  50. VUE = __webpack_require__("2b0e").default;
  51. STORE = __webpack_require__("4360").a;
  52. AXIOS = __webpack_require__("bc3a");
  53. AXIOS = getObject(AXIOS);
  54. AXIOS = AXIOS.a;
  55. callback(VUE, STORE, AXIOS);
  56. }
  57. function getObject(module) {
  58. const t = module && module.__esModule ? function () {
  59. return module.default;
  60. } : function () {
  61. return module;
  62. };
  63. defineObject(t, "a", t);
  64. return t;
  65. }
  66. function defineObject(module, key, value) {
  67. Object.prototype.hasOwnProperty.call(module, key) || Object.defineProperty(module, key, {
  68. enumerable: true,
  69. get: value
  70. });
  71. }
  72.  
  73. /**
  74. * 加载模块
  75. * @param module
  76. */
  77. function getModule(module) {
  78. if (!__webpack_require__) return null;
  79. return __webpack_require__(module);
  80. }
  81.  
  82. const Settings = _ref => {
  83. let {
  84. initialSettings,
  85. onFormInstanceReady
  86. } = _ref;
  87. const [showSetHost, setShowSetHost] = React$1.useState(false);
  88. const [form] = antd.Form.useForm();
  89. React$1.useEffect(() => {
  90. onFormInstanceReady(form);
  91. setShowSetHost(initialSettings.unlockBuy || false);
  92. }, []);
  93. return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(antd.Form, {
  94. form: form,
  95. name: "settings",
  96. labelAlign: "right",
  97. labelCol: {
  98. span: 8
  99. },
  100. wrapperCol: {
  101. span: 16
  102. },
  103. initialValues: initialSettings
  104. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  105. label: "\u53BB\u5E7F\u544A"
  106. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  107. name: "removeAds",
  108. noStyle: true
  109. }, /*#__PURE__*/React.createElement(antd.Switch, null)), /*#__PURE__*/React.createElement("span", {
  110. style: {
  111. marginLeft: 10
  112. }
  113. }, "\u53BB\u9664\u7F51\u7AD9\u5E7F\u544A")), /*#__PURE__*/React.createElement(antd.Form.Item, {
  114. label: "\u5C4F\u853D\u7F6E\u9876"
  115. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  116. name: "removeTops",
  117. noStyle: true
  118. }, /*#__PURE__*/React.createElement(antd.Switch, null)), /*#__PURE__*/React.createElement("span", {
  119. style: {
  120. marginLeft: 10
  121. }
  122. }, "\u5C4F\u853D\u5168\u5C40\u7F6E\u9876\u5E16")), /*#__PURE__*/React.createElement(antd.Form.Item, {
  123. label: "\u89E3\u9501VIP"
  124. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  125. name: "unlockVip",
  126. noStyle: true
  127. }, /*#__PURE__*/React.createElement(antd.Switch, null)), /*#__PURE__*/React.createElement("span", {
  128. style: {
  129. marginLeft: 10
  130. }
  131. }, "\u53EF\u89C2\u770Bvip\u533A\u7684\u5E16\u5B50\u53CA\u89C6\u9891")), /*#__PURE__*/React.createElement(antd.Form.Item, {
  132. label: "\u89E3\u9501\u6536\u8D39\u89C6\u9891"
  133. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  134. name: "unlockBuy",
  135. noStyle: true
  136. }, /*#__PURE__*/React.createElement(antd.Switch, {
  137. onChange: setShowSetHost
  138. })), /*#__PURE__*/React.createElement("span", {
  139. style: {
  140. marginLeft: 10
  141. }
  142. }, "\u53EF\u89C2\u770B\u9700\u8981\u8D2D\u4E70\u7684\u89C6\u9891")), showSetHost && /*#__PURE__*/React.createElement(antd.Form.Item, {
  143. label: "\u89E3\u9501\u89C6\u9891\u5730\u5740"
  144. }, /*#__PURE__*/React.createElement(antd.Form.Item, {
  145. name: "unlockBuyHost",
  146. noStyle: true,
  147. rules: [{
  148. required: true,
  149. message: "请输入服务器地址"
  150. }]
  151. }, /*#__PURE__*/React.createElement(antd.Input, null)), /*#__PURE__*/React.createElement("div", {
  152. style: {
  153. color: "red",
  154. marginTop: 5
  155. }
  156. }, "\u670D\u52A1\u5668\u5730\u5740\u4E0D\u5B9A\u65F6\u66F4\u6362"), /*#__PURE__*/React.createElement("div", null, "\u8BF7\u8BA2\u9605TG\u9891\u9053:@svip_nav\u83B7\u53D6\u6700\u65B0\u5730\u5740"))));
  157. };
  158.  
  159. class PluginStore {
  160. static get(key, defaultValue) {
  161. const value = localStorage.getItem(key);
  162. if (value === null) {
  163. return defaultValue;
  164. }
  165. if (typeof defaultValue === "number") {
  166. return parseInt(value);
  167. }
  168. if (typeof defaultValue === "boolean") {
  169. return value === "true";
  170. }
  171. return value;
  172. }
  173. static set(key, value) {
  174. localStorage.setItem(key, value.toString());
  175. }
  176. }
  177.  
  178. const App = () => {
  179. const [isModalOpen, setIsModalOpen] = React$1.useState(false);
  180. const [formInstance, setFormInstance] = React$1.useState();
  181. const showModal = () => {
  182. setIsModalOpen(true);
  183. };
  184. const handleOk = async () => {
  185. const values = await formInstance?.validateFields();
  186. onCreate(values);
  187. setIsModalOpen(false);
  188. };
  189. const handleCancel = () => {
  190. setIsModalOpen(false);
  191. };
  192. const onCreate = values => {
  193. console.log("Received values of form: ", values);
  194. PluginStore.set("removeAds", values.removeAds);
  195. PluginStore.set("removeTops", values.removeTops);
  196. PluginStore.set("unlockVip", values.unlockVip);
  197. PluginStore.set("unlockBuy", values.unlockBuy);
  198. if (values.hasOwnProperty("unlockBuyHost")) {
  199. PluginStore.set("unlockBuyHost", values.unlockBuyHost);
  200. }
  201. };
  202. return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(antd.FloatButton, {
  203. type: "primary",
  204. tooltip: "\u6D77\u89D2VIP\u8BBE\u7F6E",
  205. style: {
  206. right: 16
  207. },
  208. icon: /*#__PURE__*/React__default["default"].createElement(icons.SettingOutlined, null),
  209. onClick: showModal
  210. }), /*#__PURE__*/React__default["default"].createElement(antd.Modal, {
  211. title: "\u8BBE\u7F6E",
  212. open: isModalOpen,
  213. onOk: handleOk,
  214. onCancel: handleCancel,
  215. destroyOnClose: true,
  216. okButtonProps: {
  217. autoFocus: true
  218. }
  219. }, /*#__PURE__*/React__default["default"].createElement(Settings, {
  220. initialSettings: {
  221. removeAds: PluginStore.get("removeAds", true),
  222. removeTops: PluginStore.get("removeTops", true),
  223. unlockVip: PluginStore.get("unlockVip", true),
  224. unlockBuy: PluginStore.get("unlockBuy", false),
  225. unlockBuyHost: PluginStore.get("unlockBuyHost", "https://haijiao.ku-cloud.com")
  226. },
  227. onFormInstanceReady: instance => {
  228. setFormInstance(instance);
  229. }
  230. })));
  231. };
  232.  
  233. /**
  234. * 自定义拦截器
  235. */
  236. class Interceptor {
  237. // axios模块
  238.  
  239. constructor(axios) {
  240. this.axios = axios;
  241. }
  242.  
  243. /**
  244. * 初始化请求拦截器
  245. */
  246. initRequestInterceptor() {
  247. this.axios.interceptors.request.use(this.requestInterceptor.bind(this));
  248. }
  249.  
  250. /**
  251. * 初始化返回请求拦截器
  252. */
  253. initResponseInterceptor() {
  254. if (this.axios.interceptors.response.handlers.length != 1) {
  255. return;
  256. }
  257. // 因为返回处理会处理掉config,而我们需要config中的url,所以需要在返回处理之前处理
  258. if (this.axios.interceptors.response.handlers?.[0].fulfilled) {
  259. const origin = this.axios.interceptors.response.handlers?.[0].fulfilled;
  260. this.axios.interceptors.response.handlers[0].fulfilled = async response => {
  261. const data = await origin(response);
  262. response = {
  263. data: data,
  264. config: response.config
  265. };
  266. return response;
  267. };
  268. }
  269. this.axios.interceptors.response.use(this.responseDecodeInterceptor.bind(this));
  270. this.axios.interceptors.response.use(this.responseInterceptor.bind(this));
  271. this.axios.interceptors.response.use(this.responseEncodeInterceptor.bind(this));
  272. }
  273.  
  274. /**
  275. * 请求拦截器
  276. * @param request
  277. * @returns
  278. */
  279. async requestInterceptor(request) {
  280. if (PluginStore.get("unlockBuy", false) === true && PluginStore.get("unlockBuyHost", "").length > 0) {
  281. if (/\/api\/attachment/g.test(request.url) || /topic\/\d+/g.test(request.url)) {
  282. console.log("转发请求", request.url, request);
  283. var host = PluginStore.get("unlockBuyHost", "");
  284. request.baseURL = host;
  285. request.crossDomain = true;
  286. }
  287. }
  288. return request;
  289. }
  290.  
  291. /**
  292. * 对返回数据进行解码
  293. * @param response
  294. */
  295. async responseDecodeInterceptor(response) {
  296. if (response.data.status === 200) {
  297. const origin_response = JSON.parse(JSON.stringify(response.data.data));
  298. var enc_data = response.data.data.data;
  299. if (enc_data && typeof enc_data === "string" && enc_data.length > 0) {
  300. const Base64 = getModule("e762").a;
  301. enc_data = JSON.parse(Base64.decode(Base64.decode(Base64.decode(enc_data))));
  302. }
  303. response = {
  304. item: enc_data,
  305. url: response.config.url,
  306. mobile: true,
  307. origin_response: origin_response
  308. };
  309. } else {
  310. // 克隆一个原始请求
  311. const origin_response = JSON.parse(JSON.stringify(response.data));
  312. const item = JSON.parse(JSON.stringify(response.data.data));
  313. response = {
  314. item: item,
  315. url: response.config.url,
  316. mobile: false,
  317. origin_response: origin_response
  318. };
  319. }
  320. return response;
  321. }
  322.  
  323. /**
  324. * 对reponse重新编码
  325. * @param response
  326. * @returns
  327. */
  328. async responseEncodeInterceptor(response) {
  329. if (response.mobile === true) {
  330. var dec = response.item;
  331. if (response.origin_response.isEncrypted === true) {
  332. const Base64 = getModule("e762").a;
  333. dec = Base64.encode(Base64.encode(Base64.encode(JSON.stringify(response.item))));
  334. }
  335. return {
  336. data: {
  337. ...response.origin_response,
  338. data: dec
  339. }
  340. };
  341. } else {
  342. return {
  343. ...response.origin_response,
  344. data: response.item
  345. };
  346. }
  347. }
  348. async responseInterceptor(response) {
  349. const url = response.url.toLowerCase();
  350. var item = response.item;
  351. console.log("拦截器返回数据", url, item);
  352. if (/^\/api\/topic\/\d+/g.test(url) && PluginStore.get("unlockVip", true) === true) {
  353. item = await this.fixTopic(item);
  354. }
  355. // 去广告
  356. else if (/banner\/banner_list/g.test(url) && PluginStore.get("removeAds", true) === true) {
  357. item = await this.fixAds(item);
  358. }
  359. // 屏蔽置顶帖
  360. else if (/^\/api\/topic\/global\/topics/g.test(url) && PluginStore.get("removeTops", true) === true) {
  361. item = await this.fixTops(item);
  362. }
  363. response.item = item;
  364. return response;
  365. }
  366. /**
  367. * 修复帖子内容
  368. * @param {*} data
  369. * @returns
  370. */
  371. async fixTopic(data) {
  372. console.log("修复帖子内容", data);
  373. if (data.node?.vipLimit > 0) {
  374. data.node.vipLimit = 0;
  375. }
  376. var content = data.content;
  377. if (content && !content.startsWith("<html><head></head><body>")) {
  378. // 删除掉[]标签
  379. content = content.replace(/\[视频\]/g, "");
  380. content = content.replace(/\[图片\]/g, "");
  381. content = content.replace(/此处内容售价\d+金币.*请购买后查看./g, "");
  382. content = content.replace(/\[sell.*\/sell]/g, "");
  383. data.attachments?.forEach(attachment => {
  384. var hasImage,
  385. hasVideo = false;
  386. // 处理图片
  387. if (attachment.category === "images" && attachment.remoteUrl) {
  388. content = content += `<p><img src="${attachment.remoteUrl}" data-id="${attachment.id}"/>`;
  389. hasImage = true;
  390. }
  391. if (hasImage === true) {
  392. content = `<p>${content}</p>`;
  393. }
  394. if (attachment.category === "video") {
  395. // if (attachment.remoteUrl) {
  396. hasVideo = true;
  397. content += `<p><video src="${attachment.remoteUrl}" data-id="${attachment.id}"></video></p>`;
  398. // } else {
  399. // console.log("视频链接为空", attachment);
  400. // content += `<p><div style="color:red;text-decoration:line-through;">${attachment.error}</div></p>`;
  401. // }
  402. }
  403. if (hasVideo === true) {
  404. content = `<p>${content}</p>`;
  405. }
  406. });
  407. content = `<html><head></head><body>${content}</body></html>`;
  408. }
  409. data.content = content;
  410. return data;
  411. }
  412.  
  413. /**
  414. * 去广告
  415. * @param data
  416. */
  417. async fixAds(data) {
  418. return null;
  419. }
  420. async fixTops(data) {
  421. return [];
  422. }
  423. }
  424.  
  425. function addStyle() {
  426. let script = document.createElement("link");
  427. script.setAttribute("rel", "stylesheet");
  428. script.setAttribute("type", "text/css");
  429. script.href = "https://cdn.jsdelivr.net/npm/antd@5.16.4/dist/reset.min.css";
  430. document.documentElement.appendChild(script);
  431. }
  432. function addAnalytics() {
  433. const script = document.createElement("script");
  434. script.src = "https://www.googletagmanager.com/gtag/js?id=G-NQ08DH5N3T";
  435. script.async = true;
  436. document.head.appendChild(script);
  437. const script2 = document.createElement("script");
  438. script2.innerHTML = `
  439. window.dataLayer = window.dataLayer || [];
  440. function gtag(){dataLayer.push(arguments);}
  441. gtag('js', new Date());
  442. gtag('config', 'G-NQ08DH5N3T');
  443. `;
  444. document.head.appendChild(script2);
  445. }
  446.  
  447. function initialed(vue, store, axios) {
  448. const interceptor = new Interceptor(axios);
  449. interceptor.initRequestInterceptor();
  450. interceptor.initResponseInterceptor();
  451. }
  452. function initSetting() {
  453. const myButton = /*#__PURE__*/React__default["default"].createElement(App, null);
  454. const pluginDiv = document.createElement("div");
  455. pluginDiv.id = "haijiao-vip-plugin";
  456. document.body.appendChild(pluginDiv);
  457. ReactDOM__default["default"].render(myButton, pluginDiv);
  458. }
  459. sessionStorage.setItem("pageOpen", "1");
  460. addAnalytics();
  461. addStyle();
  462. initSetting();
  463. initHookWebpack(initialed);
  464.  
  465. })(React, ReactDOM, antd, icons);