I ended up writing a windows service with Harbour. It runs 24/7 sending messages. To receive messages back, I wrote a short modharbour script that catches POST messages, which is how you can catch text messages sent to your number.
I'm using Twilio.com to send and receive text messages. I'm consuming about 300 outbound MSMs and about 150 inbound per day. I'm paying about $50 per month. The cost depends on the number of messages sent and received.
Twilio works worldwide and so far, it is working great for me.
And now that I see Otto's message here, I'm reminded I promised to share the code. Please send back any improvements you make on it. TY.
/*----------------------------------------------------------------------------
Faxes with Twilio
<!-- m --><a class="postlink" href="https://www.twilio.com/docs/fax/receive#receiving-a-fax-with-twiml">https://www.twilio.com/docs/fax/receive ... with-twiml</a><!-- m -->
Sending emails using Twilio SendGrid
<!-- m --><a class="postlink" href="https://sendgrid.com/docs/for-developers/sending-email/api-getting-started/">https://sendgrid.com/docs/for-developer ... g-started/</a><!-- m -->
----------------------------------------------------------------------------*/
#INCLUDE "hbClass.ch"
#include "hbcurl.ch"
#define ACCOUNT_SID "xxxxxxxxxx$#B<SHJGHKxxxx"
#define AUTH_TOKEN "x@#$F&^%DEE"
#define TEL_FROM "555-555-55555"
CLASS TTwilioSMS
DATA hCurl
DATA httpcode
DATA cLogFile INIT "Twilio.log"
DATA cResponse
DATA cDestinationNum
DATA cSMSText
DATA cUrl INIT "https://api.twilio.com/2010-04-01/Accounts/" + ACCOUNT_SID + "/Messages.json"
DATA cVoiceUrl INIT "https://api.twilio.com/2010-04-01/Accounts/" + ACCOUNT_SID + "/Calls.json"
DATA nError INIT 0
DATA nMaxLogSize INIT 32768
DATA cDateStart, cDateEnd
DATA cvoiceMail, cReplyEmail
DATA lDebug INIT .F.
METHOD New()
METHOD End()
METHOD Send()
METHOD Reset()
METHOD MakePhoneCall()
METHOD GetMessagesLog()
ENDCLASS
//------------------------------------------------------------------------------------------------
METHOD New() CLASS TTwilioSMS
::hCurl := curl_easy_init()
RETURN Self
//------------------------------------------------------------------------------------------------
METHOD Send() CLASS TTwilioSMS
Local cPostFields
Local httpcode
curl_easy_setopt( ::hCurl, HB_CURLOPT_POST, 1 )
curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, ::cUrl )
curl_easy_setopt( ::hCurl, HB_CURLOPT_USERPWD, ACCOUNT_SID + ':' + AUTH_TOKEN )
curl_easy_setopt( ::hCurl, HB_CURLOPT_DL_BUFF_SETUP )
curl_easy_setopt( ::hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F. )
cPostFields := 'To=' + ::cDestinationNum + ; // Line[ 3 ] +;
'&From=' + TEL_FROM +;
'&Body=' + curl_easy_escape( ::hCurl, AllTrim( ::cSMSText ) )
If ::lDebug
LogFile( ::cLogFile, { cPostFields } )
endif
curl_easy_setopt( ::hcurl, HB_CURLOPT_POSTFIELDS, cPostFields )
::nError := curl_easy_perform( ::hCurl )
curl_easy_getinfo( ::hCurl, HB_CURLINFO_RESPONSE_CODE, @httpcode )
::httpcode := httpcode
IF ::nError = HB_CURLE_OK
::cResponse = curl_easy_dl_buff_get( ::hCurl )
Else
LogData( ::cLogFile, { "Twilio error sending SMS. Details below:" }, ::nMaxLogSize )
LogData( ::cLogFile, { "To", ::cDestinationNum, "SMS Text:", ::cSMSText }, ::nMaxLogSize )
LogData( ::cLogFile, { "Error Num:", ::nError, "Httpcode:", ::httpcode }, ::nMaxLogSize )
LogData( ::cLogFile, curl_easy_strerror( ::nError ), ::nMaxLogSize )
ENDIF
return NIL
//------------------------------------------------------------------------------------------------
METHOD Reset() CLASS TTwilioSMS
curl_easy_reset( ::hCurl )
return NIL
//------------------------------------------------------------------------------------------------
METHOD End() CLASS TTwilioSMS
curl_easy_cleanup( ::hCurl )
::hCurl := Nil
hb_gcAll( .t. )
return NIL
//------------------------------------------------------------------------------------------------
//must define ::cDateStart and ::cDateEnd before calling this method.
METHOD GetMessagesLog() CLASS TTwilioSMS
Local cparms := '?DateSent%3E=' + ::cDateStart + "&DateSent%3C=" + ::cDateEnd + "&PageSize=600"
Local httpcode
curl_easy_setopt( ::hCurl, HB_CURLOPT_HTTPGET, 1 )
curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, ::cUrl + cParms )
curl_easy_setopt( ::hCurl, HB_CURLOPT_USERPWD, ACCOUNT_SID + ':' + AUTH_TOKEN )
curl_easy_setopt( ::hCurl, HB_CURLOPT_DL_BUFF_SETUP )
curl_easy_setopt( ::hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F. )
::nError := curl_easy_perform( ::hCurl )
curl_easy_getinfo( ::hCurl, HB_CURLINFO_RESPONSE_CODE, @httpcode )
::httpcode := httpcode
IF ::nError = HB_CURLE_OK
::cResponse = curl_easy_dl_buff_get( ::hCurl )
::cResponse := StrTran( ::cResponse, Chr(10), "" )
Else
LogData( ::cLogFile, { "Twilio error sending SMS. Details below:" }, ::nMaxLogSize )
LogData( ::cLogFile, { "To", ::cDestinationNum, "SMS Text:", ::cSMSText }, ::nMaxLogSize )
LogData( ::cLogFile, { "Error Num:", ::nError, "Httpcode:", ::httpcode }, ::nMaxLogSize )
LogData( ::cLogFile, curl_easy_strerror( ::nError ), ::nMaxLogSize )
ENDIF
//MemoWrit( "Twilio.log", ::cResponse )
RETURN NIL
//------------------------------------------------------------------------------------------------
/*sample makeing a call using url to fetch parameters
<!-- m --><a class="postlink" href="https://www.twilio.com/docs/voice/api/call-resource">https://www.twilio.com/docs/voice/api/call-resource</a><!-- m -->
EXCLAMATION_MARK='!'
curl -X POST <!-- m --><a class="postlink" href="https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Calls.json">https://api.twilio.com/2010-04-01/Accou ... Calls.json</a><!-- m --> \
--data-urlencode "Twiml=<Response><Say>Ahoy there$EXCLAMATION_MARK</Say></Response>" \
--data-urlencode "To=+15558675310" \
--data-urlencode "From=+15552223214" \
-u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN
*/
//Twilml sample parameter "Twiml=<Response><Say>Ahoy there$EXCLAMATION_MARK</Say></Response>"
METHOD MakePhoneCall() CLASS TTwilioSMS
Local cPostFields
Local httpcode
curl_easy_setopt( ::hCurl, HB_CURLOPT_POST, 1 )
curl_easy_setopt( ::hCurl, HB_CURLOPT_URL, ::cVoiceUrl )
curl_easy_setopt( ::hCurl, HB_CURLOPT_USERPWD, ACCOUNT_SID + ':' + AUTH_TOKEN )
curl_easy_setopt( ::hCurl, HB_CURLOPT_DL_BUFF_SETUP )
curl_easy_setopt( ::hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F. )
cPostFields := 'To=' + ::cDestinationNum + ; // Line[ 3 ] +;
'&From=' + TEL_FROM +;
'&CallReason=' + "medical appointment reminder" +;
'&Twiml=<Say voice="alice" language="es-MX">' + ::cVoiceMail + '</Say>' +;
'&CallerId=' + ::cReplyEmail
curl_easy_setopt( ::hcurl, HB_CURLOPT_POSTFIELDS, cPostFields )
::nError := curl_easy_perform( ::hCurl )
curl_easy_getinfo( ::hCurl, HB_CURLINFO_RESPONSE_CODE, @httpcode )
::httpcode := httpcode
IF ::nError = HB_CURLE_OK
::cResponse = curl_easy_dl_buff_get( ::hCurl )
Else
LogData( ::cLogFile, { "Twilio error making voice call. Details below:" }, ::nMaxLogSize )
LogData( ::cLogFile, { "To", ::cDestinationNum, "Message:", ::cVoiceMail }, ::nMaxLogSize )
LogData( ::cLogFile, { "Error Num:", ::nError, "Httpcode:", ::httpcode }, ::nMaxLogSize )
LogData( ::cLogFile, curl_easy_strerror( ::nError ), ::nMaxLogSize )
ENDIF
RETURN NIL