跳到主要内容

DeBox ChatWidget

DeBox ChatWidget 持仓聊天浮窗

功能概述

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

ChatWidget Example

安装集成

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 文件,并保存到您的项目目录中:

    [unpkg] [jsdelivr] [github file]

  • 步骤 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',
})

参数:

  • projectIdstring,必传,DeBox 开放平台提供的app ID,需在 DeBox 开放平台 申请。
  • containerDomIdstring,可传,指定该插件的容器 DOM 元素 ID,用于样式隔离。
  • zIndexstring,可传,默认值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_idString开发者应用标识,需在 DeBox 开放平台获取。
    X-API-KEYString开发者 API 密钥,需在 DeBox 开放平台获取。
    nonceString开发者生成的随机数,长度为 8 位。
    timestampString时间戳,精确到毫秒。
    signatureString签名,signature = SHA1( { app_secret } { nonce } { timestamp } )
    • 计算 signature 时使用的 app_secret 是开发者应用密钥,需在 DeBox 开放平台获取

    query:

    query 参数支持通过链与合约信息或群组名称来获取对应聊天群组Id:

    • chain_idnumber,可选,链 ID,例如:1 表示以太坊。
    • contract_addressstring,可选,聊天群组对应代币的合约地址。
    • group_namestring,可选,群组名称,用于没有代币合约地址的情况。
  • 查询聊天群组过程:

    1. 当调用该接口时,系统会优先根据 chain_idcontract_address 查询对应的聊天群组。
    2. 如果没有chain_idcontract_address信息,则使用 group_name 进行查询。
    3. 如果能够查询到聊天群组,则直接返回已存在的聊天群组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);

参数:

  • conversationIdstring,聊天群组 ID,可以通过 DeBox 开放平台 API 获取。详见上方

4. 监听 ChatWidget 事件:

该方法用于监听 ChatWidget 的事件。开发者可在需要时作对应处理。

DeBoxChatWidget.addEventListener(e => {
// handle events
});

5. 销毁 ChatWidget:

该方法用于销毁 ChatWidget 元素。

DeBoxChatWidget.destroy();

集成示例

我们提供了一个简单的开发示例作为参考,方便开发者快速集成 ChatWidget 到自己的项目中。

网页集成在线示例(点击下方链接):

原生 HTML 集成示例:

0. 在线示例

[html-umd 集成示例]

[html-npm 集成示例]

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-KEYapp_secret,建议开发者将该 API 调用的实现放置在私有后端服务器。

  • 在本示例中,开发者将 X-API-KEYapp_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 即可进行聊天。

在聊天插件成功进行初始化后,用户即可在对应的代币聊天群中观看持有代币者的聊天。

用户在未链接钱包时,也可查看该持仓聊天群组中一定数量的消息。若用户希望在该群组中发言,则需使用持有该代币的钱包链接插件。

ChatWidget Example

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>