CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

pubnub/pubnub-chat

Build chat applications with PubNub Chat SDK

Agent Success

Agent success rate when using this tile

95%

Improvement

Agent success rate improvement when using this tile compared to baseline

1.83x

Baseline

Agent success rate without this tile

52%

Overview
Eval results
Skill reviews
Files

chat-features.mdreferences/

PubNub Chat SDK Features

Channel Types

Direct Channel (1:1 Chat)

// Create direct conversation with another user
const interlocutor = await chat.getUser('bob-123') ||
                     await chat.createUser('bob-123', { name: 'Bob' });

const { channel } = await chat.createDirectConversation({
  user: interlocutor,
  channelData: { name: 'Chat with Bob' }
});

Group Channel

// Create group channel with multiple members
const { channel } = await chat.createGroupConversation({
  users: [user1, user2, user3],
  channelId: 'team-alpha',
  channelData: {
    name: 'Team Alpha',
    description: 'Project discussions',
    custom: { type: 'project', visibility: 'members' }
  }
});

Public Channel

// Create public channel anyone can join
const channel = await chat.createPublicConversation({
  channelId: 'public-lobby',
  channelData: {
    name: 'Public Lobby',
    description: 'Open discussion'
  }
});

Sending and Receiving Messages

Connect to Channel

// Connect and receive messages
const unsubscribe = channel.connect((message) => {
  console.log('Message from:', message.userId);
  console.log('Text:', message.text);
  console.log('Time:', message.timetoken);
});

// Later: disconnect
unsubscribe();

React Pattern

useEffect(() => {
  if (!channel) return;

  const unsubscribe = channel.connect((message) => {
    setMessages(prev => [...prev, message]);
  });

  return () => {
    unsubscribe();
  };
}, [channel]);

Send Text Message

await channel.sendText('Hello, everyone!');

// With metadata
await channel.sendText('Check this out!', {
  meta: {
    linkPreview: 'https://example.com',
    priority: 'high'
  }
});

Get Message History

// Fetch past messages
const history = await channel.getHistory({
  count: 50,
  startTimetoken: '17000000000000000'  // Optional: pagination
});

history.messages.forEach(msg => {
  console.log(`${msg.userId}: ${msg.text}`);
});

Typing Indicators

Send Typing Signal

// Start typing indicator
await channel.startTyping();

// Stop typing indicator
await channel.stopTyping();

Listen for Typing

// Get who's currently typing
const typingUsers = await channel.getTyping();

// Listen for typing changes
channel.onTyping((typingUserIds) => {
  if (typingUserIds.length > 0) {
    const names = typingUserIds.map(id => getUserName(id));
    setTypingIndicator(`${names.join(', ')} typing...`);
  } else {
    setTypingIndicator('');
  }
});

Typing Indicator Component

function TypingIndicator({ channel }) {
  const [typing, setTyping] = useState([]);

  useEffect(() => {
    if (!channel) return;

    const unsubscribe = channel.onTyping((userIds) => {
      setTyping(userIds);
    });

    return () => unsubscribe();
  }, [channel]);

  if (typing.length === 0) return null;

  return (
    <div className="typing-indicator">
      {typing.length === 1 && `${typing[0]} is typing...`}
      {typing.length === 2 && `${typing[0]} and ${typing[1]} are typing...`}
      {typing.length > 2 && `${typing.length} people are typing...`}
    </div>
  );
}

Message Reactions

Add Reaction

// Add emoji reaction to message
await message.toggleReaction('thumbsup');
await message.toggleReaction('heart');
await message.toggleReaction('laugh');

Get Reactions

// Get all reactions on a message
const reactions = message.reactions;
// { thumbsup: ['user-1', 'user-2'], heart: ['user-3'] }

Listen for Reaction Updates

channel.on('reaction', ({ event, data }) => {
  console.log(`Reaction ${event}:`, data);
  // event: 'added' or 'removed'
  // data: { messageTimetoken, reactionType, userId }

  // Update local message state
  updateMessageReactions(data.messageTimetoken, data);
});

React Reactions Component

function MessageReactions({ message, currentUserId }) {
  const handleReaction = async (emoji) => {
    await message.toggleReaction(emoji);
  };

  return (
    <div className="reactions">
      {Object.entries(message.reactions || {}).map(([emoji, users]) => (
        <button
          key={emoji}
          onClick={() => handleReaction(emoji)}
          className={users.includes(currentUserId) ? 'active' : ''}
        >
          {emoji} {users.length}
        </button>
      ))}
      <button onClick={() => handleReaction('thumbsup')}>+</button>
    </div>
  );
}

Read Receipts

Mark as Read

// Mark message as read
await message.setLastReadMessage();

// Mark channel as read up to timetoken
await channel.setLastReadMessage(message);

Get Unread Count

// Get unread message count for channel
const unreadCount = await channel.getUnreadMessagesCount();

// Get unread counts for all channels
const channels = await chat.getChannels();
for (const channel of channels) {
  const unread = await channel.getUnreadMessagesCount();
  console.log(`${channel.name}: ${unread} unread`);
}

Message Threading

Create Thread

// Get thread from a message
const thread = await message.getThread();

// If no thread exists, it will be created when you reply

Reply in Thread

// Send reply to thread
await thread.sendText('This is a reply in the thread');

// Get thread messages
const threadHistory = await thread.getHistory({ count: 20 });

Thread Count

// Check if message has replies
if (message.hasThread) {
  const replyCount = message.threadRootMessage?.repliesCount || 0;
  console.log(`${replyCount} replies`);
}

User Mentions

Mention Users

// Mention user in message
await channel.sendText('Hey @alice, check this out!', {
  mentionedUsers: {
    0: { id: 'alice-123', name: 'Alice' }  // Position 0 of mention
  }
});

Listen for Mentions

// Get messages where current user is mentioned
const mentions = await chat.getMentions({
  userId: chat.currentUser.id,
  count: 20
});

mentions.forEach(mention => {
  console.log(`Mentioned in ${mention.channel}: ${mention.message.text}`);
});

Channel References

// Reference another channel in message
await channel.sendText('Check out #general for updates', {
  referencedChannels: {
    0: { id: 'general', name: 'General' }
  }
});

Files and Media

Send File

// Send file with message
await channel.sendFile({
  file: fileObject,  // File from input
  message: 'Check out this document'
});

Download File

// Get file URL from message
if (message.files && message.files.length > 0) {
  const file = message.files[0];
  console.log('File name:', file.name);
  console.log('File URL:', file.url);
}

Time Utilities

import { TimetokenUtils } from '@pubnub/chat';

// Convert timetoken to Date
const date = TimetokenUtils.timetokenToDate(message.timetoken);

// Format time display
const timeString = date.toLocaleTimeString([], {
  hour: '2-digit',
  minute: '2-digit'
});

// Convert Date to timetoken
const timetoken = TimetokenUtils.dateToTimetoken(new Date());
tessl i pubnub/pubnub-chat@0.1.4

SKILL.md

tile.json