SMS election software system, Haiti Elections

Authors: Dave Wade of Votident, Bart Jonkman of ActiveXperts Software B.V.

Content:
  1. Background
  2. Problem Statement
  3. Goals of the new System
  4. ActiveXperts SMS Messaging Server Solution

1. Background

On April 19 2009, senate elections for a third of the seats in the Senate of Haiti were held. The senate elections were originally planned for March 2008, but were postponed. Voters should vote for the renewal of 33% (ten of the thirty seats) in the Haitian Senate. This is according the Constitution of Haiti.
The government of Haiti is a semi-presidential (presidential-parliamentary) republic, where the president and the prime minister are both active participants in the day-to-day administration of the state.

2. Problem Statement

The people in Haiti did not know where to vote (which location) for the senate elections. The Haitian CEP (Provisional Election Council) were looking for an information source that could tell each voter where to vote.

3. Goals of the new System

Goals of the new system:

With an SMS based solution:

4. Votident / ActiveXperts SMS Messaging Server Solution

The Votident organization is specialized in assisting election commissions in informing voter of the polling locations, updating addresses and registering new voters. ActiveXperts are developers of the ActiveXperts SMS Messaging Server software and experienced in implementing SMS projects, connecting to SMPP providers, etc.

Votident selected '200' and the central SMS number for Haitians to send their SMS to. Votident applied for this '200' number with Digicel (70% of the market in Haiti), Comcel (20%) and Clickatell (10%). A connection is established between ActiveXperts SMS Messaging Server and those three providers (either through a VPN or directly through the internet); therefore, 3 different SMPP channels were configured. The Haitian CEP (Provisional Election Council) handed a database with all 3,4 million potential voters in Haiti.

The SMS procedure is like this:

An 'success' reply sample:

Wap vote nan 
Ecole des Soeurs Bel Air Miragoane
VILLE
MIRAGOANE, NIPPE
BV #9

An 'error' reply sample:

49834
Nimewo sa a pa bon.
Se nimewo a selman pou antre.

5. Source code

The source code of the ActiveXperts SMS Messaging Server (written by Votident):

' // BEGIN CONSTANTS AND DECLARATIONS
CONST STR_DEBUGFILE       = "C:\HaitiResponder.txt"
CONST STR_SQLCONNSTRING   = "Provider=SQLNCLI10;Server=(local);Database=CEPSMS;Uid=sa;Pwd=******"
CONST B_LIVESEND          = True 'Set to false to not actully send SMS messages but still log them.
Dim g_objMessageDB, g_objDebugger, g_objConstants
Dim g_strSmsResponseTemplate, g_strSmsFailResponse
' // END CONSTANTS AND DECLARATIONS

' // BEGIN INITIALIZATION
Set g_objConstants        = CreateObject("Axsms-messaging-server.Constants")
Set g_objMessageDB        = CreateObject("Axsms-messaging-server.MessageDB") 
Set g_objDebugger         = CreateObject("ActiveXperts.VbDebugger")
g_strSmsResponseTemplate  = "Wap vote nan " & vbCRLF & "{0}" &  vbCRLF & "{4}" & vbCRLF & "{1}, {2}" & vbCRLF & "BV #{3}"
g_strSmsFailResponse      = "{0}" & vbCRLF & "Nimewo sa a pa bon." & vbCRLF & "Se nimewo a selman pou antre."
g_objDebugger.DebugFile   = STR_DEBUGFILE
g_objDebugger.Enabled	    = False
' // END INITIALIZATION

' //////////////////////////////////////////////////////////////////////////////////////////////////////

Function ProcessMessage (numMessageID)
  Dim objMessageIn, objMessageOut
  
  g_objDebugger.WriteLine ">> ProcessMessage"
  
  g_objMessageDB.Open
  If( g_objMessageDB.LastError <> 0 ) Then
    g_objDebugger.WriteLine "<< ProcessMessage,  unable to open database"
    Exit Function
  End If
  Set objMessageIn    = g_objMessageDB.FindFirstMessage ( "ID = " & numMessageID ) 
  If g_objMessageDB.LastError <> 0 Then
    g_objMessageDB.Close
    g_objDebugger.WriteLine "<< ProcessMessage,  FindFirstMessage failed, error: [" & g_objMessageDB.LastError & "]"
    Exit Function
  End If

  ' Change Status to from Pending to Success. If you don't do it, the message will be processed by subsequent triggers (if defined) because message is still pending
  objMessageIn.Status = g_objConstants.MESSAGESTATUS_SUCCESS
  g_objMessageDB.Save objMessageIn
  g_objDebugger.WriteLine "Incoming message saved, result: [" & g_objMessageDB.LastError & "]"
  
  ProcessQuery ( objMessageIn )
  
  g_objMessageDB.Close
  
  g_objDebugger.WriteLine "<< ProcessMessage"
