Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added: Static Map Code #74

Merged
merged 4 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ TWILIO_NUMBER=
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
2 changes: 2 additions & 0 deletions .github/workflows/api_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
echo "TEST_RECEPIENT_NUMBER=${{secrets.TEST_RECEPIENT_NUMBER}}" >> .env
echo "STRAPI_TOURISM_TOKEN=${{secrets.STRAPI_TOURISM_TOKEN}}" >> .env
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

- 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 @@ -26,6 +26,9 @@ jobs:
echo "TEST_RECEPIENT_NUMBER=${{secrets.TEST_RECEPIENT_NUMBER}}" >> .env
echo "STRAPI_TOURISM_TOKEN=${{secrets.STRAPI_TOURISM_TOKEN}}" >> .env
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

- name: Create SSH key file
run: echo -e "${{ secrets.EC2_SSH_KEY }}" > ~/ec2_key
env:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/lint_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ jobs:
echo "TEST_RECEPIENT_NUMBER=${{secrets.TEST_RECEPIENT_NUMBER}}" >> .env
echo "STRAPI_TOURISM_TOKEN=${{secrets.STRAPI_TOURISM_TOKEN}}" >> .env
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

- name: Set up Node.js
uses: actions/setup-node@v2
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ jobs:
echo "TEST_RECEPIENT_NUMBER=${{secrets.TEST_RECEPIENT_NUMBER}}" >> .env
echo "STRAPI_TOURISM_TOKEN=${{secrets.STRAPI_TOURISM_TOKEN}}" >> .env
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

- name: Set up Node.js
uses: actions/setup-node@v2
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,4 @@ dist
.yarn/install-state.gz
.pnp.*
.DS_Store

13 changes: 9 additions & 4 deletions controllers/Bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ async function process_text(req, res) {

let response= {
raw: null,
formatted: null
formatted: null,
media:null
};

const EMPTY_SESSION = {
Expand Down Expand Up @@ -192,7 +193,12 @@ async function process_text(req, res) {
session.selected_route = session.routes[index];
const url = `https://www.google.com/maps/dir/${session.selected_route.source_gps.lat},${session.selected_route.source_gps.lng}/${session.selected_route.destination_gps.lat},${session.selected_route.destination_gps.lng}/`;
route_response.message = `Your route has been actived. Here is the link to navigate : ${url}. What do you want to do next?`;

const map_image_url = await mapService.get_static_image_path([session.selected_route]);
if(map_image_url){
const map_image_url_server = await actionsService.download_file(map_image_url);
logger.info(`Image url : ${map_image_url_server}`)
if(map_image_url_server) response.media=[map_image_url_server]
}
}
const formatting_response = await ai.format_response(route_response, [{ role: 'user', content: message },...session.text]);
response.formatted = formatting_response.message;
Expand Down Expand Up @@ -246,7 +252,7 @@ async function process_text(req, res) {

// Send response
if(format!='application/json'){
await actionsService.send_message(sender, response.formatted)
await actionsService.send_message(sender, response.formatted, response.media || [])
res.send("Done!")
}
else (raw_yn && response.raw) ? res.send(response.raw) : res.send(response.formatted)
Expand Down Expand Up @@ -376,7 +382,6 @@ async function process_action(action, text, session, sender=null, format='applic

return response;
}

export default {
process_wa_webhook,
process_text
Expand Down
4 changes: 4 additions & 0 deletions public/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
10 changes: 6 additions & 4 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import {
notify,
triggerExceptionOnLocation
} from './controllers/ControlCenter.js'
import path from 'path'
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const app = express()
app.use(cors())
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))

// parse application/json
app.use('/public', express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json())

// Define endpoints here
Expand All @@ -28,7 +31,6 @@ app.post('/cancel-booking', cancelBooking)
app.post('/update-catalog', updateCatalog)
app.post('/trigger-exception', triggerExceptionOnLocation)


// Reset all sessions
const db = new DBService()
await db.clear_all_sessions()
Expand Down
97 changes: 67 additions & 30 deletions services/Actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ import twilio from 'twilio'
import logger from '../utils/logger.js'
import axios from 'axios'
import AI from './AI.js'

import {createWriteStream} from 'fs'
import path from 'path'
import { v4 as uuidv4 } from 'uuid'
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const accountSid = process.env.TWILIO_ACCOUNT_SID
const authToken = process.env.TWILIO_AUTH_TOKEN
const twilioNumber = process.env.TWILIO_NUMBER

const client = twilio(accountSid, authToken)
const __dirname = path.dirname(__filename);
const rootPath = path.resolve(__dirname, './');

class Actions {

Expand Down Expand Up @@ -36,7 +42,7 @@ class Actions {
if(request.data.context && request.data.context.action==='search'){
response.data.responses = response.data.responses.filter(res => res.message && res.message.catalog && res.message.catalog.providers && res.message.catalog.providers.length > 0)
if(response.data.responses.length > 0)
response.data.responses = response.data.responses.slice(0, 1);
response.data.responses = response.data.responses.slice(0, 1);
}
responseObject = {
status: true,
Expand Down Expand Up @@ -92,37 +98,68 @@ class Actions {
const format_response_response = await this.ai.format_response(
call_api_response.data,
[...context, { role: 'user', content: message }]
)
response.formatted = format_response_response.message
}
}
} catch (error) {
logger.error(`Error processing instruction: ${error.message}`)
response.formatted = `Failed to process the instruction: ${error.message}`
)
response.formatted = format_response_response.message
}
}
} catch (error) {
logger.error(`Error processing instruction: ${error.message}`)
response.formatted = `Failed to process the instruction: ${error.message}`
}

