Building a Group Chat with Wazo
18 November 2022
2022 Hackathon
Every year, Wazo organizes a hackathon in the region of Quebec with all the members of the engineering team. For us, the hackathon is a great way to do some team building and meet in real life, since not everyone lives around Quebec City.
Kick-Off
One subject for the hackathon was to build a chat app powered by Wazo supporting: GIFs, emojis, reactions on messages, files attachment and Markdown. Also behind the scene, we wanted to build a bridge between Wazo And Matrix to explore methods for integrating Wazo with third-party platforms.
Jesse, Charles, Francois and I (Francis) teamed up to explore these ideas further.
Working on Wazo-Platform to Support Group Chat and Reactions
Support Multiple Recipients
Firstly, we needed to remove the hard limit of two members per room. This part was the easiest since it’s a hardcoded condition set by us. However, several questions would need to be addressed to make it production ready:
- How do we handle message history visibility when a new member is added to a conversation
- How to define max participants
- How to leave a conversation
- etc…
Adding Reactions to Messages
We added a new table chatd_room_message_reaction. This table will be in charge of storing all reactions added by a user to a message. Users can add as many emojis as they wants to a message, but not the same twice.
class RoomMessageReaction(Base):
__tablename__ = 'chatd_room_message_reaction'
message_uuid = Column(
UUIDType(),
ForeignKey('chatd_room_message.uuid', ondelete='CASCADE'),
primary_key=True,
)
user_uuid = Column(UUIDType(), nullable=False, primary_key=True)
emoji = Column(String(10))
created_at = Column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
server_default=text("(now() at time zone 'utc')"),
nullable=False,
)
The second part, is to add real-time reactions in the chat, so we created two new events for this :
chatd_users_room_message_reaction_created
chatd_users_room_message_reaction_deleted
These events look like this and allow the app to add/remove the reactions to a specific message in real-time.
{
"message_uuid": "a0e7dc92-92a3-485b-b8dd-09a909a1f5a0",
"data": {
"emoji": "🎉",
"user_uuid": "8040ec9d-1a61-4ca3-abe5-ad7c2f192e03"
}
}
Funny corner-case
At first, we added a limit of 1 character for the emoji column. But we figured out that some emojis are more than one character. This is how variations for an emoji are handled ❤️🧡💛💚💙💜🤎🖤🤍.
Building a UI Powered by Wazo Platform
For the UI part, we used the SolidJS reactive library and our SDK wazo-js-sdk. Building the UI was quite straightforward since our SDK handled token creation and allowed us to use the preconfigured WebSocket Client.
Live Chat Logic
A Group Chat is mainly HTTP Requests and WebSocket listeners. Here’s the HTTP Request that we need to make in the app:
import getApiClient from '@wazo/sdk/lib/service/getApiClient';
const client = getApiClient('hostname.example');
// On page load, we fetch rooms for the current user
const rooms = await client.chatd.getUserRooms();
// On room load, we fetch previous message in a room
const messages = await client.chatd.getRoomMessages(ROOM_UUID);
// On form submit, we send the new message to chatd services
await client.chatd.sendRoomMessage(ROOM_UUID, {
content: 'The new message content',
alias: 'Alias/Name of the member',
});
The second part is about listening to WebSocket messages. We used the wazo-js-sdk WebSocket client. Here are the events that we listened to :
// Initialize websocket client
import { WazoWebSocketClient } from '@wazo/sdk';
const ws = new WazoWebSocketClient({
host,
token: response.token,
events: [
'chatd_user_room_message_created',
// Other events to listen
],
version: '2',
});
// Main events to listen
ws.on('chatd_user_room_message_created', message => {
// Listen for "room message created" events
// - Validate the message is in the displayed room
// - Add message to all messages
//
// Message Payload Example:
// {
// room_uuid: "697a35a6-534c-461d-9466-6f77d0181e80",
// data: {
// content: 'The new message content',
// alias: 'Alias/Name of the member',
// created_at: '2022-10-24T15:50:00.000000+00:00',
// user_uuid: '8040ec9d-1a61-4ca3-abe5-ad7c2f192e03',
// uuid: 'a0e7dc92-92a3-485b-b8dd-09a909a1f5a0',
// }
// }
});
ws.on('chatd_user_room_created', message => {
// Listen for "room created" events
// - Add new room to the listing
//
// Message Payload Example
// {
// uuid: '6f9c7df4-8529-4336-9b65-21ceec774583'
// name: 'New Room Name',
// tenant_uuid: '47e1a830-cb3a-4ac9-9117-82acbb54d72b',
// users: [
// {
// tenant_uuid: '5d5e7e18-46ae-4046-beda-3cdcd5449142',
// uuid: '551d06a1-808a-417d-995d-7abb66d55c33',
// },
// // Other users
// ]
// }
});
The Hardest Parts of a Group Chat Application
We found that the most challenging parts of a group chat application are :
- Creating a feature-rich interface. We may have not realized it at first, but chat apps are
powerful tools with a lot of shortcuts. For example: editing previous messages, switching
to new channels, aliases in messages (@mention, emoji syntax
:smile:
, link detection, etc.). - More complex features and settings are common as well: threads, notifications, participant management, privacy settings, etc.
Conclusion
The hackathon was a great opportunity for us to work closely with our colleagues and try new technologies. It also gives us a good idea about what is missing and how much effort would be required to implement and deploy these features
Code reference: show me the code
All our code created during the hackathon is open-source. Dive in and ask us questions on our public Mattermost.