Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
1c5c868
feat
idranme Apr 19, 2026
aa2266f
refactor: use nullish coalescing for nick fallback
idranme Apr 19, 2026
caf42cb
test: add unit tests for Satori and Milky modules, update test config…
linyuchen Apr 20, 2026
0205c8f
Merge branch 'main' into dev
linyuchen Apr 20, 2026
3448385
chore
idranme Apr 20, 2026
6642ba0
chore: bump Node.js version badge to >=24.x
idranme Apr 20, 2026
b3d7473
chore: bump @hono/node-server to v2.0.0
idranme Apr 21, 2026
8b51987
refactor: log process.argv in main and remove duplicate
idranme Apr 21, 2026
3c8a552
fix(milky): use getVideoUrlByPacket for video temp URLs
idranme Apr 21, 2026
69d1320
feat(onebot): add SetInputStatus action
idranme Apr 21, 2026
f91c0e5
docs(changelog): update release notes
idranme Apr 21, 2026
a26aacc
test: ci
linyuchen Apr 22, 2026
5cdf0e9
Merge branch 'dev' of github.com:LLOneBot/LLOneBot into dev
linyuchen Apr 22, 2026
fb58ef5
ci: add e2e test workflow with self-hosted runner
linyuchen Apr 22, 2026
3232248
ci: use yarn instead of npm for dependency install
linyuchen Apr 22, 2026
c91eb28
ci: track yarn.lock for reproducible CI installs
linyuchen Apr 22, 2026
547148a
ci: disable yarn hardened mode for public PRs
linyuchen Apr 22, 2026
35f9705
ci: use yarn --no-immutable to allow lockfile updates
linyuchen Apr 22, 2026
9768739
ci: fix Dockerfile.test to check bundle size instead of executing it
linyuchen Apr 22, 2026
103b773
revert: restore Dockerfile.test to execute bundle
linyuchen Apr 22, 2026
530d59e
ci: run Docker test on self-hosted runner with PMHQ connectivity
linyuchen Apr 22, 2026
3f6f7b3
ci: poll for PMHQ connection success then exit Docker test
linyuchen Apr 22, 2026
bea43cf
fix: update test mocks for relocated config module and ctx.get() API …
linyuchen Apr 22, 2026
b3031ef
feat(onebot): the `get_msg` API now includes a `status` field in its …
idranme Apr 22, 2026
bb1afa2
ci: stop resident llbot containers before e2e test to avoid PMHQ conf…
linyuchen Apr 22, 2026
610947d
revert: remove unnecessary container stop/start steps
linyuchen Apr 22, 2026
205626b
ci: trigger re-run after QQ login
linyuchen Apr 22, 2026
ac8b9cb
fix: add @llbot path alias to jest moduleNameMapper
linyuchen Apr 22, 2026
8e00070
fix: use absolute path for test config to fix CWD issue
linyuchen Apr 22, 2026
c87c814
fix: use fileURLToPath for Jest ESM compatibility
linyuchen Apr 22, 2026
69e7314
ci: generate test.config.json in workflow (file is gitignored)
linyuchen Apr 22, 2026
8810d03
ci: fix QQ volume permissions before starting LLBot
linyuchen Apr 22, 2026
ce39373
ci: trigger re-run
linyuchen Apr 22, 2026
70db454
ci: run PR LLBot in Docker containers with shared QQ volumes
linyuchen Apr 22, 2026
9d046d5
feat(onebot): add the `get_group_album_media_list` API
idranme Apr 22, 2026
0b2cab6
ci: symlink QQ volumes to host for local LLBot e2e tests
linyuchen Apr 22, 2026
6a04255
docs(changelog): update release notes
idranme Apr 22, 2026
b20c28e
ci: run PR LLBot in Docker, mount e2e media into PMHQ and LLBot conta…
linyuchen Apr 22, 2026
7c0aa98
ci: sudo cleanup workspace dirs created by root Docker containers
linyuchen Apr 22, 2026
b5bafb2
ci: run everything in Docker - LLBot, PMHQ, and e2e tests share same …
linyuchen Apr 22, 2026
a27ec03
Merge branch 'dev' into test/e2e-workflow
idranme Apr 22, 2026
3ccd1cb
ci: share /app/test path between e2e-runner and LLBot containers to f…
linyuchen Apr 23, 2026
b8dbf25
ci: add pre-checkout cleanup and run npm install inside Docker
linyuchen Apr 23, 2026
296b1dd
ci: add test summary step
linyuchen Apr 23, 2026
8d738b3
ci: remove redundant build-test job, e2e-test already covers build+do…
linyuchen Apr 23, 2026
ce00c55
fix: auto-refresh login QR code every 120s instead of showing it once
linyuchen Apr 23, 2026
9de3d06
fix: decouple e2e tests from main project source imports
linyuchen Apr 23, 2026
ce69fe7
Revert "fix: decouple e2e tests from main project source imports"
linyuchen Apr 23, 2026
56514bf
fix: add diagnostics:false and @/ path mapping to ts-jest config
linyuchen Apr 23, 2026
ccb1b2f
fix: mount entire workspace into e2e-runner so relative paths resolve…
linyuchen Apr 23, 2026
1816fde
fix: mount workspace at same path in LLBot and e2e containers
linyuchen Apr 23, 2026
28ed579
ci: add ffmpeg to test image and mount workspace into PMHQ
linyuchen Apr 23, 2026
6432e2f
ci: use tuna mirror for alpine apk
linyuchen Apr 23, 2026
22eb023
fix: mount LLBot data dir into PMHQ and add config inject
linyuchen Apr 24, 2026
0899fff
fix: update test image URL in ocr-image test
linyuchen Apr 24, 2026
c266eb5
fix: use secondary client for get_image/get_file since file ID belong…
linyuchen Apr 24, 2026
70d86c1
fix: add timeout and fallback for getGroupShutUpMemberList when no ba…
linyuchen Apr 24, 2026
ed3b88b
fix: cleaner timeout handling for getGroupShutUpMemberList
linyuchen Apr 24, 2026
08a7c6c
feat: add onCallResult to invoke for early resolve based on call retu…
linyuchen Apr 24, 2026
0d9e06f
fix: onCallResult sets timeout fallback instead of immediate resolve
linyuchen Apr 24, 2026
bedf0ed
fix: onCallResult immediately resolves when it returns a value
linyuchen Apr 24, 2026
d457952
fix: match groupCode '0' for empty shut-up list instead of using onCa…
linyuchen Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
284 changes: 284 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
name: Test

