Skip to content

Commit 63a38b4

Browse files
committed
Initial version of 'How to set an away message' example
1 parent 4a92690 commit 63a38b4

File tree

4 files changed

+295
-0
lines changed

4 files changed

+295
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
This is an example project for TalkJS's tutorial on how to set an automatic away message outside office hours.
2+
3+
The project uses TalkJS webhooks to listen for new message events from the TalkJS server, and then calls the REST API to reply with a automatic system message when a customer sends a message outside of support hours.
4+
5+
## Prerequisites
6+
7+
To run this tutorial, you will need:
8+
9+
- A [TalkJS account](https://talkjs.com/dashboard/login)
10+
- [Node.js](https://nodejs.org/en)
11+
- [npm](https://www.npmjs.com/)
12+
- [ngrok](https://ngrok.com/)
13+
14+
## How to run the tutorial
15+
16+
1. Clone or download the project.
17+
2. Replace `<APP_ID>` and `<SECRET_KEY>` in `index.html` and `server.js` with the values found in your [TalkJS dashboard](https://talkjs.com/dashboard/login).
18+
3. Enable the `message.sent` option in the **Webhooks** section of the TalkJS dashboard.
19+
4. Add "support" and "customer" roles in the **Roles** tab of the TalkJS dashboard.
20+
5. Start ngrok with `ngrok http 3000`.
21+
6. Add the ngrok URL to **Webhook URLs** in the TalkJS dashboard.
22+
7. Run `npm install` to install dependencies.
23+
8. Run `npm start` to start the webhooks server.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<!DOCTYPE html>
2+
3+
<html lang="en">
4+
<head>
5+
<meta charset="utf-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
8+
<title>TalkJS tutorial</title>
9+
</head>
10+
11+
<script>
12+
(function (t, a, l, k, j, s) {
13+
s = a.createElement("script");
14+
s.async = 1;
15+
s.src = "https://cdn.talkjs.com/talk.js";
16+
a.head.appendChild(s);
17+
k = t.Promise;
18+
t.Talk = {
19+
v: 3,
20+
ready: {
21+
then: function (f) {
22+
if (k)
23+
return new k(function (r, e) {
24+
l.push([f, r, e]);
25+
});
26+
l.push([f]);
27+
},
28+
catch: function () {
29+
return k && new k();
30+
},
31+
c: l,
32+
},
33+
};
34+
})(window, document, []);
35+
</script>
36+
37+
<script>
38+
Talk.ready.then(function () {
39+
const me = new Talk.User({
40+
id: "autoReplyExampleSupportAgent",
41+
name: "Alice",
42+
43+
photoUrl: "https://talkjs.com/images/avatar-1.jpg",
44+
role: "support",
45+
welcomeMessage: "Hey there! How can I help?",
46+
});
47+
const talkSession = new Talk.Session({
48+
appId: "<APP_ID>", // replace with your app ID
49+
me: me,
50+
});
51+
52+
const inbox = talkSession.createInbox();
53+
inbox.mount(document.getElementById("talkjs-container"));
54+
});
55+
</script>
56+
57+
<body>
58+
<!-- container element in which TalkJS will display a chat UI -->
59+
<div id="talkjs-container" style="width: 90%; margin: 30px; height: 500px">
60+
<i>Loading chat...</i>
61+
</div>
62+
</body>
63+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "how-to-set-an-away-message",
3+
"dependencies": {
4+
"express": "^4.18.2",
5+
"node-fetch": "^3.3.1"
6+
},
7+
"type": "module",
8+
"scripts": {
9+
"start": "node server.js"
10+
}
11+
}
+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import express from "express";
2+
import fetch from "node-fetch";
3+
4+
const appId = "<APP_ID>";
5+
const secretKey = "<SECRET_KEY>";
6+
7+
const basePath = "https://api.talkjs.com";
8+
9+
const app = express().use(express.json()); // creates http server
10+
app.listen(3000, () => console.log("Server is up"));
11+
12+
const autoReplied = {};
13+
14+
// Start and end of support hours in "HH:mm:ss" format
15+
const startTime = "09:00:00";
16+
const endTime = "17:00:00";
17+
18+
function isTimeBetween(start, end, check) {
19+
if (start <= end) {
20+
return check >= start && check <= end;
21+
} else {
22+
// window overlaps midnight
23+
return (
24+
(check >= start && check <= "23:59:59") ||
25+
(check >= "00:00:00" && check <= end)
26+
);
27+
}
28+
}
29+
30+
// Convert timestamp to "HH:mm:ss" format
31+
function timeStampToTime(timestamp) {
32+
const checkDate = new Date(timestamp);
33+
const hours = "0" + checkDate.getHours();
34+
const minutes = "0" + checkDate.getMinutes();
35+
const seconds = "0" + checkDate.getSeconds();
36+
return hours.slice(-2) + ":" + minutes.slice(-2) + ":" + seconds.slice(-2);
37+
}
38+
39+
async function sendReply(conversationId) {
40+
console.log("Autoreply to conversation ID:", conversationId);
41+
42+
return fetch(
43+
`${basePath}/v1/${appId}/conversations/${conversationId}/messages`,
44+
{
45+
method: "post",
46+
headers: {
47+
"Content-Type": "application/json",
48+
Authorization: `Bearer ${secretKey}`,
49+
},
50+
body: JSON.stringify([
51+
{
52+
text: "We're currently out of the office and will get back to you during our support hours.",
53+
type: "SystemMessage",
54+
},
55+
]),
56+
}
57+
);
58+
}
59+
60+
app.post("/talkjs", async (req, res) => {
61+
const data = req.body.data;
62+
const conversationId = data.conversation.id;
63+
64+
const role = data.sender?.role;
65+
const timestamp = data.message.createdAt;
66+
67+
const outOfOffice = !isTimeBetween(
68+
startTime,
69+
endTime,
70+
timeStampToTime(timestamp)
71+
);
72+
73+
if (role === "customer" && outOfOffice === true) {
74+
if (!(conversationId in autoReplied)) {
75+
await sendReply(conversationId);
76+
}
77+
autoReplied[conversationId] = new Date();
78+
}
79+
80+
if (role === "support" && conversationId in autoReplied) {
81+
delete autoReplied[conversationId];
82+
}
83+
84+
res.status(200).end();
85+
});
86+
87+
// EVERYTHING BELOW IS SETUP CODE FOR THIS EXAMPLE
88+
// You won't need any of it in your live app!
89+
//
90+
// It's just here so that you can play around with this example more easily
91+
// Whenever you start the server, we make sure the two example users are created
92+
// recreate the two conversations, and send messages from the example users
93+
94+
async function setupConversation(i) {
95+
const conversationId = `autoReplyExample${i}`;
96+
const userId = `autoReplyExampleUser${i}`;
97+
98+
// Delete the conversation (if it exists)
99+
await fetch(`${basePath}/v1/${appId}/conversations/${conversationId}`, {
100+
method: "delete",
101+
headers: {
102+
Authorization: `Bearer ${secretKey}`,
103+
},
104+
});
105+
106+
// Create a new conversation
107+
await fetch(`${basePath}/v1/${appId}/conversations/${conversationId}`, {
108+
method: "put",
109+
headers: {
110+
"Content-Type": "application/json",
111+
Authorization: `Bearer ${secretKey}`,
112+
},
113+
body: JSON.stringify({
114+
participants: ["autoReplyExampleSupportAgent", userId],
115+
}),
116+
});
117+
118+
// Send a message from the user to make sure it will show up in the conversation list
119+
await fetch(
120+
`${basePath}/v1/${appId}/conversations/${conversationId}/messages`,
121+
{
122+
method: "post",
123+
headers: {
124+
"Content-Type": "application/json",
125+
Authorization: `Bearer ${secretKey}`,
126+
},
127+
body: JSON.stringify([
128+
{
129+
text: "Everything is broken again!",
130+
sender: userId,
131+
type: "UserMessage",
132+
},
133+
]),
134+
}
135+
);
136+
}
137+
138+
async function setup() {
139+
const supportAgent = fetch(
140+
`${basePath}/v1/${appId}/users/autoReplyExampleSupportAgent`,
141+
{
142+
method: "put",
143+
headers: {
144+
"Content-Type": "application/json",
145+
Authorization: `Bearer ${secretKey}`,
146+
},
147+
body: JSON.stringify({
148+
name: "Alice",
149+
email: ["[email protected]"],
150+
photoUrl: "https://talkjs.com/images/avatar-1.jpg",
151+
role: "support",
152+
welcomeMessage: "Hey there! How can I help?",
153+
}),
154+
}
155+
);
156+
157+
const user1 = fetch(`${basePath}/v1/${appId}/users/autoReplyExampleUser1`, {
158+
method: "put",
159+
headers: {
160+
"Content-Type": "application/json",
161+
Authorization: `Bearer ${secretKey}`,
162+
},
163+
body: JSON.stringify({
164+
name: "Sebastian",
165+
email: ["[email protected]"],
166+
photoUrl: "https://talkjs.com/images/avatar-5.jpg",
167+
role: "customer",
168+
welcomeMessage: "Hi!",
169+
}),
170+
});
171+
172+
const user2 = fetch(`${basePath}/v1/${appId}/users/autoReplyExampleUser2`, {
173+
method: "put",
174+
headers: {
175+
"Content-Type": "application/json",
176+
Authorization: `Bearer ${secretKey}`,
177+
},
178+
body: JSON.stringify({
179+
name: "Bob",
180+
email: ["[email protected]"],
181+
photoUrl: "https://talkjs.com/images/avatar-4.jpg",
182+
role: "customer",
183+
welcomeMessage: "Hello!",
184+
}),
185+
});
186+
187+
await supportAgent;
188+
await user1;
189+
await user2;
190+
191+
const conv1 = setupConversation(1);
192+
const conv2 = setupConversation(2);
193+
194+
await conv1;
195+
await conv2;
196+
}
197+
198+
setup();

0 commit comments

Comments
 (0)