mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-01 20:42:50 +08:00
Merge pull request #100 from Dr1mH4X/feat/channelid
feat: joinChannel via channelid
This commit is contained in:
7 files changed
+74
-19
No files matched your search
@@ -57,6 +57,7 @@ export interface CreateBotParams {
|
||||
queryPort?: number;
|
||||
nickname: string;
|
||||
defaultChannel?: string;
|
||||
channelId?: string;
|
||||
channelPassword?: string;
|
||||
autoStart?: boolean;
|
||||
/** Force TS3 or TS6 protocol; omit or "unknown" for auto-detect. */
|
||||
@@ -113,6 +114,7 @@ export class BotManager extends EventEmitter {
|
||||
queryPort: params.queryPort ?? 10011,
|
||||
nickname: params.nickname,
|
||||
defaultChannel: params.defaultChannel,
|
||||
channelId: params.channelId,
|
||||
channelPassword: params.channelPassword,
|
||||
serverPassword: params.serverPassword,
|
||||
serverProtocol: params.serverProtocol,
|
||||
@@ -138,6 +140,7 @@ export class BotManager extends EventEmitter {
|
||||
serverPort: params.serverPort,
|
||||
nickname: params.nickname,
|
||||
defaultChannel: params.defaultChannel ?? "",
|
||||
channelId: params.channelId ?? "",
|
||||
channelPassword: params.channelPassword ?? "",
|
||||
autoStart: params.autoStart ?? false,
|
||||
serverProtocol: params.serverProtocol ?? "",
|
||||
@@ -173,6 +176,7 @@ export class BotManager extends EventEmitter {
|
||||
serverPort: params.serverPort ?? existing.serverPort,
|
||||
nickname: params.nickname ?? existing.nickname,
|
||||
defaultChannel: params.defaultChannel ?? existing.defaultChannel,
|
||||
channelId: params.channelId ?? existing.channelId,
|
||||
channelPassword: params.channelPassword ?? existing.channelPassword,
|
||||
serverProtocol: params.serverProtocol ?? existing.serverProtocol,
|
||||
ts6ApiKey: params.ts6ApiKey ?? existing.ts6ApiKey,
|
||||
@@ -231,6 +235,7 @@ export class BotManager extends EventEmitter {
|
||||
// each connect and strips all previously granted groups.
|
||||
identity: saved.identity || undefined,
|
||||
defaultChannel: saved.defaultChannel || undefined,
|
||||
channelId: saved.channelId || undefined,
|
||||
channelPassword: saved.channelPassword || undefined,
|
||||
serverPassword: saved.serverPassword || undefined,
|
||||
serverProtocol: proto === "ts3" || proto === "ts6" ? proto : undefined,
|
||||
@@ -282,6 +287,7 @@ export class BotManager extends EventEmitter {
|
||||
nickname: saved.nickname,
|
||||
identity: saved.identity || undefined,
|
||||
defaultChannel: saved.defaultChannel || undefined,
|
||||
channelId: saved.channelId || undefined,
|
||||
channelPassword: saved.channelPassword || undefined,
|
||||
serverPassword: saved.serverPassword || undefined,
|
||||
serverProtocol: proto === "ts3" || proto === "ts6" ? proto : undefined,
|
||||
|
||||
@@ -82,6 +82,7 @@ describe("database", () => {
|
||||
serverPort: 9987,
|
||||
nickname: "MusicBot",
|
||||
defaultChannel: "Music",
|
||||
channelId: "",
|
||||
channelPassword: "",
|
||||
autoStart: true,
|
||||
serverProtocol: "",
|
||||
@@ -111,6 +112,7 @@ describe("database", () => {
|
||||
serverPort: 9987,
|
||||
nickname: "MusicBot",
|
||||
defaultChannel: "Music",
|
||||
channelId: "",
|
||||
channelPassword: "",
|
||||
autoStart: false,
|
||||
serverProtocol: "",
|
||||
@@ -131,6 +133,7 @@ describe("database", () => {
|
||||
serverPort: 9987,
|
||||
nickname: "n",
|
||||
defaultChannel: "",
|
||||
channelId: "",
|
||||
channelPassword: "",
|
||||
autoStart: false,
|
||||
serverProtocol: "",
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface BotInstance {
|
||||
serverPort: number;
|
||||
nickname: string;
|
||||
defaultChannel: string;
|
||||
channelId: string;
|
||||
channelPassword: string;
|
||||
autoStart: boolean;
|
||||
/** "ts3" | "ts6" | "" (empty = auto-detect) */
|
||||
@@ -96,6 +97,9 @@ function migrateSchema(db: Database.Database): void {
|
||||
if (!names.includes("serverPassword")) {
|
||||
db.exec("ALTER TABLE bot_instances ADD COLUMN serverPassword TEXT NOT NULL DEFAULT ''");
|
||||
}
|
||||
if (!names.includes("channelId")) {
|
||||
db.exec("ALTER TABLE bot_instances ADD COLUMN channelId TEXT NOT NULL DEFAULT ''");
|
||||
}
|
||||
// Profile feature flags
|
||||
const profileCols = [
|
||||
"profile_avatar_enabled",
|
||||
@@ -142,6 +146,7 @@ function initTables(db: Database.Database): void {
|
||||
serverPort INTEGER NOT NULL,
|
||||
nickname TEXT NOT NULL,
|
||||
defaultChannel TEXT NOT NULL,
|
||||
channelId TEXT NOT NULL DEFAULT '',
|
||||
channelPassword TEXT NOT NULL,
|
||||
autoStart INTEGER NOT NULL DEFAULT 0,
|
||||
serverProtocol TEXT NOT NULL DEFAULT '',
|
||||
@@ -254,14 +259,15 @@ export function createDatabase(dbPath: string): BotDatabase {
|
||||
`);
|
||||
|
||||
const upsertInstance = db.prepare(`
|
||||
INSERT INTO bot_instances (id, name, serverAddress, serverPort, nickname, defaultChannel, channelPassword, autoStart, serverProtocol, ts6ApiKey, serverPassword, identity)
|
||||
VALUES (@id, @name, @serverAddress, @serverPort, @nickname, @defaultChannel, @channelPassword, @autoStart, @serverProtocol, @ts6ApiKey, @serverPassword, @identity)
|
||||
INSERT INTO bot_instances (id, name, serverAddress, serverPort, nickname, defaultChannel, channelId, channelPassword, autoStart, serverProtocol, ts6ApiKey, serverPassword, identity)
|
||||
VALUES (@id, @name, @serverAddress, @serverPort, @nickname, @defaultChannel, @channelId, @channelPassword, @autoStart, @serverProtocol, @ts6ApiKey, @serverPassword, @identity)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
serverAddress = excluded.serverAddress,
|
||||
serverPort = excluded.serverPort,
|
||||
nickname = excluded.nickname,
|
||||
defaultChannel = excluded.defaultChannel,
|
||||
channelId = excluded.channelId,
|
||||
channelPassword = excluded.channelPassword,
|
||||
autoStart = excluded.autoStart,
|
||||
serverProtocol = excluded.serverProtocol,
|
||||
@@ -342,6 +348,7 @@ export function createDatabase(dbPath: string): BotDatabase {
|
||||
serverProtocol: r.serverProtocol ?? "",
|
||||
ts6ApiKey: r.ts6ApiKey ?? "",
|
||||
serverPassword: r.serverPassword ?? "",
|
||||
channelId: r.channelId ?? "",
|
||||
identity: r.identity ?? undefined,
|
||||
}));
|
||||
},
|
||||
|
||||
@@ -46,6 +46,7 @@ export interface TS3ClientOptions {
|
||||
nickname: string;
|
||||
identity?: string; // Exported identity string, or undefined to generate new
|
||||
defaultChannel?: string;
|
||||
channelId?: string; // Numeric channel ID (takes precedence over defaultChannel)
|
||||
channelPassword?: string;
|
||||
serverPassword?: string;
|
||||
/** Force a specific protocol instead of auto-detecting. */
|
||||
@@ -254,8 +255,10 @@ export class TS3Client extends EventEmitter {
|
||||
`Logged in (visible client, ${this.detectedProtocol.toUpperCase()} server)`,
|
||||
);
|
||||
|
||||
// Join default channel if specified
|
||||
if (this.options.defaultChannel) {
|
||||
// Join channel by numeric ID (takes precedence) or by name
|
||||
if (this.options.channelId) {
|
||||
await this.joinChannel(this.options.channelId, this.options.channelPassword);
|
||||
} else if (this.options.defaultChannel) {
|
||||
await this.joinChannel(
|
||||
this.options.defaultChannel,
|
||||
this.options.channelPassword
|
||||
@@ -268,6 +271,17 @@ export class TS3Client extends EventEmitter {
|
||||
async joinChannel(channelName: string, password?: string): Promise<void> {
|
||||
if (!this.client) return;
|
||||
|
||||
const isNumeric = /^\d+$/.test(channelName);
|
||||
if (isNumeric) {
|
||||
try {
|
||||
await clientMove(this.client, this.clientId, BigInt(channelName), password);
|
||||
this.logger.info({ channelName }, "Joined channel");
|
||||
} catch (err) {
|
||||
this.logger.error({ err, channelName }, "Failed to join channel");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const channels = await listChannels(this.client);
|
||||
const channel = channels.find((ch) => ch.name === channelName);
|
||||
@@ -277,12 +291,7 @@ export class TS3Client extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
await clientMove(
|
||||
this.client,
|
||||
this.clientId,
|
||||
channel.id,
|
||||
password
|
||||
);
|
||||
await clientMove(this.client, this.clientId, channel.id, password);
|
||||
this.logger.info(
|
||||
{ channelName, cid: channel.id.toString() },
|
||||
"Joined channel"
|
||||
|
||||
+4
-2
@@ -159,6 +159,7 @@ export function createBotRouter(
|
||||
serverPort,
|
||||
nickname,
|
||||
defaultChannel,
|
||||
channelId,
|
||||
channelPassword,
|
||||
serverPassword,
|
||||
autoStart,
|
||||
@@ -175,6 +176,7 @@ export function createBotRouter(
|
||||
serverPort: serverPort ?? 9987,
|
||||
nickname,
|
||||
defaultChannel,
|
||||
channelId,
|
||||
channelPassword,
|
||||
serverPassword,
|
||||
autoStart: autoStart ?? false,
|
||||
@@ -194,10 +196,10 @@ export function createBotRouter(
|
||||
res.status(404).json({ error: "Bot not found" });
|
||||
return;
|
||||
}
|
||||
const { name, serverAddress, serverPort, nickname, defaultChannel, channelPassword, serverPassword } = req.body;
|
||||
const { name, serverAddress, serverPort, nickname, defaultChannel, channelId, channelPassword, serverPassword } = req.body;
|
||||
// Update in database
|
||||
botManager.updateBot(req.params.id, {
|
||||
name, serverAddress, serverPort, nickname, defaultChannel, channelPassword, serverPassword,
|
||||
name, serverAddress, serverPort, nickname, defaultChannel, channelId, channelPassword, serverPassword,
|
||||
});
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
|
||||
@@ -98,8 +98,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>默认频道(可选)</label>
|
||||
<input v-model="editForm.defaultChannel" class="input" placeholder="音乐频道" />
|
||||
<label>默认频道名称(可选)</label>
|
||||
<input v-model="editForm.defaultChannel" :disabled="!!editForm.channelId" class="input" :class="{ disabled: !!editForm.channelId }" placeholder="音乐频道" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>默认频道ID(可选)</label>
|
||||
<input v-model="editForm.channelId" :disabled="!!editForm.defaultChannel" class="input" :class="{ disabled: !!editForm.defaultChannel }" placeholder="如 12" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>频道密码(可选)</label>
|
||||
@@ -142,8 +146,12 @@
|
||||
<input v-model="newBotNickname" class="input" placeholder="MusicBot" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>默认频道(可选)</label>
|
||||
<input v-model="newBotChannel" class="input" placeholder="音乐频道" />
|
||||
<label>默认频道名称(可选)</label>
|
||||
<input v-model="newBotChannel" :disabled="!!newBotChannelId" class="input" :class="{ disabled: !!newBotChannelId }" placeholder="音乐频道" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>默认频道ID(可选)</label>
|
||||
<input v-model="newBotChannelId" :disabled="!!newBotChannel" class="input" :class="{ disabled: !!newBotChannel }" placeholder="如 12" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>服务器密码(可选)</label>
|
||||
@@ -408,7 +416,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- Idle Timeout -->
|
||||
<section v-if="can('bot.manage')" class="settings-section">
|
||||
<h2 class="section-title">行为设置</h2>
|
||||
@@ -685,6 +693,7 @@ const newBotServer = ref('');
|
||||
const newBotPort = ref(9987);
|
||||
const newBotNickname = ref('MusicBot');
|
||||
const newBotChannel = ref('');
|
||||
const newBotChannelId = ref('');
|
||||
const newBotServerPassword = ref('');
|
||||
const newBotAvatar = ref<string | null>(null);
|
||||
|
||||
@@ -696,6 +705,7 @@ const editForm = reactive({
|
||||
serverPort: 9987,
|
||||
nickname: '',
|
||||
defaultChannel: '',
|
||||
channelId: '',
|
||||
channelPassword: '',
|
||||
serverPassword: '',
|
||||
});
|
||||
@@ -853,6 +863,7 @@ async function createBot() {
|
||||
serverPort: newBotPort.value || 9987,
|
||||
nickname: newBotNickname.value || newBotName.value,
|
||||
defaultChannel: newBotChannel.value || undefined,
|
||||
channelId: newBotChannelId.value || undefined,
|
||||
serverPassword: newBotServerPassword.value || undefined,
|
||||
autoStart: false,
|
||||
});
|
||||
@@ -868,6 +879,7 @@ async function createBot() {
|
||||
newBotPort.value = 9987;
|
||||
newBotNickname.value = 'MusicBot';
|
||||
newBotChannel.value = '';
|
||||
newBotChannelId.value = '';
|
||||
newBotServerPassword.value = '';
|
||||
newBotAvatar.value = null;
|
||||
await store.fetchBots();
|
||||
@@ -901,6 +913,7 @@ async function openEditBot(bot: any) {
|
||||
editForm.serverPort = res.data.serverPort ?? 9987;
|
||||
editForm.nickname = res.data.nickname ?? '';
|
||||
editForm.defaultChannel = res.data.defaultChannel ?? '';
|
||||
editForm.channelId = res.data.channelId ?? '';
|
||||
editForm.channelPassword = res.data.channelPassword ?? '';
|
||||
editForm.serverPassword = res.data.serverPassword ?? '';
|
||||
} catch {
|
||||
@@ -909,6 +922,7 @@ async function openEditBot(bot: any) {
|
||||
editForm.serverPort = 9987;
|
||||
editForm.nickname = bot.name;
|
||||
editForm.defaultChannel = '';
|
||||
editForm.channelId = '';
|
||||
editForm.channelPassword = '';
|
||||
editForm.serverPassword = '';
|
||||
}
|
||||
@@ -1635,6 +1649,10 @@ onUnmounted(() => {
|
||||
font-size: 13px;
|
||||
outline: none;
|
||||
&:focus { border-color: var(--color-primary); }
|
||||
&.disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.input-sm { max-width: 80px; }
|
||||
|
||||
+12
-2
@@ -46,8 +46,12 @@
|
||||
<input v-model="nickname" placeholder="MusicBot" class="input" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>默认频道 (可选)</label>
|
||||
<input v-model="defaultChannel" placeholder="音乐频道" class="input" />
|
||||
<label>默认频道名称(可选)</label>
|
||||
<input v-model="defaultChannel" :disabled="!!channelId" placeholder="音乐频道" class="input" :class="{ disabled: !!channelId }" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>默认频道ID(可选)</label>
|
||||
<input v-model="channelId" :disabled="!!defaultChannel" placeholder="如 12" class="input" :class="{ disabled: !!defaultChannel }" />
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<button class="btn-secondary" @click="currentStep = 0">上一步</button>
|
||||
@@ -87,6 +91,7 @@ const serverAddress = ref('');
|
||||
const serverPort = ref(9987);
|
||||
const nickname = ref('MusicBot');
|
||||
const defaultChannel = ref('');
|
||||
const channelId = ref('');
|
||||
|
||||
async function createBotAndNext() {
|
||||
try {
|
||||
@@ -96,6 +101,7 @@ async function createBotAndNext() {
|
||||
serverPort: serverPort.value,
|
||||
nickname: nickname.value,
|
||||
defaultChannel: defaultChannel.value,
|
||||
channelId: channelId.value || undefined,
|
||||
autoStart: true,
|
||||
});
|
||||
currentStep.value = 2;
|
||||
@@ -193,6 +199,10 @@ async function createBotAndNext() {
|
||||
&:focus {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
&.disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
|
||||
Reference in new issue
Block a user