mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d59771ac2f | ||
![]() |
fba2078fc0 | ||
![]() |
20a37fe2de | ||
![]() |
8f6d26b65c | ||
![]() |
b58a194c8a | ||
![]() |
52f1b0a0ce | ||
![]() |
c2b8fb223b |
19
README.md
19
README.md
@@ -12,13 +12,6 @@ NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。
|
|||||||
- [x] **低占用**:无头模式占用资源极低,适合在服务器上运行
|
- [x] **低占用**:无头模式占用资源极低,适合在服务器上运行
|
||||||
- [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷
|
- [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷
|
||||||
|
|
||||||
### V2 Roadmap
|
|
||||||
|
|
||||||
- **依赖注入**:彻底理清模块间依赖,明确加载顺序,根除滥用变量导出、`setTimeout` 初始化
|
|
||||||
- **ES 出包**:可读性更高,报错更可读
|
|
||||||
- **代码优化**:减少代码冗余,提高代码质量
|
|
||||||
- **输出标准化**:统一控制台输出格式
|
|
||||||
|
|
||||||
## 如何使用
|
## 如何使用
|
||||||
|
|
||||||
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
||||||
@@ -29,13 +22,13 @@ NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。
|
|||||||
|
|
||||||
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
|
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
|
||||||
|
|
||||||
## 鸣谢名单
|
## 附加协议
|
||||||
|
禁止未授权任何项目使用Core部分代码用于二次开发与分发
|
||||||
|
|
||||||
[Lagrange](https://github.com/LagrangeDev/Lagrange.Core)
|
## 鸣谢名单
|
||||||
|
感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot) 提供初始版本基础
|
||||||
|
|
||||||
|
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**
|
**任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**
|
||||||
|
|
||||||
## 附加协议
|
|
||||||
禁止任何项目使用core部分代码用于二次开发与分发
|
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCat",
|
"name": "NapCat",
|
||||||
"slug": "NapCat",
|
"slug": "NapCat",
|
||||||
"description": "OneBot v11 protocol implementation with NapCat logic",
|
"description": "OneBot v11 protocol implementation with NapCat logic",
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:framework": "vite build --mode framework",
|
"build:framework": "vite build --mode framework",
|
||||||
"build:shell": "vite build --mode shell",
|
"build:shell": "vite build --mode shell",
|
||||||
|
@@ -2,7 +2,7 @@ import path, { dirname } from 'path';
|
|||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
export const napcat_version = '2.0.1';
|
export const napcat_version = '2.0.2';
|
||||||
|
|
||||||
export class NapCatPathWrapper {
|
export class NapCatPathWrapper {
|
||||||
binaryPath: string;
|
binaryPath: string;
|
||||||
|
31
src/common/utils/LRU.ts
Normal file
31
src/common/utils/LRU.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export class LRUCache<K, V> {
|
||||||
|
private capacity: number;
|
||||||
|
private cache: Map<K, V>;
|
||||||
|
|
||||||
|
constructor(capacity: number) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
this.cache = new Map<K, V>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(key: K): V | undefined {
|
||||||
|
const value = this.cache.get(key);
|
||||||
|
if (value !== undefined) {
|
||||||
|
// Move the accessed key to the end to mark it as most recently used
|
||||||
|
this.cache.delete(key);
|
||||||
|
this.cache.set(key, value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public put(key: K, value: V): void {
|
||||||
|
if (this.cache.has(key)) {
|
||||||
|
// If the key already exists, move it to the end to mark it as most recently used
|
||||||
|
this.cache.delete(key);
|
||||||
|
} else if (this.cache.size >= this.capacity) {
|
||||||
|
// If the cache is full, remove the least recently used key (the first one in the map)
|
||||||
|
const firstKey = this.cache.keys().next().value;
|
||||||
|
this.cache.delete(firstKey);
|
||||||
|
}
|
||||||
|
this.cache.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
@@ -54,21 +54,21 @@ export class NTQQFriendApi {
|
|||||||
uids.push(
|
uids.push(
|
||||||
...buddyListV2.flatMap(item => {
|
...buddyListV2.flatMap(item => {
|
||||||
item.buddyUids.forEach(uid => {
|
item.buddyUids.forEach(uid => {
|
||||||
categoryMap.set(uid, { categoryId: item.categoryId, categroyName: item.categroyName });
|
categoryMap.set(uid, { categoryId: item.categoryId, categoryName: item.categroyName });
|
||||||
});
|
});
|
||||||
return item.buddyUids;
|
return item.buddyUids;
|
||||||
}));
|
}));
|
||||||
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
|
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
|
||||||
);
|
);
|
||||||
return Array.from(data).map(([key, value]) => {
|
return buddyListV2.map(category => ({
|
||||||
const category = categoryMap.get(key);
|
|
||||||
return category ? {
|
|
||||||
...value,
|
|
||||||
categoryId: category.categoryId,
|
categoryId: category.categoryId,
|
||||||
categroyName: category.categroyName,
|
categorySortId: category.categorySortId,
|
||||||
} : value;
|
categoryName: category.categroyName,
|
||||||
});
|
categoryMbCount: category.categroyMbCount,
|
||||||
|
onlineCount: category.onlineCount,
|
||||||
|
buddyList: category.buddyUids.map(uid => data.get(uid)!).filter(value => value),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async isBuddy(uid: string) {
|
async isBuddy(uid: string) {
|
||||||
|
@@ -231,7 +231,7 @@ export class NTQQUserApi {
|
|||||||
|
|
||||||
//后期改成流水线处理
|
//后期改成流水线处理
|
||||||
async getUidByUinV2(Uin: string) {
|
async getUidByUinV2(Uin: string) {
|
||||||
let uid = (await this.context.session.getProfileService().getUidByUinV2('FriendsServiceImpl', [Uin])).get(Uin);
|
let uid = (await this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin);
|
||||||
if (uid) return uid;
|
if (uid) return uid;
|
||||||
uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
|
uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
|
||||||
if (uid) return uid;
|
if (uid) return uid;
|
||||||
|
@@ -176,10 +176,7 @@ export interface SimpleInfo {
|
|||||||
intimate: any | null;
|
intimate: any | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FriendV2 extends SimpleInfo {
|
export type FriendV2 = SimpleInfo;
|
||||||
categoryId?: number;
|
|
||||||
categroyName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SelfStatusInfo {
|
export interface SelfStatusInfo {
|
||||||
uid: string;
|
uid: string;
|
||||||
|
@@ -18,7 +18,7 @@ export enum ProfileBizType {
|
|||||||
|
|
||||||
export interface NodeIKernelProfileService {
|
export interface NodeIKernelProfileService {
|
||||||
|
|
||||||
getUidByUinV2(callfrom: string, uin: Array<string>): Promise<Map<string, string>>;//uin->uid
|
getUidByUin(callfrom: string, uin: Array<string>): Promise<Map<string, string>>;//uin->uid
|
||||||
|
|
||||||
getUinByUid(callfrom: string, uid: Array<string>): Promise<Map<string, string>>;
|
getUinByUid(callfrom: string, uid: Array<string>): Promise<Map<string, string>>;
|
||||||
|
|
||||||
|
@@ -8,7 +8,10 @@ export class GetFriendWithCategory extends BaseAction<void, any> {
|
|||||||
async _handle(payload: void) {
|
async _handle(payload: void) {
|
||||||
if (this.CoreContext.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
if (this.CoreContext.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||||
//全新逻辑
|
//全新逻辑
|
||||||
return OB11Constructor.friendsV2(await this.CoreContext.apis.FriendApi.getBuddyV2ExWithCate(true));
|
return (await this.CoreContext.apis.FriendApi.getBuddyV2ExWithCate(true)).map(category => ({
|
||||||
|
...category,
|
||||||
|
buddyList: OB11Constructor.friendsV2(category.buddyList),
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
throw new Error('this ntqq version not support, must be 26702 or later');
|
throw new Error('this ntqq version not support, must be 26702 or later');
|
||||||
}
|
}
|
||||||
|
@@ -69,7 +69,7 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Mlikiowa V2.0.1 Refactor Todo
|
// Mlikiowa V2.0.2 Refactor Todo
|
||||||
// retMember.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.lastSpeakTime || date.toString());
|
// retMember.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.lastSpeakTime || date.toString());
|
||||||
// retMember.join_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.joinTime || date.toString());
|
// retMember.join_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.joinTime || date.toString());
|
||||||
}
|
}
|
||||||
|
@@ -83,7 +83,7 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Mlikiowa V2.0.1 Refactor Todo
|
// Mlikiowa V2.0.2 Refactor Todo
|
||||||
// _groupMembers.forEach(async item => {
|
// _groupMembers.forEach(async item => {
|
||||||
// item.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.lastSpeakTime || date.toString());
|
// item.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.lastSpeakTime || date.toString());
|
||||||
// item.join_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.joinTime || date.toString());
|
// item.join_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.joinTime || date.toString());
|
||||||
|
@@ -44,7 +44,7 @@ class DeleteMsg extends BaseAction<Payload, void> {
|
|||||||
await NTQQMsgApi.recallMsg(msg.Peer, [msg.MsgId]);
|
await NTQQMsgApi.recallMsg(msg.Peer, [msg.MsgId]);
|
||||||
const data = await ret;
|
const data = await ret;
|
||||||
if (!data) {
|
if (!data) {
|
||||||
throw new Error('Recall failed');
|
//throw new Error('Recall failed');
|
||||||
}
|
}
|
||||||
//await sleep(100);
|
//await sleep(100);
|
||||||
//await NTQQMsgApi.getMsgsByMsgId(msg.Peer, [msg.MsgId]);
|
//await NTQQMsgApi.getMsgsByMsgId(msg.Peer, [msg.MsgId]);
|
||||||
|
@@ -56,7 +56,7 @@ const _handlers: {
|
|||||||
if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员');
|
if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员');
|
||||||
|
|
||||||
// then the qq is a group member
|
// then the qq is a group member
|
||||||
// Mlikiowa V2.0.1 Refactor Todo
|
// Mlikiowa V2.0.2 Refactor Todo
|
||||||
const uid = await coreContext.apis.UserApi.getUidByUinV2(atQQ);
|
const uid = await coreContext.apis.UserApi.getUidByUinV2(atQQ);
|
||||||
if (!uid) throw new Error('Get Uid Error');
|
if (!uid) throw new Error('Get Uid Error');
|
||||||
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, '');
|
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, '');
|
||||||
@@ -161,7 +161,7 @@ const _handlers: {
|
|||||||
} else {
|
} else {
|
||||||
postData = data;
|
postData = data;
|
||||||
}
|
}
|
||||||
// Mlikiowa V2.0.1 Refactor Todo
|
// Mlikiowa V2.0.2 Refactor Todo
|
||||||
const signUrl = obContext.configLoader.configData.musicSignUrl;
|
const signUrl = obContext.configLoader.configData.musicSignUrl;
|
||||||
if (!signUrl) {
|
if (!signUrl) {
|
||||||
if (data.type === 'qq') {
|
if (data.type === 'qq') {
|
||||||
|
@@ -408,7 +408,7 @@ export class OB11Constructor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//log("group msg", msg);
|
//log("group msg", msg);
|
||||||
// Mlikiowa V2.0.1 Refactor Todo
|
// Mlikiowa V2.0.2 Refactor Todo
|
||||||
// if (msg.senderUin && msg.senderUin !== '0') {
|
// if (msg.senderUin && msg.senderUin !== '0') {
|
||||||
// const member = await getGroupMember(msg.peerUid, msg.senderUin);
|
// const member = await getGroupMember(msg.peerUid, msg.senderUin);
|
||||||
// if (member && member.cardName !== msg.sendMemberName) {
|
// if (member && member.cardName !== msg.sendMemberName) {
|
||||||
@@ -656,8 +656,6 @@ export class OB11Constructor {
|
|||||||
remark: friend.coreInfo.nick,
|
remark: friend.coreInfo.nick,
|
||||||
sex: sexValue,
|
sex: sexValue,
|
||||||
level: 0,
|
level: 0,
|
||||||
categroyName: friend.categroyName,
|
|
||||||
categoryId: friend.categoryId,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
|
@@ -33,6 +33,7 @@ import { GroupDecreaseSubType, OB11GroupDecreaseEvent } from '@/onebot/event/not
|
|||||||
import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
|
import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
|
||||||
import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent';
|
import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent';
|
||||||
import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
|
import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
|
||||||
|
import { LRUCache } from '@/common/utils/LRU';
|
||||||
|
|
||||||
//OneBot实现类
|
//OneBot实现类
|
||||||
export class NapCatOneBot11Adapter {
|
export class NapCatOneBot11Adapter {
|
||||||
@@ -87,7 +88,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
if (ob11Config.http.enablePost) {
|
if (ob11Config.http.enablePost) {
|
||||||
ob11Config.http.postUrls.forEach(url => {
|
ob11Config.http.postUrls.forEach(url => {
|
||||||
this.networkManager.registerAdapter(new OB11ActiveHttpAdapter(
|
this.networkManager.registerAdapter(new OB11ActiveHttpAdapter(
|
||||||
url, ob11Config.token, this.core
|
url, ob11Config.token, this.core, this
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -116,7 +117,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
await WebUiDataRuntime.setOnOB11ConfigChanged(async (newConfig: OB11Config) => {
|
await WebUiDataRuntime.setOnOB11ConfigChanged(async (newConfig: OB11Config) => {
|
||||||
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)}`);
|
||||||
await this.reloadNetwork(prev, newConfig);
|
await this.reloadNetwork(prev, newConfig);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -145,7 +146,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
if (now.http.enablePost) {
|
if (now.http.enablePost) {
|
||||||
now.http.postUrls.forEach(url => {
|
now.http.postUrls.forEach(url => {
|
||||||
this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter(
|
this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter(
|
||||||
url, now.token, this.core
|
url, now.token, this.core, this
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -159,7 +160,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
);
|
);
|
||||||
for (const url of added) {
|
for (const url of added) {
|
||||||
await this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter(
|
await this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter(
|
||||||
url, now.token, this.core
|
url, now.token, this.core, this
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,7 +216,6 @@ export class NapCatOneBot11Adapter {
|
|||||||
|
|
||||||
private initMsgListener() {
|
private initMsgListener() {
|
||||||
const msgListener = new MsgListener();
|
const msgListener = new MsgListener();
|
||||||
|
|
||||||
msgListener.onInputStatusPush = async data => {
|
msgListener.onInputStatusPush = async data => {
|
||||||
const uin = await this.core.apis.UserApi.getUinByUidV2(data.fromUin);
|
const uin = await this.core.apis.UserApi.getUinByUidV2(data.fromUin);
|
||||||
this.context.logger.log(`[Notice] [输入状态] ${uin} ${data.statusText}`);
|
this.context.logger.log(`[Notice] [输入状态] ${uin} ${data.statusText}`);
|
||||||
@@ -245,12 +245,15 @@ export class NapCatOneBot11Adapter {
|
|||||||
.catch(e => this.context.logger.logError('处理消息失败', e));
|
.catch(e => this.context.logger.logError('处理消息失败', e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const msgIdSend = new LRUCache<string, boolean>(100);
|
||||||
msgListener.onMsgInfoListUpdate = async msgList => {
|
msgListener.onMsgInfoListUpdate = async msgList => {
|
||||||
this.emitRecallMsg(msgList)
|
this.emitRecallMsg(msgList)
|
||||||
.catch(e => this.context.logger.logError('处理消息失败', e));
|
.catch(e => this.context.logger.logError('处理消息失败', e));
|
||||||
|
|
||||||
for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) {
|
for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) {
|
||||||
// console.log(msg);
|
// console.log(msg);
|
||||||
|
if (!!msgIdSend.get(msg.msgId)) continue;
|
||||||
|
msgIdSend.put(msg.msgId, true);
|
||||||
if (msg.sendStatus == 2) {
|
if (msg.sendStatus == 2) {
|
||||||
// 完成后再post
|
// 完成后再post
|
||||||
OB11Constructor.message(this.core, msg, this.configLoader.configData.messagePostFormat)
|
OB11Constructor.message(this.core, msg, this.configLoader.configData.messagePostFormat)
|
||||||
|
@@ -7,7 +7,7 @@ export interface OB11User {
|
|||||||
age?: number;
|
age?: number;
|
||||||
qid?: string;
|
qid?: string;
|
||||||
login_days?: number;
|
login_days?: number;
|
||||||
categroyName?: string;
|
categoryName?: string;
|
||||||
categoryId?: number;
|
categoryId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,7 +30,7 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
SettingItem(
|
SettingItem(
|
||||||
'<span id="napcat-update-title">Napcat</span>',
|
'<span id="napcat-update-title">Napcat</span>',
|
||||||
undefined,
|
undefined,
|
||||||
SettingButton('V2.0.1', 'napcat-update-button', 'secondary'),
|
SettingButton('V2.0.2', 'napcat-update-button', 'secondary'),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) {
|
|||||||
SettingItem(
|
SettingItem(
|
||||||
'<span id="napcat-update-title">Napcat</span>',
|
'<span id="napcat-update-title">Napcat</span>',
|
||||||
void 0,
|
void 0,
|
||||||
SettingButton("V2.0.1", "napcat-update-button", "secondary")
|
SettingButton("V2.0.2", "napcat-update-button", "secondary")
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
Reference in New Issue
Block a user