on:
pull_request:
branches: [main]
push:
branches: [main]

jobs:
unit-test:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24

- name: Install dependencies
run: |
corepack enable
yarn install --no-immutable
cd src/webui/FE && npm install

- name: Run unit tests
run: npm run test:unit

- name: Run WebUI tests
run: npm run test:webui

e2e-test:
name: E2E Tests
runs-on: [self-hosted, llbot-test]
needs: [unit-test]
if: github.event_name == 'pull_request'
steps:
- name: Cleanup root-owned files
run: sudo rm -rf ${{ github.workspace }}/workspace1 ${{ github.workspace }}/workspace2 ${{ github.workspace }}/test/onebot11-api-test/node_modules

- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24

- name: Install dependencies
run: |
corepack enable
yarn install --no-immutable
cd src/webui/FE && npm install

- name: Build
run: |
npm run build
npm run build-webui

- name: Build PR LLBot image
run: docker build -f docker/Dockerfile.test -t llbot-pr-test .

- name: Write configs
run: |
mkdir -p workspace1/data workspace2/data

cat > workspace1/data/config_1577491075.json << 'CONF'
{
"ob11": {
"enable": true,
"connect": [
{ "type": "http", "enable": true, "host": "", "port": 53000, "token": "123", "messageFormat": "array", "reportSelfMessage": false, "reportOfflineMessage": false, "debug": false }
]
},
"satori": { "enable": false },
"milky": { "enable": false },
"webui": { "enable": false }
}
CONF

