Skip to main content

Filtering Questions for FAQ Chatbot

Apart from typing the text directly to the FAQ chatbot, filtering questions allows users to find their answer in a pre-set flow (similar to IVRS in phones). You can categorize your FAQ entries into 3 (or more) layers, so that users can just select the categories layer by layer until they reach the final answer.

Please make sure you have inputted "Category" & "Category Priority".


Expected Outcome

  1. User goes through 3 layers of filtering questions and ends up with a filtering result.
Category 1-3 and Filtering Result
Category 1-3 and Filtering Result
Category 1-3 and Filtering Result
Category 1-3 and Filtering Result
  1. In the backend, WOZTELL sorts these questions based on the FAQ data source. Category 1-3 represents the options of chatbot filtering questions 1-3. The order of the options is based on Category 1-3 Priority.
Categories in FAQ Data Source
Categories in FAQ Data Source

Sample Tree Structure

Tree Structure of FAQ Chatbot with Filtering Questions
Tree Structure of FAQ Chatbot with Filtering Questions

Create the 1st Tree Node - Category 1

  1. Before proceeding, you should have completed up to level Step 2.1 and Step 2.2 FAQ procedure. You may edit the level four tree directly or duplicate a new tree.

  2. Create a tree node in the level four tree and rename it as "Category 1".

  3. Create a Pre-action with the following code and rename it as "Save Category 1 Answers".

Save Category 1 Answers
Save Category 1 Answers
return new Promise(async (resolve, reject) => {
console.log("in Save Category 1 answers")
let result = await this.fetchDataFromDataSource({
channel: this.channel,
collectionName: "Insert your Data Source ID here",
filter: {}
})
console.log("result", result)
result = this.lodash.uniqBy(result, "Category 1")
console.log("result", result)
result = this.lodash.reject(result, { "Category 1": "" })
console.log("result", result)

result = this.lodash.sortBy(result, "Category 1 Priority")

this.member.botMeta.tempData.currentAnswers = result.map(obj => obj["Category 1"])
this.member.botMeta.tempData.listLength = result.length

resolve({
member: this.member,
})
})
  1. Input the Data Source ID of the FAQ data source you have been using since Step 1.1 into the above code.
  1. Create a Response under "Advanced" with the following code and rename it as "Category 1 Question".
Advanced Response
Advanced Response
return new Promise((resolve) => {
let currentAnswers = this.member.botMeta.tempData.currentAnswers || []
let text = `Please input number to select🔢:\n`
currentAnswers.forEach((str, i) => {
let t = ""
switch (i + 1) {
case 1:
t = "1️⃣"
break
case 2:
t = "2️⃣"
break
case 3:
t = "3️⃣"
break
case 4:
t = "4️⃣"
break
case 5:
t = "5️⃣"
break
case 6:
t = "6️⃣"
break
case 7:
t = "7️⃣"
break
case 8:
t = "8️⃣"
break
case 9:
t = "9️⃣"
break
case 10:
t = "🔟️"
break
case 11:
t = "1️⃣1️⃣"
break
case 12:
t = "1️⃣2️⃣"
break
case 13:
t = "1️⃣3️⃣"
break
case 14:
t = "1️⃣4️⃣"
break
case 15:
t = "1️⃣5️⃣"
break
case 16:
t = "1️⃣6️⃣"
break
case 17:
t = "1️⃣7️⃣"
break
case 18:
t = "1️⃣8️⃣"
break
case 19:
t = "1️⃣9️⃣"
break
case 20:
t = "2️⃣0️⃣"
break
default:

}
text = `${text}\n${t} ${str}`
})
text = `${text}\n\nCan't find what you are looking for? Type keywords to ask WOZTELL. You can also type *#* anytime to return to this menu.`
resolve({
type: "TEXT",
text,
})
})
  1. You can edit the filtering question content (the header & footer of the text message) marked inside the back quotation marks of text = in the above code. ${text} refers to the Category 1 options displayed from FAQ data source.

  2. Save this tree node.

  3. You can also create a Global Node that redirects user to the Category 1 tree node. As a result, users can easily return to Category 1 by keyword when there are multiple categories.


