Thursday, October 2, 2014

Messaging Framework To Send SMS Using TWILIO API


Introduction  
The article is about creating the SMS messaging framework to allow user to send SMS to their application subscribers using Twilio REST Api. It provides a lot boilerplate code for someone to quickly get the task done.

Background 
I got a task to develop a SMS messaging framework using TWILIO api. Most of the things at my end were quite easy to write however things related to TWILIO were missing in most of the forums I searched and there were days of interaction with TWILIO support member to get the resolutions. That is why I thought to write this article in order to save time for others.

Prerequisites 
This article requires knowledge about VS 2010, WCF REST Service and MSMQ. 
Let us start developing the SMS inbound and outbound messaging framework using the TWILIO API. First of all you need to buy an account with TWILIO or use Trial account. They will give you a number which will be used for sending and receiving the messages. Also, you will receive the AccountId and Auth token to be used to access the TWILIO API. 
OK, now so this framework contains the following components:
  1. Outbound Message queue to persist the messages sent from you application. 
  2. Windows service to poll the message queue and send the SMS to the interested parties.
  3. REST API to collect the response received to the SMS send from your application.
  4. Inbound Message queue to collect the response messages. 
  5. Windows service to process the inbound messages. 
Creating Queues  
I am assuming you have the queues installed on your machine otherwise you can install it from Windows Components. Create a queue in the following manner (on Windows 7 OS): 
     Go to Administrative Tools -> Computer Management -> Services and Applications -> Message Queuing -> Private Queues:



   If we enabled the Journaling then we can audit the messages. 




In the same way you can create the “smsinq” for collecting inbound SMS. Create a MessageDTO class with the desired properties and don’t forget to mark it Serializable.
[Serializable]
public class MessageDTO
{
   public MessageDTO(){}
   public Guid MessageGUID{ set; get; }
   public string To { set; get; } //Phone number to whom message need to send
   public string Subject { set; get; }
   public string Body { set; get; }
   public string From { set; get; } //Phone number from whom message need to send
   public DateTime CreatedOn { set; get; }
   public DateTime SentOn { set; get; }
}

Utility.cs 
Now you need the function that will push the messages in the outbound SMS queue (smsoutq). This function which can be put in Utility.cs class to be reusable, is quite easy and can be used for pushing both inbound and outbound messages in their respective queues as follows:
public class Utility
{
    private static string smsOutQueuePath = ".\private$\smsoutq";
    private static string smsInQueuePath = ".\private$\smsinq";
    
    private static Message SetMessageProperties(Message objMsg)
    {
           objMsg.Recoverable = true;
           objMsg.UseDeadLetterQueue = true;
           objMsg.UseJournalQueue = true; 
           objMsg.Formatter = new BinaryMessageFormatter();
           return objMsg;
       }
       
       public static MessageQueue GetMessageQueue(string queuePath)
       {
           MessageQueue msgQ = null;
           try
           {
               if(!MessageQueue.Exists(queuePath))
               {
                    msgQ = MessageQueue.Create(queuePath);
               }                
               msgQ = new MessageQueue(queuePath, QueueAccessMode.SendAndReceive);
               msgQ.Formatter = new BinaryMessageFormatter();
               msgQ.DefaultPropertiesToSend.UseJournalQueue = true;
               msgQ.DefaultPropertiesToSend.Recoverable = true; //Messages remain if server goes down
               msgQ.DefaultPropertiesToSend.UseDeadLetterQueue = true;
               msgQ.MessageReadPropertyFilter.SetAll();
           }
           catch (Exception ex)
           {
                     //your exception logging code
           }
           return msgQ;
       }
 
       public static bool PostToSMSQueue(MessageDTO objMsg, string direction = Constants.MESSAGE_DIRECTION_OUT)
       {
           bool success = false;
           try
           {
               Message objMessage = new Message();
                  objMessage = SetMessageProperties(objMessage);
                  objMessage.Body = objMsg;
                  MessageQueue msgQ = null;
                  
                  if(direction == Constants.MESSAGE_DIRECTION_OUT)
                      msgQ = GetMessageQueue(smsOutQueuePath);
                  else
                      msgQ = GetMessageQueue(smsInQueuePath);
                  msgQ.Send(objMessage);
        success = true;
           }
           catch (Exception ex)
           {
              //your exception logging code
           } 
 
           return success;
       }
       