cat > workspace2/data/config_412805684.json << 'CONF'
{
"ob11": {
"enable": true,
"connect": [
{ "type": "http", "enable": true, "host": "", "port": 53001, "token": "123", "messageFormat": "array", "reportSelfMessage": false, "reportOfflineMessage": false, "debug": false }
]
},
"satori": { "enable": false },
"milky": { "enable": false },
"webui": { "enable": false }
}
CONF

- name: Restart PMHQ with workspace mount
run: |
docker stop pmhq1 pmhq2

docker rm -f pmhq1-test pmhq2-test 2>/dev/null || true

docker run -d --name pmhq1-test \
--network llbot-test_test_network \
--privileged \
-e ENABLE_HEADLESS=false \
-e AUTO_LOGIN_QQ=1577491075 \
-v llbot-test_qq_volume1:/root/.config/QQ \
-v ${{ github.workspace }}:/app/workspace:rw \
-v ${{ github.workspace }}/workspace1/data:/app/llbot/data:rw \
linyuchen/pmhq:latest

docker run -d --name pmhq2-test \
--network llbot-test_test_network \
--privileged \
-e ENABLE_HEADLESS=false \
-e AUTO_LOGIN_QQ=412805684 \
-v llbot-test_qq_volume2:/root/.config/QQ \
-v ${{ github.workspace }}:/app/workspace:rw \
-v ${{ github.workspace }}/workspace2/data:/app/llbot/data:rw \
linyuchen/pmhq:latest

