import Tabs from @theme/Tabs; import TabItem from @theme/TabItem; ## 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 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 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); js 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)); }); java 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 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 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 doesnt 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 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](https://pandas.pydata.org/docs/getting_started/install.html). Read about DateTime and how to install using pip [here](https://pypi.org/project/DateTime/). Read about the SignalWire Python SDK and how to install [here](/compatibility-api/sdks). ### 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. python 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, its 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. :::info Octal Literals in Some Python Versions For months Jan - September, A slight change was made because python version (3.9) does not support leading 0s 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. python 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. python 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 youd like. To see all of the parameters returned in the JSON response, you can view our API documentation here: [List Messages API](/rest/compatibility-api/endpoints/list-messages) Next, we will create a dataframe using pandas with column name headers. Its 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. python 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](/compatibility-api/sdks). 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 YOURSPACE.signalwire.com)); Next, we need to choose what parameters wed 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 dont hit any memory allocation errors. php $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. php $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! php 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](https://www.npmjs.com/package/danfojs) - [fs](https://nodejs.org/api/fs.html) - there is no need to install this module, as it is part of the Node.js Core - [SignalWire Rest Client](https://www.npmjs.com/package/@signalwire/compatibility-api) ### 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 javascript 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. javascript 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. javascript 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. javascript 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. javascript 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](https://github.com/signalwire-community/compatibility-api-java) Read on how to create a Maven project with Intellij [here](https://www.jetbrains.com/help/idea/delegate-build-and-run-actions-to-maven.html#maven_reimport) ** 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](/guides/navigating-your-space#api)! ### How to Run Snippet? Assuming youve 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 java public static List 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. java List 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 java 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. java try { createAndPopulateCSV(); } catch (IOException ioException) { ioException.printStackTrace(); } Full Java code: java 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 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 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 doesnt 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](https://rubygems.org/gems/signalwire) and [Dotenv](https://github.com/bkeepers/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. ruby 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 ruby 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. ruby 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 ruby 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, [create a SignalWire account and Space](https://m.signalwire.com/signups/new?s=1). Please feel free to reach out to us on our [Community Slack](https://signalwire.community/) or create a Support ticket if you need guidance!