Skip to content

IM Chat

This guide covers instant messaging features including sending messages, receiving messages, history, revocation, and chat mode control.

Sending Messages

Text Messages

typescript
const result = await classroom.sendTextMessage('Hello everyone!');
if (!result.ok) {
  console.error('Send failed:', result.message);
}

Image Messages

typescript
// Send after file selection (auto-uploaded to COS)
const fileInput = document.querySelector<HTMLInputElement>('#image-input');
const file = fileInput?.files?.[0];
if (file) {
  const result = await classroom.sendImageMessage(file);
  if (!result.ok) console.error('Image send failed:', result.message);
}

File Messages

typescript
const file = fileInput.files[0];
const result = await classroom.sendFileMessage(file);
if (!result.ok) {
  console.error('File send failed:', result.message);
}

Custom Messages

typescript
// Send custom message (content is serialized to a JSON string)
const result = await classroom.sendCustomMessage({
  type: 'emoji',
  emojiId: 'thumbs_up',
});

Direct Messages (Private)

typescript
// Send a directed private message (visible only to the target user)
const result = await classroom.sendDirectedMessage('user_002', 'Hi, I have a question');
if (!result.ok) {
  console.error('DM failed:', result.message);
}

Receiving Messages

Subscribe to Message List via Reactive State

Subscribe to the full message list (history + real-time messages):

typescript
classroom.state.messageList$.subscribe(messages => {
  renderMessageList(messages);
});

Listen for New Messages via Events

Listen for new messages to trigger notifications, scrolling, etc.:

typescript
import { TEvent } from '@tencent-classroom/sdk';

classroom.on(TEvent.RECV_MESSAGE, (message) => {
  console.log('New message:', message.content);
  scrollToBottom();
  showNotification(message);
});

Parsing Private Message Indicators

messageList$ contains public messages + private messages relevant to the current user.

Private messages are identified by fields in message.ext (the IM cloudCustomData):

typescript
interface MessageExt {
  IsPrivateMsg?: boolean;      // Whether this is a private message
  PrivateInfo?: {
    From: { ID: string };      // Sender userId
    To: { ID: string };        // Recipient userId
  };
}

Parsing example:

typescript
classroom.state.messageList$.subscribe((messages) => {
  for (const msg of messages) {
    // Parse ext field
    const ext = typeof msg.ext === 'string' ? JSON.parse(msg.ext || '{}') : (msg.ext || {});

    if (ext.IsPrivateMsg) {
      // This is a private message
      const fromId = ext.PrivateInfo?.From?.ID;
      const toId = ext.PrivateInfo?.To?.ID;
      renderPrivateMessage(msg, fromId, toId);
    } else {
      // Regular public message
      renderPublicMessage(msg);
    }
  }
});

messageList$ Filtering Rules

The SDK ensures messageList$ only contains:

  • All public messages (Text / Image / File types)
  • Private messages relevant to the current user (where the user is the sender From or recipient To)

Private messages between other users will never appear in the list — no visibility checks are needed at the business layer.

History Messages

typescript
// Fetch history messages (incremental, no duplicates)
const result = await classroom.fetchHistoryMessages(20); // default: 20
if (result.ok) {
  console.log('History messages:', result.data);
}

// Fetch more
const moreResult = await classroom.fetchHistoryMessages(20);

Note: The SDK automatically fetches history messages once on join and writes them to state.messageList$. Manual calls are usually unnecessary.

Revoking Messages

typescript
// Revoke your own message (all roles can revoke their own)
const result = await classroom.revokeMessage(message.id);
if (!result.ok) {
  console.error('Revoke failed:', result.message);
}

// Teacher/assistant revoke any message (by IM sequence number)
await classroom.revokeClassMessage(message.seq);

Listen for revocation events:

typescript
classroom.on(TEvent.MESSAGE_REVOKED, ({ msgId }) => {
  console.log('Message revoked:', msgId);
});

Unread Management

typescript
// Subscribe to unread message count
classroom.state.messageUnreadCount$.subscribe(count => {
  updateBadge(count);
});

// Mark all as read
classroom.markAllMessagesAsRead();

// Mark messages up to a specific seq as read
classroom.markMessageAsRead(latestSeq);

Chat Mode Control

Teachers/assistants can control the classroom chat mode:

typescript
// Set chat mode
await classroom.setSilenceMode('freeChat');      // Free chat
await classroom.setSilenceMode('publicOnly');    // Public messages only
await classroom.setSilenceMode('privateOnly');   // Private messages only
await classroom.setSilenceMode('muteAll');       // Mute all

// Subscribe to silence mode changes
classroom.state.silenceMode$.subscribe(mode => {
  console.log('Current chat mode:', mode);
});

Check Send Permission

typescript
// Check whether the current user can send messages
if (classroom.canSendChatMessage()) {
  // Can send
} else {
  showToast('You are currently muted');
}