mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-01 20:42:50 +08:00
feat: Add channelId support to bot configuration and database
This commit is contained in:
1 parent
6f21b6354a
commit
4244695075
7 files changed
+169
-68
No files matched your search
+21
-15
@@ -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,
|
||||
@@ -221,21 +225,22 @@ export class BotManager extends EventEmitter {
|
||||
const bot = new BotInstance({
|
||||
id: saved.id,
|
||||
name: saved.name,
|
||||
tsOptions: {
|
||||
host: saved.serverAddress,
|
||||
port: saved.serverPort,
|
||||
queryPort: proto === "ts6" ? 10080 : 10011,
|
||||
nickname: saved.nickname,
|
||||
// Reuse the stored identity so server groups assigned to this bot
|
||||
// survive restarts — without this the TS server sees a new UID
|
||||
// each connect and strips all previously granted groups.
|
||||
identity: saved.identity || undefined,
|
||||
defaultChannel: saved.defaultChannel || undefined,
|
||||
channelPassword: saved.channelPassword || undefined,
|
||||
serverPassword: saved.serverPassword || undefined,
|
||||
serverProtocol: proto === "ts3" || proto === "ts6" ? proto : undefined,
|
||||
ts6ApiKey: saved.ts6ApiKey || undefined,
|
||||
},
|
||||
tsOptions: {
|
||||
host: saved.serverAddress,
|
||||
port: saved.serverPort,
|
||||
queryPort: proto === "ts6" ? 10080 : 10011,
|
||||
nickname: saved.nickname,
|
||||
// Reuse the stored identity so server groups assigned to this bot
|
||||
// survive restarts — without this the TS server sees a new UID
|
||||
// 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,
|
||||
ts6ApiKey: saved.ts6ApiKey || undefined,
|
||||
},
|
||||
neteaseProvider: this.neteaseProvider,
|
||||
qqProvider: this.qqProvider,
|
||||
bilibiliProvider: this.bilibiliProvider,
|
||||
@@ -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,
|
||||
}));
|
||||
},
|
||||
|
||||
+97
-42
@@ -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. */
|
||||
@@ -72,7 +73,10 @@ export class TS3Client extends EventEmitter {
|
||||
private httpQuery: TS6HttpQuery | null = null;
|
||||
private udpErrorTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
constructor(private options: TS3ClientOptions, logger: Logger) {
|
||||
constructor(
|
||||
private options: TS3ClientOptions,
|
||||
logger: Logger,
|
||||
) {
|
||||
super();
|
||||
this.logger = logger;
|
||||
|
||||
@@ -109,7 +113,10 @@ export class TS3Client extends EventEmitter {
|
||||
const addr = `${this.options.host}:${this.options.port}`;
|
||||
|
||||
// Detect or use forced protocol
|
||||
if (this.options.serverProtocol && this.options.serverProtocol !== "unknown") {
|
||||
if (
|
||||
this.options.serverProtocol &&
|
||||
this.options.serverProtocol !== "unknown"
|
||||
) {
|
||||
this.detectedProtocol = this.options.serverProtocol;
|
||||
this.logger.info(
|
||||
{ addr, protocol: this.detectedProtocol },
|
||||
@@ -132,7 +139,11 @@ export class TS3Client extends EventEmitter {
|
||||
);
|
||||
} else {
|
||||
this.logger.info(
|
||||
{ addr, protocol: this.detectedProtocol, queryPort: detection.queryPort },
|
||||
{
|
||||
addr,
|
||||
protocol: this.detectedProtocol,
|
||||
queryPort: detection.queryPort,
|
||||
},
|
||||
`Server protocol detected: ${this.detectedProtocol.toUpperCase()}`,
|
||||
);
|
||||
}
|
||||
@@ -140,7 +151,8 @@ export class TS3Client extends EventEmitter {
|
||||
|
||||
// Set up TS6 HTTP Query if applicable
|
||||
if (this.detectedProtocol === "ts6") {
|
||||
const queryPort = this.options.queryPort !== 10011 ? this.options.queryPort : 10080;
|
||||
const queryPort =
|
||||
this.options.queryPort !== 10011 ? this.options.queryPort : 10080;
|
||||
this.httpQuery = new TS6HttpQuery({
|
||||
host: this.options.host,
|
||||
port: queryPort,
|
||||
@@ -151,7 +163,9 @@ export class TS3Client extends EventEmitter {
|
||||
// Guard against calling connect() while already connected.
|
||||
// Save detectedProtocol first because disconnect() resets it.
|
||||
if (this.client) {
|
||||
this.logger.warn("connect() called while already connected, disconnecting first");
|
||||
this.logger.warn(
|
||||
"connect() called while already connected, disconnecting first",
|
||||
);
|
||||
const savedProtocol = this.detectedProtocol;
|
||||
const savedHttpQuery = this.httpQuery;
|
||||
this.disconnect();
|
||||
@@ -178,7 +192,9 @@ export class TS3Client extends EventEmitter {
|
||||
if (this.udpErrorTimer) clearTimeout(this.udpErrorTimer);
|
||||
this.udpErrorTimer = setTimeout(() => {
|
||||
if (udpErrorCount > 1) {
|
||||
this.logger.warn(`udp send error (repeated ${udpErrorCount} times, connection may be lost)`);
|
||||
this.logger.warn(
|
||||
`udp send error (repeated ${udpErrorCount} times, connection may be lost)`,
|
||||
);
|
||||
}
|
||||
udpErrorCount = 0;
|
||||
this.udpErrorTimer = null;
|
||||
@@ -189,17 +205,22 @@ export class TS3Client extends EventEmitter {
|
||||
this.logger.warn(msg);
|
||||
};
|
||||
|
||||
this.client = new TS3FullClient(this.identity, addr, this.options.nickname, {
|
||||
// Forward server password to the protocol library so it can be
|
||||
// included in clientinit for password-protected servers
|
||||
serverPassword: this.options.serverPassword,
|
||||
logger: {
|
||||
debug: (msg) => this.logger.debug(msg),
|
||||
info: (msg) => this.logger.info(msg),
|
||||
warn: throttledWarn,
|
||||
error: (msg) => this.logger.error(msg),
|
||||
this.client = new TS3FullClient(
|
||||
this.identity,
|
||||
addr,
|
||||
this.options.nickname,
|
||||
{
|
||||
// Forward server password to the protocol library so it can be
|
||||
// included in clientinit for password-protected servers
|
||||
serverPassword: this.options.serverPassword,
|
||||
logger: {
|
||||
debug: (msg) => this.logger.debug(msg),
|
||||
info: (msg) => this.logger.info(msg),
|
||||
warn: throttledWarn,
|
||||
error: (msg) => this.logger.error(msg),
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
this.client.on("textMessage", (msg: TextMessage) => {
|
||||
const tsMsg: TS3TextMessage = {
|
||||
@@ -221,7 +242,7 @@ export class TS3Client extends EventEmitter {
|
||||
this.client.on("clientEnter", (info: ClientInfo) => {
|
||||
this.logger.debug(
|
||||
{ nickname: info.nickname, id: info.id },
|
||||
"Client entered"
|
||||
"Client entered",
|
||||
);
|
||||
this.emit("clientEnter", info);
|
||||
});
|
||||
@@ -234,7 +255,7 @@ export class TS3Client extends EventEmitter {
|
||||
this.client.on("clientMoved", (ev: ClientMovedEvent) => {
|
||||
this.logger.debug(
|
||||
{ id: ev.id, targetChannelID: ev.targetChannelID.toString() },
|
||||
"Client moved"
|
||||
"Client moved",
|
||||
);
|
||||
this.emit("clientMoved", ev);
|
||||
});
|
||||
@@ -254,11 +275,16 @@ 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
|
||||
this.options.channelPassword,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -268,24 +294,32 @@ 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,
|
||||
);
|
||||
} 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);
|
||||
|
||||
if (!channel) {
|
||||
this.logger.warn({ channelName }, "Channel not found");
|
||||
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"
|
||||
"Joined channel",
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.error({ err, channelName }, "Failed to join channel");
|
||||
@@ -294,7 +328,7 @@ export class TS3Client extends EventEmitter {
|
||||
|
||||
async sendTextMessage(
|
||||
message: string,
|
||||
targetMode: number = 2
|
||||
targetMode: number = 2,
|
||||
): Promise<void> {
|
||||
if (!this.client) return;
|
||||
// targetMode 2 = channel, target 0 = current channel
|
||||
@@ -326,7 +360,9 @@ export class TS3Client extends EventEmitter {
|
||||
await this.client.sendCommandNoWait(cmd);
|
||||
}
|
||||
|
||||
async execCommandWithResponse(cmd: string): Promise<Record<string, string>[]> {
|
||||
async execCommandWithResponse(
|
||||
cmd: string,
|
||||
): Promise<Record<string, string>[]> {
|
||||
if (!this.client) throw new Error("Not connected");
|
||||
return this.client.execCommandWithResponse(cmd);
|
||||
}
|
||||
@@ -339,15 +375,28 @@ export class TS3Client extends EventEmitter {
|
||||
overwrite = true,
|
||||
): Promise<FileUploadInfo> {
|
||||
if (!this.client) throw new Error("Not connected");
|
||||
return this.client.fileTransferInitUpload(channelID, path, password, size, overwrite);
|
||||
return this.client.fileTransferInitUpload(
|
||||
channelID,
|
||||
path,
|
||||
password,
|
||||
size,
|
||||
overwrite,
|
||||
);
|
||||
}
|
||||
|
||||
async uploadFileData(host: string, info: FileUploadInfo, data: Readable): Promise<void> {
|
||||
async uploadFileData(
|
||||
host: string,
|
||||
info: FileUploadInfo,
|
||||
data: Readable,
|
||||
): Promise<void> {
|
||||
if (!this.client) throw new Error("Not connected");
|
||||
await this.client.uploadFileData(host, info, data);
|
||||
}
|
||||
|
||||
async fileTransferDeleteFile(channelID: bigint, paths: string[]): Promise<void> {
|
||||
async fileTransferDeleteFile(
|
||||
channelID: bigint,
|
||||
paths: string[],
|
||||
): Promise<void> {
|
||||
if (!this.client) throw new Error("Not connected");
|
||||
await fileTransferDeleteFile(this.client, channelID, paths);
|
||||
}
|
||||
@@ -371,7 +420,10 @@ export class TS3Client extends EventEmitter {
|
||||
this.client.sendVoice(opusFrame, 5);
|
||||
this.voiceFramesSent++;
|
||||
if (this.voiceFramesSent === 1) {
|
||||
this.logger.info({ opusBytes: opusFrame.length, clientId: this.clientId }, "First voice packet sent to TeamSpeak");
|
||||
this.logger.info(
|
||||
{ opusBytes: opusFrame.length, clientId: this.clientId },
|
||||
"First voice packet sent to TeamSpeak",
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
if (this.voiceFramesSent === 0) {
|
||||
@@ -392,12 +444,15 @@ export class TS3Client extends EventEmitter {
|
||||
if (this.client && !this.disconnecting) {
|
||||
this.disconnecting = true;
|
||||
const client = this.client;
|
||||
client.disconnect().catch(() => {}).finally(() => {
|
||||
if (this.client === client) {
|
||||
this.client = null;
|
||||
}
|
||||
this.disconnecting = false;
|
||||
});
|
||||
client
|
||||
.disconnect()
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
if (this.client === client) {
|
||||
this.client = null;
|
||||
}
|
||||
this.disconnecting = false;
|
||||
});
|
||||
}
|
||||
this.clientId = 0;
|
||||
this.httpQuery = null;
|
||||
|
||||
+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="如 1234" 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