How to Build a Quokkabot

A presentation at NDC Sydney in October 2022 in Sydney NSW, Australia by Amy Kapernick

Slide 1

Slide 1

How to Build a Quokkabot @amys_kapers

Slide 2

Slide 2

How to Build a Quokkabot @amys_kapers

Slide 3

Slide 3

I acknowledge the traditional owners of this land, the Gadigal people of the Eora nation. @amys_kapers

Slide 4

Slide 4

@amys_kapers

Slide 5

Slide 5

How to Build a Quokkabot @amys_kapers

Slide 6

Slide 6

@amys_kapers

Slide 7

Slide 7

@amys_kapers

Slide 8

Slide 8

@amys_kapers

Slide 9

Slide 9

@amys_kapers

Slide 10

Slide 10

Demo Time 🤞 @amys_kapers

Slide 11

Slide 11

require(‘dotenv’).config() const twilio = require(‘twilio’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() message.body(“Welcome to Quokkabot”) }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 12

Slide 12

require(‘dotenv’).config() const twilio = require(‘twilio’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() message.body(“Welcome to Quokkabot”) }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 13

Slide 13

require(‘dotenv’).config() const twilio = require(‘twilio’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() message.body(“Welcome to Quokkabot”) }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 14

Slide 14

require(‘dotenv’).config() const twilio = require(‘twilio’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() message.body(“Welcome to Quokkabot”) }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 15

Slide 15

require(‘dotenv’).config() const twilio = require(‘twilio’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() message.body(“Welcome to Quokkabot”) }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 16

Slide 16

require(‘dotenv’).config() const twilio = require(‘twilio’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() message.body(“Welcome to Quokkabot”) }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 17

Slide 17

const twilio = require(‘twilio’) const { randomImage, quokkas, notQuokkas } = require(‘../_data/photos’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() const msgParams = new URLSearchParams(context.req.body) const msgText = msgParams.get(‘Body’) if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media( https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug} ) } else { message.body(‘This is not a quokka’) message.media( https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug} ) } }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 18

Slide 18

const twilio = require(‘twilio’) const { randomImage, quokkas, notQuokkas } = require(‘../_data/photos’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() const msgParams = new URLSearchParams(context.req.body) const msgText = msgParams.get(‘Body’) if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media( https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug} ) } else { message.body(‘This is not a quokka’) message.media( https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug} ) } }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 19

Slide 19

const twilio = require(‘twilio’) const { randomImage, quokkas, notQuokkas } = require(‘../_data/photos’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() const msgParams = new URLSearchParams(context.req.body) const msgText = msgParams.get(‘Body’) if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media( https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug} ) } else { message.body(‘This is not a quokka’) message.media( https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug} ) } }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 20

Slide 20

const twilio = require(‘twilio’) const { randomImage, quokkas, notQuokkas } = require(‘../_data/photos’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() const msgParams = new URLSearchParams(context.req.body) const msgText = msgParams.get(‘Body’) if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media( https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug} ) } else { message.body(‘This is not a quokka’) message.media( https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug} ) } }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 21

Slide 21

const twilio = require(‘twilio’) const { randomImage, quokkas, notQuokkas } = require(‘../_data/photos’) module.exports = async function (context) { const MessagingResponse = twilio.twiml.MessagingResponse const twiml = new MessagingResponse() const message = twiml.message() const msgParams = new URLSearchParams(context.req.body) const msgText = msgParams.get(‘Body’) if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media( https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug} ) } else { message.body(‘This is not a quokka’) message.media( https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug} ) } }; context.done(null, { status: 200, body: message.toString(), headers: { ‘Content-Type’: ‘text/xml’ }, }) @amys_kapers

Slide 22

Slide 22

Try it yourself! @amys_kapers

Slide 23

Slide 23

@amys_kapers

Slide 24

Slide 24

