> ## Documentation Index
> Fetch the complete documentation index at: https://wukong.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Message Management

> WuKongIM HarmonyOS SDK message management functionality, including message sending/receiving, listening and history messages

## Sending Messages

### Description

Method for sending messages

```typescript theme={null}
 /**
  *  Send message
  * @param model  Message content
  * @param channel Channel object (personal channel, group channel)
*/
WKIM.shared.messageManager().send(model: WKMessageContent, channel: WKChannel);
```

### Text Messages

```typescript theme={null}
// Text message
let textModel: WKTextContent = new WKTextContent('Hello, WuKong')

// Send to user A
WKIM.shared.messageManager().send(textModel, new WKChannel('A', WKChannelType.personal));
```

### Image Messages

```typescript theme={null}
// Image message
let imageModel: WKImageContent = new WKImageContent(localPath)
imageModel.width = 100
imageModel.height = 100

// Send to user A
WKIM.shared.messageManager().send(imageModel, new WKChannel('A', WKChannelType.personal));
```

### Custom Messages

Reference custom messages: [Custom Messages](/en/sdk/wukongim/harmonyos/advance#custom-messages)

## Message Storage Callback (Not Message Send Status)

When sending messages, the SDK will trigger a storage callback after saving the message to the local database. At this point, the message has not been sent yet, and you can display the message in the UI in this listener.

```typescript theme={null}
 // Listen for message storage
WKIM.shared.messageManager().addInsertedListener((msg) => {
    // Display message in UI
})
```

## New Messages

Listen for new message events

```typescript theme={null}
// New message listener
newMsgsListener = (msgs: WKMsg[]) => {
   // Handle new messages
  }

// Listen for new messages
WKIM.shared.messageManager().addNewMsgListener(this.newMsgsListener)

// Remove new message listener
WKIM.shared.messageManager().removeNewMsgListener(this.newMsgsListener)
```

## Message Refresh

When the SDK updates messages, such as: message send status, someone likes a message, message read receipt, message recall, message editing, etc., the SDK will callback the following event. The UI can determine which specific message has changed through the `clientMsgNO` of the message object `WKMsg`.

```typescript theme={null}
// Refresh message listener
refreshMsgListener = (msg: WKMsg) => {
// Handle refresh message
}

// Listen for refresh messages
WKIM.shared.messageManager().addRefreshListener(this.refreshMsgListener)

// Remove refresh message listener
WKIM.shared.messageManager().removeRefreshListener(this.refreshMsgListener)
```

## Message Send Status Code (ReasonCode)

When a message is sent, the `reasonCode` in the `WKMsg` object returned by the `addRefreshListener` indicates the result of the message delivery. Below are the descriptions for each status code:

| Value | Name                     | Description                                |
| ----- | ------------------------ | ------------------------------------------ |
| 0     | ReasonUnknown            | Unknown error                              |
| 1     | ReasonSuccess            | Success                                    |
| 2     | ReasonAuthFail           | Authentication failed                      |
| 3     | ReasonSubscriberNotExist | Subscriber does not exist in the channel   |
| 4     | ReasonInBlacklist        | In blacklist                               |
| 5     | ReasonChannelNotExist    | Channel does not exist                     |
| 6     | ReasonUserNotOnNode      | User is not on node                        |
| 7     | ReasonSenderOffline      | Sender is offline, message delivery failed |
| 8     | ReasonMsgKeyError        | Message key error, invalid message         |
| 9     | PayloadDecodeError       | Payload decoding failed                    |
| 10    | ForwardSendPacketError   | Forwarding send packet failed              |
| 11    | NotAllowSend             | Not allowed to send message                |
| 12    | ConnectKick              | Connection kicked                          |
| 13    | NotInWhitelist           | Not in whitelist                           |
| 14    | QueryTokenError          | Query user token error                     |
| 15    | SystemError              | System error                               |
| 16    | ChannelIDError           | Wrong channel ID                           |
| 17    | NodeMatchError           | Node matching error                        |
| 18    | NodeNotMatch             | Node not matched                           |
| 19    | Ban                      | Channel is banned                          |
| 20    | NotSupportHeader         | Unsupported header                         |
| 21    | ClientKeyIsEmpty         | clientKey is empty                         |
| 22    | RateLimit                | Rate limit exceeded                        |
| 23    | NotSupportChannelType    | Unsupported channel type                   |
| 24    | Disband                  | Channel disbanded                          |
| 25    | SendBan                  | Sending is banned                          |

## View Chat Information for a Channel

```typescript theme={null}
let option = new ChannelMsgOptions(() => {
      // Syncing - show loading as needed
    }, (list) => {
        // Message data
    })
option.oldestOrderSeq = 0 // Last message's large orderSeq, pass 0 for first entry into chat
option.contain = false // Whether to include the oldestOrderSeq message
option.pullMode = 1 // Pull mode 0: pull down 1: pull up
option.limit = 20 // Number to get each time
option.aroundMsgOrderSeq = 0 // Query messages around this message, e.g. aroundMsgOrderSeq=20 returns [16,17,19,20,21,22,23,24,25]
// View chat information for a channel
WKIM.shared.messageManager().getOrSyncHistoryMessages(channel, option)
```

<Note>
  Getting history messages is not a synchronous method, as there may be non-continuous data that needs to be synced from the server
</Note>

## Complete Message Management Example

```typescript theme={null}
import { WKIM, WKMsg, WKMessageContent, WKTextContent, WKImageContent, WKChannel, WKChannelType, ChannelMsgOptions } from '@wukong/wkim';

@Component
export struct MessageManager {
  @State private messages: WKMsg[] = [];
  @State private isLoading: boolean = false;
  
  private newMsgsListener = (msgs: WKMsg[]) => {
    this.handleNewMessages(msgs);
  }
  
  private refreshMsgListener = (msg: WKMsg) => {
    this.handleMessageRefresh(msg);
  }
  
  private insertedListener = (msg: WKMsg) => {
    this.handleMessageInserted(msg);
  }
  
  aboutToAppear(): void {
    this.setupMessageListeners();
  }
  
  aboutToDisappear(): void {
    this.removeMessageListeners();
  }
  
  private setupMessageListeners(): void {
    // Listen for message storage
    WKIM.shared.messageManager().addInsertedListener(this.insertedListener);
    
    // Listen for new messages
    WKIM.shared.messageManager().addNewMsgListener(this.newMsgsListener);
    
    // Listen for message refresh
    WKIM.shared.messageManager().addRefreshListener(this.refreshMsgListener);
  }
  
  private removeMessageListeners(): void {
    WKIM.shared.messageManager().removeInsertedListener(this.insertedListener);
    WKIM.shared.messageManager().removeNewMsgListener(this.newMsgsListener);
    WKIM.shared.messageManager().removeRefreshListener(this.refreshMsgListener);
  }
  
  private handleMessageInserted(msg: WKMsg): void {
    // Message saved to database, update UI immediately
    console.log('Message inserted:', msg.clientMsgNo);
    this.addMessageToUI(msg);
  }
  
  private handleNewMessages(msgs: WKMsg[]): void {
    console.log('Received new messages:', msgs.length);
    msgs.forEach(msg => {
      this.addMessageToUI(msg);
    });
  }
  
  private handleMessageRefresh(msg: WKMsg): void {
    console.log('Message refreshed:', msg.clientMsgNo);
    this.updateMessageInUI(msg);
  }
  
  private addMessageToUI(msg: WKMsg): void {
    // Check if message already exists
    const existingIndex = this.messages.findIndex(m => m.clientMsgNo === msg.clientMsgNo);
    
    if (existingIndex >= 0) {
      // Update existing message
      this.messages[existingIndex] = msg;
    } else {
      // Add new message
      this.messages.push(msg);
    }
    
    // Sort messages by timestamp
    this.messages.sort((a, b) => a.timestamp - b.timestamp);
  }
  
  private updateMessageInUI(msg: WKMsg): void {
    const index = this.messages.findIndex(m => m.clientMsgNo === msg.clientMsgNo);
    if (index >= 0) {
      this.messages[index] = msg;
    }
  }
  
  // Send text message
  public sendTextMessage(text: string, channel: WKChannel): void {
    try {
      const textModel = new WKTextContent(text);
      WKIM.shared.messageManager().send(textModel, channel);
    } catch (error) {
      console.error('Failed to send text message:', error);
    }
  }
  
  // Send image message
  public sendImageMessage(localPath: string, width: number, height: number, channel: WKChannel): void {
    try {
      const imageModel = new WKImageContent(localPath);
      imageModel.width = width;
      imageModel.height = height;
      WKIM.shared.messageManager().send(imageModel, channel);
    } catch (error) {
      console.error('Failed to send image message:', error);
    }
  }
  
  // Load history messages
  public loadHistoryMessages(channel: WKChannel, oldestOrderSeq: number = 0): void {
    this.isLoading = true;
    
    const option = new ChannelMsgOptions(
      () => {
        // Syncing callback
        console.log('Syncing messages...');
      },
      (list: WKMsg[]) => {
        // Message data callback
        this.isLoading = false;
        this.handleHistoryMessages(list);
      }
    );
    
    option.oldestOrderSeq = oldestOrderSeq;
    option.contain = false;
    option.pullMode = 0; // Pull down
    option.limit = 20;
    option.aroundMsgOrderSeq = 0;
    
    WKIM.shared.messageManager().getOrSyncHistoryMessages(channel, option);
  }
  
  private handleHistoryMessages(messages: WKMsg[]): void {
    console.log('Loaded history messages:', messages.length);
    
    // Merge with existing messages
    messages.forEach(msg => {
      const existingIndex = this.messages.findIndex(m => m.clientMsgNo === msg.clientMsgNo);
      if (existingIndex < 0) {
        this.messages.push(msg);
      }
    });
    
    // Sort messages by timestamp
    this.messages.sort((a, b) => a.timestamp - b.timestamp);
  }
  
  // Get messages for specific channel
  public getMessagesForChannel(channelId: string, channelType: number): WKMsg[] {
    return this.messages.filter(msg => 
      msg.channelId === channelId && msg.channelType === channelType
    );
  }
  
  // Clear messages
  public clearMessages(): void {
    this.messages = [];
  }
  
  build() {
    Column() {
      Text('Message Manager')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })
      
      if (this.isLoading) {
        Row() {
          LoadingProgress()
            .width(20)
            .height(20)
            .margin({ right: 10 })
          Text('Loading messages...')
        }
        .margin({ bottom: 20 })
      }
      
      Text(`Total Messages: ${this.messages.length}`)
        .fontSize(16)
        .margin({ bottom: 20 })
      
      List() {
        ForEach(this.messages, (message: WKMsg) => {
          ListItem() {
            this.buildMessageItem(message)
          }
        })
      }
      .layoutWeight(1)
      
      Row() {
        TextInput({ placeholder: 'Enter message...' })
          .layoutWeight(1)
          .onSubmit((value: string) => {
            const channel = new WKChannel('test_user', WKChannelType.personal);
            this.sendTextMessage(value, channel);
          })
        
        Button('Send')
          .onClick(() => {
            // Send message logic
          })
      }
      .padding(10)
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
  
  @Builder
  buildMessageItem(message: WKMsg) {
    Column() {
      Row() {
        Text(message.fromUID)
          .fontSize(14)
          .fontColor(Color.Gray)
          .margin({ right: 10 })
        
        Text(this.formatTimestamp(message.timestamp))
          .fontSize(12)
          .fontColor(Color.Gray)
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)
      .margin({ bottom: 5 })
      
      Text(message.content)
        .fontSize(16)
        .textAlign(TextAlign.Start)
        .width('100%')
      
      if (message.status === 0) {
        Text('Sending...')
          .fontSize(12)
          .fontColor(Color.Orange)
          .margin({ top: 5 })
      }
    }
    .alignItems(HorizontalAlign.Start)
    .padding(10)
    .margin({ bottom: 10 })
    .backgroundColor(Color.White)
    .borderRadius(8)
  }
  
  private formatTimestamp(timestamp: number): string {
    const date = new Date(timestamp * 1000);
    return date.toLocaleTimeString();
  }
}
```

## Offline Messages

`Need to implement sync channel message data source` [Channel Message Data Source](/en/sdk/wukongim/harmonyos/datasource#channel-message-data-source)

Because WuKongIM supports permanent message storage, it will generate massive offline messages. For this, we adopt an on-demand pull mechanism. For example, with 10 conversations each having 100,000 messages, WuKongIM will not pull all 10\*100,000=1 million messages to local storage. Instead, it pulls information for these 10 conversations and the corresponding latest 20 messages, which means actually only 200 messages are pulled. Compared to 1 million messages, this greatly improves offline pull speed. Users will only pull messages for a specific conversation when they enter that conversation. These mechanisms are already encapsulated within the SDK, so users don't need to worry about them. Users only need to focus on recent conversation changes and listen for data retrieval callbacks.

## Data Structure Description

### Message Structure

```typescript theme={null}
export class WKMsg {
  // Server unique message ID
  messageId = "";
  // Server message sequence number
  messageSeq = 0;
  // Client message sequence number
  clientSeq = 0;
  // Message timestamp
  timestamp = 0;
  // Expiration time
  expireTime = 0;
  // Expiration timestamp
  expireTimestamp = 0;
  // Client unique number
  clientMsgNo = "";
  // Sender uid
  fromUID = "";
  // Channel ID
  channelId = "";
  // Channel type
  channelType = WKChannelType.personal;
  // Message type
  contentType = 0;
  // Message content string
  content = "";
  // Message status 1.sent successfully 0.sending
  status = 0;
  voiceStatus = 0;
  // Is deleted 1.yes
  isDeleted = 0;
  // Search keyword
  searchableWord = "";
  // Message sender profile
  private from?: WKChannel
  // Message channel profile
  private channelInfo?: WKChannel
  // Message sender profile in channel (only for group messages)
  private memberOfFrom?: WKChannelMember
  // Sort number
  orderSeq = 0;
  // Is read
  viewed = 0;
  // Read time
  viewedAt = 0;
  // Topic ID
  topicId = "";
  // Local extensions
  localExtraMap?: Record<string, object>
  // Remote extensions
  wkMsgExtra?: WKMsgExtra
  // Message reaction data
  reactionList?: WKMsgReaction[]
  // Message content
  messageContent?: WKMessageContent
}
```

### Message Content Structure

```typescript theme={null}
export class WKMessageContent {
  // Message type
  contentType: number = 0
  // Message content
  content: string = ""
  // Render message content, e.g., need to render @xxx text when @someone
  entities?: ArrayList<WKMsgEntity>
  // Message reply
  reply?: WKReply
  // Mention information
  mentionInfo?: WKMentionInfo
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Channel Management" icon="hash" href="/en/sdk/wukongim/harmonyos/channel">
    Learn how to manage channels and groups
  </Card>

  <Card title="Conversation Management" icon="users" href="/en/sdk/wukongim/harmonyos/conversation">
    Handle conversation lists and unread messages
  </Card>

  <Card title="Data Source Configuration" icon="database" href="/en/sdk/wukongim/harmonyos/datasource">
    Configure message data sources
  </Card>

  <Card title="Advanced Features" icon="cog" href="/en/sdk/wukongim/harmonyos/advance">
    Explore advanced features and custom messages
  </Card>
</CardGroup>
