Skip to content

Commit 227955f

Browse files
authored
Merge pull request #573 from talkjs/feat/add-typing-indicator
Add a bot typing indicator to the OpenAI chatbot example
2 parents a1030fe + 40538e2 commit 227955f

File tree

3 files changed

+77
-23
lines changed

3 files changed

+77
-23
lines changed

chatbot-integration/openai-chatgpt/README.md

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ This is an example project for TalkJS's tutorial on [How to integrate a chatbot
44

55
The project uses TalkJS webhooks to listen for new message events from the TalkJS server, calls the OpenAI API to generate a message reply, and then adds the reply to the conversation with the TalkJS API.
66

7-
> [!TIP]
8-
> [Download this example project as a zip file](https://github.com/talkjs/talkjs-examples/releases/latest/download/chatbot-integration.openai-chatgpt.zip)
7+
> [!TIP] > [Download this example project as a zip file](https://github.com/talkjs/talkjs-examples/releases/latest/download/chatbot-integration.openai-chatgpt.zip)
98
109
## Prerequisites
1110

@@ -23,7 +22,25 @@ To run this tutorial, you will need:
2322
2. Replace `<APP_ID>` and `<TALKJS_SECRET_KEY>` in `index.html` and `server.js` with the values found in your [TalkJS dashboard](https://talkjs.com/dashboard/login).
2423
3. Replace `<OPENAI_SECRET_KEY>` with your OpenAI API key
2524
4. Enable the `message.sent` option in the **Webhooks** section of the TalkJS dashboard.
26-
5. Start ngrok with `ngrok http 3000`.
27-
6. Add the ngrok URL to **Webhook URLs** in the TalkJS dashboard.
28-
7. Run `npm install` to install dependencies.
29-
8. Run `npm start` to start the webhooks server.
25+
5. Update the theme to show a typing indicator when the bot is generating a response. In the **Themes** tab, select to **Edit** the `default` theme. Select the `UserMessage` component from the list of **Built-in components** and replace the existing `MessageBody` component with the following:
26+
27+
```jsx
28+
<div t:if="{{ custom.isTyping == 'true' }}" class="typing-indicator">
29+
<TypingIndicator />
30+
</div>
31+
32+
<MessageBody t:else
33+
body="{{ body }}"
34+
timestamp="{{ timestamp }}"
35+
floatTimestamp="auto"
36+
showStatus="{{ sender.isMe }}"
37+
isLongEmailMessage="{{isLongEmailMessage}}"
38+
darkenMenuArea="{{ darkenMenuArea }}"
39+
hasReferencedMessage="{{ hasReferencedMessage }}"
40+
/>
41+
```
42+
43+
6. Start ngrok with `ngrok http 3000`.
44+
7. Add the ngrok URL to **Webhook URLs** in the TalkJS dashboard, along with the `/onMessageSent` path: `https://<YOUR_SITE>.ngrok-free.app/onMessageSent`.
45+
8. Run `npm install` to install dependencies.
46+
9. Run `npm start` to start the webhooks server.

chatbot-integration/openai-chatgpt/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@
6565
conversation.setParticipant(me);
6666
conversation.setParticipant(bot);
6767

68-
const inbox = talkSession.createChatbox();
69-
inbox.select(conversation);
70-
inbox.mount(document.getElementById("talkjs-container"));
68+
const chatbox = talkSession.createChatbox();
69+
chatbox.select(conversation);
70+
chatbox.mount(document.getElementById("talkjs-container"));
7171
});
7272
</script>
7373

chatbot-integration/openai-chatgpt/server.js

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import OpenAI from "openai";
44

55
const appId = "<APP_ID>";
66
const talkJSSecretKey = "<TALKJS_SECRET_KEY>";
7+
const basePath = "https://api.talkjs.com";
78

89
const openAISecretKey = "<OPENAI_SECRET_KEY>";
910
const openai = new OpenAI({ apiKey: openAISecretKey });
@@ -14,16 +15,15 @@ const allMessageHistory = {};
1415
async function getCompletion(messageHistory) {
1516
const completion = await openai.chat.completions.create({
1617
messages: messageHistory,
17-
model: "gpt-3.5-turbo",
18+
model: "gpt-4o-mini",
1819
});
19-
2020
const reply = completion.choices[0].message.content;
2121
return reply;
2222
}
2323

24-
async function sendMessage(conversationId, text) {
25-
return fetch(
26-
`https://api.talkjs.com/v1/${appId}/conversations/${conversationId}/messages`,
24+
async function sendInitialMessage(conversationId) {
25+
const response = await fetch(
26+
`${basePath}/v1/${appId}/conversations/${conversationId}/messages`,
2727
{
2828
method: "POST",
2929
headers: {
@@ -32,13 +32,49 @@ async function sendMessage(conversationId, text) {
3232
},
3333
body: JSON.stringify([
3434
{
35-
text: text,
36-
sender: "chatbotExampleBot",
35+
text: "_Let me think for a bit..._", // Placeholder text that the theme will replace with a typing indicator
36+
sender: botId,
3737
type: "UserMessage",
38+
custom: { isTyping: "true" },
3839
},
3940
]),
4041
}
4142
);
43+
44+
// Return the message ID for later editing
45+
const data = await response.json();
46+
return data[0].id;
47+
}
48+
49+
async function updateBotMessage(conversationId, messageId, text) {
50+
return fetch(
51+
`${basePath}/v1/${appId}/conversations/${conversationId}/messages/${messageId}`,
52+
{
53+
method: "PUT",
54+
headers: {
55+
"Content-Type": "application/json",
56+
Authorization: `Bearer ${talkJSSecretKey}`,
57+
},
58+
body: JSON.stringify({
59+
text: text,
60+
custom: { isTyping: "false" },
61+
}),
62+
}
63+
);
64+
}
65+
66+
async function setUserAccess(conversationId, userId, access) {
67+
return fetch(
68+
`${basePath}/v1/${appId}/conversations/${conversationId}/participants/${userId}`,
69+
{
70+
method: "PUT",
71+
headers: {
72+
"Content-Type": "application/json",
73+
Authorization: `Bearer ${talkJSSecretKey}`,
74+
},
75+
body: JSON.stringify({ access: access }),
76+
}
77+
);
4278
}
4379

4480
const app = express();
@@ -58,18 +94,19 @@ app.post("/onMessageSent", async (req, res) => {
5894
},
5995
];
6096
}
97+
6198
const messageHistory = allMessageHistory[convId];
6299

63-
if (senderId == botId) {
64-
// Bot message
65-
messageHistory.push({ role: "assistant", content: messageText });
66-
} else {
67-
// User message
100+
if (senderId != botId) {
68101
messageHistory.push({ role: "user", content: messageText });
69-
getCompletion(messageHistory).then((reply) => sendMessage(convId, reply));
102+
const messageId = await sendInitialMessage(convId);
103+
await setUserAccess(convId, senderId, "Read");
104+
const reply = await getCompletion(messageHistory);
105+
await updateBotMessage(convId, messageId, reply);
106+
messageHistory.push({ role: "assistant", content: reply });
107+
await setUserAccess(convId, senderId, "ReadWrite");
70108
}
71109

72110
res.status(200).end();
73111
});
74112

75-
app.listen(3000, () => console.log("Server is up"));

0 commit comments

Comments
 (0)