// Existing imports here const { PredictionAPIClient } = require(‘@azure/cognitiveservices-customvision-prediction’) const { ApiKeyCredentials } = require(‘@azure/ms-rest-js’) const const const const key = process.env.API_KEY endpoint = process.env.ENDPOINT projectId = process.env.PROJECT_ID iteration = process.env.ITERATION const credentials = new ApiKeyCredentials({ inHeader: { “Prediction-key”: key } }) const predictor = new PredictionAPIClient(credentials, endpoint) module.exports = async function (context) { // Existing code here const image = msgParams.get(‘MediaUrl0’) let results = false if(image) { results = await predictor.classifyImageUrl(projectId, iteration, { url: image }) } else { if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) } else { message.body(‘This is not a quokka’) message.media(https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug}) } } context.log({results: JSON.stringify(results)}) }; // Finish Azure function @amys_kapers

Slide 25

Slide 25

// Existing imports here const { PredictionAPIClient } = require(‘@azure/cognitiveservices-customvision-prediction’) const { ApiKeyCredentials } = require(‘@azure/ms-rest-js’) const const const const key = process.env.API_KEY endpoint = process.env.ENDPOINT projectId = process.env.PROJECT_ID iteration = process.env.ITERATION const credentials = new ApiKeyCredentials({ inHeader: { “Prediction-key”: key } }) const predictor = new PredictionAPIClient(credentials, endpoint) module.exports = async function (context) { // Existing code here const image = msgParams.get(‘MediaUrl0’) let results = false if(image) { results = await predictor.classifyImageUrl(projectId, iteration, { url: image }) } else { if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) } else { message.body(‘This is not a quokka’) message.media(https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug}) } } context.log({results: JSON.stringify(results)}) }; // Finish Azure function @amys_kapers

Slide 26

Slide 26

// Existing imports here const { PredictionAPIClient } = require(‘@azure/cognitiveservices-customvision-prediction’) const { ApiKeyCredentials } = require(‘@azure/ms-rest-js’) const const const const key = process.env.API_KEY endpoint = process.env.ENDPOINT projectId = process.env.PROJECT_ID iteration = process.env.ITERATION const credentials = new ApiKeyCredentials({ inHeader: { “Prediction-key”: key } }) const predictor = new PredictionAPIClient(credentials, endpoint) module.exports = async function (context) { // Existing code here const image = msgParams.get(‘MediaUrl0’) let results = false if(image) { results = await predictor.classifyImageUrl(projectId, iteration, { url: image }) } else { if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) } else { message.body(‘This is not a quokka’) message.media(https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug}) } } context.log({results: JSON.stringify(results)}) }; // Finish Azure function @amys_kapers

Slide 27

Slide 27

// Existing imports here const { PredictionAPIClient } = require(‘@azure/cognitiveservices-customvision-prediction’) const { ApiKeyCredentials } = require(‘@azure/ms-rest-js’) const const const const key = process.env.API_KEY endpoint = process.env.ENDPOINT projectId = process.env.PROJECT_ID iteration = process.env.ITERATION const credentials = new ApiKeyCredentials({ inHeader: { “Prediction-key”: key } }) const predictor = new PredictionAPIClient(credentials, endpoint) module.exports = async function (context) { // Existing code here const image = msgParams.get(‘MediaUrl0’) let results = false if(image) { results = await predictor.classifyImageUrl(projectId, iteration, { url: image }) } else { if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) } else { message.body(‘This is not a quokka’) message.media(https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug}) } } context.log({results: JSON.stringify(results)}) }; // Finish Azure function @amys_kapers

Slide 28

Slide 28

// Existing imports here const { PredictionAPIClient } = require(‘@azure/cognitiveservices-customvision-prediction’) const { ApiKeyCredentials } = require(‘@azure/ms-rest-js’) const const const const key = process.env.API_KEY endpoint = process.env.ENDPOINT projectId = process.env.PROJECT_ID iteration = process.env.ITERATION const credentials = new ApiKeyCredentials({ inHeader: { “Prediction-key”: key } }) const predictor = new PredictionAPIClient(credentials, endpoint) module.exports = async function (context) { // Existing code here const image = msgParams.get(‘MediaUrl0’) let results = false if(image) { results = await predictor.classifyImageUrl(projectId, iteration, { url: image }) } else { if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) } else { message.body(‘This is not a quokka’) message.media(https://quokkas.amyskapers.dev/img/not_quokkas/${randomImage(notQuokkas).slug}) } } context.log({results: JSON.stringify(results)}) }; // Finish Azure function @amys_kapers

