Twilio sending multiple Images to WhatsApp - image

I am currently trying to send multiple pictures to WhatsApp via Twilio and got it working with one.
Already read the other Questions and this one might be an easy one.
How do I send multiple Images in one Message?
This is what I have currently and what I tried, but the second image is never sent:
exports.handler = function(context, event, callback) {
var client = context.getTwilioClient();
console.log("Sende Antwort")
client.messages.create({
to: event.From,
from: event.To,
body: "Sekunde, mache dir eben deinen Stoff klar."
}, function(err, res){
console.log("Sende Katzenbilder")
let twiml = new Twilio.twiml.MessagingResponse();
let message = twiml.message();
message.body("Hier ist dein wöchentlicher Cat-Content!")
message.media("https://images.unsplash.com/photo-1566927467984-6332be7377d0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80");
message.media("https://images.unsplash.com/photo-1548247416-ec66f4900b2e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=663&q=80")
callback(null, twiml)
})
};

This is not possible via the underlying API
Sending Media Messages
https://developers.facebook.com/docs/whatsapp/api/messages/media
The above sample shows multiple different objects such as audio, document, image, sticker, and video for illustration purposes only. A valid request body contains only one of them.

Twilio developer evangelist here.
WhatsApp only supports sending one image at a time with a message.
In the Twilio API for WhatsApp documentation, this is pointed out (emphasis mine):
To send back a media in your WhatsApp reply, you need to include the media TwiML element with the URL to the media file. One media attachment is supported per message, with a size limit of 5MB.
You could try sending more than one message though, by using twiml.message more than once. Try:
exports.handler = function(context, event, callback) {
var client = context.getTwilioClient();
console.log("Sende Antwort")
client.messages.create({
to: event.From,
from: event.To,
body: "Sekunde, mache dir eben deinen Stoff klar."
}, function(err, res){
console.log("Sende Katzenbilder");
let twiml = new Twilio.twiml.MessagingResponse();
let message = twiml.message();
message.body("Hier ist dein wöchentlicher Cat-Content!");
message.media("https://images.unsplash.com/photo-1566927467984-6332be7377d0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80");
let message2 = twiml.message();
message2.media("https://images.unsplash.com/photo-1548247416-ec66f4900b2e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=663&q=80");
callback(null, twiml)
})
};

Related

Twilio Base64 Media Payload for Google Speech To Text API not Responding

I have a need to do some real time transcriptions from twilio phone calls using Google speech-to-text api and I've followed a few demo apps showing how to set this up. My application is in .net core 3.1 and I am using webhooks with a Twilio defined callback method. Upon retrieving the media from Twilio through the callback it is passed as Raw audio in encoded in base64 as you can see here.
https://www.twilio.com/docs/voice/twiml/stream
I've referenced this demo on Live Transcribing as well and am trying to mimic the case statement in the c#. Everything connects correctly and the media and payload is passed into my app just fine from Twilio.
The audio string is then converted to a byte[] to pass to the Task that needs to transcribe the audio
byte[] audioBytes = Convert.FromBase64String(info);
I am following the examples based of the Google docs that either stream from a file or an audio input (such as a microphone.) Where my use case is different is, I already have the bytes for each chunk of audio. The examples I referenced can be seen here. Transcribing audio from streaming input
Below is my implementation of the latter although using the raw audio bytes. This Task below is hit when the Twilio websocket connection hits the media event. I pass the payload directly into it. From my console logging I am getting to the Print Responses hit... console log, but it will NOT get into the while (await responseStream.MoveNextAsync()) block and log the transcript to the console. I do not get any errors back (that break the application.) Is this possible to even do? I have also tried loading the bytes into a memorystream object and passing them in as the Google doc examples do as well.
static async Task<object> StreamingRecognizeAsync(byte[] audioBytes)
{
var speech = SpeechClient.Create();
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Mulaw,
SampleRateHertz = 8000,
LanguageCode = "en",
},
InterimResults = true,
SingleUtterance = true
}
}); ;
// Print responses as they arrive.
Task printResponses = Task.Run(async () =>
{
Console.WriteLine("Print Responses hit...");
var responseStream = streamingCall.GetResponseStream();
while (await responseStream.MoveNextAsync())
{
StreamingRecognizeResponse response = responseStream.Current;
Console.WriteLine("Response stream moveNextAsync Hit...");
foreach (StreamingRecognitionResult result in response.Results)
{
foreach (SpeechRecognitionAlternative alternative in result.Alternatives)
{
Console.WriteLine("Google transcript " + alternative.Transcript);
}
}
}
});
//using (MemoryStream memStream = new MemoryStream(audioBytes))
//{
// var buffer = new byte[32 * 1024];
// int bytesRead;
// while ((bytesRead = await memStream.ReadAsync(audioBytes, 0, audioBytes.Length)) > 0)
// {
// await streamingCall.WriteAsync(
// new StreamingRecognizeRequest()
// {
// AudioContent = Google.Protobuf.ByteString
// .CopyFrom(buffer, 0, bytesRead),
// });
// }
//}
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(audioBytes),
});
await streamingCall.WriteCompleteAsync();
await printResponses;
return 0;
}
After all this, I discovered that this code works fine, just needs to be broken up and called in different events in the Twilio stream lifecycle.
The config section needs to be placed during the connected event.
The print messages task needs to be placed in the media event.
Then, the WriteCompleteAsync needs to be placed in the stop event when the websocket is closed from Twilio.
One other important item to consider are the number of requests being sent to Google STT to ensure that too many requests aren't overloading the quota which seems to be (for now) 300 requests / minute.