echo "Waiting for PMHQ to be ready..."
for i in $(seq 1 30); do
H1=$(docker exec pmhq1-test curl -sf http://localhost:13000/health 2>/dev/null || true)
H2=$(docker exec pmhq2-test curl -sf http://localhost:13000/health 2>/dev/null || true)
[ -n "$H1" ] && [ -n "$H2" ] && echo "Both PMHQ ready" && break
echo "Waiting for PMHQ... ($i/30)"
sleep 5
done

- name: Start PR LLBot containers
run: |
docker rm -f llbot-pr1 llbot-pr2 2>/dev/null || true

docker run -d --name llbot-pr1 \
--network llbot-test_test_network \
-e pmhq_host=pmhq1-test \
-e pmhq_port=13000 \
-e WEBUI_PORT=3091 \
-v llbot-test_qq_volume1:/root/.config/QQ \
-v ${{ github.workspace }}/workspace1/data:/app/llbot/data:rw \
-v ${{ github.workspace }}:/app/workspace:ro \
llbot-pr-test

docker run -d --name llbot-pr2 \
--network llbot-test_test_network \
-e pmhq_host=pmhq2-test \
-e pmhq_port=13000 \
-e WEBUI_PORT=3092 \
-v llbot-test_qq_volume2:/root/.config/QQ \
-v ${{ github.workspace }}/workspace2/data:/app/llbot/data:rw \
-v ${{ github.workspace }}:/app/workspace:ro \
llbot-pr-test

- name: Wait for LLBot ready
run: |
for i in $(seq 1 60); do
R1=$(docker exec llbot-pr1 curl -sf http://127.0.0.1:53000/get_login_info -H "Authorization: Bearer 123" 2>/dev/null || true)
R2=$(docker exec llbot-pr2 curl -sf http://127.0.0.1:53001/get_login_info -H "Authorization: Bearer 123" 2>/dev/null || true)
[ -n "$R1" ] && [ -n "$R2" ] && echo "Both LLBot instances ready" && break
echo "Waiting... ($i/60)"
sleep 5
done

- name: Write test config
run: |
cat > ${{ github.workspace }}/test/onebot11-api-test/config/test.config.json << 'CONF'
{
"accounts": {
"primary": {
"host": "http://llbot-pr1:53000",
"apiKey": "123",
"protocol": "http",
"user_id": "1577491075"
},
"secondary": {
"host": "http://llbot-pr2:53001",
"apiKey": "123",
"protocol": "http",
"user_id": "412805684"
}
},
"test_group_id": "1098066573",
"timeout": 30000,
"retryAttempts": 3
}
CONF

- name: Run e2e tests in Docker
run: |
docker run --rm --name e2e-runner \
--network llbot-test_test_network \
-v ${{ github.workspace }}:/app/workspace:rw \
-w /app/workspace/test/onebot11-api-test \
node:24-alpine3.23 sh -c "npm install && npm test"
continue-on-error: true

- name: Show test summary
if: always()
run: |
if [ -f test/onebot11-api-test/test-report.html ]; then
echo "Test report generated"
else
echo "No test report found"
fi

- name: Stop test containers and restore PMHQ
if: always()
run: |
echo "=== LLBot1 logs ===" && docker logs llbot-pr1 2>&1 | tail -30 || true
echo "=== LLBot2 logs ===" && docker logs llbot-pr2 2>&1 | tail -30 || true
docker rm -f llbot-pr1 llbot-pr2 pmhq1-test pmhq2-test 2>/dev/null || true
docker start pmhq1 pmhq2 2>/dev/null || true
sudo rm -rf ${{ github.workspace }}/workspace1 ${{ github.workspace }}/workspace2

- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-test-report
path: test/onebot11-api-test/test-report.html
retention-days: 30

report:
name: Test Report
runs-on: ubuntu-latest
needs: [unit-test, e2e-test]
if: always() && github.event_name == 'pull_request'
permissions:
pull-requests: write
steps:
- name: Generate report comment
uses: actions/github-script@v7
with:
script: |
const jobs = {
'unit-test': '${{ needs.unit-test.result }}',
'e2e-test': '${{ needs.e2e-test.result }}',
};

const icons = { success: '✅', failure: '❌', skipped: '⏭️', cancelled: '🚫' };
const lines = Object.entries(jobs).map(([name, result]) =>
`| ${name} | ${icons[result] || '❓'} ${result} |`
);

const allPassed = Object.values(jobs).every(r => r === 'success' || r === 'skipped');
const summary = allPassed ? '✅ All tests passed' : '❌ Some tests failed';

const body = [
`## Test Report`,
``,
`| Job | Status |`,
`|-----|--------|`,
...lines,
``,
summary,
].join('\n');

// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('## Test Report')
);

if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
!.yarn/versions

package-lock.json
yarn.lock
node_modules
dist
out
Expand Down
5 changes: 5 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
approvedGitRepositories:
- "**"

enableScripts: true
Comment on lines +1 to +4
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): 全局启用脚本并允许所有 git 仓库会显著增加供应链风险。

使用 `approvedGitRepositories: [

Original comment in English

🚨 issue (security): Enabling scripts globally and approving all git repositories can increase supply-chain risk.

With `approvedGitRepositories: [


nodeLinker: node-modules
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# LLBot
![Node.js Version](https://img.shields.io/badge/node-%3E=22.x-brightgreen)
![Node.js Version](https://img.shields.io/badge/node-%3E=24.x-brightgreen)

<div align="center">
<img src="./logo.jpg" width="200" alt="Logo" />
Expand Down
10 changes: 10 additions & 0 deletions doc/更新日志.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
V7.12.3
更新时间 2026-04-

* OneBot 支持伪造合并转发发送 `at` 消息段
* OneBot 支持设置输入状态 `set_input_status` API
* OneBot 支持获取群相册媒体列表 `get_group_album_media_list` API
* OneBot 新增获取消息 `get_msg` API 返回的 `status` 字段
* Milky 修复视频链接可能获取失败

=================
V7.12.2
更新时间 2026-04-18

Expand Down
8 changes: 8 additions & 0 deletions docker/Dockerfile.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM node:24-alpine3.23
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
apk add --no-cache ffmpeg curl
WORKDIR /app/llbot
COPY dist/ .
COPY docker/startup.sh /startup.sh
RUN chmod +x /startup.sh
ENTRYPOINT ["/startup.sh"]
2 changes: 1 addition & 1 deletion package-dist.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"name":"llonebot-dist","version":"7.12.2","type":"module","description":"","main":"llbot.js","author":"linyuchen","repository":{"type":"git","url":"https://github.com/LLOneBot/LuckyLilliaBot"}}
{"name":"llonebot-dist","version":"7.12.2","type":"module","description":"","main":"llbot.js","author":"linyuchen","repository":{"type":"git","url":"https://github.com/LLOneBot/LuckyLilliaBot"}}
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
"postinstall": "npm --prefix src/webui/FE install",
"format": "prettier -cw .",
"check": "tsc",
"test:webui": "vitest run --config vitest.config.ts"
"test:webui": "vitest run --config vitest.config.ts 'test/webui'",
"test:unit": "vitest run --config vitest.config.ts 'test/unit'",
"test": "vitest run --config vitest.config.ts"
},
"author": "",
"license": "GPL-2.0",
"dependencies": {
"@cordisjs/plugin-logger": "^1.0.2",
"@cordisjs/plugin-timer": "^1.1.1",
"@hono/node-server": "^1.19.14",
"@hono/node-server": "^2.0.0",
"@hono/node-ws": "^1.3.0",
"@minatojs/driver-sqlite": "^5.0.3",
"@saltify/milky-types": "^1.2.2",
Expand Down Expand Up @@ -54,9 +56,9 @@
"ts-case-convert": "^2.1.0",
"tsx": "^4.21.0",
"typescript": "^6.0.3",
"vite": "^8.0.8",
"vite": "^8.0.9",
"vite-plugin-cp": "^6.0.3",
"vitest": "^4.1.4"
},
"packageManager": "yarn@4.13.0"
"packageManager": "yarn@4.14.1"
}
8 changes: 5 additions & 3 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ async function onLoad() {
}

const isDocker = isDockerEnvironment()
let qrCodeTriggered = false
let lastQrCodeTime = 0

const printLoginQrCode = async () => {
try {
Expand Down Expand Up @@ -135,8 +135,9 @@ async function onLoad() {
return
}
if (!pmhqSelfInfo.online) {
if (isDocker && !qrCodeTriggered) {
qrCodeTriggered = true
const now = Date.now()
if (isDocker && now - lastQrCodeTime > 120_000) {
lastQrCodeTime = now
printLoginQrCode()
}
setTimeout(checkLogin, 1000)
Expand Down Expand Up @@ -172,6 +173,7 @@ async function onLoad() {
ctx.inject(['logger'], (ctx) => {
ctx.logger.exporter(new Log(ctx, true))
ctx.logger.info(`LLBot ${version}`)
ctx.logger.info(process.argv)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): 在启动时记录 process.argv 可能会无意中泄露敏感信息。

CLI 参数中往往包含 token、密码或其他机密信息。每次启动都记录这些参数,会有将凭据泄露到日志或外部日志聚合系统的风险。如果你只是为了排查问题需要这条日志,请将其放在调试开关之后,并/或对敏感值做脱敏处理,或者干脆删除这条日志。

Original comment in English

🚨 issue (security): Logging process.argv on startup may unintentionally expose sensitive information.

CLI arguments often include tokens, passwords, or other secrets. Logging them on every start risks leaking credentials to logs and external aggregators. If you need this for troubleshooting, please gate it behind a debug flag and/or redact sensitive values, or remove it entirely.

})
// setFFMpegPath(config.ffmpeg || '')
ctx.inject(['pmhq', 'config', 'logger'], (ctx) => {
Expand Down
Loading
Loading