Slide 29

Slide 29

{ } “results”: { “id”: “06f7250b-ffb3-47a1-9508-c928b8c54b87”, “project”: “a44bf78b-da55-4a95-bfdc-21ac780d2518”, “iteration”: “8a75c738-d658-4f63-8a2e-e18318f418f8”, “created”: “2022-10-12T06:31:47.662Z”, “predictions”: [ { “probability”: 0.99992716, “tagId”: “1f6223d5-79ce-4e6b-8f58-ddfbb4635246”, “tagName”: “Quokka”, “tagType”: “Regular” }, { “probability”: 0.00007281252, “tagId”: “7a44e503-b236-4991-bf44-2e08ca0e2356”, “tagName”: “Negative”, “tagType”: “Negative” } ] } @amys_kapers

Slide 30

Slide 30

“predictions”: [ { “probability”: 0.99992716, “tagId”: “1f6223d5-79ce-4e6b-8f58-ddfbb4635246”, “tagName”: “Quokka”, “tagType”: “Regular” }, { “probability”: 0.00007281252, “tagId”: “7a44e503-b236-4991-bf44-2e08ca0e2356”, “tagName”: “Negative”, “tagType”: “Negative” } ] @amys_kapers

Slide 31

Slide 31

“predictions”: [ { “probability”: 0.99992716, “tagName”: “Quokka”, }, { “probability”: 0.00007281252, “tagName”: “Negative”, } ] @amys_kapers

Slide 32

