Skip to content

Instantly share code, notes, and snippets.

@pgilad
Created August 15, 2019 11:05
Show Gist options
  • Save pgilad/75d8ccc689f52565ae00581ba7c4d07b to your computer and use it in GitHub Desktop.
Save pgilad/75d8ccc689f52565ae00581ba7c4d07b to your computer and use it in GitHub Desktop.
AWS Lambda PolyTalk Slack Integration
const querystring = require('querystring');
const request = require('request-promise-native');
// You must bundle the latest version of the AWS JS SDK (2.7.9)
// The built-in SDK version does not know about the Polly text-to-speech service.
const AWS = require('aws-sdk');
const SLACK_TOKEN = process.env.SLACK_TOKEN;
const SLACK_UPLOAD_URI = 'https://slack.com/api/files.upload';
const AUDIO_FILENAME = 'audio.mp3';
const MAX_ALLOWED_TEXT_LENGTH = 140;
// This defaults to the region the Lambda function is in.
// Note that Polly is not available in all regions yet.
AWS.config.update({
region: process.env.AWS_DEFAULT_REGION
});
const getSpeech = text => {
const Polly = new AWS.Polly({
signatureVersion: 'v4',
region: 'eu-west-1'
});
const params = {
Text: text,
OutputFormat: 'mp3',
TextType: 'text',
VoiceId: process.env.AWS_POLLY_VOICE_ID || 'Emma',
};
return new Promise((resolve, reject) => {
Polly.synthesizeSpeech(params, (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
};
const uploadFileToSlack = (channelId, audio) => {
const formData = {
token: SLACK_TOKEN,
channels: channelId,
filename: AUDIO_FILENAME,
title: 'Audio Recording',
file: {
value: Buffer.from(audio.AudioStream, 'binary'),
options: {
filename: AUDIO_FILENAME,
contentType: 'audio/mpeg',
}
}
};
const options = {
url: SLACK_UPLOAD_URI,
method: 'post',
formData,
};
return request(options);
};
const respondWithError = (text, callback) => callback(null, {response_type: 'ephemeral', text});
exports.handler = async (event, context, callback) => {
const parsed = querystring.parse(event.body);
/** @namespace parsed.channel_id */
const channelId = parsed.channel_id;
const textToSpeak = parsed.text;
console.log('Got the following event: ', parsed);
if (!textToSpeak) {
console.error('Got empty text');
return respondWithError('No text to convert', callback);
}
if (textToSpeak === undefined || (textToSpeak.length > MAX_ALLOWED_TEXT_LENGTH)) {
console.error(`Client tried to convert text with length ${textToSpeak.length}`);
return respondWithError(`Text limited to max of ${MAX_ALLOWED_TEXT_LENGTH} characters`, callback);
}
console.log(`Converting text "${textToSpeak}"`);
const audio = await getSpeech(textToSpeak);
if (!(audio.AudioStream instanceof Buffer)) {
return respondWithError('Could not synthesize audio', callback);
}
console.log('Successfully used Polly to convert to Buffer');
console.log(`Uploading file to slack channel: ${channelId}`);
let response;
try {
response = await uploadFileToSlack(channelId, audio);
// the response returns
response = JSON.parse(response);
} catch (e) {
console.error('Could not upload audio file', e);
return respondWithError('Successfully converted text to audio, but failed to upload to Slack', callback);
}
if (!response || response.ok !== true) {
console.error('Problem in uploading file', response);
return respondWithError('Successfully converted text to audio, but failed to upload to Slack', callback);
}
console.log('Uploaded file successfully');
callback(null, {text: 'Uploaded file with great success!'});
// TODO: post a message instead of replying to channel, allowing private conversations between people
// request.post({
// url: 'https://slack.com/api/chat.postMessage',
// token: SLACK_TOKEN,
// channel: channelId,
// text: body.file.permalink,
// as_user: true
// }, (err, res, body) => {
// console.log('Sent message to channel', body);
// resolve();
// });
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment