93 Commits
Author SHA1 Message Date
saopig1andClaude Opus 4.6 b9e42d543c fix(web): make bot-link copy work over plain HTTP
navigator.clipboard requires a secure context (HTTPS or localhost). For
users hosting the bot on a remote IP and accessing via http://<ip>:3000,
clipboard.writeText is undefined and the copy silently fails. Add a
hidden-textarea + execCommand('copy') fallback, with a final prompt()
fallback so the user can always grab the URL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 00:26:05 +08:00
saopig1andClaude Opus 4.6 4406eeacbe fix(api-server): handle QQ Music API export differences across versions
The @sansenjian/qq-music-api module's export structure varies between
versions (2.2.10 vs 2.2.11+). Add fallback chain to find the Koa app:
try candidate.listen first, then candidate.default.listen.

Also resolve leftover merge conflict marker in instance.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:52:27 +08:00
saopig1andClaude Opus 4.6 a56dc5bc85 docs(readme): document bot profile auto-update feature
Add profile feature to features list, architecture tree, and changelog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:52:09 +08:00
saopig1 1a663156c3 Merge branch 'main' of https://github.com/ZHANGTIANYAO1/teamspeak-music-bot into dev 2026-04-13 00:49:40 +08:00
saopig1andClaude Opus 4.6 e92628a6b4 fix(profile): add timeout to clientedit, guard clearAvatar with generation
- Wrap clientedit (description) with 5s timeout to prevent blocking
  channel description and now-playing updates if the command hangs
- Check generation counter in clearAvatar to avoid clearing a newer
  song's avatar when stop→play happens in quick succession

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:48:09 +08:00
saopig1andClaude Opus 4.6 b4ad78ce84 fix(profile): harden against race conditions and edge cases
- Add generation counter to prevent stale avatar uploads from
  overwriting newer song's profile when rapidly skipping tracks
- Fix nickname truncation to use UTF-8 byte length instead of JS
  string length (TS3 counts bytes, Chinese chars are 3 bytes)
- Add timeout to clearAvatar file transfer (was missing)
- Extract withTimeout helper to deduplicate timeout logic
- Add BiliBili CDN thumbnail resize support (@200w_200h)
- Bump generation on reconnect to discard in-flight updates from
  the old connection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:44:57 +08:00
saopig1andClaude Opus 4.6 30aced6d36 fix(profile): use clientedit for description, add sendCommandNoWait
Description via clientupdate is rejected (error 1538) on TS3 full-client
protocol. Switch to clientedit on the bot's own clid, which is how
TS3AudioBot handles it. Requires b_client_modify_description permission.

