mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
feat: convert wav by ffmpeg
This commit is contained in:
107
package-lock.json
generated
107
package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"audio-buffer-from": "^1.1.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"json-bigint": "^1.0.0",
|
"json-bigint": "^1.0.0",
|
||||||
@@ -2725,6 +2726,51 @@
|
|||||||
"resolved": "https://mirrors.cloud.tencent.com/npm/async/-/async-3.2.5.tgz",
|
"resolved": "https://mirrors.cloud.tencent.com/npm/async/-/async-3.2.5.tgz",
|
||||||
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
|
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/atob-lite": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/atob-lite/-/atob-lite-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY="
|
||||||
|
},
|
||||||
|
"node_modules/audio-buffer": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/audio-buffer/-/audio-buffer-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-phH+MR3G+N/PO5ZKKxx7HlU6vJwAJFa0+FCaTjr/4lUZU/RCjUTqlk3nMJTRy5+b+6cbx8m//EtwZOVI5Ht9+w==",
|
||||||
|
"dependencies": {
|
||||||
|
"audio-context": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/audio-buffer-from": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/audio-buffer-from/-/audio-buffer-from-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-8Wcira24z+26GXDFe7ZFRF1bJm1iWrz8O+XL8iNZxZjxqAPXIoK1IrJiOqStv35ASPbRjG57ZK/T0WO92MDUSg==",
|
||||||
|
"dependencies": {
|
||||||
|
"audio-buffer": "^4.0.4",
|
||||||
|
"audio-context": "^1.0.1",
|
||||||
|
"audio-format": "^2.0.0",
|
||||||
|
"is-audio-buffer": "^1.0.11",
|
||||||
|
"is-plain-obj": "^1.1.0",
|
||||||
|
"pcm-convert": "^1.6.0",
|
||||||
|
"pick-by-alias": "^1.2.0",
|
||||||
|
"string-to-arraybuffer": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/audio-context": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/audio-context/-/audio-context-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-RH3/rM74f2ITlohhjgC7oYZVS97wtv/SEjXLCzEinnrIPIDxc39m2aFc6wmdkM0NYRKo1DMleYPMAIbnTRW0eA=="
|
||||||
|
},
|
||||||
|
"node_modules/audio-format": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/audio-format/-/audio-format-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-5IA2grZhaVhpGxX6lbJm8VVh/SKQULMXXrFxuiodi0zhzDPRB8BJfieo89AclEQv4bDxZRH4lv06qNnxqkFhKQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-audio-buffer": "^1.0.11",
|
||||||
|
"is-buffer": "^1.1.5",
|
||||||
|
"is-plain-obj": "^1.1.0",
|
||||||
|
"pick-by-alias": "^1.2.0",
|
||||||
|
"sample-rate": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/babel-loader": {
|
"node_modules/babel-loader": {
|
||||||
"version": "9.1.3",
|
"version": "9.1.3",
|
||||||
"resolved": "https://mirrors.cloud.tencent.com/npm/babel-loader/-/babel-loader-9.1.3.tgz",
|
"resolved": "https://mirrors.cloud.tencent.com/npm/babel-loader/-/babel-loader-9.1.3.tgz",
|
||||||
@@ -4256,6 +4302,21 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-audio-buffer": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/is-audio-buffer/-/is-audio-buffer-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-fmPC/dizJmP4ITCsW5oTQGMJ9wZVE+A/zAe6FQo3XwgERxmXHmm3ON5XkWDAxmyxvsrDmWx3NArpSgamp/59AA=="
|
||||||
|
},
|
||||||
|
"node_modules/is-base64": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/is-base64/-/is-base64-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-WRRyllsGXJM7ZN7gPTCCQ/6wNPTRDwiWdPK66l5sJzcU/oOzcIcRRf0Rux8bkpox/1yjt0F6VJRsQOIG2qz5sg=="
|
||||||
|
},
|
||||||
|
"node_modules/is-buffer": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||||
|
},
|
||||||
"node_modules/is-core-module": {
|
"node_modules/is-core-module": {
|
||||||
"version": "2.13.1",
|
"version": "2.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
|
||||||
@@ -4298,6 +4359,14 @@
|
|||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-plain-obj": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-plain-object": {
|
"node_modules/is-plain-object": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||||
@@ -4681,6 +4750,14 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.1",
|
"version": "1.13.1",
|
||||||
"resolved": "https://mirrors.cloud.tencent.com/npm/object-inspect/-/object-inspect-1.13.1.tgz",
|
"resolved": "https://mirrors.cloud.tencent.com/npm/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||||
@@ -4813,6 +4890,17 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pcm-convert": {
|
||||||
|
"version": "1.6.5",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/pcm-convert/-/pcm-convert-1.6.5.tgz",
|
||||||
|
"integrity": "sha512-5CEspU4j8aEQ80AhNbcLfpT0apc93E6endFxahWd4sV70I6PN7LPdz8GoYm/1qr400K9bUVsVA+KxNgbFROZPw==",
|
||||||
|
"dependencies": {
|
||||||
|
"audio-format": "^2.3.2",
|
||||||
|
"is-audio-buffer": "^1.0.11",
|
||||||
|
"is-buffer": "^1.1.5",
|
||||||
|
"object-assign": "^4.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/peek-readable": {
|
"node_modules/peek-readable": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://mirrors.cloud.tencent.com/npm/peek-readable/-/peek-readable-5.0.0.tgz",
|
"resolved": "https://mirrors.cloud.tencent.com/npm/peek-readable/-/peek-readable-5.0.0.tgz",
|
||||||
@@ -4831,6 +4919,11 @@
|
|||||||
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
|
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/pick-by-alias": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/pick-by-alias/-/pick-by-alias-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-X3yysfIabh6ISgyHhVqko3NhEHs="
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://mirrors.cloud.tencent.com/npm/picocolors/-/picocolors-1.0.0.tgz",
|
"resolved": "https://mirrors.cloud.tencent.com/npm/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
@@ -5223,6 +5316,11 @@
|
|||||||
"resolved": "https://mirrors.cloud.tencent.com/npm/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://mirrors.cloud.tencent.com/npm/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/sample-rate": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/sample-rate/-/sample-rate-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-AIK0vVBiAEObmpJOxQu/WCyklnWGqzTSDII4O7nBo+SJHmfgBUiYhgV/Y3Ohz76gfSlU6R5CIAKggj+nAOLSvg=="
|
||||||
|
},
|
||||||
"node_modules/schema-utils": {
|
"node_modules/schema-utils": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||||
@@ -5454,6 +5552,15 @@
|
|||||||
"safe-buffer": "~5.2.0"
|
"safe-buffer": "~5.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string-to-arraybuffer": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://mirrors.cloud.tencent.com/npm/string-to-arraybuffer/-/string-to-arraybuffer-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-DaGZidzi93dwjQen5I2osxR9ERS/R7B1PFyufNMnzhj+fmlDQAc1DSDIJVJhgI8Oq221efIMbABUBdPHDRt43Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"atob-lite": "^2.0.0",
|
||||||
|
"is-base64": "^0.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strtok3": {
|
"node_modules/strtok3": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://mirrors.cloud.tencent.com/npm/strtok3/-/strtok3-7.0.0.tgz",
|
"resolved": "https://mirrors.cloud.tencent.com/npm/strtok3/-/strtok3-7.0.0.tgz",
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
"json-bigint": "^1.0.0",
|
"json-bigint": "^1.0.0",
|
||||||
"music-metadata": "^8.1.4",
|
"music-metadata": "^8.1.4",
|
||||||
"silk-wasm": "^3.2.3",
|
"silk-wasm": "^3.2.3",
|
||||||
|
"utf-8-validate": "^6.0.3",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.16.0"
|
||||||
},
|
},
|
||||||
@@ -34,7 +35,6 @@
|
|||||||
"@types/ws": "^8.5.10",
|
"@types/ws": "^8.5.10",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
"copy-webpack-plugin": "^12.0.2",
|
"copy-webpack-plugin": "^12.0.2",
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"electron": "^29.0.1",
|
"electron": "^29.0.1",
|
||||||
"ts-loader": "^9.5.0",
|
"ts-loader": "^9.5.0",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
|
@@ -3,3 +3,5 @@ import {Peer} from "../ntqqapi/ntcall";
|
|||||||
export const CHANNEL_GET_CONFIG = "llonebot_get_config"
|
export const CHANNEL_GET_CONFIG = "llonebot_get_config"
|
||||||
export const CHANNEL_SET_CONFIG = "llonebot_set_config"
|
export const CHANNEL_SET_CONFIG = "llonebot_set_config"
|
||||||
export const CHANNEL_LOG = "llonebot_log"
|
export const CHANNEL_LOG = "llonebot_log"
|
||||||
|
export const CHANNEL_ERROR = "llonebot_error"
|
||||||
|
export const CHANNEL_SELECT_FILE = "llonebot_select_ffmpeg"
|
@@ -1,10 +1,17 @@
|
|||||||
import {NTQQApi} from '../ntqqapi/ntcall';
|
import {NTQQApi} from '../ntqqapi/ntcall';
|
||||||
import {Friend, FriendRequest, Group, GroupMember, GroupNotify, RawMessage, SelfInfo} from "../ntqqapi/types";
|
import {Friend, FriendRequest, Group, GroupMember, GroupNotify, RawMessage, SelfInfo} from "../ntqqapi/types";
|
||||||
|
import {LLOneBotError} from "./types";
|
||||||
|
|
||||||
export let groups: Group[] = []
|
export let groups: Group[] = []
|
||||||
export let friends: Friend[] = []
|
export let friends: Friend[] = []
|
||||||
export let msgHistory: Record<string, RawMessage> = {} // msgId: RawMessage
|
export let msgHistory: Record<string, RawMessage> = {} // msgId: RawMessage
|
||||||
|
export const version = "3.7.0"
|
||||||
|
export let groupNotifies: Map<string, GroupNotify> = new Map<string, GroupNotify>();
|
||||||
|
export let friendRequests: Map<number, FriendRequest> = new Map<number, FriendRequest>();
|
||||||
|
export let llonebotError: LLOneBotError = {
|
||||||
|
ffmpegError: "",
|
||||||
|
otherError: ""
|
||||||
|
}
|
||||||
let globalMsgId = Math.floor(Date.now() / 1000);
|
let globalMsgId = Math.floor(Date.now() / 1000);
|
||||||
|
|
||||||
export function addHistoryMsg(msg: RawMessage): boolean {
|
export function addHistoryMsg(msg: RawMessage): boolean {
|
||||||
@@ -86,7 +93,3 @@ export function getUidByUin(uin: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const version = "3.7.0"
|
|
||||||
|
|
||||||
export let groupNotifies: Map<string, GroupNotify> = new Map<string, GroupNotify>();
|
|
||||||
export let friendRequests: Map<number, FriendRequest> = new Map<number, FriendRequest>();
|
|
@@ -19,4 +19,10 @@ export interface Config {
|
|||||||
reportSelfMessage?: boolean
|
reportSelfMessage?: boolean
|
||||||
log?: boolean
|
log?: boolean
|
||||||
autoDeleteFile?: boolean
|
autoDeleteFile?: boolean
|
||||||
|
ffmpeg?: string // ffmpeg路径
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LLOneBotError = {
|
||||||
|
ffmpegError?: string
|
||||||
|
otherError?: string
|
||||||
}
|
}
|
@@ -138,17 +138,18 @@ export function mergeNewProperties(newObj: any, oldObj: any) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkFFMPEG(): boolean {
|
export function checkFFMPEG(newPath: string=null): Promise<boolean> {
|
||||||
let ffmpegExist: boolean;
|
return new Promise((resolve, reject) => {
|
||||||
exec('ffmpeg -version', (error, stdout, stderr) => {
|
const ffmpegPath = newPath || 'ffmpeg'
|
||||||
if (error) {
|
exec(ffmpegPath + ' -version', (error, stdout, stderr) => {
|
||||||
log('ffmpeg is not installed or not found in PATH:', error);
|
if (error) {
|
||||||
ffmpegExist = false
|
log('ffmpeg is not installed or not found in PATH:', error);
|
||||||
}
|
resolve(false)
|
||||||
log('ffmpeg is installed. Version info:', stdout);
|
}
|
||||||
ffmpegExist = true;
|
log('ffmpeg is installed. Version info:', stdout);
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return ffmpegExist;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function encodeSilk(filePath: string) {
|
export async function encodeSilk(filePath: string) {
|
||||||
@@ -214,28 +215,37 @@ export async function encodeSilk(filePath: string) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const fileName = path.basename(filePath);
|
const fileName = path.basename(filePath);
|
||||||
const pcm = fs.readFileSync(filePath);
|
|
||||||
const pttPath = path.join(CONFIG_DIR, uuidv4());
|
const pttPath = path.join(CONFIG_DIR, uuidv4());
|
||||||
if (getFileHeader(filePath) !== "02232153494c4b") {
|
if (getFileHeader(filePath) !== "02232153494c4b") {
|
||||||
log(`语音文件${filePath}需要转换`)
|
log(`语音文件${filePath}需要转换`)
|
||||||
const isWav = await isWavFile(filePath);
|
const isWav = await isWavFile(filePath);
|
||||||
if (!isWav) {
|
if (!isWav) {
|
||||||
log(`语音文件${filePath}正在转换成wav`)
|
log(`语音文件${filePath}正在转换成wav`)
|
||||||
let voiceData = await fsp.readFile(filePath)
|
// let voiceData = await fsp.readFile(filePath)
|
||||||
const wavPath = pttPath + ".wav"
|
const wavPath = pttPath + ".wav"
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
|
const ffmpegPath = getConfigUtil().getConfig().ffmpeg;
|
||||||
|
if (ffmpegPath){
|
||||||
|
ffmpeg.setFfmpegPath(ffmpegPath);
|
||||||
|
}
|
||||||
ffmpeg(filePath).toFormat("wav").on('end', function () {
|
ffmpeg(filePath).toFormat("wav").on('end', function () {
|
||||||
// console.log('转换完成');
|
log('wav转换完成');
|
||||||
filePath = wavPath
|
|
||||||
})
|
})
|
||||||
.on('error', function (err) {
|
.on('error', function (err) {
|
||||||
// console.log('转换出错: ' + err.message);
|
log(`wav转换出错: `, err.message,);
|
||||||
|
reject(err);
|
||||||
})
|
})
|
||||||
.save(wavPath);
|
.save(wavPath)
|
||||||
|
.on("end", ()=>{
|
||||||
|
filePath = wavPath
|
||||||
|
resolve(wavPath);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
const sampleRate = await getAudioSampleRate(filePath) || 44100;
|
const sampleRate = await getAudioSampleRate(filePath) || 44100;
|
||||||
|
const pcm = fs.readFileSync(filePath);
|
||||||
const silk = await encode(pcm, sampleRate);
|
const silk = await encode(pcm, sampleRate);
|
||||||
fs.writeFileSync(pttPath, silk.data);
|
fs.writeFileSync(pttPath, silk.data);
|
||||||
|
fs.unlink(wavPath, (err) => {});
|
||||||
log(`语音文件${filePath}转换成功!`, pttPath)
|
log(`语音文件${filePath}转换成功!`, pttPath)
|
||||||
return {
|
return {
|
||||||
converted: true,
|
converted: true,
|
||||||
@@ -243,6 +253,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
duration: silk.duration,
|
duration: silk.duration,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
const pcm = fs.readFileSync(filePath);
|
||||||
const duration = getDuration(pcm);
|
const duration = getDuration(pcm);
|
||||||
return {
|
return {
|
||||||
converted: false,
|
converted: false,
|
||||||
|
105
src/main/main.ts
105
src/main/main.ts
@@ -1,17 +1,24 @@
|
|||||||
// 运行在 Electron 主进程 下的插件入口
|
// 运行在 Electron 主进程 下的插件入口
|
||||||
|
|
||||||
import {BrowserWindow, ipcMain} from 'electron';
|
import {BrowserWindow, dialog, ipcMain} from 'electron';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import {Config} from "../common/types";
|
import {Config} from "../common/types";
|
||||||
import {CHANNEL_GET_CONFIG, CHANNEL_LOG, CHANNEL_SET_CONFIG,} from "../common/channels";
|
import {
|
||||||
|
CHANNEL_ERROR,
|
||||||
|
CHANNEL_GET_CONFIG,
|
||||||
|
CHANNEL_LOG,
|
||||||
|
CHANNEL_SELECT_FILE,
|
||||||
|
CHANNEL_SET_CONFIG,
|
||||||
|
} from "../common/channels";
|
||||||
import {ob11WebsocketServer} from "../onebot11/server/ws/WebsocketServer";
|
import {ob11WebsocketServer} from "../onebot11/server/ws/WebsocketServer";
|
||||||
import {CONFIG_DIR, getConfigUtil, log} from "../common/utils";
|
import {checkFFMPEG, CONFIG_DIR, getConfigUtil, log} from "../common/utils";
|
||||||
import {
|
import {
|
||||||
addHistoryMsg,
|
addHistoryMsg,
|
||||||
friendRequests,
|
friendRequests,
|
||||||
getGroup,
|
getGroup,
|
||||||
getGroupMember,
|
getGroupMember,
|
||||||
groupNotifies,
|
groupNotifies,
|
||||||
|
llonebotError,
|
||||||
msgHistory,
|
msgHistory,
|
||||||
selfInfo
|
selfInfo
|
||||||
} from "../common/data";
|
} from "../common/data";
|
||||||
@@ -35,6 +42,7 @@ import {OB11GroupAdminNoticeEvent} from "../onebot11/event/notice/OB11GroupAdmin
|
|||||||
import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecreaseEvent";
|
import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecreaseEvent";
|
||||||
import {OB11GroupRequestEvent} from "../onebot11/event/request/OB11GroupRequest";
|
import {OB11GroupRequestEvent} from "../onebot11/event/request/OB11GroupRequest";
|
||||||
import {OB11FriendRequestEvent} from "../onebot11/event/request/OB11FriendRequest";
|
import {OB11FriendRequestEvent} from "../onebot11/event/request/OB11FriendRequest";
|
||||||
|
import * as path from "node:path";
|
||||||
|
|
||||||
|
|
||||||
let running = false;
|
let running = false;
|
||||||
@@ -43,13 +51,48 @@ let running = false;
|
|||||||
// 加载插件时触发
|
// 加载插件时触发
|
||||||
function onLoad() {
|
function onLoad() {
|
||||||
log("llonebot main onLoad");
|
log("llonebot main onLoad");
|
||||||
|
|
||||||
|
ipcMain.handle(CHANNEL_SELECT_FILE, async (event, arg) => {
|
||||||
|
const selectPath = new Promise<string>((resolve, reject) => {
|
||||||
|
dialog
|
||||||
|
.showOpenDialog({
|
||||||
|
title: "请选择ffmpeg",
|
||||||
|
properties: ["openFile"],
|
||||||
|
buttonLabel: "确定",
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
log("选择文件", result);
|
||||||
|
if (!result.canceled) {
|
||||||
|
const _selectPath = path.join(result.filePaths[0]);
|
||||||
|
resolve(_selectPath);
|
||||||
|
// let config = getConfigUtil().getConfig()
|
||||||
|
// config.ffmpeg = path.join(result.filePaths[0]);
|
||||||
|
// getConfigUtil().setConfig(config);
|
||||||
|
}
|
||||||
|
resolve("")
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
return await selectPath;
|
||||||
|
} catch (e) {
|
||||||
|
log("选择文件出错", e)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
})
|
||||||
if (!fs.existsSync(CONFIG_DIR)) {
|
if (!fs.existsSync(CONFIG_DIR)) {
|
||||||
fs.mkdirSync(CONFIG_DIR, {recursive: true});
|
fs.mkdirSync(CONFIG_DIR, {recursive: true});
|
||||||
}
|
}
|
||||||
ipcMain.handle(CHANNEL_GET_CONFIG, (event: any, arg: any) => {
|
ipcMain.handle(CHANNEL_ERROR, (event, arg) => {
|
||||||
return getConfigUtil().getConfig()
|
return llonebotError;
|
||||||
})
|
})
|
||||||
ipcMain.on(CHANNEL_SET_CONFIG, (event: any, arg: Config) => {
|
ipcMain.handle(CHANNEL_GET_CONFIG, async (event, arg) => {
|
||||||
|
const config = getConfigUtil().getConfig()
|
||||||
|
return config;
|
||||||
|
})
|
||||||
|
ipcMain.on(CHANNEL_SET_CONFIG, (event, arg: Config) => {
|
||||||
let oldConfig = getConfigUtil().getConfig();
|
let oldConfig = getConfigUtil().getConfig();
|
||||||
getConfigUtil().setConfig(arg)
|
getConfigUtil().setConfig(arg)
|
||||||
if (arg.ob11.httpPort != oldConfig.ob11.httpPort && arg.ob11.enableHttp) {
|
if (arg.ob11.httpPort != oldConfig.ob11.httpPort && arg.ob11.enableHttp) {
|
||||||
@@ -94,13 +137,20 @@ function onLoad() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查ffmpeg
|
||||||
|
if (arg.ffmpeg) {
|
||||||
|
checkFFMPEG(arg.ffmpeg).then(success => {
|
||||||
|
llonebotError.ffmpegError = ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on(CHANNEL_LOG, (event: any, arg: any) => {
|
ipcMain.on(CHANNEL_LOG, (event, arg) => {
|
||||||
log(arg);
|
log(arg);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function postReceiveMsg(msgList: RawMessage[]) {
|
function postReceiveMsg(msgList: RawMessage[]) {
|
||||||
const {debug, reportSelfMessage} = getConfigUtil().getConfig();
|
const {debug, reportSelfMessage} = getConfigUtil().getConfig();
|
||||||
for (let message of msgList) {
|
for (let message of msgList) {
|
||||||
@@ -186,7 +236,7 @@ function onLoad() {
|
|||||||
let notify: GroupNotifies;
|
let notify: GroupNotifies;
|
||||||
try {
|
try {
|
||||||
notify = await NTQQApi.getGroupNotifies();
|
notify = await NTQQApi.getGroupNotifies();
|
||||||
}catch (e) {
|
} catch (e) {
|
||||||
// log("获取群通知详情失败", e);
|
// log("获取群通知详情失败", e);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -197,12 +247,12 @@ function onLoad() {
|
|||||||
for (const notify of notifies) {
|
for (const notify of notifies) {
|
||||||
const notifyTime = parseInt(notify.seq) / 1000
|
const notifyTime = parseInt(notify.seq) / 1000
|
||||||
log(`加群通知时间${notifyTime}`, `LLOneBot启动时间${startTime}`);
|
log(`加群通知时间${notifyTime}`, `LLOneBot启动时间${startTime}`);
|
||||||
if ( notifyTime < startTime){
|
if (notifyTime < startTime) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const member1 = await getGroupMember(notify.group.groupCode, null, notify.user1.uid);
|
const member1 = await getGroupMember(notify.group.groupCode, null, notify.user1.uid);
|
||||||
let member2: GroupMember;
|
let member2: GroupMember;
|
||||||
if (notify.user2.uid){
|
if (notify.user2.uid) {
|
||||||
member2 = await getGroupMember(notify.group.groupCode, null, notify.user2.uid);
|
member2 = await getGroupMember(notify.group.groupCode, null, notify.user2.uid);
|
||||||
}
|
}
|
||||||
if ([GroupNotifyTypes.ADMIN_SET, GroupNotifyTypes.ADMIN_UNSET].includes(notify.type)) {
|
if ([GroupNotifyTypes.ADMIN_SET, GroupNotifyTypes.ADMIN_UNSET].includes(notify.type)) {
|
||||||
@@ -210,22 +260,19 @@ function onLoad() {
|
|||||||
let groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent()
|
let groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent()
|
||||||
groupAdminNoticeEvent.group_id = parseInt(notify.group.groupCode);
|
groupAdminNoticeEvent.group_id = parseInt(notify.group.groupCode);
|
||||||
log("开始获取变动的管理员")
|
log("开始获取变动的管理员")
|
||||||
if(member1){
|
if (member1) {
|
||||||
log("变动管理员获取成功")
|
log("变动管理员获取成功")
|
||||||
groupAdminNoticeEvent.user_id = parseInt(member1.uin);
|
groupAdminNoticeEvent.user_id = parseInt(member1.uin);
|
||||||
groupAdminNoticeEvent.sub_type = notify.type == GroupNotifyTypes.ADMIN_UNSET ? "unset" : "set";
|
groupAdminNoticeEvent.sub_type = notify.type == GroupNotifyTypes.ADMIN_UNSET ? "unset" : "set";
|
||||||
postOB11Event(groupAdminNoticeEvent, true);
|
postOB11Event(groupAdminNoticeEvent, true);
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
log("获取群通知的成员信息失败", notify, getGroup(notify.group.groupCode));
|
log("获取群通知的成员信息失败", notify, getGroup(notify.group.groupCode));
|
||||||
}
|
}
|
||||||
}
|
} else if (notify.type == GroupNotifyTypes.MEMBER_EXIT) {
|
||||||
else if (notify.type == GroupNotifyTypes.MEMBER_EXIT){
|
|
||||||
log("有成员退出通知");
|
log("有成员退出通知");
|
||||||
let groupDecreaseEvent = new OB11GroupDecreaseEvent(parseInt(notify.group.groupCode), parseInt(member1.uin))
|
let groupDecreaseEvent = new OB11GroupDecreaseEvent(parseInt(notify.group.groupCode), parseInt(member1.uin))
|
||||||
// postEvent(groupDecreaseEvent, true);
|
// postEvent(groupDecreaseEvent, true);
|
||||||
}
|
} else if ([GroupNotifyTypes.JOIN_REQUEST].includes(notify.type)) {
|
||||||
else if ([GroupNotifyTypes.JOIN_REQUEST].includes(notify.type)){
|
|
||||||
log("有加群请求");
|
log("有加群请求");
|
||||||
groupNotifies[notify.seq] = notify;
|
groupNotifies[notify.seq] = notify;
|
||||||
let groupRequestEvent = new OB11GroupRequestEvent();
|
let groupRequestEvent = new OB11GroupRequestEvent();
|
||||||
@@ -233,7 +280,7 @@ function onLoad() {
|
|||||||
let requestQQ = ""
|
let requestQQ = ""
|
||||||
try {
|
try {
|
||||||
requestQQ = (await NTQQApi.getUserDetailInfo(notify.user1.uid)).uin;
|
requestQQ = (await NTQQApi.getUserDetailInfo(notify.user1.uid)).uin;
|
||||||
}catch (e) {
|
} catch (e) {
|
||||||
log("获取加群人QQ号失败", e)
|
log("获取加群人QQ号失败", e)
|
||||||
}
|
}
|
||||||
groupRequestEvent.user_id = parseInt(requestQQ) || 0;
|
groupRequestEvent.user_id = parseInt(requestQQ) || 0;
|
||||||
@@ -243,22 +290,22 @@ function onLoad() {
|
|||||||
postOB11Event(groupRequestEvent);
|
postOB11Event(groupRequestEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch (e) {
|
} catch (e) {
|
||||||
log("解析群通知失败", e.stack);
|
log("解析群通知失败", e.stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
registerReceiveHook<FriendRequestNotify>(ReceiveCmd.FRIEND_REQUEST, async (payload) => {
|
registerReceiveHook<FriendRequestNotify>(ReceiveCmd.FRIEND_REQUEST, async (payload) => {
|
||||||
for(const req of payload.data.buddyReqs){
|
for (const req of payload.data.buddyReqs) {
|
||||||
if (req.isUnread && !friendRequests[req.sourceId] && (parseInt(req.reqTime) > startTime / 1000)){
|
if (req.isUnread && !friendRequests[req.sourceId] && (parseInt(req.reqTime) > startTime / 1000)) {
|
||||||
friendRequests[req.sourceId] = req;
|
friendRequests[req.sourceId] = req;
|
||||||
log("有新的好友请求", req);
|
log("有新的好友请求", req);
|
||||||
let friendRequestEvent = new OB11FriendRequestEvent();
|
let friendRequestEvent = new OB11FriendRequestEvent();
|
||||||
try{
|
try {
|
||||||
let requester = await NTQQApi.getUserDetailInfo(req.friendUid)
|
let requester = await NTQQApi.getUserDetailInfo(req.friendUid)
|
||||||
friendRequestEvent.user_id = parseInt(requester.uin);
|
friendRequestEvent.user_id = parseInt(requester.uin);
|
||||||
}catch (e) {
|
} catch (e) {
|
||||||
log("获取加好友者QQ号失败", e);
|
log("获取加好友者QQ号失败", e);
|
||||||
}
|
}
|
||||||
friendRequestEvent.flag = req.sourceId.toString();
|
friendRequestEvent.flag = req.sourceId.toString();
|
||||||
@@ -268,12 +315,20 @@ function onLoad() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let startTime = 0;
|
let startTime = 0;
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
startTime = Date.now();
|
startTime = Date.now();
|
||||||
startReceiveHook().then();
|
startReceiveHook().then();
|
||||||
NTQQApi.getGroups(true).then()
|
NTQQApi.getGroups(true).then()
|
||||||
const config = getConfigUtil().getConfig()
|
const config = getConfigUtil().getConfig()
|
||||||
|
// 检查ffmpeg
|
||||||
|
checkFFMPEG(config.ffmpeg).then(exist => {
|
||||||
|
if (!exist) {
|
||||||
|
llonebotError.ffmpegError = `环境变量${process.env.PATH}中不存在ffmpeg,音频只能发送wav和silk`
|
||||||
|
}
|
||||||
|
})
|
||||||
if (config.ob11.enableHttp) {
|
if (config.ob11.enableHttp) {
|
||||||
try {
|
try {
|
||||||
ob11HTTPServer.start(config.ob11.httpPort)
|
ob11HTTPServer.start(config.ob11.httpPort)
|
||||||
@@ -309,7 +364,7 @@ function onLoad() {
|
|||||||
selfInfo.nick = userInfo.nick;
|
selfInfo.nick = userInfo.nick;
|
||||||
} else {
|
} else {
|
||||||
getSelfNickCount++;
|
getSelfNickCount++;
|
||||||
if (getSelfNickCount < 10){
|
if (getSelfNickCount < 10) {
|
||||||
return setTimeout(init, 1000);
|
return setTimeout(init, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import {
|
|||||||
} from "./types";
|
} from "./types";
|
||||||
import {NTQQApi} from "./ntcall";
|
import {NTQQApi} from "./ntcall";
|
||||||
import {encodeSilk} from "../common/utils";
|
import {encodeSilk} from "../common/utils";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
|
||||||
export class SendMsgElementConstructor {
|
export class SendMsgElementConstructor {
|
||||||
@@ -84,7 +85,7 @@ export class SendMsgElementConstructor {
|
|||||||
// log("生成语音", silkPath, duration);
|
// log("生成语音", silkPath, duration);
|
||||||
const {md5, fileName, path, fileSize} = await NTQQApi.uploadFile(silkPath, ElementType.PTT);
|
const {md5, fileName, path, fileSize} = await NTQQApi.uploadFile(silkPath, ElementType.PTT);
|
||||||
if (converted){
|
if (converted){
|
||||||
// fs.unlink(silkPath, ()=>{});
|
fs.unlink(silkPath, ()=>{});
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.PTT,
|
elementType: ElementType.PTT,
|
||||||
|
@@ -1,7 +1,14 @@
|
|||||||
// Electron 主进程 与 渲染进程 交互的桥梁
|
// Electron 主进程 与 渲染进程 交互的桥梁
|
||||||
|
|
||||||
import {Config} from "./common/types";
|
import {Config, LLOneBotError} from "./common/types";
|
||||||
import {CHANNEL_GET_CONFIG, CHANNEL_LOG, CHANNEL_SET_CONFIG,} from "./common/channels";
|
import {
|
||||||
|
CHANNEL_ERROR,
|
||||||
|
CHANNEL_GET_CONFIG,
|
||||||
|
CHANNEL_LOG,
|
||||||
|
CHANNEL_SELECT_FILE,
|
||||||
|
CHANNEL_SET_CONFIG,
|
||||||
|
} from "./common/channels";
|
||||||
|
|
||||||
const {contextBridge} = require("electron");
|
const {contextBridge} = require("electron");
|
||||||
const {ipcRenderer} = require('electron');
|
const {ipcRenderer} = require('electron');
|
||||||
|
|
||||||
@@ -12,9 +19,15 @@ const llonebot = {
|
|||||||
setConfig: (config: Config) => {
|
setConfig: (config: Config) => {
|
||||||
ipcRenderer.send(CHANNEL_SET_CONFIG, config);
|
ipcRenderer.send(CHANNEL_SET_CONFIG, config);
|
||||||
},
|
},
|
||||||
getConfig: async () => {
|
getConfig: async (): Promise<Config> => {
|
||||||
return ipcRenderer.invoke(CHANNEL_GET_CONFIG);
|
return ipcRenderer.invoke(CHANNEL_GET_CONFIG);
|
||||||
},
|
},
|
||||||
|
getError: async (): Promise<LLOneBotError> => {
|
||||||
|
return ipcRenderer.invoke(CHANNEL_ERROR);
|
||||||
|
},
|
||||||
|
selectFile: (): Promise<string> => {
|
||||||
|
return ipcRenderer.invoke(CHANNEL_SELECT_FILE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LLOneBot = typeof llonebot;
|
export type LLOneBot = typeof llonebot;
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
// 打开设置界面时触发
|
// 打开设置界面时触发
|
||||||
|
|
||||||
async function onSettingWindowCreated(view: Element) {
|
async function onSettingWindowCreated(view: Element) {
|
||||||
window.llonebot.log("setting window created");
|
window.llonebot.log("setting window created");
|
||||||
const isEmpty = (value: any) => value === undefined || value === null || value === '';
|
const isEmpty = (value: any) => value === undefined || value === null || value === '';
|
||||||
@@ -10,7 +11,8 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
const httpPostClass = "http-post";
|
const httpPostClass = "http-post";
|
||||||
const wsClass = "ws";
|
const wsClass = "ws";
|
||||||
const reverseWSClass = "reverse-ws";
|
const reverseWSClass = "reverse-ws";
|
||||||
|
const llonebotError = await window.llonebot.getError();
|
||||||
|
window.llonebot.log("获取error" + JSON.stringify(llonebotError));
|
||||||
function createHttpHostEleStr(host: string) {
|
function createHttpHostEleStr(host: string) {
|
||||||
let eleStr = `
|
let eleStr = `
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item ${httpPostClass}">
|
<setting-item data-direction="row" class="hostItem vertical-list-item ${httpPostClass}">
|
||||||
@@ -48,6 +50,18 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
let html = `
|
let html = `
|
||||||
<div class="config_view llonebot">
|
<div class="config_view llonebot">
|
||||||
<setting-section>
|
<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-panel>
|
||||||
<setting-list class="wrap">
|
<setting-list class="wrap">
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
||||||
@@ -107,7 +121,16 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
</setting-list>
|
</setting-list>
|
||||||
</setting-panel>
|
</setting-panel>
|
||||||
<setting-panel>
|
<setting-panel>
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-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>
|
||||||
|
<setting-item data-direction="row" class="vertical-list-item">
|
||||||
<div>
|
<div>
|
||||||
<setting-text>消息上报数据类型</setting-text>
|
<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>
|
<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>
|
||||||
@@ -117,35 +140,35 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
<setting-option data-value="string" ${config.ob11.messagePostFormat === "string" ? "is-selected" : ""}>CQ码</setting-option>
|
<setting-option data-value="string" ${config.ob11.messagePostFormat === "string" ? "is-selected" : ""}>CQ码</setting-option>
|
||||||
</setting-select>
|
</setting-select>
|
||||||
</setting-item>
|
</setting-item>
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
<setting-item data-direction="row" class="vertical-list-item">
|
||||||
<div>
|
<div>
|
||||||
<div>上报文件不采用本地路径</div>
|
<div>上报文件不采用本地路径</div>
|
||||||
<div class="tips">开启后,上报文件(图片语音等)为http链接或base64编码</div>
|
<div class="tips">开启后,上报文件(图片语音等)为http链接或base64编码</div>
|
||||||
</div>
|
</div>
|
||||||
<setting-switch id="switchFileUrl" ${config.enableLocalFile2Url ? "is-active" : ""}></setting-switch>
|
<setting-switch id="switchFileUrl" ${config.enableLocalFile2Url ? "is-active" : ""}></setting-switch>
|
||||||
</setting-item>
|
</setting-item>
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
<setting-item data-direction="row" class="vertical-list-item">
|
||||||
<div>
|
<div>
|
||||||
<div>debug模式</div>
|
<div>debug模式</div>
|
||||||
<div class="tips">开启后上报消息添加raw字段附带原始消息</div>
|
<div class="tips">开启后上报消息添加raw字段附带原始消息</div>
|
||||||
</div>
|
</div>
|
||||||
<setting-switch id="debug" ${config.debug ? "is-active" : ""}></setting-switch>
|
<setting-switch id="debug" ${config.debug ? "is-active" : ""}></setting-switch>
|
||||||
</setting-item>
|
</setting-item>
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
<setting-item data-direction="row" class="vertical-list-item">
|
||||||
<div>
|
<div>
|
||||||
<div>上报自身消息</div>
|
<div>上报自身消息</div>
|
||||||
<div class="tips">慎用,不然会自己和自己聊个不停</div>
|
<div class="tips">慎用,不然会自己和自己聊个不停</div>
|
||||||
</div>
|
</div>
|
||||||
<setting-switch id="reportSelfMessage" ${config.reportSelfMessage ? "is-active" : ""}></setting-switch>
|
<setting-switch id="reportSelfMessage" ${config.reportSelfMessage ? "is-active" : ""}></setting-switch>
|
||||||
</setting-item>
|
</setting-item>
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
<setting-item data-direction="row" class="vertical-list-item">
|
||||||
<div>
|
<div>
|
||||||
<div>日志</div>
|
<div>日志</div>
|
||||||
<div class="tips">目录:${window.LiteLoader.plugins["LLOneBot"].path.data}</div>
|
<div class="tips">目录:${window.LiteLoader.plugins["LLOneBot"].path.data}</div>
|
||||||
</div>
|
</div>
|
||||||
<setting-switch id="log" ${config.log ? "is-active" : ""}></setting-switch>
|
<setting-switch id="log" ${config.log ? "is-active" : ""}></setting-switch>
|
||||||
</setting-item>
|
</setting-item>
|
||||||
<setting-item data-direction="row" class="hostItem vertical-list-item">
|
<setting-item data-direction="row" class="vertical-list-item">
|
||||||
<div>
|
<div>
|
||||||
<div>自动删除收到的文件</div>
|
<div>自动删除收到的文件</div>
|
||||||
<div class="tips">一分钟后会删除收到的图片语音</div>
|
<div class="tips">一分钟后会删除收到的图片语音</div>
|
||||||
@@ -173,6 +196,36 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(html, "text/html");
|
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 as HTMLElement).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 = "") {
|
function addHostEle(type: string, initValue: string = "") {
|
||||||
let addressEle, hostItemsEle;
|
let addressEle, hostItemsEle;
|
||||||
@@ -197,8 +250,8 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
window.llonebot.setConfig(config);
|
window.llonebot.setConfig(config);
|
||||||
})
|
})
|
||||||
|
|
||||||
function switchClick(eleId: string, configKey: string, _config=null) {
|
function switchClick(eleId: string, configKey: string, _config = null) {
|
||||||
if (!_config){
|
if (!_config) {
|
||||||
_config = config
|
_config = config
|
||||||
}
|
}
|
||||||
doc.getElementById(eleId)?.addEventListener("click", (e) => {
|
doc.getElementById(eleId)?.addEventListener("click", (e) => {
|
||||||
@@ -242,6 +295,7 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
const wsPortEle: HTMLInputElement = document.getElementById("wsPort") as HTMLInputElement;
|
const wsPortEle: HTMLInputElement = document.getElementById("wsPort") as HTMLInputElement;
|
||||||
const wsHostEles: HTMLCollectionOf<HTMLInputElement> = document.getElementsByClassName("wsHost") as HTMLCollectionOf<HTMLInputElement>;
|
const wsHostEles: HTMLCollectionOf<HTMLInputElement> = document.getElementsByClassName("wsHost") as HTMLCollectionOf<HTMLInputElement>;
|
||||||
const tokenEle = document.getElementById("token") as HTMLInputElement;
|
const tokenEle = document.getElementById("token") as HTMLInputElement;
|
||||||
|
const ffmpegPathEle = document.getElementById("ffmpegPath") as HTMLInputElement;
|
||||||
|
|
||||||
// 获取端口和host
|
// 获取端口和host
|
||||||
const httpPort = httpPortEle.value
|
const httpPort = httpPortEle.value
|
||||||
@@ -266,15 +320,25 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
config.ob11.wsPort = parseInt(wsPort);
|
config.ob11.wsPort = parseInt(wsPort);
|
||||||
config.ob11.wsHosts = wsHosts;
|
config.ob11.wsHosts = wsHosts;
|
||||||
config.token = token;
|
config.token = token;
|
||||||
|
config.ffmpeg = ffmpegPathEle.value.trim();
|
||||||
window.llonebot.setConfig(config);
|
window.llonebot.setConfig(config);
|
||||||
|
setTimeout(()=>{
|
||||||
|
getError().then();
|
||||||
|
}, 1000);
|
||||||
alert("保存成功");
|
alert("保存成功");
|
||||||
})
|
})
|
||||||
|
|
||||||
|
doc.getElementById("selectFFMPEG")?.addEventListener("click", ()=>{
|
||||||
|
window.llonebot.selectFile().then(selectPath=>{
|
||||||
|
if (selectPath){
|
||||||
|
(document.getElementById("ffmpegPath") as HTMLInputElement).value = selectPath;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
doc.body.childNodes.forEach(node => {
|
doc.body.childNodes.forEach(node => {
|
||||||
view.appendChild(node);
|
view.appendChild(node);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
// import path from "path";
|
// import path from "path";
|
||||||
|
const webpack = require('webpack');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const CopyPlugin = require('copy-webpack-plugin');
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
@@ -62,7 +63,7 @@ let config = {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
patterns: copyModules.map(m=>{
|
patterns: copyModules.map(m => {
|
||||||
m = `node_modules/${m}`
|
m = `node_modules/${m}`
|
||||||
return {
|
return {
|
||||||
from: m,
|
from: m,
|
||||||
@@ -70,6 +71,9 @@ let config = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env.FLUENTFFMPEG_COV': false,
|
||||||
|
}),
|
||||||
], // devtool: 'source-map',
|
], // devtool: 'source-map',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user