Create the 2nd Tree Node - Category 2

  1. Select the Category 1 tree node. Create the next tree node and rename it as "Category 2".
  1. Create a trigger with the following sample code:
return this.messageEvent.type === "TEXT" && this.member.botMeta.tempData?.listLength ? new RegExp("\\b" + [...Array(this.member.botMeta.tempData.listLength + 1).keys()].slice(1).join("\\b|\\b") + "\\b").test(this.messageEvent.data.text) : false
Category Option Trigger
Category Option Trigger
  1. Create a Pre-action with the following code and rename it as "Save Category 1".
Save Category 1
Save Category 1
return new Promise((resolve) => {
console.log("in Save FAQ Category 1")
if (/(\+)/i.test(this.messageEvent.data.text)) {
resolve()
} else {
let match = this.messageEvent.data.text.match(new RegExp("\\b" + [...Array(this.member.botMeta.tempData.listLength + 1).keys()].slice(1).join("\\b|\\b") + "\\b"))
let indexStr = match[0]
let index = parseInt(indexStr) - 1
let currentAnswers = this.member.botMeta.tempData.currentAnswers
let currentAnswer = currentAnswers[index]
this.member.botMeta.tempData.category1 = currentAnswer
resolve({ member: this.member })
}
})
  1. Create a 2nd Pre-action with the following code and rename it as "Save Category 2 Answers".
return new Promise(async (resolve, reject) => {
console.log("in Save Category 2 Answers")
let result = await this.fetchDataFromDataSource({
channel: this.channel,
collectionName: "Insert your Data Source ID here",
filter: {
"Category 1": this.member.botMeta.tempData.category1
}
})
console.log("result", result)

if (result.length === 1) {
this.member.displayAnswer = true
this.member.botMeta.tempData.faq = result[0]
resolve({ member: this.member })
} else {
result = this.lodash.uniqBy(result, "Category 2")
console.log("result", result)
result = this.lodash.reject(result, { "Category 2": "" })
console.log("result", result)

result = this.lodash.sortBy(result, "Category 2 Priority")

this.member.botMeta.tempData.currentAnswers = result.map(obj => obj["Category 2"])
this.member.botMeta.tempData.listLength = result.length

resolve({
member: this.member,
})
}
})
  1. Input the Data Source ID of the FAQ data source you have been using since level one into the above code.
  1. Create a Response under "Advanced" with the following code and rename it as "Category 2 Question".
Advanced Response
Advanced Response
return new Promise((resolve) => {
if (this.member.displayAnswer) {
resolve()
} else {
let currentAnswers = this.member.botMeta.tempData.currentAnswers || []
let text = `Do you want to know about ${this.member.botMeta.tempData.category1} ?\n`
currentAnswers.forEach((str, i) => {
let t = ""
switch (i + 1) {
case 1:
t = "1️⃣"
break
case 2:
t = "2️⃣"
break
case 3:
t = "3️⃣"
break
case 4:
t = "4️⃣"
break
case 5:
t = "5️⃣"
break
case 6:
t = "6️⃣"
break
case 7:
t = "7️⃣"
break
case 8:
t = "8️⃣"
break
case 9:
t = "9️⃣"
break
case 10:
t = "1️⃣0️⃣️"
break
case 11:
t = "1️⃣1️⃣"
break
case 12:
t = "1️⃣2️⃣"
break
case 13:
t = "1️⃣3️⃣"
break
case 14:
t = "1️⃣4️⃣"
break
case 15:
t = "1️⃣5️⃣"
break
case 16:
t = "1️⃣6️⃣"
break
case 17:
t = "1️⃣7️⃣"
break
case 18:
t = "1️⃣8️⃣"
break
case 19:
t = "1️⃣9️⃣"
break
case 20:
t = "2️⃣0️⃣"
break
default:

}
text = `${text}\n${t} ${str}`
})
text = `${text}\n\nCan't find what you are looking for? Type keywords to ask WOZTELL. You can also type *#* anytime to return to this menu.`
resolve({
type: "TEXT",
text,
})
}
})
  1. You can edit the filtering question content (the header & footer of the text message) marked inside the back quotation marks of text = in the above code. ${text} refers to the Category 2 options displayed from FAQ data source. ${this.member.botMeta.tempData.category1} displays the Category 1 that user has chosen.

  2. Toggle on the Advanced tab in "Redirect" and paste the following code:

Advanced Redirect
Advanced Redirect
return new Promise((resolve) => {
if (this.member.displayAnswer) {
resolve({
tree: this.node.tree,
nodeCompositeId: "Input the node composite ID of the filtering result tree node here",
runPreAction: false
})
} else {
resolve()
}
})
  1. Input the node composite ID of the filtering result tree node into the above code. You will be able to retrieve the node composite ID in the coming steps. Remember to come back and edit later.

  2. Save this tree node.


Create the 3rd Tree Node - Category 3

  1. Select the Category 2 tree node. Create the next tree node and rename it as "Category 3".
  1. Use the same trigger you made in Step 9 tree node.
Category Option Trigger
Category Option Trigger
  1. Create a Pre-action with the following code and rename it as "Save Category 2".
Save Category 2
Save Category 2
return new Promise((resolve) => {
console.log("in Save FAQ Category 2")
let match = this.messageEvent.data.text.match(new RegExp("\\b" + [...Array(this.member.botMeta.tempData.listLength + 1).keys()].slice(1).join("\\b|\\b") + "\\b"))
let indexStr = match[0]
let index = parseInt(indexStr) - 1
let currentAnswers = this.member.botMeta.tempData.currentAnswers
let currentAnswer = currentAnswers[index]
console.log("category2 ans", currentAnswer)
this.member.botMeta.tempData.category2 = currentAnswer
resolve({ member: this.member })
})
  1. Create a 3rd Pre-action with the following code and rename it as "Save Category 3 Answers".
return new Promise(async (resolve, reject) => {
console.log("in Save Category 3 Answers")
let result = await this.fetchDataFromDataSource({
channel: this.channel,
collectionName: "Insert your Data Source ID here",
filter: {
"Category 1": this.member.botMeta.tempData.category1,
"Category 2": this.member.botMeta.tempData.category2,
}
})
console.log("result", result)

if (result.length === 1) {
this.member.displayAnswer = true
this.member.botMeta.tempData.faq = result[0]
resolve({ member: this.member })
} else {
result = this.lodash.uniqBy(result, "Category 3")
console.log("result", result)
result = this.lodash.reject(result, { "Category 3": "" })
console.log("result", result)

result = this.lodash.sortBy(result, "Category 3 Priority")

this.member.botMeta.tempData.currentAnswers = result.map(obj => obj["Category 3"])
this.member.botMeta.tempData.listLength = result.length

resolve({
member: this.member,
})
}
})
  1. Input the Data Source ID of the FAQ data source you have been using since level one into the above code.
  1. Create a Response under "Advanced" with the following code and rename it as "Category 3 Question".
Advanced Response
Advanced Response
return new Promise((resolve) => {
if (this.member.displayAnswer) {
resolve()
} else {
let currentAnswers = this.member.botMeta.tempData.currentAnswers || []
let text = `Do you want to know about ${this.member.botMeta.tempData.category2}?\n`
currentAnswers.forEach((str, i) => {
let t = ""
switch (i + 1) {
case 1:
t = "1️⃣"
break
case 2:
t = "2️⃣"
break
case 3:
t = "3️⃣"
break
case 4:
t = "4️⃣"
break
case 5:
t = "5️⃣"
break
case 6:
t = "6️⃣"
break
case 7:
t = "7️⃣"
break
case 8:
t = "8️⃣"
break
case 9:
t = "9️⃣"
break
case 10:
t = "1️⃣0️⃣️"
break
case 11:
t = "1️⃣1️⃣"
break
case 12:
t = "1️⃣2️⃣"
break
case 13:
t = "1️⃣3️⃣"
break
case 14:
t = "1️⃣4️⃣"
break
case 15:
t = "1️⃣5️⃣"
break
case 16:
t = "1️⃣6️⃣"
break
case 17:
t = "1️⃣7️⃣"
break
case 18:
t = "1️⃣8️⃣"
break
case 19:
t = "1️⃣9️⃣"
break
case 20:
t = "2️⃣0️⃣"
break
default:

}
text = `${text}\n${t} ${str}`
})
text = `${text}\n\nCan't find what you are looking for? Type keywords to ask WOZTELL. You can also type *#* anytime to return to this menu.`
resolve({
type: "TEXT",
text,
})
}
})
  1. You can edit the filtering question content (the header & footer of the text message) marked inside the back quotation marks of text = in the above code. ${text} refers to the Category 3 options displayed from FAQ data source. ${this.member.botMeta.tempData.category2} displays the Category 1 that user has chosen.

  2. Toggle on the Advanced tab in "Redirect" and use the same code as you've made in Step 15.