Also add sendCommandNoWait to TS3Client for fire-and-forget commands
(clientupdate, channeledit) that don't return timely responses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:39:36 +08:00
saopig1andClaude Opus 4.6 da5b34f5f4 feat(profile): auto-update bot avatar, nickname, and away status based on playing song
Add BotProfileManager that updates the bot's TeamSpeak presence when
songs change: album cover as avatar, song info in nickname, away status
toggled on stop/play. Each feature is independently configurable via
REST API and persisted to the database. Permission-safe — features that
fail due to insufficient server permissions are silently disabled until
reconnect. Description falls back to nickname display on TS3 (only
supported via TS6 HTTP Query).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:14:29 +08:00
saopig1andClaude Opus 4.6 5647cf6d36 feat(qq): consume local @sansenjian/qq-music-api fork with VIP-aware getMusicPlay
Repoints the `@sansenjian/qq-music-api` dependency from the public npm
release to a local fork at ../qq-music-api, which ships a corrected
getMusicPlay that:
  - drops the hardcoded-sign GET path (no longer honored by QQ's vkey
    server for VIP entitlement lookups)
  - POSTs JSON directly to u.y.qq.com/cgi-bin/musicu.fcg (mirroring
    the library's own getLyric.ts pattern)
  - extracts qqmusic_key from the forwarded cookie and passes it as
    `comm.authst` — the inline auth field the jsososo/QQMusicApi
    reference implementation sets
  - uses `ct: 19` (was 24) to match the community reference

For accounts that actually have entitlement to a given track, this
now returns the real VIP URL. For accounts that don't, QQ's vkey
server still returns result=104003 with empty purl — this is correct
server-side behavior and not a bug. Verified by observing the real
QQ Music web player on y.qq.com fall back to the same 30-second
preview on a logged-in account that lacks the specific track tier.

Supporting changes:

  src/music/api-server.ts
    The fork (v2.2.11) stopped auto-starting a Koa server on import —
    it only listens when run as `require.main`. Explicitly import the
    default Koa app and call .listen() with a server handle we can
    clean up on shutdown. Without this fix, port 3200 silently fails
    to bind and every QQ endpoint 502s.

  src/music/qq.ts (getSongDetail)
    The library's /getSongInfo endpoint returns upstream code 500001
    because its param format no longer matches QQ's current API.
    resolveAndPlay only needs `id` + `platform` to fetch a play URL,
    so fall through to a minimal stub on /getSongInfo failure. This
    unblocks /play-by-id and /add-by-id for QQ — they had been
    returning "Song not found" for every QQ track regardless of
    entitlement.

  scripts/qq_browser_login.py
    Visible-browser diagnostic tool that opens Chromium at y.qq.com,
    auto-detects login via uin cookie poll, captures the full
    post-login cookie set, tests it against /getMusicPlay for 稻香,
    and writes the cookie to data/cookies/qq.json only if VIP
    actually unlocks. On failure, dumps the full cookie to
    data/cookies/qq.browser-capture.json for OAuth-vs-browser diff.

  scripts/qq_verify_entitlement.py
    Companion diagnostic: opens the real QQ Music web player at a
    specific song's detail page so the user can manually click play
    and verify whether their account has entitlement — independent
    of any code path in this project. If the browser plays the full
    song, HTTP 104003 is a request-signing issue; if the browser
    also falls back to a 30-second preview, the account lacks the
    tier/album purchase and no code fix can change that.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:52:12 +08:00
saopig1andClaude Opus 4.6 3aa06006fe fix(qq): repair QR code login flow against @sansenjian/qq-music-api 2.x
QR login against QQ Music has been silently broken: every call to
checkQrCodeStatus returned "expired", so the scan-and-confirm cycle
never completed even when the user successfully scanned the code. The
root cause was four independent bugs in our wrapper talking past the
library's actual HTTP shape.

1. getQrCode lost ptqrtoken.
   /getQQLoginQr returns { img, qrsig, ptqrtoken }, but we stored only
   one of them in the single `key` field (picking qrsig, falling back
   to ptqrtoken). The polling endpoint needs BOTH — passing only one
   fails with 400 "参数错误". Fix: pack both into the opaque `key` as
   "qrsig|ptqrtoken" and split on the receive side.

2. checkQrCodeStatus used GET.
   @sansenjian/qq-music-api 2.x registers /checkQQLoginQr as POST only
   (router.js: `router.post('/checkQQLoginQr', ...)`). GET returns 405
   Method Not Allowed, axios throws, the catch returns "expired".
   Fix: api.post(url, null, { params }).

3. checkQrCodeStatus parsed the wrong response shape.
   The endpoint uses customResponse, not successResponse, so axios sees
   the body directly (no { response: ... } wrapper). The actual shape
   for each state is:
     waiting:  { isOk: false, refresh: false, message: '未扫描二维码' }
     expired:  { isOk: false, refresh: true,  message: '二维码已失效' }
     success:  { isOk: true, message: '登录成功', session: { cookie } }
   We were looking for a numeric `code === 0/1/2` field that does not
   exist, so every state fell through to "expired". Fix: switch on
   isOk / refresh / message.

4. Cookie read from the wrong path on success.
   On isOk=true the cookie lives at res.data.session.cookie, not
   res.data.cookie — so even if everything else had worked, the cookie
   would never have been saved. Fix: read session.cookie.

Also rewrites getAuthStatus to actually validate the cookie:

5. getAuthStatus hit a non-validating endpoint.
   /getUserAvatar is not registered on the library's main router; the
   real route is /user/getUserAvatar, and even that just builds a
   static avatar URL from a uin without round-tripping through QQ
   Music with the cookie. The result: the bot happily persisted any
   user-supplied cookie to disk and sent it on every request while
   every downstream login check returned "not logged in". Fix: parse
   uin from the cookie, call /user/getUserPlaylists (which actually
   hits QQ Music with the cookie), and derive the avatar URL from the
   uin via q.qlogo.cn/headimg_dl.

This is the same getAuthStatus fix that was sitting on the
claude/bot-shutdown-disconnect-cwFvF branch, now combined with the
QR login repairs.

Verification:
- tsc --noEmit clean
- vitest: 93/93 pass
- Live: POST /api/auth/qrcode platform=qq returns both tokens packed
  into `key`; polling a freshly-issued QR returns {"status":"waiting"}
  instead of the old {"status":"expired"}; raw library response is
  {"isOk":false,"refresh":false,"message":"未扫描二维码"} as expected.
- Regression: netease and bilibili QR flows still produce non-empty
  qrUrl/key — no collateral damage to the other providers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:07:26 +08:00
saopig1andClaude Opus 4.6 5692d043c9 docs(readme): prepare for merge to main — remove dev-branch framing, add changelog
- Delete the top-of-file "dev 分支" warning block and the whole
  "dev 分支最新变更" section. Content that was only a dev-branch
  changelog is now folded into the new "更新日志" section.
- Reword the identity migration warning in the upgrade section so it
  refers to the library version (0.1.x → 0.2.x) instead of a specific
  dev-branch commit hash, and add a "how to tell if you need to
  migrate" note for users on fresh installs.
- Remove "`dev` 分支已实现" phrasing from the TS6 FAQ entry.
- Add a new "更新日志" section before "致谢" that summarizes recent
  changes in four buckets — protocol/stability, HTTP API hardening,
  connection state consistency, and feature improvements — plus a
  brief historical milestones block pointing at git log for full
  history.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 02:15:08 +08:00
saopig1andClaude Opus 4.6 49879f987c docs(readme): document identity migration for @honeybbq/teamspeak-client 0.2.x upgrade
Adds a prominent warning block at the top of the "更新升级" section
explaining why identities generated by 0.1.0 are incompatible with
0.2.x's corrected P-256 DER encoding path, and how to migrate: clear
the identity column so the next start regenerates a fresh key.

Covers three scenarios:
- TS3 + old identity: mostly still works (TS3 is tolerant)
- TS6 + old identity: must clear — otherwise handshake hangs at
  `received initivexpand2`
- After clearing: server groups must be re-granted to the new UID once

Also adds a back-reference at the end of the upgrade section so
readers skimming per-platform instructions don't miss the migration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 01:57:39 +08:00
saopig1andClaude Opus 4.6 ca4cb1790a chore(deps): bump @honeybbq/teamspeak-client to 0.2.1, drop ts6-compat shim
Version 0.2.1 ships a universal clientinit format that works natively
against both TS3 and TS6 servers:

    client_version: "3.?.? [Build: 5680278000]"
    client_version_sign: DX5NIYLvfJEUjuIbCidnoeozxIDRRkpq3I9vVMBmE9L2qnekOo
                         BzSenkzsg2lC9CMv8K5hkEzhr2TYUYSwUXCg==

The old ts6-compat.ts workaround (monkey-patching handler.sendPacket to
rewrite clientinit's client_version to "3.6.2") is now actively wrong:
it replaces the library's new correct version/signature pair with a
stale one that TS6 servers reject, which is why the first 0.2.1 TS6
handshake attempt still hung at `received initivexpand2`.

Changes:
- package.json: "@honeybbq/teamspeak-client": "^0.1.0" -> "^0.2.1"
- src/ts-protocol/client.ts: remove patchClientInitVersion import and
  the sendPacket monkey-patch block. Leave an inline comment so anyone
  reading the git blame understands why the shim is gone.
- Delete src/ts-protocol/ts6-compat.ts and ts6-compat.test.ts — no
  callers remain.

Other 0.2.x notes worth knowing (no code change here, just documenting):
- ClientOptions gained serverPassword / defaultChannel /
  defaultChannelPassword that are sent DURING clientinit. We still call
  our own joinChannel() post-connect because the existing flow works
  and switching is an orthogonal refactor.
- 0.1.1 contains the P-256 DER encoding fix (PR #5 by ZHANGTIANYAO1).
  Identities generated by 0.1.0 are cryptographically incompatible with
  0.2.x's corrected handshake path — a bot whose identity column was
  populated before this upgrade will hang at `received initivexpand2`
  and fall through the 15s connect deadline. Workaround: clear the
  identity column so the next start generates a fresh key. Server
  groups assigned to the old UID must be re-granted once against the
  new one.

Verification:
- tsc --noEmit clean
- vitest: 93/93 unit tests pass (1 test file removed with ts6-compat)
- scripts/test_full_feature.py against a local TS6 server: 51/51 pass,
  including handshake, voice playback, identity persistence, WebSocket
  stateChange broadcasts, and all corner-case regressions.
- Live: bot connected to TS6 in ~80ms after identity regeneration,
  played NetEase audio through the voice channel, clean stop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 01:54:43 +08:00
saopig1andClaude Opus 4.6 4643f70f4a fix: harden bot lifecycle, validate HTTP inputs, make YouTube truly optional
Major bug fixes and corner-case hardening across the backend, plus a
comprehensive feature test suite. All 94 unit tests + 51 integration
tests pass against a local TS3 server.

Lifecycle & state consistency
-----------------------------
- Bug A: startBot() now wraps connect() in a 15s deadline. A hung TS
  handshake no longer blocks the /start HTTP call forever; the failing
  instance is torn down and the caller gets a clean 500.
- Bug B: executeCommand rejects audio-dispatching commands (play, add,
  next, skip, prev, playlist, album, fm) when the bot is disconnected.
  Config-only commands (vol, mode, clear, stop, queue, now, lyrics)
  still work so the UI stays usable while offline.
- Bug C: the tsClient 'disconnected' handler always clears player state
  now, even when connect() never completed. A separate disconnectEmitted
  flag guards duplicate external event emission. Previously an orphaned
  connect attempt that idle-timed-out would leave playing=true forever.
- resolveAndPlay re-checks this.connected AFTER the URL-resolve await so
  a stop() during the network call can't spawn ffmpeg on a disconnected
  bot.
- connect() throws if disconnect() fired during the handshake await,
  preventing a concurrent stop from being overwritten by a late connected
  flag flip.
- startBot always disconnects the outgoing BotInstance before creating
  a replacement, covering the mid-handshake case where isConnected()
  still returned false but the library client was live.
- startBot now reuses the stored identity so server groups granted to
  the bot survive restarts (was regenerating a fresh UID each time).

WebSocket reliability
---------------------
- BotManager extends EventEmitter and emits 'botInstance' whenever a
  new instance is created. websocket.ts listens and re-attaches its
  stateChange / connected / disconnected listeners immediately, fixing
  the bug where player-bar UI never updated until manual refresh.
- attachedBots map now stores the BotInstance reference and detaches
  stale listeners when the instance is replaced. Safety-net interval
  (5s) also reconciles to catch anything missed.
- removeBot emits 'botInstanceRemoved' -> WS broadcasts a new
  {type:"botRemoved", botId} message. Client drops the bot from its
  local store instead of showing it as permanently offline.

HTTP input validation
---------------------
- /volume rejects non-number, NaN, Infinity, and out-of-range values
  with a proper 400 instead of a 200 OK wrapping a usage-text string.
- /mode rejects anything not in {seq, loop, random, rloop} with 400.
- /seek rejects NaN / Infinity / negative (previously NaN slipped
  through typeof==="number" and poisoned seekOffset).
- /play-at validates index < queue.size() BEFORE stopping current
  playback (was silently killing the current song on invalid input).
- /play, /add, /playlist, /play-by-id, /add-by-id, /play-playlist
  all honour platform=youtube now (previously fell through to netease
  and silently played the wrong platform).

YouTube made truly optional
---------------------------
- Lazy checkYtDlpAvailable() runs `yt-dlp --version` once, caches only
  positive results so users can install yt-dlp mid-run and have it
  picked up without a restart.
- getAuthStatus() returns loggedIn=false with nickname
  "YouTube (yt-dlp not installed)" when the binary is missing. UI can
  grey out YouTube instead of silently returning empty searches.
- findYtDlp() picks .exe on win32 and bare binary elsewhere.
- /auth/status?platform=youtube now routes to the YouTube provider
  instead of falling through to NetEase and leaking the NetEase
  user's nickname + avatar.
- /auth/cookie rejects platform=youtube with 400 instead of clobbering
  the NetEase cookie entry.
- README documents yt-dlp install paths (bin/ local vs PATH) and adds
  a dedicated "Optional: YouTube source" section.

Bot Selector UI
---------------
- New power button in each row of the dropdown with play-state-aware
  styling: disabled + wait-cursor during API call, green highlight when
  connected, greys out when the bot is offline.
- Dropdown always visible when >=1 bot exists, bigger font + padding.

Queue correctness
-----------------
- PlayQueue.remove(current) now decrements currentIndex so next() in
  sequential mode advances to the shifted song. Previously removing
  the currently-playing track silently skipped the next track because
  current() falsely reported it as active and next() then incremented
  past it.

Vote-skip hardening
-------------------
- cmdVote: needed threshold is Math.max(1, ceil(users/2)) so a single
  voter in an empty channel can't unanimously pass a vote with
  needed=0.
- resolveAndPlay clears voteSkipUsers on every new track load so votes
  can't leak across songs via cmdPlay/cmdPlaylist/cmdAlbum/cmdFm paths.

cmdAdd parity
-------------
- cmdAdd auto-plays the newly-added song if the player was idle,
  matching /api/player/:id/add-by-id behaviour. Previously add'ing to
  an empty queue on a connected+idle bot silently enqueued without
  starting playback.

Test suite
----------
- scripts/test_full_feature.py — 51 tests across 10 groups exercising
  every HTTP endpoint, WebSocket broadcasts, all music providers, bot
  lifecycle, disconnected-state corners, seek validation, input
  validation, and the main race conditions. Captures and restores the
  target bot's initial state. Resilient to TS3 anti-flood via retry
  with exponential backoff. Runs against a real local TS3 server.
- scripts/test_rapid_cycle.py — Bugs A/B/C regressions
- scripts/test_corner_cases.py — disconnect-during-connect race, config
  commands while disconnected, etc.
- scripts/test_more_corners.py — resolveAndPlay race, seek NaN
- scripts/test_power_button.py — E2E for the new power button
- scripts/test_bot_remove.py — E2E for WS botRemoved broadcast
- scripts/test_playbar.py — player bar auto-show regression (updated
  to restore bot state on exit)
- scripts/test_multibot.py — two-bot concurrent playback monitor
- src/audio/queue.test.ts — 4 new vitest cases for remove() edge cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 01:35:14 +08:00
saopig1andClaude Opus 4.6 6e828b9c2d fix(ws): re-attach stateChange listeners when bot instance is replaced
startBot creates a fresh BotInstance but the WS layer keyed its listener
map by bot.id, so ensureAllBotsAttached skipped the new object and
stateChange events were never broadcast — the player bar only appeared
after a manual refresh. BotManager now extends EventEmitter and emits
"botInstance" whenever a new bot object is created; websocket.ts stores
the bot reference, detaches on replacement, and subscribes to the event
for immediate wiring.

Also enlarges the bot selector (padding 10×20, font 16, min-height 44,
bigger dot/chevron/state icons, wider name) and adds Playwright repro
scripts for the player bar bug and navbar sizing check.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:16:29 +08:00
saopig1andClaude Sonnet 4.6 d5d6abeb1e feat: implement server password login, YouTube source, and improved bot selector UI
- **TS server password** (#7/#9): add serverPassword field across database,
  manager, bot API, and Settings UI — allows joining password-protected servers
- **YouTube audio source** (#1/#10): new YouTubeProvider using yt-dlp binary;
  adds -y flag in chat commands, /api/music supports platform=youtube,
  YouTube badge in SongCard, yt-dlp-wrap npm dependency
- **Bot selector UI** (#14): selector always visible (not just when >1 bot),
  bigger button with border and play-state indicator; per-bot URL routing at
  /bot/:id with BotRedirect view; copy-link button in dropdown

Closes #1, #7, #9, #10, #14

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:58:07 +08:00
saopig1 b20ddf2603 feat(manager): reload bot config from database on startBot
When starting a bot, re-instantiate BotInstance from latest database
config so changes to host, nickname, channel, protocol etc. take
effect immediately without requiring a full app restart.
2026-04-09 11:31:00 +08:00
saopig1 419911b78b Merge origin/main into dev: sync bug fixes from main
Merges 4 PRs from main:
- PR#15: Fix ffmpeg spawn failure causing infinite retry loop
- PR#16: Fix Docker restart crash (respect autoStart flag, persist identity)
- PR#17: Add missing "play" script to package.json
- PR#18: Add detailed update/upgrade instructions to README

Conflict resolution in src/data/database.ts:
- Combined dev's serverProtocol/ts6ApiKey fields with main's identity field
- Extended migrateSchema() to also migrate serverProtocol and ts6ApiKey columns
- Updated SQL schema and upsert query to include all three new columns
2026-04-08 15:48:40 +08:00
saopig1andClaude Opus 4.6 43004fc44a fix: 停止机器人后UI消失 & UDP send error日志刷屏
- botDisconnected事件改为更新状态而非删除,停止机器人后正确显示离线状态
- 节流UDP send error日志,2秒内重复错误只输出一条汇总

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:50:04 +08:00
saopig1andClaude Opus 4.6 d0d2f57066 fix: 自动连接改为非阻塞并行 — 一个bot连接失败不影响其他bot
之前 loadSavedBots 用 await 顺序连接所有bot,一个超时会阻塞
后续所有bot和web server启动。改为 .then()/.catch() 并行连接,
每个bot独立成败互不影响。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:33:32 +08:00
saopig1andClaude Opus 4.6 eb92b04e11 fix: 机器人状态显示 — 区分在线/播放中/已暂停/离线四种状态
之前只有在线/离线两种,播放中也显示为在线。
现在:绿色=播放中,蓝色=在线,橙色=已暂停,灰色=离线

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:27:01 +08:00
saopig1andClaude Opus 4.6 01d16281d5 fix: Settings页面mount时刷新bot状态,防止显示过期数据
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:16:06 +08:00
saopig1andClaude Opus 4.6 e466f5cd61 fix: NetEase API端口冲突检查,Settings页面create-bot样式修复
- NetEase API启动前检查端口是否被占用(与QQ API相同逻辑)
- 添加.create-bot CSS(分隔线+间距)
- 防止EADDRINUSE崩溃

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:13:11 +08:00
saopig1andClaude Opus 4.6 ef9d41fd44 docs: 更新多机器人独立播放功能描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:58:36 +08:00
saopig1andClaude Opus 4.6 4dbbaaf8f5 fix: 删除bot后重置activeBotId,编辑立即更新名称,config fetch await防空值
1. 删除活跃bot后将activeBotId置null,由fetchBots自动选新的
2. updateBot同时更新内存中的name(无需重启即可看到名称变化)
3. openEditBot改为async await config请求,失败时用bot名称做fallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:54:33 +08:00
saopig1andClaude Opus 4.6 067a30233f feat: 机器人管理 — 删除、编辑配置、完整创建表单
- PUT /api/bot/:id 更新机器人配置(保存到数据库,重启生效)
- GET /api/bot/:id/config 获取保存的配置
- BotManager.updateBot/getBotConfig 方法
- Settings 页面:删除按钮(带确认)、编辑弹窗(修改所有字段)
- 创建表单:完整字段(名称、地址、端口、昵称、默认频道)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:50:49 +08:00
saopig1andClaude Opus 4.6 bc9b2366d0 feat: make frontend fully support multiple independent bots
Store queue and timing state per-bot instead of globally, move bot
selector from Home page into Navbar dropdown, show bot name badge
in the player bar, and route WebSocket stateChange queues per-bot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:45:24 +08:00
saopig1andClaude Opus 4.6 75d359f65f docs: 标注兼容 TS3/TS5/TS6 服务器(协议层向后兼容,无需额外适配)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:36:13 +08:00
saopig1andClaude Opus 4.6 223ba45e65 feat: 启动时自动连接所有已保存的机器人实例
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:28:43 +08:00
saopig1andClaude Opus 4.6 942a715542 docs: README 更新 — 三平台支持(网易云+QQ+B站),B站徽章/命令/架构/FAQ
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:26:01 +08:00
saopig1andClaude Opus 4.6 fbf8cc82de fix: cidCache 大小限制为500条,防止内存泄漏
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:20:42 +08:00
saopig1andClaude Opus 4.6 5e4d318a2a feat: 我的歌单超过20个时折叠,点击展开/收起
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:16:31 +08:00
saopig1andClaude Opus 4.6 cf293299c8 feat: B站首页推荐 — 个性化推荐/音乐区排行/热门视频
- BiliBiliProvider.getDailyRecommendSongs: 个性化推荐(登录后更准确),
  fallback 到音乐区排行榜
- BiliBiliProvider.getPopularVideos: 热门视频列表
- 新增 GET /api/music/bilibili/popular 端点
- 首页新增"B站热门"板块,带B站蓝色徽标
- Store 缓存 bilibiliPopular 数据

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:12:06 +08:00
saopig1andClaude Opus 4.6 05d7f7d5f7 fix: 封面加载失败 — 添加referrerpolicy=no-referrer,移除crossorigin,添加placeholder
B站CDN可能基于Referer拒绝图片请求。添加referrerpolicy="no-referrer"
让浏览器不发送Referer头。同时:
- 移除crossorigin="anonymous"避免CORS问题
- alt=""替代alt="Cover",不再显示文字
- 图片加载失败时显示音符图标placeholder
- watch url变化重置错误状态

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:04:11 +08:00
saopig1andClaude Opus 4.6 960ca756a6 fix: 使用SPI API获取buvid3替代访问主页 — 可靠解决412问题
之前通过axios访问bilibili.com主页获取cookie失败(axios不解析set-cookie)。
改用专用API: /x/frontend/finger/spi 直接获取buvid3和buvid4值,
构造cookie字符串传入搜索请求。已验证搜索正常返回结果和封面URL。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:54:21 +08:00
saopig1andClaude Opus 4.6 dcec26feb1 fix: B站搜索412错误 — 自动获取buvid3匿名Cookie绕过反爬
B站搜索API强制要求buvid3 cookie,否则返回412。
Provider启动时自动从bilibili.com获取buvid3/b_nut cookie,
与用户登录cookie合并发送。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:46:51 +08:00
saopig1andClaude Opus 4.6 341faffdbb fix: B站封面 — http转https防止mixed content,CDN参数@300w_300h_1c正方形裁切
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:40:07 +08:00
saopig1andClaude Opus 4.6 a6881403ef feat: add BiliBili audio source integration
Implement BiliBiliProvider for video-as-audio playback using direct
BiliBili API calls (search, video info, DASH audio URL extraction).
Add QR code login support and cookie persistence. Update FFmpeg to
send Referer header for BiliBili CDN URLs. Extend platform union type
to "netease" | "qq" | "bilibili" across all interfaces. Add -b flag
for chat commands and B站 badge in web UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 15:14:27 +08:00
saopig1andClaude Opus 4.6 7d329a0db1 fix: safe router.back() — fallback to home if no history (prevents navigating to external site)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:57:32 +08:00
saopig1andClaude Opus 4.6 eb0088d110 fix: edge case bugs + toggle lyrics from player bar and lyrics button
Bugs fixed:
- play-at: stop current playback before jumping to queue index
- playNext retry: use break instead of return to ensure stateChange emits
- sendVoiceData: skip if disconnecting to avoid errors during teardown

UI: player-left and lyrics button now toggle lyrics page (open/close)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:50:14 +08:00
saopig1andClaude Opus 4.6 90019e7424 feat: 点击播放条左侧歌曲封面/信息打开歌词页面
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:43:54 +08:00
saopig1andClaude Opus 4.6 d8c0911593 feat: 双击播放队列中的歌曲(不清空队列)
- 新增 POST /api/player/:botId/play-at 端点,跳转到队列指定索引
- 使用 queue.playAt(index) + resolveAndPlay,保留队列完整
- Queue.vue 添加 @dblclick 处理,双击即播放对应歌曲
- 添加 store.playAtIndex() action

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:37:53 +08:00
saopig1andClaude Opus 4.6 17991b936a fix: resolve critical race conditions and high-severity bugs
- Add isAdvancing guard to playNext() to prevent concurrent calls from
  skipping songs (trackEnd + user !next race condition)
- Replace removeAllListeners in WebSocket setup with tracked named
  listeners, attach only once per bot instead of every 5 seconds
- Add .catch() to unhandled playNext() in cmdVote
- Add sessionId counter to AudioPlayer to discard stale setTimeout
  callbacks after stop()+play() transitions
- Defer nulling TS3 client until disconnect() promise resolves
- Add null checks for providers in play-by-id and add-by-id endpoints
- Add PCM buffer backpressure (pause FFmpeg at 960KB, resume at 480KB)
- Change volume slider from @input to @change to avoid flooding server
- Wrap error-reporting sendTextMessage in try/catch to prevent
  unhandled rejection in error handler
- Clamp elapsed getter to song duration
- Log FFmpeg stderr at debug level instead of swallowing
- Return null from prev() in Sequential mode instead of wrapping

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:31:03 +08:00
saopig1andClaude Opus 4.6 4c71a0a37f fix: auto-play next song — set ffmpeg=null on close, handle async playNext
Two bugs prevented auto-advance:
1. FFmpeg close handler was empty — ChildProcess object persisted after
   close, so frame loop check `!this.ffmpeg` never triggered trackEnd
2. playNext() is async (resolves URL) but trackEnd handler didn't catch
   rejected promises, causing silent failures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:14:56 +08:00
saopig1andClaude Opus 4.6 97fccc44eb docs: 扩展致谢列表 — 添加所有参考项目和开发者
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 13:52:38 +08:00
saopig1andClaude Opus 4.6 833c6b7b05 docs: 全中文 README — 徽章、快速开始、使用说明、FAQ、贡献指南
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 13:38:27 +08:00
saopig1andClaude Opus 4.6 c4b16c8205 docs: professional GitHub README with badges, FAQ, contributing guide, LICENSE
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 13:28:28 +08:00
saopig1andClaude Opus 4.6 ee5b0d36c6 docs: expand Docker deployment section with commands and network notes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 13:23:26 +08:00
saopig1andClaude Opus 4.6 74193ffbf0 fix: Docker one-click deployment — all deps bundled, native modules, health check
- Multi-stage build: builder installs backend + frontend deps, compiles
- Production stage: npm ci --production rebuilds native modules (opus, sqlite3)
- FFmpeg bundled via ffmpeg-static (no apt install ffmpeg)
- .dockerignore prevents node_modules/dist from being copied
- docker-compose uses host network for TS3 UDP connectivity
- Named volume for persistent data
- Health check endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 13:20:00 +08:00
saopig1andClaude Opus 4.6 ea38636e9b docs: update README — one-click deploy, quality levels, full architecture, credits
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 12:59:35 +08:00
saopig1andClaude Opus 4.6 61d9bbe0b7 feat: bundle FFmpeg via ffmpeg-static for one-click deployment
Users no longer need to install FFmpeg separately — it is now bundled as
an npm dependency (ffmpeg-static). player.ts resolves the bundled binary
automatically and falls back to system PATH. start.bat and a new
setup.bat script handle dependency installation and project building.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:43:10 +08:00
saopig1andClaude Opus 4.6 00231db0cb docs: add comprehensive README with features, architecture, and usage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:38:12 +08:00
saopig1andClaude Opus 4.6 b74c8e8add feat: add audio quality selector (standard to jymaster/Hi-Res)
- MusicProvider interface: setQuality/getQuality methods
- NeteaseProvider: passes quality level to /song/url/v1
- Default: exhigh (320kbps MP3)
- Settings page: 6-option grid selector
- GET/POST /api/music/quality endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:37:26 +08:00
saopig1andClaude Opus 4.6 0439bd047f fix: server-side elapsed tracking via frame count, periodic sync, playlist respects play mode
Progress/lyrics sync:
- AudioPlayer tracks framesPlayed (ground truth: frames * 20ms + seekOffset)
- BotStatus.elapsed is the real elapsed from server
- Frontend polls GET /elapsed every 3s for ground truth
- Client interpolates between syncs (serverElapsed + timeSinceSync)
- Pause freezes at interpolated value, resume resyncs from server
- No more client-side timestamp drift

Playlist play all:
- Stops current playback before loading
- Respects play mode: random/rloop picks random first song, seq plays first

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:27:16 +08:00
saopig1andClaude Opus 4.6 1cdface33c feat: implement seeking (FFmpeg -ss restart), fix lyrics sync with seekOffset
- AudioPlayer.seek() restarts FFmpeg with -ss parameter for actual seeking
- New POST /api/player/:botId/seek endpoint
- BotStatus includes seekOffset and playStartTime from server
- Frontend elapsed = seekOffset + (now - playStartedAt)
- Progress bar click now seeks to clicked position
- All timing actions use _resetTiming() helper with seekOffset
- Lyrics sync uses store.elapsed which includes seekOffset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:16:37 +08:00
saopig1andClaude Opus 4.6 080c2836c9 refactor: lazy URL resolution — store only metadata, fetch URL at play time
Follows TS3AudioBot-NetEaseCloudmusic-plugin pattern:
- QueuedSong.url is now optional (metadata-only queue entries)
- resolveAndPlay() fetches URL on-demand right before playback
- Playlist/album/FM load instantly (only metadata stored)
- playNext() resolves URL lazily, skips up to 3 songs on failure
- Avoids expired URLs for songs deep in the queue

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:05:40 +08:00
saopig1andClaude Opus 4.6 2e4d6d8d40 feat: play-by-id and play-playlist endpoints, fix lyrics sync timing
- New endpoints: play-by-id, add-by-id, play-playlist (load all songs at once)
- Playlist 'Play All' now uses server-side bulk load (no N sequential searches)
- Search/Home/Playlist views use playById instead of search-by-name
- Fixed updateBotStatus timing: capture prev state before mutation
- Early return on song change prevents stale pause/resume logic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:55:15 +08:00
saopig1andClaude Opus 4.6 2c87f1cff9 fix: progress bar smooth via rAF with direct Date.now(), queue panel moved outside player div
- Progress bar reads playStartedAt directly in requestAnimationFrame loop
  bypassing Vue reactivity caching for smooth 60fps updates
- Queue panel moved to sibling of player-bar (was trapped inside causing z-index issues)
- Added progress thumb dot on hover
- Click on progress shows 'seek not available' tooltip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:47:25 +08:00
saopig1andClaude Opus 4.6 dc9d181783 fix: check port availability before starting QQ Music API, update default port to 3200
Prevents EADDRINUSE crash that killed the entire app when port 3200
was still occupied from a previous run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:40:02 +08:00
saopig1andClaude Opus 4.6 d84f23c2b2 fix: resolve multiple UI issues in player, playlist, lyrics, and queue
- Playlist: add console.error in catch block so load failures are visible
- Player: add progress bar with requestAnimationFrame and time display
- Player: extract toggleQueue function with debug logging for queue panel
- WebSocket: fetch queue on stateChange when not included in event payload
- Lyrics: use CSS transform scrolling and reduce sync to 500ms to fix flicker

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:34:48 +08:00
saopig1andClaude Opus 4.6 94a0df8391 feat: add QQ Music API integration, Pinia home cache, and unified search
- Start @sansenjian/qq-music-api server via dynamic import in api-server.ts
- Rewrite QQ Music provider to use real API endpoints (getSearchByKey,
  getMusicPlay, getSongListDetail, getAlbumInfo, getLyric, etc.)
- Cache home page data (recommend playlists, daily songs, user playlists)
  in Pinia store with 5-minute TTL to avoid refetching on every mount
- Add unified /api/music/search/all endpoint that queries both Netease
  and QQ providers in parallel and returns merged results
- Remove platform toggle from Search.vue; search both platforms at once
- Add platform badge (网易云 / QQ) to SongCard component

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:31:15 +08:00
saopig1andClaude Opus 4.6 ce6fc92780 fix: playlist cards use RouterLink for reliable navigation, lyrics use scrollTo for smooth sync
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:22:50 +08:00
saopig1andClaude Opus 4.6 ee5a75ab5e fix: add play mode toggle, wire play history API, add back navigation buttons
- Add play mode cycle button (seq/loop/random/rloop) to Player bar
- Wire GET /api/player/:botId/history to query database instead of returning empty array
- Pass database instance from server.ts to createPlayerRouter
- Add back navigation button to Playlist, Lyrics, History, Search, and Settings views
- SongCard already had dblclick-to-play (no change needed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:16:18 +08:00
saopig1andClaude Opus 4.6 9dce883384 fix: queue panel fetches data on open, lyrics auto-sync with playback time
- Player store tracks playStartedAt/pausedElapsed for elapsed time
- Queue component fetches from server when opened
- Lyrics page syncs every 200ms using elapsed time
- Lyrics auto-scroll to active line with smooth animation
- Translation lines shown below main lyrics
- Fade mask on lyrics scroll area

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:12:12 +08:00
saopig1andClaude Opus 4.6 67584d2622 feat: add YesPlayMusic-style home page with daily recs, FM, and user playlists
Add backend endpoints for daily recommended songs, personal FM, user
playlists, and playlist detail. Extend NeteaseProvider and MusicProvider
interface with getDailyRecommendSongs and getUserPlaylists. Rewrite Home
page with FM card, daily recommendations grid, and user playlists section.
Fix Playlist view to fetch detail and songs from separate endpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:07:11 +08:00
saopig1andClaude Opus 4.6 6b725f43f9 fix: replace setInterval with drift-correcting setTimeout for audio timing
- High-precision frame loop using performance.now() tracking
- PCM-level volume control (live adjustable, no FFmpeg restart)
- Proper buffer drain on track end
- Fixes audio quality and playback speed issues

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:01:16 +08:00
saopig1andClaude Opus 4.6 9755ba2909 fix: QR login now persists cookies, shows server-generated QR image, passes timestamp
- Auth router accepts CookieStore and persists cookies on login
- NeteaseProvider passes timestamp to prevent cached QR responses
- QR image from server used directly (qrimg field)
- Cookie saved to disk on confirmed QR scan

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:47:25 +08:00
saopig1andClaude Opus 4.6 f77a6f0fd7 fix: start NeteaseCloudMusicApi server for real — enables QR code login
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:37:35 +08:00
saopig1andClaude Opus 4.6 814910123e feat: add QR code login for NetEase/QQ Music accounts in Settings page
- QR code generated client-side via qrcode library
- Auto-polls backend every 2s for scan status
- Shows waiting/scanned/confirmed/expired states
- Falls back to cookie paste method
- Auth status check on page load

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:34:06 +08:00
saopig1andClaude Opus 4.6 0559f09f0a feat: replace ServerQuery with full TS3 client protocol — bot is now VISIBLE
Switch from TCP ServerQuery (port 10011, invisible) to real TS3 client
protocol via @honeybbq/teamspeak-client (UDP 9987, ECDH handshake,
AES-EAX encryption). Bot now appears as a regular client visible to
all users in TeamSpeak.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:28:25 +08:00
saopig1andClaude Opus 4.6 df7b944742 test: add integration test suite — 74 tests passing against local TS3 server
Fix @discordjs/opus ESM import for Node 24 compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:08:57 +08:00
saopig1andClaude Opus 4.6 5041a4f3f7 fix: address Phase 7 review — move WS/theme init to App.vue, wire Queue component
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:02:42 +08:00
saopig1andClaude Opus 4.6 41d9e88c8e feat: add deployment scripts, Docker, and Setup Wizard — Phase 8 complete
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:00:23 +08:00
saopig1andClaude Opus 4.6 694e3c953b feat: add Vue.js WebUI — YesPlayMusic-inspired SPA with all pages and components
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:58:33 +08:00
saopig1andClaude Opus 4.6 2a966e914c fix: address Phase 6 review — error handling on player routes, SPA fallback scope, WS cleanup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:49:34 +08:00
saopig1andClaude Opus 4.6 0b54809113 feat: add web backend — Express REST API, WebSocket, full wiring in entry point
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:47:19 +08:00
saopig1andClaude Opus 4.6 e5d5185f14 fix: address Phase 5 review — use strict equality check in cmdMode
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:45:14 +08:00
saopig1andClaude Opus 4.6 6692f49a85 feat: add bot core — command parser, BotInstance with full commands, BotManager
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:43:41 +08:00
saopig1andClaude Opus 4.6 fbc4cbe4d6 fix: address Phase 4 review — cookie file permissions (0600), gitignore cookies dir
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:41:24 +08:00
saopig1andClaude Opus 4.6 94902fc2c4 feat: add music source service — NetEase/QQ providers, unified interface, cookie auth
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:39:47 +08:00
saopig1andClaude Opus 4.6 17d2decc91 fix: address Phase 3 review — Logger type export, PlayerEvents interface, PCM backpressure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:37:16 +08:00
saopig1andClaude Opus 4.6 5afc8c5224 feat: add audio engine — Opus encoder, play queue with 4 modes, FFmpeg player
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:34:57 +08:00
saopig1andClaude Opus 4.6 d66355a829 fix: address Phase 2 review — error handlers, drain queue, prevent listener accumulation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:32:22 +08:00
saopig1andClaude Opus 4.6 a6bfd9d32c feat: add TCP connection, UDP voice, and high-level TS3Client
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:29:47 +08:00
saopig1andClaude Opus 4.6 e059ca5bfe feat: add TS3 command encoding/decoding with escape handling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:28:46 +08:00
saopig1andClaude Opus 4.6 2b6e81119b feat: add TS3 identity generation with Ed25519 keypairs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:28:12 +08:00
saopig1andClaude Opus 4.6 bc89c553b7 fix: address Phase 1 review — union types, auto playedAt, ESM imports, gitignore config.json
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:26:55 +08:00
saopig1andClaude Opus 4.6 9931d8b40d chore: Phase 1 complete — project scaffold, config, database, logger
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:21:55 +08:00
saopig1andClaude Opus 4.6 7c60bd69ef feat: add application entry point with config, database, and logger initialization
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:21:38 +08:00
saopig1andClaude Opus 4.6 8727605036 feat: add SQLite database module with play history and bot instances
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:21:07 +08:00
saopig1andClaude Opus 4.6 6324953394 feat: add pino logger with file and stdout transports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:18:09 +08:00
saopig1andClaude Opus 4.6 f152450931 feat: add JSON config module with load/save/defaults
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:16:59 +08:00
saopig1andClaude Opus 4.6 101d593c0e chore: initialize project with TypeScript and dependencies
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:16:01 +08:00