boss直聘自动沟通助手

boss直聘辅助沟通助手

// ==UserScript==
// @name       boss直聘自动沟通助手
// @namespace  npm/vite-plugin-monkey
// @version      0.0.2
// @author     monkey
// @icon       https://vitejs.dev/logo.svg
// @match      https://www.zhipin.com/*
// @require    http://code.jquery.com/jquery-2.1.1.min.js
// @require    https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/core-js/3.21.1/minified.min.js
// @require    https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js
// @require    https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.production.min.js
// @grant      GM_xmlhttpRequest
// @grant      window.close
// @description boss直聘辅助沟通助手
// ==/UserScript==

(e=>{const o=document.createElement("style");o.dataset.source="vite-plugin-monkey",o.innerText=e,document.head.appendChild(o)})(" .boss-container{--background-color: orange;--font-color: #fff;position:fixed;left:50px;bottom:150px;display:flex;flex-direction:column;align-items:center;gap:10px;z-index:999}.boss-conversation-container .button{color:var(--font-color);background:var(--background-color);border:none;border-radius:5px;padding:6px 5px}.boss-conversation-setting{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:400px;height:400px;background:#eef;z-index:9999}.boss-conversation-container .multiple-publish{padding:5px;border-radius:5px} ");

var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
(function(require$$0, require$$0$1) {
  "use strict";
  var jsxRuntimeExports = {};
  var jsxRuntime = {
    get exports() {
      return jsxRuntimeExports;
    },
    set exports(v) {
      jsxRuntimeExports = v;
    }
  };
  var reactJsxRuntime_production_min = {};
  /**
   * @license React
   * react-jsx-runtime.production.min.js
   *
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   */
  var f = require$$0, k = Symbol.for("react.element"), l = Symbol.for("react.fragment"), m$1 = Object.prototype.hasOwnProperty, n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, p = { key: true, ref: true, __self: true, __source: true };
  function q(c, a, g) {
    var b, d = {}, e = null, h = null;
    void 0 !== g && (e = "" + g);
    void 0 !== a.key && (e = "" + a.key);
    void 0 !== a.ref && (h = a.ref);
    for (b in a)
      m$1.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
    if (c && c.defaultProps)
      for (b in a = c.defaultProps, a)
        void 0 === d[b] && (d[b] = a[b]);
    return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current };
  }
  reactJsxRuntime_production_min.Fragment = l;
  reactJsxRuntime_production_min.jsx = q;
  reactJsxRuntime_production_min.jsxs = q;
  (function(module) {
    {
      module.exports = reactJsxRuntime_production_min;
    }
  })(jsxRuntime);
  var client = {};
  var m = require$$0$1;
  {
    client.createRoot = m.createRoot;
    client.hydrateRoot = m.hydrateRoot;
  }
  const isNil = (input) => {
    return input == null;
  };
  const composeArrayToRegExp = (list) => {
    const regexStr = list.reduce((prev, cur, index2) => {
      prev += cur;
      if (index2 < list.length - 1) {
        prev += "|";
      }
      return prev;
    }, "");
    return new RegExp(regexStr);
  };
  class CheckPage {
    static isDetail() {
      const conversationBtn = document.querySelector(".btn-container>.btn-startchat");
      return !isNil(conversationBtn) || /job_detail/.test(location.pathname);
    }
    static isList() {
      const reg = composeArrayToRegExp(["geek/recommend", "geek/job"]);
      return reg.test(location.pathname);
    }
  }
  const localStorageConfigKey = "conversation_config";
  const initialConfig = {
    concurrentLimit: 2,
    maxLimit: 30,
    executeInterval: 1e4,
    targetList: ["[Rr]eact"],
    excludeList: ["985", "211", "硕士"],
    automation: false,
    idleMap: {}
  };
  class Config {
    constructor() {
      __publicField(this, "config", {});
      this.syncReadData();
    }
    syncReadData() {
      const dataStr = localStorage.getItem(localStorageConfigKey);
      if (isNil(dataStr)) {
        this.config = initialConfig;
        return;
      }
      try {
        this.config = JSON.parse(dataStr);
      } catch (error) {
        console.error(error);
      }
    }
    syncWriteData() {
      localStorage.setItem(localStorageConfigKey, JSON.stringify(this.config));
    }
  }
  class Polling {
    constructor(polling, ms) {
      __publicField(this, "timer");
      this.polling = polling;
      this.ms = ms;
    }
    togglePolling() {
      this.polling = !this.polling;
    }
    startPolling() {
      this.polling = true;
    }
    stopPolling() {
      this.polling = false;
    }
    get status() {
      return this.polling;
    }
    clear() {
      const fn = window.clearInterval;
      fn(this.timer);
    }
    start(callback) {
      const fn = window.setInterval;
      fn(() => {
        callback();
      }, this.ms);
    }
  }
  class Conversation {
    constructor() {
      __publicField(this, "polling", {});
      __publicField(this, "setting", {});
      __publicField(this, "maxLimit", 0);
      this.init();
    }
    init() {
      this.setting = new Config();
      this.polling = new Polling(this.setting.config.automation, this.setting.config.executeInterval);
      this.maxLimit = this.setting.config.maxLimit;
      this.polling.status && this.start();
    }
    executeIdleQueue() {
      if (this.setting.config.maxLimit <= 0) {
        this.polling.clear();
        this.setting.config.maxLimit = this.maxLimit;
        this.setting.syncWriteData();
        return;
      }
      if (Object.values(this.setting.config.idleMap).filter((v) => !v.status).length === 0) {
        this.polling.status && this.navigateNextPage();
        this.polling.clear();
        return;
      }
      for (let i = 0; i < this.setting.config.concurrentLimit; i++) {
        const url = Object.entries(this.setting.config.idleMap).filter((v) => !v[1].status).pop();
        if (url) {
          this.setting.config.idleMap[url[0]].status = true;
          url && window.open(url[0]);
        }
      }
      this.setting.syncWriteData();
    }
    start() {
      this.polling.start(() => {
        this.setting.syncReadData();
        this.addJobTask();
        this.executeIdleQueue();
      });
    }
    addJobTask() {
      const list = document.querySelectorAll(".job-list-box>li a.job-card-left");
      list.forEach((el) => {
        if (isNil(this.setting.config.idleMap[el.href])) {
          this.setting.config.idleMap[el.href] = {
            status: false
          };
        }
      });
    }
    navigateNextPage() {
      var _a;
      let search = "";
      const pageReg = /(page)=([0-9])+/;
      const hasSearchParam = location.search.length !== 0;
      const pageNo = parseInt(((_a = pageReg.exec(location.search)) == null ? void 0 : _a[2]) || "1") + 1;
      if (/page=[0-9]+/.test(location.search)) {
        search = location.search.replace(pageReg, `$1=${pageNo}`);
      } else {
        search = location.search + `${hasSearchParam ? "&" : "?"}page=${pageNo}`;
      }
      const newUrl = `${location.origin}${location.pathname}${search}`;
      window.location.href = newUrl;
    }
  }
  function MultiplePublishButton() {
    const publish = () => {
      new Conversation().start();
    };
    return /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "button", onClick: publish, children: "一键投递" });
  }
  function Automation() {
    var _a;
    const conversation = require$$0.useRef();
    require$$0.useEffect(() => {
      conversation.current = new Conversation();
    }, []);
    const togglePolling = () => {
      if (conversation.current) {
        conversation.current.polling.togglePolling();
        conversation.current.setting.config.automation = conversation.current.polling.status;
        conversation.current.setting.syncWriteData();
        conversation.current.polling.status && conversation.current.start();
      }
    };
    return /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "button", onClick: togglePolling, children: ((_a = conversation.current) == null ? void 0 : _a.polling.status) ? "关闭自动轮询" : "开启自动轮询" });
  }
  const useBoolean = (initialValue = false) => {
    const [visible, setVisible] = require$$0.useState(initialValue);
    const action = {
      setFalse() {
        setVisible(false);
      },
      setTrue() {
        setVisible(true);
      },
      toggle() {
        setVisible(!visible);
      },
      set(value) {
        setVisible(value);
      }
    };
    return [visible, action];
  };
  function TextRegion(props) {
    const { type } = props;
    const [value, setValue] = require$$0.useState();
    const save = (e) => {
      const config = new Config();
      const values = e.currentTarget.value.split(",");
      if (type === "target") {
        config.config.targetList = values;
      } else if (type === "exclude") {
        config.config.excludeList = values;
      }
    };
    const input = (e) => {
      setValue(e.target.value);
    };
    return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
      /* @__PURE__ */ jsxRuntimeExports.jsxs("h5", { children: [
        type,
        ":"
      ] }),
      /* @__PURE__ */ jsxRuntimeExports.jsx("textarea", { onInput: input, value }),
      /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: save, children: "保存" })
    ] });
  }
  function Setting() {
    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "boss-conversation-setting", children: [
      "设置",
      /* @__PURE__ */ jsxRuntimeExports.jsx(TextRegion, { type: "target" }),
      /* @__PURE__ */ jsxRuntimeExports.jsx(TextRegion, { type: "exclude" })
    ] });
  }
  function SettingButton() {
    const [visible, { toggle }] = useBoolean();
    return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
      /* @__PURE__ */ jsxRuntimeExports.jsx(
        "button",
        {
          className: "button",
          onClick: () => {
            toggle();
          },
          children: "设置"
        }
      ),
      visible && require$$0$1.createPortal(/* @__PURE__ */ jsxRuntimeExports.jsx(Setting, {}), document.body)
    ] });
  }
  function ConversationContainer() {
    if (!CheckPage.isList()) {
      return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, {});
    }
    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "boss-container boss-conversation-container", children: [
      /* @__PURE__ */ jsxRuntimeExports.jsx(MultiplePublishButton, {}),
      /* @__PURE__ */ jsxRuntimeExports.jsx(Automation, {}),
      /* @__PURE__ */ jsxRuntimeExports.jsx(SettingButton, {})
    ] });
  }
  function App() {
    return /* @__PURE__ */ jsxRuntimeExports.jsx(ConversationContainer, {});
  }
  const index = "";
  client.createRoot(
    (() => {
      const app = document.createElement("div");
      document.body.append(app);
      return app;
    })()
  ).render(
    /* @__PURE__ */ jsxRuntimeExports.jsx(require$$0.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(App, {}) })
  );
  class Publish {
    constructor() {
      __publicField(this, "mountedTimer");
      __publicField(this, "setting", {});
      this.start();
      this.setting = new Config();
    }
    checkCondition() {
      const target = document.querySelector(".job-sec-text");
      if (isNil(target)) {
        return false;
      }
      const result = composeArrayToRegExp(this.setting.config.targetList).test(target.textContent);
      const exclude = composeArrayToRegExp(this.setting.config.excludeList).test(target.textContent);
      return result && !exclude;
    }
    publish() {
      if (this.setting.config.maxLimit <= 0) {
        return;
      }
      const link = document.querySelector(".btn-container>.btn-startchat");
      if (link) {
        link.click();
        this.setting.config.automation && this.setting.config.maxLimit--;
        this.setting.syncWriteData();
      } else {
        console.log("没有找到沟通按钮");
        window.close();
      }
    }
    start() {
      this.mountedTimer = window.setInterval(() => {
        if (!this.checkCondition()) {
          window.close();
          clearInterval(this.mountedTimer);
          return;
        }
        const conversationBtn = document.querySelector(".btn-container>.btn-startchat");
        const leftTitleEl = document.querySelector(".left-title");
        const dialogEl = document.querySelector(".dialog-container");
        if (!CheckPage.isDetail()) {
          clearInterval(this.mountedTimer);
          window.close();
          return;
        }
        this.publish();
        if (dialogEl || leftTitleEl || conversationBtn.textContent === "继续沟通") {
          clearInterval(this.mountedTimer);
          window.close();
        }
      }, 3e3);
    }
  }
  CheckPage.isDetail() && new Publish().start();
})(React, ReactDOM);