// ==UserScript==
// @name bilibili自动发送直播弹幕
// @namespace https://greasyfork.org/zh-CN/users/1196880-ling2ling4
// @version 1.2.4
// @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_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_notification
// @noframes
// @compatible chrome
// @compatible edge
// @compatible firefox
// ==/UserScript==
(() => {
"use strict";
const keyBase = "ll_auto_send_dm_",
info = {
keyBase,
settingsArea: null,
timerId: null,
replyTimer: null,
errorTimer: null,
isStarted: !1,
isStartedReply: !1,
data: {
curIndex: 0,
startTime: 0,
replyStartTime: 0,
testDm: "test",
testText: "-",
errorTt: "脚本启动失败",
errorTxt:
"脚本启动失败, 请在 B站:零泠丶 或 https://greasyfork.org/zh-CN/users/1196880-ling2ling4 进行反馈",
},
curClassList: null,
classList: {
v_20240823: {
textarea: "#control-panel-ctnr-box textarea",
btn: "#control-panel-ctnr-box button.bl-button",
dm: ".danmaku-item .danmaku-item-right",
myDm: '.danmaku-item[data-uname="零泠丶"] .danmaku-item-right',
dmDataset: { dm: "danmaku", name: "uname" },
},
v_20240823Before: {
textarea: "#control-panel-ctnr-box .chat-input-ctnr textarea",
btn: "#control-panel-ctnr-box .bottom-actions button",
dm: ".danmaku-item .danmaku-item-right",
myDm: '.danmaku-item[data-uname="零泠丶"] .danmaku-item-right',
dmDataset: { dm: "danmaku", name: "uname" },
},
},
dmDataset: { dm: "danmaku", name: "uname" },
doms: {},
settings: {
dmText: {
value: "零泠丶",
base: "零泠丶",
key: keyBase + "dmText",
title: "弹幕内容",
desc: "自动发送的直播弹幕的内容, 也可书写多条弹幕, 多条弹幕的书写规则如下\n【规则】\n1.若每条弹幕用 ;; 分隔(两个分号,中英文字符都可以), 则每次将随机发送其中的一条弹幕\n2.若每条弹幕用 == 分隔, 则依次循环发送每条弹幕\n3.可在每条弹幕的后面通过 --N 的格式添加权重, 权重越高则越容易发送该条弹幕或重复次数越多(默认权重为1), 权重之和可以超过100.\n【示例】\n666;;赞--9;;好耶--90 表示发送666的概率为1%, 赞的概率9%, 好耶的概率90%\n666==赞--10 表示发送1次666后发送10次赞",
type: "弹幕设置",
valType: "string",
compType: "textarea",
compH: "80px",
},
intervalTime: {
value: 5,
base: 5,
key: keyBase + "intervalTime",
desc: "弹幕发送的间隔时间(秒)",
type: "基础设置",
valType: "number",
compType: "textarea",
compH: "30px",
},
runTime: {
value: 0,
base: 0,
key: keyBase + "runTime",
desc: "自动弹幕功能的单次运行时长(分钟), 0表示运行后不自动停止",
type: "基础设置",
valType: "number",
compType: "textarea",
compH: "30px",
},
replyRunTime: {
value: 0,
base: 0,
key: keyBase + "replyRunTime",
desc: "自动回复功能的单次运行时长(分钟), 0表示运行后不自动停止",
type: "基础设置",
valType: "number",
compType: "textarea",
compH: "30px",
},
replyRule: {
value: "",
base: "",
key: keyBase + "replyRule",
title: "回复规则",
desc: "自动回复规则, 多条规则时每条规则用;;分隔或换行书写\n【格式1】 A==>B, 表示检测到存在弹幕内容A后发送弹幕B\n【格式2】 A=>B, 表示检测到存在含A的弹幕内容后发送弹幕B\n【注】\n可在回复的内容B中用$name表示弹幕A的用户的用户名称, $dm表示弹幕A的用户发送的弹幕内容. 若在弹幕发送的最小间隔时间内触发了自动回复, 则该回复不会生效\n【示例】\n1==>2, 表示当某用户发送了1后, 将自动回复2\nO.o=>回复$name:O.O, 表示当用户LL(假设)发送了含O.o的弹幕后, 将自动回复'回复LL:O.O'",
type: "自动回复",
valType: "string",
compType: "textarea",
compH: "150px",
},
replyInterval: {
value: "3",
base: "3",
key: keyBase + "replyInterval",
desc: "自动回复的检测的间隔时间(每n秒检测弹幕一次,满足规则则按照规则发送弹幕)",
type: "自动回复",
valType: "number",
compType: "textarea",
compH: "30px",
},
isDblclickStop: {
value: !1,
base: !1,
key: keyBase + "isDblclickStop",
desc: "双击页面任意位置后是否停止发送弹幕",
type: "其他设置",
valType: "boolean",
compType: "radio",
valueText: { true: "双击后停止", false: "无效果" },
},
isCloseTips: {
value: !1,
base: !1,
key: keyBase + "isCloseTips",
desc: "是否关闭启动和结束时的弹窗",
type: "其他设置",
valType: "boolean",
compType: "radio",
valueText: { true: "关闭弹窗", false: "弹窗提示" },
},
},
};
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;
}
function getValue({
base,
key,
valType = "string",
isReSet = !0,
getValue = null,
setValue = null,
getVal = null,
setVal = null,
} = {}) {
getValue && (getVal = getValue), setValue && (setVal = setValue);
let val = getVal ? getVal(key) : localStorage.getItem(key);
return (
void 0 !== base &&
null == val &&
((val = base),
isReSet &&
("string" != typeof base && (base = JSON.stringify(base)),
setVal ? setVal(key, base) : localStorage.setItem(key, base))),
(valType = valType.toLowerCase()),
"string" == typeof val
? "string" === valType
? val
: "boolean" === valType || "number" === valType
? JSON.parse(val)
: "object" === valType
? val
? JSON.parse(val)
: {}
: "array" === valType
? val
? JSON.parse(val)
: []
: val
: val
);
}
function getData(settings, getVal = null, setVal = null) {
for (const valName in settings) {
const setting = settings[valName];
setting.value = getValue({
base: setting.base,
key: setting.key,
valType: setting.valType,
getVal,
setVal,
});
}
return settings;
}
const baseCfg = {
state: "",
isEditing: !1,
hasSelectedPage: !1,
param: {
id: "ll_edit_wrap",
box: document.body,
classBase: "ll_edit_",
w: "500px",
h: "",
contentH: "450px",
bg: "rgba(0, 0, 0, 0.15)",
color: "#333",
fontSize: "15px",
fontFamily:
"PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei, sans-serif",
zIndex: 11e3,
resetTt: "重置当前页的所有设置为默认值",
isShowMenu: !1,
isScrollStyle: !0,
isResetBtn: !0,
isOnlyResetCurPage: !0,
showPage: void 0,
isIntervalRun: !1,
interval: 1e3,
page: [],
callback: {
resetBefore: null,
reset: null,
confirmBefore: null,
finished: null,
interval: null,
cancelBefore: null,
cancelled: null,
},
},
},
cfg = {
version: "v1.2.2",
isEditing: baseCfg.isEditing,
hasSelectedPage: baseCfg.hasSelectedPage,
timer: null,
interval: 1e3,
param: {},
tempParam: {},
allData: {},
oldData: {},
lastData: {},
baseData: {},
controls: {},
doms: { page: [] },
editText: {},
};
const css = function getCss() {
const param = cfg.param,
cBase = (param.page, param.classBase),
baseStart = `#${param.id} .${cBase}`,
fSize = param.fontSize ? param.fontSize : "14px",
css = `#${
param.id
} {\n position: fixed;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n z-index: ${
param.zIndex || 11e3
};\n background: ${
param.bg || "rgba(0, 0, 0, 0.12)"
};\n display: none;\n}\n${baseStart}box {\n text-align: initial;\n letter-spacing: 1px;\n position: relative;\n width: ${
param.w || "450px"
};\n ${
param.h ? "max-height:" + param.h : ""
};\n margin: auto;\n color: ${
param.color || "#333"
};\n background: #fff;\n font-size: ${fSize};\n line-height: normal;\n font-family: ${
param.fontFamily ||
"PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei, sans-serif"
};\n border: 3px solid #dfedfe;\n border-radius: 10px;\n box-sizing: border-box;\n padding: 14px 8px 10px 15px;\n overflow: hidden;\n overflow-y: auto;\n}\n${baseStart}menu {\n font-weight: bold;\n font-size: ${
parseInt(fSize) + 1
}px;\n display: flex;\n flex-wrap: wrap;\n gap: 0 8px;\n}\n${baseStart}menu-item {\n margin-bottom: 8px;\n border: 1px solid #dfedfe;\n color: #9ecaff;\n background: #eef6ff;\n border-radius: 6px;\n padding: 6px 10px;\n cursor: pointer;\n}\n${baseStart}menu-item:hover {\n color: #65aaff;\n background: #dfedfe;\n border: 1px solid #dfedfe;\n}\n${baseStart}menu-item.active {\n color: #65aaff;\n background: #dfedfe;\n border: 1px solid #dfedfe;\n}\n${baseStart}page-box {\n max-height: ${
param.contentH || ""
};\n padding-right: 7px;\n margin-bottom: 8px;\n overflow: hidden;\n overflow-y: auto;\n}\n${baseStart}page {\n display: none;\n}\n${baseStart}page.curPage {\n display: block;\n}\n${baseStart}comp {\n margin-bottom: 8px;\n}\n${baseStart}comp:last-child {\n margin-bottom: 2px;\n}\n${baseStart}tt {\n font-weight: bold;\n font-size: ${
parseInt(fSize) + 6
}px;\n margin-top: 4px;\n}\n${baseStart}tt2 {\n font-weight: bold;\n font-size: ${
parseInt(fSize) + 4
}px;\n margin-top: 3px;\n margin-bottom: 7px;\n}\n${baseStart}tt3 {\n font-weight: bold;\n font-size: ${
parseInt(fSize) + 2
}px;\n margin-top: 2px;\n margin-bottom: 6px;\n}\n${baseStart}desc {\n line-height: 1.5;\n}\n${baseStart}comp-tt {\n font-weight: bold;\n font-size: ${
parseInt(fSize) + 1
}px;\n line-height: 1.5;\n}\n${baseStart}comp-desc {\n line-height: 1.5;\n}\n${baseStart}rd-arr {\n line-height: 22px;\n}\n${baseStart}rd-arr label {\n margin-right: 6px;\n cursor: pointer;\n}\n${baseStart}rd-arr input {\n vertical-align: -2px;\n cursor: pointer;\n}\n${baseStart}rd-arr span {\n color: #666;\n margin-left: 2px;\n}\n#${
param.id
} textarea {\n width: 100%;\n max-width: 100%;\n max-height: 300px;\n border-radius: 6px;\n line-height: normal;\n padding: 5px 7px;\n outline-color: #cee4ff;\n border: 1px solid #aaa;\n box-sizing: border-box;\n font-size: ${
parseInt(fSize) - 2
}px;\n font-family: PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei, sans-serif;\n /* 保留空格 */\n white-space: pre-wrap;\n /* 允许词内换行 */\n word-break: break-all;\n letter-spacing: 1px;\n overflow: hidden;\n overflow-y: auto;\n}\n#${
param.id
} textarea::placeholder {\n color: #bbb;\n}\n${baseStart}ta-desc {\n margin-bottom: 3px;\n}\n${baseStart}btn-box {\n display: flex;\n justify-content: flex-end;\n}\n${baseStart}btn-box button {\n font-size: 16px;\n line-height: normal;\n color: #65aaff;\n background: #dfedfe;\n outline: none;\n border: none;\n border-radius: 6px;\n padding: 8px 16px;\n box-sizing: border-box;\n cursor: pointer;\n}\n${baseStart}btn-box .${cBase}reset-btn {\n position: absolute;\n left: 15px;\n bottom: 10px;\n color: #888;\n background: #f4f4f4;\n margin-right: 15px;\n}\n${baseStart}btn-box .${cBase}reset-btn:hover {\n color: #666;\n background: #eee;\n}\n${baseStart}btn-box .${cBase}cancel-btn {\n color: #888;\n background: #f4f4f4;\n margin-right: 15px;\n}\n${baseStart}btn-box .${cBase}cancel-btn:hover {\n color: #666;\n background: #eee;\n}\n${baseStart}btn-box .${cBase}confirm-btn {\n margin-right: 7px;\n}\n${baseStart}btn-box .${cBase}confirm-btn:hover {\n background: #cee4ff;\n}\n`;
return param.isScrollStyle
? css +
"\n.ll-scroll-style-1::-webkit-scrollbar,\n.ll-scroll-style-1 ::-webkit-scrollbar {\n width: 8px;\n}\n.ll-scroll-style-1-size-2::-webkit-scrollbar,\n.ll-scroll-style-1 .ll-scroll-style-1-size-2::-webkit-scrollbar {\n width: 10px;\n}\n.ll-scroll-style-1-size-3::-webkit-scrollbar,\n.ll-scroll-style-1 .ll-scroll-style-1-size-3::-webkit-scrollbar {\n width: 12px;\n}\n.ll-scroll-style-1::-webkit-scrollbar-thumb,\n.ll-scroll-style-1 ::-webkit-scrollbar-thumb {\n border-radius: 10px;\n -webkit-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.05);\n opacity: 0.2;\n background: #daedff;\n}\n.ll-scroll-style-1::-webkit-scrollbar-track,\n.ll-scroll-style-1 ::-webkit-scrollbar-track {\n -webkit-box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.08);\n border-radius: 0;\n background: #fff;\n border-radius: 5px;\n}"
: css;
};
const editArea_html = function getHTML() {
function getCompHTML({ info, active = "", id }) {
let type = info.type;
if (
((type = {
menuTitle: "mtt",
title: "tt",
title2: "tt2",
title3: "tt3",
desc: "ds",
radio: "rd",
checkbox: "cb",
textarea: "ta",
mtt: "mtt",
tt: "tt",
tt2: "tt2",
tt3: "tt3",
ds: "ds",
rd: "rd",
cb: "cb",
ta: "ta",
}[type]),
(id = 0 === id ? "0" : id || ""),
0 === info.value && (info.value = "0"),
!type)
)
return console.log("不存在的组件类型"), !1;
let title = "",
desc = "",
ctrlTt = "";
switch (
(["tt", "tt2", "tt3", "ds", "mtt"].includes(type) ||
((title = info.title
? `<div class="${cBase}comp-tt ${cBase}${type}-tt" title="${
info.tt || ""
}">${info.title}</div>`
: ""),
(desc = info.desc
? `<div class="${cBase}comp-desc ${cBase}${type}-desc">${info.desc}</div>`
: "")),
type)
) {
case "mtt":
return (
(info.value = info.value || ""),
info.value
? `<div class="${cBase}menu-item ${active || ""}" title="${
info.tt || ""
}">${info.value}</div>`
: ""
);
case "tt":
case "tt2":
case "tt3":
return (
(info.value = info.value || ""),
info.value
? `<div class="${cBase}${type} ${cBase}comp" title="${
info.tt || ""
}">${info.value}</div>`
: ""
);
case "ds":
return (
(info.value = info.value || ""),
info.value
? `<div class="${cBase}desc ${cBase}comp" title="${
info.descTt || ""
}">${info.value}</div>`
: ""
);
case "rd":
const name = info.name || info.id + new Date().getTime();
(ctrlTt = info.ctrlTt || ""),
ctrlTt && (ctrlTt = `title="${ctrlTt}"`);
let radio = `<div class="${cBase}rd ${cBase}rd-arr" ${ctrlTt}>`;
if (void 0 === info.value && info.radioList[0]) {
const obj = info.radioList[0];
info.value = void 0 === obj.value ? obj.text : obj.value;
}
return (
info.radioList.forEach((item, i) => {
void 0 === item.value && (info.radioList[i].value = item.text),
void 0 === item.text && (info.radioList[i].text = item.value);
const value = item.value;
let tt = item.tt || "";
tt && (tt = `title="${tt}"`);
let selected = "";
info.value + "" == item.value + "" && (selected = "checked"),
(radio += `<label ${tt}><input ${selected} type="radio" name="${name}" data-val="${value}" data-cpid="${id}"><span>${item.text}</span></label>`);
}),
(radio += "</div>"),
`<div class="${cBase}comp ${cBase}ctrl ${cBase}rd-box" data-type="${type}" data-cpid="${id}">${title}${desc}${radio}</div>`
);
case "cb":
const name2 = info.name || new Date().getTime();
if (
((ctrlTt = info.ctrlTt || ""),
ctrlTt && (ctrlTt = `title="${ctrlTt}"`),
void 0 === info.value && info.radioList[0])
) {
const obj = info.radioList[0];
info.value = void 0 === obj.value ? obj.text : obj.value;
}
let checkbox = `<div class="${cBase}cb ${cBase}rd-arr" ${ctrlTt}>`;
return (
info.radioList.forEach((item, i) => {
void 0 === item.value && (info.radioList[i].value = item.text),
void 0 === item.text && (info.radioList[i].text = item.value);
const value = item.value;
let tt = item.tt || "";
tt && (tt = `title="${tt}"`);
let selected = "";
info.value.includes(value) && (selected = "checked"),
(checkbox += `<label ${tt}><input ${selected} type="checkbox" name="${name2}" data-val="${value}" data-cpid="${id}"><span>${item.text}</span></label>`);
}),
(checkbox += "</div>"),
`<div class="${cBase}comp ${cBase}ctrl ${cBase}cb-box" data-type="${type}" data-cpid="${id}">${title}${desc}${checkbox}</div>`
);
case "ta":
const taH = `height:${info.height || "30px"};`,
style = `style="${
info.width ? "width:" + info.width + ";" : ""
}${taH}${
info.fontSize ? "font-size:" + info.fontSize + ";" : ""
}${
info.fontFamily ? "font-family:" + info.fontFamily + ";" : ""
}"`,
textarea = `<textarea class="${cBase}ta" ${style} data-cpid="${id}" placeholder="${
info.ph || ""
}" title="${info.ctrlTt || "拖动右下角可调节宽高"}"></textarea>`;
return `<div class="${cBase}comp ${cBase}ctrl ${cBase}ta-box" data-type="${type}" data-cpid="${id}">${title}${desc}${textarea}</div>`;
}
}
const param = cfg.param,
page = param.page,
cBase = param.classBase,
isMenu = 1 !== page.length;
let menu = `<div class="${cBase}menu">`,
pageHTML = `<div class="${cBase}page-box ll-scroll-style-1 ll-scroll-style-1-size-2">`;
page.forEach((curPage, index) => {
let pgid = curPage.id || index;
(pgid += ""), (cfg.allData[pgid] = {}), (cfg.baseData[pgid] = {});
let pageFlag = "";
if (
(cfg.hasSelectedPage ||
((void 0 === param.showPage || pgid === param.showPage + "") &&
((pageFlag = "curPage"), (cfg.hasSelectedPage = !0))),
(pageHTML += `<div class="${cBase}page ${pageFlag}" data-pgid="${pgid}">`),
curPage.components)
) {
let compIndex = 0;
if (isMenu || param.isShowMenu) {
let curMenu = curPage.components.find(
(item) => "menuTitle" === item.type
);
curMenu || (curMenu = { type: "menuTitle", value: pgid }),
(menu += getCompHTML({
info: curMenu,
active: pageFlag ? "active" : "",
}));
}
curPage.components.forEach((item) => {
const cpid = item.id || compIndex;
"menuTitle" !== item.type &&
(pageHTML += getCompHTML({ info: item, id: cpid })),
["title", "title2", "title3", "desc", "menuTitle"].includes(
item.type
) ||
((item.base = void 0 === item.base ? item.value : item.base),
(cfg.allData[pgid][cpid] = item.value),
(cfg.baseData[pgid][cpid] = item.base),
compIndex++);
});
}
pageHTML += "</div>";
}),
(pageHTML += "</div>"),
isMenu || param.isShowMenu ? (menu += "</div>") : (menu = "");
const resetBtn = param.isResetBtn
? `<button class="${cBase}reset-btn" title="${
param.resetTt || "重置所有设置为默认值"
}">重置</button>`
: "",
btnBox = `<div class="${cBase}btn-box">\n${resetBtn}\n<button class="${cBase}cancel-btn">取 消</button>\n<button class="${cBase}confirm-btn">确 认</button>\n</div>`;
return `<div class="${cBase}box ll-scroll-style-1 ll-scroll-style-1-size-3" data-version="${cfg.version}">\n${menu}\n${pageHTML}\n${btnBox}\n</div>`;
},
baseParam = baseCfg.param,
controls = cfg.controls,
doms = cfg.doms;
function createEditEle({
id = baseParam.id,
box = baseParam.box,
classBase = baseParam.classBase,
w = baseParam.w,
h = baseParam.h,
contentH = baseParam.contentH,
bg = baseParam.bg,
color = baseParam.color,
fontSize = baseParam.fontSize,
fontFamily = baseParam.fontFamily,
zIndex = baseParam.zIndex,
resetTt = baseParam.resetTt,
isShowMenu = baseParam.isShowMenu,
isScrollStyle = baseParam.isScrollStyle,
isResetBtn = baseParam.isResetBtn,
isOnlyResetCurPage = baseParam.isOnlyResetCurPage,
showPage = baseParam.showPage,
isIntervalRun = baseParam.isIntervalRun,
interval = baseParam.interval,
page = [],
callback = baseParam.callback,
} = {}) {
(cfg.state = baseCfg.state),
(cfg.isEditing = baseCfg.isEditing),
(cfg.hasSelectedPage = baseCfg.hasSelectedPage),
(cfg.param = { ...baseParam });
const param = cfg.param;
(box = box || document.body),
(param.id = id),
(param.box = box),
(param.classBase = classBase),
(param.w = w),
(param.h = h),
(param.contentH = contentH),
(param.bg = bg),
(param.color = color),
(param.fontSize = fontSize),
(param.fontFamily = fontFamily),
(param.zIndex = zIndex),
(param.resetTt = resetTt),
(param.isShowMenu = isShowMenu),
(param.isScrollStyle = isScrollStyle),
(param.isResetBtn = isResetBtn),
(param.isOnlyResetCurPage = isOnlyResetCurPage),
(param.showPage = showPage),
(param.isIntervalRun = isIntervalRun),
(param.interval = interval),
(param.page = page),
(param.callback = callback),
(cfg.interval = interval),
(cfg.callback = callback);
const html = editArea_html();
return (
box.querySelector(`#${param.classBase}${param.id}-css`) ||
(function addCss(cssText, box = document.body, id = "") {
const style = document.createElement("style");
return (
id && (style.id = id),
box.appendChild(style),
(style.innerHTML = cssText),
style
);
})(css(), box, param.classBase + param.id + "-css"),
(doms.wrap = (function createEle({
className = "",
id = "",
title = "",
css,
box = document.body,
type = "div",
} = {}) {
const ele = document.createElement(type);
return (
id && (ele.id = id),
className && (ele.className = className),
title && (ele.title = title),
css && (ele.style.cssText = css),
box.appendChild(ele),
ele
);
})({ className: id, id })),
(doms.wrap.innerHTML = html),
(function getDoms() {
const param = cfg.param,
cBase = param.classBase;
(doms.box = doms.wrap.querySelector(`.${cBase}box`)),
(doms.cancel = doms.box.querySelector(`.${cBase}cancel-btn`)),
(doms.confirm = doms.box.querySelector(`.${cBase}confirm-btn`));
const isMenu = 1 !== param.page.length;
(isMenu || param.isShowMenu) &&
((doms.menu = doms.box.querySelector(`.${cBase}menu`)),
(doms.menus = [].slice.call(
doms.menu.querySelectorAll(`.${cBase}menu-item`)
)));
const pages = [].slice.call(doms.box.querySelectorAll(`.${cBase}page`));
(doms.page = []),
param.isResetBtn &&
(doms.reset = doms.box.querySelector(`.${cBase}reset-btn`));
pages.forEach((curPage, index) => {
cfg.hasSelectedPage ||
(curPage.classList.add("curPage"),
(isMenu || param.isShowMenu) &&
doms.menus[0].classList.add("active"),
(cfg.hasSelectedPage = !0));
const page = {},
pgid = curPage.dataset.pgid;
(page.pgid = curPage.pgid = pgid),
(page.controls = [].slice.call(
curPage.querySelectorAll(`.${cBase}ctrl`)
)),
(page.ele = curPage),
doms.page.push(page),
(isMenu || param.isShowMenu) &&
(doms.menus[index].settingsPage = curPage);
const ctrls = {};
(controls[pgid] = ctrls),
page.controls.forEach((item, i) => {
const cpid = item.dataset.cpid,
cType = item.dataset.type;
let dom;
(item.cpid = cpid),
"rd" === cType || "cb" === cType
? ((dom = [].slice.call(item.querySelectorAll("input"))),
(dom.compType = cType))
: "ta" === cType &&
((dom = item.querySelector("textarea")),
(dom.compType = cType),
(dom.value = cfg.allData[pgid][cpid])),
(ctrls[cpid] = dom);
});
});
})(),
cfg.timer && clearInterval(cfg.timer),
(function bindEvents() {
const param = cfg.param;
function menuHandle(e) {
const dom = e.target,
cBase = param.classBase;
if (dom.classList.contains(`${cBase}menu-item`)) {
const old = doms.menu.querySelector(".active");
old.classList.remove("active"),
old.settingsPage.classList.remove("curPage"),
dom.classList.add("active"),
dom.settingsPage.classList.add("curPage");
}
}
function cancelEdit(e) {
const cBase = param.classBase;
if (
(e.stopPropagation(),
e.target.className !== `${cBase}wrap` &&
e.target.className !== `${cBase}cancel-btn`)
)
return;
const callback = cfg.callback;
!1 !== runCallback(callback.cancelBefore) &&
(showEditArea(!1),
setCompValue(cfg.oldData),
param.isIntervalRun &&
(setCompValue(cfg.oldData), (cfg.allData = cfg.oldData)),
runCallback(callback.cancelled));
}
function confirmEdit() {
const callback = cfg.callback,
data = getAllData();
(cfg.allData = data),
!1 !== runCallback(callback.confirmBefore, data) &&
(showEditArea(!1),
(cfg.state = "finished"),
runCallback(callback.finished, data),
(cfg.state = ""));
}
function resetEdit() {
const callback = cfg.callback,
data = getAllData();
!1 !== runCallback(callback.resetBefore, data) &&
(!(function resetEditData(isOnlyPage = !1) {
const param = cfg.param;
if (param.isResetBtn)
if (isOnlyPage) {
const data = getAllData(),
curMenu = doms.menu.querySelector(".active");
(data[curMenu.innerText] = cfg.baseData[curMenu.innerText]),
setCompValue(data);
} else setCompValue(cfg.baseData);
})(param.isOnlyResetCurPage),
runCallback(callback.reset, data));
}
doms.menu && doms.menu.addEventListener("click", menuHandle),
doms.wrap.addEventListener("click", cancelEdit),
doms.cancel.addEventListener("click", cancelEdit),
doms.confirm.addEventListener("click", confirmEdit),
doms.reset && doms.reset.addEventListener("click", resetEdit);
})(),
(cfg.state = "created"),
cfg
);
}
function getAllData() {
function getCompItem(pgid, cpid) {
if (!controls[pgid]) return;
const ctrl = controls[pgid][cpid];
if (ctrl) {
if (!Array.isArray(ctrl)) return ctrl.value;
if ("rd" === ctrl.compType) {
const result = ctrl.find((item) => item.checked).dataset.val;
return "false" !== result && ("true" === result || result);
}
if ("cb" === ctrl.compType) {
return ctrl
.filter((item) => item.checked)
.map((item) => {
const value = item.dataset.val;
return "false" !== value && ("true" === value || value);
});
}
}
}
const data = {};
if (0 === arguments.length) {
for (const key in controls) {
const page = controls[key];
data[key] = {};
for (const key2 in page) data[key][key2] = getCompItem(key, key2);
}
return data;
}
if (1 === arguments.length) {
const ctrls = arguments[0];
for (const pgid in ctrls) {
data[pgid] = {};
controls[pgid].forEach((cpid) => {
data[pgid][cpid] = getCompItem(pgid, cpid);
});
}
return cfg.allData;
}
return getCompItem(arguments[0], arguments[1]);
}
function setCompValue() {
function setCompItem(pgid, cpid, value) {
if (!controls[pgid]) return;
const ctrl = controls[pgid][cpid];
if (ctrl)
if (Array.isArray(ctrl)) {
if ("rd" === ctrl.compType) {
const selected = ctrl.find((item) => item.checked);
selected && (selected.checked = !1);
const select = ctrl.find((item) => item.dataset.val === value + "");
select && (select.checked = !0);
} else if ("cb" === ctrl.compType) {
if (
(ctrl
.filter((item) => item.checked)
.forEach((item) => {
item.checked = !1;
}),
Array.isArray(value))
)
value.forEach((val) => {
const select = ctrl.find(
(item) => item.dataset.val === val + ""
);
select && (select.checked = !0);
});
else {
const select = ctrl.find(
(item) => item.dataset.val === value + ""
);
select && (select.checked = !0);
}
}
} else ctrl.value = value;
}
if (1 === arguments.length) {
const data = arguments[0];
for (const key in data) {
const pageData = data[key];
for (const key2 in pageData) {
setCompItem(key, key2, pageData[key2]);
}
}
} else {
setCompItem(arguments[0], arguments[1], arguments[2]);
}
}
function showEditArea(isShow = !0, callback = null) {
if (
(cfg.param.isIntervalRun &&
(cfg.timer && clearInterval(cfg.timer),
(cfg.timer = setInterval(() => {
const data = getAllData(),
oldType = cfg.state;
(cfg.state = "interval"),
runCallback(cfg.callback.interval, data),
(cfg.state = oldType),
(cfg.lastData = data);
}, cfg.interval))),
(cfg.state = "created"),
isShow)
) {
if (((cfg.oldData = getAllData()), "function" == typeof callback)) {
if (!1 === callback(cfg.oldData, cfg.oldData, cfg.baseData)) return;
}
cfg.state = "show";
}
(cfg.isEditing = isShow),
(doms.wrap.style.display = isShow ? "block" : "none"),
isShow &&
!doms.box.style.top &&
(doms.box.style.top =
window.innerHeight / 2 - doms.box.clientHeight / 2 + "px"),
callback && (cfg.callback = callback);
}
function runCallback(callback, data) {
let result;
if (callback) {
data || (data = getAllData());
const func = callback;
Array.isArray(func)
? func.curFn
? ((result = func[curFn](data, cfg.oldData, cfg.baseData)),
(func.curFn = null))
: func.forEach((fn) => {
result = fn(data, cfg.oldData, cfg.baseData);
})
: "function" == typeof callback &&
(result = func(data, cfg.oldData, cfg.baseData));
}
return result;
}
function setValue({
value,
base,
key,
verification = null,
getValue = null,
setValue = null,
getVal = null,
setVal = null,
} = {}) {
getValue && (getVal = getValue), setValue && (setVal = setValue);
let newVal = value,
oldVal = getVal ? getVal(key) : localStorage.getItem(key);
return (
void 0 !== base &&
null == oldVal &&
((oldVal = base),
"string" != typeof base && (base = JSON.stringify(base)),
setVal ? setVal(key, base) : localStorage.setItem(key, base)),
null !== newVal &&
("function" != typeof verification ||
((newVal = verification(newVal, oldVal, base)), null !== newVal)) &&
newVal !== oldVal &&
("string" != typeof newVal && (newVal = JSON.stringify(newVal)),
setVal ? setVal(key, newVal) : localStorage.setItem(key, newVal),
!0)
);
}
function toPageObj({ settings, param = {}, otherPageName = "无分类" } = {}) {
param = { ...param };
const pageArr = [],
menuList = [];
let isOtherType = !1;
for (let key in settings) {
const item = settings[key];
item.type
? menuList.includes(item.type) || menuList.push(item.type)
: isOtherType || (isOtherType = !0);
}
return (
isOtherType && menuList.push(otherPageName),
menuList.forEach((menuTt) => {
const components = [],
page = { id: menuTt, components },
arr = [];
for (let key in settings) {
const item = settings[key];
menuTt === otherPageName
? item.type || arr.push(item)
: item.type === menuTt && arr.push(item);
}
arr.forEach((item) => {
let desc = item.desc || item.txt || "";
desc && (desc = desc.replaceAll("\n", "<br>").trim());
let comp,
base = item.base;
if (
(Array.isArray(base) && (base = base.join(", ")), item.groupTitle1)
) {
const comp = {
id: item.key + "-gTt1",
type: "title",
value: item.groupTitle1,
};
components.push(comp);
}
if (item.groupTitle2) {
const comp = {
id: item.key + "-gTt2",
type: "title2",
value: item.groupTitle2,
};
components.push(comp);
}
if (item.groupTitle3) {
const comp = {
id: item.key + "-gTt3",
type: "title3",
value: item.groupTitle3,
};
components.push(comp);
}
if (item.groupDesc) {
const comp = {
id: item.key + "-gDesc",
type: "desc",
value: item.groupDesc,
};
components.push(comp);
}
if (
(["menuTitle", "title", "desc", "title2", "title3"].includes(
item.compType
)
? ((comp = { ...item }),
(comp.type = comp.compType),
(comp.desc = desc))
: (comp = {
id: item.key,
type: item.compType,
tt: item.tt || "",
title: item.title || "",
desc,
descTt: item.descTt || "",
name: item.key,
value: item.value,
base: item.base,
}),
"textarea" === comp.type)
)
(comp.ph = base),
(comp.width = item.compW),
(comp.height = item.compH),
(comp.ctrlTt = "默认: " + base);
else if ("radio" === comp.type || "checkbox" === comp.type) {
let str = "默认: ";
if ("checkbox" === comp.type) {
let arr = item.base;
Array.isArray(arr) || (arr = arr.split(/,|,/)),
arr.forEach((val, i) => {
0 !== i && (str += ", "), (val = val.trim());
let valTxt = item.valueText[val];
void 0 === valTxt && (valTxt = val), (str += valTxt);
});
} else {
let val = item.valueText[item.base];
void 0 === val && (val = item.base), (str += val);
}
comp.ctrlTt = str;
}
if (item.valueText) {
comp.radioList = [];
for (let key in item.valueText) {
const rd = { text: item.valueText[key], value: key };
comp.radioList.push(rd);
}
}
components.push(comp);
}),
pageArr.push(page);
}),
(param.page = pageArr),
param
);
}
function showSettings() {
const settings = info.settings;
info.settingsArea = (function createEdit({
settings,
param = {},
oldEditCfg,
updateDataFn,
isNewEdit = !0,
isSyncOtherPage = !0,
otherPageName = "无分类",
} = {}) {
let oldSettings, curSettings;
updateDataFn &&
isSyncOtherPage &&
((oldSettings = JSON.stringify(settings)),
(settings = updateDataFn() || settings),
(curSettings = JSON.stringify(settings)));
const editInfo = { settings, param, otherPageName };
if (oldEditCfg) {
if (isNewEdit)
return (
oldEditCfg.doms.wrap.remove(), createEditEle(toPageObj(editInfo))
);
isSyncOtherPage &&
updateDataFn &&
oldSettings !== curSettings &&
(oldEditCfg.doms.wrap.remove(),
(oldEditCfg = createEditEle(toPageObj(editInfo)))),
isSyncOtherPage &&
!updateDataFn &&
(oldEditCfg.doms.wrap.remove(),
(oldEditCfg = createEditEle(toPageObj(editInfo))));
} else oldEditCfg = createEditEle(toPageObj(editInfo));
return oldEditCfg;
})({
settings,
param: {
resetTt: "重置当前页的所有设置为默认值",
isOnlyResetCurPage: !0,
},
oldEditCfg: info.settingsArea,
updateDataFn: () => getData(settings, GM_getValue, GM_setValue),
});
showEditArea(!0, {
resetBefore: () => confirm("是否重置当前页的所有设置为默认值?"),
confirmBefore: () => {},
finished: (data) => {
console.log(data);
if (
(function isValueChange(type = "auto") {
const param = cfg.param,
curData = getAllData(),
curDataStr = JSON.stringify(curData);
let oldDataStr;
return (
"auto" === type &&
("interval" === cfg.state &&
param.isIntervalRun &&
(type = "interval_current"),
"finished" === cfg.state && (type = "auto")),
(oldDataStr =
"interval_current" === type
? JSON.stringify(cfg.lastData)
: "base_current" === type
? JSON.stringify(cfg.baseData)
: JSON.stringify(cfg.oldData)),
"{}" !== oldDataStr && curDataStr !== oldDataStr
);
})()
) {
for (const pageName in data) {
const page = data[pageName];
for (const key in page) {
const value = page[key];
let verifyFn;
const flag = key.replace(info.keyBase, ""),
item = settings[flag];
switch (key) {
case settings.dmText.key:
verifyFn = (newVal) => newVal || settings.dmText.base;
break;
case settings.intervalTime.key:
verifyFn = (newVal) =>
!+newVal || +newVal < 0
? settings.intervalTime.base
: +newVal;
break;
case settings.runTime.key:
verifyFn = (newVal) =>
0 == +newVal
? +newVal
: !+newVal || +newVal < 0
? settings.runTime.base
: +newVal;
break;
case settings.replyRule.key:
break;
case settings.replyInterval.key:
verifyFn = (newVal) =>
!+newVal || +newVal < 0
? settings.replyInterval.base
: +newVal;
case settings.isDblclickStop.key:
case settings.isCloseTips.key:
}
if (!item)
return void console.log("设置的数据对应的对象获取失败");
setValue({
value,
base: item.base,
key,
verification: verifyFn,
getValue: GM_getValue,
setValue: GM_setValue,
});
}
}
getData(settings, GM_getValue, GM_setValue);
}
},
});
}
const settings = info.settings;
function main_getDoms() {
if (info.doms.textarea && info.doms.btn) return;
const fn = (selectors) => {
(selectors = selectors || info.curClassList),
(info.doms.textarea = document.querySelector(selectors.textarea)),
(info.doms.btn = document.querySelector(selectors.btn));
};
for (const key in info.classList) {
const vClass = info.classList[key];
if ((fn(vClass), info.doms.textarea && info.doms.btn)) {
(info.curClassList = vClass), (info.dmDataset = vClass.dmDataset);
break;
}
}
return info.doms.textarea && info.doms.btn
? void 0
: (info.timerId && clearInterval(info.timerId),
info.replyTimer && clearInterval(info.replyTimer),
info.errorTimer && clearTimeout(info.errorTimer),
(info.errorTimer = setTimeout(() => {
fn(),
(info.doms.textarea && info.doms.btn) ||
(info.doms.textarea || console.log("文本框的dom获取失败"),
info.doms.btn || console.log("发送按钮的dom获取失败"),
info.timerId && clearInterval(info.timerId),
settings.isCloseTips.value ||
GM_notification({
title: info.data.errorTt,
text: info.data.errorTxt,
timeout: 1e4,
}));
}, 4e3)),
-1);
}
function getSendText(str) {
const newStr = str
.replaceAll(";", ";")
.replaceAll(";;\n", ";;")
.replaceAll("==\n", "==");
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 sendOneDm(text) {
if (!text) return -1;
(info.doms.textarea.value = text),
(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();
}
function test() {
[].slice
.call(document.querySelectorAll(info.curClassList.myDm))
.forEach((dm) => {
dm.innerText.trim() !== info.data.testDm ||
dm.isReply ||
(sendOneDm(info.data.testText), (dm.isReply = !0));
});
}
function stop(istip = !0) {
istip &&
!settings.isCloseTips.value &&
GM_notification({ text: "已停止发送弹幕", timeout: 3500 }),
(info.isStarted = !1),
info.timerId && clearInterval(info.timerId),
info.replyTimer && clearInterval(info.replyTimer),
(info.data.curIndex = 0);
}
function autoReply() {
if (!settings.replyRule.value) return;
let replyRule, endReplyRule;
main_getDoms();
const verifyDm = (dm) => {
if (
((() => {
endReplyRule = [];
const ruleArr = settings.replyRule.value
.replaceAll(";", ";")
.replaceAll(";;", "\n")
.split("\n");
(replyRule = ruleArr.map((rule) => {
let arr;
return (
rule.includes("==>")
? ((arr = rule.split("==>")), (arr.flag = "==>"))
: rule.includes("=>") &&
((arr = rule.split("=>")), (arr.flag = "=>")),
arr
);
})),
replyRule.forEach((item) => {
Array.isArray(item) &&
2 === item.length &&
endReplyRule.push(item);
});
})(),
!replyRule || 0 === replyRule.length)
)
return -1;
return endReplyRule.find((item) =>
"==>" === item.flag
? dm === item[0]
: "=>" === item.flag
? dm.includes(item[0])
: void 0
);
};
settings.isCloseTips.value ||
GM_notification({ text: "开启自动回复功能", timeout: 3500 }),
(info.isStartedReply = !0),
(info.data.replyStartTime = new Date().getTime()),
info.replyTimer && clearInterval(info.replyTimer),
(info.replyTimer = setInterval(() => {
test();
const dmList = document.querySelectorAll(info.curClassList.dm),
len = dmList.length;
if (0 !== dmList.length) {
for (let i = 0; i < len; i++) {
const dm = dmList[i];
if (dm.isReply) continue;
const text = dm.innerText.trim();
let rule = verifyDm(text);
if (-1 === rule) break;
if (rule) {
let name,
dmText,
rpText = rule[1];
dm.parentElement.dataset &&
((name = dm.parentElement.dataset[info.dmDataset.name]),
(dmText = dm.parentElement.dataset[info.dmDataset.dm])),
name && (rpText = rpText.replaceAll("$name", name)),
dmText && (rpText = rpText.replaceAll("$dm", dmText)),
(dm.isReply = !0);
-1 !== sendOneDm(rpText) &&
console.log(
`回复弹幕: ${name}: ${dmText}\n回复内容: ${rpText}`
);
break;
}
}
if (0 !== settings.replyRunTime.value) {
if (
(new Date().getTime() - info.data.replyStartTime) / 1e3 / 60 >
settings.replyRunTime.value
)
return (
clearInterval(info.replyTimer),
settings.isCloseTips.value ||
GM_notification({
text: `已停止自动回复, 共运行${settings.replyRunTime.value}分钟`,
timeout: 4500,
}),
console.log(
`已停止自动回复, 共运行${settings.replyRunTime.value}分钟`
),
-1
);
}
} else console.log("未获取到弹幕");
}, 1e3 * settings.replyInterval.value));
}
function setMenu() {
GM_registerMenuCommand("开始/停止 自动弹幕", () => {
info.isStarted
? stop()
: (function start() {
stop(!1);
const sendDm = () => {
main_getDoms(), test();
const sendText = getSendText(settings.dmText.value);
if (
(sendOneDm(sendText),
console.log("发送弹幕:", sendText),
0 !== settings.runTime.value &&
(new Date().getTime() - info.data.startTime) / 1e3 / 60 >
settings.runTime.value)
)
return (
clearInterval(info.timerId),
settings.isCloseTips.value ||
GM_notification({
text: `已停止自动弹幕, 共运行${settings.runTime.value}分钟`,
timeout: 4500,
}),
console.log(
`已停止自动弹幕, 共运行${settings.runTime.value}分钟`
),
-1
);
};
settings.isCloseTips.value ||
GM_notification({ text: "开始发送弹幕", timeout: 3500 }),
(info.isStarted = !0),
(info.data.startTime = new Date().getTime()),
-1 !== sendDm() &&
(info.timerId = setInterval(
sendDm,
1e3 * settings.intervalTime.value
));
})();
}),
GM_registerMenuCommand("开启/关闭 自动回复", () => {
info.isStartedReply
? (function replyStop() {
settings.isCloseTips.value ||
GM_notification({ text: "已关闭自动回复功能", timeout: 3500 }),
(info.isStartedReply = !1),
info.replyTimer && clearInterval(info.replyTimer);
})()
: autoReply();
}),
GM_registerMenuCommand("设置", () => {
showSettings();
});
}
function main() {
for (const key in info.classList) {
(info.curClassList = info.classList[key]),
(info.dmDataset = info.curClassList.dmDataset);
break;
}
getData(settings, GM_getValue, GM_setValue),
main_getDoms(),
setMenu(),
(function main_bindEvents() {
window.addEventListener(
"dblclick",
() => {
settings.isDblclickStop.value && stop();
},
!0
);
})();
}
"/" !== window.location.pathname &&
setTimeout(() => {
main();
}, 1e3);
})();