mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
Merge branch 'dev' of github.com:linyuchen/LiteLoaderQQNT-OneBotApi into dev
This commit is contained in:
commit
3fb4b6a8da
@ -34,9 +34,9 @@ wget -O fastboot.sh https://cdn.jsdelivr.net/gh/MliKiowa/llonebot-docker/fastboo
|
|||||||
```
|
```
|
||||||
├── plugins
|
├── plugins
|
||||||
│ ├── LLOneBot
|
│ ├── LLOneBot
|
||||||
│ │ └── main.js
|
│ │ └── main/
|
||||||
│ │ └── preload.js
|
│ │ └── preload/
|
||||||
│ │ └── renderer.js
|
│ │ └── renderer/
|
||||||
│ │ └── manifest.json
|
│ │ └── manifest.json
|
||||||
│ │ └── node_modules/...
|
│ │ └── node_modules/...
|
||||||
```
|
```
|
||||||
|
@ -52,11 +52,11 @@ let config = {
|
|||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
lib: {
|
lib: {
|
||||||
formats: ["es"],
|
formats: ["es"],
|
||||||
entry: { "renderer": "src/renderer.ts" },
|
entry: { "renderer": "src/renderer/index.ts" },
|
||||||
},
|
},
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
// external: externalAll,
|
// external: externalAll,
|
||||||
input: "src/renderer.ts",
|
input: "src/renderer/index.ts",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolve:{
|
resolve:{
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"injects": {
|
"injects": {
|
||||||
"renderer": "./renderer/renderer.js",
|
"renderer": "./renderer/index.js",
|
||||||
"main": "./main/main.cjs",
|
"main": "./main/main.cjs",
|
||||||
"preload": "./preload/preload.cjs"
|
"preload": "./preload/preload.cjs"
|
||||||
}
|
}
|
||||||
|
380
src/renderer.ts
380
src/renderer.ts
@ -1,380 +0,0 @@
|
|||||||
/// <reference path="./global.d.ts" />
|
|
||||||
|
|
||||||
// 打开设置界面时触发
|
|
||||||
|
|
||||||
async function onSettingWindowCreated (view: Element) {
|
|
||||||
window.llonebot.log('setting window created')
|
|
||||||
const isEmpty = (value: any) => value === undefined || value === null || value === ''
|
|
||||||
const config = await window.llonebot.getConfig()
|
|
||||||
const httpClass = 'http'
|
|
||||||
const httpPostClass = 'http-post'
|
|
||||||
const wsClass = 'ws'
|
|
||||||
const reverseWSClass = 'reverse-ws'
|
|
||||||
const llonebotError = await window.llonebot.getError()
|
|
||||||
window.llonebot.log('获取error' + JSON.stringify(llonebotError))
|
|
||||||
|
|
||||||
function createHttpHostEleStr (host: string) {
|
|
||||||
const eleStr = `
|
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item ${httpPostClass}">
|
|
||||||
<h2>HTTP事件上报地址(http)</h2>
|
|
||||||
<input class="httpHost input-text" type="text" value="${host}"
|
|
||||||
style="width:60%;padding: 5px"
|
|
||||||
placeholder="如:http://127.0.0.1:8080/onebot/v11/http"/>
|
|
||||||
</setting-item>
|
|
||||||
`
|
|
||||||
return eleStr
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWsHostEleStr (host: string) {
|
|
||||||
const eleStr = `
|
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item ${reverseWSClass}">
|
|
||||||
<h2>反向websocket地址:</h2>
|
|
||||||
<input class="wsHost input-text" type="text" value="${host}"
|
|
||||||
style="width:60%;padding: 5px"
|
|
||||||
placeholder="如: ws://127.0.0.1:5410/onebot"/>
|
|
||||||
</setting-item>
|
|
||||||
`
|
|
||||||
return eleStr
|
|
||||||
}
|
|
||||||
|
|
||||||
let httpHostsEleStr = ''
|
|
||||||
for (const host of config.ob11.httpHosts) {
|
|
||||||
httpHostsEleStr += createHttpHostEleStr(host)
|
|
||||||
}
|
|
||||||
|
|
||||||
let wsHostsEleStr = ''
|
|
||||||
for (const host of config.ob11.wsHosts) {
|
|
||||||
wsHostsEleStr += createWsHostEleStr(host)
|
|
||||||
}
|
|
||||||
|
|
||||||
const html = `
|
|
||||||
<div class="config_view llonebot">
|
|
||||||
<setting-section>
|
|
||||||
<setting-panel id="llonebotError" style="display:${llonebotError.ffmpegError || llonebotError.otherError ? '' : 'none'}">
|
|
||||||
<setting-item id="ffmpegError" data-direction="row"
|
|
||||||
style="diplay:${llonebotError.ffmpegError ? '' : 'none'}"
|
|
||||||
class="hostItem vertical-list-item">
|
|
||||||
<setting-text data-type="secondary" class="err-content">${llonebotError.ffmpegError}</setting-text>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item id="otherError" data-direction="row"
|
|
||||||
style="diplay:${llonebotError.otherError ? '' : 'none'}"
|
|
||||||
class="hostItem vertical-list-item">
|
|
||||||
<setting-text data-type="secondary" class="err-content">${llonebotError.otherError}</setting-text>
|
|
||||||
</setting-item>
|
|
||||||
</setting-panel>
|
|
||||||
<setting-panel>
|
|
||||||
<setting-list class="wrap">
|
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<div>启用HTTP服务</div>
|
|
||||||
</div>
|
|
||||||
<setting-switch id="http" ${config.ob11.enableHttp ? 'is-active' : ''}></setting-switch>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item class="vertical-list-item ${httpClass}" data-direction="row" style="display: ${config.ob11.enableHttp ? '' : 'none'}">
|
|
||||||
<setting-text>HTTP监听端口</setting-text>
|
|
||||||
<input id="httpPort" type="number" value="${config.ob11.httpPort}"/>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<div>启用HTTP事件上报</div>
|
|
||||||
</div>
|
|
||||||
<setting-switch id="httpPost" ${config.ob11.enableHttpPost ? 'is-active' : ''}></setting-switch>
|
|
||||||
</setting-item>
|
|
||||||
<div class="${httpPostClass}" style="display: ${config.ob11.enableHttpPost ? '' : 'none'}">
|
|
||||||
<div >
|
|
||||||
<button id="addHttpHost" class="q-button">添加HTTP POST上报地址</button>
|
|
||||||
</div>
|
|
||||||
<div id="httpHostItems">
|
|
||||||
${httpHostsEleStr}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<div>启用正向Websocket协议</div>
|
|
||||||
</div>
|
|
||||||
<setting-switch id="websocket" ${config.ob11.enableWs ? 'is-active' : ''}></setting-switch>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item class="vertical-list-item ${wsClass}" data-direction="row" style="display: ${config.ob11.enableWs ? '' : 'none'}">
|
|
||||||
<setting-text>正向Websocket监听端口</setting-text>
|
|
||||||
<input id="wsPort" type="number" value="${config.ob11.wsPort}"/>
|
|
||||||
</setting-item>
|
|
||||||
|
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<div>启用反向Websocket协议</div>
|
|
||||||
</div>
|
|
||||||
<setting-switch id="websocketReverse" ${config.ob11.enableWsReverse ? 'is-active' : ''}></setting-switch>
|
|
||||||
</setting-item>
|
|
||||||
<div class="${reverseWSClass}" style="display: ${config.ob11.enableWsReverse ? '' : 'none'}">
|
|
||||||
<div>
|
|
||||||
<button id="addWsHost" class="q-button">添加反向Websocket地址</button>
|
|
||||||
</div>
|
|
||||||
<div id="wsHostItems">
|
|
||||||
${wsHostsEleStr}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<setting-item class="vertical-list-item" data-direction="row">
|
|
||||||
<setting-text>Access Token</setting-text>
|
|
||||||
<input id="token" type="text" placeholder="可为空" value="${config.token}"/>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item data-direction="row" class="vertical-list-item">
|
|
||||||
<setting-item data-direction="row" class="vertical-list-item" style="width: 80%">
|
|
||||||
<setting-text>ffmpeg路径</setting-text>
|
|
||||||
<input id="ffmpegPath" class="input-text" type="text"
|
|
||||||
style="width:80%;padding: 5px"
|
|
||||||
value="${config.ffmpeg || ''}"/>
|
|
||||||
</setting-item>
|
|
||||||
<button id="selectFFMPEG" class="q-button q-button--small q-button--secondary">选择ffmpeg</button>
|
|
||||||
</setting-item>
|
|
||||||
<button id="save" class="q-button">保存</button>
|
|
||||||
</setting-list>
|
|
||||||
</setting-panel>
|
|
||||||
<setting-panel>
|
|
||||||
|
|
||||||
<setting-item data-direction="row" class="vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<setting-text>消息上报数据类型</setting-text>
|
|
||||||
<setting-text data-type="secondary">如客户端无特殊需求推荐保持默认设置,两者的详细差异可参考 <a href="javascript:LiteLoader.api.openExternal('https://github.com/botuniverse/onebot-11/tree/master/message#readme');">OneBot v11 文档</a></setting-text>
|
|
||||||
</div>
|
|
||||||
<setting-select id="messagePostFormat">
|
|
||||||
<setting-option data-value="array" ${config.ob11.messagePostFormat !== 'string' ? 'is-selected' : ''}>消息段</setting-option>
|
|
||||||
<setting-option data-value="string" ${config.ob11.messagePostFormat === 'string' ? 'is-selected' : ''}>CQ码</setting-option>
|
|
||||||
</setting-select>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item data-direction="row" class="vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<div>获取文件使用base64编码</div>
|
|
||||||
<div class="tips">开启后,调用/get_image、/get_record时,获取不到url时添加一个base64字段</div>
|
|
||||||
</div>
|
|
||||||
<setting-switch id="switchFileUrl" ${config.enableLocalFile2Url ? 'is-active' : ''}></setting-switch>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item data-direction="row" class="vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<div>debug模式</div>
|
|
||||||
<div class="tips">开启后上报消息添加raw字段附带原始消息</div>
|
|
||||||
</div>
|
|
||||||
<setting-switch id="debug" ${config.debug ? 'is-active' : ''}></setting-switch>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item data-direction="row" class="vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<div>上报自身发送消息</div>
|
|
||||||
<div class="tips"></div>
|
|
||||||
</div>
|
|
||||||
<setting-switch id="reportSelfMessage" ${config.reportSelfMessage ? 'is-active' : ''}></setting-switch>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item data-direction="row" class="vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<div>日志</div>
|
|
||||||
<div class="tips">目录:${window.LiteLoader.plugins.LLOneBot.path.data}</div>
|
|
||||||
</div>
|
|
||||||
<setting-switch id="log" ${config.log ? 'is-active' : ''}></setting-switch>
|
|
||||||
</setting-item>
|
|
||||||
<setting-item data-direction="row" class="vertical-list-item">
|
|
||||||
<div>
|
|
||||||
<div>自动删除收到的文件</div>
|
|
||||||
<div class="tips">
|
|
||||||
收到文件
|
|
||||||
<input id="autoDeleteMin"
|
|
||||||
min="1" style="width: 50px"
|
|
||||||
value="${config.autoDeleteFileSecond || 60}" type="number"/>秒后自动删除
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<setting-switch id="autoDeleteFile" ${config.autoDeleteFile ? 'is-active' : ''}></setting-switch>
|
|
||||||
</setting-item>
|
|
||||||
</setting-panel>
|
|
||||||
</setting-section>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
setting-panel {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
.tips {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark){
|
|
||||||
.llonebot input {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
`
|
|
||||||
|
|
||||||
const parser = new DOMParser()
|
|
||||||
const doc = parser.parseFromString(html, 'text/html')
|
|
||||||
|
|
||||||
const getError = async () => {
|
|
||||||
const llonebotError = await window.llonebot.getError()
|
|
||||||
console.log(llonebotError)
|
|
||||||
const llonebotErrorEle = document.getElementById('llonebotError')
|
|
||||||
const ffmpegErrorEle = document.getElementById('ffmpegError')
|
|
||||||
const otherErrorEle = document.getElementById('otherError')
|
|
||||||
if (llonebotError.otherError || llonebotError.ffmpegError) {
|
|
||||||
llonebotErrorEle.style.display = ''
|
|
||||||
} else {
|
|
||||||
llonebotErrorEle.style.display = 'none'
|
|
||||||
}
|
|
||||||
if (llonebotError.ffmpegError) {
|
|
||||||
const errContentEle = doc.querySelector('#ffmpegError .err-content')
|
|
||||||
// const errContent = ffmpegErrorEle.getElementsByClassName("err-content")[0];
|
|
||||||
errContentEle.textContent = llonebotError.ffmpegError;
|
|
||||||
(ffmpegErrorEle).style.display = ''
|
|
||||||
} else {
|
|
||||||
ffmpegErrorEle.style.display = ''
|
|
||||||
}
|
|
||||||
if (llonebotError.otherError) {
|
|
||||||
const errContentEle = doc.querySelector('#otherError .err-content')
|
|
||||||
errContentEle.textContent = llonebotError.otherError
|
|
||||||
otherErrorEle.style.display = ''
|
|
||||||
} else {
|
|
||||||
otherErrorEle.style.display = 'none'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addHostEle (type: string, initValue: string = '') {
|
|
||||||
let addressEle, hostItemsEle
|
|
||||||
if (type === 'ws') {
|
|
||||||
const addressDoc = parser.parseFromString(createWsHostEleStr(initValue), 'text/html')
|
|
||||||
addressEle = addressDoc.querySelector('setting-item')
|
|
||||||
hostItemsEle = document.getElementById('wsHostItems')
|
|
||||||
} else {
|
|
||||||
const addressDoc = parser.parseFromString(createHttpHostEleStr(initValue), 'text/html')
|
|
||||||
addressEle = addressDoc.querySelector('setting-item')
|
|
||||||
hostItemsEle = document.getElementById('httpHostItems')
|
|
||||||
}
|
|
||||||
|
|
||||||
hostItemsEle.appendChild(addressEle)
|
|
||||||
}
|
|
||||||
|
|
||||||
doc.getElementById('addHttpHost').addEventListener('click', () => {
|
|
||||||
addHostEle('http')
|
|
||||||
})
|
|
||||||
doc.getElementById('addWsHost').addEventListener('click', () => {
|
|
||||||
addHostEle('ws')
|
|
||||||
})
|
|
||||||
doc.getElementById('messagePostFormat').addEventListener('selected', (e: CustomEvent) => {
|
|
||||||
config.ob11.messagePostFormat = e.detail && !isEmpty(e.detail.value) ? e.detail.value : 'array'
|
|
||||||
window.llonebot.setConfig(config)
|
|
||||||
})
|
|
||||||
|
|
||||||
function switchClick (eleId: string, configKey: string, _config = null) {
|
|
||||||
if (!_config) {
|
|
||||||
_config = config
|
|
||||||
}
|
|
||||||
doc.getElementById(eleId)?.addEventListener('click', (e) => {
|
|
||||||
const switchEle = e.target as HTMLInputElement
|
|
||||||
if (_config[configKey]) {
|
|
||||||
_config[configKey] = false
|
|
||||||
switchEle.removeAttribute('is-active')
|
|
||||||
} else {
|
|
||||||
_config[configKey] = true
|
|
||||||
switchEle.setAttribute('is-active', '')
|
|
||||||
}
|
|
||||||
// 妈蛋,手动操作DOM越写越麻烦,要不用vue算了
|
|
||||||
const keyClassMap = {
|
|
||||||
enableHttp: httpClass,
|
|
||||||
enableHttpPost: httpPostClass,
|
|
||||||
enableWs: wsClass,
|
|
||||||
enableWsReverse: reverseWSClass
|
|
||||||
}
|
|
||||||
for (const e of document.getElementsByClassName(keyClassMap[configKey])) {
|
|
||||||
(e as HTMLElement).style.display = _config[configKey] ? '' : 'none'
|
|
||||||
}
|
|
||||||
|
|
||||||
window.llonebot.setConfig(config)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
switchClick('http', 'enableHttp', config.ob11)
|
|
||||||
switchClick('httpPost', 'enableHttpPost', config.ob11)
|
|
||||||
switchClick('websocket', 'enableWs', config.ob11)
|
|
||||||
switchClick('websocketReverse', 'enableWsReverse', config.ob11)
|
|
||||||
switchClick('debug', 'debug')
|
|
||||||
switchClick('switchFileUrl', 'enableLocalFile2Url')
|
|
||||||
switchClick('reportSelfMessage', 'reportSelfMessage')
|
|
||||||
switchClick('log', 'log')
|
|
||||||
switchClick('autoDeleteFile', 'autoDeleteFile')
|
|
||||||
|
|
||||||
doc.getElementById('save')?.addEventListener('click',
|
|
||||||
() => {
|
|
||||||
const httpPortEle: HTMLInputElement = document.getElementById('httpPort') as HTMLInputElement
|
|
||||||
const httpHostEles: HTMLCollectionOf<HTMLInputElement> = document.getElementsByClassName('httpHost') as HTMLCollectionOf<HTMLInputElement>
|
|
||||||
const wsPortEle: HTMLInputElement = document.getElementById('wsPort') as HTMLInputElement
|
|
||||||
const wsHostEles: HTMLCollectionOf<HTMLInputElement> = document.getElementsByClassName('wsHost') as HTMLCollectionOf<HTMLInputElement>
|
|
||||||
const tokenEle = document.getElementById('token') as HTMLInputElement
|
|
||||||
const ffmpegPathEle = document.getElementById('ffmpegPath') as HTMLInputElement
|
|
||||||
|
|
||||||
// 获取端口和host
|
|
||||||
const httpPort = httpPortEle.value
|
|
||||||
const httpHosts: string[] = []
|
|
||||||
|
|
||||||
for (const hostEle of httpHostEles) {
|
|
||||||
const value = hostEle.value.trim()
|
|
||||||
value && httpHosts.push(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const wsPort = wsPortEle.value
|
|
||||||
const token = tokenEle.value.trim()
|
|
||||||
const wsHosts: string[] = []
|
|
||||||
|
|
||||||
for (const hostEle of wsHostEles) {
|
|
||||||
const value = hostEle.value.trim()
|
|
||||||
value && wsHosts.push(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.ob11.httpPort = parseInt(httpPort)
|
|
||||||
config.ob11.httpHosts = httpHosts
|
|
||||||
config.ob11.wsPort = parseInt(wsPort)
|
|
||||||
config.ob11.wsHosts = wsHosts
|
|
||||||
config.token = token
|
|
||||||
config.ffmpeg = ffmpegPathEle.value.trim()
|
|
||||||
window.llonebot.setConfig(config)
|
|
||||||
setTimeout(() => {
|
|
||||||
getError().then()
|
|
||||||
}, 1000)
|
|
||||||
alert('保存成功')
|
|
||||||
})
|
|
||||||
|
|
||||||
doc.getElementById('selectFFMPEG')?.addEventListener('click', () => {
|
|
||||||
window.llonebot.selectFile().then(selectPath => {
|
|
||||||
if (selectPath) {
|
|
||||||
config.ffmpeg = (document.getElementById('ffmpegPath') as HTMLInputElement).value = selectPath
|
|
||||||
// window.llonebot.setConfig(config);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// 自动保存删除文件延时时间
|
|
||||||
const autoDeleteMinEle = doc.getElementById('autoDeleteMin') as HTMLInputElement
|
|
||||||
let st = null
|
|
||||||
autoDeleteMinEle.addEventListener('change', () => {
|
|
||||||
if (st) {
|
|
||||||
clearTimeout(st)
|
|
||||||
}
|
|
||||||
st = setTimeout(() => {
|
|
||||||
console.log('auto delete file minute change')
|
|
||||||
config.autoDeleteFileSecond = parseInt(autoDeleteMinEle.value) || 1
|
|
||||||
window.llonebot.setConfig(config)
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
|
|
||||||
doc.body.childNodes.forEach(node => {
|
|
||||||
view.appendChild(node)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function init () {
|
|
||||||
const hash = location.hash
|
|
||||||
if (hash === '#/blank') {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location.hash === '#/blank') {
|
|
||||||
(window as any).navigation.addEventListener('navigatesuccess', init, { once: true })
|
|
||||||
} else {
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
onSettingWindowCreated
|
|
||||||
}
|
|
4
src/renderer/components/button.ts
Normal file
4
src/renderer/components/button.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export const SettingButton = (text: string, id?: string, type: string = 'secondary') => {
|
||||||
|
return `<setting-button ${type ? `data-type="${type}"` : ''} ${id ? `id="${id}"` : ''}>${text}</setting-button>`;
|
||||||
|
}
|
5
src/renderer/components/index.ts
Normal file
5
src/renderer/components/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from './list';
|
||||||
|
export * from './item';
|
||||||
|
export * from './button';
|
||||||
|
export * from './switch';
|
||||||
|
export * from './select';
|
10
src/renderer/components/item.ts
Normal file
10
src/renderer/components/item.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
export const SettingItem = (title: string, subtitle?: string, action?: string, id?: string, visible: boolean = true) => {
|
||||||
|
return `<setting-item ${id ? `id="${id}"` : ''} ${!visible ? 'is-hidden' : ''}>
|
||||||
|
<div>
|
||||||
|
<setting-text>${title}</setting-text>
|
||||||
|
${subtitle ? `<setting-text data-type="secondary">${subtitle}</setting-text>` : ''}
|
||||||
|
</div>
|
||||||
|
${action ? `<div>${action}</div>` : ''}
|
||||||
|
</setting-item>`;
|
||||||
|
}
|
10
src/renderer/components/list.ts
Normal file
10
src/renderer/components/list.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
export const SettingList = (items: string[], title?: string, isCollapsible: boolean = false, direction: string = 'column') => {
|
||||||
|
return `<setting-section ${title && !isCollapsible ? `data-title="${title}"` : ''}>
|
||||||
|
<setting-panel>
|
||||||
|
<setting-list ${direction ? `data-direction="${direction}"` : ''} ${isCollapsible ? 'is-collapsible' : ''} ${title && isCollapsible ? `data-title="${title}"` : ''}>
|
||||||
|
${items.join('')}
|
||||||
|
</setting-list>
|
||||||
|
</setting-panel>
|
||||||
|
</setting-section>`;
|
||||||
|
}
|
4
src/renderer/components/option.ts
Normal file
4
src/renderer/components/option.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export const SettingOption = (text: string, value?: string, isSelected: boolean = false) => {
|
||||||
|
return `<setting-option ${value ? `data-value="${value}"` : ''} ${isSelected ? 'is-selected' : ''}>${text}</setting-option>`;
|
||||||
|
}
|
11
src/renderer/components/select.ts
Normal file
11
src/renderer/components/select.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SettingOption } from "./option";
|
||||||
|
|
||||||
|
export const SettingSelect = (items: Array<{ text: string, value: string }>, configKey?: string, configValue?: any) => {
|
||||||
|
return `<setting-select ${configKey ? `data-config-key="${configKey}"` : ''}>
|
||||||
|
<div>
|
||||||
|
${items.map((e, i) => {
|
||||||
|
return SettingOption(e.text, e.value, (configKey && configValue ? configValue === e.value : i === 0));
|
||||||
|
}).join('')}
|
||||||
|
</div>
|
||||||
|
</setting-select>`;
|
||||||
|
}
|
9
src/renderer/components/switch.ts
Normal file
9
src/renderer/components/switch.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
export const SettingSwitch = (configKey?: string, isActive: boolean = false, extraData?: Record<string, string>) => {
|
||||||
|
return `<setting-switch
|
||||||
|
${configKey ? `data-config-key="${configKey}"` : ''}
|
||||||
|
${isActive ? 'is-active' : ''}
|
||||||
|
${extraData ? Object.keys(extraData).map(key => `data-${key}="${extraData[key]}"`) : ''}
|
||||||
|
>
|
||||||
|
</setting-switch>`;
|
||||||
|
}
|
282
src/renderer/index.ts
Normal file
282
src/renderer/index.ts
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
/// <reference path="../global.d.ts" />
|
||||||
|
import {
|
||||||
|
SettingButton,
|
||||||
|
SettingItem,
|
||||||
|
SettingList,
|
||||||
|
SettingSelect,
|
||||||
|
SettingSwitch
|
||||||
|
} from './components';
|
||||||
|
import StyleRaw from './style.css?raw';
|
||||||
|
|
||||||
|
// 打开设置界面时触发
|
||||||
|
|
||||||
|
async function onSettingWindowCreated(view: Element) {
|
||||||
|
window.llonebot.log("setting window created");
|
||||||
|
const isEmpty = (value: any) => value === undefined || value === null || value === '';
|
||||||
|
let config = await window.llonebot.getConfig();
|
||||||
|
let ob11Config = { ...config.ob11 };
|
||||||
|
const setConfig = (key: string, value: any) => {
|
||||||
|
const configKey = key.split('.');
|
||||||
|
|
||||||
|
if (key.indexOf('ob11') === 0) {
|
||||||
|
if (configKey.length === 2) ob11Config[configKey[1]] = value;
|
||||||
|
else ob11Config[key] = value;
|
||||||
|
} else {
|
||||||
|
if (configKey.length === 2) config[configKey[0]][configKey[1]] = value;
|
||||||
|
else config[key] = value;
|
||||||
|
|
||||||
|
window.llonebot.setConfig(config);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString([
|
||||||
|
'<div>',
|
||||||
|
`<style>${StyleRaw}</style>`,
|
||||||
|
SettingList([
|
||||||
|
SettingItem('启用 HTTP 服务', null,
|
||||||
|
SettingSwitch('ob11.enableHttp', config.ob11.enableHttp, { 'control-display-id': 'config-ob11-httpPort' }),
|
||||||
|
),
|
||||||
|
SettingItem('HTTP 服务监听端口', null,
|
||||||
|
`<div class="q-input"><input class="q-input__inner" data-config-key="ob11.httpPort" type="number" min="1" max="65534" value="${config.ob11.httpPort}" placeholder="${config.ob11.httpPort}" /></div>`,
|
||||||
|
'config-ob11-httpPort', config.ob11.enableHttp
|
||||||
|
),
|
||||||
|
SettingItem('启用 HTTP 事件上报', null,
|
||||||
|
SettingSwitch('ob11.enableHttpPost', config.ob11.enableHttpPost, { 'control-display-id': 'config-ob11-httpHosts' }),
|
||||||
|
),
|
||||||
|
`<div class="config-host-list" id="config-ob11-httpHosts" ${config.ob11.enableHttpPost ? '' : 'is-hidden'}>
|
||||||
|
<setting-item data-direction="row">
|
||||||
|
<div>
|
||||||
|
<setting-text>HTTP 事件上报地址</setting-text>
|
||||||
|
</div>
|
||||||
|
<setting-button id="config-ob11-httpHosts-add" data-type="primary">添加</setting-button>
|
||||||
|
</setting-item>
|
||||||
|
<div id="config-ob11-httpHosts-list"></div>
|
||||||
|
</div>`,
|
||||||
|
SettingItem('启用正向 WebSocket 服务', null,
|
||||||
|
SettingSwitch('ob11.enableWs', config.ob11.enableWs, { 'control-display-id': 'config-ob11-wsPort' }),
|
||||||
|
),
|
||||||
|
SettingItem('正向 WebSocket 服务监听端口', null,
|
||||||
|
`<div class="q-input"><input class="q-input__inner" data-config-key="ob11.wsPort" type="number" min="1" max="65534" value="${config.ob11.wsPort}" placeholder="${config.ob11.wsPort}" /></div>`,
|
||||||
|
'config-ob11-wsPort', config.ob11.enableWs
|
||||||
|
),
|
||||||
|
SettingItem('启用反向 WebSocket 服务', null,
|
||||||
|
SettingSwitch('ob11.enableWsReverse', config.ob11.enableWsReverse, { 'control-display-id': 'config-ob11-wsHosts' }),
|
||||||
|
),
|
||||||
|
`<div class="config-host-list" id="config-ob11-wsHosts" ${config.ob11.enableWsReverse ? '' : 'is-hidden'}>
|
||||||
|
<setting-item data-direction="row">
|
||||||
|
<div>
|
||||||
|
<setting-text>反向 WebSocket 监听地址</setting-text>
|
||||||
|
</div>
|
||||||
|
<setting-button id="config-ob11-wsHosts-add" data-type="primary">添加</setting-button>
|
||||||
|
</setting-item>
|
||||||
|
<div id="config-ob11-wsHosts-list"></div>
|
||||||
|
</div>`,
|
||||||
|
SettingItem('反向 WebSocket 服务心跳间隔',
|
||||||
|
'控制每隔多久发送一个心跳包,单位为毫秒',
|
||||||
|
`<div class="q-input"><input class="q-input__inner" data-config-key="heartInterval" type="number" min="1000" value="${config.heartInterval}" placeholder="${config.heartInterval}" /></div>`,
|
||||||
|
),
|
||||||
|
SettingItem('Access token', null,
|
||||||
|
`<div class="q-input" style="width:210px;"><input class="q-input__inner" data-config-key="token" type="text" value="${config.token}" placeholder="未设置" /></div>`,
|
||||||
|
),
|
||||||
|
SettingItem(
|
||||||
|
'消息上报格式类型',
|
||||||
|
'如客户端无特殊需求推荐保持默认设置,两者的详细差异可参考 <a href="javascript:LiteLoader.api.openExternal(\'https://github.com/botuniverse/onebot-11/tree/master/message#readme\');">OneBot v11 文档</a>',
|
||||||
|
SettingSelect([
|
||||||
|
{ text: '消息段', value: 'array' },
|
||||||
|
{ text: 'CQ码', value: 'string' },
|
||||||
|
], 'ob11.messagePostFormat', config.ob11.messagePostFormat),
|
||||||
|
),
|
||||||
|
SettingItem(
|
||||||
|
'ffmpeg 路径', `<span id="config-ffmpeg-path-text">${!isEmpty(config.ffmpeg) ? config.ffmpeg : '未指定'}</span>`,
|
||||||
|
SettingButton('选择', 'config-ffmpeg-select'),
|
||||||
|
),
|
||||||
|
SettingItem(
|
||||||
|
'', null,
|
||||||
|
SettingButton('保存', 'config-ob11-save', 'primary'),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
SettingList([
|
||||||
|
SettingItem(
|
||||||
|
'使用 Base64 编码获取文件',
|
||||||
|
'开启后,调用 /get_image、/get_record 时,获取不到 url 时添加一个 Base64 字段',
|
||||||
|
SettingSwitch('enableLocalFile2Url', config.enableLocalFile2Url),
|
||||||
|
),
|
||||||
|
SettingItem(
|
||||||
|
'调试模式',
|
||||||
|
'开启后上报信息会添加 raw 字段以附带原始信息',
|
||||||
|
SettingSwitch('debug', config.debug),
|
||||||
|
),
|
||||||
|
SettingItem(
|
||||||
|
'上报 Bot 自身发送的消息',
|
||||||
|
'慎用,可能会导致自己跟自己聊个不停',
|
||||||
|
SettingSwitch('reportSelfMessage', config.reportSelfMessage),
|
||||||
|
),
|
||||||
|
SettingItem(
|
||||||
|
'自动删除收到的文件',
|
||||||
|
'在收到文件后的指定时间内删除该文件',
|
||||||
|
SettingSwitch('autoDeleteFile', config.autoDeleteFile, { 'control-display-id': 'config-auto-delete-file-second' }),
|
||||||
|
),
|
||||||
|
SettingItem(
|
||||||
|
'自动删除文件时间',
|
||||||
|
'单位为秒',
|
||||||
|
`<div class="q-input"><input class="q-input__inner" data-config-key="autoDeleteFileSecond" type="number" min="1" value="${config.autoDeleteFileSecond}" placeholder="${config.autoDeleteFileSecond}" /></div>`,
|
||||||
|
'config-auto-delete-file-second', config.autoDeleteFile
|
||||||
|
),
|
||||||
|
SettingItem(
|
||||||
|
'写入日志',
|
||||||
|
`将日志文件写入插件的数据文件夹`,
|
||||||
|
SettingSwitch('log', config.log),
|
||||||
|
),
|
||||||
|
SettingItem(
|
||||||
|
'日志文件目录',
|
||||||
|
`${window.LiteLoader.plugins['LLOneBot'].path.data}`,
|
||||||
|
SettingButton('打开', 'config-open-log-path'),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
'</div>',
|
||||||
|
].join(''), "text/html");
|
||||||
|
|
||||||
|
// 生成反向地址列表
|
||||||
|
const buildHostListItem = (type: string, host: string, index: number) => {
|
||||||
|
const dom = {
|
||||||
|
container: document.createElement('setting-item'),
|
||||||
|
input: document.createElement('input'),
|
||||||
|
inputContainer: document.createElement('div'),
|
||||||
|
deleteBtn: document.createElement('setting-button'),
|
||||||
|
};
|
||||||
|
|
||||||
|
dom.container.classList.add('setting-host-list-item');
|
||||||
|
dom.container.dataset.direction = 'row';
|
||||||
|
|
||||||
|
dom.input.classList.add('q-input__inner');
|
||||||
|
dom.input.type = 'url';
|
||||||
|
dom.input.value = host;
|
||||||
|
dom.input.addEventListener('input', () => {
|
||||||
|
ob11Config[type][index] = dom.input.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
dom.inputContainer.classList.add('q-input');
|
||||||
|
dom.inputContainer.appendChild(dom.input);
|
||||||
|
|
||||||
|
dom.deleteBtn.innerHTML = '删除';
|
||||||
|
dom.deleteBtn.dataset.type = 'secondary';
|
||||||
|
dom.deleteBtn.addEventListener('click', () => {
|
||||||
|
ob11Config[type].splice(index, 1);
|
||||||
|
initReverseHost(type);
|
||||||
|
});
|
||||||
|
|
||||||
|
dom.container.appendChild(dom.inputContainer);
|
||||||
|
dom.container.appendChild(dom.deleteBtn);
|
||||||
|
|
||||||
|
return dom.container;
|
||||||
|
};
|
||||||
|
const buildHostList = (hosts: string[], type: string) => {
|
||||||
|
const result: HTMLElement[] = [];
|
||||||
|
|
||||||
|
hosts.forEach((host, index) => {
|
||||||
|
result.push(buildHostListItem(type, host, index));
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const addReverseHost = (type: string, doc: Document = document) => {
|
||||||
|
const hostContainerDom = doc.body.querySelector(`#config-ob11-${type}-list`);
|
||||||
|
hostContainerDom.appendChild(buildHostListItem(type, '', ob11Config[type].length));
|
||||||
|
ob11Config[type].push('');
|
||||||
|
};
|
||||||
|
const initReverseHost = (type: string, doc: Document = document) => {
|
||||||
|
const hostContainerDom = doc.body.querySelector(`#config-ob11-${type}-list`);
|
||||||
|
[ ...hostContainerDom.childNodes ].forEach(dom => dom.remove());
|
||||||
|
buildHostList(ob11Config[type], type).forEach(dom => {
|
||||||
|
hostContainerDom.appendChild(dom);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
initReverseHost('httpHosts', doc);
|
||||||
|
initReverseHost('wsHosts', doc);
|
||||||
|
|
||||||
|
doc.querySelector('#config-ob11-httpHosts-add').addEventListener('click', () => addReverseHost('httpHosts'));
|
||||||
|
doc.querySelector('#config-ob11-wsHosts-add').addEventListener('click', () => addReverseHost('wsHosts'));
|
||||||
|
|
||||||
|
doc.querySelector('#config-ffmpeg-select').addEventListener('click', () => {
|
||||||
|
window.llonebot.selectFile()
|
||||||
|
.then(path => {
|
||||||
|
if (!isEmpty(path)) {
|
||||||
|
setConfig('ffmpeg', path);
|
||||||
|
document.querySelector('#config-ffmpeg-path-text').innerHTML = path;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.querySelector('#config-open-log-path').addEventListener('click', () => {
|
||||||
|
window.LiteLoader.api.openPath(window.LiteLoader.plugins['LLOneBot'].path.data);
|
||||||
|
})
|
||||||
|
|
||||||
|
// 开关
|
||||||
|
doc.querySelectorAll('setting-switch[data-config-key]').forEach((dom: HTMLElement) => {
|
||||||
|
dom.addEventListener('click', () => {
|
||||||
|
const active = dom.getAttribute('is-active') === null;
|
||||||
|
|
||||||
|
setConfig(dom.dataset.configKey, active);
|
||||||
|
|
||||||
|
if (active) dom.setAttribute('is-active', '');
|
||||||
|
else dom.removeAttribute('is-active');
|
||||||
|
|
||||||
|
if (!isEmpty(dom.dataset.controlDisplayId)) {
|
||||||
|
const displayDom = document.querySelector(`#${dom.dataset.controlDisplayId}`);
|
||||||
|
if (active) displayDom.removeAttribute('is-hidden');
|
||||||
|
else displayDom.setAttribute('is-hidden', '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 输入框
|
||||||
|
doc.querySelectorAll('setting-item .q-input input.q-input__inner[data-config-key]').forEach((dom: HTMLInputElement) => {
|
||||||
|
dom.addEventListener('input', () => {
|
||||||
|
const Type = dom.getAttribute('type');
|
||||||
|
const configKey = dom.dataset.configKey;
|
||||||
|
const configValue = Type === 'number' ? (parseInt(dom.value) >= 1 ? parseInt(dom.value) : 1) : dom.value;
|
||||||
|
|
||||||
|
setConfig(configKey, configValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 下拉框
|
||||||
|
doc.querySelectorAll('setting-select').forEach((dom: HTMLElement) => {
|
||||||
|
dom.addEventListener('selected', (e: CustomEvent) => {
|
||||||
|
const configKey = dom.dataset.configKey;
|
||||||
|
const configValue = e.detail.value;
|
||||||
|
|
||||||
|
setConfig(configKey, configValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 保存按钮
|
||||||
|
doc.querySelector('#config-ob11-save').addEventListener('click', () => {
|
||||||
|
config.ob11 = ob11Config;
|
||||||
|
|
||||||
|
window.llonebot.setConfig(config);
|
||||||
|
alert('保存成功');
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.body.childNodes.forEach(node => {
|
||||||
|
view.appendChild(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init () {
|
||||||
|
const hash = location.hash
|
||||||
|
if (hash === '#/blank') {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location.hash === '#/blank') {
|
||||||
|
(window as any).navigation.addEventListener('navigatesuccess', init, { once: true })
|
||||||
|
} else {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
onSettingWindowCreated
|
||||||
|
}
|
64
src/renderer/style.css
Normal file
64
src/renderer/style.css
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
setting-item[is-hidden],
|
||||||
|
setting-item[is-hidden] + setting-divider {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-host-list {
|
||||||
|
width: 100%;
|
||||||
|
padding-left: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.config-host-list[is-hidden],
|
||||||
|
.config-host-list[is-hidden] + setting-divider {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
setting-item .q-input {
|
||||||
|
height: 24px;
|
||||||
|
width: 100px;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
background: var(--bg_bottom_light);
|
||||||
|
border: 1px solid var(--border_dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
setting-item .q-input .q-input__inner {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: var(--text_primary);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 0px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
setting-item .q-input input[type=number].q-input__inner::-webkit-outer-spin-button,
|
||||||
|
setting-item .q-input input[type=number].q-input__inner::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-host-list setting-item.setting-host-list-item .q-input {
|
||||||
|
width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
setting-item a {
|
||||||
|
color: var(--text-link);
|
||||||
|
}
|
||||||
|
setting-item a:hover {
|
||||||
|
color: var(--hover-link);
|
||||||
|
}
|
||||||
|
setting-item a:active,
|
||||||
|
setting-item a:visited {
|
||||||
|
color: var(--text-link);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user