return response;
}

return response;
}

async send_message(recipient, message, media_url=null) {
try {
let body = {
body: message,
from: `whatsapp:${twilioNumber}`,
to: recipient.includes('whatsapp:') ? recipient : `whatsapp:${recipient}`,
async send_message(recipient, message, media_url=null) {
try {
let body = {
body: message,
from: `whatsapp:${twilioNumber}`,
to: recipient.includes('whatsapp:') ? recipient : `whatsapp:${recipient}`,
}

if(media_url && !process.env.DEVELOPER_MODE_ON){
body.mediaUrl = [media_url];
}
let data = await client.messages.create(body)
const status = await client.messages(data.sid).fetch()
return { deliveryStatus: status.status }
} catch (error) {
logger.error(`Error sending message: ${error.message}`)
return false;
}

if(media_url){
body.mediaUrl = [media_url];
}

async download_file(url) {
const destination_path = path.join(rootPath,'../public');
try {
const response = await axios({
method: 'GET',
url: url,
responseType: 'stream',
});

const fileName = `${uuidv4()}.png`;
const filePath = path.join(destination_path, fileName);

// Create a write stream to save the file
const writer = createWriteStream(filePath);

// Pipe the response data to the file
response.data.pipe(writer);

return new Promise((resolve, reject) => {
writer.on('finish', ()=>{
resolve(`${process.env.SERVER_URL}/public/${fileName}`)
});
writer.on('error', reject);

});
} catch (error) {
logger.error(`Error sending message: ${error.message}`)
return null;
}
let data = await client.messages.create(body)
const status = await client.messages(data.sid).fetch()
return { deliveryStatus: status.status }
} catch (error) {
logger.error(`Error sending message: ${error.message}`)
return false;
}
}
}

export default Actions

export default Actions
6 changes: 3 additions & 3 deletions tests/unit/controllers/bot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('Test cases for Google maps', () => {
expect(source_gps).to.have.property('lng');
})

it('It should take a trip plannign input and generate static route image and directions link.', async () => {
it.only('It should take a trip plannign input and generate static route image and directions link.', async () => {
const ask = "Can you plean a trip from Denver to Yellowstone national park?";

// identify source and destination
Expand Down Expand Up @@ -90,8 +90,8 @@ describe('Test cases for Google maps', () => {
const directions = `https://www.google.com/maps/dir/${source_gps.lat},${source_gps.lng}/${destination_gps.lat},${destination_gps.lng}/`;
const route_image = `https://maps.googleapis.com/maps/api/staticmap?size=300x300&path=enc:${routes[selected_route].overview_polyline.points}&key=${process.env.GOOGLE_MAPS_API_KEY}`;


await actionsService.send_message(process.env.TEST_RECEPIENT_NUMBER, `Here are the directions: ${directions}`); // should also pass the route image, its correctly throwing an error.
// const server_route_image = await actionsService.download_file(route_image);
await actionsService.send_message(process.env.TEST_RECEPIENT_NUMBER, `Here are the directions: ${directions}`);
logger.info(`directions: ${directions}`);
logger.info(`route_image: ${route_image}`);
expect(routes).to.be.an('array').that.is.not.empty;
Expand Down
1 change: 1 addition & 0 deletions tests/unit/services/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as chai from 'chai'
const expect = chai.expect
import ActionService from '../../../services/Actions.js'
import { describe } from 'mocha'

const actionsService = new ActionService()

describe.skip('Test cases for process_instruction function', ()=> {
Expand Down
Loading