# Add Channel Subscribers Source: https://wukong.mintlify.app/en/api/channel/add-subscribers POST /channel/subscriber_add Add subscribers (members) to a channel ## Overview Add subscribers (members) to a channel, supporting batch addition and reset mode. ## Request Body ### Required Parameters Channel ID Channel type * `1` - Personal channel * `2` - Group channel List of subscriber user IDs to add User ID ### Optional Parameters Whether to reset existing subscribers * `0` - Do not reset, append new subscribers * `1` - Reset, replace all existing subscribers Whether as temporary subscriber * `0` - Permanent subscriber * `1` - Temporary subscriber ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel/subscriber_add" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "subscribers": ["user4", "user5", "user6"], "reset": 0, "temp_subscriber": 0 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/subscriber_add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, subscribers: ['user4', 'user5', 'user6'], reset: 0, temp_subscriber: 0 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "group123", "channel_type": 2, "subscribers": ["user4", "user5", "user6"], "reset": 0, "temp_subscriber": 0 } response = requests.post('http://localhost:5001/channel/subscriber_add', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "subscribers": []string{"user4", "user5", "user6"}, "reset": 0, "temp_subscriber": 0, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/subscriber_add", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ------------------------------ | | 200 | Subscribers added successfully | | 400 | Request parameter error | | 403 | No operation permission | | 404 | Channel does not exist | | 500 | Internal server error | ## Parameter Description ### Reset Mode (reset) | Value | Mode | Description | Use Case | | ----- | ----------- | ---------------------------------------------- | -------------------------- | | 0 | Append mode | Add new members to existing members | Invite new members to join | | 1 | Reset mode | Clear existing members, set as new member list | Rebuild group | ### Temporary Subscriber (temp\_subscriber) | Value | Type | Features | Applicable Scenario | | ----- | -------------------- | ------------------------------ | ---------------------------------------- | | 0 | Permanent subscriber | Persistent member relationship | Official group members | | 1 | Temporary subscriber | Temporary member relationship | Temporary visitors, meeting participants | ## Use Cases ### Group Management * **Invite new members**: Group owner or admin invites new users to join the group * **Batch import**: Batch import member lists from other platforms * **Rebuild group**: Use reset mode to rebuild group members # Add Channel Blacklist Source: https://wukong.mintlify.app/en/api/channel/blacklist POST /channel/blacklist_add Add users to channel blacklist ## Overview Add users to channel blacklist. Users added to the blacklist will be unable to join the channel or send messages. ## Request Body ### Required Parameters Channel ID Channel type * `1` - Personal channel * `2` - Group channel List of user IDs to add to blacklist User ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel/blacklist_add" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["user456", "user789"] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/blacklist_add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, uids: ['user456', 'user789'] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "group123", "channel_type": 2, "uids": ["user456", "user789"] } response = requests.post('http://localhost:5001/channel/blacklist_add', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "uids": []string{"user456", "user789"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/blacklist_add", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ------------------------------ | | 200 | Blacklist operation successful | | 400 | Request parameter error | | 403 | No management permission | | 404 | Channel does not exist | | 500 | Internal server error | ## Blacklist Mechanism ### Restriction Scope Users added to the blacklist will face the following restrictions: | Operation | Restriction Effect | Description | | ------------- | ------------------ | ------------------------------------------ | | Send Messages | Prohibited | Cannot send any messages in the channel | | Join Channel | Blocked | Cannot join or rejoin the channel | | View Messages | Limited | May have limited access to channel content | ### Permission Hierarchy Blacklist has higher priority than other permission settings: 1. **Blacklist** > Whitelist > Regular member permissions 2. **Administrator permissions** > Blacklist (Administrators are not restricted by blacklist) 3. **System users** > Blacklist (System users are not restricted by blacklist) ## Use Cases ### Violation Handling **Handle Content Violations**: ```javascript theme={null} // Add users to blacklist for content violations async function handleContentViolation(channelId, channelType, violatingUsers, reason) { try { // Add to blacklist await addToBlacklist({ channel_id: channelId, channel_type: channelType, uids: violatingUsers }); // Log violation await logViolation({ channel_id: channelId, channel_type: channelType, violating_users: violatingUsers, reason: reason, action: 'blacklisted', timestamp: new Date().toISOString() }); // Notify users about blacklisting for (const userId of violatingUsers) { await notifyUser(userId, { type: 'blacklisted', channel_id: channelId, reason: reason, appeal_process: 'Contact administrators to appeal' }); } console.log(`Added ${violatingUsers.length} users to blacklist for: ${reason}`); } catch (error) { console.error('Failed to handle content violation:', error); } } // Usage await handleContentViolation( 'group123', 2, ['user456', 'user789'], 'Inappropriate content sharing' ); ``` ### Spam and Abuse Prevention **Anti-Spam Management**: ```javascript theme={null} // Automated spam detection and blacklisting class SpamDetector { constructor(channelId, channelType) { this.channelId = channelId; this.channelType = channelType; this.messageHistory = new Map(); this.spamThresholds = { messagesPerMinute: 10, duplicateMessages: 3, linkSpamCount: 5 }; } async analyzeMessage(userId, messageContent) { const now = Date.now(); const userHistory = this.messageHistory.get(userId) || { messages: [], duplicates: new Map(), linkCount: 0 }; // Add current message userHistory.messages.push({ content: messageContent, timestamp: now }); // Clean old messages (older than 1 minute) userHistory.messages = userHistory.messages.filter( msg => now - msg.timestamp < 60000 ); // Check for spam patterns const spamDetected = this.detectSpamPatterns(userHistory, messageContent); if (spamDetected.isSpam) { await this.handleSpamUser(userId, spamDetected.reasons); } this.messageHistory.set(userId, userHistory); } detectSpamPatterns(userHistory, messageContent) { const reasons = []; // Check message frequency if (userHistory.messages.length > this.spamThresholds.messagesPerMinute) { reasons.push('High message frequency'); } // Check for duplicate messages const duplicateCount = userHistory.messages.filter( msg => msg.content === messageContent ).length; if (duplicateCount > this.spamThresholds.duplicateMessages) { reasons.push('Duplicate message spam'); } // Check for link spam const linkCount = (messageContent.match(/https?:\/\/\S+/g) || []).length; if (linkCount > 2) { userHistory.linkCount += linkCount; if (userHistory.linkCount > this.spamThresholds.linkSpamCount) { reasons.push('Link spam'); } } return { isSpam: reasons.length > 0, reasons: reasons }; } async handleSpamUser(userId, reasons) { try { // Add to blacklist await addToBlacklist({ channel_id: this.channelId, channel_type: this.channelType, uids: [userId] }); // Log spam incident await logSpamIncident({ channel_id: this.channelId, user_id: userId, reasons: reasons, action: 'auto_blacklisted', timestamp: new Date().toISOString() }); console.log(`Auto-blacklisted user ${userId} for spam: ${reasons.join(', ')}`); } catch (error) { console.error(`Failed to blacklist spam user ${userId}:`, error); } } } // Usage const spamDetector = new SpamDetector('group123', 2); // Analyze incoming messages await spamDetector.analyzeMessage('user456', 'Buy now! Click here: http://spam.com'); ``` ### Moderation Tools **Advanced Moderation System**: ```javascript theme={null} // Comprehensive moderation system class ChannelModerator { constructor(channelId, channelType) { this.channelId = channelId; this.channelType = channelType; this.moderationRules = { profanityFilter: true, linkRestriction: true, capsLockLimit: 0.7, // 70% caps lock threshold mentionSpamLimit: 5 }; } async moderateUser(userId, violations) { const severity = this.calculateViolationSeverity(violations); switch (severity) { case 'low': await this.issueWarning(userId, violations); break; case 'medium': await this.temporaryRestriction(userId, violations); break; case 'high': await this.addToBlacklist(userId, violations); break; } } calculateViolationSeverity(violations) { const severityScores = { 'profanity': 2, 'spam': 3, 'harassment': 4, 'inappropriate_content': 3, 'link_spam': 2, 'caps_abuse': 1 }; const totalScore = violations.reduce((sum, violation) => sum + (severityScores[violation.type] || 1), 0 ); if (totalScore >= 6) return 'high'; if (totalScore >= 3) return 'medium'; return 'low'; } async addToBlacklist(userId, violations) { try { await addToBlacklist({ channel_id: this.channelId, channel_type: this.channelType, uids: [userId] }); // Create moderation record await createModerationRecord({ channel_id: this.channelId, user_id: userId, action: 'blacklisted', violations: violations, moderator: 'system', timestamp: new Date().toISOString(), appeal_deadline: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() }); // Notify user await notifyUser(userId, { type: 'blacklisted', channel_id: this.channelId, violations: violations, appeal_process: 'You can appeal this decision within 7 days' }); console.log(`User ${userId} blacklisted for violations:`, violations); } catch (error) { console.error(`Failed to blacklist user ${userId}:`, error); } } async issueWarning(userId, violations) { await createModerationRecord({ channel_id: this.channelId, user_id: userId, action: 'warning', violations: violations, moderator: 'system', timestamp: new Date().toISOString() }); await notifyUser(userId, { type: 'warning', channel_id: this.channelId, violations: violations, message: 'Please follow channel guidelines' }); } async temporaryRestriction(userId, violations) { // Implement temporary restriction logic // This might involve a separate temporary blacklist system console.log(`Temporary restriction for user ${userId}:`, violations); } } ``` ### Batch Blacklist Management **Bulk Blacklist Operations**: ```javascript theme={null} // Manage blacklists in bulk async function bulkBlacklistManagement(operations) { const results = []; for (const operation of operations) { try { switch (operation.action) { case 'add': await addToBlacklist({ channel_id: operation.channelId, channel_type: operation.channelType, uids: operation.userIds }); break; case 'remove': await removeFromBlacklist({ channel_id: operation.channelId, channel_type: operation.channelType, uids: operation.userIds }); break; } results.push({ operation: operation, success: true }); } catch (error) { results.push({ operation: operation, success: false, error: error.message }); } } return results; } // Usage const operations = [ { action: 'add', channelId: 'group123', channelType: 2, userIds: ['spammer1', 'spammer2'], reason: 'Spam detection' }, { action: 'remove', channelId: 'group456', channelType: 2, userIds: ['reformed_user'], reason: 'Appeal approved' } ]; const results = await bulkBlacklistManagement(operations); console.log('Bulk operation results:', results); ``` ## Best Practices 1. **Clear Policies**: Establish clear guidelines for blacklisting users 2. **Documentation**: Document all blacklist actions with reasons 3. **Appeal Process**: Provide a clear appeal process for blacklisted users 4. **Graduated Response**: Use warnings before blacklisting when appropriate 5. **Regular Review**: Periodically review blacklists and remove outdated entries 6. **Notification**: Inform users when they are blacklisted and why 7. **Audit Trail**: Maintain detailed logs of all blacklist operations # Remove Channel Blacklist Source: https://wukong.mintlify.app/en/api/channel/blacklist-remove POST /channel/blacklist_remove Remove specific users from channel blacklist ## Overview Remove specific users from channel blacklist. Removed users will regain normal permissions and can send and receive channel messages again. Related conversations will be restored if applicable. ## Request Body ### Required Parameters Channel ID, cannot be empty or contain special characters Channel type * `1` - Person channel * `2` - Group channel List of user IDs to remove from blacklist User ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel/blacklist_remove" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2"] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/blacklist_remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, uids: ['user1', 'user2'] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2"] } response = requests.post('http://localhost:5001/channel/blacklist_remove', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "uids": []string{"user1", "user2"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/blacklist_remove", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ----------------------------------------- | | 200 | Users removed from blacklist successfully | | 400 | Request parameter error | | 403 | No management permission | | 404 | Channel does not exist | | 500 | Internal server error | ## Functionality ### Remove Operation The remove operation performs the following steps: 1. **Verify Users**: Check if specified users are in the blacklist 2. **Remove Entries**: Delete specified users from channel blacklist 3. **Restore Permissions**: Users regain ability to send and receive messages 4. **Restore Conversations**: Restore user's related conversations if applicable ### Permission Restoration Removed users will regain: | Permission | Description | Effective Time | | ------------------- | -------------------------------------------------- | -------------- | | Send Messages | Can send messages to the channel | Immediately | | Receive Messages | Can receive channel messages | Immediately | | Conversation Access | Restore channel conversations (if applicable) | Immediately | | Normal Interaction | Restore all normal channel interaction permissions | Immediately | ## Special Cases ### Live Channels * **No Conversation Restoration**: Live channels do not automatically restore conversations * **Permissions Take Effect Immediately**: Users can still immediately regain send and receive permissions ### Person Channels * **Blacklist Not Supported**: Person channels do not support blacklist operations * **Returns Error**: Attempting operations will return corresponding error messages ## Use Cases ### Lift Mistaken Ban ```bash theme={null} # Remove users mistakenly added to blacklist curl -X POST "/channel/blacklist_remove" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["innocent_user"] }' ``` ### Batch Restoration ```bash theme={null} # Batch remove multiple users curl -X POST "/channel/blacklist_remove" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2", "user3"] }' ``` ### Temporary Unban ```bash theme={null} # Temporarily lift user restrictions curl -X POST "/channel/blacklist_remove" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["temp_banned_user"] }' ``` ## Best Practices 1. **Permission Verification**: Ensure operator has sufficient management permissions 2. **Operation Recording**: Record all blacklist change operations 3. **Notification Mechanism**: Notify relevant administrators and users 4. **Progressive Unbanning**: For serious violators, consider progressive permission restoration 5. **Monitoring Mechanism**: Continue monitoring user behavior after removal 6. **Backup Strategy**: Backup current blacklist state before bulk operations ## Error Handling ### Common Errors | Error Message | Cause | Solution | | -------------------------- | ------------------------------- | ----------------------------------- | | Channel ID cannot be empty | No channel ID provided | Ensure valid channel ID is provided | | Channel type cannot be 0 | Invalid channel type | Use valid channel type (1 or 2) | | uids cannot be empty | No user list provided | Provide list of user IDs to remove | | Remove blacklist failed | Remove operation failed | Check if users are in blacklist | | Add conversation failed | Conversation restoration failed | Check conversation system status | ## Related APIs * [Add Channel Blacklist](/en/api/channel/blacklist) - Add users to blacklist * [Set Channel Blacklist](/en/api/channel/blacklist-set) - Set complete blacklist * [Add Channel Whitelist](/en/api/channel/whitelist) - Manage whitelist users * [Remove Channel Whitelist](/en/api/channel/whitelist-remove) - Remove whitelist users # Set Channel Blacklist Source: https://wukong.mintlify.app/en/api/channel/blacklist-set POST /channel/blacklist_set Set (replace) the entire blacklist for a channel ## Overview Set (replace) the entire blacklist for a channel. This operation will first remove all existing blacklist entries and then add new ones. If UIDs is empty, the entire blacklist will be cleared. ## Request Body ### Required Parameters Channel ID, cannot be empty or contain special characters Channel type * `1` - Person channel * `2` - Group channel ### Optional Parameters List of user IDs to set as blacklist. If empty, clears the entire blacklist. User ID ```bash Set Blacklist theme={null} curl -X POST "http://localhost:5001/channel/blacklist_set" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2", "user3"] }' ``` ```bash Clear Blacklist theme={null} curl -X POST "http://localhost:5001/channel/blacklist_set" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "uids": [] }' ``` ```javascript JavaScript theme={null} // Set blacklist const response = await fetch('http://localhost:5001/channel/blacklist_set', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, uids: ['user1', 'user2', 'user3'] }) }); const data = await response.json(); console.log(data); // Clear blacklist const clearResponse = await fetch('http://localhost:5001/channel/blacklist_set', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, uids: [] }) }); ``` ```python Python theme={null} import requests # Set blacklist data = { "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2", "user3"] } response = requests.post('http://localhost:5001/channel/blacklist_set', json=data) result = response.json() print(result) # Clear blacklist clear_data = { "channel_id": "group123", "channel_type": 2, "uids": [] } clear_response = requests.post('http://localhost:5001/channel/blacklist_set', json=clear_data) clear_result = clear_response.json() print(clear_result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { // Set blacklist data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "uids": []string{"user1", "user2", "user3"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/blacklist_set", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) // Clear blacklist clearData := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "uids": []string{}, } clearJsonData, _ := json.Marshal(clearData) clearResp, err := http.Post( "http://localhost:5001/channel/blacklist_set", "application/json", bytes.NewBuffer(clearJsonData), ) if err != nil { panic(err) } defer clearResp.Body.Close() var clearResult map[string]interface{} json.NewDecoder(clearResp.Body).Decode(&clearResult) fmt.Printf("%+v\n", clearResult) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | -------------------------- | | 200 | Blacklist set successfully | | 400 | Request parameter error | | 403 | No management permission | | 404 | Channel does not exist | | 500 | Internal server error | ## Functionality ### Set Operation The set operation performs the following steps: 1. **Clear Existing Blacklist**: Remove all existing blacklist entries for the channel 2. **Add New Entries**: Add the provided user ID list to the blacklist 3. **Delete Conversations**: Automatically delete related conversations for blacklisted users 4. **Update Permissions**: Take effect immediately, blacklisted users cannot send or receive messages ### Clear Operation When `uids` is an empty array: * Clear all blacklist entries for the channel * Restore normal permissions for all users * Does not affect existing conversations ## Blacklist Mechanism ### Permission Restrictions Blacklisted users are subject to the following restrictions: | Restriction | Description | Scope | | ----------------------- | ------------------------------------------------ | -------------------- | | Cannot Send Messages | Blocked from sending any messages to the channel | All message types | | Cannot Receive Messages | Will not receive messages from the channel | All channel messages | | Conversation Deletion | Related conversations are automatically deleted | Except live channels | | Permission Revocation | Lose all channel-related permissions | Complete isolation | ### Special Cases * **Live Channels**: Do not process conversation deletion * **Person Channels**: Do not support blacklist operations * **System Users**: May have special permissions, not affected by blacklist ## Use Cases ### Batch Management ```bash theme={null} # Set new blacklist, replacing all existing entries curl -X POST "/channel/blacklist_set" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["spammer1", "spammer2", "violator1"] }' ``` ### Clear Blacklist ```bash theme={null} # Clear all blacklist, restore all user permissions curl -X POST "/channel/blacklist_set" -d '{ "channel_id": "group123", "channel_type": 2, "uids": [] }' ``` ### Reset Blacklist ```bash theme={null} # Completely reset blacklist to new user list curl -X POST "/channel/blacklist_set" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["new_blocked_user"] }' ``` ## Comparison with Other Blacklist Operations | Operation | Function | Use Case | | ------------------ | ------------------------------------ | ----------------------------------- | | `blacklist_add` | Add users to existing blacklist | Incrementally add violating users | | `blacklist_set` | Replace entire blacklist | Batch management, reset blacklist | | `blacklist_remove` | Remove specific users from blacklist | Lift restrictions on specific users | ## Best Practices 1. **Use Carefully**: Set operation clears all existing blacklist, ensure this is intended behavior 2. **Backup Existing Data**: Get current blacklist before setting as backup 3. **Permission Verification**: Ensure operator has sufficient management permissions 4. **Log Recording**: Record blacklist changes for auditing 5. **Notification Mechanism**: Consider notifying relevant administrators of blacklist changes 6. **Test Validation**: Thoroughly test before using in production environment ## Error Handling ### Common Errors | Error Message | Cause | Solution | | -------------------------------------------- | ------------------------- | ------------------------------------------- | | Channel ID cannot be empty | No channel ID provided | Ensure valid channel ID is provided | | Channel type cannot be 0 | Invalid channel type | Use valid channel type (1 or 2) | | Channel ID cannot contain special characters | Invalid channel ID format | Use alphanumeric characters and underscores | | Remove all blacklist failed | Clear operation failed | Check channel status and permissions | | Add blacklist failed | Add operation failed | Check user ID validity | ## Related APIs * [Add Channel Blacklist](/en/api/channel/blacklist) - Incrementally add blacklist users * [Remove Channel Blacklist](/en/api/channel/blacklist-remove) - Remove specific blacklist users * [Add Channel Whitelist](/en/api/channel/whitelist) - Manage whitelist users # Create Channel Source: https://wukong.mintlify.app/en/api/channel/create POST /channel Create new chat channels ## Overview Create new chat channels, supporting both personal and group channel creation. ## Request Body ### Required Parameters Channel ID, must be unique Channel type * `1` - Personal channel * `2` - Group channel ### Optional Parameters Whether to mute * `0` - Allow speaking * `1` - Mute all members Whether to disband channel * `1` - Disband channel (irreversible) Whether to prohibit sending messages (0=not prohibited, 1=prohibited). When prohibited, all members in the channel cannot send messages. Personal channels can receive messages but cannot send messages. Whether to allow strangers to send messages (0=not allowed, 1=allowed) (this configuration currently only supports personal channels) Personal channel: If AllowStranger is 1, strangers can send messages to the current user. For example: if the current account needs to accept stranger messages, channel\_id is the current user's uid Subscriber list Subscriber user ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "large": 0, "ban": 0, "subscribers": ["user1", "user2", "user3"] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, large: 0, ban: 0, subscribers: ['user1', 'user2', 'user3'] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "group123", "channel_type": 2, "large": 0, "ban": 0, "subscribers": ["user1", "user2", "user3"] } response = requests.post('http://localhost:5001/channel', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "large": 0, "ban": 0, "subscribers": []string{"user1", "user2", "user3"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ---------------------------- | | 200 | Channel created successfully | | 400 | Request parameter error | | 409 | Channel ID already exists | | 500 | Internal server error | ## Best Practices 1. **Channel ID Uniqueness**: Ensure channel ID is unique in the system 2. **Member Management**: Properly set initial subscriber list 3. **Permission Control**: Set mute status as needed # Delete Channel Source: https://wukong.mintlify.app/en/api/channel/delete POST /channel/delete Delete a specified channel ## Overview Delete a specified channel, including all related data and member relationships. ## Request Body ### Required Parameters Channel ID Channel type * `1` - Personal channel * `2` - Group channel ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel/delete" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "group123", "channel_type": 2 } response = requests.post('http://localhost:5001/channel/delete', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/delete", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ---------------------------- | | 200 | Channel deleted successfully | | 400 | Request parameter error | | 403 | No deletion permission | | 404 | Channel does not exist | | 500 | Internal server error | ## Deletion Impact ### Data Cleanup Deleting a channel will clean up the following related data: | Data Type | Cleanup Scope | Impact | | -------------------- | ------------------------------------- | --------------------------------------- | | Channel Info | Channel basic info, settings | Channel completely disappears | | Member Relationships | All member subscription relationships | Members can no longer receive messages | | Message History | All messages in the channel | Message records permanently deleted | | Conversation Records | Channel in user conversation lists | Channel removed from conversation lists | ### User Impact * **Member Notification**: All members will receive channel dissolution notification * **Conversation Cleanup**: Channel removed from all members' conversation lists * **Message Loss**: Message history in the channel will be unrecoverable * **Permission Invalidation**: All channel-related permissions immediately invalidated ## Permission Requirements ### Personal Channel (channel\_type = 1) * **Participants**: Any participant in the channel can delete it * **System Administrator**: Has permission to delete any personal channel ### Group Channel (channel\_type = 2) * **Group Owner**: Only the group owner can dissolve the group * **System Administrator**: Has permission to delete any group * **Regular Members**: No deletion permission, can only leave the group ## Security Considerations ### Deletion Confirmation Recommend secondary confirmation before deletion: 1. **Permission Verification**: Confirm the operator has deletion permission 2. **Identity Verification**: Require password or verification code input 3. **Impact Warning**: Clearly inform the scope of deletion impact 4. **Final Confirmation**: Provide a final chance to cancel ### Data Backup Consider data backup before deletion: * **Message Export**: Allow export of important messages * **Member List**: Save member information for rebuilding * **File Backup**: Backup important files in the channel * **Operation Logging**: Record detailed information about deletion operations ## Alternative Solutions ### Soft Delete For important channels, consider soft deletion: * **Hide Channel**: Hide from user interface but retain data * **Disable Features**: Prohibit sending messages but retain history * **Set Expiration**: Set automatic deletion time * **Permission Revocation**: Remove all member permissions ### Archive Processing * **Message Archive**: Transfer messages to archive storage * **Read-only Mode**: Set to read-only status * **History Viewing**: Allow viewing but no operations * **Periodic Cleanup**: Regularly clean up archived data ## Best Practices 1. **Permission Control**: Strictly control deletion permissions to prevent misoperations 2. **Operation Logging**: Detailed logging of deletion operations including time, operator, reason 3. **Notification Mechanism**: Timely notification to all related members 4. **Data Backup**: Perform data backup before deleting important channels 5. **Recovery Mechanism**: Provide recovery functionality within a certain time period 6. **Batch Deletion**: Support batch deletion of multiple channels # Get Channel Whitelist Source: https://wukong.mintlify.app/en/api/channel/get-whitelist GET /channel/whitelist Get the whitelist user list for a channel ## Overview Get the whitelist user list for a specified channel, returning all user IDs in the whitelist. ## Query Parameters Channel ID Channel type * `1` - Personal channel * `2` - Group channel ```bash cURL theme={null} curl -X GET "http://localhost:5001/channel/whitelist?channel_id=group123&channel_type=2" ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/whitelist?channel_id=group123&channel_type=2'); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests params = { 'channel_id': 'group123', 'channel_type': 2 } response = requests.get('http://localhost:5001/channel/whitelist', params=params) data = response.json() print(data) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "net/http" "net/url" ) func main() { baseURL := "http://localhost:5001/channel/whitelist" params := url.Values{} params.Add("channel_id", "group123") params.Add("channel_type", "2") fullURL := baseURL + "?" + params.Encode() resp, err := http.Get(fullURL) if err != nil { panic(err) } defer resp.Body.Close() var result []string json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} [ "user456", "user789", "user101" ] ``` ```json Empty Whitelist theme={null} [] ``` ## Response Fields List of whitelisted user IDs User ID ## Status Codes | Status Code | Description | | ----------- | -------------------------------- | | 200 | Successfully retrieved whitelist | | 400 | Request parameter error | | 403 | No view permission | | 404 | Channel does not exist | | 500 | Internal server error | ## Use Cases ### Whitelist Management Dashboard **Display Whitelist Members**: ```javascript theme={null} // Create a whitelist management dashboard class WhitelistDashboard { constructor(channelId, channelType) { this.channelId = channelId; this.channelType = channelType; this.whitelistMembers = []; } async loadWhitelist() { try { const response = await fetch( `/channel/whitelist?channel_id=${this.channelId}&channel_type=${this.channelType}` ); this.whitelistMembers = await response.json(); await this.enrichMemberData(); this.renderDashboard(); } catch (error) { console.error('Failed to load whitelist:', error); this.showError('Failed to load whitelist members'); } } async enrichMemberData() { // Get additional user information for each whitelisted member const enrichedMembers = []; for (const userId of this.whitelistMembers) { try { const userInfo = await getUserInfo(userId); enrichedMembers.push({ userId: userId, username: userInfo.username || userId, avatar: userInfo.avatar, joinedWhitelistAt: userInfo.whitelistJoinDate, privileges: await this.getUserPrivileges(userId) }); } catch (error) { // Fallback for users we can't get info for enrichedMembers.push({ userId: userId, username: userId, avatar: null, joinedWhitelistAt: null, privileges: ['basic_whitelist'] }); } } this.enrichedMembers = enrichedMembers; } async getUserPrivileges(userId) { // Get specific privileges for this user const privileges = []; // Check if user has moderator privileges const isModerator = await checkModeratorStatus(this.channelId, userId); if (isModerator) { privileges.push('moderator'); } // Check for VIP status const isVIP = await checkVIPStatus(this.channelId, userId); if (isVIP) { privileges.push('vip'); } // Default whitelist privileges privileges.push('bypass_mute', 'priority_access'); return privileges; } renderDashboard() { const dashboardHTML = `

Channel Whitelist (${this.enrichedMembers.length} members)

${this.renderMemberList()}
`; document.getElementById('whitelist-dashboard').innerHTML = dashboardHTML; } renderMemberList() { return this.enrichedMembers.map(member => `
Avatar
${member.username}
${member.userId}
${member.privileges.join(', ')}
`).join(''); } async addMember() { const userId = prompt('Enter user ID to add to whitelist:'); if (userId) { try { await addToWhitelist({ channel_id: this.channelId, channel_type: this.channelType, uids: [userId] }); await this.loadWhitelist(); // Refresh the list this.showSuccess(`User ${userId} added to whitelist`); } catch (error) { this.showError(`Failed to add user ${userId} to whitelist`); } } } async removeMember(userId) { if (confirm(`Remove ${userId} from whitelist?`)) { try { await removeFromWhitelist({ channel_id: this.channelId, channel_type: this.channelType, uids: [userId] }); await this.loadWhitelist(); // Refresh the list this.showSuccess(`User ${userId} removed from whitelist`); } catch (error) { this.showError(`Failed to remove user ${userId} from whitelist`); } } } async refresh() { await this.loadWhitelist(); this.showSuccess('Whitelist refreshed'); } exportWhitelist() { const exportData = { channel_id: this.channelId, channel_type: this.channelType, whitelist_members: this.enrichedMembers, exported_at: new Date().toISOString(), total_members: this.enrichedMembers.length }; const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `whitelist-${this.channelId}-${Date.now()}.json`; a.click(); URL.revokeObjectURL(url); } showSuccess(message) { console.log(`✓ ${message}`); // Implement UI notification } showError(message) { console.error(`✗ ${message}`); // Implement UI error notification } } // Usage const dashboard = new WhitelistDashboard('group123', 2); dashboard.loadWhitelist(); ``` ### Permission Checking System **Check User Whitelist Status**: ```javascript theme={null} // Comprehensive permission checking system class PermissionChecker { constructor() { this.whitelistCache = new Map(); this.cacheTimeout = 5 * 60 * 1000; // 5 minutes } async isUserWhitelisted(channelId, channelType, userId) { try { const cacheKey = `${channelId}:${channelType}`; const cached = this.whitelistCache.get(cacheKey); // Check cache first if (cached && Date.now() - cached.timestamp < this.cacheTimeout) { return cached.whitelist.includes(userId); } // Fetch fresh whitelist const whitelist = await this.getChannelWhitelist(channelId, channelType); // Update cache this.whitelistCache.set(cacheKey, { whitelist: whitelist, timestamp: Date.now() }); return whitelist.includes(userId); } catch (error) { console.error('Failed to check whitelist status:', error); return false; } } async getChannelWhitelist(channelId, channelType) { const response = await fetch( `/channel/whitelist?channel_id=${channelId}&channel_type=${channelType}` ); return await response.json(); } async checkUserPermissions(channelId, channelType, userId) { const [isWhitelisted, isBlacklisted, isModerator] = await Promise.all([ this.isUserWhitelisted(channelId, channelType, userId), this.isUserBlacklisted(channelId, channelType, userId), this.isUserModerator(channelId, userId) ]); return { userId: userId, isWhitelisted: isWhitelisted, isBlacklisted: isBlacklisted, isModerator: isModerator, effectivePermissions: this.calculateEffectivePermissions({ isWhitelisted, isBlacklisted, isModerator }) }; } calculateEffectivePermissions({ isWhitelisted, isBlacklisted, isModerator }) { if (isBlacklisted && !isModerator) { return { canSendMessage: false, canJoinChannel: false, canBypassMute: false, level: 'restricted' }; } if (isWhitelisted || isModerator) { return { canSendMessage: true, canJoinChannel: true, canBypassMute: true, priorityAccess: true, level: isModerator ? 'moderator' : 'privileged' }; } return { canSendMessage: true, canJoinChannel: true, canBypassMute: false, level: 'regular' }; } async isUserBlacklisted(channelId, channelType, userId) { // Implementation for blacklist checking try { const blacklist = await getChannelBlacklist(channelId, channelType); return blacklist.includes(userId); } catch (error) { return false; } } async isUserModerator(channelId, userId) { // Implementation for moderator checking try { const moderators = await getChannelModerators(channelId); return moderators.includes(userId); } catch (error) { return false; } } clearCache() { this.whitelistCache.clear(); } clearChannelCache(channelId, channelType) { const cacheKey = `${channelId}:${channelType}`; this.whitelistCache.delete(cacheKey); } } // Usage const permissionChecker = new PermissionChecker(); // Check if user is whitelisted const isWhitelisted = await permissionChecker.isUserWhitelisted('group123', 2, 'user456'); console.log('User whitelisted:', isWhitelisted); // Get comprehensive permissions const permissions = await permissionChecker.checkUserPermissions('group123', 2, 'user456'); console.log('User permissions:', permissions); ``` ### Whitelist Analytics **Analyze Whitelist Patterns**: ```javascript theme={null} // Whitelist analytics and reporting class WhitelistAnalytics { constructor() { this.analyticsData = new Map(); } async analyzeChannelWhitelist(channelId, channelType) { try { const whitelist = await this.getChannelWhitelist(channelId, channelType); const analysis = { channelId: channelId, channelType: channelType, totalMembers: whitelist.length, memberDetails: await this.analyzeMemberDetails(whitelist), activityAnalysis: await this.analyzeActivity(channelId, whitelist), privilegeDistribution: await this.analyzePrivileges(channelId, whitelist), trends: await this.analyzeTrends(channelId, channelType), recommendations: [] }; // Generate recommendations analysis.recommendations = this.generateRecommendations(analysis); this.analyticsData.set(`${channelId}:${channelType}`, analysis); return analysis; } catch (error) { console.error('Failed to analyze whitelist:', error); return null; } } async getChannelWhitelist(channelId, channelType) { const response = await fetch( `/channel/whitelist?channel_id=${channelId}&channel_type=${channelType}` ); return await response.json(); } async analyzeMemberDetails(whitelist) { const memberDetails = { activeMembers: 0, inactiveMembers: 0, newMembers: 0, // Added in last 30 days longTermMembers: 0, // More than 6 months memberTypes: { moderators: 0, vips: 0, regular: 0 } }; for (const userId of whitelist) { const userInfo = await getUserInfo(userId); const lastActivity = await getLastActivity(userId); // Activity analysis const daysSinceActivity = (Date.now() - lastActivity) / (1000 * 60 * 60 * 24); if (daysSinceActivity <= 7) { memberDetails.activeMembers++; } else { memberDetails.inactiveMembers++; } // Membership duration const membershipDuration = Date.now() - userInfo.whitelistJoinDate; const daysMember = membershipDuration / (1000 * 60 * 60 * 24); if (daysMember <= 30) { memberDetails.newMembers++; } else if (daysMember >= 180) { memberDetails.longTermMembers++; } // Member type if (userInfo.isModerator) { memberDetails.memberTypes.moderators++; } else if (userInfo.isVIP) { memberDetails.memberTypes.vips++; } else { memberDetails.memberTypes.regular++; } } return memberDetails; } async analyzeActivity(channelId, whitelist) { const activityData = { messagesSent: 0, averageMessagesPerMember: 0, mostActiveMembers: [], leastActiveMembers: [] }; const memberActivity = []; for (const userId of whitelist) { const messageCount = await getUserMessageCount(channelId, userId, 30); // Last 30 days memberActivity.push({ userId, messageCount }); activityData.messagesSent += messageCount; } activityData.averageMessagesPerMember = activityData.messagesSent / whitelist.length; // Sort by activity memberActivity.sort((a, b) => b.messageCount - a.messageCount); activityData.mostActiveMembers = memberActivity.slice(0, 5); activityData.leastActiveMembers = memberActivity.slice(-5); return activityData; } async analyzePrivileges(channelId, whitelist) { const privilegeData = { bypassMute: 0, priorityAccess: 0, rateExempt: 0, customPrivileges: {} }; for (const userId of whitelist) { const privileges = await getUserPrivileges(channelId, userId); if (privileges.includes('bypass_mute')) privilegeData.bypassMute++; if (privileges.includes('priority_access')) privilegeData.priorityAccess++; if (privileges.includes('rate_exempt')) privilegeData.rateExempt++; // Count custom privileges privileges.forEach(privilege => { if (!['bypass_mute', 'priority_access', 'rate_exempt'].includes(privilege)) { privilegeData.customPrivileges[privilege] = (privilegeData.customPrivileges[privilege] || 0) + 1; } }); } return privilegeData; } async analyzeTrends(channelId, channelType) { // Analyze whitelist growth/shrinkage trends const trends = { growthRate: 0, additionRate: 0, removalRate: 0, seasonality: {} }; // Implementation would analyze historical data // This is a simplified version return trends; } generateRecommendations(analysis) { const recommendations = []; // Inactive member recommendation if (analysis.memberDetails.inactiveMembers > analysis.memberDetails.activeMembers) { recommendations.push({ type: 'cleanup', priority: 'medium', message: 'Consider reviewing inactive whitelist members for removal' }); } // Privilege distribution recommendation if (analysis.privilegeDistribution.bypassMute === analysis.totalMembers) { recommendations.push({ type: 'privilege_review', priority: 'low', message: 'All whitelist members have bypass_mute privilege - consider if this is necessary' }); } // Growth recommendation if (analysis.memberDetails.newMembers > analysis.totalMembers * 0.5) { recommendations.push({ type: 'monitoring', priority: 'high', message: 'High rate of new whitelist additions - monitor for abuse' }); } return recommendations; } generateReport(channelId, channelType) { const analysis = this.analyticsData.get(`${channelId}:${channelType}`); if (!analysis) return null; return { summary: { channel: `${channelId} (Type: ${channelType})`, totalMembers: analysis.totalMembers, activeMembers: analysis.memberDetails.activeMembers, recommendations: analysis.recommendations.length }, details: analysis, generatedAt: new Date().toISOString() }; } } // Usage const analytics = new WhitelistAnalytics(); // Analyze channel whitelist const analysis = await analytics.analyzeChannelWhitelist('group123', 2); console.log('Whitelist analysis:', analysis); // Generate report const report = analytics.generateReport('group123', 2); console.log('Analytics report:', report); ``` ## Best Practices 1. **Regular Monitoring**: Regularly check whitelist membership for accuracy 2. **Access Control**: Ensure only authorized users can view whitelist information 3. **Caching**: Cache whitelist data to improve performance for frequent checks 4. **Analytics**: Use whitelist data for channel management insights 5. **Documentation**: Document the purpose and criteria for whitelist membership 6. **Privacy**: Respect user privacy when displaying whitelist information 7. **Performance**: Optimize whitelist queries for channels with large member counts # Update Channel Info Source: https://wukong.mintlify.app/en/api/channel/info POST /channel/info Update or add channel basic information ## Overview Update or add basic information for channels, including channel type, large group identifier, mute status, etc. ## Request Body ### Required Parameters Channel ID Channel type * `1` - Personal channel * `2` - Group channel ### Optional Parameters Whether to mute * `0` - Allow speaking * `1` - Mute all members Whether to disband channel * `1` - Disband channel (irreversible) Whether to prohibit sending messages (0=not prohibited, 1=prohibited). When prohibited, all members in the channel cannot send messages. Personal channels can receive messages but cannot send messages. Whether to allow strangers to send messages (0=not allowed, 1=allowed) (this configuration currently only supports personal channels) Personal channel: If AllowStranger is 1, strangers can send messages to the current user. For example: if the current account needs to accept stranger messages, channel\_id is the current user's uid ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel/info" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "large": 1, "ban": 0 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/info', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, large: 1, ban: 0 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "group123", "channel_type": 2, "large": 1, "ban": 0 } response = requests.post('http://localhost:5001/channel/info', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "large": 1, "ban": 0, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/info", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | --------------------------------- | | 200 | Channel info updated successfully | | 400 | Request parameter error | | 404 | Channel does not exist | | 500 | Internal server error | ## Parameter Details ### Channel Type (channel\_type) | Value | Type | Description | Features | | ----- | ---------------- | ----------------------- | ---------------------------------------------------- | | 1 | Personal channel | One-on-one private chat | Only two members, no group features | | 2 | Group channel | Multi-user group chat | Supports multiple members, group management features | ### Mute Status (ban) | Value | Description | Scope | Management Permission | | ----- | -------------- | -------------------------------------- | --------------------------------- | | 0 | Allow speaking | All members can send messages normally | Group owner and admins can modify | | 1 | Mute all | Only admins can send messages | Only group owner can modify | # Remove Channel Subscribers Source: https://wukong.mintlify.app/en/api/channel/remove-subscribers POST /channel/subscriber_remove Remove subscribers (members) from a channel ## Overview Remove subscribers (members) from a channel, supporting batch removal operations. ## Request Body ### Required Parameters Channel ID Channel type * `1` - Personal channel * `2` - Group channel List of subscriber user IDs to remove User ID ### Optional Parameters Whether as temporary subscriber * `0` - Permanent subscriber * `1` - Temporary subscriber ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel/subscriber_remove" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "subscribers": ["user4", "user5"], "temp_subscriber": 0 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/subscriber_remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, subscribers: ['user4', 'user5'], temp_subscriber: 0 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "group123", "channel_type": 2, "subscribers": ["user4", "user5"], "temp_subscriber": 0 } response = requests.post('http://localhost:5001/channel/subscriber_remove', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "subscribers": []string{"user4", "user5"}, "temp_subscriber": 0, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/subscriber_remove", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | --------------------------------------------- | | 200 | Subscribers removed successfully | | 400 | Request parameter error | | 403 | No operation permission | | 404 | Channel does not exist or user not in channel | | 500 | Internal server error | ## Parameter Description ### Temporary Subscriber (temp\_subscriber) | Value | Type | Description | Impact | | ----- | -------------------- | ----------------------- | ------------------------------------- | | 0 | Permanent subscriber | Remove official member | Completely remove member relationship | | 1 | Temporary subscriber | Remove temporary member | Remove temporary access permission | ## Use Cases ### Group Management * **Kick members**: Group owner or admin removes violating members * **Member leaves**: User voluntarily leaves the group * **Batch cleanup**: Clean up inactive or invalid members # Set Temporary Channel Subscribers Source: https://wukong.mintlify.app/en/api/channel/tmp-subscriber-set POST /tmpchannel/subscriber_set Set subscribers for a temporary channel (Internal API) This interface is primarily used for internal system calls and cluster node communication. External direct use is not recommended. ## Overview Set subscribers for a temporary channel. This is an internal API used for cluster node communication to manage temporary channel subscriptions. Temporary channels are used for special scenarios such as temporary groups or sessions, and automatically create and manage channel tags. ## Request Body ### Required Parameters Temporary channel ID, cannot contain special characters List of user IDs to set as subscribers for the temporary channel User ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/tmpchannel/subscriber_set" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "tmp_channel_123", "uids": ["user1", "user2", "user3"] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/tmpchannel/subscriber_set', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'tmp_channel_123', uids: ['user1', 'user2', 'user3'] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "tmp_channel_123", "uids": ["user1", "user2", "user3"] } response = requests.post('http://localhost:5001/tmpchannel/subscriber_set', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "tmp_channel_123", "uids": []string{"user1", "user2", "user3"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/tmpchannel/subscriber_set", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ---------------------------------------------- | | 200 | Temporary channel subscribers set successfully | | 400 | Request parameter error | | 500 | Internal server error | ## Temporary Channel Mechanism ### Temporary Channel Features * **Temporary Nature**: Used for special scenarios like temporary groups or sessions * **Auto Management**: System automatically creates and manages channel tags * **Cluster Communication**: Primarily used for internal communication between cluster nodes * **Subscription Management**: Supports dynamic setting of subscriber lists ### Use Cases | Scenario | Description | Applicable Situation | | ------------------ | ---------------------------------------- | -------------------------------- | | Temporary Groups | Create temporary discussion groups | Short-term project collaboration | | Session Migration | Migrate sessions between cluster nodes | Load balancing | | System Maintenance | Temporarily reorganize channel structure | System upgrades | ## Important Notes **Internal API Warning** This interface is designed for internal system calls. Direct use may cause: * Data inconsistency * Cluster state anomalies * Unexpected system behavior It is recommended to use standard channel management interfaces for regular operations. ### Parameter Restrictions * **Channel ID**: Cannot be empty, cannot contain special characters * **User List**: Must contain at least one user ID * **Character Length**: Channel ID maximum 64 characters ### Error Handling | Error Message | Cause | Solution | | -------------------------------------------- | --------------------------- | ------------------------------------------- | | channel\_id cannot be empty | No channel ID provided | Ensure valid channel ID is provided | | uids cannot be empty | User list is empty | Provide at least one user ID | | Channel ID cannot contain special characters | Invalid channel ID format | Use alphanumeric characters and underscores | | Failed to get channel's cluster node | Cluster communication error | Check cluster status | | Failed to create tag | Tag system error | Check tag service status | ## Best Practices 1. **Internal Use Only**: Avoid calling directly in client applications 2. **Parameter Validation**: Ensure all parameters are correctly formatted 3. **Error Handling**: Implement comprehensive error handling mechanisms 4. **Monitor Logs**: Record API calls for troubleshooting 5. **Cluster Status**: Ensure cluster status is normal before calling ## Related APIs * [Create Channel](/en/api/channel/create) - Create standard channels * [Add Channel Subscribers](/en/api/channel/add-subscribers) - Add standard channel subscribers * [Remove Channel Subscribers](/en/api/channel/remove-subscribers) - Remove standard channel subscribers # Add Channel Whitelist Source: https://wukong.mintlify.app/en/api/channel/whitelist POST /channel/whitelist_add Add users to channel whitelist ## Overview Add users to channel whitelist. Whitelisted users have special privileges and can bypass certain restrictions. ## Request Body ### Required Parameters Channel ID Channel type * `1` - Personal channel * `2` - Group channel List of user IDs to add to whitelist User ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel/whitelist_add" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["user456", "user789"] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/whitelist_add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, uids: ['user456', 'user789'] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "group123", "channel_type": 2, "uids": ["user456", "user789"] } response = requests.post('http://localhost:5001/channel/whitelist_add', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "uids": []string{"user456", "user789"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/whitelist_add", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ------------------------------ | | 200 | Whitelist operation successful | | 400 | Request parameter error | | 403 | No management permission | | 404 | Channel does not exist | | 500 | Internal server error | ## Whitelist Mechanism ### Special Privileges Whitelisted users enjoy the following special privileges: | Privilege | Description | Use Cases | | -------------------- | ----------------------------------- | ------------------------------- | | Bypass Mute | Can speak during channel-wide mute | Administrators, important users | | Priority Access | Priority access during high traffic | VIP users, moderators | | Rate Limit Exemption | Exempt from standard rate limits | Trusted users, bots | ### Permission Hierarchy Whitelist position in the permission system: 1. **Administrator permissions** > Whitelist > Regular member permissions 2. **Blacklist** > Whitelist (Blacklist has higher priority) 3. **System users** > Whitelist ## Use Cases ### VIP User Management **Grant VIP Privileges**: ```javascript theme={null} // Add VIP users to whitelist for special privileges async function grantVIPPrivileges(channelId, channelType, vipUsers) { try { await addToWhitelist({ channel_id: channelId, channel_type: channelType, uids: vipUsers }); // Log VIP privilege grant await logPrivilegeGrant({ channel_id: channelId, channel_type: channelType, users: vipUsers, privilege_type: 'vip_whitelist', granted_by: 'system', timestamp: new Date().toISOString() }); // Notify users about VIP status for (const userId of vipUsers) { await notifyUser(userId, { type: 'vip_granted', channel_id: channelId, privileges: ['bypass_mute', 'priority_access'], message: 'You have been granted VIP privileges in this channel' }); } console.log(`Granted VIP privileges to ${vipUsers.length} users`); } catch (error) { console.error('Failed to grant VIP privileges:', error); } } // Usage await grantVIPPrivileges('group123', 2, ['vip_user1', 'vip_user2']); ``` ### Moderator Management **Moderator Whitelist System**: ```javascript theme={null} // Comprehensive moderator management system class ModeratorManager { constructor(channelId, channelType) { this.channelId = channelId; this.channelType = channelType; this.moderatorLevels = { senior: ['bypass_mute', 'priority_access', 'rate_limit_exempt'], junior: ['bypass_mute', 'priority_access'], trainee: ['bypass_mute'] }; } async promoteModerator(userId, level = 'junior') { try { // Add to whitelist await addToWhitelist({ channel_id: this.channelId, channel_type: this.channelType, uids: [userId] }); // Record moderator status await this.recordModeratorStatus(userId, level); // Grant additional permissions based on level await this.grantModeratorPermissions(userId, level); // Notify about promotion await this.notifyModeratorPromotion(userId, level); console.log(`User ${userId} promoted to ${level} moderator`); } catch (error) { console.error(`Failed to promote moderator ${userId}:`, error); } } async recordModeratorStatus(userId, level) { await createModeratorRecord({ channel_id: this.channelId, user_id: userId, level: level, privileges: this.moderatorLevels[level], promoted_at: new Date().toISOString(), status: 'active' }); } async grantModeratorPermissions(userId, level) { const permissions = this.moderatorLevels[level]; for (const permission of permissions) { await grantChannelPermission(this.channelId, userId, permission); } } async notifyModeratorPromotion(userId, level) { await notifyUser(userId, { type: 'moderator_promotion', channel_id: this.channelId, level: level, privileges: this.moderatorLevels[level], responsibilities: this.getModeratorResponsibilities(level) }); } getModeratorResponsibilities(level) { const responsibilities = { senior: ['Manage junior moderators', 'Handle appeals', 'Policy enforcement'], junior: ['Monitor chat', 'Issue warnings', 'Report violations'], trainee: ['Observe and learn', 'Assist with basic moderation'] }; return responsibilities[level] || []; } async demoteModerator(userId) { try { // Remove from whitelist await removeFromWhitelist({ channel_id: this.channelId, channel_type: this.channelType, uids: [userId] }); // Update moderator record await updateModeratorRecord(this.channelId, userId, { status: 'inactive', demoted_at: new Date().toISOString() }); // Revoke permissions await this.revokeModeratorPermissions(userId); console.log(`User ${userId} demoted from moderator`); } catch (error) { console.error(`Failed to demote moderator ${userId}:`, error); } } async revokeModeratorPermissions(userId) { const allPermissions = Object.values(this.moderatorLevels).flat(); const uniquePermissions = [...new Set(allPermissions)]; for (const permission of uniquePermissions) { await revokeChannelPermission(this.channelId, userId, permission); } } } // Usage const moderatorManager = new ModeratorManager('group123', 2); // Promote user to moderator await moderatorManager.promoteModerator('user456', 'junior'); // Promote to senior moderator await moderatorManager.promoteModerator('user789', 'senior'); ``` ### Event-Based Whitelist Management **Dynamic Whitelist for Events**: ```javascript theme={null} // Dynamic whitelist management for special events class EventWhitelistManager { constructor() { this.activeEvents = new Map(); } async createEventWhitelist(eventId, channelId, channelType, eventConfig) { try { const event = { id: eventId, channelId: channelId, channelType: channelType, config: eventConfig, participants: [], startTime: new Date(eventConfig.startTime), endTime: new Date(eventConfig.endTime), status: 'scheduled' }; this.activeEvents.set(eventId, event); // Schedule automatic whitelist management await this.scheduleEventWhitelist(event); console.log(`Event whitelist created for event ${eventId}`); } catch (error) { console.error(`Failed to create event whitelist for ${eventId}:`, error); } } async scheduleEventWhitelist(event) { const now = Date.now(); const startDelay = event.startTime.getTime() - now; const endDelay = event.endTime.getTime() - now; if (startDelay > 0) { // Schedule event start setTimeout(() => { this.startEventWhitelist(event.id); }, startDelay); } else if (endDelay > 0) { // Event already started, activate immediately await this.startEventWhitelist(event.id); } if (endDelay > 0) { // Schedule event end setTimeout(() => { this.endEventWhitelist(event.id); }, endDelay); } } async startEventWhitelist(eventId) { const event = this.activeEvents.get(eventId); if (!event) return; try { // Add event participants to whitelist if (event.participants.length > 0) { await addToWhitelist({ channel_id: event.channelId, channel_type: event.channelType, uids: event.participants }); } event.status = 'active'; // Notify participants for (const participantId of event.participants) { await notifyUser(participantId, { type: 'event_started', event_id: eventId, channel_id: event.channelId, message: 'Event has started, you now have special privileges' }); } console.log(`Event whitelist activated for event ${eventId}`); } catch (error) { console.error(`Failed to start event whitelist for ${eventId}:`, error); } } async endEventWhitelist(eventId) { const event = this.activeEvents.get(eventId); if (!event) return; try { // Remove event participants from whitelist if (event.participants.length > 0) { await removeFromWhitelist({ channel_id: event.channelId, channel_type: event.channelType, uids: event.participants }); } event.status = 'completed'; // Notify participants for (const participantId of event.participants) { await notifyUser(participantId, { type: 'event_ended', event_id: eventId, channel_id: event.channelId, message: 'Event has ended, special privileges revoked' }); } console.log(`Event whitelist deactivated for event ${eventId}`); } catch (error) { console.error(`Failed to end event whitelist for ${eventId}:`, error); } } async addEventParticipant(eventId, userId) { const event = this.activeEvents.get(eventId); if (!event) return; if (!event.participants.includes(userId)) { event.participants.push(userId); // If event is active, add to whitelist immediately if (event.status === 'active') { await addToWhitelist({ channel_id: event.channelId, channel_type: event.channelType, uids: [userId] }); } } } } // Usage const eventManager = new EventWhitelistManager(); // Create event with whitelist await eventManager.createEventWhitelist('webinar_001', 'group123', 2, { startTime: '2024-01-15T10:00:00Z', endTime: '2024-01-15T12:00:00Z', type: 'webinar' }); // Add participants await eventManager.addEventParticipant('webinar_001', 'speaker1'); await eventManager.addEventParticipant('webinar_001', 'speaker2'); ``` ## Whitelist vs Blacklist Relationship ### Priority Rules ``` Blacklist > Whitelist > Regular Permissions ``` ### Conflict Resolution | Situation | Result | Description | | ------------------------------- | ---------------------------- | ----------------------------- | | In both blacklist and whitelist | Blacklist takes effect | Blacklist has higher priority | | Only in whitelist | Whitelist privileges apply | Normal whitelisted user | | Only in blacklist | Blacklist restrictions apply | Normal blacklisted user | | In neither list | Regular user permissions | Default permission handling | ### Implementation Example ```javascript theme={null} // Check user permissions considering both lists async function checkUserPermissions(channelId, channelType, userId) { try { const [blacklist, whitelist] = await Promise.all([ getChannelBlacklist(channelId, channelType), getChannelWhitelist(channelId, channelType) ]); const isBlacklisted = blacklist.includes(userId); const isWhitelisted = whitelist.includes(userId); if (isBlacklisted) { return { status: 'blacklisted', permissions: ['none'], canSendMessage: false, canJoinChannel: false }; } if (isWhitelisted) { return { status: 'whitelisted', permissions: ['bypass_mute', 'priority_access'], canSendMessage: true, canJoinChannel: true, specialPrivileges: true }; } return { status: 'regular', permissions: ['basic'], canSendMessage: true, canJoinChannel: true }; } catch (error) { console.error('Failed to check user permissions:', error); return { status: 'error', permissions: ['none'], canSendMessage: false, canJoinChannel: false }; } } ``` ## Best Practices 1. **Clear Criteria**: Establish clear criteria for whitelist inclusion 2. **Regular Review**: Periodically review whitelist members and remove inactive users 3. **Documentation**: Document reasons for adding users to whitelist 4. **Graduated Privileges**: Implement different levels of whitelist privileges 5. **Conflict Resolution**: Clearly define how blacklist/whitelist conflicts are resolved 6. **Notification**: Inform users when they are added to or removed from whitelist 7. **Audit Trail**: Maintain logs of all whitelist operations for accountability # Remove Channel Whitelist Source: https://wukong.mintlify.app/en/api/channel/whitelist-remove POST /channel/whitelist_remove Remove specific users from channel whitelist ## Overview Remove specific users from channel whitelist. After removal, users will lose whitelist privileges and will not be able to send messages to the channel if whitelist mode is enabled. ## Request Body ### Required Parameters Channel ID, cannot be empty or contain special characters Channel type * `1` - Person channel * `2` - Group channel List of user IDs to remove from whitelist User ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel/whitelist_remove" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2"] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/whitelist_remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, uids: ['user1', 'user2'] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2"] } response = requests.post('http://localhost:5001/channel/whitelist_remove', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "uids": []string{"user1", "user2"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/whitelist_remove", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ----------------------------------------- | | 200 | Users removed from whitelist successfully | | 400 | Request parameter error | | 403 | No management permission | | 404 | Channel does not exist | | 500 | Internal server error | ## Functionality ### Remove Operation The remove operation performs the following steps: 1. **Verify Users**: Check if specified users are in the whitelist 2. **Remove Entries**: Delete specified users from channel whitelist 3. **Permission Changes**: Users lose whitelist privileges 4. **Take Effect Immediately**: Permission changes take effect immediately ### Permission Impact Removed users will lose: | Privilege | Impact | Effective Time | | ------------------- | ---------------------------------------------- | -------------- | | Send Permission | Cannot send messages in whitelist mode | Immediately | | Special Permissions | Lose ability to bypass certain restrictions | Immediately | | Priority Processing | Messages no longer receive priority processing | Immediately | | Privilege Identity | Lose whitelist user status | Immediately | ## Whitelist Mode Impact ### When Whitelist Mode is Enabled * **Removed Users**: Cannot send messages to the channel * **Other Whitelisted Users**: Unaffected, continue to enjoy privileges * **Regular Users**: Still cannot send messages ### When Whitelist Mode is Disabled * **Removed Users**: Can still send messages normally * **Permission Changes**: Mainly affects special permissions and priority ## Use Cases ### Permission Downgrade ```bash theme={null} # Remove users who no longer need privileges curl -X POST "/channel/whitelist_remove" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["former_admin"] }' ``` ### Batch Cleanup ```bash theme={null} # Batch remove multiple users curl -X POST "/channel/whitelist_remove" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2", "user3"] }' ``` ### Temporary Restriction ```bash theme={null} # Temporarily remove user privileges curl -X POST "/channel/whitelist_remove" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["temp_restricted_user"] }' ``` ## Whitelist Management Strategy ### Operation Comparison | Operation | Function | Impact Scope | Use Case | | ------------------ | ------------------------ | ----------------- | ---------------------- | | `whitelist_add` | Add to whitelist | Grant privileges | Grant user privileges | | `whitelist_remove` | Remove from whitelist | Remove privileges | Revoke user privileges | | `whitelist_set` | Replace entire whitelist | Complete reset | Batch management | ### Management Principles 1. **Principle of Least Privilege**: Only give necessary users whitelist permissions 2. **Regular Review**: Regularly check the necessity of whitelist users 3. **Permission Grading**: Assign different levels of permissions based on user roles 4. **Transparent Management**: Record all permission change operations ## Best Practices 1. **Permission Audit**: Regularly review whitelist user activity and necessity 2. **Progressive Removal**: For important users, consider progressive permission downgrade 3. **Notification Mechanism**: Notify relevant users and administrators before removal 4. **Backup Strategy**: Backup current whitelist state before batch operations 5. **Monitor Impact**: Monitor channel activity changes after removal 6. **Document Records**: Record removal reasons and expected impact ## Error Handling ### Common Errors | Error Message | Cause | Solution | | -------------------------- | ----------------------- | ----------------------------------- | | Channel ID cannot be empty | No channel ID provided | Ensure valid channel ID is provided | | Channel type cannot be 0 | Invalid channel type | Use valid channel type (1 or 2) | | uids cannot be empty | No user list provided | Provide list of user IDs to remove | | Remove whitelist failed | Remove operation failed | Check if users are in whitelist | ## Related APIs * [Add Channel Whitelist](/en/api/channel/whitelist) - Add users to whitelist * [Set Channel Whitelist](/en/api/channel/whitelist-set) - Set complete whitelist * [Get Channel Whitelist](/en/api/channel/get-whitelist) - Get current whitelist * [Remove Channel Blacklist](/en/api/channel/blacklist-remove) - Remove blacklist users # Set Channel Whitelist Source: https://wukong.mintlify.app/en/api/channel/whitelist-set POST /channel/whitelist_set Set (replace) the entire whitelist for a channel ## Overview Set (replace) the entire whitelist for a channel. This operation will first remove all existing whitelist entries and then add new ones. If UIDs is empty, the entire whitelist will be cleared. ## Request Body ### Required Parameters Channel ID, cannot be empty or contain special characters Channel type * `1` - Person channel * `2` - Group channel ### Optional Parameters List of user IDs to set as whitelist. If empty, clears the entire whitelist. User ID ```bash Set Whitelist theme={null} curl -X POST "http://localhost:5001/channel/whitelist_set" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2", "user3"] }' ``` ```bash Clear Whitelist theme={null} curl -X POST "http://localhost:5001/channel/whitelist_set" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "group123", "channel_type": 2, "uids": [] }' ``` ```javascript JavaScript theme={null} // Set whitelist const response = await fetch('http://localhost:5001/channel/whitelist_set', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, uids: ['user1', 'user2', 'user3'] }) }); const data = await response.json(); console.log(data); // Clear whitelist const clearResponse = await fetch('http://localhost:5001/channel/whitelist_set', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: 'group123', channel_type: 2, uids: [] }) }); ``` ```python Python theme={null} import requests # Set whitelist data = { "channel_id": "group123", "channel_type": 2, "uids": ["user1", "user2", "user3"] } response = requests.post('http://localhost:5001/channel/whitelist_set', json=data) result = response.json() print(result) # Clear whitelist clear_data = { "channel_id": "group123", "channel_type": 2, "uids": [] } clear_response = requests.post('http://localhost:5001/channel/whitelist_set', json=clear_data) clear_result = clear_response.json() print(clear_result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { // Set whitelist data := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "uids": []string{"user1", "user2", "user3"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/whitelist_set", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) // Clear whitelist clearData := map[string]interface{}{ "channel_id": "group123", "channel_type": 2, "uids": []string{}, } clearJsonData, _ := json.Marshal(clearData) clearResp, err := http.Post( "http://localhost:5001/channel/whitelist_set", "application/json", bytes.NewBuffer(clearJsonData), ) if err != nil { panic(err) } defer clearResp.Body.Close() var clearResult map[string]interface{} json.NewDecoder(clearResp.Body).Decode(&clearResult) fmt.Printf("%+v\n", clearResult) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | -------------------------- | | 200 | Whitelist set successfully | | 400 | Request parameter error | | 403 | No management permission | | 404 | Channel does not exist | | 500 | Internal server error | ## Functionality ### Set Operation The set operation performs the following steps: 1. **Clear Existing Whitelist**: Remove all existing whitelist entries for the channel 2. **Add New Entries**: Add the provided user ID list to the whitelist 3. **Update Permissions**: Take effect immediately, only whitelisted users can send messages (if whitelist mode is enabled) 4. **Conversation Management**: Automatically create related conversations for person channels ### Clear Operation When `uids` is an empty array: * Clear all whitelist entries for the channel * Restore normal permissions for all users * Disable whitelist mode ## Whitelist Mechanism ### Permission Features Whitelisted users enjoy the following privileges: | Privilege | Description | Applicable Condition | | ---------------------- | ----------------------------------------- | ------------------------------ | | Send Permission | Can send messages to the channel | When whitelist mode is enabled | | Bypass Restrictions | Bypass certain channel restrictions | Based on channel configuration | | Priority Processing | Messages may receive priority processing | System configuration related | | Conversation Guarantee | Ensure conversations are created normally | Person channels | ### Whitelist Mode * **When Enabled**: Only whitelisted users can send messages * **When Disabled**: Whitelist does not affect message sending permissions * **Person Channels**: Whitelisted users automatically create conversations ## Use Cases ### Batch Management ```bash theme={null} # Set new whitelist, replacing all existing entries curl -X POST "/channel/whitelist_set" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["admin1", "admin2", "moderator1"] }' ``` ### Clear Whitelist ```bash theme={null} # Clear all whitelist, restore all user permissions curl -X POST "/channel/whitelist_set" -d '{ "channel_id": "group123", "channel_type": 2, "uids": [] }' ``` ### Reset Whitelist ```bash theme={null} # Completely reset whitelist to new user list curl -X POST "/channel/whitelist_set" -d '{ "channel_id": "group123", "channel_type": 2, "uids": ["new_privileged_user"] }' ``` ## Comparison with Other Whitelist Operations | Operation | Function | Use Case | | ------------------ | ------------------------------------ | ---------------------------------- | | `whitelist_add` | Add users to existing whitelist | Incrementally add privileged users | | `whitelist_set` | Replace entire whitelist | Batch management, reset whitelist | | `whitelist_remove` | Remove specific users from whitelist | Remove specific user privileges | ## Permission Hierarchy Whitelist position in the permission system: ``` System Users > Administrators > Blacklist > Whitelist > Regular Users ``` ### Conflict Handling | Situation | Result | Description | | ------------------------------- | --------------------------------- | ----------------------------- | | Both in blacklist and whitelist | Treated as blacklist | Blacklist has higher priority | | Only in whitelist | Enjoy whitelist privileges | Normal whitelist user | | Only in blacklist | Subject to blacklist restrictions | Normal blacklist user | | In neither list | Regular user permissions | Default permission handling | ## Best Practices 1. **Use Carefully**: Set operation clears all existing whitelist, ensure this is intended behavior 2. **Backup Existing Data**: Get current whitelist before setting as backup 3. **Permission Verification**: Ensure operator has sufficient management permissions 4. **Progressive Management**: For large channels, consider progressive whitelist management 5. **Monitor Effects**: Monitor channel activity and user feedback after setting 6. **Document Records**: Record whitelist change reasons and expected effects ## Error Handling ### Common Errors | Error Message | Cause | Solution | | -------------------------------------------- | ------------------------- | ------------------------------------------- | | Channel ID cannot be empty | No channel ID provided | Ensure valid channel ID is provided | | Channel type cannot be 0 | Invalid channel type | Use valid channel type (1 or 2) | | Channel ID cannot contain special characters | Invalid channel ID format | Use alphanumeric characters and underscores | | Remove all whitelist failed | Clear operation failed | Check channel status and permissions | | Add whitelist failed | Add operation failed | Check user ID validity | ## Related APIs * [Add Channel Whitelist](/en/api/channel/whitelist) - Incrementally add whitelist users * [Remove Channel Whitelist](/en/api/channel/whitelist-remove) - Remove specific whitelist users * [Get Channel Whitelist](/en/api/channel/get-whitelist) - Get current whitelist * [Set Channel Blacklist](/en/api/channel/blacklist-set) - Manage blacklist users # Kick Connection Source: https://wukong.mintlify.app/en/api/connection/kick POST /conn/kick Kick/disconnect a specific connection for a specified user ## Overview Kick/disconnect a specific connection for a specified user. Similar to removing connections, but typically used for actively disconnecting live connections. ## Request Body ### Required Parameters User ID Connection ID ### Optional Parameters Node ID, specifies connection on a specific node ```bash cURL theme={null} curl -X POST "http://localhost:5001/conn/kick" \ -H "Content-Type: application/json" \ -d '{ "uid": "user123", "conn_id": 12345, "node_id": 1 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/conn/kick', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: 'user123', conn_id: 12345, node_id: 1 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uid": "user123", "conn_id": 12345, "node_id": 1 } response = requests.post('http://localhost:5001/conn/kick', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uid": "user123", "conn_id": 12345, "node_id": 1, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/conn/kick", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ------------------------------ | | 200 | Connection kicked successfully | | 400 | Request parameter error | | 404 | Connection does not exist | | 500 | Internal server error | ## Difference from Remove Connection | Operation | Endpoint | Purpose | Characteristics | | ----------------- | -------------- | ------------------------------------ | ---------------------------------------------- | | Kick Connection | `/conn/kick` | Actively disconnect live connections | Sends disconnect notification to client | | Remove Connection | `/conn/remove` | Clean up connection records | Directly cleans server-side connection records | ## Use Cases ### User Session Management **Force User Logout**: ```javascript theme={null} // Force logout a user from specific device async function forceUserLogout(userId, connectionId, nodeId, reason = 'admin_action') { try { // Log the action await logAdminAction({ action: 'force_logout', target_user: userId, connection_id: connectionId, node_id: nodeId, reason: reason, timestamp: new Date().toISOString() }); // Kick the connection await kickConnection({ uid: userId, conn_id: connectionId, node_id: nodeId }); // Notify user about forced logout await sendNotificationToUser(userId, { type: 'forced_logout', reason: reason, timestamp: new Date().toISOString() }); console.log(`User ${userId} forcibly logged out from connection ${connectionId}`); } catch (error) { console.error('Failed to force user logout:', error); } } ``` **Session Timeout Management**: ```javascript theme={null} // Manage session timeouts class SessionTimeoutManager { constructor(timeoutMinutes = 30) { this.timeoutDuration = timeoutMinutes * 60 * 1000; this.activeSessions = new Map(); this.checkInterval = 60000; // Check every minute } startMonitoring() { this.monitoringInterval = setInterval(() => { this.checkExpiredSessions(); }, this.checkInterval); console.log('Session timeout monitoring started'); } stopMonitoring() { if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = null; } console.log('Session timeout monitoring stopped'); } updateSessionActivity(userId, connectionId, nodeId) { const sessionKey = `${userId}:${connectionId}:${nodeId}`; this.activeSessions.set(sessionKey, { userId, connectionId, nodeId, lastActivity: Date.now() }); } async checkExpiredSessions() { const now = Date.now(); const expiredSessions = []; for (const [sessionKey, session] of this.activeSessions) { const timeSinceActivity = now - session.lastActivity; if (timeSinceActivity > this.timeoutDuration) { expiredSessions.push(session); this.activeSessions.delete(sessionKey); } } // Kick expired sessions for (const session of expiredSessions) { try { await this.kickExpiredSession(session); } catch (error) { console.error(`Failed to kick expired session for user ${session.userId}:`, error); } } if (expiredSessions.length > 0) { console.log(`Kicked ${expiredSessions.length} expired sessions`); } } async kickExpiredSession(session) { await kickConnection({ uid: session.userId, conn_id: session.connectionId, node_id: session.nodeId }); // Log timeout event await logSessionEvent({ type: 'session_timeout', user_id: session.userId, connection_id: session.connectionId, node_id: session.nodeId, last_activity: new Date(session.lastActivity).toISOString(), timeout_duration_minutes: this.timeoutDuration / 60000 }); } getActiveSessionCount() { return this.activeSessions.size; } } // Usage const sessionManager = new SessionTimeoutManager(30); // 30 minute timeout sessionManager.startMonitoring(); // Update activity when user performs actions sessionManager.updateSessionActivity('user123', 12345, 1); ``` ### Security Management **Suspicious Activity Response**: ```javascript theme={null} // Kick connections showing suspicious activity async function handleSuspiciousActivity(userId, connectionId, nodeId, activityType) { try { // Log security incident const incidentId = await logSecurityIncident({ type: 'suspicious_activity', user_id: userId, connection_id: connectionId, node_id: nodeId, activity_type: activityType, timestamp: new Date().toISOString(), action_taken: 'connection_kicked' }); // Kick the suspicious connection await kickConnection({ uid: userId, conn_id: connectionId, node_id: nodeId }); // Notify security team await notifySecurityTeam({ incident_id: incidentId, user_id: userId, connection_id: connectionId, activity_type: activityType, action: 'Connection kicked due to suspicious activity' }); // Temporarily block user if needed if (activityType === 'multiple_failed_auth' || activityType === 'rate_limit_exceeded') { await temporarilyBlockUser(userId, 15); // 15 minute block } console.log(`Kicked suspicious connection ${connectionId} for user ${userId}`); } catch (error) { console.error('Failed to handle suspicious activity:', error); } } ``` ### Administrative Control **Bulk Connection Management**: ```javascript theme={null} // Kick multiple connections for administrative purposes async function bulkKickConnections(connections, reason = 'admin_maintenance') { const results = []; for (const conn of connections) { try { await kickConnection({ uid: conn.uid, conn_id: conn.conn_id, node_id: conn.node_id }); results.push({ uid: conn.uid, conn_id: conn.conn_id, success: true }); // Small delay to avoid overwhelming the system await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { results.push({ uid: conn.uid, conn_id: conn.conn_id, success: false, error: error.message }); } } // Log bulk operation await logAdminAction({ action: 'bulk_kick_connections', reason: reason, total_connections: connections.length, successful_kicks: results.filter(r => r.success).length, failed_kicks: results.filter(r => !r.success).length, timestamp: new Date().toISOString() }); return results; } ``` ### Maintenance Operations **Graceful Node Shutdown**: ```javascript theme={null} // Gracefully kick all connections from a node before shutdown async function gracefulNodeShutdown(nodeId, notificationMessage = 'Server maintenance in progress') { try { // Get all connections on the node const nodeConnections = await getNodeConnections(nodeId); console.log(`Starting graceful shutdown for node ${nodeId} (${nodeConnections.length} connections)`); // Send notification to all users first for (const conn of nodeConnections) { try { await sendMaintenanceNotification(conn.uid, { message: notificationMessage, estimated_downtime: '10-15 minutes', reconnect_instructions: 'Please reconnect in a few minutes' }); } catch (error) { console.error(`Failed to notify user ${conn.uid}:`, error); } } // Wait a bit for notifications to be delivered await new Promise(resolve => setTimeout(resolve, 5000)); // Kick connections in batches const batchSize = 10; const batches = chunkArray(nodeConnections, batchSize); for (let i = 0; i < batches.length; i++) { const batch = batches[i]; console.log(`Kicking batch ${i + 1}/${batches.length}`); const batchResults = await bulkKickConnections(batch, 'node_maintenance'); // Wait between batches if (i < batches.length - 1) { await new Promise(resolve => setTimeout(resolve, 2000)); } } console.log(`Graceful shutdown completed for node ${nodeId}`); } catch (error) { console.error(`Graceful shutdown failed for node ${nodeId}:`, error); } } ``` ### Connection Quality Management **Poor Connection Cleanup**: ```javascript theme={null} // Kick connections with poor quality metrics class ConnectionQualityManager { constructor() { this.qualityThresholds = { maxLatency: 5000, // 5 seconds minHeartbeatInterval: 60000, // 1 minute maxFailedPings: 3 }; } async monitorAndKickPoorConnections() { try { const allConnections = await getAllActiveConnections(); const poorConnections = []; for (const conn of allConnections) { const quality = await this.assessConnectionQuality(conn); if (!quality.isGood) { poorConnections.push({ connection: conn, quality: quality }); } } // Kick poor quality connections for (const poor of poorConnections) { await this.kickPoorConnection(poor.connection, poor.quality); } return { total_checked: allConnections.length, poor_connections: poorConnections.length, kicked: poorConnections.length }; } catch (error) { console.error('Connection quality monitoring failed:', error); return { error: error.message }; } } async assessConnectionQuality(connection) { const metrics = await getConnectionMetrics(connection.conn_id); const issues = []; if (metrics.latency > this.qualityThresholds.maxLatency) { issues.push(`High latency: ${metrics.latency}ms`); } if (metrics.lastHeartbeat && Date.now() - metrics.lastHeartbeat > this.qualityThresholds.minHeartbeatInterval) { issues.push('Missed heartbeat'); } if (metrics.failedPings > this.qualityThresholds.maxFailedPings) { issues.push(`Too many failed pings: ${metrics.failedPings}`); } return { isGood: issues.length === 0, issues: issues, metrics: metrics }; } async kickPoorConnection(connection, quality) { try { await kickConnection({ uid: connection.uid, conn_id: connection.conn_id, node_id: connection.node_id }); // Log quality issue await logConnectionQualityEvent({ user_id: connection.uid, connection_id: connection.conn_id, node_id: connection.node_id, quality_issues: quality.issues, metrics: quality.metrics, action: 'connection_kicked', timestamp: new Date().toISOString() }); console.log(`Kicked poor quality connection ${connection.conn_id} for user ${connection.uid}`); } catch (error) { console.error(`Failed to kick poor connection ${connection.conn_id}:`, error); } } } // Usage const qualityManager = new ConnectionQualityManager(); // Run quality check every 5 minutes setInterval(async () => { const result = await qualityManager.monitorAndKickPoorConnections(); if (result.kicked > 0) { console.log(`Quality check: kicked ${result.kicked} poor connections`); } }, 5 * 60 * 1000); ``` ## Best Practices 1. **Graceful Notification**: Send notifications to users before kicking connections when possible 2. **Logging**: Log all kick operations with reasons for audit purposes 3. **Rate Limiting**: Avoid kicking too many connections simultaneously 4. **Error Handling**: Handle kick failures gracefully 5. **User Experience**: Provide clear reconnection instructions to affected users 6. **Security**: Use kick operations as part of security incident response 7. **Monitoring**: Monitor kick operations to identify patterns or issues # Remove Connection Source: https://wukong.mintlify.app/en/api/connection/remove POST /conn/remove Remove a specific connection for a specified user ## Overview Remove a specific connection for a specified user, used for cleaning up invalid connections or managing user connection states. ## Request Body ### Required Parameters User ID Connection ID ### Optional Parameters Node ID, specifies connection on a specific node ```bash cURL theme={null} curl -X POST "http://localhost:5001/conn/remove" \ -H "Content-Type: application/json" \ -d '{ "uid": "user123", "conn_id": 12345, "node_id": 1 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/conn/remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: 'user123', conn_id: 12345, node_id: 1 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uid": "user123", "conn_id": 12345, "node_id": 1 } response = requests.post('http://localhost:5001/conn/remove', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uid": "user123", "conn_id": 12345, "node_id": 1, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/conn/remove", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ------------------------------- | | 200 | Connection removed successfully | | 400 | Request parameter error | | 404 | Connection does not exist | | 500 | Internal server error | ## Use Cases ### Connection Cleanup **Remove Stale Connections**: ```javascript theme={null} // Remove stale or inactive connections async function removeStaleConnection(userId, connectionId, nodeId) { try { await removeConnection({ uid: userId, conn_id: connectionId, node_id: nodeId }); console.log(`Stale connection ${connectionId} removed for user ${userId}`); } catch (error) { console.error('Failed to remove stale connection:', error); } } ``` **Batch Connection Cleanup**: ```javascript theme={null} // Clean up multiple stale connections async function batchRemoveConnections(connectionsToRemove) { const results = []; for (const conn of connectionsToRemove) { try { await removeConnection({ uid: conn.uid, conn_id: conn.conn_id, node_id: conn.node_id }); results.push({ uid: conn.uid, conn_id: conn.conn_id, success: true }); } catch (error) { results.push({ uid: conn.uid, conn_id: conn.conn_id, success: false, error: error.message }); } } return results; } ``` ### Connection Management **Manage User Connection Limits**: ```javascript theme={null} // Enforce connection limits per user class ConnectionLimitManager { constructor(maxConnectionsPerUser = 5) { this.maxConnections = maxConnectionsPerUser; } async enforceConnectionLimit(userId) { try { // Get user's current connections const connections = await getUserConnections(userId); if (connections.length > this.maxConnections) { // Sort by last activity (oldest first) connections.sort((a, b) => a.last_activity - b.last_activity); // Remove oldest connections const toRemove = connections.slice(0, connections.length - this.maxConnections); for (const conn of toRemove) { await removeConnection({ uid: userId, conn_id: conn.conn_id, node_id: conn.node_id }); console.log(`Removed old connection ${conn.conn_id} for user ${userId}`); } } } catch (error) { console.error('Failed to enforce connection limit:', error); } } } // Usage const limitManager = new ConnectionLimitManager(3); await limitManager.enforceConnectionLimit('user123'); ``` ### Security Management **Remove Suspicious Connections**: ```javascript theme={null} // Remove connections flagged as suspicious async function removeSuspiciousConnection(userId, connectionId, nodeId, reason) { try { // Log security event await logSecurityEvent({ type: 'suspicious_connection_removal', user_id: userId, connection_id: connectionId, node_id: nodeId, reason: reason, timestamp: new Date().toISOString() }); // Remove the connection await removeConnection({ uid: userId, conn_id: connectionId, node_id: nodeId }); // Notify security team await notifySecurityTeam({ action: 'connection_removed', user_id: userId, connection_id: connectionId, reason: reason }); console.log(`Suspicious connection ${connectionId} removed for user ${userId}`); } catch (error) { console.error('Failed to remove suspicious connection:', error); } } ``` ### Node Maintenance **Node-specific Connection Cleanup**: ```javascript theme={null} // Remove all connections from a specific node during maintenance async function removeNodeConnections(nodeId) { try { // Get all connections on the node const nodeConnections = await getNodeConnections(nodeId); console.log(`Removing ${nodeConnections.length} connections from node ${nodeId}`); const results = []; for (const conn of nodeConnections) { try { await removeConnection({ uid: conn.uid, conn_id: conn.conn_id, node_id: nodeId }); results.push({ uid: conn.uid, conn_id: conn.conn_id, success: true }); } catch (error) { results.push({ uid: conn.uid, conn_id: conn.conn_id, success: false, error: error.message }); } } const successful = results.filter(r => r.success).length; const failed = results.filter(r => !r.success).length; console.log(`Node ${nodeId} cleanup: ${successful} successful, ${failed} failed`); return results; } catch (error) { console.error(`Failed to cleanup node ${nodeId} connections:`, error); } } ``` ### Monitoring and Diagnostics **Connection Health Monitoring**: ```javascript theme={null} class ConnectionHealthMonitor { constructor(checkInterval = 60000) { this.checkInterval = checkInterval; this.isMonitoring = false; } startMonitoring() { if (this.isMonitoring) return; this.isMonitoring = true; this.monitoringInterval = setInterval(() => { this.checkConnectionHealth(); }, this.checkInterval); console.log('Connection health monitoring started'); } stopMonitoring() { if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = null; } this.isMonitoring = false; console.log('Connection health monitoring stopped'); } async checkConnectionHealth() { try { // Get all active connections const connections = await getAllConnections(); const unhealthyConnections = connections.filter(conn => this.isConnectionUnhealthy(conn) ); if (unhealthyConnections.length > 0) { console.log(`Found ${unhealthyConnections.length} unhealthy connections`); // Remove unhealthy connections for (const conn of unhealthyConnections) { await this.removeUnhealthyConnection(conn); } } } catch (error) { console.error('Connection health check failed:', error); } } isConnectionUnhealthy(connection) { const now = Date.now(); const lastActivity = connection.last_activity || 0; const idleTime = now - lastActivity; // Consider connection unhealthy if idle for more than 10 minutes return idleTime > 10 * 60 * 1000; } async removeUnhealthyConnection(connection) { try { await removeConnection({ uid: connection.uid, conn_id: connection.conn_id, node_id: connection.node_id }); console.log(`Removed unhealthy connection ${connection.conn_id} for user ${connection.uid}`); } catch (error) { console.error(`Failed to remove unhealthy connection ${connection.conn_id}:`, error); } } } // Usage const healthMonitor = new ConnectionHealthMonitor(30000); // Check every 30 seconds healthMonitor.startMonitoring(); ``` ### Administrative Operations **Admin Connection Management**: ```javascript theme={null} // Administrative function to manage user connections async function adminManageUserConnections(adminUserId, targetUserId, action) { try { // Verify admin permissions const hasPermission = await verifyAdminPermission(adminUserId, 'connection_management'); if (!hasPermission) { throw new Error('Insufficient admin permissions'); } // Get target user's connections const connections = await getUserConnections(targetUserId); let results = []; switch (action.type) { case 'remove_all': results = await batchRemoveConnections(connections); break; case 'remove_by_device': const deviceConnections = connections.filter(conn => conn.device_flag === action.device_flag ); results = await batchRemoveConnections(deviceConnections); break; case 'remove_specific': const specificConn = connections.find(conn => conn.conn_id === action.conn_id ); if (specificConn) { results = await batchRemoveConnections([specificConn]); } break; } // Log admin action await logAdminAction(adminUserId, 'connection_management', { target_user: targetUserId, action: action, results: results }); return results; } catch (error) { console.error('Admin connection management failed:', error); throw error; } } ``` ## Best Practices 1. **Permission Verification**: Ensure proper authorization before removing connections 2. **Logging**: Log all connection removals for audit and debugging purposes 3. **Graceful Handling**: Handle connection removal errors gracefully 4. **Batch Operations**: Use batch operations for better performance when removing multiple connections 5. **Monitoring**: Implement monitoring to detect and remove unhealthy connections 6. **Security**: Remove suspicious connections promptly to maintain system security 7. **Node Awareness**: Consider node distribution when managing connections in clustered environments # Clear Unread Messages Source: https://wukong.mintlify.app/en/api/conversation/clear-unread POST /conversations/clearUnread Clear the unread message count for a conversation ## Overview Clear the unread message count for a specified conversation, resetting the unread count to 0. ## Request Body ### Required Parameters User ID Channel ID Channel type * `1` - Personal channel * `2` - Group channel ### Optional Parameters Message sequence number, specifies up to which message to clear ```bash cURL theme={null} curl -X POST "http://localhost:5001/conversations/clearUnread" \ -H "Content-Type: application/json" \ -d '{ "uid": "user123", "channel_id": "group123", "channel_type": 2, "message_seq": 1001 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/conversations/clearUnread', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: 'user123', channel_id: 'group123', channel_type: 2, message_seq: 1001 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uid": "user123", "channel_id": "group123", "channel_type": 2, "message_seq": 1001 } response = requests.post('http://localhost:5001/conversations/clearUnread', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uid": "user123", "channel_id": "group123", "channel_type": 2, "message_seq": 1001, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/conversations/clearUnread", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ------------------------------------ | | 200 | Unread messages cleared successfully | | 400 | Request parameter error | | 403 | No operation permission | | 404 | Conversation does not exist | | 500 | Internal server error | ## Use Cases ### Chat Interface Integration **Mark Messages as Read**: ```javascript theme={null} // Clear unread when user opens a conversation async function openConversation(channelId, channelType, userId) { try { // Clear unread count await clearUnreadMessages({ uid: userId, channel_id: channelId, channel_type: channelType }); // Update UI to remove unread badge updateUnreadBadge(channelId, 0); console.log('Conversation opened and unread cleared'); } catch (error) { console.error('Failed to clear unread messages:', error); } } ``` **Mark Read Up to Specific Message**: ```javascript theme={null} // Clear unread up to a specific message when user scrolls async function markReadUpToMessage(channelId, channelType, userId, messageSeq) { try { await clearUnreadMessages({ uid: userId, channel_id: channelId, channel_type: channelType, message_seq: messageSeq }); console.log(`Marked read up to message ${messageSeq}`); } catch (error) { console.error('Failed to mark messages as read:', error); } } ``` ### Batch Operations **Clear Multiple Conversations**: ```javascript theme={null} // Clear unread for multiple conversations async function clearMultipleConversations(userId, conversations) { const results = []; for (const conv of conversations) { try { await clearUnreadMessages({ uid: userId, channel_id: conv.channel_id, channel_type: conv.channel_type }); results.push({ channel_id: conv.channel_id, success: true }); } catch (error) { results.push({ channel_id: conv.channel_id, success: false, error: error.message }); } } return results; } ``` ### Auto-Read Functionality **Auto-mark as Read on Focus**: ```javascript theme={null} // Automatically clear unread when window gains focus class AutoReadManager { constructor(userId) { this.userId = userId; this.activeConversation = null; this.setupEventListeners(); } setupEventListeners() { // Clear unread when window gains focus window.addEventListener('focus', () => { if (this.activeConversation) { this.clearUnreadForActive(); } }); // Clear unread when user is actively typing document.addEventListener('keydown', () => { if (this.activeConversation) { this.clearUnreadForActive(); } }); } setActiveConversation(channelId, channelType) { this.activeConversation = { channelId, channelType }; this.clearUnreadForActive(); } async clearUnreadForActive() { if (!this.activeConversation) return; try { await clearUnreadMessages({ uid: this.userId, channel_id: this.activeConversation.channelId, channel_type: this.activeConversation.channelType }); } catch (error) { console.error('Auto-read failed:', error); } } } ``` ### Read Receipt Integration **Combine with Read Receipts**: ```javascript theme={null} // Clear unread and send read receipt async function markAsReadWithReceipt(channelId, channelType, userId, messageSeq) { try { // Clear unread count await clearUnreadMessages({ uid: userId, channel_id: channelId, channel_type: channelType, message_seq: messageSeq }); // Send read receipt to other participants await sendReadReceipt({ channel_id: channelId, channel_type: channelType, message_seq: messageSeq, reader_uid: userId }); console.log('Messages marked as read with receipt sent'); } catch (error) { console.error('Failed to mark as read with receipt:', error); } } ``` ## Best Practices 1. **User Intent**: Only clear unread when user actually views the messages 2. **Batch Operations**: Use batch clearing for better performance when possible 3. **Error Handling**: Handle network errors gracefully without affecting UI 4. **Real-time Updates**: Combine with WebSocket events for real-time unread updates 5. **Offline Support**: Queue clear operations when offline and sync when reconnected 6. **Performance**: Avoid excessive API calls by debouncing clear operations 7. **User Experience**: Provide visual feedback when clearing unread counts # Delete Conversation Source: https://wukong.mintlify.app/en/api/conversation/delete POST /conversations/delete Delete a specified user's conversation record ## Overview Delete a specified user's conversation record, clearing conversation history and status. ## Request Body ### Required Parameters User ID Channel ID Channel type (1=personal channel, 2=group channel) ```bash cURL theme={null} curl -X POST "http://localhost:5001/conversations/delete" \ -H "Content-Type: application/json" \ -d '{ "uid": "user123", "channel_id": "group123", "channel_type": 2 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/conversations/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: 'user123', channel_id: 'group123', channel_type: 2 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uid": "user123", "channel_id": "group123", "channel_type": 2 } response = requests.post('http://localhost:5001/conversations/delete', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uid": "user123", "channel_id": "group123", "channel_type": 2, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/conversations/delete", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "success" } ``` ## Response Fields Operation status, returns `"success"` on success ## Status Codes | Status Code | Description | | ----------- | --------------------------------- | | 200 | Conversation deleted successfully | | 400 | Request parameter error | | 403 | No deletion permission | | 500 | Internal server error | ## Deletion Impact ### What Gets Deleted When a conversation is deleted, the following data is removed: | Data Type | Scope | Impact | | ------------------- | ------------------------------ | -------------------------------------- | | Conversation Record | User's conversation list entry | Conversation disappears from chat list | | Unread Count | User's unread message count | Unread badge removed | | Last Message Info | Last message and timestamp | No preview in conversation list | | User Preferences | Conversation-specific settings | Notification settings reset | ### What Remains Unchanged * **Channel Messages**: Actual messages in the channel remain intact * **Other Users**: Other users' conversations with the same channel are unaffected * **Channel Membership**: User remains a member of the channel * **Message History**: User can still access messages by rejoining the conversation ## Use Cases ### Chat List Management **Remove Conversation from Chat List**: ```javascript theme={null} // Remove conversation from user's chat list async function removeFromChatList(userId, channelId, channelType) { try { await deleteConversation({ uid: userId, channel_id: channelId, channel_type: channelType }); // Update UI to remove conversation removeConversationFromUI(channelId); console.log('Conversation removed from chat list'); } catch (error) { console.error('Failed to remove conversation:', error); } } ``` **Clean Up Old Conversations**: ```javascript theme={null} // Clean up conversations older than specified days async function cleanupOldConversations(userId, daysOld = 30) { try { // Get user's conversations const conversations = await getUserConversations(userId); const cutoffTime = Date.now() - (daysOld * 24 * 60 * 60 * 1000); const oldConversations = conversations.filter(conv => conv.timestamp < cutoffTime && conv.unread === 0 ); // Delete old conversations for (const conv of oldConversations) { await deleteConversation({ uid: userId, channel_id: conv.channel_id, channel_type: conv.channel_type }); } console.log(`Cleaned up ${oldConversations.length} old conversations`); } catch (error) { console.error('Failed to cleanup old conversations:', error); } } ``` ### Privacy and Data Management **User Privacy Control**: ```javascript theme={null} // Allow users to remove conversations for privacy async function removeConversationForPrivacy(userId, channelId, channelType) { try { // Confirm with user const confirmed = await confirmDeletion( 'Remove this conversation from your chat list? You can still access messages by searching.' ); if (confirmed) { await deleteConversation({ uid: userId, channel_id: channelId, channel_type: channelType }); // Log privacy action await logPrivacyAction(userId, 'conversation_removed', { channel_id: channelId, channel_type: channelType }); showNotification('Conversation removed from your chat list'); } } catch (error) { console.error('Failed to remove conversation for privacy:', error); } } ``` ### Batch Operations **Batch Delete Conversations**: ```javascript theme={null} // Delete multiple conversations async function batchDeleteConversations(userId, conversationsToDelete) { const results = []; for (const conv of conversationsToDelete) { try { await deleteConversation({ uid: userId, channel_id: conv.channel_id, channel_type: conv.channel_type }); results.push({ channel_id: conv.channel_id, success: true }); } catch (error) { results.push({ channel_id: conv.channel_id, success: false, error: error.message }); } } // Update UI for successful deletions const successful = results.filter(r => r.success); successful.forEach(result => { removeConversationFromUI(result.channel_id); }); return results; } ``` ### Archive and Restore **Archive Conversation (Soft Delete)**: ```javascript theme={null} // Implement archive functionality using conversation deletion class ConversationArchive { constructor(userId) { this.userId = userId; this.archivedKey = `archived_conversations_${userId}`; } async archiveConversation(channelId, channelType) { try { // Get conversation data before deletion const conversationData = await getConversationData( this.userId, channelId, channelType ); // Store in local archive await this.storeInArchive(conversationData); // Delete from active conversations await deleteConversation({ uid: this.userId, channel_id: channelId, channel_type: channelType }); console.log('Conversation archived successfully'); } catch (error) { console.error('Failed to archive conversation:', error); } } async storeInArchive(conversationData) { const archived = this.getArchivedConversations(); archived.push({ ...conversationData, archived_at: Date.now() }); localStorage.setItem(this.archivedKey, JSON.stringify(archived)); } getArchivedConversations() { const stored = localStorage.getItem(this.archivedKey); return stored ? JSON.parse(stored) : []; } async restoreConversation(channelId, channelType) { // Note: Restoration requires rejoining the conversation // The conversation will reappear when new messages arrive const archived = this.getArchivedConversations(); const filtered = archived.filter(conv => !(conv.channel_id === channelId && conv.channel_type === channelType) ); localStorage.setItem(this.archivedKey, JSON.stringify(filtered)); console.log('Conversation restored from archive'); } } // Usage const archive = new ConversationArchive('user123'); await archive.archiveConversation('group123', 2); ``` ### Administrative Operations **Admin Conversation Management**: ```javascript theme={null} // Administrative function to clean up user conversations async function adminCleanupUserConversations(adminUserId, targetUserId, criteria) { try { // Verify admin permissions const hasPermission = await verifyAdminPermission(adminUserId, 'conversation_management'); if (!hasPermission) { throw new Error('Insufficient admin permissions'); } // Get target user's conversations const conversations = await getUserConversations(targetUserId); // Filter based on criteria const toDelete = conversations.filter(conv => { if (criteria.inactive_days && conv.last_activity) { const daysSinceActivity = (Date.now() - conv.last_activity) / (1000 * 60 * 60 * 24); return daysSinceActivity > criteria.inactive_days; } if (criteria.channel_types) { return criteria.channel_types.includes(conv.channel_type); } return false; }); // Delete conversations const results = await batchDeleteConversations(targetUserId, toDelete); // Log admin action await logAdminAction(adminUserId, 'conversation_cleanup', { target_user: targetUserId, deleted_count: results.filter(r => r.success).length, criteria: criteria }); return results; } catch (error) { console.error('Admin conversation cleanup failed:', error); throw error; } } ``` ## Important Notes **Data Recovery**: Once a conversation is deleted, it cannot be automatically restored. The conversation will only reappear if: * New messages are sent to the channel * The user manually rejoins the conversation * The conversation is recreated through other means **Message Preservation**: Deleting a conversation does not delete the actual messages in the channel. Other users can still see all messages, and the user can still access message history through other means. ## Best Practices 1. **User Confirmation**: Always confirm with users before deleting conversations 2. **Soft Delete Option**: Consider implementing archive functionality instead of permanent deletion 3. **Batch Operations**: Use batch operations for better performance when deleting multiple conversations 4. **Error Handling**: Handle deletion errors gracefully without breaking the UI 5. **Logging**: Log conversation deletions for audit and support purposes 6. **UI Updates**: Immediately update the UI to reflect conversation removal 7. **Data Backup**: Consider backing up conversation metadata before deletion # Set Conversation Unread Count Source: https://wukong.mintlify.app/en/api/conversation/set-unread POST /conversations/setUnread Set the unread message count for a specified conversation ## Overview Set the unread message count for a specified conversation, used for manually adjusting the unread status of conversations. ## Request Body ### Required Parameters User ID Channel ID Channel type (1=personal channel, 2=group channel) Unread count to set ```bash cURL theme={null} curl -X POST "http://localhost:5001/conversations/setUnread" \ -H "Content-Type: application/json" \ -d '{ "uid": "user123", "channel_id": "group123", "channel_type": 2, "unread": 5 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/conversations/setUnread', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: 'user123', channel_id: 'group123', channel_type: 2, unread: 5 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uid": "user123", "channel_id": "group123", "channel_type": 2, "unread": 5 } response = requests.post('http://localhost:5001/conversations/setUnread', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uid": "user123", "channel_id": "group123", "channel_type": 2, "unread": 5, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/conversations/setUnread", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "success" } ``` ## Response Fields Operation status, returns `"success"` on success ## Status Codes | Status Code | Description | | ----------- | ----------------------------- | | 200 | Unread count set successfully | | 400 | Request parameter error | | 403 | No operation permission | | 500 | Internal server error | ## Use Cases ### Manual Unread Management **Mark Conversation as Important**: ```javascript theme={null} // Mark conversation as having unread messages to draw attention async function markAsImportant(userId, channelId, channelType) { try { await setUnreadCount({ uid: userId, channel_id: channelId, channel_type: channelType, unread: 1 }); console.log('Conversation marked as important'); updateUIBadge(channelId, 1); } catch (error) { console.error('Failed to mark conversation as important:', error); } } ``` **Reset Unread Count**: ```javascript theme={null} // Reset unread count to zero async function resetUnreadCount(userId, channelId, channelType) { try { await setUnreadCount({ uid: userId, channel_id: channelId, channel_type: channelType, unread: 0 }); console.log('Unread count reset to zero'); updateUIBadge(channelId, 0); } catch (error) { console.error('Failed to reset unread count:', error); } } ``` ### Notification Management **Custom Notification Badges**: ```javascript theme={null} // Set custom unread count for special notifications async function setCustomNotification(userId, channelId, channelType, count) { try { await setUnreadCount({ uid: userId, channel_id: channelId, channel_type: channelType, unread: count }); // Update UI with custom badge updateNotificationBadge(channelId, count); // Show system notification if count > 0 if (count > 0) { showSystemNotification(`${count} new items in ${channelId}`); } } catch (error) { console.error('Failed to set custom notification:', error); } } ``` ### Batch Unread Operations **Batch Set Unread Counts**: ```javascript theme={null} // Set unread counts for multiple conversations async function batchSetUnreadCounts(userId, conversationUpdates) { const results = []; for (const update of conversationUpdates) { try { await setUnreadCount({ uid: userId, channel_id: update.channelId, channel_type: update.channelType, unread: update.unreadCount }); results.push({ channelId: update.channelId, success: true, unreadCount: update.unreadCount }); } catch (error) { results.push({ channelId: update.channelId, success: false, error: error.message }); } } return results; } // Usage const updates = [ { channelId: 'group123', channelType: 2, unreadCount: 5 }, { channelId: 'user456', channelType: 1, unreadCount: 2 }, { channelId: 'group789', channelType: 2, unreadCount: 0 } ]; const results = await batchSetUnreadCounts('user123', updates); console.log('Batch update results:', results); ``` ### Priority Management **Set Priority-based Unread Counts**: ```javascript theme={null} class ConversationPriorityManager { constructor(userId) { this.userId = userId; this.priorityLevels = { urgent: 99, high: 10, normal: 1, low: 0 }; } async setPriority(channelId, channelType, priority) { const unreadCount = this.priorityLevels[priority] || 1; try { await setUnreadCount({ uid: this.userId, channel_id: channelId, channel_type: channelType, unread: unreadCount }); // Update UI with priority styling this.updatePriorityUI(channelId, priority, unreadCount); console.log(`Set ${channelId} priority to ${priority} (unread: ${unreadCount})`); } catch (error) { console.error(`Failed to set priority for ${channelId}:`, error); } } updatePriorityUI(channelId, priority, unreadCount) { const element = document.querySelector(`[data-channel="${channelId}"]`); if (element) { element.className = `conversation-item priority-${priority}`; const badge = element.querySelector('.unread-badge'); if (badge) { badge.textContent = unreadCount > 0 ? unreadCount : ''; badge.style.display = unreadCount > 0 ? 'block' : 'none'; } } } async markAsUrgent(channelId, channelType) { await this.setPriority(channelId, channelType, 'urgent'); } async markAsNormal(channelId, channelType) { await this.setPriority(channelId, channelType, 'normal'); } async clearPriority(channelId, channelType) { await this.setPriority(channelId, channelType, 'low'); } } // Usage const priorityManager = new ConversationPriorityManager('user123'); // Mark conversation as urgent await priorityManager.markAsUrgent('group123', 2); // Set normal priority await priorityManager.markAsNormal('user456', 1); // Clear priority await priorityManager.clearPriority('group789', 2); ``` ### Sync and Recovery **Sync Unread Counts from External Source**: ```javascript theme={null} // Sync unread counts from external system async function syncUnreadFromExternal(userId, externalUnreadData) { const syncResults = []; for (const item of externalUnreadData) { try { await setUnreadCount({ uid: userId, channel_id: item.channelId, channel_type: item.channelType, unread: item.unreadCount }); syncResults.push({ channelId: item.channelId, synced: true, unreadCount: item.unreadCount }); } catch (error) { syncResults.push({ channelId: item.channelId, synced: false, error: error.message }); } } // Log sync results const successful = syncResults.filter(r => r.synced).length; const failed = syncResults.filter(r => !r.synced).length; console.log(`Sync completed: ${successful} successful, ${failed} failed`); return syncResults; } ``` ### Testing and Development **Test Unread Scenarios**: ```javascript theme={null} // Helper function for testing different unread scenarios async function testUnreadScenarios(userId, channelId, channelType) { const scenarios = [ { name: 'No unread', count: 0 }, { name: 'Single unread', count: 1 }, { name: 'Multiple unread', count: 5 }, { name: 'High unread', count: 99 }, { name: 'Max unread', count: 999 } ]; for (const scenario of scenarios) { console.log(`Testing scenario: ${scenario.name}`); try { await setUnreadCount({ uid: userId, channel_id: channelId, channel_type: channelType, unread: scenario.count }); // Wait for UI update await new Promise(resolve => setTimeout(resolve, 500)); // Verify UI state const badge = document.querySelector(`[data-channel="${channelId}"] .unread-badge`); const displayedCount = badge ? badge.textContent : '0'; console.log(`✓ ${scenario.name}: Set ${scenario.count}, displayed ${displayedCount}`); } catch (error) { console.error(`✗ ${scenario.name}: Failed -`, error.message); } } } ``` ## Best Practices 1. **Validation**: Validate unread count values (non-negative integers) 2. **UI Consistency**: Ensure UI updates match the set unread count 3. **Error Handling**: Handle API errors gracefully without breaking UI 4. **Performance**: Batch operations when setting multiple unread counts 5. **User Experience**: Use meaningful unread counts that help users prioritize 6. **Sync Strategy**: Implement proper sync mechanisms for unread counts 7. **Testing**: Test various unread count scenarios during development # Sync User Conversations Source: https://wukong.mintlify.app/en/api/conversation/sync POST /conversation/sync Sync user's conversation list and status ## Overview Sync user's conversation list and status, supporting both incremental and full synchronization. ## Request Body ### Required Parameters User ID ### Optional Parameters Version timestamp for incremental sync Client's last message sequence numbers, format: channelID:channelType:last\_msg\_seq|channelID:channelType:last\_msg\_seq Number of recent messages to return for each conversation Whether to return only unread conversations (1=only unread, 0=return all) Array of channel types to exclude Channel type ```bash cURL theme={null} curl -X POST "http://localhost:5001/conversation/sync" \ -H "Content-Type: application/json" \ -d '{ "uid": "user123", "version": 1640995200000000000, "last_msg_seqs": "user1:1:100|group1:2:200", "msg_count": 10, "only_unread": 0, "exclude_channel_types": [3, 4] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/conversation/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: 'user123', version: 1640995200000000000, last_msg_seqs: 'user1:1:100|group1:2:200', msg_count: 10, only_unread: 0, exclude_channel_types: [3, 4] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uid": "user123", "version": 1640995200000000000, "last_msg_seqs": "user1:1:100|group1:2:200", "msg_count": 10, "only_unread": 0, "exclude_channel_types": [3, 4] } response = requests.post('http://localhost:5001/conversation/sync', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uid": "user123", "version": 1640995200000000000, "last_msg_seqs": "user1:1:100|group1:2:200", "msg_count": 10, "only_unread": 0, "exclude_channel_types": []int{3, 4}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/conversation/sync", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result []map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} [ { "channel_id": "group123", "channel_type": 2, "unread": 5, "timestamp": 1640995200, "last_msg_seq": 1005, "version": 1640995200000000000, "recents": [ { "message_id": 123456789, "message_seq": 1005, "client_msg_no": "msg_123", "from_uid": "user456", "timestamp": 1640995200, "payload": "SGVsbG8gV29ybGQ=" } ] }, { "channel_id": "private_user123_user789", "channel_type": 1, "unread": 2, "timestamp": 1640995100, "last_msg_seq": 502, "version": 1640995100000000000, "recents": [ { "message_id": 123456788, "message_seq": 502, "client_msg_no": "msg_122", "from_uid": "user789", "timestamp": 1640995100, "payload": "SGkgdGhlcmU=" } ] } ] ``` ## Response Fields The response is an array of conversations, each containing the following fields: ### Conversation Information Channel ID Channel type * `1` - Personal channel * `2` - Group channel Number of unread messages Last message timestamp Last message sequence number Conversation version number (nanosecond timestamp) ### Message List List of latest messages in the conversation Message ID Message sequence number Client message number Sender user ID Message timestamp Base64 encoded message content ## Status Codes | Status Code | Description | | ----------- | ---------------------------- | | 200 | Conversation sync successful | | 400 | Request parameter error | | 403 | No access permission | | 500 | Internal server error | ## Parameter Details ### Message Count (msg\_count) Controls the number of messages returned for each conversation: | Value | Description | Use Case | | ----- | ----------------------- | --------------------------- | | 0 | No messages returned | Only need conversation list | | 1-50 | Return specified number | Normal usage | | > 50 | System limited to 50 | Avoid excessive data | ### Version-based Incremental Sync Use version parameter for efficient incremental synchronization: ```javascript theme={null} // First sync - get all conversations const initialSync = await syncConversations({ uid: "user123", msg_count: 1 }); // Save the latest version const latestVersion = Math.max(...initialSync.map(conv => conv.version)); // Later incremental sync - only get updated conversations const incrementalSync = await syncConversations({ uid: "user123", version: latestVersion, msg_count: 1 }); ``` ### Last Message Sequence Tracking Track message sequences to detect missed messages: ```javascript theme={null} // Build last_msg_seqs string from current conversations function buildLastMsgSeqs(conversations) { return conversations .map(conv => `${conv.channel_id}:${conv.channel_type}:${conv.last_msg_seq}`) .join('|'); } // Use in sync request const lastMsgSeqs = buildLastMsgSeqs(currentConversations); const syncResult = await syncConversations({ uid: "user123", last_msg_seqs: lastMsgSeqs, msg_count: 5 }); ``` ## Use Cases ### Chat List Display **Initial Load**: ```javascript theme={null} // Load conversation list for chat interface const conversations = await syncConversations({ uid: "user123", msg_count: 1, exclude_channel_types: [3, 4] // Exclude system channels }); // Display in UI conversations.forEach(conv => { displayConversation(conv); }); ``` **Unread Badge Update**: ```javascript theme={null} // Get only unread conversations for badge updates const unreadConversations = await syncConversations({ uid: "user123", only_unread: 1, msg_count: 0 }); const totalUnread = unreadConversations.reduce((sum, conv) => sum + conv.unread, 0); updateUnreadBadge(totalUnread); ``` ### Real-time Sync **Periodic Sync**: ```javascript theme={null} let lastSyncVersion = 0; async function periodicSync() { const conversations = await syncConversations({ uid: "user123", version: lastSyncVersion, msg_count: 1 }); if (conversations.length > 0) { updateConversationList(conversations); lastSyncVersion = Math.max(...conversations.map(c => c.version)); } } // Sync every 30 seconds setInterval(periodicSync, 30000); ``` ### Offline Recovery **Sync After Reconnection**: ```javascript theme={null} async function syncAfterReconnection(uid, lastKnownVersion) { try { const missedConversations = await syncConversations({ uid: uid, version: lastKnownVersion, msg_count: 10 }); // Process missed conversations missedConversations.forEach(conv => { updateConversation(conv); // Show notification for new messages if (conv.unread > 0) { showNewMessageNotification(conv); } }); } catch (error) { console.error('Failed to sync conversations:', error); } } ``` ## Best Practices 1. **Incremental Sync**: Use version-based incremental sync to reduce data transfer 2. **Appropriate Message Count**: Set reasonable msg\_count based on UI needs 3. **Error Handling**: Handle network errors and implement retry logic 4. **Caching**: Cache conversation data locally to improve performance 5. **Real-time Updates**: Combine with WebSocket events for real-time updates 6. **Filtering**: Use exclude\_channel\_types to filter out unwanted channel types 7. **Batch Processing**: Process conversation updates in batches for better performance # Send Event Source: https://wukong.mintlify.app/en/api/event/send POST /event Send various types of events to channels, including streaming text messages and custom events ## Overview Send various types of events to channels, including streaming text messages and custom events. Supports AG-UI protocol events for real-time streaming communication. ## Query Parameters Force end existing streams in the channel before starting a new one * `0` - Do not force end * `1` - Force end existing streams ## Request Body ### Required Parameters Client message number - must be unique and not repeated. Used to identify and track the message/stream. For streaming messages, all events in the same stream should use the same client\_msg\_no. UUID format is recommended. Target channel ID where the event will be sent. For person channels, this should be the target user ID. For group channels, this should be the group ID. Channel type * `1` - Person channel * `2` - Group channel Event object Event type - supports AG-UI protocol events and custom events **AG-UI Protocol Events:** * `___TextMessageStart` - Initiates a streaming text message session * `___TextMessageContent` - Sends content chunks during streaming * `___TextMessageEnd` - Terminates a streaming text message session * `___ToolCallStart` - Begins a tool/function call event * `___ToolCallArgs` - Sends arguments for tool calls * `___ToolCallEnd` - Ends a tool call event * `___ToolCallResult` - Returns results from tool execution **Custom Events:** Any string not starting with `___` is treated as a custom event type Event ID (optional, auto-generated for some event types) Event timestamp (optional, Unix timestamp in milliseconds) Event data content. The format depends on the event type: **For Text Message Events:** * `___TextMessageStart` - Initial message content or metadata * `___TextMessageContent` - Text chunk for streaming * `___TextMessageEnd` - Final content or completion marker **For Tool Call Events:** * `___ToolCallStart` - Tool name or metadata * `___ToolCallArgs` - JSON string with function arguments * `___ToolCallEnd` - Completion status * `___ToolCallResult` - JSON string with execution results **For Custom Events:** Any string data relevant to your application ### Optional Parameters Sender user ID. If not provided or empty, defaults to the system UID. This identifies who is sending the event. ```bash Start Streaming Text Message theme={null} curl -X POST "http://localhost:5001/event" \ -H "Content-Type: application/json" \ -d '{ "client_msg_no": "msg_001_stream_start", "channel_id": "group_ai_chat", "channel_type": 2, "from_uid": "ai_assistant", "event": { "type": "___TextMessageStart", "data": "{\"type\":1,\"content\":\"Starting AI response...\"}" } }' ``` ```bash Send Text Content Chunk theme={null} curl -X POST "http://localhost:5001/event" \ -H "Content-Type: application/json" \ -d '{ "client_msg_no": "msg_001_stream_start", "channel_id": "group_ai_chat", "channel_type": 2, "from_uid": "ai_assistant", "event": { "type": "___TextMessageContent", "data": "Hello! How can I help you today?" } }' ``` ```bash End Streaming Text Message theme={null} curl -X POST "http://localhost:5001/event" \ -H "Content-Type: application/json" \ -d '{ "client_msg_no": "msg_001_stream_start", "channel_id": "group_ai_chat", "channel_type": 2, "from_uid": "ai_assistant", "event": { "type": "___TextMessageEnd", "data": "" } }' ``` ```bash Send Custom Event theme={null} curl -X POST "http://localhost:5001/event" \ -H "Content-Type: application/json" \ -d '{ "client_msg_no": "custom_event_001", "channel_id": "user_123", "channel_type": 1, "from_uid": "system", "event": { "type": "user_status_update", "timestamp": 1640995200000, "data": "{\"status\": \"online\", \"last_seen\": 1640995200000}" } }' ``` ```javascript JavaScript theme={null} // Streaming text message example const streamId = `stream_${Date.now()}`; // 1. Start stream await fetch('http://localhost:5001/event', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_msg_no: streamId, channel_id: 'group_ai_chat', channel_type: 2, from_uid: 'ai_assistant', event: { type: '___TextMessageStart', data: 'Starting AI response...' } }) }); // 2. Send content await fetch('http://localhost:5001/event', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_msg_no: streamId, channel_id: 'group_ai_chat', channel_type: 2, from_uid: 'ai_assistant', event: { type: '___TextMessageContent', data: 'Hello! How can I help you today?' } }) }); // 3. End stream await fetch('http://localhost:5001/event', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_msg_no: streamId, channel_id: 'group_ai_chat', channel_type: 2, from_uid: 'ai_assistant', event: { type: '___TextMessageEnd', data: '' } }) }); ``` ```python Python theme={null} import requests import time # Streaming text message example stream_id = f"stream_{int(time.time() * 1000)}" base_url = "http://localhost:5001/event" # 1. Start stream requests.post(base_url, json={ "client_msg_no": stream_id, "channel_id": "group_ai_chat", "channel_type": 2, "from_uid": "ai_assistant", "event": { "type": "___TextMessageStart", "data": "Starting AI response..." } }) # 2. Send content requests.post(base_url, json={ "client_msg_no": stream_id, "channel_id": "group_ai_chat", "channel_type": 2, "from_uid": "ai_assistant", "event": { "type": "___TextMessageContent", "data": "Hello! How can I help you today?" } }) # 3. End stream requests.post(base_url, json={ "client_msg_no": stream_id, "channel_id": "group_ai_chat", "channel_type": 2, "from_uid": "ai_assistant", "event": { "type": "___TextMessageEnd", "data": "" } }) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" "time" ) type Event struct { Type string `json:"type"` Data string `json:"data"` Timestamp int64 `json:"timestamp,omitempty"` } type EventRequest struct { ClientMsgNo string `json:"client_msg_no"` ChannelID string `json:"channel_id"` ChannelType int `json:"channel_type"` FromUID string `json:"from_uid"` Event Event `json:"event"` } func sendEvent(req EventRequest) error { jsonData, _ := json.Marshal(req) resp, err := http.Post( "http://localhost:5001/event", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { return err } defer resp.Body.Close() return nil } func main() { streamID := fmt.Sprintf("stream_%d", time.Now().UnixMilli()) // 1. Start stream sendEvent(EventRequest{ ClientMsgNo: streamID, ChannelID: "group_ai_chat", ChannelType: 2, FromUID: "ai_assistant", Event: Event{ Type: "___TextMessageStart", Data: "Starting AI response...", }, }) // 2. Send content sendEvent(EventRequest{ ClientMsgNo: streamID, ChannelID: "group_ai_chat", ChannelType: 2, FromUID: "ai_assistant", Event: Event{ Type: "___TextMessageContent", Data: "Hello! How can I help you today?", }, }) // 3. End stream sendEvent(EventRequest{ ClientMsgNo: streamID, ChannelID: "group_ai_chat", ChannelType: 2, FromUID: "ai_assistant", Event: Event{ Type: "___TextMessageEnd", Data: "", }, }) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ---------------------------------------------- | | 200 | Event sent successfully | | 400 | Bad request - invalid parameters or event data | | 500 | Internal server error | ## Streaming Message Flow ### Streaming Message Process 1. **Start Stream**: Send `___TextMessageStart` event to initiate a stream 2. **Send Content**: Send multiple `___TextMessageContent` events with message chunks 3. **End Stream**: Send `___TextMessageEnd` event to close the stream ### Important Notes * The same `client_msg_no` must be used for all events in a streaming session * Only one stream can be active per channel unless `force=1` is used * For person channels, the system automatically handles fake channel ID generation * Events are automatically routed to the appropriate cluster node ## Event Types ### AG-UI Protocol Events AG-UI protocol events enable real-time streaming communication for AI applications: | Event Type | Purpose | Data Format | | ----------------------- | ------------------------------------------- | --------------------------- | | `___TextMessageStart` | Initiates a streaming text message session | Initial content or metadata | | `___TextMessageContent` | Sends content chunks during streaming | Text chunk content | | `___TextMessageEnd` | Terminates a streaming text message session | Completion marker | | `___ToolCallStart` | Begins a tool/function call event | Tool name or metadata | | `___ToolCallArgs` | Sends arguments for tool calls | JSON formatted arguments | | `___ToolCallEnd` | Ends a tool call event | Completion status | | `___ToolCallResult` | Returns results from tool execution | JSON formatted results | ### Custom Events Any event type not starting with `___` is treated as a custom event, useful for: * User status updates * System notifications * Business logic events * Application-specific interactions ## Use Cases ### AI Chatbot ```bash theme={null} # Simulate AI typing effect curl -X POST "/event" -d '{ "client_msg_no": "ai_response_001", "channel_id": "user_123", "channel_type": 1, "from_uid": "ai_bot", "event": { "type": "___TextMessageStart", "data": "Thinking..." } }' # Gradually send response content curl -X POST "/event" -d '{ "client_msg_no": "ai_response_001", "channel_id": "user_123", "channel_type": 1, "from_uid": "ai_bot", "event": { "type": "___TextMessageContent", "data": "Based on your question, I recommend..." } }' ``` ### Real-time Collaboration ```bash theme={null} # Document editing status curl -X POST "/event" -d '{ "client_msg_no": "doc_edit_001", "channel_id": "doc_room_456", "channel_type": 2, "from_uid": "user_789", "event": { "type": "document_editing", "data": "{\"action\": \"start_edit\", \"section\": \"paragraph_1\"}" } }' ``` ### System Notifications ```bash theme={null} # User online notification curl -X POST "/event" -d '{ "client_msg_no": "status_update_001", "channel_id": "group_general", "channel_type": 2, "from_uid": "system", "event": { "type": "user_online", "timestamp": 1640995200000, "data": "{\"user_id\": \"user_123\", \"status\": \"online\"}" } }' ``` ## Best Practices 1. **Unique Identification**: Use UUID format for `client_msg_no` to ensure uniqueness 2. **Stream Management**: Close unused streams promptly to avoid resource waste 3. **Error Handling**: Handle stream conflicts and send failures 4. **Permission Verification**: Ensure sender has channel send permissions 5. **Data Format**: Use JSON format strings for complex data 6. **Performance Optimization**: Control streaming message send frequency reasonably ## Error Handling ### Common Errors | Error | Cause | Solution | | --------------------------------- | --------------------------------------------- | ------------------------------------------------ | | Event type cannot be empty | No event.type provided | Ensure valid event type is provided | | Stream already running in channel | Trying to start new stream when one is active | Use `force=1` or wait for existing stream to end | | Stream does not exist | Trying to send content to non-existent stream | Check if stream was created correctly | | Stream is already closed | Sending content to closed stream | Start a new stream | ### Retry Mechanism For temporary errors, implement exponential backoff retry mechanism: ```javascript theme={null} async function sendEventWithRetry(eventData, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { const response = await fetch('/event', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(eventData) }); if (response.ok) return await response.json(); if (response.status >= 400 && response.status < 500) { // Client error, don't retry throw new Error(`Client error: ${response.status}`); } } catch (error) { if (i === maxRetries - 1) throw error; await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000)); } } } ``` # API Introduction Source: https://wukong.mintlify.app/en/api/introduction Complete guide to WuKongIM REST API, including security specifications, request formats, and response standards ## Overview WuKongIM provides a complete REST API interface for managing all aspects of the instant messaging system, including message sending, channel management, user management, conversation management, and other functions. **Important Security Notice**: WuKongIM API is designed for internal business systems only. It must not be exposed to external networks to prevent security issues. ## Security Considerations ### 🔒 Internal Use Restrictions * **Internal use only**: All API interfaces are for internal business system calls only * **No external exposure**: Strictly prohibit exposing API services to public networks or external networks * **Port protection**: Must block external network access to port 5001 * **Network isolation**: Recommended to deploy and use in internal network environments ### 🛡️ Security Best Practices * Block external access to port 5001 in firewall * Use VPN or internal network environment for API calls * Regularly review network access permissions and logs * Avoid exposing API endpoint information in public documentation ## Basic Information ### Server Address ```bash theme={null} # Internal network environment (recommended) http://localhost:5001 # Or internal IP http://192.168.1.100:5001 ``` ### Authentication Mechanism WuKongIM API **does not require Token authentication** because it is designed for internal systems only. All API calls are direct access without additional authentication headers. ## API Standards and Rules ### 📊 HTTP Status Code Standards WuKongIM API follows standard HTTP status code specifications: #### ✅ Success Response (200) * **HTTP 200**: Indicates successful request execution * Some interfaces only need to check HTTP 200 status code without parsing JSON response * Success responses may contain data or just return status confirmation #### ❌ Failure Response (non-200) * **HTTP 400**: Request parameter error * **HTTP 500**: Internal server error ### 🔧 Request Format Specifications #### Content-Type All POST requests must use JSON format: ```bash theme={null} Content-Type: application/json ``` #### Request Body Example ```json theme={null} { "channel_id": "group123", "channel_type": 2, "from_uid": "user123", "payload": "SGVsbG8gV29ybGQ=" } ``` ### 📋 Response Format Specifications #### Success Response Example ```json theme={null} { "message_id": 123456789, "message_seq": 1001, "client_msg_no": "client_msg_123" } ``` #### Error Response Format (HTTP 400) ```json theme={null} { "msg": "channel_id parameter cannot be empty", "status": 400 } ``` **Documentation Note**: This documentation only shows parameter descriptions for success responses. Error response format is unified as shown above. ## Core Data Types ### 📁 Channel Type (channel\_type) | Value | Type | Description | | ----- | ---------------- | ------------------------------- | | 1 | Personal Channel | One-on-one private chat channel | | 2 | Group Channel | Multi-user group chat channel | ### 📱 Device Flag (device\_flag) | Flag Value | Device Type | Description | | ---------- | ----------- | ----------------------------- | | 0 | App | Android, iPhone, iPad devices | | 1 | Web | Browser, Web applications | | 2 | Desktop | Desktop applications | ### 💬 Message Format Message content is transmitted using **Base64 encoding**: ```json theme={null} { "payload": "SGVsbG8gV29ybGQ=", // Base64 encoded message content "from_uid": "user123", "channel_id": "group123", "channel_type": 2 } ``` ### 🔑 Important Parameter Specifications #### channel\_id Usage Specifications * **Direct use**: `channel_id` parameter should be used directly without adding `@` prefix * **Correct example**: `"channel_id": "group123"` * **Incorrect example**: `"channel_id": "@group123"` Do not add `@` symbol before `channel_id` parameter, use the original ID value directly. # Manager Login Source: https://wukong.mintlify.app/en/api/manager/login POST /manager/login Manager user login to obtain access token ## Overview Manager user login interface for obtaining access tokens for the management backend. ## Request Body ### Required Parameters Manager username Manager password ```bash cURL theme={null} curl -X POST "http://localhost:5001/manager/login" \ -H "Content-Type: application/json" \ -d '{ "username": "admin", "password": "your_password" }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/manager/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'admin', password: 'your_password' }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "username": "admin", "password": "your_password" } response = requests.post('http://localhost:5001/manager/login', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]string{ "username": "admin", "password": "your_password", } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/manager/login", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expire": 3600, "user": { "username": "admin", "role": "administrator", "permissions": ["read", "write", "admin"] } } ``` ```json Error Response theme={null} { "error": "Invalid username or password" } ``` ## Response Fields Access token for authentication in subsequent API calls Token expiration time (seconds) User information Username User role User permissions list ## Status Codes | Status Code | Description | | ----------- | ---------------------------- | | 200 | Login successful | | 401 | Invalid username or password | | 429 | Too many login attempts | | 500 | Internal server error | ## Best Practices 1. **Password Security**: Use strong password policies, change passwords regularly 2. **Token Management**: Implement automatic token refresh mechanism 3. **Access Control**: Role-based and permission-based access control 4. **Login Restrictions**: Implement login attempt limits 5. **Session Management**: Set reasonable token expiration times 6. **Secure Storage**: Don't store sensitive information in insecure places # Batch Message Search Source: https://wukong.mintlify.app/en/api/message/batch-search POST /messages Batch search multiple messages by message ID list ## Overview Batch search multiple messages by message ID list, suitable for scenarios where you need to retrieve details of multiple messages simultaneously. ## Request Body ### Required Parameters List of message IDs Message ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/messages" \ -H "Content-Type: application/json" \ -d '{ "message_ids": [123456789, 123456790, 123456791] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message_ids: [123456789, 123456790, 123456791] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "message_ids": [123456789, 123456790, 123456791] } response = requests.post('http://localhost:5001/messages', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "message_ids": []int64{123456789, 123456790, 123456791}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/messages", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result []map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} [ { "message_id": 123456789, "message_seq": 1001, "client_msg_no": "msg_123", "from_uid": "user123", "channel_id": "group123", "channel_type": 2, "timestamp": 1640995200, "payload": "SGVsbG8gV29ybGQ=" }, { "message_id": 123456790, "message_seq": 1002, "client_msg_no": "msg_124", "from_uid": "user456", "channel_id": "group123", "channel_type": 2, "timestamp": 1640995260, "payload": "SGkgdGhlcmU=" } ] ``` ## Response Fields The response is an array of message objects, each containing the following fields: Server-generated message ID Message sequence number Client message number Sender user ID Channel ID Channel type * `1` - Personal channel * `2` - Group channel Message timestamp (Unix timestamp) Base64 encoded message content ## Status Codes | Status Code | Description | | ----------- | ------------------------------ | | 200 | Message search successful | | 400 | Request parameter error | | 404 | Some or all messages not found | | 500 | Internal server error | ## Use Cases ### Message Details Retrieval **Get Multiple Message Details**: ```javascript theme={null} // Retrieve details for multiple messages async function getMessageDetails(messageIds) { try { const response = await fetch('/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message_ids: messageIds }) }); const messages = await response.json(); // Process and decode messages const processedMessages = messages.map(msg => ({ ...msg, content: atob(msg.payload), // Decode base64 content formatted_time: new Date(msg.timestamp * 1000).toLocaleString() })); return processedMessages; } catch (error) { console.error('Failed to get message details:', error); return []; } } // Usage const messageIds = [123456789, 123456790, 123456791]; const messageDetails = await getMessageDetails(messageIds); console.log('Message details:', messageDetails); ``` ### Message Thread Reconstruction **Reconstruct Message Threads**: ```javascript theme={null} // Reconstruct conversation threads from message IDs async function reconstructMessageThread(messageIds) { try { const messages = await getMessageDetails(messageIds); // Sort messages by timestamp messages.sort((a, b) => a.timestamp - b.timestamp); // Group by channel const threadsByChannel = messages.reduce((acc, msg) => { const channelKey = `${msg.channel_id}:${msg.channel_type}`; if (!acc[channelKey]) { acc[channelKey] = []; } acc[channelKey].push(msg); return acc; }, {}); return threadsByChannel; } catch (error) { console.error('Failed to reconstruct message thread:', error); return {}; } } // Usage const threadMessageIds = [123456789, 123456790, 123456791, 123456792]; const threads = await reconstructMessageThread(threadMessageIds); for (const [channelKey, messages] of Object.entries(threads)) { console.log(`Thread for ${channelKey}:`, messages); } ``` ### Message Validation and Verification **Validate Message Existence**: ```javascript theme={null} // Validate that messages exist and are accessible async function validateMessages(messageIds) { try { const response = await fetch('/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message_ids: messageIds }) }); const foundMessages = await response.json(); const foundIds = foundMessages.map(msg => msg.message_id); const missingIds = messageIds.filter(id => !foundIds.includes(id)); return { found: foundMessages, missing: missingIds, foundCount: foundMessages.length, missingCount: missingIds.length, totalRequested: messageIds.length }; } catch (error) { console.error('Message validation failed:', error); return { found: [], missing: messageIds, foundCount: 0, missingCount: messageIds.length, totalRequested: messageIds.length }; } } // Usage const idsToValidate = [123456789, 123456790, 999999999]; // Last ID doesn't exist const validation = await validateMessages(idsToValidate); console.log(`Found ${validation.foundCount}/${validation.totalRequested} messages`); if (validation.missingCount > 0) { console.log('Missing message IDs:', validation.missing); } ``` ### Message Export and Backup **Export Messages for Backup**: ```javascript theme={null} // Export messages with full details for backup class MessageExporter { constructor() { this.batchSize = 100; // Process in batches } async exportMessages(messageIds) { const batches = this.chunkArray(messageIds, this.batchSize); const allMessages = []; for (let i = 0; i < batches.length; i++) { const batch = batches[i]; console.log(`Processing batch ${i + 1}/${batches.length} (${batch.length} messages)`); try { const batchMessages = await this.getBatchMessages(batch); allMessages.push(...batchMessages); // Small delay between batches to avoid overwhelming the server await this.delay(100); } catch (error) { console.error(`Failed to process batch ${i + 1}:`, error); } } return this.formatExportData(allMessages); } async getBatchMessages(messageIds) { const response = await fetch('/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message_ids: messageIds }) }); return await response.json(); } formatExportData(messages) { return { export_timestamp: new Date().toISOString(), message_count: messages.length, messages: messages.map(msg => ({ ...msg, content: atob(msg.payload), formatted_timestamp: new Date(msg.timestamp * 1000).toISOString() })) }; } chunkArray(array, size) { const chunks = []; for (let i = 0; i < array.length; i += size) { chunks.push(array.slice(i, i + size)); } return chunks; } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } // Usage const exporter = new MessageExporter(); const messageIds = Array.from({length: 500}, (_, i) => 123456789 + i); const exportData = await exporter.exportMessages(messageIds); console.log(`Exported ${exportData.message_count} messages`); // Save to file or send to backup service const exportJson = JSON.stringify(exportData, null, 2); // saveToFile('message_export.json', exportJson); ``` ### Message Analytics **Analyze Message Patterns**: ```javascript theme={null} // Analyze patterns in batch of messages async function analyzeMessageBatch(messageIds) { try { const messages = await getMessageDetails(messageIds); const analysis = { total_messages: messages.length, unique_senders: new Set(messages.map(m => m.from_uid)).size, unique_channels: new Set(messages.map(m => `${m.channel_id}:${m.channel_type}`)).size, time_range: { earliest: Math.min(...messages.map(m => m.timestamp)), latest: Math.max(...messages.map(m => m.timestamp)) }, channel_distribution: {}, sender_distribution: {}, message_types: {} }; // Analyze channel distribution messages.forEach(msg => { const channelKey = `${msg.channel_id}:${msg.channel_type}`; analysis.channel_distribution[channelKey] = (analysis.channel_distribution[channelKey] || 0) + 1; }); // Analyze sender distribution messages.forEach(msg => { analysis.sender_distribution[msg.from_uid] = (analysis.sender_distribution[msg.from_uid] || 0) + 1; }); // Calculate time span analysis.time_span_hours = (analysis.time_range.latest - analysis.time_range.earliest) / 3600; return analysis; } catch (error) { console.error('Message analysis failed:', error); return null; } } // Usage const analysisMessageIds = [123456789, 123456790, 123456791, 123456792, 123456793]; const analysis = await analyzeMessageBatch(analysisMessageIds); if (analysis) { console.log('Message Analysis:'); console.log(`- Total messages: ${analysis.total_messages}`); console.log(`- Unique senders: ${analysis.unique_senders}`); console.log(`- Unique channels: ${analysis.unique_channels}`); console.log(`- Time span: ${analysis.time_span_hours.toFixed(2)} hours`); console.log('- Channel distribution:', analysis.channel_distribution); } ``` ### Message Cache Management **Efficient Message Caching**: ```javascript theme={null} // Cache management for frequently accessed messages class MessageCache { constructor(maxSize = 1000) { this.cache = new Map(); this.maxSize = maxSize; this.accessOrder = []; } async getMessages(messageIds) { const cached = []; const uncached = []; // Check cache first messageIds.forEach(id => { if (this.cache.has(id)) { cached.push(this.cache.get(id)); this.updateAccessOrder(id); } else { uncached.push(id); } }); // Fetch uncached messages let fetchedMessages = []; if (uncached.length > 0) { fetchedMessages = await this.fetchMessages(uncached); // Add to cache fetchedMessages.forEach(msg => { this.addToCache(msg.message_id, msg); }); } // Combine and sort by original order const allMessages = [...cached, ...fetchedMessages]; return messageIds.map(id => allMessages.find(msg => msg.message_id === id) ).filter(Boolean); } async fetchMessages(messageIds) { const response = await fetch('/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message_ids: messageIds }) }); return await response.json(); } addToCache(messageId, message) { // Remove oldest if cache is full if (this.cache.size >= this.maxSize) { const oldestId = this.accessOrder.shift(); this.cache.delete(oldestId); } this.cache.set(messageId, message); this.accessOrder.push(messageId); } updateAccessOrder(messageId) { const index = this.accessOrder.indexOf(messageId); if (index > -1) { this.accessOrder.splice(index, 1); this.accessOrder.push(messageId); } } getCacheStats() { return { size: this.cache.size, maxSize: this.maxSize, utilization: (this.cache.size / this.maxSize * 100).toFixed(2) + '%' }; } } // Usage const messageCache = new MessageCache(500); // First request - will fetch from server const messages1 = await messageCache.getMessages([123456789, 123456790]); console.log('First request:', messages1.length, 'messages'); // Second request - will use cache const messages2 = await messageCache.getMessages([123456789, 123456791]); console.log('Second request:', messages2.length, 'messages'); console.log('Cache stats:', messageCache.getCacheStats()); ``` ## Best Practices 1. **Batch Size**: Use reasonable batch sizes (50-100 messages) to balance performance and memory usage 2. **Error Handling**: Handle partial failures gracefully when some messages are not found 3. **Caching**: Implement caching for frequently accessed messages 4. **Rate Limiting**: Respect rate limits when making multiple batch requests 5. **Memory Management**: Process large batches in chunks to avoid memory issues 6. **Validation**: Validate message IDs before making requests 7. **Performance**: Use batch search instead of individual requests for better performance # Batch Send Messages Source: https://wukong.mintlify.app/en/api/message/batch-send POST /message/sendbatch Send multiple messages in batch ## Overview Send multiple messages in batch to improve message sending efficiency, suitable for group notifications, bulk push, and other scenarios. ## Request Body The request body is an array of message objects, each message object contains the following fields: ### Required Parameters Base64 encoded message content Sender user ID Target channel ID Channel type (1=personal channel, 2=group channel) ### Optional Parameters Message header information Whether to not persist message (0=persist, 1=do not persist) Whether to show red dot notification (0=do not show, 1=show) Whether it's write diffusion, generally 0, only cmd messages are 1 Client message number Stream message number Message expiration time (seconds), 0 means no expiration Specified list of subscribers to receive the message Subscriber user ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/message/sendbatch" \ -H "Content-Type: application/json" \ -d '[ { "header": { "no_persist": 0, "red_dot": 1, "sync_once": 0 }, "client_msg_no": "batch_msg_1", "from_uid": "system", "channel_id": "group123", "channel_type": 2, "expire": 0, "payload": "SGVsbG8gR3JvdXAgMQ==", "tag_key": "notification" }, { "header": { "no_persist": 0, "red_dot": 1, "sync_once": 0 }, "client_msg_no": "batch_msg_2", "from_uid": "system", "channel_id": "group456", "channel_type": 2, "expire": 0, "payload": "SGVsbG8gR3JvdXAgMg==", "tag_key": "notification" } ]' ``` ```javascript JavaScript theme={null} const messages = [ { header: { no_persist: 0, red_dot: 1, sync_once: 0 }, client_msg_no: `batch_msg_${Date.now()}_1`, from_uid: "system", channel_id: "group123", channel_type: 2, expire: 0, payload: btoa(JSON.stringify({ type: "text", content: "Hello Group 1" })), tag_key: "notification" }, { header: { no_persist: 0, red_dot: 1, sync_once: 0 }, client_msg_no: `batch_msg_${Date.now()}_2`, from_uid: "system", channel_id: "group456", channel_type: 2, expire: 0, payload: btoa(JSON.stringify({ type: "text", content: "Hello Group 2" })), tag_key: "notification" } ]; const response = await fetch('http://localhost:5001/message/sendbatch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(messages) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests import base64 import json messages = [ { "header": { "no_persist": 0, "red_dot": 1, "sync_once": 0 }, "client_msg_no": "batch_msg_1", "from_uid": "system", "channel_id": "group123", "channel_type": 2, "expire": 0, "payload": base64.b64encode( json.dumps({"type": "text", "content": "Hello Group 1"}).encode() ).decode(), "tag_key": "notification" }, { "header": { "no_persist": 0, "red_dot": 1, "sync_once": 0 }, "client_msg_no": "batch_msg_2", "from_uid": "system", "channel_id": "group456", "channel_type": 2, "expire": 0, "payload": base64.b64encode( json.dumps({"type": "text", "content": "Hello Group 2"}).encode() ).decode(), "tag_key": "notification" } ] response = requests.post('http://localhost:5001/message/sendbatch', json=messages) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/base64" "encoding/json" "fmt" "net/http" ) func main() { message1Content := map[string]interface{}{ "type": "text", "content": "Hello Group 1", } content1, _ := json.Marshal(message1Content) payload1 := base64.StdEncoding.EncodeToString(content1) message2Content := map[string]interface{}{ "type": "text", "content": "Hello Group 2", } content2, _ := json.Marshal(message2Content) payload2 := base64.StdEncoding.EncodeToString(content2) messages := []map[string]interface{}{ { "header": map[string]interface{}{ "no_persist": 0, "red_dot": 1, "sync_once": 0, }, "client_msg_no": "batch_msg_1", "from_uid": "system", "channel_id": "group123", "channel_type": 2, "expire": 0, "payload": payload1, "tag_key": "notification", }, { "header": map[string]interface{}{ "no_persist": 0, "red_dot": 1, "sync_once": 0, }, "client_msg_no": "batch_msg_2", "from_uid": "system", "channel_id": "group456", "channel_type": 2, "expire": 0, "payload": payload2, "tag_key": "notification", }, } jsonData, _ := json.Marshal(messages) resp, err := http.Post( "http://localhost:5001/message/sendbatch", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result []map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} [ { "message_id": 123456789, "message_seq": 1001, "client_msg_no": "batch_msg_1" }, { "message_id": 123456790, "message_seq": 1002, "client_msg_no": "batch_msg_2" } ] ``` ## Response Fields The response is an array, each element corresponds to a sent message: Server-generated message ID Message sequence number Client message number (echo) ## Status Codes | Status Code | Description | | ----------- | -------------------------------- | | 200 | Batch messages sent successfully | | 400 | Request parameter error | | 403 | No sending permission | | 500 | Internal server error | ## Use Cases ### System Notifications * **Announcement Push**: Send system announcements to multiple groups * **Activity Notifications**: Batch send activity reminder messages * **Maintenance Notifications**: Batch notifications before system maintenance ### Marketing Promotion * **Promotional Messages**: Send promotional information to target user groups * **New Feature Introduction**: Batch push new feature usage guides * **User Surveys**: Send questionnaire survey messages ### Operations Management * **Data Statistics**: Batch send data reports * **Task Assignment**: Batch assign tasks to team members * **Meeting Notifications**: Batch send meeting invitations ## Performance Optimization ### Batch Size * **Recommended Batch**: Single batch sending should not exceed 100 messages * **Batch Processing**: Large volumes of messages can be sent in batches to avoid timeouts * **Concurrency Control**: Control the number of concurrent batch requests ### Message Optimization * **Content Compression**: For identical content, use templates to reduce data transmission * **Asynchronous Processing**: Use asynchronous methods to handle batch sending * **Error Retry**: Implement retry mechanism for failed messages ## Best Practices 1. **Message Deduplication**: Ensure each message's client\_msg\_no is unique 2. **Error Handling**: Handle cases where some messages fail to send 3. **Permission Verification**: Verify sender's sending permission for all target channels 4. **Content Review**: Review batch message content 5. **Rate Limiting**: Implement reasonable batch sending rate limits 6. **Monitoring & Alerts**: Monitor batch sending success rate and performance # Get Channel Max Message Sequence Source: https://wukong.mintlify.app/en/api/message/max-message-seq GET /channel/max_message_seq Get the maximum message sequence number for a specified channel ## Overview Get the maximum message sequence number for a specified channel, used for message synchronization and status checking. ## Query Parameters Channel ID Channel type (1=personal channel, 2=group channel) Current logged-in user ID ```bash cURL theme={null} curl -X GET "http://localhost:5001/channel/max_message_seq?channel_id=group123&channel_type=2&login_uid=user123" ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/max_message_seq?channel_id=group123&channel_type=2&login_uid=user123'); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests params = { 'channel_id': 'group123', 'channel_type': 2 } response = requests.get('http://localhost:5001/channel/max_message_seq&login_uid=user123', params=params) data = response.json() print(data) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "net/http" ) func main() { resp, err := http.Get("http://localhost:5001/channel/max_message_seq?channel_id=group123&channel_type=2&login_uid=user123") if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "message_seq": 1500 } ``` ```json Channel Not Exists theme={null} { "message_seq": 0 } ``` ## Response Fields Maximum message sequence number for the channel. Returns 0 if channel doesn't exist or has no messages. ## Status Codes | Status Code | Description | | ----------- | ----------------------------------------------- | | 200 | Successfully retrieved maximum message sequence | | 400 | Request parameter error | | 500 | Internal server error | ## Use Cases ### Message Synchronization **Check for New Messages**: ```javascript theme={null} // Check if there are new messages since last sync async function checkForNewMessages(channelId, channelType, lastKnownSeq) { try { const response = await fetch( `/channel/max_message_seq?channel_id=${channelId}&channel_type=${channelType}&login_uid=user123` ); const data = await response.json(); const hasNewMessages = data.message_seq > lastKnownSeq; const newMessageCount = hasNewMessages ? data.message_seq - lastKnownSeq : 0; return { hasNewMessages, newMessageCount, maxSeq: data.message_seq }; } catch (error) { console.error('Failed to check for new messages:', error); return { hasNewMessages: false, newMessageCount: 0, maxSeq: lastKnownSeq }; } } // Usage const result = await checkForNewMessages('group123', 2, 1450); if (result.hasNewMessages) { console.log(`${result.newMessageCount} new messages available`); // Sync new messages await syncNewMessages('group123', 2, 1450, result.maxSeq); } ``` ### Offline Message Detection **Detect Missed Messages After Reconnection**: ```javascript theme={null} class OfflineMessageDetector { constructor() { this.lastSeqMap = new Map(); // Store last known seq for each channel } // Store last known sequence before going offline storeLastSequence(channelId, channelType, seq) { const key = `${channelId}:${channelType}`; this.lastSeqMap.set(key, seq); } // Check for missed messages after coming back online async checkMissedMessages(channels) { const missedMessages = []; for (const channel of channels) { try { const response = await fetch( `/channel/max_message_seq?channel_id=${channel.id}&channel_type=${channel.type}&login_uid=user123` ); const data = await response.json(); const key = `${channel.id}:${channel.type}`; const lastKnownSeq = this.lastSeqMap.get(key) || 0; if (data.message_seq > lastKnownSeq) { missedMessages.push({ channelId: channel.id, channelType: channel.type, missedCount: data.message_seq - lastKnownSeq, fromSeq: lastKnownSeq + 1, toSeq: data.message_seq }); } } catch (error) { console.error(`Failed to check missed messages for ${channel.id}:`, error); } } return missedMessages; } } // Usage const detector = new OfflineMessageDetector(); // Before going offline detector.storeLastSequence('group123', 2, 1450); // After coming back online const missedMessages = await detector.checkMissedMessages([ { id: 'group123', type: 2 }, { id: 'user456', type: 1 } ]); for (const missed of missedMessages) { console.log(`Channel ${missed.channelId} has ${missed.missedCount} missed messages`); // Sync missed messages await syncMissedMessages(missed); } ``` ### Channel Activity Monitoring **Monitor Channel Activity**: ```javascript theme={null} class ChannelActivityMonitor { constructor(channels, checkInterval = 30000) { this.channels = channels; this.checkInterval = checkInterval; this.lastSeqMap = new Map(); this.isMonitoring = false; } async startMonitoring() { if (this.isMonitoring) return; this.isMonitoring = true; // Initialize last sequences await this.initializeSequences(); // Start periodic checking this.monitoringInterval = setInterval(() => { this.checkActivity(); }, this.checkInterval); console.log('Channel activity monitoring started'); } stopMonitoring() { if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = null; } this.isMonitoring = false; console.log('Channel activity monitoring stopped'); } async initializeSequences() { for (const channel of this.channels) { try { const response = await fetch( `/channel/max_message_seq?channel_id=${channel.id}&channel_type=${channel.type}&login_uid=user123` ); const data = await response.json(); const key = `${channel.id}:${channel.type}`; this.lastSeqMap.set(key, data.message_seq); } catch (error) { console.error(`Failed to initialize sequence for ${channel.id}:`, error); } } } async checkActivity() { for (const channel of this.channels) { try { const response = await fetch( `/channel/max_message_seq?channel_id=${channel.id}&channel_type=${channel.type}&login_uid=user123` ); const data = await response.json(); const key = `${channel.id}:${channel.type}`; const lastSeq = this.lastSeqMap.get(key) || 0; if (data.message_seq > lastSeq) { const newMessages = data.message_seq - lastSeq; this.onChannelActivity(channel, newMessages, data.message_seq); this.lastSeqMap.set(key, data.message_seq); } } catch (error) { console.error(`Failed to check activity for ${channel.id}:`, error); } } } onChannelActivity(channel, newMessageCount, currentSeq) { console.log(`Channel ${channel.id} has ${newMessageCount} new messages (seq: ${currentSeq})`); // Trigger notifications or UI updates this.notifyChannelActivity(channel, newMessageCount); } notifyChannelActivity(channel, count) { // Implement notification logic if (count > 0) { // Show notification badge updateChannelBadge(channel.id, count); // Play notification sound for important channels if (channel.priority === 'high') { playNotificationSound(); } } } } // Usage const monitor = new ChannelActivityMonitor([ { id: 'group123', type: 2, priority: 'high' }, { id: 'user456', type: 1, priority: 'normal' } ], 15000); // Check every 15 seconds await monitor.startMonitoring(); ``` ### Batch Sequence Checking **Check Multiple Channels Efficiently**: ```javascript theme={null} // Check max sequences for multiple channels async function batchCheckMaxSequences(channels) { const promises = channels.map(async (channel) => { try { const response = await fetch( `/channel/max_message_seq?channel_id=${channel.id}&channel_type=${channel.type}&login_uid=user123` ); const data = await response.json(); return { channelId: channel.id, channelType: channel.type, maxSeq: data.message_seq, success: true }; } catch (error) { return { channelId: channel.id, channelType: channel.type, maxSeq: 0, success: false, error: error.message }; } }); const results = await Promise.all(promises); // Separate successful and failed results const successful = results.filter(r => r.success); const failed = results.filter(r => !r.success); if (failed.length > 0) { console.warn('Failed to get max sequence for some channels:', failed); } return { successful, failed }; } // Usage const channels = [ { id: 'group123', type: 2 }, { id: 'group456', type: 2 }, { id: 'user789', type: 1 } ]; const { successful, failed } = await batchCheckMaxSequences(channels); console.log('Max sequences:', successful); ``` ## Best Practices 1. **Caching**: Cache max sequence values to reduce API calls 2. **Batch Operations**: Check multiple channels efficiently when possible 3. **Error Handling**: Handle network errors gracefully 4. **Rate Limiting**: Avoid excessive polling by using reasonable intervals 5. **Offline Support**: Store last known sequences for offline message detection 6. **Performance**: Use this API for sync decisions rather than full message retrieval 7. **Monitoring**: Implement activity monitoring for real-time updates # Sync Channel Messages Source: https://wukong.mintlify.app/en/api/message/message-sync POST /channel/messagesync Sync historical messages from a specified channel ## Overview Sync messages from a specified channel, supporting message retrieval by sequence number range. ## Request Body ### Required Parameters Current logged-in user ID Channel ID Channel type (1=personal channel, 2=group channel) ### Optional Parameters Starting message sequence number (inclusive) Ending message sequence number (exclusive) Maximum number of messages to return, maximum 10000 Pull mode (0=pull down, 1=pull up) ```bash cURL theme={null} curl -X POST "http://localhost:5001/channel/messagesync" \ -H "Content-Type: application/json" \ -d '{ "login_uid": "user123", "channel_id": "group123", "channel_type": 2, "start_message_seq": 1000, "end_message_seq": 1100, "limit": 50, "pull_mode": 0 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/channel/messagesync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ login_uid: 'user123', channel_id: 'group123', channel_type: 2, start_message_seq: 1000, end_message_seq: 1100, limit: 50, pull_mode: 0 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "login_uid": "user123", "channel_id": "group123", "channel_type": 2, "start_message_seq": 1000, "end_message_seq": 1100, "limit": 50, "pull_mode": 0 } response = requests.post('http://localhost:5001/channel/messagesync', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "login_uid": "user123", "channel_id": "group123", "channel_type": 2, "start_message_seq": 1000, "end_message_seq": 1100, "limit": 50, "pull_mode": 0, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/channel/messagesync", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result []map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} [ { "message_id": 123456789, "message_seq": 1001, "client_msg_no": "client_msg_123", "from_uid": "user123", "channel_id": "group123", "channel_type": 2, "timestamp": 1640995200, "payload": "SGVsbG8gV29ybGQ=" }, { "message_id": 123456790, "message_seq": 1002, "client_msg_no": "client_msg_124", "from_uid": "user456", "channel_id": "group123", "channel_type": 2, "timestamp": 1640995260, "payload": "SGkgdGhlcmU=" } ] ``` ## Response Fields The response is an array of message objects, each containing: Message ID Message sequence number Client message number Sender user ID Channel ID Channel type Message timestamp Base64 encoded message content ## Status Codes | Status Code | Description | | ----------- | ----------------------- | | 200 | Message sync successful | | 400 | Request parameter error | | 403 | No access permission | | 500 | Internal server error | ## Use Cases ### Chat History Loading **Load Recent Messages**: ```javascript theme={null} // Load last 50 messages const messages = await syncChannelMessages({ login_uid: "user123", channel_id: "group123", channel_type: 2, limit: 50, pull_mode: 0 }); ``` **Load Older Messages**: ```javascript theme={null} // Load messages before a specific sequence const olderMessages = await syncChannelMessages({ login_uid: "user123", channel_id: "group123", channel_type: 2, end_message_seq: 1000, limit: 50, pull_mode: 1 }); ``` ### Message Search and Export **Export Chat History**: ```javascript theme={null} async function exportChatHistory(channelId, channelType, loginUid) { let allMessages = []; let startSeq = 0; const batchSize = 1000; while (true) { const messages = await syncChannelMessages({ login_uid: loginUid, channel_id: channelId, channel_type: channelType, start_message_seq: startSeq, limit: batchSize, pull_mode: 0 }); if (messages.length === 0) break; allMessages = allMessages.concat(messages); startSeq = messages[messages.length - 1].message_seq + 1; } return allMessages; } ``` ### Offline Message Sync **Sync Missed Messages**: ```javascript theme={null} async function syncMissedMessages(channelId, channelType, loginUid, lastSeq) { const missedMessages = await syncChannelMessages({ login_uid: loginUid, channel_id: channelId, channel_type: channelType, start_message_seq: lastSeq + 1, limit: 1000, pull_mode: 0 }); return missedMessages; } ``` ## Best Practices 1. **Reasonable Range**: Avoid syncing too many messages at once 2. **Pagination**: Use limit parameter to control return quantity 3. **Error Handling**: Handle network errors and permission errors 4. **Caching Strategy**: Reasonably cache synced messages 5. **Performance Optimization**: Adjust sync frequency based on actual needs 6. **Permission Check**: Verify user has access to the channel before syncing 7. **Rate Limiting**: Implement rate limiting to prevent excessive API calls # Send Message Source: https://wukong.mintlify.app/en/api/message/send POST /message/send Send messages to specified channels ## Overview Send messages to specified channels, supporting various message types including text, images, files, and more. ## Request Body ### Required Parameters Base64 encoded message content Sender user ID Target channel ID Channel type (1=personal channel, 2=group channel) ### Optional Parameters Message header information Whether to not persist message (0=persist, 1=do not persist) Whether to show red dot notification (0=do not show, 1=show) Whether it's write diffusion, generally 0, only cmd messages are 1 Client message number for deduplication and status tracking Stream message number Message expiration time (seconds), 0 means no expiration Specified list of subscribers to receive the message (only valid for CMD messages) Subscriber user ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/message/send" \ -H "Content-Type: application/json" \ -d '{ "header": { "no_persist": 0, "red_dot": 1, "sync_once": 0 }, "client_msg_no": "client_msg_123", "from_uid": "user123", "channel_id": "group123", "channel_type": 2, "expire": 0, "payload": "SGVsbG8gV29ybGQ=", "tag_key": "important" }' ``` ```javascript JavaScript theme={null} // Send text message const textMessage = { header: { no_persist: 0, red_dot: 1, sync_once: 0 }, client_msg_no: `msg_${Date.now()}`, from_uid: "user123", channel_id: "group123", channel_type: 2, expire: 0, payload: btoa(JSON.stringify({ type: "text", content: "Hello, World!" })), tag_key: "normal" }; const response = await fetch('http://localhost:5001/message/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(textMessage) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests import base64 import json # Send text message message_content = { "type": "text", "content": "Hello, World!" } payload = base64.b64encode( json.dumps(message_content).encode('utf-8') ).decode('utf-8') data = { "header": { "no_persist": 0, "red_dot": 1, "sync_once": 0 }, "client_msg_no": f"msg_{int(time.time() * 1000)}", "from_uid": "user123", "channel_id": "group123", "channel_type": 2, "expire": 0, "payload": payload, "tag_key": "normal" } response = requests.post('http://localhost:5001/message/send', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/base64" "encoding/json" "fmt" "net/http" "time" ) func main() { // Message content messageContent := map[string]interface{}{ "type": "text", "content": "Hello, World!", } contentBytes, _ := json.Marshal(messageContent) payload := base64.StdEncoding.EncodeToString(contentBytes) data := map[string]interface{}{ "header": map[string]interface{}{ "no_persist": 0, "red_dot": 1, "sync_once": 0, }, "client_msg_no": fmt.Sprintf("msg_%d", time.Now().UnixMilli()), "from_uid": "user123", "channel_id": "group123", "channel_type": 2, "expire": 0, "payload": payload, "tag_key": "normal", } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/message/send", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "message_id": 123456789, "message_seq": 1001, "client_msg_no": "client_msg_123" } ``` ## Response Fields Server-generated message ID Message sequence number Client message number (echo) ## Status Codes | Status Code | Description | | ----------- | ------------------------- | | 200 | Message sent successfully | | 400 | Request parameter error | | 403 | No sending permission | | 500 | Internal server error | ## Message Type Examples According to WuKongIM protocol specifications, here are recommended Payload structure examples: ### Regular Messages #### Text Message ```json theme={null} { "type": 1, "content": "This is a text message" } ``` #### Text Message (with @ functionality) ```json theme={null} { "type": 1, "content": "This is a text message", "mention": { "all": 0, "uids": ["1223", "2323"] } } ``` * `mention.all`: Whether to @everyone (0=@users, 1=@everyone) * `mention.uids`: If all=1, this field is empty #### Text Message (with reply) ```json theme={null} { "type": 1, "content": "Replied to someone", "reply": { "root_mid": "xxx", "message_id": "xxxx", "message_seq": 123, "from_uid": "xxxx", "from_name": "xxx", "payload": {} } } ``` #### Image Message ```json theme={null} { "type": 2, "url": "http://xxxxx.com/xxx", "width": 200, "height": 320 } ``` #### GIF Message ```json theme={null} { "type": 3, "url": "http://xxxxx.com/xxx", "width": 72, "height": 72 } ``` #### Voice Message ```json theme={null} { "type": 4, "url": "http://xxxxx.com/xxx", "timeTrad": 10 } ``` `timeTrad`: Voice duration (seconds) #### File Message ```json theme={null} { "type": 8, "url": "http://xxxxx.com/xxx", "name": "xxxx.docx", "size": 238734 } ``` `size`: File size in bytes #### Command Message ```json theme={null} { "type": 99, "cmd": "groupUpdate", "param": {} } ``` ### System Messages System message type must be greater than 1000 #### Create Group Chat Message settings: `NoPersist:0, RedDot:0, SyncOnce:1` ```json theme={null} { "type": 1001, "creator": "xxx", "creator_name": "John", "content": "{0} invited {1}, {2} to join the group chat", "extra": [ {"uid": "xxx", "name": "John"}, {"uid": "xx01", "name": "Alice"}, {"uid": "xx02", "name": "Bob"} ] } ``` #### Add Group Members Message settings: `NoPersist:0, RedDot:0, SyncOnce:1` ```json theme={null} { "type": 1002, "content": "{0} invited {1}, {2} to join the group chat", "extra": [ {"uid": "xxx", "name": "John"}, {"uid": "xx01", "name": "Alice"}, {"uid": "xx02", "name": "Bob"} ] } ``` #### Remove Group Members Message settings: `NoPersist:0, RedDot:0, SyncOnce:1` ```json theme={null} { "type": 1003, "content": "{0} removed {1} from the group chat", "extra": [ {"uid": "xxx", "name": "John"}, {"uid": "xx01", "name": "Alice"} ] } ``` #### Group Member Kicked Message settings: `NoPersist:0, RedDot:1, SyncOnce:0` ```json theme={null} { "type": 1010, "content": "You were removed from the group chat by {0}", "extra": [ {"uid": "xxx", "name": "John"} ] } ``` #### Update Group Name Message settings: `NoPersist:0, RedDot:0, SyncOnce:1` ```json theme={null} { "type": 1005, "content": "{0} changed the group name to \"Test Group\"", "extra": [ {"uid": "xxx", "name": "John"} ] } ``` #### Update Group Announcement Message settings: `NoPersist:0, RedDot:0, SyncOnce:1` ```json theme={null} { "type": 1005, "content": "{0} changed the group announcement to \"This is a group announcement\"", "extra": [ {"uid": "xxx", "name": "John"} ] } ``` #### Recall Message Message settings: `NoPersist:0, RedDot:0, SyncOnce:1` ```json theme={null} { "type": 1006, "message_id": "234343435", "content": "{0} recalled a message", "extra": [ {"uid": "xxx", "name": "John"} ] } ``` ### Command Messages #### Basic Command Message Message settings: `SyncOnce:1` ```json theme={null} { "type": 99, "cmd": "cmd", "param": {} } ``` #### Group Member Info Update Upon receiving this message, the client should incrementally sync group member information ```json theme={null} { "type": 99, "cmd": "memberUpdate", "param": { "group_no": "xxxx" } } ``` #### Red Dot Clear Upon receiving this command, the client should clear the red dot for the corresponding conversation ```json theme={null} { "type": 99, "cmd": "unreadClear", "param": { "channel_id": "xxxx", "channel_type": 2 } } ``` ### Usage Examples ```javascript JavaScript theme={null} // Send text message const textMessage = { type: 1, content: "This is a text message" }; const payload = btoa(JSON.stringify(textMessage)); const response = await fetch('/message/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ from_uid: "user123", channel_id: "group123", channel_type: 2, payload: payload }) }); ``` ```python Python theme={null} import base64 import json # Send image message image_message = { "type": 2, "url": "http://example.com/image.jpg", "width": 200, "height": 320 } payload = base64.b64encode( json.dumps(image_message).encode('utf-8') ).decode('utf-8') data = { "from_uid": "user123", "channel_id": "group123", "channel_type": 2, "payload": payload } ``` ```go Go theme={null} package main import ( "encoding/base64" "encoding/json" ) // Send voice message voiceMessage := map[string]interface{}{ "type": 4, "url": "http://example.com/voice.mp3", "timeTrad": 10, } contentBytes, _ := json.Marshal(voiceMessage) payload := base64.StdEncoding.EncodeToString(contentBytes) ``` ## Best Practices 1. **Message Deduplication**: Use unique client\_msg\_no to avoid duplicate sending 2. **Message Queue**: Add failed messages to retry queue 3. **Content Encoding**: Ensure payload is correctly Base64 encoded 4. **Permission Check**: Check if user has sending permission before sending 5. **Message Types**: Strictly follow protocol specifications for correct message type numbers 6. **System Messages**: System message types must be greater than 1000 with correct message flags 7. **Command Messages**: Command messages should set SyncOnce:1 flag # Single Message Search Source: https://wukong.mintlify.app/en/api/message/single-search POST /message Search for a single message by message ID ## Overview Search for a single message by message ID, suitable for scenarios where you need to retrieve specific message details. ## Request Body ### Required Parameters Message ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/message" \ -H "Content-Type: application/json" \ -d '{ "message_id": 123456789 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/message', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message_id: 123456789 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "message_id": 123456789 } response = requests.post('http://localhost:5001/message', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "message_id": 123456789, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/message", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "message_id": 123456789, "message_seq": 1001, "client_msg_no": "msg_123", "from_uid": "user123", "channel_id": "group123", "channel_type": 2, "timestamp": 1640995200, "payload": "SGVsbG8gV29ybGQ=" } ``` ```json Message Not Found theme={null} { "error": "Message not found" } ``` ## Response Fields Server-generated message ID Message sequence number Client message number Sender user ID Channel ID Channel type * `1` - Personal channel * `2` - Group channel Message timestamp (Unix timestamp) Base64 encoded message content ## Status Codes | Status Code | Description | | ----------- | ------------------------- | | 200 | Message search successful | | 400 | Request parameter error | | 404 | Message not found | | 500 | Internal server error | ## Use Cases ### Message Detail Retrieval **Get Specific Message Details**: ```javascript theme={null} // Retrieve details for a specific message async function getMessageDetail(messageId) { try { const response = await fetch('/message', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message_id: messageId }) }); if (response.status === 404) { return { found: false, message: null }; } const message = await response.json(); // Decode and format message const formattedMessage = { ...message, content: atob(message.payload), // Decode base64 content formatted_time: new Date(message.timestamp * 1000).toLocaleString(), found: true }; return formattedMessage; } catch (error) { console.error('Failed to get message detail:', error); return { found: false, message: null, error: error.message }; } } // Usage const messageDetail = await getMessageDetail(123456789); if (messageDetail.found) { console.log('Message content:', messageDetail.content); console.log('Sent by:', messageDetail.from_uid); console.log('Sent at:', messageDetail.formatted_time); } else { console.log('Message not found'); } ``` ### Message Verification **Verify Message Existence and Integrity**: ```javascript theme={null} // Verify that a message exists and check its properties async function verifyMessage(messageId, expectedProperties = {}) { try { const result = await getMessageDetail(messageId); if (!result.found) { return { exists: false, verified: false, message: 'Message not found' }; } const message = result; const verification = { exists: true, verified: true, checks: {}, message: message }; // Verify expected properties for (const [key, expectedValue] of Object.entries(expectedProperties)) { const actualValue = message[key]; const matches = actualValue === expectedValue; verification.checks[key] = { expected: expectedValue, actual: actualValue, matches: matches }; if (!matches) { verification.verified = false; } } return verification; } catch (error) { return { exists: false, verified: false, message: error.message }; } } // Usage const verification = await verifyMessage(123456789, { from_uid: 'user123', channel_id: 'group123', channel_type: 2 }); if (verification.verified) { console.log('Message verified successfully'); } else { console.log('Verification failed:', verification.checks); } ``` ### Message Reference Resolution **Resolve Message References**: ```javascript theme={null} // Resolve message references in replies or quotes async function resolveMessageReference(referenceId) { try { const message = await getMessageDetail(referenceId); if (!message.found) { return { resolved: false, reference: null, display: '[Message not found]' }; } // Create a display-friendly reference const reference = { id: message.message_id, sender: message.from_uid, content: message.content.substring(0, 100), // Truncate for preview timestamp: message.timestamp, channel: message.channel_id, display: `${message.from_uid}: ${message.content.substring(0, 50)}...` }; return { resolved: true, reference: reference, display: reference.display }; } catch (error) { return { resolved: false, reference: null, display: '[Error loading message]' }; } } // Usage in message rendering async function renderMessageWithReferences(messageText) { // Find message references in format @msg:123456789 const referencePattern = /@msg:(\d+)/g; let match; const references = []; while ((match = referencePattern.exec(messageText)) !== null) { const messageId = parseInt(match[1]); const reference = await resolveMessageReference(messageId); references.push({ original: match[0], messageId: messageId, reference: reference }); } // Replace references with display text let renderedText = messageText; for (const ref of references) { renderedText = renderedText.replace(ref.original, ref.reference.display); } return { originalText: messageText, renderedText: renderedText, references: references }; } ``` ### Message Audit and Logging **Audit Message Access**: ```javascript theme={null} // Audit message access for security and compliance class MessageAuditor { constructor() { this.accessLog = []; } async getMessageWithAudit(messageId, accessorUserId, reason = 'general_access') { const startTime = Date.now(); try { const message = await getMessageDetail(messageId); const endTime = Date.now(); // Log successful access this.logAccess({ message_id: messageId, accessor_user_id: accessorUserId, reason: reason, success: message.found, timestamp: new Date().toISOString(), response_time_ms: endTime - startTime, message_found: message.found, channel_id: message.found ? message.channel_id : null, sender_uid: message.found ? message.from_uid : null }); return message; } catch (error) { const endTime = Date.now(); // Log failed access this.logAccess({ message_id: messageId, accessor_user_id: accessorUserId, reason: reason, success: false, timestamp: new Date().toISOString(), response_time_ms: endTime - startTime, error: error.message }); throw error; } } logAccess(logEntry) { this.accessLog.push(logEntry); // Send to audit system this.sendToAuditSystem(logEntry); // Clean up old logs (keep last 1000) if (this.accessLog.length > 1000) { this.accessLog = this.accessLog.slice(-1000); } } async sendToAuditSystem(logEntry) { try { // Send to external audit system await fetch('/audit/message-access', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(logEntry) }); } catch (error) { console.error('Failed to send audit log:', error); } } getAccessStats() { const stats = { total_accesses: this.accessLog.length, successful_accesses: this.accessLog.filter(log => log.success).length, failed_accesses: this.accessLog.filter(log => !log.success).length, unique_messages: new Set(this.accessLog.map(log => log.message_id)).size, unique_accessors: new Set(this.accessLog.map(log => log.accessor_user_id)).size, avg_response_time: this.accessLog.reduce((sum, log) => sum + log.response_time_ms, 0) / this.accessLog.length }; return stats; } } // Usage const auditor = new MessageAuditor(); // Access message with audit trail const message = await auditor.getMessageWithAudit( 123456789, 'admin_user', 'compliance_review' ); // Get audit statistics const stats = auditor.getAccessStats(); console.log('Audit stats:', stats); ``` ### Message Cache with Single Lookup **Efficient Single Message Caching**: ```javascript theme={null} // Cache for single message lookups class SingleMessageCache { constructor(maxSize = 500, ttlMinutes = 30) { this.cache = new Map(); this.maxSize = maxSize; this.ttl = ttlMinutes * 60 * 1000; // Convert to milliseconds } async getMessage(messageId) { // Check cache first const cached = this.cache.get(messageId); if (cached && this.isValid(cached)) { cached.lastAccessed = Date.now(); return cached.message; } // Fetch from server try { const message = await getMessageDetail(messageId); // Cache the result (even if not found) this.addToCache(messageId, message); return message; } catch (error) { // Cache error result temporarily this.addToCache(messageId, { found: false, error: error.message }, 5 * 60 * 1000); // 5 minute TTL for errors throw error; } } addToCache(messageId, message, customTtl = null) { // Remove oldest if cache is full if (this.cache.size >= this.maxSize) { this.evictOldest(); } const cacheEntry = { message: message, timestamp: Date.now(), lastAccessed: Date.now(), ttl: customTtl || this.ttl }; this.cache.set(messageId, cacheEntry); } isValid(cacheEntry) { const age = Date.now() - cacheEntry.timestamp; return age < cacheEntry.ttl; } evictOldest() { let oldestKey = null; let oldestTime = Date.now(); for (const [key, entry] of this.cache) { if (entry.lastAccessed < oldestTime) { oldestTime = entry.lastAccessed; oldestKey = key; } } if (oldestKey) { this.cache.delete(oldestKey); } } getCacheStats() { const now = Date.now(); const validEntries = Array.from(this.cache.values()).filter(entry => this.isValid(entry) ); return { total_entries: this.cache.size, valid_entries: validEntries.length, expired_entries: this.cache.size - validEntries.length, hit_rate: this.hitRate || 0, memory_usage: this.cache.size / this.maxSize * 100 }; } } // Usage const messageCache = new SingleMessageCache(1000, 60); // 1000 messages, 60 minute TTL // Get message (will cache result) const message1 = await messageCache.getMessage(123456789); console.log('First access:', message1.found); // Get same message (will use cache) const message2 = await messageCache.getMessage(123456789); console.log('Second access (cached):', message2.found); console.log('Cache stats:', messageCache.getCacheStats()); ``` ## Best Practices 1. **Error Handling**: Always handle 404 responses when message might not exist 2. **Caching**: Cache frequently accessed messages to reduce API calls 3. **Validation**: Validate message ID format before making requests 4. **Security**: Implement proper access controls for message retrieval 5. **Audit Trail**: Log message access for security and compliance 6. **Performance**: Use batch search for multiple messages instead of individual calls 7. **Content Decoding**: Remember to decode base64 payload content for display # User Message Search Source: https://wukong.mintlify.app/en/api/message/user-search POST /plugins/wk.plugin.search/usersearch Search all messages belonging to the current user, supporting multi-dimensional search and Chinese word segmentation ## Overview Search all messages belonging to the current user, supporting multi-dimensional search and Chinese word segmentation functionality. * Requires WuKongIM v2.1.3-20250210 or above * Requires installation of `wk.plugin.search` plugin * Plugin usage documentation: [Plugin Development Guide](/en/getting-started/learning/plugin-development) ## Request Body ### Required Parameters Current user UID (restricts search to specified user's messages) ### Optional Parameters Message payload, supports searching custom fields Message content search keywords Message type search Message type value Sender UID Channel ID, when specified, only search messages within this channel Channel type * `1` - Personal channel * `2` - Group channel Search by topic Query limit, default 10 Page number for pagination, default 1 Message time (start), Unix timestamp Message time (end, result includes end\_time), Unix timestamp Keyword fields that need highlighting Field name, e.g., "payload.content" ```bash cURL theme={null} curl -X POST "http://localhost:5001/plugins/wk.plugin.search/usersearch" \ -H "Content-Type: application/json" \ -d '{ "uid": "user123", "payload": { "content": "Beijing" }, "payload_types": [1, 2], "channel_type": 2, "limit": 10, "page": 1, "highlights": ["payload.content"] }' ``` ```javascript JavaScript theme={null} const searchParams = { uid: "user123", payload: { content: "Beijing" }, payload_types: [1, 2], channel_type: 2, limit: 10, page: 1, highlights: ["payload.content"] }; const response = await fetch('http://localhost:5001/plugins/wk.plugin.search/usersearch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(searchParams) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests search_params = { "uid": "user123", "payload": { "content": "Beijing" }, "payload_types": [1, 2], "channel_type": 2, "limit": 10, "page": 1, "highlights": ["payload.content"] } response = requests.post( 'http://localhost:5001/plugins/wk.plugin.search/usersearch', json=search_params ) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { searchParams := map[string]interface{}{ "uid": "user123", "payload": map[string]interface{}{ "content": "Beijing", }, "payload_types": []int{1, 2}, "channel_type": 2, "limit": 10, "page": 1, "highlights": []string{"payload.content"}, } jsonData, _ := json.Marshal(searchParams) resp, err := http.Post( "http://localhost:5001/plugins/wk.plugin.search/usersearch", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "total": 25, "limit": 10, "page": 1, "messages": [ { "message_id": 1234, "message_idstr": "1234", "message_seq": 1, "client_msg_no": "djzdfdfdf", "from_uid": "u1", "channel_id": "g1", "channel_type": 2, "payload": { "type": 1, "content": "Are you from Beijing University?" }, "topic": "", "timestamp": 762834 }, { "message_id": 1235, "message_idstr": "1235", "message_seq": 2, "client_msg_no": "djzdfdfde", "from_uid": "u2", "channel_id": "g1", "channel_type": 2, "payload": { "type": 1, "content": "I work in Beijing" }, "topic": "", "timestamp": 762835 } ] } ``` ## Response Fields Total number of messages Query limit Current page number Message list Message unique ID Message unique ID (string format) Message sequence number Client message unique number Sender UID Channel ID Channel type Message content object Message type Message content (may contain highlight tags) Message topic Message timestamp (10-digit seconds) ## Status Codes | Status Code | Description | | ----------- | ----------------------- | | 200 | Search successful | | 400 | Request parameter error | | 403 | No search permission | | 500 | Internal server error | ## Search Features ### Chinese Word Segmentation Supports Chinese word segmentation, intelligently recognizing Chinese vocabulary for search. **Examples**: * Searching "Beijing University" can match messages containing "Beijing" or "University" * Supports both fuzzy matching and exact matching ### Multi-dimensional Search Supports combined search across multiple dimensions: 1. **Content Search**: Search message content through `payload.content` 2. **Type Search**: Limit message types through `payload_types` 3. **User Search**: Search specific user's messages through `from_uid` 4. **Channel Search**: Search specific channel's messages through `channel_id` 5. **Time Search**: Limit time range through `start_time` and `end_time` 6. **Topic Search**: Search specific topic messages through `topic` ### Highlighting Through the `highlights` parameter, you can specify fields that need highlighting. Matching keywords in search results will be surrounded by `` tags. **Example**: ```json theme={null} { "payload": { "content": "Beijing" }, "highlights": ["payload.content"] } ``` Return result: ```json theme={null} { "payload": { "content": "Are you from Beijing University?" } } ``` ## Use Cases ### Chat History Search * **Keyword Search**: Users search for keywords in chat history * **User Messages**: Search messages sent by specific users * **Group Messages**: Search messages within specific groups ### Content Management * **Message Moderation**: Search messages containing specific content * **Data Analysis**: Analyze user message content and behavior * **Compliance Check**: Check for sensitive content ### Advanced Search Examples **Search by Time Range**: ```javascript theme={null} const timeRangeSearch = { uid: "user123", payload: { content: "project" }, start_time: 1640995200, // 2022-01-01 end_time: 1672531200, // 2023-01-01 limit: 20 }; ``` **Search by Message Type**: ```javascript theme={null} const typeSearch = { uid: "user123", payload_types: [1, 2], // Text and image messages only channel_id: "group123", limit: 50 }; ``` **Search with Multiple Criteria**: ```javascript theme={null} const complexSearch = { uid: "user123", payload: { content: "meeting" }, from_uid: "manager123", channel_type: 2, topic: "work", highlights: ["payload.content"], limit: 10 }; ``` ## Best Practices 1. **Pagination**: Use appropriate page size to avoid performance issues 2. **Time Limits**: Set reasonable time ranges for better performance 3. **Keyword Optimization**: Use specific keywords for more accurate results 4. **Result Caching**: Cache search results for frequently used queries 5. **Permission Check**: Ensure users can only search their own messages 6. **Rate Limiting**: Implement rate limiting to prevent search abuse # Get Connection Information Source: https://wukong.mintlify.app/en/api/monitoring/connections GET /connz Get current server connection statistics including connection count and user distribution ## Overview Get current server connection statistics, including connection count, user distribution and other monitoring data. ## Query Parameters Offset for pagination Limit for pagination Whether to include subscription information * `0` - Do not include subscription information * `1` - Include subscription information ```bash cURL theme={null} curl -X GET "http://localhost:5001/connz?offset=0&limit=50&subs=1" ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/connz?offset=0&limit=50&subs=1'); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests params = { 'offset': 0, 'limit': 50, 'subs': 1 } response = requests.get('http://localhost:5001/connz', params=params) data = response.json() print(data) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "net/http" ) func main() { resp, err := http.Get("http://localhost:5001/connz?offset=0&limit=50&subs=1") if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "now": "2024-01-15T10:30:00Z", "num_connections": 1250, "total": 1250, "offset": 0, "limit": 50, "connections": [ { "cid": 12345, "uid": "user123", "ip": "192.168.1.100", "port": 54321, "start": "2024-01-15T09:15:30Z", "last_activity": "2024-01-15T10:29:45Z", "uptime": "1h14m15s", "idle": "15s", "pending_bytes": 0, "in_msgs": 156, "out_msgs": 203, "in_bytes": 15680, "out_bytes": 25440, "subscriptions": 8, "device_flag": 1, "device_level": 1, "version": "1.0.0" } ] } ``` ## Response Fields Current server time (ISO 8601 format) Current number of connections Total number of connections Current offset Current limit Connection details list Connection ID User ID Client IP address Client port Connection start time Last activity time Connection duration Idle time Pending bytes to send Number of received messages Number of sent messages Number of received bytes Number of sent bytes Number of subscriptions Device flag Device level Client version ## Status Codes | Status Code | Description | | ----------- | --------------------------------------------- | | 200 | Successfully retrieved connection information | | 500 | Internal server error | ## Best Practices 1. **Paginated Queries**: Use pagination for large numbers of connections to avoid performance issues 2. **Regular Monitoring**: Set reasonable monitoring intervals, avoid excessive frequency 3. **Alert Mechanisms**: Set alerts for key metrics like connection count and activity 4. **Data Export**: Support export and analysis of connection data 5. **Performance Optimization**: Monitor connection performance metrics to identify issues promptly 6. **Security Monitoring**: Pay attention to connections from abnormal IPs # Get System Variables Source: https://wukong.mintlify.app/en/api/monitoring/variables GET /varz Get system variables and performance metrics ## Overview Get WuKongIM system runtime variables and performance metrics for system monitoring and performance analysis. ## Query Parameters Sort field * `in_msgs` - Sort by received message count * `out_msgs` - Sort by sent message count * `in_bytes` - Sort by received bytes * `out_bytes` - Sort by sent bytes Connection information limit count Specify node ID (cluster environment) ```bash cURL theme={null} curl -X GET "http://localhost:5001/varz?sort=in_msgs&conn_limit=50" ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/varz?sort=in_msgs&conn_limit=50'); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests params = { 'sort': 'in_msgs', 'conn_limit': 50 } response = requests.get('http://localhost:5001/varz', params=params) data = response.json() print(data) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "net/http" ) func main() { resp, err := http.Get("http://localhost:5001/varz?sort=in_msgs&conn_limit=50") if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "server_id": "wukongim-node-1", "version": "2.0.0", "git_commit": "abc123def", "go_version": "go1.21.0", "start": "2024-01-15T08:00:00Z", "now": "2024-01-15T10:30:00Z", "uptime": "2h30m0s", "connections": 1250, "total_connections": 15000, "in_msgs": 125000, "out_msgs": 130000, "in_bytes": 12500000, "out_bytes": 13000000, "slow_consumers": 5, "subscriptions": 8500, "http_req_stats": { "uri_stats": [ { "uri": "/message/send", "count": 5000, "avg_time": "15ms" }, { "uri": "/channel/create", "count": 200, "avg_time": "25ms" } ] }, "cpu": 15.5, "mem": 536870912, "config": { "max_connections": 10000, "max_subscriptions_per_conn": 100, "max_payload": 1048576 } } ``` ## Response Fields ### Server Information Server identifier WuKongIM version number Git commit hash Go language version ### Runtime Information Server start time (ISO 8601 format) Current time (ISO 8601 format) Runtime duration ### Connection Statistics Current connection count Total connections (historical cumulative) Number of slow consumers Total subscriptions ### Message Statistics Total received messages Total sent messages Total received bytes Total sent bytes ### HTTP Request Statistics HTTP request statistics URI statistics list Request URI Request count Average response time ### System Resources CPU usage (percentage) Memory usage (bytes) ### Configuration Information System configuration information Maximum connection limit Maximum subscriptions per connection Maximum message payload size (bytes) ## Status Codes | Status Code | Description | | ----------- | --------------------------------------- | | 200 | Successfully retrieved system variables | | 500 | Internal server error | ## Monitoring Metrics Description ### Performance Metrics | Metric | Description | Normal Range | Alert Threshold | | -------------- | ------------------------------------- | --------------- | --------------- | | CPU Usage | Server CPU utilization percentage | \< 70% | > 80% | | Memory Usage | Server memory consumption | \< 80% | > 90% | | Connections | Current active connections | Based on config | Near maximum | | Slow Consumers | Number of slow processing connections | \< 5% | > 10% | ### Throughput Metrics | Metric | Description | Monitoring Focus | | -------------------- | ------------------------------------ | ------------------------------------ | | Message Receive Rate | Messages received per second | Sudden increases or decreases | | Message Send Rate | Messages sent per second | Ratio to receive rate | | Byte Transfer Rate | Network transfer speed | Bandwidth usage | | HTTP Request Stats | API call frequency and response time | Hot APIs and performance bottlenecks | ## Best Practices 1. **Regular Monitoring**: Recommended to get system variables every 30-60 seconds 2. **Alert Setup**: Set reasonable alert thresholds for key metrics 3. **Trend Analysis**: Record historical data to analyze system performance trends 4. **Capacity Planning**: Perform capacity planning based on monitoring data 5. **Performance Optimization**: Identify performance bottlenecks and optimize 6. **Cluster Monitoring**: Monitor all node status in cluster environments # Batch Get User IM Addresses Source: https://wukong.mintlify.app/en/api/route/batch-address POST /route/batch Batch get IM connection addresses for multiple users ## Overview Batch get IM connection addresses for multiple users, used to assign different connection nodes for different users. ## Query Parameters Whether to return intranet addresses * `0` - Return external network addresses * `1` - Return internal network addresses ## Request Body Array of user IDs User ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/route/batch?intranet=0" \ -H "Content-Type: application/json" \ -d '["user1", "user2", "user3"]' ``` ```javascript JavaScript theme={null} const userIds = ["user1", "user2", "user3"]; const response = await fetch('http://localhost:5001/route/batch?intranet=0', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userIds) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests user_ids = ["user1", "user2", "user3"] response = requests.post( 'http://localhost:5001/route/batch', params={'intranet': 0}, json=user_ids ) data = response.json() print(data) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { userIds := []string{"user1", "user2", "user3"} jsonData, _ := json.Marshal(userIds) resp, err := http.Post( "http://localhost:5001/route/batch?intranet=0", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result []map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} [ { "uids": ["user1", "user2"], "tcp_addr": "127.0.0.1:5100", "ws_addr": "ws://127.0.0.1:5200", "wss_addr": "wss://127.0.0.1:5300" }, { "uids": ["user3"], "tcp_addr": "127.0.0.1:5101", "ws_addr": "ws://127.0.0.1:5201", "wss_addr": "wss://127.0.0.1:5301" } ] ``` ## Response Fields The response is an array, each element contains the following fields: List of user IDs assigned to this address TCP connection address, format: `host:port` WebSocket connection address, format: `ws://host:port` WebSocket Secure connection address, format: `wss://host:port` ## Status Codes | Status Code | Description | | ----------- | ---------------------------------------------------- | | 200 | Successfully retrieved batch IM connection addresses | | 400 | Request parameter error | | 500 | Internal server error | ## Use Cases ### Multi-User Connection Setup **Batch Connect Multiple Users**: ```javascript theme={null} // Connect multiple users to their assigned nodes async function batchConnectUsers(userIds) { try { // Get connection addresses for all users const response = await fetch('/route/batch?intranet=0', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userIds) }); const addressGroups = await response.json(); const connections = []; // Connect users to their assigned nodes for (const group of addressGroups) { const connectionUrl = window.location.protocol === 'https:' ? group.wss_addr : group.ws_addr; for (const uid of group.uids) { try { const connection = await connectUser(uid, connectionUrl); connections.push({ uid: uid, connection: connection, node_address: connectionUrl, success: true }); } catch (error) { connections.push({ uid: uid, connection: null, node_address: connectionUrl, success: false, error: error.message }); } } } return connections; } catch (error) { console.error('Batch user connection failed:', error); throw error; } } // Usage const userIds = ['user1', 'user2', 'user3', 'user4', 'user5']; const connections = await batchConnectUsers(userIds); const successful = connections.filter(c => c.success); const failed = connections.filter(c => !c.success); console.log(`Connected ${successful.length} users, ${failed.length} failed`); ``` ### Load Distribution **Distribute Users Across Nodes**: ```javascript theme={null} // Distribute users across available nodes for load balancing class UserLoadDistributor { constructor() { this.nodeUserMap = new Map(); } async distributeUsers(userIds, batchSize = 50) { const batches = this.chunkArray(userIds, batchSize); const allDistributions = []; for (const batch of batches) { try { const distributions = await this.getBatchDistribution(batch); allDistributions.push(...distributions); // Track node assignments this.trackNodeAssignments(distributions); } catch (error) { console.error('Failed to distribute batch:', error); } } return allDistributions; } async getBatchDistribution(userIds) { const response = await fetch('/route/batch?intranet=0', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userIds) }); return await response.json(); } trackNodeAssignments(distributions) { for (const dist of distributions) { const nodeKey = dist.ws_addr; if (!this.nodeUserMap.has(nodeKey)) { this.nodeUserMap.set(nodeKey, []); } this.nodeUserMap.get(nodeKey).push(...dist.uids); } } getNodeStatistics() { const stats = []; for (const [nodeAddr, users] of this.nodeUserMap) { stats.push({ node_address: nodeAddr, user_count: users.length, users: users }); } return stats; } chunkArray(array, size) { const chunks = []; for (let i = 0; i < array.length; i += size) { chunks.push(array.slice(i, i + size)); } return chunks; } } // Usage const distributor = new UserLoadDistributor(); const userIds = Array.from({length: 1000}, (_, i) => `user${i + 1}`); const distributions = await distributor.distributeUsers(userIds); const stats = distributor.getNodeStatistics(); console.log('Node distribution statistics:', stats); ``` ### Connection Pool Management **Manage Connection Pools by Node**: ```javascript theme={null} // Manage connection pools for different nodes class NodeConnectionPoolManager { constructor() { this.pools = new Map(); this.userNodeMap = new Map(); } async initializePools(userIds) { // Get node assignments for users const distributions = await this.getBatchAddresses(userIds); // Create connection pools for each node for (const dist of distributions) { const nodeKey = dist.ws_addr; if (!this.pools.has(nodeKey)) { this.pools.set(nodeKey, { address: dist.ws_addr, tcp_addr: dist.tcp_addr, wss_addr: dist.wss_addr, connections: new Map(), userCount: 0 }); } const pool = this.pools.get(nodeKey); // Track user assignments for (const uid of dist.uids) { this.userNodeMap.set(uid, nodeKey); pool.userCount++; } } console.log(`Initialized ${this.pools.size} connection pools`); } async getBatchAddresses(userIds) { const response = await fetch('/route/batch?intranet=0', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userIds) }); return await response.json(); } async connectUser(userId) { const nodeKey = this.userNodeMap.get(userId); if (!nodeKey) { throw new Error(`No node assignment found for user ${userId}`); } const pool = this.pools.get(nodeKey); if (!pool) { throw new Error(`No connection pool found for node ${nodeKey}`); } // Check if user already has a connection if (pool.connections.has(userId)) { return pool.connections.get(userId); } // Create new connection try { const connection = new WebSocket(pool.address); await new Promise((resolve, reject) => { connection.onopen = resolve; connection.onerror = reject; setTimeout(reject, 5000); // 5 second timeout }); pool.connections.set(userId, connection); console.log(`User ${userId} connected to node ${nodeKey}`); return connection; } catch (error) { console.error(`Failed to connect user ${userId} to node ${nodeKey}:`, error); throw error; } } disconnectUser(userId) { const nodeKey = this.userNodeMap.get(userId); if (!nodeKey) return; const pool = this.pools.get(nodeKey); if (!pool) return; const connection = pool.connections.get(userId); if (connection) { connection.close(); pool.connections.delete(userId); console.log(`User ${userId} disconnected from node ${nodeKey}`); } } getPoolStatistics() { const stats = []; for (const [nodeKey, pool] of this.pools) { stats.push({ node_address: nodeKey, assigned_users: pool.userCount, active_connections: pool.connections.size, connection_rate: (pool.connections.size / pool.userCount * 100).toFixed(2) + '%' }); } return stats; } } // Usage const poolManager = new NodeConnectionPoolManager(); // Initialize with user list const userIds = ['user1', 'user2', 'user3', 'user4', 'user5']; await poolManager.initializePools(userIds); // Connect users for (const userId of userIds) { try { await poolManager.connectUser(userId); } catch (error) { console.error(`Failed to connect ${userId}:`, error); } } // Get statistics const stats = poolManager.getPoolStatistics(); console.log('Pool statistics:', stats); ``` ### Geographic Distribution **Route Users by Geographic Location**: ```javascript theme={null} // Route users to geographically optimal nodes async function routeUsersByLocation(userLocations) { const userIds = userLocations.map(ul => ul.userId); try { // Get node assignments const distributions = await fetch('/route/batch?intranet=0', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userIds) }).then(r => r.json()); // Analyze geographic distribution const geoAnalysis = []; for (const dist of distributions) { const nodeUsers = dist.uids.map(uid => userLocations.find(ul => ul.userId === uid) ); const avgLatitude = nodeUsers.reduce((sum, u) => sum + u.latitude, 0) / nodeUsers.length; const avgLongitude = nodeUsers.reduce((sum, u) => sum + u.longitude, 0) / nodeUsers.length; geoAnalysis.push({ node_address: dist.ws_addr, users: dist.uids, user_count: dist.uids.length, avg_location: { latitude: avgLatitude, longitude: avgLongitude }, user_locations: nodeUsers.map(u => ({ userId: u.userId, latitude: u.latitude, longitude: u.longitude, country: u.country })) }); } return geoAnalysis; } catch (error) { console.error('Geographic routing failed:', error); throw error; } } // Usage const userLocations = [ { userId: 'user1', latitude: 40.7128, longitude: -74.0060, country: 'US' }, { userId: 'user2', latitude: 51.5074, longitude: -0.1278, country: 'UK' }, { userId: 'user3', latitude: 35.6762, longitude: 139.6503, country: 'JP' }, { userId: 'user4', latitude: 39.9042, longitude: 116.4074, country: 'CN' }, { userId: 'user5', latitude: 52.5200, longitude: 13.4050, country: 'DE' } ]; const geoDistribution = await routeUsersByLocation(userLocations); console.log('Geographic distribution:', geoDistribution); ``` ## Best Practices 1. **Batch Size**: Use appropriate batch sizes to balance performance and resource usage 2. **Error Handling**: Handle partial failures gracefully when some users can't be routed 3. **Caching**: Cache node assignments to reduce API calls for frequently accessed users 4. **Load Monitoring**: Monitor node load distribution and adjust routing as needed 5. **Failover**: Implement failover mechanisms when assigned nodes become unavailable 6. **Geographic Optimization**: Consider user geographic location for optimal routing 7. **Connection Pooling**: Use connection pools to efficiently manage multiple user connections # Get User IM Address Source: https://wukong.mintlify.app/en/api/route/get-address GET /route Get the IM connection address for users ## Overview Get the IM connection address for users, including TCP, WebSocket, and WebSocket Secure addresses. ## Query Parameters Whether to return intranet address * `0` - Return external network address * `1` - Return internal network address ```bash cURL theme={null} curl -X GET "http://localhost:5001/route?intranet=0" ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/route?intranet=0'); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get('http://localhost:5001/route', params={'intranet': 0}) data = response.json() print(data) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "net/http" ) func main() { resp, err := http.Get("http://localhost:5001/route?intranet=0") if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "tcp_addr": "127.0.0.1:5100", "ws_addr": "ws://127.0.0.1:5200", "wss_addr": "wss://127.0.0.1:5300" } ``` ## Response Fields TCP connection address, format: `host:port` WebSocket connection address, format: `ws://host:port` WebSocket Secure connection address, format: `wss://host:port` ## Status Codes | Status Code | Description | | ----------- | -------------------------------------------- | | 200 | Successfully retrieved IM connection address | | 500 | Internal server error | ## Use Cases ### Client Connection Setup **Dynamic Connection Discovery**: ```javascript theme={null} // Get connection addresses and establish connection async function connectToWuKongIM() { try { // Get connection addresses const addresses = await fetch('/route?intranet=0').then(r => r.json()); // Choose appropriate connection type based on environment let connectionUrl; if (window.location.protocol === 'https:') { connectionUrl = addresses.wss_addr; } else { connectionUrl = addresses.ws_addr; } // Establish WebSocket connection const ws = new WebSocket(connectionUrl); ws.onopen = () => { console.log('Connected to WuKongIM:', connectionUrl); }; return ws; } catch (error) { console.error('Failed to connect to WuKongIM:', error); } } ``` ### Load Balancing **Multiple Server Discovery**: ```javascript theme={null} // Get addresses from multiple servers for load balancing async function discoverWuKongIMServers(serverList) { const availableServers = []; for (const server of serverList) { try { const response = await fetch(`${server}/route?intranet=0`); const addresses = await response.json(); availableServers.push({ server: server, addresses: addresses, priority: calculateServerPriority(server) }); } catch (error) { console.warn(`Server ${server} is not available:`, error); } } // Sort by priority and return best server availableServers.sort((a, b) => b.priority - a.priority); return availableServers[0]?.addresses; } ``` ### Environment-based Connection **Internal vs External Network**: ```javascript theme={null} // Choose connection type based on network environment async function getOptimalConnection() { try { // Try internal network first (faster) const internalAddresses = await fetch('/route?intranet=1').then(r => r.json()); // Test internal connectivity const isInternalReachable = await testConnectivity(internalAddresses.ws_addr); if (isInternalReachable) { return internalAddresses; } else { // Fallback to external network const externalAddresses = await fetch('/route?intranet=0').then(r => r.json()); return externalAddresses; } } catch (error) { console.error('Failed to get optimal connection:', error); throw error; } } async function testConnectivity(wsUrl) { return new Promise((resolve) => { const testWs = new WebSocket(wsUrl); const timeout = setTimeout(() => { testWs.close(); resolve(false); }, 3000); testWs.onopen = () => { clearTimeout(timeout); testWs.close(); resolve(true); }; testWs.onerror = () => { clearTimeout(timeout); resolve(false); }; }); } ``` ### Connection Failover **Automatic Failover**: ```javascript theme={null} class WuKongIMConnector { constructor(serverUrls) { this.serverUrls = serverUrls; this.currentServerIndex = 0; this.connection = null; } async connect() { for (let i = 0; i < this.serverUrls.length; i++) { try { const serverUrl = this.serverUrls[this.currentServerIndex]; const addresses = await this.getAddresses(serverUrl); this.connection = await this.establishConnection(addresses); console.log(`Connected to server: ${serverUrl}`); return this.connection; } catch (error) { console.warn(`Failed to connect to server ${this.currentServerIndex}:`, error); this.currentServerIndex = (this.currentServerIndex + 1) % this.serverUrls.length; } } throw new Error('Failed to connect to any WuKongIM server'); } async getAddresses(serverUrl) { const response = await fetch(`${serverUrl}/route?intranet=0`); return await response.json(); } async establishConnection(addresses) { return new Promise((resolve, reject) => { const ws = new WebSocket(addresses.ws_addr); ws.onopen = () => resolve(ws); ws.onerror = (error) => reject(error); setTimeout(() => reject(new Error('Connection timeout')), 5000); }); } } // Usage const connector = new WuKongIMConnector([ 'http://server1.example.com:5001', 'http://server2.example.com:5001', 'http://server3.example.com:5001' ]); const connection = await connector.connect(); ``` ### Mobile App Integration **Platform-specific Connection**: ```javascript theme={null} // React Native example import { Platform } from 'react-native'; async function getMobileConnection() { try { const addresses = await fetch('/route?intranet=0').then(r => r.json()); // Use appropriate connection for mobile platforms if (Platform.OS === 'ios' || Platform.OS === 'android') { // Mobile apps typically use WebSocket return addresses.ws_addr; } else { // Web apps use secure WebSocket if available return window.location.protocol === 'https:' ? addresses.wss_addr : addresses.ws_addr; } } catch (error) { console.error('Failed to get mobile connection:', error); throw error; } } ``` ## Best Practices 1. **Connection Type Selection**: Choose appropriate connection type based on environment (HTTP/HTTPS) 2. **Failover Strategy**: Implement failover mechanism for high availability 3. **Network Detection**: Detect internal vs external network for optimal performance 4. **Connection Testing**: Test connectivity before establishing full connection 5. **Caching**: Cache connection addresses to reduce API calls 6. **Error Handling**: Handle network errors gracefully with retry logic 7. **Security**: Use secure connections (WSS) in production environments # Health Check Source: https://wukong.mintlify.app/en/api/system/health GET /health Check the health status of WuKongIM server and cluster ## Overview The health check endpoint is used to monitor the operational status of WuKongIM server and cluster, ensuring the system is running normally. ```bash cURL theme={null} curl -X GET "http://localhost:5001/health" ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/health'); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get('http://localhost:5001/health') data = response.json() print(data) ``` ```go Go theme={null} package main import ( "fmt" "io" "net/http" ) func main() { resp, err := http.Get("http://localhost:5001/health") if err != nil { panic(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { panic(err) } fmt.Println(string(body)) } ``` ```json Success Response (200) theme={null} { "status": "ok" } ``` ```json Error Response (500) theme={null} { "status": "error", "message": "Cluster status check failed" } ``` ## Response Fields Health status: `ok` indicates normal, `error` indicates abnormal Error message (only appears when status is error) ## Status Codes | Status Code | Description | | ----------- | ------------------------------------ | | 200 | Server health status is normal | | 500 | Server or cluster status is abnormal | ## Use Cases ### Load Balancer Health Checks **Nginx Configuration**: ```nginx theme={null} upstream wukongim_backend { server 192.168.1.10:5001; server 192.168.1.11:5001; server 192.168.1.12:5001; } server { location /health { proxy_pass http://wukongim_backend/health; proxy_connect_timeout 5s; proxy_read_timeout 5s; } location / { proxy_pass http://wukongim_backend; # Health check configuration health_check uri=/health interval=30s fails=3 passes=2; } } ``` **HAProxy Configuration**: ```haproxy theme={null} backend wukongim_servers balance roundrobin option httpchk GET /health http-check expect status 200 server wk1 192.168.1.10:5001 check inter 30s server wk2 192.168.1.11:5001 check inter 30s server wk3 192.168.1.12:5001 check inter 30s ``` ### Container Orchestration **Docker Compose**: ```yaml theme={null} version: '3.7' services: wukongim: image: registry.cn-shanghai.aliyuncs.com/wukongim/wukongim:v2 healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:5001/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s ports: - "5001:5001" ``` **Kubernetes Deployment**: ```yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: wukongim spec: replicas: 3 selector: matchLabels: app: wukongim template: metadata: labels: app: wukongim spec: containers: - name: wukongim image: registry.cn-shanghai.aliyuncs.com/wukongim/wukongim:v2 ports: - containerPort: 5001 livenessProbe: httpGet: path: /health port: 5001 initialDelaySeconds: 30 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /health port: 5001 initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 ``` ### Monitoring and Alerting **Prometheus Monitoring**: ```yaml theme={null} # prometheus.yml scrape_configs: - job_name: 'wukongim-health' metrics_path: '/health' static_configs: - targets: ['192.168.1.10:5001', '192.168.1.11:5001', '192.168.1.12:5001'] scrape_interval: 30s scrape_timeout: 10s ``` **Custom Health Check Script**: ```bash theme={null} #!/bin/bash SERVERS=("192.168.1.10:5001" "192.168.1.11:5001" "192.168.1.12:5001") WEBHOOK_URL="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK" for server in "${SERVERS[@]}"; do response=$(curl -s -o /dev/null -w "%{http_code}" "http://$server/health" --max-time 10) if [ "$response" != "200" ]; then # Send alert curl -X POST -H 'Content-type: application/json' \ --data "{\"text\":\"🚨 WuKongIM Health Check Failed: $server returned $response\"}" \ "$WEBHOOK_URL" fi done ``` ### Application Integration **Service Discovery**: ```javascript theme={null} class WuKongIMServiceDiscovery { constructor(servers) { this.servers = servers; this.healthyServers = []; this.checkInterval = 30000; // 30 seconds this.startHealthChecks(); } async checkServerHealth(server) { try { const response = await fetch(`http://${server}/health`, { timeout: 5000 }); return response.status === 200; } catch (error) { console.error(`Health check failed for ${server}:`, error); return false; } } async updateHealthyServers() { const healthChecks = this.servers.map(async (server) => { const isHealthy = await this.checkServerHealth(server); return { server, isHealthy }; }); const results = await Promise.all(healthChecks); this.healthyServers = results .filter(result => result.isHealthy) .map(result => result.server); console.log('Healthy servers:', this.healthyServers); } startHealthChecks() { this.updateHealthyServers(); setInterval(() => { this.updateHealthyServers(); }, this.checkInterval); } getHealthyServer() { if (this.healthyServers.length === 0) { throw new Error('No healthy WuKongIM servers available'); } // Round-robin selection const server = this.healthyServers[Math.floor(Math.random() * this.healthyServers.length)]; return server; } } // Usage const discovery = new WuKongIMServiceDiscovery([ '192.168.1.10:5001', '192.168.1.11:5001', '192.168.1.12:5001' ]); ``` ## Best Practices 1. **Monitoring Frequency**: Recommended to check health status every 30-60 seconds 2. **Timeout Settings**: Set reasonable timeout values to avoid false alarms 3. **Load Balancing**: Can be used for load balancer health checks 4. **Container Orchestration**: Suitable for Docker and Kubernetes health check configurations 5. **Alerting Mechanism**: Integrate with monitoring systems for automated alerting 6. **Graceful Degradation**: Implement fallback mechanisms when health checks fail 7. **Circuit Breaker**: Use circuit breaker pattern to handle unhealthy services 8. **Logging**: Log health check results for troubleshooting and analysis # Get Migration Result Source: https://wukong.mintlify.app/en/api/system/migrate GET /migrate/result Get the result status of data migration process ## Overview Get the result status of data migration process, used to monitor the progress and status of data migration. ```bash cURL theme={null} curl -X GET "http://localhost:5001/migrate/result" ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/migrate/result'); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get('http://localhost:5001/migrate/result') data = response.json() print(data) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "net/http" ) func main() { resp, err := http.Get("http://localhost:5001/migrate/result") if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Migration Completed theme={null} { "status": "completed", "step": "message", "last_err": null, "try_count": 1 } ``` ```json Migration In Progress theme={null} { "status": "running", "step": "user", "last_err": null, "try_count": 2 } ``` ```json Migration Already Completed (Historical Status) theme={null} { "status": "migrated", "step": "channel", "last_err": null, "try_count": 1 } ``` ```json Migration Error theme={null} { "status": "running", "step": "message", "last_err": "Database connection timeout", "try_count": 3 } ``` ## Response Fields Migration status: * `running` - Migration is in progress * `completed` - Migration has completed * `migrated` - Migration has completed (historical status) * `failed` - Migration has failed Current migration step, e.g., `message`, `user`, `channel`, etc. Last error message, `null` if no error occurred Number of attempts made ## Status Codes | Status Code | Description | | ----------- | --------------------------------------- | | 200 | Successfully retrieved migration result | | 500 | Internal server error | ## Use Cases ### Migration Monitoring **Real-time Migration Monitoring**: ```javascript theme={null} // Monitor migration progress in real-time class MigrationMonitor { constructor(checkInterval = 5000) { this.checkInterval = checkInterval; this.isMonitoring = false; this.lastStatus = null; this.callbacks = { onProgress: null, onComplete: null, onError: null }; } async startMonitoring() { if (this.isMonitoring) return; this.isMonitoring = true; console.log('Starting migration monitoring...'); while (this.isMonitoring) { try { const status = await this.checkMigrationStatus(); await this.handleStatusUpdate(status); // Stop monitoring if migration is complete or failed if (status.status === 'completed' || status.status === 'migrated') { this.isMonitoring = false; if (this.callbacks.onComplete) { this.callbacks.onComplete(status); } break; } // Handle errors if (status.last_err) { if (this.callbacks.onError) { this.callbacks.onError(status); } } await this.delay(this.checkInterval); } catch (error) { console.error('Migration monitoring error:', error); if (this.callbacks.onError) { this.callbacks.onError({ error: error.message }); } await this.delay(this.checkInterval); } } } async checkMigrationStatus() { const response = await fetch('/migrate/result'); return await response.json(); } async handleStatusUpdate(status) { // Check if status has changed if (!this.lastStatus || this.hasStatusChanged(this.lastStatus, status)) { console.log(`Migration status: ${status.status}, step: ${status.step}, attempts: ${status.try_count}`); if (status.last_err) { console.warn(`Migration error: ${status.last_err}`); } if (this.callbacks.onProgress) { this.callbacks.onProgress(status); } this.lastStatus = status; } } hasStatusChanged(oldStatus, newStatus) { return oldStatus.status !== newStatus.status || oldStatus.step !== newStatus.step || oldStatus.try_count !== newStatus.try_count || oldStatus.last_err !== newStatus.last_err; } stopMonitoring() { this.isMonitoring = false; console.log('Migration monitoring stopped'); } onProgress(callback) { this.callbacks.onProgress = callback; } onComplete(callback) { this.callbacks.onComplete = callback; } onError(callback) { this.callbacks.onError = callback; } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } // Usage const monitor = new MigrationMonitor(3000); // Check every 3 seconds monitor.onProgress((status) => { updateProgressUI(status); }); monitor.onComplete((status) => { showCompletionNotification(status); }); monitor.onError((status) => { showErrorAlert(status); }); await monitor.startMonitoring(); ``` ### Migration Dashboard **Migration Dashboard Implementation**: ```javascript theme={null} // Create a comprehensive migration dashboard class MigrationDashboard { constructor() { this.migrationHistory = []; this.currentMigration = null; this.monitor = new MigrationMonitor(2000); this.setupEventHandlers(); } setupEventHandlers() { this.monitor.onProgress((status) => { this.updateCurrentMigration(status); this.renderDashboard(); }); this.monitor.onComplete((status) => { this.completeMigration(status); this.renderDashboard(); this.showNotification('Migration completed successfully!', 'success'); }); this.monitor.onError((status) => { this.handleMigrationError(status); this.renderDashboard(); this.showNotification(`Migration error: ${status.last_err || status.error}`, 'error'); }); } async startDashboard() { // Initialize dashboard this.renderDashboard(); // Check initial status try { const initialStatus = await this.monitor.checkMigrationStatus(); this.updateCurrentMigration(initialStatus); // Start monitoring if migration is in progress if (initialStatus.status === 'running') { await this.monitor.startMonitoring(); } } catch (error) { console.error('Failed to get initial migration status:', error); } } updateCurrentMigration(status) { this.currentMigration = { ...status, timestamp: new Date().toISOString(), progress: this.calculateProgress(status.step) }; } calculateProgress(step) { const steps = ['user', 'channel', 'message', 'conversation', 'cleanup']; const stepIndex = steps.indexOf(step); return stepIndex >= 0 ? ((stepIndex + 1) / steps.length) * 100 : 0; } completeMigration(status) { const completedMigration = { ...this.currentMigration, completedAt: new Date().toISOString(), duration: this.calculateDuration() }; this.migrationHistory.push(completedMigration); this.currentMigration = null; } handleMigrationError(status) { if (this.currentMigration) { this.currentMigration.hasError = true; this.currentMigration.errorDetails = status.last_err || status.error; } } calculateDuration() { if (!this.currentMigration || !this.currentMigration.timestamp) { return null; } const start = new Date(this.currentMigration.timestamp); const end = new Date(); return Math.round((end - start) / 1000); // Duration in seconds } renderDashboard() { const dashboardHTML = `

Migration Dashboard

${this.renderCurrentMigration()} ${this.renderMigrationHistory()} ${this.renderControls()}
`; document.getElementById('migration-dashboard').innerHTML = dashboardHTML; } renderCurrentMigration() { if (!this.currentMigration) { return '
No active migration
'; } const status = this.currentMigration; const progressBar = `
`; return `

Current Migration

Status: ${status.status}
Step: ${status.step}
Attempts: ${status.try_count}
${status.hasError ? `
Error: ${status.errorDetails}
` : ''}
Progress: ${status.progress.toFixed(1)}% ${progressBar}
`; } renderMigrationHistory() { if (this.migrationHistory.length === 0) { return '
No migration history
'; } const historyItems = this.migrationHistory.map(migration => `
${new Date(migration.completedAt).toLocaleString()}
Duration: ${migration.duration}s
Final Step: ${migration.step}
`).join(''); return `

Migration History

${historyItems}
`; } renderControls() { return `
${this.currentMigration ? '' : '' }
`; } async refreshStatus() { try { const status = await this.monitor.checkMigrationStatus(); this.updateCurrentMigration(status); this.renderDashboard(); this.showNotification('Status refreshed', 'info'); } catch (error) { this.showNotification('Failed to refresh status', 'error'); } } async startMonitoring() { await this.monitor.startMonitoring(); this.renderDashboard(); } stopMonitoring() { this.monitor.stopMonitoring(); this.renderDashboard(); } exportLogs() { const logs = { currentMigration: this.currentMigration, history: this.migrationHistory, exportedAt: new Date().toISOString() }; const blob = new Blob([JSON.stringify(logs, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `migration-logs-${Date.now()}.json`; a.click(); URL.revokeObjectURL(url); } showNotification(message, type) { // Implement notification display console.log(`[${type.toUpperCase()}] ${message}`); } } // Initialize dashboard const dashboard = new MigrationDashboard(); dashboard.startDashboard(); ``` ### Automated Migration Management **Automated Migration Workflow**: ```javascript theme={null} // Automated migration management with retry logic class MigrationManager { constructor() { this.maxRetries = 3; this.retryDelay = 30000; // 30 seconds this.healthCheckInterval = 10000; // 10 seconds } async manageMigration() { let retryCount = 0; while (retryCount < this.maxRetries) { try { const result = await this.runMigrationWithMonitoring(); if (result.success) { await this.postMigrationTasks(result); return result; } else { retryCount++; if (retryCount < this.maxRetries) { console.log(`Migration failed, retrying in ${this.retryDelay/1000} seconds... (${retryCount}/${this.maxRetries})`); await this.delay(this.retryDelay); } } } catch (error) { retryCount++; console.error(`Migration attempt ${retryCount} failed:`, error); if (retryCount < this.maxRetries) { await this.delay(this.retryDelay); } } } throw new Error(`Migration failed after ${this.maxRetries} attempts`); } async runMigrationWithMonitoring() { const monitor = new MigrationMonitor(this.healthCheckInterval); return new Promise((resolve, reject) => { let migrationResult = null; monitor.onComplete((status) => { migrationResult = { success: true, status }; resolve(migrationResult); }); monitor.onError((status) => { if (status.try_count >= 3) { // Max retries reached migrationResult = { success: false, status }; resolve(migrationResult); } }); // Start monitoring monitor.startMonitoring().catch(reject); // Set timeout for migration setTimeout(() => { monitor.stopMonitoring(); if (!migrationResult) { reject(new Error('Migration timeout')); } }, 30 * 60 * 1000); // 30 minute timeout }); } async postMigrationTasks(result) { console.log('Running post-migration tasks...'); // Verify migration integrity await this.verifyMigrationIntegrity(); // Update system configuration await this.updateSystemConfiguration(); // Notify stakeholders await this.notifyMigrationComplete(result); console.log('Post-migration tasks completed'); } async verifyMigrationIntegrity() { // Implement migration verification logic console.log('Verifying migration integrity...'); } async updateSystemConfiguration() { // Update system configuration after migration console.log('Updating system configuration...'); } async notifyMigrationComplete(result) { // Notify relevant parties about migration completion console.log('Notifying migration completion...'); } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } // Usage const migrationManager = new MigrationManager(); try { const result = await migrationManager.manageMigration(); console.log('Migration completed successfully:', result); } catch (error) { console.error('Migration management failed:', error); } ``` ## Best Practices 1. **Regular Monitoring**: Check migration status every 5-10 seconds during active migration 2. **Error Handling**: Handle network errors and server errors gracefully 3. **Status Caching**: Avoid redundant processing when status hasn't changed 4. **Notification System**: Notify relevant personnel when migration completes or fails 5. **Logging**: Record key status changes during the migration process 6. **Timeout Management**: Set appropriate timeouts for migration operations 7. **Retry Logic**: Implement retry mechanisms for failed migration attempts 8. **Progress Tracking**: Provide clear progress indicators for long-running migrations # Add System User IDs Source: https://wukong.mintlify.app/en/api/user/add-system-uids POST /user/systemuids_add Add users to the system user ID list ## Overview Add specified users to the system user ID list, granting them special system user privileges and identification. ## Request Body ### Required Parameters Array of user IDs to add to the system user list User ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/user/systemuids_add" \ -H "Content-Type: application/json" \ -d '{ "uids": ["bot_assistant", "notification_service", "admin_helper"] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/user/systemuids_add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uids: ['bot_assistant', 'notification_service', 'admin_helper'] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uids": ["bot_assistant", "notification_service", "admin_helper"] } response = requests.post('http://localhost:5001/user/systemuids_add', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uids": []string{"bot_assistant", "notification_service", "admin_helper"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/user/systemuids_add", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ---------------------------------- | | 200 | System user IDs added successfully | | 400 | Request parameter error | | 403 | No administrative permission | | 500 | Internal server error | ## System User Privileges ### Special Permissions After becoming a system user, users will gain the following privileges: | Permission | Description | Scope | | -------------------- | ---------------------------------------------- | ---------------- | | Bypass Validation | Skip certain validations when sending messages | Message sending | | System Channels | Access to system-only channels | Channel access | | Rate Limit Exemption | Exempt from standard rate limiting | API calls | | Priority Processing | Higher priority in message processing | Message delivery | ### Permission Hierarchy ``` System Users > Administrators > Whitelist > Regular Users > Blacklist ``` ## Use Cases ### Bot Service Registration **Register AI Assistant**: ```javascript theme={null} // Register a new AI assistant as system user async function registerAIAssistant(botId) { try { await addSystemUIDs([botId]); console.log(`AI Assistant ${botId} registered as system user`); // Grant additional bot permissions await configureBotPermissions(botId); } catch (error) { console.error('Failed to register AI assistant:', error); } } // Usage await registerAIAssistant('ai_assistant_v2'); ``` ### Notification Service Setup **Setup Notification Services**: ```javascript theme={null} // Setup multiple notification services async function setupNotificationServices() { const notificationServices = [ 'email_notifications', 'push_notifications', 'sms_notifications', 'webhook_notifications' ]; try { await addSystemUIDs(notificationServices); console.log('Notification services registered as system users'); // Configure each service for (const service of notificationServices) { await configureNotificationService(service); } } catch (error) { console.error('Failed to setup notification services:', error); } } ``` ### Administrative Tools **Register Admin Tools**: ```javascript theme={null} // Register administrative and monitoring tools async function registerAdminTools() { const adminTools = [ 'system_monitor', 'log_analyzer', 'performance_tracker', 'security_scanner' ]; await addSystemUIDs(adminTools); // Grant system-level access for (const tool of adminTools) { await grantSystemAccess(tool); } } ``` ### Integration Services **Third-party Integration**: ```javascript theme={null} // Register external integration services async function registerIntegrations() { const integrations = [ 'slack_integration', 'teams_integration', 'discord_integration', 'telegram_bridge' ]; try { await addSystemUIDs(integrations); // Configure integration permissions for (const integration of integrations) { await configureIntegrationPermissions(integration, { canBridgeMessages: true, canAccessAllChannels: true, canBypassRateLimit: true }); } } catch (error) { console.error('Failed to register integrations:', error); } } ``` ### Batch Operations **Bulk System User Registration**: ```javascript theme={null} // Register multiple system users in batches async function bulkRegisterSystemUsers(userGroups) { const batchSize = 10; // Process in batches to avoid overwhelming the system for (const group of userGroups) { const batches = chunkArray(group.users, batchSize); for (const batch of batches) { try { await addSystemUIDs(batch); console.log(`Registered batch of ${batch.length} users for ${group.category}`); // Small delay between batches await delay(100); } catch (error) { console.error(`Failed to register batch for ${group.category}:`, error); } } } } // Usage await bulkRegisterSystemUsers([ { category: 'bots', users: ['bot1', 'bot2', 'bot3', 'bot4', 'bot5'] }, { category: 'services', users: ['service1', 'service2', 'service3'] } ]); ``` ## Security Considerations ### Access Control * **Admin Only**: Only system administrators should have access to this API * **Audit Logging**: Log all system user additions for security auditing * **Validation**: Validate user IDs before adding them to prevent injection attacks ### Best Practices 1. **Principle of Least Privilege**: Only grant system user status when necessary 2. **Regular Review**: Periodically review system user list and remove unused accounts 3. **Documentation**: Document the purpose of each system user 4. **Monitoring**: Monitor system user activities for suspicious behavior 5. **Backup**: Maintain backups of system user configurations ### Risk Management ```javascript theme={null} // Implement safety checks before adding system users async function safeAddSystemUIDs(uids) { // Validate user IDs const validUIDs = uids.filter(uid => isValidUID(uid)); if (validUIDs.length !== uids.length) { throw new Error('Invalid user IDs detected'); } // Check if users exist const existingUsers = await checkUsersExist(validUIDs); const nonExistentUsers = validUIDs.filter(uid => !existingUsers.includes(uid)); if (nonExistentUsers.length > 0) { console.warn('Adding non-existent users as system users:', nonExistentUsers); } // Add with audit logging await addSystemUIDs(validUIDs); await logSystemUserAddition(validUIDs, getCurrentAdmin()); } ``` ## Error Handling ```javascript theme={null} async function handleSystemUserAddition(uids) { try { await addSystemUIDs(uids); return { success: true, message: 'System users added successfully' }; } catch (error) { if (error.status === 403) { return { success: false, message: 'Insufficient permissions' }; } else if (error.status === 400) { return { success: false, message: 'Invalid user IDs provided' }; } else { return { success: false, message: 'Internal server error' }; } } } ``` # Force Device Quit Source: https://wukong.mintlify.app/en/api/user/device-quit POST /user/device_quit Force user device to quit/disconnect ## Overview Force a specified user's device to quit/disconnect, used for administrators to kick out users or handle abnormal connections. ## Request Body ### Required Parameters User ID ### Optional Parameters Device identifier, used to specify a particular device type * `0` - App (Android, iPhone, iPad devices) * `1` - Web (Browser, Web applications) * `2` - Desktop (Desktop applications) If not specified, all devices for the user will be disconnected. ```bash cURL theme={null} curl -X POST "http://localhost:5001/user/device_quit" \ -H "Content-Type: application/json" \ -d '{ "uid": "user123", "device_flag": 1 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/user/device_quit', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: 'user123', device_flag: 1 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uid": "user123", "device_flag": 1 } response = requests.post('http://localhost:5001/user/device_quit', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uid": "user123", "device_flag": 1, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/user/device_quit", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | ------------------------------------------- | | 200 | Device quit successfully | | 400 | Request parameter error | | 404 | User does not exist or device not connected | | 500 | Internal server error | ## Use Cases ### Administrative Control **Kick Violating Users**: ```javascript theme={null} // Kick user from all devices await forceDeviceQuit("violating_user"); // Kick user from specific device type await forceDeviceQuit("violating_user", 1); // Web only ``` **Session Management**: ```javascript theme={null} // Force logout from web sessions for security await forceDeviceQuit("user123", 1); ``` ### Security Management **Suspicious Activity**: ```javascript theme={null} // Disconnect suspicious connections const suspiciousUsers = ["user1", "user2"]; for (const uid of suspiciousUsers) { await forceDeviceQuit(uid); } ``` **Account Compromise**: ```javascript theme={null} // Emergency disconnect all devices await forceDeviceQuit("compromised_user"); ``` ### Multi-Device Management **Device Limit Enforcement**: ```javascript theme={null} // Enforce single device policy async function enforceSingleDevice(uid, allowedDeviceFlag) { // Get current online devices const status = await getUserOnlineStatus([uid]); if (status[0]?.online && status[0].device_flag !== allowedDeviceFlag) { // Disconnect other devices await forceDeviceQuit(uid, status[0].device_flag); } } ``` **Platform Migration**: ```javascript theme={null} // Force users to migrate to new platform async function migrateFromOldPlatform(userIds) { for (const uid of userIds) { // Disconnect old platform (device_flag: 0) await forceDeviceQuit(uid, 0); } } ``` ## Best Practices 1. **Logging**: Always log device quit operations for audit purposes 2. **Notification**: Notify users when their devices are forcibly disconnected 3. **Reason Codes**: Include reason codes for different disconnect scenarios 4. **Rate Limiting**: Implement rate limiting to prevent abuse 5. **Permission Check**: Verify administrator permissions before allowing device quit 6. **Graceful Handling**: Allow users to reconnect after resolving issues ## Security Considerations * **Admin Only**: This API should only be accessible to system administrators * **Audit Trail**: Maintain detailed logs of all forced disconnections * **User Notification**: Consider notifying users about forced disconnections * **Reason Documentation**: Document reasons for forced disconnections * **Appeal Process**: Provide a process for users to appeal disconnections # Get User Online Status Source: https://wukong.mintlify.app/en/api/user/online-status POST /user/onlinestatus Get online status information for multiple users ## Overview Get online status information for multiple users, including whether they are online and device type. ## Request Body The request body is an array of user ID strings: ```json theme={null} ["user1", "user2", "user3"] ``` ```bash cURL theme={null} curl -X POST "http://localhost:5001/user/onlinestatus" \ -H "Content-Type: application/json" \ -d '["user1", "user2", "user3"]' ``` ```javascript JavaScript theme={null} const userIds = ["user1", "user2", "user3"]; const response = await fetch('http://localhost:5001/user/onlinestatus', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userIds) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests user_ids = ["user1", "user2", "user3"] response = requests.post('http://localhost:5001/user/onlinestatus', json=user_ids) data = response.json() print(data) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { userIds := []string{"user1", "user2", "user3"} jsonData, _ := json.Marshal(userIds) resp, err := http.Post( "http://localhost:5001/user/onlinestatus", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result []map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} [ { "uid": "user1", "online": 1, "device_flag": 1 }, { "uid": "user2", "online": 0, "device_flag": 0 }, { "uid": "user3", "online": 1, "device_flag": 2 } ] ``` ## Response Fields The response is an array, each element contains the following fields: User ID Online status * `0` - Offline * `1` - Online Device identifier * `0` - App (Android, iPhone, iPad devices) * `1` - Web (Browser, Web applications) * `2` - Desktop (Desktop applications) ## Status Codes | Status Code | Description | | ----------- | ----------------------------------------- | | 200 | Successfully retrieved user online status | | 400 | Request parameter error | | 500 | Internal server error | ## Use Cases ### Contact List Status Display online status indicators in contact lists: ```javascript theme={null} // Get online status for contact list const contactIds = ["friend1", "friend2", "colleague1"]; const statuses = await getUserOnlineStatus(contactIds); // Update UI with status indicators statuses.forEach(status => { updateContactStatus(status.uid, status.online, status.device_flag); }); ``` ### Group Member Status Check online status of group members: ```javascript theme={null} // Get group member online status const groupMembers = ["member1", "member2", "member3"]; const memberStatuses = await getUserOnlineStatus(groupMembers); // Show online member count const onlineCount = memberStatuses.filter(s => s.online === 1).length; console.log(`${onlineCount} members online`); ``` ### Presence Indicators Implement presence indicators in chat interfaces: ```javascript theme={null} // Real-time presence updates function updatePresenceIndicators(userStatuses) { userStatuses.forEach(status => { const indicator = document.querySelector(`[data-user="${status.uid}"] .presence`); if (indicator) { indicator.className = status.online ? 'presence online' : 'presence offline'; indicator.title = getDeviceTypeName(status.device_flag); } }); } function getDeviceTypeName(deviceFlag) { const deviceTypes = { 0: 'Mobile App', 1: 'Web Browser', 2: 'Desktop App' }; return deviceTypes[deviceFlag] || 'Unknown'; } ``` ## Best Practices 1. **Batch Requests**: Query multiple users in a single request to reduce API calls 2. **Caching**: Cache online status for a short period to avoid excessive requests 3. **Real-time Updates**: Combine with WebSocket events for real-time status updates 4. **UI Optimization**: Update UI efficiently when status changes 5. **Privacy**: Respect user privacy settings for status visibility # Remove System User IDs Source: https://wukong.mintlify.app/en/api/user/remove-system-uids POST /user/systemuids_remove Remove users from the system user ID list ## Overview Remove specified users from the system user ID list, revoking their special system user privileges and identification. ## Request Body ### Required Parameters Array of user IDs to remove from the system user list User ID ```bash cURL theme={null} curl -X POST "http://localhost:5001/user/systemuids_remove" \ -H "Content-Type: application/json" \ -d '{ "uids": ["old_bot", "deprecated_service"] }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/user/systemuids_remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uids: ['old_bot', 'deprecated_service'] }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uids": ["old_bot", "deprecated_service"] } response = requests.post('http://localhost:5001/user/systemuids_remove', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uids": []string{"old_bot", "deprecated_service"}, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/user/systemuids_remove", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | -------------------------------------- | | 200 | System user IDs removed successfully | | 400 | Request parameter error | | 403 | No administrative permission | | 404 | Specified user not in system user list | | 500 | Internal server error | ## Impact of Removal ### Revoked Privileges When users are removed from the system user list, they lose: | Privilege | Impact | Immediate Effect | | -------------------- | ----------------------------------- | ---------------------------------- | | Bypass Validation | Must pass standard validation | Message sending restrictions apply | | System Channels | Lose access to system-only channels | Cannot access restricted channels | | Rate Limit Exemption | Subject to standard rate limits | API calls limited | | Priority Processing | Normal priority in message queue | Standard message delivery | ### Transition Period * **Immediate**: System user status revoked * **Active Connections**: Existing connections remain but lose privileges * **Cached Permissions**: May take up to 5 minutes to fully propagate * **Message Queue**: Messages in queue processed with new permissions ## Use Cases ### Service Decommissioning **Decommission Old Bot Services**: ```javascript theme={null} // Remove deprecated bot services async function decommissionBotServices() { const deprecatedBots = [ 'old_chatbot_v1', 'legacy_assistant', 'deprecated_analyzer' ]; try { // Remove system user privileges await removeSystemUIDs(deprecatedBots); // Disable bot services for (const bot of deprecatedBots) { await disableBotService(bot); await revokeAPIKeys(bot); } console.log('Bot services decommissioned successfully'); } catch (error) { console.error('Failed to decommission bot services:', error); } } ``` ### Security Incident Response **Emergency Privilege Revocation**: ```javascript theme={null} // Emergency removal of compromised system users async function emergencyPrivilegeRevocation(compromisedUIDs) { try { // Immediately revoke system privileges await removeSystemUIDs(compromisedUIDs); // Force disconnect all sessions for (const uid of compromisedUIDs) { await forceDeviceQuit(uid); } // Log security incident await logSecurityIncident({ type: 'privilege_revocation', affected_users: compromisedUIDs, timestamp: new Date().toISOString(), reason: 'security_incident' }); // Notify security team await notifySecurityTeam(compromisedUIDs); } catch (error) { console.error('Emergency revocation failed:', error); // Escalate to manual intervention await escalateToManualIntervention(compromisedUIDs); } } ``` ### Cleanup Operations **Regular Cleanup of Unused System Users**: ```javascript theme={null} // Regular cleanup of inactive system users async function cleanupInactiveSystemUsers() { try { // Get current system users const systemUIDs = await getSystemUIDs(); // Check activity for each system user const inactiveUsers = []; for (const uid of systemUIDs) { const lastActivity = await getLastActivity(uid); const daysSinceActivity = (Date.now() - lastActivity) / (1000 * 60 * 60 * 24); if (daysSinceActivity > 90) { // 90 days inactive inactiveUsers.push(uid); } } if (inactiveUsers.length > 0) { // Request approval for removal const approved = await requestCleanupApproval(inactiveUsers); if (approved) { await removeSystemUIDs(inactiveUsers); console.log(`Cleaned up ${inactiveUsers.length} inactive system users`); } } } catch (error) { console.error('Cleanup operation failed:', error); } } // Schedule regular cleanup setInterval(cleanupInactiveSystemUsers, 7 * 24 * 60 * 60 * 1000); // Weekly ``` ### Migration and Upgrades **Service Migration**: ```javascript theme={null} // Migrate from old service to new service async function migrateSystemService(oldServiceUID, newServiceUID) { try { // Add new service as system user await addSystemUIDs([newServiceUID]); // Configure new service with same permissions await migrateServiceConfiguration(oldServiceUID, newServiceUID); // Test new service functionality const testResult = await testServiceFunctionality(newServiceUID); if (testResult.success) { // Remove old service from system users await removeSystemUIDs([oldServiceUID]); // Gracefully shutdown old service await gracefulServiceShutdown(oldServiceUID); console.log(`Successfully migrated from ${oldServiceUID} to ${newServiceUID}`); } else { // Rollback if test fails await removeSystemUIDs([newServiceUID]); throw new Error('Service migration test failed'); } } catch (error) { console.error('Service migration failed:', error); // Ensure old service remains functional await ensureServiceHealth(oldServiceUID); } } ``` ### Batch Operations **Bulk Removal with Validation**: ```javascript theme={null} // Safely remove multiple system users with validation async function bulkRemoveSystemUsers(uidsToRemove, options = {}) { const { validateBeforeRemoval = true, requireApproval = true, batchSize = 5 } = options; try { if (validateBeforeRemoval) { // Validate each user before removal const validationResults = await validateUsersForRemoval(uidsToRemove); const invalidUsers = validationResults.filter(r => !r.canRemove); if (invalidUsers.length > 0) { console.warn('Cannot remove users:', invalidUsers.map(u => u.uid)); uidsToRemove = validationResults .filter(r => r.canRemove) .map(r => r.uid); } } if (requireApproval) { const approved = await requestBulkRemovalApproval(uidsToRemove); if (!approved) { throw new Error('Bulk removal not approved'); } } // Process in batches const batches = chunkArray(uidsToRemove, batchSize); const results = []; for (const batch of batches) { try { await removeSystemUIDs(batch); results.push({ batch, success: true }); // Small delay between batches await delay(500); } catch (error) { results.push({ batch, success: false, error: error.message }); } } return results; } catch (error) { console.error('Bulk removal operation failed:', error); throw error; } } ``` ## Security Considerations ### Pre-removal Validation ```javascript theme={null} // Validate users before removal async function validateUserForRemoval(uid) { const checks = { isSystemUser: await isInSystemUserList(uid), hasActiveConnections: await hasActiveConnections(uid), hasCriticalServices: await hasCriticalServices(uid), hasRecentActivity: await hasRecentActivity(uid, 24), // 24 hours isProtectedUser: await isProtectedUser(uid) }; const canRemove = checks.isSystemUser && !checks.isProtectedUser && !checks.hasCriticalServices; return { uid, canRemove, checks, warnings: generateRemovalWarnings(checks) }; } ``` ### Audit and Compliance ```javascript theme={null} // Comprehensive audit logging async function auditSystemUserRemoval(uids, adminUser, reason) { const auditEntry = { action: 'system_user_removal', timestamp: new Date().toISOString(), admin_user: adminUser, affected_users: uids, reason: reason, system_state_before: await captureSystemState(), ip_address: await getClientIP(), user_agent: await getClientUserAgent() }; await writeAuditLog(auditEntry); await notifyComplianceTeam(auditEntry); } ``` ## Best Practices 1. **Validation First**: Always validate users before removal 2. **Approval Process**: Implement approval workflows for system user changes 3. **Gradual Rollout**: Remove privileges gradually for critical services 4. **Monitoring**: Monitor system behavior after privilege removal 5. **Rollback Plan**: Have a rollback plan in case of issues 6. **Documentation**: Document reasons for removal 7. **Notification**: Notify relevant teams about privilege changes # Get System User IDs Source: https://wukong.mintlify.app/en/api/user/system-uids GET /user/systemuids Get the list of system user IDs ## Overview Get the list of system user IDs, used to identify special built-in user accounts in the system. ```bash cURL theme={null} curl -X GET "http://localhost:5001/user/systemuids" ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/user/systemuids'); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get('http://localhost:5001/user/systemuids') data = response.json() print(data) ``` ```go Go theme={null} package main import ( "encoding/json" "fmt" "net/http" ) func main() { resp, err := http.Get("http://localhost:5001/user/systemuids") if err != nil { panic(err) } defer resp.Body.Close() var result []string json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} [ "system", "admin", "bot", "notification", "webhook" ] ``` ## Response Fields List of system user IDs System user ID ## Status Codes | Status Code | Description | | ----------- | ------------------------------------------ | | 200 | Successfully retrieved system user ID list | | 500 | Internal server error | ## System User Types ### Common System Users | User ID | Purpose | Description | | -------------- | ------------------- | ---------------------------------------------------- | | `system` | System Messages | Used for system-generated messages and notifications | | `admin` | Administrator | System administrator account | | `bot` | Bot Services | Automated bot services and AI assistants | | `notification` | Notifications | Push notifications and alerts | | `webhook` | Webhook Integration | External system integrations | ## Use Cases ### Message Filtering **Filter System Messages**: ```javascript theme={null} // Get system user IDs const systemUIDs = await getSystemUIDs(); // Filter out system messages in chat display function filterUserMessages(messages) { return messages.filter(message => !systemUIDs.includes(message.from_uid) ); } // Display only user messages const userMessages = filterUserMessages(allMessages); displayMessages(userMessages); ``` ### Permission Management **Check System User Permissions**: ```javascript theme={null} async function isSystemUser(uid) { const systemUIDs = await getSystemUIDs(); return systemUIDs.includes(uid); } // Grant special permissions to system users async function checkUserPermissions(uid) { if (await isSystemUser(uid)) { return { canSendToAll: true, canBypassLimits: true, canAccessSystemChannels: true }; } return { canSendToAll: false, canBypassLimits: false, canAccessSystemChannels: false }; } ``` ### UI Customization **Special Display for System Messages**: ```javascript theme={null} async function renderMessage(message) { const systemUIDs = await getSystemUIDs(); if (systemUIDs.includes(message.from_uid)) { // Render system message with special styling return renderSystemMessage(message); } else { // Render regular user message return renderUserMessage(message); } } function renderSystemMessage(message) { return `
${message.from_uid} ${message.content}
`; } ``` ### Analytics and Reporting **Separate System vs User Activity**: ```javascript theme={null} async function analyzeMessageActivity(messages) { const systemUIDs = await getSystemUIDs(); const userMessages = messages.filter(m => !systemUIDs.includes(m.from_uid)); const systemMessages = messages.filter(m => systemUIDs.includes(m.from_uid)); return { userActivity: { count: userMessages.length, messages: userMessages }, systemActivity: { count: systemMessages.length, messages: systemMessages } }; } ``` ### Bot Integration **Identify Bot Messages**: ```javascript theme={null} async function handleIncomingMessage(message) { const systemUIDs = await getSystemUIDs(); if (systemUIDs.includes(message.from_uid)) { // Handle system/bot message if (message.from_uid === 'bot') { await processBotCommand(message); } else if (message.from_uid === 'notification') { await showNotification(message); } } else { // Handle regular user message await processUserMessage(message); } } ``` ## Best Practices 1. **Caching**: Cache system user IDs to avoid repeated API calls 2. **Regular Updates**: Periodically refresh the system user list 3. **Error Handling**: Handle cases where system users might change 4. **UI Distinction**: Clearly distinguish system messages from user messages 5. **Permission Checks**: Always verify system user permissions before granting special access 6. **Logging**: Log interactions with system users for audit purposes ## Security Considerations * **Access Control**: Ensure only authorized applications can access system user information * **Rate Limiting**: Implement rate limiting to prevent abuse * **Audit Logging**: Log all requests to track system user ID access * **Validation**: Validate system user IDs before using them in operations # Update User Token Source: https://wukong.mintlify.app/en/api/user/token POST /user/token Update user authentication token ## Overview Update user authentication token, used for user re-login or token refresh scenarios. ## Request Body ### Required Parameters Unique user ID for communication, can be random uuid (recommended to use your server's unique user uid) (required by WuKongIMSDK) Verification token, random uuid (recommended to use your server's user token) (required by WuKongIMSDK) Device identifier: 0=app, 1=web, 2=desktop (main devices with same user and same device flag will kick each other, secondary devices will coexist) ### Optional Parameters Device level: 0=secondary device, 1=main device ```bash cURL theme={null} curl -X POST "http://localhost:5001/user/token" \ -H "Content-Type: application/json" \ -d '{ "uid": "user123", "token": "new_auth_token_here", "device_flag": 1, "device_level": 1 }' ``` ```javascript JavaScript theme={null} const response = await fetch('http://localhost:5001/user/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: 'user123', token: 'new_auth_token_here', device_flag: 1, device_level: 1 }) }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests data = { "uid": "user123", "token": "new_auth_token_here", "device_flag": 1, "device_level": 1 } response = requests.post('http://localhost:5001/user/token', json=data) result = response.json() print(result) ``` ```go Go theme={null} package main import ( "bytes" "encoding/json" "fmt" "net/http" ) func main() { data := map[string]interface{}{ "uid": "user123", "token": "new_auth_token_here", "device_flag": 1, "device_level": 1, } jsonData, _ := json.Marshal(data) resp, err := http.Post( "http://localhost:5001/user/token", "application/json", bytes.NewBuffer(jsonData), ) if err != nil { panic(err) } defer resp.Body.Close() var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) fmt.Printf("%+v\n", result) } ``` ```json Success Response theme={null} { "status": "ok" } ``` ## Response Fields Operation status, returns `"ok"` on success ## Status Codes | Status Code | Description | | ----------- | -------------------------- | | 200 | Token updated successfully | | 400 | Request parameter error | | 500 | Internal server error | ## Best Practices 1. **Token Security**: Ensure tokens have sufficient complexity and uniqueness 2. **Device Identification**: Properly set device\_flag to distinguish different device types 3. **Permission Control**: Use device\_level to implement permission control for different devices 4. **Regular Refresh**: Implement regular token refresh mechanism 5. **Multi-device Management**: Generate different tokens for different devices for easier management and revocation # Webhook Callbacks Source: https://wukong.mintlify.app/en/api/webhook WuKongIM pushes user online status, offline messages and all messages to third-party applications through webhooks # Webhook Callbacks ## Overview Some data from WuKongIM will be sent to third-party application services through webhooks, such as user online status, messages that need to be pushed, all messages, etc. All webhooks are **POST requests**, and the event name is passed through query parameters. For example, if the third-party server provides a webhook address of `http://example.com/webhook`, then the online status webhook would be: ``` http://example.com/webhook?event=user.onlinestatus ``` The request body data would be similar to: ```json theme={null} ["uid1-0-1", "uid2-1-0"] ``` ## Webhook Workflow Webhook workflow diagram ## Event Types ### 1. User Online Status Notification Each user's online and offline status will be notified to the third-party server through this webhook. **Event Name**: `user.onlinestatus` **Request Method**: `POST` **Request URL**: `{webhook_url}?event=user.onlinestatus` #### Request Body The request body is a string array, each element formatted as: ``` UserUID-DeviceFlag-OnlineStatus-ConnectionID-DeviceOnlineCount-UserTotalOnlineCount ``` **Example Data**: ```json theme={null} ["uid1-1-0-1001-2-4", "uid2-0-0-1001-1-2"] ``` #### Data Field Description | Position | Field Name | Type | Description | | -------- | ----------------------- | ------- | ----------------------------------------------------- | | 1 | User UID | string | User unique identifier | | 2 | Device Flag | integer | 0=APP, 1=Web | | 3 | Online Status | integer | 0=Offline, 1=Online | | 4 | Connection ID | integer | Connection ID established by current device on server | | 5 | Device Online Count | integer | Online count for same user and same device type | | 6 | User Total Online Count | integer | Total online count for all user devices | ```bash cURL theme={null} # Simulate webhook request sent by WuKongIM curl -X POST "http://your-server.com/webhook?event=user.onlinestatus" \ -H "Content-Type: application/json" \ -d '["user123-1-1-1001-1-1", "user456-0-0-1002-0-0"]' ``` ```javascript JavaScript theme={null} // Server-side code example for receiving webhook app.post('/webhook', (req, res) => { const event = req.query.event; if (event === 'user.onlinestatus') { const statusData = req.body; // Array format statusData.forEach(status => { const [uid, deviceFlag, online, connId, deviceCount, totalCount] = status.split('-'); console.log({ uid, deviceFlag: parseInt(deviceFlag), online: parseInt(online), connId: parseInt(connId), deviceCount: parseInt(deviceCount), totalCount: parseInt(totalCount) }); // Handle user online status change handleUserOnlineStatus(uid, online === '1'); }); } res.status(200).send('OK'); }); ``` ```python Python theme={null} from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/webhook', methods=['POST']) def webhook(): event = request.args.get('event') if event == 'user.onlinestatus': status_data = request.json # Array format for status in status_data: parts = status.split('-') uid = parts[0] device_flag = int(parts[1]) online = int(parts[2]) conn_id = int(parts[3]) device_count = int(parts[4]) total_count = int(parts[5]) # Handle user online status change handle_user_online_status(uid, online == 1) return 'OK', 200 ``` ```go Go theme={null} package main import ( "encoding/json" "net/http" "strconv" "strings" ) func webhookHandler(w http.ResponseWriter, r *http.Request) { event := r.URL.Query().Get("event") if event == "user.onlinestatus" { var statusData []string json.NewDecoder(r.Body).Decode(&statusData) for _, status := range statusData { parts := strings.Split(status, "-") if len(parts) >= 6 { uid := parts[0] deviceFlag, _ := strconv.Atoi(parts[1]) online, _ := strconv.Atoi(parts[2]) connId, _ := strconv.Atoi(parts[3]) deviceCount, _ := strconv.Atoi(parts[4]) totalCount, _ := strconv.Atoi(parts[5]) // Handle user online status change handleUserOnlineStatus(uid, online == 1) } } } w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) } ``` ### 2. Offline Message Notification Offline message notification mainly notifies the third-party server of messages that need to be pushed offline. After receiving this webhook, the third-party server needs to call mobile vendor push interfaces to push the message content to users in the ToUIDs list. **Event Name**: `msg.offline` **Request Method**: `POST` **Request URL**: `{webhook_url}?event=msg.offline` #### Request Body The request body is a MessageResp message object: Message header information Message setting identifier Server message ID (globally unique) String type server message ID (globally unique) Client message unique number Message sequence number (channel unique, ordered increment) Sender UID Channel ID Channel type Server message timestamp (10 digits, to seconds) Base64 encoded message content Recipient user list Recipient user UID ```javascript JavaScript theme={null} // Receive offline message webhook app.post('/webhook', (req, res) => { const event = req.query.event; if (event === 'msg.offline') { const message = req.body; // Decode message content const payload = JSON.parse(atob(message.payload)); // Send push notification to users in to_uids message.to_uids.forEach(uid => { sendPushNotification(uid, { title: `Message from ${message.from_uid}`, body: payload.content, messageId: message.message_idstr }); }); } res.status(200).send('OK'); }); ``` ```python Python theme={null} import base64 import json @app.route('/webhook', methods=['POST']) def webhook(): event = request.args.get('event') if event == 'msg.offline': message = request.json # Decode message content payload = json.loads(base64.b64decode(message['payload']).decode('utf-8')) # Send push notification to users in to_uids for uid in message['to_uids']: send_push_notification(uid, { 'title': f"Message from {message['from_uid']}", 'body': payload['content'], 'message_id': message['message_idstr'] }) return 'OK', 200 ``` ### 3. All Messages Notification WuKongIM server will push all messages to the third-party server. To reduce pressure on the third-party server, messages are not pushed one by one but with delay processing. By default, batch push occurs every 500 milliseconds (`webhook.msgNotifyEventPushInterval`), which can be configured as needed. **Event Name**: `msg.notify` **Request Method**: `POST` **Request URL**: `{webhook_url}?event=msg.notify` #### Request Body The request body is an array of MessageResp message objects, each containing the following fields: Message header information Message setting identifier Server message ID (globally unique) String type server message ID (globally unique) Client message unique number Message sequence number (channel unique, ordered increment) Sender UID Channel ID Channel type Server message timestamp (10 digits, to seconds) Base64 encoded message content ```javascript JavaScript theme={null} // Receive all messages webhook app.post('/webhook', (req, res) => { const event = req.query.event; if (event === 'msg.notify') { const messages = req.body; // Message array messages.forEach(message => { // Decode message content const payload = JSON.parse(atob(message.payload)); // Save message to database or search engine saveMessageToDatabase({ messageId: message.message_idstr, fromUid: message.from_uid, channelId: message.channel_id, channelType: message.channel_type, content: payload, timestamp: message.timestamp }); }); } res.status(200).send('OK'); }); ``` ```python Python theme={null} @app.route('/webhook', methods=['POST']) def webhook(): event = request.args.get('event') if event == 'msg.notify': messages = request.json # Message array for message in messages: # Decode message content payload = json.loads(base64.b64decode(message['payload']).decode('utf-8')) # Save message to database or search engine save_message_to_database({ 'message_id': message['message_idstr'], 'from_uid': message['from_uid'], 'channel_id': message['channel_id'], 'channel_type': message['channel_type'], 'content': payload, 'timestamp': message['timestamp'] }) return 'OK', 200 ``` ## Configure Webhook Set the webhook URL in the WuKongIM configuration file: ```yaml theme={null} webhook: url: "http://your-server.com/webhook" timeout: 5s msgNotifyEventPushInterval: 500ms ``` ## Best Practices 1. **Response Speed**: Webhook processing should be as fast as possible to avoid blocking WuKongIM service 2. **Idempotency**: Ensure webhook processing is idempotent and can be safely retried 3. **Error Handling**: Return appropriate HTTP status codes, 2xx indicates success 4. **Async Processing**: For complex business logic, recommend asynchronous processing 5. **Monitoring & Alerting**: Monitor webhook success rate and response time 6. **Security Verification**: Verify request source to prevent malicious requests ## Troubleshooting ### Common Issues 1. **Webhook Not Received**: Check URL configuration and network connectivity 2. **Processing Timeout**: Optimize processing logic to reduce response time 3. **Duplicate Processing**: Implement idempotent processing mechanism 4. **Message Loss**: Ensure correct HTTP status codes are returned # Channel Source: https://wukong.mintlify.app/en/getting-started/concepts/channel Core structure and management methods of WuKongIM channels ## Concept Explanation ### What is a Channel? A Channel is the carrier for message transmission in WuKongIM, defining the target and scope of message delivery. Each channel has a unique identifier and type, used to organize and manage different communication scenarios. ### Why are Channels Important? * **Message Routing**: Channels determine where messages are sent, forming the foundation of message transmission * **Permission Control**: Different channel types have different permissions and management rules * **Scenario Differentiation**: Channel types distinguish between different scenarios like one-on-one chat and group chat ### Relationship with Other Concepts * **Message**: All messages must specify a target channel to be sent * **User**: Users communicate through channels, can be channel creators or participants * **Conversation**: Each conversation corresponds to a channel; the conversation list is actually the list of channels a user participates in ## Core Structure Channels are the carriers of message transmission, containing the following core attributes: | Attribute | Type | Description | | -------------- | ------- | -------------------------------------- | | `channel_id` | string | Channel unique identifier | | `channel_type` | integer | Channel type (1=personal, 2=group) | | `large` | integer | Large channel identifier (0=no, 1=yes) | | `ban` | integer | Disabled status (0=no, 1=yes) | | `disband` | integer | Disbanded status (0=no, 1=yes) | ### Channel Types * **Personal Channel (1)**: One-on-one private chat * **Group Channel (2)**: Multi-user group chat * **Customer Service Channel (3)**: Customer service channel, no permission checks * **Community Channel (4)**: Community channel, similar to Discord channels * **Community Topic Channel (5)**: Community topic channel, similar to Discord sub-channels * **News Channel (6)**: News channel (with temporary subscriber concept, join temporary subscription when viewing news, exit when leaving) * **Live Channel (9)**: Live channel (live channels don't save recent conversation data) * **Visitor Channel (10)**: Visitor channel (channel ID is visitor ID, supports only one visitor subscriber and multiple customer service subscribers, can replace customer service channels for customer service scenarios) * **Personal Agent Channel (11)**: Personal Agent channel (AI Agent channel, channel ID structure is UID\@AgentID, similar to personal chat channel, optimized for AI Agent scenarios) * **Group Agent Channel (12)**: Group Agent channel (AI Agent group chat channel, similar to group chat channel, optimized for multi-Agent collaboration scenarios) ### Channel Example ```json theme={null} { "channel_id": "group123", "channel_type": 2, "large": 1, "ban": 0, "disband": 0 } ``` ## Related API Endpoints | Endpoint | Method | Description | | ---------------------------- | ------ | ----------------------- | | `/channel` | POST | Create channel | | `/channel/info` | POST | Get channel information | | `/channel/delete` | DELETE | Delete channel | | `/channel/subscriber_add` | POST | Add subscribers | | `/channel/subscriber_remove` | POST | Remove subscribers | | `/channel/messagesync` | POST | Sync channel messages | ## EasySDK Code Examples ### Send Messages to Different Channel Types ```javascript Web theme={null} import { WKIM, WKIMChannelType } from 'easyjssdk'; const im = WKIM.init("ws://your-server.com:5200", { uid: "your_user_id", token: "your_token" }); // Send to personal channel (one-on-one chat) const personalPayload = { type: 1, content: "Hello!" }; await im.send("user123", WKIMChannelType.Person, personalPayload); // Send to group channel (group chat) const groupPayload = { type: 1, content: "Hello everyone!" }; await im.send("group123", WKIMChannelType.Group, groupPayload); ``` ```swift iOS theme={null} import WuKongEasySDK let config = WuKongConfig( serverUrl: "ws://your-server.com:5200", uid: "your_user_id", token: "your_token" ) let easySDK = WuKongEasySDK(config: config) // Send to personal channel (one-on-one chat) let personalPayload = MessagePayload( type: 1, content: "Hello!" ) try await easySDK.send( channelId: "user123", channelType: .person, payload: personalPayload ) // Send to group channel (group chat) let groupPayload = MessagePayload( type: 1, content: "Hello everyone!" ) try await easySDK.send( channelId: "group123", channelType: .group, payload: groupPayload ) ``` ```kotlin Android theme={null} import com.githubim.easysdk.WuKongEasySDK import com.githubim.easysdk.WuKongConfig import com.githubim.easysdk.WuKongChannelType val config = WuKongConfig.Builder() .serverUrl("ws://your-server.com:5200") .uid("your_user_id") .token("your_token") .build() val easySDK = WuKongEasySDK.getInstance() easySDK.init(this, config) // Send to personal channel (one-on-one chat) val personalPayload = MessagePayload( type = 1, content = "Hello!" ) easySDK.send( channelId = "user123", channelType = WuKongChannelType.PERSON, payload = personalPayload ) // Send to group channel (group chat) val groupPayload = MessagePayload( type = 1, content = "Hello everyone!" ) easySDK.send( channelId = "group123", channelType = WuKongChannelType.GROUP, payload = groupPayload ) ``` ```dart Flutter theme={null} import 'package:wukong_easy_sdk/wukong_easy_sdk.dart'; final config = WuKongConfig( serverUrl: "ws://your-server.com:5200", uid: "your_user_id", token: "your_token", ); final easySDK = WuKongEasySDK.getInstance(); await easySDK.init(config); // Send to personal channel (one-on-one chat) final personalPayload = MessagePayload( type: 1, content: "Hello!", ); await easySDK.send( channelId: "user123", channelType: WuKongChannelType.person, payload: personalPayload, ); // Send to group channel (group chat) final groupPayload = MessagePayload( type: 1, content: "Hello everyone!", ); await easySDK.send( channelId: "group123", channelType: WuKongChannelType.group, payload: groupPayload, ); ``` ### Channel Type Description **Personal Channel (person)**: * `channelId` is the other user's ID * Used for one-on-one private chat * Example: To send to user "user123", channelId is "user123" **Group Channel (group)**: * `channelId` is the group's ID * Used for multi-user group chat * Example: To send to group "group123", channelId is "group123" Channels are uniquely identified by the combination of `channel_id` and `channel_type`, serving as the fundamental carrier for message transmission. # Conversation Source: https://wukong.mintlify.app/en/getting-started/concepts/conversation Core structure and management methods of WuKongIM conversations ## Concept Explanation ### What is a Conversation? A Conversation is the interaction record between a user and a specific channel, containing the user's message history in that channel, unread status, last active time, and other information. Conversations are the fundamental data structure for chat lists in user interfaces. ### Why are Conversations Important? * **Chat List**: The conversation list is the chat list that users see, displaying all ongoing conversations * **Unread Management**: Conversations record the number of unread messages for each chat, helping users quickly understand which chats have new messages * **Quick Access**: Through the conversation list, users can quickly access recent chat records ### Relationship with Other Concepts * **Channel**: Each conversation corresponds to a channel; the conversation's `channel_id` is the channel's ID * **Message**: Conversations display the last message of that channel and the number of unread messages * **User**: Conversations belong to specific users; different users see different conversation lists ## Core Structure Conversations contain the following core attributes: | Attribute | Type | Description | | -------------- | ------- | ---------------------------------- | | `channel_id` | string | Channel identifier | | `channel_type` | integer | Channel type (1=personal, 2=group) | | `unread` | integer | Number of unread messages | | `last_msg_seq` | integer | Last message sequence number | | `timestamp` | integer | Last update timestamp | | `version` | integer | Conversation version number | ### Conversation Example ```json theme={null} { "channel_id": "group123", "channel_type": 2, "unread": 5, "last_msg_seq": 1001, "timestamp": 1640995200, "version": 100 } ``` ## Related API Endpoints | Endpoint | Method | Description | | --------------------------- | ------ | ---------------------- | | `/conversation/sync` | POST | Sync conversation list | | `/conversation/setUnread` | POST | Set unread count | | `/conversation/clearUnread` | POST | Clear unread count | | `/conversation/delete` | POST | Delete conversation | ## EasySDK Code Examples ### Conversation Management ```javascript Web theme={null} import { WKIM, WKIMEvent } from 'easyjssdk'; // Listen for conversation updates im.on(WKIMEvent.ConversationUpdate, (conversations) => { console.log('Conversations updated:', conversations); // Update UI conversation list conversations.forEach(conversation => { console.log(`Channel: ${conversation.channel_id}, Unread: ${conversation.unread}`); }); }); // Get conversation list const conversations = await im.getConversations(); console.log('Current conversations:', conversations); ``` ```swift iOS theme={null} import WuKongEasySDK // Listen for conversation updates easySDK.onConversationUpdate { conversations in print("Conversations updated:", conversations) // Update UI conversation list conversations.forEach { conversation in print("Channel: \(conversation.channelId), Unread: \(conversation.unread)") } } // Get conversation list Task { let conversations = await easySDK.getConversations() print("Current conversations:", conversations) } ``` ```kotlin Android theme={null} import com.githubim.easysdk.WuKongEvent import com.githubim.easysdk.listener.WuKongEventListener // Listen for conversation updates easySDK.addEventListener(WuKongEvent.CONVERSATION_UPDATE, object : WuKongEventListener> { override fun onEvent(conversations: List) { Log.d("WuKong", "Conversations updated: $conversations") // Update UI conversation list conversations.forEach { conversation -> Log.d("WuKong", "Channel: ${conversation.channelId}, Unread: ${conversation.unread}") } } }) // Get conversation list lifecycleScope.launch { val conversations = easySDK.getConversations() Log.d("WuKong", "Current conversations: $conversations") } ``` ```dart Flutter theme={null} import 'package:wukong_easy_sdk/wukong_easy_sdk.dart'; // Listen for conversation updates easySDK.addEventListener(WuKongEvent.conversationUpdate, (List conversations) { print('Conversations updated: $conversations'); // Update UI conversation list conversations.forEach((conversation) { print('Channel: ${conversation.channelId}, Unread: ${conversation.unread}'); }); }); // Get conversation list final conversations = await easySDK.getConversations(); print('Current conversations: $conversations'); ``` ### Clear Unread Messages ```javascript Web theme={null} // Clear unread count for specific conversation await im.clearUnread("group123", 2); // channel_id, channel_type console.log("Unread count cleared"); ``` ```swift iOS theme={null} // Clear unread count for specific conversation Task { try await easySDK.clearUnread(channelId: "group123", channelType: 2) print("Unread count cleared") } ``` ```kotlin Android theme={null} // Clear unread count for specific conversation lifecycleScope.launch { try { easySDK.clearUnread("group123", 2) // channel_id, channel_type Log.d("WuKong", "Unread count cleared") } catch (e: Exception) { Log.e("WuKong", "Failed to clear unread: $e") } } ``` ```dart Flutter theme={null} // Clear unread count for specific conversation try { await easySDK.clearUnread("group123", 2); // channel_id, channel_type print("Unread count cleared"); } catch (e) { print("Failed to clear unread: $e"); } ``` Conversations are sorted by last message time and serve as the fundamental data structure for chat lists in user interfaces. # Device Source: https://wukong.mintlify.app/en/getting-started/concepts/device Core structure and management methods of WuKongIM devices ## Concept Explanation ### What is a Device? A Device is the terminal carrier for users to access the WuKongIM system, representing the specific device used by users to send and receive messages. Each device has a unique identifier and type, supporting users to simultaneously use instant messaging services on multiple devices. ### Why are Devices Important? * **Multi-device Sync**: Users can log in simultaneously on multiple devices like phones, computers, and tablets with real-time message synchronization * **Device Management**: Can view and manage all user login devices, supporting remote device logout * **Push Strategy**: Adopt different message push strategies based on device type ### Relationship with Other Concepts * **User**: A user can own multiple devices, each device is associated with a user ID * **Message**: Messages are synchronized to all online devices of the user * **Connection**: Each device has an independent network connection ## Core Structure Devices contain the following core attributes: | Attribute | Type | Description | | ------------- | ------- | ---------------------------- | | `cid` | integer | Connection unique identifier | | `uid` | string | User ID that owns the device | | `device_id` | string | Device unique identifier | | `device_flag` | integer | Device type identifier | | `uptime` | string | Online duration | | `idle` | string | Idle time | ### Device Example ```json theme={null} { "cid": 12345, "uid": "user123", "device_id": "device_456", "device_flag": 1, "uptime": "1h30m", "idle": "5m" } ``` ## Related API Endpoints | Endpoint | Method | Description | | ------------------- | ------ | --------------------------- | | `/user/device_quit` | POST | Force device offline | | `/connz` | GET | View connection information | ## Device Type Identifiers | Identifier Value | Device Type | Description | | ---------------- | ----------- | ----------------------------- | | 0 | App | Android, iPhone, iPad devices | | 1 | Web | Browser, Web applications | | 2 | Desktop | Desktop applications | ## EasySDK Code Examples ### Device Initialization and Configuration ```javascript Web theme={null} import { WKIM } from 'easyjssdk'; // Device type is automatically set to Web (1) during initialization const im = WKIM.init("ws://your-server.com:5200", { uid: "user123", token: "your_token", deviceId: "web_device_001" // Optional: custom device ID }); // EasySDK automatically handles device type, Web platform defaults to device type 1 console.log('Current platform: Web'); console.log('Device type: 1 (Web)'); ``` ```swift iOS theme={null} import WuKongEasySDK // Device type is automatically set to APP (0) during initialization let config = WuKongConfig( serverUrl: "ws://your-server.com:5200", uid: "user123", token: "your_token", deviceId: "ios_device_001" // Optional: custom device ID ) let easySDK = WuKongEasySDK(config: config) // EasySDK automatically handles device type, iOS platform defaults to device type 0 (APP) print("Current platform: APP") print("Device type: 0 (APP)") ``` ```kotlin Android theme={null} import com.githubim.easysdk.WuKongEasySDK import com.githubim.easysdk.WuKongConfig // Device type is automatically set to APP (0) during initialization val config = WuKongConfig.Builder() .serverUrl("ws://your-server.com:5200") .uid("user123") .token("your_token") .deviceId("android_device_001") // Optional: custom device ID .build() val easySDK = WuKongEasySDK.getInstance() easySDK.init(this, config) // EasySDK automatically handles device type, Android platform defaults to device type 0 Log.d("WuKong", "Current platform: APP") Log.d("WuKong", "Device type: 0 (APP)") ``` ```dart Flutter theme={null} import 'package:wukong_easy_sdk/wukong_easy_sdk.dart'; // Device type is automatically set during initialization final config = WuKongConfig( serverUrl: "ws://your-server.com:5200", uid: "user123", token: "your_token", deviceId: "flutter_device_001", // Optional: custom device ID ); final easySDK = WuKongEasySDK.getInstance(); await easySDK.init(config); // EasySDK automatically handles device type print('Current platform: Flutter'); print('Device type: Auto-detected'); ``` ### Multi-device Synchronization **Important Note**: EasySDK automatically handles message synchronization between multiple devices. When a user logs in on multiple devices, messages are automatically synchronized to all online devices. ```javascript Web theme={null} // EasySDK automatically handles multi-device sync // When receiving messages, all online devices will receive the same message im.on(WKIMEvent.Message, (message) => { console.log('Received message (auto-synced to all devices):', message); }); // Check connection status console.log('Current device connection status:', im.isConnected()); ``` ```swift iOS theme={null} // EasySDK automatically handles multi-device sync // When receiving messages, all online devices will receive the same message easySDK.onMessage { message in print("Received message (auto-synced to all devices):", message) } // Check connection status print("Current device connection status:", easySDK.isConnected()) ``` ```kotlin Android theme={null} // EasySDK automatically handles multi-device sync // When receiving messages, all online devices will receive the same message easySDK.addEventListener(WuKongEvent.MESSAGE, object : WuKongEventListener { override fun onEvent(message: Message) { Log.d("WuKong", "Received message (auto-synced to all devices): $message") } }) // Check connection status Log.d("WuKong", "Current device connection status: ${easySDK.isConnected()}") ``` ```dart Flutter theme={null} // EasySDK automatically handles multi-device sync // When receiving messages, all online devices will receive the same message easySDK.addEventListener(WuKongEvent.message, (Message message) { print('Received message (auto-synced to all devices): $message'); }); // Check connection status print('Current device connection status: ${easySDK.isConnected()}'); ``` ### Device Management ```javascript Web theme={null} // EasySDK automatically manages device connections // Each device maintains its own connection state // Listen for connection events im.on(WKIMEvent.Connect, (result) => { console.log('Device connected:', result); }); im.on(WKIMEvent.Disconnect, (info) => { console.log('Device disconnected:', info); }); // Graceful disconnect await im.disconnect(); console.log('Device disconnected gracefully'); ``` ```swift iOS theme={null} // EasySDK automatically manages device connections // Each device maintains its own connection state // Listen for connection events easySDK.onConnect { result in print("Device connected:", result) } easySDK.onDisconnect { info in print("Device disconnected:", info) } // Graceful disconnect Task { await easySDK.disconnect() print("Device disconnected gracefully") } ``` ```kotlin Android theme={null} // EasySDK automatically manages device connections // Each device maintains its own connection state // Listen for connection events easySDK.addEventListener(WuKongEvent.CONNECT, object : WuKongEventListener { override fun onEvent(result: ConnectResult) { Log.d("WuKong", "Device connected: $result") } }) easySDK.addEventListener(WuKongEvent.DISCONNECT, object : WuKongEventListener { override fun onEvent(info: DisconnectInfo) { Log.d("WuKong", "Device disconnected: $info") } }) // Graceful disconnect easySDK.disconnect() Log.d("WuKong", "Device disconnected gracefully") ``` ```dart Flutter theme={null} // EasySDK automatically manages device connections // Each device maintains its own connection state // Listen for connection events easySDK.addEventListener(WuKongEvent.connect, (ConnectResult result) { print('Device connected: $result'); }); easySDK.addEventListener(WuKongEvent.disconnect, (DisconnectInfo info) { print('Device disconnected: $info'); }); // Graceful disconnect await easySDK.disconnect(); print('Device disconnected gracefully'); ``` A user can be online on multiple devices simultaneously. EasySDK automatically handles message synchronization between multiple devices. Each device has an independent connection and device identifier. # Message Source: https://wukong.mintlify.app/en/getting-started/concepts/message Core structure and usage methods of WuKongIM messages ## Concept Explanation ### What is a Message? A Message is the basic unit of information transmission in WuKongIM, serving as the carrier for real-time communication between users. Each message contains information about the sender, receiver, content, and metadata. ### Why are Messages Important? * **Communication Foundation**: Messages are the core of instant messaging systems; all chat functionality is based on message transmission * **Data Carrier**: Messages can transmit not only text but also various types of data like images, files, and locations * **Status Tracking**: Each message has a unique identifier and status, making it easy to track message sending, receiving, and read status ### Relationship with Other Concepts * **Channel**: Messages are transmitted through channels, which define the message destination * **User**: Messages have clear senders and receivers, all of whom are users in the system * **Conversation**: Messages update corresponding conversation records, affecting conversation list display ## Core Structure Messages contain the following core attributes: | Attribute | Type | Description | | --------------- | ------- | -------------------------------------- | | `message_id` | integer | Message unique identifier | | `message_seq` | integer | Message sequence number within channel | | `client_msg_no` | string | Client message identifier | | `from_uid` | string | Sender user ID | | `channel_id` | string | Target channel ID | | `channel_type` | integer | Channel type (1=personal, 2=group) | | `timestamp` | integer | Message timestamp | | `payload` | string | Base64 encoded message content | ### Message Example ```json theme={null} { "message_id": 123456789, "message_seq": 1001, "client_msg_no": "client_msg_123", "from_uid": "user123", "channel_id": "group123", "channel_type": 2, "timestamp": 1640995200, "payload": "SGVsbG8gV29ybGQ=" } ``` ## Related API Endpoints | Endpoint | Method | Description | | ---------------------- | ------ | -------------------------- | | `/message/send` | POST | Send single message | | `/message/sendbatch` | POST | Send batch messages | | `/message/search` | POST | Search historical messages | | `/channel/messagesync` | POST | Sync channel messages | ## EasySDK Code Examples ### Send Messages ```javascript Web theme={null} import { WKIM, WKIMChannelType } from 'easyjssdk'; // Initialize SDK const im = WKIM.init("ws://your-server.com:5200", { uid: "your_user_id", token: "your_token" }); // Send text message const textPayload = { type: 1, content: "Hello, World!" }; const result = await im.send("friend_user_id", WKIMChannelType.Person, textPayload); // Send image message const imagePayload = { type: 2, url: "https://example.com/image.jpg", width: 800, height: 600 }; const imageResult = await im.send("group_id", WKIMChannelType.Group, imagePayload); ``` ```swift iOS theme={null} import WuKongEasySDK // Initialize SDK let config = WuKongConfig( serverUrl: "ws://your-server.com:5200", uid: "your_user_id", token: "your_token" ) let easySDK = WuKongEasySDK(config: config) // Send text message let textPayload = MessagePayload( type: 1, content: "Hello, World!" ) try await easySDK.send( channelId: "friend_user_id", channelType: .person, payload: textPayload ) // Send image message let imagePayload = MessagePayload( type: 2, url: "https://example.com/image.jpg", width: 800, height: 600 ) try await easySDK.send( channelId: "group_id", channelType: .group, payload: imagePayload ) ``` ```kotlin Android theme={null} import com.githubim.easysdk.WuKongEasySDK import com.githubim.easysdk.WuKongConfig import com.githubim.easysdk.WuKongChannelType // Initialize SDK val config = WuKongConfig.Builder() .serverUrl("ws://your-server.com:5200") .uid("your_user_id") .token("your_token") .build() val easySDK = WuKongEasySDK.getInstance() easySDK.init(this, config) // Send text message val textPayload = MessagePayload( type = 1, content = "Hello, World!" ) easySDK.send( channelId = "friend_user_id", channelType = WuKongChannelType.PERSON, payload = textPayload ) // Send image message val imagePayload = MessagePayload( type = 2, url = "https://example.com/image.jpg", width = 800, height = 600 ) easySDK.send( channelId = "group_id", channelType = WuKongChannelType.GROUP, payload = imagePayload ) ``` ```dart Flutter theme={null} import 'package:wukong_easy_sdk/wukong_easy_sdk.dart'; // Initialize SDK final config = WuKongConfig( serverUrl: "ws://your-server.com:5200", uid: "your_user_id", token: "your_token", ); final easySDK = WuKongEasySDK.getInstance(); await easySDK.init(config); // Send text message final textPayload = MessagePayload( type: 1, content: "Hello, World!", ); await easySDK.send( channelId: "friend_user_id", channelType: WuKongChannelType.person, payload: textPayload, ); // Send image message final imagePayload = MessagePayload( type: 2, url: "https://example.com/image.jpg", width: 800, height: 600, ); await easySDK.send( channelId: "group_id", channelType: WuKongChannelType.group, payload: imagePayload, ); ``` ### Listen for Messages ```javascript Web theme={null} import { WKIMEvent } from 'easyjssdk'; // Listen for new messages im.on(WKIMEvent.Message, (message) => { console.log("Received new message:", message); console.log("Message content:", message.payload); console.log("Sender:", message.fromUid); }); ``` ```swift iOS theme={null} // Listen for new messages easySDK.onMessage { message in print("Received new message:", message) print("Message content:", message.payload) print("Sender:", message.fromUid) } ``` ```kotlin Android theme={null} import com.githubim.easysdk.WuKongEvent import com.githubim.easysdk.listener.WuKongEventListener // Listen for new messages easySDK.addEventListener(WuKongEvent.MESSAGE, object : WuKongEventListener { override fun onEvent(message: Message) { Log.d("WuKong", "Received new message: $message") Log.d("WuKong", "Message content: ${message.payload}") Log.d("WuKong", "Sender: ${message.fromUid}") } }) ``` ```dart Flutter theme={null} // Listen for new messages easySDK.addEventListener(WuKongEvent.message, (Message message) { print("Received new message: $message"); print("Message content: ${message.payload}"); print("Sender: ${message.fromUid}"); }); ``` ### Message Type Handling ```javascript Web theme={null} // Handle different content based on message type im.on(WKIMEvent.Message, (message) => { const payload = JSON.parse(message.payload); switch (payload.type) { case 1: // Text message console.log("Text message:", payload.content); break; case 2: // Image message console.log("Image message:", payload.url); break; case 100: // Custom message console.log("Custom message:", payload); break; } }); ``` ```swift iOS theme={null} // Handle different content based on message type easySDK.onMessage { message in if let payloadData = Data(base64Encoded: message.payload), let payload = try? JSONSerialization.jsonObject(with: payloadData) as? [String: Any], let type = payload["type"] as? Int { switch type { case 1: // Text message if let content = payload["content"] as? String { print("Text message:", content) } case 2: // Image message if let url = payload["url"] as? String { print("Image message:", url) } case 100: // Custom message print("Custom message:", payload) default: print("Unknown message type:", type) } } } ``` ```kotlin Android theme={null} // Handle different content based on message type easySDK.addEventListener(WuKongEvent.MESSAGE, object : WuKongEventListener { override fun onEvent(message: Message) { try { val payloadJson = JSONObject(String(Base64.decode(message.payload, Base64.DEFAULT))) val type = payloadJson.getInt("type") when (type) { 1 -> { // Text message val content = payloadJson.getString("content") Log.d("WuKong", "Text message: $content") } 2 -> { // Image message val url = payloadJson.getString("url") Log.d("WuKong", "Image message: $url") } 100 -> { // Custom message Log.d("WuKong", "Custom message: $payloadJson") } } } catch (e: Exception) { Log.e("WuKong", "Failed to parse message", e) } } }) ``` ```dart Flutter theme={null} // Handle different content based on message type easySDK.addEventListener(WuKongEvent.message, (Message message) { try { final payloadString = utf8.decode(base64.decode(message.payload)); final payload = json.decode(payloadString); final type = payload['type']; switch (type) { case 1: // Text message print("Text message: ${payload['content']}"); break; case 2: // Image message print("Image message: ${payload['url']}"); break; case 100: // Custom message print("Custom message: $payload"); break; } } catch (e) { print("Failed to parse message: $e"); } }); ``` The message content `payload` field uses Base64 encoding, with the specific format defined by the application layer. # User Source: https://wukong.mintlify.app/en/getting-started/concepts/user Core structure and management methods of WuKongIM users ## Concept Explanation ### What is a User? A User is the basic entity in the WuKongIM system, representing an individual or application using the instant messaging service. Each user has a unique identifier and related attributes, serving as the subject for sending and receiving messages. ### Why are Users Important? * **Identity Identification**: User ID is the key to uniquely identify a user in the system * **Permission Foundation**: All message sending and receiving permissions are based on user identity * **Status Management**: User online status determines the method and timing of message delivery ### Relationship with Other Concepts * **Message**: Users are the senders and receivers of messages * **Channel**: Users communicate through channels; personal channel ID is the user ID * **Conversation**: User conversation list shows all chats the user participates in * **Device**: A user can log in and use the service on multiple devices ## Core Structure Users contain the following core attributes: | Attribute | Type | Description | | ------------- | ------- | ----------------------------------- | | `uid` | string | User unique identifier | | `online` | integer | Online status (0=offline, 1=online) | | `device_flag` | integer | Device type identifier | ### Device Type Identifiers | Value | Device Type | Description | | ----- | ----------- | ------------------------- | | 1 | iOS | iPhone, iPad devices | | 2 | Android | Android devices | | 3 | Web | Browser, Web applications | | 4 | Desktop | Desktop applications | ### User Example ```json theme={null} { "uid": "user123", "online": 1, "device_flag": 1 } ``` ## Related API Endpoints | Endpoint | Method | Description | | -------------------- | ------ | ------------------------ | | `/user/onlinestatus` | POST | Query user online status | | `/user/token` | POST | Update user token | | `/user/device_quit` | POST | Force device offline | | `/user/systemuids` | GET | Get system user list | ## EasySDK Code Examples ### User Initialization and Connection ```javascript Web theme={null} import { WKIM, WKIMEvent } from 'easyjssdk'; // Initialize SDK const im = WKIM.init("ws://your-server.com:5200", { uid: "user123", // User unique identifier token: "your_auth_token" // User authentication token }); // Listen for connection status im.on(WKIMEvent.Connect, (result) => { console.log('User connected:', result); }); im.on(WKIMEvent.Disconnect, (disconnectInfo) => { console.log('User disconnected:', disconnectInfo); }); // Connect to server try { await im.connect(); console.log("Connection successful!"); } catch (error) { console.error("Connection failed:", error); } ``` ```swift iOS theme={null} import WuKongEasySDK // Configure user information let config = WuKongConfig( serverUrl: "ws://your-server.com:5200", uid: "user123", // User unique identifier token: "your_auth_token" // User authentication token ) // Initialize SDK let easySDK = WuKongEasySDK(config: config) // Listen for connection status easySDK.onConnect { result in print("User connected:", result) } easySDK.onDisconnect { disconnectInfo in print("User disconnected:", disconnectInfo) } // Connect to server Task { do { try await easySDK.connect() print("Connection successful!") } catch { print("Connection failed:", error) } } ``` ```kotlin Android theme={null} import com.githubim.easysdk.WuKongEasySDK import com.githubim.easysdk.WuKongConfig import com.githubim.easysdk.WuKongEvent import com.githubim.easysdk.listener.WuKongEventListener // Configure user information val config = WuKongConfig.Builder() .serverUrl("ws://your-server.com:5200") .uid("user123") // User unique identifier .token("your_auth_token") // User authentication token .build() // Initialize SDK val easySDK = WuKongEasySDK.getInstance() easySDK.init(this, config) // Listen for connection status easySDK.addEventListener(WuKongEvent.CONNECT, object : WuKongEventListener { override fun onEvent(result: ConnectResult) { Log.d("WuKong", "User connected: $result") } }) easySDK.addEventListener(WuKongEvent.DISCONNECT, object : WuKongEventListener { override fun onEvent(disconnectInfo: DisconnectInfo) { Log.d("WuKong", "User disconnected: $disconnectInfo") } }) // Connect to server lifecycleScope.launch { try { easySDK.connect() Log.d("WuKong", "Connection successful!") } catch (e: Exception) { Log.e("WuKong", "Connection failed: $e") } } ``` ```dart Flutter theme={null} import 'package:wukong_easy_sdk/wukong_easy_sdk.dart'; // Configure user information final config = WuKongConfig( serverUrl: "ws://your-server.com:5200", uid: "user123", // User unique identifier token: "your_auth_token", // User authentication token ); // Initialize SDK final easySDK = WuKongEasySDK.getInstance(); await easySDK.init(config); // Listen for connection status easySDK.addEventListener(WuKongEvent.connect, (ConnectResult result) { print('User connected: $result'); }); easySDK.addEventListener(WuKongEvent.disconnect, (DisconnectInfo disconnectInfo) { print('User disconnected: $disconnectInfo'); }); // Connect to server try { await easySDK.connect(); print("Connection successful!"); } catch (e) { print("Connection failed: $e"); } ``` ### User Status Management ```javascript Web theme={null} // Check connection status const isConnected = im.isConnected(); console.log('Connection status:', isConnected); // Disconnect await im.disconnect(); // Listen for error events im.on(WKIMEvent.Error, (error) => { console.log('Error occurred:', error); }); ``` ```swift iOS theme={null} // Check connection status let isConnected = easySDK.isConnected() print("Connection status:", isConnected) // Disconnect Task { await easySDK.disconnect() } // Listen for error events easySDK.onError { error in print("Error occurred:", error) } ``` ```kotlin Android theme={null} // Check connection status val isConnected = easySDK.isConnected() Log.d("WuKong", "Connection status: $isConnected") // Disconnect easySDK.disconnect() // Listen for error events easySDK.addEventListener(WuKongEvent.ERROR, object : WuKongEventListener { override fun onEvent(error: WuKongError) { Log.e("WuKong", "Error occurred: $error") } }) ``` ```dart Flutter theme={null} // Check connection status final isConnected = easySDK.isConnected(); print('Connection status: $isConnected'); // Disconnect await easySDK.disconnect(); // Listen for error events easySDK.addEventListener(WuKongEvent.error, (WuKongError error) { print('Error occurred: $error'); }); ``` WuKongIM focuses on message transmission; detailed user profiles (such as nicknames, avatars, etc.) are typically managed by the application layer. # JSON-RPC Protocol Source: https://wukong.mintlify.app/en/getting-started/learning/jsonrpc Learn about the JSON-RPC 2.0 communication protocol format and message types used by WuKongIM # JSON-RPC Protocol ## Overview WuKongIM uses the JSON-RPC 2.0 specification for communication. All requests, responses and notifications follow the JSON-RPC 2.0 standard structure. ### Protocol Field Description * `jsonrpc`: **Optional** string, fixed as "2.0". If omitted, server should assume it's "2.0" * `method`: Method name for request or notification * `params`: Parameters for request or notification, usually an object * `id`: Unique identifier for request (string type). Response must contain the same id as request. Notifications don't have id * `result`: Result data for successful response * `error`: Error object for error response Complete protocol schema can be found at: [wukongim\_rpc\_schema.json](https://github.com/WuKongIM/WuKongIM/blob/main/pkg/jsonrpc/wukongim_rpc_schema.json) ## Important Notes 1. After establishing WebSocket connection, authentication (`connect`) must be performed **within 2 seconds**. Connections exceeding 2 seconds or failing authentication will be disconnected 2. Send ping packets regularly to let server know you're alive (recommended interval around 1-2 minutes) ## Common Components ### ErrorObject When request processing fails, this object will be included in the response: | Field | Type | Required | Description | | :-------- | :------ | :------- | :-------------------- | | `code` | integer | Yes | Error code | | `message` | string | Yes | Error description | | `data` | any | No | Additional error data | ### Header Optional message header information: | Field | Type | Required | Description | | :---------- | :------ | :------- | :---------------------------- | | `noPersist` | boolean | No | Whether message is not stored | | `redDot` | boolean | No | Whether to show red dot | | `syncOnce` | boolean | No | Whether only synced once | | `dup` | boolean | No | Whether it's a resent message | ### SettingFlags Message setting flags: | Field | Type | Required | Description | | :-------- | :------ | :------- | :-------------------------- | | `receipt` | boolean | No | Message read receipt | | `stream` | boolean | No | Whether it's stream message | | `topic` | boolean | No | Whether contains Topic | ## Core Message Types ### 1. Connection Authentication (Connect) #### Connect Request The first request initiated by client, used to establish connection and authentication. **Parameters (`params`)** | Field | Type | Required | Description | | :---------------- | :------ | :------- | :------------------------------------ | | `uid` | string | Yes | User ID | | `token` | string | Yes | Authentication Token | | `header` | Header | No | Message header | | `version` | integer | No | Client protocol version | | `clientKey` | string | No | Client public key | | `deviceId` | string | No | Device ID | | `deviceFlag` | integer | No | Device flag (0:APP, 1:WEB...) | | `clientTimestamp` | integer | No | Client 13-digit millisecond timestamp | **Example Request** ```json theme={null} { "method": "connect", "params": { "uid": "testUser", "token": "testToken" }, "id": "req-conn-1" } ``` #### Connect Response **Success Response (`result`)** | Field | Type | Required | Description | | :----------- | :------ | :------- | :-------------------------- | | `timeDiff` | integer | No | Time difference with server | | `reasonCode` | integer | No | Connection reason code | | `serverKey` | string | No | Server public key | | `salt` | string | No | Encryption salt | | `nodeId` | integer | No | Server node ID | **Example Success Response** ```json theme={null} { "result": { "timeDiff": 0, "reasonCode": 1 }, "id": "req-conn-1" } ``` **Error Response** ```json theme={null} { "error": { "code": -32001, "message": "Authentication failed" }, "id": "req-conn-1" } ``` ### 2. Send Message #### Send Request Send message to specified channel. **Parameters (`params`)** | Field | Type | Required | Description | | :------------ | :----------- | :------- | :----------------------------- | | `header` | Header | No | Message header | | `setting` | SettingFlags | No | Message settings | | `clientMsgNo` | string | Yes | Client message number | | `channelId` | string | Yes | Channel ID | | `channelType` | integer | Yes | Channel type | | `payload` | string | Yes | Base64 encoded message content | | `expire` | integer | No | Message expiration time | **Example Request** ```json theme={null} { "method": "send", "params": { "clientMsgNo": "msg-001", "channelId": "user123", "channelType": 1, "payload": "eyJ0eXBlIjoidGV4dCIsImNvbnRlbnQiOiJIZWxsbyJ9" }, "id": "req-send-1" } ``` #### Send Response **Success Response (`result`)** | Field | Type | Required | Description | | :------------ | :------ | :------- | :-------------------- | | `clientMsgNo` | string | Yes | Client message number | | `messageId` | integer | Yes | Server message ID | | `messageSeq` | integer | Yes | Message sequence | | `timestamp` | integer | Yes | Server timestamp | **Example Success Response** ```json theme={null} { "result": { "clientMsgNo": "msg-001", "messageId": 12345, "messageSeq": 1, "timestamp": 1640995200 }, "id": "req-send-1" } ``` ### 3. Receive Message (Notification) Server pushes messages to client. **Parameters (`params`)** | Field | Type | Required | Description | | :------------ | :----------- | :------- | :----------------------------- | | `header` | Header | No | Message header | | `setting` | SettingFlags | No | Message settings | | `messageId` | integer | Yes | Server message ID | | `messageSeq` | integer | Yes | Message sequence | | `clientMsgNo` | string | No | Client message number | | `timestamp` | integer | Yes | Server timestamp | | `fromUid` | string | Yes | Sender user ID | | `channelId` | string | Yes | Channel ID | | `channelType` | integer | Yes | Channel type | | `payload` | string | Yes | Base64 encoded message content | | `expire` | integer | No | Message expiration time | **Example Notification** ```json theme={null} { "method": "message", "params": { "messageId": 12345, "messageSeq": 1, "timestamp": 1640995200, "fromUid": "user456", "channelId": "user123", "channelType": 1, "payload": "eyJ0eXBlIjoidGV4dCIsImNvbnRlbnQiOiJIZWxsbyJ9" } } ``` ### 4. Ping/Pong #### Ping Request Client sends ping to keep connection alive. **Example Request** ```json theme={null} { "method": "ping", "id": "req-ping-1" } ``` #### Pong Response **Example Response** ```json theme={null} { "result": {}, "id": "req-ping-1" } ``` ### 5. Disconnect #### Disconnect Request Client initiates disconnection. **Parameters (`params`)** | Field | Type | Required | Description | | :----------- | :------ | :------- | :---------------- | | `reasonCode` | integer | No | Disconnect reason | **Example Request** ```json theme={null} { "method": "disconnect", "params": { "reasonCode": 1 }, "id": "req-disc-1" } ``` ## Error Codes Common error codes and their meanings: | Code | Description | | :----- | :--------------------- | | -32001 | Authentication failed | | -32002 | Invalid parameters | | -32003 | Channel not found | | -32004 | Permission denied | | -32005 | Rate limit exceeded | | -32006 | Message too large | | -32007 | Invalid message format | ## Best Practices 1. **Connection Management** * Always authenticate within 2 seconds after connection * Send ping regularly to maintain connection * Handle reconnection gracefully 2. **Message Handling** * Use unique `clientMsgNo` for each message * Handle message deduplication on client side * Implement proper error handling 3. **Performance Optimization** * Batch multiple operations when possible * Use appropriate channel types * Implement message queuing for offline scenarios ## Related Resources * [API Documentation](/en/api/introduction) * [SDK Documentation](/en/sdk/overview) * [System Integration](/en/getting-started/learning/system-integration) # Offline Messages Source: https://wukong.mintlify.app/en/getting-started/learning/offline-messages Understanding WuKongIM's offline message processing mechanism and recent conversation synchronization # Offline Messages *WuKongIM* adopts a read-diffusion model. What are read-diffusion and write-diffusion? Reference article: [Link](https://blog.csdn.net/m0_53246313/article/details/122674197) After enabling the recent conversation configuration, *WuKongIM* maintains a recent conversation list for each user on the server side. 1. When an application goes from offline to online, it needs to synchronize the recent conversation list, as shown in the diagram below: Offline Message Sync Process 2. After the application comes online, the application should maintain the recent conversation list online. 3. When clicking on a conversation (channel) to enter the chat interface, you need to synchronize the messages of that conversation (channel), as shown in the diagram below (at the same time, you need to clear the unread count of the recent conversation using `/conversations/setUnread`): Conversation Message Sync ## Offline Message Processing Flow ### 1. Application Startup Process When your application starts up and the user comes online, follow this process: ```mermaid theme={null} sequenceDiagram participant App as Application participant WK as WuKongIM Server participant DB as Database App->>WK: Connect to server WK-->>App: Connection established App->>WK: Request recent conversations WK->>DB: Query user's conversation list DB-->>WK: Return conversation data WK-->>App: Send conversation list App->>App: Update local conversation list App->>App: Display conversations with unread counts ``` ### 2. Message Synchronization Process When entering a specific conversation: ```mermaid theme={null} sequenceDiagram participant App as Application participant WK as WuKongIM Server participant DB as Database App->>WK: Request messages for channel WK->>DB: Query channel messages DB-->>WK: Return message history WK-->>App: Send message list App->>App: Display messages App->>WK: Clear unread count WK->>DB: Update conversation status DB-->>WK: Confirm update WK-->>App: Acknowledge unread cleared ``` ## Implementation Best Practices ### 1. Conversation List Management **Sync Recent Conversations on Startup:** ```javascript theme={null} // Example: Sync conversations when app starts async function syncConversationsOnStartup() { try { // Get recent conversations from server const conversations = await getRecentConversations(); // Update local conversation list updateLocalConversations(conversations); // Update UI with unread counts displayConversationsWithUnreadCounts(conversations); console.log('Conversation sync completed'); } catch (error) { console.error('Failed to sync conversations:', error); // Handle sync failure - maybe retry or show error } } // Call on app startup window.addEventListener('load', syncConversationsOnStartup); ``` ### 2. Message History Synchronization **Load Messages When Entering Chat:** ```javascript theme={null} // Example: Load messages when entering a conversation async function enterConversation(channelId, channelType) { try { // 1. Load message history const messages = await getChannelMessages(channelId, channelType); // 2. Display messages in chat interface displayMessages(messages); // 3. Clear unread count await clearUnreadCount(channelId, channelType); // 4. Update conversation list UI updateConversationUnreadCount(channelId, 0); console.log(`Entered conversation ${channelId}`); } catch (error) { console.error('Failed to enter conversation:', error); } } ``` ### 3. Real-time Updates **Handle Real-time Message Updates:** ```javascript theme={null} // Example: Handle incoming messages while online function handleIncomingMessage(message) { const { channelId, channelType } = message; // Check if user is currently viewing this conversation if (isCurrentlyViewingConversation(channelId, channelType)) { // Display message immediately displayNewMessage(message); // Mark as read automatically markMessageAsRead(message); } else { // Update conversation list with new message updateConversationLastMessage(channelId, message); // Increment unread count incrementUnreadCount(channelId); // Show notification if needed showNotification(message); } } ``` ## API Integration Examples ### Using WuKongIM APIs **1. Get Recent Conversations:** ```javascript theme={null} async function getRecentConversations() { const response = await fetch('/conversations/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: currentUserId, version: lastSyncVersion || 0, limit: 50 }) }); return await response.json(); } ``` **2. Get Channel Messages:** ```javascript theme={null} async function getChannelMessages(channelId, channelType, limit = 20) { const response = await fetch('/channel/messagesync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: channelId, channel_type: channelType, start_message_seq: 0, end_message_seq: 0, limit: limit, pull_mode: 1 // Pull down mode }) }); return await response.json(); } ``` **3. Clear Unread Count:** ```javascript theme={null} async function clearUnreadCount(channelId, channelType) { const response = await fetch('/conversations/clearUnread', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: currentUserId, channel_id: channelId, channel_type: channelType }) }); return await response.json(); } ``` ## Performance Optimization ### 1. Incremental Synchronization Use version-based incremental sync to reduce data transfer: ```javascript theme={null} // Store last sync version locally let lastConversationSyncVersion = localStorage.getItem('lastSyncVersion') || 0; async function incrementalConversationSync() { const response = await fetch('/conversations/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: currentUserId, version: lastConversationSyncVersion, limit: 50 }) }); const data = await response.json(); // Update local version if (data.version) { lastConversationSyncVersion = data.version; localStorage.setItem('lastSyncVersion', data.version); } return data; } ``` ### 2. Pagination for Large Conversations Implement pagination for conversations with many messages: ```javascript theme={null} async function loadMoreMessages(channelId, channelType, oldestMessageSeq) { const response = await fetch('/channel/messagesync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: channelId, channel_type: channelType, start_message_seq: 0, end_message_seq: oldestMessageSeq - 1, limit: 20, pull_mode: 1 }) }); const data = await response.json(); // Prepend older messages to chat prependMessagesToChat(data.messages); return data; } ``` ## Error Handling ### Robust Sync Error Handling ```javascript theme={null} async function robustConversationSync(retryCount = 3) { for (let i = 0; i < retryCount; i++) { try { const conversations = await getRecentConversations(); updateLocalConversations(conversations); return conversations; } catch (error) { console.error(`Sync attempt ${i + 1} failed:`, error); if (i === retryCount - 1) { // Last attempt failed, show error to user showSyncError('Failed to sync conversations. Please check your connection.'); throw error; } // Wait before retry (exponential backoff) await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000)); } } } ``` ## Best Practices Summary 1. **Always sync conversations on app startup** to get the latest state 2. **Use incremental sync** with version numbers to optimize performance 3. **Clear unread counts** when users view conversations 4. **Handle real-time updates** properly based on current user context 5. **Implement proper error handling** with retry mechanisms 6. **Use pagination** for large message histories 7. **Cache data locally** to improve user experience during network issues # Plugin Development Source: https://wukong.mintlify.app/en/getting-started/learning/plugin-development Learn how to develop WuKongIM plugins to extend and enhance message processing functionality ## Overview WuKongIM defines a set of plugin rules that third-party developers can implement to extend or enhance existing WuKongIM message processing logic. Through plugins, you can implement features like sensitive word filtering, message search, AI chat, and more. Plugin functionality is only supported in WuKongIM version 2.1.3 and above ## Plugin Types ### User Plugins User plugins can receive all messages from users bound to the plugin. After installation, they need to be bound to users to take effect. **Features**: * Only process messages from bound users * Suitable for AI chat, personal assistant scenarios * Implement `Receive` function **Use Cases**: * Chat with large language models: Bind specific user UID, sending messages to this user means sending to the LLM * Personal assistant: Provide customized services for specific users ### Global Plugins Global plugins can receive all messages in the system. After installation, they take effect globally. **Features**: * Monitor all system messages * No need to bind users * Suitable for system-level functionality **Use Cases**: * Sensitive word filtering: Monitor every sent message for filtering * Message search: Build search index for all messages * Data analysis: Statistics and analysis of message data ## Development Environment Setup ### Prerequisites * Go language environment (currently plugin development only supports Go) * WuKongIM source code or running instance * [Go PDK](https://github.com/WuKongIM/go-pdk) plugin development library ### Environment Preparation **1. Download WuKongIM Source Code** ```bash theme={null} git clone https://github.com/WuKongIM/WuKongIM.git cd WuKongIM ``` **2. Start Single-Node WuKongIM** ```bash theme={null} go run main.go --config exampleconfig/single.yaml ``` **3. Create Plugin Project** ```bash theme={null} mkdir my-plugin cd my-plugin go mod init my-plugin go get github.com/WuKongIM/go-pdk ``` ## Plugin Development ### Basic Structure Here's a complete plugin development example: ```go Plugin Structure Definition theme={null} package main import ( "encoding/json" "fmt" "github.com/WuKongIM/go-pdk" ) // Define plugin configuration struct type Config struct { Name string `json:"name" label:"AI Name"` // json is config item name, label is display name in WuKongIM backend } // Plugin struct type AIExample struct { Config Config // Plugin configuration, name must be Config } // Plugin initialization func (a *AIExample) Init() error { pdk.Log.Info("AI plugin initialized", pdk.Any("config", a.Config)) return nil } // Plugin information func (a *AIExample) Info() pdk.PluginInfo { return pdk.PluginInfo{ Uid: "ai_example", // Plugin unique ID Name: "AI Example Plugin", // Plugin name Description: "AI chat example plugin", // Plugin description Version: "1.0.0", // Plugin version Type: pdk.PluginTypeUser, // Plugin type: user plugin } } // Receive message processing func (a *AIExample) Receive(message pdk.Message) error { // Parse message content var content map[string]interface{} if err := json.Unmarshal([]byte(message.Payload), &content); err != nil { return err } // Get message text text, ok := content["content"].(string) if !ok { return fmt.Errorf("invalid message content") } // AI processing logic (simplified example) response := fmt.Sprintf("AI %s replies: I received your message '%s'", a.Config.Name, text) // Send reply message replyContent := map[string]interface{}{ "type": "text", "content": response, } replyPayload, _ := json.Marshal(replyContent) return pdk.SendMessage(pdk.SendMessageReq{ ChannelId: message.ChannelId, ChannelType: message.ChannelType, Payload: string(replyPayload), }) } // Plugin entry point func main() { plugin := &AIExample{} pdk.Run(plugin) } ``` ```go Global Plugin Example theme={null} package main import ( "encoding/json" "strings" "github.com/WuKongIM/go-pdk" ) type SensitiveWordFilter struct { Config struct { Words []string `json:"words" label:"Sensitive Words"` } } func (s *SensitiveWordFilter) Init() error { pdk.Log.Info("Sensitive word filter initialized") return nil } func (s *SensitiveWordFilter) Info() pdk.PluginInfo { return pdk.PluginInfo{ Uid: "sensitive_filter", Name: "Sensitive Word Filter", Description: "Filter sensitive words in messages", Version: "1.0.0", Type: pdk.PluginTypeGlobal, // Global plugin } } func (s *SensitiveWordFilter) Receive(message pdk.Message) error { var content map[string]interface{} if err := json.Unmarshal([]byte(message.Payload), &content); err != nil { return err } text, ok := content["content"].(string) if !ok { return nil } // Check for sensitive words for _, word := range s.Config.Words { if strings.Contains(text, word) { pdk.Log.Warn("Sensitive word detected", pdk.String("word", word), pdk.String("message", text)) // Block message or replace with *** content["content"] = strings.ReplaceAll(text, word, "***") newPayload, _ := json.Marshal(content) // Update message content return pdk.UpdateMessage(pdk.UpdateMessageReq{ MessageId: message.MessageId, Payload: string(newPayload), }) } } return nil } func main() { plugin := &SensitiveWordFilter{} pdk.Run(plugin) } ``` ### Core Interfaces #### PluginInfo Plugin information structure: ```go theme={null} type PluginInfo struct { Uid string // Plugin unique ID Name string // Plugin name Description string // Plugin description Version string // Plugin version Type PluginType // Plugin type } ``` #### Message Message structure received by plugin: ```go theme={null} type Message struct { MessageId int64 // Message ID MessageSeq int64 // Message sequence ChannelId string // Channel ID ChannelType int // Channel type FromUid string // Sender UID Payload string // Message content (JSON string) Timestamp int64 // Timestamp } ``` #### Plugin Types ```go theme={null} const ( PluginTypeUser PluginType = "user" // User plugin PluginTypeGlobal PluginType = "global" // Global plugin ) ``` ### Plugin Configuration Plugins can define configuration items that users can set in the WuKongIM management interface: ```go theme={null} type Config struct { APIKey string `json:"api_key" label:"API Key" placeholder:"Enter your API key"` Model string `json:"model" label:"AI Model" default:"gpt-3.5-turbo"` MaxTokens int `json:"max_tokens" label:"Max Tokens" default:"1000"` Keywords []string `json:"keywords" label:"Keywords"` Enabled bool `json:"enabled" label:"Enable Plugin" default:"true"` } ``` **Configuration Tags**: * `json`: Configuration item name * `label`: Display name in management interface * `placeholder`: Input placeholder text * `default`: Default value * `required`: Whether required ## Plugin Compilation and Installation ### Compilation **1. Build Plugin** ```bash theme={null} go build -buildmode=plugin -o ai_example.so main.go ``` **2. Upload Plugin** Upload the compiled `.so` file to WuKongIM management interface. ### Installation and Configuration **1. Install Plugin** In WuKongIM management interface: * Go to Plugin Management * Upload plugin file * Configure plugin parameters * Enable plugin **2. Bind Users (User Plugins Only)** For user plugins, you need to bind specific users: * Go to User Management * Select target user * Bind plugin to user ## API Reference ### Send Message ```go theme={null} func SendMessage(req SendMessageReq) error type SendMessageReq struct { ChannelId string // Target channel ID ChannelType int // Channel type Payload string // Message content (JSON string) FromUid string // Sender UID (optional) } ``` ### Update Message ```go theme={null} func UpdateMessage(req UpdateMessageReq) error type UpdateMessageReq struct { MessageId int64 // Message ID to update Payload string // New message content } ``` ### Logging ```go theme={null} pdk.Log.Info("Info message", pdk.String("key", "value")) pdk.Log.Warn("Warning message", pdk.Int("count", 10)) pdk.Log.Error("Error message", pdk.Any("data", obj)) ``` ## Best Practices 1. **Error Handling** * Always handle errors gracefully * Use appropriate logging levels * Don't panic in plugin code 2. **Performance** * Avoid blocking operations * Use goroutines for heavy processing * Implement proper timeouts 3. **Security** * Validate all input data * Sanitize user content * Use secure API calls 4. **Configuration** * Provide sensible defaults * Validate configuration values * Document all configuration options ## Related Resources * [Go PDK Documentation](https://github.com/WuKongIM/go-pdk) * [Plugin Examples](https://github.com/WuKongIM/plugin-examples) * [API Documentation](/en/api/introduction) # Binary Protocol Source: https://wukong.mintlify.app/en/getting-started/learning/protocol Detailed specification of WuKongIM communication protocol and packet structure # WuKongIM Protocol WuKongIM protocol is an efficient binary communication protocol designed specifically for instant messaging scenarios. This document provides detailed descriptions of packet structure, control types, and encoding specifications. *** ## 📋 Control Packet Structure Each WuKongIM control packet consists of the following three parts: | Parameter Name | Type | Description | | :-------------- | :----- | :-------------- | | Fixed header | 1 byte | Fixed header | | Variable header | bytes | Variable header | | Payload | bytes | Message body | *** ## 🔧 Fixed Header Each WuKongIM control packet contains a fixed header used to identify packet type and length information. | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | --------- | ---------------------------- | ---------------------------- | ---------------------------- | ---------------------------- | --------------------------------- | --------------------------------- | --------------------------------- | --------------------------------- | | byte 1 | WuKongIM control packet type | WuKongIM control packet type | WuKongIM control packet type | WuKongIM control packet type | Flag bits for control packet type | Flag bits for control packet type | Flag bits for control packet type | Flag bits for control packet type | | byte 2... | Remaining length | Remaining length | Remaining length | Remaining length | Remaining length | Remaining length | Remaining length | Remaining length | ### 📝 WuKongIM Control Packet Types WuKongIM protocol defines 10 different control packet types, each with specific purposes and data structures. | Name | Value | Description | | ---------- | ----- | --------------------------------------------------------------------- | | Reserved | 0 | Reserved bit | | CONNECT | 1 | Client request to connect to server (c2s) | | CONNACK | 2 | Server acknowledgment packet after receiving connection request (s2c) | | SEND | 3 | Send message (c2s) | | SENDACK | 4 | Message acknowledgment packet (s2c) | | RECV | 5 | Receive message (s2c) | | RECVACK | 6 | Receive message acknowledgment (c2s) | | PING | 7 | Ping request | | PONG | 8 | Response to ping request | | DISCONNECT | 9 | Request to disconnect | ### 🏷️ Control Packet Flag Bits Different protocol packets use different flag bits to control message behavior: Flag bits in Send and Recv protocols | bit | 3 | 2 | 1 | 0 | | ---- | --- | -------- | ------ | --------- | | byte | DUP | SyncOnce | RedDot | NoPersist | Flag bits in Connack protocol | bit | 3 | 2 | 1 | 0 | | ---- | -------- | -------- | -------- | ---------------- | | byte | Reserved | Reserved | Reserved | HasServerVersion | Flag bits in Chunk protocol | bit | 3 | 2 | 1 | 0 | | ---- | -------- | -------- | -------- | --- | | byte | Reserved | Reserved | Reserved | End | **Flag bit descriptions**: * **DUP**: Whether it's a duplicate message (clients need to mark DUP as 1 when resending messages) * **SyncOnce**: Sync only once. In multi-device scenarios, if one device has pulled this message, other devices will not pull this message again (e.g., friend request messages) * **RedDot**: Whether the client should show a red dot when receiving the message * **NoPersist**: Whether not to store this message * **Reserved**: Reserved bit * **HasServerVersion**: Whether there is a server version number * **End**: Whether it's the ending message chunk ### 📏 Remaining Length Encoding The remaining length field indicates the number of bytes remaining in the current message, including variable header and payload (content). This is a variable-length encoding field. **Encoding rules**: * Single byte maximum value: `01111111` (0x7F, 127) * The eighth bit (most significant bit) being 1 indicates there are subsequent bytes * Maximum of 4 bytes allowed to represent remaining length * Maximum length: `0xFF,0xFF,0xFF,0x7F` = 268,435,455 bytes = 256MB **Byte ranges**: | Digits | From | To | | ------ | ---------------------------------- | ------------------------------------ | | 1 | 0 (0x00) | 127 (0x7F) | | 2 | 128 (0x80, 0x01) | 16 383 (0xFF, 0x7F) | | 3 | 16 384 (0x80, 0x80, 0x01) | 2 097 151 (0xFF, 0xFF, 0x7F) | | 4 | 2 097 152 (0x80, 0x80, 0x80, 0x01) | 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F) | **Encoding understanding**: * 1st byte base: 1 * 2nd byte base: 128 (2^7) * 3rd byte base: 128×128 = 2^14 * 4th byte base: 128×128×128 = 2^21 **Encoding example**: Expressing 321 = 65 + 2×128 (2 bytes): `11000001 00000010` ``` First byte 193 (11000001): - Most significant bit is 1, indicating subsequent bytes - Lower 7 bits are 1000001 (65) Second byte 2 (00000010): - Most significant bit is 0, indicating end - Lower 7 bits are 0000010 (2) Calculation: 321 = 65 + 2 × 128 = 65 + 256 ``` **Byte order**: The first byte is low-order, subsequent bytes are high-order, but within bytes, low-order is on the right, high-order is on the left. ### 🔤 String UTF-8 Encoding WuKongIM uses a modified UTF-8 encoding format: | bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | ---------- | ---------------------- | ---------------------- | ---------------------- | ---------------------- | ---------------------- | ---------------------- | ---------------------- | ---------------------- | | byte 1 | String Length MSB | String Length MSB | String Length MSB | String Length MSB | String Length MSB | String Length MSB | String Length MSB | String Length MSB | | byte 2 | String Length MSB | String Length MSB | String Length MSB | String Length MSB | String Length MSB | String Length MSB | String Length MSB | String Length MSB | | bytes 3... | Encoded Character Data | Encoded Character Data | Encoded Character Data | Encoded Character Data | Encoded Character Data | Encoded Character Data | Encoded Character Data | Encoded Character Data | *** ## 🔄 Variable Header Some control packets contain a variable header section located between the fixed header and payload. The content of the variable header varies depending on the packet type. *** # 📡 Protocol Packet Specifications ## 🔌 CONNECT Connection Packet Packet format used when a client initiates a connection request to the server. | Parameter Name | Type | Description | | ---------------- | -------- | -------------------------------------------------------------- | | Packet Type | 0.5 byte | Packet type (1) | | Flag | 0.5 byte | Flag bits | | Remaining Length | ... byte | Remaining packet length | | Protocol Version | int8 | Protocol version number | | Device Flag | int8 | Device flag (same flag for same account mutual kick) | | Device ID | string | Unique device ID | | UID | string | User ID | | Token | string | User token | | Client Timestamp | int64 | Client current timestamp (13-digit timestamp, to milliseconds) | | Client Key | string | Client KEY (base64 encoded DH public key) | ## ✅ CONNACK Connection Acknowledgment CONNACK packet is sent by the server as a response to the client's CONNECT packet. If the client doesn't receive a CONNACK packet within a reasonable time, it should close the network connection. | Parameter Name | Type | Description | | ---------------- | -------- | ------------------------------------------------------------------------------ | | Packet Type | 0.5 byte | Packet type (2) | | Flag | 0.5 byte | Flag bits | | Remaining Length | ... byte | Remaining packet length | | ServerVersion | uint8 | Maximum version supported by server, valid when flag contains HasServerVersion | | Time Diff | int64 | Difference between client time and server time, in milliseconds | | Reason Code | uint8 | Connection reason code (see appendix) | | Server Key | string | Server base64 DH public key | | Salt | string | Security code | ## 📤 SEND Send Message Packet format used when a client sends a message to the server. | Parameter Name | Type | Description | | ---------------- | -------- | ----------------------------------------------------------------------- | | Packet Type | 0.5 byte | Packet type (3) | | Flag | 0.5 byte | Flag bits | | Remaining Length | ... byte | Remaining packet length | | Setting | 1 byte | Message settings | | Client Seq | uint32 | Client message sequence number (generated by client, unique per client) | | Client Msg No | string | Client unique identifier for message deduplication | | StreamNo | string | Stream message number (Stream must be enabled in settings) | | Channel Id | string | Channel ID (for personal channels, ChannelId is the person's UID) | | Channel Type | int8 | Channel type (1. Personal 2. Group) | | Expire | uint32 | Message expiration time (in seconds) version>=3 | | Msg Key | string | Used to verify message legitimacy (prevent man-in-the-middle tampering) | | Topic | string | Topic ID (only present when topic is enabled in settings) | | Payload | ... byte | Message content | ## ✅ SENDACK Send Message Acknowledgment Server acknowledgment response to client message sending. | Parameter Name | Type | Description | | ---------------- | -------- | ----------------------------------------------------------- | | Packet Type | 0.5 byte | Packet type (4) | | Flag | 0.5 byte | Flag bits | | Remaining Length | ... byte | Remaining packet length | | Message ID | uint64 | Server message ID (globally unique) | | Client Seq | uint32 | Client message sequence number | | Message Seq | uint32 | Message sequence number (ordered increment, channel unique) | | Reason Code | uint8 | Send reason code, 1 indicates success | ## 📥 RECV Receive Message Packet format used when the server pushes messages to the client. | Parameter Name | Type | Description | | ----------------- | -------- | ------------------------------------------------------------------------------ | | Packet Type | 0.5 byte | Packet type (5) | | Flag | 0.5 byte | Flag bits | | Remaining Length | ... byte | Remaining packet length | | Setting | 1 byte | Message settings (see below, valid for version 4) | | Msg Key | string | Used to verify message legitimacy (prevent man-in-the-middle tampering) | | From UID | string | Sender UID | | Channel ID | string | Channel ID | | Channel Type | int8 | Channel type | | Expire | uint32 | Message expiration time (in seconds) version>=3 | | Client Msg No | string | Client unique identifier for message deduplication | | StreamNo | string | Stream message number, present based on whether stream is enabled in settings | | StreamId | uint32 | Stream sequence number, present based on whether stream is enabled in settings | | Message ID | uint64 | Server message ID (globally unique) | | Message Seq | uint32 | Server message sequence number (ordered increment, channel unique) | | Message Timestamp | int32 | Server message timestamp (10 digits, to seconds) | | Topic | string | Topic ID (only present when topic is enabled in settings) | | Payload | ... byte | Message content | ## ✅ RECVACK Receive Message Acknowledgment Client acknowledgment response to server message push. | Parameter Name | Type | Description | | ---------------- | -------- | ----------------------------------- | | Packet Type | 0.5 byte | Packet type (6) | | Flag | 0.5 byte | Flag bits | | Remaining Length | ... byte | Remaining packet length | | Message ID | uint64 | Server message ID (globally unique) | | Message Seq | uint32 | Sequence number | ## 🏓 PING Heartbeat request packet sent by the client to keep the connection active. | Parameter Name | Type | Description | | -------------- | -------- | --------------- | | Packet Type | 0.5 byte | Packet type (7) | | Flag | 0.5 byte | Flag bits | ## 🏓 PONG Server response packet to client PING request. | Parameter Name | Type | Description | | -------------- | -------- | --------------- | | Packet Type | 0.5 byte | Packet type (8) | | Flag | 0.5 byte | Flag bits | ## 🔌 DISCONNECT Packet format used when client or server requests to disconnect. | Parameter Name | Type | Description | | ---------------- | -------- | ----------------------- | | Packet Type | 0.5 byte | Packet type (9) | | Flag | 0.5 byte | Flag bits | | Remaining Length | ... byte | Remaining packet length | | ReasonCode | uint8 | Reason code | | Reason | string | Reason | *** # ⚙️ Message Settings ## 📊 Message Setting Bit Fields Message settings are 1 byte (8 bits) used to control various message behavior characteristics. | bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | ---- | ------- | -------- | ------ | --------- | ----- | -------- | ------ | -------- | | byte | Receipt | Reserved | Signal | NoEncrypt | Topic | Reserved | Stream | Reserved | ## 🔧 Setting Bit Descriptions **Bit function descriptions**: * **Receipt**: Message read receipt, this flag indicates this message requires read receipt * **Reserved**: Reserved bit, not yet used * **Signal**: Encryption flag * **NoEncrypt**: Whether message encryption is disabled * **Topic**: Whether message contains topic (if 1, both send and receive packets will include topic field) * **Reserved**: Reserved bit, not yet used * **Stream**: Stream message flag * **Reserved**: Reserved bit, not yet used *** **Complete Protocol Documentation**: This document contains the core parts of the WuKongIM protocol. The complete protocol specification also includes recommended Payload structure, regular message format, system message format, and other detailed content. Please refer to the source documentation for complete information. # Stress Testing Source: https://wukong.mintlify.app/en/getting-started/learning/stress-testing Learn how to perform stress testing on WuKongIM, including configuration, stress tester installation, running tests and analyzing reports # Stress Testing WuKongIM stress testing functionality is only available in versions v2.1.2-20250120 and above ## Server Stress Configuration WuKongIM server new configuration: ```yaml wk.yaml theme={null} ... stress: true intranet: tcpAddr: "ip:port" ... ``` ```yaml environment theme={null} WK_STRESS=true WK_INTRANET_TCPADDR=ip:port ``` ### Configuration Description **`stress`** Enable stress testing configuration. Only when enabled can the server be stress tested. **`intranet.tcpAddr`** * **No load balancer configured (single node mode)**: `tcpAddr` is WuKongIM server's internal IP + port 5100, e.g.: 192.168.1.12:5100 * **Load balancer configured (distributed mode)**: `tcpAddr` is load balancer's internal IP + port 15100, e.g.: 192.168.1.11:15100 ## Install Stress Tester ### Configuration Requirements Stress tester requires a server with large memory, recommended 4 cores 16GB or above. ### Installation Steps **1. Download Executable File** ```bash amd64 theme={null} sudo curl -L -o wkstress https://github.com/WuKongIM/StressTester/releases/download/v1.1.0/wkstress-linux-amd64 ``` ```bash arm64 theme={null} sudo curl -L -o wkstress https://github.com/WuKongIM/StressTester/releases/download/v1.1.0/wkstress-linux-arm64 ``` **2. Modify Executable File Permissions** ```bash theme={null} sudo chmod +x wkstress ``` ### Run Stress Tester **Start** ```bash theme={null} nohup ./wkstress & ``` Port: 9466 **Stop** ```bash theme={null} kill -9 $(lsof -t -i :9466) ``` ## Start Stress Testing ### 1. Add Stress Testing Machine Address: `http://ip:9466`, where ip is the **internal IP** of the stress testing machine, as shown below: Add Stress Tester ### 2. Set Stress Testing Metrics and Run Set stress testing metrics and run, as shown below: Run Stress Test ## View Stress Testing Report Stress Testing Report ### Report Field Explanation **Test Metrics** Represents the stress testing data currently being tested by the stress tester. **Test Report** * **Runtime**: Duration since stress testing started * **Online Users**: Number of simulated concurrent online users * **Offline Users**: Number of simulated online users that have disconnected * **Sent Messages**: Total number of messages sent to server * **Send Rate**: Current rate of messages sent to server * **Send Size**: Total size of sent messages * **Send Traffic**: Current bandwidth real-time traffic of sent messages * **Send Success**: Number of messages that received successful response from server after sending * **Send Errors**: Number of messages that received failed response from server after sending * **Send Min Latency**: Minimum round-trip time from sending message to server until receiving server response * **Send Max Latency**: Maximum round-trip time from sending message to server until receiving server response * **Send Avg Latency**: Average round-trip time from sending message to server until receiving server response * **Received Messages**: Total number of messages received * **Receive Rate**: Current rate of messages received * **Receive Size**: Total size of received messages * **Receive Traffic**: Current bandwidth traffic of received messages * **Receive Min Latency**: Minimum time from sending message until subscriber receives message * **Receive Max Latency**: Maximum time from sending message until subscriber receives message * **Receive Avg Latency**: Average time from sending message until subscriber receives message ## Report Analysis ### Core Metrics Analysis #### Send Rate Also known as send message concurrency. **Description** This metric shows the server's ability to handle concurrent messages. The higher the value, the stronger the server's concurrency capability, and the more users can send messages simultaneously per second. Based on the following algorithm, you can roughly estimate the relationship between `send rate` and `Daily Active Users (DAU)`: ``` Peak Concurrency = DAU × Peak Active User Ratio × Send Rate Per User ``` **Assumptions**: * Daily Active Users (DAU): 100,000 people * Peak online user ratio: approximately 10%~20%, take 15% * Send rate per user per minute ~= 6 messages/minute = 0.1 messages/second **Then**: `Peak message sending concurrency = 100,000 × 0.15 × 0.1 = 1,500 messages/second` **Conclusion**: `A send rate around 1,500 messages/second can support approximately 100,000 daily active users.` By adjusting peak online user ratio and send rate per user, you can roughly estimate your peak send rate, and indirectly verify through stress testing whether the server can meet your daily active user requirements. #### Receive Rate Also known as receive message concurrency. **Description** This metric shows the server's ability to deliver messages concurrently. The higher the value, the stronger the server's concurrency capability, and the more private chats and group chats it can support simultaneously. For group chat or broadcast scenarios, one message may be received by multiple users. Therefore, receive concurrency also needs to be calculated: ``` Peak Receive Message Concurrency = Peak Active Group Count × Average Online Members Per Group × Message Send Frequency Per Group ``` **Assumptions**: * Daily active group chat count: 10,000 groups * Average online members per group: 20 people * Peak active group ratio: approximately 10%~20%, take 15% * Message send frequency per group: 0.5 messages/second **Then**: `Peak receive message concurrency = 10,000 × 20 × 0.15 × 0.5 = 15,000 messages/second` **Conclusion**: `Message receive rate around 15,000 messages/second can support approximately 10,000 daily active groups with 20 online members each` Through this formula + stress tester + daily active group chats, you can stress test the required server resource configuration. #### Send Success and Errors `Send success and failure` refers to the result from when client starts sending message until server receives and stores it, then returns success or failure to client. `This metric reflects server health. Higher send success rate indicates healthier system.` #### Send and Receive Average Latency `This metric observes server stability` Lower average latency indicates more stable system. #### Send Success and Expected Received Message Count `This metric observes whether there are missing messages` Expected received message count can be viewed by clicking the question mark next to the receive message metric. After stopping stress testing, click view report again. If the send success message count in the report equals the expected received message count, it means all messages were received by subscribing clients. This metric is only meaningful when Send Messages = Send Success #### Send Traffic and Receive Traffic `This metric observes the bandwidth size the server needs to support` ## Common Issues **Answer**: Modify file descriptor limits for WuKongIM and nginx servers **Check current file descriptor limit**: ```bash theme={null} ulimit -n ``` **Temporarily modify file descriptor limit**: ```bash theme={null} ulimit -n 100000 ``` **Permanently modify file descriptor limit**: Edit `/etc/security/limits.conf` file, add the following lines: ``` * soft nofile 100000 * hard nofile 100000 ``` Modify `worker_connections` in nginx configuration, can be changed to around 40960 ## Related Resources * [Stress Testing Report](/en/getting-started/stress-report) * [System Integration Guide](/en/getting-started/learning/system-integration) * [API Documentation](/en/api/introduction) # System Integration Source: https://wukong.mintlify.app/en/getting-started/learning/system-integration Complete guide for integrating WuKongIM into existing business systems # System Integration `All WuKongIM APIs should not be called directly by the APP client. The frontend should first call your own business API, and the business API should then call WuKongIM's API. WuKongIM's API should not be exposed to the external network to ensure data security.` ## Step 1: Integrate with Your Business System Users User Integration Flow 1. Your application calls your own login or registration business API. 2. After your login or registration business API processes its own business logic, it generates a user ID (uid) and token, then calls WuKongIM's [Login or Register User](/en/api/user/token) API to update WuKongIM 3. Return the uid and token to your own application client. 4. The application client calls WuKongIM SDK's connect method, passing in the uid and token. WuKongIM SDK will pass the uid and token to WuKongIM server for verification. If the client-passed values match the server-passed values, verification will pass, and after successful verification, a persistent connection will be maintained. ## Step 2: Provide Webhook Interface The third-party service provides an HTTP API interface. WuKongIM will pass corresponding data to this interface according to the Webhook agreement using an event mechanism. ## Step 3: Configure Webhook Interface to WuKongIM Configure your HTTP API interface into ***WuKongIM***. ```yaml theme={null} --- webhook: # httpAddr: 'http://xxxxx' # webhook HTTP address, used to notify data to third parties ``` Events from applications integrated with WuKongIM SDK will be pushed to the third-party system. For example, after a user comes online, ***WuKong SDK*** will notify ***WuKongIM*** server, and ***WuKongIM*** will push the online event to the third-party server through the configured webhook address. # Stress Test Report (Single Node) Source: https://wukong.mintlify.app/en/getting-started/stress-report Detailed performance stress test report for WuKongIM single node mode ## Preface The excellence of a communication system is primarily measured by four indicators: `high performance`, `stability`, `reliability`, and `ordering`. **High performance**: Extreme stress testing of message sending concurrency and message receiving concurrency. **Stability**: Testing whether CPU and memory remain stable during long-term operation under high concurrency. **Reliability**: Testing whether messages are lost. **Ordering**: Testing whether messages are out of order. ## Test Environment All stress test results can be reproduced on your own server according to the test content and hardware specifications. **WuKongIM Test Version**: `v2.1.2-20250120` ### Hardware Information | Resource Description | Hardware Configuration | Quantity | | :------------------- | :------------------------------------------------------------------------------------------------------------------------- | :------- | | WuKongIM Server | Ubuntu 22.04 LTS SA5.4XLARGE32 (Standard SA5, 16 cores 32GB), 50G Enhanced SSD Cloud Disk, Baseline Performance: 4300 IOPS | 1 unit | | Stress Test Machine | Ubuntu 22.04 LTS SA5.4XLARGE32 (Standard SA5, 16 cores 32GB), 50G Enhanced SSD Cloud Disk, Baseline Performance: 4300 IOPS | 1 unit | ## Performance Testing `Core indicator of whether a communication system is high-performance: message sending concurrency and message receiving concurrency (in simple terms: the maximum number of messages the system can process for sending and delivery per second)` ### Send Rate Testing **Test Content** | Content | Online | Send Rate | | :---------------------------- | :------------------------ | :----------------------------------- | | 100 groups of 100 people each | 1 person online per group | 24,000 messages per minute per group | **Total Send Rate**: `100 * (24000/60) = 40,000 messages/second` **Test Screenshots** | Test Content | System Top Information | | :-------------------------------- | :-------------------------------- | | Running Stress Test | Running Stress Test |