       public static NameValueCollection ConvertUrlStringToNSCollection(string strUri)
       {
               NameValueCollection nsCol = new NameValueCollection();
               if(!String.IsNullOrEmpty(strUri))
               {
                   try
                   {
                       int endPos = 0;
                       string[] messageArray = null;
                       messageArray = strUri.Split('&');
                       for(int count = 0; count < messageArray.Length; count++)
                       {
                           endPos = messageArray[count].LastIndexOf('=');
                           nsCol.Add(messageArray[count].Substring(0, endPos),
                           messageArray[count].Substring(endPos + 1, messageArray[count].Length - (endPos + 1)));
                       }
                   }
                   catch(Exception ex)
                   {
                       throw ex;
                   }
               }
               return nsCol;
           }
} 
Creating OutboundMsgPoller Service to Send Message to TWILIO
Now you have to write the windows service (OutboundMsgPoller) which will read the messages to the desired parties at some defined time interval and pick the messages and sent them to TWILO using their API to be sent further. Note the code will be same for the ReponseProcessor (Windows service to process the received responses, read inline comments below), so you can reuse it. 
public partial class OutboundMsgPoller : ServiceBase
{
    private static string smsOutQueuePath = ".\private$\smsoutq";
    private static string accountSID = string.Empty;
    private static string authToken = string.Empty;
    private static string callbackURL = string.Empty;
    private Thread m_oPollingThread = null;
    private bool shallRun = false;  
 
    protected override void OnStart(string[] args)
    {
        try
        {
            this.EventLog.WriteEntry("OutboundMessagePoller service started successfully", EventLogEntryType.Information);
            shallRun = true; 
            if (m_oPollingThread == null)
                m_oPollingThread = new Thread(SendMessage);
        m_oPollingThread.IsBackground = false;
        m_oPollingThread.Start();
        }
        catch (Exception ex)
        {
            //your logging code
        }
    }
    
    protected override void OnStop()
    {
               this.EventLog.WriteEntry("OutboundMessagePoller service is OnStop.", EventLogEntryType.Information);
    }
 
    private void SendMessage()
    {
        IList lstMsg = new List();
        int pollInterval = 0; 
        MessageQueue queue = new MessageQueue(smsOutQueuePath, QueueAccessMode.SendAndReceive);
        accountSID = "Your Account ID"; 
        authToken = "Your Auth Token";
        callbackURL = string.Empty;//in case you want to collect the message status then you can specify a url for your application where TWILIO can POST the status. 
        
        do
        {
            try
            {
                using (MessageEnumerator messageEnumerator = queue.GetMessageEnumerator2())
                {
                    while (messageEnumerator.MoveNext(new TimeSpan(0, 0, 1)))
                    {
                        Message msg = queue.ReceiveById(messageEnumerator.Current.Id, new TimeSpan(0, 0, 1));
                        lstMsg.Add(msg);
                    }
                }
                queue.Close();
                
                if (lstMsg != null && lstMsg.Count > 0)
               {
                //for OutboundMsgPoller service
                this.SendMessageToTwilio(lstMsg, accountSID, authToken, callbackURL);
                //for ReponseProcessor service, you have to write your own method
                YourMethod(lstMsg);
            }
            lstMsg.Clear();
            //Wait... 
            {
                pollInterval = 30000; //time in milliseconds, can be made configurable
            }
            Thread.Sleep(pollInterval);
        }
        catch (Exception ex)
        {
            shallRun = false;
            //your logging code
        }
        } while (shallRun);
    } 
 
    private void SendMessageToTwilio(IList objMessage, string accountSID, string authToken, string callbackURL)
    {
        try
        {
            if (objMessage != null && objMessage.Count > 0)
            {
                // instantiate a new Twilio Rest Client
                var client = new TwilioRestClient(accountSID, authToken); 
                // iterate over message
                foreach (Message ms in objMessage)
                {
                    ms.Formatter = new BinaryMessageFormatter();//this is important
                    if (ms.Body != null)
                    {
                        var objTw = client.SendSmsMessage(((MessageDTO)ms.Body).From, ((MessageDTO)ms.Body).To, ((MessageDTO)ms.Body).Body, callbackURL);
                            if (objTw.RestException != null)// this is important
                            {
                                throw new Exception("SendMessageToTwilio - Twilio Error Code - " + objTw.RestException.Code + "|| Error Message - " + objTw.RestException.Message);
                            }
                            else
                            {
                                //Update the SMSID received from TWILIO in your DB or other appropriate repository.
                            }
                        }
                    }
                }
        }
        catch (Exception ex)
        {
             //your logging code
        }
    }
}  
Creating REST Service to Collect TWILIO Responses
In case you don’t have please install the install the WCF REST template from given link: http://visualstudiogallery.msdn.microsoft.com/fbc7e5c1-a0d2-41bd-9d7b-e54c845394cd
Once you install it, create a new project using this template as follows:

