# @tf2pickup-org/mumble-client 中文文档

规范地址：https://npmcn.com/package/%40tf2pickup-org/mumble-client/

版本：0\.15\.2
npm 元数据更新时间：2026-11-02T15:34:03.591Z
中文译文更新时间：2026-11-02T16:18:34.394Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40tf2pickup-org/mumble-client
原文对照：https://npmcn.com/package/%40tf2pickup-org/mumble-client/?lang=original
项目主页：https://github.com/tf2pickup-org/mumble-client
源码仓库：https://github.com/tf2pickup-org/mumble-client

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：306 kB，109 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：3 个
- 首次发布：2022-06-07T08:29:05.233Z
- 最新版本发布：2026-11-02T15:34:03.166Z
- 已发布版本数：24（近 12 个月 8 个）

## 中文简介

一个简单的机器人，用于管理mumble服务器

## 安装

```sh
npm install @tf2pickup-org/mumble-client
```

也可以使用：`pnpm add @tf2pickup-org/mumble-client`、`yarn add @tf2pickup-org/mumble-client`、`bun add @tf2pickup-org/mumble-client`

## README

# mumble-客户端

一个简单的模糊客户端，用于管理模糊服务器

 [![Latest release](https://img.shields.io/npm/v/@tf2pickup-org/mumble-client)](https://www.npmjs.com/package/@tf2pickup-org/mumble-client)[![Test status](https://github.com/tf2pickup-org/mumble-client/actions/workflows/test.yml/badge.svg) ](https://github.com/tf2pickup-org/mumble-client/actions?query=workflow%3Atest)[![MIT license](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## 功能

-   通过TLS连接到任何Mumble服务器
-   管理通道：创建、删除、链接/解除链接、获取acl
-   管理用户: 移动，静音/耳聋，注册/注销，重命名
-   发送和接收私人短信；读取证书哈希值和客户端IP地址
-   渠道权限查询
-   具有完整TypeScript支持的事件驱动架构
-   自动保持活ping

## 安装

```bash
npm i @tf2pickup-org/mumble-client
```

## 快速启动

```typescript
import { Client } from '@tf2pickup-org/mumble-client';

const client = new Client({
  host: 'mumble.example.com',
  port: 64738,
  username: 'my-bot',
  rejectUnauthorized: false,
});

await client.connect();
console.log(`Connected! Welcome message: ${client.welcomeText}`);
console.log(`Logged in as ${client.self?.name}`);

// ... do work ...

client.disconnect();
```

## 示例

### 使用密码和访问令牌进行连接

```typescript
const client = new Client({
  host: 'mumble.example.com',
  port: 64738,
  username: 'my-bot',
  password: 'server-password',
  tokens: ['token-for-acl-group'],
  clientName: 'my-awesome-bot',
  rejectUnauthorized: false,
});

await client.connect();
```

### 列出所有频道

```typescript
import { type Channel } from '@tf2pickup-org/mumble-client';

function printChannel(channel: Channel, level = 0) {
  if (channel.name) {
    console.log(' '.repeat(level * 2) + channel.name);
  }

  for (const subChannel of channel.subChannels) {
    printChannel(subChannel, level + 1);
  }
}

printChannel(client.channels.root);
```

### 找一个频道

```typescript
// by name
const lobby = client.channels.byName('Lobby');

// by path (from root)
const redTeam = client.channels.byPath('Match 1', 'RED');

// by custom predicate
const tempChannels = client.channels.findAll(ch => ch.temporary);
```

### 创建和管理渠道

```typescript
const root = client.channels.root;

// create a sub-channel under root
const matchChannel = await root.createSubChannel('Match 1');

// create team channels
const red = await matchChannel.createSubChannel('RED');
const blu = await matchChannel.createSubChannel('BLU');

// link two channels together
await red.link(blu);

// unlink them
await red.unlink(blu);

// remove a channel (and all its sub-channels)
await matchChannel.remove();
```

### 在不同渠道之间移动用户

```typescript
const user = client.users.byName('PlayerOne');
const targetChannel = client.channels.byName('RED');

if (user && targetChannel) {
  await user.moveToChannel(targetChannel.id);
}
```

### 使自己静音/耳聋

```typescript
// self-mute
await client.self.setSelfMute(true);

// self-deafen
await client.self.setSelfDeaf(true);

// unmute
await client.self.setSelfMute(false);
```

### 注册和管理用户

```typescript
// register the currently logged-in user
await client.self.register();

// fetch all registered users from the server
const registeredUsers = await client.fetchRegisteredUsers();
for (const user of registeredUsers) {
  console.log(`${user.userId}: ${user.name}`);
}

// rename a registered user (does not need to be online)
await client.renameRegisteredUser(userId, 'NewName');

// deregister a user (does not need to be online)
await client.deregisterUser(userId);
```

### 短信、证书哈希和地址

```typescript
client.on('textMessage', async ({ sender, message, isPrivate }) => {
  if (sender && isPrivate) {
    await sender.sendMessage(`you said: ${message}`);
  }
});

// message everyone in a channel
await client.channels.byName('Lobby')?.sendMessage('hello');

// SHA1 hash of the user's certificate, sent for every user
console.log(user.hash);

// IP address; needs the Ban permission on the root channel
console.log(await user.fetchAddress());
```

### 检查通道权限

```typescript
const channel = client.channels.byName('Lobby');
if (channel) {
  const permissions = await channel.getPermissions();
  console.log('Can join:', permissions.canJoinChannel);
  console.log('Can create sub-channels:', permissions.canCreateChannel);
  console.log('Can remove:', permissions.canRemoveChannel);
  console.log('Can link:', permissions.canLinkChannel);
}
```

### 听事件

```typescript
// user connected
client.on('userCreate', user => {
  console.log(`${user.name} connected`);
});

// user disconnected
client.on('userRemove', user => {
  console.log(`${user.name} disconnected`);
});

// user state changed (e.g. moved channel, muted, etc.)
client.on('userUpdate', (user, changes) => {
  if (changes.channelId) {
    const from = client.channels.byId(changes.channelId.previousValue);
    const to = client.channels.byId(changes.channelId.currentValue);
    console.log(`${user.name} moved from ${from?.name} to ${to?.name}`);
  }
});

// channel created
client.on('channelCreate', channel => {
  console.log(`Channel created: ${channel.name}`);
});

// channel removed
client.on('channelRemove', channel => {
  console.log(`Channel removed: ${channel.name}`);
});

// speaking state changes
client.on('speakingStateChange', ({ user, speaking }) => {
  console.log(`${user.name} ${speaking ? 'started' : 'stopped'} speaking`);
});

// disconnected from server
client.on('disconnect', payload => {
  console.log(`Disconnected: ${payload?.reason ?? 'unknown reason'}`);
});
```

### 错误处理

```typescript
import {
  Client,
  ClientDisconnectedError,
  ConnectionRejectedError,
  PermissionDeniedError,
} from '@tf2pickup-org/mumble-client';

try {
  await client.connect();
} catch (error) {
  if (error instanceof ConnectionRejectedError) {
    console.error('Connection rejected by server');
  } else if (error instanceof ClientDisconnectedError) {
    // the server dropped the connection during the handshake,
    // or another connect() call superseded this one
    console.error('Connection lost');
  }
}

// background errors, e.g. a failed keep-alive ping; without a listener
// Node.js throws on an 'error' event
client.on('error', error => {
  console.error('Client error:', error);
});

try {
  await client.channels.root.createSubChannel('test');
} catch (error) {
  if (error instanceof PermissionDeniedError) {
    console.error('Insufficient permissions to create channel');
  }
}
```

## API概述

### `Client`

主要入口点。连接到服务器并提供对通道、用户和事件的访问。

| 属性/方法 | 描述 |
| --- | --- |
| `channels` | `ChannelManager` 用于查找频道的实例 |
| `users` | `UserManager` 查找用户的实例 |
| `self` | 当前登录的 `User` |
| `session` | 当前登录用户的会话ID |
| `welcomeText` | 服务器欢迎信息 |
| `serverVersion` | 服务器版本信息 |
| `serverConfig` | 服务器配置 (最大用户数、消息长度…) |
| `socket` | 底层套接字，for [音频](https://npmcn.com/package/%40tf2pickup-org/mumble-client/AUDIO_EXAMPLE.md)； `undefined` 当断开连接 |
| `connect()` | 连接到Mumble服务器; 如果已连接，请先断开连接 |
| `disconnect()` | 断开与服务器的连接；不连接时什么都不做 |
| `isConnected()` | 返回 `true` 如果已连接 |
| `fetchRegisteredUsers()` | 获取所有注册用户 |
| `deregisterUser(userId)` | 按ID取消注册用户 |
| `renameRegisteredUser(userId, name)` | 重命名已注册用户 |

### `ChannelManager`

| 方法 | 描述 |
| --- | --- |
| `root` | 根通道 |
| `byId(channelId)` | 按ID查找频道 |
| `byName(name)` | 按名称查找频道 |
| `byPath(...path)` | 按路径段查找通道 |
| `find(predicate)` | 找到第一个匹配通道 |
| `findAll(predicate)` | 找到所有匹配的频道 |

### `Channel`

| 属性/方法 | 描述 |
| --- | --- |
| `id` | 通道标识 |
| `name` | 频道名称 |
| `temporary` | 通道是否为临时通道 |
| `users` | 当前在此通道中的用户 |
| `subChannels` | 直接子信道 |
| `linkedChannels` | 链接渠道 |
| `createSubChannel(name)` | 创建子通道 |
| `remove()` | 删除此通道 |
| `link(channel)` | 与另一个频道连接 |
| `unlink(channel)` | 从另一个通道取消链接 |
| `getPermissions()` | 获取该通道的权限 |
| `fetchAcl()` | 获取通道ACL |
| `saveAcl(acl)` | 保存通道ACL |

### `UserManager`

| 方法 | 描述 |
| --- | --- |
| `bySession(session)` | 根据会话ID查找用户 |
| `byName(name)` | 按名称查找用户 |
| `findAll(predicate)` | 查找所有匹配的用户 |

### `User`

| 属性/方法 | 描述 |
| --- | --- |
| `session` | 会话ID |
| `name` | 用户名 |
| `channel` | 当前频道 |
| `channelId` | 当前通道ID |
| `isRegistered` | 用户是否已注册 |
| `mute` / `deaf` / `suppress` | 服务器静音/聋哑/抑制状态 |
| `selfMute` / `selfDeaf` | Self-mute /聋状态 |
| `moveToChannel(channelId)` | 将用户移动到频道 |
| `setSelfMute(mute)` | 设置自静音状态 |
| `setSelfDeaf(deaf)` | 设置自聋状态 |
| `register()` | 注册用户 |
| `deregister()` | 注销用户 |
| `rename(name)` | 重命名已注册用户 |

### `Permissions`

| 属性/方法 | 描述 |
| --- | --- |
| `canJoinChannel` | 可以加入/进入频道吗 |
| `canCreateChannel` | 可以创建子通道 |
| `canRemoveChannel` | 可以去除通道 |
| `canLinkChannel` | 可以链接频道 |
| `canSelfRegister` | 可以自行注册 |
| `canRegister` | 可以注册其他用户 |

### 事件

| 事件 | 有效载荷 | 描述 |
| --- | --- | --- |
| `connect` | \- - - - - - | 客户端连接成功 |
| `disconnect` | `{ reason: string }?` | 客户端已断开连接; 每个连接发出一次。 `reason` 是服务器的（例如，当踢）或 `'connection closed'` 当服务器断开连接时；之后没有有效载荷 `disconnect()` 或者重新连接 |
| `error` | `unknown` | 后台错误，例如keepalive ping失败 |
| `channelCreate` | `Channel` | 创建了一个频道 |
| `channelUpdate` | `Channel, ChannelChanges` | 一个频道被更新 |
| `channelRemove` | `Channel` | 一个通道已被删除 |
| `userCreate` | `User` | 已连接的用户 |
| `userUpdate` | `User, UserChanges` | 用户的状态已更改 |
| `userRemove` | `User` | 用户已断开连接 |
| `speakingStateChange` | `{ user: User, speaking: boolean }` | 用户开始/停止说话 |

### `ClientOptions`

扩展Node.js `tls.ConnectionOptions`.

| 选项 | 类型 | 默认的 | 描述 |
| --- | --- | --- | --- |
| `host` | `string` | \- - - - - - | 服务器主机名 |
| `port` | `number` | `64738` | 服务器端口 |
| `username` | `string` | \- - - - - - | 用于身份验证的用户名 |
| `password` | `string?` | \- - - - - - | 服务器或用户密码 |
| `tokens` | `string[]?` | `[]` | 服务器ACL组的访问令牌 |
| `clientName` | `string?` | `'simple mumble bot'` | 发送到服务器的客户端名称 |
| `pingInterval` | `number?` | `10000` | 保持活动ping间隔 (毫秒) |

自 `ClientOptions` 扩展 `tls.ConnectionOptions`，您可以传递任何TLS选项，例如 `rejectUnauthorized`， `cert`， `key`， `ca`等。

## 许可证

[麻省理工学院](https://opensource.org/licenses/MIT)

## 常见问题

### @tf2pickup-org/mumble-client 是什么？

一个简单的机器人，用于管理mumble服务器

### 如何安装 @tf2pickup-org/mumble-client？

在项目目录运行 npm install @tf2pickup-org/mumble-client，或使用 pnpm add / yarn add / bun add。

### @tf2pickup-org/mumble-client 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @tf2pickup-org/mumble-client 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @tf2pickup-org/mumble-client 可以商用吗？

@tf2pickup-org/mumble-client 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @tf2pickup-org/mumble-client 还在维护吗？

最新版本 0\.15\.2 发布于 2026/11/02（今天）；至今共发布 24 个版本，其中过去 12 个月发布了 8 个；首次发布于 2022/06/07。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

