Compare commits

...

65 Commits

Author SHA1 Message Date
手瓜一十雪
44ff92ad4b style: lint 2024-12-17 09:25:10 +08:00
手瓜一十雪
892262eb85 Merge pull request #635 from NapNeko/ref/ob-network
refactor: adjust onebot network
2024-12-17 09:07:39 +08:00
pk5ls20
2d9cc4d198 fix: as design 2024-12-17 07:07:04 +08:00
pk5ls20
a0c479485d refactor: adjust onebot network 2024-12-17 05:26:27 +08:00
手瓜一十雪
5650f18e50 Merge pull request #634 from bietiaop/main
fix: handleQuickOperation error
2024-12-17 00:29:16 +08:00
bietiaop
553885d025 fix: handleQuickOperation 2024-12-17 00:27:56 +08:00
Mlikiowa
35de00c4af release: v4.2.35 2024-12-16 14:10:08 +00:00
手瓜一十雪
09583e5de5 fuck javascript 2024-12-16 22:09:37 +08:00
Mlikiowa
38b0b7cd00 release: v4.2.34 2024-12-16 13:17:43 +00:00
手瓜一十雪
8b9c7b0c27 Merge pull request #632 from q8018414/patch-1
Update AboutUs.vue
2024-12-16 21:16:39 +08:00
手瓜一十雪
1005619bf3 Merge pull request #630 from NapNeko/dependabot/npm_and_yarn/rollup/plugin-typescript-12.1.2
chore(deps-dev): bump @rollup/plugin-typescript from 11.1.6 to 12.1.2
2024-12-16 21:16:01 +08:00
手瓜一十雪
3e09cff9cb Merge branch 'main' into dependabot/npm_and_yarn/rollup/plugin-typescript-12.1.2 2024-12-16 21:15:52 +08:00
手瓜一十雪
c24384e454 Merge pull request #629 from NapNeko/dependabot/npm_and_yarn/rollup/plugin-node-resolve-16.0.0
chore(deps-dev): bump @rollup/plugin-node-resolve from 15.3.1 to 16.0.0
2024-12-16 21:15:23 +08:00
手瓜一十雪
f87a543406 fix: #631 2024-12-16 21:14:14 +08:00
手瓜一十雪
f752136283 fix: #631 2024-12-16 21:06:51 +08:00
my_key
7e71622a44 Update AboutUs.vue
新增用于显示New NapCat的tag,便于区分当前版本和最新版本
2024-12-16 20:09:40 +08:00
dependabot[bot]
da92afb379 chore(deps-dev): bump @rollup/plugin-typescript from 11.1.6 to 12.1.2
Bumps [@rollup/plugin-typescript](https://github.com/rollup/plugins/tree/HEAD/packages/typescript) from 11.1.6 to 12.1.2.
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/typescript/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/typescript-v12.1.2/packages/typescript)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:44:42 +00:00
dependabot[bot]
d3062de5f9 chore(deps-dev): bump @rollup/plugin-node-resolve from 15.3.1 to 16.0.0
Bumps [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve) from 15.3.1 to 16.0.0.
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/node-resolve/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/commonjs-v16.0.0/packages/node-resolve)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-node-resolve"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 08:43:46 +00:00
Mlikiowa
f1440b03a8 release: v4.2.33 2024-12-16 05:03:59 +00:00
手瓜一十雪
9a8b266cef Merge pull request #627 from bietiaop/main
feat: 查看登录QQ信息&获取快速登录列表详细信息&获取nc的包信息&优化了部分写法
2024-12-16 13:03:09 +08:00
手瓜一十雪
2a9bc57120 fix: #628 2024-12-16 13:00:07 +08:00
bietiaop
2ed83a0e30 feat: 查看登录QQ信息&获取快速登录列表详细信息&获取nc的包信息&优化了部分写法 2024-12-16 12:46:27 +08:00
Mlikiowa
116e8fd30a release: v4.2.32 2024-12-14 07:03:02 +00:00
手瓜一十雪
891f11173b fix 2024-12-14 15:01:47 +08:00
手瓜一十雪
dfc7996c17 Merge branch 'maybe-feat/type' 2024-12-14 15:00:18 +08:00
手瓜一十雪
dc0561d34f refactor: OB11ActiveHttpAdapter 2024-12-14 14:26:23 +08:00
手瓜一十雪
4fb0845d79 fix: #619 2024-12-14 13:46:25 +08:00
pk5ls20
0e0d4837b8 feat & fix: GetMiniAppArk 2024-12-14 06:56:23 +08:00
pk5ls20
a6adde7966 feat: attempt to enhance type inference 2024-12-14 05:56:49 +08:00
手瓜一十雪
7b693132f9 fix 2024-12-13 23:27:24 +08:00
手瓜一十雪
3c3114b6ab fix: 支持类型推导 2024-12-13 23:23:58 +08:00
Mlikiowa
5cdbf58f59 release: v4.2.31 2024-12-13 14:04:13 +00:00
手瓜一十雪
6f0a4131a2 fix: 异常 2024-12-13 22:03:51 +08:00
Mlikiowa
aa520e2f5d release: v4.2.30 2024-12-13 11:28:32 +00:00
手瓜一十雪
2c3b7e9ee8 fix: fuck! tencent 2024-12-13 19:28:04 +08:00
手瓜一十雪
b86a28092a fix: Login Check 2024-12-13 17:38:27 +08:00
手瓜一十雪
d59e5f2133 fix 2024-12-13 16:45:35 +08:00
手瓜一十雪
3fdd187102 fix: #610 2024-12-13 14:29:11 +08:00
Mlikiowa
3f085fd8ae release: v4.2.29 2024-12-13 04:47:13 +00:00
手瓜一十雪
a4fc131aec feat: 全平台30594 2024-12-13 12:36:51 +08:00
手瓜一十雪
d7d446c3fc fix 2024-12-12 23:02:10 +08:00
手瓜一十雪
212666e603 fix 2024-12-12 18:41:46 +08:00
手瓜一十雪
b545c28340 feat: 30483全平台兼容 2024-12-12 16:57:50 +08:00
手瓜一十雪
72bc345515 feat: linux 3.2.15-30483 2024-12-12 16:53:31 +08:00
手瓜一十雪
cc5082a9e3 feat: mac 6.9.62-30483 2024-12-12 11:43:31 +08:00
手瓜一十雪
45782a6c6c chore: docs 2024-12-12 10:44:09 +08:00
手瓜一十雪
e86d646cce chore: 万里妹妹干坏事 2024-12-12 10:43:13 +08:00
手瓜一十雪
92cfc6b8c8 feat: 更换演示代码 2024-12-12 10:00:38 +08:00
手瓜一十雪
82289d9f1f chore: debug log remove 2024-12-12 09:57:52 +08:00
手瓜一十雪
4cdbdaaf4e feat: win 30483 2024-12-12 09:47:44 +08:00
手瓜一十雪
ecde2427da fix: path 2024-12-12 09:37:51 +08:00
手瓜一十雪
fed1ec5d83 Merge pull request #620 from NapNeko/plugin-support
Feat: 支持快速基于NapCat进行二次开发 不需要通过传统NetWork
2024-12-12 09:34:27 +08:00
手瓜一十雪
4fbd764ced fix 2024-12-12 09:34:13 +08:00
手瓜一十雪
5361079010 Merge pull request #616 from Ander-pixe/webui-new
fix:路由守卫
2024-12-12 09:33:23 +08:00
手瓜一十雪
002d135ef5 fix 2024-12-11 18:26:26 +08:00
手瓜一十雪
a39b0a4a78 feat: plugin 2024-12-11 17:07:21 +08:00
纸凤孤凰
eb5d68422f fix: 路由守卫 2024-12-10 16:59:57 +08:00
纸凤孤凰
3dc13e5c2e Merge remote-tracking branch 'origin/main' into webui-new 2024-12-10 15:29:27 +08:00
huankong233
16881f057a fix: solve the token error
fix: remove useless defineProps
fix: add the missing dependencies to package.json
fix: add ES2022 into tsconfig.json
2024-12-10 09:44:38 +08:00
手瓜一十雪
1cd7d0577f fix: error 2024-12-10 09:44:23 +08:00
Mlikiowa
3c872df97a release: v4.2.28 2024-12-08 14:37:51 +00:00
pk5ls20
218b7bd2a0 fix: #607 2024-12-08 22:23:01 +08:00
Mlikiowa
4552d6970d release: v4.2.27 2024-12-06 03:40:03 +00:00
手瓜一十雪
4b319d15a7 refactor: GetGroupInfo 2024-12-06 11:39:11 +08:00
Mlikiowa
0ae3a4172c release: v4.2.26 2024-12-06 02:40:53 +00:00
70 changed files with 868 additions and 453 deletions

View File

