DeBox ChatWidget
DeBox ChatWidget 持仓聊天浮窗
功能概述
DeBox ChatWidget 是一个去中心化的聊天组件,能够让你轻松地将去中心化的实时聊天功能集成到网页中。只需10分钟,你就可以将强大的聊天程序嵌入到你的产品中,为用户提供流畅、安全、去中心化的聊天体验。

安装集成
DeBox ChatWidget 提供多种集成方式,支持灵活适配不同的 JavaScript 框架和开发环境。
方式一:通过 npm 引入:
适用于使用构建工具(如 Webpack、Vite)的项目,根据具体框架选择适配的 ChatWidget 包:
-
原生 HTML 项目
安装适用于 HTML 项目的 ChatWidget 包:
npm install @debox-pro/chat-widget-html
然后在项目中导入并使用:
// 在使用构建工具的项目中导入
import { DeBoxChatWidget } from '@debox-pro/chat-widget-html';
// 初始化 ChatWidget
DeBoxChatWidget.init({
projectId: 'your-project-id',
zIndex: '999',
containerDomId: 'chat-container',
});
// 设置聊天群组
DeBoxChatWidget.setConversation('conversation-id'); -
React 项目
安装针对 React 框架优化的 ChatWidget 包:
npm install @debox-pro/chat-widget-react
在 React 组件中使用:
import { DeBoxChatWidget } from '@debox-pro/chat-widget-react';
function ChatComponent() {
return (
<DeBoxChatWidget
projectId="your-project-id"
conversationId="conversation-id"
onEvent={(e) => console.log('ChatWidget event:', e.detail)}
/>
);
}
方式二:通过外部脚本引入:
适用于无需构建工具或快速集成的场景,通过 <script>
标签直接加载 ChatWidget 的 UMD 文件。
-
步骤 1:下载 UMD 文件
从以下链接获取 ChatWidget 的 UMD 文件,并保存到您的项目目录中:
-
步骤 2:在 HTML 文件中通过
<script>
标签引入假设您将 UMD 文件保存为 index.umd.js,可在 HTML 文件中添加以下代码引入 ChatWidget:
<script src="./path/to/index.umd.js"></script>
<script>
// 从全局对象获取 DeBoxChatWidget
const { DeBoxChatWidget } = window.DeBoxChatWidget;
// 初始化 ChatWidget
DeBoxChatWidget.init({
projectId: 'your-project-id',
zIndex: '999',
containerDomId: 'chat-container'
});
// 设置聊天群组
DeBoxChatWidget.setConversation('conversation-id');
</script>请将 ./path/to/index.umd.js 替换为 UMD 文件实际路径。
支持方法
1. 初始化 ChatWidget:
该方法用于初始化 ChatWidget 插件,在页面加载时调用。
DeBoxChatWidget.init({
projectId: getQueryVariable('xxx'),
zIndex: 'xxx',
containerDomId: 'xxx',
})
参数:
projectId
:string
,必传,DeBox 开放平台提供的app ID,需在 DeBox 开放平台 申请。containerDomId
:string
,可传,指定该插件的容器 DOM 元素 ID,用于样式隔离。zIndex
:string
,可传,默认值999,设置聊天窗口的堆叠顺序,确保聊天窗口不会被页面其他元素覆盖。
2. 获取聊天群组Id:
若想在 ChatWidget 中打开聊天,需指定 聊天群组Id。 聊天群组Id 需要您通过 DeBox 开放平台 提供的接口获取。该接口支持根据链和合约地址信息,或群组名称来获取对应的聊天群组Id。
DeBox 开放平台获取聊天群组Id接口
目前,ChatWidget 聊天浮窗插件仅支持链上代币的持仓群聊。非链上代币群聊请使用 DeBox 网页版或 DeBox app。
注意:该接口涉及您在 DeBox 开放平台 中的 X-API-KEY
以及 app_secret
等隐私信息,请不要在前端浏览器中直接请求。我们建议您将对于该请求的调用放置在私有后端服务器。
-
接口端点:
http://open.debox.pro/openapi/chatwidget/conversation/id
-
请求方法:POST
-
参数:
Header:
Header 类型 描述 app_id
String 开发者应用标识,需在 DeBox 开放平台获取。 X-API-KEY
String 开发者 API 密钥,需在 DeBox 开放平台获取。 nonce
String 开发者生成的随机数,长度为 8 位。 timestamp
String 时间戳,精确到毫秒。 signature
String 签名, signature
= SHA1( {app_secret
} {nonce
} {timestamp
} )- 计算
signature
时使用的app_secret
是开发者应用密钥,需在 DeBox 开放平台获取
query:
query 参数支持通过链与合约信息或群组名称来获取对应聊天群组Id:
chain_id
:number
,可选,链 ID,例如:1 表示以太坊。contract_address
:string
,可选,聊天群组对应代币的合约地址。group_name
:string
,可选,群组名称,用于没有代币合约地址的情况。
- 计算
-
查询聊天群组过程:
- 当调用该接口时,系统会优先根据
chain_id
和contract_address
查询对应的聊天群组。 - 如果没有
chain_id
和contract_address
信息,则使用group_name
进行查询。 - 如果能够查询到聊天群组,则直接返回已存在的聊天群组
id
。如果查询不到相关的聊天群组,则会新建聊天群组并返回聊天群组id
。
响应:
- 成功时返回:
{
"code": 1,
"data": {
"cid": "xxxxxxxx"
},
"message": "success",
"success": true
}-
cid
:聊天群组Id,聊天群组的唯一标识符,在初始化或切换聊天群组时使用。 -
失败时返回:
{
"code": 0,
"data": null,
"message": "error message",
"success": false
} - 当调用该接口时,系统会优先根据
3. 切换聊天群组:
该方法用于切换 ChatWidget 中的聊天群组。
注意:在初始化 ChatWidget 后,需要调用一次 setConversation
才能够进入聊天群组页面:
DeBoxChatWidget.setConversation(conversationId);
参数:
conversationId
:string
,聊天群组 ID,可以通过 DeBox 开放平台 API 获取。详见上方
4. 监听 ChatWidget 事件:
该方法用于监听 ChatWidget 的事件。开发者可在需要时作对应处理。
DeBoxChatWidget.addEventListener(e => {
// handle events
});
5. 销毁 ChatWidget:
该方法用于销毁 ChatWidget 元素。
DeBoxChatWidget.destroy();
集成示例
我们提供了一个简单的开发示例作为参考,方便开发者快速集成 ChatWidget 到自己的项目中。
原生 HTML 集成示例:
0. 在线示例:
1. 引入 ChatWidget 插件:
本示例中,通过 umd 文件形式引入 ChatWidget 插件:
<script src="./ChatWidget.umd.js"></script>
2. 初始化聊天插件:
本示例中,在网页加载时初始化 DeBox ChatWidget。
<script>
window.DeBoxChatWidget.init({
projectId: getQueryVariable('projectId'),
zIndex: '9999',
containerDomId: 'test-dom',
});
</script>
3. 获取页面对应聊天群组 ID:
开发者在某个特定代币的网页上,可以通过调用 DeBox 开放平台的 API 获取该代币对应聊天群组Id(详见上文)。
本示例中,ChatWidget 默认打开时的群组使用 url 中信息对应的聊天群组。在页面初始化时,使用 url 中的代币信息、群组信息,通过调用后端封装好 DeBox 开放平台服务的私有接口,获取代币对应的聊天群组 ID:
代码示例:
<script>
// Check for input parameters in the URL and initialize Conversation ID if present
async function initializeConversationId() {
const groupName = getQueryVariable('group_name');
const chainId = getQueryVariable('chain_id');
const contractAddress = getQueryVariable('contract_address');
if (groupName || chainId || contractAddress) {
const groupParam = groupName ? encodeURIComponent(groupName) : '';
const chainParam = chainId ? encodeURIComponent(chainId) : '';
const addressParam = contractAddress ? encodeURIComponent(contractAddress) : '';
// use your own backend endpoint:
const url = `https://xxxx.xxx/getconversationid?group_name=${groupParam}&chain_id=${chainParam}&contract_address=${addressParam}`;
try {
const response = await fetch(url);
const data = await response.json();
if (data.success) {
const conversationId = data.data.cid;
DeBoxChatWidget.setConversation(conversationId);
} else {
console.warn("Failed to retrieve Conversation ID during initialization");
}
} catch (error) {
console.error("Error fetching Conversation ID during initialization", error);
}
}
}
</script>
本示例中亦提供了手动获取聊天群组信息的入口,可使用网页输入的目标代币信息、群组信息,获取聊天群组 ID,在需要时可切换到对应聊天:
代码示例:
<h3>Get Conversation ID using given parameters:</h3>
<div>
<input type="text" id="groupName" placeholder="Group Name" />
<input type="text" id="chainId" placeholder="Chain ID" />
<input type="text" id="contractAddress" placeholder="Contract Address" />
<button onclick="getConversationId()">Get Conversation ID</button>
</div>
<script>
async function getConversationId() {
const groupName = document.getElementById('groupName').value;
const chainId = document.getElementById('chainId').value;
const contractAddress = document.getElementById('contractAddress').value;
// use your own backend endpoint:
const url = `https://xxxx.xxx/getconversationid?group_name=${groupParam}&chain_id=${chainParam}&contract_address=${addressParam}`;
if (!groupParam && !chainParam && !addressParam) {
alert("Please fill in at least one field.");
return;
}
try {
const response = await fetch(url);
const data = await response.json();
if (data.success) {
const conversationId = data.data.cid;
document.getElementById('conversationInput').value = conversationId;
alert(`Conversation ID: ${conversationId}`);
} else {
alert("Failed to get conversation ID");
}
} catch (error) {
alert("Error fetching conversation ID");
console.error(error);
}
}
</script>
-
注意:由于获取聊天群组 Id 的 API 调用会涉及 DeBox 开放平台的
X-API-KEY
、app_secret
,建议开发者将该 API 调用的实现放置在私有后端服务器。 -
在本示例中,开发者将
X-API-KEY
、app_secret
等敏感信息的使用放在私有的后端服务器,并创建了一个私有接口供前端调用,以保证隐私信息不被泄露。
服务器端私有接口代码示例:
package main
import (
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"github.com/redis/go-redis/v9"
"golang.org/x/net/context"
)
var (
rdb *redis.Client
)
func initializeRedis() *redis.Client {
redisAddr := os.Getenv("REDIS_ADDR")
redisPassword := os.Getenv("REDIS_PASSWORD")
redisDB, err := strconv.Atoi(os.Getenv("REDIS_DB"))
if err != nil {
fmt.Println("Invalid REDIS_DB value in .env file:", err)
return nil
}
rdb = redis.NewClient(&redis.Options{
Addr: redisAddr,
Password: redisPassword,
DB: redisDB,
})
// Check the redis connection
err = rdb.Ping(context.Background()).Err()
if err != nil {
fmt.Println("Redis connection failed:", err)
return nil
}
return rdb
}
func calculateSignature(appSecret, nonce, timestamp string) string {
h := sha1.New()
h.Write([]byte(appSecret + nonce + timestamp))
return hex.EncodeToString(h.Sum(nil))
}
func main() {
// Load environment variables from .env.local file
if err := godotenv.Load(".env.local"); err != nil {
fmt.Println("Error loading .env file")
}
appSecret := os.Getenv("APP_SECRET")
apiKey := os.Getenv("API_KEY")
appId := os.Getenv("APP_ID")
port := os.Getenv("PORT")
redisExpiryTime := os.Getenv("REDIS_EXPIRY_TIME")
redisExpiryDuration, err := time.ParseDuration(redisExpiryTime)
if err != nil {
fmt.Println("Invalid REDIS_EXPIRY_TIME value in .env file:", err)
return
}
// Initialize Redis
rdb := initializeRedis()
if rdb == nil {
fmt.Println("Redis initialization failed, exiting")
return
}
fmt.Println("Redis initialized")
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
// Define a route to get conversation ID
r.GET("/getconversationid", func(c *gin.Context) {
ctx := c.Request.Context()
// Get parameters from the query string
groupName := c.Query("group_name")
chainID := c.Query("chain_id")
contractAddress := c.Query("contract_address")
if groupName == "" && chainID == "" && contractAddress == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required parameters", "success": false})
return
}
// Set request URL
url := "http://open.debox.pro/openapi/chatwidget/conversation/id"
method := "POST"
nonce := fmt.Sprintf("%08d", rand.Intn(100000000))
timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
signature := calculateSignature(appSecret, nonce, timestamp)
// Construct request payload
payloadMap := make(map[string]interface{})
if groupName != "" {
payloadMap["group_name"] = groupName
}
if chainID != "" {
chainIDInt, err := strconv.Atoi(chainID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid chain_id format", "success": false})
return
}
payloadMap["chain_id"] = chainIDInt
}
if contractAddress != "" {
payloadMap["contract_address"] = contractAddress
}
// Convert payload to JSON
payloadBytes, err := json.Marshal(payloadMap)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to construct request payload", "success": false})
return
}
// Redis cache key
cacheKey := fmt.Sprintf("conversation:%s:%s:%s", groupName, chainID, contractAddress)
// Check if the response is already cached
cachedResponse, err := rdb.Get(ctx, cacheKey).Result()
if err == nil {
// Cache hit, return cached data
c.Data(http.StatusOK, "application/json", []byte(cachedResponse))
return
} else if err != redis.Nil {
// Redis error
c.JSON(http.StatusInternalServerError, gin.H{"error": "server Redis error", "success": false})
return
}
// Make the request to Debox Open API
client := &http.Client{}
req, err := http.NewRequest(method, url, strings.NewReader(string(payloadBytes)))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Add headers
req.Header.Add("app_id", appId)
req.Header.Add("X-API-KEY", apiKey)
req.Header.Add("nonce", nonce)
req.Header.Add("timestamp", timestamp)
req.Header.Add("signature", signature)
req.Header.Add("Content-Type", "application/json")
// Send request
res, err := client.Do(req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Cache the response for future use
err = rdb.Set(ctx, cacheKey, body, redisExpiryDuration).Err()
if err != nil {
fmt.Println("Failed to cache the response:", err)
}
c.Data(res.StatusCode, "application/json", body)
})
// Start the server
fmt.Println("Server is running on port " + port)
r.Run(":" + port)
}
4. 初始化聊天:
在获取聊天群id后,需手动调用 setConversation
函数,方能进入聊天群组页:
<script>
window.DeBoxChatWidget.setConversation('1f2IELs0');
</script>
5. 集成完成,用户打开 ChatWidget 即可进行聊天。
在聊天插件成功进行初始化后,用户即可在对应的代币聊天群中观看持有代币者的聊天。
用户在未链接钱包时,也可查看该持仓聊天群组中一定数量的消息。若用户希望在该群组中发言,则需使用持有该代币的钱包链接插件。

