bilibili自动发送直播弹幕

bilibili自动发送直播弹幕, 刷屏和互动专用, 右上角插件菜单中启动

目前為 2024-08-06 提交的版本,檢視 最新版本

// ==UserScript==
// @name          bilibili自动发送直播弹幕
// @namespace     https://greasyfork.org/zh-CN/users/1196880-ling2ling4
// @version       1.1.2
// @author        Ling2Ling4
// @description   bilibili自动发送直播弹幕, 刷屏和互动专用, 右上角插件菜单中启动
// @license       AGPL-3.0-or-later
// @icon data:image/png;base64,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
// @match         *://live.bilibili.com/*
// @run-at        document-end
// @grant         GM_registerMenuCommand
// @grant         GM_notification
// @noframes
// @compatible    chrome
// @compatible    edge
// @compatible    firefox
// ==/UserScript==

(() => {
  "use strict";
  function getListAndWeight(infoItems, weightArr = null) {
    const newWeightArr = [],
      newList = [];
    let flag = !1;
    for (let i = 0; i < infoItems.length; i++) {
      const item = infoItems[i];
      let num = weightArr && weightArr.length > 0 ? weightArr[i] : 1;
      if (
        item &&
        (item.includes("--") || item.includes("=>") || item.includes("=tmp>"))
      ) {
        let arr;
        (arr = item.split("=tmp>")),
          1 === arr.length &&
            ((arr = item.split("=>")),
            1 === arr.length && (arr = item.split("--")));
        let curNum = +arr[1];
        if ((!curNum && 0 !== curNum) || "" === arr[1]) {
          newWeightArr.push(num), newList.push(item);
          continue;
        }
        (flag = !0), newWeightArr.push(curNum), newList.push(arr[0]);
      } else newWeightArr.push(num), newList.push(item);
    }
    return flag
      ? { tagList: newList, weightArr: newWeightArr }
      : { tagList: newList, weightArr: new Array(newList.length).fill(1) };
  }
  function getRandomWeight(weightArr) {
    const num = (function getRandom(min, max) {
      return Math.floor(Math.random() * (max - min + 1) + min);
    })(
      1,
      weightArr.reduce((a, b) => parseInt(a) + parseInt(b), 0)
    );
    let curSum = 0;
    for (let i = 0; i < weightArr.length; i++) {
      if (((curSum += parseInt(weightArr[i])), num <= curSum)) return i;
    }
    return 0;
  }
  const info = {
    data: {
      text: "",
      time: 5,
      stopTime: 30,
      baseText: "",
      baseTime: 5,
      baseStopTime: 30,
      curIndex: 0,
      startTime: 0,
    },
    keys: {
      text: "auto-send-dm_text",
      time: "auto-send-dm_time",
      stopTime: "auto-send-dm_stopTime",
    },
    txt: {
      textMin: "请设置自动发送的直播弹幕的内容\n默认: base\n当前: current",
      text: "请设置自动发送的直播弹幕的内容\n当前: current\n————也可书写多条弹幕\n    1.若每条弹幕用 ;; 分隔(两个分号,中英文字符都可以), 则每次将随机发送其中的一条弹幕\n    2.若每条弹幕用 == 分隔, 则依次发送每条弹幕\n    可在每条弹幕的后面通过 --N 的格式添加权重, 权重越高则越容易发送该条弹幕或重复次数越多(默认权重为1), 权重之和可以超过100.\n————如:\n    666;;赞--9;;好耶--90  表示发送666的概率为1%, 赞的概率9%, 好耶的概率90%\n    666==赞--10  表示发送1次666后发送10次赞",
      time: "请设置弹幕发送的间隔时间 (单位秒)\n默认: base\n当前: current",
      stopTime:
        "请设置运行时长(分钟), 0表示运行后不自动停止\n默认: base\n当前: current",
    },
    classList: {
      textarea: "#control-panel-ctnr-box .chat-input-ctnr textarea",
      btn: "#control-panel-ctnr-box .bottom-actions button",
    },
    doms: {},
    timerId: null,
  };
  function getDoms() {
    (info.doms.textarea = document.querySelector(info.classList.textarea)),
      (info.doms.btn = document.querySelector(info.classList.btn)),
      console.log(info.doms.textarea, info.doms.btn);
  }
  function getSendText(str) {
    const newStr = str.replaceAll(";", ";");
    let arr = [],
      text = str;
    if (newStr.includes(";;"))
      (arr = newStr.split(";;")),
        (text = (function getRandomWeightItem(infoItems) {
          const result = getListAndWeight(infoItems);
          return result.tagList[getRandomWeight(result.weightArr)];
        })(arr));
    else {
      arr = newStr.split("==");
      const obj = (function getOrderItem(infoItems, index = 0) {
        const result = getListAndWeight(infoItems),
          oldArr = result.tagList,
          sizeArr = result.weightArr,
          len = sizeArr.reduce((a, b) => parseInt(a) + parseInt(b), 0),
          curP = index + 1;
        let curRange = 0;
        for (let i = 0; i < oldArr.length; i++)
          if (((curRange += parseInt(sizeArr[i])), curP <= curRange))
            return { value: oldArr[i], length: len };
        return { value: oldArr[0], length: len };
      })(arr, info.data.curIndex);
      (info.data.curIndex = (info.data.curIndex + 1) % obj.length),
        (text = obj.value);
    }
    return text;
  }
  function start() {
    stop();
    let errorTimer = null;
    const sendDm = () => {
      if (
        !(
          (info.doms.textarea && info.doms.btn) ||
          (getDoms(), info.doms.textarea && info.doms.btn)
        )
      )
        return (
          info.doms.textarea || console.log("文本框的dom获取失败"),
          info.doms.btn || console.log("发送按钮的dom获取失败"),
          info.timerId && clearInterval(info.timerId),
          errorTimer && clearTimeout(errorTimer),
          (errorTimer = setTimeout(() => {
            getDoms(),
              info.doms.textarea && info.doms.btn
                ? (info.timerId = setInterval(sendDm, 1e3 * info.data.time))
                : (info.timerId && clearInterval(info.timerId),
                  GM_notification({
                    title: "脚本启动失败",
                    text: "脚本启动失败, 请前往 https://greasyfork.org/zh-CN/users/1196880-ling2ling4 进行反馈",
                    timeout: 1e4,
                  }));
          }, 5e3)),
          -1
        );
      const sendText = getSendText(info.data.text);
      if (
        ((info.doms.textarea.value = sendText),
        (function emitEvent(ele, eventType) {
          try {
            if (ele.dispatchEvent) {
              var evt = new Event(eventType, { bubbles: !1, cancelable: !1 });
              ele.dispatchEvent(evt);
            } else ele.fireEvent && ele.fireEvent("on" + eventType);
          } catch (e) {}
        })(info.doms.textarea, "input"),
        info.doms.btn.click(),
        console.log("发送弹幕:", sendText),
        0 !== info.data.stopTime)
      ) {
        if (
          (new Date().getTime() - info.data.startTime) / 1e3 / 60 >
          info.data.stopTime
        )
          return (
            clearInterval(info.timerId),
            GM_notification({
              text: `结束运行, 已运行${info.data.stopTime}分钟`,
              timeout: 4500,
            }),
            console.log(`结束运行, 已运行${info.data.stopTime}分钟`),
            -1
          );
      }
    };
    GM_notification({ text: "开始发送弹幕", timeout: 3500 }),
      (info.data.startTime = new Date().getTime());
    -1 !== sendDm() &&
      (info.timerId = setInterval(sendDm, 1e3 * info.data.time));
  }
  function stop(isLog = !1) {
    isLog && GM_notification({ text: "已停止发送弹幕", timeout: 3500 }),
      info.timerId && clearInterval(info.timerId),
      (info.data.curIndex = 0);
  }
  function setTextItem({
    txt,
    base,
    key,
    isChangeTxt = !0,
    verification = null,
  } = {}) {
    let val = localStorage.getItem(key);
    null == val &&
      ((val = base),
      "string" != typeof base && (base = JSON.stringify(base)),
      localStorage.setItem(key, base)),
      isChangeTxt && (txt = txt.replace("base", base).replace("current", val));
    let newVal = prompt(txt, val);
    return (
      null !== newVal &&
      ("function" != typeof verification ||
        ((newVal = verification(newVal, val)), null !== newVal)) &&
      newVal !== val &&
      ("string" != typeof newVal && (newVal = JSON.stringify(newVal)),
      localStorage.setItem(key, newVal),
      !0)
    );
  }
  !(function main() {
    !(function getData() {
      (info.data.text =
        localStorage.getItem(info.keys.text) || info.data.baseText),
        (info.data.time =
          +localStorage.getItem(info.keys.time) || info.data.baseTime),
        (info.data.stopTime = +localStorage.getItem(info.keys.stopTime)),
        0 === info.data.stopTime ||
          info.data.stopTime ||
          (info.data.stopTime = info.data.baseStopTime);
    })(),
      getDoms(),
      (function setMenu() {
        GM_registerMenuCommand("开始自动发送弹幕", () => {
          info.data.text
            ? start()
            : (setTextItem({
                txt: info.txt.textMin,
                base: info.data.baseText,
                key: info.keys.text,
                verification: (newVal, val) =>
                  newVal
                    ? ((info.data.text = newVal), newVal)
                    : ((info.data.text = val), val),
              }),
              info.data.text ? start() : stop());
        }),
          GM_registerMenuCommand("停止", () => {
            stop(!0);
          }),
          GM_registerMenuCommand("设置", () => {
            setTextItem({
              txt: info.txt.text,
              base: info.data.baseText,
              key: info.keys.text,
              verification: (newVal, val) =>
                newVal
                  ? ((info.data.text = newVal), newVal)
                  : ((info.data.text = val), val),
            }),
              setTextItem({
                txt: info.txt.time,
                base: info.data.baseTime,
                key: info.keys.time,
                verification: (newVal, val) =>
                  +newVal
                    ? ((info.data.time = +newVal), newVal)
                    : ((info.data.time = +val), val),
              }),
              setTextItem({
                txt: info.txt.stopTime,
                base: info.data.baseStopTime,
                key: info.keys.stopTime,
                verification: (newVal, val) =>
                  0 == +newVal
                    ? ((info.data.stopTime = +newVal), newVal)
                    : +newVal < 0 || !newVal
                    ? ((info.data.stopTime = +val), val)
                    : ((info.data.stopTime = +newVal), newVal),
              });
          });
      })();
  })();
})();