mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-12 16:00:27 +00:00
Compare commits
63 Commits
feat/suppo
...
v4.13.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
001fe01ace | ||
|
|
0aa0c44634 | ||
|
|
93126e514e | ||
|
|
1ae10ae0c6 | ||
|
|
4b693bf6e2 | ||
|
|
574c257591 | ||
|
|
d680328762 | ||
|
|
d711cdecaf | ||
|
|
c5f1792009 | ||
|
|
a5e705e6a4 | ||
|
|
007f1db339 | ||
|
|
008fb39f8f | ||
|
|
6b8cc6756d | ||
|
|
24623f18d8 | ||
|
|
8b676ed693 | ||
|
|
ab2dfcfd8f | ||
|
|
2998e04435 | ||
|
|
613690f5af | ||
|
|
a60d8d109c | ||
|
|
28c9761e3d | ||
|
|
805cc32d7f | ||
|
|
de9d5180fe | ||
|
|
791e359199 | ||
|
|
63a9d571f3 | ||
|
|
59d4b08982 | ||
|
|
81e4e54f25 | ||
|
|
b75b733bb0 | ||
|
|
246269b519 | ||
|
|
3c24d6b700 | ||
|
|
679c980683 | ||
|
|
19766002ae | ||
|
|
c2d3a8034d | ||
|
|
58220d3fbc | ||
|
|
2daddbb030 | ||
|
|
6ec5bbeddf | ||
|
|
75236dd50c | ||
|
|
01958d47a4 | ||
|
|
772f07c58b | ||
|
|
0f9647bf64 | ||
|
|
8197ebcbcf | ||
|
|
d0519feb4f | ||
|
|
d43c6b10a3 | ||
|
|
857be5ee49 | ||
|
|
af8005dd6f | ||
|
|
6e8adad7ca | ||
|
|
0f8584b8e1 | ||
|
|
37f40a2635 | ||
|
|
1b4d604e32 | ||
|
|
81a0c07922 | ||
|
|
a8cb6b5865 | ||
|
|
d25bd65b2d | ||
|
|
e510a75f0c | ||
|
|
e3c6048a7f | ||
|
|
789c72d4cf | ||
|
|
711a060dd9 | ||
|
|
6268923f01 | ||
|
|
f6b9017429 | ||
|
|
178e51bbb8 | ||
|
|
8a232d8c68 | ||
|
|
7216755430 | ||
|
|
0c91f9c66b | ||
|
|
e8855a59b0 | ||
|
|
5de2664af4 |
2
.github/prompt/default.md
vendored
2
.github/prompt/default.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
[使用文档](https://napneko.github.io/)
|
[使用文档](https://napneko.github.io/)
|
||||||
|
|
||||||
## Windows 一键包
|
## Windows 一键包
|
||||||
我们为提供了的轻量化一键部署方案
|
我们提供了轻量化的一键部署方案
|
||||||
相对于普通需要安装QQ的方案,下面已内置QQ和Napcat 阅读使用文档参考
|
相对于普通需要安装QQ的方案,下面已内置QQ和Napcat 阅读使用文档参考
|
||||||
|
|
||||||
你可以下载
|
你可以下载
|
||||||
|
|||||||
2
.github/prompt/release_note_prompt.txt
vendored
2
.github/prompt/release_note_prompt.txt
vendored
@@ -31,7 +31,7 @@
|
|||||||
[使用文档](https://napneko.github.io/)
|
[使用文档](https://napneko.github.io/)
|
||||||
|
|
||||||
## Windows 一键包
|
## Windows 一键包
|
||||||
我们为提供了的轻量化一键部署方案
|
我们提供了轻量化的一键部署方案
|
||||||
相对于普通需要安装QQ的方案,下面已内置QQ和Napcat 阅读使用文档参考
|
相对于普通需要安装QQ的方案,下面已内置QQ和Napcat 阅读使用文档参考
|
||||||
|
|
||||||
你可以下载
|
你可以下载
|
||||||
|
|||||||
70
.github/workflows/auto-release.yml
vendored
70
.github/workflows/auto-release.yml
vendored
@@ -5,6 +5,63 @@ on:
|
|||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
publish-schema:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
|
||||||
|
- name: Get Version
|
||||||
|
id: get_version
|
||||||
|
run: |
|
||||||
|
latest_tag=$(git describe --tags $(git rev-list --tags --max-count=1))
|
||||||
|
version=${latest_tag#v}
|
||||||
|
echo "version=${version}" >> $GITHUB_ENV
|
||||||
|
echo "latest_tag=${latest_tag}" >> $GITHUB_ENV
|
||||||
|
echo "Debug: Version is ${version}"
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build napcat-schema
|
||||||
|
run: |
|
||||||
|
cd packages/napcat-schema
|
||||||
|
pnpm run build:openapi
|
||||||
|
|
||||||
|
- name: Checkout NapCatDocs
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: NapNeko/NapCatDocs
|
||||||
|
token: ${{ secrets.NAPCAT_BUILD }}
|
||||||
|
path: napcat-docs
|
||||||
|
|
||||||
|
- name: Copy OpenAPI Schema
|
||||||
|
run: |
|
||||||
|
mkdir -p napcat-docs/src/api/${{ env.version }}
|
||||||
|
cp packages/napcat-schema/dist/openapi.json napcat-docs/src/api/${{ env.version }}/openapi.json
|
||||||
|
echo "OpenAPI schema copied to napcat-docs/src/api/${{ env.version }}/openapi.json"
|
||||||
|
|
||||||
|
- name: Commit and Push
|
||||||
|
run: |
|
||||||
|
cd napcat-docs
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add src/api/${{ env.version }}/openapi.json
|
||||||
|
git commit -m "chore: update OpenAPI schema for version ${{ env.version }}" || echo "No changes to commit"
|
||||||
|
git push
|
||||||
|
|
||||||
shell-docker:
|
shell-docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -80,4 +137,17 @@ jobs:
|
|||||||
-H "Accept: application/vnd.github+json" \
|
-H "Accept: application/vnd.github+json" \
|
||||||
-H "Authorization: Bearer $GH_TOKEN" \
|
-H "Authorization: Bearer $GH_TOKEN" \
|
||||||
https://api.github.com/repos/NapNeko/NapCatLinuxNodeLoader/actions/workflows/release.yml/dispatches \
|
https://api.github.com/repos/NapNeko/NapCatLinuxNodeLoader/actions/workflows/release.yml/dispatches \
|
||||||
|
-d "{\"ref\":\"main\",\"inputs\":{\"napcat_version\":\"${NAPCAT_VERSION}\",\"qq_url_amd64\":\"${QQ_VERSION_X86_64}\",\"qq_url_arm64\":\"${QQ_VERSION_ARM64}\"}}"
|
||||||
|
- name: Trigger Release NapCat AppImage Workflow
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.NAPCAT_BUILD }}
|
||||||
|
NAPCAT_VERSION: ${{ env.latest_tag }}
|
||||||
|
QQ_VERSION_X86_64: 'https://dldir1v6.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_x86_64.AppImage' # 写死 QQ 版本
|
||||||
|
QQ_VERSION_ARM64: 'https://dldir1v6.qq.com/qqfile/qq/QQNT/94704804/linuxqq_3.2.23-44343_arm64.AppImage' # 写死 QQ 版本
|
||||||
|
run: |
|
||||||
|
echo "Debug: Triggering Release NapCat AppImage with napcat_version=${NAPCAT_VERSION}, qq_url_amd64=${QQ_VERSION_X86_64}, qq_url_arm64=${QQ_VERSION_ARM64}"
|
||||||
|
curl -X POST \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
-H "Authorization: Bearer $GH_TOKEN" \
|
||||||
|
https://api.github.com/repos/NapNeko/NapCatLinuxNodeLoader/actions/workflows/docker-publish.yml/dispatches \
|
||||||
-d "{\"ref\":\"main\",\"inputs\":{\"napcat_version\":\"${NAPCAT_VERSION}\",\"qq_url_amd64\":\"${QQ_VERSION_X86_64}\",\"qq_url_arm64\":\"${QQ_VERSION_ARM64}\"}}"
|
-d "{\"ref\":\"main\",\"inputs\":{\"napcat_version\":\"${NAPCAT_VERSION}\",\"qq_url_amd64\":\"${QQ_VERSION_X86_64}\",\"qq_url_arm64\":\"${QQ_VERSION_ARM64}\"}}"
|
||||||
@@ -50,7 +50,7 @@ _Modern protocol-side framework implemented based on NTQQ._
|
|||||||
| Docs | [](https://napneko.github.io/) | [](https://doc.napneko.icu/) | [](https://napcat.napneko.icu/) |
|
| Docs | [](https://napneko.github.io/) | [](https://doc.napneko.icu/) | [](https://napcat.napneko.icu/) |
|
||||||
|:-:|:-:|:-:|:-:|
|
|:-:|:-:|:-:|:-:|
|
||||||
|
|
||||||
| Docs | [](https://napneko.pages.dev/) | [](https://napcat.cyou/) | [](https://www.napcat.wiki) |
|
| Docs | [](https://napneko.pages.dev/) | [](https://napcat.top/) | [](https://napcat.top/) |
|
||||||
|:-:|:-:|:-:|:-:|
|
|:-:|:-:|:-:|:-:|
|
||||||
|
|
||||||
| QQ Group | [](https://qm.qq.com/q/CMmPbGw0jA) | [](https://qm.qq.com/q/8zJMLjqy2Y) | [](https://qm.qq.com/q/CMmPbGw0jA) | [](https://qm.qq.com/q/I6LU87a0Yq) |
|
| QQ Group | [](https://qm.qq.com/q/CMmPbGw0jA) | [](https://qm.qq.com/q/8zJMLjqy2Y) | [](https://qm.qq.com/q/CMmPbGw0jA) | [](https://qm.qq.com/q/I6LU87a0Yq) |
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"build:shell:dev": "pnpm --filter napcat-shell run build:dev || exit 1",
|
"build:shell:dev": "pnpm --filter napcat-shell run build:dev || exit 1",
|
||||||
"build:framework": "pnpm --filter napcat-framework run build || exit 1",
|
"build:framework": "pnpm --filter napcat-framework run build || exit 1",
|
||||||
"build:webui": "pnpm --filter napcat-webui-frontend run build || exit 1",
|
"build:webui": "pnpm --filter napcat-webui-frontend run build || exit 1",
|
||||||
|
"build:plugin-builtin": "pnpm --filter napcat-plugin-builtin run build || exit 1",
|
||||||
"dev:shell": "pnpm --filter napcat-develop run dev || exit 1",
|
"dev:shell": "pnpm --filter napcat-develop run dev || exit 1",
|
||||||
"typecheck": "pnpm -r --if-present run typecheck",
|
"typecheck": "pnpm -r --if-present run typecheck",
|
||||||
"test": "pnpm --filter napcat-test run test",
|
"test": "pnpm --filter napcat-test run test",
|
||||||
|
|||||||
@@ -220,13 +220,13 @@ export function parseAppidFromMajor (nodeMajor: string): string | undefined {
|
|||||||
// ============== GitHub Tags 获取 ==============
|
// ============== GitHub Tags 获取 ==============
|
||||||
// 使用 mirror 模块统一管理镜像
|
// 使用 mirror 模块统一管理镜像
|
||||||
|
|
||||||
export async function getAllTags (): Promise<{ tags: string[], mirror: string; }> {
|
export async function getAllTags (mirror?: string): Promise<{ tags: string[], mirror: string; }> {
|
||||||
return getAllTagsFromMirror('NapNeko', 'NapCatQQ');
|
return getAllTagsFromMirror('NapNeko', 'NapCatQQ', mirror);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function getLatestTag (): Promise<string> {
|
export async function getLatestTag (mirror?: string): Promise<string> {
|
||||||
const { tags } = await getAllTags();
|
const { tags } = await getAllTags(mirror);
|
||||||
|
|
||||||
// 使用 SemVer 规范排序
|
// 使用 SemVer 规范排序
|
||||||
tags.sort((a, b) => compareSemVer(a, b));
|
tags.sort((a, b) => compareSemVer(a, b));
|
||||||
|
|||||||
@@ -58,12 +58,12 @@ export class LimitedHashTable<K, V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取最近刚写入的几个值
|
// 获取最近刚写入的几个值
|
||||||
getHeads (size: number): { key: K; value: V }[] | undefined {
|
getHeads (size: number): { key: K; value: V; }[] | undefined {
|
||||||
const keyList = this.getKeyList();
|
const keyList = this.getKeyList();
|
||||||
if (keyList.length === 0) {
|
if (keyList.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const result: { key: K; value: V }[] = [];
|
const result: { key: K; value: V; }[] = [];
|
||||||
const listSize = Math.min(size, keyList.length);
|
const listSize = Math.min(size, keyList.length);
|
||||||
for (let i = 0; i < listSize; i++) {
|
for (let i = 0; i < listSize; i++) {
|
||||||
const key = keyList[listSize - i];
|
const key = keyList[listSize - i];
|
||||||
@@ -108,7 +108,7 @@ class MessageUniqueWrapper {
|
|||||||
return shortId;
|
return shortId;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMsgIdAndPeerByShortId (shortId: number): { MsgId: string; Peer: Peer } | undefined {
|
getMsgIdAndPeerByShortId (shortId: number): { MsgId: string; Peer: Peer; } | undefined {
|
||||||
const data = this.msgDataMap.getKey(shortId);
|
const data = this.msgDataMap.getKey(shortId);
|
||||||
if (data) {
|
if (data) {
|
||||||
const [msgId, chatTypeStr, peerUid] = data.split('|');
|
const [msgId, chatTypeStr, peerUid] = data.split('|');
|
||||||
@@ -136,6 +136,12 @@ class MessageUniqueWrapper {
|
|||||||
this.msgIdMap.resize(maxSize);
|
this.msgIdMap.resize(maxSize);
|
||||||
this.msgDataMap.resize(maxSize);
|
this.msgDataMap.resize(maxSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isShortId (message_id: string): boolean {
|
||||||
|
const num = Number(message_id);
|
||||||
|
// 判断是否是整数并且在 INT32 的范围内
|
||||||
|
return Number.isInteger(num) && num >= -2147483648 && num <= 2147483647;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper();
|
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper();
|
||||||
|
|||||||
@@ -23,66 +23,50 @@ import { PromiseTimer } from './helper';
|
|||||||
* 懒加载测速:首次使用时自动测速,缓存 30 分钟
|
* 懒加载测速:首次使用时自动测速,缓存 30 分钟
|
||||||
*/
|
*/
|
||||||
export const GITHUB_FILE_MIRRORS = [
|
export const GITHUB_FILE_MIRRORS = [
|
||||||
// 延迟 < 800ms 的最快镜像
|
'https://github.chenc.dev/',
|
||||||
'https://github.chenc.dev/', // 666ms
|
'https://ghproxy.cfd/',
|
||||||
'https://ghproxy.cfd/', // 719ms - 支持重定向
|
'https://github.tbedu.top/',
|
||||||
'https://github.tbedu.top/', // 760ms
|
'https://ghproxy.cc/',
|
||||||
'https://ghps.cc/', // 768ms
|
'https://gh.monlor.com/',
|
||||||
'https://gh.llkk.cc/', // 774ms
|
'https://cdn.akaere.online/',
|
||||||
'https://ghproxy.cc/', // 777ms
|
'https://gh.idayer.com/',
|
||||||
'https://gh.monlor.com/', // 779ms
|
'https://gh.llkk.cc/',
|
||||||
'https://cdn.akaere.online/', // 784ms
|
'https://ghpxy.hwinzniej.top/',
|
||||||
// 延迟 800-1000ms 的快速镜像
|
'https://github-proxy.memory-echoes.cn/',
|
||||||
'https://gh.idayer.com/', // 869ms
|
'https://git.yylx.win/',
|
||||||
'https://gh-proxy.net/', // 885ms
|
'https://gitproxy.mrhjx.cn/',
|
||||||
'https://ghpxy.hwinzniej.top/', // 890ms
|
'https://gh.fhjhy.top/',
|
||||||
'https://github-proxy.memory-echoes.cn/', // 896ms
|
'https://gp.zkitefly.eu.org/',
|
||||||
'https://git.yylx.win/', // 917ms
|
'https://gh-proxy.com/',
|
||||||
'https://gitproxy.mrhjx.cn/', // 950ms
|
'https://ghfile.geekertao.top/',
|
||||||
'https://jiashu.1win.eu.org/', // 954ms
|
'https://j.1lin.dpdns.org/',
|
||||||
'https://ghproxy.cn/', // 981ms
|
'https://ghproxy.imciel.com/',
|
||||||
// 延迟 1000-1500ms 的中速镜像
|
'https://github-proxy.teach-english.tech/',
|
||||||
'https://gh.fhjhy.top/', // 1014ms
|
'https://gh.927223.xyz/',
|
||||||
'https://gp.zkitefly.eu.org/', // 1015ms
|
'https://github.ednovas.xyz/',
|
||||||
'https://gh-proxy.com/', // 1022ms
|
'https://ghf.xn--eqrr82bzpe.top/',
|
||||||
'https://hub.gitmirror.com/', // 1027ms
|
'https://gh.dpik.top/',
|
||||||
'https://ghfile.geekertao.top/', // 1029ms
|
'https://gh.jasonzeng.dev/',
|
||||||
'https://j.1lin.dpdns.org/', // 1037ms
|
'https://gh.xxooo.cf/',
|
||||||
'https://ghproxy.imciel.com/', // 1047ms
|
'https://gh.bugdey.us.kg/',
|
||||||
'https://github-proxy.teach-english.tech/', // 1047ms
|
'https://ghm.078465.xyz/',
|
||||||
'https://gh.927223.xyz/', // 1071ms
|
'https://j.1win.ggff.net/',
|
||||||
'https://github.ednovas.xyz/', // 1099ms
|
'https://tvv.tw/',
|
||||||
'https://ghf.xn--eqrr82bzpe.top/',// 1122ms
|
'https://gitproxy.127731.xyz/',
|
||||||
'https://gh.dpik.top/', // 1131ms
|
'https://gh.inkchills.cn/',
|
||||||
'https://gh.jasonzeng.dev/', // 1139ms
|
'https://ghproxy.cxkpro.top/',
|
||||||
'https://gh.xxooo.cf/', // 1157ms
|
'https://gh.sixyin.com/',
|
||||||
'https://gh.bugdey.us.kg/', // 1228ms
|
'https://github.geekery.cn/',
|
||||||
'https://ghm.078465.xyz/', // 1289ms
|
'https://git.669966.xyz/',
|
||||||
'https://j.1win.ggff.net/', // 1329ms
|
'https://gh.5050net.cn/',
|
||||||
'https://tvv.tw/', // 1393ms
|
'https://gh.felicity.ac.cn/',
|
||||||
'https://gh.chjina.com/', // 1446ms
|
'https://github.dpik.top/',
|
||||||
'https://gitproxy.127731.xyz/', // 1458ms
|
'https://ghp.keleyaa.com/',
|
||||||
// 延迟 1500-2500ms 的较慢镜像
|
'https://gh.wsmdn.dpdns.org/',
|
||||||
'https://gh.inkchills.cn/', // 1617ms
|
'https://ghproxy.monkeyray.net/',
|
||||||
'https://ghproxy.cxkpro.top/', // 1651ms
|
'https://fastgit.cc/',
|
||||||
'https://gh.sixyin.com/', // 1686ms
|
'https://gh.catmak.name/',
|
||||||
'https://github.geekery.cn/', // 1734ms
|
'https://gh.noki.icu/',
|
||||||
'https://git.669966.xyz/', // 1824ms
|
|
||||||
'https://gh.5050net.cn/', // 1858ms
|
|
||||||
'https://gh.felicity.ac.cn/', // 1903ms
|
|
||||||
'https://gh.ddlc.top/', // 2056ms
|
|
||||||
'https://cf.ghproxy.cc/', // 2058ms
|
|
||||||
'https://gitproxy.click/', // 2068ms
|
|
||||||
'https://github.dpik.top/', // 2313ms
|
|
||||||
'https://gh.zwnes.xyz/', // 2434ms
|
|
||||||
'https://ghp.keleyaa.com/', // 2440ms
|
|
||||||
'https://gh.wsmdn.dpdns.org/', // 2744ms
|
|
||||||
// 延迟 > 2500ms 的慢速镜像(作为备用)
|
|
||||||
'https://ghproxy.monkeyray.net/', // 3023ms
|
|
||||||
'https://fastgit.cc/', // 3369ms
|
|
||||||
'https://cdn.gh-proxy.com/', // 3394ms
|
|
||||||
'https://gh.catmak.name/', // 4119ms
|
|
||||||
'https://gh.noki.icu/', // 5990ms
|
|
||||||
'', // 原始 URL(无镜像)
|
'', // 原始 URL(无镜像)
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -109,7 +93,6 @@ export const GITHUB_RAW_MIRRORS = [
|
|||||||
// 测试确认支持 raw 文件的镜像
|
// 测试确认支持 raw 文件的镜像
|
||||||
'https://github.chenc.dev/https://raw.githubusercontent.com',
|
'https://github.chenc.dev/https://raw.githubusercontent.com',
|
||||||
'https://ghproxy.cfd/https://raw.githubusercontent.com',
|
'https://ghproxy.cfd/https://raw.githubusercontent.com',
|
||||||
'https://gh.llkk.cc/https://raw.githubusercontent.com',
|
|
||||||
'https://ghproxy.cc/https://raw.githubusercontent.com',
|
'https://ghproxy.cc/https://raw.githubusercontent.com',
|
||||||
'https://gh-proxy.net/https://raw.githubusercontent.com',
|
'https://gh-proxy.net/https://raw.githubusercontent.com',
|
||||||
];
|
];
|
||||||
@@ -150,7 +133,7 @@ const defaultConfig: MirrorConfig = {
|
|||||||
apiMirrors: GITHUB_API_MIRRORS,
|
apiMirrors: GITHUB_API_MIRRORS,
|
||||||
rawMirrors: GITHUB_RAW_MIRRORS,
|
rawMirrors: GITHUB_RAW_MIRRORS,
|
||||||
nightlyLinkMirrors: NIGHTLY_LINK_MIRRORS,
|
nightlyLinkMirrors: NIGHTLY_LINK_MIRRORS,
|
||||||
timeout: 10000, // 10秒超时,平衡速度和可靠性
|
timeout: 5000, // 5秒超时,平衡速度和可靠性
|
||||||
enabled: true,
|
enabled: true,
|
||||||
customMirror: undefined,
|
customMirror: undefined,
|
||||||
};
|
};
|
||||||
@@ -274,7 +257,7 @@ export async function getFastMirrors (forceRefresh: boolean = false): Promise<st
|
|||||||
async function performMirrorTest (): Promise<string[]> {
|
async function performMirrorTest (): Promise<string[]> {
|
||||||
// 开始镜像测速
|
// 开始镜像测速
|
||||||
|
|
||||||
const timeout = 8000; // 测速超时 8 秒
|
const timeout = 3000; // 测速超时 3 秒
|
||||||
|
|
||||||
// 并行测试所有镜像
|
// 并行测试所有镜像
|
||||||
const mirrors = currentConfig.fileMirrors.filter(m => m);
|
const mirrors = currentConfig.fileMirrors.filter(m => m);
|
||||||
@@ -400,17 +383,14 @@ export async function testUrlHead (url: string, timeout: number = 5000): Promise
|
|||||||
}, (res) => {
|
}, (res) => {
|
||||||
const statusCode = res.statusCode || 0;
|
const statusCode = res.statusCode || 0;
|
||||||
const contentType = (res.headers['content-type'] as string) || '';
|
const contentType = (res.headers['content-type'] as string) || '';
|
||||||
const contentLength = parseInt((res.headers['content-length'] as string) || '0', 10);
|
|
||||||
|
|
||||||
// 验证条件:
|
// 简化验证条件:
|
||||||
// 1. 状态码 2xx 或 3xx
|
// 1. 状态码 2xx 或 3xx
|
||||||
// 2. Content-Type 不应该是 text/html(表示错误页面)
|
// 2. Content-Type 不应该是 text/html(表示错误页面)
|
||||||
// 3. 对于 .zip 文件,Content-Length 应该 > 1MB(避免获取到错误页面)
|
|
||||||
const isValidStatus = statusCode >= 200 && statusCode < 400;
|
const isValidStatus = statusCode >= 200 && statusCode < 400;
|
||||||
const isNotHtmlError = !contentType.includes('text/html');
|
const isNotHtmlError = !contentType.includes('text/html');
|
||||||
const isValidSize = url.endsWith('.zip') ? contentLength > 1024 * 1024 : true;
|
|
||||||
|
|
||||||
resolve(isValidStatus && isNotHtmlError && isValidSize);
|
resolve(isValidStatus && isNotHtmlError);
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('error', () => resolve(false));
|
req.on('error', () => resolve(false));
|
||||||
@@ -454,10 +434,9 @@ export async function validateUrl (url: string, timeout: number = 5000): Promise
|
|||||||
const contentType = (res.headers['content-type'] as string) || '';
|
const contentType = (res.headers['content-type'] as string) || '';
|
||||||
const contentLength = parseInt((res.headers['content-length'] as string) || '0', 10);
|
const contentLength = parseInt((res.headers['content-length'] as string) || '0', 10);
|
||||||
|
|
||||||
// 验证条件
|
// 简化验证条件
|
||||||
const isValidStatus = statusCode >= 200 && statusCode < 400;
|
const isValidStatus = statusCode >= 200 && statusCode < 400;
|
||||||
const isNotHtmlError = !contentType.includes('text/html');
|
const isNotHtmlError = !contentType.includes('text/html');
|
||||||
const isValidSize = url.endsWith('.zip') ? contentLength > 1024 * 1024 : true;
|
|
||||||
|
|
||||||
if (!isValidStatus) {
|
if (!isValidStatus) {
|
||||||
resolve({
|
resolve({
|
||||||
@@ -475,14 +454,6 @@ export async function validateUrl (url: string, timeout: number = 5000): Promise
|
|||||||
contentLength,
|
contentLength,
|
||||||
error: '返回了 HTML 页面而非文件',
|
error: '返回了 HTML 页面而非文件',
|
||||||
});
|
});
|
||||||
} else if (!isValidSize) {
|
|
||||||
resolve({
|
|
||||||
valid: false,
|
|
||||||
statusCode,
|
|
||||||
contentType,
|
|
||||||
contentLength,
|
|
||||||
error: `文件过小 (${contentLength} bytes),可能是错误页面`,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
resolve({
|
resolve({
|
||||||
valid: true,
|
valid: true,
|
||||||
@@ -559,21 +530,21 @@ export async function findAvailableDownloadUrl (
|
|||||||
const testWithValidation = async (url: string): Promise<boolean> => {
|
const testWithValidation = async (url: string): Promise<boolean> => {
|
||||||
if (validateContent) {
|
if (validateContent) {
|
||||||
const result = await validateUrl(url, timeout);
|
const result = await validateUrl(url, timeout);
|
||||||
// 额外检查文件大小
|
// 额外检查文件大小(仅当指定了 minFileSize 时)
|
||||||
if (result.valid && minFileSize && result.contentLength && result.contentLength < minFileSize) {
|
if (result.valid && minFileSize && result.contentLength && result.contentLength < minFileSize) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return result.valid;
|
return result.valid;
|
||||||
}
|
}
|
||||||
return testMethod === 'head' ? testUrlHead(url, timeout) : testUrl(url, timeout);
|
// 不验证内容,只检查状态码
|
||||||
|
const isValid = testMethod === 'head' ? await testUrlHead(url, timeout) : await testUrl(url, timeout);
|
||||||
|
return isValid;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1. 如果设置了自定义镜像,优先使用
|
// 1. 如果设置了自定义镜像,直接使用(不测试,信任用户选择)
|
||||||
if (customMirror) {
|
if (customMirror) {
|
||||||
const customUrl = buildMirrorUrl(originalUrl, customMirror);
|
const customUrl = buildMirrorUrl(originalUrl, customMirror);
|
||||||
if (await testWithValidation(customUrl)) {
|
return customUrl;
|
||||||
return customUrl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 先测试原始 URL
|
// 2. 先测试原始 URL
|
||||||
@@ -585,7 +556,7 @@ export async function findAvailableDownloadUrl (
|
|||||||
let testedCount = 0;
|
let testedCount = 0;
|
||||||
for (const mirror of mirrors) {
|
for (const mirror of mirrors) {
|
||||||
if (!mirror) continue; // 跳过空字符串
|
if (!mirror) continue; // 跳过空字符串
|
||||||
|
|
||||||
// 特殊处理 nightly.link URL
|
// 特殊处理 nightly.link URL
|
||||||
let mirrorUrl: string;
|
let mirrorUrl: string;
|
||||||
if (originalUrl.includes('nightly.link')) {
|
if (originalUrl.includes('nightly.link')) {
|
||||||
@@ -594,7 +565,7 @@ export async function findAvailableDownloadUrl (
|
|||||||
} else {
|
} else {
|
||||||
mirrorUrl = buildMirrorUrl(originalUrl, mirror);
|
mirrorUrl = buildMirrorUrl(originalUrl, mirror);
|
||||||
}
|
}
|
||||||
|
|
||||||
testedCount++;
|
testedCount++;
|
||||||
if (await testWithValidation(mirrorUrl)) {
|
if (await testWithValidation(mirrorUrl)) {
|
||||||
return mirrorUrl;
|
return mirrorUrl;
|
||||||
@@ -650,8 +621,15 @@ function compareSemVerSimple (a: string, b: string): number {
|
|||||||
* 从 tags 列表中获取最新的 release tag
|
* 从 tags 列表中获取最新的 release tag
|
||||||
* 不依赖 GitHub API
|
* 不依赖 GitHub API
|
||||||
*/
|
*/
|
||||||
export async function getLatestReleaseTag (owner: string, repo: string): Promise<string> {
|
// Update definitions validation locally first if possible.
|
||||||
const result = await getAllGitHubTags(owner, repo);
|
// I'll assume valid typescript.
|
||||||
|
// I will split this into two tool calls to avoid complexity.
|
||||||
|
// 1. Update mirror.ts
|
||||||
|
// 2. Update UpdateNapCat.ts
|
||||||
|
|
||||||
|
// This tool call: Update mirror.ts
|
||||||
|
export async function getLatestReleaseTag (owner: string, repo: string, mirror?: string): Promise<string> {
|
||||||
|
const result = await getAllGitHubTags(owner, repo, mirror);
|
||||||
|
|
||||||
// 过滤出符合 semver 的 tags
|
// 过滤出符合 semver 的 tags
|
||||||
const releaseTags = result.tags.filter(tag => SEMVER_REGEX.test(tag));
|
const releaseTags = result.tags.filter(tag => SEMVER_REGEX.test(tag));
|
||||||
@@ -701,6 +679,8 @@ export async function getGitHubRelease (
|
|||||||
assetNames?: string[];
|
assetNames?: string[];
|
||||||
/** 是否需要获取 changelog(需要调用 API) */
|
/** 是否需要获取 changelog(需要调用 API) */
|
||||||
fetchChangelog?: boolean;
|
fetchChangelog?: boolean;
|
||||||
|
/** 指定镜像 */
|
||||||
|
mirror?: string;
|
||||||
} = {}
|
} = {}
|
||||||
): Promise<{
|
): Promise<{
|
||||||
tag_name: string;
|
tag_name: string;
|
||||||
@@ -710,15 +690,16 @@ export async function getGitHubRelease (
|
|||||||
}>;
|
}>;
|
||||||
body?: string;
|
body?: string;
|
||||||
}> {
|
}> {
|
||||||
const { assetNames = [], fetchChangelog = false } = options;
|
const { assetNames = [], fetchChangelog = false, mirror } = options;
|
||||||
|
|
||||||
// 1. 获取实际的 tag 名称
|
// 1. 获取实际的 tag 名称
|
||||||
let actualTag: string;
|
let actualTag: string;
|
||||||
if (tag === 'latest') {
|
if (tag === 'latest') {
|
||||||
actualTag = await getLatestReleaseTag(owner, repo);
|
actualTag = await getLatestReleaseTag(owner, repo, mirror);
|
||||||
} else {
|
} else {
|
||||||
actualTag = tag;
|
actualTag = tag;
|
||||||
}
|
}
|
||||||
|
// ...
|
||||||
|
|
||||||
// 2. 构建 assets 列表(不需要 API)
|
// 2. 构建 assets 列表(不需要 API)
|
||||||
const assets = assetNames.map(name => ({
|
const assets = assetNames.map(name => ({
|
||||||
@@ -782,8 +763,8 @@ const tagsCache: Map<string, TagsCache> = new Map();
|
|||||||
* 获取所有 GitHub tags(带缓存)
|
* 获取所有 GitHub tags(带缓存)
|
||||||
* 优化:并行请求多个镜像,使用第一个成功返回的结果
|
* 优化:并行请求多个镜像,使用第一个成功返回的结果
|
||||||
*/
|
*/
|
||||||
export async function getAllGitHubTags (owner: string, repo: string): Promise<{ tags: string[], mirror: string; }> {
|
export async function getAllGitHubTags (owner: string, repo: string, mirror?: string): Promise<{ tags: string[], mirror: string; }> {
|
||||||
const cacheKey = `${owner}/${repo}`;
|
const cacheKey = `${owner}/${repo}/${mirror || 'auto'}`;
|
||||||
|
|
||||||
// 检查缓存
|
// 检查缓存
|
||||||
const cached = tagsCache.get(cacheKey);
|
const cached = tagsCache.get(cacheKey);
|
||||||
@@ -805,7 +786,7 @@ export async function getAllGitHubTags (owner: string, repo: string): Promise<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 尝试从 URL 获取 tags
|
// 尝试从 URL 获取 tags
|
||||||
const fetchFromUrl = async (url: string, mirror: string): Promise<{ tags: string[], mirror: string; } | null> => {
|
const fetchFromUrl = async (url: string, usedMirror: string): Promise<{ tags: string[], mirror: string; } | null> => {
|
||||||
try {
|
try {
|
||||||
const raw = await PromiseTimer(
|
const raw = await PromiseTimer(
|
||||||
RequestUtil.HttpGetText(url),
|
RequestUtil.HttpGetText(url),
|
||||||
@@ -813,79 +794,55 @@ export async function getAllGitHubTags (owner: string, repo: string): Promise<{
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 检查返回内容是否有效(不是 HTML 错误页面)
|
// 检查返回内容是否有效(不是 HTML 错误页面)
|
||||||
if (raw.includes('<!DOCTYPE') || raw.includes('<html')) {
|
if (raw.includes('refs/tags')) {
|
||||||
return null;
|
return { tags: parseTags(raw), mirror: usedMirror };
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = parseTags(raw);
|
|
||||||
if (tags.length > 0) {
|
|
||||||
return { tags, mirror };
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
// 忽略错误
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取快速镜像列表
|
// 准备镜像列表
|
||||||
let fastMirrors: string[] = [];
|
let mirrors: string[] = [];
|
||||||
try {
|
if (mirror) {
|
||||||
fastMirrors = await getFastMirrors();
|
// 如果指定了镜像,只使用该镜像
|
||||||
} catch {
|
mirrors = [mirror];
|
||||||
// 忽略错误
|
} else {
|
||||||
|
// 否则使用 auto 逻辑,利用缓存的快速镜像列表
|
||||||
|
mirrors = await getFastMirrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建 URL 列表(取前 5 个快速镜像 + 原始 URL 并行请求)
|
// 并行请求
|
||||||
const topMirrors = fastMirrors.slice(0, 5);
|
const promises = mirrors.map(m => {
|
||||||
const mirrorUrls = [
|
const url = m ? buildMirrorUrl(baseUrl, m) : baseUrl;
|
||||||
{ url: baseUrl, mirror: 'github.com' }, // 原始 URL
|
return fetchFromUrl(url, m || 'https://github.com');
|
||||||
...topMirrors.filter(m => m).map(m => ({ url: buildMirrorUrl(baseUrl, m), mirror: m })),
|
});
|
||||||
];
|
|
||||||
|
|
||||||
// 并行请求所有镜像,使用 Promise.any 获取第一个成功的结果
|
|
||||||
try {
|
try {
|
||||||
const result = await Promise.any(
|
const result = await Promise.any(promises.filter(p => p !== null) as Promise<{ tags: string[], mirror: string; } | null>[]);
|
||||||
mirrorUrls.map(async ({ url, mirror }) => {
|
|
||||||
const res = await fetchFromUrl(url, mirror);
|
|
||||||
if (res) return res;
|
|
||||||
throw new Error('Failed');
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// 缓存结果
|
|
||||||
tagsCache.set(cacheKey, { tags: result.tags, mirror: result.mirror, timestamp: Date.now() });
|
|
||||||
return result;
|
|
||||||
} catch {
|
|
||||||
// Promise.any 全部失败,回退到顺序尝试剩余镜像
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回退:顺序尝试剩余镜像
|
|
||||||
const remainingMirrors = fastMirrors.slice(5).filter(m => m);
|
|
||||||
for (const mirror of remainingMirrors) {
|
|
||||||
const url = buildMirrorUrl(baseUrl, mirror);
|
|
||||||
const result = await fetchFromUrl(url, mirror);
|
|
||||||
if (result) {
|
if (result) {
|
||||||
tagsCache.set(cacheKey, { tags: result.tags, mirror: result.mirror, timestamp: Date.now() });
|
tagsCache.set(cacheKey, {
|
||||||
|
tags: result.tags,
|
||||||
|
mirror: result.mirror,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
// all failed
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最后尝试所有镜像
|
if (mirror) {
|
||||||
const allMirrors = currentConfig.fileMirrors.filter(m => m && !fastMirrors.includes(m));
|
throw new Error(`指定镜像 ${mirror} 获取 tags 失败`);
|
||||||
for (const mirror of allMirrors) {
|
|
||||||
const url = buildMirrorUrl(baseUrl, mirror);
|
|
||||||
const result = await fetchFromUrl(url, mirror);
|
|
||||||
if (result) {
|
|
||||||
tagsCache.set(cacheKey, { tags: result.tags, mirror: result.mirror, timestamp: Date.now() });
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('无法获取 tags,所有源都不可用');
|
throw new Error('无法获取 tags,所有镜像源都不可用');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============== Action Artifacts 支持 ==============
|
// ============== Action Artifacts 支持 ==============
|
||||||
|
|
||||||
|
// ActionArtifact 接口定义
|
||||||
export interface ActionArtifact {
|
export interface ActionArtifact {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -895,12 +852,14 @@ export interface ActionArtifact {
|
|||||||
archive_download_url: string;
|
archive_download_url: string;
|
||||||
workflow_run_id?: number;
|
workflow_run_id?: number;
|
||||||
head_sha?: string;
|
head_sha?: string;
|
||||||
|
workflow_title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============== Action Artifacts 缓存 ==============
|
// ============== Action Artifacts 缓存 ==============
|
||||||
|
|
||||||
interface ArtifactsCache {
|
interface ArtifactsCache {
|
||||||
artifacts: ActionArtifact[];
|
artifacts: ActionArtifact[];
|
||||||
|
mirror: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -920,68 +879,116 @@ export function clearArtifactsCache (): void {
|
|||||||
* 当 api.github.com 不可用时使用
|
* 当 api.github.com 不可用时使用
|
||||||
* 页面格式: https://github.com/{owner}/{repo}/actions/workflows/{workflow}
|
* 页面格式: https://github.com/{owner}/{repo}/actions/workflows/{workflow}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function getWorkflowRunsFromHtml (
|
async function getWorkflowRunsFromHtml (
|
||||||
owner: string,
|
owner: string,
|
||||||
repo: string,
|
repo: string,
|
||||||
workflow: string = 'build.yml',
|
workflow: string = 'build.yml',
|
||||||
maxRuns: number = 10
|
maxRuns: number = 10,
|
||||||
): Promise<Array<{ id: number; created_at: string; }>> {
|
mirror?: string
|
||||||
|
): Promise<{ runs: Array<{ id: number; created_at: string; title: string; }>; mirror: string; }> {
|
||||||
const baseUrl = `https://github.com/${owner}/${repo}/actions/workflows/${workflow}`;
|
const baseUrl = `https://github.com/${owner}/${repo}/actions/workflows/${workflow}`;
|
||||||
|
|
||||||
// 尝试使用镜像获取 HTML
|
// 尝试使用镜像获取 HTML
|
||||||
const mirrors = ['', ...currentConfig.fileMirrors.filter(m => m)];
|
// 如果指定了 mirror,则只使用该 mirror
|
||||||
|
let mirrors: string[] = [];
|
||||||
for (const mirror of mirrors) {
|
if (mirror) {
|
||||||
|
mirrors = [mirror];
|
||||||
|
} else {
|
||||||
|
// 使用缓存的快速镜像列表
|
||||||
|
mirrors = await getFastMirrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const mirrorItem of mirrors) {
|
||||||
try {
|
try {
|
||||||
const url = mirror ? buildMirrorUrl(baseUrl, mirror) : baseUrl;
|
const allRuns: Array<{ id: number; created_at: string; title: string; }> = [];
|
||||||
|
|
||||||
const html = await PromiseTimer(
|
|
||||||
RequestUtil.HttpGetText(url),
|
|
||||||
10000
|
|
||||||
);
|
|
||||||
|
|
||||||
// 从 HTML 中提取 run IDs 和时间
|
|
||||||
// 格式: href="/NapNeko/NapCatQQ/actions/runs/20676123968"
|
|
||||||
// 时间格式: <relative-time datetime="2026-01-03T10:37:29Z"
|
|
||||||
const runPattern = new RegExp(`href="/${owner}/${repo}/actions/runs/(\\d+)"`, 'gi');
|
|
||||||
const timePattern = /<relative-time\s+datetime="([^"]+)"/gi;
|
|
||||||
|
|
||||||
// 提取所有时间
|
|
||||||
const times: string[] = [];
|
|
||||||
let timeMatch;
|
|
||||||
while ((timeMatch = timePattern.exec(html)) !== null) {
|
|
||||||
times.push(timeMatch[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const runs: Array<{ id: number; created_at: string; }> = [];
|
|
||||||
const foundIds = new Set<number>();
|
const foundIds = new Set<number>();
|
||||||
let timeIndex = 0;
|
let page = 1;
|
||||||
|
const maxPages = 10; // 防止无限请求,最多翻10页(约250个条目)
|
||||||
let match;
|
|
||||||
while ((match = runPattern.exec(html)) !== null && runs.length < maxRuns) {
|
while (allRuns.length < maxRuns && page <= maxPages) {
|
||||||
const id = parseInt(match[1]);
|
const pageUrl = page > 1 ? `${baseUrl}?page=${page}` : baseUrl;
|
||||||
if (!foundIds.has(id)) {
|
const url = mirrorItem ? buildMirrorUrl(pageUrl, mirrorItem) : pageUrl;
|
||||||
foundIds.add(id);
|
|
||||||
// 尝试获取对应的时间,每个 run 通常有两个时间(桌面和移动端显示)
|
const html = await PromiseTimer(
|
||||||
// 所以每找到一个 run,跳过两个时间
|
RequestUtil.HttpGetText(url),
|
||||||
const created_at = times[timeIndex] || new Date().toISOString();
|
10000
|
||||||
timeIndex += 2; // 跳过两个时间(桌面端和移动端各一个)
|
);
|
||||||
runs.push({
|
|
||||||
id,
|
// 使用 Block 分割策略,更稳健地关联 ID 和时间
|
||||||
created_at,
|
const rows = html.split('<div class="Box-row');
|
||||||
});
|
let foundOnThisPage = 0;
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
// 提取 Run ID 和 Status
|
||||||
|
// <a href="/NapNeko/NapCatQQ/actions/runs/20799940346" ... aria-label="completed successfully: ...">
|
||||||
|
const runMatch = new RegExp(`href="/${owner}/${repo}/actions/runs/(\\d+)"[^>]*aria-label="([^"]*)"`, 'i').exec(row);
|
||||||
|
|
||||||
|
if (!runMatch || !runMatch[1] || !runMatch[2]) continue;
|
||||||
|
|
||||||
|
const id = parseInt(runMatch[1]);
|
||||||
|
const ariaLabel = runMatch[2];
|
||||||
|
const ariaLabelLower = ariaLabel.toLowerCase();
|
||||||
|
|
||||||
|
// 只需要判断 completed
|
||||||
|
if (ariaLabelLower.includes('completed')) {
|
||||||
|
if (!foundIds.has(id)) {
|
||||||
|
// 提取时间 (取 Block 内的第一个 relative-time)
|
||||||
|
const timeMatch = /<relative-time\s+datetime="([^"]+)"/.exec(row);
|
||||||
|
if (timeMatch && timeMatch[1]) {
|
||||||
|
foundIds.add(id);
|
||||||
|
foundOnThisPage++;
|
||||||
|
|
||||||
|
// 优先从 markdown-title class 提取标题
|
||||||
|
let title = '';
|
||||||
|
const titleMatch = /class="[^"]*markdown-title[^"]*"[^>]*>([\s\S]*?)<\/span>/i.exec(row);
|
||||||
|
if (titleMatch && titleMatch[1]) {
|
||||||
|
title = titleMatch[1].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没找到,回退到 aria-label 逻辑
|
||||||
|
if (!title) {
|
||||||
|
title = ariaLabel;
|
||||||
|
const prefixMatch = /^(completed successfully:\s*)/i.exec(title);
|
||||||
|
if (prefixMatch) {
|
||||||
|
title = title.substring(prefixMatch[0].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allRuns.push({
|
||||||
|
id,
|
||||||
|
created_at: timeMatch[1],
|
||||||
|
title: title.trim()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果本页没有找到任何 completed 的 run(但页面可能不为空),或者页面内容太少(可能是最后一页或错误)
|
||||||
|
// 这里简化判断: 如果本页没提取到任何有效数据,就认为没有更多数据了
|
||||||
|
if (foundOnThisPage === 0) {
|
||||||
|
// 也要考虑到可能是页面解析失败或者全是 failed 状态
|
||||||
|
// 检查是否有翻页按钮可能更复杂,暂时假设如果一整页都没有 successful run,可能后面也没有了,或者我们已经获取够多了
|
||||||
|
// 为了稳健,如果本页没找到,且 allRuns 还没满,尝试下一页 (除非页面很小说明是空页)
|
||||||
|
if (rows.length < 2) { // 只有 split 的第一个空元素
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页逻辑:总是尝试下一页,直到满足 maxRuns
|
||||||
|
page++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runs.length > 0) {
|
if (allRuns.length > 0) {
|
||||||
return runs;
|
return { runs: allRuns, mirror: mirrorItem || 'https://github.com' };
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return { runs: [], mirror: '' };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -996,122 +1003,49 @@ async function getArtifactsFromNightlyLink (
|
|||||||
owner: string,
|
owner: string,
|
||||||
repo: string,
|
repo: string,
|
||||||
workflow: string = 'build.yml',
|
workflow: string = 'build.yml',
|
||||||
branch: string = 'main',
|
_branch: string = 'main',
|
||||||
maxRuns: number = 10
|
maxRuns: number = 10,
|
||||||
): Promise<ActionArtifact[]> {
|
mirror?: string
|
||||||
let workflowRuns: Array<{ id: number; head_sha?: string; created_at: string; }> = [];
|
): Promise<{ artifacts: ActionArtifact[], mirror: string; }> {
|
||||||
|
// 策略: 优先使用 nightly.link(更稳定,无需认证)+ HTML 解析
|
||||||
// 策略1: 优先尝试 GitHub API
|
|
||||||
try {
|
try {
|
||||||
const endpoint = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow}/runs?branch=${branch}&status=success&per_page=${maxRuns}`;
|
// 以前尝试使用 GitHub API,现在弃用,完全使用 HTML 解析逻辑
|
||||||
|
// 并获取 workflow // 直接从 HTML 页面解析
|
||||||
const runsResponse = await PromiseTimer(
|
const { runs: workflowRuns, mirror: runsMirror } = await getWorkflowRunsFromHtml(owner, repo, workflow, maxRuns, mirror);
|
||||||
RequestUtil.HttpGetJson<{
|
|
||||||
workflow_runs: Array<{ id: number; head_sha: string; created_at: string; }>;
|
|
||||||
}>(endpoint, 'GET', undefined, {
|
|
||||||
'User-Agent': 'NapCat',
|
|
||||||
'Accept': 'application/vnd.github.v3+json',
|
|
||||||
}),
|
|
||||||
10000
|
|
||||||
);
|
|
||||||
|
|
||||||
if (runsResponse.workflow_runs && runsResponse.workflow_runs.length > 0) {
|
if (workflowRuns.length === 0) {
|
||||||
workflowRuns = runsResponse.workflow_runs;
|
return { artifacts: [], mirror: runsMirror };
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
// API 请求失败,继续尝试 HTML 解析
|
|
||||||
}
|
|
||||||
|
|
||||||
// 策略2: API 失败时,从 HTML 页面解析
|
// 直接拼接 nightly.link URL
|
||||||
if (workflowRuns.length === 0) {
|
// 格式: https://nightly.link/{owner}/{repo}/actions/runs/{run_id}/{artifact_name}.zip
|
||||||
workflowRuns = await getWorkflowRunsFromHtml(owner, repo, workflow, maxRuns);
|
const artifacts: ActionArtifact[] = [];
|
||||||
}
|
const artifactNames = ['NapCat.Framework', 'NapCat.Shell']; // 已知的 artifact 名称
|
||||||
|
|
||||||
if (workflowRuns.length === 0) {
|
// 如果 HTML 解析使用的 mirror 是 github.com(空),则 nightly.link 使用默认配置
|
||||||
return [];
|
// 如果使用了镜像,可能需要特殊的 nightly.link 镜像,或者这里仅记录 HTML 来源镜像
|
||||||
}
|
// 实际上 nightly.link 本身就是一个服务,我们使用配置中的 nightlyLinkMirrors
|
||||||
|
const baseNightlyMirror = currentConfig.nightlyLinkMirrors[0] || 'https://nightly.link';
|
||||||
|
|
||||||
// 直接拼接 nightly.link URL
|
for (const run of workflowRuns) {
|
||||||
// 格式: https://nightly.link/{owner}/{repo}/actions/runs/{run_id}/{artifact_name}.zip
|
for (const artifactName of artifactNames) {
|
||||||
const artifacts: ActionArtifact[] = [];
|
artifacts.push({
|
||||||
const artifactNames = ['NapCat.Framework', 'NapCat.Shell']; // 已知的 artifact 名称
|
id: run.id,
|
||||||
|
name: artifactName,
|
||||||
for (const run of workflowRuns) {
|
size_in_bytes: 0,
|
||||||
for (const artifactName of artifactNames) {
|
created_at: run.created_at,
|
||||||
const mirror = currentConfig.nightlyLinkMirrors[0] || 'https://nightly.link';
|
expires_at: new Date(new Date(run.created_at).getTime() + 3 * 24 * 60 * 60 * 1000).toISOString(),
|
||||||
artifacts.push({
|
archive_download_url: `${baseNightlyMirror}/${owner}/${repo}/actions/runs/${run.id}/${artifactName}.zip`,
|
||||||
id: run.id,
|
workflow_run_id: run.id,
|
||||||
name: artifactName,
|
workflow_title: run.title,
|
||||||
size_in_bytes: 0,
|
});
|
||||||
created_at: run.created_at,
|
|
||||||
expires_at: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(),
|
|
||||||
archive_download_url: `${mirror}/${owner}/${repo}/actions/runs/${run.id}/${artifactName}.zip`,
|
|
||||||
workflow_run_id: run.id,
|
|
||||||
head_sha: run.head_sha,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return artifacts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过 GitHub API 获取 artifacts(主要方案)
|
|
||||||
*/
|
|
||||||
async function getArtifactsFromAPI (
|
|
||||||
owner: string,
|
|
||||||
repo: string,
|
|
||||||
workflow: string = 'build.yml',
|
|
||||||
branch: string = 'main',
|
|
||||||
maxRuns: number = 10
|
|
||||||
): Promise<ActionArtifact[]> {
|
|
||||||
const endpoint = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow}/runs?branch=${branch}&status=success&per_page=${maxRuns}`;
|
|
||||||
|
|
||||||
const runsResponse = await PromiseTimer(
|
|
||||||
RequestUtil.HttpGetJson<{
|
|
||||||
workflow_runs: Array<{ id: number; head_sha: string; created_at: string; }>;
|
|
||||||
}>(endpoint, 'GET', undefined, {
|
|
||||||
'User-Agent': 'NapCat',
|
|
||||||
'Accept': 'application/vnd.github.v3+json',
|
|
||||||
}),
|
|
||||||
10000
|
|
||||||
);
|
|
||||||
|
|
||||||
const workflowRuns = runsResponse.workflow_runs;
|
|
||||||
if (!workflowRuns || workflowRuns.length === 0) {
|
|
||||||
throw new Error('No successful workflow runs found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取所有 runs 的 artifacts
|
|
||||||
const allArtifacts: ActionArtifact[] = [];
|
|
||||||
|
|
||||||
for (const run of workflowRuns) {
|
|
||||||
try {
|
|
||||||
const artifactsEndpoint = `https://api.github.com/repos/${owner}/${repo}/actions/runs/${run.id}/artifacts`;
|
|
||||||
const artifactsResponse = await PromiseTimer(
|
|
||||||
RequestUtil.HttpGetJson<{
|
|
||||||
artifacts: ActionArtifact[];
|
|
||||||
}>(artifactsEndpoint, 'GET', undefined, {
|
|
||||||
'User-Agent': 'NapCat',
|
|
||||||
'Accept': 'application/vnd.github.v3+json',
|
|
||||||
}),
|
|
||||||
10000
|
|
||||||
);
|
|
||||||
|
|
||||||
if (artifactsResponse.artifacts) {
|
|
||||||
// 为每个 artifact 添加 run 信息
|
|
||||||
for (const artifact of artifactsResponse.artifacts) {
|
|
||||||
artifact.workflow_run_id = run.id;
|
|
||||||
artifact.head_sha = run.head_sha;
|
|
||||||
allArtifacts.push(artifact);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
// 单个 run 获取失败,继续下一个
|
|
||||||
}
|
}
|
||||||
}
|
return { artifacts, mirror: runsMirror };
|
||||||
|
|
||||||
return allArtifacts;
|
} catch {
|
||||||
|
return { artifacts: [], mirror: '' };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1121,48 +1055,41 @@ async function getArtifactsFromAPI (
|
|||||||
* 策略:
|
* 策略:
|
||||||
* 1. 检查缓存(10分钟有效)
|
* 1. 检查缓存(10分钟有效)
|
||||||
* 2. 优先尝试从 nightly.link 获取(无需认证,更稳定)
|
* 2. 优先尝试从 nightly.link 获取(无需认证,更稳定)
|
||||||
* 3. 如果失败,回退到 GitHub API
|
* 3. 这里的实现已经完全移除了对 GitHub API 的依赖,直接解析 HTML
|
||||||
*/
|
*/
|
||||||
export async function getLatestActionArtifacts (
|
export async function getLatestActionArtifacts (
|
||||||
owner: string,
|
owner: string,
|
||||||
repo: string,
|
repo: string,
|
||||||
workflow: string = 'build.yml',
|
workflow: string = 'build.yml',
|
||||||
branch: string = 'main',
|
branch: string = 'main',
|
||||||
maxRuns: number = 10
|
maxRuns: number = 10,
|
||||||
): Promise<ActionArtifact[]> {
|
mirror?: string
|
||||||
const cacheKey = `${owner}/${repo}/${workflow}/${branch}`;
|
): Promise<{ artifacts: ActionArtifact[], mirror: string; }> {
|
||||||
|
const cacheKey = `${owner}/${repo}/${workflow}/${branch}/${mirror || 'auto'}`;
|
||||||
|
|
||||||
// 检查缓存
|
// 检查缓存
|
||||||
const cached = artifactsCache.get(cacheKey);
|
const cached = artifactsCache.get(cacheKey);
|
||||||
if (cached && (Date.now() - cached.timestamp) < ARTIFACTS_CACHE_TTL) {
|
if (cached && (Date.now() - cached.timestamp) < ARTIFACTS_CACHE_TTL) {
|
||||||
return cached.artifacts;
|
return { artifacts: cached.artifacts, mirror: cached.mirror };
|
||||||
}
|
}
|
||||||
|
|
||||||
let artifacts: ActionArtifact[] = [];
|
let result: { artifacts: ActionArtifact[], mirror: string; } = { artifacts: [], mirror: '' };
|
||||||
|
|
||||||
// 策略1: 优先使用 nightly.link(更稳定,无需认证)
|
// 策略: 优先使用 nightly.link(更稳定,无需认证)+ HTML 解析
|
||||||
try {
|
try {
|
||||||
artifacts = await getArtifactsFromNightlyLink(owner, repo, workflow, branch, maxRuns);
|
result = await getArtifactsFromNightlyLink(owner, repo, workflow, branch, maxRuns, mirror);
|
||||||
} catch {
|
} catch {
|
||||||
// nightly.link 获取失败
|
// 获取失败
|
||||||
}
|
|
||||||
|
|
||||||
// 策略2: 回退到 GitHub API
|
|
||||||
if (artifacts.length === 0) {
|
|
||||||
try {
|
|
||||||
artifacts = await getArtifactsFromAPI(owner, repo, workflow, branch, maxRuns);
|
|
||||||
} catch {
|
|
||||||
// API 获取失败
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 缓存结果(即使为空也缓存,避免频繁请求)
|
// 缓存结果(即使为空也缓存,避免频繁请求)
|
||||||
if (artifacts.length > 0) {
|
if (result.artifacts.length > 0) {
|
||||||
artifactsCache.set(cacheKey, {
|
artifactsCache.set(cacheKey, {
|
||||||
artifacts,
|
artifacts: result.artifacts,
|
||||||
|
mirror: result.mirror,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return artifacts;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,11 @@ export function compareSemVer (v1: string, v2: string): -1 | 0 | 1 {
|
|||||||
const a = parseSemVer(v1);
|
const a = parseSemVer(v1);
|
||||||
const b = parseSemVer(v2);
|
const b = parseSemVer(v2);
|
||||||
|
|
||||||
if (!a.valid || !b.valid) {
|
if (!a.valid && !b.valid) {
|
||||||
return 0;
|
return v1.localeCompare(v2) as -1 | 0 | 1;
|
||||||
}
|
}
|
||||||
|
if (!a.valid) return -1;
|
||||||
|
if (!b.valid) return 1;
|
||||||
|
|
||||||
// 比较主版本号
|
// 比较主版本号
|
||||||
if (a.major !== b.major) return a.major > b.major ? 1 : -1;
|
if (a.major !== b.major) return a.major > b.major ? 1 : -1;
|
||||||
|
|||||||
307
packages/napcat-core/apis/flash.ts
Normal file
307
packages/napcat-core/apis/flash.ts
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
import { GeneralCallResult, InstanceContext, NapCatCore } from '@/napcat-core';
|
||||||
|
import {
|
||||||
|
createFlashTransferResult,
|
||||||
|
FileListResponse,
|
||||||
|
FlashFileSetInfo,
|
||||||
|
SendStatus,
|
||||||
|
UploadSceneType,
|
||||||
|
} from '@/napcat-core/data/flash';
|
||||||
|
import { Peer } from '@/napcat-core/types';
|
||||||
|
|
||||||
|
export class NTQQFlashApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
|
||||||
|
constructor (context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发起闪传上传任务
|
||||||
|
* @param fileListToUpload 上传文件绝对路径的列表,可以是文件夹!!
|
||||||
|
* @param thumbnailPath
|
||||||
|
* @param filesetName
|
||||||
|
*/
|
||||||
|
async createFlashTransferUploadTask (fileListToUpload: string[], thumbnailPath: string, filesetName: string): Promise<GeneralCallResult & {
|
||||||
|
createFlashTransferResult: createFlashTransferResult;
|
||||||
|
seq: number;
|
||||||
|
}> {
|
||||||
|
const flashService = this.context.session.getFlashTransferService();
|
||||||
|
|
||||||
|
const timestamp: number = Date.now();
|
||||||
|
const selfInfo = this.core.selfInfo;
|
||||||
|
|
||||||
|
const fileUploadArg = {
|
||||||
|
screen: 1, // 1
|
||||||
|
name: filesetName,
|
||||||
|
uploaders: [{
|
||||||
|
uin: selfInfo.uin,
|
||||||
|
uid: selfInfo.uid,
|
||||||
|
sendEntrance: '',
|
||||||
|
nickname: selfInfo.nick,
|
||||||
|
}],
|
||||||
|
coverPath: thumbnailPath,
|
||||||
|
paths: fileListToUpload,
|
||||||
|
excludePaths: [],
|
||||||
|
expireLeftTime: 0,
|
||||||
|
isNeedDelDeviceInfo: false,
|
||||||
|
isNeedDelLocation: false,
|
||||||
|
coverOriginalInfos: [
|
||||||
|
{
|
||||||
|
path: fileListToUpload[0] || '',
|
||||||
|
thumbnailPath,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
uploadSceneType: UploadSceneType.KUPLOADSCENEAIOFILESELECTOR, // 不知道怎么枚举 先硬编码吧 (PC QQ 10)
|
||||||
|
detectPrivacyInfoResult: {
|
||||||
|
exists: false,
|
||||||
|
allDetectResults: new Map(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadResult = await flashService.createFlashTransferUploadTask(timestamp, fileUploadArg);
|
||||||
|
if (uploadResult.result === 0) {
|
||||||
|
this.context.logger.log('[Flash] 发起闪传任务成功');
|
||||||
|
return uploadResult;
|
||||||
|
} else {
|
||||||
|
this.context.logger.logError('[Flash] 发起闪传上传任务失败!!');
|
||||||
|
return uploadResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载闪传文件集
|
||||||
|
* @param fileSetId
|
||||||
|
*/
|
||||||
|
async downloadFileSetBySetId (fileSetId: string): Promise<GeneralCallResult & {
|
||||||
|
extraInfo: unknown;
|
||||||
|
}> {
|
||||||
|
const flashService = this.context.session.getFlashTransferService();
|
||||||
|
|
||||||
|
const result = await flashService.startFileSetDownload(fileSetId, 1, { isIncludeCompressInnerFiles: false }); // 为了方便,暂时硬编码
|
||||||
|
if (result.result === 0) {
|
||||||
|
this.context.logger.log('[Flash] 成功开始下载文件集');
|
||||||
|
} else {
|
||||||
|
this.context.logger.logError('[Flash] 尝试下载文件集失败!');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取闪传的外链分享
|
||||||
|
* @param fileSetId
|
||||||
|
*/
|
||||||
|
async getShareLinkBySetId (fileSetId: string): Promise<GeneralCallResult & {
|
||||||
|
shareLink: string;
|
||||||
|
expireTimestamp: string;
|
||||||
|
}> {
|
||||||
|
const flashService = this.context.session.getFlashTransferService();
|
||||||
|
|
||||||
|
const result = await flashService.getShareLinkReq(fileSetId);
|
||||||
|
if (result.result === 0) {
|
||||||
|
this.context.logger.log('[Flash] 获取闪传外链分享成功:', result.shareLink);
|
||||||
|
} else {
|
||||||
|
this.context.logger.logError('[Flash] 获取闪传外链失败!!');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从分享外链获取文件集id
|
||||||
|
* @param shareCode
|
||||||
|
*/
|
||||||
|
async fromShareLinkFindSetId (shareCode: string): Promise<GeneralCallResult & {
|
||||||
|
fileSetId: string;
|
||||||
|
}> {
|
||||||
|
const flashService = this.context.session.getFlashTransferService();
|
||||||
|
|
||||||
|
const result = await flashService.getFileSetIdByCode(shareCode);
|
||||||
|
if (result.result === 0) {
|
||||||
|
this.context.logger.log('[Flash] 获取shareCode的文件集Id成功!');
|
||||||
|
} else {
|
||||||
|
this.context.logger.logError('[Flash] 获取文件集ID失败!!');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取fileSet的文件结构信息 (未来可能需要深度遍历)
|
||||||
|
* == 注意返回结构和其它的不同,没有GeneralCallResult!!! ==
|
||||||
|
* @param fileSetId
|
||||||
|
*/
|
||||||
|
async getFileListBySetId (fileSetId: string): Promise<FileListResponse> {
|
||||||
|
const flashService = this.context.session.getFlashTransferService();
|
||||||
|
|
||||||
|
const requestArg = {
|
||||||
|
seq: 0,
|
||||||
|
fileSetId,
|
||||||
|
isUseCache: false,
|
||||||
|
sceneType: 1, // 硬编码
|
||||||
|
reqInfos: [
|
||||||
|
{
|
||||||
|
count: 18, // 18 ??
|
||||||
|
paginationInfo: {},
|
||||||
|
parentId: '',
|
||||||
|
reqIndexPath: '',
|
||||||
|
reqDepth: 1,
|
||||||
|
filterCondition: {
|
||||||
|
fileCategory: 0,
|
||||||
|
filterType: 0,
|
||||||
|
},
|
||||||
|
sortConditions: [
|
||||||
|
{
|
||||||
|
sortField: 0,
|
||||||
|
sortOrder: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isNeedPhysicalInfoReady: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const result = await flashService.getFileList(requestArg);
|
||||||
|
if (result.rsp.result === 0) {
|
||||||
|
this.context.logger.log('[Flash] 获取fileSet文件信息成功!');
|
||||||
|
return result.rsp;
|
||||||
|
} else {
|
||||||
|
this.context.logger.logError(`[Flash] 获取文件信息失败:ErrMsg: ${result.rsp.errMs}`);
|
||||||
|
return result.rsp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取闪传文件集合信息
|
||||||
|
* @param fileSetId
|
||||||
|
*/
|
||||||
|
async getFileSetIndoBySetId (fileSetId: string): Promise<GeneralCallResult & {
|
||||||
|
seq: number;
|
||||||
|
isCache: boolean;
|
||||||
|
fileSet: FlashFileSetInfo;
|
||||||
|
}> {
|
||||||
|
const flashService = this.context.session.getFlashTransferService();
|
||||||
|
|
||||||
|
const requestArg = {
|
||||||
|
fileSetId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await flashService.getFileSet(requestArg);
|
||||||
|
if (result.result === 0) {
|
||||||
|
this.context.logger.log('[Flash] 获取闪传文件集信息成功!');
|
||||||
|
} else {
|
||||||
|
this.context.logger.logError('[Flash] 获取闪传文件信息失败!!');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送闪传消息(私聊/群聊)
|
||||||
|
* @param fileSetId
|
||||||
|
* @param peer
|
||||||
|
*/
|
||||||
|
async sendFlashMessage (fileSetId: string, peer: Peer): Promise<{
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
rsp: {
|
||||||
|
sendStatus: SendStatus[];
|
||||||
|
};
|
||||||
|
}> {
|
||||||
|
const flashService = this.context.session.getFlashTransferService();
|
||||||
|
|
||||||
|
const target = {
|
||||||
|
destUid: peer.peerUid,
|
||||||
|
destType: peer.chatType,
|
||||||
|
// destUin: peer.peerUin,
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestsArg = {
|
||||||
|
fileSetId,
|
||||||
|
targets: [target],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await flashService.sendFlashTransferMsg(requestsArg);
|
||||||
|
if (result.errCode === 0) {
|
||||||
|
this.context.logger.log('[Flash] 消息发送成功');
|
||||||
|
} else {
|
||||||
|
this.context.logger.logError(`[Flash] 消息发送失败!!原因:${result.errMsg}`);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取闪传文件集中某个文件的下载URL(外链)
|
||||||
|
* @param fileSetId
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
async getFileTransUrl (fileSetId: string, options: { fileName?: string; fileIndex?: number; }): Promise<GeneralCallResult & {
|
||||||
|
transferUrl: string;
|
||||||
|
}> {
|
||||||
|
const flashService = this.context.session.getFlashTransferService();
|
||||||
|
const result = await this.getFileListBySetId(fileSetId);
|
||||||
|
|
||||||
|
const { fileName, fileIndex } = options;
|
||||||
|
|
||||||
|
let targetFile: any;
|
||||||
|
let file: any;
|
||||||
|
|
||||||
|
const allFolder = result.fileLists;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-labels
|
||||||
|
searchLoop: for (const folder of allFolder) {
|
||||||
|
const fileList = folder.fileList;
|
||||||
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
|
file = fileList[i];
|
||||||
|
|
||||||
|
if (fileName !== undefined && file.name === fileName) {
|
||||||
|
targetFile = file;
|
||||||
|
// eslint-disable-next-line no-labels
|
||||||
|
break searchLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileIndex !== undefined && i === fileIndex) {
|
||||||
|
targetFile = file;
|
||||||
|
// eslint-disable-next-line no-labels
|
||||||
|
break searchLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (targetFile === undefined) {
|
||||||
|
this.context.logger.logError('[Flash] 未找到对应文件!!');
|
||||||
|
return {
|
||||||
|
result: -1,
|
||||||
|
errMsg: '未找到对应文件',
|
||||||
|
transferUrl: '',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.context.logger.log('[Flash] 找到对应文件,准备尝试获取传输链接');
|
||||||
|
const res = await flashService.startFileTransferUrl(targetFile);
|
||||||
|
return {
|
||||||
|
result: 0,
|
||||||
|
errMsg: '',
|
||||||
|
transferUrl: res.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createFileThumbnail (filePath: string): Promise<any> {
|
||||||
|
const msgService = this.context.session.getMsgService();
|
||||||
|
const savePath = msgService.getFileThumbSavePathForSend(750, true);
|
||||||
|
|
||||||
|
const result = await this.core.util.createThumbnailImage(
|
||||||
|
'flashtransfer',
|
||||||
|
filePath,
|
||||||
|
savePath,
|
||||||
|
{
|
||||||
|
width: 520,
|
||||||
|
height: 520,
|
||||||
|
},
|
||||||
|
'jpeg',
|
||||||
|
null
|
||||||
|
);
|
||||||
|
if (result.result === 0) {
|
||||||
|
this.context.logger.log('获取缩略图成功!!');
|
||||||
|
result.targetPath = savePath;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ export class NTQQFriendApi {
|
|||||||
return retMap;
|
return retMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
async delBuudy (uid: string, tempBlock = false, tempBothDel = false) {
|
async delBuddy (uid: string, tempBlock = false, tempBothDel = false) {
|
||||||
return this.context.session.getBuddyService().delBuddy({
|
return this.context.session.getBuddyService().delBuddy({
|
||||||
friendUid: uid,
|
friendUid: uid,
|
||||||
tempBlock,
|
tempBlock,
|
||||||
|
|||||||
@@ -7,3 +7,5 @@ export * from './webapi';
|
|||||||
export * from './system';
|
export * from './system';
|
||||||
export * from './packet';
|
export * from './packet';
|
||||||
export * from './file';
|
export * from './file';
|
||||||
|
export * from './online';
|
||||||
|
export * from './flash';
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ export class NTQQMsgApi {
|
|||||||
return this.context.session.getMsgService().getSourceOfReplyMsgV2(peer, clientSeq, time);
|
return this.context.session.getMsgService().getSourceOfReplyMsgV2(peer, clientSeq, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMsgEmojiLikesList (peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
|
async getMsgEmojiLikesList (peer: Peer, msgSeq: string, emojiId: string, emojiType: string, cookie: string = '', count: number = 20) {
|
||||||
// 注意此处emojiType 可选值一般为1-2 2好像是unicode表情dec值 大部分情况 Taged Mlikiowa
|
// 注意此处emojiType 可选值一般为1-2 2好像是unicode表情dec值 大部分情况 Taged Mlikiowa
|
||||||
return this.context.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, '', false, count);
|
return this.context.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, cookie, false, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setEmojiLike (peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
|
async setEmojiLike (peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
|
||||||
|
|||||||
241
packages/napcat-core/apis/online.ts
Normal file
241
packages/napcat-core/apis/online.ts
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
import { InstanceContext, NapCatCore } from '@/napcat-core';
|
||||||
|
import { Peer } from '@/napcat-core/types';
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
import * as path from 'node:path';
|
||||||
|
import { GeneralCallResultStatus } from '@/napcat-core/services/common';
|
||||||
|
import { sleep } from '@/napcat-common/src/helper';
|
||||||
|
|
||||||
|
const normalizePath = (p: string) => path.normalize(p).toLowerCase();
|
||||||
|
|
||||||
|
export class NTQQOnlineApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
|
||||||
|
constructor (context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这里不等待node返回,因为the fuck wrapper.node 根本不返回(会卡死不知道为什么)!!! 只能手动查询判断死活
|
||||||
|
* @param peer
|
||||||
|
* @param filePath
|
||||||
|
* @param fileName
|
||||||
|
*/
|
||||||
|
async sendOnlineFile (peer: Peer, filePath: string, fileName: string): Promise<any> {
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
throw new Error(`[NapCat] 文件不存在: ${filePath}`);
|
||||||
|
}
|
||||||
|
const actualFileName = fileName || path.basename(filePath);
|
||||||
|
const fileSize = fs.statSync(filePath).size.toString();
|
||||||
|
|
||||||
|
const fileElementToSend = [{
|
||||||
|
elementType: 23,
|
||||||
|
elementId: '',
|
||||||
|
fileElement: {
|
||||||
|
fileName: actualFileName,
|
||||||
|
filePath,
|
||||||
|
fileSize,
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
|
||||||
|
const msgService = this.context.session.getMsgService();
|
||||||
|
const startTime = Math.floor(Date.now() / 1000) - 2; // 容错时间窗口
|
||||||
|
|
||||||
|
msgService.sendMsg('0', peer, fileElementToSend, new Map()).catch((_e: any) => {
|
||||||
|
});
|
||||||
|
|
||||||
|
const maxRetries = 10;
|
||||||
|
let retryCount = 0;
|
||||||
|
|
||||||
|
while (retryCount < maxRetries) {
|
||||||
|
await sleep(1000);
|
||||||
|
retryCount++;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const msgListResult = await msgService.getOnlineFileMsgs(peer);
|
||||||
|
|
||||||
|
const msgs = msgListResult?.msgList || [];
|
||||||
|
|
||||||
|
const foundMsg = msgs.find((msg: any) => {
|
||||||
|
if (parseInt(msg.msgTime) < startTime) return false;
|
||||||
|
|
||||||
|
const validElement = msg.elements.find((el: any) => {
|
||||||
|
if (el.elementType !== 23 || !el.fileElement) return false;
|
||||||
|
|
||||||
|
const isNameMatch = el.fileElement.fileName === actualFileName;
|
||||||
|
const isPathMatch = normalizePath(el.fileElement.filePath) === normalizePath(filePath);
|
||||||
|
|
||||||
|
return isNameMatch && isPathMatch;
|
||||||
|
});
|
||||||
|
|
||||||
|
return !!validElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (foundMsg) {
|
||||||
|
const targetElement = foundMsg.elements.find((el: any) => el.elementType === 23);
|
||||||
|
this.context.logger.log('[OnlineFile] 在线文件发送成功!');
|
||||||
|
return {
|
||||||
|
result: GeneralCallResultStatus.OK,
|
||||||
|
errMsg: '',
|
||||||
|
msgId: foundMsg.msgId,
|
||||||
|
elementId: targetElement?.elementId || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (_e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.context.logger.logError('[OnlineFile] 在线文件发送失败!!!');
|
||||||
|
return {
|
||||||
|
result: GeneralCallResultStatus.ERROR,
|
||||||
|
errMsg: '[NapCat] Send Online File Timeout: Message not found in history.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送在线文件夹
|
||||||
|
* @param peer
|
||||||
|
* @param folderPath
|
||||||
|
* @param folderName
|
||||||
|
*/
|
||||||
|
async sendOnlineFolder (peer: Peer, folderPath: string, folderName?: string): Promise<any> {
|
||||||
|
const actualFolderName = folderName || path.basename(folderPath);
|
||||||
|
|
||||||
|
if (!fs.existsSync(folderPath)) {
|
||||||
|
return { result: GeneralCallResultStatus.ERROR, errMsg: `Folder not found: ${folderPath}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.statSync(folderPath).isDirectory()) {
|
||||||
|
return { result: GeneralCallResultStatus.ERROR, errMsg: `Path is not a directory: ${folderPath}` };
|
||||||
|
}
|
||||||
|
const folderElementItem = {
|
||||||
|
elementType: 30,
|
||||||
|
elementId: '',
|
||||||
|
fileElement: {
|
||||||
|
fileName: actualFolderName,
|
||||||
|
filePath: folderPath,
|
||||||
|
fileSize: "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const msgService = this.context.session.getMsgService();
|
||||||
|
const startTime = Math.floor(Date.now() / 1000) - 2;
|
||||||
|
msgService.sendMsg('0', peer, [folderElementItem], new Map()).catch((_e: any) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const maxRetries = 10;
|
||||||
|
let retryCount = 0;
|
||||||
|
|
||||||
|
while (retryCount < maxRetries) {
|
||||||
|
await sleep(1000);
|
||||||
|
retryCount++;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const msgListResult = await msgService.getOnlineFileMsgs(peer);
|
||||||
|
const msgs = msgListResult?.msgList || [];
|
||||||
|
|
||||||
|
const foundMsg = msgs.find((msg: any) => {
|
||||||
|
if (parseInt(msg.msgTime) < startTime) return false;
|
||||||
|
|
||||||
|
const validElement = msg.elements.find((el: any) => {
|
||||||
|
if (el.elementType !== 30 || !el.fileElement) return false;
|
||||||
|
|
||||||
|
const isNameMatch = el.fileElement.fileName === actualFolderName;
|
||||||
|
const isPathMatch = normalizePath(el.fileElement.filePath) === normalizePath(folderPath);
|
||||||
|
|
||||||
|
return isNameMatch && isPathMatch;
|
||||||
|
});
|
||||||
|
return !!validElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (foundMsg) {
|
||||||
|
const targetElement = foundMsg.elements.find((el: any) => el.elementType === 30);
|
||||||
|
this.context.logger.log('[OnlineFile] 在线文件夹发送成功!');
|
||||||
|
return {
|
||||||
|
result: GeneralCallResultStatus.OK,
|
||||||
|
errMsg: '',
|
||||||
|
msgId: foundMsg.msgId,
|
||||||
|
elementId: targetElement?.elementId || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (_e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.context.logger.logError('[OnlineFile] 在线文件发送失败!!!');
|
||||||
|
return {
|
||||||
|
result: GeneralCallResultStatus.ERROR,
|
||||||
|
errMsg: '[NapCat] Send Online Folder Timeout: Message not found in history.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取好友的在线文件消息
|
||||||
|
* @param peer
|
||||||
|
*/
|
||||||
|
async getOnlineFileMsg (peer: Peer): Promise<any> {
|
||||||
|
const msgService = this.context.session.getMsgService();
|
||||||
|
return await msgService.getOnlineFileMsgs(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消在线文件的发送
|
||||||
|
* @param peer
|
||||||
|
* @param msgId
|
||||||
|
*/
|
||||||
|
async cancelMyOnlineFileMsg (peer: Peer, msgId: string): Promise<void> {
|
||||||
|
const msgService = this.context.session.getMsgService();
|
||||||
|
await msgService.cancelSendMsg(peer, msgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拒绝接收在线文件
|
||||||
|
* @param peer
|
||||||
|
* @param msgId
|
||||||
|
* @param elementId
|
||||||
|
*/
|
||||||
|
async refuseOnlineFileMsg (peer: Peer, msgId: string, elementId: string): Promise<void> {
|
||||||
|
const msgService = this.context.session.getMsgService();
|
||||||
|
const arrToSend = {
|
||||||
|
msgId,
|
||||||
|
peerUid: peer.peerUid,
|
||||||
|
chatType: 1,
|
||||||
|
elementId,
|
||||||
|
downloadType: 1,
|
||||||
|
downSourceType: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
await msgService.refuseGetRichMediaElement(arrToSend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收在线文件/文件夹
|
||||||
|
* @param peer
|
||||||
|
* @param msgId
|
||||||
|
* @param elementId
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
async receiveOnlineFileOrFolder (peer: Peer, msgId: string, elementId: string): Promise<any> {
|
||||||
|
const msgService = this.context.session.getMsgService();
|
||||||
|
const arrToSend = {
|
||||||
|
msgId,
|
||||||
|
peerUid: peer.peerUid,
|
||||||
|
chatType: 1,
|
||||||
|
elementId,
|
||||||
|
downSourceType: 1,
|
||||||
|
downloadType: 1,
|
||||||
|
};
|
||||||
|
return await msgService.getRichMediaElement(arrToSend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在线文件/文件夹转离线
|
||||||
|
* @param peer
|
||||||
|
* @param msgId
|
||||||
|
*/
|
||||||
|
async switchFileToOffline (peer: Peer, msgId: string): Promise<void> {
|
||||||
|
const msgService = this.context.session.getMsgService();
|
||||||
|
await msgService.switchToOfflineSendMsg(peer, msgId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,17 @@ import { createHash } from 'node:crypto';
|
|||||||
import { basename } from 'node:path';
|
import { basename } from 'node:path';
|
||||||
import { qunAlbumControl } from '../data/webapi';
|
import { qunAlbumControl } from '../data/webapi';
|
||||||
import { createAlbumCommentRequest, createAlbumFeedPublish, createAlbumMediaFeed } from '../data/album';
|
import { createAlbumCommentRequest, createAlbumFeedPublish, createAlbumMediaFeed } from '../data/album';
|
||||||
|
export interface SetNoticeRetSuccess {
|
||||||
|
ec: number;
|
||||||
|
em: string;
|
||||||
|
id: number;
|
||||||
|
ltsm: number;
|
||||||
|
new_fid: string;
|
||||||
|
read_only: number;
|
||||||
|
role: number;
|
||||||
|
srv_code: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class NTQQWebApi {
|
export class NTQQWebApi {
|
||||||
context: InstanceContext;
|
context: InstanceContext;
|
||||||
core: NapCatCore;
|
core: NapCatCore;
|
||||||
@@ -25,12 +36,12 @@ export class NTQQWebApi {
|
|||||||
async shareDigest (groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
|
async shareDigest (groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
|
||||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${new URLSearchParams({
|
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${new URLSearchParams({
|
||||||
bkn: this.getBknFromCookie(cookieObject),
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
group_code: groupCode,
|
group_code: groupCode,
|
||||||
msg_seq: msgSeq,
|
msg_seq: msgSeq,
|
||||||
msg_random: msgRandom,
|
msg_random: msgRandom,
|
||||||
target_group_code: targetGroupCode,
|
target_group_code: targetGroupCode,
|
||||||
}).toString()}`;
|
}).toString()}`;
|
||||||
try {
|
try {
|
||||||
return RequestUtil.HttpGetText(url, 'GET', '', { Cookie: this.cookieToString(cookieObject) });
|
return RequestUtil.HttpGetText(url, 'GET', '', { Cookie: this.cookieToString(cookieObject) });
|
||||||
} catch {
|
} catch {
|
||||||
@@ -52,11 +63,11 @@ export class NTQQWebApi {
|
|||||||
async getGroupEssenceMsg (GroupCode: string, page_start: number = 0, page_limit: number = 50) {
|
async getGroupEssenceMsg (GroupCode: string, page_start: number = 0, page_limit: number = 50) {
|
||||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${new URLSearchParams({
|
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${new URLSearchParams({
|
||||||
bkn: this.getBknFromCookie(cookieObject),
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
page_start: page_start.toString(),
|
page_start: page_start.toString(),
|
||||||
page_limit: page_limit.toString(),
|
page_limit: page_limit.toString(),
|
||||||
group_code: GroupCode,
|
group_code: GroupCode,
|
||||||
}).toString()}`;
|
}).toString()}`;
|
||||||
try {
|
try {
|
||||||
const ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet>(
|
const ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet>(
|
||||||
url,
|
url,
|
||||||
@@ -76,16 +87,16 @@ export class NTQQWebApi {
|
|||||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
const retList: Promise<WebApiGroupMemberRet>[] = [];
|
const retList: Promise<WebApiGroupMemberRet>[] = [];
|
||||||
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>(
|
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>(
|
||||||
`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
|
`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
|
||||||
st: '0',
|
st: '0',
|
||||||
end: '40',
|
end: '40',
|
||||||
sort: '1',
|
sort: '1',
|
||||||
gc: GroupCode,
|
gc: GroupCode,
|
||||||
bkn: this.getBknFromCookie(cookieObject),
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
}).toString()}`,
|
}).toString()}`,
|
||||||
'POST',
|
'POST',
|
||||||
'',
|
'',
|
||||||
{ Cookie: this.cookieToString(cookieObject) }
|
{ Cookie: this.cookieToString(cookieObject) }
|
||||||
);
|
);
|
||||||
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
|
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
|
||||||
return [];
|
return [];
|
||||||
@@ -101,16 +112,16 @@ export class NTQQWebApi {
|
|||||||
// 遍历批量请求
|
// 遍历批量请求
|
||||||
for (let i = 2; i <= PageNum; i++) {
|
for (let i = 2; i <= PageNum; i++) {
|
||||||
const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>(
|
const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>(
|
||||||
`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
|
`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
|
||||||
st: ((i - 1) * 40).toString(),
|
st: ((i - 1) * 40).toString(),
|
||||||
end: (i * 40).toString(),
|
end: (i * 40).toString(),
|
||||||
sort: '1',
|
sort: '1',
|
||||||
gc: GroupCode,
|
gc: GroupCode,
|
||||||
bkn: this.getBknFromCookie(cookieObject),
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
}).toString()}`,
|
}).toString()}`,
|
||||||
'POST',
|
'POST',
|
||||||
'',
|
'',
|
||||||
{ Cookie: this.cookieToString(cookieObject) }
|
{ Cookie: this.cookieToString(cookieObject) }
|
||||||
);
|
);
|
||||||
retList.push(ret);
|
retList.push(ret);
|
||||||
}
|
}
|
||||||
@@ -153,16 +164,7 @@ export class NTQQWebApi {
|
|||||||
imgWidth: number = 540,
|
imgWidth: number = 540,
|
||||||
imgHeight: number = 300
|
imgHeight: number = 300
|
||||||
) {
|
) {
|
||||||
interface SetNoticeRetSuccess {
|
|
||||||
ec: number;
|
|
||||||
em: string;
|
|
||||||
id: number;
|
|
||||||
ltsm: number;
|
|
||||||
new_fid: string;
|
|
||||||
read_only: number;
|
|
||||||
role: number;
|
|
||||||
srv_code: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
|
|
||||||
@@ -178,18 +180,18 @@ export class NTQQWebApi {
|
|||||||
imgHeight: imgHeight.toString(),
|
imgHeight: imgHeight.toString(),
|
||||||
};
|
};
|
||||||
const ret: SetNoticeRetSuccess = await RequestUtil.HttpGetJson<SetNoticeRetSuccess>(
|
const ret: SetNoticeRetSuccess = await RequestUtil.HttpGetJson<SetNoticeRetSuccess>(
|
||||||
`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?${new URLSearchParams({
|
`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?${new URLSearchParams({
|
||||||
bkn: this.getBknFromCookie(cookieObject),
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
qid: GroupCode,
|
qid: GroupCode,
|
||||||
text: Content,
|
text: Content,
|
||||||
pinned: pinned.toString(),
|
pinned: pinned.toString(),
|
||||||
type: type.toString(),
|
type: type.toString(),
|
||||||
settings,
|
settings,
|
||||||
...(picId === '' ? {} : externalParam),
|
...(picId === '' ? {} : externalParam),
|
||||||
}).toString()}`,
|
}).toString()}`,
|
||||||
'POST',
|
'POST',
|
||||||
'',
|
'',
|
||||||
{ Cookie: this.cookieToString(cookieObject) }
|
{ Cookie: this.cookieToString(cookieObject) }
|
||||||
);
|
);
|
||||||
return ret;
|
return ret;
|
||||||
} catch {
|
} catch {
|
||||||
@@ -201,20 +203,20 @@ export class NTQQWebApi {
|
|||||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
try {
|
try {
|
||||||
const ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(
|
const ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(
|
||||||
`https://web.qun.qq.com/cgi-bin/announce/get_t_list?${new URLSearchParams({
|
`https://web.qun.qq.com/cgi-bin/announce/get_t_list?${new URLSearchParams({
|
||||||
bkn: this.getBknFromCookie(cookieObject),
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
qid: GroupCode,
|
qid: GroupCode,
|
||||||
ft: '23',
|
ft: '23',
|
||||||
ni: '1',
|
ni: '1',
|
||||||
n: '1',
|
n: '1',
|
||||||
i: '1',
|
i: '1',
|
||||||
log_read: '1',
|
log_read: '1',
|
||||||
platform: '1',
|
platform: '1',
|
||||||
s: '-1',
|
s: '-1',
|
||||||
}).toString()}&n=20`,
|
}).toString()}&n=20`,
|
||||||
'GET',
|
'GET',
|
||||||
'',
|
'',
|
||||||
{ Cookie: this.cookieToString(cookieObject) }
|
{ Cookie: this.cookieToString(cookieObject) }
|
||||||
);
|
);
|
||||||
return ret?.ec === 0 ? ret : undefined;
|
return ret?.ec === 0 ? ret : undefined;
|
||||||
} catch {
|
} catch {
|
||||||
@@ -222,17 +224,17 @@ export class NTQQWebApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getDataInternal (cookieObject: { [key: string]: string }, groupCode: string, type: number) {
|
private async getDataInternal (cookieObject: { [key: string]: string; }, groupCode: string, type: number) {
|
||||||
let resJson;
|
let resJson;
|
||||||
try {
|
try {
|
||||||
const res = await RequestUtil.HttpGetText(
|
const res = await RequestUtil.HttpGetText(
|
||||||
`https://qun.qq.com/interactive/honorlist?${new URLSearchParams({
|
`https://qun.qq.com/interactive/honorlist?${new URLSearchParams({
|
||||||
gc: groupCode,
|
gc: groupCode,
|
||||||
type: type.toString(),
|
type: type.toString(),
|
||||||
}).toString()}`,
|
}).toString()}`,
|
||||||
'GET',
|
'GET',
|
||||||
'',
|
'',
|
||||||
{ Cookie: this.cookieToString(cookieObject) }
|
{ Cookie: this.cookieToString(cookieObject) }
|
||||||
);
|
);
|
||||||
const match = /window\.__INITIAL_STATE__=(.*?);/.exec(res);
|
const match = /window\.__INITIAL_STATE__=(.*?);/.exec(res);
|
||||||
if (match?.[1]) {
|
if (match?.[1]) {
|
||||||
@@ -245,7 +247,7 @@ export class NTQQWebApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getHonorList (cookieObject: { [key: string]: string }, groupCode: string, type: number) {
|
private async getHonorList (cookieObject: { [key: string]: string; }, groupCode: string, type: number) {
|
||||||
const data = await this.getDataInternal(cookieObject, groupCode, type);
|
const data = await this.getDataInternal(cookieObject, groupCode, type);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
this.context.logger.logError(`获取类型 ${type} 的荣誉信息失败`);
|
this.context.logger.logError(`获取类型 ${type} 的荣誉信息失败`);
|
||||||
@@ -304,11 +306,11 @@ export class NTQQWebApi {
|
|||||||
return HonorInfo;
|
return HonorInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private cookieToString (cookieObject: { [key: string]: string }) {
|
private cookieToString (cookieObject: { [key: string]: string; }) {
|
||||||
return Object.entries(cookieObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
return Object.entries(cookieObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBknFromCookie (cookieObject: { [key: string]: string }) {
|
public getBknFromCookie (cookieObject: { [key: string]: string; }) {
|
||||||
const sKey = cookieObject['skey'] as string;
|
const sKey = cookieObject['skey'] as string;
|
||||||
|
|
||||||
let hash = 5381;
|
let hash = 5381;
|
||||||
@@ -361,7 +363,7 @@ export class NTQQWebApi {
|
|||||||
uin,
|
uin,
|
||||||
getMemberRole: '0',
|
getMemberRole: '0',
|
||||||
});
|
});
|
||||||
const response = await RequestUtil.HttpGetJson<{ data: { album: Array<{ id: string, title: string }> } }>(api + params.toString(), 'GET', '', {
|
const response = await RequestUtil.HttpGetJson<{ data: { album: Array<{ id: string, title: string; }>; }; }>(api + params.toString(), 'GET', '', {
|
||||||
Cookie: cookies,
|
Cookie: cookies,
|
||||||
});
|
});
|
||||||
return response.data.album;
|
return response.data.album;
|
||||||
@@ -384,7 +386,7 @@ export class NTQQWebApi {
|
|||||||
sAlbumID,
|
sAlbumID,
|
||||||
});
|
});
|
||||||
const api = `https://h5.qzone.qq.com/webapp/json/sliceUpload/FileBatchControl/${img_md5}?g_tk=${GTK}`;
|
const api = `https://h5.qzone.qq.com/webapp/json/sliceUpload/FileBatchControl/${img_md5}?g_tk=${GTK}`;
|
||||||
const post = await RequestUtil.HttpGetJson<{ data: { session: string }, ret: number, msg: string }>(api, 'POST', body, {
|
const post = await RequestUtil.HttpGetJson<{ data: { session: string; }, ret: number, msg: string; }>(api, 'POST', body, {
|
||||||
Cookie: cookie,
|
Cookie: cookie,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
});
|
});
|
||||||
@@ -430,7 +432,7 @@ export class NTQQWebApi {
|
|||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const post = await response.json() as { ret: number, msg: string }; if (post.ret !== 0) {
|
const post = await response.json() as { ret: number, msg: string; }; if (post.ret !== 0) {
|
||||||
throw new Error(`分片 ${seq} 上传失败: ${post.msg}`);
|
throw new Error(`分片 ${seq} 上传失败: ${post.msg}`);
|
||||||
}
|
}
|
||||||
offset += chunk.length;
|
offset += chunk.length;
|
||||||
@@ -475,10 +477,10 @@ export class NTQQWebApi {
|
|||||||
const client_key = Date.now() * 1000;
|
const client_key = Date.now() * 1000;
|
||||||
return await this.context.session.getAlbumService().doQunComment(
|
return await this.context.session.getAlbumService().doQunComment(
|
||||||
random_seq, {
|
random_seq, {
|
||||||
map_info: [],
|
map_info: [],
|
||||||
map_bytes_info: [],
|
map_bytes_info: [],
|
||||||
map_user_account: [],
|
map_user_account: [],
|
||||||
},
|
},
|
||||||
qunId,
|
qunId,
|
||||||
2,
|
2,
|
||||||
createAlbumMediaFeed(uin, albumId, lloc),
|
createAlbumMediaFeed(uin, albumId, lloc),
|
||||||
@@ -509,13 +511,13 @@ export class NTQQWebApi {
|
|||||||
const uin = this.core.selfInfo.uin || '10001';
|
const uin = this.core.selfInfo.uin || '10001';
|
||||||
return await this.context.session.getAlbumService().doQunLike(
|
return await this.context.session.getAlbumService().doQunLike(
|
||||||
random_seq, {
|
random_seq, {
|
||||||
map_info: [],
|
map_info: [],
|
||||||
map_bytes_info: [],
|
map_bytes_info: [],
|
||||||
map_user_account: [],
|
map_user_account: [],
|
||||||
}, {
|
}, {
|
||||||
id,
|
id,
|
||||||
status: 1,
|
status: 1,
|
||||||
},
|
},
|
||||||
createAlbumFeedPublish(qunId, uin, albumId, lloc)
|
createAlbumFeedPublish(qunId, uin, albumId, lloc)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
358
packages/napcat-core/data/flash.ts
Normal file
358
packages/napcat-core/data/flash.ts
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
export interface FlashBaseRequest {
|
||||||
|
fileSetId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UploaderInfo {
|
||||||
|
uin: string,
|
||||||
|
nickname: string,
|
||||||
|
uid: string,
|
||||||
|
sendEntrance: string, // ""
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface thumbnailInfo {
|
||||||
|
id: string,
|
||||||
|
url: {
|
||||||
|
spec: number,
|
||||||
|
uri: string,
|
||||||
|
}[],
|
||||||
|
localCachePath: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendTarget {
|
||||||
|
destType: number; // 1私聊
|
||||||
|
destUin?: string,
|
||||||
|
destUid: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendTargetRequests {
|
||||||
|
fileSetId: string;
|
||||||
|
targets: SendTarget[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DownloadStatusInfo {
|
||||||
|
result: number; // 0
|
||||||
|
fileSetId: string;
|
||||||
|
status: number;
|
||||||
|
info: {
|
||||||
|
curDownLoadFailFileNum: number,
|
||||||
|
curDownLoadedPauseFileNum: number,
|
||||||
|
curDownLoadedFileNum: number,
|
||||||
|
curRealDownLoadedFileNum: number,
|
||||||
|
curDownloadingFileNum: number,
|
||||||
|
totalDownLoadedFileNum: number,
|
||||||
|
curDownLoadedBytes: string, // "0"
|
||||||
|
totalDownLoadedBytes: string,
|
||||||
|
curSpeedBps: number,
|
||||||
|
avgSpeedBps: number,
|
||||||
|
maxSpeedBps: number,
|
||||||
|
remainDownLoadSeconds: number,
|
||||||
|
failFileIdList: [],
|
||||||
|
allFileIdList: [],
|
||||||
|
hasNormalFileDownloading: boolean,
|
||||||
|
onlyCompressInnerFileDownloading: boolean,
|
||||||
|
isAllFileAlreadyDownloaded: boolean,
|
||||||
|
saveFileSetDir: string,
|
||||||
|
allWaitingStatusTask: boolean,
|
||||||
|
downloadSceneType: DownloadSceneType,
|
||||||
|
retryCount: number,
|
||||||
|
statisticInfo: {
|
||||||
|
downloadTaskId: string,
|
||||||
|
downloadFilesetName: string,
|
||||||
|
downloadFileTypeDistribution: string,
|
||||||
|
downloadFileSizeDistribution: string;
|
||||||
|
},
|
||||||
|
albumStorageFailImageNum: number,
|
||||||
|
albumStorageFailVideoNum: number,
|
||||||
|
albumStorageFailFileIdList: [],
|
||||||
|
albumStorageSucImageNum: number,
|
||||||
|
albumStorageSucVideoNum: number,
|
||||||
|
albumStorageSucFileIdList: [],
|
||||||
|
albumStorageFileNum: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface physicalInfo {
|
||||||
|
id: string,
|
||||||
|
url: string,
|
||||||
|
status: number, // 2 已下载
|
||||||
|
processing: string,
|
||||||
|
localPath: string,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
time: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface downloadInfo {
|
||||||
|
status: number,
|
||||||
|
curDownLoadBytes: string,
|
||||||
|
totalFileBytes: string,
|
||||||
|
errorCode: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface uploadInfo {
|
||||||
|
uploadedBytes: string,
|
||||||
|
errorCode: number,
|
||||||
|
svrRrrCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
isNeedDelDeviceInfo: boolean,
|
||||||
|
thumbnailUploadState: number;
|
||||||
|
isSecondHit: boolean,
|
||||||
|
hasModifiedErr: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface folderUploadInfo {
|
||||||
|
totalUploadedFileSize: string;
|
||||||
|
successCount: number;
|
||||||
|
failedCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface folderDownloadInfo {
|
||||||
|
totalDownloadedFileSize: string;
|
||||||
|
totalFileSize: string;
|
||||||
|
totalDownloadFileCount: number;
|
||||||
|
successCount: number;
|
||||||
|
failedCount: number;
|
||||||
|
pausedCount: number;
|
||||||
|
cancelCount: number;
|
||||||
|
downloadingCount: number;
|
||||||
|
partialDownloadCount: number;
|
||||||
|
curLevelDownloadedFileCount: number;
|
||||||
|
curLevelUnDownloadedFileCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface compressFileFolderInfo {
|
||||||
|
downloadStatus: number;
|
||||||
|
saveFileDirPath: string;
|
||||||
|
totalFileCount: string;
|
||||||
|
totalFileSize: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface albumStorgeInfo {
|
||||||
|
status: number;
|
||||||
|
localIdentifier: string;
|
||||||
|
errorCode: number;
|
||||||
|
timeCost: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlashOneFileInfo {
|
||||||
|
fileSetId: string;
|
||||||
|
cliFileId: string; // client?? 或许可以换取url
|
||||||
|
compressedFileFolderId: string;
|
||||||
|
archiveIndex: 0;
|
||||||
|
indexPath: string;
|
||||||
|
isDir: boolean; // 文件或者文件夹!!
|
||||||
|
parentId: string;
|
||||||
|
depth: number; // 1
|
||||||
|
cliFileIndex: number;
|
||||||
|
fileType: number; // 枚举!! 已完成枚举!!
|
||||||
|
name: string;
|
||||||
|
namePinyin: string;
|
||||||
|
isCover: boolean;
|
||||||
|
isCoverOriginal: boolean;
|
||||||
|
fileSize: string;
|
||||||
|
fileCount: number;
|
||||||
|
thumbnail: thumbnailInfo;
|
||||||
|
physical: physicalInfo;
|
||||||
|
srvFileId: string; // service?? 服务器上面的id吗?
|
||||||
|
srvParentFileId: string;
|
||||||
|
svrLastUpdateTimestamp: string;
|
||||||
|
downloadInfo: downloadInfo;
|
||||||
|
saveFilePath: string;
|
||||||
|
search_relative_path: string;
|
||||||
|
disk_relative_path: string;
|
||||||
|
uploadInfo: uploadInfo;
|
||||||
|
status: number;
|
||||||
|
uploadStatus: number; // 3已上传成功
|
||||||
|
downloadStatus: number; // 0未下载
|
||||||
|
folderUploadInfo: folderUploadInfo;
|
||||||
|
folderDownloadInfo: folderDownloadInfo;
|
||||||
|
sha1: string;
|
||||||
|
bookmark: string;
|
||||||
|
compressFileFolderInfo: compressFileFolderInfo;
|
||||||
|
uploadPauseReason: string;
|
||||||
|
downloadPauseReason: string;
|
||||||
|
filePhysicalSize: string;
|
||||||
|
thumbnail_sha1: string | null;
|
||||||
|
thumbnail_size: string | null;
|
||||||
|
needAlbumStorage: boolean;
|
||||||
|
albumStorageInfo: albumStorgeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface fileListsInfo {
|
||||||
|
parentId: string,
|
||||||
|
depth: number, // 1
|
||||||
|
fileList: FlashOneFileInfo[],
|
||||||
|
paginationInfo: {};
|
||||||
|
isEnd: boolean,
|
||||||
|
isCache: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileListResponse {
|
||||||
|
seq: number,
|
||||||
|
result: number,
|
||||||
|
errMs: string,
|
||||||
|
fileLists: fileListsInfo[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface createFlashTransferResult {
|
||||||
|
fileSetId: string,
|
||||||
|
shareLink: string,
|
||||||
|
expireTime: string,
|
||||||
|
expireLeftTime: string,
|
||||||
|
}
|
||||||
|
export enum UploadSceneType {
|
||||||
|
KUPLOADSCENEUNKNOWN,
|
||||||
|
KUPLOADSCENEFLOATWINDOWRIGHTCLICKMENU,
|
||||||
|
KUPLOADSCENEFLOATWINDOWDRAG,
|
||||||
|
KUPLOADSCENEFLOATWINDOWFILESELECTOR,
|
||||||
|
KUPLOADSCENEFLOATWINDOWSHORTCUTKEYCTRLCV,
|
||||||
|
KUPLOADSCENEH5LAUNCHCLIENTRIGHTCLICKMENU,
|
||||||
|
KUPLOADSCENEH5LAUNCHCLIENTDRAG,
|
||||||
|
KUPLOADSCENEH5LAUNCHCLIENTFILESELECTOR,
|
||||||
|
KUPLOADSCENEH5LAUNCHCLIENTSHORTCUTKEYCTRLCV,
|
||||||
|
KUPLOADSCENEAIODRAG,
|
||||||
|
KUPLOADSCENEAIOFILESELECTOR,
|
||||||
|
KUPLOADSCENEAIOSHORTCUTKEYCTRLCV
|
||||||
|
}
|
||||||
|
export interface StartFlashTaskRequests {
|
||||||
|
screen: number; // 1 PC-QQ
|
||||||
|
name?: string;
|
||||||
|
uploaders: UploaderInfo[];
|
||||||
|
permission?: {};
|
||||||
|
coverPath?: string;
|
||||||
|
paths: string[]; // 文件的绝对路径,可以是文件夹
|
||||||
|
excludePaths?: string[];
|
||||||
|
expireLeftTime?: number, // 0
|
||||||
|
isNeedDelDeviceInfo: boolean,
|
||||||
|
isNeedDelLocation: boolean,
|
||||||
|
coverOriginalInfos?: {
|
||||||
|
path: string,
|
||||||
|
thumbnailPath: string,
|
||||||
|
}[],
|
||||||
|
uploadSceneType: UploadSceneType, // 不知道怎么枚举 先硬编码吧 (PC QQ 10)
|
||||||
|
detectPrivacyInfoResult: {
|
||||||
|
exists: boolean,
|
||||||
|
allDetectResults: {};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export enum BusiScene {
|
||||||
|
KBUSISCENEINVALID,
|
||||||
|
KBUSISCENEFLASHSCENE
|
||||||
|
}
|
||||||
|
export interface FileListInfoRequests {
|
||||||
|
seq: number, // 0
|
||||||
|
fileSetId: string,
|
||||||
|
isUseCache: boolean,
|
||||||
|
sceneType: BusiScene, // 1
|
||||||
|
reqInfos: {
|
||||||
|
count: number, // 18 ?? 硬编码吧 不懂
|
||||||
|
paginationInfo: {},
|
||||||
|
parentId: string,
|
||||||
|
reqIndexPath: string,
|
||||||
|
reqDepth: number, // 1
|
||||||
|
filterCondition: {
|
||||||
|
fileCategory: number,
|
||||||
|
filterType: number,
|
||||||
|
}, // 0
|
||||||
|
sortConditions: {
|
||||||
|
sortField: number,
|
||||||
|
sortOrder: number,
|
||||||
|
}[],
|
||||||
|
isNeedPhysicalInfoReady: boolean;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
export enum DownloadSceneType {
|
||||||
|
KDOWNLOADSCENEUNKNOWN,
|
||||||
|
KDOWNLOADSCENEARKC2C,
|
||||||
|
KDOWNLOADSCENEARKC2CDETAILPAGE,
|
||||||
|
KDOWNLOADSCENEARKGROUP,
|
||||||
|
KDOWNLOADSCENEARKGROUPDETAILPAGE,
|
||||||
|
KDOWNLOADSCENELINKC2C,
|
||||||
|
KDOWNLOADSCENELINKGROUP,
|
||||||
|
KDOWNLOADSCENELINKCHANNEL,
|
||||||
|
KDOWNLOADSCENELINKTEMPCHAT,
|
||||||
|
KDOWNLOADSCENELINKOTHERINQQ,
|
||||||
|
KDOWNLOADSCENESCANQRCODE,
|
||||||
|
KDWONLOADSCENEFLASHTRANSFERCENTERCLIENT,
|
||||||
|
KDWONLOADSCENEFLASHTRANSFERCENTERSCHEMA
|
||||||
|
}
|
||||||
|
export interface FlashFileSetInfo {
|
||||||
|
fileSetId: string,
|
||||||
|
name: string,
|
||||||
|
namePinyin: string,
|
||||||
|
totalFileCount: number,
|
||||||
|
totalFileSize: number,
|
||||||
|
permission: {},
|
||||||
|
shareInfo: {
|
||||||
|
shareLink: string,
|
||||||
|
extractionCode: string,
|
||||||
|
},
|
||||||
|
cover: {
|
||||||
|
id: string,
|
||||||
|
urls: [
|
||||||
|
{
|
||||||
|
spec: number, // 2
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
],
|
||||||
|
localCachePath: string;
|
||||||
|
},
|
||||||
|
uploaders: [
|
||||||
|
{
|
||||||
|
uin: string,
|
||||||
|
nickname: string,
|
||||||
|
uid: string,
|
||||||
|
sendEntrance: string;
|
||||||
|
}
|
||||||
|
],
|
||||||
|
expireLeftTime: number,
|
||||||
|
aiClusteringStatus: {
|
||||||
|
firstClusteringList: [],
|
||||||
|
shouldPull: boolean;
|
||||||
|
},
|
||||||
|
createTime: number,
|
||||||
|
expireTime: number,
|
||||||
|
firstLevelItemCount: 1,
|
||||||
|
svrLastUpdateTimestamp: 0,
|
||||||
|
taskId: string, // 同 fileSetId
|
||||||
|
uploadInfo: {
|
||||||
|
totalUploadedFileSize: number,
|
||||||
|
successCount: number,
|
||||||
|
failedCount: number;
|
||||||
|
},
|
||||||
|
downloadInfo: {
|
||||||
|
totalDownloadedFileSize: 0,
|
||||||
|
totalFileSize: 0,
|
||||||
|
totalDownloadFileCount: 0,
|
||||||
|
successCount: 0,
|
||||||
|
failedCount: 0,
|
||||||
|
pausedCount: 0,
|
||||||
|
cancelCount: 0,
|
||||||
|
status: 0,
|
||||||
|
curLevelDownloadedFileCount: number,
|
||||||
|
curLevelUnDownloadedFileCount: 0;
|
||||||
|
},
|
||||||
|
transferType: number,
|
||||||
|
isLocalCreate: true,
|
||||||
|
status: number, // todo 枚举全部状态
|
||||||
|
uploadStatus: number, // todo 同上
|
||||||
|
uploadPauseReason: 0,
|
||||||
|
downloadStatus: 0,
|
||||||
|
downloadPauseReason: 0,
|
||||||
|
saveFileSetDir: string,
|
||||||
|
uploadSceneType: UploadSceneType,
|
||||||
|
downloadSceneType: DownloadSceneType, // 0 PC-QQ 103 web
|
||||||
|
retryCount: number,
|
||||||
|
isMergeShareUpload: 0,
|
||||||
|
isRemoveDeviceInfo: boolean,
|
||||||
|
isRemoveLocation: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendStatus {
|
||||||
|
result: number,
|
||||||
|
msg: string,
|
||||||
|
target: {
|
||||||
|
destType: number,
|
||||||
|
destUid: string,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import fs from 'node:fs/promises';
|
|||||||
import { NTMsgAtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/napcat-core/index';
|
import { NTMsgAtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/napcat-core/index';
|
||||||
import { ILogWrapper } from 'napcat-common/src/log-interface';
|
import { ILogWrapper } from 'napcat-common/src/log-interface';
|
||||||
import EventEmitter from 'node:events';
|
import EventEmitter from 'node:events';
|
||||||
|
|
||||||
export enum LogLevel {
|
export enum LogLevel {
|
||||||
DEBUG = 'debug',
|
DEBUG = 'debug',
|
||||||
INFO = 'info',
|
INFO = 'info',
|
||||||
@@ -263,7 +264,13 @@ function msgElementToText (element: MessageElement, msg: RawMessage, recursiveLe
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (element.fileElement) {
|
if (element.fileElement) {
|
||||||
return `[文件 ${element.fileElement.fileName}]`;
|
if (element.fileElement.fileUuid) {
|
||||||
|
return `[文件 ${element.fileElement.fileName}]`;
|
||||||
|
} else if (element.elementType === ElementType.TOFURECORD) {
|
||||||
|
return `[在线文件 ${element.fileElement.fileName}]`;
|
||||||
|
} else if (element.elementType === ElementType.ONLINEFOLDER) {
|
||||||
|
return `[在线文件夹 ${element.fileElement.fileName}/]`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.videoElement) {
|
if (element.videoElement) {
|
||||||
@@ -287,7 +294,11 @@ function msgElementToText (element: MessageElement, msg: RawMessage, recursiveLe
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (element.markdownElement) {
|
if (element.markdownElement) {
|
||||||
return '[Markdown 消息]';
|
if (element.markdownElement?.mdSummary) {
|
||||||
|
return element.markdownElement.mdSummary;
|
||||||
|
} else {
|
||||||
|
return '[Markdown 消息]';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.multiForwardMsgElement) {
|
if (element.multiForwardMsgElement) {
|
||||||
@@ -296,6 +307,8 @@ function msgElementToText (element: MessageElement, msg: RawMessage, recursiveLe
|
|||||||
|
|
||||||
if (element.elementType === ElementType.GreyTip) {
|
if (element.elementType === ElementType.GreyTip) {
|
||||||
return '[灰条消息]';
|
return '[灰条消息]';
|
||||||
|
} else if (element.elementType === ElementType.FILE) {
|
||||||
|
return '[文件发送中]';
|
||||||
}
|
}
|
||||||
|
|
||||||
return `[未实现 (ElementType = ${element.elementType})]`;
|
return `[未实现 (ElementType = ${element.elementType})]`;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import AppidTable from '@/napcat-core/external/appid.json';
|
|||||||
import { LogWrapper } from './log';
|
import { LogWrapper } from './log';
|
||||||
import { getMajorPath } from '@/napcat-core/index';
|
import { getMajorPath } from '@/napcat-core/index';
|
||||||
import { QQAppidTableType, QQPackageInfoType, QQVersionConfigType } from 'napcat-common/src/types';
|
import { QQAppidTableType, QQPackageInfoType, QQVersionConfigType } from 'napcat-common/src/types';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
export class QQBasicInfoWrapper {
|
export class QQBasicInfoWrapper {
|
||||||
QQMainPath: string | undefined;
|
QQMainPath: string | undefined;
|
||||||
@@ -21,6 +22,10 @@ export class QQBasicInfoWrapper {
|
|||||||
// 基础目录获取
|
// 基础目录获取
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.QQMainPath = process.execPath;
|
this.QQMainPath = process.execPath;
|
||||||
|
if (process.platform === 'darwin' && path.basename(this.QQMainPath) === 'QQ Helper') {
|
||||||
|
// 实用进程特殊处理 实用进程目录和QQ差远了
|
||||||
|
this.QQMainPath = path.resolve(path.dirname(this.QQMainPath), '../../../../', 'MacOS', 'QQ');
|
||||||
|
}
|
||||||
this.QQVersionConfigPath = getQQVersionConfigPath(this.QQMainPath);
|
this.QQVersionConfigPath = getQQVersionConfigPath(this.QQMainPath);
|
||||||
|
|
||||||
// 基础信息获取 无快更则启用默认模板填充
|
// 基础信息获取 无快更则启用默认模板填充
|
||||||
@@ -99,7 +104,10 @@ export class QQBasicInfoWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAppidV2ByMajor (QQVersion: string) {
|
getAppidV2ByMajor (QQVersion: string) {
|
||||||
const majorPath = getMajorPath(QQVersion);
|
if (!this.QQMainPath) {
|
||||||
|
throw new Error('QQMainPath未定义 无法通过Major获取Appid');
|
||||||
|
}
|
||||||
|
const majorPath = getMajorPath(QQVersion, this.QQMainPath);
|
||||||
const appid = parseAppidFromMajor(majorPath);
|
const appid = parseAppidFromMajor(majorPath);
|
||||||
return appid;
|
return appid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
NTQQSystemApi,
|
NTQQSystemApi,
|
||||||
NTQQUserApi,
|
NTQQUserApi,
|
||||||
NTQQWebApi,
|
NTQQWebApi,
|
||||||
|
NTQQFlashApi,
|
||||||
|
NTQQOnlineApi,
|
||||||
} from '@/napcat-core/apis';
|
} from '@/napcat-core/apis';
|
||||||
import { NTQQCollectionApi } from '@/napcat-core/apis/collection';
|
import { NTQQCollectionApi } from '@/napcat-core/apis/collection';
|
||||||
import {
|
import {
|
||||||
@@ -23,7 +25,7 @@ import path from 'node:path';
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import { hostname, systemName, systemVersion } from 'napcat-common/src/system';
|
import { hostname, systemName, systemVersion } from 'napcat-common/src/system';
|
||||||
import { NTEventWrapper } from '@/napcat-core/helper/event';
|
import { NTEventWrapper } from '@/napcat-core/helper/event';
|
||||||
import { KickedOffLineInfo, SelfInfo, SelfStatusInfo } from '@/napcat-core/types';
|
import { KickedOffLineInfo, RawMessage, SelfInfo, SelfStatusInfo } from '@/napcat-core/types';
|
||||||
import { NapCatConfigLoader, NapcatConfigSchema } from '@/napcat-core/helper/config';
|
import { NapCatConfigLoader, NapcatConfigSchema } from '@/napcat-core/helper/config';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/napcat-core/listeners';
|
import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/napcat-core/listeners';
|
||||||
@@ -37,6 +39,12 @@ export * from './wrapper';
|
|||||||
export * from './types/index';
|
export * from './types/index';
|
||||||
export * from './services/index';
|
export * from './services/index';
|
||||||
export * from './listeners/index';
|
export * from './listeners/index';
|
||||||
|
export * from './apis/index';
|
||||||
|
export * from './helper/log';
|
||||||
|
export * from './helper/qq-basic-info';
|
||||||
|
export * from './helper/event';
|
||||||
|
export * from './helper/config';
|
||||||
|
export * from './helper/proxy-handler';
|
||||||
|
|
||||||
export enum NapCatCoreWorkingEnv {
|
export enum NapCatCoreWorkingEnv {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
@@ -44,20 +52,23 @@ export enum NapCatCoreWorkingEnv {
|
|||||||
Framework = 2,
|
Framework = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadQQWrapper (QQVersion: string): WrapperNodeApi {
|
export function loadQQWrapper (execPath: string | undefined, QQVersion: string): WrapperNodeApi {
|
||||||
if (process.env['NAPCAT_WRAPPER_PATH']) {
|
if (process.env['NAPCAT_WRAPPER_PATH']) {
|
||||||
const wrapperPath = process.env['NAPCAT_WRAPPER_PATH'];
|
const wrapperPath = process.env['NAPCAT_WRAPPER_PATH'];
|
||||||
const nativemodule: { exports: WrapperNodeApi; } = { exports: {} as WrapperNodeApi };
|
const nativemodule: { exports: WrapperNodeApi; } = { exports: {} as WrapperNodeApi };
|
||||||
process.dlopen(nativemodule, wrapperPath);
|
process.dlopen(nativemodule, wrapperPath);
|
||||||
return nativemodule.exports;
|
return nativemodule.exports;
|
||||||
}
|
}
|
||||||
|
if (!execPath) {
|
||||||
|
throw new Error('无法加载Wrapper,execPath未定义');
|
||||||
|
}
|
||||||
let appPath;
|
let appPath;
|
||||||
if (os.platform() === 'darwin') {
|
if (os.platform() === 'darwin') {
|
||||||
appPath = path.resolve(path.dirname(process.execPath), '../Resources/app');
|
appPath = path.resolve(path.dirname(execPath), '../Resources/app');
|
||||||
} else if (os.platform() === 'linux') {
|
} else if (os.platform() === 'linux') {
|
||||||
appPath = path.resolve(path.dirname(process.execPath), './resources/app');
|
appPath = path.resolve(path.dirname(execPath), './resources/app');
|
||||||
} else {
|
} else {
|
||||||
appPath = path.resolve(path.dirname(process.execPath), `./versions/${QQVersion}/`);
|
appPath = path.resolve(path.dirname(execPath), `./versions/${QQVersion}/`);
|
||||||
}
|
}
|
||||||
let wrapperNodePath = path.resolve(appPath, 'wrapper.node');
|
let wrapperNodePath = path.resolve(appPath, 'wrapper.node');
|
||||||
if (!fs.existsSync(wrapperNodePath)) {
|
if (!fs.existsSync(wrapperNodePath)) {
|
||||||
@@ -65,21 +76,22 @@ export function loadQQWrapper (QQVersion: string): WrapperNodeApi {
|
|||||||
}
|
}
|
||||||
// 老版本兼容 未来去掉
|
// 老版本兼容 未来去掉
|
||||||
if (!fs.existsSync(wrapperNodePath)) {
|
if (!fs.existsSync(wrapperNodePath)) {
|
||||||
wrapperNodePath = path.join(path.dirname(process.execPath), `./resources/app/versions/${QQVersion}/wrapper.node`);
|
wrapperNodePath = path.join(path.dirname(execPath), `./resources/app/versions/${QQVersion}/wrapper.node`);
|
||||||
}
|
}
|
||||||
const nativemodule: { exports: WrapperNodeApi; } = { exports: {} as WrapperNodeApi };
|
const nativemodule: { exports: WrapperNodeApi; } = { exports: {} as WrapperNodeApi };
|
||||||
process.dlopen(nativemodule, wrapperNodePath);
|
process.dlopen(nativemodule, wrapperNodePath);
|
||||||
|
process.env['NAPCAT_WRAPPER_PATH'] = wrapperNodePath;
|
||||||
return nativemodule.exports;
|
return nativemodule.exports;
|
||||||
}
|
}
|
||||||
export function getMajorPath (QQVersion: string): string {
|
export function getMajorPath (execPath: string, QQVersion: string): string {
|
||||||
// major.node
|
// major.node
|
||||||
let appPath;
|
let appPath;
|
||||||
if (os.platform() === 'darwin') {
|
if (os.platform() === 'darwin') {
|
||||||
appPath = path.resolve(path.dirname(process.execPath), '../Resources/app');
|
appPath = path.resolve(path.dirname(execPath), '../Resources/app');
|
||||||
} else if (os.platform() === 'linux') {
|
} else if (os.platform() === 'linux') {
|
||||||
appPath = path.resolve(path.dirname(process.execPath), './resources/app');
|
appPath = path.resolve(path.dirname(execPath), './resources/app');
|
||||||
} else {
|
} else {
|
||||||
appPath = path.resolve(path.dirname(process.execPath), `./versions/${QQVersion}/`);
|
appPath = path.resolve(path.dirname(execPath), `./versions/${QQVersion}/`);
|
||||||
}
|
}
|
||||||
let majorPath = path.resolve(appPath, 'major.node');
|
let majorPath = path.resolve(appPath, 'major.node');
|
||||||
if (!fs.existsSync(majorPath)) {
|
if (!fs.existsSync(majorPath)) {
|
||||||
@@ -87,7 +99,7 @@ export function getMajorPath (QQVersion: string): string {
|
|||||||
}
|
}
|
||||||
// 老版本兼容 未来去掉
|
// 老版本兼容 未来去掉
|
||||||
if (!fs.existsSync(majorPath)) {
|
if (!fs.existsSync(majorPath)) {
|
||||||
majorPath = path.join(path.dirname(process.execPath), `./resources/app/versions/${QQVersion}/major.node`);
|
majorPath = path.join(path.dirname(execPath), `./resources/app/versions/${QQVersion}/major.node`);
|
||||||
}
|
}
|
||||||
return majorPath;
|
return majorPath;
|
||||||
}
|
}
|
||||||
@@ -120,6 +132,8 @@ export class NapCatCore {
|
|||||||
MsgApi: new NTQQMsgApi(this.context, this),
|
MsgApi: new NTQQMsgApi(this.context, this),
|
||||||
UserApi: new NTQQUserApi(this.context, this),
|
UserApi: new NTQQUserApi(this.context, this),
|
||||||
GroupApi: new NTQQGroupApi(this.context, this),
|
GroupApi: new NTQQGroupApi(this.context, this),
|
||||||
|
FlashApi: new NTQQFlashApi(this.context, this),
|
||||||
|
OnlineApi: new NTQQOnlineApi(this.context, this),
|
||||||
};
|
};
|
||||||
container.bind(NapCatCore).toConstantValue(this);
|
container.bind(NapCatCore).toConstantValue(this);
|
||||||
container.bind(TypedEventEmitter).toConstantValue(this.event);
|
container.bind(TypedEventEmitter).toConstantValue(this.event);
|
||||||
@@ -175,6 +189,11 @@ export class NapCatCore {
|
|||||||
async initNapCatCoreListeners () {
|
async initNapCatCoreListeners () {
|
||||||
const msgListener = new NodeIKernelMsgListener();
|
const msgListener = new NodeIKernelMsgListener();
|
||||||
|
|
||||||
|
// 在线文件/文件夹消息
|
||||||
|
msgListener.onRecvOnlineFileMsg = (msgs: RawMessage[]) => {
|
||||||
|
msgs.forEach(msg => this.context.logger.logMessage(msg, this.selfInfo));
|
||||||
|
};
|
||||||
|
|
||||||
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
||||||
// 下线通知
|
// 下线通知
|
||||||
const tips = `[KickedOffLine] [${Info.tipsTitle}] ${Info.tipsDesc}`;
|
const tips = `[KickedOffLine] [${Info.tipsTitle}] ${Info.tipsDesc}`;
|
||||||
@@ -294,4 +313,6 @@ export interface StableNTApiWrapper {
|
|||||||
MsgApi: NTQQMsgApi,
|
MsgApi: NTQQMsgApi,
|
||||||
UserApi: NTQQUserApi,
|
UserApi: NTQQUserApi,
|
||||||
GroupApi: NTQQGroupApi;
|
GroupApi: NTQQGroupApi;
|
||||||
|
FlashApi: NTQQFlashApi,
|
||||||
|
OnlineApi: NTQQOnlineApi,
|
||||||
}
|
}
|
||||||
|
|||||||
303
packages/napcat-core/services/NodeIKernelFlashTransferService.ts
Normal file
303
packages/napcat-core/services/NodeIKernelFlashTransferService.ts
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
import { GeneralCallResult } from './common';
|
||||||
|
import {
|
||||||
|
SendStatus,
|
||||||
|
StartFlashTaskRequests,
|
||||||
|
createFlashTransferResult,
|
||||||
|
FlashBaseRequest,
|
||||||
|
FlashFileSetInfo,
|
||||||
|
FileListInfoRequests,
|
||||||
|
FileListResponse,
|
||||||
|
DownloadStatusInfo,
|
||||||
|
SendTargetRequests,
|
||||||
|
FlashOneFileInfo,
|
||||||
|
DownloadSceneType,
|
||||||
|
} from '../data/flash';
|
||||||
|
|
||||||
|
export interface NodeIKernelFlashTransferService {
|
||||||
|
/**
|
||||||
|
* 开始闪传服务 并上传文件/文件夹(可以多选,非常好用)
|
||||||
|
* @param timestamp
|
||||||
|
* @param fileInfo
|
||||||
|
*/
|
||||||
|
createFlashTransferUploadTask (timestamp: number, fileInfo: StartFlashTaskRequests): Promise<GeneralCallResult & {
|
||||||
|
createFlashTransferResult: createFlashTransferResult;
|
||||||
|
seq: number;
|
||||||
|
}>; // 2 arg 重点 // 自动上传
|
||||||
|
|
||||||
|
createMergeShareTask (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
updateFlashTransfer (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
getFileSetList (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
getFileSetListCount (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取file set 的信息
|
||||||
|
* @param fileSetIdDict
|
||||||
|
*/
|
||||||
|
getFileSet (fileSetIdDict: FlashBaseRequest): Promise<GeneralCallResult & {
|
||||||
|
seq: number;
|
||||||
|
isCache: boolean;
|
||||||
|
fileSet: FlashFileSetInfo;
|
||||||
|
}>; // 1 arg
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取file set 里面的文件信息(文件夹结构)
|
||||||
|
* @param requestArgs
|
||||||
|
*/
|
||||||
|
getFileList (requestArgs: FileListInfoRequests): Promise<{
|
||||||
|
rsp: FileListResponse;
|
||||||
|
}>; // 1 arg 这个方法QQ有bug??? 并没有,是我参数有问题
|
||||||
|
|
||||||
|
getDownloadedFileCount (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
getLocalFileList (...args: unknown[]): unknown; // 3 arg
|
||||||
|
|
||||||
|
batchRemoveUserFileSetHistory (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分享链接
|
||||||
|
* @param fileSetId
|
||||||
|
*/
|
||||||
|
getShareLinkReq (fileSetId: string): Promise<GeneralCallResult & {
|
||||||
|
shareLink: string;
|
||||||
|
expireTimestamp: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 由分享链接到fileSetId
|
||||||
|
* @param shareCode
|
||||||
|
*/
|
||||||
|
getFileSetIdByCode (shareCode: string): Promise<GeneralCallResult & {
|
||||||
|
fileSetId: string;
|
||||||
|
}>; // 1 arg code == share code
|
||||||
|
|
||||||
|
batchRemoveFile (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
checkUploadPathValid (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
cleanFailedFiles (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停所有的任务
|
||||||
|
*/
|
||||||
|
resumeAllUnfinishedTasks (): unknown; // 0 arg !!
|
||||||
|
|
||||||
|
addFileSetUploadListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeFileSetUploadListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始上传任务 适用于已暂停的
|
||||||
|
* @param fileSetId
|
||||||
|
*/
|
||||||
|
startFileSetUpload (fileSetId: string): void; // 1 arg 并不是新建任务,应该是暂停后的启动
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束,无法再次启动
|
||||||
|
* @param fileSetId
|
||||||
|
*/
|
||||||
|
stopFileSetUpload (fileSetId: string): void; // 1 arg stop 后start无效
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停上传
|
||||||
|
* @param fileSetId
|
||||||
|
*/
|
||||||
|
pauseFileSetUpload (fileSetId: string): void; // 1 arg 暂停上传
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 继续上传
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
resumeFileSetUpload (...args: unknown[]): unknown; // 1 arg 继续
|
||||||
|
|
||||||
|
pauseFileUpload (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
resumeFileUpload (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
stopFileUpload (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
asyncGetThumbnailPath (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
setDownLoadDefaultFileDir (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
setFileSetDownloadDir (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
getFileSetDownloadDir (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
setFlashTransferDir (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
addFileSetDownloadListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeFileSetDownloadListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始下载file set的函数 同开始上传
|
||||||
|
* @param fileSetId
|
||||||
|
* @param downloadSceneType 下载类型 //因为没有peer,其实可以硬编码为1 (好友私聊)
|
||||||
|
* @param arg // 默认为false
|
||||||
|
*/
|
||||||
|
startFileSetDownload (fileSetId: string, downloadSceneType: DownloadSceneType, downloadOptionParams: { isIncludeCompressInnerFiles: boolean; }): Promise<GeneralCallResult & {
|
||||||
|
extraInfo: 0;
|
||||||
|
}>; // 3 arg
|
||||||
|
|
||||||
|
stopFileSetDownload (fileSetId: string, downloadOptionParams: { isIncludeCompressInnerFiles: boolean; }): Promise<GeneralCallResult & {
|
||||||
|
extraInfo: 0;
|
||||||
|
}>; // 2 arg 结束不可重启!!
|
||||||
|
|
||||||
|
pauseFileSetDownload (fileSetId: string, downloadOptionParams: { isIncludeCompressInnerFiles: boolean; }): Promise<GeneralCallResult & {
|
||||||
|
extraInfo: 0;
|
||||||
|
}>; // 2 arg
|
||||||
|
|
||||||
|
resumeFileSetDownload (fileSetId: string, downloadOptionParams: { isIncludeCompressInnerFiles: boolean; }): Promise<GeneralCallResult & {
|
||||||
|
extraInfo: 0;
|
||||||
|
}>; // 2 arg
|
||||||
|
|
||||||
|
startFileListDownLoad (...args: unknown[]): unknown; // 4 arg // 大概率是选择set里面的部分文件进行下载,没必要,不想写
|
||||||
|
|
||||||
|
pauseFileListDownLoad (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
resumeFileListDownLoad (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
stopFileListDownLoad (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
startThumbnailListDownload (fileSetId: string): Promise<GeneralCallResult>; // 1 arg // 缩略图下载
|
||||||
|
|
||||||
|
stopThumbnailListDownload (fileSetId: string): Promise<GeneralCallResult>; // 1 arg
|
||||||
|
|
||||||
|
asyncRequestDownLoadStatus (fileSetId: string): Promise<DownloadStatusInfo>; // 1 arg
|
||||||
|
|
||||||
|
startFileTransferUrl (fileInfo: FlashOneFileInfo): Promise<{
|
||||||
|
ret: number,
|
||||||
|
url: string,
|
||||||
|
expireTimestampSeconds: string;
|
||||||
|
}>; // 1 arg
|
||||||
|
|
||||||
|
startFileListDownLoadBySessionId (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
addFileSetSimpleStatusListener (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
addFileSetSimpleStatusMonitoring (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
removeFileSetSimpleStatusMonitoring (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
removeFileSetSimpleStatusListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
addDesktopFileSetSimpleStatusListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
addDesktopFileSetSimpleStatusMonitoring (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeDesktopFileSetSimpleStatusMonitoring (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeDesktopFileSetSimpleStatusListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
addFileSetSimpleUploadInfoListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
addFileSetSimpleUploadInfoMonitoring (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeFileSetSimpleUploadInfoMonitoring (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeFileSetSimpleUploadInfoListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
/**
|
||||||
|
* 发送闪传消息
|
||||||
|
* @param sendArgs
|
||||||
|
*/
|
||||||
|
sendFlashTransferMsg (sendArgs: SendTargetRequests): Promise<{
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
rsp: {
|
||||||
|
sendStatus: SendStatus[];
|
||||||
|
};
|
||||||
|
}>; // 1 arg 估计是file set id
|
||||||
|
|
||||||
|
addFlashTransferTaskInfoListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeFlashTransferTaskInfoListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
retrieveLocalLastFailedSetTasksInfo (): unknown; // 0 arg
|
||||||
|
|
||||||
|
getFailedFileList (fileSetId: string): Promise<{
|
||||||
|
rsp: {
|
||||||
|
seq: number;
|
||||||
|
result: number;
|
||||||
|
errMs: string;
|
||||||
|
fileSetId: string;
|
||||||
|
fileList: [];
|
||||||
|
};
|
||||||
|
}>; // 1 arg
|
||||||
|
|
||||||
|
getLocalFileListByStatuses (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
addTransferStateListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeTransferStateListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
getFileSetFirstClusteringList (...args: unknown[]): unknown; // 3 arg
|
||||||
|
|
||||||
|
getFileSetClusteringList (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
addFileSetClusteringListListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeFileSetClusteringListListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
getFileSetClusteringDetail (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
doAIOFlashTransferBubbleActionWithStatus (...args: unknown[]): unknown; // 4 arg
|
||||||
|
|
||||||
|
getFilesTransferProgress (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
pollFilesTransferProgress (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
cancelPollFilesTransferProgress (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
checkDownloadStatusBeforeLocalFileOper (...args: unknown[]): unknown; // 3 arg
|
||||||
|
|
||||||
|
getCompressedFileFolder (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
addFolderListener (...args: unknown[]): unknown; // 1 arg
|
||||||
|
|
||||||
|
removeFolderListener (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addCompressedFileListener (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
removeCompressedFileListener (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFileCategoryList (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addDeviceStatusListener (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
removeDeviceStatusListener (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
checkDeviceStatus (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
pauseAllTasks (...args: unknown[]): unknown; // 2 arg
|
||||||
|
|
||||||
|
resumePausedTasksAfterDeviceStatus (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onSystemGoingToSleep (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onSystemWokeUp (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFileMetas (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addDownloadCntStatisticsListener (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
removeDownloadCntStatisticsListener (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
detectPrivacyInfoInPaths (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFileThumbnailUrl (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
handleDownloadFinishAfterSaveToAlbum (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
checkBatchFilesDownloadStatus (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onCheckAlbumStorageStatusResult (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addFileAlbumStorageListener (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
removeFileAlbumStorageListener (...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
refreshFolderStatus (...args: unknown[]): unknown;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
export enum GeneralCallResultStatus {
|
export enum GeneralCallResultStatus {
|
||||||
OK = 0,
|
OK = 0,
|
||||||
|
ERROR = -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GeneralCallResult {
|
export interface GeneralCallResult {
|
||||||
|
|||||||
21
packages/napcat-core/types/flashfile.ts
Normal file
21
packages/napcat-core/types/flashfile.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export enum fileType {
|
||||||
|
MP3 = 1,
|
||||||
|
VIDEO = 2,
|
||||||
|
DOC = 3,
|
||||||
|
ZIP = 4,
|
||||||
|
XLS = 6,
|
||||||
|
PPT = 7,
|
||||||
|
CODE = 8,
|
||||||
|
PDF = 9,
|
||||||
|
TXT = 10,
|
||||||
|
UNKNOW = 11,
|
||||||
|
FOLDER = 25,
|
||||||
|
IMG = 26,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FileStatus {
|
||||||
|
UPLOADING = 0,
|
||||||
|
// DOWNLOADED = 1, ??? 不太清楚
|
||||||
|
OK = 2,
|
||||||
|
STOP = 3,
|
||||||
|
}
|
||||||
@@ -11,3 +11,7 @@ export * from './constant';
|
|||||||
export * from './graytip';
|
export * from './graytip';
|
||||||
export * from './emoji';
|
export * from './emoji';
|
||||||
export * from './service';
|
export * from './service';
|
||||||
|
export * from './adapter';
|
||||||
|
export * from './contact';
|
||||||
|
export * from './file';
|
||||||
|
export * from './flashfile';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NTGroupMemberRole } from '@/napcat-core/index';
|
import { NTGroupMemberRole } from './group';
|
||||||
import { ActionBarElement, ArkElement, AvRecordElement, CalendarElement, FaceBubbleElement, FaceElement, FileElement, GiphyElement, GrayTipElement, MarketFaceElement, PicElement, PttElement, RecommendedMsgElement, ReplyElement, ShareLocationElement, StructLongMsgElement, TaskTopMsgElement, TextElement, TofuRecordElement, VideoElement, YoloGameResultElement } from './element';
|
import { ActionBarElement, ArkElement, AvRecordElement, CalendarElement, FaceBubbleElement, FaceElement, FileElement, GiphyElement, GrayTipElement, MarketFaceElement, PicElement, PttElement, RecommendedMsgElement, ReplyElement, ShareLocationElement, StructLongMsgElement, TaskTopMsgElement, TextElement, TofuRecordElement, VideoElement, YoloGameResultElement } from './element';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -66,13 +66,14 @@ export enum ElementType {
|
|||||||
YOLOGAMERESULT = 20,
|
YOLOGAMERESULT = 20,
|
||||||
AVRECORD = 21,
|
AVRECORD = 21,
|
||||||
FEED = 22,
|
FEED = 22,
|
||||||
TOFURECORD = 23,
|
TOFURECORD = 23, // tofu record?? 在线文件的id是这个
|
||||||
ACEBUBBLE = 24,
|
ACEBUBBLE = 24,
|
||||||
ACTIVITY = 25,
|
ACTIVITY = 25,
|
||||||
TOFU = 26,
|
TOFU = 26,
|
||||||
FACEBUBBLE = 27,
|
FACEBUBBLE = 27,
|
||||||
SHARELOCATION = 28,
|
SHARELOCATION = 28,
|
||||||
TASKTOPMSG = 29,
|
TASKTOPMSG = 29,
|
||||||
|
ONLINEFOLDER = 30, // 在线文件夹
|
||||||
RECOMMENDEDMSG = 43,
|
RECOMMENDEDMSG = 43,
|
||||||
ACTIONBAR = 44,
|
ACTIONBAR = 44,
|
||||||
}
|
}
|
||||||
@@ -303,11 +304,40 @@ export enum NTVideoType {
|
|||||||
VIDEO_FORMAT_WMV = 3,
|
VIDEO_FORMAT_WMV = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 闪传图标
|
||||||
|
*/
|
||||||
|
export interface FlashTransferIcon {
|
||||||
|
spec: number;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 闪传文件信息
|
||||||
|
*/
|
||||||
|
export interface FlashTransferInfo {
|
||||||
|
filesetId: string;
|
||||||
|
name: string;
|
||||||
|
fileSize: string;
|
||||||
|
thnumbnail: {
|
||||||
|
id: string;
|
||||||
|
urls: FlashTransferIcon[];
|
||||||
|
localCachePath: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Markdown元素接口
|
* Markdown元素接口
|
||||||
*/
|
*/
|
||||||
export interface MarkdownElement {
|
export interface MarkdownElement {
|
||||||
content: string;
|
content: string;
|
||||||
|
style?: {};
|
||||||
|
processMsg?: string;
|
||||||
|
mdSummary?: string;
|
||||||
|
mdExtType?: number;
|
||||||
|
mdExtInfo?: {
|
||||||
|
flashTransferInfo: FlashTransferInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -57,24 +57,24 @@ export interface BaseInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 音乐信息
|
// 音乐信息
|
||||||
interface MusicInfo {
|
export interface MusicInfo {
|
||||||
buf: string;
|
buf: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 视频业务信息
|
// 视频业务信息
|
||||||
interface VideoBizInfo {
|
export interface VideoBizInfo {
|
||||||
cid: string;
|
cid: string;
|
||||||
tvUrl: string;
|
tvUrl: string;
|
||||||
synchType: string;
|
synchType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 视频信息
|
// 视频信息
|
||||||
interface VideoInfo {
|
export interface VideoInfo {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 扩展在线业务信息
|
// 扩展在线业务信息
|
||||||
interface ExtOnlineBusinessInfo {
|
export interface ExtOnlineBusinessInfo {
|
||||||
buf: string;
|
buf: string;
|
||||||
customStatus: unknown;
|
customStatus: unknown;
|
||||||
videoBizInfo: VideoBizInfo;
|
videoBizInfo: VideoBizInfo;
|
||||||
@@ -82,12 +82,12 @@ interface ExtOnlineBusinessInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 扩展缓冲区
|
// 扩展缓冲区
|
||||||
interface ExtBuffer {
|
export interface ExtBuffer {
|
||||||
buf: string;
|
buf: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户状态
|
// 用户状态
|
||||||
interface UserStatus {
|
export interface UserStatus {
|
||||||
uid: string;
|
uid: string;
|
||||||
uin: string;
|
uin: string;
|
||||||
status: number;
|
status: number;
|
||||||
@@ -109,14 +109,14 @@ interface UserStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 特权图标
|
// 特权图标
|
||||||
interface PrivilegeIcon {
|
export interface PrivilegeIcon {
|
||||||
jumpUrl: string;
|
jumpUrl: string;
|
||||||
openIconList: unknown[];
|
openIconList: unknown[];
|
||||||
closeIconList: unknown[];
|
closeIconList: unknown[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 增值服务信息
|
// 增值服务信息
|
||||||
interface VasInfo {
|
export interface VasInfo {
|
||||||
vipFlag: boolean;
|
vipFlag: boolean;
|
||||||
yearVipFlag: boolean;
|
yearVipFlag: boolean;
|
||||||
svipFlag: boolean;
|
svipFlag: boolean;
|
||||||
@@ -149,7 +149,7 @@ interface VasInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 关系标志
|
// 关系标志
|
||||||
interface RelationFlags {
|
export interface RelationFlags {
|
||||||
topTime: string;
|
topTime: string;
|
||||||
isBlock: boolean;
|
isBlock: boolean;
|
||||||
isMsgDisturb: boolean;
|
isMsgDisturb: boolean;
|
||||||
@@ -167,7 +167,7 @@ interface RelationFlags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 通用扩展信息
|
// 通用扩展信息
|
||||||
interface CommonExt {
|
export interface CommonExt {
|
||||||
constellation: number;
|
constellation: number;
|
||||||
shengXiao: number;
|
shengXiao: number;
|
||||||
kBloodType: number;
|
kBloodType: number;
|
||||||
@@ -193,14 +193,14 @@ export enum BuddyListReqType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 图片信息
|
// 图片信息
|
||||||
interface Pic {
|
export interface Pic {
|
||||||
picId: string;
|
picId: string;
|
||||||
picTime: number;
|
picTime: number;
|
||||||
picUrlMap: Record<string, string>;
|
picUrlMap: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 照片墙
|
// 照片墙
|
||||||
interface PhotoWall {
|
export interface PhotoWall {
|
||||||
picList: Pic[];
|
picList: Pic[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +247,7 @@ export interface ModifyProfileParams {
|
|||||||
nick: string;
|
nick: string;
|
||||||
longNick: string;
|
longNick: string;
|
||||||
sex: NTSex;
|
sex: NTSex;
|
||||||
birthday: { birthday_year: string, birthday_month: string, birthday_day: string };
|
birthday: { birthday_year: string, birthday_month: string, birthday_day: string; };
|
||||||
location: unknown;
|
location: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from './adapters';
|
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from './adapters';
|
||||||
import {
|
import {
|
||||||
|
GeneralCallResult,
|
||||||
NodeIKernelAvatarService,
|
NodeIKernelAvatarService,
|
||||||
NodeIKernelBuddyService,
|
NodeIKernelBuddyService,
|
||||||
NodeIKernelGroupService,
|
NodeIKernelGroupService,
|
||||||
@@ -27,77 +28,78 @@ import { NodeIKernelMSFService } from './services/NodeIKernelMSFService';
|
|||||||
import { NodeIkernelTestPerformanceService } from './services/NodeIkernelTestPerformanceService';
|
import { NodeIkernelTestPerformanceService } from './services/NodeIkernelTestPerformanceService';
|
||||||
import { NodeIKernelECDHService } from './services/NodeIKernelECDHService';
|
import { NodeIKernelECDHService } from './services/NodeIKernelECDHService';
|
||||||
import { NodeIO3MiscService } from './services/NodeIO3MiscService';
|
import { NodeIO3MiscService } from './services/NodeIO3MiscService';
|
||||||
|
import { NodeIKernelFlashTransferService } from './services/NodeIKernelFlashTransferService';
|
||||||
|
|
||||||
export interface NodeQQNTWrapperUtil {
|
export interface NodeQQNTWrapperUtil {
|
||||||
get(): NodeQQNTWrapperUtil;
|
get (): NodeQQNTWrapperUtil;
|
||||||
|
|
||||||
getNTUserDataInfoConfig(): string;
|
getNTUserDataInfoConfig (): string;
|
||||||
|
|
||||||
emptyWorkingSet(n: number): void;
|
emptyWorkingSet (n: number): void;
|
||||||
|
|
||||||
getSsoCmdOfOidbReq(arg1: number, arg2: number): unknown;
|
getSsoCmdOfOidbReq (arg1: number, arg2: number): unknown;
|
||||||
|
|
||||||
getSsoBufferOfOidbReq(...args: unknown[]): unknown; // 有点看不懂参数定义 待补充 好像是三个参数
|
getSsoBufferOfOidbReq (...args: unknown[]): unknown; // 有点看不懂参数定义 待补充 好像是三个参数
|
||||||
|
|
||||||
getOidbRspInfo(arg: string): unknown; // 可能是错的
|
getOidbRspInfo (arg: string): unknown; // 可能是错的
|
||||||
|
|
||||||
getFileSize(path: string): Promise<number>; // 直接的猜测
|
getFileSize (path: string): Promise<number>; // 直接的猜测
|
||||||
|
|
||||||
genFileMd5Buf(arg: string): unknown; // 可能是错的
|
genFileMd5Buf (arg: string): unknown; // 可能是错的
|
||||||
|
|
||||||
genFileMd5Hex(path: string): unknown; // 直接的猜测
|
genFileMd5Hex (path: string): unknown; // 直接的猜测
|
||||||
|
|
||||||
genFileShaBuf(path: string): unknown; // 直接的猜测
|
genFileShaBuf (path: string): unknown; // 直接的猜测
|
||||||
|
|
||||||
genFileCumulateSha1(path: string): unknown; // 直接的猜测
|
genFileCumulateSha1 (path: string): unknown; // 直接的猜测
|
||||||
|
|
||||||
genFileShaHex(path: string): unknown; // 直接的猜测
|
genFileShaHex (path: string): unknown; // 直接的猜测
|
||||||
|
|
||||||
fileIsExist(path: string): unknown;
|
fileIsExist (path: string): unknown;
|
||||||
|
|
||||||
startTrace(path: string): unknown; // 可能是错的
|
startTrace (path: string): unknown; // 可能是错的
|
||||||
|
|
||||||
copyFile(src: string, dst: string): unknown;
|
copyFile (src: string, dst: string): unknown;
|
||||||
|
|
||||||
genFileShaAndMd5Hex(path: string, unknown: number): unknown; // 可能是错的
|
genFileShaAndMd5Hex (path: string, unknown: number): unknown; // 可能是错的
|
||||||
|
|
||||||
setTraceInfo(unknown: unknown): unknown;
|
setTraceInfo (unknown: unknown): unknown;
|
||||||
|
|
||||||
encodeOffLine(unknown: unknown): unknown;
|
encodeOffLine (unknown: unknown): unknown;
|
||||||
|
|
||||||
decodeOffLine(arg: string): unknown; // 可能是错的 传递hex
|
decodeOffLine (arg: string): unknown; // 可能是错的 传递hex
|
||||||
|
|
||||||
DecoderRecentInfo(arg: string): unknown; // 可能是错的 传递hex
|
DecoderRecentInfo (arg: string): unknown; // 可能是错的 传递hex
|
||||||
|
|
||||||
getPinyin(arg0: string, arg1: boolean): unknown;
|
getPinyin (arg0: string, arg1: boolean): unknown;
|
||||||
|
|
||||||
matchInPinyin(arg0: unknown[], arg1: string): unknown; // 参数特复杂 arg0是个复杂数据类型
|
matchInPinyin (arg0: unknown[], arg1: string): unknown; // 参数特复杂 arg0是个复杂数据类型
|
||||||
|
|
||||||
makeDirByPath(arg0: string): unknown;
|
makeDirByPath (arg0: string): unknown;
|
||||||
|
|
||||||
emptyWorkingSet(arg0: number): unknown; // 参数是UINT32
|
emptyWorkingSet (arg0: number): unknown; // 参数是UINT32
|
||||||
|
|
||||||
runProcess(arg0: string, arg1: boolean): unknown;
|
runProcess (arg0: string, arg1: boolean): unknown;
|
||||||
|
|
||||||
runProcessArgs(arg0: string, arg1: { [key: string]: string }, arg2: boolean): unknown;
|
runProcessArgs (arg0: string, arg1: { [key: string]: string; }, arg2: boolean): unknown;
|
||||||
|
|
||||||
calcThumbSize(arg0: number, arg1: number, arg2: unknown): unknown;
|
calcThumbSize (arg0: number, arg1: number, arg2: unknown): unknown;
|
||||||
|
|
||||||
fullWordToHalfWord(word: string): unknown;
|
fullWordToHalfWord (word: string): unknown;
|
||||||
|
|
||||||
getNTUserDataInfoConfig(): unknown;
|
getNTUserDataInfoConfig (): unknown;
|
||||||
|
|
||||||
pathIsReadableAndWriteable(path: string): unknown; // 直接的猜测
|
pathIsReadableAndWriteable (path: string): unknown; // 直接的猜测
|
||||||
|
|
||||||
resetUserDataSavePathToDocument(): unknown;
|
resetUserDataSavePathToDocument (): unknown;
|
||||||
|
|
||||||
getSoBuildInfo(): unknown; // 例如 0[0]_d491dc01e0a_0
|
getSoBuildInfo (): unknown; // 例如 0[0]_d491dc01e0a_0
|
||||||
|
|
||||||
registerCountInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown;
|
registerCountInstruments (arg0: string, arg1: string[], arg2: number, arg3: number): unknown;
|
||||||
|
|
||||||
registerValueInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown;
|
registerValueInstruments (arg0: string, arg1: string[], arg2: number, arg3: number): unknown;
|
||||||
|
|
||||||
registerValueInstrumentsWithBoundary(
|
registerValueInstrumentsWithBoundary (
|
||||||
arg0: string,
|
arg0: string,
|
||||||
arg1: unknown,
|
arg1: unknown,
|
||||||
arg2: unknown,
|
arg2: unknown,
|
||||||
@@ -105,7 +107,7 @@ export interface NodeQQNTWrapperUtil {
|
|||||||
arg4: number,
|
arg4: number,
|
||||||
): unknown;
|
): unknown;
|
||||||
|
|
||||||
reportCountIndicators(
|
reportCountIndicators (
|
||||||
arg0: string,
|
arg0: string,
|
||||||
arg1: Map<unknown, unknown>,
|
arg1: Map<unknown, unknown>,
|
||||||
arg2: string,
|
arg2: string,
|
||||||
@@ -113,7 +115,7 @@ export interface NodeQQNTWrapperUtil {
|
|||||||
arg4: boolean,
|
arg4: boolean,
|
||||||
): unknown;
|
): unknown;
|
||||||
|
|
||||||
reportValueIndicators(
|
reportValueIndicators (
|
||||||
arg0: string,
|
arg0: string,
|
||||||
arg1: Map<unknown, unknown>,
|
arg1: Map<unknown, unknown>,
|
||||||
arg2: string,
|
arg2: string,
|
||||||
@@ -121,140 +123,154 @@ export interface NodeQQNTWrapperUtil {
|
|||||||
arg4: number,
|
arg4: number,
|
||||||
): unknown;
|
): unknown;
|
||||||
|
|
||||||
checkNewUserDataSaveDirAvailable(arg0: string): unknown;
|
checkNewUserDataSaveDirAvailable (arg0: string): unknown;
|
||||||
|
|
||||||
copyUserData(arg0: string, arg1: string): Promise<unknown>;
|
copyUserData (arg0: string, arg1: string): Promise<unknown>;
|
||||||
|
|
||||||
setUserDataSaveDirectory(arg0: string): Promise<unknown>;
|
setUserDataSaveDirectory (arg0: string): Promise<unknown>;
|
||||||
|
|
||||||
hasOtherRunningQQProcess(): boolean;
|
hasOtherRunningQQProcess (): boolean;
|
||||||
|
|
||||||
quitAllRunningQQProcess(arg: boolean): unknown;
|
quitAllRunningQQProcess (arg: boolean): unknown;
|
||||||
|
|
||||||
checkNvidiaConfig(): unknown;
|
checkNvidiaConfig (): unknown;
|
||||||
|
|
||||||
repairNvidiaConfig(): unknown;
|
repairNvidiaConfig (): unknown;
|
||||||
|
|
||||||
getNvidiaDriverVersion(): unknown;
|
getNvidiaDriverVersion (): unknown;
|
||||||
|
|
||||||
isNull(): unknown;
|
isNull (): unknown;
|
||||||
|
|
||||||
|
createThumbnailImage (
|
||||||
|
serviceName: string,
|
||||||
|
filePath: string,
|
||||||
|
targetPath: string,
|
||||||
|
imgSize: {
|
||||||
|
width: number,
|
||||||
|
height: number;
|
||||||
|
},
|
||||||
|
fileFormat: string,
|
||||||
|
arg: number | null | undefined, // null undefined都行
|
||||||
|
): Promise<GeneralCallResult & { targetPath?: string; }>;
|
||||||
}
|
}
|
||||||
export interface NodeIQQNTStartupSessionWrapper {
|
export interface NodeIQQNTStartupSessionWrapper {
|
||||||
create(): NodeIQQNTStartupSessionWrapper;
|
create (): NodeIQQNTStartupSessionWrapper;
|
||||||
stop(): void;
|
stop (): void;
|
||||||
start(): void;
|
start (): void;
|
||||||
createWithModuleList(uk: unknown): unknown;
|
createWithModuleList (uk: unknown): unknown;
|
||||||
getSessionIdList(): unknown;
|
getSessionIdList (): unknown;
|
||||||
}
|
}
|
||||||
export interface NodeIQQNTWrapperSession {
|
export interface NodeIQQNTWrapperSession {
|
||||||
getNTWrapperSession(str: string): NodeIQQNTWrapperSession;
|
getNTWrapperSession (str: string): NodeIQQNTWrapperSession;
|
||||||
|
|
||||||
get(): NodeIQQNTWrapperSession;
|
get (): NodeIQQNTWrapperSession;
|
||||||
|
|
||||||
new(): NodeIQQNTWrapperSession;
|
new(): NodeIQQNTWrapperSession;
|
||||||
|
|
||||||
create(): NodeIQQNTWrapperSession;
|
create (): NodeIQQNTWrapperSession;
|
||||||
|
|
||||||
init(
|
init (
|
||||||
wrapperSessionInitConfig: WrapperSessionInitConfig,
|
wrapperSessionInitConfig: WrapperSessionInitConfig,
|
||||||
nodeIDependsAdapter: NodeIDependsAdapter,
|
nodeIDependsAdapter: NodeIDependsAdapter,
|
||||||
nodeIDispatcherAdapter: NodeIDispatcherAdapter,
|
nodeIDispatcherAdapter: NodeIDispatcherAdapter,
|
||||||
nodeIKernelSessionListener: NodeIKernelSessionListener,
|
nodeIKernelSessionListener: NodeIKernelSessionListener,
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
startNT(session: number): void;
|
startNT (session: number): void;
|
||||||
|
|
||||||
startNT(): void;
|
startNT (): void;
|
||||||
|
|
||||||
getBdhUploadService(): unknown;
|
getBdhUploadService (): unknown;
|
||||||
|
|
||||||
getECDHService(): NodeIKernelECDHService;
|
getECDHService (): NodeIKernelECDHService;
|
||||||
|
|
||||||
getMsgService(): NodeIKernelMsgService;
|
getMsgService (): NodeIKernelMsgService;
|
||||||
|
|
||||||
getProfileService(): NodeIKernelProfileService;
|
getProfileService (): NodeIKernelProfileService;
|
||||||
|
|
||||||
getProfileLikeService(): NodeIKernelProfileLikeService;
|
getProfileLikeService (): NodeIKernelProfileLikeService;
|
||||||
|
|
||||||
getGroupService(): NodeIKernelGroupService;
|
getGroupService (): NodeIKernelGroupService;
|
||||||
|
|
||||||
getStorageCleanService(): NodeIKernelStorageCleanService;
|
getStorageCleanService (): NodeIKernelStorageCleanService;
|
||||||
|
|
||||||
getBuddyService(): NodeIKernelBuddyService;
|
getBuddyService (): NodeIKernelBuddyService;
|
||||||
|
|
||||||
getRobotService(): NodeIKernelRobotService;
|
getRobotService (): NodeIKernelRobotService;
|
||||||
|
|
||||||
getTicketService(): NodeIKernelTicketService;
|
getTicketService (): NodeIKernelTicketService;
|
||||||
|
|
||||||
getTipOffService(): NodeIKernelTipOffService;
|
getTipOffService (): NodeIKernelTipOffService;
|
||||||
|
|
||||||
getNodeMiscService(): NodeIKernelNodeMiscService;
|
getNodeMiscService (): NodeIKernelNodeMiscService;
|
||||||
|
|
||||||
getRichMediaService(): NodeIKernelRichMediaService;
|
getRichMediaService (): NodeIKernelRichMediaService;
|
||||||
|
|
||||||
getMsgBackupService(): NodeIKernelMsgBackupService;
|
getMsgBackupService (): NodeIKernelMsgBackupService;
|
||||||
|
|
||||||
getAlbumService(): NodeIKernelAlbumService;
|
getAlbumService (): NodeIKernelAlbumService;
|
||||||
|
|
||||||
getTianShuService(): NodeIKernelTianShuService;
|
getTianShuService (): NodeIKernelTianShuService;
|
||||||
|
|
||||||
getUnitedConfigService(): NodeIKernelUnitedConfigService;
|
getUnitedConfigService (): NodeIKernelUnitedConfigService;
|
||||||
|
|
||||||
getSearchService(): NodeIKernelSearchService;
|
getSearchService (): NodeIKernelSearchService;
|
||||||
|
|
||||||
getDirectSessionService(): unknown;
|
getFlashTransferService (): NodeIKernelFlashTransferService;
|
||||||
|
|
||||||
getRDeliveryService(): unknown;
|
getDirectSessionService (): unknown;
|
||||||
|
|
||||||
getAvatarService(): NodeIKernelAvatarService;
|
getRDeliveryService (): unknown;
|
||||||
|
|
||||||
getFeedChannelService(): unknown;
|
getAvatarService (): NodeIKernelAvatarService;
|
||||||
|
|
||||||
getYellowFaceService(): unknown;
|
getFeedChannelService (): unknown;
|
||||||
|
|
||||||
getCollectionService(): NodeIKernelCollectionService;
|
getYellowFaceService (): unknown;
|
||||||
|
|
||||||
getSettingService(): unknown;
|
getCollectionService (): NodeIKernelCollectionService;
|
||||||
|
|
||||||
getQiDianService(): unknown;
|
getSettingService (): unknown;
|
||||||
|
|
||||||
getFileAssistantService(): unknown;
|
getQiDianService (): unknown;
|
||||||
|
|
||||||
getGuildService(): unknown;
|
getFileAssistantService (): unknown;
|
||||||
|
|
||||||
getSkinService(): unknown;
|
getGuildService (): unknown;
|
||||||
|
|
||||||
getTestPerformanceService(): NodeIkernelTestPerformanceService;
|
getSkinService (): unknown;
|
||||||
|
|
||||||
getQQPlayService(): unknown;
|
getTestPerformanceService (): NodeIkernelTestPerformanceService;
|
||||||
|
|
||||||
getDbToolsService(): unknown;
|
getQQPlayService (): unknown;
|
||||||
|
|
||||||
getUixConvertService(): NodeIKernelUixConvertService;
|
getDbToolsService (): unknown;
|
||||||
|
|
||||||
getOnlineStatusService(): unknown;
|
getUixConvertService (): NodeIKernelUixConvertService;
|
||||||
|
|
||||||
getRemotingService(): unknown;
|
getOnlineStatusService (): unknown;
|
||||||
|
|
||||||
getGroupTabService(): unknown;
|
getRemotingService (): unknown;
|
||||||
|
|
||||||
getGroupSchoolService(): unknown;
|
getGroupTabService (): unknown;
|
||||||
|
|
||||||
getLiteBusinessService(): unknown;
|
getGroupSchoolService (): unknown;
|
||||||
|
|
||||||
getGuildMsgService(): unknown;
|
getLiteBusinessService (): unknown;
|
||||||
|
|
||||||
getLockService(): unknown;
|
getGuildMsgService (): unknown;
|
||||||
|
|
||||||
getMSFService(): NodeIKernelMSFService;
|
getLockService (): unknown;
|
||||||
|
|
||||||
getGuildHotUpdateService(): unknown;
|
getMSFService (): NodeIKernelMSFService;
|
||||||
|
|
||||||
getAVSDKService(): unknown;
|
getGuildHotUpdateService (): unknown;
|
||||||
|
|
||||||
getRecentContactService(): NodeIKernelRecentContactService;
|
getAVSDKService (): unknown;
|
||||||
|
|
||||||
getConfigMgrService(): unknown;
|
getRecentContactService (): NodeIKernelRecentContactService;
|
||||||
|
|
||||||
|
getConfigMgrService (): unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnginInitDesktopConfig {
|
export interface EnginInitDesktopConfig {
|
||||||
@@ -268,20 +284,20 @@ export interface EnginInitDesktopConfig {
|
|||||||
global_path_config: {
|
global_path_config: {
|
||||||
desktopGlobalPath: string;
|
desktopGlobalPath: string;
|
||||||
};
|
};
|
||||||
thumb_config: { maxSide: 324; minSide: 48; longLimit: 6; density: 2 };
|
thumb_config: { maxSide: 324; minSide: 48; longLimit: 6; density: 2; };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeIQQNTWrapperEngine {
|
export interface NodeIQQNTWrapperEngine {
|
||||||
get(): NodeIQQNTWrapperEngine;
|
get (): NodeIQQNTWrapperEngine;
|
||||||
|
|
||||||
initWithDeskTopConfig(config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void;
|
initWithDeskTopConfig (config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WrapperNodeApi {
|
export interface WrapperNodeApi {
|
||||||
NodeIO3MiscService: NodeIO3MiscService;
|
NodeIO3MiscService: NodeIO3MiscService;
|
||||||
NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;
|
NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;
|
||||||
NodeIQQNTWrapperSession: NodeIQQNTWrapperSession;
|
NodeIQQNTWrapperSession: NodeIQQNTWrapperSession;
|
||||||
NodeIQQNTStartupSessionWrapper: NodeIQQNTStartupSessionWrapper
|
NodeIQQNTStartupSessionWrapper: NodeIQQNTStartupSessionWrapper;
|
||||||
NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine;
|
NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine;
|
||||||
NodeIKernelLoginService: NodeIKernelLoginService;
|
NodeIKernelLoginService: NodeIKernelLoginService;
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export async function NCoreInitFramework (
|
|||||||
const logger = new LogWrapper(pathWrapper.logsPath);
|
const logger = new LogWrapper(pathWrapper.logsPath);
|
||||||
await applyPendingUpdates(pathWrapper, logger);
|
await applyPendingUpdates(pathWrapper, logger);
|
||||||
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
||||||
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVersion());
|
const wrapper = loadQQWrapper(basicInfoWrapper.QQMainPath, basicInfoWrapper.getFullQQVersion());
|
||||||
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
|
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
|
||||||
// nativePacketHandler.onAll((packet) => {
|
// nativePacketHandler.onAll((packet) => {
|
||||||
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
|
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -5,9 +5,18 @@ import { NapCatOneBot11Adapter, OB11Return } from '@/napcat-onebot/index';
|
|||||||
import { NetworkAdapterConfig } from '../config/config';
|
import { NetworkAdapterConfig } from '../config/config';
|
||||||
import { TSchema } from '@sinclair/typebox';
|
import { TSchema } from '@sinclair/typebox';
|
||||||
import { StreamPacket, StreamPacketBasic, StreamStatus } from './stream/StreamBasic';
|
import { StreamPacket, StreamPacketBasic, StreamStatus } from './stream/StreamBasic';
|
||||||
|
export const ActionExamples = {
|
||||||
|
Common: {
|
||||||
|
errors: [
|
||||||
|
{ code: 1400, description: '请求参数错误或业务逻辑执行失败' },
|
||||||
|
{ code: 1401, description: '权限不足' },
|
||||||
|
{ code: 1404, description: '资源不存在' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export class OB11Response {
|
export class OB11Response {
|
||||||
private static createResponse<T>(data: T, status: string, retcode: number, message: string = '', echo: unknown = null, useStream: boolean = false): OB11Return<T> {
|
private static createResponse<T> (data: T, status: string, retcode: number, message: string = '', echo: unknown = null, useStream: boolean = false): OB11Return<T> {
|
||||||
return {
|
return {
|
||||||
status,
|
status,
|
||||||
retcode,
|
retcode,
|
||||||
@@ -19,11 +28,11 @@ export class OB11Response {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static res<T>(data: T, status: string, retcode: number, message: string = '', echo: unknown = null, useStream: boolean = false): OB11Return<T> {
|
static res<T> (data: T, status: string, retcode: number, message: string = '', echo: unknown = null, useStream: boolean = false): OB11Return<T> {
|
||||||
return this.createResponse(data, status, retcode, message, echo, useStream);
|
return this.createResponse(data, status, retcode, message, echo, useStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ok<T>(data: T, echo: unknown = null, useStream: boolean = false): OB11Return<T> {
|
static ok<T> (data: T, echo: unknown = null, useStream: boolean = false): OB11Return<T> {
|
||||||
return this.createResponse(data, 'ok', 0, '', echo, useStream);
|
return this.createResponse(data, 'ok', 0, '', echo, useStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,15 +41,22 @@ export class OB11Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
export abstract class OneBotRequestToolkit {
|
export abstract class OneBotRequestToolkit {
|
||||||
abstract send<T>(packet: StreamPacket<T>): Promise<void>;
|
abstract send<T> (packet: StreamPacket<T>): Promise<void>;
|
||||||
}
|
}
|
||||||
export abstract class OneBotAction<PayloadType, ReturnDataType> {
|
export abstract class OneBotAction<PayloadType, ReturnDataType> {
|
||||||
actionName: typeof ActionName[keyof typeof ActionName] = ActionName.Unknown;
|
actionName: typeof ActionName[keyof typeof ActionName] = ActionName.Unknown;
|
||||||
core: NapCatCore;
|
core: NapCatCore;
|
||||||
private validate?: ValidateFunction<unknown> = undefined;
|
private validate?: ValidateFunction<unknown> = undefined;
|
||||||
payloadSchema?: TSchema = undefined;
|
payloadSchema?: TSchema = undefined;
|
||||||
|
returnSchema?: TSchema = undefined;
|
||||||
|
payloadExample?: unknown = undefined;
|
||||||
|
returnExample?: unknown = undefined;
|
||||||
|
actionSummary: string = '';
|
||||||
|
actionDescription: string = '';
|
||||||
|
actionTags: string[] = [];
|
||||||
obContext: NapCatOneBot11Adapter;
|
obContext: NapCatOneBot11Adapter;
|
||||||
useStream: boolean = false;
|
useStream: boolean = false;
|
||||||
|
errorExamples: Array<{ code: number, description: string; }> = ActionExamples.Common.errors;
|
||||||
|
|
||||||
constructor (obContext: NapCatOneBot11Adapter, core: NapCatCore) {
|
constructor (obContext: NapCatOneBot11Adapter, core: NapCatCore) {
|
||||||
this.obContext = obContext;
|
this.obContext = obContext;
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
export const ExtendsActionsExamples = {
|
||||||
|
OCRImage: {
|
||||||
|
payload: { image: 'image_id_123' },
|
||||||
|
response: { texts: [{ text: '识别内容', coordinates: [] }] },
|
||||||
|
},
|
||||||
|
GetAiCharacters: {
|
||||||
|
payload: { group_id: '123456' },
|
||||||
|
response: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
characters: [
|
||||||
|
{ character_id: 'id', character_name: 'name', preview_url: 'url' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
GetClientkey: {
|
||||||
|
payload: {},
|
||||||
|
response: { clientkey: 'abcdef123456' },
|
||||||
|
},
|
||||||
|
SetQQAvatar: {
|
||||||
|
payload: { file: 'base64://...' },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetGroupKickMembers: {
|
||||||
|
payload: { group_id: '123456', user_id: ['123456789'], reject_add_request: false },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
TranslateEnWordToZn: {
|
||||||
|
payload: { words: ['hello'] },
|
||||||
|
response: { words: ['你好'] },
|
||||||
|
},
|
||||||
|
GetRkey: {
|
||||||
|
payload: {},
|
||||||
|
response: { rkey: '...' },
|
||||||
|
},
|
||||||
|
SetLongNick: {
|
||||||
|
payload: { longNick: '个性签名' },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetSpecialTitle: {
|
||||||
|
payload: { group_id: '123456', user_id: '123456789', special_title: '头衔' },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
22
packages/napcat-onebot/action/example/FileActionsExamples.ts
Normal file
22
packages/napcat-onebot/action/example/FileActionsExamples.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
export const FileActionsExamples = {
|
||||||
|
GetFile: {
|
||||||
|
payload: { file: 'file_id_123' },
|
||||||
|
response: { file: '/path/to/file', url: 'http://...', file_size: 1024, file_name: 'test.jpg' },
|
||||||
|
},
|
||||||
|
GetGroupFileUrl: {
|
||||||
|
payload: { group_id: '123456', file_id: 'file_id_123', busid: 102 },
|
||||||
|
response: { url: 'http://...' },
|
||||||
|
},
|
||||||
|
GetImage: {
|
||||||
|
payload: { file: 'image_id_123' },
|
||||||
|
response: { file: '/path/to/image', url: 'http://...' },
|
||||||
|
},
|
||||||
|
GetPrivateFileUrl: {
|
||||||
|
payload: { user_id: '123456789', file_id: 'file_id_123' },
|
||||||
|
response: { url: 'http://...' },
|
||||||
|
},
|
||||||
|
GetRecord: {
|
||||||
|
payload: { file: 'record_id_123', out_format: 'mp3' },
|
||||||
|
response: { file: '/path/to/record', url: 'http://...' },
|
||||||
|
},
|
||||||
|
};
|
||||||
102
packages/napcat-onebot/action/example/GoCQHTTPActionsExamples.ts
Normal file
102
packages/napcat-onebot/action/example/GoCQHTTPActionsExamples.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
export const GoCQHTTPActionsExamples = {
|
||||||
|
GetStrangerInfo: {
|
||||||
|
payload: { user_id: '123456789' },
|
||||||
|
response: { user_id: 123456789, nickname: '昵称', sex: 'unknown' },
|
||||||
|
},
|
||||||
|
GetGroupHonorInfo: {
|
||||||
|
payload: { group_id: '123456', type: 'all' },
|
||||||
|
response: { group_id: 123456, current_talkative: {}, talkative_list: [] },
|
||||||
|
},
|
||||||
|
GetForwardMsg: {
|
||||||
|
payload: { message_id: '123456' },
|
||||||
|
response: { messages: [] },
|
||||||
|
},
|
||||||
|
SendForwardMsg: {
|
||||||
|
payload: { group_id: '123456', messages: [] },
|
||||||
|
response: { message_id: 123456 },
|
||||||
|
},
|
||||||
|
GetGroupAtAllRemain: {
|
||||||
|
payload: { group_id: '123456' },
|
||||||
|
response: { can_at_all: true, remain_at_all_count_for_group: 10, remain_at_all_count_for_self: 10 },
|
||||||
|
},
|
||||||
|
CreateGroupFileFolder: {
|
||||||
|
payload: { group_id: '123456', name: '测试目录' },
|
||||||
|
response: { result: {}, groupItem: {} },
|
||||||
|
},
|
||||||
|
DeleteGroupFile: {
|
||||||
|
payload: { group_id: '123456', file_id: 'file_uuid_123' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
DeleteGroupFileFolder: {
|
||||||
|
payload: { group_id: '123456', folder_id: 'folder_uuid_123' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
DownloadFile: {
|
||||||
|
payload: { url: 'https://example.com/file.png', thread_count: 1, headers: 'User-Agent: NapCat' },
|
||||||
|
response: { file: '/path/to/downloaded/file' },
|
||||||
|
},
|
||||||
|
GetFriendMsgHistory: {
|
||||||
|
payload: { user_id: '123456789', message_seq: 0, count: 20 },
|
||||||
|
response: { messages: [] },
|
||||||
|
},
|
||||||
|
GetGroupFilesByFolder: {
|
||||||
|
payload: { group_id: '123456', folder_id: 'folder_id' },
|
||||||
|
response: { files: [], folders: [] },
|
||||||
|
},
|
||||||
|
GetGroupFileSystemInfo: {
|
||||||
|
payload: { group_id: '123456' },
|
||||||
|
response: { file_count: 10, limit_count: 10000, used_space: 1024, total_space: 10737418240 },
|
||||||
|
},
|
||||||
|
GetGroupMsgHistory: {
|
||||||
|
payload: { group_id: '123456', message_seq: 0, count: 20 },
|
||||||
|
response: { messages: [] },
|
||||||
|
},
|
||||||
|
GetGroupRootFiles: {
|
||||||
|
payload: { group_id: '123456' },
|
||||||
|
response: { files: [], folders: [] },
|
||||||
|
},
|
||||||
|
GetOnlineClient: {
|
||||||
|
payload: { no_cache: false },
|
||||||
|
response: [],
|
||||||
|
},
|
||||||
|
GoCQHTTPCheckUrlSafely: {
|
||||||
|
payload: { url: 'https://example.com' },
|
||||||
|
response: { level: 1 },
|
||||||
|
},
|
||||||
|
GoCQHTTPDeleteFriend: {
|
||||||
|
payload: { user_id: '123456789' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
GoCQHTTPGetModelShow: {
|
||||||
|
payload: { model: 'iPhone 13' },
|
||||||
|
response: { variants: [] },
|
||||||
|
},
|
||||||
|
GoCQHTTPSetModelShow: {
|
||||||
|
payload: { model: 'iPhone 13', model_show: 'iPhone 13' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
QuickAction: {
|
||||||
|
payload: { context: {}, operation: {} },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
SendGroupNotice: {
|
||||||
|
payload: { group_id: '123456', content: '公告内容', image: 'base64://...' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
SetGroupPortrait: {
|
||||||
|
payload: { group_id: '123456', file: 'base64://...' },
|
||||||
|
response: { result: 0, errMsg: '' },
|
||||||
|
},
|
||||||
|
SetQQProfile: {
|
||||||
|
payload: { nickname: '新昵称', personal_note: '个性签名' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
UploadGroupFile: {
|
||||||
|
payload: { group_id: '123456', file: '/path/to/file', name: 'test.txt' },
|
||||||
|
response: { file_id: 'file_uuid_123' },
|
||||||
|
},
|
||||||
|
UploadPrivateFile: {
|
||||||
|
payload: { user_id: '123456789', file: '/path/to/file', name: 'test.txt' },
|
||||||
|
response: { file_id: 'file_uuid_123' },
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
export const GroupActionsExamples = {
|
||||||
|
DelEssenceMsg: {
|
||||||
|
payload: { message_id: 123456 },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
DelGroupNotice: {
|
||||||
|
payload: { group_id: '123456', notice_id: 'notice_123' },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
GetGroupDetailInfo: {
|
||||||
|
payload: { group_id: '123456' },
|
||||||
|
response: { group_id: 123456, group_name: '测试群', member_count: 100, max_member_count: 500 },
|
||||||
|
},
|
||||||
|
GetGroupEssence: {
|
||||||
|
payload: { group_id: '123456' },
|
||||||
|
response: [{ message_id: 123456, sender_id: 123456, sender_nick: '昵称', operator_id: 123456, operator_nick: '昵称', operator_time: 1710000000, content: '精华内容' }],
|
||||||
|
},
|
||||||
|
GetGroupInfo: {
|
||||||
|
payload: { group_id: '123456' },
|
||||||
|
response: { group_id: 123456, group_name: '测试群', member_count: 100, max_member_count: 500 },
|
||||||
|
},
|
||||||
|
GetGroupList: {
|
||||||
|
payload: {},
|
||||||
|
response: [{ group_id: 123456, group_name: '测试群', member_count: 100, max_member_count: 500 }],
|
||||||
|
},
|
||||||
|
GetGroupMemberInfo: {
|
||||||
|
payload: { group_id: '123456', user_id: '123456789' },
|
||||||
|
response: { group_id: 123456, user_id: 123456789, nickname: '昵称', card: '名片', role: 'member' },
|
||||||
|
},
|
||||||
|
GetGroupMemberList: {
|
||||||
|
payload: { group_id: '123456' },
|
||||||
|
response: [{ group_id: 123456, user_id: 123456789, nickname: '昵称', card: '名片', role: 'member' }],
|
||||||
|
},
|
||||||
|
GetGroupNotice: {
|
||||||
|
payload: { group_id: '123456' },
|
||||||
|
response: [{ notice_id: 'notice_123', sender_id: 123456, publish_time: 1710000000, message: { text: '公告内容', image: [] } }],
|
||||||
|
},
|
||||||
|
SendGroupMsg: {
|
||||||
|
payload: { group_id: '123456', message: 'hello' },
|
||||||
|
response: { message_id: 123456 },
|
||||||
|
},
|
||||||
|
SetEssenceMsg: {
|
||||||
|
payload: { message_id: 123456 },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetGroupAddRequest: {
|
||||||
|
payload: { flag: 'flag_123', sub_type: 'add', approve: true },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetGroupAdmin: {
|
||||||
|
payload: { group_id: '123456', user_id: '123456789', enable: true },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetGroupBan: {
|
||||||
|
payload: { group_id: '123456', user_id: '123456789', duration: 1800 },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetGroupCard: {
|
||||||
|
payload: { group_id: '123456', user_id: '123456789', card: '新名片' },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetGroupKick: {
|
||||||
|
payload: { group_id: '123456', user_id: '123456789', reject_add_request: false },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetGroupLeave: {
|
||||||
|
payload: { group_id: '123456', is_dismiss: false },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetGroupName: {
|
||||||
|
payload: { group_id: '123456', group_name: '新群名' },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
SetGroupWholeBan: {
|
||||||
|
payload: { group_id: '123456', enable: true },
|
||||||
|
response: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export const GuildActionsExamples = {
|
||||||
|
GetGuildList: {
|
||||||
|
payload: {},
|
||||||
|
response: [{ guild_id: '123456', guild_name: '测试频道' }],
|
||||||
|
},
|
||||||
|
GetGuildProfile: {
|
||||||
|
payload: { guild_id: '123456' },
|
||||||
|
response: { guild_id: '123456', guild_name: '测试频道', guild_display_id: '123' },
|
||||||
|
},
|
||||||
|
};
|
||||||
10
packages/napcat-onebot/action/example/NewActionsExamples.ts
Normal file
10
packages/napcat-onebot/action/example/NewActionsExamples.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export const NewActionsExamples = {
|
||||||
|
GetDoubtFriendsAddRequest: {
|
||||||
|
payload: { count: 10 },
|
||||||
|
response: [{ user_id: 123456789, nickname: '昵称', age: 20, sex: 'male', reason: '申请理由', flag: 'flag_123' }],
|
||||||
|
},
|
||||||
|
SetDoubtFriendsAddRequest: {
|
||||||
|
payload: { flag: 'flag_123', approve: true },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export const PacketActionsExamples = {
|
||||||
|
GetPacketStatus: {
|
||||||
|
payload: {},
|
||||||
|
response: { status: 'ok' },
|
||||||
|
},
|
||||||
|
SendPoke: {
|
||||||
|
payload: { user_id: '123456789' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
SetGroupTodo: {
|
||||||
|
payload: { group_id: '123456', message_id: '123456789' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
export const SystemActionsExamples = {
|
||||||
|
CanSendImage: {
|
||||||
|
payload: {},
|
||||||
|
response: { yes: true },
|
||||||
|
},
|
||||||
|
CanSendRecord: {
|
||||||
|
payload: {},
|
||||||
|
response: { yes: true },
|
||||||
|
},
|
||||||
|
CleanCache: {
|
||||||
|
payload: {},
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
GetCredentials: {
|
||||||
|
payload: {},
|
||||||
|
response: { cookies: '...', csrf_token: 123456789 },
|
||||||
|
},
|
||||||
|
GetCSRF: {
|
||||||
|
payload: {},
|
||||||
|
response: { token: 123456789 },
|
||||||
|
},
|
||||||
|
GetLoginInfo: {
|
||||||
|
payload: {},
|
||||||
|
response: { user_id: 123456789, nickname: '机器人' },
|
||||||
|
},
|
||||||
|
GetStatus: {
|
||||||
|
payload: {},
|
||||||
|
response: { online: true, good: true },
|
||||||
|
},
|
||||||
|
GetSystemMsg: {
|
||||||
|
payload: {},
|
||||||
|
response: { invited_requests: [], join_requests: [] },
|
||||||
|
},
|
||||||
|
GetVersionInfo: {
|
||||||
|
payload: {},
|
||||||
|
response: { app_name: 'NapCatQQ', app_version: '1.0.0', protocol_version: 'v11' },
|
||||||
|
},
|
||||||
|
SetRestart: {
|
||||||
|
payload: { delay: 0 },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
38
packages/napcat-onebot/action/example/UserActionsExamples.ts
Normal file
38
packages/napcat-onebot/action/example/UserActionsExamples.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
export const UserActionsExamples = {
|
||||||
|
GetCookies: {
|
||||||
|
payload: { domain: 'qun.qq.com' },
|
||||||
|
response: { cookies: 'p_skey=xxx; p_uin=o0123456789;' },
|
||||||
|
},
|
||||||
|
GetFriendList: {
|
||||||
|
payload: {},
|
||||||
|
response: [{ user_id: 123456789, nickname: '昵称', remark: '备注' }],
|
||||||
|
},
|
||||||
|
GetRecentContact: {
|
||||||
|
payload: { count: 10 },
|
||||||
|
response: [
|
||||||
|
{
|
||||||
|
lastestMsg: 'hello',
|
||||||
|
peerUin: '123456789',
|
||||||
|
remark: 'remark',
|
||||||
|
msgTime: '1710000000',
|
||||||
|
chatType: 1,
|
||||||
|
msgId: '12345',
|
||||||
|
sendNickName: 'nick',
|
||||||
|
sendMemberName: 'card',
|
||||||
|
peerName: 'name',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
SendLike: {
|
||||||
|
payload: { user_id: '123456789', times: 10 },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
SetFriendAddRequest: {
|
||||||
|
payload: { flag: 'flag_123', approve: true, remark: '好友' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
SetFriendRemark: {
|
||||||
|
payload: { user_id: '123456789', remark: '新备注' },
|
||||||
|
response: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { OneBotAction } from '../OneBotAction';
|
import { OneBotAction } from '../OneBotAction';
|
||||||
|
import { Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
export class BotExit extends OneBotAction<void, void> {
|
export class BotExit extends OneBotAction<void, void> {
|
||||||
override actionName = ActionName.Exit;
|
override actionName = ActionName.Exit;
|
||||||
|
override payloadSchema = Type.Void();
|
||||||
|
override returnSchema = Type.Void();
|
||||||
|
override actionSummary = '退出登录';
|
||||||
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {};
|
||||||
|
override returnExample = null;
|
||||||
|
|
||||||
async _handle () {
|
async _handle () {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
@@ -2,21 +2,36 @@ import { ActionName } from '@/napcat-onebot/action/router';
|
|||||||
import { OneBotAction } from '../OneBotAction';
|
import { OneBotAction } from '../OneBotAction';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
group_id: Type.String({ description: '群号' }),
|
||||||
bot_appid: Type.String(),
|
bot_appid: Type.String({ description: '机器人AppID' }),
|
||||||
button_id: Type.String({ default: '' }),
|
button_id: Type.String({ default: '', description: '按钮ID' }),
|
||||||
callback_data: Type.String({ default: '' }),
|
callback_data: Type.String({ default: '', description: '回调数据' }),
|
||||||
msg_seq: Type.String({ default: '10086' }),
|
msg_seq: Type.String({ default: '10086', description: '消息序列号' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class ClickInlineKeyboardButton extends OneBotAction<Payload, unknown> {
|
const ReturnSchema = Type.Any({ description: '点击结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class ClickInlineKeyboardButton extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.ClickInlineKeyboardButton;
|
override actionName = ActionName.ClickInlineKeyboardButton;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
async _handle (payload: Payload) {
|
override actionSummary = '点击内联键盘按钮';
|
||||||
|
override actionTags = ['消息扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
bot_appid: '1234567890',
|
||||||
|
button_id: 'btn_1',
|
||||||
|
callback_data: '',
|
||||||
|
msg_seq: '10086'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
};
|
||||||
|
async _handle (payload: PayloadType) {
|
||||||
return await this.core.apis.MsgApi.clickInlineKeyboardButton({
|
return await this.core.apis.MsgApi.clickInlineKeyboardButton({
|
||||||
buttonId: payload.button_id,
|
buttonId: payload.button_id,
|
||||||
peerId: payload.group_id.toString(),
|
peerId: payload.group_id.toString(),
|
||||||
|
|||||||
@@ -2,18 +2,33 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Type, Static } from '@sinclair/typebox';
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
rawData: Type.String(),
|
rawData: Type.String({ description: '原始数据' }),
|
||||||
brief: Type.String(),
|
brief: Type.String({ description: '简要描述' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class CreateCollection extends OneBotAction<Payload, unknown> {
|
const ReturnSchema = Type.Any({ description: '创建结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class CreateCollection extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.CreateCollection;
|
override actionName = ActionName.CreateCollection;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '创建收藏';
|
||||||
|
override actionTags = ['扩展接口'];
|
||||||
|
override payloadExample = {
|
||||||
|
rawData: '收藏内容',
|
||||||
|
brief: '收藏标题'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
result: 0,
|
||||||
|
errMsg: ''
|
||||||
|
};
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
return await this.core.apis.CollectionApi.createCollection(
|
return await this.core.apis.CollectionApi.createCollection(
|
||||||
this.core.selfInfo.uin,
|
this.core.selfInfo.uin,
|
||||||
this.core.selfInfo.uid,
|
this.core.selfInfo.uid,
|
||||||
|
|||||||
@@ -2,19 +2,34 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.String(),
|
group_id: Type.String({ description: '群号' }),
|
||||||
album_id: Type.String(),
|
album_id: Type.String({ description: '相册ID' }),
|
||||||
lloc: Type.String(),
|
lloc: Type.String({ description: '媒体ID (lloc)' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class DelGroupAlbumMedia extends OneBotAction<Payload, unknown> {
|
const ReturnSchema = Type.Any({ description: '删除结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class DelGroupAlbumMedia extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.DelGroupAlbumMedia;
|
override actionName = ActionName.DelGroupAlbumMedia;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '删除群相册媒体';
|
||||||
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
album_id: 'album_id_1',
|
||||||
|
lloc: 'media_id_1',
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
result: {}
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
return await this.core.apis.WebApi.deleteAlbumMediaByNTQQ(
|
return await this.core.apis.WebApi.deleteAlbumMediaByNTQQ(
|
||||||
payload.group_id,
|
payload.group_id,
|
||||||
payload.album_id,
|
payload.album_id,
|
||||||
|
|||||||
@@ -2,20 +2,32 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
export const DoGroupAlbumCommentPayloadSchema = Type.Object({
|
||||||
group_id: Type.String(),
|
group_id: Type.String({ description: '群号' }),
|
||||||
album_id: Type.String(),
|
album_id: Type.String({ description: '相册 ID' }),
|
||||||
lloc: Type.String(),
|
lloc: Type.String({ description: '图片 ID' }),
|
||||||
content: Type.String(),
|
content: Type.String({ description: '评论内容' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
export type DoGroupAlbumCommentPayload = Static<typeof DoGroupAlbumCommentPayloadSchema>;
|
||||||
|
|
||||||
export class DoGroupAlbumComment extends OneBotAction<Payload, unknown> {
|
export class DoGroupAlbumComment extends OneBotAction<DoGroupAlbumCommentPayload, any> {
|
||||||
override actionName = ActionName.DoGroupAlbumComment;
|
override actionName = ActionName.DoGroupAlbumComment;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '发表群相册评论';
|
||||||
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
album_id: 'album_id_1',
|
||||||
|
lloc: 'media_id_1',
|
||||||
|
content: '很有意思'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
result: {}
|
||||||
|
};
|
||||||
|
override payloadSchema = DoGroupAlbumCommentPayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '评论结果' });
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: DoGroupAlbumCommentPayload) {
|
||||||
return await this.core.apis.WebApi.doAlbumMediaPlainCommentByNTQQ(
|
return await this.core.apis.WebApi.doAlbumMediaPlainCommentByNTQQ(
|
||||||
payload.group_id,
|
payload.group_id,
|
||||||
payload.album_id,
|
payload.album_id,
|
||||||
|
|||||||
@@ -2,18 +2,32 @@ import { Type, Static } from '@sinclair/typebox';
|
|||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
count: Type.Union([Type.Number(), Type.String()], { default: 48 }),
|
count: Type.Union([Type.Number(), Type.String()], { default: 48, description: '获取数量' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class FetchCustomFace extends OneBotAction<Payload, string[]> {
|
const ReturnSchema = Type.Array(Type.String(), { description: '表情URL列表' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class FetchCustomFace extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.FetchCustomFace;
|
override actionName = ActionName.FetchCustomFace;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取自定义表情';
|
||||||
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
count: 10
|
||||||
|
};
|
||||||
|
override returnExample = [
|
||||||
|
'http://example.com/face1.png'
|
||||||
|
];
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const ret = await this.core.apis.MsgApi.fetchFavEmojiList(+payload.count);
|
const ret = await this.core.apis.MsgApi.fetchFavEmojiList(+payload.count);
|
||||||
return ret.emojiInfoList.map(e => e.url);
|
return ret.emojiInfoList.map(e => e.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,28 +2,68 @@ import { Type, Static } from '@sinclair/typebox';
|
|||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { MessageUnique } from 'napcat-common/src/message-unique';
|
import { MessageUnique } from 'napcat-common/src/message-unique';
|
||||||
import { type NTQQMsgApi } from 'napcat-core/apis';
|
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
message_id: Type.Union([Type.Number(), Type.String()]),
|
message_id: Type.Union([Type.Number(), Type.String()], { description: '消息ID' }),
|
||||||
emojiId: Type.Union([Type.Number(), Type.String()]),
|
emojiId: Type.Union([Type.Number(), Type.String()], { description: '表情ID' }),
|
||||||
emojiType: Type.Union([Type.Number(), Type.String()]),
|
emojiType: Type.Union([Type.Number(), Type.String()], { description: '表情类型' }),
|
||||||
count: Type.Union([Type.Number(), Type.String()], { default: 20 }),
|
count: Type.Union([Type.Number(), Type.String()], { default: 20, description: '获取数量' }),
|
||||||
|
cookie: Type.String({ default: '', description: '分页Cookie' })
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class FetchEmojiLike extends OneBotAction<Payload, Awaited<ReturnType<NTQQMsgApi['getMsgEmojiLikesList']>>> {
|
const ReturnSchema = Type.Object({
|
||||||
|
emojiLikesList: Type.Array(Type.Object({
|
||||||
|
tinyId: Type.String({ description: 'TinyID' }),
|
||||||
|
nickName: Type.String({ description: '昵称' }),
|
||||||
|
headUrl: Type.String({ description: '头像URL' }),
|
||||||
|
}), { description: '表情回应列表' }),
|
||||||
|
cookie: Type.String({ description: '分页Cookie' }),
|
||||||
|
isLastPage: Type.Boolean({ description: '是否最后一页' }),
|
||||||
|
isFirstPage: Type.Boolean({ description: '是否第一页' }),
|
||||||
|
result: Type.Number({ description: '结果状态码' }),
|
||||||
|
errMsg: Type.String({ description: '错 误信息' }),
|
||||||
|
}, { description: '表情回应详情' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class FetchEmojiLike extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.FetchEmojiLike;
|
override actionName = ActionName.FetchEmojiLike;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '获取表情点赞详情';
|
||||||
|
override actionTags = ['消息扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
message_id: 12345,
|
||||||
|
emojiId: '123',
|
||||||
|
emojiType: 1,
|
||||||
|
count: 10,
|
||||||
|
cookie: ''
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
emojiLikesList: [
|
||||||
|
{
|
||||||
|
tinyId: '123456',
|
||||||
|
nickName: '测试用户',
|
||||||
|
headUrl: 'http://example.com/avatar.png'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
cookie: '',
|
||||||
|
isLastPage: true,
|
||||||
|
isFirstPage: true,
|
||||||
|
result: 0,
|
||||||
|
errMsg: ''
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id);
|
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id);
|
||||||
if (!msgIdPeer) throw new Error('消息不存在');
|
if (!msgIdPeer) throw new Error('消息不存在');
|
||||||
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
|
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
|
||||||
if (!msg) throw new Error('消息不存在');
|
if (!msg) throw new Error('消息不存在');
|
||||||
return await this.core.apis.MsgApi.getMsgEmojiLikesList(
|
const res = await this.core.apis.MsgApi.getMsgEmojiLikesList(
|
||||||
msgIdPeer.Peer, msg.msgSeq, payload.emojiId.toString(), payload.emojiType.toString(), +payload.count
|
msgIdPeer.Peer, msg.msgSeq, payload.emojiId.toString(), payload.emojiType.toString(), payload.cookie, +payload.count
|
||||||
);
|
);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,46 @@
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { AIVoiceChatType } from 'napcat-core/packet/entities/aiChat';
|
|
||||||
import { Type, Static } from '@sinclair/typebox';
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
import { ExtendsActionsExamples } from '../example/ExtendsActionsExamples';
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
|
||||||
chat_type: Type.Union([Type.Union([Type.Number(), Type.String()])], { default: 1 }),
|
const PayloadSchema = Type.Object({
|
||||||
|
group_id: Type.String({ description: '群号' }),
|
||||||
|
chat_type: Type.Union([Type.Number(), Type.String()], { default: 1, description: '聊天类型' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
interface GetAiCharactersResponse {
|
const ReturnSchema = Type.Array(
|
||||||
type: string;
|
Type.Object({
|
||||||
characters: {
|
type: Type.String({ description: '角色类型' }),
|
||||||
character_id: string;
|
characters: Type.Array(
|
||||||
character_name: string;
|
Type.Object({
|
||||||
preview_url: string;
|
character_id: Type.String({ description: '角色ID' }),
|
||||||
}[];
|
character_name: Type.String({ description: '角色名称' }),
|
||||||
}
|
preview_url: Type.String({ description: '预览URL' }),
|
||||||
|
}),
|
||||||
|
{ description: '角色列表' }
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
{ description: 'AI角色列表' }
|
||||||
|
);
|
||||||
|
|
||||||
export class GetAiCharacters extends GetPacketStatusDepends<Payload, GetAiCharactersResponse[]> {
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetAiCharacters extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.GetAiCharacters;
|
override actionName = ActionName.GetAiCharacters;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取AI角色列表';
|
||||||
|
override actionDescription = '获取群聊中的AI角色列表';
|
||||||
|
override actionTags = ['扩展接口'];
|
||||||
|
override payloadExample = ExtendsActionsExamples.GetAiCharacters.payload;
|
||||||
|
override returnExample = ExtendsActionsExamples.GetAiCharacters.response;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const rawList = await this.core.apis.PacketApi.pkt.operation.FetchAiVoiceList(+payload.group_id, +payload.chat_type as AIVoiceChatType);
|
const chatTypeNum = Number(payload.chat_type);
|
||||||
|
const rawList = await this.core.apis.PacketApi.pkt.operation.FetchAiVoiceList(+payload.group_id, chatTypeNum);
|
||||||
return rawList?.map((item) => ({
|
return rawList?.map((item) => ({
|
||||||
type: item.category,
|
type: item.category,
|
||||||
characters: item.voices.map((voice) => ({
|
characters: item.voices.map((voice) => ({
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { OneBotAction } from '../OneBotAction';
|
import { OneBotAction } from '../OneBotAction';
|
||||||
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
|
||||||
interface GetClientkeyResponse {
|
import { ExtendsActionsExamples } from '../example/ExtendsActionsExamples';
|
||||||
clientkey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GetClientkey extends OneBotAction<void, GetClientkeyResponse> {
|
const ReturnSchema = Type.Object({
|
||||||
|
clientkey: Type.Optional(Type.String({ description: '客户端Key' })),
|
||||||
|
}, { description: '获取ClientKey结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetClientkey extends OneBotAction<void, ReturnType> {
|
||||||
override actionName = ActionName.GetClientkey;
|
override actionName = ActionName.GetClientkey;
|
||||||
|
override payloadSchema = Type.Void();
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取ClientKey';
|
||||||
|
override actionDescription = '获取当前登录帐号的ClientKey';
|
||||||
|
override actionTags = ['扩展接口'];
|
||||||
|
override payloadExample = ExtendsActionsExamples.GetClientkey.payload;
|
||||||
|
override returnExample = ExtendsActionsExamples.GetClientkey.response;
|
||||||
|
|
||||||
async _handle () {
|
async _handle () {
|
||||||
return { clientkey: (await this.core.apis.UserApi.forceFetchClientKey()).clientKey };
|
return { clientkey: (await this.core.apis.UserApi.forceFetchClientKey()).clientKey };
|
||||||
|
|||||||
@@ -1,20 +1,81 @@
|
|||||||
import { type NTQQCollectionApi } from 'napcat-core/apis/collection';
|
|
||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Type, Static } from '@sinclair/typebox';
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
category: Type.Union([Type.Number(), Type.String()]),
|
category: Type.String({ description: '分类ID' }),
|
||||||
count: Type.Union([Type.Union([Type.Number(), Type.String()])], { default: 1 }),
|
count: Type.String({ default: '50', description: '获取数量' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class GetCollectionList extends OneBotAction<Payload, Awaited<ReturnType<NTQQCollectionApi['getAllCollection']>>> {
|
const ReturnSchema = Type.Any({ description: '收藏列表' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetCollectionList extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.GetCollectionList;
|
override actionName = ActionName.GetCollectionList;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取收藏列表';
|
||||||
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
category: '0',
|
||||||
|
count: '50'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
errCode: 0,
|
||||||
|
errMsg: "",
|
||||||
|
collectionSearchList: {
|
||||||
|
collectionItemList: [
|
||||||
|
{
|
||||||
|
cid: "123456",
|
||||||
|
type: 8,
|
||||||
|
status: 1,
|
||||||
|
author: {
|
||||||
|
type: 2,
|
||||||
|
numId: "123456",
|
||||||
|
strId: "昵称",
|
||||||
|
groupId: "123456",
|
||||||
|
groupName: "群名",
|
||||||
|
uid: "123456"
|
||||||
|
},
|
||||||
|
bid: 1,
|
||||||
|
category: 1,
|
||||||
|
createTime: "1769169157000",
|
||||||
|
collectTime: "1769413477691",
|
||||||
|
modifyTime: "1769413477691",
|
||||||
|
sequence: "1769413476735",
|
||||||
|
shareUrl: "",
|
||||||
|
customGroupId: 0,
|
||||||
|
securityBeat: false,
|
||||||
|
summary: {
|
||||||
|
textSummary: null,
|
||||||
|
linkSummary: null,
|
||||||
|
gallerySummary: null,
|
||||||
|
audioSummary: null,
|
||||||
|
videoSummary: null,
|
||||||
|
fileSummary: null,
|
||||||
|
locationSummary: null,
|
||||||
|
richMediaSummary: {
|
||||||
|
title: "",
|
||||||
|
subTitle: "",
|
||||||
|
brief: "text",
|
||||||
|
picList: [],
|
||||||
|
contentType: 1,
|
||||||
|
originalUri: "",
|
||||||
|
publisher: "",
|
||||||
|
richMediaVersion: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
hasMore: false,
|
||||||
|
bottomTimeStamp: "1769413477691"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
return await this.core.apis.CollectionApi.getAllCollection(+payload.category, +payload.count);
|
return await this.core.apis.CollectionApi.getAllCollection(+payload.category, +payload.count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
88
packages/napcat-onebot/action/extends/GetEmojiLikes.ts
Normal file
88
packages/napcat-onebot/action/extends/GetEmojiLikes.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { MessageUnique } from 'napcat-common/src/message-unique';
|
||||||
|
import { Peer, ChatType } from '@/napcat-core';
|
||||||
|
|
||||||
|
const PayloadSchema = Type.Object({
|
||||||
|
group_id: Type.Optional(Type.String({ description: '群号,短ID可不传' })),
|
||||||
|
message_id: Type.String({ description: '消息ID,可以传递长ID或短ID' }),
|
||||||
|
emoji_id: Type.String({ description: '表情ID' }),
|
||||||
|
emoji_type: Type.Optional(Type.String({ description: '表情类型' })),
|
||||||
|
count: Type.Number({ default: 0, description: '数量,0代表全部' }),
|
||||||
|
});
|
||||||
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
|
const ReturnSchema = Type.Object({
|
||||||
|
emoji_like_list: Type.Array(
|
||||||
|
Type.Object({
|
||||||
|
user_id: Type.String({ description: '点击者QQ号' }),
|
||||||
|
nick_name: Type.String({ description: '昵称?' }),
|
||||||
|
}),
|
||||||
|
{ description: '表情回应列表' }
|
||||||
|
),
|
||||||
|
});
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetEmojiLikes extends OneBotAction<PayloadType, ReturnType> {
|
||||||
|
override actionName = ActionName.GetEmojiLikes;
|
||||||
|
override actionSummary = '获取消息表情点赞列表';
|
||||||
|
override actionTags = ['消息扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
message_id: '12345',
|
||||||
|
emoji_id: '123'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
emoji_like_list: [
|
||||||
|
{
|
||||||
|
user_id: '654321',
|
||||||
|
nick_name: '测试用户'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
|
async _handle (payload: PayloadType) {
|
||||||
|
let peer: Peer;
|
||||||
|
let msgId: string;
|
||||||
|
|
||||||
|
if (MessageUnique.isShortId(payload.message_id)) {
|
||||||
|
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id);
|
||||||
|
if (!msgIdPeer) throw new Error('消息不存在');
|
||||||
|
peer = msgIdPeer.Peer;
|
||||||
|
msgId = msgIdPeer.MsgId;
|
||||||
|
} else {
|
||||||
|
if (!payload.group_id) throw new Error('长ID模式下必须提供群号');
|
||||||
|
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id };
|
||||||
|
msgId = payload.message_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgId])).msgList[0];
|
||||||
|
if (!msg) throw new Error('消息不存在');
|
||||||
|
|
||||||
|
const emojiType = payload.emoji_type ?? (payload.emoji_id.length > 3 ? '2' : '1');
|
||||||
|
const emojiLikeList: Array<{ user_id: string; nick_name: string; }> = [];
|
||||||
|
let cookie = '';
|
||||||
|
let needFetchCount = payload.count == 0 ? 200 : Math.ceil(payload.count / 15);
|
||||||
|
for (let page = 0; page < needFetchCount; page++) {
|
||||||
|
const res = await this.core.apis.MsgApi.getMsgEmojiLikesList(
|
||||||
|
peer, msg.msgSeq, payload.emoji_id.toString(), emojiType, cookie, 15
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Array.isArray(res.emojiLikesList)) {
|
||||||
|
for (const like of res.emojiLikesList) {
|
||||||
|
emojiLikeList.push({ user_id: like.tinyId, nick_name: like.nickName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.isLastPage || !res.cookie) break;
|
||||||
|
cookie = res.cookie;
|
||||||
|
}
|
||||||
|
// 切断多余部分
|
||||||
|
if (payload.count > 0) {
|
||||||
|
emojiLikeList.splice(payload.count);
|
||||||
|
}
|
||||||
|
return { emoji_like_list: emojiLikeList };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,40 @@
|
|||||||
import { OB11Construct } from '@/napcat-onebot/helper/data';
|
import { OB11Construct } from '@/napcat-onebot/helper/data';
|
||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
import { OB11UserSchema } from '../schemas';
|
||||||
|
|
||||||
export class GetFriendWithCategory extends OneBotAction<void, unknown> {
|
const ReturnSchema = Type.Array(
|
||||||
|
Type.Object({
|
||||||
|
categoryId: Type.Number({ description: '分组ID' }),
|
||||||
|
categoryName: Type.String({ description: '分组名称' }),
|
||||||
|
categoryMbCount: Type.Number({ description: '分组内好友数量' }),
|
||||||
|
buddyList: Type.Array(OB11UserSchema, { description: '好友列表' }),
|
||||||
|
}),
|
||||||
|
{ description: '带分组的好友列表' }
|
||||||
|
);
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetFriendWithCategory extends OneBotAction<void, ReturnType> {
|
||||||
override actionName = ActionName.GetFriendsWithCategory;
|
override actionName = ActionName.GetFriendsWithCategory;
|
||||||
|
override payloadSchema = Type.Void();
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取带分组的好友列表';
|
||||||
|
override actionTags = ['用户扩展'];
|
||||||
|
override payloadExample = {};
|
||||||
|
override returnExample = [
|
||||||
|
{
|
||||||
|
categoryId: 1,
|
||||||
|
categoryName: '我的好友',
|
||||||
|
categoryMbCount: 1,
|
||||||
|
buddyList: []
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
async _handle () {
|
async _handle () {
|
||||||
return (await this.core.apis.FriendApi.getBuddyV2ExWithCate()).map(category => ({
|
const categories = await this.core.apis.FriendApi.getBuddyV2ExWithCate();
|
||||||
|
return categories.map(category => ({
|
||||||
...category,
|
...category,
|
||||||
buddyList: OB11Construct.friends(category.buddyList),
|
buddyList: OB11Construct.friends(category.buddyList),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,16 +1,51 @@
|
|||||||
import { GroupNotifyMsgStatus } from 'napcat-core';
|
import { GroupNotifyMsgStatus } from 'napcat-core';
|
||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Notify } from '@/napcat-onebot/types';
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
|
||||||
export default class GetGroupAddRequest extends OneBotAction<null, Notify[] | null> {
|
const ReturnSchema = Type.Array(
|
||||||
|
Type.Object({
|
||||||
|
request_id: Type.Number({ description: '请求ID' }),
|
||||||
|
invitor_uin: Type.Number({ description: '邀请者QQ' }),
|
||||||
|
invitor_nick: Type.Optional(Type.String({ description: '邀请者昵称' })),
|
||||||
|
group_id: Type.Number({ description: '群号' }),
|
||||||
|
message: Type.Optional(Type.String({ description: '验证信息' })),
|
||||||
|
group_name: Type.Optional(Type.String({ description: '群名称' })),
|
||||||
|
checked: Type.Boolean({ description: '是否已处理' }),
|
||||||
|
actor: Type.Number({ description: '处理者QQ' }),
|
||||||
|
requester_nick: Type.Optional(Type.String({ description: '请求者昵称' })),
|
||||||
|
}),
|
||||||
|
{ description: '群通知列表' }
|
||||||
|
);
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export default class GetGroupAddRequest extends OneBotAction<void, ReturnType> {
|
||||||
override actionName = ActionName.GetGroupIgnoreAddRequest;
|
override actionName = ActionName.GetGroupIgnoreAddRequest;
|
||||||
|
override payloadSchema = Type.Void();
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取群被忽略的加群请求';
|
||||||
|
override actionTags = ['群组接口'];
|
||||||
|
override payloadExample = {};
|
||||||
|
override returnExample = [
|
||||||
|
{
|
||||||
|
request_id: 12345,
|
||||||
|
invitor_uin: 123456789,
|
||||||
|
invitor_nick: '邀请者',
|
||||||
|
group_id: 123456789,
|
||||||
|
message: '加群请求',
|
||||||
|
group_name: '群名称',
|
||||||
|
checked: false,
|
||||||
|
actor: 0,
|
||||||
|
requester_nick: '请求者'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
async _handle (): Promise<Notify[] | null> {
|
async _handle (): Promise<ReturnType> {
|
||||||
const NTQQUserApi = this.core.apis.UserApi;
|
const NTQQUserApi = this.core.apis.UserApi;
|
||||||
const NTQQGroupApi = this.core.apis.GroupApi;
|
const NTQQGroupApi = this.core.apis.GroupApi;
|
||||||
const ignoredNotifies = await NTQQGroupApi.getSingleScreenNotifies(true, 10);
|
const ignoredNotifies = await NTQQGroupApi.getSingleScreenNotifies(true, 10);
|
||||||
const retData: Notify[] = [];
|
const retData: ReturnType = [];
|
||||||
|
|
||||||
const notifyPromises = ignoredNotifies
|
const notifyPromises = ignoredNotifies
|
||||||
.filter(notify => notify.type === 7)
|
.filter(notify => notify.type === 7)
|
||||||
|
|||||||
@@ -2,19 +2,35 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.String(),
|
group_id: Type.String({ description: '群号' }),
|
||||||
album_id: Type.String(),
|
album_id: Type.String({ description: '相册ID' }),
|
||||||
attach_info: Type.String({ default: '' }),
|
attach_info: Type.String({ default: '', description: '附加信息(用于分页)' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class GetGroupAlbumMediaList extends OneBotAction<Payload, unknown> {
|
const ReturnSchema = Type.Any({ description: '相册媒体列表' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetGroupAlbumMediaList extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.GetGroupAlbumMediaList;
|
override actionName = ActionName.GetGroupAlbumMediaList;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '获取群相册媒体列表';
|
||||||
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
album_id: 'album_id_1',
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
media_list: [
|
||||||
|
{ media_id: 'media_id_1', url: 'http://example.com/1.jpg' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
return await this.core.apis.WebApi.getAlbumMediaListByNTQQ(
|
return await this.core.apis.WebApi.getAlbumMediaListByNTQQ(
|
||||||
payload.group_id,
|
payload.group_id,
|
||||||
payload.album_id,
|
payload.album_id,
|
||||||
|
|||||||
@@ -1,17 +1,30 @@
|
|||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Type, Static } from '@sinclair/typebox';
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
group_id: Type.String({ description: '群号' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class GetGroupInfoEx extends OneBotAction<Payload, unknown> {
|
const ReturnSchema = Type.Any({ description: '群扩展信息' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetGroupInfoEx extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.GetGroupInfoEx;
|
override actionName = ActionName.GetGroupInfoEx;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '获取群详细信息 (扩展)';
|
||||||
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
return (await this.core.apis.GroupApi.getGroupExtFE0Info([payload.group_id.toString()])).result.groupExtInfos.get(payload.group_id.toString());
|
return (await this.core.apis.GroupApi.getGroupExtFE0Info([payload.group_id.toString()])).result.groupExtInfos.get(payload.group_id.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,79 @@
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { MiniAppInfo, MiniAppInfoHelper } from 'napcat-core/packet/utils/helper/miniAppHelper';
|
import { MiniAppInfo, MiniAppInfoHelper } from 'napcat-core/packet/utils/helper/miniAppHelper';
|
||||||
import { MiniAppData, MiniAppRawData, MiniAppReqCustomParams, MiniAppReqParams } from 'napcat-core/packet/entities/miniApp';
|
import { MiniAppReqCustomParams, MiniAppReqParams } from 'napcat-core/packet/entities/miniApp';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Union([
|
const PayloadSchema = Type.Union([
|
||||||
Type.Object({
|
Type.Object({
|
||||||
type: Type.Union([Type.Literal('bili'), Type.Literal('weibo')]),
|
type: Type.Union([Type.Literal('bili'), Type.Literal('weibo')], { description: '模板类型' }),
|
||||||
title: Type.String(),
|
title: Type.String({ description: '标题' }),
|
||||||
desc: Type.String(),
|
desc: Type.String({ description: '描述' }),
|
||||||
picUrl: Type.String(),
|
picUrl: Type.String({ description: '图片URL' }),
|
||||||
jumpUrl: Type.String(),
|
jumpUrl: Type.String({ description: '跳转URL' }),
|
||||||
webUrl: Type.Optional(Type.String()),
|
webUrl: Type.Optional(Type.String({ description: '网页URL' })),
|
||||||
rawArkData: Type.Optional(Type.Union([Type.String()])),
|
rawArkData: Type.Optional(Type.Union([Type.String()], { description: '是否返回原始Ark数据' })),
|
||||||
}),
|
}),
|
||||||
Type.Object({
|
Type.Object({
|
||||||
title: Type.String(),
|
title: Type.String({ description: '标题' }),
|
||||||
desc: Type.String(),
|
desc: Type.String({ description: '描述' }),
|
||||||
picUrl: Type.String(),
|
picUrl: Type.String({ description: '图片URL' }),
|
||||||
jumpUrl: Type.String(),
|
jumpUrl: Type.String({ description: '跳转URL' }),
|
||||||
iconUrl: Type.String(),
|
iconUrl: Type.String({ description: '图标URL' }),
|
||||||
webUrl: Type.Optional(Type.String()),
|
webUrl: Type.Optional(Type.String({ description: '网页URL' })),
|
||||||
appId: Type.String(),
|
appId: Type.String({ description: '小程序AppID' }),
|
||||||
scene: Type.Union([Type.Number(), Type.String()]),
|
scene: Type.String({ description: '场景ID' }),
|
||||||
templateType: Type.Union([Type.Number(), Type.String()]),
|
templateType: Type.String({ description: '模板类型' }),
|
||||||
businessType: Type.Union([Type.Number(), Type.String()]),
|
businessType: Type.String({ description: '业务类型' }),
|
||||||
verType: Type.Union([Type.Number(), Type.String()]),
|
verType: Type.String({ description: '版本类型' }),
|
||||||
shareType: Type.Union([Type.Number(), Type.String()]),
|
shareType: Type.String({ description: '分享类型' }),
|
||||||
versionId: Type.String(),
|
versionId: Type.String({ description: '版本ID' }),
|
||||||
sdkId: Type.String(),
|
sdkId: Type.String({ description: 'SDK ID' }),
|
||||||
withShareTicket: Type.Union([Type.Number(), Type.String()]),
|
withShareTicket: Type.String({ description: '是否携带分享票据' }),
|
||||||
rawArkData: Type.Optional(Type.Union([Type.String()])),
|
rawArkData: Type.Optional(Type.String({ description: '是否返回原始Ark数据' })),
|
||||||
}),
|
}),
|
||||||
]);
|
], { description: '小程序Ark参数' });
|
||||||
type Payload = Static<typeof SchemaData>;
|
|
||||||
|
|
||||||
export class GetMiniAppArk extends GetPacketStatusDepends<Payload, {
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
data: MiniAppData | MiniAppRawData
|
|
||||||
}> {
|
const ReturnSchema = Type.Object({
|
||||||
|
data: Type.Any({ description: 'Ark数据' }),
|
||||||
|
}, { description: '获取小程序Ark结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetMiniAppArk extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.GetMiniAppArk;
|
override actionName = ActionName.GetMiniAppArk;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema; override actionSummary = '获取小程序 Ark';
|
||||||
async _handle (payload: Payload) {
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
type: 'bili',
|
||||||
|
title: '测试标题',
|
||||||
|
desc: '测试描述',
|
||||||
|
picUrl: 'http://example.com/pic.jpg',
|
||||||
|
jumpUrl: 'http://example.com'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
data: {
|
||||||
|
ark: 'ark_content'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
async _handle (payload: PayloadType) {
|
||||||
let reqParam: MiniAppReqParams;
|
let reqParam: MiniAppReqParams;
|
||||||
const customParams = {
|
const customParams: MiniAppReqCustomParams = {
|
||||||
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,
|
webUrl: payload.webUrl ?? '',
|
||||||
} as MiniAppReqCustomParams;
|
};
|
||||||
if ('type' in payload) {
|
if ('type' in payload) {
|
||||||
reqParam = MiniAppInfoHelper.generateReq(customParams, MiniAppInfo.get(payload.type)!.template);
|
const template = MiniAppInfo.get(payload.type)?.template;
|
||||||
|
if (!template) {
|
||||||
|
throw new Error('未知的模板类型');
|
||||||
|
}
|
||||||
|
reqParam = MiniAppInfoHelper.generateReq(customParams, template);
|
||||||
} else {
|
} else {
|
||||||
const { appId, scene, iconUrl, templateType, businessType, verType, shareType, versionId, withShareTicket } = payload;
|
const { appId, scene, iconUrl, templateType, businessType, verType, shareType, versionId, withShareTicket } = payload;
|
||||||
reqParam = MiniAppInfoHelper.generateReq(
|
reqParam = MiniAppInfoHelper.generateReq(
|
||||||
|
|||||||
@@ -1,36 +1,65 @@
|
|||||||
import { NTVoteInfo } from 'napcat-core';
|
|
||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Type, Static } from '@sinclair/typebox';
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
user_id: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
user_id: Type.Optional(Type.String({ description: 'QQ号' })),
|
||||||
start: Type.Union([Type.Number(), Type.String()], { default: 0 }),
|
start: Type.Union([Type.Number(), Type.String()], { default: 0, description: '起始位置' }),
|
||||||
count: Type.Union([Type.Number(), Type.String()], { default: 10 }),
|
count: Type.Union([Type.Number(), Type.String()], { default: 10, description: '获取数量' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class GetProfileLike extends OneBotAction<Payload, {
|
const ReturnSchema = Type.Object({
|
||||||
uid: string;
|
uid: Type.String({ description: '用户UID' }),
|
||||||
time: string;
|
time: Type.String({ description: '时间' }),
|
||||||
favoriteInfo: {
|
favoriteInfo: Type.Object({
|
||||||
userInfos: Array<NTVoteInfo>;
|
userInfos: Type.Array(Type.Any(), { description: '点赞用户信息' }),
|
||||||
total_count: number;
|
total_count: Type.Number({ description: '总点赞数' }),
|
||||||
last_time: number;
|
last_time: Type.Number({ description: '最后点赞时间' }),
|
||||||
today_count: number;
|
today_count: Type.Number({ description: '今日点赞数' }),
|
||||||
};
|
}),
|
||||||
voteInfo: {
|
voteInfo: Type.Object({
|
||||||
total_count: number;
|
total_count: Type.Number({ description: '总点赞数' }),
|
||||||
new_count: number;
|
new_count: Type.Number({ description: '新增点赞数' }),
|
||||||
new_nearby_count: number;
|
new_nearby_count: Type.Number({ description: '新增附近点赞数' }),
|
||||||
last_visit_time: number;
|
last_visit_time: Type.Number({ description: '最后访问时间' }),
|
||||||
userInfos: Array<NTVoteInfo>;
|
userInfos: Type.Array(Type.Any(), { description: '点赞用户信息' }),
|
||||||
};
|
}),
|
||||||
}> {
|
}, { description: '点赞详情' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetProfileLike extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.GetProfileLike;
|
override actionName = ActionName.GetProfileLike;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
async _handle (payload: Payload) {
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取资料点赞';
|
||||||
|
override actionTags = ['用户扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
user_id: '123456789',
|
||||||
|
start: 0,
|
||||||
|
count: 10
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
uid: 'u_123',
|
||||||
|
time: '1734567890',
|
||||||
|
favoriteInfo: {
|
||||||
|
userInfos: [],
|
||||||
|
total_count: 10,
|
||||||
|
last_time: 1734567890,
|
||||||
|
today_count: 5
|
||||||
|
},
|
||||||
|
voteInfo: {
|
||||||
|
total_count: 100,
|
||||||
|
new_count: 2,
|
||||||
|
new_nearby_count: 0,
|
||||||
|
last_visit_time: 1734567890,
|
||||||
|
userInfos: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const isSelf = this.core.selfInfo.uin === payload.user_id || !payload.user_id;
|
const isSelf = this.core.selfInfo.uin === payload.user_id || !payload.user_id;
|
||||||
const userUid = isSelf || !payload.user_id ? this.core.selfInfo.uid : await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
const userUid = isSelf || !payload.user_id ? this.core.selfInfo.uid : await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
const type = isSelf ? 2 : 1;
|
const type = isSelf ? 2 : 1;
|
||||||
|
|||||||
@@ -1,18 +1,37 @@
|
|||||||
import { NTQQWebApi } from 'napcat-core/apis';
|
|
||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
const SchemaData = Type.Object({
|
import { NTQQWebApi } from 'napcat-core/apis';
|
||||||
group_id: Type.String(),
|
|
||||||
|
const PayloadSchema = Type.Object({
|
||||||
|
group_id: Type.String({ description: '群号' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class GetQunAlbumList extends OneBotAction<Payload, Awaited<ReturnType<NTQQWebApi['getAlbumListByNTQQ']>>['response']['album_list']> {
|
const ReturnSchema = Type.Array(Type.Any(), { description: '群相册列表' });
|
||||||
|
|
||||||
|
type GetQunAlbumListReturn = Awaited<globalThis.ReturnType<NTQQWebApi['getAlbumListByNTQQ']>>['response']['album_list'];
|
||||||
|
|
||||||
|
export class GetQunAlbumList extends OneBotAction<PayloadType, GetQunAlbumListReturn> {
|
||||||
override actionName = ActionName.GetQunAlbumList;
|
override actionName = ActionName.GetQunAlbumList;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '获取群相册列表';
|
||||||
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
};
|
||||||
|
override returnExample = [
|
||||||
|
{
|
||||||
|
album_id: 'album_1',
|
||||||
|
album_name: '测试相册',
|
||||||
|
cover_url: 'http://example.com/cover.jpg',
|
||||||
|
create_time: 1734567890
|
||||||
|
}
|
||||||
|
];
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType): Promise<GetQunAlbumListReturn> {
|
||||||
return (await this.core.apis.WebApi.getAlbumListByNTQQ(payload.group_id)).response.album_list;
|
return (await this.core.apis.WebApi.getAlbumListByNTQQ(payload.group_id)).response.album_list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,24 @@
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
|
||||||
export class GetRkey extends GetPacketStatusDepends<void, Array<unknown>> {
|
const ReturnSchema = Type.Array(Type.Any(), { description: 'Rkey列表' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetRkey extends GetPacketStatusDepends<void, ReturnType> {
|
||||||
override actionName = ActionName.GetRkey;
|
override actionName = ActionName.GetRkey;
|
||||||
|
override payloadSchema = Type.Void();
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取 RKey';
|
||||||
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {};
|
||||||
|
override returnExample = [
|
||||||
|
{
|
||||||
|
"key": "rkey_value",
|
||||||
|
"expired": 1734567890
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
async _handle () {
|
async _handle () {
|
||||||
return await this.core.apis.PacketApi.pkt.operation.FetchRkey();
|
return await this.core.apis.PacketApi.pkt.operation.FetchRkey();
|
||||||
|
|||||||
@@ -1,8 +1,21 @@
|
|||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
|
||||||
export class GetRobotUinRange extends OneBotAction<void, Array<unknown>> {
|
const ReturnSchema = Type.Array(Type.Any(), { description: '机器人Uin范围列表' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetRobotUinRange extends OneBotAction<void, ReturnType> {
|
||||||
override actionName = ActionName.GetRobotUinRange;
|
override actionName = ActionName.GetRobotUinRange;
|
||||||
|
override actionSummary = '获取机器人 UIN 范围';
|
||||||
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {};
|
||||||
|
override returnExample = [
|
||||||
|
{ minUin: '12345678', maxUin: '87654321' }
|
||||||
|
];
|
||||||
|
override payloadSchema = Type.Void();
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle () {
|
async _handle () {
|
||||||
return await this.core.apis.UserApi.getRobotUinRange();
|
return await this.core.apis.UserApi.getRobotUinRange();
|
||||||
|
|||||||
@@ -2,26 +2,37 @@ import { PacketBuf } from 'napcat-core/packet/transformer/base';
|
|||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { ProtoBuf, ProtoBufBase, PBUint32, PBString } from 'napcat.protobuf';
|
import { ProtoBuf, ProtoBufBase, PBUint32, PBString } from 'napcat.protobuf';
|
||||||
|
import { Type, Static } from '@sinclair/typebox';
|
||||||
|
|
||||||
interface Friend {
|
const ReturnSchema = Type.Array(
|
||||||
uin: number;
|
Type.Object({
|
||||||
uid: string;
|
uin: Type.Number({ description: 'QQ号' }),
|
||||||
nick_name: string;
|
uid: Type.String({ description: '用户UID' }),
|
||||||
age: number;
|
nick_name: Type.String({ description: '昵称' }),
|
||||||
source: string;
|
age: Type.Number({ description: '年龄' }),
|
||||||
}
|
source: Type.String({ description: '来源' }),
|
||||||
|
}),
|
||||||
|
{ description: '单向好友列表' }
|
||||||
|
);
|
||||||
|
|
||||||
interface Block {
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
str_uid: string;
|
|
||||||
bytes_source: string;
|
|
||||||
uint32_sex: number;
|
|
||||||
uint32_age: number;
|
|
||||||
bytes_nick: string;
|
|
||||||
uint64_uin: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GetUnidirectionalFriendList extends OneBotAction<void, Friend[]> {
|
export class GetUnidirectionalFriendList extends OneBotAction<void, ReturnType> {
|
||||||
override actionName = ActionName.GetUnidirectionalFriendList;
|
override actionName = ActionName.GetUnidirectionalFriendList;
|
||||||
|
override payloadSchema = Type.Void();
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取单向好友列表';
|
||||||
|
override actionTags = ['用户扩展'];
|
||||||
|
override payloadExample = {};
|
||||||
|
override returnExample = [
|
||||||
|
{
|
||||||
|
uin: 123456789,
|
||||||
|
uid: 'u_123',
|
||||||
|
nick_name: '单向好友',
|
||||||
|
age: 20,
|
||||||
|
source: '来源'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
async pack_data (data: string): Promise<Uint8Array> {
|
async pack_data (data: string): Promise<Uint8Array> {
|
||||||
return ProtoBuf(class extends ProtoBufBase {
|
return ProtoBuf(class extends ProtoBufBase {
|
||||||
@@ -30,7 +41,7 @@ export class GetUnidirectionalFriendList extends OneBotAction<void, Friend[]> {
|
|||||||
}).encode();
|
}).encode();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handle (): Promise<Friend[]> {
|
async _handle (): Promise<ReturnType> {
|
||||||
const self_id = this.core.selfInfo.uin;
|
const self_id = this.core.selfInfo.uin;
|
||||||
const req_json = {
|
const req_json = {
|
||||||
uint64_uin: self_id,
|
uint64_uin: self_id,
|
||||||
@@ -40,10 +51,18 @@ export class GetUnidirectionalFriendList extends OneBotAction<void, Friend[]> {
|
|||||||
};
|
};
|
||||||
const packed_data = await this.pack_data(JSON.stringify(req_json));
|
const packed_data = await this.pack_data(JSON.stringify(req_json));
|
||||||
const data = Buffer.from(packed_data);
|
const data = Buffer.from(packed_data);
|
||||||
const rsq = { cmd: 'MQUpdateSvc_com_qq_ti.web.OidbSvc.0xe17_0', data: data as PacketBuf };
|
const rsq = { cmd: 'MQUpdateSvc_com_qq_ti.web.OidbSvc.0xe17_0', data: data as unknown as PacketBuf };
|
||||||
const rsp_data = await this.core.apis.PacketApi.pkt.operation.sendPacket(rsq, true);
|
const rsp_data = await this.core.apis.PacketApi.pkt.operation.sendPacket(rsq, true);
|
||||||
const block_json = ProtoBuf(class extends ProtoBufBase { data = PBString(4); }).decode(rsp_data);
|
const block_json = ProtoBuf(class extends ProtoBufBase { data = PBString(4); }).decode(rsp_data);
|
||||||
const block_list: Block[] = JSON.parse(block_json.data).rpt_block_list;
|
interface BlockItem {
|
||||||
|
uint64_uin: number;
|
||||||
|
str_uid: string;
|
||||||
|
bytes_nick: string;
|
||||||
|
uint32_age: number;
|
||||||
|
bytes_source: string;
|
||||||
|
}
|
||||||
|
const block_data: { rpt_block_list: BlockItem[]; } = JSON.parse(block_json.data);
|
||||||
|
const block_list = block_data.rpt_block_list;
|
||||||
|
|
||||||
return block_list.map((block) => ({
|
return block_list.map((block) => ({
|
||||||
uin: block.uint64_uin,
|
uin: block.uint64_uin,
|
||||||
|
|||||||
@@ -2,17 +2,38 @@ import { ActionName } from '@/napcat-onebot/action/router';
|
|||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
user_id: Type.Union([Type.Number(), Type.String()]),
|
user_id: Type.String({ description: 'QQ号' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class GetUserStatus extends GetPacketStatusDepends<Payload, { status: number; ext_status: number; } | undefined> {
|
const ReturnSchema = Type.Object({
|
||||||
|
status: Type.Number({ description: '在线状态' }),
|
||||||
|
ext_status: Type.Number({ description: '扩展状态' }),
|
||||||
|
}, { description: '用户状态' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetUserStatus extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.GetUserStatus;
|
override actionName = ActionName.GetUserStatus;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取用户在线状态';
|
||||||
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
user_id: '123456789'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
status: 10,
|
||||||
|
ext_status: 0
|
||||||
|
};
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
return await this.core.apis.PacketApi.pkt.operation.GetStrangerStatus(+payload.user_id);
|
const res = await this.core.apis.PacketApi.pkt.operation.GetStrangerStatus(+payload.user_id);
|
||||||
|
if (!res) {
|
||||||
|
throw new Error('无法获取用户状态');
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,38 @@ import { FileNapCatOneBotUUID } from 'napcat-common/src/file-uuid';
|
|||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
group_id: Type.String({ description: '群号' }),
|
||||||
file_id: Type.String(),
|
file_id: Type.String({ description: '文件ID' }),
|
||||||
current_parent_directory: Type.String(),
|
current_parent_directory: Type.String({ description: '当前父目录' }),
|
||||||
target_parent_directory: Type.String(),
|
target_parent_directory: Type.String({ description: '目标父目录' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
interface MoveGroupFileResponse {
|
const ReturnSchema = Type.Object({
|
||||||
ok: boolean;
|
ok: Type.Boolean({ description: '是否成功' }),
|
||||||
}
|
}, { description: '移动文件结果' });
|
||||||
|
|
||||||
export class MoveGroupFile extends GetPacketStatusDepends<Payload, MoveGroupFileResponse> {
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class MoveGroupFile extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.MoveGroupFile;
|
override actionName = ActionName.MoveGroupFile;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '移动群文件';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
file_id: '/file_id',
|
||||||
|
current_parent_directory: '/current_folder_id',
|
||||||
|
target_parent_directory: '/target_folder_id',
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
ok: true
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id) || FileNapCatOneBotUUID.decodeModelId(payload.file_id);
|
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id) || FileNapCatOneBotUUID.decodeModelId(payload.file_id);
|
||||||
if (contextMsgFile?.fileUUID) {
|
if (contextMsgFile?.fileUUID) {
|
||||||
await this.core.apis.PacketApi.pkt.operation.MoveGroupFile(+payload.group_id, contextMsgFile.fileUUID, payload.current_parent_directory, payload.target_parent_directory);
|
await this.core.apis.PacketApi.pkt.operation.MoveGroupFile(+payload.group_id, contextMsgFile.fileUUID, payload.current_parent_directory, payload.target_parent_directory);
|
||||||
|
|||||||
@@ -3,18 +3,29 @@ import { ActionName } from '@/napcat-onebot/action/router';
|
|||||||
import { checkFileExist, uriToLocalFile } from 'napcat-common/src/file';
|
import { checkFileExist, uriToLocalFile } from 'napcat-common/src/file';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
import { GeneralCallResultStatus } from 'napcat-core';
|
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
import { ExtendsActionsExamples } from '../example/ExtendsActionsExamples';
|
||||||
image: Type.String(),
|
|
||||||
|
const PayloadSchema = Type.Object({
|
||||||
|
image: Type.String({ description: '图片路径、URL或Base64' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
class OCRImageBase extends OneBotAction<Payload, GeneralCallResultStatus> {
|
const ReturnSchema = Type.Any({ description: 'OCR结果' });
|
||||||
override payloadSchema = SchemaData;
|
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
class OCRImageBase extends OneBotAction<PayloadType, ReturnType> {
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '图片 OCR 识别';
|
||||||
|
override actionDescription = '识别图片中的文字内容(仅Windows端支持)';
|
||||||
|
override actionTags = ['扩展接口'];
|
||||||
|
override payloadExample = ExtendsActionsExamples.OCRImage.payload;
|
||||||
|
override returnExample = ExtendsActionsExamples.OCRImage.response;
|
||||||
|
|
||||||
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const { path, success } = await uriToLocalFile(this.core.NapCatTempPath, payload.image);
|
const { path, success } = await uriToLocalFile(this.core.NapCatTempPath, payload.image);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(`OCR ${payload.image}失败, image字段可能格式不正确`);
|
throw new Error(`OCR ${payload.image}失败, image字段可能格式不正确`);
|
||||||
@@ -37,8 +48,10 @@ class OCRImageBase extends OneBotAction<Payload, GeneralCallResultStatus> {
|
|||||||
|
|
||||||
export class OCRImage extends OCRImageBase {
|
export class OCRImage extends OCRImageBase {
|
||||||
override actionName = ActionName.OCRImage;
|
override actionName = ActionName.OCRImage;
|
||||||
|
override actionSummary = '图片 OCR 识别';
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IOCRImage extends OCRImageBase {
|
export class IOCRImage extends OCRImageBase {
|
||||||
override actionName = ActionName.IOCRImage;
|
override actionName = ActionName.IOCRImage;
|
||||||
|
override actionSummary = '图片 OCR 识别 (内部)';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,38 @@ import { FileNapCatOneBotUUID } from 'napcat-common/src/file-uuid';
|
|||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
group_id: Type.String({ description: '群号' }),
|
||||||
file_id: Type.String(),
|
file_id: Type.String({ description: '文件ID' }),
|
||||||
current_parent_directory: Type.String(),
|
current_parent_directory: Type.String({ description: '当前父目录' }),
|
||||||
new_name: Type.String(),
|
new_name: Type.String({ description: '新文件名' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
interface RenameGroupFileResponse {
|
const ReturnSchema = Type.Object({
|
||||||
ok: boolean;
|
ok: Type.Boolean({ description: '是否成功' }),
|
||||||
}
|
}, { description: '重命名文件结果' });
|
||||||
|
|
||||||
export class RenameGroupFile extends GetPacketStatusDepends<Payload, RenameGroupFileResponse> {
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class RenameGroupFile extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.RenameGroupFile;
|
override actionName = ActionName.RenameGroupFile;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '重命名群文件';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
file_id: '/file_id',
|
||||||
|
current_parent_directory: '/',
|
||||||
|
new_name: 'new_name.jpg'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
ok: true
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id) || FileNapCatOneBotUUID.decodeModelId(payload.file_id);
|
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id) || FileNapCatOneBotUUID.decodeModelId(payload.file_id);
|
||||||
if (contextMsgFile?.fileUUID) {
|
if (contextMsgFile?.fileUUID) {
|
||||||
await this.core.apis.PacketApi.pkt.operation.RenameGroupFile(+payload.group_id, contextMsgFile.fileUUID, payload.current_parent_directory, payload.new_name);
|
await this.core.apis.PacketApi.pkt.operation.RenameGroupFile(+payload.group_id, contextMsgFile.fileUUID, payload.current_parent_directory, payload.new_name);
|
||||||
|
|||||||
@@ -3,20 +3,35 @@ import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketS
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
cmd: Type.String(),
|
cmd: Type.String({ description: '命令字' }),
|
||||||
data: Type.String(),
|
data: Type.String({ description: '十六进制数据' }),
|
||||||
rsp: Type.Union([Type.String(), Type.Boolean()], { default: true }),
|
rsp: Type.Union([Type.String(), Type.Boolean()], { default: true, description: '是否等待响应' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class SendPacket extends GetPacketStatusDepends<Payload, string | undefined> {
|
const ReturnSchema = Type.Union([Type.String({ description: '响应十六进制数据' }), Type.Undefined()], { description: '发包结果' });
|
||||||
override payloadSchema = SchemaData;
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class SendPacket extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
override actionName = ActionName.SendPacket;
|
override actionName = ActionName.SendPacket;
|
||||||
async _handle (payload: Payload) {
|
override actionSummary = '发送原始数据包';
|
||||||
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
cmd: 'Example.Cmd',
|
||||||
|
data: '123456',
|
||||||
|
rsp: true
|
||||||
|
};
|
||||||
|
override returnExample = '123456';
|
||||||
|
|
||||||
|
async _handle (payload: PayloadType) {
|
||||||
const rsp = typeof payload.rsp === 'boolean' ? payload.rsp : payload.rsp === 'true';
|
const rsp = typeof payload.rsp === 'boolean' ? payload.rsp : payload.rsp === 'true';
|
||||||
const data = await this.core.apis.PacketApi.pkt.operation.sendPacket({ cmd: payload.cmd, data: Buffer.from(payload.data, 'hex') as PacketBuf }, rsp);
|
const packetData = Buffer.from(payload.data, 'hex') as unknown as PacketBuf;
|
||||||
|
const data = await this.core.apis.PacketApi.pkt.operation.sendPacket({ cmd: payload.cmd, data: packetData }, rsp);
|
||||||
return typeof data === 'object' ? data.toString('hex') : undefined;
|
return typeof data === 'object' ? data.toString('hex') : undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,31 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
face_id: Type.Union([Type.Number(), Type.String()]), // 参考 face_config.json 的 QSid
|
face_id: Type.Union([Type.Number(), Type.String()], { description: '图标ID' }), // 参考 face_config.json 的 QSid
|
||||||
face_type: Type.Union([Type.Number(), Type.String()], { default: '1' }),
|
face_type: Type.Union([Type.Number(), Type.String()], { default: '1', description: '图标类型' }),
|
||||||
wording: Type.String({ default: ' ' }),
|
wording: Type.String({ default: ' ', description: '状态文字内容' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class SetDiyOnlineStatus extends OneBotAction<Payload, string> {
|
const ReturnSchema = Type.String({ description: '错误信息(如果有)' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class SetDiyOnlineStatus extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetDiyOnlineStatus;
|
override actionName = ActionName.SetDiyOnlineStatus;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema; override actionSummary = '设置自定义在线状态';
|
||||||
async _handle (payload: Payload) {
|
override actionDescription = '设置自定义在线状态';
|
||||||
|
override actionTags = ['用户扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
face_id: '123',
|
||||||
|
face_type: '1',
|
||||||
|
wording: '自定义状态'
|
||||||
|
};
|
||||||
|
override returnExample = '';
|
||||||
|
async _handle (payload: PayloadType) {
|
||||||
const ret = await this.core.apis.UserApi.setDiySelfOnlineStatus(
|
const ret = await this.core.apis.UserApi.setDiySelfOnlineStatus(
|
||||||
payload.face_id.toString(),
|
payload.face_id.toString(),
|
||||||
payload.wording,
|
payload.wording,
|
||||||
|
|||||||
@@ -2,19 +2,31 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.String(),
|
group_id: Type.String({ description: '群号' }),
|
||||||
add_type: Type.Number(),
|
add_type: Type.Number({ description: '加群方式' }),
|
||||||
group_question: Type.Optional(Type.String()),
|
group_question: Type.Optional(Type.String({ description: '加群问题' })),
|
||||||
group_answer: Type.Optional(Type.String()),
|
group_answer: Type.Optional(Type.String({ description: '加群答案' })),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export default class SetGroupAddOption extends OneBotAction<Payload, null> {
|
const ReturnSchema = Type.Null({ description: '返回结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export default class SetGroupAddOption extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetGroupAddOption;
|
override actionName = ActionName.SetGroupAddOption;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '设置群加群选项';
|
||||||
async _handle (payload: Payload): Promise<null> {
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
add_type: 1,
|
||||||
|
};
|
||||||
|
override returnExample = null;
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const ret = await this.core.apis.GroupApi.setGroupAddOption(payload.group_id, {
|
const ret = await this.core.apis.GroupApi.setGroupAddOption(payload.group_id, {
|
||||||
addOption: payload.add_type,
|
addOption: payload.add_type,
|
||||||
groupQuestion: payload.group_question,
|
groupQuestion: payload.group_question,
|
||||||
|
|||||||
@@ -2,21 +2,37 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.String(),
|
group_id: Type.String({ description: '群号' }),
|
||||||
album_id: Type.String(),
|
album_id: Type.String({ description: '相册ID' }),
|
||||||
lloc: Type.String(),
|
lloc: Type.String({ description: '媒体ID (lloc)' }),
|
||||||
id: Type.String(), // 421_1_0_1012959257|V61Yiali4PELg90bThrH4Bo2iI1M5Kab|V5bCgAxMDEyOTU5MjU3.PyqaPndPxg!^||^421_1_0_1012959257|V61Yiali4PELg90bThrH4Bo2iI1M5Kab|17560363448^||^1
|
id: Type.String({ description: '点赞ID' }), // 421_1_0_1012959257|V61Yiali4PELg90bThrH4Bo2iI1M5Kab|V5bCgAxMDEyOTU5MjU3.PyqaPndPxg!^||^421_1_0_1012959257|V61Yiali4PELg90bThrH4Bo2iI1M5Kab|17560363448^||^1
|
||||||
set: Type.Boolean({ default: true }), // true=点赞 false=取消点赞 未实现
|
set: Type.Boolean({ default: true, description: '是否点赞' }), // true=点赞 false=取消点赞 未实现
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class SetGroupAlbumMediaLike extends OneBotAction<Payload, unknown> {
|
const ReturnSchema = Type.Any({ description: '操作结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class SetGroupAlbumMediaLike extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetGroupAlbumMediaLike;
|
override actionName = ActionName.SetGroupAlbumMediaLike;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '点赞群相册媒体';
|
||||||
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
album_id: 'album_id_1',
|
||||||
|
lloc: 'media_id_1',
|
||||||
|
id: '123456',
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
result: {}
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
return await this.core.apis.WebApi.doAlbumMediaLikeByNTQQ(
|
return await this.core.apis.WebApi.doAlbumMediaLikeByNTQQ(
|
||||||
payload.group_id,
|
payload.group_id,
|
||||||
payload.album_id,
|
payload.album_id,
|
||||||
|
|||||||
@@ -2,19 +2,31 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
import { ExtendsActionsExamples } from '../example/ExtendsActionsExamples';
|
||||||
group_id: Type.String(),
|
|
||||||
user_id: Type.Array(Type.String()),
|
const PayloadSchema = Type.Object({
|
||||||
reject_add_request: Type.Optional(Type.Union([Type.Boolean(), Type.String()])),
|
group_id: Type.String({ description: '群号' }),
|
||||||
|
user_id: Type.Array(Type.String(), { description: 'QQ号列表' }),
|
||||||
|
reject_add_request: Type.Optional(Type.Union([Type.Boolean(), Type.String()], { description: '是否拒绝加群请求' })),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export default class SetGroupKickMembers extends OneBotAction<Payload, null> {
|
const ReturnSchema = Type.Null({ description: '返回结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export default class SetGroupKickMembers extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetGroupKickMembers;
|
override actionName = ActionName.SetGroupKickMembers;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '批量踢出群成员';
|
||||||
|
override actionDescription = '从指定群聊中批量踢出多个成员';
|
||||||
|
override actionTags = ['扩展接口'];
|
||||||
|
override payloadExample = ExtendsActionsExamples.SetGroupKickMembers.payload;
|
||||||
|
override returnExample = ExtendsActionsExamples.SetGroupKickMembers.response;
|
||||||
|
|
||||||
async _handle (payload: Payload): Promise<null> {
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const rejectReq = payload.reject_add_request?.toString() === 'true';
|
const rejectReq = payload.reject_add_request?.toString() === 'true';
|
||||||
const uids: string[] = await Promise.all(payload.user_id.map(async uin => await this.core.apis.UserApi.getUidByUinV2(uin)));
|
const uids: string[] = await Promise.all(payload.user_id.map(async uin => await this.core.apis.UserApi.getUidByUinV2(uin)));
|
||||||
await this.core.apis.GroupApi.kickMember(payload.group_id.toString(), uids.filter(uid => !!uid), rejectReq);
|
await this.core.apis.GroupApi.kickMember(payload.group_id.toString(), uids.filter(uid => !!uid), rejectReq);
|
||||||
|
|||||||
@@ -2,17 +2,31 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.String(),
|
group_id: Type.String({ description: '群号' }),
|
||||||
remark: Type.String(),
|
remark: Type.String({ description: '备注' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export default class SetGroupRemark extends OneBotAction<Payload, null> {
|
const ReturnSchema = Type.Null({ description: '返回结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export default class SetGroupRemark extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetGroupRemark;
|
override actionName = ActionName.SetGroupRemark;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
async _handle (payload: Payload): Promise<null> {
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '设置群备注';
|
||||||
|
override actionDescription = '设置群备注';
|
||||||
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
remark: '测试群备注'
|
||||||
|
};
|
||||||
|
override returnExample = null;
|
||||||
|
|
||||||
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const ret = await this.core.apis.GroupApi.setGroupRemark(payload.group_id, payload.remark);
|
const ret = await this.core.apis.GroupApi.setGroupRemark(payload.group_id, payload.remark);
|
||||||
if (ret.result !== 0) {
|
if (ret.result !== 0) {
|
||||||
throw new Error(`设置群备注失败, ${ret.result}:${ret.errMsg}`);
|
throw new Error(`设置群备注失败, ${ret.result}:${ret.errMsg}`);
|
||||||
|
|||||||
@@ -2,18 +2,29 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.String(),
|
group_id: Type.String({ description: '群号' }),
|
||||||
robot_member_switch: Type.Optional(Type.Number()),
|
robot_member_switch: Type.Optional(Type.Number({ description: '机器人成员开关' })),
|
||||||
robot_member_examine: Type.Optional(Type.Number()),
|
robot_member_examine: Type.Optional(Type.Number({ description: '机器人成员审核' })),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export default class SetGroupRobotAddOption extends OneBotAction<Payload, null> {
|
const ReturnSchema = Type.Null({ description: '返回结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export default class SetGroupRobotAddOption extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetGroupRobotAddOption;
|
override actionName = ActionName.SetGroupRobotAddOption;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '设置群机器人加群选项';
|
||||||
async _handle (payload: Payload): Promise<null> {
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456'
|
||||||
|
};
|
||||||
|
override returnExample = null;
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const ret = await this.core.apis.GroupApi.setGroupRobotAddOption(
|
const ret = await this.core.apis.GroupApi.setGroupRobotAddOption(
|
||||||
payload.group_id,
|
payload.group_id,
|
||||||
payload.robot_member_switch,
|
payload.robot_member_switch,
|
||||||
|
|||||||
@@ -2,18 +2,29 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.String(),
|
group_id: Type.String({ description: '群号' }),
|
||||||
no_code_finger_open: Type.Optional(Type.Number()),
|
no_code_finger_open: Type.Optional(Type.Number({ description: '未知' })),
|
||||||
no_finger_open: Type.Optional(Type.Number()),
|
no_finger_open: Type.Optional(Type.Number({ description: '未知' })),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export default class SetGroupSearch extends OneBotAction<Payload, null> {
|
const ReturnSchema = Type.Null({ description: '返回结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export default class SetGroupSearch extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetGroupSearch;
|
override actionName = ActionName.SetGroupSearch;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '设置群搜索选项';
|
||||||
async _handle (payload: Payload): Promise<null> {
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456'
|
||||||
|
};
|
||||||
|
override returnExample = null;
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const ret = await this.core.apis.GroupApi.setGroupSearch(payload.group_id, {
|
const ret = await this.core.apis.GroupApi.setGroupSearch(payload.group_id, {
|
||||||
noCodeFingerOpenFlag: payload.no_code_finger_open,
|
noCodeFingerOpenFlag: payload.no_code_finger_open,
|
||||||
noFingerOpenFlag: payload.no_finger_open,
|
noFingerOpenFlag: payload.no_finger_open,
|
||||||
|
|||||||
@@ -2,16 +2,27 @@ import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketS
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
group_id: Type.String({ description: '群号' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
class SetGroupSignBase extends GetPacketStatusDepends<Payload, void> {
|
const ReturnSchema = Type.Void({ description: '打卡结果' });
|
||||||
override payloadSchema = SchemaData;
|
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
class SetGroupSignBase extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '群打卡';
|
||||||
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456789'
|
||||||
|
};
|
||||||
|
override returnExample = null;
|
||||||
|
|
||||||
|
async _handle (payload: PayloadType) {
|
||||||
return await this.core.apis.PacketApi.pkt.operation.GroupSign(+payload.group_id);
|
return await this.core.apis.PacketApi.pkt.operation.GroupSign(+payload.group_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,30 @@ import { ActionName } from '@/napcat-onebot/action/router';
|
|||||||
import { ChatType } from 'napcat-core';
|
import { ChatType } from 'napcat-core';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
user_id: Type.Union([Type.Number(), Type.String()]),
|
user_id: Type.String({ description: 'QQ号' }),
|
||||||
event_type: Type.Number(),
|
event_type: Type.Number({ description: '事件类型' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class SetInputStatus extends OneBotAction<Payload, unknown> {
|
const ReturnSchema = Type.Any({ description: '设置结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class SetInputStatus extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetInputStatus;
|
override actionName = ActionName.SetInputStatus;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
async _handle (payload: Payload) {
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '设置输入状态';
|
||||||
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
user_id: '123456789',
|
||||||
|
event_type: 1
|
||||||
|
};
|
||||||
|
override returnExample = null;
|
||||||
|
|
||||||
|
async _handle (payload: PayloadType) {
|
||||||
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
if (!uid) throw new Error('uid is empty');
|
if (!uid) throw new Error('uid is empty');
|
||||||
const peer = {
|
const peer = {
|
||||||
|
|||||||
@@ -2,17 +2,29 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
import { ExtendsActionsExamples } from '../example/ExtendsActionsExamples';
|
||||||
longNick: Type.String(),
|
|
||||||
|
const PayloadSchema = Type.Object({
|
||||||
|
longNick: Type.String({ description: '签名内容' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class SetLongNick extends OneBotAction<Payload, unknown> {
|
const ReturnSchema = Type.Any({ description: '设置结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class SetLongNick extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetLongNick;
|
override actionName = ActionName.SetLongNick;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '设置个性签名';
|
||||||
|
override actionDescription = '修改当前登录帐号的个性签名';
|
||||||
|
override actionTags = ['扩展接口'];
|
||||||
|
override payloadExample = ExtendsActionsExamples.SetLongNick.payload;
|
||||||
|
override returnExample = ExtendsActionsExamples.SetLongNick.response;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
return await this.core.apis.UserApi.setLongNick(payload.longNick);
|
return await this.core.apis.UserApi.setLongNick(payload.longNick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,33 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
status: Type.Union([Type.Number(), Type.String()]),
|
status: Type.Union([Type.Number(), Type.String()], { description: '在线状态' }),
|
||||||
ext_status: Type.Union([Type.Number(), Type.String()]),
|
ext_status: Type.Union([Type.Number(), Type.String()], { description: '扩展状态' }),
|
||||||
battery_status: Type.Union([Type.Number(), Type.String()]),
|
battery_status: Type.Union([Type.Number(), Type.String()], { description: '电量状态' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class SetOnlineStatus extends OneBotAction<Payload, null> {
|
const ReturnSchema = Type.Null({ description: '设置结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class SetOnlineStatus extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetOnlineStatus;
|
override actionName = ActionName.SetOnlineStatus;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '设置在线状态';
|
||||||
|
override actionDescription = statusText;
|
||||||
|
override actionTags = ['系统扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
status: 11,
|
||||||
|
ext_status: 0,
|
||||||
|
battery_status: 100
|
||||||
|
};
|
||||||
|
override returnExample = null;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const ret = await this.core.apis.UserApi.setSelfOnlineStatus(
|
const ret = await this.core.apis.UserApi.setSelfOnlineStatus(
|
||||||
+payload.status,
|
+payload.status,
|
||||||
+payload.ext_status,
|
+payload.ext_status,
|
||||||
@@ -26,3 +40,216 @@ export class SetOnlineStatus extends OneBotAction<Payload, null> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const statusText = `
|
||||||
|
## 状态列表
|
||||||
|
|
||||||
|
### 在线
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 0, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Q我吧
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 60, "ext_status": 0, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 离开
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 30, "ext_status": 0, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 忙碌
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 50, "ext_status": 0, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 请勿打扰
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 70, "ext_status": 0, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 隐身
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 40, "ext_status": 0, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 听歌中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1028, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 春日限定
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2037, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 一起元梦
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2025, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 求星搭子
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2026, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 被掏空
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2014, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 今日天气
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1030, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 我crash了
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2019, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 爱你
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2006, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 恋爱中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1051, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 好运锦鲤
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1071, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 水逆退散
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1201, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 嗨到飞起
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1056, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 元气满满
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1058, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 宝宝认证
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1070, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 一言难尽
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1063, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 难得糊涂
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2001, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### emo中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1401, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 我太难了
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1062, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 我想开了
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2013, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 我没事
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1052, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 想静静
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1061, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 悠哉哉
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1059, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 去旅行
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2015, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 信号弱
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1011, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 出去浪
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2003, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 肝作业
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2012, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 学习中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1018, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 搬砖中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 2023, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 摸鱼中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1300, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 无聊中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1060, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### timi中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1027, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 睡觉中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1016, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 熬夜中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1032, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 追剧中
|
||||||
|
\`\`\`json5;
|
||||||
|
{ "status": 10, "ext_status": 1021, "battery_status": 0; }
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 我的电量
|
||||||
|
\`\`\`json5;
|
||||||
|
{
|
||||||
|
"status": 10,
|
||||||
|
"ext_status": 1000,
|
||||||
|
"battery_status": 0;
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
`;
|
||||||
@@ -4,16 +4,29 @@ import fs from 'node:fs/promises';
|
|||||||
import { checkFileExist, uriToLocalFile } from 'napcat-common/src/file';
|
import { checkFileExist, uriToLocalFile } from 'napcat-common/src/file';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
import { ExtendsActionsExamples } from '../example/ExtendsActionsExamples';
|
||||||
file: Type.String(),
|
|
||||||
|
const PayloadSchema = Type.Object({
|
||||||
|
file: Type.String({ description: '图片路径、URL或Base64' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export default class SetAvatar extends OneBotAction<Payload, null> {
|
const ReturnSchema = Type.Null({ description: '设置结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export default class SetAvatar extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetQQAvatar;
|
override actionName = ActionName.SetQQAvatar;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
async _handle (payload: Payload): Promise<null> {
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '设置QQ头像';
|
||||||
|
override actionDescription = '修改当前账号的QQ头像';
|
||||||
|
override actionTags = ['扩展接口'];
|
||||||
|
override payloadExample = ExtendsActionsExamples.SetQQAvatar.payload;
|
||||||
|
override returnExample = ExtendsActionsExamples.SetQQAvatar.response;
|
||||||
|
|
||||||
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file));
|
const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file));
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
|
throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
|
||||||
@@ -26,7 +39,7 @@ export default class SetAvatar extends OneBotAction<Payload, null> {
|
|||||||
throw new Error(`头像${payload.file}设置失败,api无返回`);
|
throw new Error(`头像${payload.file}设置失败,api无返回`);
|
||||||
}
|
}
|
||||||
// log(`头像设置返回:${JSON.stringify(ret)}`)
|
// log(`头像设置返回:${JSON.stringify(ret)}`)
|
||||||
if (ret.result as number === 1004022) {
|
if (Number(ret.result) === 1004022) {
|
||||||
throw new Error(`头像${payload.file}设置失败,文件可能不是图片格式`);
|
throw new Error(`头像${payload.file}设置失败,文件可能不是图片格式`);
|
||||||
} else if (ret.result !== 0) {
|
} else if (ret.result !== 0) {
|
||||||
throw new Error(`头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`);
|
throw new Error(`头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`);
|
||||||
|
|||||||
@@ -2,19 +2,31 @@ import { ActionName } from '@/napcat-onebot/action/router';
|
|||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
import { ExtendsActionsExamples } from '../example/ExtendsActionsExamples';
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
|
||||||
user_id: Type.Union([Type.Number(), Type.String()]),
|
const PayloadSchema = Type.Object({
|
||||||
special_title: Type.String({ default: '' }),
|
group_id: Type.String({ description: '群号' }),
|
||||||
|
user_id: Type.String({ description: 'QQ号' }),
|
||||||
|
special_title: Type.String({ default: '', description: '专属头衔' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class SetSpecialTitle extends GetPacketStatusDepends<Payload, void> {
|
const ReturnSchema = Type.Void({ description: '设置结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class SetSpecialTitle extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.SetSpecialTitle;
|
override actionName = ActionName.SetSpecialTitle;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '设置专属头衔';
|
||||||
|
override actionDescription = '设置群聊中指定成员的专属头衔';
|
||||||
|
override actionTags = ['扩展接口'];
|
||||||
|
override payloadExample = ExtendsActionsExamples.SetSpecialTitle.payload;
|
||||||
|
override returnExample = ExtendsActionsExamples.SetSpecialTitle.response;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
if (!uid) throw new Error('User not found');
|
if (!uid) throw new Error('User not found');
|
||||||
await this.core.apis.PacketApi.pkt.operation.SetGroupSpecialTitle(+payload.group_id, uid, payload.special_title);
|
await this.core.apis.PacketApi.pkt.operation.SetGroupSpecialTitle(+payload.group_id, uid, payload.special_title);
|
||||||
|
|||||||
@@ -1,24 +1,35 @@
|
|||||||
import { GeneralCallResult } from 'napcat-core';
|
|
||||||
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
user_id: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
user_id: Type.Optional(Type.String({ description: 'QQ号' })),
|
||||||
group_id: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
group_id: Type.Optional(Type.String({ description: '群号' })),
|
||||||
phone_number: Type.String({ default: '' }),
|
phone_number: Type.String({ default: '', description: '手机号' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class SharePeerBase extends OneBotAction<Payload, GeneralCallResult & {
|
const ReturnSchema = Type.Any({ description: '分享结果' });
|
||||||
arkMsg?: string;
|
|
||||||
arkJson?: string;
|
|
||||||
}> {
|
|
||||||
|
|
||||||
override payloadSchema = SchemaData;
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
export class SharePeerBase extends OneBotAction<PayloadType, ReturnType> {
|
||||||
|
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '分享用户 (Ark)';
|
||||||
|
override actionDescription = '获取用户推荐的 Ark 内容';
|
||||||
|
override actionTags = ['消息扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
user_id: '123456',
|
||||||
|
phone_number: ''
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
ark: '...'
|
||||||
|
};
|
||||||
|
|
||||||
|
async _handle (payload: PayloadType) {
|
||||||
if (payload.group_id) {
|
if (payload.group_id) {
|
||||||
return await this.core.apis.GroupApi.getGroupRecommendContactArkJson(payload.group_id.toString());
|
return await this.core.apis.GroupApi.getGroupRecommendContactArkJson(payload.group_id.toString());
|
||||||
} else if (payload.user_id) {
|
} else if (payload.user_id) {
|
||||||
@@ -28,18 +39,30 @@ export class SharePeerBase extends OneBotAction<Payload, GeneralCallResult & {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SchemaDataGroupEx = Type.Object({
|
const PayloadSchemaGroupEx = Type.Object({
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
group_id: Type.String({ description: '群号' }),
|
||||||
});
|
});
|
||||||
export class SharePeer extends SharePeerBase {
|
export class SharePeer extends SharePeerBase {
|
||||||
override actionName = ActionName.SharePeer;
|
override actionName = ActionName.SharePeer;
|
||||||
}
|
}
|
||||||
type PayloadGroupEx = Static<typeof SchemaDataGroupEx>;
|
type PayloadTypeGroupEx = Static<typeof PayloadSchemaGroupEx>;
|
||||||
|
|
||||||
export class ShareGroupExBase extends OneBotAction<PayloadGroupEx, string> {
|
const ReturnSchemaGroupEx = Type.String({ description: 'Ark Json内容' });
|
||||||
override payloadSchema = SchemaDataGroupEx;
|
|
||||||
|
|
||||||
async _handle (payload: PayloadGroupEx) {
|
type ReturnTypeGroupEx = Static<typeof ReturnSchemaGroupEx>;
|
||||||
|
|
||||||
|
export class ShareGroupExBase extends OneBotAction<PayloadTypeGroupEx, ReturnTypeGroupEx> {
|
||||||
|
override payloadSchema = PayloadSchemaGroupEx;
|
||||||
|
override returnSchema = ReturnSchemaGroupEx;
|
||||||
|
override actionSummary = '分享群 (Ark)';
|
||||||
|
override actionDescription = '获取群分享的 Ark 内容';
|
||||||
|
override actionTags = ['消息扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456'
|
||||||
|
};
|
||||||
|
override returnExample = '{"app": "com.tencent.structmsg", ...}';
|
||||||
|
|
||||||
|
async _handle (payload: PayloadTypeGroupEx) {
|
||||||
return await this.core.apis.GroupApi.getArkJsonGroupShare(payload.group_id.toString());
|
return await this.core.apis.GroupApi.getArkJsonGroupShare(payload.group_id.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,34 @@ import { FileNapCatOneBotUUID } from 'napcat-common/src/file-uuid';
|
|||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
group_id: Type.String({ description: '群号' }),
|
||||||
file_id: Type.String(),
|
file_id: Type.String({ description: '文件ID' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
interface TransGroupFileResponse {
|
const ReturnSchema = Type.Object({
|
||||||
ok: boolean;
|
ok: Type.Boolean({ description: '是否成功' }),
|
||||||
}
|
}, { description: '转发文件结果' });
|
||||||
|
|
||||||
export class TransGroupFile extends GetPacketStatusDepends<Payload, TransGroupFileResponse> {
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class TransGroupFile extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.TransGroupFile;
|
override actionName = ActionName.TransGroupFile;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '传输群文件';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
file_id: '/file_id'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
ok: true
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id) || FileNapCatOneBotUUID.decodeModelId(payload.file_id);
|
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id) || FileNapCatOneBotUUID.decodeModelId(payload.file_id);
|
||||||
if (contextMsgFile?.fileUUID) {
|
if (contextMsgFile?.fileUUID) {
|
||||||
const result = await this.core.apis.GroupApi.transGroupFile(payload.group_id.toString(), contextMsgFile.fileUUID);
|
const result = await this.core.apis.GroupApi.transGroupFile(payload.group_id.toString(), contextMsgFile.fileUUID);
|
||||||
|
|||||||
@@ -2,21 +2,37 @@ import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
import { ExtendsActionsExamples } from '../example/ExtendsActionsExamples';
|
||||||
words: Type.Array(Type.String()),
|
|
||||||
|
const PayloadSchema = Type.Object({
|
||||||
|
words: Type.Array(Type.String(), { description: '待翻译单词列表' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class TranslateEnWordToZn extends OneBotAction<Payload, Array<unknown> | null> {
|
const ReturnSchema = Type.Object({
|
||||||
|
words: Type.Array(Type.String(), { description: '翻译结果列表' }),
|
||||||
|
}, { description: '翻译结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class TranslateEnWordToZn extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.TranslateEnWordToZn;
|
override actionName = ActionName.TranslateEnWordToZn;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '英文单词翻译';
|
||||||
|
override actionDescription = '将英文单词列表翻译为中文';
|
||||||
|
override actionTags = ['扩展接口'];
|
||||||
|
override payloadExample = ExtendsActionsExamples.TranslateEnWordToZn.payload;
|
||||||
|
override returnExample = ExtendsActionsExamples.TranslateEnWordToZn.response;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType): Promise<ReturnType> {
|
||||||
const ret = await this.core.apis.SystemApi.translateEnWordToZn(payload.words);
|
const ret = await this.core.apis.SystemApi.translateEnWordToZn(payload.words);
|
||||||
if (ret.result !== 0) {
|
if (ret.result !== 0) {
|
||||||
throw new Error('翻译失败');
|
throw new Error('翻译失败');
|
||||||
}
|
}
|
||||||
return ret.words;
|
return {
|
||||||
|
words: ret.words
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,36 @@ import { Static, Type } from '@sinclair/typebox';
|
|||||||
import { existsSync } from 'node:fs';
|
import { existsSync } from 'node:fs';
|
||||||
import { unlink } from 'node:fs/promises';
|
import { unlink } from 'node:fs/promises';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const PayloadSchema = Type.Object({
|
||||||
group_id: Type.String(),
|
group_id: Type.String({ description: '群号' }),
|
||||||
album_id: Type.String(),
|
album_id: Type.String({ description: '相册ID' }),
|
||||||
album_name: Type.String(),
|
album_name: Type.String({ description: '相册名称' }),
|
||||||
file: Type.String(),
|
file: Type.String({ description: '图片路径、URL或Base64' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export class UploadImageToQunAlbum extends OneBotAction<Payload, unknown> {
|
const ReturnSchema = Type.Any({ description: '上传结果' });
|
||||||
|
|
||||||
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class UploadImageToQunAlbum extends OneBotAction<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.UploadImageToQunAlbum;
|
override actionName = ActionName.UploadImageToQunAlbum;
|
||||||
override payloadSchema = SchemaData;
|
override actionSummary = '上传图片到群相册';
|
||||||
|
override actionTags = ['群组扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
group_id: '123456',
|
||||||
|
album_id: 'album_id_1',
|
||||||
|
album_name: '相册1',
|
||||||
|
file: '/path/to/image.jpg'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
result: null
|
||||||
|
};
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const downloadResult = await uriToLocalFile(this.core.NapCatTempPath, payload.file);
|
const downloadResult = await uriToLocalFile(this.core.NapCatTempPath, payload.file);
|
||||||
try {
|
try {
|
||||||
return await this.core.apis.WebApi.uploadImageToQunAlbum(payload.group_id, payload.album_id, payload.album_name, downloadResult.path);
|
return await this.core.apis.WebApi.uploadImageToQunAlbum(payload.group_id, payload.album_id, payload.album_name, downloadResult.path);
|
||||||
|
|||||||
@@ -5,23 +5,29 @@ import { ActionName } from '@/napcat-onebot/action/router';
|
|||||||
import { OB11MessageImage, OB11MessageVideo } from '@/napcat-onebot/types';
|
import { OB11MessageImage, OB11MessageVideo } from '@/napcat-onebot/types';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
export interface GetFileResponse {
|
import { FileActionsExamples } from '../example/FileActionsExamples';
|
||||||
file?: string; // path
|
|
||||||
url?: string;
|
|
||||||
file_size?: string;
|
|
||||||
file_name?: string;
|
|
||||||
base64?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const GetFileBase_PayloadSchema = Type.Object({
|
export const GetFilePayloadSchema = Type.Object({
|
||||||
file: Type.Optional(Type.String()),
|
file: Type.Optional(Type.String({ description: '文件路径、URL或Base64' })),
|
||||||
file_id: Type.Optional(Type.String()),
|
file_id: Type.Optional(Type.String({ description: '文件ID' })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type GetFilePayload = Static<typeof GetFileBase_PayloadSchema>;
|
export type GetFilePayload = Static<typeof GetFilePayloadSchema>;
|
||||||
|
|
||||||
|
export const GetFileReturnSchema = Type.Object({
|
||||||
|
file: Type.Optional(Type.String({ description: '本地路径' })),
|
||||||
|
url: Type.Optional(Type.String({ description: '下载URL' })),
|
||||||
|
file_size: Type.Optional(Type.String({ description: '文件大小' })),
|
||||||
|
file_name: Type.Optional(Type.String({ description: '文件名' })),
|
||||||
|
base64: Type.Optional(Type.String({ description: 'Base64编码' })),
|
||||||
|
}, { description: '文件信息' });
|
||||||
|
|
||||||
|
export type GetFileResponse = Static<typeof GetFileReturnSchema>;
|
||||||
|
|
||||||
export class GetFileBase extends OneBotAction<GetFilePayload, GetFileResponse> {
|
export class GetFileBase extends OneBotAction<GetFilePayload, GetFileResponse> {
|
||||||
override payloadSchema = GetFileBase_PayloadSchema;
|
override payloadSchema = GetFilePayloadSchema;
|
||||||
|
override returnSchema = GetFileReturnSchema;
|
||||||
|
override actionTags = ['文件接口'];
|
||||||
|
|
||||||
async _handle (payload: GetFilePayload): Promise<GetFileResponse> {
|
async _handle (payload: GetFilePayload): Promise<GetFileResponse> {
|
||||||
payload.file ||= payload.file_id || '';
|
payload.file ||= payload.file_id || '';
|
||||||
@@ -40,12 +46,12 @@ export class GetFileBase extends OneBotAction<GetFilePayload, GetFileResponse> {
|
|||||||
let url = '';
|
let url = '';
|
||||||
if (mixElement?.picElement && rawMessage) {
|
if (mixElement?.picElement && rawMessage) {
|
||||||
const tempData =
|
const tempData =
|
||||||
await this.obContext.apis.MsgApi.rawToOb11Converters.picElement?.(mixElement?.picElement, rawMessage, mixElement, { parseMultMsg: false, disableGetUrl: false, quick_reply: true }) as OB11MessageImage | undefined;
|
await this.obContext.apis.MsgApi.rawToOb11Converters.picElement?.(mixElement?.picElement, rawMessage, mixElement, { parseMultMsg: false, disableGetUrl: false, quick_reply: true }) as OB11MessageImage | undefined;
|
||||||
url = tempData?.data.url ?? '';
|
url = tempData?.data.url ?? '';
|
||||||
}
|
}
|
||||||
if (mixElement?.videoElement && rawMessage) {
|
if (mixElement?.videoElement && rawMessage) {
|
||||||
const tempData =
|
const tempData =
|
||||||
await this.obContext.apis.MsgApi.rawToOb11Converters.videoElement?.(mixElement?.videoElement, rawMessage, mixElement, { parseMultMsg: false, disableGetUrl: false, quick_reply: true }) as OB11MessageVideo | undefined;
|
await this.obContext.apis.MsgApi.rawToOb11Converters.videoElement?.(mixElement?.videoElement, rawMessage, mixElement, { parseMultMsg: false, disableGetUrl: false, quick_reply: true }) as OB11MessageVideo | undefined;
|
||||||
url = tempData?.data.url ?? '';
|
url = tempData?.data.url ?? '';
|
||||||
}
|
}
|
||||||
const res: GetFileResponse = {
|
const res: GetFileResponse = {
|
||||||
@@ -113,4 +119,9 @@ export class GetFileBase extends OneBotAction<GetFilePayload, GetFileResponse> {
|
|||||||
|
|
||||||
export default class GetFile extends GetFileBase {
|
export default class GetFile extends GetFileBase {
|
||||||
override actionName = ActionName.GetFile;
|
override actionName = ActionName.GetFile;
|
||||||
|
override actionSummary = '获取文件';
|
||||||
|
override actionDescription = '获取指定文件的详细信息及下载路径';
|
||||||
|
override actionTags = ['文件接口'];
|
||||||
|
override payloadExample = FileActionsExamples.GetFile.payload;
|
||||||
|
override returnExample = FileActionsExamples.GetFile.response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,32 @@ import { FileNapCatOneBotUUID } from 'napcat-common/src/file-uuid';
|
|||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
import { FileActionsExamples } from '../example/FileActionsExamples';
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
|
||||||
file_id: Type.String(),
|
const PayloadSchema = Type.Object({
|
||||||
|
group_id: Type.String({ description: '群号' }),
|
||||||
|
file_id: Type.String({ description: '文件ID' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
interface GetGroupFileUrlResponse {
|
const ReturnSchema = Type.Object({
|
||||||
url?: string;
|
url: Type.Optional(Type.String({ description: '文件下载链接' })),
|
||||||
}
|
}, { description: '群文件URL信息' });
|
||||||
|
|
||||||
export class GetGroupFileUrl extends GetPacketStatusDepends<Payload, GetGroupFileUrlResponse> {
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetGroupFileUrl extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.GOCQHTTP_GetGroupFileUrl;
|
override actionName = ActionName.GOCQHTTP_GetGroupFileUrl;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取群文件URL';
|
||||||
|
override actionDescription = '获取指定群文件的下载链接';
|
||||||
|
override actionTags = ['文件接口'];
|
||||||
|
override payloadExample = FileActionsExamples.GetGroupFileUrl.payload;
|
||||||
|
override returnExample = FileActionsExamples.GetGroupFileUrl.response;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id) || FileNapCatOneBotUUID.decodeModelId(payload.file_id);
|
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id) || FileNapCatOneBotUUID.decodeModelId(payload.file_id);
|
||||||
if (contextMsgFile?.fileUUID) {
|
if (contextMsgFile?.fileUUID) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { GetFileBase } from './GetFile';
|
import { GetFileBase } from './GetFile';
|
||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
|
||||||
|
import { FileActionsExamples } from '../example/FileActionsExamples';
|
||||||
|
|
||||||
export default class GetImage extends GetFileBase {
|
export default class GetImage extends GetFileBase {
|
||||||
override actionName = ActionName.GetImage;
|
override actionName = ActionName.GetImage;
|
||||||
|
override actionSummary = '获取图片';
|
||||||
|
override actionDescription = '获取指定图片的信息及路径';
|
||||||
|
override actionTags = ['文件接口'];
|
||||||
|
override payloadExample = FileActionsExamples.GetImage.payload;
|
||||||
|
override returnExample = FileActionsExamples.GetImage.response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,31 @@ import { FileNapCatOneBotUUID } from 'napcat-common/src/file-uuid';
|
|||||||
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
import { FileActionsExamples } from '../example/FileActionsExamples';
|
||||||
file_id: Type.String(),
|
|
||||||
|
const PayloadSchema = Type.Object({
|
||||||
|
file_id: Type.String({ description: '文件ID' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type Payload = Static<typeof SchemaData>;
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
interface GetPrivateFileUrlResponse {
|
const ReturnSchema = Type.Object({
|
||||||
url?: string;
|
url: Type.Optional(Type.String({ description: '文件下载链接' })),
|
||||||
}
|
}, { description: '私聊文件URL信息' });
|
||||||
|
|
||||||
export class GetPrivateFileUrl extends GetPacketStatusDepends<Payload, GetPrivateFileUrlResponse> {
|
type ReturnType = Static<typeof ReturnSchema>;
|
||||||
|
|
||||||
|
export class GetPrivateFileUrl extends GetPacketStatusDepends<PayloadType, ReturnType> {
|
||||||
override actionName = ActionName.NapCat_GetPrivateFileUrl;
|
override actionName = ActionName.NapCat_GetPrivateFileUrl;
|
||||||
override payloadSchema = SchemaData;
|
override payloadSchema = PayloadSchema;
|
||||||
|
override returnSchema = ReturnSchema;
|
||||||
|
override actionSummary = '获取私聊文件URL';
|
||||||
|
override actionDescription = '获取指定私聊文件的下载链接';
|
||||||
|
override actionTags = ['文件接口'];
|
||||||
|
override payloadExample = FileActionsExamples.GetPrivateFileUrl.payload;
|
||||||
|
override returnExample = FileActionsExamples.GetPrivateFileUrl.response;
|
||||||
|
|
||||||
async _handle (payload: Payload) {
|
async _handle (payload: PayloadType) {
|
||||||
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id);
|
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file_id);
|
||||||
|
|
||||||
if (contextMsgFile?.fileUUID && contextMsgFile.msgId) {
|
if (contextMsgFile?.fileUUID && contextMsgFile.msgId) {
|
||||||
|
|||||||
@@ -2,22 +2,35 @@ import { GetFileBase, GetFilePayload, GetFileResponse } from './GetFile';
|
|||||||
import { ActionName } from '@/napcat-onebot/action/router';
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
|
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
const out_format = ['mp3', 'amr', 'wma', 'm4a', 'spx', 'ogg', 'wav', 'flac'];
|
import { FileActionsExamples } from '../example/FileActionsExamples';
|
||||||
|
|
||||||
type Payload = {
|
const out_format_list = ['mp3', 'amr', 'wma', 'm4a', 'spx', 'ogg', 'wav', 'flac'];
|
||||||
out_format: string;
|
|
||||||
} & GetFilePayload;
|
const PayloadSchema = Type.Object({
|
||||||
|
file: Type.Optional(Type.String({ description: '文件路径、URL或Base64' })),
|
||||||
|
file_id: Type.Optional(Type.String({ description: '文件ID' })),
|
||||||
|
out_format: Type.String({ description: '输出格式' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
type PayloadType = Static<typeof PayloadSchema>;
|
||||||
|
|
||||||
export default class GetRecord extends GetFileBase {
|
export default class GetRecord extends GetFileBase {
|
||||||
override actionName = ActionName.GetRecord;
|
override actionName = ActionName.GetRecord;
|
||||||
|
override payloadSchema = PayloadSchema;
|
||||||
|
override actionSummary = '获取语音';
|
||||||
|
override actionDescription = '获取指定语音文件的信息,并支持格式转换';
|
||||||
|
override actionTags = ['文件接口'];
|
||||||
|
override payloadExample = FileActionsExamples.GetRecord.payload;
|
||||||
|
override returnExample = FileActionsExamples.GetRecord.response;
|
||||||
|
|
||||||
override async _handle (payload: Payload): Promise<GetFileResponse> {
|
override async _handle (payload: PayloadType): Promise<GetFileResponse> {
|
||||||
const res = await super._handle(payload);
|
const res = await super._handle(payload as GetFilePayload);
|
||||||
if (payload.out_format && typeof payload.out_format === 'string') {
|
if (payload.out_format && typeof payload.out_format === 'string') {
|
||||||
const inputFile = res.file;
|
const inputFile = res.file;
|
||||||
if (!inputFile) throw new Error('file not found');
|
if (!inputFile) throw new Error('file not found');
|
||||||
if (!out_format.includes(payload.out_format)) {
|
if (!out_format_list.includes(payload.out_format)) {
|
||||||
throw new Error('转换失败 out_format 字段可能格式不正确');
|
throw new Error('转换失败 out_format 字段可能格式不正确');
|
||||||
}
|
}
|
||||||
const outputFile = `${inputFile}.${payload.out_format}`;
|
const outputFile = `${inputFile}.${payload.out_format}`;
|
||||||
|
|||||||
71
packages/napcat-onebot/action/file/flash/CreateFlashTask.ts
Normal file
71
packages/napcat-onebot/action/file/flash/CreateFlashTask.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
const richMediaList = [
|
||||||
|
'.mp4', '.mov', '.avi', '.wmv', '.mpeg', '.mpg', '.flv', '.mkv',
|
||||||
|
'.png', '.gif', '.jpg', '.jpeg', '.webp', '.bmp',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const CreateFlashTaskPayloadSchema = Type.Object({
|
||||||
|
files: Type.Union([
|
||||||
|
Type.Array(Type.String()),
|
||||||
|
Type.String(),
|
||||||
|
], { description: '文件列表或单个文件路径' }),
|
||||||
|
name: Type.Optional(Type.String({ description: '任务名称' })),
|
||||||
|
thumb_path: Type.Optional(Type.String({ description: '缩略图路径' })),
|
||||||
|
});
|
||||||
|
export type CreateFlashTaskPayload = Static<typeof CreateFlashTaskPayloadSchema>;
|
||||||
|
|
||||||
|
export class CreateFlashTask extends OneBotAction<CreateFlashTaskPayload, any> {
|
||||||
|
override actionName = ActionName.CreateFlashTask;
|
||||||
|
override payloadSchema = CreateFlashTaskPayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '任务创建结果' });
|
||||||
|
override actionSummary = '创建闪传任务';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
files: 'C:\\test.jpg',
|
||||||
|
name: 'test_task'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
task_id: 'task_123'
|
||||||
|
};
|
||||||
|
|
||||||
|
async _handle (payload: CreateFlashTaskPayload) {
|
||||||
|
const fileList = Array.isArray(payload.files) ? payload.files : [payload.files];
|
||||||
|
let thumbPath: string = '';
|
||||||
|
|
||||||
|
if (fileList.length === 1) {
|
||||||
|
// 我是真没hook到那种合并的缩略图是哪个方法产生的,暂时不实现(怀疑是js直接canvas渲染的!!) // 确认了猜想
|
||||||
|
const filePath = fileList[0];
|
||||||
|
if (filePath === undefined) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const ext = path.extname(filePath).toLowerCase();
|
||||||
|
|
||||||
|
if (richMediaList.includes(ext)) {
|
||||||
|
try {
|
||||||
|
const res = await this.core.apis.FlashApi.createFileThumbnail(filePath);
|
||||||
|
if (res && typeof res === 'object' && 'result' in res && res.result === 0) {
|
||||||
|
thumbPath = res.targetPath as string;
|
||||||
|
}
|
||||||
|
} catch (_e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toPlatformPath (inputPath: string) {
|
||||||
|
const unifiedPath = inputPath.replace(/[\\/]/g, path.sep);
|
||||||
|
return path.normalize(unifiedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalPath: string;
|
||||||
|
if (payload.thumb_path !== undefined) {
|
||||||
|
normalPath = path.normalize(payload.thumb_path);
|
||||||
|
} else {
|
||||||
|
normalPath = toPlatformPath(thumbPath);
|
||||||
|
}
|
||||||
|
return await this.core.apis.FlashApi.createFlashTransferUploadTask(fileList, normalPath, payload.name || '');
|
||||||
|
}
|
||||||
|
}
|
||||||
26
packages/napcat-onebot/action/file/flash/DownloadFileset.ts
Normal file
26
packages/napcat-onebot/action/file/flash/DownloadFileset.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
|
export const DownloadFilesetPayloadSchema = Type.Object({
|
||||||
|
fileset_id: Type.String({ description: '文件集 ID' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type DownloadFilesetPayload = Static<typeof DownloadFilesetPayloadSchema>;
|
||||||
|
|
||||||
|
export class DownloadFileset extends OneBotAction<DownloadFilesetPayload, any> {
|
||||||
|
override actionName = ActionName.DownloadFileset;
|
||||||
|
override payloadSchema = DownloadFilesetPayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '下载结果' });
|
||||||
|
override actionSummary = '下载文件集';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
fileset_id: 'set_123'
|
||||||
|
};
|
||||||
|
override returnExample = null;
|
||||||
|
|
||||||
|
async _handle (payload: DownloadFilesetPayload) {
|
||||||
|
// 默认路径 / fileset_id /为下载路径
|
||||||
|
return await this.core.apis.FlashApi.downloadFileSetBySetId(payload.fileset_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
|
export const GetFilesetIdPayloadSchema = Type.Object({
|
||||||
|
share_code: Type.String({ description: '分享码或分享链接' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GetFilesetIdPayload = Static<typeof GetFilesetIdPayloadSchema>;
|
||||||
|
|
||||||
|
export class GetFilesetId extends OneBotAction<GetFilesetIdPayload, { fileset_id: string }> {
|
||||||
|
override actionName = ActionName.GetFilesetId;
|
||||||
|
override payloadSchema = GetFilesetIdPayloadSchema;
|
||||||
|
override returnSchema = Type.Object({
|
||||||
|
fileset_id: Type.String({ description: '文件集 ID' })
|
||||||
|
});
|
||||||
|
override actionSummary = '获取文件集 ID';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
share_code: '123456'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
fileset_id: 'set_123'
|
||||||
|
};
|
||||||
|
|
||||||
|
async _handle (payload: GetFilesetIdPayload) {
|
||||||
|
// 适配share_link 防止被传 Link无法解析
|
||||||
|
const code = payload.share_code.includes('=') ? payload.share_code.split('=').slice(1).join('=') : payload.share_code;
|
||||||
|
const result = await this.core.apis.FlashApi.fromShareLinkFindSetId(code);
|
||||||
|
return { fileset_id: result.fileSetId };
|
||||||
|
}
|
||||||
|
}
|
||||||
28
packages/napcat-onebot/action/file/flash/GetFilesetInfo.ts
Normal file
28
packages/napcat-onebot/action/file/flash/GetFilesetInfo.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
|
export const GetFilesetInfoPayloadSchema = Type.Object({
|
||||||
|
fileset_id: Type.String({ description: '文件集 ID' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GetFilesetInfoPayload = Static<typeof GetFilesetInfoPayloadSchema>;
|
||||||
|
|
||||||
|
export class GetFilesetInfo extends OneBotAction<GetFilesetInfoPayload, any> {
|
||||||
|
override actionName = ActionName.GetFilesetInfo;
|
||||||
|
override payloadSchema = GetFilesetInfoPayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '文件集信息' });
|
||||||
|
override actionSummary = '获取文件集信息';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
fileset_id: 'set_123'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
fileset_id: 'set_123',
|
||||||
|
file_list: []
|
||||||
|
};
|
||||||
|
|
||||||
|
async _handle (payload: GetFilesetInfoPayload) {
|
||||||
|
return await this.core.apis.FlashApi.getFileSetIndoBySetId(payload.fileset_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
packages/napcat-onebot/action/file/flash/GetFlashFileList.ts
Normal file
30
packages/napcat-onebot/action/file/flash/GetFlashFileList.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
|
export const GetFlashFileListPayloadSchema = Type.Object({
|
||||||
|
fileset_id: Type.String({ description: '文件集 ID' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GetFlashFileListPayload = Static<typeof GetFlashFileListPayloadSchema>;
|
||||||
|
|
||||||
|
export class GetFlashFileList extends OneBotAction<GetFlashFileListPayload, any> {
|
||||||
|
override actionName = ActionName.GetFlashFileList;
|
||||||
|
override payloadSchema = GetFlashFileListPayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '文件列表' });
|
||||||
|
override actionSummary = '获取闪传文件列表';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
fileset_id: 'set_123'
|
||||||
|
};
|
||||||
|
override returnExample = [
|
||||||
|
{
|
||||||
|
file_name: 'test.jpg',
|
||||||
|
size: 1024
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
async _handle (payload: GetFlashFileListPayload) {
|
||||||
|
return await this.core.apis.FlashApi.getFileListBySetId(payload.fileset_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
packages/napcat-onebot/action/file/flash/GetFlashFileUrl.ts
Normal file
33
packages/napcat-onebot/action/file/flash/GetFlashFileUrl.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
|
export const GetFlashFileUrlPayloadSchema = Type.Object({
|
||||||
|
fileset_id: Type.String({ description: '文件集 ID' }),
|
||||||
|
file_name: Type.Optional(Type.String({ description: '文件名' })),
|
||||||
|
file_index: Type.Optional(Type.Number({ description: '文件索引' })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GetFlashFileUrlPayload = Static<typeof GetFlashFileUrlPayloadSchema>;
|
||||||
|
|
||||||
|
export class GetFlashFileUrl extends OneBotAction<GetFlashFileUrlPayload, any> {
|
||||||
|
override actionName = ActionName.GetFlashFileUrl;
|
||||||
|
override payloadSchema = GetFlashFileUrlPayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '文件下载链接' });
|
||||||
|
override actionSummary = '获取闪传文件链接';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
fileset_id: 'set_123'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
url: 'http://example.com/flash.jpg'
|
||||||
|
};
|
||||||
|
|
||||||
|
async _handle (payload: GetFlashFileUrlPayload) {
|
||||||
|
// 文件的索引依旧从0开始
|
||||||
|
return await this.core.apis.FlashApi.getFileTransUrl(payload.fileset_id, {
|
||||||
|
fileName: payload.file_name,
|
||||||
|
fileIndex: payload.file_index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
25
packages/napcat-onebot/action/file/flash/GetShareLink.ts
Normal file
25
packages/napcat-onebot/action/file/flash/GetShareLink.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
|
||||||
|
export const GetShareLinkPayloadSchema = Type.Object({
|
||||||
|
fileset_id: Type.String({ description: '文件集 ID' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GetShareLinkPayload = Static<typeof GetShareLinkPayloadSchema>;
|
||||||
|
|
||||||
|
export class GetShareLink extends OneBotAction<GetShareLinkPayload, any> {
|
||||||
|
override actionName = ActionName.GetShareLink;
|
||||||
|
override payloadSchema = GetShareLinkPayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '分享链接' });
|
||||||
|
override actionSummary = '获取文件分享链接';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
fileset_id: 'set_123'
|
||||||
|
};
|
||||||
|
override returnExample = 'http://example.com/share';
|
||||||
|
|
||||||
|
async _handle (payload: GetShareLinkPayload) {
|
||||||
|
return await this.core.apis.FlashApi.getShareLinkBySetId(payload.fileset_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
packages/napcat-onebot/action/file/flash/SendFlashMsg.ts
Normal file
49
packages/napcat-onebot/action/file/flash/SendFlashMsg.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
import { ChatType, Peer } from 'napcat-core/types';
|
||||||
|
|
||||||
|
export const SendFlashMsgPayloadSchema = Type.Object({
|
||||||
|
fileset_id: Type.String({ description: '文件集 ID' }),
|
||||||
|
user_id: Type.Optional(Type.String({ description: '用户 QQ' })),
|
||||||
|
group_id: Type.Optional(Type.String({ description: '群号' })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SendFlashMsgPayload = Static<typeof SendFlashMsgPayloadSchema>;
|
||||||
|
|
||||||
|
export class SendFlashMsg extends OneBotAction<SendFlashMsgPayload, any> {
|
||||||
|
override actionName = ActionName.SendFlashMsg;
|
||||||
|
override payloadSchema = SendFlashMsgPayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '发送结果' });
|
||||||
|
override actionSummary = '发送闪传消息';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
fileset_id: 'set_123',
|
||||||
|
user_id: '123456789'
|
||||||
|
};
|
||||||
|
override returnExample = {
|
||||||
|
message_id: 123456
|
||||||
|
};
|
||||||
|
|
||||||
|
async _handle (payload: SendFlashMsgPayload) {
|
||||||
|
let peer: Peer;
|
||||||
|
|
||||||
|
if (payload.group_id) {
|
||||||
|
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
|
||||||
|
} else if (payload.user_id) {
|
||||||
|
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
|
if (!uid) throw new Error('User not found');
|
||||||
|
|
||||||
|
// 可能需要更严格的判断
|
||||||
|
const isBuddy = await this.core.apis.FriendApi.isBuddy(uid);
|
||||||
|
peer = {
|
||||||
|
chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP,
|
||||||
|
peerUid: uid,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw new Error('user_id or group_id is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.core.apis.FlashApi.sendFlashMessage(payload.fileset_id, peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
import { ChatType } from 'napcat-core/types';
|
||||||
|
|
||||||
|
export const CancelOnlineFilePayloadSchema = Type.Object({
|
||||||
|
user_id: Type.String({ description: '用户 QQ' }),
|
||||||
|
msg_id: Type.String({ description: '消息 ID' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CancelOnlineFilePayload = Static<typeof CancelOnlineFilePayloadSchema>;
|
||||||
|
|
||||||
|
export class CancelOnlineFile extends OneBotAction<CancelOnlineFilePayload, any> {
|
||||||
|
override actionName = ActionName.CancelOnlineFile;
|
||||||
|
override payloadSchema = CancelOnlineFilePayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '取消结果' });
|
||||||
|
override actionSummary = '取消在线文件';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
user_id: '123456789',
|
||||||
|
msg_id: '123'
|
||||||
|
};
|
||||||
|
override returnExample = null;
|
||||||
|
|
||||||
|
async _handle (payload: CancelOnlineFilePayload) {
|
||||||
|
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
|
if (!uid) throw new Error('User not found');
|
||||||
|
|
||||||
|
// 仅私聊
|
||||||
|
const peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: uid };
|
||||||
|
|
||||||
|
return await this.core.apis.OnlineApi.cancelMyOnlineFileMsg(peer, payload.msg_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
|
||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
import { ChatType } from 'napcat-core/types';
|
||||||
|
|
||||||
|
export const GetOnlineFileMessagesPayloadSchema = Type.Object({
|
||||||
|
user_id: Type.String({ description: '用户 QQ' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GetOnlineFileMessagesPayload = Static<typeof GetOnlineFileMessagesPayloadSchema>;
|
||||||
|
|
||||||
|
export class GetOnlineFileMessages extends OneBotAction<GetOnlineFileMessagesPayload, any> {
|
||||||
|
override actionName = ActionName.GetOnlineFileMessages;
|
||||||
|
override payloadSchema = GetOnlineFileMessagesPayloadSchema;
|
||||||
|
override returnSchema = Type.Any({ description: '在线文件消息列表' });
|
||||||
|
override actionSummary = '获取在线文件消息';
|
||||||
|
override actionTags = ['文件扩展'];
|
||||||
|
override payloadExample = {
|
||||||
|
user_id: '123456789'
|
||||||
|
};
|
||||||
|
override returnExample = [];
|
||||||
|
|
||||||
|
async _handle (payload: GetOnlineFileMessagesPayload) {
|
||||||
|
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
|
if (!uid) throw new Error('User not found');
|
||||||
|
|
||||||
|
// 仅私聊
|
||||||
|
const peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: uid };
|
||||||
|
|
||||||
|
return await this.core.apis.OnlineApi.getOnlineFileMsg(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user