6. 切换聊天群组:
本示例中,用户可通过输入目标聊天群的群组id,切换到对应聊天群。
实际开发过程中,开发者应在用户需要时获取对应代币聊天群的群组id并切换 ChatWidget 到对应聊天群中。
<script>
function setConversationFromInput() {
const inputValue = document.getElementById('conversationInput').value;
window.DeBoxChatWidget.setConversation(inputValue);
}
</script>
7. 监听插件事件:
本示例中,监听了Debox ChatWidget 的事件并输出。
实际开发过程中,开发者可在需要时监听事件并作对应处理。
<script>
window.DeBoxChatWidget.addEventListener(e => {
const events = document.getElementById('events');
const li = document.createElement('li');
li.innerHTML = JSON.stringify(e.detail);
events.appendChild(li);
});
</script>
8. 销毁组件:
本示例中,提供了销毁 ChatWidget 的按钮,用户可点击按钮销毁 ChatWidget。
<script>
window.DeBoxChatWidget.destroy();
</script>
完整示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>HTML UMD Example</title>
<meta name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
</head>
<body style="margin: 0">
<div id="app" style="min-height: 100vh; width: 100vw; word-wrap: break-word">
<main>
<!-- Plugin load position -->
<div id="test-dom">
<h2>This is the dApp page...</h2>
</div>
<br />
<h3>Retrieve Conversation ID based on input parameters:</h3>
<div>
<input type="text" id="groupName" placeholder="Group Name" />
<input type="text" id="chainId" placeholder="Chain ID" />
<input type="text" id="contractAddress" placeholder="Contract Address" />
<button onclick="getConversationId()">Get Conversation ID</button>
</div>
<h3>Input Conversation ID to change the chat:</h3>
<div>
<input type="text" id="conversationInput" placeholder="Enter conversation ID here" />
<button onclick="setConversationFromInput()">Change Conversation</button>
</div>
<h3>Destroy the plugin:</h3>
<button onclick="window.DeBoxChatWidget.destroy()">Destroy</button>
<br />
<h3>Event Log:</h3>
<div id="events"></div>
</main>
<br />
</div>
<script src="./index.umd.js"></script>
<script>
const { DeBoxChatWidget } = window.DeBoxChatWidget;
function getQueryVariable(variable) {
return new URL(window.location.href).searchParams.get(variable);
}
// Initialize plugin
DeBoxChatWidget.init({
projectId: getQueryVariable('project_id'),
zIndex: '9999',
containerDomId: 'test-dom',
});
// Check for input parameters in the URL and initialize Conversation ID if present
async function initializeConversationId() {
const groupName = getQueryVariable('group_name');
const chainId = getQueryVariable('chain_id');
const contractAddress = getQueryVariable('contract_address');
if (groupName || chainId || contractAddress) {
const groupParam = groupName ? encodeURIComponent(groupName) : '';
const chainParam = chainId ? encodeURIComponent(chainId) : '';
const addressParam = contractAddress ? encodeURIComponent(contractAddress) : '';
// use your own backend endpoint:
const url = `https://xxxx.xxx/getconversationid?group_name=${groupParam}&chain_id=${chainParam}&contract_address=${addressParam}`;
try {
const response = await fetch(url);
const data = await response.json();
if (data.success) {
const conversationId = data.data.cid;
DeBoxChatWidget.setConversation(conversationId);
} else {
console.warn("Failed to retrieve Conversation ID during initialization");
}
} catch (error) {
console.error("Error fetching Conversation ID during initialization", error);
}
}
}
// Execute initialization logic
initializeConversationId();
// Change the conversation
function setConversationFromInput() {
const inputValue = document.getElementById('conversationInput').value;
if (inputValue) {
DeBoxChatWidget.setConversation(inputValue);
} else {
alert('Please enter a valid Conversation ID');
}
}
// Fetch Conversation ID based on inputs
async function getConversationId() {
const groupName = document.getElementById('groupName').value;
const chainId = document.getElementById('chainId').value;
const contractAddress = document.getElementById('contractAddress').value;
const groupParam = groupName ? encodeURIComponent(groupName) : '';
const chainParam = chainId ? encodeURIComponent(chainId) : '';
const addressParam = contractAddress ? encodeURIComponent(contractAddress) : '';
if (!groupParam && !chainParam && !addressParam) {
alert("Please fill in at least one field.");
return;
}
// use your own backend endpoint:
const url = `https://xxxx.xxx/getconversationid?group_name=${groupParam}&chain_id=${chainParam}&contract_address=${addressParam}`;
try {
const response = await fetch(url);
const data = await response.json();
if (data.success) {
const conversationId = data.data.cid;
document.getElementById('conversationInput').value = conversationId;
alert(`Conversation ID: ${conversationId}`);
} else {
alert("Failed to get Conversation ID");
}
} catch (error) {
alert("Error fetching Conversation ID");
console.error(error);
}
}
// Listen for plugin events
DeBoxChatWidget.addEventListener((e) => {
const events = document.getElementById('events');
const li = document.createElement('li');
li.innerHTML = JSON.stringify(e.detail);
events.appendChild(li);
});
</script>
</body>
</html>