@@ -30,11 +30,16 @@ NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现
[Cloudflare.Pages](https://napneko.pages.dev/) [Cloudflare.Pages](https://napneko.pages.dev/)
[Server.Other](https://napcat.cyou/) [Server.Other](https://docs.napcat.cyou/)
## 回家旅途 ## 回家旅途
[QQ Group](https://qm.qq.com/q/I6LU87a0Yq) [QQ Group#1](https://qm.qq.com/q/I6LU87a0Yq)
[QQ Group#2](https://qm.qq.com/q/uqh4I87KoM)
[Telegram](https://t.me/MelodicMoonlight)
> QQ Group#2 准许Bot / Telegram与QQ Group#2 为新建Group
## 性能设计/协议标准 ## 性能设计/协议标准
NapCat 已实现90+的 OneBot / GoCQ 标准接口,并提供兼容性保留接口,其设计理念遵守 无数据库/异步优先/后台刷新 的性能思想。 NapCat 已实现90+的 OneBot / GoCQ 标准接口,并提供兼容性保留接口,其设计理念遵守 无数据库/异步优先/后台刷新 的性能思想。

Binary file not shown.

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ", "name": "NapCatQQ",
"slug": "NapCat.Framework", "slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现", "description": "高性能的 OneBot 11 协议实现",
"version": "4.2.25", "version": "4.2.35",
"icon": "./logo.png", "icon": "./logo.png",
"authors": [ "authors": [
{ {

View File

@@ -12,6 +12,7 @@
"dependencies": { "dependencies": {
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.2.1",
"event-source-polyfill": "^1.0.31", "event-source-polyfill": "^1.0.31",
"mitt": "^3.0.1",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"tdesign-icons-vue-next": "^0.3.3", "tdesign-icons-vue-next": "^0.3.3",
"tdesign-vue-next": "^1.10.3", "tdesign-vue-next": "^1.10.3",

View File

@@ -1,7 +1,7 @@
export class githubApiManager { export class githubApiManager {
public async GetBaseData(): Promise<Response | null> { public async GetBaseData(): Promise<Response | null> {
try { try {
const ConfigResponse= await fetch('https://api.github.com/repos/NapNeko/NapCatQQ', { const ConfigResponse = await fetch('https://api.github.com/repos/NapNeko/NapCatQQ', {
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',

View File

@@ -1,5 +1,5 @@
import { OneBotConfig } from '../../../src/onebot/config/config'; import { OneBotConfig } from '../../../src/onebot/config/config';
import { ResponseCode } from '../../../src/webui/src/const/status';
export class QQLoginManager { export class QQLoginManager {
private retCredential: string; private retCredential: string;
private readonly apiPrefix: string; private readonly apiPrefix: string;
@@ -22,8 +22,8 @@ export class QQLoginManager {
}); });
if (ConfigResponse.status == 200) { if (ConfigResponse.status == 200) {
const ConfigResponseJson = await ConfigResponse.json(); const ConfigResponseJson = await ConfigResponse.json();
if (ConfigResponseJson.code == 0) { if (ConfigResponseJson.code == ResponseCode.Success) {
return ConfigResponseJson?.data as OneBotConfig; return ConfigResponseJson.data;
} }
} }
} catch (error) { } catch (error) {

View File

@@ -4,31 +4,17 @@
<h2 class="sotheby-font">QQ Login</h2> <h2 class="sotheby-font">QQ Login</h2>
<div class="login-methods"> <div class="login-methods">
<t-tooltip content="快速登录"> <t-tooltip content="快速登录">
<t-button <t-button id="quick-login" class="login-method" :class="{ active: loginMethod === 'quick' }"
id="quick-login" @click="loginMethod = 'quick'">Quick Login</t-button>
class="login-method"
:class="{ active: loginMethod === 'quick' }"
@click="loginMethod = 'quick'"
>Quick Login</t-button
>
</t-tooltip> </t-tooltip>
<t-tooltip content="二维码登录"> <t-tooltip content="二维码登录">
<t-button <t-button id="qrcode-login" class="login-method" :class="{ active: loginMethod === 'qrcode' }"
id="qrcode-login" @click="loginMethod = 'qrcode'">QR Code</t-button>
class="login-method"
:class="{ active: loginMethod === 'qrcode' }"
@click="loginMethod = 'qrcode'"
>QR Code</t-button
>
</t-tooltip> </t-tooltip>
</div> </div>
<div v-show="loginMethod === 'quick'" id="quick-login-dropdown" class="login-form"> <div v-show="loginMethod === 'quick'" id="quick-login-dropdown" class="login-form">
<t-select <t-select id="quick-login-select" v-model="selectedAccount" placeholder="Select Account"
id="quick-login-select" @change="selectAccount">
v-model="selectedAccount"
placeholder="Select Account"
@change="selectAccount"
>
<t-option v-for="account in quickLoginList" :key="account" :value="account">{{ account }}</t-option> <t-option v-for="account in quickLoginList" :key="account" :value="account">{{ account }}</t-option>
</t-select> </t-select>
</div> </div>
@@ -41,7 +27,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import * as QRCode from 'qrcode'; import * as QRCode from 'qrcode';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { MessagePlugin } from 'tdesign-vue-next'; import { MessagePlugin } from 'tdesign-vue-next';
@@ -55,6 +41,7 @@ const qrcodeCanvas = ref<HTMLCanvasElement | null>(null);
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth') || ''); const qqLoginManager = new QQLoginManager(localStorage.getItem('auth') || '');
let heartBeatTimer: number | null = null; let heartBeatTimer: number | null = null;
let qrcodeUrl: string = ''; let qrcodeUrl: string = '';
const selectAccount = async (accountName: string): Promise<void> => { const selectAccount = async (accountName: string): Promise<void> => {
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName); const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
if (result) { if (result) {
@@ -88,10 +75,6 @@ const HeartBeat = async (): Promise<void> => {
if (heartBeatTimer) { if (heartBeatTimer) {
clearInterval(heartBeatTimer); clearInterval(heartBeatTimer);
} }
// //判断是否已经调转
// if (router.currentRoute.value.path !== '/dashboard/basic-info') {
// return;
// }
await MessagePlugin.success('登录成功即将跳转'); await MessagePlugin.success('登录成功即将跳转');
await router.push({ path: '/dashboard/basic-info' }); await router.push({ path: '/dashboard/basic-info' });
} else if (isLogined?.qrcodeurl && qrcodeUrl !== isLogined.qrcodeurl) { } else if (isLogined?.qrcodeurl && qrcodeUrl !== isLogined.qrcodeurl) {
@@ -103,19 +86,38 @@ const HeartBeat = async (): Promise<void> => {
const InitPages = async (): Promise<void> => { const InitPages = async (): Promise<void> => {
quickLoginList.value = await qqLoginManager.getQQQuickLoginList(); quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
qrcodeUrl = await qqLoginManager.getQQLoginQrcode(); qrcodeUrl = await qqLoginManager.getQQLoginQrcode();
await nextTick();
generateQrCode(qrcodeUrl, qrcodeCanvas.value); generateQrCode(qrcodeUrl, qrcodeCanvas.value);
heartBeatTimer = window.setInterval(HeartBeat, 3000);
}; };
onMounted(() => { onMounted(() => {
InitPages(); InitPages().then().catch((err) => {
console.error('InitPages Error:', err);
});
heartBeatTimer = window.setInterval(HeartBeat, 3000);
}); });
onBeforeUnmount(() => {
if (heartBeatTimer) {
clearInterval(heartBeatTimer);
}
});
watch(loginMethod, async (newMethod) => {
if (newMethod === 'qrcode') {
await nextTick();
generateQrCode(qrcodeUrl, qrcodeCanvas.value);
}
});
</script> </script>
<style scoped> <style scoped>
.layout { .layout {
height: 100vh; height: 100vh;
} }
.login-container { .login-container {
padding: 20px; padding: 20px;
border-radius: 5px; border-radius: 5px;
@@ -182,4 +184,4 @@ onMounted(() => {
left: 0; left: 0;
right: 0; right: 0;
} }
</style> </style>

View File

@@ -33,7 +33,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, defineProps, onMounted, watch } from 'vue'; import { ref, onMounted, watch } from 'vue';
import emitter from '@/ts/event-bus'; import emitter from '@/ts/event-bus';
type MenuItem = { type MenuItem = {

View File

@@ -1,5 +1,5 @@
<template> <template>
<t-head-menu theme="light" class="bottom-menu"> <t-head-menu theme="light" class="bottom-menu">
<router-link v-for="item in menuItems" :key="item.value" :to="item.route"> <router-link v-for="item in menuItems" :key="item.value" :to="item.route">
<t-tooltip :content="item.label" placement="top"> <t-tooltip :content="item.label" placement="top">
<t-menu-item :value="item.value" :disabled="item.disabled" class="menu-item"> <t-menu-item :value="item.value" :disabled="item.disabled" class="menu-item">
@@ -7,15 +7,13 @@
<t-icon :name="item.icon" /> <t-icon :name="item.icon" />
</template> </template>
<!-- {{item.label}}--> <!-- {{item.label}}-->
</t-menu-item> </t-menu-item>
</t-tooltip> </t-tooltip>
</router-link> </router-link>
</t-head-menu> </t-head-menu>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps } from 'vue';
type MenuItem = { type MenuItem = {
value: string; value: string;
label: string; label: string;

View File

@@ -46,7 +46,7 @@ import {
Loading as TLoading, Loading as TLoading,
HeadMenu as THeadMenu HeadMenu as THeadMenu
} from 'tdesign-vue-next'; } from 'tdesign-vue-next';
import { router } from './router'; import router from './router';
import 'tdesign-vue-next/es/style/index.css'; import 'tdesign-vue-next/es/style/index.css';
const app = createApp(App); const app = createApp(App);
app.use(router); app.use(router);

View File

@@ -89,7 +89,11 @@
<t-tag class="tag-item pgk-color"> WebUi: {{ pkg.version }} </t-tag> <t-tag class="tag-item pgk-color"> WebUi: {{ pkg.version }} </t-tag>
<t-tag class="tag-item nc-color"> <t-tag class="tag-item nc-color">
NapCat: NapCat:
{{ githubReleasesData&&githubReleasesData[0] ?.tag_name ? githubReleasesData[0].tag_name : napCatVersion }} {{ napCatVersion }}
</t-tag>
<t-tag v-if="githubReleasesData&&githubReleasesData[0] ?.tag_name" class="tag-item nc-color">
New NapCat:
{{ githubReleasesData[0].tag_name }}
</t-tag> </t-tag>
<t-tag class="tag-item td-color"> TDesign: {{ pkg.dependencies['tdesign-vue-next'] }} </t-tag> <t-tag class="tag-item td-color"> TDesign: {{ pkg.dependencies['tdesign-vue-next'] }} </t-tag>
</span> </span>

View File

@@ -24,7 +24,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { HttpClientConfig } from '../../../../src/onebot/config/config'; import { HttpClientConfig } from '../../../../src/onebot/config/config';
const props = defineProps<{ const props = defineProps<{

View File

@@ -30,7 +30,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { HttpServerConfig } from '../../../../src/onebot/config/config'; import { HttpServerConfig } from '../../../../src/onebot/config/config';
const props = defineProps<{ const props = defineProps<{

View File

@@ -27,7 +27,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { WebsocketClientConfig } from '../../../../src/onebot/config/config'; import { WebsocketClientConfig } from '../../../../src/onebot/config/config';
const props = defineProps<{ const props = defineProps<{

View File

@@ -33,7 +33,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { WebsocketServerConfig } from '../../../../src/onebot/config/config'; import { WebsocketServerConfig } from '../../../../src/onebot/config/config';
const props = defineProps<{ const props = defineProps<{

View File

@@ -7,6 +7,8 @@ import NetWork from '../pages/NetWork.vue';
import QQLogin from '../components/QQLogin.vue'; import QQLogin from '../components/QQLogin.vue';
import WebUiLogin from '../components/WebUiLogin.vue'; import WebUiLogin from '../components/WebUiLogin.vue';
import OtherConfig from '../pages/OtherConfig.vue'; import OtherConfig from '../pages/OtherConfig.vue';
import { MessagePlugin } from 'tdesign-vue-next';
import { QQLoginManager } from '@/backend/shell';
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ path: '/', redirect: '/webui' }, { path: '/', redirect: '/webui' },
@@ -26,7 +28,27 @@ const routes: Array<RouteRecordRaw> = [
}, },
]; ];
export const router = createRouter({ const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
routes, routes,
}); });
router.beforeEach(async (to, from, next) => {
const isPublicRoute = ['/webui', '/qqlogin'].includes(to.path);
const token = localStorage.getItem('auth');
if (!isPublicRoute) {
if (!token) {
MessagePlugin.error('请先登录');
return next('/webui');
}
const login = await new QQLoginManager(token).checkWebUiLogined();
if (!login) {
MessagePlugin.error('请先登录');
return next('/webui');
}
}
next();
});
export default router;

View File

@@ -3,22 +3,15 @@
"target": "ESNext", "target": "ESNext",
"jsx": "preserve", "jsx": "preserve",
"jsxImportSource": "vue", "jsxImportSource": "vue",
"lib": [ "lib": ["DOM", "DOM.Iterable", "ES2022"],
"DOM",
"DOM.Iterable"
],
"baseUrl": ".", "baseUrl": ".",
"module": "esnext", "module": "esnext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"paths": { "paths": {
"@/*": [ "@/*": ["src/*"]
"src/*"
]
}, },
"resolveJsonModule": true, "resolveJsonModule": true,
"types": [ "types": ["vite/client"],
"vite/client"
],
"strict": true, "strict": true,
"strictNullChecks": true, "strictNullChecks": true,
"noUnusedLocals": true, "noUnusedLocals": true,
@@ -30,5 +23,5 @@
}, },
"include": ["src"], "include": ["src"],
"exclude": ["node_modules"], "exclude": ["node_modules"],
"references": [{"path": "./tsconfig.node.json"}] "references": [{ "path": "./tsconfig.node.json" }]
} }

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "4.2.25", "version": "4.2.35",
"scripts": { "scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1",
@@ -23,8 +23,8 @@
"@eslint/js": "^9.14.0", "@eslint/js": "^9.14.0",
"@log4js-node/log4js-api": "^1.0.2", "@log4js-node/log4js-api": "^1.0.2",
"@napneko/nap-proto-core": "^0.0.4", "@napneko/nap-proto-core": "^0.0.4",
"@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^12.1.2",
"@rollup/plugin-typescript": "^11.1.6", "@rollup/plugin-node-resolve": "^16.0.0",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@sinclair/typebox": "^0.34.9", "@sinclair/typebox": "^0.34.9",
"@types/express": "^5.0.0", "@types/express": "^5.0.0",

View File

@@ -1 +1 @@
export const napCatVersion = '4.2.25'; export const napCatVersion = '4.2.35';

View File

@@ -8,6 +8,7 @@ import {
MemberExtSourceType, MemberExtSourceType,
NapCatCore, NapCatCore,
GroupNotify, GroupNotify,
GroupInfoSource,
} from '@/core'; } from '@/core';
import { isNumeric, solveAsyncProblem } from '@/common/helper'; import { isNumeric, solveAsyncProblem } from '@/common/helper';
import { LimitedHashTable } from '@/common/message-unique'; import { LimitedHashTable } from '@/common/message-unique';
@@ -24,6 +25,19 @@ export class NTQQGroupApi {
this.core = core; this.core = core;
} }
async fetchGroupDetail(groupCode: string) {
const [, detailInfo] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getGroupDetailInfo',
'NodeIKernelGroupListener/onGroupDetailInfoChange',
[groupCode, GroupInfoSource.KDATACARD],
(ret) => ret.result === 0,
(detailInfo) => detailInfo.groupCode === groupCode,
1,
5000
);
return detailInfo;
}
async initApi() { async initApi() {
this.initCache().then().catch(e => this.context.logger.logError(e)); this.initCache().then().catch(e => this.context.logger.logError(e));
} }

View File

@@ -110,5 +110,29 @@
"6.9.62-30366": { "6.9.62-30366": {
"appid": 537258401, "appid": 537258401,
"qua": "V1_MAC_NQ_6.9.62_30366_GW_B" "qua": "V1_MAC_NQ_6.9.62_30366_GW_B"
},
"9.9.17-30483": {
"appid": 537258439,
"qua": "V1_WIN_NQ_9.9.17_30483_GW_B"
},
"6.9.62-30483": {
"appid": 537258463,
"qua": "V1_MAC_NQ_6.9.62_30483_GW_B"
},
"3.2.15-30483": {
"appid": 537258474,
"qua": "V1_LNX_NQ_3.2.15_30483_GW_B"
},
"9.9.17-30594": {
"appid": 537258439,
"qua": "V1_WIN_NQ_9.9.17_30594_GW_B"
},
"6.9.62-30594": {
"appid": 537258463,
"qua": "V1_MAC_NQ_6.9.62_30594_GW_B"
},
"3.2.15-30594": {
"appid": 537258474,
"qua": "V1_LNX_NQ_3.2.15_30594_GW_B"
} }
} }

View File

@@ -122,5 +122,45 @@
"6.9.62-30366-arm64": { "6.9.62-30366-arm64": {
"send": "4189770", "send": "4189770",
"recv": "418BF88" "recv": "418BF88"
},
"9.9.17-30483-x64": {
"send": "39AC1B0",
"recv": "39B05E4"
},
"6.9.62-30483-arm64": {
"send": "41896B0",
"recv": "418bec8"
},
"6.9.62-30483-x64": {
"send": "4669460",
"recv": "466BCCC"
},
"3.2.15-30483-x64": {
"send": "A402540",
"recv": "A405E40"
},
"3.2.15-30483-arm64": {
"send": "70C40E8",
"recv": "70C7920"
},
"9.9.17-30594-x64": {
"send": "39AC1B0",
"recv": "39B05E4"
},
"6.9.62-30594-arm64": {
"send": "41896B0",
"recv": "418bec8"
},
"6.9.62-30594-x64": {
"send": "4669460",
"recv": "466BCCC"
},
"3.2.15-30594-x64": {
"send": "A402540",
"recv": "A405E40"
},
"3.2.15-30594-arm64": {
"send": "70C40E8",
"recv": "70C7920"
} }
} }

View File

@@ -1,4 +1,4 @@
import { DataSource, Group, GroupListUpdateType, GroupMember, GroupNotify, ShutUpGroupMember } from '@/core/types'; import { DataSource, Group, GroupDetailInfo, GroupListUpdateType, GroupMember, GroupNotify, ShutUpGroupMember } from '@/core/types';
export class NodeIKernelGroupListener { export class NodeIKernelGroupListener {
onGroupListInited(listEmpty: boolean): any { } onGroupListInited(listEmpty: boolean): any { }
@@ -28,7 +28,7 @@ export class NodeIKernelGroupListener {
onGroupConfMemberChange(...args: unknown[]): any { onGroupConfMemberChange(...args: unknown[]): any {
} }
onGroupDetailInfoChange(...args: unknown[]): any { onGroupDetailInfoChange(detailInfo: GroupDetailInfo): any {
} }
onGroupExtListUpdate(...args: unknown[]): any { onGroupExtListUpdate(...args: unknown[]): any {

View File

@@ -3,6 +3,7 @@ export interface MiniAppReqCustomParams {
desc: string; desc: string;
picUrl: string; picUrl: string;
jumpUrl: string; jumpUrl: string;
webUrl?: string;
} }
export interface MiniAppReqTemplateParams { export interface MiniAppReqTemplateParams {

View File

@@ -124,7 +124,7 @@ export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
} }
get isGroupReply(): boolean { get isGroupReply(): boolean {
return this.messageClientSeq !== 0; return this.messageClientSeq === 0;
} }
buildElement(): NapProtoEncodeStructType<typeof Elem>[] { buildElement(): NapProtoEncodeStructType<typeof Elem>[] {

View File

@@ -30,7 +30,7 @@ class GetMiniAppAdaptShareInfo extends PacketTransformer<typeof proto.MiniAppAda
shareType: req.shareType, shareType: req.shareType,
versionId: req.versionId, versionId: req.versionId,
withShareTicket: req.withShareTicket, withShareTicket: req.withShareTicket,
webURL: "", webURL: req.webUrl ?? "",
appidRich: Buffer.alloc(0), appidRich: Buffer.alloc(0),
template: { template: {
templateId: "", templateId: "",

View File

@@ -149,7 +149,7 @@ export interface NodeIKernelGroupService {
getGroupExtList(force: boolean): Promise<GeneralCallResult>; getGroupExtList(force: boolean): Promise<GeneralCallResult>;
getGroupDetailInfo(groupCode: string, groupInfoSource: GroupInfoSource): Promise<unknown>; getGroupDetailInfo(groupCode: string, groupInfoSource: GroupInfoSource): Promise<GeneralCallResult>;
getMemberExtInfo(param: GroupExtParam): Promise<unknown>;//req getMemberExtInfo(param: GroupExtParam): Promise<unknown>;//req

View File

@@ -4,6 +4,7 @@ import { GeneralCallResult } from '@/core/services/common';
import { MsgReqType, QueryMsgsParams, TmpChatInfoApi } from '@/core/types/msg'; import { MsgReqType, QueryMsgsParams, TmpChatInfoApi } from '@/core/types/msg';
export interface NodeIKernelMsgService { export interface NodeIKernelMsgService {
buildMultiForwardMsg(req: { srcMsgIds: Array<string>, srcContact: Peer }): Promise<GeneralCallResult & { rspInfo: { elements: unknown } }>;
generateMsgUniqueId(chatType: number, time: string): string; generateMsgUniqueId(chatType: number, time: string): string;

View File

@@ -16,7 +16,7 @@ export interface NodeIKernelSearchService {
penetrate: string penetrate: string
}): Promise<GeneralCallResult>;// needs 1 arguments }): Promise<GeneralCallResult>;// needs 1 arguments
searchLocalInfo(keywords: string, unknown: number/*4*/): unknown; searchLocalInfo(keywords: string, type: number/*4*/): unknown;
cancelSearchLocalInfo(...args: any[]): unknown;// needs 3 arguments cancelSearchLocalInfo(...args: any[]): unknown;// needs 3 arguments

View File

@@ -17,7 +17,160 @@ export enum GroupInfoSource {
KRECENTCONTACT, KRECENTCONTACT,
KMOREPANEL KMOREPANEL
} }
export interface GroupDetailInfo {
groupCode: string;
groupUin: string;
ownerUid: string;
ownerUin: string;
groupFlag: number;
groupFlagExt: number;
maxMemberNum: number;
memberNum: number;
groupOption: number;
classExt: number;
groupName: string;
fingerMemo: string;
groupQuestion: string;
certType: number;
richFingerMemo: string;
tagRecord: any[];
shutUpAllTimestamp: number;
shutUpMeTimestamp: number;
groupTypeFlag: number;
privilegeFlag: number;
groupSecLevel: number;
groupFlagExt3: number;
isConfGroup: number;
isModifyConfGroupFace: number;
isModifyConfGroupName: number;
groupFlagExt4: number;
groupMemo: string;
cmdUinMsgSeq: number;
cmdUinJoinTime: number;
cmdUinUinFlag: number;
cmdUinMsgMask: number;
groupSecLevelInfo: number;
cmdUinPrivilege: number;
cmdUinFlagEx2: number;
appealDeadline: number;
remarkName: string;
isTop: boolean;
groupFace: number;
groupGeoInfo: {
ownerUid: string;
SetTime: number;
CityId: number;
Longitude: string;
Latitude: string;
GeoContent: string;
poiId: string;
};
certificationText: string;
cmdUinRingtoneId: number;
longGroupName: string;
autoAgreeJoinGroupUserNumForConfGroup: number;
autoAgreeJoinGroupUserNumForNormalGroup: number;
cmdUinFlagExt3Grocery: number;
groupCardPrefix: {
introduction: string;
rptPrefix: any[];
};
groupExt: {
groupInfoExtSeq: number;
reserve: number;
luckyWordId: string;
lightCharNum: number;
luckyWord: string;
starId: number;
essentialMsgSwitch: number;
todoSeq: number;
blacklistExpireTime: number;
isLimitGroupRtc: number;
companyId: number;
hasGroupCustomPortrait: number;
bindGuildId: string;
groupOwnerId: {
memberUin: string;
memberUid: string;
memberQid: string;
};
essentialMsgPrivilege: number;
msgEventSeq: string;
inviteRobotSwitch: number;
gangUpId: string;
qqMusicMedalSwitch: number;
showPlayTogetherSwitch: number;
groupFlagPro1: string;
groupBindGuildIds: {
guildIds: any[];
};
viewedMsgDisappearTime: string;
groupExtFlameData: {
switchState: number;
state: number;
dayNums: any[];
version: number;
updateTime: string;
isDisplayDayNum: boolean;
};
groupBindGuildSwitch: number;
groupAioBindGuildId: string;
groupExcludeGuildIds: {
guildIds: any[];
};
fullGroupExpansionSwitch: number;
fullGroupExpansionSeq: string;
inviteRobotMemberSwitch: number;
inviteRobotMemberExamine: number;
groupSquareSwitch: number;
};
msgLimitFrequency: number;
hlGuildAppid: number;
hlGuildSubType: number;
isAllowRecallMsg: number;
confUin: string;
confMaxMsgSeq: number;
confToGroupTime: number;
groupSchoolInfo: {
location: string;
grade: number;
school: string;
};
activeMemberNum: number;
groupGrade: number;
groupCreateTime: number;
subscriptionUin: string;
subscriptionUid: string;
noFingerOpenFlag: number;
noCodeFingerOpenFlag: number;
isGroupFreeze: number;
allianceId: string;
groupExtOnly: {
tribeId: number;
moneyForAddGroup: number;
};
isAllowConfGroupMemberModifyGroupName: number;
isAllowConfGroupMemberNick: number;
isAllowConfGroupMemberAtAll: number;
groupClassText: string;
groupFreezeReason: number;
headPortraitSeq: number;
groupHeadPortrait: {
portraitCnt: number;
portraitInfo: any[];
defaultId: number;
verifyingPortraitCnt: number;
verifyingPortraitInfo: any[];
};
cmdUinJoinMsgSeq: number;
cmdUinJoinRealMsgSeq: number;
groupAnswer: string;
groupAdminMaxNum: number;
inviteNoAuthNumLimit: string;
hlGuildOrgId: number;
isAllowHlGuildBinary: number;
localExitGroupReason: number;
}
export interface GroupExt0xEF0InfoFilter { export interface GroupExt0xEF0InfoFilter {
bindGuildId: number; bindGuildId: number;
blacklistExpireTime: number; blacklistExpireTime: number;

View File

@@ -1,5 +1,5 @@
//LiteLoader需要提供部分IPC接口以便于其他插件调用 //LiteLoader需要提供部分IPC接口以便于其他插件调用
const { ipcMain } = require('electron'); const { ipcMain, BrowserWindow } = require('electron');
const napcat = require('./napcat.cjs'); const napcat = require('./napcat.cjs');
const { shell } = require('electron'); const { shell } = require('electron');
ipcMain.handle('napcat_get_webtoken', async (event, arg) => { ipcMain.handle('napcat_get_webtoken', async (event, arg) => {
@@ -13,4 +13,14 @@ ipcMain.handle('napcat_get_reactweb', async (event, arg) => {
let port = url.port; let port = url.port;
let token = url.searchParams.get('token'); let token = url.searchParams.get('token');
return `https://napcat.152710.xyz/web_login?back=http://127.0.0.1:${port}&token=${token}`; return `https://napcat.152710.xyz/web_login?back=http://127.0.0.1:${port}&token=${token}`;
});
ipcMain.on('napcat_open_inner_url', (event, url) => {
const win = new BrowserWindow({
autoHideMenuBar: true,
});
win.loadURL(url);
win.webContents.setWindowOpenHandler(details => {
win.loadURL(details.url)
})
}); });

View File

@@ -6,6 +6,9 @@ const napcat = {
openExternalUrl: async (url) => { openExternalUrl: async (url) => {
ipcRenderer.send('open_external_url', url); ipcRenderer.send('open_external_url', url);
}, },
openInnerUrl: async (url) => {
ipcRenderer.send('napcat_open_inner_url', url);
},
getWebUiUrlReact: async () => { getWebUiUrlReact: async () => {
return ipcRenderer.invoke('napcat_get_reactweb'); return ipcRenderer.invoke('napcat_get_reactweb');
} }

View File

@@ -24,7 +24,7 @@ export const onSettingWindowCreated = async (view) => {
`; `;
view.querySelector('.nc_openwebui').addEventListener('click', () => { view.querySelector('.nc_openwebui').addEventListener('click', () => {
window.open(webui, '_blank'); window.napcat.openInnerUrl(webui);
}); });
view.querySelector('.nc_openwebui_ex').addEventListener('click', () => { view.querySelector('.nc_openwebui_ex').addEventListener('click', () => {
window.napcat.openExternalUrl(webui); window.napcat.openExternalUrl(webui);

View File

@@ -29,7 +29,7 @@ export class OB11Response {
} }
export abstract class OneBotAction<PayloadType, ReturnDataType> { export abstract class OneBotAction<PayloadType, ReturnDataType> {
actionName: ActionName = ActionName.Unknown; actionName: typeof ActionName[keyof typeof ActionName] = ActionName.Unknown;
core: NapCatCore; core: NapCatCore;
private validate: ValidateFunction<any> | undefined = undefined; private validate: ValidateFunction<any> | undefined = undefined;
payloadSchema: any = undefined; payloadSchema: any = undefined;
@@ -84,4 +84,4 @@ export abstract class OneBotAction<PayloadType, ReturnDataType> {
} }
abstract _handle(payload: PayloadType, adaptername: string): PromiseLike<ReturnDataType>; abstract _handle(payload: PayloadType, adaptername: string): PromiseLike<ReturnDataType>;
} }

View File

@@ -11,7 +11,8 @@ const SchemaData = Type.Union([
desc: Type.String(), desc: Type.String(),
picUrl: Type.String(), picUrl: Type.String(),
jumpUrl: Type.String(), jumpUrl: Type.String(),
rawArkData: Type.Optional(Type.Union([Type.Boolean(), Type.String()])) webUrl: Type.Optional(Type.String()),
rawArkData: Type.Optional(Type.Union([Type.String()]))
}), }),
Type.Object({ Type.Object({
title: Type.String(), title: Type.String(),
@@ -19,6 +20,7 @@ const SchemaData = Type.Union([
picUrl: Type.String(), picUrl: Type.String(),
jumpUrl: Type.String(), jumpUrl: Type.String(),
iconUrl: Type.String(), iconUrl: Type.String(),
webUrl: Type.Optional(Type.String()),
appId: Type.String(), appId: Type.String(),
scene: Type.Union([Type.Number(), Type.String()]), scene: Type.Union([Type.Number(), Type.String()]),
templateType: Type.Union([Type.Number(), Type.String()]), templateType: Type.Union([Type.Number(), Type.String()]),
@@ -28,7 +30,7 @@ const SchemaData = Type.Union([
versionId: Type.String(), versionId: Type.String(),
sdkId: Type.String(), sdkId: Type.String(),
withShareTicket: Type.Union([Type.Number(), Type.String()]), withShareTicket: Type.Union([Type.Number(), Type.String()]),
rawArkData: Type.Optional(Type.Union([Type.Boolean(), Type.String()])) rawArkData: Type.Optional(Type.Union([Type.String()]))
}) })
]); ]);
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
@@ -45,7 +47,8 @@ export class GetMiniAppArk extends GetPacketStatusDepends<Payload, {
title: payload.title, title: payload.title,
desc: payload.desc, desc: payload.desc,
picUrl: payload.picUrl, picUrl: payload.picUrl,
jumpUrl: payload.jumpUrl jumpUrl: payload.jumpUrl,
webUrl: payload.webUrl,
} as MiniAppReqCustomParams; } as MiniAppReqCustomParams;
if ('type' in payload) { if ('type' in payload) {
reqParam = MiniAppInfoHelper.generateReq(customParams, MiniAppInfo.get(payload.type)!.template); reqParam = MiniAppInfoHelper.generateReq(customParams, MiniAppInfo.get(payload.type)!.template);
@@ -63,13 +66,13 @@ export class GetMiniAppArk extends GetPacketStatusDepends<Payload, {
verType: +verType, verType: +verType,
shareType: +shareType, shareType: +shareType,
versionId: versionId, versionId: versionId,
withShareTicket: +withShareTicket withShareTicket: +withShareTicket,
} }
); );
} }
const arkData = await this.core.apis.PacketApi.pkt.operation.GetMiniAppAdaptShareInfo(reqParam); const arkData = await this.core.apis.PacketApi.pkt.operation.GetMiniAppAdaptShareInfo(reqParam);
return { return {
data: payload.rawArkData ? arkData : MiniAppInfoHelper.RawToSend(arkData) data: payload.rawArkData === 'true' ? arkData : MiniAppInfoHelper.RawToSend(arkData)
}; };
} }
} }

View File

@@ -10,8 +10,7 @@ const SchemaData = Type.Object({
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
export class OCRImage extends OneBotAction<Payload, any> { class OCRImageBase extends OneBotAction<Payload, any> {
actionName = ActionName.OCRImage;
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
@@ -34,6 +33,10 @@ export class OCRImage extends OneBotAction<Payload, any> {
} }
} }
export class IOCRImage extends OCRImage { export class OCRImage extends OCRImageBase {
actionName = ActionName.OCRImage;
}
export class IOCRImage extends OCRImageBase {
actionName = ActionName.IOCRImage; actionName = ActionName.IOCRImage;
} }

View File

@@ -8,14 +8,18 @@ const SchemaData = Type.Object({
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
export class SetGroupSign extends GetPacketStatusDepends<Payload, any> { class SetGroupSignBase extends GetPacketStatusDepends<Payload, any> {
actionName = ActionName.SetGroupSign;
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
return await this.core.apis.PacketApi.pkt.operation.GroupSign(+payload.group_id); return await this.core.apis.PacketApi.pkt.operation.GroupSign(+payload.group_id);
} }
} }
export class SendGroupSign extends SetGroupSign {
export class SetGroupSign extends SetGroupSignBase {
actionName = ActionName.SendGroupSign;
}
export class SendGroupSign extends SetGroupSignBase {
actionName = ActionName.SendGroupSign; actionName = ActionName.SendGroupSign;
} }

View File

@@ -17,14 +17,13 @@ class GetGroupInfo extends OneBotAction<Payload, OB11Group> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const group = (await this.core.apis.GroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString()); const group = (await this.core.apis.GroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString());
if (!group) { if (!group) {
const data = await this.core.apis.GroupApi.searchGroup(payload.group_id.toString()); const data = await this.core.apis.GroupApi.fetchGroupDetail(payload.group_id.toString());
if (!data) throw new Error('Group not found');
return { return {
...data.searchGroupInfo, ...data,
group_id: +payload.group_id, group_id: +payload.group_id,
group_name: data.searchGroupInfo.groupName, group_name: data.groupName,
member_count: data.searchGroupInfo.memberNum, member_count: data.memberNum,
max_member_count: data.searchGroupInfo.maxMemberNum, max_member_count: data.maxMemberNum,
}; };
} }
return OB11Construct.group(group); return OB11Construct.group(group);

View File

@@ -29,13 +29,14 @@ class GetGroupMemberInfo extends OneBotAction<Payload, OB11GroupMember> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const isNocache = this.parseBoolean(payload.no_cache ?? true); const isNocache = this.parseBoolean(payload.no_cache ?? true);
const uid = await this.getUid(payload.user_id); const uid = await this.getUid(payload.user_id);
const [member, info] = await Promise.all([ const groupMember = this.core.apis.GroupApi.groupMemberCache.get(payload.group_id.toString())?.get(uid);
let [member, info] = await Promise.all([
this.core.apis.GroupApi.getGroupMemberEx(payload.group_id.toString(), uid, isNocache), this.core.apis.GroupApi.getGroupMemberEx(payload.group_id.toString(), uid, isNocache),
this.core.apis.UserApi.getUserDetailInfo(uid), this.core.apis.UserApi.getUserDetailInfo(uid),
]); ]);
if (!member) throw new Error(`群(${payload.group_id})成员${payload.user_id}不存在`); if (!member || !groupMember) throw new Error(`群(${payload.group_id})成员${payload.user_id}不存在`);
if (info) { if (info) {
Object.assign(member, info); member = { ...groupMember, ...member, ...info };
} else { } else {
this.core.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息`); this.core.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息`);
} }

View File

@@ -1,9 +1,9 @@
import SendMsg, { ContextMode } from '@/onebot/action/msg/SendMsg'; import { ContextMode, SendMsgBase } from '@/onebot/action/msg/SendMsg';
import { ActionName, BaseCheckResult } from '@/onebot/action/router'; import { ActionName, BaseCheckResult } from '@/onebot/action/router';
import { OB11PostSendMsg } from '@/onebot/types'; import { OB11PostSendMsg } from '@/onebot/types';
// 未检测参数 // 未检测参数
class SendGroupMsg extends SendMsg { class SendGroupMsg extends SendMsgBase {
actionName = ActionName.SendGroupMsg; actionName = ActionName.SendGroupMsg;
contextMode: ContextMode = ContextMode.Group; contextMode: ContextMode = ContextMode.Group;

View File

@@ -103,10 +103,7 @@ import { GetAiCharacters } from "@/onebot/action/extends/GetAiCharacters";
import { GetGuildList } from './guild/GetGuildList'; import { GetGuildList } from './guild/GetGuildList';
import { GetGuildProfile } from './guild/GetGuildProfile'; import { GetGuildProfile } from './guild/GetGuildProfile';
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
export type ActionMap = Map<string, OneBotAction<any, any>>;
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore): ActionMap {
const actionHandlers = [ const actionHandlers = [
new GetGroupInfoEx(obContext, core), new GetGroupInfoEx(obContext, core),
@@ -220,12 +217,30 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new SendGroupAiRecord(obContext, core), new SendGroupAiRecord(obContext, core),
new GetAiCharacters(obContext, core), new GetAiCharacters(obContext, core),
]; ];
const actionMap = new Map();
for (const action of actionHandlers) { type HandlerUnion = typeof actionHandlers[number];
actionMap.set(action.actionName, action); type MapType = {
actionMap.set(action.actionName + '_async', action); [H in HandlerUnion as H['actionName']]: H;
actionMap.set(action.actionName + '_rate_limited', action); } & {
[H in HandlerUnion as `${H['actionName']}_async`]: H;
} & {
[H in HandlerUnion as `${H['actionName']}_rate_limited`]: H;
};
const _map = new Map<keyof MapType, HandlerUnion>();
actionHandlers.forEach(h => {
_map.set(h.actionName as keyof MapType, h);
_map.set(`${h.actionName}_async` as keyof MapType, h);
_map.set(`${h.actionName}_rate_limited` as keyof MapType, h);
});
function get<K extends keyof MapType>(key: K): MapType[K];
function get<K extends keyof MapType>(key: K): null;
function get<K extends keyof MapType>(key: K): HandlerUnion | null | MapType[K] {
return _map.get(key as keyof MapType) ?? null;
} }
return actionMap; return { get };
} }
export type ActionMap = ReturnType<typeof createActionMap>

View File

@@ -88,8 +88,7 @@ function getSpecialMsgNum(payload: OB11PostSendMsg, msgType: OB11MessageDataType
return 0; return 0;
} }
export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> { export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
actionName = ActionName.SendMsg;
contextMode = ContextMode.Normal; contextMode = ContextMode.Normal;
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> { protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
@@ -379,4 +378,6 @@ export class SendMsg extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
} }
} }
export default SendMsg; export default class SendMsg extends SendMsgBase {
actionName = ActionName.SendMsg;
}

View File

@@ -1,9 +1,9 @@
import SendMsg, { ContextMode } from './SendMsg'; import { ContextMode, SendMsgBase } from './SendMsg';
import { ActionName, BaseCheckResult } from '@/onebot/action/router'; import { ActionName, BaseCheckResult } from '@/onebot/action/router';
import { OB11PostSendMsg } from '@/onebot/types'; import { OB11PostSendMsg } from '@/onebot/types';
// 未检测参数 // 未检测参数
class SendPrivateMsg extends SendMsg { class SendPrivateMsg extends SendMsgBase {
actionName = ActionName.SendPrivateMsg; actionName = ActionName.SendPrivateMsg;
contextMode: ContextMode = ContextMode.Private; contextMode: ContextMode = ContextMode.Private;

View File

@@ -3,8 +3,6 @@ import { ActionName, BaseCheckResult } from '@/onebot/action/router';
export abstract class GetPacketStatusDepends<PT, RT> extends OneBotAction<PT, RT> { export abstract class GetPacketStatusDepends<PT, RT> extends OneBotAction<PT, RT> {
actionName = ActionName.GetPacketStatus;
protected async check(payload: PT): Promise<BaseCheckResult>{ protected async check(payload: PT): Promise<BaseCheckResult>{
if (!this.core.apis.PacketApi.available) { if (!this.core.apis.PacketApi.available) {
return { return {
@@ -18,6 +16,8 @@ export abstract class GetPacketStatusDepends<PT, RT> extends OneBotAction<PT, RT
} }
export class GetPacketStatus extends GetPacketStatusDepends<any, null> { export class GetPacketStatus extends GetPacketStatusDepends<any, null> {
actionName = ActionName.GetPacketStatus;
async _handle(payload: any) { async _handle(payload: any) {
return null; return null;
} }

View File

@@ -13,134 +13,134 @@ export interface InvalidCheckResult {
[k: string | number]: any; [k: string | number]: any;
} }
export enum ActionName { export const ActionName = {
// onebot 11 // onebot 11
SendPrivateMsg = 'send_private_msg', SendPrivateMsg : 'send_private_msg',
SendGroupMsg = 'send_group_msg', SendGroupMsg : 'send_group_msg',
SendMsg = 'send_msg', SendMsg : 'send_msg',
DeleteMsg = 'delete_msg', DeleteMsg : 'delete_msg',
GetMsg = 'get_msg', GetMsg : 'get_msg',
GoCQHTTP_GetForwardMsg = 'get_forward_msg', GoCQHTTP_GetForwardMsg : 'get_forward_msg',
SendLike = 'send_like', SendLike : 'send_like',
SetGroupKick = 'set_group_kick', SetGroupKick : 'set_group_kick',
SetGroupBan = 'set_group_ban', SetGroupBan : 'set_group_ban',
// SetGroupAnoymousBan = 'set_group_anonymous_ban', // SetGroupAnoymousBan : 'set_group_anonymous_ban',
SetGroupWholeBan = 'set_group_whole_ban', SetGroupWholeBan : 'set_group_whole_ban',
SetGroupAdmin = 'set_group_admin', SetGroupAdmin : 'set_group_admin',
// SetGroupAnoymous = 'set_group_anonymous', // SetGroupAnoymous : 'set_group_anonymous',
SetGroupCard = 'set_group_card', SetGroupCard : 'set_group_card',
SetGroupName = 'set_group_name', SetGroupName : 'set_group_name',
SetGroupLeave = 'set_group_leave', SetGroupLeave : 'set_group_leave',
SetSpecialTittle = 'set_group_special_title', SetSpecialTittle : 'set_group_special_title',
SetFriendAddRequest = 'set_friend_add_request', SetFriendAddRequest : 'set_friend_add_request',
SetGroupAddRequest = 'set_group_add_request', SetGroupAddRequest : 'set_group_add_request',
GetLoginInfo = 'get_login_info', GetLoginInfo : 'get_login_info',
GoCQHTTP_GetStrangerInfo = 'get_stranger_info', GoCQHTTP_GetStrangerInfo : 'get_stranger_info',
GetFriendList = 'get_friend_list', GetFriendList : 'get_friend_list',
GetGroupInfo = 'get_group_info', GetGroupInfo : 'get_group_info',
GetGroupList = 'get_group_list', GetGroupList : 'get_group_list',
GetGroupMemberInfo = 'get_group_member_info', GetGroupMemberInfo : 'get_group_member_info',
GetGroupMemberList = 'get_group_member_list', GetGroupMemberList : 'get_group_member_list',
GetGroupHonorInfo = 'get_group_honor_info', GetGroupHonorInfo : 'get_group_honor_info',
GetCookies = 'get_cookies', GetCookies : 'get_cookies',
GetCSRF = 'get_csrf_token', GetCSRF : 'get_csrf_token',
GetCredentials = 'get_credentials', GetCredentials : 'get_credentials',
GetRecord = 'get_record', GetRecord : 'get_record',
GetImage = 'get_image', GetImage : 'get_image',
CanSendImage = 'can_send_image', CanSendImage : 'can_send_image',
CanSendRecord = 'can_send_record', CanSendRecord : 'can_send_record',
GetStatus = 'get_status', GetStatus : 'get_status',
GetVersionInfo = 'get_version_info', GetVersionInfo : 'get_version_info',
// Reboot = 'set_restart', // Reboot : 'set_restart',
// CleanCache = 'clean_cache', // CleanCache : 'clean_cache',
// go-cqhttp // go-cqhttp
SetQQProfile = 'set_qq_profile', SetQQProfile : 'set_qq_profile',
// QidianGetAccountInfo = 'qidian_get_account_info', // QidianGetAccountInfo : 'qidian_get_account_info',
GoCQHTTP_GetModelShow = '_get_model_show', GoCQHTTP_GetModelShow : '_get_model_show',
GoCQHTTP_SetModelShow = '_set_model_show', GoCQHTTP_SetModelShow : '_set_model_show',
GetOnlineClient = 'get_online_clients', GetOnlineClient : 'get_online_clients',
// GetUnidirectionalFriendList = 'get_unidirectional_friend_list', // GetUnidirectionalFriendList : 'get_unidirectional_friend_list',
GoCQHTTP_DeleteFriend = 'delete_friend', GoCQHTTP_DeleteFriend : 'delete_friend',
// DeleteUnidirectionalFriendList = 'delete_unidirectional_friend', // DeleteUnidirectionalFriendList : 'delete_unidirectional_friend',
GoCQHTTP_MarkMsgAsRead = 'mark_msg_as_read', GoCQHTTP_MarkMsgAsRead : 'mark_msg_as_read',
GoCQHTTP_SendGroupForwardMsg = 'send_group_forward_msg', GoCQHTTP_SendGroupForwardMsg : 'send_group_forward_msg',
GoCQHTTP_SendPrivateForwardMsg = 'send_private_forward_msg', GoCQHTTP_SendPrivateForwardMsg : 'send_private_forward_msg',
GoCQHTTP_GetGroupMsgHistory = 'get_group_msg_history', GoCQHTTP_GetGroupMsgHistory : 'get_group_msg_history',
OCRImage = 'ocr_image', OCRImage : 'ocr_image',
IOCRImage = '.ocr_image', IOCRImage : '.ocr_image',
GetGroupSystemMsg = 'get_group_system_msg', GetGroupSystemMsg : 'get_group_system_msg',
GoCQHTTP_GetEssenceMsg = 'get_essence_msg_list', GoCQHTTP_GetEssenceMsg : 'get_essence_msg_list',
GoCQHTTP_GetGroupAtAllRemain = 'get_group_at_all_remain', GoCQHTTP_GetGroupAtAllRemain : 'get_group_at_all_remain',
SetGroupPortrait = 'set_group_portrait', SetGroupPortrait : 'set_group_portrait',
SetEssenceMsg = 'set_essence_msg', SetEssenceMsg : 'set_essence_msg',
DelEssenceMsg = 'delete_essence_msg', DelEssenceMsg : 'delete_essence_msg',
GoCQHTTP_SendGroupNotice = '_send_group_notice', GoCQHTTP_SendGroupNotice : '_send_group_notice',
GoCQHTTP_GetGroupNotice = '_get_group_notice', GoCQHTTP_GetGroupNotice : '_get_group_notice',
GoCQHTTP_UploadGroupFile = 'upload_group_file', GoCQHTTP_UploadGroupFile : 'upload_group_file',
GOCQHTTP_DeleteGroupFile = 'delete_group_file', GOCQHTTP_DeleteGroupFile : 'delete_group_file',
GoCQHTTP_CreateGroupFileFolder = 'create_group_file_folder', GoCQHTTP_CreateGroupFileFolder : 'create_group_file_folder',
GoCQHTTP_DeleteGroupFileFolder = 'delete_group_folder', GoCQHTTP_DeleteGroupFileFolder : 'delete_group_folder',
GoCQHTTP_GetGroupFileSystemInfo = 'get_group_file_system_info', GoCQHTTP_GetGroupFileSystemInfo : 'get_group_file_system_info',
GoCQHTTP_GetGroupRootFiles = 'get_group_root_files', GoCQHTTP_GetGroupRootFiles : 'get_group_root_files',
GoCQHTTP_GetGroupFilesByFolder = 'get_group_files_by_folder', GoCQHTTP_GetGroupFilesByFolder : 'get_group_files_by_folder',
GOCQHTTP_GetGroupFileUrl = 'get_group_file_url', GOCQHTTP_GetGroupFileUrl : 'get_group_file_url',
GOCQHTTP_UploadPrivateFile = 'upload_private_file', GOCQHTTP_UploadPrivateFile : 'upload_private_file',
// GOCQHTTP_ReloadEventFilter = 'reload_event_filter', // GOCQHTTP_ReloadEventFilter : 'reload_event_filter',
GoCQHTTP_DownloadFile = 'download_file', GoCQHTTP_DownloadFile : 'download_file',
GoCQHTTP_CheckUrlSafely = 'check_url_safely', GoCQHTTP_CheckUrlSafely : 'check_url_safely',
GoCQHTTP_GetWordSlices = '.get_word_slices', GoCQHTTP_GetWordSlices : '.get_word_slices',
GoCQHTTP_HandleQuickAction = '.handle_quick_operation', GoCQHTTP_HandleQuickAction : '.handle_quick_operation',
// 以下为扩展napcat扩展 // 以下为扩展napcat扩展
Unknown = 'unknown', Unknown : 'unknown',
SharePeer = 'ArkSharePeer', SharePeer : 'ArkSharePeer',
ShareGroupEx = 'ArkShareGroup', ShareGroupEx : 'ArkShareGroup',
// RebootNormal = 'reboot_normal', //无快速登录重新启动 // RebootNormal : 'reboot_normal', //无快速登录重新启动
GetRobotUinRange = 'get_robot_uin_range', GetRobotUinRange : 'get_robot_uin_range',
SetOnlineStatus = 'set_online_status', SetOnlineStatus : 'set_online_status',
GetFriendsWithCategory = 'get_friends_with_category', GetFriendsWithCategory : 'get_friends_with_category',
SetQQAvatar = 'set_qq_avatar', SetQQAvatar : 'set_qq_avatar',
GetFile = 'get_file', GetFile : 'get_file',
ForwardFriendSingleMsg = 'forward_friend_single_msg', ForwardFriendSingleMsg : 'forward_friend_single_msg',
ForwardGroupSingleMsg = 'forward_group_single_msg', ForwardGroupSingleMsg : 'forward_group_single_msg',
TranslateEnWordToZn = 'translate_en2zh', TranslateEnWordToZn : 'translate_en2zh',
SetMsgEmojiLike = 'set_msg_emoji_like', SetMsgEmojiLike : 'set_msg_emoji_like',
GoCQHTTP_SendForwardMsg = 'send_forward_msg', GoCQHTTP_SendForwardMsg : 'send_forward_msg',
MarkPrivateMsgAsRead = 'mark_private_msg_as_read', MarkPrivateMsgAsRead : 'mark_private_msg_as_read',
MarkGroupMsgAsRead = 'mark_group_msg_as_read', MarkGroupMsgAsRead : 'mark_group_msg_as_read',
GetFriendMsgHistory = 'get_friend_msg_history', GetFriendMsgHistory : 'get_friend_msg_history',
CreateCollection = 'create_collection', CreateCollection : 'create_collection',
GetCollectionList = 'get_collection_list', GetCollectionList : 'get_collection_list',
SetLongNick = 'set_self_longnick', SetLongNick : 'set_self_longnick',
GetRecentContact = 'get_recent_contact', GetRecentContact : 'get_recent_contact',
_MarkAllMsgAsRead = '_mark_all_as_read', _MarkAllMsgAsRead : '_mark_all_as_read',
GetProfileLike = 'get_profile_like', GetProfileLike : 'get_profile_like',
FetchCustomFace = 'fetch_custom_face', FetchCustomFace : 'fetch_custom_face',
FetchEmojiLike = 'fetch_emoji_like', FetchEmojiLike : 'fetch_emoji_like',
SetInputStatus = 'set_input_status', SetInputStatus : 'set_input_status',
GetGroupInfoEx = 'get_group_info_ex', GetGroupInfoEx : 'get_group_info_ex',
GetGroupIgnoreAddRequest = 'get_group_ignore_add_request', GetGroupIgnoreAddRequest : 'get_group_ignore_add_request',
DelGroupNotice = '_del_group_notice', DelGroupNotice : '_del_group_notice',
FetchUserProfileLike = 'fetch_user_profile_like', FetchUserProfileLike : 'fetch_user_profile_like',
FriendPoke = 'friend_poke', FriendPoke : 'friend_poke',
GroupPoke = 'group_poke', GroupPoke : 'group_poke',
GetPacketStatus = 'nc_get_packet_status', GetPacketStatus : 'nc_get_packet_status',
GetUserStatus = 'nc_get_user_status', GetUserStatus : 'nc_get_user_status',
GetRkey = 'nc_get_rkey', GetRkey : 'nc_get_rkey',
GetGroupShutList = 'get_group_shut_list', GetGroupShutList : 'get_group_shut_list',
GetGuildList = 'get_guild_list', GetGuildList : 'get_guild_list',
GetGuildProfile = 'get_guild_service_profile', GetGuildProfile : 'get_guild_service_profile',
GetGroupIgnoredNotifies = 'get_group_ignored_notifies', GetGroupIgnoredNotifies : 'get_group_ignored_notifies',
SetGroupSign = "set_group_sign", SetGroupSign : "set_group_sign",
SendGroupSign = "send_group_sign", SendGroupSign : "send_group_sign",
GetMiniAppArk = "get_mini_app_ark", GetMiniAppArk : "get_mini_app_ark",
// UploadForwardMsg = "upload_forward_msg", // UploadForwardMsg : "upload_forward_msg",
GetAiRecord = "get_ai_record", GetAiRecord : "get_ai_record",
GetAiCharacters = "get_ai_characters", GetAiCharacters : "get_ai_characters",
SendGroupAiRecord = "send_group_ai_record", SendGroupAiRecord : "send_group_ai_record",
} } as const;

View File

@@ -1,10 +1,10 @@
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import CanSendRecord from './CanSendRecord'; import CanSendRecord, { CanSend } from './CanSendRecord';
interface ReturnType { interface ReturnType {
yes: boolean; yes: boolean;
} }
export default class CanSendImage extends CanSendRecord { export default class CanSendImage extends CanSend {
actionName = ActionName.CanSendImage; actionName = ActionName.CanSendImage;
} }

View File

@@ -5,12 +5,15 @@ interface ReturnType {
yes: boolean; yes: boolean;
} }
export default class CanSendRecord extends OneBotAction<any, ReturnType> { export class CanSend extends OneBotAction<any, ReturnType> {
actionName = ActionName.CanSendRecord;
async _handle(_payload: void): Promise<ReturnType> { async _handle(_payload: void): Promise<ReturnType> {
return { return {
yes: true, yes: true,
}; };
} }
} }
export default class CanSendRecord extends CanSend{
actionName = ActionName.CanSendRecord;
}

View File

@@ -1,19 +1,5 @@
import type { OneBotFriendApi } from '@/onebot/api/friend';
import type { OneBotUserApi } from '@/onebot/api/user';
import type { OneBotGroupApi } from '@/onebot/api/group';
import type { OneBotMsgApi } from '@/onebot/api/msg';
import type { OneBotQuickActionApi } from '@/onebot/api/quick-action';
export * from './friend'; export * from './friend';
export * from './group'; export * from './group';
export * from './user'; export * from './user';
export * from './msg'; export * from './msg';
export * from './quick-action'; export * from './quick-action';
export interface StableOneBotApiWrapper {
FriendApi: OneBotFriendApi;
UserApi: OneBotUserApi;
GroupApi: OneBotGroupApi;
MsgApi: OneBotMsgApi;
QuickActionApi: OneBotQuickActionApi,
}

View File

@@ -895,16 +895,16 @@ export class OneBotMsgApi {
const calculateTotalSize = async (elements: SendMessageElement[]): Promise<number> => { const calculateTotalSize = async (elements: SendMessageElement[]): Promise<number> => {
const sizePromises = elements.map(async element => { const sizePromises = elements.map(async element => {
switch (element.elementType) { switch (element.elementType) {
case ElementType.PTT: case ElementType.PTT:
return (await fsPromise.stat(element.pttElement.filePath)).size; return (await fsPromise.stat(element.pttElement.filePath)).size;
case ElementType.FILE: case ElementType.FILE:
return (await fsPromise.stat(element.fileElement.filePath)).size; return (await fsPromise.stat(element.fileElement.filePath)).size;
case ElementType.VIDEO: case ElementType.VIDEO:
return (await fsPromise.stat(element.videoElement.filePath)).size; return (await fsPromise.stat(element.videoElement.filePath)).size;
case ElementType.PIC: case ElementType.PIC:
return (await fsPromise.stat(element.picElement.sourcePath)).size; return (await fsPromise.stat(element.picElement.sourcePath)).size;
default: default:
return 0; return 0;
} }
}); });
const sizes = await Promise.all(sizePromises); const sizes = await Promise.all(sizePromises);
@@ -970,14 +970,14 @@ export class OneBotMsgApi {
} }
groupChangDecreseType2String(type: number): GroupDecreaseSubType { groupChangDecreseType2String(type: number): GroupDecreaseSubType {
switch (type) { switch (type) {
case 130: case 130:
return 'leave'; return 'leave';
case 131: case 131:
return 'kick'; return 'kick';
case 3: case 3:
return 'kick_me'; return 'kick_me';
default: default:
return 'kick'; return 'kick';
} }
} }

View File

@@ -18,8 +18,8 @@ import { ContextMode, createContext, normalize } from '@/onebot/action/msg/SendM
import { isNull } from '@/common/helper'; import { isNull } from '@/common/helper';
export class OneBotQuickActionApi { export class OneBotQuickActionApi {
private obContext: NapCatOneBot11Adapter; obContext: NapCatOneBot11Adapter;
private core: NapCatCore; core: NapCatCore;
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore) { constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
this.obContext = obContext; this.obContext = obContext;
this.core = core; this.core = core;
@@ -82,11 +82,20 @@ export class OneBotQuickActionApi {
this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles, false).then().catch(e => this.core.context.logger.logError(e)); this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles, false).then().catch(e => this.core.context.logger.logError(e));
} }
} }
async findNotify(flag: string) {
let notify = (await this.core.apis.GroupApi.getSingleScreenNotifies(false, 100)).find(e => e.seq == flag);
if (!notify) {
notify = (await this.core.apis.GroupApi.getSingleScreenNotifies(true, 100)).find(e => e.seq == flag);
}
return notify;
}
async handleGroupRequest(request: OB11GroupRequestEvent, quickAction: QuickActionGroupRequest) { async handleGroupRequest(request: OB11GroupRequestEvent, quickAction: QuickActionGroupRequest) {
if (!isNull(quickAction.approve)) { const noify = await this.findNotify(request.flag);
if (!isNull(quickAction.approve) && noify) {
this.core.apis.GroupApi.handleGroupRequest( this.core.apis.GroupApi.handleGroupRequest(
request.flag, noify,
quickAction.approve ? NTGroupRequestOperateTypes.KAGREE : NTGroupRequestOperateTypes.KREFUSE, quickAction.approve ? NTGroupRequestOperateTypes.KAGREE : NTGroupRequestOperateTypes.KREFUSE,
quickAction.reason, quickAction.reason,
).catch(e => this.core.context.logger.logError(e)); ).catch(e => this.core.context.logger.logError(e));
@@ -94,8 +103,9 @@ export class OneBotQuickActionApi {
} }
async handleFriendRequest(request: OB11FriendRequestEvent, quickAction: QuickActionFriendRequest) { async handleFriendRequest(request: OB11FriendRequestEvent, quickAction: QuickActionFriendRequest) {
if (!isNull(quickAction.approve)) { const notify = (await this.core.apis.FriendApi.getBuddyReq()).buddyReqs.find(e => e.reqTime == request.flag.toString());
this.core.apis.FriendApi.handleFriendRequest(request.flag, !!quickAction.approve).then().catch(e => this.core.context.logger.logError(e)); if (!isNull(quickAction.approve) && notify) {
this.core.apis.FriendApi.handleFriendRequest(notify, !!quickAction.approve).then().catch(e => this.core.context.logger.logError(e));
} }
} }
} }

View File

@@ -28,6 +28,7 @@ interface v1Config {
export interface AdapterConfigInner { export interface AdapterConfigInner {
name: string; name: string;
enable: boolean; enable: boolean;
} }
export type AdapterConfigWrap = AdapterConfigInner & Partial<NetworkConfigAdapter>; export type AdapterConfigWrap = AdapterConfigInner & Partial<NetworkConfigAdapter>;
@@ -37,6 +38,14 @@ export interface AdapterConfig extends AdapterConfigInner {
const createDefaultAdapterConfig = <T extends AdapterConfig>(config: T): T => config; const createDefaultAdapterConfig = <T extends AdapterConfig>(config: T): T => config;
export interface PluginConfig extends AdapterConfig {
name: string;
enable: boolean;
messagePostFormat: string;
reportSelfMessage: boolean;
debug: boolean;
}
export const httpServerDefaultConfigs = createDefaultAdapterConfig({ export const httpServerDefaultConfigs = createDefaultAdapterConfig({
name: 'http-server', name: 'http-server',
enable: false as boolean, enable: false as boolean,
@@ -127,7 +136,7 @@ export const mergeNetworkDefaultConfig = {
websocketClients: websocketClientDefaultConfigs, websocketClients: websocketClientDefaultConfigs,
} as const; } as const;
export type NetworkConfigAdapter = HttpServerConfig | HttpClientConfig | WebsocketServerConfig | WebsocketClientConfig; export type NetworkConfigAdapter = HttpServerConfig | HttpClientConfig | WebsocketServerConfig | WebsocketClientConfig | PluginConfig;
type NetworkConfigKeys = keyof typeof mergeNetworkDefaultConfig; type NetworkConfigKeys = keyof typeof mergeNetworkDefaultConfig;
export function mergeOneBotConfigs( export function mergeOneBotConfigs(
@@ -233,4 +242,4 @@ export function getConfigBoolKey(
} }
}); });
return result; return result;
} }

View File

@@ -16,7 +16,6 @@ import {
} from '@/core'; } from '@/core';
import { OB11ConfigLoader } from '@/onebot/config'; import { OB11ConfigLoader } from '@/onebot/config';
import { import {
IOB11NetworkAdapter,
OB11ActiveHttpAdapter, OB11ActiveHttpAdapter,
OB11ActiveWebSocketAdapter, OB11ActiveWebSocketAdapter,
OB11NetworkManager, OB11NetworkManager,
@@ -31,7 +30,6 @@ import {
OneBotMsgApi, OneBotMsgApi,
OneBotQuickActionApi, OneBotQuickActionApi,
OneBotUserApi, OneBotUserApi,
StableOneBotApiWrapper,
} from '@/onebot/api'; } from '@/onebot/api';
import { ActionMap, createActionMap } from '@/onebot/action'; import { ActionMap, createActionMap } from '@/onebot/action';
import { WebUiDataRuntime } from '@/webui/src/helper/Data'; import { WebUiDataRuntime } from '@/webui/src/helper/Data';
@@ -45,8 +43,16 @@ import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecal
import { LRUCache } from '@/common/lru-cache'; import { LRUCache } from '@/common/lru-cache';
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener'; import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
import { BotOfflineEvent } from './event/notice/BotOfflineEvent'; import { BotOfflineEvent } from './event/notice/BotOfflineEvent';
import { AdapterConfigWrap, mergeOneBotConfigs, migrateOneBotConfigsV1, NetworkConfigAdapter, OneBotConfig } from './config/config'; import {
AdapterConfigWrap,
mergeOneBotConfigs,
migrateOneBotConfigsV1,
NetworkConfigAdapter,
OneBotConfig,
} from './config/config';
import { OB11Message } from './types'; import { OB11Message } from './types';
import { OB11PluginAdapter } from './network/plugin';
import { IOB11NetworkAdapter } from "@/onebot/network/adapter";
//OneBot实现类 //OneBot实现类
export class NapCatOneBot11Adapter { export class NapCatOneBot11Adapter {
@@ -54,7 +60,7 @@ export class NapCatOneBot11Adapter {
readonly context: InstanceContext; readonly context: InstanceContext;
configLoader: OB11ConfigLoader; configLoader: OB11ConfigLoader;
public readonly apis: StableOneBotApiWrapper; public readonly apis;
networkManager: OB11NetworkManager; networkManager: OB11NetworkManager;
actions: ActionMap; actions: ActionMap;
private readonly bootTime = Date.now() / 1000; private readonly bootTime = Date.now() / 1000;
@@ -71,7 +77,7 @@ export class NapCatOneBot11Adapter {
UserApi: new OneBotUserApi(this, core), UserApi: new OneBotUserApi(this, core),
FriendApi: new OneBotFriendApi(this, core), FriendApi: new OneBotFriendApi(this, core),
MsgApi: new OneBotMsgApi(this, core), MsgApi: new OneBotMsgApi(this, core),
QuickActionApi: new OneBotQuickActionApi(this, core), QuickActionApi: new OneBotQuickActionApi(this, core)
} as const; } as const;
this.actions = createActionMap(this, core); this.actions = createActionMap(this, core);
this.networkManager = new OB11NetworkManager(); this.networkManager = new OB11NetworkManager();
@@ -106,11 +112,16 @@ export class NapCatOneBot11Adapter {
const serviceInfo = await this.creatOneBotLog(ob11Config); const serviceInfo = await this.creatOneBotLog(ob11Config);
this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`); this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`);
// //创建NetWork服务 //创建NetWork服务
// 注册Plugin 如果需要基于NapCat进行快速开发
// this.networkManager.registerAdapter(
// new OB11PluginAdapter('myPlugin', this.core, this,this.actions)
// );
for (const key of ob11Config.network.httpServers) { for (const key of ob11Config.network.httpServers) {
if (key.enable) { if (key.enable) {
this.networkManager.registerAdapter( this.networkManager.registerAdapter(
new OB11PassiveHttpAdapter(key.name, key, this.core, this.actions) new OB11PassiveHttpAdapter(key.name, key, this.core, this, this.actions)
); );
} }
} }
@@ -128,6 +139,7 @@ export class NapCatOneBot11Adapter {
key.name, key.name,
key, key,
this.core, this.core,
this,
this.actions this.actions
) )
); );
@@ -140,6 +152,7 @@ export class NapCatOneBot11Adapter {
key.name, key.name,
key, key,
this.core, this.core,
this,
this.actions this.actions
) )
); );
@@ -151,9 +164,9 @@ export class NapCatOneBot11Adapter {
this.initBuddyListener(); this.initBuddyListener();
this.initGroupListener(); this.initGroupListener();
await WebUiDataRuntime.setQQLoginUin(selfInfo.uin.toString()); WebUiDataRuntime.setQQLoginInfo(selfInfo);
await WebUiDataRuntime.setQQLoginStatus(true); WebUiDataRuntime.setQQLoginStatus(true);
await WebUiDataRuntime.setOnOB11ConfigChanged(async (newConfig) => { WebUiDataRuntime.setOnOB11ConfigChanged(async (newConfig) => {
const prev = this.configLoader.configData; const prev = this.configLoader.configData;
this.configLoader.save(newConfig); this.configLoader.save(newConfig);
this.context.logger.log(`OneBot11 配置更改:${JSON.stringify(prev)} -> ${JSON.stringify(newConfig)}`); this.context.logger.log(`OneBot11 配置更改:${JSON.stringify(prev)} -> ${JSON.stringify(newConfig)}`);
@@ -186,10 +199,12 @@ export class NapCatOneBot11Adapter {
await this.handleConfigChange(prev.network.websocketClients, now.network.websocketClients, OB11ActiveWebSocketAdapter); await this.handleConfigChange(prev.network.websocketClients, now.network.websocketClients, OB11ActiveWebSocketAdapter);
} }
private async handleConfigChange( private async handleConfigChange<CT extends NetworkConfigAdapter>(
prevConfig: NetworkConfigAdapter[], prevConfig: NetworkConfigAdapter[],
nowConfig: NetworkConfigAdapter[], nowConfig: NetworkConfigAdapter[],
adapterClass: new (...args: any[]) => IOB11NetworkAdapter adapterClass: new (
...args: ConstructorParameters<typeof IOB11NetworkAdapter<CT>>
) => IOB11NetworkAdapter<CT>
): Promise<void> { ): Promise<void> {
// 比较旧的在新的找不到的回收 // 比较旧的在新的找不到的回收
for (const adapterConfig of prevConfig) { for (const adapterConfig of prevConfig) {
@@ -201,7 +216,7 @@ export class NapCatOneBot11Adapter {
} }
} }
} }
// 通知新配置重载 删除关闭的 加入新开的 // 通知新配置重载 删除关闭的 加入新开的
for (const adapterConfig of nowConfig) { for (const adapterConfig of nowConfig) {
const existingAdapter = this.networkManager.findSomeAdapter(adapterConfig.name); const existingAdapter = this.networkManager.findSomeAdapter(adapterConfig.name);
if (existingAdapter) { if (existingAdapter) {
@@ -210,7 +225,7 @@ export class NapCatOneBot11Adapter {
await this.networkManager.closeSomeAdaterWhenOpen([existingAdapter]); await this.networkManager.closeSomeAdaterWhenOpen([existingAdapter]);
} }
} else if (adapterConfig.enable) { } else if (adapterConfig.enable) {
const newAdapter = new adapterClass(adapterConfig.name, adapterConfig, this.core, this.actions); const newAdapter = new adapterClass(adapterConfig.name, adapterConfig as CT, this.core, this, this.actions);
await this.networkManager.registerAdapterAndOpen(newAdapter); await this.networkManager.registerAdapterAndOpen(newAdapter);
} }
} }
@@ -443,7 +458,7 @@ export class NapCatOneBot11Adapter {
} }
private async emitMsg(message: RawMessage) { private async emitMsg(message: RawMessage) {
const network = Object.values(this.configLoader.configData.network).flat() as Array<AdapterConfigWrap>; const network = await this.networkManager.getAllConfig();
this.context.logger.logDebug('收到新消息 RawMessage', message); this.context.logger.logDebug('收到新消息 RawMessage', message);
await Promise.allSettled([ await Promise.allSettled([
this.handleMsg(message, network), this.handleMsg(message, network),
@@ -483,7 +498,7 @@ export class NapCatOneBot11Adapter {
ob11Msg.stringMsg.target_id = parseInt(message.peerUin); ob11Msg.stringMsg.target_id = parseInt(message.peerUin);
ob11Msg.arrayMsg.target_id = parseInt(message.peerUin); ob11Msg.arrayMsg.target_id = parseInt(message.peerUin);
} }
if (e.messagePostFormat == 'string') { if ('messagePostFormat' in e && e.messagePostFormat == 'string') {
msgMap.set(e.name, structuredClone(ob11Msg.stringMsg)); msgMap.set(e.name, structuredClone(ob11Msg.stringMsg));
} else { } else {
msgMap.set(e.name, structuredClone(ob11Msg.arrayMsg)); msgMap.set(e.name, structuredClone(ob11Msg.arrayMsg));

View File

@@ -1,62 +1,42 @@
import { IOB11NetworkAdapter, OB11EmitEventContent, OB11NetworkReloadType } from '@/onebot/network/index'; import { OB11EmitEventContent, OB11NetworkReloadType } from '@/onebot/network/index';
import { createHmac } from 'crypto'; import { createHmac } from 'crypto';
import { LogWrapper } from '@/common/log';
import { QuickAction, QuickActionEvent } from '@/onebot/types'; import { QuickAction, QuickActionEvent } from '@/onebot/types';
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '..'; import { NapCatOneBot11Adapter } from '..';
import { RequestUtil } from '@/common/request'; import { RequestUtil } from '@/common/request';
import { HttpClientConfig } from '@/onebot/config/config'; import { HttpClientConfig } from '@/onebot/config/config';
import { ActionMap } from '@/onebot/action'; import { ActionMap } from '@/onebot/action';
import { IOB11NetworkAdapter } from "@/onebot/network/adapter";
export class OB11ActiveHttpAdapter implements IOB11NetworkAdapter { export class OB11ActiveHttpAdapter extends IOB11NetworkAdapter<HttpClientConfig> {
logger: LogWrapper;
isEnable: boolean = false;
public config: HttpClientConfig;
constructor( constructor(
public name: string, name: string, config: HttpClientConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap
config: HttpClientConfig,
public core: NapCatCore,
public obContext: NapCatOneBot11Adapter,
public actions: ActionMap,
) { ) {
this.logger = core.context.logger; super(name, config, core, obContext, actions);
this.config = structuredClone(config);
} }
onEvent<T extends OB11EmitEventContent>(event: T) { onEvent<T extends OB11EmitEventContent>(event: T) {
if (!this.isEnable) { this.emitEventAsync(event).catch(e => this.logger.logError('[OneBot] [Http Client] 新消息事件HTTP上报返回快速操作失败', e));
return; }
}
async emitEventAsync<T extends OB11EmitEventContent>(event: T) {
if (!this.isEnable) return;
const headers: Record<string, string> = { const headers: Record<string, string> = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'x-self-id': this.core.selfInfo.uin, 'x-self-id': this.core.selfInfo.uin,
}; };
const msgStr = JSON.stringify(event); const msgStr = JSON.stringify(event);
if (this.config.token && this.config.token.length > 0) { if (this.config.token) {
const hmac = createHmac('sha1', this.config.token); const hmac = createHmac('sha1', this.config.token);
hmac.update(msgStr); hmac.update(msgStr);
const sig = hmac.digest('hex'); headers['x-signature'] = 'sha1=' + hmac.digest('hex');
headers['x-signature'] = 'sha1=' + sig;
} }
RequestUtil.HttpGetText(this.config.url, 'POST', msgStr, headers).then(async (res) => {
let resJson: QuickAction; const data = await RequestUtil.HttpGetText(this.config.url, 'POST', msgStr, headers);
try { const resJson: QuickAction = data ? JSON.parse(data) : {};
resJson = JSON.parse(res);
//logDebug('新消息事件HTTP上报返回快速操作: ', JSON.stringify(resJson)); await this.obContext.apis.QuickActionApi.handleQuickOperation(event as QuickActionEvent, resJson);
} catch (e) {
this.logger.logDebug('[OneBot] [Http Client] 新消息事件HTTP上报没有返回快速操作不需要处理');
return;
}
try {
this.obContext.apis.QuickActionApi
.handleQuickOperation(event as QuickActionEvent, resJson)
.catch(e => this.logger.logError(e));
} catch (e: any) {
this.logger.logError('[OneBot] [Http Client] 新消息事件HTTP上报返回快速操作失败', e);
}
}).catch((e) => {
this.logger.logError('[OneBot] [Http Client] 新消息事件HTTP上报失败', e);
});
} }
open() { open() {
@@ -66,20 +46,24 @@ export class OB11ActiveHttpAdapter implements IOB11NetworkAdapter {
close() { close() {
this.isEnable = false; this.isEnable = false;
} }
async reload(newconfig: HttpClientConfig) {
async reload(newConfig: HttpClientConfig) {
const wasEnabled = this.isEnable; const wasEnabled = this.isEnable;
const oldUrl = this.config.url; const oldUrl = this.config.url;
this.config = newconfig; this.config = newConfig;
if (newconfig.enable && !wasEnabled) {
if (newConfig.enable && !wasEnabled) {
this.open(); this.open();
return OB11NetworkReloadType.NetWorkOpen; return OB11NetworkReloadType.NetWorkOpen;
} else if (!newconfig.enable && wasEnabled) { } else if (!newConfig.enable && wasEnabled) {
this.close(); this.close();
return OB11NetworkReloadType.NetWorkClose; return OB11NetworkReloadType.NetWorkClose;
} }
if (oldUrl !== newconfig.url) {
if (oldUrl !== newConfig.url) {
return OB11NetworkReloadType.NetWorkReload; return OB11NetworkReloadType.NetWorkReload;
} }
return OB11NetworkReloadType.Normal; return OB11NetworkReloadType.Normal;
} }
} }

View File

@@ -1,29 +1,21 @@
import { IOB11NetworkAdapter, OB11EmitEventContent, OB11NetworkReloadType } from '@/onebot/network/index'; import { OB11EmitEventContent, OB11NetworkReloadType } from '@/onebot/network/index';
import { WebSocket } from 'ws'; import { WebSocket } from 'ws';
import { OB11HeartbeatEvent } from '@/onebot/event/meta/OB11HeartbeatEvent'; import { OB11HeartbeatEvent } from '@/onebot/event/meta/OB11HeartbeatEvent';
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { OB11Response } from '@/onebot/action/OneBotAction'; import { OB11Response } from '@/onebot/action/OneBotAction';
import { LogWrapper } from '@/common/log';
import { ActionMap } from '@/onebot/action'; import { ActionMap } from '@/onebot/action';
import { LifeCycleSubType, OB11LifeCycleEvent } from '@/onebot/event/meta/OB11LifeCycleEvent'; import { LifeCycleSubType, OB11LifeCycleEvent } from '@/onebot/event/meta/OB11LifeCycleEvent';
import { WebsocketClientConfig } from '@/onebot/config/config'; import { WebsocketClientConfig } from '@/onebot/config/config';
import { NapCatOneBot11Adapter } from "@/onebot";
import { IOB11NetworkAdapter } from "@/onebot/network/adapter";
export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter { export class OB11ActiveWebSocketAdapter extends IOB11NetworkAdapter<WebsocketClientConfig> {
isEnable: boolean = false;
logger: LogWrapper;
private connection: WebSocket | null = null; private connection: WebSocket | null = null;
private heartbeatRef: NodeJS.Timeout | null = null; private heartbeatRef: NodeJS.Timeout | null = null;
public config: WebsocketClientConfig;
constructor( constructor(name: string, config: WebsocketClientConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap) {
public name: string, super(name, config, core, obContext, actions);
confg: WebsocketClientConfig,
public core: NapCatCore,
public actions: ActionMap,
) {
this.logger = core.context.logger;
this.config = structuredClone(confg);
} }
onEvent<T extends OB11EmitEventContent>(event: T) { onEvent<T extends OB11EmitEventContent>(event: T) {
@@ -133,7 +125,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
} }
private async handleMessage(message: any) { private async handleMessage(message: any) {
let receiveData: { action: ActionName, params?: any, echo?: any } = { action: ActionName.Unknown, params: {} }; let receiveData: { action: typeof ActionName[keyof typeof ActionName], params?: any, echo?: any } = { action: ActionName.Unknown, params: {} };
let echo = undefined; let echo = undefined;
try { try {
@@ -145,7 +137,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
return; return;
} }
receiveData.params = (receiveData?.params) ? receiveData.params : {};// 兼容类型验证 receiveData.params = (receiveData?.params) ? receiveData.params : {};// 兼容类型验证
const action = this.actions.get(receiveData.action); const action = this.actions.get(receiveData.action as any);
if (!action) { if (!action) {
this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的Api ' + receiveData.action); this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的Api ' + receiveData.action);
this.checkStateAndReply<any>(OB11Response.error('不支持的Api ' + receiveData.action, 1404, echo)); this.checkStateAndReply<any>(OB11Response.error('不支持的Api ' + receiveData.action, 1404, echo));

View File

@@ -0,0 +1,33 @@
import { NetworkConfigAdapter } from "@/onebot/config/config";
import { LogWrapper } from "@/common/log";
import { NapCatCore } from "@/core";
import { NapCatOneBot11Adapter } from "@/onebot";
import { ActionMap } from "@/onebot/action";
import { OB11EmitEventContent, OB11NetworkReloadType } from "@/onebot/network/index";
export abstract class IOB11NetworkAdapter<CT extends NetworkConfigAdapter> {
name: string;
isEnable: boolean = false;
config: CT;
readonly logger: LogWrapper;
readonly core: NapCatCore;
readonly obContext: NapCatOneBot11Adapter;
readonly actions: ActionMap;
constructor(name: string, config: CT, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap) {
this.name = name;
this.config = structuredClone(config);
this.core = core;
this.obContext = obContext;
this.actions = actions;
this.logger = core.context.logger;
}
abstract onEvent<T extends OB11EmitEventContent>(event: T): void;
abstract open(): void | Promise<void>;
abstract close(): void | Promise<void>;
abstract reload(config: any): OB11NetworkReloadType | Promise<OB11NetworkReloadType>;
}

View File

@@ -1,7 +1,7 @@
import { OneBotEvent } from '@/onebot/event/OneBotEvent'; import { OneBotEvent } from '@/onebot/event/OneBotEvent';
import { OB11Message } from '@/onebot'; import { OB11Message } from '@/onebot';
import { ActionMap } from '@/onebot/action';
import { NetworkConfigAdapter } from '@/onebot/config/config'; import { NetworkConfigAdapter } from '@/onebot/config/config';
import { IOB11NetworkAdapter } from "@/onebot/network/adapter";
export type OB11EmitEventContent = OneBotEvent | OB11Message; export type OB11EmitEventContent = OneBotEvent | OB11Message;
export enum OB11NetworkReloadType { export enum OB11NetworkReloadType {
@@ -11,30 +11,20 @@ export enum OB11NetworkReloadType {
NetWorkClose = 3, NetWorkClose = 3,
NetWorkOpen = 4 NetWorkOpen = 4
} }
export interface IOB11NetworkAdapter {
actions: ActionMap;
name: string;
isEnable: boolean;
config: NetworkConfigAdapter;
onEvent<T extends OB11EmitEventContent>(event: T): void;
open(): void | Promise<void>;
close(): void | Promise<void>;
reload(config: any): OB11NetworkReloadType | Promise<OB11NetworkReloadType>;
}
export class OB11NetworkManager { export class OB11NetworkManager {
adapters: Map<string, IOB11NetworkAdapter> = new Map(); adapters: Map<string, IOB11NetworkAdapter<NetworkConfigAdapter>> = new Map();
async openAllAdapters() { async openAllAdapters() {
return Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.open())); return Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.open()));
} }
async emitEvent(event: OB11EmitEventContent) { async emitEvent(event: OB11EmitEventContent) {
return Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.onEvent(event))); return Promise.all(Array.from(this.adapters.values()).map(adapter => {
if (adapter.isEnable) {
return adapter.onEvent(event);
}
}));
} }
async emitEvents(events: OB11EmitEventContent[]) { async emitEvents(events: OB11EmitEventContent[]) {
@@ -44,35 +34,37 @@ export class OB11NetworkManager {
async emitEventByName(names: string[], event: OB11EmitEventContent) { async emitEventByName(names: string[], event: OB11EmitEventContent) {
return Promise.all(names.map(name => { return Promise.all(names.map(name => {
const adapter = this.adapters.get(name); const adapter = this.adapters.get(name);
if (adapter) { if (adapter && adapter.isEnable) {
return adapter.onEvent(event); return adapter.onEvent(event);
} }
})); }));
} }
async emitEventByNames(map: Map<string, OB11EmitEventContent>) { async emitEventByNames(map: Map<string, OB11EmitEventContent>) {
return Promise.all(Array.from(map.entries()).map(([name, event]) => { return Promise.all(Array.from(map.entries()).map(([name, event]) => {
const adapter = this.adapters.get(name); const adapter = this.adapters.get(name);
if (adapter) { if (adapter && adapter.isEnable) {
return adapter.onEvent(event); return adapter.onEvent(event);
} }
})); }));
} }
registerAdapter(adapter: IOB11NetworkAdapter) {
registerAdapter<CT extends NetworkConfigAdapter>(adapter: IOB11NetworkAdapter<CT>) {
this.adapters.set(adapter.name, adapter); this.adapters.set(adapter.name, adapter);
} }
async registerAdapterAndOpen(adapter: IOB11NetworkAdapter) { async registerAdapterAndOpen<CT extends NetworkConfigAdapter>(adapter: IOB11NetworkAdapter<CT>) {
this.registerAdapter(adapter); this.registerAdapter(adapter);
await adapter.open(); await adapter.open();
} }
async closeSomeAdapters(adaptersToClose: IOB11NetworkAdapter[]) { async closeSomeAdapters<CT extends NetworkConfigAdapter>(adaptersToClose: IOB11NetworkAdapter<CT>[]) {
for (const adapter of adaptersToClose) { for (const adapter of adaptersToClose) {
this.adapters.delete(adapter.name); this.adapters.delete(adapter.name);
await adapter.close(); await adapter.close();
} }
} }
async closeSomeAdaterWhenOpen(adaptersToClose: IOB11NetworkAdapter[]) { async closeSomeAdaterWhenOpen<CT extends NetworkConfigAdapter>(adaptersToClose: IOB11NetworkAdapter<CT>[]) {
for (const adapter of adaptersToClose) { for (const adapter of adaptersToClose) {
this.adapters.delete(adapter.name); this.adapters.delete(adapter.name);
if (adapter.isEnable) { if (adapter.isEnable) {
@@ -85,7 +77,7 @@ export class OB11NetworkManager {
return this.adapters.get(name); return this.adapters.get(name);
} }
async closeAdapterByPredicate(closeFilter: (adapter: IOB11NetworkAdapter) => boolean) { async closeAdapterByPredicate(closeFilter: (adapter: IOB11NetworkAdapter<NetworkConfigAdapter>) => boolean) {
const adaptersToClose = Array.from(this.adapters.values()).filter(closeFilter); const adaptersToClose = Array.from(this.adapters.values()).filter(closeFilter);
await this.closeSomeAdapters(adaptersToClose); await this.closeSomeAdapters(adaptersToClose);
} }
@@ -104,9 +96,12 @@ export class OB11NetworkManager {
async readloadSomeAdapters<T>(configMap: Map<string, T>) { async readloadSomeAdapters<T>(configMap: Map<string, T>) {
await Promise.all(Array.from(configMap.entries()).map(([name, config]) => this.readloadAdapter(name, config))); await Promise.all(Array.from(configMap.entries()).map(([name, config]) => this.readloadAdapter(name, config)));
} }
async getAllConfig() {
return Array.from(this.adapters.values()).map(adapter => adapter.config);
}
} }
export * from './active-http'; export * from './active-http';
export * from './active-websocket'; export * from './active-websocket';
export * from './passive-http'; export * from './passive-http';
export * from './passive-websocket'; export * from './passive-websocket';

View File

@@ -1,4 +1,4 @@
import { IOB11NetworkAdapter, OB11NetworkReloadType } from './index'; import { OB11NetworkReloadType } from './index';
import express, { Express, Request, Response } from 'express'; import express, { Express, Request, Response } from 'express';
import http from 'http'; import http from 'http';
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
@@ -6,20 +6,15 @@ import { OB11Response } from '@/onebot/action/OneBotAction';
import { ActionMap } from '@/onebot/action'; import { ActionMap } from '@/onebot/action';
import cors from 'cors'; import cors from 'cors';
import { HttpServerConfig } from '@/onebot/config/config'; import { HttpServerConfig } from '@/onebot/config/config';
import { NapCatOneBot11Adapter } from "@/onebot";
import { IOB11NetworkAdapter } from "@/onebot/network/adapter";
export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter { export class OB11PassiveHttpAdapter extends IOB11NetworkAdapter<HttpServerConfig> {
private app: Express | undefined; private app: Express | undefined;
private server: http.Server | undefined; private server: http.Server | undefined;
isEnable: boolean = false;
public config: HttpServerConfig;
constructor( constructor(name: string, config: HttpServerConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap) {
public name: string, super(name, config, core, obContext, actions);
config: HttpServerConfig,
public core: NapCatCore,
public actions: ActionMap,
) {
this.config = structuredClone(config);
} }
onEvent() { onEvent() {
@@ -105,7 +100,7 @@ export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter {
return res.json(hello); return res.json(hello);
} }
const actionName = req.path.split('/')[1]; const actionName = req.path.split('/')[1];
const action = this.actions.get(actionName); const action = this.actions.get(actionName as any);
if (action) { if (action) {
try { try {
const result = await action.handle(payload, this.name); const result = await action.handle(payload, this.name);

View File

@@ -1,36 +1,30 @@
import { IOB11NetworkAdapter, OB11EmitEventContent, OB11NetworkReloadType } from './index'; import { OB11EmitEventContent, OB11NetworkReloadType } from './index';
import urlParse from 'url'; import urlParse from 'url';
import { WebSocket, WebSocketServer } from 'ws'; import { WebSocket, WebSocketServer } from 'ws';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
import { OB11Response } from '@/onebot/action/OneBotAction'; import { OB11Response } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
import { LogWrapper } from '@/common/log';
import { OB11HeartbeatEvent } from '@/onebot/event/meta/OB11HeartbeatEvent'; import { OB11HeartbeatEvent } from '@/onebot/event/meta/OB11HeartbeatEvent';
import { IncomingMessage } from 'http'; import { IncomingMessage } from 'http';
import { ActionMap } from '@/onebot/action'; import { ActionMap } from '@/onebot/action';
import { LifeCycleSubType, OB11LifeCycleEvent } from '@/onebot/event/meta/OB11LifeCycleEvent'; import { LifeCycleSubType, OB11LifeCycleEvent } from '@/onebot/event/meta/OB11LifeCycleEvent';
import { WebsocketServerConfig } from '@/onebot/config/config'; import { WebsocketServerConfig } from '@/onebot/config/config';
import { NapCatOneBot11Adapter } from "@/onebot";
import { IOB11NetworkAdapter } from "@/onebot/network/adapter";
export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter { export class OB11PassiveWebSocketAdapter extends IOB11NetworkAdapter<WebsocketServerConfig> {
wsServer: WebSocketServer; wsServer: WebSocketServer;
wsClients: WebSocket[] = []; wsClients: WebSocket[] = [];
wsClientsMutex = new Mutex(); wsClientsMutex = new Mutex();
isEnable: boolean = false;
heartbeatInterval: number = 0; heartbeatInterval: number = 0;
logger: LogWrapper;
public config: WebsocketServerConfig;
private heartbeatIntervalId: NodeJS.Timeout | null = null; private heartbeatIntervalId: NodeJS.Timeout | null = null;
wsClientWithEvent: WebSocket[] = []; wsClientWithEvent: WebSocket[] = [];
constructor( constructor(
public name: string, name: string, config: WebsocketServerConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap
config: WebsocketServerConfig,
public core: NapCatCore,
public actions: ActionMap,
) { ) {
this.config = structuredClone(config); super(name, config, core, obContext, actions);
this.logger = core.context.logger;
this.wsServer = new WebSocketServer({ this.wsServer = new WebSocketServer({
port: this.config.port, port: this.config.port,
host: this.config.host === '0.0.0.0' ? '' : this.config.host, host: this.config.host === '0.0.0.0' ? '' : this.config.host,
@@ -166,7 +160,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
} }
private async handleMessage(wsClient: WebSocket, message: any) { private async handleMessage(wsClient: WebSocket, message: any) {
let receiveData: { action: ActionName, params?: any, echo?: any } = { action: ActionName.Unknown, params: {} }; let receiveData: { action: typeof ActionName[keyof typeof ActionName], params?: any, echo?: any } = { action: ActionName.Unknown, params: {} };
let echo = undefined; let echo = undefined;
try { try {
receiveData = JSON.parse(message.toString()); receiveData = JSON.parse(message.toString());
@@ -177,7 +171,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
return; return;
} }
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 不然类型校验爆炸 receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 不然类型校验爆炸
const action = this.actions.get(receiveData.action); const action = this.actions.get(receiveData.action as any);
if (!action) { if (!action) {
this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的API ' + receiveData.action); this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的API ' + receiveData.action);
this.checkStateAndReply<any>(OB11Response.error('不支持的API ' + receiveData.action, 1404, echo), wsClient); this.checkStateAndReply<any>(OB11Response.error('不支持的API ' + receiveData.action, 1404, echo), wsClient);

View File

@@ -0,0 +1,40 @@
import { OB11EmitEventContent, OB11NetworkReloadType } from './index';
import { NapCatOneBot11Adapter, OB11Message } from '@/onebot';
import { NapCatCore } from '@/core';
import { PluginConfig } from '../config/config';
import { plugin_onmessage } from '@/plugin';
import { ActionMap } from '../action';
import { IOB11NetworkAdapter } from "@/onebot/network/adapter";
export class OB11PluginAdapter extends IOB11NetworkAdapter<PluginConfig> {
constructor(
name: string, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap
) {
const config = {
name: name,
messagePostFormat: 'array',
reportSelfMessage: false,
enable: true,
debug: false,
};
super(name, config, core, obContext, actions);
}
onEvent<T extends OB11EmitEventContent>(event: T) {
if (event.post_type === 'message') {
plugin_onmessage(this.config.name, this.core, this.obContext, event as OB11Message,this.actions).then().catch();
}
}
open() {
this.isEnable = true;
}
async close() {
this.isEnable = false;
}
async reload() {
return OB11NetworkReloadType.Normal;
}
}

10
src/plugin/index.ts Normal file
View File

@@ -0,0 +1,10 @@
import { NapCatOneBot11Adapter, OB11Message } from "@/onebot";
import { NapCatCore } from "@/core";
import { ActionMap } from "@/onebot/action";
export const plugin_onmessage = async (adapter: string, core: NapCatCore, obCtx: NapCatOneBot11Adapter, message: OB11Message, action: ActionMap) => {
if (message.raw_message === 'ping') {
const ret = await action.get('send_group_msg')?.handle({ group_id: String(message.group_id), message: 'pong' }, adapter);
console.log(ret);
}
};

View File

@@ -175,7 +175,9 @@ async function handleLogin(
loginService.getLoginList().then((res) => { loginService.getLoginList().then((res) => {
// 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList // 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList
WebUiDataRuntime.setQQQuickLoginList(res.LocalLoginInfoList.filter((item) => item.isQuickLogin).map((item) => item.uin.toString())); const list = res.LocalLoginInfoList.filter((item) => item.isQuickLogin);
WebUiDataRuntime.setQQQuickLoginList(list.map((item) => item.uin.toString()));
WebUiDataRuntime.setQQNewLoginList(list);
}); });
WebUiDataRuntime.setQuickLoginCall(async (uin: string) => { WebUiDataRuntime.setQuickLoginCall(async (uin: string) => {
@@ -285,7 +287,7 @@ export async function NCoreInitShell() {
await initializeEngine(engine, basicInfoWrapper, dataPathGlobal, systemPlatform, systemVersion); await initializeEngine(engine, basicInfoWrapper, dataPathGlobal, systemPlatform, systemVersion);
await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname); await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname);
program.option('-q, --qq [number]', 'QQ号').parse(process.argv); program.option('-q, --qq [number]', 'QQ号').parse(process.argv);
const cmdOptions = program.opts(); const cmdOptions = program.opts();
const quickLoginUin = cmdOptions.qq; const quickLoginUin = cmdOptions.qq;
@@ -354,8 +356,6 @@ export class NapCatShell {
}; };
this.core = new NapCatCore(this.context, selfInfo); this.core = new NapCatCore(this.context, selfInfo);
} }
async InitNapCat() { async InitNapCat() {
await this.core.initCore(); await this.core.initCore();

View File

@@ -18,7 +18,7 @@ export const LoginHandler: RequestHandler = async (req, res) => {
return sendError(res, 'token is empty'); return sendError(res, 'token is empty');
} }
// 检查登录频率 // 检查登录频率
if (!(await WebUiDataRuntime.checkLoginRate(WebUiConfigData.loginRate))) { if (!WebUiDataRuntime.checkLoginRate(WebUiConfigData.loginRate)) {
return sendError(res, 'login rate limit'); return sendError(res, 'login rate limit');
} }
//验证config.token是否等于token //验证config.token是否等于token

View File

@@ -1,15 +1,9 @@
import { RequestHandler } from 'express'; import { RequestHandler } from 'express';
import { WebUiDataRuntime } from '@webapi/helper/Data';
import { sendSuccess } from '@webapi/utils/response'; import { sendSuccess } from '@webapi/utils/response';
// TODO: Implement LogFileListHandler export const PackageInfoHandler: RequestHandler = (_, res) => {
export const LogFileListHandler: RequestHandler = async (_, res) => { const data = WebUiDataRuntime.getPackageJson();
const fakeData = { sendSuccess(res, data);
uin: 0,
nick: 'NapCat',
avatar: 'https://q1.qlogo.cn/g?b=qq&nk=0&s=640',
status: 'online',
boottime: Date.now(),
};
sendSuccess(res, fakeData);
}; };

View File

@@ -10,15 +10,15 @@ import { sendError, sendSuccess } from '@webapi/utils/response';
import { isEmpty } from '@webapi/utils/check'; import { isEmpty } from '@webapi/utils/check';
// 获取OneBot11配置 // 获取OneBot11配置
export const OB11GetConfigHandler: RequestHandler = async (_, res) => { export const OB11GetConfigHandler: RequestHandler = (_, res) => {
// 获取QQ登录状态 // 获取QQ登录状态
const isLogin = await WebUiDataRuntime.getQQLoginStatus(); const isLogin = WebUiDataRuntime.getQQLoginStatus();
// 如果未登录,返回错误 // 如果未登录,返回错误
if (!isLogin) { if (!isLogin) {
return sendError(res, 'Not Login'); return sendError(res, 'Not Login');
} }
// 获取登录的QQ号 // 获取登录的QQ号
const uin = await WebUiDataRuntime.getQQLoginUin(); const uin = WebUiDataRuntime.getQQLoginUin();
// 读取配置文件 // 读取配置文件
const configFilePath = resolve(webUiPathWrapper.configPath, `./onebot11_${uin}.json`); const configFilePath = resolve(webUiPathWrapper.configPath, `./onebot11_${uin}.json`);
// 尝试解析配置文件 // 尝试解析配置文件
@@ -39,7 +39,7 @@ export const OB11GetConfigHandler: RequestHandler = async (_, res) => {
// 写入OneBot11配置 // 写入OneBot11配置
export const OB11SetConfigHandler: RequestHandler = async (req, res) => { export const OB11SetConfigHandler: RequestHandler = async (req, res) => {
// 获取QQ登录状态 // 获取QQ登录状态
const isLogin = await WebUiDataRuntime.getQQLoginStatus(); const isLogin = WebUiDataRuntime.getQQLoginStatus();
// 如果未登录,返回错误 // 如果未登录,返回错误
if (!isLogin) { if (!isLogin) {
return sendError(res, 'Not Login'); return sendError(res, 'Not Login');

View File

@@ -7,12 +7,12 @@ import { sendError, sendSuccess } from '@webapi/utils/response';
// 获取QQ登录二维码 // 获取QQ登录二维码
export const QQGetQRcodeHandler: RequestHandler = async (req, res) => { export const QQGetQRcodeHandler: RequestHandler = async (req, res) => {
// 判断是否已经登录 // 判断是否已经登录
if (await WebUiDataRuntime.getQQLoginStatus()) { if (WebUiDataRuntime.getQQLoginStatus()) {
// 已经登录 // 已经登录
return sendError(res, 'QQ Is Logined'); return sendError(res, 'QQ Is Logined');
} }
// 获取二维码 // 获取二维码
const qrcodeUrl = await WebUiDataRuntime.getQQLoginQrcodeURL(); const qrcodeUrl = WebUiDataRuntime.getQQLoginQrcodeURL();
// 判断二维码是否为空 // 判断二维码是否为空
if (isEmpty(qrcodeUrl)) { if (isEmpty(qrcodeUrl)) {
return sendError(res, 'QRCode Get Error'); return sendError(res, 'QRCode Get Error');
@@ -27,8 +27,8 @@ export const QQGetQRcodeHandler: RequestHandler = async (req, res) => {
// 获取QQ登录状态 // 获取QQ登录状态
export const QQCheckLoginStatusHandler: RequestHandler = async (req, res) => { export const QQCheckLoginStatusHandler: RequestHandler = async (req, res) => {
const data = { const data = {
isLogin: await WebUiDataRuntime.getQQLoginStatus(), isLogin: WebUiDataRuntime.getQQLoginStatus(),
qrcodeurl: await WebUiDataRuntime.getQQLoginQrcodeURL(), qrcodeurl: WebUiDataRuntime.getQQLoginQrcodeURL(),
}; };
return sendSuccess(res, data); return sendSuccess(res, data);
}; };
@@ -38,7 +38,7 @@ export const QQSetQuickLoginHandler: RequestHandler = async (req, res) => {
// 获取QQ号 // 获取QQ号
const { uin } = req.body; const { uin } = req.body;
// 判断是否已经登录 // 判断是否已经登录
const isLogin = await WebUiDataRuntime.getQQLoginStatus(); const isLogin = WebUiDataRuntime.getQQLoginStatus();
if (isLogin) { if (isLogin) {
return sendError(res, 'QQ Is Logined'); return sendError(res, 'QQ Is Logined');
} }
@@ -53,12 +53,24 @@ export const QQSetQuickLoginHandler: RequestHandler = async (req, res) => {
return sendError(res, message); return sendError(res, message);
} }
//本来应该验证 但是http不宜这么搞 建议前端验证 //本来应该验证 但是http不宜这么搞 建议前端验证
//isLogin = await WebUiDataRuntime.getQQLoginStatus(); //isLogin = WebUiDataRuntime.getQQLoginStatus();
return sendSuccess(res, null); return sendSuccess(res, null);
}; };
// 获取快速登录列表 // 获取快速登录列表
export const QQGetQuickLoginListHandler: RequestHandler = async (_, res) => { export const QQGetQuickLoginListHandler: RequestHandler = async (_, res) => {
const quickLoginList = await WebUiDataRuntime.getQQQuickLoginList(); const quickLoginList = WebUiDataRuntime.getQQQuickLoginList();
return sendSuccess(res, quickLoginList); return sendSuccess(res, quickLoginList);
}; };
// 获取快速登录列表(新)
export const QQGetLoginListNewHandler: RequestHandler = async (_, res) => {
const newLoginList = WebUiDataRuntime.getQQNewLoginList();
return sendSuccess(res, newLoginList);
};
// 获取登录的QQ的信息
export const getQQLoginInfoHandler: RequestHandler = async (_, res) => {
const data = WebUiDataRuntime.getQQLoginInfo();
return sendSuccess(res, data);
};

View File

@@ -1,11 +1,16 @@
import { OneBotConfig } from '@/onebot/config/config'; import type { LoginRuntimeType } from '../types/data';
import packageJson from '../../../../package.json';
const LoginRuntime: LoginRuntimeType = { const LoginRuntime: LoginRuntimeType = {
LoginCurrentTime: Date.now(), LoginCurrentTime: Date.now(),
LoginCurrentRate: 0, LoginCurrentRate: 0,
QQLoginStatus: false, //已实现 但太傻了 得去那边注册个回调刷新 QQLoginStatus: false, //已实现 但太傻了 得去那边注册个回调刷新
QQQRCodeURL: '', QQQRCodeURL: '',
QQLoginUin: '', QQLoginUin: '',
QQLoginInfo: {
uid: '',
uin: '',
nick: '',
},
NapCatHelper: { NapCatHelper: {
onOB11ConfigChanged: async () => { onOB11ConfigChanged: async () => {
return; return;
@@ -14,11 +19,13 @@ const LoginRuntime: LoginRuntimeType = {
return { result: false, message: '' }; return { result: false, message: '' };
}, },
QQLoginList: [], QQLoginList: [],
NewQQLoginList: [],
}, },
packageJson: packageJson,
}; };
export const WebUiDataRuntime = { export const WebUiDataRuntime = {
checkLoginRate: async function (RateLimit: number): Promise<boolean> { checkLoginRate(RateLimit: number): boolean {
LoginRuntime.LoginCurrentRate++; LoginRuntime.LoginCurrentRate++;
//console.log(RateLimit, LoginRuntime.LoginCurrentRate, Date.now() - LoginRuntime.LoginCurrentTime); //console.log(RateLimit, LoginRuntime.LoginCurrentRate, Date.now() - LoginRuntime.LoginCurrentTime);
if (Date.now() - LoginRuntime.LoginCurrentTime > 1000 * 60) { if (Date.now() - LoginRuntime.LoginCurrentTime > 1000 * 60) {
@@ -29,51 +36,68 @@ export const WebUiDataRuntime = {
return LoginRuntime.LoginCurrentRate <= RateLimit; return LoginRuntime.LoginCurrentRate <= RateLimit;
}, },
getQQLoginStatus: async function (): Promise<boolean> { getQQLoginStatus(): LoginRuntimeType['QQLoginStatus'] {
return LoginRuntime.QQLoginStatus; return LoginRuntime.QQLoginStatus;
}, },
setQQLoginStatus: async function (status: boolean): Promise<void> { setQQLoginStatus(status: LoginRuntimeType['QQLoginStatus']): void {
LoginRuntime.QQLoginStatus = status; LoginRuntime.QQLoginStatus = status;
}, },
setQQLoginQrcodeURL: async function (url: string): Promise<void> { setQQLoginQrcodeURL(url: LoginRuntimeType['QQQRCodeURL']): void {
LoginRuntime.QQQRCodeURL = url; LoginRuntime.QQQRCodeURL = url;
}, },
getQQLoginQrcodeURL: async function (): Promise<string> { getQQLoginQrcodeURL(): LoginRuntimeType['QQQRCodeURL'] {
return LoginRuntime.QQQRCodeURL; return LoginRuntime.QQQRCodeURL;
}, },
setQQLoginUin: async function (uin: string): Promise<void> { setQQLoginInfo(info: LoginRuntimeType['QQLoginInfo']): void {
LoginRuntime.QQLoginUin = uin; LoginRuntime.QQLoginInfo = info;
LoginRuntime.QQLoginUin = info.uin.toString();
}, },
getQQLoginUin: async function (): Promise<string> { getQQLoginInfo(): LoginRuntimeType['QQLoginInfo'] {
return LoginRuntime.QQLoginInfo;
},
getQQLoginUin(): LoginRuntimeType['QQLoginUin'] {
return LoginRuntime.QQLoginUin; return LoginRuntime.QQLoginUin;
}, },
getQQQuickLoginList: async function (): Promise<any[]> { getQQQuickLoginList(): LoginRuntimeType['NapCatHelper']['QQLoginList'] {
return LoginRuntime.NapCatHelper.QQLoginList; return LoginRuntime.NapCatHelper.QQLoginList;
}, },
setQQQuickLoginList: async function (list: string[]): Promise<void> { setQQQuickLoginList(list: LoginRuntimeType['NapCatHelper']['QQLoginList']): void {
LoginRuntime.NapCatHelper.QQLoginList = list; LoginRuntime.NapCatHelper.QQLoginList = list;
}, },
setQuickLoginCall(func: (uin: string) => Promise<{ result: boolean; message: string }>): void { getQQNewLoginList(): LoginRuntimeType['NapCatHelper']['NewQQLoginList'] {
return LoginRuntime.NapCatHelper.NewQQLoginList;
},
setQQNewLoginList(list: LoginRuntimeType['NapCatHelper']['NewQQLoginList']): void {
LoginRuntime.NapCatHelper.NewQQLoginList = list;
},
setQuickLoginCall(func: LoginRuntimeType['NapCatHelper']['onQuickLoginRequested']): void {
LoginRuntime.NapCatHelper.onQuickLoginRequested = func; LoginRuntime.NapCatHelper.onQuickLoginRequested = func;
}, },
requestQuickLogin: async function (uin: string): Promise<{ result: boolean; message: string }> { requestQuickLogin: function (uin) {
return await LoginRuntime.NapCatHelper.onQuickLoginRequested(uin); return LoginRuntime.NapCatHelper.onQuickLoginRequested(uin);
}, } as LoginRuntimeType['NapCatHelper']['onQuickLoginRequested'],
setOnOB11ConfigChanged: async function (func: (ob11: OneBotConfig) => Promise<void>): Promise<void> { setOnOB11ConfigChanged(func: LoginRuntimeType['NapCatHelper']['onOB11ConfigChanged']): void {
LoginRuntime.NapCatHelper.onOB11ConfigChanged = func; LoginRuntime.NapCatHelper.onOB11ConfigChanged = func;
}, },
setOB11Config: async function (ob11: OneBotConfig): Promise<void> { setOB11Config: function (ob11) {
await LoginRuntime.NapCatHelper.onOB11ConfigChanged(ob11); return LoginRuntime.NapCatHelper.onOB11ConfigChanged(ob11);
} as LoginRuntimeType['NapCatHelper']['onOB11ConfigChanged'],
getPackageJson() {
return LoginRuntime.packageJson;
}, },
}; };

View File

@@ -0,0 +1,8 @@
import { Router } from 'express';
import { PackageInfoHandler } from '../api/BaseInfo';
const router = Router();
// router: 获取nc的package.json信息
router.get('/PackageInfo', PackageInfoHandler);
export { router as BaseRouter };

View File

@@ -5,16 +5,22 @@ import {
QQGetQRcodeHandler, QQGetQRcodeHandler,
QQGetQuickLoginListHandler, QQGetQuickLoginListHandler,
QQSetQuickLoginHandler, QQSetQuickLoginHandler,
QQGetLoginListNewHandler,
getQQLoginInfoHandler,
} from '@webapi/api/QQLogin'; } from '@webapi/api/QQLogin';
const router = Router(); const router = Router();
// router:获取快速登录列表 // router:获取快速登录列表
router.all('/GetQuickLoginList', QQGetQuickLoginListHandler); router.all('/GetQuickLoginList', QQGetQuickLoginListHandler);
// router:获取快速登录列表(新)
router.all('/GetQuickLoginListNew', QQGetLoginListNewHandler);
// router:检查QQ登录状态 // router:检查QQ登录状态
router.post('/CheckLoginStatus', QQCheckLoginStatusHandler); router.post('/CheckLoginStatus', QQCheckLoginStatusHandler);
// router:获取QQ登录二维码 // router:获取QQ登录二维码
router.post('/GetQQLoginQrcode', QQGetQRcodeHandler); router.post('/GetQQLoginQrcode', QQGetQRcodeHandler);
// router:设置QQ快速登录 // router:设置QQ快速登录
router.post('/SetQuickLogin', QQSetQuickLoginHandler); router.post('/SetQuickLogin', QQSetQuickLoginHandler);
// router:获取QQ登录信息
router.post('/GetQQLoginInfo', getQQLoginInfoHandler);
export { router as QQLoginRouter }; export { router as QQLoginRouter };

View File

@@ -11,6 +11,7 @@ import { sendSuccess } from '@webapi/utils/response';
import { QQLoginRouter } from '@webapi/router/QQLogin'; import { QQLoginRouter } from '@webapi/router/QQLogin';
import { AuthRouter } from '@webapi/router/auth'; import { AuthRouter } from '@webapi/router/auth';
import { LogRouter } from '@webapi/router/Log'; import { LogRouter } from '@webapi/router/Log';
import { BaseRouter } from '@webapi/router/Base';
const router = Router(); const router = Router();
@@ -21,6 +22,8 @@ router.use(auth);
router.all('/test', (_, res) => { router.all('/test', (_, res) => {
return sendSuccess(res); return sendSuccess(res);
}); });
// router:基础信息相关路由
router.use('/base', BaseRouter);
// router:WebUI登录相关路由 // router:WebUI登录相关路由
router.use('/auth', AuthRouter); router.use('/auth', AuthRouter);
// router:QQ登录相关路由 // router:QQ登录相关路由

View File

@@ -1,12 +1,17 @@
import type { LoginListItem, SelfInfo } from '@/core';
interface LoginRuntimeType { interface LoginRuntimeType {
LoginCurrentTime: number; LoginCurrentTime: number;
LoginCurrentRate: number; LoginCurrentRate: number;
QQLoginStatus: boolean; QQLoginStatus: boolean;
QQQRCodeURL: string; QQQRCodeURL: string;
QQLoginUin: string; QQLoginUin: string;
QQLoginInfo: SelfInfo;
NapCatHelper: { NapCatHelper: {
onQuickLoginRequested: (uin: string) => Promise<{ result: boolean; message: string }>; onQuickLoginRequested: (uin: string) => Promise<{ result: boolean; message: string }>;
onOB11ConfigChanged: (ob11: OneBotConfig) => Promise<void>; onOB11ConfigChanged: (ob11: OneBotConfig) => Promise<void>;
QQLoginList: string[]; QQLoginList: string[];
NewQQLoginList: LoginListItem[];
}; };
packageJson: object;
} }