I have started using ModHarbour but so far not so much to build html pages but to trap webhooks. A webhook is a POST request sent to a page from services to notify or transmit certain data.
There is something I still can't figure out about Modharbour. It is the use of other functions and classes on other source files from my Index.prg source.
For example, my code below receives POST data from a service call Twilio. Anytime someone texts my Twilio phone number, this code receives and processes POST data from Twilio that contain all the SMS text information.
Notice how on this code I'm using functions like Connect2DD() and LogData() and ExecuteSQL(). These are my own functions. In order to be able to use them I find I have to include all their source into Index.prg. I want to avoid that.
Is there a better way to link other code, classes, and functions and use them on my Index.prg? Can I use my own created classes like TAdsQuery() that contain many data and methods without having to add all this code into Index.prg?
- Code: Select all Expand view
#include "\harbour\include\fileio.ch"
#include "\harbour\include\ads.ch"
#define CRLF Chr(13)+Chr(10)
#define MAX_LOG_SIZE 65536
#define LOG_DEBUG .T.
#define DEBUGFILE "trace.log"
function Main()
Local h := AP_PostPairs()
Local i, cTel, cConfirmCode
Local cSql, lConfirmed
LogData( DEBUGFILE, { "Twilio Page hit with", AP_Method() }, MAX_LOG_SIZE )
For i := 1 To LEN( h )
LogData( DEBUGFILE, HGetPairAt( h, i ), MAX_LOG_SIZE )
Next
if AP_Method() == "POST" .AND. ;
hHasKey( h, "MessageSid") .AND. ;
hHasKey( h, "AccountSid") .AND. ;
hHasKey( h, "From") .AND. ;
hHasKey( h, "Body") .AND. ;
;//At( ALLTRIM ( h[ "Body "] ), [1,2] ) > 0 .AND. ;
( AdsGetConnectionType() != AE_NO_CONNECTION .OR. Connect2DD() )
cConfirmCode := AllTrim( hb_ValToStr( Val( Left( AllTrim( h[ "Body" ] ), 1 ) ) + 1 ) )
cTel := StrTran( AllTrim( h[ 'From'] ), "+1", "" ) //remove +1 from tel numb
lConfirmed := isConfirmed( cTel )
//we need to first check if appntment hasnt been confirmed already.
//patients sometimes send a 1 and then follow with more texts.
//if appntmnet is already confirmed, should we ignore further replies...?
//+1 was added to cConfirmCode
if cConfirmCode != '2' .AND. cConfirmCode != '3' .and. !lconfirmed
cSql := "INSERT INTO SMSMessages( celnumber, Text ) Values( '$1$', '$2$' ) \n"
ExecuteSQL( cSql, ADS_ADT, { cTel, "Este sistema automatizado solo entiende 1 para confirmar o 2 para cancelar su cita. Para preguntas marque 787-751-1487" } , LOG_DEBUG )
elseif lConfirmed //once confirmed stop texting me! call the office insetad.
cSql := "INSERT INTO SMSMessages( celnumber, Text ) Values( '$1$', '$2$' ) \n"
ExecuteSQL( cSql, ADS_ADT, { cTel, "Este sistema automatizado ya tiene su cita confirmada o cancelada previamente. Para preguntas marque 787-751-1487" } , LOG_DEBUG )
else //it hasn't been confirmed and a 1 or a 2 was received as expected.
cSql := ;
"DECLARE tbl CURSOR ; \n"+;
"OPEN tbl AS SELECT Top 1 guid, service, mrec, StartTime \n"+;
" FROM $3$ \n"+;
" WHERE tel = '$2$' \n"+;
" AND starttime > Now() \n"+;
" AND ( confirmed is null or confirmed= 1 or confirmed > 3 )\n"+;
" ORDER BY StartTime ; \n"+;
" \n"+;
"IF FETCH tbl then \n"+;
" \n"+;
" UPDATE $3$ \n"+;
" SET confirmed = $1$ \n"+;
" WHERE tel = '$2$' \n"+;
" AND CONVERT( starttime, SQL_DATE ) = convert( tbl.starttime, SQL_DATE );\n"+;
" \n"+;
"end; \n"+;
"CLOSE tbl ; \n"
ExecuteSQL( cSql, ADS_ADT, { cConfirmCode, cTel,'sfi_msk_appntmnts' }, LOG_DEBUG )
ExecuteSQL( cSql, ADS_ADT, { cConfirmCode, cTel,'mo_appntmnts' }, LOG_DEBUG )
ExecuteSQL( cSql, ADS_ADT, { cConfirmCode, cTel,'appntmnts' }, LOG_DEBUG )
ExecuteSQL( cSql, ADS_ADT, { cConfirmCode, cTel,'degetau_appntmnts' }, LOG_DEBUG )
if cConfirmCode = '2' //.and. !isConfirmed?
cSql := "INSERT INTO SMSMessages( celnumber, Text ) Values( '$1$', '$2$' ) \n"
ExecuteSQL( cSql, ADS_ADT, { cTel, "Es importante llegar 15 minutos antes de la hora de su cita." } , LOG_DEBUG )
endif
endif
AdsDisconnect()
endif
return nil