TWILIO Response Class
You will be needing to create a Twilio Response class as follows: 
[Serializable]// this is important
public class TwilioRespDTO 
{   
    public TwilioRespDTO(){}
    public string SmsSid { get; set; }
    public string AccountSid { get; set; }
    public string From { get; set; }
    public string To { get; set; }
    public string Body { get; set; }
} 
Response Collection REST API
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
// NOTE: If the service is renamed, remember to update the global.asax.cs file
public class Responder
{    
    [WebInvoke(UriTemplate = "response/sms", Method = "POST")] 
    //IMPORTANT: response/sms is the part of the uri you will register with Twilio account for getting responses.
    public void GetTwilioSMSResponse(Stream sr)
    {
        if (sr != null)
        {
            try
            {
                string postData = string.Empty;
                NameValueCollection nsCol = new NameValueCollection();  
 
                using (StreamReader srt = new StreamReader(sr))
                {
                    postData = srt.ReadToEnd();
                }
 
                nsCol = Utility.ConvertUrlStringToNSCollection(postData); 
 
                if (nsCol != null && nsCol.Count > 0)
                {
                    TwilioResponseDTO objTw = new TwilioResponseDTO();
                    objTw = CreateTwilioResponseObject(nsCol);
                    Utility.PostToSMSQueue(objResp, Constants.MESSAGE_DIRECTION_IN);
                }
            }
            catch (Exception ex)
            {
               //your error handling code here
            }
        }
    } 
 
    private TwilioResponseDTO CreateTwilioResponseObject(NameValueCollection nsCol)
    {
        MethodInfo objMethod = (MethodInfo)MethodBase.GetCurrentMethod();
        TwilioResponseDTO objTw = new TwilioResponseDTO();
        objTw.SmsSid = nsCol.Get("SmsId");
        objTw.AccountSid = nsCol.Get("AccountId");
        objTw.From = HttpUtility.UrlDecode(nsCol.Get("From"));
        objTw.To = HttpUtility.UrlDecode(nsCol.Get("To"));
        objTw.Body = nsCol.Get("Body");
        return objTw;
    }   
}            
Conclusion
I hope you enjoyed reading about this article as much as I enjoyed writing it. I also hope that it will relieve you from writing boilerplate code and allow you to concentrate on the task at hand instead. Feel free to borrow the code and use it for your needs.

Wednesday, January 8, 2014

Some Important Utility Functions in C#

//To get the UnixTimeMilliseconds
private static string GetUnixTimeMilliseconds()
{
 TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc));
 long timestamp = (long)t.TotalMilliseconds;
 return timestamp.ToString();
}
 
//To get the EasternTime
private static string GetEasternTime()
{ 
 string timestamp = string.Empty;
 var zone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
 var utcNow = DateTime.UtcNow;
 var easternNow = TimeZoneInfo.ConvertTimeFromUtc(utcNow, zone);
 timestamp = easternNow.ToString("yyyy-MM-dd HH':'mm':'ss");
 return timestamp;
}

Wednesday, November 27, 2013

Getting All Responses From TWILIO API

Hi Friends,

I got a requirement where I have to retrieve all the messages I sent using Twilio number in one day. This was like any other simple requirement and can be easily achieved through exporting the records in .CSV format as given in Twilio Dashboard. The twist is that Twilio dashboard supports only to fetch 10000 records(however in my case it was not exporting more than 5000 records out of some odd 8000 records and putting error message "Could not finish exporting all records." in the exported .CSV file).


This is the story about when you have to fetch within 10000 records. In case you want to fetch records more than that then following is the code for your rescue which I figured out after hit and trial as Twilio support was also much not helpful:

This is how I got it working:

For Collecting Responses to Twilio Numbers
TwilioRestClient client = new TwilioRestClient(accountSID, authToken);
for (int count = 0; count <= 3; count++)
{
     var smss = client.ListSmsMessages("Your number to which you received messages", null, null, count, 1000);
     foreach (var sms in smss.SMSMessages)
     {
         //Saving in DB
         SaveToDB(sms.Sid, sms.From, sms.To, sms.DateSent, sms.Body);
     }
}


For Collecting Sent Message From Twilio Numbers
TwilioRestClient client = new TwilioRestClient(accountSID, authToken);
for (int count = 0; count <= 3; count++)
{
     var smss = client.ListSmsMessages(null, "Your number through which you sent messages", null, count, 1000);
     foreach (var sms in smss.SMSMessages)
     {
         //Saving in DB
         SaveToDB(sms.Sid, sms.From, sms.To, sms.DateSent, sms.Body);
     }
}
*count is the number of pages you are expecting which can be calculated by dividing the no. of messages by 1000(max records supported by API in one request)

Monday, October 28, 2013

Listing all tables in PostGreSQL DB

You should be able to just run
select * from information_schema.tables
to get a listing of every table being managed by Postgres for a particular database.

You can also add a where table_schema = 'information_schema' to see just the tables in the information schema.

This is another version to get the specific results:

SELECT *
FROM information_schema.tables
WHERE table_schema='public'
AND table_type='BASE TABLE';