Finding All Undelivered Messages
Overview
A common need with message campaigns involves pulling all of the undelivered messages for further analysis. This guide will show how you can use the SignalWire Python SDK and SignalWire PHP SDK in order to do exactly that.
Full code example: Pull Undelivered Messages
- Python
- PHP
- Node
- Java
- Ruby
from datetime import datetime
from signalwire.rest import Client as signalwire_client
import pandas as pd
client = signalwire_client("ProjectID", "AuthToken",
signalwire_space_url='YOURSPACE.signalwire.com')
messages = client.messages.list(date_sent_after=datetime(2021, 10, 20),
date_sent_before=datetime(2021, 10, 30),
from_="+1xxxxxxxxxx",
)
d = []
for record in messages:
if record.status == "undelivered":
d.append((record.from_, record.to, record.date_sent, record.sid, record.error_code))
df = pd.DataFrame(d, columns=('From', 'To', 'Date', 'MessageSID', 'Error Code'))
print(df)
df.to_csv('UndeliveredMessages.csv', index=False, encoding='utf-8')
<?php
require './vendor/autoload.php';
use SignalWire\Rest\Client;
$client = new Client("ProjectID", "AuthToken", array("signalwireSpaceUrl" => "YOURSPACE.signalwire.com"));
$messages = $client->messages->read([
"dateSentAfter" => new \DateTime('2021-11-15'),
"dateSentBefore" => new \DateTime('2021-11-30'),
'from' => '+1xxxxxxxxxx',
]);
$fields = array('Message SID', 'From', 'To', 'Date Sent', 'Status', 'Direction', 'Price');
echo '"'.implode('","', $fields).'"'."\n";
$fp = fopen(date("Y-m-d").'_messageReport.csv', 'w');
fputcsv($fp, $fields);
foreach ($messages as $message) {
if ($message->status == "undelivered")
$row = array(
$message->sid,
$message->from,
$message->to,
$message->dateSent->format('Y-m-d H:i:s'),
$message->status,
$message->direction,
$message->price,
);
fputcsv($fp, $row);
echo '"'.implode('","', $row).'"'."\n";
}
fclose($fp);
const dfd = require("danfojs");
const fs = require('fs');
const { RestClient } = require("@signalwire/compatibility-api");
// TODO: Update with your credentials
let space_url = "YOURSPACENAME.signalwire.com";
let project_id = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX";
let access_token = "PTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
const client = RestClient(
project_id,
access_token, {
signalwireSpaceUrl: space_url,
});
// TODO: Update numbers list
const from_numbers = [
"+###########",
"+###########",
"+###########",
"+###########",
];
let data = [];
Promise.all(
from_numbers.map((number) =>
client.messages
.list({
from: number,
dateSentAfter: new Date(Date.UTC(2022, 01, 01)),
dateSentBefore: new Date(Date.UTC(2022, 04, 25))
})
.then((messages) =>
messages.forEach((message) => {
if (message.status == "undelivered") {
data.push([
message.from,
message.to,
message.dateSent,
message.sid,
message.errorCode,
]);
}
})
)
)
).then(() => {
let allData = new dfd.DataFrame(data, {
columns: [
"From",
"To",
"Date",
"Message SID",
"Error Code",
],
config: {
tableDisplayConfig: {
columns: [
{ width: 1 },
{ width: 13 },
{ width: 13 },
{ width: 8 },
{ width: 36 },
{ width: 16 }
],
}
}
});
allData.print();
fs.writeFileSync("UndeliveredMessages.csv", dfd.toCSV(allData));
});
import io.github.olajhidey.SignalWireClient;
import io.github.olajhidey.model.message.Message;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MessageUndelivered {
public static List<String[]> dataLines = new ArrayList<>();
public static SignalWireClient client() {
return new SignalWireClient("YOUR-PROJECT-TOKEN",
"YOUR-API-TOKEN",
"YOUR-SPACE-NAME");
}
public static void main(String[] args) {
List<Message> messages = client().message().getMessages().messages;
dataLines.add(new String[]{"From", "To", "Date", "Status", "MessageSID"});
for (Message message : messages) {
if(message.status == "undelivered"){
dataLines.add(new String[]{message.from, message.to, message.date_created, message.to, message.sid});
}
}
try {
createAndPopulateCSV();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
public static String convertToCsv(String[] data) {
return Stream.of(data)
.map(MessageUndelivered::escapeSpecialCharacters)
.collect(Collectors.joining(","));
}
public static void createAndPopulateCSV() throws IOException {
// Auto-generate the name of the file
String filename = "message_result" + System.currentTimeMillis();
// Get the absolute path to the folder you would like to create in the project directory
File file = new File(Paths.get("csv").toAbsolutePath().toUri());
// if file doesn't exist. create a new folder
if (!file.exists()) {
boolean isFolder = file.mkdir();
if (isFolder) {
System.out.println("New folder created!");
} else {
System.out.println("Couldn\'t create folder");
}
}
// create new csv file with the autogenerated name
File csvOutputFile = new File(Paths.get("csv").toAbsolutePath() + "\\" + filename);
try (PrintWriter printWriter = new PrintWriter(csvOutputFile)) {
dataLines.stream()
.map(MessageUndelivered::convertToCsv)
.forEach(printWriter::println); // Write item into the CSV file
System.out.println("Write operation done successfully!");
}
}
public static String escapeSpecialCharacters(String data) {
String escapeData = data.replace("\\R", " ");
if (data.contains(",") || data.contains("\"")) {
data = data.replace("\"", "\"\"");
escapeData = "\"" + data + "\"";
}
return escapeData;
}
}
require 'dotenv/load'
require 'signalwire/sdk'
require 'csv'
username = ENV['projectID']
password = ENV['token']
host = ''
@client = Signalwire::REST::Client.new username, password, signalwire_space_url: host
messages = @client.messages.list( )
#some extra helper variables
messageList = []
headers = ['sid','to','from','date']
# set status. Options: delivered, undelivered, sent, failed
status = 'undelivered'
if message.status == status
messageList.push([message.sid,message.to,message.from,message.date_sent,])
end
end
CSV.open('undelivered-MSG.csv','w',headers: headers, write_headers:true) do |csv|
messageList.each do |message|
csv << message
end
end
Python
What do we need to run this code?
For the following code to work, you will need to have pandas, DateTime, and the SignalWire Python SDK installed.
Read about the different ways to install pandas here.
Read about DateTime and how to install using pip here.
Read about the SignalWire Python SDK and how to install here.
Code Walkthrough
We will start by importing the necessary resources. In this example, that is DateTime, pandas, and the SignalWire Client. We also need to instantiate the SignalWire client using the project ID, auth token, and space URL.
from datetime import datetime
from signalwire.rest import Client as signalwire_client
import pandas as pd
client = signalwire_client("ProjectID", "AuthToken", signalwire_space_url = 'SpaceURL')
As this script involves pulling all messages and then filtering out the ones that are undelivered, it's very important to try and narrow down the results by using a date range, From numbers, or To numbers. DateSent
is a DateTime object where the order for the arguments is Year, Month, Date, Hour, Minute, Seconds. You can leave hour, minute, and seconds at 0, unless you have a specific time of day you would like to filter by.
For months Jan - September, A slight change was made because python version (3.9) does not support leading 0's in datetime anymore. You must use the 0o prefix for octal literals now. That is reflected below in the code. If your version doesn’t include that limitation, you can switch it back to 01 or whatever month you need.
The example below will show how to find all the messages within a specific date range that were sent from a specific From number.
messages = client.messages.list(date_sent_after=datetime(2021, 10, 20),
date_sent_before=datetime(2021, 10, 30)
from_="+1xxxxxxxxxx",
)
In the next section we will create an empty array d
to keep track of our message data. We will loop through all the returned messages from the previous section and check for those where the status is undelivered. When a matches this condition, we will insert the data into the d array.
d = []
for record in messages:
if record.status == "undelivered":
d.append((record.from_, record.to, record.date_sent, record.sid, record.error_code))
You can expand this to include as many or as few parameters as you'd like. To see all of the parameters returned in the JSON response, you can view our API documentation here: List Messages API
Next, we will create a dataframe using pandas with column name headers. It's important to make sure that the order of the headers matches the order of the parameters you inserted into the array above. If you choose to add more or remove parameters, make sure to double-check that the order matches or your data will be mismatched in the CSV.
Lastly, we will print the dataframe for debugging purposes and export it to CSV. Using the parameter index=False
turns off the indexing for each row.
df = pd.DataFrame(d, columns=('From', 'To', 'Date', 'MessageSID', 'Error Code'))
print(df)
df.to_csv('UndeliveredMessages.csv', index=False, encoding='utf-8')
PHP
What do we need to run this code?
For the following code to work, you will need to have the SignalWire PHP SDK installed. Read about the SignalWire PHP SDK and how to install here.
You will also need to make sure that the vendor/autoload.php path points to the correct location on your computer, as we can’t determine that for you. However, if you want to run this script exactly, install the SDK from within the folder that contains this PHP script.
Code Walkthrough
First, we need to import the necessary resources. In this example, that just means we need to point to the correct path of the vendor autoload file on your computer. We also need to instantiate the SignalWire client using the project ID, auth token, and space URL.
<?php
require './vendor/autoload.php';
use SignalWire\Rest\Client;
$client = new Client("ProjectID", "AuthToken", array("signalwireSpaceUrl" => "YOURSPACE.signalwire.com"));
Next, we need to choose what parameters we'd like to filter by. In this example, I have filtered by a date range and a from number to help narrow down the returned results. Depending on the volume of messaging that you send, you may have to restrict your date range down further so that you don't hit any memory allocation errors.
$messages = $client->messages->read([
"dateSentAfter" => "2021-04-01",
"dateSentBefore" => "2021-04-20",
'from' => '+1xxxxxxxxxx',
]);
In the next section, we need to create the file to insert the message data into.
Begin by writing headers and storing them in a variable called $fields
. We also use implode
to join the elements of the array with a string. We will then use fopen
to create and open a file named TodaysDate_MessageReport. After that, we use fputcsv
to insert our headers stored in $fields
into our file.
$fields = array('Message SID', 'From', 'To', 'Date Sent', 'Status', 'Direction', 'Price');
echo '"'.implode('","', $fields).'"'."\n";
$fp = fopen(date("Y-m-d").'_messageReport.csv', 'w');
fputcsv($fp, $fields);
Next, we need to loop through our $messages
array in order to gather the necessary message data. We will first check each message record to see what the message status is. If the status is undelivered, we will insert the message record into our CSV.
It is important to make sure the order of the headers is the same as the order of the values we will be gathering. As we loop through each message record, we will insert it into the CSV one by one and use implode to join the elements of the array with a string. The last step is to close the file!
foreach ($messages as $message) {
if ($message->status == "undelivered")
$row = array(
$message->sid,
$message->from,
$message->to,
$message->dateSent->format('Y-m-d H:i:s'),
$message->status,
$message->direction,
$message->price,
);
fputcsv($fp, $row);
echo '"'.implode('","', $row).'"'."\n";
}
fclose($fp);
Node.js
What do I need to run this code?
We will need the following libraries (click their names to get instructions on how to install them):
- Danfo.js
- fs - there is no need to install this module, as it is part of the Node.js Core
- SignalWire Rest Client
How to Run Snippet?
If you save this code snippet in a file called undeliveredMessages.js
, for example, you then need to run:
node undeliveredMessages.js
.
Code Walkthrough
Load the necessary libraries
const dfd = require("danfojs");
const fs = require("fs");
const { RestClient } = require("@signalwire/compatibility-api");
Instantiate the SignalWire Rest Client
In order for us to connect to SignalWire later on in the code using the Rest Client we first need to make sure we update space_url
, project_id
, and access_token
.
let space_url = "YOURSPACENAME.signalwire.com";
let project_id = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX";
let access_token = "PTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const client = RestClient(project_id, access_token, {
signalwireSpaceUrl: space_url,
});
Set the numbers to look for
In this step we need to create two arrays: one will contain all of the from
numbers that we want to filter by and the other will be an empty array used for storing message records in greater detail.
Make sure to update the from_numbers
array with your list of numbers, in E.164 format.
const from_numbers = ["+###########", "+###########", "+###########", "+###########"];
let data = [];
Loop through all of the numbers
For each of the numbers outlined in the previous step, we make use of the List Messages endpoint, through client.messages.list
, and specify the from
, dateSentAfter
, and dateSentBefore
parameters.
After we get the list of messages back, all messages where the status is undelivered
are added to the data
array.
Only once all of the numbers have been parsed do we get all of the data passed to the data
array, and create the allData
DataFrame using it. Using DataFrames allows us to easily print all of the message data, as well as export it to a CSV file.
Promise.all(
from_numbers.map((number) =>
client.messages
.list({
from: number,
dateSentAfter: new Date(Date.UTC(2022, 01, 01)),
dateSentBefore: new Date(Date.UTC(2022, 04, 25))
})
.then((messages) =>
messages.forEach((message) => {
if (message.status == "undelivered") {
data.push([
message.from,
message.to,
message.dateSent,
message.sid,
message.errorCode,
]);
}
})
)
)
).then(() => {
let allData = new dfd.DataFrame(data, {
columns: [
"From",
"To",
"Date",
"Message SID",
"Error Code",
],
config: {
tableDisplayConfig: {
columns: [
{ width: 1 },
{ width: 13 },
{ width: 13 },
{ width: 8 },
{ width: 36 },
{ width: 16 }
],
}
}
});
Print the results
Lastly, we print the allData
DataFrame to the terminal, and create the UndeliveredMessages.csv
file using it.
allData.print();
fs.writeFileSync("UndeliveredMessages.csv", dfd.toCSV(allData));
Java
What do I need to run this code?
For the following code to work, you will need to have a Maven project, The Java Wrapper Library and a Signalwire account.
Read about how to get started with the Java Wrapper API here
Read on how to create a Maven project with Intellij here
** How to get API Credentials **
The API also requires that you authenticate yourself using your Project ID, API Token, and Space URL. If you do not know where to find these values, check out our guide here!
How to Run Snippet?
Assuming you've named your script MessageUndelivered.java
you would need to do the following to run the javac MessageUndelivered.java
then java MessageUndelivered.java
.
Code Walkthrough
We also need to instantiate the SignalWire client using the project ID, auth token, and space URL
public static List<String[]> dataLines = new ArrayList<>();
public static SignalWireClient client() {
return new SignalWireClient("YOUR-PROJECT-TOKEN",
"YOUR-API-TOKEN",
"YOUR-SPACE-NAME");
}
We need to get all the messages from the SignalWire space using the Java Wrapper library. added the below line of code.
List<Message> messages = client().message().getMessages().messages;
Loop through the messages
and add an if statement to check if the element in the array has the status "undelivered"
for (Message message : messages) {
if(message.status == "undelivered"){
dataLines.add(new String[]{message.from, message.to, message.date_created, message.to, message.sid});
}
}
Now we have the filtered result of the messages
in our dataLines
variable, we can then call the createPopulateCsv()
method which is an helper method that generates and populate our csv.
try {
createAndPopulateCSV();
} catch (IOException ioException) {
ioException.printStackTrace();
}
Full Java code:
import io.github.olajhidey.SignalWireClient;
import io.github.olajhidey.model.message.Message;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MessageUndelivered {
public static List<String[]> dataLines = new ArrayList<>();
public static SignalWireClient client() {
return new SignalWireClient("YOUR-PROJECT-TOKEN",
"YOUR-API-TOKEN",
"YOUR-SPACE-NAME");
}
public static void main(String[] args) {
List<Message> messages = client().message().getMessages().messages;
dataLines.add(new String[]{"From", "To", "Date", "Status", "MessageSID"});
for (Message message : messages) {
if(message.status == "undelivered"){
dataLines.add(new String[]{message.from, message.to, message.date_created, message.to, message.sid});
}
}
try {
createAndPopulateCSV();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
public static String convertToCsv(String[] data) {
return Stream.of(data)
.map(MessageUndelivered::escapeSpecialCharacters)
.collect(Collectors.joining(","));
}
public static void createAndPopulateCSV() throws IOException {
// Auto-generate the name of the file
String filename = "message_result" + System.currentTimeMillis();
// Get the absolute path to the folder you would like to create in the project directory
File file = new File(Paths.get("csv").toAbsolutePath().toUri());
// if file doesn't exist. create a new folder
if (!file.exists()) {
boolean isFolder = file.mkdir();
if (isFolder) {
System.out.println("New folder created!");
} else {
System.out.println("Couldn\'t create folder");
}
}
// create new csv file with the autogenerated name
File csvOutputFile = new File(Paths.get("csv").toAbsolutePath() + "\\" + filename);
try (PrintWriter printWriter = new PrintWriter(csvOutputFile)) {
dataLines.stream()
.map(MessageUndelivered::convertToCsv)
.forEach(printWriter::println); // Write item into the CSV file
System.out.println("Write operation done successfully!");
}
}
public static String escapeSpecialCharacters(String data) {
String escapeData = data.replace("\\R", " ");
if (data.contains(",") || data.contains("\"")) {
data = data.replace("\"", "\"\"");
escapeData = "\"" + data + "\"";
}
return escapeData;
}
}
Ruby
What do we need to run this code?
This snippet uses two gems SignalWire
and Dotenv
.
Additionally this snippet requires CSV
which is part of the ruby standard gems
Code Walkthrough
Authentication
First we will load our required gems, and construct our SignalWire client using our credentials.
require 'dotenv/load'
require 'signalwire/sdk'
require 'csv'
username = ENV['projectID']
password = ENV['token']
host = ''
@client = Signalwire::REST::Client.new username, password, signalwire_space_url: host
requesting a list of messages
Next we can use our client to request a list of all messages in the project, as well as establish some helper variables
messages = @client.messages.list( )
messageList = []
headers = ['sid','to','from','date']
# set status. Options: delivered, undelivered, sent, failed
status = 'undelivered'
filter messages and add them to our list
Then for each message SignalWire returns we can check the message.status
, and add it to our messageList
if it matches our desired status.
messages.each do|message|
if message.status == status
messageList.push([message.sid,message.to,message.from,message.date_sent,])
end
end
write our list to a csv
Finally, we can write each message from our filtered list to a CSV
CSV.open('undelivered-MSG.csv','w',headers: headers, write_headers:true) do |csv|
messageList.each do |message|
csv << message
end
end
Wrap up
Our List Messages API makes it easy to get lots of detail on your messages, and utilizing Python, PHP, or Node.js you can look for a particular status in particular, and get Messages SIDs for our Support team to investigate if needed!
Sign Up Here
If you would like to test this example out, you can create a SignalWire account and space here.
Please feel free to reach out to us on our Community Slack or create a Support ticket if you need guidance!