End Function

' //////////////////////////////////////////////////////////////////////////////////////////////////////

Function ProcessQuery ( objMessageIn )
  Dim objMessageOut, curCurrentPrice, strReplymessage, voterId
  
  g_objDebugger.WriteLine ">> ProcessQuery"

  If ( CleanVoterId( objMessageIn.Body, voterId ) = False ) Then
    HandleBadIncomingMessage objMessageIn, ""
  Else
    HandleIncomingMessage objMessageIn, voterId
  End If
  
  g_objDebugger.WriteLine "<< ProcessQuery"  
  
End Function

' //////////////////////////////////////////////////////////////////////////////////////////////////////

Function HandleBadIncomingMessage(objMessageIn, voterId)

  g_objDebugger.WriteLine ">> ProcessQuery"
  
	SendMessage objMessageIn.FromAddress, objMessageIn.ChannelID, objMessageIn.BodyFormat, Replace( g_strSmsFailResponse, "{0}", voterId )
  
  g_objDebugger.WriteLine "<< ProcessQuery"  
End Function

' //////////////////////////////////////////////////////////////////////////////////////////////////////

Function HandleIncomingMessage(objMessageIn, voterId)
  Dim responseSms, FoundNumber
  FoundNumber = GetResponseSms(voterId, responseSms)
  If Not (FoundNumber) Then
    HandleBadIncomingMessage objMessageIn, voterId
    Exit Function
  End If
  g_objDebugger.WriteLine responseSms
  SendMessage objMessageIn.FromAddress, objMessageIn.ChannelID, objMessageIn.BodyFormat, responseSms
End Function

' //////////////////////////////////////////////////////////////////////////////////////////////////////

Sub SendMessage( strFromAddress, numChannelID, numBodyFormat, strBody )
  Dim objMessageOut

  g_objDebugger.WriteLine ">> Sending Message to: " & strFromAddress & " Body: " & strBody

  If( B_LIVESEND ) Then  
    Set objMessageOut = g_objMessageDB.Create   
    If( g_objMessageDB.LastError = 0 ) Then
      objMessageOut.Direction = g_objConstants.MESSAGEDIRECTION_OUT
      objMessageOut.Type		  = g_objConstants.MESSAGETYPE_SMS 
      objMessageOut.Status	  = g_objConstants.MESSAGESTATUS_PENDING
      objMessageOut.ToAddress = strFromAddress
      objMessageOut.ChannelID	= numChannelID
      objMessageOut.BodyFormat= numBodyFormat
      objMessageOut.Body		  = strBody

      g_objMessageDB.Save objMessageOut
    End If
  End If
  
End Sub

' //////////////////////////////////////////////////////////////////////////////////////////////////////

Function GetResponseSms(voterId, ByRef responseSms)

  g_objDebugger.WriteLine ">> GetResponseSms"

  Dim objConn, RS, strQuery

  responseSms = g_strSmsFailResponse
  GetResponseSms = False
  Set objConn = CreateObject("ADODB.Connection")
  objConn.Open STR_SQLCONNSTRING
  strQuery = "SELECT * FROM VOTERS WHERE ID_CITOYEN = '" & voterId & "'"
  Set RS = objConn.Execute(strQuery)
  If(RS.EOF = False) Then
    'Yuck. VBS doesn't have good support for this.
    responseSms = Replace(Replace(Replace(Replace(Replace(g_strSmsResponseTemplate, "{0}", RS("CV")), "{1}", RS("COMMUNE")), "{2}", RS("DEPARTEMENT")), "{3}", RS("BUREAUVOTE")), "{4}", RS("ADRESSE"))
    GetResponseSms = True
  Else
    GetResponseSms = False
  End If

  objConn.Close

  g_objDebugger.WriteLine "<< GetResponseSms"  
End Function

' //////////////////////////////////////////////////////////////////////////////////////////////////////

Function CleanVoterId(messageBody, ByRef voterId)
  Dim myRegExp
  Set myRegExp = New RegExp
  myRegExp.IgnoreCase = True
  myRegExp.Global     = True
  myRegExp.Pattern    = "[^\d]"
  voterId = myRegExp.Replace( messageBody, "" )
  If( Len(voterId) < 1 ) Then
    CleanVoterId = False
  Else
    CleanVoterId = True
  End If
End Function