Send multiple files to Slack via API

According to Slack's documentation is only possible to send one file per time via API. The method is this: https://api.slack.com/methods/files.upload.
Using Slack's desktop and web applications we can send multiple files at once, which is useful because the files are grouped, helping in the visualization when we have more than one image with the same context. See the example below:
Do you guys know if it's possible, via API, to send multiple files at once or somehow achieve the same results as the image above?
Thanks in advance!
I've faced with the same problem. But I've tried to compose one message with several pdf files.
How I solved this task
Upload files without setting channel parameter(this prevents publishing) and collect permalinks from response. Please, check file object ref. https://api.slack.com/types/file. Via "files.upload" method you can upload only one file. So, you will need to invoke this method as many times as you have files to upload.
Compose message using Slack markdown <{permalink1_from_first_step}| ><{permalink2_from_first_step}| > - Slack parse links and automatically reformat message
Here is an implementation of the procedure recommended in the other answer in python
def postMessageWithFiles(message,fileList,channel):
import slack_sdk
SLACK_TOKEN = "slackTokenHere"
client = slack_sdk.WebClient(token=SLACK_TOKEN)
for file in fileList:
upload=client.files_upload(file=file,filename=file)
message=message+"<"+upload['file']['permalink']+"| >"
outP = client.chat_postMessage(
channel=channel,
text=message
)
postMessageWithFiles(
message="Here is my message",
fileList=["1.jpg", "1-Copy1.jpg"],
channel="myFavoriteChannel",
)
A Node.JS (es6) example using Slack's Bolt framework
import pkg from '#slack/bolt';
const { App } = pkg;
import axios from 'axios'
// In Bolt, you can get channel ID in the callback from the `body` argument
const channelID = 'C000000'
// Sample Data - URLs of images to post in a gallery view
const imageURLs = ['https://source.unsplash.com/random', 'https://source.unsplash.com/random']
const uploadFile = async (fileURL) {
const image = await axios.get(fileURL, { responseType: 'arraybuffer' });
return await app.client.files.upload({
file: image.data
// Do not use "channels" here for image gallery view to work
})
}
const permalinks = await Promise.all(imageURLs.map(async (imageURL) => {
return (await uploadImage(imageURL)).file.permalink
}))
const images = permalinks.map((permalink) => `<${permalink}| >`).join('')
const message = `Check out the images below: ${images}`
// Post message with images in a "gallery" view
// In Bolt, this is the same as doing `await say({`
// If you use say(, you don't need a channel param.
await app.client.chat.postMessage({
text: message,
channel: channelID,
// Do not use blocks here for image gallery view to work
})
The above example includes some added functionality - download images from a list of image URLs and then upload those images to Slack. Note that this is untested, I trimmed down the fully functioning code I'm using.
Slack's image.upload API docs will mention that the file format needs to be in multipart/form-data. Don't worry about that part, Bolt (via Slack's Node API), will handle that conversion automatically (and may not even work if you feed it FormData). It accepts file data as arraybuffer (used here), stream, and possibly other formats too.
If you're uploading local files, look at passing an fs readstream to the Slack upload function.
For Python you can use:
permalink_list = []
file_list=['file1.csv', 'file2.csv', 'file3.csv']
for file in file_list:
response = client.files_upload(file=file)
permalink = response['file']['permalink']
permalink_list.append(permalink)
text = ""
for permalink in permalink_list:
text_single_link = "<{}| >".format(permalink)
text = text + text_single_link
response = client.chat_postMessage(channel=channelid, text=text)
Here you can play around with the link logic - Slack Block Kit Builder
Python solution using new recommended client.files_upload_v2 (tested on slack-sdk-3.19.5 on 2022-12-21):
import slack_sdk
def slack_msg_with_files(message, file_uploads_data, channel):
client = slack_sdk.WebClient(token='your_slack_bot_token_here')
upload = client.files_upload_v2(
file_uploads=file_uploads_data,
channel=channel,
initial_comment=message,
)
print("Result of Slack send:\n%s" % upload)
file_uploads = [
{
"file": path_to_file1,
"title": "My File 1",
},
{
"file": path_to_file2,
"title": "My File 2",
},
]
slack_msg_with_files(
message='Text to add to a slack message along with the files',
file_uploads_data=file_uploads,
channel=SLACK_CHANNEL_ID # can be found in Channel settings in Slack. For some reason the channel names don't work with `files_upload_v2` on slack-sdk-3.19.5
)
(some additional error handling would not hurt)
Simply use Slack Blocks: Block Kit Builder. Great feature for the message customizations.

MS Teams - Don't show notification of specific message in the activity feed

Question
I have a simple Bot for MS Teams developed in C# with the Bot Builder SDK 3.15.0.0 targeting .NET framework 4.7.1.
When mentioned, it retrieves the Jira ticket Ids in the message and returns a single reply with a list of Cards, each one displaying a summary of a Jira Issue.
I'd like to know if it's possible to not populate the activity feed when sending the reply with the card attachments as it's not needed for my use case.
Example
This is how I usually build the reply to a user message
var reply = activity.CreateReply();
reply.AttachmentLayout = AttachmentLayoutTypes.List;
reply.Attachments = thumbnailCards;
await context.PostAsync(reply);
And this is what I tried after reading the docs at https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/activity-feed#rest-api-sample
var reply = activity.CreateReply();
reply.AttachmentLayout = AttachmentLayoutTypes.List;
reply.Attachments = thumbnailCards;
reply.ChannelData = JsonConvert.SerializeObject(new
{
notification = new
{
alert = false
}
});
await context.PostAsync(reply);
I was hoping that setting the ChannelData with notification.alert = false would just disable the notifications, but it actually doesn't display any message.
Have you tried using the Teams nuget package: https://www.nuget.org/packages/Microsoft.Bot.Connector.Teams
var reply = activity.CreateReply();
reply.ChannelData = JObject.FromObject(new TeamsChannelData()
{
Notification = new NotificationInfo(false)
});
Source for this package can be found here: https://github.com/OfficeDev/BotBuilder-MicrosoftTeams/
The alert you are getting in the activity feed is simply the "someone replied to your message" alert and is nothing special coming from the bot. This notification in the activity feed cannot be disabled as of now. Other team members won't receive this alert in activity feed unless they are following the same channel.
Sending notification using Rest API is designed to work for 1:1 chat.

Telegram Bot Random Images (How to send random images with Telegram-Bot)

const TeleBot = require('telebot');
const bot = new TeleBot({
token: 'i9NhrhCQGq7rxaA' // Telegram Bot API token.
});
bot.on(/^([Hh]ey|[Hh]oi|[Hh]a*i)$/, function (msg) {
return bot.sendMessage(msg.from.id, "Hello Commander");
});
var Historiepics = ['Schoolfotos/grr.jpg', 'Schoolfotos/boe.jpg',
'Schoolfotos/tobinsexy.jpg'];
console.log('Historiepics')
console.log(Math.floor(Math.random() * Historiepics.length));
var foto = Historiepics[(Math.floor(Math.random() * Historiepics.length))];
bot.on(/aap/, (msg) => {
return bot.sendPhoto(msg.from.id, foto);
});
bot.start();
The result I'm getting from this is just one picture everytime, but if I ask for another random picture it keeps showing me the same one without change.
I recently figured this out, so I'll drop an answer for anyone that runs into this issue.
The problem is with Telegram's cache. They cache images server side so that they don't have to do multiple requests to the same url. This protects them from potentially getting blacklisted for too many requests, and makes things snappier.
Unfortunately if you're using an API like The Cat API this means you will be sending the same image over and over again. The simplest solution is just to somehow make the link a little different every time. This is most easily accomplished by including the current epoch time as a part of the url.
For your example with javascript this can be accomplished with the following modifications
bot.on(/aap/, (msg) => {
let epoch = (new Date).getTime();
return bot.sendPhoto(msg.from.id, foto + "?time=" + epoch);
});
Or something similar. The main point is, as long as the URL is different you won't receive a cached result. The other option is to download the file and then send it locally. This is what Telebot does if you pass the serverDownload option into sendPhoto.

Can I send multiple responses via Node.js to the client?

I'm using Node.js and I want to send back multiple responses to the client. So the client will send an AJAX POST request and get back some data. But the server has to continue to do some processing and when that's done, I want it to send more data back.
I know this is a good candidate for Socket.io, but I haven't really seen an example of how to use socket.io in the context of an MVC framework. Does it go in the controller?
You could use Server Sent Events.
Here's an example:
https://github.com/chovy/nodejs-stream (full source code example)
UI
var source = new EventSource('stream');
source.addEventListener('a_server_sent_event', function(e) {
var data = JSON.parse(e.data);
//do something with data
});
Node
if ( uri == '/stream' ) {
//setup http server response handling and get some data from another service
http.get(options, function(resp){
resp.on('data', function(chunk){
res.write("event: a_server_sent_event\n");
res.write("data: "+chunk.toString()+"\n\n");
});
});
}

Resources