This commit is contained in:
2026-04-25 16:36:34 +08:00
commit db90e7579b
1876 changed files with 189777 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
---
sidebar_position: 4
title: 自定义内置插件
---
在 Tailchat 的插件中心中可以看到系统已经内置了一部分插件默认给用户安装,且这部分插件是不可被卸载的。而对于自部署的企业用户来说,让所有的成员都默认安装上企业或者预设好的其他插件尤为重要。
接下来我们将学习如何自定义内置插件列表
*因为插件的加载时机很早,因此在 Tailchat 的设计中会将内置插件列表编译到源码中以确保尽可能快的加载页面。因此你需要手动编译镜像*
首先需要下载源码:
```bash
git clone https://github.com/msgbyte/tailchat.git
```
修改内置插件列表:
```bash
cd tailchat
vim client/web/src/plugin/builtin.ts
```
将你的配置文件(一般可以在插件目录的`manifest.json`文件中找到)按照已有的插件列表格式添加到导出的变量`builtinPlugins` 数组中。
:::info
已有的插件列表可以在这里看到 [插件列表](/docs/plugin-list/fe)
:::
当编辑完成后保存确保当前目录在tailchat的根目录。此时你的目录下应该可以直接看到 `Dockerfile` 文件
执行命令编译自己的镜像
```bash
docker build -t tailchat .
```
其中 `.` 表示当前目录,`-t tailchat` 表示编译的标签是 `tailchat`, 这可以直接被 `docker-compose.yml` 文件读取
编译完成后按照正常操作启动即可 `docker compose up -d`
:::info
自己编译镜像建议配置在 **2C4G** 以上
:::

View File

@@ -0,0 +1,49 @@
---
sidebar_position: 3
title: 外部存储
---
## 背景
随着使用规模的推移,用户对 Tailchat 文件系统的存储成本会逐步上升,因此私有化部署的对象存储服务`minio`可能会有很高的磁盘存储成本。为了降低成本 Tailchat 提供了使用外部第三方对象存储服务的解决方案
## 前提条件
- 该外部存储服务需要支持 `aws s3` 存储协议
- `Tailchat` 版本在 `1.8.7+`
## 配置
你需要通过环境变量配置如下:
- `MINIO_URL`: s3 服务地址,`<hostname>:<port>`,例如:`example.com:443`,不需要协议部分如 `https://`
- `MINIO_SSL`: s3服务是否启用ssl验证对于某些提供商是必须的。默认为`false`
- `MINIO_USER`: s3服务用户名
- `MINIO_PASS`: s3服务密码
- `MINIO_BUCKET_NAME`: s3服务 bucket 名
- `MINIO_PATH_STYLE`: 路径模式,可选值: `VirtualHosted``Path`
- S3 协议有两种风格,`VirtualHosted` 用于 `<bucketname>.example.com``Path` 用于 `example.com/<bucketname>`
- `STATIC_URL`: 上传后的静态路径地址,默认走服务器中转,如果想要走外部存储直连的话需要改为外部可访问的地址
> 对于 `aliyunoss` 我们可以参考该文档获得内容相关帮助: https://www.alibabacloud.com/help/en/oss/developer-reference/use-amazon-s3-sdks-to-access-oss
### 示例
**R2**:
```bash
MINIO_URL=<account-id>.r2.cloudflarestorage.com:443
MINIO_PATH_STYLE=Path
```
## 数据迁移
如果你是从私有化部署的 `minio` 服务迁移到公有云上,那么你需要对旧的数据进行迁移。
迁移文件: `files/**`
你可以通过`docker volume inspect tailchat_storage` 获取到存储卷的相信内容,其中 `Mountpoint` 表示路径,把 `<Mountpoint>/tailchat/files` 目录打包上传到对应的
### 数据库迁移脚本(可选)
> 这不是一个必须的操作, 因为就算不迁移也会按照原来的路径去走服务器中转
TODO: 欢迎共建

View File

@@ -0,0 +1,56 @@
---
sidebar_position: 2
title: Github 集成
---
:::caution
该篇内容仍在调整中...
:::
![](/img/github-app/github-integration.excalidraw.png)
## 普通用户使用
### 在项目安装应用
地址: https://github.com/apps/tailchat
安装到项目仓库中。
### 在项目中进行配置
在根目录创建 `.tailchat/topic.json` 文件:
```json
{
"groupId": "<your-notify-group-id>",
"panelId": "<your-topic-panel-id>"
}
```
## 自部署配置
应用启动前需要在github中注册一个应用:
![](/img/github-app/github-new-app.png)
部署时需要配置以下环境变量:
- `APP_ID`: 来自github 应用设置
- `WEBHOOK_SECRET`: 来自github 应用设置
- `PRIVATE_KEY`: 来自github 应用设置
- `TAILCHAT_APP_ID`: Tailchat 开放平台的id
- `TAILCHAT_APP_SECRET`: Tailchat 开放平台的秘钥
- `TAILCHAT_API_URL`: Tailchat 后台地址
为获取 `TAILCHAT_APP_ID``TAILCHAT_APP_SECRET` 需要在 Tailchat 开放平台中创建一个开放平台应用
同时开启机器人权限,并设置消息回调地址: `https://<your_app_url>/message/webhook`
### 部署开放平台应用
> 源码: [https://github.com/msgbyte/tailchat/tree/master/apps/github-app](https://github.com/msgbyte/tailchat/tree/master/apps/github-app)
拉取源码后部署到可供访问的线上,提供两种方式:
- 独立应用: `npm run build` 后执行 `node lib/index.js`运行应用
- Vercel: 直接推送到Vercel即可

View File

@@ -0,0 +1,34 @@
---
sidebar_position: 1
title: 关于开放平台
---
开放平台是常见且传统的应用与应用之间进行交互的方式,对于一些简单的需求我们可以通过开放平台来实现应用之间的数据传递。
在 Tailchat 中。目前主要提供两种形式的开放平台应用能力: `OAuth``Bot`
## 功能介绍
### OAuth
`OAuth` 能够使外部应用能够通过`Tailchat`的账号登录,就像是 `Google`, `Github` 登录方式一样,可以方便用户打造基于 `Tailchat` 的统一用户平台
:::info
`com.msgbyte.iam` 插件的区别: `iam`插件是提供外部账号登录`Tailchat`的方式,如使用`Github`账号登录`Tailchat`, 而开放平台的OAuth能力则是以`Tailchat`账号登录其他平台。
:::
[了解更多](./oauth)
### Bot
`Bot` 赋予聊天机器人可交互的应用能力,这意味着 Tailchat 不仅仅可以被动接收来自外部的消息,也可以主动将内部聊天的请求转发到外部应用代为处理。
[了解更多](./bot)
## 使用前提
在使用开放平台的相关能力前,请确保安装了对应的插件,并确保管理员已经部署与开放了相关的开放平台的能力。
作为用户,你需要安装 `com.msgbyte.integration` 插件来将应用加入到自己的群组
作为开放平台应用的开发者,你需要额外安装 `com.msgbyte.openapi` 来展示开放平台应用相关所需要的界面

View File

@@ -0,0 +1,230 @@
---
sidebar_position: 4
title: 机器人
---
开放平台的机器人是可交互式的机器人解决方案
他的主要流程是:
- 创建openapp
- 获取appid和appsecret
- 进入bot页面开启bot能力
- 回调地址填入可以访问到的公网http服务地址
- 回到群组页面在群组详情集成功能中输入appid找到刚刚创建的应用加入到群组中
- 在群组中@机器人并输入文本此时tailchat会向回调地址所示的地址发送一个带消息内容的http请求
- 在机器人服务中接受到tailchat发送的请求并通过机器人给对应群组的对应面板发送响应
当然,在开始一切工作前请[先在 Tailchat 中创建](./create)
下面我们来实际操作一下机器人的开发流程:
## 使用SDK进行开发(Node.js)
Tailchat 提供了sdk作为快速开发的工具`tailchat-client-sdk`, 你可以通过以下命令安装
```bash
npm install tailchat-client-sdk
```
`koa`为例,我们先创建一个简单的`koa`服务如下:
创建node项目:
```bash
mkdir tailchat-bot && cd tailchat-bot
npm init -y
npm install koa koa-router tailchat-client-sdk
```
创建`server.js`文件:
```js
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// 定义路由
router.get('/', async (ctx) => {
ctx.body = 'Hello, World!';
});
router.post('/bot/callback', async (ctx) => {
ctx.body = 'Bot Callback Page';
});
// 注册路由中间件
app.use(router.routes());
app.use(router.allowedMethods());
// 启动服务
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
```
在此时我们建立了两个基本的路由,`/``/bot/callback`, 并监听了`3000`端口, 注意`/bot/callback`监听的是**POST**请求
此时我们执行 `node server.js` 可以看到我们的应用会被启动。
现在我们要增加一些逻辑,比如我们想要实现一个复读机器人, 那么修改 `/bot/callback` 路由的实现如下:
```js
import { TailchatHTTPClient, stripMentionTag } from 'tailchat-client-sdk';
const host = '<your tailchat instance backend host>';
const appId = '<appId>';
const appSecret = '<appSecret>';
const client = new TailchatHTTPClient(host, appId, appSecret)
// ...
router.post('/bot/callback', async (ctx) => {
const type = ctx.body.type;
if (type === 'message') {
const payload = ctx.body.payload;
try {
const message = await client.replyMessage({
messageId: payload.messageId,
author: payload.messageAuthor,
content: payload.messageSnippet
}, {
groupId: payload.groupId,
converseId: payload.converseId,
content: `Your message: ${stripMentionTag(payload.messageSnippet)}`,
})
console.log('send message success:', message)
} catch (err) {
console.log('send message failed:', err)
}
}
ctx.body = 'Bot Callback Page';
});
```
请将 `host` `appId``appSecret` 分别填入在创建时获取到的 `appId``appSecret``host` 填入 `Tailchat` 服务端的地址, 官方 `nightly` 的地址是 `https://tailchat-nightly.moonrailgun.com`
至于回复的内容并不重要只需要确保不要主动返回错误信息即可Tailchat并不关心返回内容
**请注意如果你要把你的代码分享出去的话请保管好你的`appSecret`, 这等价于你的账号的密码**
逻辑非常简单,从请求中拿到消息的内容, 作者, id, 以及所在的群组id, 会话id。以回复的形式将内容发送出去
将该应用部署到线上即可看到效果。
:::info
在测试前请确保你已经开启了机器人能力并填入了正确的回调地址
:::
## 使用其他语言进行开发
既然是网络应用,那么当然不仅仅局限于`nodejs`, 下面是所需要用到的一些网络请求的格式主要是以开放平台机器人身份向Tailchat服务端发送请求。
官方 `nightly` 的api地址是 `https://tailchat-nightly.moonrailgun.com` , 自部署的请替换成自己的后端地址
### 登录
在所有的请求之前都需要先登录获取jwt token以表明身份需要发送以下内容:
```
POST /api/openapi/bot/login
Header
Content-Type: application/json
Body
{
appId: <your app id>,
token: <md5(appId+appSecret)>,
}
Response
{
data: {
jwt: "..........",
userId: ".........."
}
}
```
其中请求体的`token`是一个固定值,需要拼接 `appId``appSecret` 后以`md5`算法进行加密。最后拿到 `jwt`, `jwt` 要在之后的所有请求中在请求头带上
```
Header
X-Token: <your-jwt>
```
### 调用
机器人可以和普通的用户一样调用接口,如:
```
POST /api/xxx
Header
Content-Type: application/json
X-Token: <your-jwt>
Body
{
...
}
```
你可以将机器人视为一个普通用户来看待,普通用户所能做的事情机器人都能做,普通用户需要受到的权限限制机器人也会受到。
区别在于普通用户是用可视化进行交互的而机器人是通过api进行交互的。
#### 发送信息
```
POST /api/chat/message/sendMessage
Header
Content-Type: application/json
X-Token: <your-jwt>
Body
{
"converseId": "",
"groupId": "",
"content": "",
"plain": "",
"meta": {},
}
```
#### 回复信息
```
POST /api/chat/message/sendMessage
Header
Content-Type: application/json
X-Token: <your-jwt>
Body
{
"converseId": "<converId/panelId>",
"groupId": "<groupId, optional in DM>",
"content": "<your message content>",
"plain": "<your plained message, optional>",
"meta": {
mentions: ["<replyMessageAuthorId>"],
reply: {
_id: "<replyMessageId>",
author: "<replyMessageAuthor>",
content: "<replyMessageContent>",
},
},
}
```
## 其他文档
- [Tailchat x Laf: 十分钟开发一个对话机器人](/blog/tailchat-laf-robot)

View File

@@ -0,0 +1,14 @@
---
sidebar_position: 2
title: 创建开放平台应用
---
安装 `com.msgbyte.openapi` 插件后可以在左下角设置页面看到多出来一个开放API的功能
![](/img/advanced-usage/openapp/1.png)
填入应用名称与应用描述即可
![](/img/advanced-usage/openapp/2.png)
成功后你可以在你的应用列表中看到你的开放平台应用

View File

@@ -0,0 +1,94 @@
---
sidebar_position: 5
title: OAuth 第三方登录
---
`Tailchat` 开放平台支持 `OAuth` 登录协议, 你可以很方便的将 `Tailchat` 账号体系接入到你的系统中。正如我们常见的 `Github 登录``Google 登录``Apple 登录` 一样
而现在,你可以通过 `Tailchat` 对你的多个平台做统一的账号管理体系。
## 在 Tailchat 中新建开放平台应用
你需要创建一个开放平台应用并开启 **OAuth** 服务。
在**回调地址**处填入允许被重定向的地址。
![](/img/advanced-usage/openapp/3.png)
## 创建独立 应用发起并接受回调
首先我们在正式开始之前要大概了解一下 **OAuth** 的基本流程
![](/img/advanced-usage/openapp/4.png)
简单的来说,就是分为三步:
- 第一步访问授权要传client_id:客户端idredirect_uri:重定向uriresponse_type为codescope是授权范围默认填`openid profile`即可state是其它自定义参数
- 第二步授权通过会重定向到redirect_uricode码会作为它的参数
- 第三步拿到code后可以换取 access token, 之后就可以通过token直接访问资源
你可以参考 [https://github.com/msgbyte/tailchat/blob/master/server/test/demo/openapi-client-simple/index.ts](https://github.com/msgbyte/tailchat/blob/master/server/test/demo/openapi-client-simple/index.ts) 来实现你自己的 OAuth 应用
### 主要流程
这里简单简述一下过程:
首先构建一个请求地址,形如:
```
<API>/open/auth?client_id=<clientId>&redirect_uri=<redirect_uri>&scope=openid profile&response_type=code&state=123456789
```
其中:
- `API` 是你的tailchat后端地址如果是使用默认部署方案的话就是你的访问地址。
- `clientId` 是你第一步申请到的开放平台的地址。
- `redirect_uri` 为你的回调地址,你需要确保其已经被添加到允许回调地址的白名单中
- `scope` 是申请授权范围,目前固定填入 `openid profile` 即可
- `response_type` 是响应类型,固定填入 `code` 即可
- `state` 其他的自定义参数,会随着重定向和`code`参数一起调用.
用户访问该地址后,会跳转到 Tailchat 平台进行登录授权,如果授权通过的话会重定向到 `redirect_uri` 规定的地址. 此时接收地址可以在query string 中获取到 `code``state`.
下一步我们需要通过发送 POST 请求使用 `code` 换取 `token`. 后续我们需要通过 `token` 来获取用户信息
```
POST <API>/open/token
{
"client_id": clientId,
"client_secret": clientSecret,
"redirect_uri": redirect_uri,
"code": code,
"grant_type": 'authorization_code',
}
```
返回值:
```
{
access_token,
expires_in,
id_token,
scope,
token_type
}
```
此时我们拿到了 `access_token`, 我们可以用来请求用户信息:
```
POST <API>/open/me
{
"access_token": access_token,
}
```
返回值:
```
{
sub,
nickname,
discriminator,
avatar,
}
```
其中`sub`可以理解为用户的id是用户的唯一标识

View File

@@ -0,0 +1,49 @@
---
sidebar_position: 4
title: Websocket 机器人
---
除了传统的HTTP回调机器人以外我们还支持基于websocket长连接协议的机器人。
长连接机器人可以如正常用户一样监听所有的消息,并无需通过 `@` 来唤起。
当然缺点在于在一些只支持http请求的平台如`serverless`等不支持`websocket`的平台不够便利。且目前只有`nodejs`版本的实现。
以下是一个操作示例:
```ts
import { TailchatWsClient } from 'tailchat-client-sdk';
const HOST = process.env.HOST;
const APPID = process.env.APPID;
const APPSECRET = process.env.APPSECRET;
if (!HOST || !APPID || !APPSECRET) {
console.log('require env: HOST, APPID, APPSECRET');
process.exit(1);
}
const client = new TailchatWsClient(HOST, APPID, APPSECRET);
client.connect().then(async () => {
console.log('Login Success!');
client.onMessage((message) => {
console.log('Receive message', message);
});
});
```
其中 `HOST`, `APPID`, `APPSECRET` 分别表示服务器地址开放平台的id与秘钥。
**注意请不要在公共平台上泄露你的秘钥,秘钥等价于密码**
该操作在连接到服务器后创建了一个事件监听当接收到任何消息的时候会把message的内容打印出来。
类似的,如果我们需要监听消息的更新事件的话,则可以使用监听消息更新操作
```ts
client.onMessageUpdate((message) => {
console.log('Receive message', message);
});
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,16 @@
---
sidebar_position: 4
title: 自动加入群组
---
`com.msgbyte.autojoinGroup`
只需要配置环境:`AUTOJOIN_GROUP_ID=xxxxx`并重新启动服务器
您应该从admin或web端手动创建群组。 然后就可以得到一个groupId。
在 Web端您可以从 url 获取groupId形如`/main/group/<groupId>/<panelId>`
> 用户注册后如果想加入多个群组,可以用英文逗号`,`分割
>
> 例如AUTOJOIN_GROUP_ID=xxxxx,xxxx

View File

@@ -0,0 +1,40 @@
---
sidebar_position: 3
title: iam - 第三方登录
---
`com.msgbyte.iam`
`IAM` 插件提供第三方登录功能
目前支持:
- github
## 配置方式
### Github
`Github` 中创建 OAuth 应用。获得 `Client ID``Client secrets`
配置`Github`应用的授权回调地址`Authorization callback URL``{API_URL}/api/plugin:com.msgbyte.iam/github/redirect`。注意,`API_URL` 为环境变量中的值,两者应当保持一致。
配置Tailchat环境变量:
- IAM_GITHUB_ID
- IAM_GITHUB_SECRET
分别为之前获取到的`Client ID``Client secrets`
> 你也可以通过环境变量重写请求地址,例如使用代理:
>
> - IAM_GITHUB_URI_AUTHORIZE=https://github.com/login/oauth/authorize
> - IAM_GITHUB_URI_ACCESS_TOKEN=https://github.com/login/oauth/access_token
> - IAM_GITHUB_URI_USERINFO=https://api.github.com/user
## 安全强化
为了token的安全不被恶意应用获取建议增加前端域名校验。
在环境变量中配置`IAM_FE_URL`即可,值为前端域名。如: `IAM_FE_URL=http://localhost:11011`
> 用于 `window.opener.postMessage(<data>, "IAM_FE_URL")`

View File

@@ -0,0 +1,35 @@
---
sidebar_position: 5
title: livekit - 视频会议
---
`com.msgbyte.livekit`
## 使用文档
本插件提供了 `Tailchat` 视频会议的功能,安装插件后即可创建语音频道。
安装后可以在创建面板页面看见语音频道的面板类型
![](/img/advanced-usage/plugins/livekit/1.png)
语音频道入口大概是这样的:
![](/img/advanced-usage/plugins/livekit/2.png)
你可以在加入前准备好多媒体的选项,并切换媒体设备。
当然,在加入后也可以随时切换设备。就像下面这样
![](/img/advanced-usage/plugins/livekit/3.png)
你可以自由的与朋友们视频通话、分享屏幕、以及聊天。
最后,请自由切换页面,这并不会让你退出频道,你可以随时点击左下角的图标快速回到活动的频道.
![](/img/advanced-usage/plugins/livekit/4.png)
## 部署文档
见教程 [livekit](../../meeting/livekit.md)

View File

@@ -0,0 +1,36 @@
---
sidebar_position: 2
title: wxpusher
---
`com.msgbyte.wxpusher`
## 使用文档
- 在插件中心找到`wxpusher`插件,安装
-`左下角...` -> `更多` -> `wxpusher`
- 使用微信扫码绑定账号
- 之后 **@你** 的消息都会自动在微信中推送
## 部署文档
### 在wxpusher平台注册账号
在应用设置中设置回调地址, 如下:
![](./assets/wxpusher.png)
`https://<your backend domain>/api/plugin:com.msgbyte.wxpusher/callback`
### 获取appToken
在 [https://wxpusher.zjiecode.com/admin/main/app/appToken](https://wxpusher.zjiecode.com/admin/main/app/appToken) 中获取 appToken
### 配置Tailchat环境变量
将上一步获取到的appToken写入环境变量中如:
```
WXPUSHER_APP_TOKEN=xxxxxxxxxxxxxx
```
启动后生效, 你可以访问 `https://<your backend domain>/api/plugin:com.msgbyte.wxpusher/available`来检查服务可用性

View File

@@ -0,0 +1,34 @@
---
sidebar_position: 1
title: 富文本语法
---
## 对于普通用户
Tailchat 内置了 `com.msgbyte.bbcode` 插件用于对富文本消息做支持(且是默认安装的)。
以下是目前 `bbcode` 插件支持的语法列表:
| 关键字 | 描述 | 用法示例 | 预览 |
| ------ | ----- | ------ | ----- |
| b | 文本加粗 | `[b]foo[/b]` | <b>foo</b> |
| i | 文本倾斜 | `[i]foo[/i]` | <i>foo</i> |
| u | 文本下划线 | `[u]foo[/u]` | <ins>foo</ins> |
| s | 文本删除线 | `[s]foo[/s]` | <del>foo</del> |
| url | 超链接 | <div style={{width: 400}}>`[url]https://tailchat.msgbyte.com[/url]` / `[url=https://tailchat.msgbyte.com]官网[/url]`</div> | <a>https://tailchat.msgbyte.com</a> / <a href="https://tailchat.msgbyte.com">官网</a> |
| img | 图片 | `[img]https://tailchat.msgbyte.com/img/logo.svg[/img]` | <div style={{width: 60}}><img src="https://tailchat.msgbyte.com/img/logo.svg" /></div> |
| at | 提及 | `[at=<hereisuserid>]moonrailgun[/at]` | - |
| emoji | 表情 | `[emoji]smile[/emoji]` | - |
| markdown / md | markdown语法支持 | `[markdown]## Heading[/markdown]` / `[md]## Heading[/md]` | - |
## 对于插件开发者
如果你的插件需要使用统一的富文本支持,请在你的渲染函数中这样实现:
```jsx
import { getMessageRender } from '@capital/common';
const Component = (text: string) => {
return <div>{getMessageRender(text)}</div>
}
```