diff --git a/napcat.webui/.gitignore b/napcat.webui/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/napcat.webui/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/napcat.webui/.vscode/extensions.json b/napcat.webui/.vscode/extensions.json new file mode 100644 index 00000000..a7cea0b0 --- /dev/null +++ b/napcat.webui/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/napcat.webui/README.md b/napcat.webui/README.md new file mode 100644 index 00000000..33895ab2 --- /dev/null +++ b/napcat.webui/README.md @@ -0,0 +1,5 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/napcat.webui/package.json b/napcat.webui/package.json new file mode 100644 index 00000000..98d32e61 --- /dev/null +++ b/napcat.webui/package.json @@ -0,0 +1,31 @@ +{ + "name": "napcat.webui", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "webui:lint": "eslint . --fix", + "webui:dev": "vite", + "webui:build": "vue-tsc -b && vite build", + "webui:preview": "vite preview" + }, + "dependencies": { + "eslint-plugin-prettier": "^5.2.1", + "qrcode": "^1.5.4", + "tdesign-vue-next": "^1.10.3", + "vue": "^3.5.12", + "vue-router": "^4.4.5" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.14.0", + "@types/qrcode": "^1.5.5", + "@vitejs/plugin-vue": "^5.1.4", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-vue": "^9.31.0", + "globals": "^15.12.0", + "typescript": "~5.6.2", + "vite": "^5.4.10", + "vue-tsc": "^2.1.8" + } +} diff --git a/napcat.webui/public/logo.png b/napcat.webui/public/logo.png new file mode 100644 index 00000000..0eef6b84 Binary files /dev/null and b/napcat.webui/public/logo.png differ diff --git a/napcat.webui/public/vite.svg b/napcat.webui/public/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/napcat.webui/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/napcat.webui/src/App.vue b/napcat.webui/src/App.vue new file mode 100644 index 00000000..e5fe963f --- /dev/null +++ b/napcat.webui/src/App.vue @@ -0,0 +1,7 @@ + + + diff --git a/napcat.webui/src/assets/Sotheby.ttf b/napcat.webui/src/assets/Sotheby.ttf new file mode 100644 index 00000000..bb9630f2 Binary files /dev/null and b/napcat.webui/src/assets/Sotheby.ttf differ diff --git a/napcat.webui/src/assets/logo.png b/napcat.webui/src/assets/logo.png new file mode 100644 index 00000000..0eef6b84 Binary files /dev/null and b/napcat.webui/src/assets/logo.png differ diff --git a/napcat.webui/src/assets/vue.svg b/napcat.webui/src/assets/vue.svg new file mode 100644 index 00000000..770e9d33 --- /dev/null +++ b/napcat.webui/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/napcat.webui/src/backend/shell.ts b/napcat.webui/src/backend/shell.ts new file mode 100644 index 00000000..efc7f013 --- /dev/null +++ b/napcat.webui/src/backend/shell.ts @@ -0,0 +1,185 @@ +import { OneBotConfig } from '../../../src/onebot/config/config'; + +export class QQLoginManager { + private retCredential: string; + private readonly apiPrefix: string; + + //调试时http://127.0.0.1:6099/api 打包时 ../api + constructor(retCredential: string, apiPrefix: string = '../api') { + this.retCredential = retCredential; + this.apiPrefix = apiPrefix; + } + + // TODO: + public async GetOB11Config(): Promise { + try { + const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/GetConfig`, { + method: 'POST', + headers: { + Authorization: 'Bearer ' + this.retCredential, + 'Content-Type': 'application/json', + }, + }); + if (ConfigResponse.status == 200) { + const ConfigResponseJson = await ConfigResponse.json(); + if (ConfigResponseJson.code == 0) { + return ConfigResponseJson?.data as OneBotConfig; + } + } + } catch (error) { + console.error('Error getting OB11 config:', error); + } + return {} as OneBotConfig; + } + + public async SetOB11Config(config: OneBotConfig): Promise { + try { + const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/SetConfig`, { + method: 'POST', + headers: { + Authorization: 'Bearer ' + this.retCredential, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ config: JSON.stringify(config) }), + }); + if (ConfigResponse.status == 200) { + const ConfigResponseJson = await ConfigResponse.json(); + if (ConfigResponseJson.code == 0) { + return true; + } + } + } catch (error) { + console.error('Error setting OB11 config:', error); + } + return false; + } + + public async checkQQLoginStatus(): Promise { + try { + const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/CheckLoginStatus`, { + method: 'POST', + headers: { + Authorization: 'Bearer ' + this.retCredential, + 'Content-Type': 'application/json', + }, + }); + if (QQLoginResponse.status == 200) { + const QQLoginResponseJson = await QQLoginResponse.json(); + if (QQLoginResponseJson.code == 0) { + return QQLoginResponseJson.data.isLogin; + } + } + } catch (error) { + console.error('Error checking QQ login status:', error); + } + return false; + } + + public async checkWebUiLogined(): Promise { + try { + const LoginResponse = await fetch(`${this.apiPrefix}/auth/check`, { + method: 'POST', + headers: { + Authorization: 'Bearer ' + this.retCredential, + 'Content-Type': 'application/json', + }, + }); + if (LoginResponse.status == 200) { + const LoginResponseJson = await LoginResponse.json(); + if (LoginResponseJson.code == 0) { + return true; + } + } + } catch (error) { + console.error('Error checking web UI login status:', error); + } + return false; + } + + public async loginWithToken(token: string): Promise { + try { + const loginResponse = await fetch(`${this.apiPrefix}/auth/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ token: token }), + }); + const loginResponseJson = await loginResponse.json(); + const retCode = loginResponseJson.code; + if (retCode === 0) { + this.retCredential = loginResponseJson.data.Credential; + return this.retCredential; + } + } catch (error) { + console.error('Error logging in with token:', error); + } + return null; + } + + public async getQQLoginQrcode(): Promise { + try { + const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQQLoginQrcode`, { + method: 'POST', + headers: { + Authorization: 'Bearer ' + this.retCredential, + 'Content-Type': 'application/json', + }, + }); + if (QQLoginResponse.status == 200) { + const QQLoginResponseJson = await QQLoginResponse.json(); + if (QQLoginResponseJson.code == 0) { + return QQLoginResponseJson.data.qrcode || ''; + } + } + } catch (error) { + console.error('Error getting QQ login QR code:', error); + } + return ''; + } + + public async getQQQuickLoginList(): Promise { + try { + const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQuickLoginList`, { + method: 'POST', + headers: { + Authorization: 'Bearer ' + this.retCredential, + 'Content-Type': 'application/json', + }, + }); + if (QQLoginResponse.status == 200) { + const QQLoginResponseJson = await QQLoginResponse.json(); + if (QQLoginResponseJson.code == 0) { + return QQLoginResponseJson.data || []; + } + } + } catch (error) { + console.error('Error getting QQ quick login list:', error); + } + return []; + } + + public async setQuickLogin(uin: string): Promise<{ result: boolean; errMsg: string }> { + try { + const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/SetQuickLogin`, { + method: 'POST', + headers: { + Authorization: 'Bearer ' + this.retCredential, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ uin: uin }), + }); + if (QQLoginResponse.status == 200) { + const QQLoginResponseJson = await QQLoginResponse.json(); + if (QQLoginResponseJson.code == 0) { + return { result: true, errMsg: '' }; + } else { + return { result: false, errMsg: QQLoginResponseJson.message }; + } + } + } catch (error) { + console.error('Error setting quick login:', error); + } + return { result: false, errMsg: '接口异常' }; + } +} diff --git a/napcat.webui/src/components/Dashboard.vue b/napcat.webui/src/components/Dashboard.vue new file mode 100644 index 00000000..7d8fcb13 --- /dev/null +++ b/napcat.webui/src/components/Dashboard.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/napcat.webui/src/components/QQLogin.vue b/napcat.webui/src/components/QQLogin.vue new file mode 100644 index 00000000..13d4b674 --- /dev/null +++ b/napcat.webui/src/components/QQLogin.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/napcat.webui/src/components/WebUiLogin.vue b/napcat.webui/src/components/WebUiLogin.vue new file mode 100644 index 00000000..956f3d60 --- /dev/null +++ b/napcat.webui/src/components/WebUiLogin.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/napcat.webui/src/components/webui/Nav.vue b/napcat.webui/src/components/webui/Nav.vue new file mode 100644 index 00000000..ddf489bd --- /dev/null +++ b/napcat.webui/src/components/webui/Nav.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/napcat.webui/src/css/font.css b/napcat.webui/src/css/font.css new file mode 100644 index 00000000..e00fdda5 --- /dev/null +++ b/napcat.webui/src/css/font.css @@ -0,0 +1,6 @@ +@font-face { + font-family: 'Sotheby'; + src: url('../assets/Sotheby.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} \ No newline at end of file diff --git a/napcat.webui/src/css/style.css b/napcat.webui/src/css/style.css new file mode 100644 index 00000000..6305f260 --- /dev/null +++ b/napcat.webui/src/css/style.css @@ -0,0 +1,84 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} + +button:hover { + border-color: #646cff; +} + +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +.card { + padding: 2em; +} + +#app { + height: 100%; + width: 100%; + margin: 0; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} \ No newline at end of file diff --git a/napcat.webui/src/main.ts b/napcat.webui/src/main.ts new file mode 100644 index 00000000..7e071636 --- /dev/null +++ b/napcat.webui/src/main.ts @@ -0,0 +1,62 @@ +import { createApp } from 'vue'; +import App from './App.vue'; +import { + Button as TButton, + Input as TInput, + Form as TForm, + FormItem as TFormItem, + Select as TSelect, + Option as TOption, + Menu as TMenu, + MenuItem as TMenuItem, + Icon as TIcon, + Submenu as TSubmenu, + Col as TCol, + Row as TRow, + Card as TCard, + Divider as TDivider, + Link as TLink, + List as TList, + Alert as TAlert, + Tag as TTag, + ListItem as TListItem, + Tabs as TTabs, + TabPanel as TTabPanel, + Space as TSpace, + Checkbox as TCheckbox, + Popup as TPopup, + Dialog as TDialog, + Switch as TSwitch, +} from 'tdesign-vue-next'; +import { router } from './router'; +import 'tdesign-vue-next/es/style/index.css'; + +const app = createApp(App); +app.use(router); +app.use(TButton); +app.use(TInput); +app.use(TForm); +app.use(TFormItem); +app.use(TSelect); +app.use(TOption); +app.use(TMenu); +app.use(TMenuItem); +app.use(TIcon); +app.use(TSubmenu); +app.use(TCol); +app.use(TRow); +app.use(TCard); +app.use(TDivider); +app.use(TLink); +app.use(TList); +app.use(TAlert); +app.use(TTag); +app.use(TListItem); +app.use(TTabs); +app.use(TTabPanel); +app.use(TSpace); +app.use(TCheckbox); +app.use(TPopup); +app.use(TDialog); +app.use(TSwitch); +app.mount('#app'); diff --git a/napcat.webui/src/pages/AboutUs.vue b/napcat.webui/src/pages/AboutUs.vue new file mode 100644 index 00000000..63a45fb2 --- /dev/null +++ b/napcat.webui/src/pages/AboutUs.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/napcat.webui/src/pages/BasicInfo.vue b/napcat.webui/src/pages/BasicInfo.vue new file mode 100644 index 00000000..1f9e8418 --- /dev/null +++ b/napcat.webui/src/pages/BasicInfo.vue @@ -0,0 +1,6 @@ + diff --git a/napcat.webui/src/pages/Log.vue b/napcat.webui/src/pages/Log.vue new file mode 100644 index 00000000..bc19a827 --- /dev/null +++ b/napcat.webui/src/pages/Log.vue @@ -0,0 +1,6 @@ + diff --git a/napcat.webui/src/pages/NetWork.vue b/napcat.webui/src/pages/NetWork.vue new file mode 100644 index 00000000..a2b5119e --- /dev/null +++ b/napcat.webui/src/pages/NetWork.vue @@ -0,0 +1,244 @@ + + + + + \ No newline at end of file diff --git a/napcat.webui/src/pages/OtherConfig.vue b/napcat.webui/src/pages/OtherConfig.vue new file mode 100644 index 00000000..f2e4dc66 --- /dev/null +++ b/napcat.webui/src/pages/OtherConfig.vue @@ -0,0 +1,134 @@ + + + + + \ No newline at end of file diff --git a/napcat.webui/src/pages/network/EmptyStateComponent.vue b/napcat.webui/src/pages/network/EmptyStateComponent.vue new file mode 100644 index 00000000..6ce5e6a4 --- /dev/null +++ b/napcat.webui/src/pages/network/EmptyStateComponent.vue @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/napcat.webui/src/pages/network/HttpClientComponent.vue b/napcat.webui/src/pages/network/HttpClientComponent.vue new file mode 100644 index 00000000..b36d36e1 --- /dev/null +++ b/napcat.webui/src/pages/network/HttpClientComponent.vue @@ -0,0 +1,65 @@ + + + + + \ No newline at end of file diff --git a/napcat.webui/src/pages/network/HttpServerComponent.vue b/napcat.webui/src/pages/network/HttpServerComponent.vue new file mode 100644 index 00000000..7525536d --- /dev/null +++ b/napcat.webui/src/pages/network/HttpServerComponent.vue @@ -0,0 +1,71 @@ + + + + + \ No newline at end of file diff --git a/napcat.webui/src/pages/network/WebsocketClientComponent.vue b/napcat.webui/src/pages/network/WebsocketClientComponent.vue new file mode 100644 index 00000000..3215bcac --- /dev/null +++ b/napcat.webui/src/pages/network/WebsocketClientComponent.vue @@ -0,0 +1,68 @@ + + + + + \ No newline at end of file diff --git a/napcat.webui/src/pages/network/WebsocketServerComponent.vue b/napcat.webui/src/pages/network/WebsocketServerComponent.vue new file mode 100644 index 00000000..8aa40809 --- /dev/null +++ b/napcat.webui/src/pages/network/WebsocketServerComponent.vue @@ -0,0 +1,74 @@ + + + + + \ No newline at end of file diff --git a/napcat.webui/src/pages/new/NetWork.old.vue b/napcat.webui/src/pages/new/NetWork.old.vue new file mode 100644 index 00000000..09534da4 --- /dev/null +++ b/napcat.webui/src/pages/new/NetWork.old.vue @@ -0,0 +1,267 @@ + + + + + + \ No newline at end of file diff --git a/napcat.webui/src/router/index.ts b/napcat.webui/src/router/index.ts new file mode 100644 index 00000000..50f67c11 --- /dev/null +++ b/napcat.webui/src/router/index.ts @@ -0,0 +1,32 @@ +import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'; +import Dashboard from '../components/Dashboard.vue'; +import BasicInfo from '../pages/BasicInfo.vue'; +import AboutUs from '../pages/AboutUs.vue'; +import LogView from '../pages/Log.vue'; +import NetWork from '../pages/NetWork.vue'; +import QQLogin from '../components/QQLogin.vue'; +import WebUiLogin from '../components/WebUiLogin.vue'; +import OtherConfig from '../pages/OtherConfig.vue'; + +const routes: Array = [ + { path: '/', redirect: '/webui' }, + { path: '/webui', component: WebUiLogin, name: 'WebUiLogin' }, + { path: '/qqlogin', component: QQLogin, name: 'QQLogin' }, + { + path: '/dashboard', + component: Dashboard, + children: [ + { path: '', redirect: 'basic-info' }, + { path: 'basic-info', component: BasicInfo, name: 'BasicInfo' }, + { path: 'network-config', component: NetWork, name: 'NetWork' }, + { path: 'log-view', component: LogView, name: 'LogView' }, + { path: 'other-config', component: OtherConfig, name: 'OtherConfig' }, + { path: 'about-us', component: AboutUs, name: 'AboutUs' }, + ], + }, +]; + +export const router = createRouter({ + history: createWebHashHistory(), + routes, +}); diff --git a/napcat.webui/src/vite-env.d.ts b/napcat.webui/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/napcat.webui/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/napcat.webui/tsconfig.json b/napcat.webui/tsconfig.json new file mode 100644 index 00000000..bf12aee2 --- /dev/null +++ b/napcat.webui/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ESNext", + "jsx": "preserve", + "jsxImportSource": "vue", + "lib": [ + "DOM", + "DOM.Iterable" + ], + "baseUrl": ".", + "module": "esnext", + "moduleResolution": "bundler", + "paths": { + "@/*": [ + "src/*" + ] + }, + "resolveJsonModule": true, + "types": [ + "vite/client" + ], + "strict": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "experimentalDecorators": true, + "useDefineForClassFields": true + }, + "include": ["src"], + "exclude": ["node_modules"], + "references": [{"path": "./tsconfig.node.json"}] +} diff --git a/napcat.webui/tsconfig.node.json b/napcat.webui/tsconfig.node.json new file mode 100644 index 00000000..f026910e --- /dev/null +++ b/napcat.webui/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strictNullChecks": true + }, + "include": ["vite.config.ts"] +} diff --git a/napcat.webui/vite.config.ts b/napcat.webui/vite.config.ts new file mode 100644 index 00000000..583be2bb --- /dev/null +++ b/napcat.webui/vite.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; +import path from 'path'; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue()], + base: './', + resolve: { + alias: { + '@': path.resolve(__dirname, 'src'), + }, + }, + server: { + proxy: { + '/api': 'http://localhost:6099', + }, + }, +}); diff --git a/package.json b/package.json index 761edaf7..85512352 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,13 @@ "type": "module", "version": "4.0.3", "scripts": { - "build:framework": "vite build --mode framework", - "build:shell": "vite build --mode shell", - "build:webui": "cd ./src/webui && vite build", - "lint": "eslint --fix src/**/*.{js,ts}", + "build:framework": "npm run build:webui && vite build --mode framework", + "build:shell": "npm run build:webui && vite build --mode shell", + "build:webui": "cd napcat.webui && vite build", + "dev:framework": "vite build --mode framework", + "dev:shell": "vite build --mode shell", + "dev:webui": "cd napcat.webui && npm run webui:dev", + "lint": "eslint --fix src/**/*.{js,ts,vue}", "depend": "cd dist && npm install --omit=dev" }, "devDependencies": { diff --git a/script/checkVersion.cjs b/script/checkVersion.cjs index 2b8f1d8f..40d53f4d 100644 --- a/script/checkVersion.cjs +++ b/script/checkVersion.cjs @@ -45,7 +45,6 @@ try { sed -i "s/\\"version\\": \\"${currentVersion}\\"/\\"version\\": \\"${targetVersion}\\"/g" package.json sed -i "s/\\"version\\": \\"${manifestCurrentVersion}\\"/\\"version\\": \\"${targetVersion}\\"/g" manifest.json sed -i "s/napCatVersion = '.*'/napCatVersion = '${targetVersion}'/g" ./src/common/version.ts - sed -i "s/SettingButton(\\"V.*\\", \\"napcat-update-button\\", \\"secondary\\")/SettingButton(\\"V${targetVersion}\\", \\"napcat-update-button\\", \\"secondary\\")/g" ./static/assets/renderer.js git add . git commit -m "release: v${targetVersion}" git push -u origin main`; diff --git a/src/common/audio.ts b/src/common/audio.ts index 954cfd77..6f849d6c 100644 --- a/src/common/audio.ts +++ b/src/common/audio.ts @@ -11,8 +11,12 @@ const ALLOW_SAMPLE_RATE = [8000, 12000, 16000, 24000, 32000, 44100, 48000]; const EXIT_CODES = [0, 255]; const FFMPEG_PATH = process.env.FFMPEG_PATH ?? 'ffmpeg'; +async function getWorkerPath() { + return new URL(/* @vite-ignore */ './audio-worker.mjs', import.meta.url).href; +} + const piscina = new Piscina({ - filename: new URL('./audio-worker.mjs', import.meta.url).href, + filename: await getWorkerPath(), }); async function guessDuration(pttPath: string, logger: LogWrapper) { diff --git a/src/common/config-base.ts b/src/common/config-base.ts index 8070c79b..4593c78c 100644 --- a/src/common/config-base.ts +++ b/src/common/config-base.ts @@ -8,12 +8,12 @@ export abstract class ConfigBase { configPath: string; configData: T = {} as T; - protected constructor(name: string, core: NapCatCore, configPath: string) { + protected constructor(name: string, core: NapCatCore, configPath: string, copy_default: boolean = true) { this.name = name; this.core = core; this.configPath = configPath; fs.mkdirSync(this.configPath, { recursive: true }); - this.read(); + this.read(copy_default); } protected getKeys(): string[] | null { @@ -32,16 +32,18 @@ export abstract class ConfigBase { } } - read(): T { + read(copy_default: boolean = true): T { const logger = this.core.context.logger; const configPath = this.getConfigPath(this.core.selfInfo.uin); - if (!fs.existsSync(configPath)) { + if (!fs.existsSync(configPath) && copy_default) { try { fs.writeFileSync(configPath, fs.readFileSync(this.getConfigPath(undefined), 'utf-8')); logger.log(`[Core] [Config] 配置文件创建成功!\n`); } catch (e: any) { logger.logError.bind(logger)(`[Core] [Config] 创建配置文件时发生错误:`, e.message); } + } else if (!fs.existsSync(configPath) && !copy_default) { + fs.writeFileSync(configPath, '{}'); } try { this.configData = JSON.parse(fs.readFileSync(configPath, 'utf-8')); diff --git a/src/core/apis/file.ts b/src/core/apis/file.ts index b0cd2309..5954b657 100644 --- a/src/core/apis/file.ts +++ b/src/core/apis/file.ts @@ -410,27 +410,27 @@ export class NTQQFileApi { if (!element) { return ''; } - + const url: string = element.originImageUrl ?? ''; const md5HexStr = element.md5HexStr; const fileMd5 = element.md5HexStr; - + if (url) { const parsedUrl = new URL(IMAGE_HTTP_HOST + url); const rkeyData = await this.getRkeyData(); return this.getImageUrlFromParsedUrl(parsedUrl, rkeyData); } - + return this.getImageUrlFromMd5(fileMd5, md5HexStr); } - + private async getRkeyData() { const rkeyData = { private_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qEc3Rbib9LP4', group_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qffcqm614gds', online_rkey: false }; - + try { if (this.core.apis.PacketApi.available) { const rkey_expired_private = !this.packetRkey || this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000; @@ -447,7 +447,7 @@ export class NTQQFileApi { } catch (error: any) { this.context.logger.logError.bind(this.context.logger)('获取rkey失败', error.message); } - + if (!rkeyData.online_rkey) { try { const tempRkeyData = await this.rkeyManager.getRkey(); @@ -458,34 +458,30 @@ export class NTQQFileApi { this.context.logger.logError.bind(this.context.logger)('获取rkey失败 Fallback Old Mode', e); } } - + return rkeyData; } - + private getImageUrlFromParsedUrl(parsedUrl: URL, rkeyData: any): string { - const urlRkey = parsedUrl.searchParams.get('rkey'); const imageAppid = parsedUrl.searchParams.get('appid'); const isNTV2 = imageAppid && ['1406', '1407'].includes(imageAppid); const imageFileId = parsedUrl.searchParams.get('fileid'); - - if (isNTV2 && urlRkey) { - return IMAGE_HTTP_HOST_NT + urlRkey; - } else if (isNTV2 && rkeyData.online_rkey) { + if (isNTV2 && rkeyData.online_rkey) { const rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey; return IMAGE_HTTP_HOST_NT + `/download?appid=${imageAppid}&fileid=${imageFileId}&rkey=${rkey}`; } else if (isNTV2 && imageFileId) { const rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey; return IMAGE_HTTP_HOST + `/download?appid=${imageAppid}&fileid=${imageFileId}&rkey=${rkey}`; } - + return ''; } - + private getImageUrlFromMd5(fileMd5: string | undefined, md5HexStr: string | undefined): string { if (fileMd5 || md5HexStr) { - return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 ?? md5HexStr)!.toUpperCase()}/0`; + return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 ?? md5HexStr ?? '').toUpperCase()}/0`; } - + this.context.logger.logDebug('图片url获取失败', { fileMd5, md5HexStr }); return ''; } diff --git a/src/core/external/napcat.json b/src/core/external/napcat.json index 6abfe1e6..44952ac2 100644 --- a/src/core/external/napcat.json +++ b/src/core/external/napcat.json @@ -1,5 +1,5 @@ { - "fileLog": true, + "fileLog": false, "consoleLog": true, "fileLogLevel": "debug", "consoleLogLevel": "info", diff --git a/src/onebot/action/BaseAction.ts b/src/onebot/action/BaseAction.ts index f374d02a..a19f155e 100644 --- a/src/onebot/action/BaseAction.ts +++ b/src/onebot/action/BaseAction.ts @@ -37,13 +37,13 @@ abstract class BaseAction { }; } - public async handle(payload: PayloadType): Promise> { + public async handle(payload: PayloadType, adaptername: string): Promise> { const result = await this.check(payload); if (!result.valid) { return OB11Response.error(result.message, 400); } try { - const resData = await this._handle(payload); + const resData = await this._handle(payload, adaptername); return OB11Response.ok(resData); } catch (e: any) { this.core.context.logger.logError.bind(this.core.context.logger)('发生错误', e); @@ -51,13 +51,13 @@ abstract class BaseAction { } } - public async websocketHandle(payload: PayloadType, echo: any): Promise> { + public async websocketHandle(payload: PayloadType, echo: any, adaptername: string): Promise> { const result = await this.check(payload); if (!result.valid) { return OB11Response.error(result.message, 1400, echo); } try { - const resData = await this._handle(payload); + const resData = await this._handle(payload, adaptername); return OB11Response.ok(resData, echo); } catch (e: any) { this.core.context.logger.logError.bind(this.core.context.logger)('发生错误', e); @@ -65,7 +65,7 @@ abstract class BaseAction { } } - abstract _handle(payload: PayloadType): PromiseLike; + abstract _handle(payload: PayloadType, adaptername: string): PromiseLike; } export default BaseAction; diff --git a/src/onebot/action/go-cqhttp/GetForwardMsg.ts b/src/onebot/action/go-cqhttp/GetForwardMsg.ts index b640b2d8..7db98ba1 100644 --- a/src/onebot/action/go-cqhttp/GetForwardMsg.ts +++ b/src/onebot/action/go-cqhttp/GetForwardMsg.ts @@ -72,7 +72,7 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction { } const singleMsg = data.msgList[0]; - const resMsg = await this.obContext.apis.MsgApi.parseMessage(singleMsg, 'array');//强制array 以便处理 + const resMsg = (await this.obContext.apis.MsgApi.parseMessageV2(singleMsg))?.arrayMsg;//强制array 以便处理 if (!(resMsg?.message?.[0] as OB11MessageForward)?.data?.content) { throw new Error('找不到相关的聊天记录'); } diff --git a/src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts b/src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts index b8ce16d6..15cc53cb 100644 --- a/src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts +++ b/src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts @@ -26,7 +26,7 @@ export default class GetFriendMsgHistory extends BaseAction { actionName = ActionName.GetFriendMsgHistory; payloadSchema = SchemaData; - async _handle(payload: Payload): Promise { + async _handle(payload: Payload, adapter: string): Promise { //处理参数 const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString()); const MsgCount = +(payload.count ?? 20); @@ -45,9 +45,10 @@ export default class GetFriendMsgHistory extends BaseAction { await Promise.all(msgList.map(async msg => { msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); })); + const network = Object.values(this.obContext.configLoader.configData.network) as Array; //烘焙消息 const ob11MsgList = (await Promise.all( - msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg))) + msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'))) ).filter(msg => msg !== undefined); return { 'messages': ob11MsgList }; } diff --git a/src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts b/src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts index 7be377e6..63047433 100644 --- a/src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts +++ b/src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts @@ -26,7 +26,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction { + async _handle(payload: Payload, adapter: string): Promise { //处理参数 const isReverseOrder = typeof payload.reverseOrder === 'string' ? payload.reverseOrder === 'true' : !!payload.reverseOrder; const MsgCount = +(payload.count ?? 20); @@ -43,9 +43,11 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction { msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); })); + const network = Object.values(this.obContext.configLoader.configData.network) as Array; //烘焙消息 + const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'; const ob11MsgList = (await Promise.all( - msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg))) + msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, msgFormat))) ).filter(msg => msg !== undefined); return { 'messages': ob11MsgList }; } diff --git a/src/onebot/action/group/GetGroupEssence.ts b/src/onebot/action/group/GetGroupEssence.ts index 9e809ab4..374874b8 100644 --- a/src/onebot/action/group/GetGroupEssence.ts +++ b/src/onebot/action/group/GetGroupEssence.ts @@ -30,7 +30,9 @@ export class GetGroupEssence extends BaseAction { }; } - async _handle(payload: Payload) { + async _handle(payload: Payload, adapter: string) { + const network = Object.values(this.obContext.configLoader.configData.network) as Array; + const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'; const msglist = (await this.core.apis.WebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list); if (!msglist) { throw new Error('获取失败'); @@ -51,7 +53,7 @@ export class GetGroupEssence extends BaseAction { operator_nick: msg.add_digest_nick, message_id: message_id, operator_time: msg.add_digest_time, - content: (await this.obContext.apis.MsgApi.parseMessage(rawMessage))?.message + content: (await this.obContext.apis.MsgApi.parseMessage(rawMessage, msgFormat))?.message }; } const msgTempData = JSON.stringify({ diff --git a/src/onebot/action/msg/GetMsg.ts b/src/onebot/action/msg/GetMsg.ts index 851baf98..58f61eea 100644 --- a/src/onebot/action/msg/GetMsg.ts +++ b/src/onebot/action/msg/GetMsg.ts @@ -22,8 +22,10 @@ class GetMsg extends BaseAction { actionName = ActionName.GetMsg; payloadSchema = SchemaData; - async _handle(payload: Payload) { + async _handle(payload: Payload, adapter: string) { // log("history msg ids", Object.keys(msgHistory)); + const network = Object.values(this.obContext.configLoader.configData.network) as Array; + const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'; if (!payload.message_id) { throw Error('参数message_id不能为空'); } @@ -40,7 +42,7 @@ class GetMsg extends BaseAction { } else { msg = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgIdWithPeer?.MsgId || payload.message_id.toString()])).msgList[0]; } - const retMsg = await this.obContext.apis.MsgApi.parseMessage(msg); + const retMsg = await this.obContext.apis.MsgApi.parseMessage(msg, msgFormat); if (!retMsg) throw Error('消息为空'); try { retMsg.message_id = MessageUnique.createUniqueMsgId(peer, msg.msgId)!; diff --git a/src/onebot/action/user/GetRecentContact.ts b/src/onebot/action/user/GetRecentContact.ts index 9a9487fe..8ce2c58d 100644 --- a/src/onebot/action/user/GetRecentContact.ts +++ b/src/onebot/action/user/GetRecentContact.ts @@ -15,13 +15,16 @@ export default class GetRecentContact extends BaseAction { actionName = ActionName.GetRecentContact; payloadSchema = SchemaData; - async _handle(payload: Payload) { + async _handle(payload: Payload, adapter: string) { const ret = await this.core.apis.UserApi.getRecentContactListSnapShot(+(payload.count || 10)); + const network = Object.values(this.obContext.configLoader.configData.network) as Array; + //烘焙消息 + const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'; return await Promise.all(ret.info.changedList.map(async (t) => { const FastMsg = await this.core.apis.MsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]); if (FastMsg.msgList.length > 0) { //扩展ret.info.changedList - const lastestMsg = await this.obContext.apis.MsgApi.parseMessage(FastMsg.msgList[0]); + const lastestMsg = await this.obContext.apis.MsgApi.parseMessage(FastMsg.msgList[0], msgFormat); return { lastestMsg: lastestMsg, peerUin: t.peerUin, diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index 6058f508..85299bb5 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -368,7 +368,7 @@ export class OneBotMsgApi { multiMsgItem.parentMsgPeer = parentMsgPeer; multiMsgItem.parentMsgIdList = msg.parentMsgIdList; multiMsgItem.id = MessageUnique.createUniqueMsgId(parentMsgPeer, multiMsgItem.msgId); //该ID仅用查看 无法调用 - return await this.parseMessage(multiMsgItem); + return await this.parseMessage(multiMsgItem, 'array'); }, ))).filter(item => item !== undefined), }, @@ -693,7 +693,16 @@ export class OneBotMsgApi { async parseMessage( msg: RawMessage, - messagePostFormat: string = this.obContext.configLoader.configData.messagePostFormat, + messagePostFormat: string, + ) { + if (messagePostFormat === 'string') { + return (await this.parseMessageV2(msg))?.stringMsg; + } + return (await this.parseMessageV2(msg))?.arrayMsg; + } + + async parseMessageV2( + msg: RawMessage, ) { if (msg.senderUin == '0' || msg.senderUin == '') return; if (msg.peerUin == '0' || msg.peerUin == '') return; @@ -714,8 +723,8 @@ export class OneBotMsgApi { raw_message: '', font: 14, sub_type: 'friend', - message: messagePostFormat === 'string' ? '' : [], - message_format: messagePostFormat === 'string' ? 'string' : 'array', + message: [], + message_format: 'array', post_type: this.core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE, }; if (this.core.selfInfo.uin == msg.senderUin) { @@ -785,17 +794,17 @@ export class OneBotMsgApi { }).map((entry) => (>entry).value).filter(value => value != null); const msgAsCQCode = validSegments.map(msg => encodeCQCode(msg)).join('').trim(); - - if (messagePostFormat === 'string') { - resMsg.message = msgAsCQCode; - resMsg.raw_message = msgAsCQCode; - } else { - resMsg.message = validSegments; - resMsg.raw_message = msgAsCQCode; - } - return resMsg; + resMsg.message = validSegments; + resMsg.raw_message = msgAsCQCode; + let stringMsg = structuredClone(resMsg); + stringMsg = await this.importArrayTostringMsg(stringMsg); + return { stringMsg: stringMsg, arrayMsg: resMsg }; + } + async importArrayTostringMsg(msg: OB11Message) { + msg.message_format = 'string'; + msg.message = msg.raw_message; + return msg; } - async createSendElements( messageData: OB11MessageData[], peer: Peer, diff --git a/src/onebot/config/config.ts b/src/onebot/config/config.ts new file mode 100644 index 00000000..7938106f --- /dev/null +++ b/src/onebot/config/config.ts @@ -0,0 +1,206 @@ +interface v1Config { + http: { + enable: boolean; + host: string; + port: number; + secret: string; + enableHeart: boolean; + enablePost: boolean; + postUrls: string[]; + }; + ws: { + enable: boolean; + host: string; + port: number; + }; + reverseWs: { + enable: boolean; + urls: string[]; + }; + debug: boolean; + heartInterval: number; + messagePostFormat: string; + enableLocalFile2Url: boolean; + musicSignUrl: string; + reportSelfMessage: boolean; + token: string; +} + +export interface AdapterConfig { + name: string; + enable: boolean; + [key: string]: any; +} + +const createDefaultAdapterConfig = (config: T): T => config; + +export const httpServerDefaultConfigs = createDefaultAdapterConfig({ + name: 'http-server', + enable: false as boolean, + port: 3000, + host: '0.0.0.0', + enableCors: true, + enableWebsocket: true, + messagePostFormat: 'array', + token: '', + debug: false, +}); +export type HttpServerConfig = typeof httpServerDefaultConfigs; + +export const httpClientDefaultConfigs = createDefaultAdapterConfig({ + name: 'http-client', + enable: false as boolean, + url: 'http://localhost:8080', + messagePostFormat: 'array', + reportSelfMessage: false, + token: '', + debug: false, +}); +export type HttpClientConfig = typeof httpClientDefaultConfigs; + +export const websocketServerDefaultConfigs = createDefaultAdapterConfig({ + name: 'websocket-server', + enable: false as boolean, + host: '0.0.0.0', + port: 3001, + messagePostFormat: 'array', + reportSelfMessage: false, + token: '', + enablePushEvent: true, + debug: false, + heartInterval: 30000, +}); +export type WebsocketServerConfig = typeof websocketServerDefaultConfigs; + +export const websocketClientDefaultConfigs = createDefaultAdapterConfig({ + name: 'websocket-client', + enable: false as boolean, + url: 'ws://localhost:8082', + messagePostFormat: 'array', + reportSelfMessage: false, + token: '', + debug: false, + heartInterval: 30000, +}); +export type WebsocketClientConfig = typeof websocketClientDefaultConfigs; + +export interface NetworkConfig { + httpServers: Array; + httpClients: Array; + websocketServers: Array; + websocketClients: Array; +} + +export function mergeConfigs(defaultConfig: T, userConfig: Partial): T { + return { ...defaultConfig, ...userConfig }; +} + +export interface OneBotConfig { + network: NetworkConfig; // 网络配置 + musicSignUrl: string; // 音乐签名地址 + enableLocalFile2Url: boolean; +} + +const createDefaultConfig = (config: T): T => config; + +export const defaultOneBotConfigs = createDefaultConfig({ + network: { + httpServers: [], + httpClients: [], + websocketServers: [], + websocketClients: [], + }, + musicSignUrl: '', + enableLocalFile2Url: false, +}); + +export const mergeNetworkDefaultConfig = { + httpServers: httpServerDefaultConfigs, + httpClients: httpClientDefaultConfigs, + websocketServers: websocketServerDefaultConfigs, + websocketClients: websocketClientDefaultConfigs, +} as const; + +type NetworkConfigKeys = keyof typeof mergeNetworkDefaultConfig; + +export function mergeOneBotConfigs( + userConfig: Partial, + defaultConfig: OneBotConfig = defaultOneBotConfigs +): OneBotConfig { + const mergedConfig = { ...defaultConfig }; + + if (userConfig.network) { + mergedConfig.network = { ...defaultConfig.network }; + for (const key in userConfig.network) { + const userNetworkConfig = userConfig.network[key as keyof NetworkConfig]; + const defaultNetworkConfig = mergeNetworkDefaultConfig[key as NetworkConfigKeys]; + if (Array.isArray(userNetworkConfig)) { + mergedConfig.network[key as keyof NetworkConfig] = userNetworkConfig.map((e) => + mergeConfigs(defaultNetworkConfig, e) + ); + } + } + } + if (userConfig.musicSignUrl !== undefined) { + mergedConfig.musicSignUrl = userConfig.musicSignUrl; + } + return mergedConfig; +} + +function checkIsOneBotConfigV1(v1Config: Partial): boolean { + return v1Config.http !== undefined || v1Config.ws !== undefined || v1Config.reverseWs !== undefined; +} + +export function migrateOneBotConfigsV1(config: Partial): OneBotConfig { + if (!checkIsOneBotConfigV1(config)) { + return config as OneBotConfig; + } + const mergedConfig = { ...defaultOneBotConfigs }; + if (config.http) { + mergedConfig.network.httpServers = [ + mergeConfigs(httpServerDefaultConfigs, { + enable: config.http.enable, + port: config.http.port, + host: config.http.host, + token: config.http.secret, + debug: config.debug, + messagePostFormat: config.messagePostFormat, + }), + ]; + } + if (config.ws) { + mergedConfig.network.websocketServers = [ + mergeConfigs(websocketServerDefaultConfigs, { + enable: config.ws.enable, + port: config.ws.port, + host: config.ws.host, + token: config.token, + debug: config.debug, + messagePostFormat: config.messagePostFormat, + reportSelfMessage: config.reportSelfMessage, + }), + ]; + } + if (config.reverseWs) { + mergedConfig.network.websocketClients = config.reverseWs.urls.map((url) => + mergeConfigs(websocketClientDefaultConfigs, { + enable: config.reverseWs?.enable, + url: url, + token: config.token, + debug: config.debug, + messagePostFormat: config.messagePostFormat, + reportSelfMessage: config.reportSelfMessage, + }) + ); + } + if (config.heartInterval) { + mergedConfig.network.websocketServers[0].heartInterval = config.heartInterval; + } + if (config.musicSignUrl) { + mergedConfig.musicSignUrl = config.musicSignUrl; + } + if (config.enableLocalFile2Url) { + mergedConfig.enableLocalFile2Url = config.enableLocalFile2Url; + } + return mergedConfig; +} diff --git a/src/onebot/config/index.ts b/src/onebot/config/index.ts index 7d517095..9e80df5a 100644 --- a/src/onebot/config/index.ts +++ b/src/onebot/config/index.ts @@ -1,11 +1,9 @@ import { ConfigBase } from '@/common/config-base'; -import ob11DefaultConfig from './onebot11.json'; import { NapCatCore } from '@/core'; +import { OneBotConfig } from './config'; -export type OB11Config = typeof ob11DefaultConfig; - -export class OB11ConfigLoader extends ConfigBase { +export class OB11ConfigLoader extends ConfigBase { constructor(core: NapCatCore, configPath: string) { - super('onebot11', core, configPath); + super('onebot11', core, configPath, false); } } diff --git a/src/onebot/config/onebot11.json b/src/onebot/config/onebot11.json deleted file mode 100644 index 8ee5a368..00000000 --- a/src/onebot/config/onebot11.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "http": { - "enable": false, - "host": "", - "port": 3000, - "secret": "", - "enableHeart": false, - "enablePost": false, - "postUrls": [] - }, - "ws": { - "enable": false, - "host": "", - "port": 3001 - }, - "reverseWs": { - "enable": false, - "urls": [] - }, - "GroupLocalTime": { - "Record": false, - "RecordList": [] - }, - "debug": false, - "heartInterval": 30000, - "messagePostFormat": "array", - "enableLocalFile2Url": true, - "musicSignUrl": "", - "reportSelfMessage": false, - "token": "" -} diff --git a/src/onebot/index.ts b/src/onebot/index.ts index f496d2e1..b7537dc0 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -14,7 +14,7 @@ import { RawMessage, SendStatusType, } from '@/core'; -import { OB11Config, OB11ConfigLoader } from '@/onebot/config'; +import { OB11ConfigLoader } from '@/onebot/config'; import { OB11ActiveHttpAdapter, OB11ActiveWebSocketAdapter, @@ -45,6 +45,8 @@ import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecal import { LRUCache } from '@/common/lru-cache'; import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener'; import { BotOfflineEvent } from './event/notice/BotOfflineEvent'; +import { mergeOneBotConfigs, migrateOneBotConfigsV1, OneBotConfig } from './config/config'; +import { OB11Message } from './types'; //OneBot实现类 export class NapCatOneBot11Adapter { @@ -62,6 +64,8 @@ export class NapCatOneBot11Adapter { this.core = core; this.context = context; this.configLoader = new OB11ConfigLoader(core, pathWrapper.configPath); + this.configLoader.save(migrateOneBotConfigsV1(this.configLoader.configData)); + this.configLoader.save(mergeOneBotConfigs(this.configLoader.configData)); this.apis = { GroupApi: new OneBotGroupApi(this, core), UserApi: new OneBotUserApi(this, core), @@ -72,70 +76,102 @@ export class NapCatOneBot11Adapter { this.actions = createActionMap(this, core); this.networkManager = new OB11NetworkManager(); } - + async creatOneBotLog(ob11Config: OneBotConfig) { + let log = `[network] 配置加载\n`; + for (const key of ob11Config.network.httpServers) { + log += `HTTP服务: ${key.host}:${key.port}, : ${key.enable ? '已启动' : '未启动'}\n`; + } + for (const key of ob11Config.network.httpClients) { + log += `HTTP上报服务: ${key.url}, : ${key.enable ? '已启动' : '未启动'}\n`; + } + for (const key of ob11Config.network.websocketServers) { + log += `WebSocket服务: ${key.host}:${key.port}, : ${key.enable ? '已启动' : '未启动'}\n`; + } + for (const key of ob11Config.network.websocketClients) { + log += `WebSocket反向服务: ${key.url}, : ${key.enable ? '已启动' : '未启动'}\n`; + } + return log; + } async InitOneBot() { const selfInfo = this.core.selfInfo; const ob11Config = this.configLoader.configData; - const serviceInfo = ` - HTTP服务 ${ob11Config.http.enable ? '已启动' : '未启动'}, ${ob11Config.http.host}:${ob11Config.http.port} - HTTP上报服务 ${ob11Config.http.enablePost ? '已启动' : '未启动'}, 上报地址: ${ob11Config.http.postUrls} - WebSocket服务 ${ob11Config.ws.enable ? '已启动' : '未启动'}, ${ob11Config.ws.host}:${ob11Config.ws.port} - WebSocket反向服务 ${ob11Config.reverseWs.enable ? '已启动' : '未启动'}, 反向地址: ${ob11Config.reverseWs.urls}`; + this.core.apis.UserApi.getUserDetailInfo(selfInfo.uid) + .then((user) => { + selfInfo.nick = user.nick; + this.context.logger.setLogSelfInfo(selfInfo); + }) + .catch(this.context.logger.logError.bind(this.context.logger)); - this.core.apis.UserApi.getUserDetailInfo(selfInfo.uid).then(user => { - selfInfo.nick = user.nick; - this.context.logger.setLogSelfInfo(selfInfo); - }).catch(this.context.logger.logError.bind(this.context.logger)); + const serviceInfo = await this.creatOneBotLog(ob11Config); this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`); - //创建NetWork服务 - if (ob11Config.http.enable) { - this.networkManager.registerAdapter(new OB11PassiveHttpAdapter( - ob11Config.http.port, ob11Config.token, this.core, this.actions, - )); + // //创建NetWork服务 + for (const key of ob11Config.network.httpServers) { + if (key.enable) { + this.networkManager.registerAdapter( + new OB11PassiveHttpAdapter(key.name, key.port, key.token, this.core, this.actions) + ); + } } - if (ob11Config.http.enablePost) { - ob11Config.http.postUrls.forEach(url => { - this.networkManager.registerAdapter(new OB11ActiveHttpAdapter( - url, ob11Config.http.secret, this.core, this, - )); - }); + for (const key of ob11Config.network.httpClients) { + if (key.enable) { + this.networkManager.registerAdapter( + new OB11ActiveHttpAdapter(key.name, key.url, key.token, this.core, this) + ); + } } - if (ob11Config.ws.enable) { - const OBPassiveWebSocketAdapter = new OB11PassiveWebSocketAdapter( - ob11Config.ws.host, ob11Config.ws.port, ob11Config.heartInterval, ob11Config.token, this.core, this.actions, - ); - this.networkManager.registerAdapter(OBPassiveWebSocketAdapter); + for (const key of ob11Config.network.websocketServers) { + if (key.enable) { + this.networkManager.registerAdapter( + new OB11PassiveWebSocketAdapter( + key.name, + key.host, + key.port, + key.heartInterval, + key.token, + this.core, + this.actions + ) + ); + } } - if (ob11Config.reverseWs.enable) { - ob11Config.reverseWs.urls.forEach(url => { - this.networkManager.registerAdapter(new OB11ActiveWebSocketAdapter( - url, 5000, ob11Config.heartInterval, ob11Config.token, this.core, this.actions, - )); - }); + for (const key of ob11Config.network.websocketClients) { + if (key.enable) { + this.networkManager.registerAdapter( + new OB11ActiveWebSocketAdapter( + key.name, + key.url, + 5000, + key.heartInterval, + key.token, + this.core, + this.actions + ) + ); + } } - await this.networkManager.openAllAdapters(); this.initMsgListener(); this.initBuddyListener(); this.initGroupListener(); - //this.initRecentContactListener(); await WebUiDataRuntime.setQQLoginUin(selfInfo.uin.toString()); await WebUiDataRuntime.setQQLoginStatus(true); - await WebUiDataRuntime.setOnOB11ConfigChanged(async (newConfig: OB11Config) => { + await WebUiDataRuntime.setOnOB11ConfigChanged(async (newConfig) => { const prev = this.configLoader.configData; this.configLoader.save(newConfig); this.context.logger.log(`OneBot11 配置更改:${JSON.stringify(prev)} -> ${JSON.stringify(newConfig)}`); - await this.reloadNetwork(prev, newConfig); + //await this.reloadNetwork(prev, newConfig); }); } initRecentContactListener() { const recentContactListener = new NodeIKernelRecentContactListener(); - recentContactListener.onRecentContactNotification = function (msgList: any[] /* arg0: { msgListUnreadCnt: string }, arg1: number */) { + recentContactListener.onRecentContactNotification = function ( + msgList: any[] /* arg0: { msgListUnreadCnt: string }, arg1: number */ + ) { msgList.forEach((msg) => { if (msg.chatType == ChatType.KCHATTYPEGROUP) { // log("recent contact", msgList, arg0, arg1); @@ -144,115 +180,120 @@ export class NapCatOneBot11Adapter { }; } - private async reloadNetwork(prev: OB11Config, now: OB11Config) { - const serviceInfo = ` - HTTP服务 ${now.http.enable ? '已启动' : '未启动'}, ${now.http.host}:${now.http.port} - HTTP上报服务 ${now.http.enablePost ? '已启动' : '未启动'}, 上报地址: ${now.http.postUrls} - WebSocket服务 ${now.ws.enable ? '已启动' : '未启动'}, ${now.ws.host}:${now.ws.port} - WebSocket反向服务 ${now.reverseWs.enable ? '已启动' : '未启动'}, 反向地址: ${now.reverseWs.urls}`; - this.context.logger.log(`[Notice] [OneBot11] 热重载 ${serviceInfo}`); + // private async reloadNetwork(prev: OB11Config, now: OB11Config) { + // const serviceInfo = ` + // HTTP服务 ${now.http.enable ? '已启动' : '未启动'}, ${now.http.host}:${now.http.port} + // HTTP上报服务 ${now.http.enablePost ? '已启动' : '未启动'}, 上报地址: ${now.http.postUrls} + // WebSocket服务 ${now.ws.enable ? '已启动' : '未启动'}, ${now.ws.host}:${now.ws.port} + // WebSocket反向服务 ${now.reverseWs.enable ? '已启动' : '未启动'}, 反向地址: ${now.reverseWs.urls}`; + // this.context.logger.log(`[Notice] [OneBot11] 热重载 ${serviceInfo}`); - // check difference in passive http (Http) - if (prev.http.enable !== now.http.enable) { - if (now.http.enable) { - await this.networkManager.registerAdapterAndOpen(new OB11PassiveHttpAdapter( - now.http.port, now.token, this.core, this.actions, - )); - } else { - await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11PassiveHttpAdapter); - } - } + // // check difference in passive http (Http) + // if (prev.http.enable !== now.http.enable) { + // if (now.http.enable) { + // await this.networkManager.registerAdapterAndOpen(new OB11PassiveHttpAdapter( + // now.http.port, now.token, this.core, this.actions, + // )); + // } else { + // await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11PassiveHttpAdapter); + // } + // } - // check difference in active http (HttpPost) - if (prev.http.enablePost !== now.http.enablePost) { - if (now.http.enablePost) { - now.http.postUrls.forEach(url => { - this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter( - url, now.http.secret, this.core, this, - )); - }); - } else { - await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11ActiveHttpAdapter); - } - } else if (now.http.enablePost) { - const { added, removed } = this.findDifference(prev.http.postUrls, now.http.postUrls); - await this.networkManager.closeAdapterByPredicate( - adapter => adapter instanceof OB11ActiveHttpAdapter && removed.includes(adapter.url), - ); - for (const url of added) { - await this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter( - url, now.http.secret, this.core, this, - )); - } - } + // // check difference in active http (HttpPost) + // if (prev.http.enablePost !== now.http.enablePost) { + // if (now.http.enablePost) { + // now.http.postUrls.forEach(url => { + // this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter( + // url, now.http.secret, this.core, this, + // )); + // }); + // } else { + // await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11ActiveHttpAdapter); + // } + // } else if (now.http.enablePost) { + // const { added, removed } = this.findDifference(prev.http.postUrls, now.http.postUrls); + // await this.networkManager.closeAdapterByPredicate( + // adapter => adapter instanceof OB11ActiveHttpAdapter && removed.includes(adapter.url), + // ); + // for (const url of added) { + // await this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter( + // url, now.http.secret, this.core, this, + // )); + // } + // } + // // check difference in passive websocket (Ws) + // if (prev.ws.enable !== now.ws.enable) { + // if (now.ws.enable) { + // await this.networkManager.registerAdapterAndOpen(new OB11PassiveWebSocketAdapter( + // now.ws.host, now.ws.port, now.heartInterval, now.token, this.core, this.actions, + // )); + // } else { + // await this.networkManager.closeAdapterByPredicate( + // adapter => adapter instanceof OB11PassiveWebSocketAdapter, + // ); + // } + // } - // check difference in passive websocket (Ws) - if (prev.ws.enable !== now.ws.enable) { - if (now.ws.enable) { - await this.networkManager.registerAdapterAndOpen(new OB11PassiveWebSocketAdapter( - now.ws.host, now.ws.port, now.heartInterval, now.token, this.core, this.actions, - )); - } else { - await this.networkManager.closeAdapterByPredicate( - adapter => adapter instanceof OB11PassiveWebSocketAdapter, - ); - } - } + // // check difference in active websocket (ReverseWs) + // if (prev.reverseWs.enable !== now.reverseWs.enable) { + // if (now.reverseWs.enable) { + // now.reverseWs.urls.forEach(url => { + // this.networkManager.registerAdapterAndOpen(new OB11ActiveWebSocketAdapter( + // url, 5000, now.heartInterval, now.token, this.core, this.actions, + // )); + // }); + // } else { + // await this.networkManager.closeAdapterByPredicate( + // adapter => adapter instanceof OB11ActiveWebSocketAdapter, + // ); + // } + // } else if (now.reverseWs.enable) { + // const { added, removed } = this.findDifference(prev.reverseWs.urls, now.reverseWs.urls); + // await this.networkManager.closeAdapterByPredicate( + // adapter => adapter instanceof OB11ActiveWebSocketAdapter && removed.includes(adapter.url), + // ); + // for (const url of added) { + // await this.networkManager.registerAdapterAndOpen(new OB11ActiveWebSocketAdapter( + // url, 5000, now.heartInterval, now.token, this.core, this.actions, + // )); + // } + // } - // check difference in active websocket (ReverseWs) - if (prev.reverseWs.enable !== now.reverseWs.enable) { - if (now.reverseWs.enable) { - now.reverseWs.urls.forEach(url => { - this.networkManager.registerAdapterAndOpen(new OB11ActiveWebSocketAdapter( - url, 5000, now.heartInterval, now.token, this.core, this.actions, - )); - }); - } else { - await this.networkManager.closeAdapterByPredicate( - adapter => adapter instanceof OB11ActiveWebSocketAdapter, - ); - } - } else if (now.reverseWs.enable) { - const { added, removed } = this.findDifference(prev.reverseWs.urls, now.reverseWs.urls); - await this.networkManager.closeAdapterByPredicate( - adapter => adapter instanceof OB11ActiveWebSocketAdapter && removed.includes(adapter.url), - ); - for (const url of added) { - await this.networkManager.registerAdapterAndOpen(new OB11ActiveWebSocketAdapter( - url, 5000, now.heartInterval, now.token, this.core, this.actions, - )); - } - } + // } - } - - private findDifference(prev: T[], now: T[]): { added: T[], removed: T[] } { - const added = now.filter(item => !prev.includes(item)); - const removed = prev.filter(item => !now.includes(item)); + private findDifference(prev: T[], now: T[]): { added: T[]; removed: T[] } { + const added = now.filter((item) => !prev.includes(item)); + const removed = prev.filter((item) => !now.includes(item)); return { added, removed }; } private initMsgListener() { const msgListener = new NodeIKernelMsgListener(); msgListener.onRecvSysMsg = (msg) => { - this.apis.MsgApi.parseSysMessage(msg).then((event) => { - if (event) this.networkManager.emitEvent(event); - }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructSysMessage error: ', e, '\n Parse Hex:', Buffer.from(msg).toString('hex'))); + this.apis.MsgApi.parseSysMessage(msg) + .then((event) => { + if (event) this.networkManager.emitEvent(event); + }) + .catch((e) => + this.context.logger.logError.bind(this.context.logger)( + 'constructSysMessage error: ', + e, + '\n Parse Hex:', + Buffer.from(msg).toString('hex') + ) + ); }; - msgListener.onInputStatusPush = async data => { + msgListener.onInputStatusPush = async (data) => { const uin = await this.core.apis.UserApi.getUinByUidV2(data.fromUin); this.context.logger.log(`[Notice] [输入状态] ${uin} ${data.statusText}`); - await this.networkManager.emitEvent(new OB11InputStatusEvent( - this.core, - parseInt(uin), - data.eventType, - data.statusText, - )); + await this.networkManager.emitEvent( + new OB11InputStatusEvent(this.core, parseInt(uin), data.eventType, data.statusText) + ); }; - msgListener.onRecvMsg = async msg => { + msgListener.onRecvMsg = async (msg) => { for (const m of msg) { if (this.bootTime > parseInt(m.msgTime)) { this.context.logger.logDebug(`消息时间${m.msgTime}早于启动时间${this.bootTime},忽略上报`); @@ -264,59 +305,54 @@ export class NapCatOneBot11Adapter { peerUid: m.peerUid, guildId: '', }, - m.msgId, + m.msgId + ); + await this.emitMsg(m).catch((e) => + this.context.logger.logError.bind(this.context.logger)('处理消息失败', e) ); - await this.emitMsg(m) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e)); } }; const msgIdSend = new LRUCache(100); const recallMsgs = new LRUCache(100); - msgListener.onAddSendMsg = async msg => { + msgListener.onAddSendMsg = async (msg) => { if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) { msgIdSend.put(msg.msgId, 0); } }; - msgListener.onMsgInfoListUpdate = async msgList => { - this.emitRecallMsg(msgList, recallMsgs) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e)); - for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) { + msgListener.onMsgInfoListUpdate = async (msgList) => { + this.emitRecallMsg(msgList, recallMsgs).catch((e) => + this.context.logger.logError.bind(this.context.logger)('处理消息失败', e) + ); + for (const msg of msgList.filter((e) => e.senderUin == this.core.selfInfo.uin)) { if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && msgIdSend.get(msg.msgId) == 0) { msgIdSend.put(msg.msgId, 1); // 完成后再post - this.apis.MsgApi.parseMessage(msg) - .then((ob11Msg) => { - if (!ob11Msg) return; - ob11Msg.target_id = parseInt(msg.peerUin); - if (this.configLoader.configData.reportSelfMessage) { - msg.id = MessageUnique.createUniqueMsgId({ - chatType: msg.chatType, - peerUid: msg.peerUid, - guildId: '', - }, msg.msgId); - this.emitMsg(msg); - } else { - // logOB11Message(this.core, ob11Msg); - } - }); + msg.id = MessageUnique.createUniqueMsgId( + { + chatType: msg.chatType, + peerUid: msg.peerUid, + guildId: '', + }, + msg.msgId + ); + this.emitMsg(msg); } } }; msgListener.onKickedOffLine = async (kick) => { const event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc); - this.networkManager.emitEvent(event) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理Bot掉线失败', e)); + this.networkManager + .emitEvent(event) + .catch((e) => this.context.logger.logError.bind(this.context.logger)('处理Bot掉线失败', e)); }; - this.context.session.getMsgService().addKernelMsgListener( - proxiedListenerOf(msgListener, this.context.logger), - ); + this.context.session.getMsgService().addKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)); } private initBuddyListener() { const buddyListener = new NodeIKernelBuddyListener(); - buddyListener.onBuddyReqChange = async reqs => { + buddyListener.onBuddyReqChange = async (reqs) => { this.core.apis.FriendApi.clearBuddyReqUnreadCnt(); for (let i = 0; i < reqs.unreadNums; i++) { const req = reqs.buddyReqs[i]; @@ -325,21 +361,23 @@ export class NapCatOneBot11Adapter { } try { const requesterUin = await this.core.apis.UserApi.getUinByUidV2(req.friendUid); - await this.networkManager.emitEvent(new OB11FriendRequestEvent( - this.core, - +requesterUin, - req.extWords, - req.friendUid + '|' + req.reqTime, - )); + await this.networkManager.emitEvent( + new OB11FriendRequestEvent( + this.core, + +requesterUin, + req.extWords, + req.friendUid + '|' + req.reqTime + ) + ); } catch (e) { this.context.logger.logDebug('获取加好友者QQ号失败', e); } } }; - this.context.session.getBuddyService().addKernelBuddyListener( - proxiedListenerOf(buddyListener, this.context.logger), - ); + this.context.session + .getBuddyService() + .addKernelBuddyListener(proxiedListenerOf(buddyListener, this.context.logger)); } private initGroupListener() { @@ -348,11 +386,13 @@ export class NapCatOneBot11Adapter { groupListener.onGroupNotifiesUpdated = async (_, notifies) => { //console.log('ob11 onGroupNotifiesUpdated', notifies[0]); await this.core.apis.GroupApi.clearGroupNotifiesUnreadCount(false); - if (![ - GroupNotifyMsgType.SET_ADMIN, - GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED, - GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN, - ].includes(notifies[0]?.type)) { + if ( + ![ + GroupNotifyMsgType.SET_ADMIN, + GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED, + GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN, + ].includes(notifies[0]?.type) + ) { for (const notify of notifies) { const notifyTime = parseInt(notify.seq) / 1000 / 1000; // log(`群通知时间${notifyTime}`, `启动时间${this.bootTime}`); @@ -363,15 +403,19 @@ export class NapCatOneBot11Adapter { const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type; this.context.logger.logDebug('收到群通知', notify); - if ([ - GroupNotifyMsgType.SET_ADMIN, - GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED, - GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN, - ].includes(notify.type)) { - const member1 = await this.core.apis.GroupApi.getGroupMember(notify.group.groupCode, notify.user1.uid); + if ( + [ + GroupNotifyMsgType.SET_ADMIN, + GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED, + GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN, + ].includes(notify.type) + ) { + const member1 = await this.core.apis.GroupApi.getGroupMember( + notify.group.groupCode, + notify.user1.uid + ); this.context.logger.logDebug('有管理员变动通知'); // refreshGroupMembers(notify.group.groupCode).then(); - this.context.logger.logDebug('开始获取变动的管理员'); if (member1) { this.context.logger.logDebug('变动管理员获取成功'); @@ -382,16 +426,28 @@ export class NapCatOneBot11Adapter { [ GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED, GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN, - ].includes(notify.type) ? 'unset' : 'set', + ].includes(notify.type) + ? 'unset' + : 'set' ); - this.networkManager.emitEvent(groupAdminNoticeEvent) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理群管理员变动失败', e)); + this.networkManager + .emitEvent(groupAdminNoticeEvent) + .catch((e) => + this.context.logger.logError.bind(this.context.logger)('处理群管理员变动失败', e) + ); } else { - this.context.logger.logDebug('获取群通知的成员信息失败', notify, this.core.apis.GroupApi.getGroup(notify.group.groupCode)); + this.context.logger.logDebug( + '获取群通知的成员信息失败', + notify, + this.core.apis.GroupApi.getGroup(notify.group.groupCode) + ); } - } else if (notify.type == GroupNotifyMsgType.MEMBER_LEAVE_NOTIFY_ADMIN || notify.type == GroupNotifyMsgType.KICK_MEMBER_NOTIFY_ADMIN) { + } else if ( + notify.type == GroupNotifyMsgType.MEMBER_LEAVE_NOTIFY_ADMIN || + notify.type == GroupNotifyMsgType.KICK_MEMBER_NOTIFY_ADMIN + ) { this.context.logger.logDebug('有成员退出通知', notify); - const member1Uin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid)); + const member1Uin = await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid); let operatorId = member1Uin; let subType: GroupDecreaseSubType = 'leave'; if (notify.user2.uid) { @@ -407,17 +463,21 @@ export class NapCatOneBot11Adapter { parseInt(notify.group.groupCode), parseInt(member1Uin), parseInt(operatorId), - subType, + subType ); - this.networkManager.emitEvent(groupDecreaseEvent) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理群成员退出失败', e)); + this.networkManager + .emitEvent(groupDecreaseEvent) + .catch((e) => + this.context.logger.logError.bind(this.context.logger)('处理群成员退出失败', e) + ); // notify.status == 1 表示未处理 2表示处理完成 - } else if ([ - GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS, - ].includes(notify.type) && notify.status == GroupNotifyMsgStatus.KUNHANDLE) { + } else if ( + [GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS].includes(notify.type) && + notify.status == GroupNotifyMsgStatus.KUNHANDLE + ) { this.context.logger.logDebug('有加群请求'); try { - let requestUin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid)); + let requestUin = await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid); if (isNaN(parseInt(requestUin))) { requestUin = (await this.core.apis.UserApi.getUserDetailInfo(notify.user1.uid)).uin; } @@ -427,14 +487,24 @@ export class NapCatOneBot11Adapter { parseInt(requestUin), 'add', notify.postscript, - flag, + flag ); - this.networkManager.emitEvent(groupRequestEvent) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理加群请求失败', e)); + this.networkManager + .emitEvent(groupRequestEvent) + .catch((e) => + this.context.logger.logError.bind(this.context.logger)('处理加群请求失败', e) + ); } catch (e) { - this.context.logger.logError.bind(this.context.logger)('获取加群人QQ号失败 Uid:', notify.user1.uid, e); + this.context.logger.logError.bind(this.context.logger)( + '获取加群人QQ号失败 Uid:', + notify.user1.uid, + e + ); } - } else if (notify.type == GroupNotifyMsgType.INVITED_BY_MEMBER && notify.status == GroupNotifyMsgStatus.KUNHANDLE) { + } else if ( + notify.type == GroupNotifyMsgType.INVITED_BY_MEMBER && + notify.status == GroupNotifyMsgStatus.KUNHANDLE + ) { this.context.logger.logDebug(`收到邀请我加群通知:${notify}`); const groupInviteEvent = new OB11GroupRequestEvent( this.core, @@ -442,11 +512,17 @@ export class NapCatOneBot11Adapter { parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid)), 'invite', notify.postscript, - flag, + flag ); - this.networkManager.emitEvent(groupInviteEvent) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理邀请本人加群失败', e)); - } else if (notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS && notify.status == GroupNotifyMsgStatus.KUNHANDLE) { + this.networkManager + .emitEvent(groupInviteEvent) + .catch((e) => + this.context.logger.logError.bind(this.context.logger)('处理邀请本人加群失败', e) + ); + } else if ( + notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS && + notify.status == GroupNotifyMsgStatus.KUNHANDLE + ) { this.context.logger.logDebug(`收到群员邀请加群通知:${notify}`); const groupInviteEvent = new OB11GroupRequestEvent( this.core, @@ -454,10 +530,13 @@ export class NapCatOneBot11Adapter { parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid)), 'add', notify.postscript, - flag, + flag ); - this.networkManager.emitEvent(groupInviteEvent) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理邀请本人加群失败', e)); + this.networkManager + .emitEvent(groupInviteEvent) + .catch((e) => + this.context.logger.logError.bind(this.context.logger)('处理邀请本人加群失败', e) + ); } } } @@ -476,92 +555,102 @@ export class NapCatOneBot11Adapter { this.core, parseInt(groupCode), parseInt(member.uin), - member.role === GroupMemberRole.admin ? 'set' : 'unset', + member.role === GroupMemberRole.admin ? 'set' : 'unset' ); - this.networkManager.emitEvent(groupAdminNoticeEvent) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理群管理员变动失败', e)); + this.networkManager + .emitEvent(groupAdminNoticeEvent) + .catch((e) => + this.context.logger.logError.bind(this.context.logger)('处理群管理员变动失败', e) + ); existMember.isChangeRole = false; this.context.logger.logDebug.bind(this.context.logger)('群管理员变动处理完毕'); }); } }; - this.context.session.getGroupService().addKernelGroupListener( - proxiedListenerOf(groupListener, this.context.logger), - ); + this.context.session + .getGroupService() + .addKernelGroupListener(proxiedListenerOf(groupListener, this.context.logger)); } - private async emitMsg(message: RawMessage, parseEvent: boolean = true) { - const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData; + private async emitMsg(message: RawMessage) { + const network = Object.values(this.configLoader.configData.network) as Array< + (typeof this.configLoader.configData.network)[keyof typeof this.configLoader.configData.network] + >; this.context.logger.logDebug('收到新消息 RawMessage', message); - this.apis.MsgApi.parseMessage(message, messagePostFormat).then((ob11Msg) => { - if (!ob11Msg) return; - this.context.logger.logDebug('转化为 OB11Message', ob11Msg); - if (debug) { - ob11Msg.raw = message; - } else if (ob11Msg.message.length === 0) { - return; + this.apis.MsgApi.parseMessageV2(message) + .then((ob11Msg) => { + if (!ob11Msg) return; + const isSelfMsg = + ob11Msg.stringMsg.user_id.toString() == this.core.selfInfo.uin || + ob11Msg.arrayMsg.user_id.toString() == this.core.selfInfo.uin; + this.context.logger.logDebug('转化为 OB11Message', ob11Msg); + const msgMap: Map = new Map(); + const enable_client: string[] = []; + network + .flat() + .filter((e) => e.enable) + .map((e) => { + enable_client.push(e.name); + if (e.messagePostFormat == 'string') { + msgMap.set(e.name, structuredClone(ob11Msg.stringMsg)); + } else { + msgMap.set(e.name, structuredClone(ob11Msg.arrayMsg)); + } + if (isSelfMsg) { + ob11Msg.stringMsg.target_id = parseInt(message.peerUin); + ob11Msg.arrayMsg.target_id = parseInt(message.peerUin); + } + }); - } - const isSelfMsg = ob11Msg.user_id.toString() == this.core.selfInfo.uin; - if (isSelfMsg && !reportSelfMessage) { - return; - } - if (isSelfMsg) { - ob11Msg.target_id = parseInt(message.peerUin); - } - // if (ob11Msg.raw_message.startsWith('!set')) { - // this.core.apis.UserApi.getUidByUinV2(ob11Msg.user_id.toString()).then(uid => { - // if(uid){ - // this.core.apis.PacketApi.sendSetSpecialTittlePacket(message.peerUin, uid, '测试'); - // console.log('set', message.peerUin, uid); - // } + const debug_network = network.flat().filter((e) => e.enable && e.debug); + if (debug_network.length > 0) { + for (const adapter of debug_network) { + if (adapter.name) { + const msg = msgMap.get(adapter.name); + if (msg) { + msg.raw = message; + } + } + } + } else if (ob11Msg.stringMsg.message.length === 0 || ob11Msg.arrayMsg.message.length == 0) { + return; + } + const notreportSelf_network = network.flat().filter((e) => e.enable && (('reportSelfMessage' in e && !e.reportSelfMessage) || !('reportSelfMessage' in e))); + if (isSelfMsg) { + for (const adapter of notreportSelf_network) { + msgMap.delete(adapter.name); + } + } - // }); + this.networkManager.emitEventByNames(msgMap); + }) + .catch((e) => this.context.logger.logError.bind(this.context.logger)('constructMessage error: ', e)); - // } - // if (ob11Msg.raw_message.startsWith('!status')) { - // console.log('status', message.peerUin, message.senderUin); - // let delMsg: string[] = []; - // let peer = { - // peerUid: message.peerUin, - // chatType: 2, - // }; - // this.core.apis.PacketApi.sendStatusPacket(+message.senderUin).then(async e => { - // if (e) { - // const { sendElements } = await this.apis.MsgApi.createSendElements([{ - // type: OB11MessageDataType.text, - // data: { - // text: 'status ' + JSON.stringify(e, null, 2), - // } - // }], peer) + this.apis.GroupApi.parseGroupEvent(message) + .then((groupEvent) => { + if (groupEvent) { + // log("post group event", groupEvent); + this.networkManager.emitEvent(groupEvent); + } + }) + .catch((e) => this.context.logger.logError.bind(this.context.logger)('constructGroupEvent error: ', e)); - // this.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, delMsg) - // } - // }) - // } - this.networkManager.emitEvent(ob11Msg); - }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructMessage error: ', e)); - - this.apis.GroupApi.parseGroupEvent(message).then(groupEvent => { - if (groupEvent) { - // log("post group event", groupEvent); - this.networkManager.emitEvent(groupEvent); - } - }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructGroupEvent error: ', e)); - - this.apis.MsgApi.parsePrivateMsgEvent(message).then(privateEvent => { - if (privateEvent) { - // log("post private event", privateEvent); - this.networkManager.emitEvent(privateEvent); - } - }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructPrivateEvent error: ', e)); + this.apis.MsgApi.parsePrivateMsgEvent(message) + .then((privateEvent) => { + if (privateEvent) { + // log("post private event", privateEvent); + this.networkManager.emitEvent(privateEvent); + } + }) + .catch((e) => this.context.logger.logError.bind(this.context.logger)('constructPrivateEvent error: ', e)); } private async emitRecallMsg(msgList: RawMessage[], cache: LRUCache) { for (const message of msgList) { // log("message update", message.sendStatus, message.msgId, message.msgSeq) const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' }; - if (message.recallTime != '0' && !cache.get(message.msgId)) { //work:这个判断方法不太好,应该使用灰色消息元素来判断? + if (message.recallTime != '0' && !cache.get(message.msgId)) { + //work:这个判断方法不太好,应该使用灰色消息元素来判断? cache.put(message.msgId, true); // 撤回消息上报 let oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId); @@ -572,10 +661,13 @@ export class NapCatOneBot11Adapter { const friendRecallEvent = new OB11FriendRecallNoticeEvent( this.core, +message.senderUin, - oriMessageId, + oriMessageId ); - this.networkManager.emitEvent(friendRecallEvent) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理好友消息撤回失败', e)); + this.networkManager + .emitEvent(friendRecallEvent) + .catch((e) => + this.context.logger.logError.bind(this.context.logger)('处理好友消息撤回失败', e) + ); } else if (message.chatType == ChatType.KCHATTYPEGROUP) { let operatorId = message.senderUin; for (const element of message.elements) { @@ -591,8 +683,9 @@ export class NapCatOneBot11Adapter { +operatorId, oriMessageId ); - this.networkManager.emitEvent(groupRecallEvent) - .catch(e => this.context.logger.logError.bind(this.context.logger)('处理群消息撤回失败', e)); + this.networkManager + .emitEvent(groupRecallEvent) + .catch((e) => this.context.logger.logError.bind(this.context.logger)('处理群消息撤回失败', e)); } } } diff --git a/src/onebot/network/active-http.ts b/src/onebot/network/active-http.ts index 26687929..16cec876 100644 --- a/src/onebot/network/active-http.ts +++ b/src/onebot/network/active-http.ts @@ -10,6 +10,7 @@ export class OB11ActiveHttpAdapter implements IOB11NetworkAdapter { isOpen: boolean = false; constructor( + public name: string, public url: string, public secret: string | undefined, public core: NapCatCore, diff --git a/src/onebot/network/active-websocket.ts b/src/onebot/network/active-websocket.ts index 9b33e11e..fc9f5bdb 100644 --- a/src/onebot/network/active-websocket.ts +++ b/src/onebot/network/active-websocket.ts @@ -15,6 +15,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter { private heartbeatRef: NodeJS.Timeout | null = null; constructor( + public name: string, public url: string, public reconnectIntervalInMillis: number, public heartbeatIntervalInMillis: number, @@ -35,11 +36,14 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter { if (this.connection) { return; } - this.heartbeatRef = setInterval(() => { - if (this.connection && this.connection.readyState === WebSocket.OPEN) { - this.connection.send(JSON.stringify(new OB11HeartbeatEvent(this.core, this.heartbeatIntervalInMillis, this.core.selfInfo.online ?? true, true))); - } - }, this.heartbeatIntervalInMillis); + if (this.heartbeatIntervalInMillis > 0) { + this.heartbeatRef = setInterval(() => { + if (this.connection && this.connection.readyState === WebSocket.OPEN) { + this.connection.send(JSON.stringify(new OB11HeartbeatEvent(this.core, this.heartbeatIntervalInMillis, this.core.selfInfo.online ?? true, true))); + } + }, this.heartbeatIntervalInMillis); + } + await this.tryConnect(); } @@ -147,7 +151,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter { this.checkStateAndReply(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo)); return; } - const retdata = await action.websocketHandle(receiveData.params, echo ?? ''); + const retdata = await action.websocketHandle(receiveData.params, echo ?? '', this.name); this.checkStateAndReply({ ...retdata }); } } diff --git a/src/onebot/network/index.ts b/src/onebot/network/index.ts index 9d4b16b5..7373a9bb 100644 --- a/src/onebot/network/index.ts +++ b/src/onebot/network/index.ts @@ -6,6 +6,7 @@ export type OB11EmitEventContent = OB11BaseEvent | OB11Message; export interface IOB11NetworkAdapter { actions?: ActionMap; + name: string; onEvent(event: T): void; @@ -15,19 +16,34 @@ export interface IOB11NetworkAdapter { } export class OB11NetworkManager { - adapters: IOB11NetworkAdapter[] = []; + adapters: Map = new Map(); async openAllAdapters() { - return Promise.all(this.adapters.map(adapter => adapter.open())); + return Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.open())); } async emitEvent(event: OB11EmitEventContent) { - //console.log('adapters', this.adapters.length); - return Promise.all(this.adapters.map(adapter => adapter.onEvent(event))); + return Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.onEvent(event))); } + async emitEventByName(names: string[], event: OB11EmitEventContent) { + return Promise.all(names.map(name => { + const adapter = this.adapters.get(name); + if (adapter) { + return adapter.onEvent(event); + } + })); + } + async emitEventByNames(map:Map){ + return Promise.all(Array.from(map.entries()).map(([name, event]) => { + const adapter = this.adapters.get(name); + if (adapter) { + return adapter.onEvent(event); + } + })); + } registerAdapter(adapter: IOB11NetworkAdapter) { - this.adapters.push(adapter); + this.adapters.set(adapter.name, adapter); } async registerAdapterAndOpen(adapter: IOB11NetworkAdapter) { @@ -36,24 +52,28 @@ export class OB11NetworkManager { } async closeSomeAdapters(adaptersToClose: IOB11NetworkAdapter[]) { - this.adapters = this.adapters.filter(adapter => !adaptersToClose.includes(adapter)); - await Promise.all(adaptersToClose.map(adapter => adapter.close())); + for (const adapter of adaptersToClose) { + this.adapters.delete(adapter.name); + await adapter.close(); + } + } + + findSomeAdapter(name: string) { + return this.adapters.get(name); } - /** - * Close all adapters that satisfy the predicate. - */ async closeAdapterByPredicate(closeFilter: (adapter: IOB11NetworkAdapter) => boolean) { - await this.closeSomeAdapters(this.adapters.filter(closeFilter)); + const adaptersToClose = Array.from(this.adapters.values()).filter(closeFilter); + await this.closeSomeAdapters(adaptersToClose); } async closeAllAdapters() { - await Promise.all(this.adapters.map(adapter => adapter.close())); - this.adapters = []; + await Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.close())); + this.adapters.clear(); } } export * from './active-http'; export * from './active-websocket'; export * from './passive-http'; -export * from './passive-websocket'; +export * from './passive-websocket'; \ No newline at end of file diff --git a/src/onebot/network/passive-http.ts b/src/onebot/network/passive-http.ts index 2ebaf624..bab26cd8 100644 --- a/src/onebot/network/passive-http.ts +++ b/src/onebot/network/passive-http.ts @@ -12,6 +12,7 @@ export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter { private isOpen: boolean = false; constructor( + public name: string, public port: number, public token: string, public core: NapCatCore, @@ -101,7 +102,7 @@ export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter { const action = this.actions.get(actionName); if (action) { try { - const result = await action.handle(payload); + const result = await action.handle(payload,this.name); return res.json(result); } catch (error: any) { return res.json(OB11Response.error(error?.stack?.toString() || error?.message || 'Error Handle', 200)); diff --git a/src/onebot/network/passive-websocket.ts b/src/onebot/network/passive-websocket.ts index e4a8f597..276abbdd 100644 --- a/src/onebot/network/passive-websocket.ts +++ b/src/onebot/network/passive-websocket.ts @@ -24,6 +24,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter { wsClientWithEvent: WebSocket[] = []; constructor( + public name: string, ip: string, port: number, heartbeatInterval: number, @@ -114,7 +115,10 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter { this.logger.log('[OneBot] [WebSocket Server] Server Started', typeof (addressInfo) === 'string' ? addressInfo : addressInfo?.address + ':' + addressInfo?.port); this.isOpen = true; - this.registerHeartBeat(); + if (this.heartbeatInterval > 0) { + this.registerHeartBeat(); + } + } async close() { @@ -188,7 +192,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter { this.checkStateAndReply(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo), wsClient); return; } - const retdata = await action.websocketHandle(receiveData.params, echo ?? ''); + const retdata = await action.websocketHandle(receiveData.params, echo ?? '', this.name); this.checkStateAndReply({ ...retdata }, wsClient); } } diff --git a/src/webui/index.ts b/src/webui/index.ts index 3d4cae46..68ac18b1 100644 --- a/src/webui/index.ts +++ b/src/webui/index.ts @@ -36,26 +36,34 @@ export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapp // 配置静态文件服务,提供./static目录下的文件服务,访问路径为/webui app.use(config.prefix + '/webui', express.static(pathWrapper.staticPath)); //挂载API接口 + // 添加CORS支持 + // TODO: + app.use((req, res, next) => { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); + res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization'); + next(); + }); app.use(config.prefix + '/api', ALLRouter); app.listen(config.port, config.host, async () => { log(`[NapCat] [WebUi] Current WebUi is running at http://${config.host}:${config.port}${config.prefix}`); log(`[NapCat] [WebUi] Login Token is ${config.token}`); - log(`[NapCat] [WebUi] WebUi User Panel Url: http://${config.host}:${config.port}${config.prefix}/webui?token=${config.token}`); - log(`[NapCat] [WebUi] WebUi Local Panel Url: http://127.0.0.1:${config.port}${config.prefix}/webui?token=${config.token}`); + log( + `[NapCat] [WebUi] WebUi User Panel Url: http://${config.host}:${config.port}${config.prefix}/webui?token=${config.token}` + ); + log( + `[NapCat] [WebUi] WebUi Local Panel Url: http://127.0.0.1:${config.port}${config.prefix}/webui?token=${config.token}` + ); //获取上网Ip //https://www.ip.cn/api/index?ip&type=0 - RequestUtil.HttpGetJson<{ IP: {IP:string} }>( - 'https://ip.011102.xyz/', - 'GET', - {}, - {}, - true, - true - ).then((data) => { - log(`[NapCat] [WebUi] WebUi Publish Panel Url: http://${data.IP.IP}:${config.port}${config.prefix}/webui/?token=${config.token}`); - }).catch((err) => { - logger.logError.bind(logger)(`[NapCat] [WebUi] Get Publish Panel Url Error: ${err}`); - }); - + RequestUtil.HttpGetJson<{ IP: { IP: string } }>('https://ip.011102.xyz/', 'GET', {}, {}, true, true) + .then((data) => { + log( + `[NapCat] [WebUi] WebUi Publish Panel Url: http://${data.IP.IP}:${config.port}${config.prefix}/webui/?token=${config.token}` + ); + }) + .catch((err) => { + logger.logError.bind(logger)(`[NapCat] [WebUi] Get Publish Panel Url Error: ${err}`); + }); }); } diff --git a/src/webui/src/api/BaseInfo.ts b/src/webui/src/api/BaseInfo.ts new file mode 100644 index 00000000..d7e9a825 --- /dev/null +++ b/src/webui/src/api/BaseInfo.ts @@ -0,0 +1,15 @@ +import { RequestHandler } from 'express'; +import { WebUiDataRuntime } from '../helper/Data'; + +export const LogFileListHandler: RequestHandler = async (req, res) => { + res.send({ + code: 0, + data: { + uin: 0, + nick: 'NapCat', + avatar: 'https://q1.qlogo.cn/g?b=qq&nk=0&s=640', + status: 'online', + boottime: Date.now() + } + }); +}; diff --git a/src/webui/src/api/OB11Config.ts b/src/webui/src/api/OB11Config.ts index f2fbd145..60de2264 100644 --- a/src/webui/src/api/OB11Config.ts +++ b/src/webui/src/api/OB11Config.ts @@ -1,12 +1,11 @@ import { RequestHandler } from 'express'; import { WebUiDataRuntime } from '../helper/Data'; import { existsSync, readFileSync } from 'node:fs'; -import { OB11Config } from '@/webui/ui/components/WebUiApiOB11Config'; +import { OneBotConfig } from '@/onebot/config/config'; import { resolve } from 'node:path'; import { webUiPathWrapper } from '@/webui'; -const isEmpty = (data: any) => - data === undefined || data === null || data === ''; +const isEmpty = (data: any) => data === undefined || data === null || data === ''; export const OB11GetConfigHandler: RequestHandler = async (req, res) => { const isLogin = await WebUiDataRuntime.getQQLoginStatus(); if (!isLogin) { @@ -19,15 +18,15 @@ export const OB11GetConfigHandler: RequestHandler = async (req, res) => { const uin = await WebUiDataRuntime.getQQLoginUin(); const configFilePath = resolve(webUiPathWrapper.configPath, `./onebot11_${uin}.json`); //console.log(configFilePath); - let data: OB11Config; + let data: OneBotConfig; try { data = JSON.parse( existsSync(configFilePath) ? readFileSync(configFilePath).toString() - : readFileSync(resolve(webUiPathWrapper.configPath, './onebot11.json')).toString(), + : readFileSync(resolve(webUiPathWrapper.configPath, './onebot11.json')).toString() ); } catch (e) { - data = {} as OB11Config; + data = {} as OneBotConfig; res.send({ code: -1, message: 'Config Get Error', diff --git a/src/webui/src/helper/Data.ts b/src/webui/src/helper/Data.ts index c569d912..9563e6f7 100644 --- a/src/webui/src/helper/Data.ts +++ b/src/webui/src/helper/Data.ts @@ -1,4 +1,4 @@ -import { OB11Config } from '@/onebot/config'; +import { OneBotConfig } from '@/onebot/config/config'; interface LoginRuntimeType { LoginCurrentTime: number; @@ -7,9 +7,9 @@ interface LoginRuntimeType { QQQRCodeURL: string; QQLoginUin: string; NapCatHelper: { - onQuickLoginRequested: (uin: string) => Promise<{ result: boolean, message: string }>; - onOB11ConfigChanged: (ob11: OB11Config) => Promise; - QQLoginList: string[] + onQuickLoginRequested: (uin: string) => Promise<{ result: boolean; message: string }>; + onOB11ConfigChanged: (ob11: OneBotConfig) => Promise; + QQLoginList: string[]; }; } @@ -31,62 +31,62 @@ const LoginRuntime: LoginRuntimeType = { }; export const WebUiDataRuntime = { - checkLoginRate: async function(RateLimit: number): Promise { + checkLoginRate: async function (RateLimit: number): Promise { LoginRuntime.LoginCurrentRate++; //console.log(RateLimit, LoginRuntime.LoginCurrentRate, Date.now() - LoginRuntime.LoginCurrentTime); if (Date.now() - LoginRuntime.LoginCurrentTime > 1000 * 60) { - LoginRuntime.LoginCurrentRate = 0;//超出时间重置限速 + LoginRuntime.LoginCurrentRate = 0; //超出时间重置限速 LoginRuntime.LoginCurrentTime = Date.now(); return true; } return LoginRuntime.LoginCurrentRate <= RateLimit; }, - getQQLoginStatus: async function(): Promise { + getQQLoginStatus: async function (): Promise { return LoginRuntime.QQLoginStatus; }, - setQQLoginStatus: async function(status: boolean): Promise { + setQQLoginStatus: async function (status: boolean): Promise { LoginRuntime.QQLoginStatus = status; }, - setQQLoginQrcodeURL: async function(url: string): Promise { + setQQLoginQrcodeURL: async function (url: string): Promise { LoginRuntime.QQQRCodeURL = url; }, - getQQLoginQrcodeURL: async function(): Promise { + getQQLoginQrcodeURL: async function (): Promise { return LoginRuntime.QQQRCodeURL; }, - setQQLoginUin: async function(uin: string): Promise { + setQQLoginUin: async function (uin: string): Promise { LoginRuntime.QQLoginUin = uin; }, - getQQLoginUin: async function(): Promise { + getQQLoginUin: async function (): Promise { return LoginRuntime.QQLoginUin; }, - getQQQuickLoginList: async function(): Promise { + getQQQuickLoginList: async function (): Promise { return LoginRuntime.NapCatHelper.QQLoginList; }, - setQQQuickLoginList: async function(list: string[]): Promise { + setQQQuickLoginList: async function (list: string[]): Promise { LoginRuntime.NapCatHelper.QQLoginList = list; }, - setQuickLoginCall(func: (uin: string) => Promise<{ result: boolean, message: string }>): void { + setQuickLoginCall(func: (uin: string) => Promise<{ result: boolean; message: string }>): void { LoginRuntime.NapCatHelper.onQuickLoginRequested = func; }, - requestQuickLogin: async function(uin: string): Promise<{ result: boolean, message: string }> { + requestQuickLogin: async function (uin: string): Promise<{ result: boolean; message: string }> { return await LoginRuntime.NapCatHelper.onQuickLoginRequested(uin); }, - setOnOB11ConfigChanged: async function(func: (ob11: OB11Config) => Promise): Promise { + setOnOB11ConfigChanged: async function (func: (ob11: OneBotConfig) => Promise): Promise { LoginRuntime.NapCatHelper.onOB11ConfigChanged = func; }, - setOB11Config: async function(ob11: OB11Config): Promise { + setOB11Config: async function (ob11: OneBotConfig): Promise { await LoginRuntime.NapCatHelper.onOB11ConfigChanged(ob11); }, }; diff --git a/src/webui/ui/NapCat.ts b/src/webui/ui/NapCat.ts deleted file mode 100644 index 7519f00d..00000000 --- a/src/webui/ui/NapCat.ts +++ /dev/null @@ -1,393 +0,0 @@ -import { SettingList } from './components/SettingList'; -import { SettingItem } from './components/SettingItem'; -import { SettingButton } from './components/SettingButton'; -import { SettingSwitch } from './components/SettingSwitch'; -import { SettingSelect } from './components/SettingSelect'; -import { OB11Config, OB11ConfigWrapper } from './components/WebUiApiOB11Config'; - -async function onSettingWindowCreated(view: Element) { - const isEmpty = (value: any) => value === undefined || false || value === ''; - await OB11ConfigWrapper.Init(localStorage.getItem('auth') as string); - const ob11Config: OB11Config = await OB11ConfigWrapper.GetOB11Config(); - const setOB11Config = (key: string, value: any) => { - const configKey = key.split('.'); - if (configKey.length === 2) { - ob11Config[configKey[1]] = value; - } else if (configKey.length === 3) { - ob11Config[configKey[1]][configKey[2]] = value; - } - // OB11ConfigWrapper.SetOB11Config(ob11Config); // 只有当点保存时才下发配置,而不是在修改值后立即下发 - }; - - const parser = new DOMParser(); - const doc = parser.parseFromString( - [ - '
', - ` -
-
`, - SettingList([ - SettingItem( - 'Napcat', - undefined, - SettingButton('V3.3.12', 'napcat-update-button', 'secondary'), - ), - ]), - SettingList([ - SettingItem( - '启用 HTTP 服务', - undefined, - SettingSwitch('ob11.http.enable', ob11Config.http.enable, { - 'control-display-id': 'config-ob11-http-port', - }), - ), - SettingItem( - 'HTTP 服务监听端口', - undefined, - `
`, - 'config-ob11-http-port', - ob11Config.http.enable, - ), - SettingItem( - '启用 HTTP 心跳', - undefined, - SettingSwitch('ob11.http.enableHeart', ob11Config.http.enableHeart, { - 'control-display-id': 'config-ob11-HTTP.enableHeart', - }), - ), - SettingItem( - '启用 HTTP 事件上报', - undefined, - SettingSwitch('ob11.http.enablePost', ob11Config.http.enablePost, { - 'control-display-id': 'config-ob11-http-postUrls', - }), - ), - `
- -
- HTTP 事件上报密钥 -
-
- -
-
- -
- HTTP 事件上报地址 -
- 添加 -
-
-
`, - SettingItem( - '启用正向 WebSocket 服务', - undefined, - SettingSwitch('ob11.ws.enable', ob11Config.ws.enable, { - 'control-display-id': 'config-ob11-ws-port', - }), - ), - SettingItem( - '正向 WebSocket 服务监听端口', - undefined, - `
`, - 'config-ob11-ws-port', - ob11Config.ws.enable, - ), - SettingItem( - '启用反向 WebSocket 服务', - undefined, - SettingSwitch('ob11.reverseWs.enable', ob11Config.reverseWs.enable, { - 'control-display-id': 'config-ob11-reverseWs-urls', - }), - ), - `
- -
- 反向 WebSocket 监听地址 -
- 添加 -
-
-
`, - SettingItem( - ' WebSocket 服务心跳间隔', - '控制每隔多久发送一个心跳包,单位为毫秒', - `
`, - ), - SettingItem( - 'Access token', - undefined, - `
`, - ), - SettingItem( - '新消息上报格式', - '如客户端无特殊需求推荐保持默认设置,两者的详细差异可参考 OneBot v11 文档', - SettingSelect( - [ - { text: '消息段', value: 'array' }, - { text: 'CQ码', value: 'string' }, - ], - 'ob11.messagePostFormat', - ob11Config.messagePostFormat, - ), - ), - SettingItem( - '音乐卡片签名地址', - undefined, - `
`, - 'ob11.musicSignUrl', - ), - SettingItem( - '启用本地进群时间与发言时间记录', - undefined, - SettingSwitch('ob11.GroupLocalTime.Record', ob11Config.GroupLocalTime.Record, { - 'control-display-id': 'config-ob11-GroupLocalTime-RecordList', - }), - ), - `
- -
- 群列表 -
- 添加 -
-
-
`, - SettingItem( - '', - undefined, - SettingButton('保存', 'config-ob11-save', 'primary'), - ), - ]), - SettingList([ - SettingItem( - '上报 Bot 自身发送的消息', - '上报 event 为 message_sent', - SettingSwitch('ob11.reportSelfMessage', ob11Config.reportSelfMessage), - ), - ]), - SettingList([ - SettingItem( - 'GitHub 仓库', - 'https://github.com/NapNeko/NapCatQQ', - SettingButton('点个星星', 'open-github'), - ), - SettingItem('NapCat 文档', '', SettingButton('看看文档', 'open-docs')), - ]), - SettingItem( - 'Telegram 群', - 'https://t.me/+nLZEnpne-pQ1OWFl', - SettingButton('进去逛逛', 'open-telegram'), - ), - SettingItem( - 'QQ 群', - '518662028', - SettingButton('我要进去', 'open-qq-group'), - ), - '
', - ].join(''), - 'text/html', - ); - - // 外链按钮 - doc.querySelector('#open-github')?.addEventListener('click', () => { - window.open('https://github.com/NapNeko/NapCatQQ', '_blank'); - }); - doc.querySelector('#open-docs')?.addEventListener('click', () => { - window.open('https://napneko.github.io/', '_blank'); - }); - doc.querySelector('#open-telegram')?.addEventListener('click', () => { - window.open('https://t.me/+nLZEnpne-pQ1OWFl', '_blank'); - }); - doc.querySelector('#open-qq-group')?.addEventListener('click', () => { - window.open('https://qm.qq.com/q/VfjAq5HIMS', '_blank'); - }); - // 生成反向地址列表 - const buildHostListItem = ( - type: string, - host: string, - index: number, - inputAttrs: any = {}, - ) => { - 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'; - Object.assign(dom.input, inputAttrs); - dom.input.classList.add('q-input__inner'); - dom.input.type = 'url'; - dom.input.value = host; - dom.input.addEventListener('input', () => { - ob11Config[type.split('-')[0]][type.split('-')[1]][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.split('-')[0]][type.split('-')[1]].splice(index, 1); - initReverseHost(type); - }); - - dom.container.appendChild(dom.inputContainer); - dom.container.appendChild(dom.deleteBtn); - - return dom.container; - }; - const buildHostList = ( - hosts: string[], - type: string, - inputAttr: any = {}, - ) => { - const result: HTMLElement[] = []; - - hosts?.forEach((host, index) => { - result.push(buildHostListItem(type, host, index, inputAttr)); - }); - - return result; - }; - const addReverseHost = ( - type: string, - doc: Document = document, - inputAttr: any = {}, - ) => { - type = type.replace(/\./g, '-');//替换操作 - const hostContainerDom = doc.body.querySelector( - `#config-ob11-${type}-list`, - ); - hostContainerDom?.appendChild( - buildHostListItem( - type, - '', - ob11Config[type.split('-')[0]][type.split('-')[1]].length, - inputAttr, - ), - ); - ob11Config[type.split('-')[0]][type.split('-')[1]].push(''); - }; - const initReverseHost = (type: string, doc: Document = document) => { - type = type.replace(/\./g, '-');//替换操作 - const hostContainerDom = doc.body?.querySelector( - `#config-ob11-${type}-list`, - ); - if (hostContainerDom) { - [...hostContainerDom.childNodes].forEach((dom) => dom.remove()); - buildHostList( - ob11Config[type.split('-')[0]][type.split('-')[1]], - type, - ).forEach((dom) => { - hostContainerDom?.appendChild(dom); - }); - } - }; - - initReverseHost('http.postUrls', doc); - initReverseHost('reverseWs.urls', doc); - initReverseHost('GroupLocalTime.RecordList', doc); - - doc - .querySelector('#config-ob11-http-postUrls-add') - ?.addEventListener('click', () => - addReverseHost('http.postUrls', document, { - placeholder: '如:http://127.0.0.1:5140/onebot', - }), - ); - - doc - .querySelector('#config-ob11-reverseWs-urls-add') - ?.addEventListener('click', () => - addReverseHost('reverseWs.urls', document, { - placeholder: '如:ws://127.0.0.1:5140/onebot', - }), - ); - doc - .querySelector('#config-ob11-GroupLocalTime-RecordList-add') - ?.addEventListener('click', () => - addReverseHost('GroupLocalTime.RecordList', document, { - placeholder: '此处填写群号 -1为全部', - }), - ); - doc.querySelector('#config-ffmpeg-select')?.addEventListener('click', () => { - //选择ffmpeg - }); - - doc.querySelector('#config-open-log-path')?.addEventListener('click', () => { - //打开日志 - }); - - // 开关 - doc - .querySelectorAll('setting-switch[data-config-key]') - .forEach((dom: Element) => { - dom.addEventListener('click', () => { - const active = dom.getAttribute('is-active') == undefined; - //@ts-expect-error 等待修复 - setOB11Config(dom.dataset.configKey, active); - if (active) dom.setAttribute('is-active', ''); - else dom.removeAttribute('is-active'); - //@ts-expect-error 等待修复 - if (!isEmpty(dom.dataset.controlDisplayId)) { - const displayDom = document.querySelector( - //@ts-expect-error 等待修复 - `#${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: Element) => { - dom.addEventListener('input', () => { - const Type = dom.getAttribute('type'); - //@ts-expect-error等待修复 - const configKey = dom.dataset.configKey; - const configValue = - Type === 'number' - ? parseInt((dom as HTMLInputElement).value) >= 1 - ? parseInt((dom as HTMLInputElement).value) - : 1 - : (dom as HTMLInputElement).value; - - setOB11Config(configKey, configValue); - }); - }); - - // 下拉框 - doc - .querySelectorAll('ob-setting-select[data-config-key]') - .forEach((dom: Element) => { - //@ts-expect-error等待修复 - dom?.addEventListener('selected', (e: CustomEvent) => { - //@ts-expect-error等待修复 - const configKey = dom.dataset.configKey; - const configValue = e.detail.value; - setOB11Config(configKey, configValue); - }); - }); - - // 保存按钮 - doc.querySelector('#config-ob11-save')?.addEventListener('click', () => { - OB11ConfigWrapper.SetOB11Config(ob11Config); - alert('保存成功'); - }); - doc.body.childNodes.forEach((node) => { - view.appendChild(node); - }); -} - -export { onSettingWindowCreated }; diff --git a/src/webui/ui/components/SettingButton.ts b/src/webui/ui/components/SettingButton.ts deleted file mode 100644 index 02132f3c..00000000 --- a/src/webui/ui/components/SettingButton.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SettingButton = (text: string, id?: string, type: string = 'secondary') => { - return `${text}`; -}; \ No newline at end of file diff --git a/src/webui/ui/components/SettingItem.ts b/src/webui/ui/components/SettingItem.ts deleted file mode 100644 index 11c15426..00000000 --- a/src/webui/ui/components/SettingItem.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const SettingItem = ( - title: string, - subtitle?: string, - action?: string, - id?: string, - visible: boolean = true, -) => { - return ` -
- ${title} - ${subtitle ? `${subtitle}` : ''} -
- ${action ? `
${action}
` : ''} -
`; -}; \ No newline at end of file diff --git a/src/webui/ui/components/SettingList.ts b/src/webui/ui/components/SettingList.ts deleted file mode 100644 index 8db03dc2..00000000 --- a/src/webui/ui/components/SettingList.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const SettingList = ( - items: string[], - title?: string, - isCollapsible: boolean = false, - direction: string = 'column', -) => { - return ` - - - ${items.join('')} - - - `; -}; \ No newline at end of file diff --git a/src/webui/ui/components/SettingOption.ts b/src/webui/ui/components/SettingOption.ts deleted file mode 100644 index ce44e2e4..00000000 --- a/src/webui/ui/components/SettingOption.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SettingOption = (text: string, value?: string, isSelected: boolean = false) => { - return `${text}`; -}; \ No newline at end of file diff --git a/src/webui/ui/components/SettingSelect.ts b/src/webui/ui/components/SettingSelect.ts deleted file mode 100644 index 461f1388..00000000 --- a/src/webui/ui/components/SettingSelect.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { SettingOption } from './SettingOption'; - -interface MouseEventExtend extends MouseEvent { - target: HTMLElement; -} - -// -const SelectTemplate = document.createElement('template'); -SelectTemplate.innerHTML = ` -
-
- - - - -
- -
`; - -window.customElements.define( - 'ob-setting-select', - class extends HTMLElement { - readonly _button: HTMLDivElement; - readonly _text: HTMLInputElement; - readonly _context: HTMLUListElement; - - constructor() { - super(); - - this.attachShadow({ mode: 'open' }); - this.shadowRoot?.append(SelectTemplate.content.cloneNode(true)); - - this._button = this.shadowRoot!.querySelector('div[part="button"]')!; - this._text = this.shadowRoot!.querySelector('input[part="current-text"]')!; - this._context = this.shadowRoot!.querySelector('ul[part="option-list"]')!; - - const buttonClick = () => { - const isHidden = this._context.classList.toggle('hidden'); - window[`${isHidden ? 'remove' : 'add'}EventListener`]('pointerdown', windowPointerDown); - }; - - const windowPointerDown = ({ target }: any) => { - if (!this.contains(target)) buttonClick(); - }; - - this._button.addEventListener('click', buttonClick); - this._context.addEventListener('click', (event) => { - const { target } = event as MouseEventExtend; - - if (target.tagName !== 'SETTING-OPTION') return; - buttonClick(); - - if (target.hasAttribute('is-selected')) return; - - this.querySelectorAll('setting-option[is-selected]').forEach((dom) => dom.toggleAttribute('is-selected')); - target.toggleAttribute('is-selected'); - - this._text.value = target.textContent as string; - this.dispatchEvent( - new CustomEvent('selected', { - bubbles: true, - composed: true, - detail: { - name: target.textContent, - value: target.dataset.value, - }, - }), - ); - }); - - this._text.value = this.querySelector('setting-option[is-selected]')?.textContent as string; - } - }, -); - -export const SettingSelect = (items: Array<{ text: string; value: string }>, configKey?: string, configValue?: any) => { - return ` - ${items - .map((e, i) => { - return SettingOption(e.text, e.value, configKey && configValue ? configValue === e.value : i === 0); - }) - .join('')} -`; -}; diff --git a/src/webui/ui/components/SettingSwitch.ts b/src/webui/ui/components/SettingSwitch.ts deleted file mode 100644 index 65573b7d..00000000 --- a/src/webui/ui/components/SettingSwitch.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const SettingSwitch = (configKey?: string, isActive: boolean = false, extraData?: Record) => { - return ` `data-${key}="${extraData[key]}"`) : ''} - > - `; -}; \ No newline at end of file diff --git a/src/webui/ui/components/WebUiApiOB11Config.ts b/src/webui/ui/components/WebUiApiOB11Config.ts deleted file mode 100644 index 2d510ecd..00000000 --- a/src/webui/ui/components/WebUiApiOB11Config.ts +++ /dev/null @@ -1,79 +0,0 @@ -export interface OB11Config { - [key: string]: any; - - http: { - enable: boolean; - host: ''; - port: number; - secret: ''; - enableHeart: boolean; - enablePost: boolean; - postUrls: string[]; - }; - ws: { - enable: boolean; - host: ''; - port: number; - }; - reverseWs: { - enable: boolean; - urls: string[]; - }; - GroupLocalTime: { - Record: boolean, - RecordList: Array - }; - debug: boolean; - heartInterval: number; - messagePostFormat: 'array' | 'string'; - enableLocalFile2Url: boolean; - musicSignUrl: ''; - reportSelfMessage: boolean; - token: ''; - -} - -class WebUiApiOB11ConfigWrapper { - private retCredential: string = ''; - - async Init(Credential: string) { - this.retCredential = Credential; - } - - async GetOB11Config(): Promise { - const ConfigResponse = await fetch('../api/OB11Config/GetConfig', { - method: 'POST', - headers: { - Authorization: 'Bearer ' + this.retCredential, - 'Content-Type': 'application/json', - }, - }); - if (ConfigResponse.status == 200) { - const ConfigResponseJson = await ConfigResponse.json(); - if (ConfigResponseJson.code == 0) { - return ConfigResponseJson?.data; - } - } - return {} as OB11Config; - } - - async SetOB11Config(config: OB11Config): Promise { - const ConfigResponse = await fetch('../api/OB11Config/SetConfig', { - method: 'POST', - headers: { - Authorization: 'Bearer ' + this.retCredential, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ config: JSON.stringify(config) }), - }); - if (ConfigResponse.status == 200) { - const ConfigResponseJson = await ConfigResponse.json(); - if (ConfigResponseJson.code == 0) { - return true; - } - } - return false; - } -} - -export const OB11ConfigWrapper = new WebUiApiOB11ConfigWrapper(); diff --git a/src/webui/vite.config.ts b/src/webui/vite.config.ts deleted file mode 100644 index 1cffeba7..00000000 --- a/src/webui/vite.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineConfig } from 'vite'; - -export default defineConfig({ - build: { - target: 'esnext', - minify: false, - lib: { - entry: 'ui/NapCat.ts', - formats: ['es'], - fileName: () => 'renderer.js', - }, - }, -}); diff --git a/static/QQLogin.html b/static/QQLogin.html deleted file mode 100644 index 0714ebf6..00000000 --- a/static/QQLogin.html +++ /dev/null @@ -1,294 +0,0 @@ - - - - - - - NapCat - WebUi - - - - - - - - - - \ No newline at end of file diff --git a/static/assets/NapCat.css b/static/assets/NapCat.css deleted file mode 100644 index f82825c6..00000000 --- a/static/assets/NapCat.css +++ /dev/null @@ -1,180 +0,0 @@ -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); -} - -ob-setting-select { - width: 100px; -} - -ob-setting-select, -ob-setting-select::part(parent), -ob-setting-select::part(button) { - display: block; - position: relative; - height: 24px; - font-size: 12px; - line-height: 24px; - box-sizing: border-box; -} - -ob-setting-select::part(button) { - display: flex; - padding: 0px 8px; - background-color: transparent; - border-radius: 4px; - border: 1px solid var(--border_dark); - z-index: 5; - cursor: default; - align-items: center; - flex-direction: row; - flex-wrap: nowrap; -} - -ob-setting-select::part(current-text) { - display: block; - margin-right: 8px; - padding: 0px; - background: none; - background-color: transparent; - font-size: 12px; - color: var(--text_primary); - text-overflow: ellipsis; - border-radius: 0px; - border: none; - outline: none; - overflow: hidden; - appearance: none; - box-sizing: border-box; - cursor: default; - flex: 1; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -o-user-select: none; - user-select: none; - -webkit-pointer-events: none; - -moz-pointer-events: none; - -ms-pointer-events: none; - -o-pointer-events: none; - pointer-events: none; -} - -ob-setting-select::part(button-arrow) { - position: relative; - display: block; - width: 16px; - height: 16px; - color: var(--icon_primary); -} - -ob-setting-select::part(option-list) { - display: flex; - position: absolute; - top: 100%; - padding: 4px; - margin: 5px 0px; - width: 100%; - max-height: var(--q-contextmenu-max-height); - background-color: var(--blur_middle_standard); - background-clip: padding-box; - backdrop-filter: blur(8px); - font-size: 12px; - box-shadow: var(--shadow_bg_middle_secondary); - border: 1px solid var(--border_secondary); - border-radius: 4px; - box-sizing: border-box; - app-region: no-drag; - overflow-x: hidden; - overflow-y: auto; - list-style: none; - z-index: 999; - flex-direction: column; - align-items: stretch; - flex-wrap: nowrap; - justify-content: flex-start; - gap: 4px; -} - -#napcat-error { - display: none; -} - -#napcat-error setting-panel { - background: rgba(255, 0, 0, 0.5); - color: white; -} - -#napcat-error setting-panel pre { - margin: 0; - padding: 16px; - box-sizing: border-box; -} - -#napcat-error setting-panel pre code { - font-family: 'FiraCode Nerd Font', 'Fira Code', 'Cascadia Code', Consolas, 'Courier New', monospace; -} - -#napcat-error.show { - display: block; -} \ No newline at end of file diff --git a/static/assets/color.css b/static/assets/color.css deleted file mode 100644 index 0f77d861..00000000 --- a/static/assets/color.css +++ /dev/null @@ -1,679 +0,0 @@ -:root { - --font-bold: 400; - --font_size_1: 10px; - --font_size_2: 12px; - --font_size_3: 14px; - --font_size_4: 16px; - --font_size_5: 18px; - --avatar_size_1: 20px; - --avatar_size_2: 32px; - --avatar_size_3: 40px; - --font_size_main_1: 12px; - --font_size_main_2: 14px; - --line_height_1: 14px; - --line_height_2: 16px; - --line_height_3: 20px; - --line_height_4: 22px; - --line_height_5: 24px; - --line_height_main_1: 18px; - --line_height_main_2: 22px; - - --shadow_card_rest: 0px 2px 4px rgba(0, 0, 0, 0.12); - --shadow_tooltip: 0px 4px 8px rgba(0, 0, 0, 0.26); - --shadow_flyout: 0px 8px 16px rgba(0, 0, 0, 0.14); - --shadow_dialog: 0px 30px 60px rgba(0, 0, 0, 0.36) 0 2px 20px rgba(0, 0, 0, 0.37); - - --blend_brightness_white_004: 1.04; - --blend_brightness_white_008: 1.08; - --blend_brightness_white_010: 1.10; - --blend_brightness_white_016: 1.16; - --blend_brightness_white_020: 1.20; - --blend_brightness_black_004: 0.96; - --blend_brightness_black_008: 0.92; - --blend_brightness_black_010: 0.90; - --blend_brightness_black_016: 0.84; - --blend_brightness_black_020: 0.80; - --blend_white_004: rgba(255, 255, 255, 0.04); - --blend_white_008: rgba(255, 255, 255, 0.08); - --blend_white_010: rgba(255, 255, 255, 0.10); - --blend_white_016: rgba(255, 255, 255, 0.16); - --blend_white_020: rgba(255, 255, 255, 0.20); - --blend_black_004: rgba(0, 0, 0, 0.04); - --blend_black_008: rgba(0, 0, 0, 0.08); - --blend_black_010: rgba(0, 0, 0, 0.10); - --blend_black_016: rgba(0, 0, 0, 0.16); - --blend_black_020: rgba(0, 0, 0, 0.20); - --blend_transparent: rgba(0, 0, 0, 0); - - --el-color-white: #fff; - --el-color-black: #000; - --el-color-primary: #409eff; - --el-color-primary-rgb: 64,158,255; - --el-color-success-rgb: 103,194,58; - --el-color-warning-rgb: 230,162,60; - --el-color-danger-rgb: 245,108,108; - --el-color-error-rgb: 245,108,108; - --el-color-info-rgb: 144,147,153; - --el-color-primary-light-1: #53a8ff; - --el-color-primary-light-2: #66b1ff; - --el-color-primary-light-3: #79bbff; - --el-color-primary-light-4: #8cc5ff; - --el-color-primary-light-5: #a0cfff; - --el-color-primary-light-6: #b3d8ff; - --el-color-primary-light-7: #c6e2ff; - --el-color-primary-light-8: #d9ecff; - --el-color-primary-light-9: #ecf5ff; - --el-color-primary-dark-2: #337ecc; - --el-color-success: #67c23a; - --el-color-success-light-3: #95d475; - --el-color-success-light-5: #b3e19d; - --el-color-success-light-7: #d1edc4; - --el-color-success-light-8: #e1f3d8; - --el-color-success-light-9: #f0f9eb; - --el-color-success-dark-2: #529b2e; - --el-color-warning: #e6a23c; - --el-color-warning-light-3: #eebe77; - --el-color-warning-light-5: #f3d19e; - --el-color-warning-light-7: #f8e3c5; - --el-color-warning-light-8: #faecd8; - --el-color-warning-light-9: #fdf6ec; - --el-color-warning-dark-2: #b88230; - --el-color-danger: #f56c6c; - --el-color-danger-light-3: #f89898; - --el-color-danger-light-5: #fab6b6; - --el-color-danger-light-7: #fcd3d3; - --el-color-danger-light-8: #fde2e2; - --el-color-danger-light-9: #fef0f0; - --el-color-danger-dark-2: #c45656; - --el-color-error: #f56c6c; - --el-color-error-light-3: #f89898; - --el-color-error-light-5: #fab6b6; - --el-color-error-light-7: #fcd3d3; - --el-color-error-light-8: #fde2e2; - --el-color-error-light-9: #fef0f0; - --el-color-error-dark-2: #c45656; - --el-color-info: #909399; - --el-color-info-light-3: #b1b3b8; - --el-color-info-light-5: #c8c9cc; - --el-color-info-light-7: #dedfe0; - --el-color-info-light-8: #e9e9eb; - --el-color-info-light-9: #f4f4f5; - --el-color-info-dark-2: #73767a; - --el-bg-color: #fff; - --el-bg-color-page: #fff; - --el-bg-color-overlay: #fff; - --el-text-color-primary: #303133; - --el-text-color-regular: #606266; - --el-text-color-secondary: #909399; - --el-text-color-placeholder: #a8abb2; - --el-text-color-disabled: #c0c4cc; - --el-border-color: #dcdfe6; - --el-border-color-light: #e4e7ed; - --el-border-color-lighter: #ebeef5; - --el-border-color-extra-light: #f2f6fc; - --el-border-color-dark: #d4d7de; - --el-border-color-darker: #cdd0d6; - --el-fill-color: #f0f2f5; - --el-fill-color-light: #f5f7fa; - --el-fill-color-lighter: #fafafa; - --el-fill-color-extra-light: #fafcff; - --el-fill-color-dark: #ebedf0; - --el-fill-color-darker: #e6e8eb; - --el-fill-color-blank: #fff; - --el-border-radius-base: 4px; - --el-border-radius-small: 2px; - --el-border-radius-round: 20px; - --el-border-radius-circle: 100%; - --el-font-size-extra-large: 20px; - --el-font-size-large: 18px; - --el-font-size-medium: 16px; - --el-font-size-base: 14px; - --el-font-size-small: 13px; - --el-font-size-extra-small: 12px; - --el-font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; - --el-box-shadow: 0px 12px 32px 4px rgba(0,0,0,.04),0px 8px 20px rgba(0,0,0,.08); - --el-box-shadow-light: 0px 0px 12px rgba(0,0,0,.12); - --el-box-shadow-lighter: 0px 0px 6px rgba(0,0,0,.12); - --el-box-shadow-dark: 0px 16px 48px 16px rgba(0,0,0,.08),0px 12px 32px rgba(0,0,0,.12),0px 8px 16px -8px rgba(0,0,0,.16); - --el-disabled-bg-color: var(--el-fill-color-light); - --el-disabled-text-color: var(--el-text-color-placeholder); - --el-disabled-border-color: var(--el-border-color-light); - --el-index-normal: 1; - --el-index-top: 1000; - --el-index-popper: 2000; - --el-overlay-color: rgba(0,0,0,.8); - --el-overlay-color-light: rgba(0,0,0,.7); - --el-overlay-color-lighter: rgba(0,0,0,.5); - --el-mask-color: hsla(0,0%,100%,.9); - --el-mask-color-extra-light: hsla(0,0%,100%,.3); - --el-border-width: 1px; - --el-border-style: solid; - --el-border-color-hover: var(--el-text-color-disabled); - --el-border: var(--el-border-width) var(--el-border-style) var(--el-border-color); - --el-svg-monochrome-grey: var(--el-border-color); - --el-font-weight-primary: 500; - --el-font-line-height-primary: 24px; - --el-transition-duration: 0.3s; - --el-transition-duration-fast: 0.2s; - --el-transition-function-ease-in-out-bezier: cubic-bezier(0.645,0.045,0.355,1); - --el-transition-function-fast-bezier: cubic-bezier(0.23,1,0.32,1); - --el-transition-all: all var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier); - --el-transition-fade: opacity var(--el-transition-duration) var(--el-transition-function-fast-bezier); - --el-transition-md-fade: transform var(--el-transition-duration) var(--el-transition-function-fast-bezier),opacity var(--el-transition-duration) var(--el-transition-function-fast-bezier); - --el-transition-fade-linear: opacity var(--el-transition-duration-fast) linear; - --el-transition-border: border-color var(--el-transition-duration-fast) var(--el-transition-function-ease-in-out-bezier); - --el-transition-box-shadow: box-shadow var(--el-transition-duration-fast) var(--el-transition-function-ease-in-out-bezier); - --el-transition-color: color var(--el-transition-duration-fast) var(--el-transition-function-ease-in-out-bezier); - - --nt_brand_standard_2_overlay_hover_brand_2_mix: #008debff; - --nt_brand_standard_2_overlay_pressed_brand_2_mix: #0080d6ff; - --nt_feedback_error_2_overlay_hover_brand_2_mix: #e4462cff; - --nt_feedback_error_2_overlay_pressed_brand_2_mix: #cf4028ff; - --nt_icon_white_2_overlay_hover_2_mix: #f5f5f5ff; - --nt_icon_white_2_overlay_pressed_2_mix: #e0e0e0ff; - --nt_bg_top_light_2_overlay_hover_2_mix: #f5f5f5ff; - --nt_bg_top_light_2_overlay_pressed_2_mix: #e0e0e0ff; - --nt_icon_secondary_02_2_0_2_alpha: rgba(204, 204, 204, 0); - --nt_icon_secondary_02_2_70_2_alpha: rgba(204, 204, 204, 0.7); - --nt_text_link_2_50_2_alpha: rgba(45, 119, 229, 0.5); - --nt_bubble_host_2_overlay_pressed_brand_2_mix: #0080d6ff; - --nt_bg_white_2_overlay_pressed_brand_2_mix: #d6d6d6ff; - --nt_bg_white_2_overlay_hover_2_mix: #f5f5f5ff; - --nt_bg_white_2_overlay_pressed_2_mix: #e0e0e0ff; - --nt_fg_white_2_overlay_hover_2_mix: #f5f5f5ff; - --nt_fg_white_2_overlay_pressed_2_mix: #e0e0e0ff; - --nt_icon_red_2_overlay_hover_2_mix: #f5314fff; - --nt_icon_red_2_overlay_pressed_2_mix: #e02d48ff; - --nt_fg_grey_standard_2_overlay_hover_2_mix: #0000003b; - --nt_fg_grey_standard_2_overlay_pressed_2_mix: #0000004c; - --nt_bubble_guest_2_overlay_pressed_2_mix: #e0e0e0ff; - --nt_icon_primary_2_20_2_alpha: rgba(0, 0, 0, 0.2); - --nt_bg_grey_standard_2_95_2_alpha: rgba(242, 242, 242, 0.95); - --nt_tag_red_2_20_2_alpha: rgba(255, 134, 46, 0.2); - --nt_tag_red_2_25_2_alpha: rgba(255, 134, 46, 0.25); - --nt_tag_blue_2_20_2_alpha: rgba(0, 153, 255, 0.2); - --nt_tag_blue_2_25_2_alpha: rgba(0, 153, 255, 0.25); - --nt_tag_blue_2_10_2_alpha: rgba(0, 153, 255, 0.1); - --nt_brand_standard_2_20_2_alpha: rgba(0, 153, 255, 0.2); - --nt_feedback_error_2_20_2_alpha: rgba(247, 76, 48, 0.2); - --nt_text_white_2_60_2_alpha: rgba(255, 255, 255, 0.6); - --nt_bg_white_2_70_2_alpha: rgba(255, 255, 255, 0.7); - --nt_bg_white_2_90_2_alpha: rgba(255, 255, 255, 0.9); - --nt_bg_white_2_97_2_alpha: rgba(255, 255, 255, 0.97); - --nt_bg_white_2_40_2_alpha: rgba(255, 255, 255, 0.4); - --nt_bg_white_2_30_2_alpha: rgba(255, 255, 255, 0.3); - --nt_text_white_2_80_2_alpha: rgba(255, 255, 255, 0.8); - --nt_brand_standard_2_50_2_alpha: rgba(0, 153, 255, 0.5); - --nt_bg_nav_secondary_2_60_2_alpha: rgba(255, 255, 255, 0.6); - --nt_bg_nav_2_60_2_alpha: rgba(242, 242, 242, 0.6); - --nt_feedback_error_2_10_2_alpha: rgba(247, 76, 48, 0.1); - --nt_brand_standard_2_10_2_alpha: rgba(0, 153, 255, 0.1); - --nt_on_brand_primary_2_40_2_alpha: rgba(255, 255, 255, 0.4); - --nt_text_primary_2_72_2_alpha: rgba(0, 0, 0, 0.72); - --nt_text_white_2_72_2_alpha: rgba(255, 255, 255, 0.72); - - --border_secondary: 1px solid rgba(0, 0, 0, 0.0578); - --border_primary: 1px solid rgba(117, 117, 117, 0.4); - - --shadow_bg_top: 0px 4px 8px rgba(0, 0, 0, 0.14); - --shadow_bg_middle_secondary: 0px 8px 16px rgba(0, 0, 0, 0.14); - --shadow_bg_middle_primary: 0px 32px 64px rgba(0, 0, 0, 0.1876), 0px 2px 21px rgba(0, 0, 0, 0.1474); - --shadow_bg_bottom_inactive: 0px 16px 32px rgba(0, 0, 0, 0.1876), 0px 2px 10.67px rgba(0, 0, 0, 0.1474); - --shadow_bg_bottom_active: 0px 32px 64px rgba(0, 0, 0, 0.28), 0px 2px 21px rgba(0, 0, 0, 0.22); - - --brand_standard: #0099ffff; - --on_brand_primary: #ffffffff; - --on_brand_secondary: #ffffffff; - --text_primary: #000000ff; - --text_primary_light: #00000099; - --text_secondary_02: #ccccccff; - --text_white: #ffffffff; - --text_secondary_01: #999999ff; - --text_black: #000000ff; - --on_bg_text: #999999ff; - --text_link: #2d77e5ff; - --text_secondary: #00000080; - --text_tertiary: #0000004d; - --icon_primary: #000000ff; - --icon_secondary_01: #999999ff; - --icon_secondary_02: #ccccccff; - --icon_white: #ffffffff; - --icon_red: #ff3352ff; - --icon_black: #000000ff; - --icon_secondary: #00000066; - --icon_tertiary: #0000004d; - --feedback_success: #15d173ff; - --feedback_warning: #ffb300ff; - --feedback_error: #f74c30ff; - --bg_grey_standard: #f2f2f2ff; - --bg_white: #ffffffff; - --bg_list: #ffffffff; - --bg_aio_1: #f2f2f2ff; - --bg_aio_2: #f2f2f2ff; - --bg_aio_3: #f2f2f2ff; - --bg_aio_4: #f2f2f2ff; - --bg_nav: #f2f2f2ff; - --mac_bg_nav: #ffffff1a; - --bg_bottom_standard: #f2f2f2ff; - --bg_bottom_light: #ffffffff; - --bg_middle_standard: #f2f2f2ff; - --bg_middle_light: #ffffffff; - --bg_top_standard: #f2f2f2ff; - --bg_top_light: #ffffffff; - --bg_nav_secondary: #ffffffff; - --bubble_host: #0099ffff; - --bubble_guest: #ffffffff; - --bubble_host_text: #ffffff; - --bubble_guest_text: #000000ff; - --bubble_host_1: #0099ffff; - --bubble_host_2: #0099ffff; - --fg_grey_standard: #00000033; - --fg_white: #ffffffff; - --fg_grey_light: #0000000a; - --fill_standard_secondary: #0000000a; - --fill_standard_primary: #00000033; - --fill_light_primary: #ffffffff; - --fill_light_secondary: #ffffffff; - --divider_standard: #0000000a; - --divider_dark: #00000014; - --border_standard: #0000000a; - --border_dark: #00000014; - --overlay_hover: #0000000a; - --overlay_hover_brand: #00000014; - --overlay_pressed_brand: #00000029; - --overlay_active_brand: #0099ffff; - --overlay_top: #0000000f; - --overlay_mask_standard: #00000080; - --overlay_mask_dark: #00000099; - --overlay_pressed: #0000001f; - --overlay_active: #00000014; - --overlay_mask_aio: #00000000; - --blur_standard: #ffffffcc; - --blur_superlight: #ffffff1a; - --blur_middle_standard: #ffffffcc; - --blur_bottom_superlight: #ffffff1a; - --extend_blue: #eaf1ffff; - --svip_red: #ff4222ff; - --tag_sage_green_bg: #a3c4c633; - --tag_sage_green_text: #769698ff; - --tag_red_bg: #ff3f3233; - --tag_red_text: #f74c30ff; - --tag_orange_text: #ff8d40ff; - --tag_orange_bg: #ff862e33; - --tag_purple_text: #aa76f6ff; - --tag_purple_bg: #b27eff33; - --tag_blue_text: #0099ffff; - --tag_blue_bg: #0099ff33; - --tag_blue: #0099ff33; - --tag_red: #ff862e33; - --border_white: #ffffffff; - --border_secondary: #0000000f; - --border_primary: #75757566; - --mac_border_primary: #00000014; - --mac_border_secondary: #0000000a; - --host_bubble_bg_css_value: #0099ff; - --on_bubble_host_text: #ffffffff; - --brand_text: #0099ffff; - - --text-primary: #000; - --text-primary-light: #666; - --text-secondary-01: #999; - --text-secondary-02: #ccc; - --text-white: #fff; - --text-brand: #0099ff; - --text-link: #2d77e5; - --text-success: #12d173; - --text-warning: #ffb300; - --text-error: #ff5967; - --icon-primary: #000; - --icon-secondary-01: #999999; - --icon-secondary-02: #cccccc; - --icon-white: #fff; - --icon-brand: #0099ff; - --icon-success: #15d173; - --icon-warning: #ffb300; - --icon-error: #ff5967; - --button-primary-default: #0099ff; - --button-primary-hover: #4DB7FF; - --button-primary-pressed: #0089E5; - --button-primary-disable: #CCEBFF; - --button-secondary-default: #ccc; - --button-secondary-hover: #E5E5E5; - --button-secondary-pressed: #B2B2B2; - --button-secondary-disable: #F0F0F0; - --button-white-default: #FFFFFF; - --button-white-hover: #E5E5E5; - --button-white-pressed: #B2B2B2; - --button-white-disable: #ffffff4d; - --button-error-default: #FF5967; - --button-error-hover: #FF8B94; - --button-error-pressed: #E6505C; - --button-error-disable: #FFEEEF; - --bubble-host: #0099FF; - --bubble-guest: #EBEBEB; - --divider-standard: #E5E5E5; - --divider-light: #F5F5F5; - --divider-brand: #0099FF; - --background-01: #000; - --background-02: #E5E5E5; - --background-03: #F5F5F5; - --background-04: #FAFAFA; - --background-05: #FFFFFF; - --background-dialogue: #FFFFFF; - --hover-list: #F0F0F0; - --hover-icon: #EBEBEB; - --hover-link: #81ADEF; - --press-list: #E5E5E5; - --press-icon: #E5E5E5; - --press-link: #286BCE; - --badge-brand: #0099FF; - --badge-red: #FF5967; - --badge-grey: #CCCCCC; - --audio-hangup: #FF3350; - --gray-black: #000; - --gray-20: #333333; - --gray-40: #666666; - --gray-60: #999999; - --gray-80: #cccccc; - --gray-90: #e5e5e5; - --gray-96: #f5f5f5; - --gray-white: #ffffff; - --blue-dark: #0089E5; - --blue-standard: #0099FF; - --blue-light: #4DB7FF; - --blue-superlight: #E6F5FF; - --green-dark: #12BC67; - --green-standard: #15D173; - --green-light: #5BDE9D; - --green-superlight: #E8FAF1; - --yellow-dark: #E5A000; - --yellow-standard: #FFB300; - --yellow-light: #FFC94C; - --yellow-superlight: #FFF7E5; - --orange-dark: #E57E39; - --orange-standard: #FF8D40; - --orange-light: #FFAE78; - --orange-superlight: #FFF3EB; - --red-dark: #E6505C; - --red-standard: #FF5967; - --red-light: #FF8B94; - --red-superlight: #FFEEEF; - --pink-dark: #E55BA0; - --pink-standard: #FF66B3; - --pink-light: #FF93C9; - --pink-superlight: #FFEFF7; - --indigo-dark: #775CE6; - --indigo-standard: #8566FF; - --indigo-light: #A994FF; - --indigo-superlight: #F3F0FF; - --list-hover: rgba(243, 243, 243); - --list-pressed: rgba(226, 226, 226); - --background_01: #000000; - --background_02: #E6E6E6; - --background_03: #F5F5F5; - --background_04: #FAFAFA; - --background_05: #FFFFFF; - --background_dialogue: #FFFFFF; - --sidebar_win: #ebebeb; - --sidebar_mac: rgba(255,255,255,0.1); - - --nt_mix_tokens: nt_brand_standard_2_overlay_hover_brand_2_mix,nt_brand_standard_2_overlay_pressed_brand_2_mix,nt_feedback_error_2_overlay_hover_brand_2_mix,nt_feedback_error_2_overlay_pressed_brand_2_mix,nt_icon_white_2_overlay_hover_2_mix,nt_icon_white_2_overlay_pressed_2_mix,nt_bubble_host_2_overlay_pressed_brand_2_mix,nt_bg_white_2_overlay_pressed_brand_2_mix,nt_bg_white_2_overlay_hover_2_mix,nt_bg_white_2_overlay_pressed_2_mix,nt_fg_white_2_overlay_hover_2_mix,nt_fg_white_2_overlay_pressed_2_mix,nt_icon_red_2_overlay_hover_2_mix,nt_icon_red_2_overlay_pressed_2_mix,nt_fg_grey_standard_2_overlay_hover_2_mix,nt_fg_grey_standard_2_overlay_pressed_2_mix,nt_bubble_guest_2_overlay_pressed_2_mix,nt_icon_primary_2_20_2_alpha,nt_bg_grey_standard_2_95_2_alpha,nt_tag_red_2_20_2_alpha,nt_tag_red_2_25_2_alpha,nt_tag_blue_2_20_2_alpha,nt_tag_blue_2_25_2_alpha,nt_tag_blue_2_10_2_alpha,nt_tag_purple_2_20_2_alpha,nt_brand_standard_2_20_2_alpha,nt_tag_sage_green_2_20_2_alpha,nt_feedback_error_2_20_2_alpha,nt_text_white_2_60_2_alpha,nt_bg_white_2_70_2_alpha,nt_bg_white_2_90_2_alpha,nt_bg_white_2_97_2_alpha,nt_bg_white_2_40_2_alpha,nt_bg_white_2_30_2_alpha,nt_text_white_2_80_2_alpha,nt_brand_standard_2_50_2_alpha,nt_bg_nav_secondary_2_60_2_alpha,nt_bg_nav_2_60_2_alpha,nt_feedback_error_2_10_2_alpha,nt_brand_standard_2_10_2_alpha,nt_on_brand_primary_2_40_2_alpha,nt_text_primary_2_72_2_alpha,nt_text_white_2_72_2_alpha; - - color-scheme: light; - } - - .q-theme-tokens-dark { - --nt_brand_standard_2_overlay_hover_brand_2_mix: #1472d0ff; - --nt_brand_standard_2_overlay_pressed_brand_2_mix: #0056abff; - --nt_feedback_error_2_overlay_hover_brand_2_mix: #f85a40ff; - --nt_feedback_error_2_overlay_pressed_brand_2_mix: #cf4028ff; - --nt_icon_white_2_overlay_hover_2_mix: #ffffffff; - --nt_icon_white_2_overlay_pressed_2_mix: #d6d6d6ff; - --nt_bg_top_light_2_overlay_hover_2_mix: #404040ff; - --nt_bg_top_light_2_overlay_pressed_2_mix: #282828ff; - --nt_icon_secondary_02_2_0_2_alpha: rgba(77, 77, 77, 0); - --nt_icon_secondary_02_2_70_2_alpha: rgba(77, 77, 77, 0.7); - --nt_text_link_2_50_2_alpha: rgba(45, 119, 229, 0.5); - --nt_bubble_host_2_overlay_pressed_brand_2_mix: #202020ff; - --nt_bg_white_2_overlay_pressed_brand_2_mix: #202020ff; - --nt_bg_white_2_overlay_hover_2_mix: #373737ff; - --nt_bg_white_2_overlay_pressed_2_mix: #202020ff; - --nt_fg_white_2_overlay_hover_2_mix: #373737ff; - --nt_fg_white_2_overlay_pressed_2_mix: #202020ff; - --nt_icon_red_2_overlay_hover_2_mix: #ff4360ff; - --nt_icon_red_2_overlay_pressed_2_mix: #d62b45ff; - --nt_fg_grey_standard_2_overlay_hover_2_mix: #ffffff43; - --nt_fg_grey_standard_2_overlay_pressed_2_mix: #82828254; - --nt_bubble_guest_2_overlay_pressed_2_mix: #202020ff; - --nt_icon_primary_2_20_2_alpha: rgba(255, 255, 255, 0.2); - --nt_bg_grey_standard_2_95_2_alpha: rgba(26, 26, 26, 0.95); - --nt_tag_red_2_20_2_alpha: rgba(255, 134, 46, 0.2); - --nt_tag_red_2_25_2_alpha: rgba(255, 134, 46, 0.25); - --nt_tag_blue_2_20_2_alpha: rgba(0, 153, 255, 0.2); - --nt_tag_blue_2_25_2_alpha: rgba(0, 153, 255, 0.25); - --nt_tag_blue_2_10_2_alpha: rgba(0, 153, 255, 0.1); - --nt_brand_standard_2_20_2_alpha: rgba(0, 102, 204, 0.2); - --nt_feedback_error_2_20_2_alpha: rgba(247, 76, 48, 0.2); - --nt_text_white_2_60_2_alpha: rgba(255, 255, 255, 0.6); - --nt_bg_white_2_70_2_alpha: rgba(38, 38, 38, 0.7); - --nt_bg_white_2_90_2_alpha: rgba(38, 38, 38, 0.9); - --nt_bg_white_2_97_2_alpha: rgba(38, 38, 38, 0.97); - --nt_bg_white_2_40_2_alpha: rgba(38, 38, 38, 0.4); - --nt_bg_white_2_30_2_alpha: rgba(38, 38, 38, 0.3); - --nt_text_white_2_80_2_alpha: rgba(255, 255, 255, 0.8); - --nt_brand_standard_2_50_2_alpha: rgba(0, 102, 204, 0.5); - --nt_bg_nav_secondary_2_60_2_alpha: rgba(27, 27, 27, 0.6); - --nt_bg_nav_2_60_2_alpha: rgba(17, 17, 17, 0.6); - --nt_feedback_error_2_10_2_alpha: rgba(247, 76, 48, 0.1); - --nt_brand_standard_2_10_2_alpha: rgba(0, 102, 204, 0.1); - --nt_on_brand_primary_2_40_2_alpha: rgba(255, 255, 255, 0.4); - --nt_text_primary_2_72_2_alpha: rgba(255, 255, 255, 0.72); - --nt_text_white_2_72_2_alpha: rgba(255, 255, 255, 0.72); - - --border_secondary: 1px solid rgba(0, 0, 0, 0.2); - --border_primary: 1px solid rgba(117, 117, 117, 0.4); - - --shadow_bg_top: 0px 4px 8px rgba(0, 0, 0, 0.26); - --shadow_bg_middle_secondary: 0px 8px 16px rgba(0, 0, 0, 0.14); - --shadow_bg_middle_primary: 0px 32px 64px rgba(0, 0, 0, 0.37), 0px 2px 21px rgba(0, 0, 0, 0.37); - --shadow_bg_bottom_inactive: 0px 32px 64px rgba(0, 0, 0, 0.56), 0px 2px 21px rgba(0, 0, 0, 0.55); - --shadow_bg_bottom_active: 0px 32px 64px rgba(0, 0, 0, 0.56), 0px 2px 21px rgba(0, 0, 0, 0.55); - - --brand_standard: #0066ccff; - --on_brand_primary: #ffffffff; - --on_brand_secondary: #ffffffff; - --text_primary: #ffffffe6; - --text_primary_light: #ffffff99; - --text_secondary_02: #666666ff; - --text_white: #ffffffe6; - --text_secondary_01: #808080ff; - --text_black: #000000ff; - --on_bg_text: #808080ff; - --text_link: #2d77e5ff; - --text_secondary: #ffffff99; - --text_tertiary: #ffffff66; - --icon_primary: #ffffffb3; - --icon_secondary_01: #666666ff; - --icon_secondary_02: #4d4d4dff; - --icon_white: #ffffffff; - --icon_red: #ff3352ff; - --icon_black: #000000ff; - --icon_secondary: #ffffff80; - --icon_tertiary: #ffffff66; - --feedback_success: #15d173ff; - --feedback_warning: #ffb300ff; - --feedback_error: #f74c30ff; - --bg_grey_standard: #1a1a1aff; - --bg_white: #262626ff; - --bg_list: #1b1b1bff; - --bg_aio_1: #1a1a1aff; - --bg_aio_2: #1a1a1aff; - --bg_aio_3: #1a1a1aff; - --bg_aio_4: #1a1a1aff; - --bg_nav: #111111ff; - --mac_bg_nav: #0000001a; - --bg_bottom_standard: #111111ff; - --bg_bottom_light: #1b1b1bff; - --bg_middle_standard: #1b1b1bff; - --bg_middle_light: #262626ff; - --bg_top_standard: #262626ff; - --bg_top_light: #303030ff; - --bg_nav_secondary: #1b1b1bff; - --bubble_host: #262626ff; - --bubble_guest: #262626ff; - --bubble_host_text: #f2f2f2; - --bubble_guest_text: #f2f2f2ff; - --bubble_host_1: #262626ff; - --bubble_host_2: #262626ff; - --fg_grey_standard: #ffffff33; - --fg_white: #262626ff; - --fg_grey_light: #00000033; - --fill_standard_secondary: #ffffff0f; - --fill_standard_primary: #ffffff33; - --fill_light_primary: #262626ff; - --fill_light_secondary: #ffffff0f; - --divider_standard: #ffffff0a; - --divider_dark: #ffffff14; - --border_standard: #ffffff0f; - --border_dark: #ffffff14; - --overlay_hover: #ffffff14; - --overlay_hover_brand: #ffffff14; - --overlay_pressed_brand: #00000029; - --overlay_active_brand: #0066ccff; - --overlay_top: #ffffff0f; - --overlay_mask_standard: #00000080; - --overlay_mask_dark: #00000099; - --overlay_pressed: #00000029; - --overlay_active: #ffffff1f; - --overlay_mask_aio: #00000000; - --blur_standard: #000000cc; - --blur_superlight: #0000001a; - --blur_middle_standard: #262626cc; - --blur_bottom_superlight: #0000001a; - --extend_blue: #002f65ff; - --svip_red: #ff4222ff; - --tag_sage_green_bg: #a3c4c633; - --tag_sage_green_text: #769698ff; - --tag_red_bg: #ff3f3233; - --tag_red_text: #f74c30ff; - --tag_orange_text: #ff8d40ff; - --tag_orange_bg: #ff862e33; - --tag_purple_text: #aa76f6ff; - --tag_purple_bg: #b27eff33; - --tag_blue_text: #0066ccff; - --tag_blue_bg: #0099ff33; - --tag_blue: #0099ff40; - --tag_red: #ff862e33; - --border_white: #262626ff; - --border_secondary: #00000033; - --border_primary: #75757566; - --mac_border_primary: #ffffff14; - --mac_border_secondary: #ffffff0a; - --host_bubble_bg_css_value: #262626; - --on_bubble_host_text: #f2f2f2ff; - --brand_text: #0066ccff; - - --text-primary: #FFFFFF; - --text-primary-light: #CCCCCC; - --text-secondary-01: #999; - --text-secondary-02: #666666; - --text-white: #fff; - --text-brand: #0099ff; - --text-link: #2d77e5; - --text-success: #12d173; - --text-warning: #ffb300; - --text-error: #ff5967; - --icon-primary: #999999; - --icon-secondary-01: #999999; - --icon-secondary-02: #999999; - --icon-white: #fff; - --icon-brand: #0099ff; - --icon-success: #15d173; - --icon-warning: #ffb300; - --icon-error: #ff5967; - --button-primary-default: #0066CC; - --button-primary-hover: #19467F; - --button-primary-pressed: #0A1F33; - --button-primary-disable: #232323; - --button-secondary-default: #232323; - --button-secondary-hover: #1F1F1F; - --button-secondary-pressed: #1A1A1A; - --button-secondary-disable: #1A1A1A; - --button-white-default: #FFFFFF; - --button-white-hover: #FAFAFA; - --button-white-pressed: #F5F5F5; - --button-white-disable: #FFFFFF; - --button-error-default: #FF5967; - --button-error-hover: #FF8B94; - --button-error-pressed: #E6505C; - --button-error-disable: #FFEEEF; - --bubble-host: #262626; - --bubble-guest: #262626; - --divider-standard: #242424; - --divider-light: #4D4D4D; - --divider-brand: #0099FF; - --background-01: #FFFFFF; - --background-02: #242424; - --background-03: #181818; - --background-04: #1F1F1F; - --background-05: #000000; - --background-dialogue: #262626; - --hover-list: #292929; - --hover-icon: #333333; - --hover-link: #81ADEF; - --press-list: #383838; - --press-icon: #262626; - --press-link: #286BCE; - --badge-brand: #0099FF; - --badge-red: #FF5967; - --badge-grey: #4D4D4D; - --audio-hangup: #FF3350; - --gray-black: #000; - --gray-20: #333333; - --gray-40: #666666; - --gray-60: #999999; - --gray-80: #cccccc; - --gray-90: #e5e5e5; - --gray-96: #f5f5f5; - --gray-white: #ffffff; - --blue-dark: #0057BD; - --blue-standard: #0066CC; - --blue-light: #0072E4; - --blue-superlight: #E6F5FF; - --green-dark: #12BC67; - --green-standard: #15D173; - --green-light: #5BDE9D; - --green-superlight: #E8FAF1; - --yellow-dark: #E5A000; - --yellow-standard: #FFB300; - --yellow-light: #FFC94C; - --yellow-superlight: #FFF7E5; - --orange-dark: #E57E39; - --orange-standard: #FF8D40; - --orange-light: #FFAE78; - --orange-superlight: #FFF3EB; - --red-dark: #E6505C; - --red-standard: #FF5967; - --red-light: #FF8B94; - --red-superlight: #FFEEEF; - --pink-dark: #E55BA0; - --pink-standard: #FF66B3; - --pink-light: #FF93C9; - --pink-superlight: #FFEFF7; - --indigo-dark: #775CE6; - --indigo-standard: #8566FF; - --indigo-light: #A994FF; - --indigo-superlight: #F3F0FF; - --list-hover: rgba(71, 71, 71); - --list-pressed: rgba(28, 28, 28); - --background_01: #ffffff; - --background_02: #292929; - --background_03: #1A1A1A; - --background_04: #212121; - --background_05: #212121; - --background_dialogue: #292929; - --sidebar_win: rgba(0,0,0,0.8); - --sidebar_mac: rgba(0,0,0,0.1); - - color-scheme: dark; - } \ No newline at end of file diff --git a/static/assets/qrcode.min.js b/static/assets/qrcode.min.js deleted file mode 100644 index 974e0628..00000000 --- a/static/assets/qrcode.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Skipped minification because the original files appears to be already minified. - * Original file: /npm/qrcode@1.5.1/build/qrcode.js - * - * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files - */ -var QRCode=function(t){"use strict";var r,e=function(){return"function"==typeof Promise&&Promise.prototype&&Promise.prototype.then},n=[0,26,44,70,100,134,172,196,242,292,346,404,466,532,581,655,733,815,901,991,1085,1156,1258,1364,1474,1588,1706,1828,1921,2051,2185,2323,2465,2611,2761,2876,3034,3196,3362,3532,3706],o=function(t){if(!t)throw new Error('"version" cannot be null or undefined');if(t<1||t>40)throw new Error('"version" should be in range from 1 to 40');return 4*t+17},a=function(t){return n[t]},i=function(t){for(var r=0;0!==t;)r++,t>>>=1;return r},u=function(t){if("function"!=typeof t)throw new Error('"toSJISFunc" is not a valid function.');r=t},s=function(){return void 0!==r},f=function(t){return r(t)};function h(t,r){return t(r={exports:{}},r.exports),r.exports}var c=h((function(t,r){r.L={bit:1},r.M={bit:0},r.Q={bit:3},r.H={bit:2},r.isValid=function(t){return t&&void 0!==t.bit&&t.bit>=0&&t.bit<4},r.from=function(t,e){if(r.isValid(t))return t;try{return function(t){if("string"!=typeof t)throw new Error("Param is not a string");switch(t.toLowerCase()){case"l":case"low":return r.L;case"m":case"medium":return r.M;case"q":case"quartile":return r.Q;case"h":case"high":return r.H;default:throw new Error("Unknown EC Level: "+t)}}(t)}catch(t){return e}}}));function g(){this.buffer=[],this.length=0}c.L,c.M,c.Q,c.H,c.isValid,g.prototype={get:function(t){var r=Math.floor(t/8);return 1==(this.buffer[r]>>>7-t%8&1)},put:function(t,r){for(var e=0;e>>r-e-1&1))},getLengthInBits:function(){return this.length},putBit:function(t){var r=Math.floor(this.length/8);this.buffer.length<=r&&this.buffer.push(0),t&&(this.buffer[r]|=128>>>this.length%8),this.length++}};var d=g;function l(t){if(!t||t<1)throw new Error("BitMatrix size must be defined and greater than 0");this.size=t,this.data=new Uint8Array(t*t),this.reservedBit=new Uint8Array(t*t)}l.prototype.set=function(t,r,e,n){var o=t*this.size+r;this.data[o]=e,n&&(this.reservedBit[o]=!0)},l.prototype.get=function(t,r){return this.data[t*this.size+r]},l.prototype.xor=function(t,r,e){this.data[t*this.size+r]^=e},l.prototype.isReserved=function(t,r){return this.reservedBit[t*this.size+r]};var v=l,p=h((function(t,r){var e=o;r.getRowColCoords=function(t){if(1===t)return[];for(var r=Math.floor(t/7)+2,n=e(t),o=145===n?26:2*Math.ceil((n-13)/(2*r-2)),a=[n-7],i=1;i=0&&t<=7},r.from=function(t){return r.isValid(t)?parseInt(t,10):void 0},r.getPenaltyN1=function(t){for(var r=t.size,n=0,o=0,a=0,i=null,u=null,s=0;s=5&&(n+=e+(o-5)),i=h,o=1),(h=t.get(f,s))===u?a++:(a>=5&&(n+=e+(a-5)),u=h,a=1)}o>=5&&(n+=e+(o-5)),a>=5&&(n+=e+(a-5))}return n},r.getPenaltyN2=function(t){for(var r=t.size,e=0,o=0;o=10&&(1488===n||93===n)&&e++,a=a<<1&2047|t.get(u,i),u>=10&&(1488===a||93===a)&&e++}return e*o},r.getPenaltyN4=function(t){for(var r=0,e=t.data.length,n=0;n=0;){for(var n=e[0],o=0;o0){var o=new Uint8Array(this.degree);return o.set(e,n),o}return e};var L=T,b=function(t){return!isNaN(t)&&t>=1&&t<=40},U="(?:[u3000-u303F]|[u3040-u309F]|[u30A0-u30FF]|[uFF00-uFFEF]|[u4E00-u9FAF]|[u2605-u2606]|[u2190-u2195]|u203B|[u2010u2015u2018u2019u2025u2026u201Cu201Du2225u2260]|[u0391-u0451]|[u00A7u00A8u00B1u00B4u00D7u00F7])+",x="(?:(?![A-Z0-9 $%*+\\-./:]|"+(U=U.replace(/u/g,"\\u"))+")(?:.|[\r\n]))+",k=new RegExp(U,"g"),F=new RegExp("[^A-Z0-9 $%*+\\-./:]+","g"),S=new RegExp(x,"g"),D=new RegExp("[0-9]+","g"),Y=new RegExp("[A-Z $%*+\\-./:]+","g"),_=new RegExp("^"+U+"$"),z=new RegExp("^[0-9]+$"),H=new RegExp("^[A-Z0-9 $%*+\\-./:]+$"),J={KANJI:k,BYTE_KANJI:F,BYTE:S,NUMERIC:D,ALPHANUMERIC:Y,testKanji:function(t){return _.test(t)},testNumeric:function(t){return z.test(t)},testAlphanumeric:function(t){return H.test(t)}},K=h((function(t,r){r.NUMERIC={id:"Numeric",bit:1,ccBits:[10,12,14]},r.ALPHANUMERIC={id:"Alphanumeric",bit:2,ccBits:[9,11,13]},r.BYTE={id:"Byte",bit:4,ccBits:[8,16,16]},r.KANJI={id:"Kanji",bit:8,ccBits:[8,10,12]},r.MIXED={bit:-1},r.getCharCountIndicator=function(t,r){if(!t.ccBits)throw new Error("Invalid mode: "+t);if(!b(r))throw new Error("Invalid version: "+r);return r>=1&&r<10?t.ccBits[0]:r<27?t.ccBits[1]:t.ccBits[2]},r.getBestModeForData=function(t){return J.testNumeric(t)?r.NUMERIC:J.testAlphanumeric(t)?r.ALPHANUMERIC:J.testKanji(t)?r.KANJI:r.BYTE},r.toString=function(t){if(t&&t.id)return t.id;throw new Error("Invalid mode")},r.isValid=function(t){return t&&t.bit&&t.ccBits},r.from=function(t,e){if(r.isValid(t))return t;try{return function(t){if("string"!=typeof t)throw new Error("Param is not a string");switch(t.toLowerCase()){case"numeric":return r.NUMERIC;case"alphanumeric":return r.ALPHANUMERIC;case"kanji":return r.KANJI;case"byte":return r.BYTE;default:throw new Error("Unknown mode: "+t)}}(t)}catch(t){return e}}}));K.NUMERIC,K.ALPHANUMERIC,K.BYTE,K.KANJI,K.MIXED,K.getCharCountIndicator,K.getBestModeForData,K.isValid;var O=h((function(t,r){var e=i(7973);function n(t,r){return K.getCharCountIndicator(t,r)+4}function o(t,r){var e=0;return t.forEach((function(t){var o=n(t.mode,r);e+=o+t.getBitsLength()})),e}r.from=function(t,r){return b(t)?parseInt(t,10):r},r.getCapacity=function(t,r,e){if(!b(t))throw new Error("Invalid QR Code version");void 0===e&&(e=K.BYTE);var o=8*(a(t)-M(t,r));if(e===K.MIXED)return o;var i=o-n(e,t);switch(e){case K.NUMERIC:return Math.floor(i/10*3);case K.ALPHANUMERIC:return Math.floor(i/11*2);case K.KANJI:return Math.floor(i/13);case K.BYTE:default:return Math.floor(i/8)}},r.getBestVersionForData=function(t,e){var n,a=c.from(e,c.M);if(Array.isArray(t)){if(t.length>1)return function(t,e){for(var n=1;n<=40;n++){if(o(t,n)<=r.getCapacity(n,e,K.MIXED))return n}}(t,a);if(0===t.length)return 1;n=t[0]}else n=t;return function(t,e,n){for(var o=1;o<=40;o++)if(e<=r.getCapacity(o,n,t))return o}(n.mode,n.getLength(),a)},r.getEncodedBits=function(t){if(!b(t)||t<7)throw new Error("Invalid QR Code version");for(var r=t<<12;i(r)-e>=0;)r^=7973<=0;)n^=1335<0&&(e=this.data.substr(r),n=parseInt(e,10),t.put(n,3*o+1))};var j=q,$=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"," ","$","%","*","+","-",".","/",":"];function X(t){this.mode=K.ALPHANUMERIC,this.data=t}X.getBitsLength=function(t){return 11*Math.floor(t/2)+t%2*6},X.prototype.getLength=function(){return this.data.length},X.prototype.getBitsLength=function(){return X.getBitsLength(this.data.length)},X.prototype.write=function(t){var r;for(r=0;r+2<=this.data.length;r+=2){var e=45*$.indexOf(this.data[r]);e+=$.indexOf(this.data[r+1]),t.put(e,11)}this.data.length%2&&t.put($.indexOf(this.data[r]),6)};var Z=X;function W(t){this.mode=K.BYTE,"string"==typeof t&&(t=function(t){for(var r=[],e=t.length,n=0;n=55296&&o<=56319&&e>n+1){var a=t.charCodeAt(n+1);a>=56320&&a<=57343&&(o=1024*(o-55296)+a-56320+65536,n+=1)}o<128?r.push(o):o<2048?(r.push(o>>6|192),r.push(63&o|128)):o<55296||o>=57344&&o<65536?(r.push(o>>12|224),r.push(o>>6&63|128),r.push(63&o|128)):o>=65536&&o<=1114111?(r.push(o>>18|240),r.push(o>>12&63|128),r.push(o>>6&63|128),r.push(63&o|128)):r.push(239,191,189)}return new Uint8Array(r).buffer}(t)),this.data=new Uint8Array(t)}W.getBitsLength=function(t){return 8*t},W.prototype.getLength=function(){return this.data.length},W.prototype.getBitsLength=function(){return W.getBitsLength(this.data.length)},W.prototype.write=function(t){for(var r=0,e=this.data.length;r=33088&&e<=40956)e-=33088;else{if(!(e>=57408&&e<=60351))throw new Error("Invalid SJIS character: "+this.data[r]+"\nMake sure your charset is UTF-8");e-=49472}e=192*(e>>>8&255)+(255&e),t.put(e,13)}};var rt=tt,et=h((function(t){var r={single_source_shortest_paths:function(t,e,n){var o={},a={};a[e]=0;var i,u,s,f,h,c,g,d=r.PriorityQueue.make();for(d.push(e,0);!d.empty();)for(s in u=(i=d.pop()).value,f=i.cost,h=t[u]||{})h.hasOwnProperty(s)&&(c=f+h[s],g=a[s],(void 0===a[s]||g>c)&&(a[s]=c,d.push(s,c),o[s]=u));if(void 0!==n&&void 0===a[n]){var l=["Could not find a path from ",e," to ",n,"."].join("");throw new Error(l)}return o},extract_shortest_path_from_predecessor_list:function(t,r){for(var e=[],n=r;n;)e.push(n),n=t[n];return e.reverse(),e},find_path:function(t,e,n){var o=r.single_source_shortest_paths(t,e,n);return r.extract_shortest_path_from_predecessor_list(o,n)},PriorityQueue:{make:function(t){var e,n=r.PriorityQueue,o={};for(e in t=t||{},n)n.hasOwnProperty(e)&&(o[e]=n[e]);return o.queue=[],o.sorter=t.sorter||n.default_sorter,o},default_sorter:function(t,r){return t.cost-r.cost},push:function(t,r){var e={value:t,cost:r};this.queue.push(e),this.queue.sort(this.sorter)},pop:function(){return this.queue.shift()},empty:function(){return 0===this.queue.length}}};t.exports=r})),nt=h((function(t,r){function e(t){return unescape(encodeURIComponent(t)).length}function n(t,r,e){for(var n,o=[];null!==(n=t.exec(e));)o.push({data:n[0],index:n.index,mode:r,length:n[0].length});return o}function o(t){var r,e,o=n(J.NUMERIC,K.NUMERIC,t),a=n(J.ALPHANUMERIC,K.ALPHANUMERIC,t);return s()?(r=n(J.BYTE,K.BYTE,t),e=n(J.KANJI,K.KANJI,t)):(r=n(J.BYTE_KANJI,K.BYTE,t),e=[]),o.concat(a,r,e).sort((function(t,r){return t.index-r.index})).map((function(t){return{data:t.data,mode:t.mode,length:t.length}}))}function a(t,r){switch(r){case K.NUMERIC:return j.getBitsLength(t);case K.ALPHANUMERIC:return Z.getBitsLength(t);case K.KANJI:return rt.getBitsLength(t);case K.BYTE:return G.getBitsLength(t)}}function i(t,r){var e,n=K.getBestModeForData(t);if((e=K.from(r,n))!==K.BYTE&&e.bit=0?t[t.length-1]:null;return e&&e.mode===r.mode?(t[t.length-1].data+=r.data,t):(t.push(r),t)}),[])}(s))},r.rawSplit=function(t){return r.fromArray(o(t))}}));function ot(t,r,e){var n,o,a=t.size,i=V(r,e);for(n=0;n<15;n++)o=1==(i>>n&1),n<6?t.set(n,8,o,!0):n<8?t.set(n+1,8,o,!0):t.set(a-15+n,8,o,!0),n<8?t.set(8,a-n-1,o,!0):n<9?t.set(8,15-n-1+1,o,!0):t.set(8,15-n-1,o,!0);t.set(a-8,8,1,!0)}function at(t,r,e){var n=new d;e.forEach((function(r){n.put(r.mode.bit,4),n.put(r.getLength(),K.getCharCountIndicator(r.mode,t)),r.write(n)}));var o=8*(a(t)-M(t,r));for(n.getLengthInBits()+4<=o&&n.put(0,4);n.getLengthInBits()%8!=0;)n.putBit(0);for(var i=(o-n.getLengthInBits())/8,u=0;u=0&&u<=6&&(0===s||6===s)||s>=0&&s<=6&&(0===u||6===u)||u>=2&&u<=4&&s>=2&&s<=4?t.set(a+u,i+s,!0,!0):t.set(a+u,i+s,!1,!0))}(c,r),function(t){for(var r=t.size,e=8;e=7&&function(t,r){for(var e,n,o,a=t.size,i=O.getEncodedBits(r),u=0;u<18;u++)e=Math.floor(u/3),n=u%3+a-8-3,o=1==(i>>u&1),t.set(e,n,o,!0),t.set(n,e,o,!0)}(c,r),function(t,r){for(var e=t.size,n=-1,o=e-1,a=7,i=0,u=e-1;u>0;u-=2)for(6===u&&u--;;){for(var s=0;s<2;s++)if(!t.isReserved(o,u-s)){var f=!1;i>>a&1)),t.set(o,u-s,f),-1===--a&&(i++,a=7)}if((o+=n)<0||e<=o){o-=n,n=-n;break}}}(c,f),isNaN(n)&&(n=E.getBestMask(c,ot.bind(null,c,e))),E.applyMask(n,c),ot(c,e,n),{modules:c,version:r,errorCorrectionLevel:e,maskPattern:n,segments:a}}nt.fromArray,nt.fromString,nt.rawSplit;var ut=function(t,r){if(void 0===t||""===t)throw new Error("No input text");var e,n,o=c.M;return void 0!==r&&(o=c.from(r.errorCorrectionLevel,c.M),e=O.from(r.version),n=E.from(r.maskPattern),r.toSJISFunc&&u(r.toSJISFunc)),it(t,e,o,n)},st=h((function(t,r){function e(t){if("number"==typeof t&&(t=t.toString()),"string"!=typeof t)throw new Error("Color should be defined as hex string");var r=t.slice().replace("#","").split("");if(r.length<3||5===r.length||r.length>8)throw new Error("Invalid hex color: "+t);3!==r.length&&4!==r.length||(r=Array.prototype.concat.apply([],r.map((function(t){return[t,t]})))),6===r.length&&r.push("F","F");var e=parseInt(r.join(""),16);return{r:e>>24&255,g:e>>16&255,b:e>>8&255,a:255&e,hex:"#"+r.slice(0,6).join("")}}r.getOptions=function(t){t||(t={}),t.color||(t.color={});var r=void 0===t.margin||null===t.margin||t.margin<0?4:t.margin,n=t.width&&t.width>=21?t.width:void 0,o=t.scale||4;return{width:n,scale:n?4:o,margin:r,color:{dark:e(t.color.dark||"#000000ff"),light:e(t.color.light||"#ffffffff")},type:t.type,rendererOpts:t.rendererOpts||{}}},r.getScale=function(t,r){return r.width&&r.width>=t+2*r.margin?r.width/(t+2*r.margin):r.scale},r.getImageWidth=function(t,e){var n=r.getScale(t,e);return Math.floor((t+2*e.margin)*n)},r.qrToImageData=function(t,e,n){for(var o=e.modules.size,a=e.modules.data,i=r.getScale(o,n),u=Math.floor((o+2*n.margin)*i),s=n.margin*i,f=[n.color.light,n.color.dark],h=0;h=s&&c>=s&&h':"",s="0&&s>0&&t[u-1]||(n+=a?ct("M",s+e,.5+f+e):ct("m",o,0),o=0,a=!1),s+1',f='viewBox="0 0 '+i+" "+i+'"',h=''+u+s+"\n";return"function"==typeof e&&e(null,h),h};function dt(t,r,n,o,a){var i=[].slice.call(arguments,1),u=i.length,s="function"==typeof i[u-1];if(!s&&!e())throw new Error("Callback required as last argument");if(!s){if(u<1)throw new Error("Too few arguments provided");return 1===u?(n=r,r=o=void 0):2!==u||r.getContext||(o=n,n=r,r=void 0),new Promise((function(e,a){try{var i=ut(n,o);e(t(i,r,o))}catch(t){a(t)}}))}if(u<2)throw new Error("Too few arguments provided");2===u?(a=n,n=r,r=o=void 0):3===u&&(r.getContext&&void 0===a?(a=o,o=void 0):(a=o,o=n,n=r,r=void 0));try{var f=ut(n,o);a(null,t(f,r,o))}catch(t){a(t)}}var lt=ut,vt=dt.bind(null,ft.render),pt=dt.bind(null,ft.renderToDataURL),wt=dt.bind(null,(function(t,r,e){return gt(t,e)})),mt={create:lt,toCanvas:vt,toDataURL:pt,toString:wt};return t.create=lt,t.default=mt,t.toCanvas=vt,t.toDataURL=pt,t.toString=wt,Object.defineProperty(t,"__esModule",{value:!0}),t}({}); diff --git a/static/assets/renderer.js b/static/assets/renderer.js deleted file mode 100644 index d662d353..00000000 --- a/static/assets/renderer.js +++ /dev/null @@ -1,469 +0,0 @@ -const SettingList = (items, title, isCollapsible = false, direction = "column") => { - return ` - - - ${items.join("")} - - - `; -}; - -const SettingItem = (title, subtitle, action, id, visible = true) => { - return ` -
- ${title} - ${subtitle ? `${subtitle}` : ""} -
- ${action ? `
${action}
` : ""} -
`; -}; - -const SettingButton = (text, id, type = "secondary") => { - return `${text}`; -}; - -const SettingSwitch = (configKey, isActive = false, extraData) => { - return ` `data-${key}="${extraData[key]}"`) : ""} - > - `; -}; - -const SettingOption = (text, value, isSelected = false) => { - return `${text}`; -}; - -const SelectTemplate = document.createElement("template"); -SelectTemplate.innerHTML = ` -
-
- - - - -
- -
`; -window.customElements.define( - "ob-setting-select", - class extends HTMLElement { - _button; - _text; - _context; - constructor() { - super(); - this.attachShadow({ mode: "open" }); - this.shadowRoot?.append(SelectTemplate.content.cloneNode(true)); - this._button = this.shadowRoot.querySelector('div[part="button"]'); - this._text = this.shadowRoot.querySelector('input[part="current-text"]'); - this._context = this.shadowRoot.querySelector('ul[part="option-list"]'); - const buttonClick = () => { - const isHidden = this._context.classList.toggle("hidden"); - window[`${isHidden ? "remove" : "add"}EventListener`]("pointerdown", windowPointerDown); - }; - const windowPointerDown = ({ target }) => { - if (!this.contains(target)) buttonClick(); - }; - this._button.addEventListener("click", buttonClick); - this._context.addEventListener("click", (event) => { - const { target } = event; - if (target.tagName !== "SETTING-OPTION") return; - buttonClick(); - if (target.hasAttribute("is-selected")) return; - this.querySelectorAll("setting-option[is-selected]").forEach((dom) => dom.toggleAttribute("is-selected")); - target.toggleAttribute("is-selected"); - this._text.value = target.textContent; - this.dispatchEvent( - new CustomEvent("selected", { - bubbles: true, - composed: true, - detail: { - name: target.textContent, - value: target.dataset.value - } - }) - ); - }); - this._text.value = this.querySelector("setting-option[is-selected]")?.textContent; - } - } -); -const SettingSelect = (items, configKey, configValue) => { - return ` - ${items.map((e, i) => { - return SettingOption(e.text, e.value, configValue ? configValue === e.value : i === 0); - }).join("")} -`; -}; - -class WebUiApiOB11ConfigWrapper { - retCredential = ""; - async Init(Credential) { - this.retCredential = Credential; - } - async GetOB11Config() { - const ConfigResponse = await fetch("../api/OB11Config/GetConfig", { - method: "POST", - headers: { - Authorization: "Bearer " + this.retCredential, - "Content-Type": "application/json" - } - }); - if (ConfigResponse.status == 200) { - const ConfigResponseJson = await ConfigResponse.json(); - if (ConfigResponseJson.code == 0) { - return ConfigResponseJson?.data; - } - } - return {}; - } - async SetOB11Config(config) { - const ConfigResponse = await fetch("../api/OB11Config/SetConfig", { - method: "POST", - headers: { - Authorization: "Bearer " + this.retCredential, - "Content-Type": "application/json" - }, - body: JSON.stringify({ config: JSON.stringify(config) }) - }); - if (ConfigResponse.status == 200) { - const ConfigResponseJson = await ConfigResponse.json(); - if (ConfigResponseJson.code == 0) { - return true; - } - } - return false; - } -} -const OB11ConfigWrapper = new WebUiApiOB11ConfigWrapper(); - -async function onSettingWindowCreated(view) { - const isEmpty = (value) => value === void 0 || value === void 0 || value === ""; - await OB11ConfigWrapper.Init(localStorage.getItem("auth")); - const ob11Config = await OB11ConfigWrapper.GetOB11Config(); - const setOB11Config = (key, value) => { - const configKey = key.split("."); - if (configKey.length === 2) { - ob11Config[configKey[1]] = value; - } else if (configKey.length === 3) { - ob11Config[configKey[1]][configKey[2]] = value; - } - }; - const parser = new DOMParser(); - const doc = parser.parseFromString( - [ - "
", - ` -
-
`, - SettingList([ - SettingItem( - 'Napcat', - void 0, - SettingButton("V4.0.3", "napcat-update-button", "secondary") - ) - ]), - SettingList([ - SettingItem( - "启用 HTTP 服务", - void 0, - SettingSwitch("ob11.http.enable", ob11Config.http.enable, { - "control-display-id": "config-ob11-http-port" - }) - ), - SettingItem( - "HTTP 服务监听端口", - void 0, - `
`, - "config-ob11-http-port", - ob11Config.http.enable - ), - SettingItem( - "启用 HTTP 心跳", - void 0, - SettingSwitch("ob11.http.enableHeart", ob11Config.http.enableHeart, { - "control-display-id": "config-ob11-HTTP.enableHeart" - }) - ), - SettingItem( - "启用 HTTP 事件上报", - void 0, - SettingSwitch("ob11.http.enablePost", ob11Config.http.enablePost, { - "control-display-id": "config-ob11-http-postUrls" - }) - ), - `
- -
- HTTP 事件上报密钥 -
-
- -
-
- -
- HTTP 事件上报地址 -
- 添加 -
-
-
`, - SettingItem( - "启用正向 WebSocket 服务", - void 0, - SettingSwitch("ob11.ws.enable", ob11Config.ws.enable, { - "control-display-id": "config-ob11-ws-port" - }) - ), - SettingItem( - "正向 WebSocket 服务监听端口", - void 0, - `
`, - "config-ob11-ws-port", - ob11Config.ws.enable - ), - SettingItem( - "启用反向 WebSocket 服务", - void 0, - SettingSwitch("ob11.reverseWs.enable", ob11Config.reverseWs.enable, { - "control-display-id": "config-ob11-reverseWs-urls" - }) - ), - `
- -
- 反向 WebSocket 监听地址 -
- 添加 -
-
-
`, - SettingItem( - " WebSocket 服务心跳间隔", - "控制每隔多久发送一个心跳包,单位为毫秒", - `
` - ), - SettingItem( - "Access token", - void 0, - `
` - ), - SettingItem( - "新消息上报格式", - `如客户端无特殊需求推荐保持默认设置,两者的详细差异可参考 OneBot v11 文档`, - SettingSelect( - [ - { text: "消息段", value: "array" }, - { text: "CQ码", value: "string" } - ], - "ob11.messagePostFormat", - ob11Config.messagePostFormat - ) - ), - SettingItem( - "音乐卡片签名地址", - void 0, - `
`, - "ob11.musicSignUrl" - ), - SettingItem( - "启用本地进群时间与发言时间记录", - void 0, - SettingSwitch("ob11.GroupLocalTime.Record", ob11Config.GroupLocalTime.Record, { - "control-display-id": "config-ob11-GroupLocalTime-RecordList" - }) - ), - `
- -
- 群列表 -
- 添加 -
-
-
`, - SettingItem( - "", - void 0, - SettingButton("保存", "config-ob11-save", "primary") - ) - ]), - SettingList([ - SettingItem( - "上报 Bot 自身发送的消息", - "上报 event 为 message_sent", - SettingSwitch("ob11.reportSelfMessage", ob11Config.reportSelfMessage) - ) - ]), - SettingList([ - SettingItem( - "GitHub 仓库", - "https://github.com/NapNeko/NapCatQQ", - SettingButton("点个星星", "open-github") - ), - SettingItem("NapCat 文档", "", SettingButton("看看文档", "open-docs")) - ]), - SettingItem( - "Telegram 群", - "https://t.me/+nLZEnpne-pQ1OWFl", - SettingButton("进去逛逛", "open-telegram") - ), - SettingItem( - "QQ 群", - "518662028", - SettingButton("我要进去", "open-qq-group") - ), - "
" - ].join(""), - "text/html" - ); - doc.querySelector("#open-github")?.addEventListener("click", () => { - window.open("https://github.com/NapNeko/NapCatQQ", "_blank"); - }); - doc.querySelector("#open-docs")?.addEventListener("click", () => { - window.open("https://napneko.github.io/", "_blank"); - }); - doc.querySelector("#open-telegram")?.addEventListener("click", () => { - window.open("https://t.me/+nLZEnpne-pQ1OWFl", "_blank"); - }); - doc.querySelector("#open-qq-group")?.addEventListener("click", () => { - window.open("https://qm.qq.com/q/VfjAq5HIMS", "_blank"); - }); - const buildHostListItem = (type, host, index, inputAttrs = {}) => { - 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"; - Object.assign(dom.input, inputAttrs); - dom.input.classList.add("q-input__inner"); - dom.input.type = "url"; - dom.input.value = host; - dom.input.addEventListener("input", () => { - ob11Config[type.split("-")[0]][type.split("-")[1]][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.split("-")[0]][type.split("-")[1]].splice(index, 1); - initReverseHost(type); - }); - dom.container.appendChild(dom.inputContainer); - dom.container.appendChild(dom.deleteBtn); - return dom.container; - }; - const buildHostList = (hosts, type, inputAttr = {}) => { - const result = []; - hosts?.forEach((host, index) => { - result.push(buildHostListItem(type, host, index, inputAttr)); - }); - return result; - }; - const addReverseHost = (type, doc2 = document, inputAttr = {}) => { - type = type.replace(/\./g, "-"); - const hostContainerDom = doc2.body.querySelector( - `#config-ob11-${type}-list` - ); - hostContainerDom?.appendChild( - buildHostListItem( - type, - "", - ob11Config[type.split("-")[0]][type.split("-")[1]].length, - inputAttr - ) - ); - ob11Config[type.split("-")[0]][type.split("-")[1]].push(""); - }; - const initReverseHost = (type, doc2 = document) => { - type = type.replace(/\./g, "-"); - const hostContainerDom = doc2.body?.querySelector( - `#config-ob11-${type}-list` - ); - if (hostContainerDom) { - [...hostContainerDom.childNodes].forEach((dom) => dom.remove()); - buildHostList( - ob11Config[type.split("-")[0]][type.split("-")[1]], - type - ).forEach((dom) => { - hostContainerDom?.appendChild(dom); - }); - } - }; - initReverseHost("http.postUrls", doc); - initReverseHost("reverseWs.urls", doc); - initReverseHost("GroupLocalTime.RecordList", doc); - doc.querySelector("#config-ob11-http-postUrls-add")?.addEventListener( - "click", - () => addReverseHost("http.postUrls", document, { - placeholder: "如:http://127.0.0.1:5140/onebot" - }) - ); - doc.querySelector("#config-ob11-reverseWs-urls-add")?.addEventListener( - "click", - () => addReverseHost("reverseWs.urls", document, { - placeholder: "如:ws://127.0.0.1:5140/onebot" - }) - ); - doc.querySelector("#config-ob11-GroupLocalTime-RecordList-add")?.addEventListener( - "click", - () => addReverseHost("GroupLocalTime.RecordList", document, { - placeholder: "此处填写群号 -1为全部" - }) - ); - doc.querySelector("#config-ffmpeg-select")?.addEventListener("click", () => { - }); - doc.querySelector("#config-open-log-path")?.addEventListener("click", () => { - }); - doc.querySelectorAll("setting-switch[data-config-key]").forEach((dom) => { - dom.addEventListener("click", () => { - const active = dom.getAttribute("is-active") == void 0; - setOB11Config(dom.dataset.configKey, active); - if (active) dom.setAttribute("is-active", ""); - else dom.removeAttribute("is-active"); - if (!isEmpty(dom.dataset.controlDisplayId)) { - const displayDom = document.querySelector( - //@ts-expect-error 等待修复 - `#${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) => { - 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; - setOB11Config(configKey, configValue); - }); - }); - doc.querySelectorAll("ob-setting-select[data-config-key]").forEach((dom) => { - dom?.addEventListener("selected", (e) => { - const configKey = dom.dataset.configKey; - const configValue = e.detail.value; - setOB11Config(configKey, configValue); - }); - }); - doc.querySelector("#config-ob11-save")?.addEventListener("click", () => { - OB11ConfigWrapper.SetOB11Config(ob11Config); - alert("保存成功"); - }); - doc.body.childNodes.forEach((node) => { - view.appendChild(node); - }); -} - -export { onSettingWindowCreated }; diff --git a/static/assets/style.css b/static/assets/style.css deleted file mode 100644 index c52bec45..00000000 --- a/static/assets/style.css +++ /dev/null @@ -1,201 +0,0 @@ -body, html { - background: var(--bg_bottom_standard); - color: var(--text_primary); - font-family: "Color Emoji", system-ui, "PingFang SC", PingFangSC-Regular, "Microsoft YaHei", "Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei", Arial, Helvetica, sans-serif, "Apple Braille", "Color Emoji Fix"; - min-height: 100vh; - scroll-behavior: smooth; - width: 100%; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -o-user-select: none; - user-select: none; - - --z_index_popover: 999; - --nt_mix_tokens: nt_brand_standard_2_overlay_hover_brand_2_mix,nt_brand_standard_2_overlay_pressed_brand_2_mix,nt_feedback_error_2_overlay_hover_brand_2_mix,nt_feedback_error_2_overlay_pressed_brand_2_mix,nt_icon_white_2_overlay_hover_2_mix,nt_icon_white_2_overlay_pressed_2_mix,nt_bubble_host_2_overlay_pressed_brand_2_mix,nt_bg_white_2_overlay_pressed_brand_2_mix,nt_bg_white_2_overlay_hover_2_mix,nt_bg_white_2_overlay_pressed_2_mix,nt_fg_white_2_overlay_hover_2_mix,nt_fg_white_2_overlay_pressed_2_mix,nt_icon_red_2_overlay_hover_2_mix,nt_icon_red_2_overlay_pressed_2_mix,nt_fg_grey_standard_2_overlay_hover_2_mix,nt_fg_grey_standard_2_overlay_pressed_2_mix,nt_bubble_guest_2_overlay_pressed_2_mix,nt_icon_primary_2_20_2_alpha,nt_bg_grey_standard_2_95_2_alpha,nt_tag_red_2_20_2_alpha,nt_tag_red_2_25_2_alpha,nt_tag_blue_2_20_2_alpha,nt_tag_blue_2_25_2_alpha,nt_tag_blue_2_10_2_alpha,nt_tag_purple_2_20_2_alpha,nt_brand_standard_2_20_2_alpha,nt_tag_sage_green_2_20_2_alpha,nt_feedback_error_2_20_2_alpha,nt_text_white_2_60_2_alpha,nt_bg_white_2_70_2_alpha,nt_bg_white_2_90_2_alpha,nt_bg_white_2_97_2_alpha,nt_bg_white_2_40_2_alpha,nt_bg_white_2_30_2_alpha,nt_text_white_2_80_2_alpha,nt_brand_standard_2_50_2_alpha,nt_bg_nav_secondary_2_60_2_alpha,nt_bg_nav_2_60_2_alpha,nt_feedback_error_2_10_2_alpha,nt_brand_standard_2_10_2_alpha,nt_on_brand_primary_2_40_2_alpha,nt_text_primary_2_72_2_alpha,nt_text_white_2_72_2_alpha; - } - - a, address, article, aside, b, blockquote, body, div, em, fieldset, footer, form, h1, h2, h3, h4, h5, h6, header, html, i, iframe, img, label, legend, li, main, nav, ol, p, s, section, span, table, tbody, td, tfoot, th, thead, tr, ul { - box-sizing: border-box; - font-size: 100%; - font-style: inherit; - font-weight: inherit; - border: 0px; - margin: 0px; - padding: 0px; - } - - #app { - position: relative; - display: block; - padding: 20px; - min-height: 100vh; - background: transparent; - } - - .fake-bar { - position: fixed; - display: none; - top: 0; - left: 0; - } - - /* ======== Input ======== */ - .q-input { - align-items: center; - border-radius: 4px; - box-sizing: border-box; - color: var(--text_secondary); - display: inline-flex; - position: relative; - width: 100%; - border: 1px solid transparent; - } - - .q-input input, .q-input textarea { - appearance: none; - background-color: transparent; - box-sizing: border-box; - color: var(--text_primary); - flex-grow: 1; - flex-shrink: 1; - flex-basis: 0%; - font-size: 14px; - height: fit-content; - outline-color: initial; - outline-style: none; - outline-width: initial; - resize: none; - width: 100%; - border: none; - } - - /* ======== Switch ======== */ - .q-switch { - background-color: var(--fill_standard_primary); - border-radius: 14px; - box-sizing: border-box; - display: inline-flex; - position: relative; - transition-behavior: normal; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0.38, 0, 0.24, 1); - transition-delay: 0s; - transition-property: all; - width: 28px; - padding: 3px; - } - - .q-switch__handle { - border-radius: 5px; - box-shadow: rgba(0, 0, 0, 0.09) 0px 2px 4px; - box-sizing: border-box; - display: inline-block; - height: 10px; - position: relative; - transition-behavior: normal; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0.38, 0, 0.24, 1); - transition-delay: 0s; - transition-property: all; - width: 10px; - z-index: 2; - background: var(--icon_white); - } - - .q-switch:not(.is-disabled):hover { - background: var(--fill_standard_secondary); - } - - .q-switch:not(.is-disabled):active { - background: var(--nt_bg_white_2_overlay_pressed_brand_2_mix); - } - .q-switch:not(.is-disabled):active .q-switch__handle { - width: 12px; - } - - .q-switch.is-active { - background-color: var(--brand_standard); - } - - .q-switch.is-active .q-switch__handle { - transform: translateX(12px); - } - - .q-switch.is-active:not(.is-disabled):hover { - background: var(--nt_brand_standard_2_overlay_hover_brand_2_mix); - } - - .q-switch.is-active:not(.is-disabled):active { - background: var(--nt_brand_standard_2_overlay_pressed_brand_2_mix); - } - .q-switch.is-active:not(.is-disabled):active .q-switch__handle { - transform: translateX(10px); - } - - /* ======== Button ======== */ - .q-button { - align-items: center; - background-color: var(--brand_standard); - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; - display: inline-flex; - font-size: 14px; - justify-content: center; - line-height: 18px; - outline-color: initial; - outline-style: none; - outline-width: initial; - position: relative; - vertical-align: text-bottom; - border: 1px solid var(--fg_grey_standard); - padding: 5px 11px; - } - - .q-button--small { - font-size: 12px; - line-height: 14px; - min-width: 62px; - padding: 4px 7px; - } - - .q-button--primary { - background-color: var(--brand_standard); - border-color: var(--brand_standard); - color: var(--on_brand_primary); - } - - .q-button--secondary { - background-color: transparent; - border-color: var(--fg_grey_standard); - color: var(--text_primary); - } - - .q-button:not([disabled]):hover { - background-color: var(--overlay_hover); - } - - .q-button:not([disabled]):active { - background-color: var(--overlay_pressed); - } - - .q-button--primary:hover { - background-color: var(--nt_brand_standard_2_overlay_hover_brand_2_mix); - border-color: var(--nt_brand_standard_2_overlay_hover_brand_2_mix); - } - - .q-button--primary:active { - background-color: var(--nt_brand_standard_2_overlay_pressed_brand_2_mix); - border-color: var(--nt_brand_standard_2_overlay_pressed_brand_2_mix); - } - - .q-button[disabled] { - opacity: 0.3; - cursor: not-allowed; - } - - .q-button--secondary[disabled] { - background-color: transparent; - } \ No newline at end of file diff --git a/static/assets/webcomponents.css b/static/assets/webcomponents.css deleted file mode 100644 index ff7363c1..00000000 --- a/static/assets/webcomponents.css +++ /dev/null @@ -1,350 +0,0 @@ -*[is-disabled] { - opacity: 0.3; - -webkit-pointer-events: none; - -moz-pointer-events: none; - -ms-pointer-events: none; - -o-pointer-events: none; - pointer-events: none; - cursor: not-allowed; - } - - setting-section::before { - content: attr(data-title); - display: block; - margin: 0px 0px 8px 16px; - color: var(--text_primary); - font-weight: var(--font-bold); - font-size: min(var(--font_size_3), 18px); - line-height: min(var(--line_height_3), 24px); - } - - setting-panel { - display: block; - margin-bottom: 20px; - } - - setting-section:last-child setting-panel { - margin-bottom: 0; - } - - setting-list, - setting-list[data-direction="column"] { - display: flex; - background-color: var(--fill_light_primary); - border-radius: 8px; - flex-direction: column; - align-items: stretch; - justify-content: space-between; - } - - setting-list[data-direction="row"] { - padding: 16px 0; - flex-direction: row; - justify-content: space-around; - } - setting-list[data-direction="row"], - setting-list[data-direction="row"] * { - text-align: center; - } - - setting-item { - display: flex; - padding: 12px 16px; - background-color: var(--fill_light_primary); - font-size: min(var(--font_size_3),18px); - line-height: min(var(--line_height_3),24px); - border-radius: 8px; - align-items: center; - justify-content: space-between; - } - - setting-item > *:first-child { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - flex-wrap: nowrap; - } - - setting-list[data-direction="row"] setting-item { - padding: 0; - margin: 0 10px; - flex: 1; - } - - setting-list[data-direction="row"] setting-item > *:first-child { - align-items: center; - flex: 1; - } - - setting-list[data-direction="row"] setting-item > *:first-child > * { - flex: 1; - } - - setting-list setting-divider, - setting-list[data-direction="column"] setting-divider { - display: block; - position: relative; - width: unset; - height: 1px; - margin: 0 16px; - background-color: var(--border_standard); - } - - setting-list[data-direction="row"] setting-divider { - width: 1px; - height: unset; - margin: 0; - } - - setting-text[data-type="secondary"] { - margin-top: 4px; - color: var(--text_secondary); - font-size: min(var(--font_size_2),16px); - line-height: min(var(--line_height_2),22px); - } - - setting-switch { - --transition-timing: cubic-bezier(0.38, 0, 0.24, 1); - - display: block; - position: relative; - width: 28px; - height: 16px; - background: var(--fill_standard_primary); - border-radius: 14px; - transition: background var(--transition-timing) .2s; - } - setting-switch::after { - content: ''; - display: block; - position: absolute; - top: 0px; - left: 0px; - margin: 3px; - width: 10px; - height: 10px; - background: var(--icon_white); - box-shadow: rgba(0, 0, 0, 0.09) 0px 2px 4px; - border-radius: 5px; - transition: width var(--transition-timing) .2s, - left var(--transition-timing) .2s; - } - - setting-switch[is-active] { - background: var(--brand_standard); - } - setting-switch[is-active]::after { - left: calc(100% - 16px); - } - - setting-switch:hover { - background: var(--fill_standard_secondary); - } - setting-switch[is-active]:hover { - background: var(--nt_brand_standard_2_overlay_hover_brand_2_mix); - } - - setting-switch:active { - background: var(--nt_bg_white_2_overlay_pressed_brand_2_mix); - } - setting-switch[is-active]:active { - background: var(--nt_brand_standard_2_overlay_pressed_brand_2_mix); - } - - setting-switch:active::after { - width: 12px; - } - setting-switch[is-active]:active::after { - left: calc(100% - 18px); - } - - setting-button, - setting-button[data-type="secondary"] { - position: relative; - display: inline-flex; - padding: 5px 11px; - min-width: 62px; - background-color: transparent; - color: var(--text_primary); - border-radius: 4px; - font-size: 12px; - line-height: 12px; - justify-content: center; - outline-color: initial; - outline-style: none; - outline-width: initial; - vertical-align: text-bottom; - border: 1px solid var(--fg_grey_standard); - align-items: center; - box-sizing: border-box; - } - - setting-button[data-type="primary"] { - background-color: var(--brand_standard); - color: var(--on_brand_primary); - border-color: var(--brand_standard); - } - - setting-button:hover, - setting-button[data-type="secondary"]:hover { - background-color: var(--overlay_hover); - } - - setting-button:active, - setting-button[data-type="secondary"]:active { - background-color: var(--overlay_pressed); - } - - setting-button[data-type="primary"]:hover { - background-color: var(--nt_brand_standard_2_overlay_hover_brand_2_mix); - border-color: var(--nt_brand_standard_2_overlay_hover_brand_2_mix); - } - - setting-button[data-type="primary"]:active { - background-color: var(--nt_brand_standard_2_overlay_pressed_brand_2_mix); - border-color: var(--nt_brand_standard_2_overlay_pressed_brand_2_mix); - } - - setting-select, - setting-select::part(parent), - setting-select::part(button) { - display: block; - position: relative; - height: 24px; - font-size: 12px; - line-height: 24px; - box-sizing: border-box; - } - - setting-select::part(button) { - display: flex; - padding: 0px 8px; - background-color: transparent; - border-radius: 4px; - border: 1px solid var(--border_dark); - z-index: 5; - cursor: default; - align-items: center; - flex-direction: row; - flex-wrap: nowrap; - } - - setting-select::part(current-text) { - display: block; - margin-right: 8px; - padding: 0px; - background: none; - background-color: transparent; - font-size: 12px; - color: var(--text_primary); - text-overflow: ellipsis; - border-radius: 0px; - border: none; - outline: none; - overflow: hidden; - appearance: none; - box-sizing: border-box; - cursor: default; - flex: 1; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -o-user-select: none; - user-select: none; - -webkit-pointer-events: none; - -moz-pointer-events: none; - -ms-pointer-events: none; - -o-pointer-events: none; - pointer-events: none; - } - - setting-select::part(button-arrow) { - position: relative; - display: block; - width: 16px; - height: 16px; - color: var(--icon_primary); - } - - setting-select::part(option-list) { - display: flex; - position: absolute; - top: 100%; - padding: 4px; - margin: 5px 0px; - width: 100%; - max-height: var(--q-contextmenu-max-height); - background-color: var(--blur_middle_standard); - background-clip: padding-box; - backdrop-filter: blur(8px); - font-size: 12px; - box-shadow: var(--shadow_bg_middle_secondary); - border: 1px solid var(--border_secondary); - border-radius: 4px; - box-sizing: border-box; - app-region: no-drag; - overflow-x: hidden; - overflow-y: auto; - list-style: none; - z-index: 999; - flex-direction: column; - align-items: stretch; - flex-wrap: nowrap; - justify-content: flex-start; - gap: 4px; - } - - setting-option, - setting-option::part(parent) { - display: block; - position: relative; - box-sizing: border-box; - } - - setting-option::part(parent) { - display: flex; - padding: 0px 8px; - color: var(--text_primary); - font-size: 12px; - line-height: 24px; - border-radius: 4px; - flex-direction: row; - align-items: center; - flex-wrap: nowrap; - justify-content: flex-start; - } - - setting-option:hover::part(parent) { - background-color: var(--overlay_hover); - } - - setting-option:active::part(parent) { - background-color: var(--overlay_pressed); - } - - setting-option[is-selected]::part(parent) { - background-color: var(--overlay_active); - } - - setting-option::part(text) { - margin-right: 8px; - overflow: hidden; - text-wrap: nowrap; - text-overflow: ellipsis; - flex: 1; - } - - setting-option::part(check-icon) { - display: none; - position: relative; - right: -4px; - width: 1em; - height: 1em; - color: var(--icon_primary); - flex-shrink: 0; - } - - setting-option[is-selected]::part(check-icon) { - display: block; - } \ No newline at end of file diff --git a/static/config.html b/static/config.html deleted file mode 100644 index 17c743aa..00000000 --- a/static/config.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - NapCat-WebUi - - - - - - - \ No newline at end of file diff --git a/static/index.html b/static/index.html deleted file mode 100644 index f4e831cd..00000000 --- a/static/index.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - WebUi - Index - - - - - - - \ No newline at end of file diff --git a/static/login.html b/static/login.html deleted file mode 100644 index 395777c3..00000000 --- a/static/login.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - WebUi - Login - - - - - - - - - diff --git a/vite.config.ts b/vite.config.ts index a0e91db0..d83f3deb 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,14 +3,12 @@ import { defineConfig, PluginOption, UserConfig } from 'vite'; import { resolve } from 'path'; import nodeResolve from '@rollup/plugin-node-resolve'; import { builtinModules } from 'module'; - +//依赖排除 const external = ['silk-wasm', 'ws', 'express', 'qrcode-terminal', 'fluent-ffmpeg', 'piscina']; -const nodeModules = [...builtinModules, builtinModules.map(m => `node:${m}`)].flat(); - +const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat(); function genCpModule(module: string) { return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false }; } - let startScripts: string[] | undefined = undefined; if (process.env.NAPCAT_BUILDSYS == 'linux') { startScripts = []; @@ -19,15 +17,13 @@ if (process.env.NAPCAT_BUILDSYS == 'linux') { } else { startScripts = ['./script/KillQQ.bat']; } - const FrameworkBaseConfigPlugin: PluginOption[] = [ cp({ targets: [ { src: './manifest.json', dest: 'dist' }, { src: './src/core/external/napcat.json', dest: 'dist/config/' }, { src: './src/native/packet', dest: 'dist/moehoo', flatten: false }, - { src: './static/', dest: 'dist/static/', flatten: false }, - { src: './src/onebot/config/onebot11.json', dest: 'dist/config/' }, + { src: './napcat.webui/dist/', dest: 'dist/static/', flatten: false }, { src: './src/framework/liteloader.cjs', dest: 'dist' }, { src: './src/framework/napcat.cjs', dest: 'dist' }, { src: './src/framework/preload.cjs', dest: 'dist' }, @@ -42,14 +38,13 @@ const ShellBaseConfigPlugin: PluginOption[] = [ cp({ targets: [ { src: './src/native/packet', dest: 'dist/moehoo', flatten: false }, - { src: './static/', dest: 'dist/static/', flatten: false }, + { src: './napcat.webui/dist/', dest: 'dist/static/', flatten: false }, { src: './src/core/external/napcat.json', dest: 'dist/config/' }, - { src: './src/onebot/config/onebot11.json', dest: 'dist/config/' }, { src: './package.json', dest: 'dist' }, { src: './launcher/', dest: 'dist', flatten: true }, - ...(startScripts.map((startScript) => { + ...startScripts.map((startScript) => { return { src: startScript, dest: 'dist' }; - })), + }), ], }), nodeResolve(),