Advanced Redirect
Advanced Redirect
  1. Input the node composite ID of the filtering result tree node into the above code. You will be able to retrieve the node composite ID in coming steps. Remember to come back and edit later.

  2. Save this tree node.


Create the 4th Tree Node - Filtering Result

  1. Select the Category 3 tree node. Create the next tree node and rename it as "Filtering Result".
  1. Use the same trigger you made in Step 9 tree node.

    Category Option Trigger
    Category Option Trigger
  1. Create a Pre-action with the following code and rename it as "Save Category 3".

    Save Category 3
    Save Category 3
return new Promise((resolve) => {
console.log("in Save FAQ Category 3")
let match = this.messageEvent.data.text.match(new RegExp("\\b" + [...Array(this.member.botMeta.tempData.listLength + 1).keys()].slice(1).join("\\b|\\b") + "\\b"))
let indexStr = match[0]
let index = parseInt(indexStr) - 1
let currentAnswers = this.member.botMeta.tempData.currentAnswers
let currentAnswer = currentAnswers[index]
this.member.botMeta.tempData.category4 = currentAnswer
console.log("Category 3", currentAnswer)
resolve({ member: this.member })
})
  1. Create a 2nd Pre-action with the following code and rename it as "Save Filtering Result".
Save Filtering Result
Save Filtering Result
return new Promise(async (resolve, reject) => {
console.log("in Save Filtering Result")
let result = await this.fetchDataFromDataSource({
channel: this.channel,
collectionName: "Insert your Data Source ID here",
filter: {
"Category 1": this.member.botMeta.tempData.category1,
"Category 2": this.member.botMeta.tempData.category2,
"Category 3": this.member.botMeta.tempData.category3,
}
})
console.log("result", result)

this.member.botMeta.tempData.faq = result[0]

resolve({
member: this.member,
})
})
  1. Input the Data Source ID of the FAQ data source you have been using since level one into the above code.
  1. Create a Response under "Advanced" with the following code and rename it as "Show Selected Question Content".
Advanced Response
Advanced Response
return new Promise(async (resolve, reject) => {
console.log("in Show Selected Question Content")
let result = this.member.botMeta.tempData.faq
console.log("result", result)
let response = {}
let type = result["Type"]
console.log("type", type)
switch (type) {
case "Text":
response.type = "TEXT"
response.text = result.Text
if (result.Preview === true || result.Preview === "TRUE") {
response.preview_url = true
}
break
case "Image":
case "Image_Text":
case "Image with caption":
response.type = "IMAGE"
response.url = result.URL
response.text = result.Text
break
case "Video":
case "GIF":
case "File":
response.type = "FILE"
response.url = result.URL
response.text = result.Text
response.filename = result["File Name"]
break
case "Audio":
response.type = "AUDIO"
response.url = result.URL
break
case "Carousel":
response.type = "CAROUSEL"
response.carousel = [{
title: result.Text,
image_url: result.URL,
buttons: [{
type: "web_url",
title: result["Button title"],
url: result["Button url"],
}]
}]
break
case "Button":
response.type = "BUTTON"
response.text = result.Text,
response.buttons = [{
type: "web_url",
title: result["Button title"],
url: result["Button url"],
}]
break
default:
response = null
break;
}
console.log("response", response)
resolve(response)
})
  1. Toggle on the Advanced tab in "Redirect" and paste the following code:
Advanced Redirect
Advanced Redirect
return new Promise((resolve, reject) => {
resolve({
tree: this.node.tree,
nodeCompositeId: this.node.compositeId,
runPreAction: true,
sendResponse: false,
runPostAction: true,
})
})
  1. Save the node. Test your chatbot and see if you can get the expected outcome.