Skip to content

Commit

Permalink
Merge branch 'main' of github.com:beckn/beckn-action-bot
Browse files Browse the repository at this point in the history
  • Loading branch information
mayurvir committed Apr 9, 2024
2 parents 8406cc7 + 12f13fa commit 529b3d5
Show file tree
Hide file tree
Showing 13 changed files with 306 additions and 18 deletions.
5 changes: 4 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ TEST_RECEPIENT_NUMBER=
STRAPI_TOURISM_TOKEN=
GOOGLE_MAPS_API_KEY=your_api_key_here
SERVER_URL=http://13.201.62.138:3001
DEVELOPER_MODE_ON=0
DEVELOPER_MODE_ON=0
STRAPI_RETAIL_TOKEN=
STRAPI_ENERGY_TOKEN=
STRAPI_HOTEL_TOKEN=
3 changes: 3 additions & 0 deletions .github/workflows/api_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
echo "GOOGLE_MAPS_API_KEY=${{secrets.GOOGLE_MAPS_API_KEY}}" >> .env
echo "SERVER_URL=${{secrets.SERVER_URL}}" >> .env
echo "DEVELOPER_MODE_ON=${{secrets.DEVELOPER_MODE_ON}}" >> .env
echo "STRAPI_RETAIL_TOKEN=${{secrets.STRAPI_RETAIL_TOKEN}}" >> .env
echo "STRAPI_ENERGY_TOKEN=${{secrets.STRAPI_ENERGY_TOKEN}}" >> .env
echo "STRAPI_HOTEL_TOKEN=${{secrets.STRAPI_HOTEL_TOKEN}}" >> .env
- name: Set up Node.js
uses: actions/setup-node@v2
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
echo "GOOGLE_MAPS_API_KEY=${{secrets.GOOGLE_MAPS_API_KEY}}" >> .env
echo "SERVER_URL=${{secrets.SERVER_URL}}" >> .env
echo "DEVELOPER_MODE_ON=${{secrets.DEVELOPER_MODE_ON}}" >> .env
echo "STRAPI_RETAIL_TOKEN=${{secrets.STRAPI_RETAIL_TOKEN}}" >> .env
echo "STRAPI_ENERGY_TOKEN=${{secrets.STRAPI_ENERGY_TOKEN}}" >> .env
echo "STRAPI_HOTEL_TOKEN=${{secrets.STRAPI_HOTEL_TOKEN}}" >> .env
- name: Create SSH key file
run: echo -e "${{ secrets.EC2_SSH_KEY }}" > ~/ec2_key
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/lint_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ jobs:
echo "GOOGLE_MAPS_API_KEY=${{secrets.GOOGLE_MAPS_API_KEY}}" >> .env
echo "SERVER_URL=${{secrets.SERVER_URL}}" >> .env
echo "DEVELOPER_MODE_ON=${{secrets.DEVELOPER_MODE_ON}}" >> .env
echo "STRAPI_RETAIL_TOKEN=${{secrets.STRAPI_RETAIL_TOKEN}}" >> .env
echo "STRAPI_ENERGY_TOKEN=${{secrets.STRAPI_ENERGY_TOKEN}}" >> .env
echo "STRAPI_HOTEL_TOKEN=${{secrets.STRAPI_HOTEL_TOKEN}}" >> .env
- name: Set up Node.js
uses: actions/setup-node@v2
with:
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ jobs:
echo "GOOGLE_MAPS_API_KEY=${{secrets.GOOGLE_MAPS_API_KEY}}" >> .env
echo "SERVER_URL=${{secrets.SERVER_URL}}" >> .env
echo "DEVELOPER_MODE_ON=${{secrets.DEVELOPER_MODE_ON}}" >> .env
echo "STRAPI_RETAIL_TOKEN=${{secrets.STRAPI_RETAIL_TOKEN}}" >> .env
echo "STRAPI_ENERGY_TOKEN=${{secrets.STRAPI_ENERGY_TOKEN}}" >> .env
echo "STRAPI_HOTEL_TOKEN=${{secrets.STRAPI_HOTEL_TOKEN}}" >> .env
- name: Set up Node.js
uses: actions/setup-node@v2
with:
Expand Down
37 changes: 37 additions & 0 deletions config/message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"FULFILLMENT_STATUS_CODES":{
"order-picked-up":{
"message": "Your order has been picked up"
},
"order-on-the-way": {
"message": "Your order is on the way"
},
"order-delivered":{
"message": "Your order has been delivered"
},
"ticket-issued":{
"message": "Your ticket has been issued"
},
"ticket-validated":{
"message": "Your ticket has been validated"
},
"charging-started": {
"message": "Your charging has started"
},
"charging-stopped":{
"message": "Your charging has stopped"
},
"charger-not-working":{
"message": "Hi, it looks likes your charger is not working. DO you want to find other chargin stations?"
},
"charging-completed":{
"message": "Hi, your chargin is complete!"
},
"checked-in": {
"message": "You have succesfully checked-in to your stay!"
},
"checked-out":{
"message": "You have succesfully checked-out from your stay!"
}
}
}
2 changes: 1 addition & 1 deletion config/openai.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"SUPPORTED_ACTIONS": [
{ "action": "get_routes", "description": "If the user has requested for routes for a travel plan between two places or asked to plan a trip. If the assistant has suggested to re-route in the last message and asked user to share current location, it should be a get_routes." },
{ "action": "get_routes", "description": "If the user has requested for routes for a travel plan between two places. If the assistant has suggested to re-route in the last message and asked user to share current location, it should be a get_routes." },
{ "action": "select_route", "description": "If the user selects one of the routes from the routes shared by the assistant." },
{ "action": "search", "description": "If the user clearly indicates to perform a search for a specific product. Sample instructions : 'find a hotel', 'find an ev charger', 'find tickets'" },
{ "action": "select", "description": "If the user likes or selects any item, this action should be used. This action can only be called if a search has been called before." },
Expand Down
3 changes: 2 additions & 1 deletion config/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"search must have two stops for this domain.",
"Supported stop.type : check-in, check-out",
"fulfillment.stops[i].time should be an object and contain timestamp",
"fulfillment.stops[i].time should only be used if explicity shared by the user"
"fulfillment.stops[i].time should only be used if explicity shared by the user",
"fulfillment.stops[i].location should be used if user has shared a location for search"
]
},
"tourism": {
Expand Down
77 changes: 72 additions & 5 deletions controllers/Bot.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import ActionsService from '../services/Actions.js'
import AI from '../services/AI.js'
import DBService from '../services/DBService.js'
import logger from '../utils/logger.js'
import DBService from '../services/DBService.js'
import { v4 as uuidv4 } from 'uuid'
import MapsService from '../services/MapService.js'
import get_text_by_key from '../utils/language.js'
import {readFileSync} from 'fs'
const mapService = new MapsService()

const db = new DBService()
const actionsService = new ActionsService()
const db = new DBService();

const message_config = JSON.parse(readFileSync('./config/message.json'))
/**
* @deprecated
* @param {*} req
Expand Down Expand Up @@ -101,6 +102,7 @@ async function process_text(req, res) {
bookings: [],
active_transaction: null,
routes:[],
orders:[],
selected_route:null
}

Expand Down Expand Up @@ -224,9 +226,10 @@ async function process_text(req, res) {
session.bookings = ai.bookings;
response = await process_action(ai.action, message, session, sender, format);
ai.bookings = response.bookings;

// update actions
if(ai.action?.action === 'confirm') {
session.orders.push(response.raw.responses[0]);
session.actions = EMPTY_SESSION.actions;
session.text = EMPTY_SESSION.text;
}
Expand Down Expand Up @@ -383,7 +386,71 @@ async function process_action(action, text, session, sender=null, format='applic

return response;
}

async function webhookControl (req, res) {
try{
const sessions = await db.get_all_sessions();
logger.info(`Got ${sessions.length} sessions.`)


for(let session of sessions){
const orders = session.data.orders;
const index = orders ? orders.findIndex((order)=>order.message.order.id == req.body.orderId) : null;
if(index!==null && index>=0 && (!orders[index].message.order.fulfillments[0].state||orders[index].message.order.fulfillments[0].state.descriptor.code !== req.body.data.data.attributes.state_code )) {
// send whatsapp and add to context
try{
// update session
orders[index].message.order.fulfillments[0] = {
...orders[index].message.order.fulfillments[0],
state:{
descriptor:{
code:req.body.data.data.attributes.state_code,
short_desc:req.body.data.data.attributes.state_value
},
updated_at:req.body.data.data.attributes.updatedAt
}
}
let reply_message = `Hey the status of your order is updated to ${req.body.data.data.attributes.state_code}`
if(Object.keys(message_config.FULFILLMENT_STATUS_CODES).includes(req.body.data.data.attributes.state_code)){
reply_message = message_config.FULFILLMENT_STATUS_CODES[req.body.data.data.attributes.state_code].message
}
await actionsService.send_message(
session.key,
reply_message
)
if (!session.data.text) session.data.text = []
session.data.text.push({
role: 'assistant',
content: reply_message,
})
await db.update_session(
session.key,
session.data
)
}
catch(e){
logger.error(e);
throw new Error(e.message)
}

}
}
return res.status(200).json({
status:true,
message:'Notification Sent'
})
}catch(error){
return res.status(400).json({
status:false,
message:'Some Error Occured'
})
}
}



export default {
process_wa_webhook,
process_text
process_text,
webhookControl,
}
130 changes: 128 additions & 2 deletions controllers/ControlCenter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import {
NEW_CATALOG_AVAILABLE,
TRIGGER_BLIZZARD_MESSAGE,
CANCEL_BOOKING_MESSAGE,
TOURISM_STRAPI_URL
TOURISM_STRAPI_URL,
HOTEL_STRAPI_URL,
RETAIL_STRAPI_URL,
ENERGY_STRAPI_URL,
DOMAINS,
UPDATE_STATUS_MESSAGE
} from '../utils/constants.js'
import DBService from '../services/DBService.js'
import MapsService from '../services/MapService.js'
Expand Down Expand Up @@ -138,4 +143,125 @@ export const notify = async (req, res) => {
res.send("Triggered!")
}
else res.status(400).send('Point and message are required in the body.')
}
}


export const updateStatus = async (req, res) => {
try {
const { orderId, domain="", status=null } = req.body
if(!orderId){
return res.status(400).json({message:"Order Id is Required", status:false})
}
let DOMAIN_DETAILS = {
url:"",
token:"",
message:""
}
switch(domain){
case DOMAINS.ENERGY:
DOMAIN_DETAILS = {
url:ENERGY_STRAPI_URL,
token:process.env.STRAPI_ENERGY_TOKEN,
message:status || UPDATE_STATUS_MESSAGE.ENERGY

}
break;
case DOMAINS.RETAIL:
DOMAIN_DETAILS = {
url:RETAIL_STRAPI_URL,
token:process.env.STRAPI_RETAIL_TOKEN,
message:status || UPDATE_STATUS_MESSAGE.RETAIL
}
break;
case DOMAINS.HOTEL:
DOMAIN_DETAILS = {
url:HOTEL_STRAPI_URL,
token:process.env.STRAPI_HOTEL_TOKEN,
message:status || UPDATE_STATUS_MESSAGE.HOTEL
}
break;
case DOMAINS.TOURISM:
DOMAIN_DETAILS = {
url:TOURISM_STRAPI_URL,
token:process.env.STRAPI_TOURISM_TOKEN,
message: status || UPDATE_STATUS_MESSAGE.TOURISM
}
break;
}
const validOrderId = await action.call_api(`${DOMAIN_DETAILS.url}/orders/${orderId}`,'GET',{},{ Authorization: `Bearer ${DOMAIN_DETAILS.token}`})
logger.info(`OrderDetails: ${JSON.stringify(validOrderId)}`)
if(!validOrderId.status){
return res.status(400).send({ message: `Invalid Order Id`, status:false })
}

const getOrderFulfillmentDetails = await action.call_api(`${DOMAIN_DETAILS.url}/order-fulfillments?order_id=${orderId}&sort=order_id.id:desc&populate=order_id`,'GET',{},{ Authorization: `Bearer ${DOMAIN_DETAILS.token}`})
logger.info(`Order Fulfillment Details: ${JSON.stringify(getOrderFulfillmentDetails)}`)
if (getOrderFulfillmentDetails.data.data.length) {
const requiredOrder = getOrderFulfillmentDetails.data.data.find((order)=>order.attributes.order_id.data.id===orderId)
const updateStatusResponse = await action.call_api(`${DOMAIN_DETAILS.url}/order-fulfillments/${requiredOrder.id}`,'PUT',{
data: {
state_code: DOMAIN_DETAILS.message,
state_value: DOMAIN_DETAILS.message,
},
},{ Authorization: `Bearer ${DOMAIN_DETAILS.token}`})
const webhookResponse = await action.call_api(`${process.env.SERVER_URL}/webhook-ps`, 'POST',{...updateStatusResponse, orderId:orderId});
logger.info(JSON.stringify(webhookResponse));
return res.status(200).send({ message: `Status Updated to: ${updateStatusResponse.data.data.attributes.state_value}`, status:true })
}

return res.status(400).send({ message: 'Order Status Update Failed', status:false })
} catch (error) {
logger.error(error.message)
return res.status(400).send({ message: error.message, status:false })
}
}

export const unpublishItem = async (req, res) => {
try{
const {domain="", itemId=""} = req.body
let DOMAIN_DETAILS = {
url:"",
token:""
}

switch(domain){
case DOMAINS.ENERGY:
DOMAIN_DETAILS = {
url:ENERGY_STRAPI_URL,
token:process.env.STRAPI_ENERGY_TOKEN,

}
break;
case DOMAINS.RETAIL:
DOMAIN_DETAILS = {
url:RETAIL_STRAPI_URL,
token:process.env.STRAPI_RETAIL_TOKEN,
}
break;
case DOMAINS.HOTEL:
DOMAIN_DETAILS = {
url:HOTEL_STRAPI_URL,
token:process.env.STRAPI_HOTEL_TOKEN,
}
break;
case DOMAINS.TOURISM:
DOMAIN_DETAILS = {
url:TOURISM_STRAPI_URL,
token:process.env.STRAPI_TOURISM_TOKEN,
}
break;
}
const unpublishItemResp = await action.call_api(`${DOMAIN_DETAILS.url}/items/${itemId}`,'PUT',{
"data":{"publishedAt": null}
},{ Authorization: `Bearer ${DOMAIN_DETAILS.token}`})
return res.status(200).json({
status:unpublishItemResp.status,
message: unpublishItemResp.error || 'Item Unpublished'
})
}catch(error){
return res.status(400).json({
status:false,
message:error.message
})
}
}
12 changes: 9 additions & 3 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
cancelBooking,
updateCatalog,
notify,
triggerExceptionOnLocation
triggerExceptionOnLocation,
updateStatus,
unpublishItem,
} from './controllers/ControlCenter.js'
import path from 'path'
import { fileURLToPath } from 'url';
Expand All @@ -30,11 +32,15 @@ app.post('/notify', notify)
app.post('/cancel-booking', cancelBooking)
app.post('/update-catalog', updateCatalog)
app.post('/trigger-exception', triggerExceptionOnLocation)

app.post('/update-status', updateStatus)
app.post('/unpublish-item', unpublishItem)
app.post('/webhook-ps', messageController.webhookControl)
// Reset all sessions
const db = new DBService()
export const db = new DBService()

await db.clear_all_sessions()


// Start the Express server
app.listen(process.env.SERVER_PORT, () => {
logger.info(`Server is running on port ${process.env.SERVER_PORT}`)
Expand Down
Loading

0 comments on commit 529b3d5

Please sign in to comment.