Slide 32

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Get image classification results let outcome = {} results.predictions.forEach(tag => { if (tag.tagName == ‘Negative’) { outcome.negative = tag.probability } else if (tag.tagName == ‘Quokka’) { outcome.quokka = tag.probability } }) const quokka = ${(outcome.quokka * 100).toFixed(2)}% const notQuokka = ${(outcome.negative * 100).toFixed(2)}% if(outcome.negative > outcome.quokka) { message.body( Sorry that doesn't look like a quokka\nQuokka: ${quokka}, Not Quokka: ${notQuokka}\nThat's pretty sad though, so here's a quokka ) message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) } else { message.body(Yep that looks like a quokka!\nQuokka: ${quokka}, Not Quokka: ${notQuokka}) } } else { // Send Quokka or not Quokka photo } }; // Finish Azure Function @amys_kapers

Slide 33

Slide 33

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Get image classification results let outcome = {} results.predictions.forEach(tag => { if (tag.tagName == ‘Negative’) { outcome.negative = tag.probability } else if (tag.tagName == ‘Quokka’) { outcome.quokka = tag.probability } }) const quokka = ${(outcome.quokka * 100).toFixed(2)}% const notQuokka = ${(outcome.negative * 100).toFixed(2)}% if(outcome.negative > outcome.quokka) { message.body( Sorry that doesn't look like a quokka\nQuokka: ${quokka}, Not Quokka: ${notQuokka}\nThat's pretty sad though, so here's a quokka ) message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) } else { message.body(Yep that looks like a quokka!\nQuokka: ${quokka}, Not Quokka: ${notQuokka}) } } else { // Send Quokka or not Quokka photo } }; // Finish Azure Function @amys_kapers

Slide 34

Slide 34

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Get image classification results let outcome = {} results.predictions.forEach(tag => { if (tag.tagName == ‘Negative’) { outcome.negative = tag.probability } else if (tag.tagName == ‘Quokka’) { outcome.quokka = tag.probability } }) const quokka = ${(outcome.quokka * 100).toFixed(2)}% const notQuokka = ${(outcome.negative * 100).toFixed(2)}% if(outcome.negative > outcome.quokka) { message.body( Sorry that doesn't look like a quokka\nQuokka: ${quokka}, Not Quokka: ${notQuokka}\nThat's pretty sad though, so here's a quokka ) message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) } else { message.body(Yep that looks like a quokka!\nQuokka: ${quokka}, Not Quokka: ${notQuokka}) } } else { // Send Quokka or not Quokka photo } }; // Finish Azure Function @amys_kapers

Slide 35

Slide 35

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Get image classification results let outcome = {} results.predictions.forEach(tag => { if (tag.tagName == ‘Negative’) { outcome.negative = tag.probability } else if (tag.tagName == ‘Quokka’) { outcome.quokka = tag.probability } }) const quokka = ${(outcome.quokka * 100).toFixed(2)}% const notQuokka = ${(outcome.negative * 100).toFixed(2)}% if(outcome.negative > outcome.quokka) { message.body( Sorry that doesn't look like a quokka\nQuokka: ${quokka}, Not Quokka: ${notQuokka}\nThat's pretty sad though, so here's a quokka ) message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) } else { message.body(Yep that looks like a quokka!\nQuokka: ${quokka}, Not Quokka: ${notQuokka}) } } else { // Send Quokka or not Quokka photo } }; // Finish Azure Function @amys_kapers

Slide 36

Slide 36

Try it yourself! @amys_kapers

Slide 37

Slide 37

@amys_kapers

Slide 38

Slide 38

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Check Image } else { if(RegExp(/error|issue|wrong/, ‘i’).test(msgText)) { message.body(‘Sorry about that, here is a quokka to cheer you up’) message.media( https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug} ) } else if(RegExp(‘quokka’, ‘i’).test(msgText)) { // Send Quokka } else { // Send not Quokka } } }; // Finish Azure Function @amys_kapers

Slide 39

Slide 39

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Check Image } else { if(RegExp(/error|issue|wrong/, ‘i’).test(msgText)) { message.body(‘Sorry about that, here is a quokka to cheer you up’) message.media( https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug} ) } else if(RegExp(‘quokka’, ‘i’).test(msgText)) { // Send Quokka } else { // Send not Quokka } } }; // Finish Azure Function @amys_kapers

Slide 40

Slide 40

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Check Image } else { if(RegExp(/error|issue|wrong/, ‘i’).test(msgText)) { message.body(‘Sorry about that, here is a quokka to cheer you up’) message.media( https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug} ) } else if(RegExp(‘quokka’, ‘i’).test(msgText)) { // Send Quokka } else { // Send not Quokka } } }; // Finish Azure Function @amys_kapers

Slide 41

Slide 41

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Check Image } else { if(RegExp(/error|issue|wrong/, ‘i’).test(msgText)) { message.body(‘Sorry about that, here is a quokka to cheer you up’) message.media( https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug} ) } else if(RegExp(‘quokka’, ‘i’).test(msgText)) { // Send Quokka } else { // Send not Quokka } } }; // Finish Azure Function @amys_kapers

Slide 42

Slide 42

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Check Image } else { if(RegExp(/error|issue|wrong/, ‘i’).test(msgText)) { message.body(‘Sorry about that, here is a quokka to cheer you up’) } else if (RegExp(‘fact’, ‘i’).test(msgText)) { message.body(randomFacts(facts)) } else if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) } else { message.body(Welcome to Quokkabot! I can do a bunch of different things that have to do with quokkas.\nNeed a picture of a quokka? Just ask me\nNot sure if you've seen a quokka? Send me a picture and I'll tell you if there's a quokka in it) } } }; message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) // Finish Azure Function @amys_kapers

Slide 43

Slide 43

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Check Image } else { if(RegExp(/error|issue|wrong/, ‘i’).test(msgText)) { message.body(‘Sorry about that, here is a quokka to cheer you up’) } else if (RegExp(‘fact’, ‘i’).test(msgText)) { message.body(randomFacts(facts)) } else if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) } else { message.body(Welcome to Quokkabot! I can do a bunch of different things that have to do with quokkas.\nNeed a picture of a quokka? Just ask me\nNot sure if you've seen a quokka? Send me a picture and I'll tell you if there's a quokka in it) } } }; message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) // Finish Azure Function @amys_kapers

Slide 44

Slide 44

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Check Image } else { if(RegExp(/error|issue|wrong/, ‘i’).test(msgText)) { message.body(‘Sorry about that, here is a quokka to cheer you up’) } else if (RegExp(‘fact’, ‘i’).test(msgText)) { message.body(randomFacts(facts)) } else if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) } else { message.body(Welcome to Quokkabot! I can do a bunch of different things that have to do with quokkas.\nNeed a picture of a quokka? Just ask me\nNot sure if you've seen a quokka? Send me a picture and I'll tell you if there's a quokka in it) } } }; message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) // Finish Azure Function @amys_kapers

Slide 45

Slide 45

// Existing imports here module.exports = async function (context) { // Existing code here if(image) { // Check Image } else { if(RegExp(/error|issue|wrong/, ‘i’).test(msgText)) { message.body(‘Sorry about that, here is a quokka to cheer you up’) } else if (RegExp(‘fact’, ‘i’).test(msgText)) { message.body(randomFacts(facts)) } else if(RegExp(‘quokka’, ‘i’).test(msgText)) { message.body(‘This is a quokka’) } else { message.body(Welcome to Quokkabot! I can do a bunch of different things that have to do with quokkas.\nNeed a picture of a quokka? Just ask me\nNot sure if you've seen a quokka? Send me a picture and I'll tell you if there's a quokka in it) } } }; message.media(https://quokkas.amyskapers.dev/img/quokkas/${randomImage(quokkas).slug}) // Finish Azure Function @amys_kapers

Slide 46

Slide 46

Demo Time 🤞 @amys_kapers

Slide 47

Slide 47

const sgMail = require(‘@sendgrid/mail’) const parseMultipartFormData = require(‘@anzp/azure-function-multipart’).default module.exports = async function (context, req) { const { fields, files } = await parseMultipartFormData(req) const body = {} const image = (files && files.length) && files[0].bufferFile fields.forEach(field => { body[field.name] = field.value }) sgMail.setApiKey(process.env.SENDGRID_API_KEY) let msg = { to: body.from, from: { name: ‘Quokkabot’, email: ‘[email protected]’, }, subject: Re: ${body.subject} } if (image) { const results = results = await predictor. classifyImage(projectId, iteration, image) // Format and return image results msg.html = formatResults(results) } else { // Send a Quokka, fact or information } msg.html = ${msg.html} }; await sgMail.send(msg) @amys_kapers

Slide 48

Slide 48

const sgMail = require(‘@sendgrid/mail’) const parseMultipartFormData = require(‘@anzp/azure-function-multipart’).default module.exports = async function (context, req) { const { fields, files } = await parseMultipartFormData(req) const body = {} const image = (files && files.length) && files[0].bufferFile fields.forEach(field => { body[field.name] = field.value }) sgMail.setApiKey(process.env.SENDGRID_API_KEY) let msg = { to: body.from, from: { name: ‘Quokkabot’, email: ‘[email protected]’, }, subject: Re: ${body.subject} } if (image) { const results = results = await predictor. classifyImage(projectId, iteration, image) // Format and return image results } msg.html = formatResults(results) else { // Send a Quokka, fact or information } msg.html = ${msg.html} }; await sgMail.send(msg) @amys_kapers

Slide 49

Slide 49

const sgMail = require(‘@sendgrid/mail’) const parseMultipartFormData = require(‘@anzp/azure-function-multipart’).default module.exports = async function (context, req) { const { fields, files } = await parseMultipartFormData(req) const body = {} const image = (files && files.length) && files[0].bufferFile fields.forEach(field => { body[field.name] = field.value }) sgMail.setApiKey(process.env.SENDGRID_API_KEY) let msg = { to: body.from, from: { name: ‘Quokkabot’, email: ‘[email protected]’, }, subject: Re: ${body.subject} } if (image) { const results = results = await predictor. classifyImage(projectId, iteration, image) // Format and return image results msg.html = formatResults(results) } else { // Send a Quokka, fact or information } msg.html = ${msg.html} }; await sgMail.send(msg) @amys_kapers

Slide 50

Slide 50

const sgMail = require(‘@sendgrid/mail’) const parseMultipartFormData = require(‘@anzp/azure-function-multipart’).default module.exports = async function (context, req) { const { fields, files } = await parseMultipartFormData(req) const body = {} const image = (files && files.length) && files[0].bufferFile fields.forEach(field => { body[field.name] = field.value }) sgMail.setApiKey(process.env.SENDGRID_API_KEY) let msg = { to: body.from, from: { name: ‘Quokkabot’, email: ‘[email protected]’, }, subject: Re: ${body.subject} } if (image) { const results = results = await predictor. classifyImage(projectId, iteration, image) // Format and return image results msg.html = formatResults(results) } else { // Send a Quokka, fact or information } msg.html = ${msg.html} }; await sgMail.send(msg) @amys_kapers

Slide 51

Slide 51

const sgMail = require(‘@sendgrid/mail’) const parseMultipartFormData = require(‘@anzp/azure-function-multipart’).default module.exports = async function (context, req) { const { fields, files } = await parseMultipartFormData(req) const body = {} const image = (files && files.length) && files[0].bufferFile fields.forEach(field => { body[field.name] = field.value }) sgMail.setApiKey(process.env.SENDGRID_API_KEY) let msg = { to: body.from, from: { name: ‘Quokkabot’, email: ‘[email protected]’, }, subject: Re: ${body.subject} } if (image) { const results = results = await predictor. classifyImage(projectId, iteration, image) // Format and return image results msg.html = formatResults(results) } else { // Send a Quokka, fact or information } msg.html = ${msg.html} }; await sgMail.send(msg) @amys_kapers

Slide 52

Slide 52

const sgMail = require(‘@sendgrid/mail’) const parseMultipartFormData = require(‘@anzp/azure-function-multipart’).default module.exports = async function (context, req) { const { fields, files } = await parseMultipartFormData(req) const body = {} const image = (files && files.length) && files[0].bufferFile fields.forEach(field => { body[field.name] = field.value }) sgMail.setApiKey(process.env.SENDGRID_API_KEY) let msg = { to: body.from, from: { name: ‘Quokkabot’, email: ‘[email protected]’, }, subject: Re: ${body.subject} } if (image) { const results = results = await predictor. classifyImage(projectId, iteration, image) // Format and return image results msg.html = formatResults(results) } else { // Send a Quokka, fact or information } msg.html = ${msg.html} }; await sgMail.send(msg) @amys_kapers

Slide 53

Slide 53

const sgMail = require(‘@sendgrid/mail’) const parseMultipartFormData = require(‘@anzp/azure-function-multipart’).default module.exports = async function (context, req) { const { fields, files } = await parseMultipartFormData(req) const body = {} const image = (files && files.length) && files[0].bufferFile fields.forEach(field => { body[field.name] = field.value }) sgMail.setApiKey(process.env.SENDGRID_API_KEY) let msg = { to: body.from, from: { name: ‘Quokkabot’, email: ‘[email protected]’, }, subject: Re: ${body.subject} } if (image) { const results = results = await predictor. classifyImage(projectId, iteration, image) // Format and return image results msg.html = formatResults(results) } else { // Send a Quokka, fact or information } msg.html = ${msg.html} }; await sgMail.send(msg) @amys_kapers

Slide 54

Slide 54

const sgMail = require(‘@sendgrid/mail’) const parseMultipartFormData = require(‘@anzp/azure-function-multipart’).default module.exports = async function (context, req) { const { fields, files } = await parseMultipartFormData(req) const body = {} const image = (files && files.length) && files[0].bufferFile fields.forEach(field => { body[field.name] = field.value }) sgMail.setApiKey(process.env.SENDGRID_API_KEY) let msg = { to: body.from, from: { name: ‘Quokkabot’, email: ‘[email protected]’, }, subject: Re: ${body.subject} } if (image) { const results = results = await predictor. classifyImage(projectId, iteration, image) // Format and return image results msg.html = formatResults(results) } else { // Send a Quokka, fact or information } msg.html = ${msg.html} }; await sgMail.send(msg) @amys_kapers

Slide 55

Slide 55

Try it yourself! @amys_kapers

Slide 56

Slide 56

[email protected] @amys_kapers

Slide 57

Slide 57

quokkas.amyskapers.dev @amys_kapers

Slide 58

Slide 58

quokkas.amyskapers.dev/results @amys_kapers

Slide 59

Slide 59

What next? @amys_kapers

Slide 60

Slide 60

github.com/amykapernick/quokkas @amys_kapers

Slide 61

Slide 61

github.com/amykapernick/quokkas @amys_kapers

Slide 62

Slide 62

But why? @amys_kapers

Slide 63

Slide 63

Thank You 👏 @amys_kapers

Slide 64

Slide 64

Questions? @amys_kapers