*-------------------------------------------------------------------------------------------------------------------------------------------------- *Program Listing for: bot_process.prg *Project: webbot *Namespace: foxpro *---------------------------------------------------------------------------------------------------------------------------------------------------- ***************************************************************************************************************************** ***************************************************************************************************************************** ***** bot_process.prg ***** processing routines for autobot project ***** Scott Laing ***************************************************************************************************************************** ***************************************************************************************************************************** #include "foxpro.h" ***************************************************************************************************************************** function nothing() ***************************************************************************************************************************** if MB2("Cancel process?", MB_YESNO) = IDYES lHitCancel = .T. *goBotApp.lCancel = .T. endif return ***************************************************************************************************************************** function process_day( tnDOW, toListBox, tlManual, toMainForm ) ***************************************************************************************************************************** goBotApp.oMainForm = toMainForm * nrec is private it keeps track of record number for wait window private nrec private npage private critical_stop critical_stop = .F. goBotApp.lCancel = .F. goBotApp.lHitCancel = .F. lHitCancel = .F. if tlManual select * from dayrun where ddate = date() into cursor day_chk if recc() > 0 if MB2("Process has run for today already, run again?", MB_YESNO) != IDYES use in day_chk return endif endif endif use in day_chk * all makes supported by the site as of the writing of this dime amakes[47] amakes[1]="ACURA" amakes[2]="ALFA" amakes[3]="AMC" amakes[4]="AMGEN" amakes[5]="AUDI" amakes[6]="BMW" amakes[7]="BUICK" amakes[8]="CAD" amakes[9]="CHEV" amakes[10]="CHRY" amakes[11]="DAEW" amakes[12]="DAIHAT" amakes[13]="DATSUN" amakes[14]="DODGE" amakes[15]="EAGLE" amakes[16]="FORD" amakes[17]="GEO" amakes[18]="GMC" amakes[19]="HONDA" amakes[20]="HYUND" amakes[21]="INFIN" amakes[22]="ISU" amakes[23]="JAG" amakes[24]="JEEP" amakes[25]="KIA" amakes[26]="ROV" amakes[27]="LEXUS" amakes[28]="LINC" amakes[29]="MAZDA" amakes[30]="MB" amakes[31]="MERC" amakes[32]="MERKUR" amakes[33]="MIT" amakes[34]="NISSAN" amakes[35]="OLDS" amakes[36]="PLYM" amakes[37]="PONT" amakes[38]="POR" amakes[39]="SAAB" amakes[40]="SATURN" amakes[41]="SUB" amakes[42]="SUZUKI" amakes[43]="TOYOTA" amakes[44]="trI" amakes[45]="VOLKS" amakes[46] ="VOLVO" amakes[47]="YUGO" if ! used("note_text") use note_text in 0 endif select note_text go top if empty(note_text.mtext) MB2("No email message in the note_text data file!") return endif private gcNoteText local ctemp ctemp = note_text.mtext gcNoteText = cgi_ify(ctemp) if empty(gcNoteText) MB2("email note text appears empty after cgi translation - possible bug - cannot proceed") return else *MB2("note text / cgi-encoded:" + chr(13) + gcNoteText ) endif *So, the locations we need to search for emails right now are: *1. Arizona.. I do the entire state, makes it kinda easy *2. Garden Grove, CA 92841... Ive been doing a 75 mile radius of that zip *3. Las Vegas, NV 89119 75 mile radius *4. Reno, NV 89431 75 mile radius *5. Tulsa, OK 74112 100 mile radius local n, n1, n2 select * from runinfo where ndow = tnDOW order by norder into cursor run_this * if no zip records for this date then just return here, this day isn't really "processed" since no records exist for * this date, so don't get new batch_id or insert record into dayrun, this way if they add a new record to this * day, a new zipcode record, it will continue to attempt to run and then, run by hitting this record if recc() = 0 use in run_this if tlManual MB2("No zip's seem to be defined for this day - nothing run") endif return endif goBotApp.nbatch_id = nextkey( "BATCH_ID") if ! used("batches") use batches in 0 endif select batches append blank repl batch_id with goBotApp.nbatch_id repl truntime with datetime() repl drundate with date() repl ndow with tnDow repl lstart with .T. goBotApp.nnew_phones = 0 goBotApp.nemails_sent = 0 goBotApp.nold_phones = 0 goBotApp.nemails_skpd = 0 goBotApp.nspam_skip = 0 if ! used("dayrun") use dayrun in 0 endif sele dayrun append blank repl ddate with date() repl lstarted with .T. repl tstart with datetime() repl batch_id with goBotApp.nbatch_id repl dayrun_id with nextkey("DAYRUN_ID") use in dayrun * open phone_contacts if ! used("phone_contacts") use phone_contacts in 0 endif select phone_contacts set order to tag cphone if ! used("phone_link") use phone_link in 0 endif goBotApp.nday = tnDow select run_this scan if type("toListBox") = "O" toListBox.additem("start run for zip:" + run_this.czip + " " + ttoc( datetime() ) ) endif if val(run_this.czip ) = 0 MB2("A valid record for this day of the week had an empty zip, it will be skipped, please delete this record or fix") select run_this loop endif if val(run_this.cdistance) = 0 MB2("A valid record for this day of the week had an empty distance (of zero), it will be skipped, please delete this record or fix") select run_this loop endif n1 = seconds() goBotApp.nGrpLastEmailCnt = 0 goBotApp.nGrpLastNonEmailCnt = 0 goBotApp.nGrpRecentMailSkip = 0 for n = 1 to alen(amakes) if type("toListBox") = "O" toListBox.additem("processing:" + run_this.czip + ":" + amakes[n] ) endif goBotApp.nLastEmailCnt = 0 goBotApp.nLastNonEmailCnt = 0 goBotApp.nRecentMailSkip = 0 if type("goBotApp.oMainForm") = "O" goBotApp.oMainForm.txtDetails.value = "beginning processing of make: " + amakes[n] goBotApp.oMainForm.refresh() endif * do the actual search now SearchInfo( amakes[n], 1999, 2002, val(run_this.czip), val(run_this.cdistance) ) if goBotApp.lCancel exit endif if type("toListBox") = "O" toListBox.additem("emails sent:" + ltrim(str(goBotApp.nLastEmailCnt )) ) endif if type("toListBox") = "O" toListBox.additem("recent mail skips:" + ltrim(str( goBotApp.nRecentMailSkip )) ) endif if type("toListBox") = "O" toListBox.additem("non-email contacts:" + ltrim(str(goBotApp.nLastNonEmailCnt )) ) endif toListBox.listindex = toListBox.listcount toListBox.parent.refresh() next if goBotApp.lCancel if type("toListBox") = "O" toListBox.additem("process aborted by user" ) endif endif n2 = seconds() if type("toListBox") = "O" if ! goBotApp.lCancel toListBox.additem("finished run for " + run_this.czip ) endif if type("toListBox") = "O" toListBox.additem("emails sent:" + ltrim(str(goBotApp.nGrpLastEmailCnt )) ) endif if type("toListBox") = "O" toListBox.additem("recent mail skips:" + ltrim(str( goBotApp.nGrpRecentMailSkip )) ) endif if type("toListBox") = "O" toListBox.additem("non-email contacts:" + ltrim(str(goBotApp.nGrpLastNonEmailCnt )) ) endif if type("toListBox") = "O" toListBox.additem("mins for run: " + run_this.czip + ":" + str( (n2 - n1)/60 ) ) endif endif if goBotApp.lCancel exit endif select run_this endscan use in run_this update batches set lfinish = ! goBotApp.lCancel, ; labort = goBotApp.lCancel, ; nemails_sent = goBotApp.nemails_sent, ; nnew_phones = goBotApp.nnew_phones, ; nold_phones = goBotApp.nold_phones , ; nemails_skpd = goBotApp.nemails_skpd , ; nspam_skip = goBotApp.nspam_skip ; where batch_id = goBotApp.nbatch_id if _tally = 0 MB2("trouble updating finish flag in batches table") endif wait clear *select maininfo *browse nowait return ***************************************************************************************************************************** function SearchInfo( tcMake, tnStartYr, tnEndYr, tnZip, tnDistance) ***************************************************************************************************************************** if type("tnStartYr") = "L" tnStartYr = 1999 endif if type("tnEndYr") = "L" tnEndYr = 2002 endif if type("tnZip") = "L" tnZip = 85281 endif local nStartRec nStartRec = 1 * nrec used with wait window nrec = 1 npage = 1 do while .T. SearchDetail( tcMake, tnStartYr, tnEndYr, tnZip, nStartRec, tnDistance) if goBotApp.lCancel return endif nStartRec = nStartRec + 25 npage = npage + 1 * 500 is max results if nStartRec > 500 exit endif enddo return ***************************************************************************************************************************** function SearchDetail( tcMake, tnStartYr, tnEndYr, tnZip, tnStartRec, tnDistance) ***************************************************************************************************************************** local buildstr local cfullpage local mstr2 local lEmail local crecrange local lPhoneFound, lnPhoneID local lnMainInfoID local cphone local cmisc local ccorrected cphone = "" private cphonepage, cphone2 buildstr = "/findacar/results.jtmpl?&model=&start_year=" + alltrim(str(tnStartYr)) + ; "&end_year=" + alltrim(str(tnEndYr)) + ; "&min_price=&max_price=&distance=" + alltrim(str(tnDistance)) + "&make=" + alltrim(tcMake) + ; "&address=" + alltrim(str(tnZip)) + "&search_type=used&advcd_on=n&advanced=y&ac_afflt=none&" *MB2(buildstr) * "x=54&y=9&" crecrange = "first_record=" + alltrim(str(tnStartRec)) + "&" * "first_record=26&" buildstr = buildstr + crecrange if type("goBotApp.oMainForm") = "O" goBotApp.oMainForm.txtDetails.value = "getting next page of results, page: " + ltrim(str(npage)) goBotApp.oMainForm.refresh() endif cfullpage = GetUrlAsString("www.autotrader.com", buildstr) if empty(cfullpage) cfullpage = GetUrlAsString("www.autotrader.com", buildstr) if empty(cfullpage) k = select() if ! used("url_errors") use url_errors in 0 endif sele url_errors append blank repl ctype with "EMPTY_RET" repl curl with buildstr repl tadded with datetime() repl batch_id with goBotApp.nbatch_id select (k) *MB2("possible error, no text returned from last lookup:" + buildstr) endif return endif local cparsestr cparsestr = cfullpage local cthis_string local n, k, k2 k = len("<!-- Set Car properties -->") n = 2 && skip the first set car properties area that isn't a car but some other stuff local crawcopy do while .T. *set step on if lHitCancel if MB2("Stop report?", MB_YESNO) == IDYES goBotApp.lCancel = .T. return else lHitCancel = .F. endif endif wait window "Processing Day: " + ltrim(str(goBotApp.nday)) + " Zip: " + ltrim(str(tnzip)) + " Make: " + tcMake + " Record: " + ltrim(str(nrec)) ; + chr(13) + " Hit ESC to stop processing" nowait noclear *? str(nrec,4) + " " + tcMake npos = at("<!-- Set Car properties -->", cfullpage, n) npos2 = at("<!-- Set Car properties -->", cfullpage, n + 1) n = n + 1 if npos == 0 AND npos2 == 0 exit endif * create the sub string to parse for this particular vehicle if npos2 == 0 cthis_string = substr( cfullpage, npos + k) else cthis_string = substr(cfullpage, npos + k, npos2 - npos) endif * translate out these because this is chunking info, the site returns chunked info per http1.1 * note I am using low level sockets not an IE wrapper, I am grabbing the lowest level http return text, for * speed and a few other reasons, IE wrappers force caching always on (some of them do) and this is possibly a bit more efficient also * chunking is a low level HTTP methodology where it sends in info in "chunks" separated out by lines which give the length of the next * chunk, for this server these length indicator lines usually contain "2000" per their server, so this info is extraneous and can throw off * the parsing logic hence it is removed. a better version would removing chunking on the data obtaining end but that is for the future * or until I switch back to IE's or inetlib url obtaining functions (warning: some of them are made to always cache pages, despite documentation * to the contrary in visual c++ msdn). also some are quite slow compared to their raw socket equivalents. cthis_string = strtran( cthis_string, chr(13) + chr(10) + "2000" + chr(13) + chr(10), "") * save the raw copy of this parsing string in case of errors to the dbf also crawcopy = cthis_string * we don't want to deal with dealers if ! ( "Private Seller" $ cthis_string ) * don't increment record counter for ads, ads dont have a checkbox cgi param in them, for this part of the html page if [type="checkbox"] $ cthis_string nrec = nrec + 1 endif if .F. select maininfo append blank *replace make with tcMake replace mrawstr with crawcopy endif loop endif lEmail = ( "Email Seller" $ cthis_string ) * skips ahead in the string a certain distance based on pattern match * MB2( "a:" + cthis_string ) *SkipAhead( @cthis_string, "<a href", 1) lWithImage = .F. if "/img/findacar/photo_icon.gif" $ cthis_string lWithImage = .T. endif *set step on if ! lWithImage cdetails_link = ParseStr( @cthis_string, "<a href", 1, [">] ) else discard = ParseStr( @cthis_string, "<a href", 1, [">] ) cdetails_link = ParseStr( @cthis_string, [href="], 1, [">] ) endif * MB2( "b:" + cthis_string ) * find a string bounded by the these sub strings, alters cthis_string cfulltitle = ParseStr( @cthis_string, [], 1, "</a>") cdescrip = ParseStr( @cthis_string, [size="1">], 1, "</font>" ) lPhoneFound = .F. lnMainInfoID = 0 if left(cfulltitle,4) != "<img" lnMainInfoID = nextkey( "MAININFO_ID") select maininfo append blank replace make with tcMake replace mrawstr with crawcopy replace fulltitle with cfulltitle repl descrip with cdescrip repl tadd_dt with datetime() repl details_link with cdetails_link repl maininfo_id with lnMainInfoID repl batch_id with goBotApp.nbatch_id repl czip with alltrim(str( tnZip ) ) local car_id, dealer_id, car_year if ! empty( cdetails_link ) *MB2( cdetails_link) ccar_id = ParseStr( @cdetails_link, [car_id=], 1, [&] ) cdealer_id = ParseStr( @cdetails_link, [dealer_id=], 1, [&] ) ccar_year = ParseStr( @cdetails_link, [car_year=], 1, [&] ) */findacar/vdetail.jtmpl?car_id=83796030&dealer_id=1328115&max_price=&start_year=1999&end_year=2002&address=85281&search_type=used&make=FORD&model=&min_price=&distance=25&advcd_on=n&advanced=n&=&color=&car_year=2001&ac_afflt=none repl car_id1 with m.ccar_id repl dealer_id1 with m.cdealer_id repl car_year1 with m.ccar_year phone_linka = "/findacar/phone_seller.jtmpl?message_type=link#link_type=PHONE#type=phone#max_price=#start_year=1999" + ; "#end_year=2002#ac_afflt=none#address=85281#car_year=" + alltrim(m.ccar_year) phone_linkb = "#search_type=used#make=FORD#model=#" + ; "car_id=" + alltrim(m.ccar_id) + "#min_price=#distance=25#dealer_id=" + alltrim(m.cdealer_id) + "#advcd_on=n#advanced=n#=#color=" repl phone_link with strtran(m.phone_linka + m.phone_linkb, "#", "&") repl lhas_email with lEmail if type("goBotApp.oMainForm") = "O" goBotApp.oMainForm.txtDetails.value = "getting phone details per record " + ltrim(str(nrec)) goBotApp.oMainForm.refresh() endif cphonepage = GetUrlAsString("www.autotrader.com", alltrim(phone_link)) if ! empty( cphonepage ) if ! empty(cphonepage) repl phone_raw with padr(cphonepage, 15) endif cphone2 = cphonepage SkipAhead( @cphone2, "<b>Comments</b>", 1) SkipAhead( @cphone2, "<tr>", 3) SkipAhead( @cphone2, ">", 2) repl mcomments with ParseStr( @cphone2, [], 1, "</font>") *MB2( mcomments ) SkipAhead( @cphonepage, "<i>Contact:</i>", 1) cname = ParseStr( @cphonepage, [size="2">], 1, "</font>" ) repl name with m.cname m.cphone = ParseStr( @cphonepage, [<b>Phone:], 1, [</b></font>] ) repl phone with alltrim(m.cphone) lPhoneFound = .T. cAddr = ParseStr( @cphonepage, [&dAddr=], 1, [">] ) cAddr = strtran( cAddr, "&dCity=", chr(13) ) cAddr = strtran( cAddr, "&dState=", chr(13) ) cAddr = strtran( cAddr, "&dZip=", chr(13) ) cAddr = strtran( cAddr, "+", " ") repl address with cAddr endif * if this item didn't have an email option, save it as a phone contact to be sent to HQ if ! lEmail * update the tracking var for LB goBotApp.nLastNonEmailCnt = goBotApp.nLastNonEmailCnt + 1 * make sure this last record found a phone, and it's not empty if lPhoneFound AND ! empty( maininfo.phone) sele phone_contacts * if this phone number doesn't exist in phone_contacts, add it and add it to the links * file, the links file will be used to see what phone numbers were new per this batch run if ! seek( alltrim(maininfo.phone) ) lnPhoneID = nextkey( "PHONE_CONTACTS_ID") if empty(lnPhoneID) MB2("trouble getting new phone record ID, critical error") loop endif goBotApp.nnew_phones = goBotApp.nnew_phones + 1 select phone_contacts append blank repl phone_contacts_id with lnPhoneID repl cphone with alltrim(maininfo.phone) repl tadded with datetime() repl batch_id with goBotApp.nbatch_id repl dlastsend with date() * phone link tracks what batches had new phone numbers assigned to phone_contacts table * this could be extracted from existing tables but logic would be complex this is simpler and faster * to use a separate linking table, info less likely to be lost also * a report will use phone_link to see what new phone numbers exist for a given batch, note the phone * number isn't kept in the link table that would be redundant, it is in maininfo and also in phone_contacts, * whichever is more convenient to grab. batch_id corresponds to a record in batches.dbf select phone_link append blank repl batch_id with goBotApp.nbatch_id repl maininfo_id with lnMainInfoID repl phone_contacts_id with lnPhoneID repl phone_link_id with nextkey( "PHONE_LINK_ID") repl tadded with datetime() else goBotApp.nold_phones = goBotApp.nold_phones + 1 endif endif else * make sure a valid car id was found for this section if val(m.ccar_id) > 0 lOkay = .F. llNoSpam = .F. * to avoid spams avoid this car id if in nospam table and if ! empty(m.ccar_id) * see if match in the "nospam" table, table with list of people who dont want emails in it select * from nospam where alltrim(car_id) == alltrim(m.ccar_id) into cursor nospam_chk if recc() > 0 llNoSpam = .T. endif use in nospam_chk endif * and avoid any cars with this same phone number in nospam also if lPhoneFound AND ! empty(m.cphone) select * from nospam where alltrim(phone) == alltrim(m.cphone) into cursor nospam_chk if recc() > 0 llNoSpam = .T. endif use in nospam_chk endif if llNoSpam goBotApp.nspam_skip = goBotApp.nspam_skip + 1 lOkay = .F. else * now look in tracking for a previous email send and make sure expire period has passed before sending another select * from tracking where car_id = val(m.ccar_id) order by tlastemailsent desc into cursor quick_check select quick_check select quick_check if recc() = 0 lOkay = .T. else dlastsend = ttod( quick_check.tlastemailsent) if dlastsend <= date() - goBotApp.nExpirePeriod lOkay = .T. else goBotApp.nRecentMailSkip = goBotApp.nRecentMailSkip + 1 goBotApp.nemails_skpd = goBotApp.nemails_skpd + 1 endif endif use in quick_check endif if lOkay goBotApp.nLastEmailCnt = goBotApp.nLastEmailCnt + 1 goBotApp.nemails_sent = goBotApp.nemails_sent + 1 select maininfo repl lemailsent with .T. lcCgiParams = "message_type=email&" lcCgiParams = lcCgiParams + "contact_name=Nancy+Pomeroy&" cmisc = cgi_ify("480-464-1969") *messagebox(cmisc) lcCgiParams = lcCgiParams + "day_phone_number=" + cmisc + "&" lcCgiParams = lcCgiParams + "night_phone_number=&" cmisc = cgi_ify("nancy.pomeroy@usautomanagementllc.com") *messagebox(cmisc) lcCgiParams = lcCgiParams + "email_address=" + cmisc + "&" *messagebox(gcNoteText) lcCgiParams = lcCgiParams + "contact_time=Day&" lcCgiParams = lcCgiParams + "comment=" + gcNoteText + "&" lcCgiParams = lcCgiParams + "ac_contact=no&" lcCgiParams = lcCgiParams + "partner_contact=no&" lcCgiParams = lcCgiParams + "page=results&" lcCgiParams = lcCgiParams + "car_id=" + alltrim(m.ccar_id) + "&" lcCgiParams = lcCgiParams + "dealer_id=" + alltrim(m.cdealer_id) + "&" lcCgiParams = lcCgiParams + "address=" + alltrim(str(tnZip)) + "&" lcCgiParams = lcCgiParams + "sort_type=" + "&" lcCgiParams = lcCgiParams + "start_year=" + alltrim(str(tnStartYr)) + "&" lcCgiParams = lcCgiParams + "distance=25&" lcCgiParams = lcCgiParams + "end_year=" + alltrim(str(tnEndYr)) + "&" lcCgiParams = lcCgiParams + "max_price=&" lcCgiParams = lcCgiParams + "make=" + alltrim(tcMake) + "&" lcCgiParams = lcCgiParams + "model=&" lcCgiParams = lcCgiParams + "first_record=1&" lcCgiParams = lcCgiParams + "ac_afflt=none&" *<input type="hidden" value="82616543" name="car_id"> * <input type="hidden" value="" name="dealer_id"> * <input type="hidden" value="85281" name="address"> * <input type="hidden" value="" name="sort_type"> * <input type="hidden" value="1999" name="start_year"> * <input type="hidden" value="25" name="distance"> * <input type="hidden" value="2002" name="end_year"> * <input type="hidden" value="" name="max_price"> * <input type="hidden" value="FORD" name="make"> * <input type="hidden" value="" name="model"> * <input type="hidden" value="1" name="first_record"> * <!-- pass ac_afflt so it appears in location field for tracking --> * <input value="none" type="Hidden" name="ac_afflt"> </form> select tracking append blank repl tracking_id with nextkey( "TRACKING_ID") replace car_id with val(m.ccar_id) repl tlastemailsent with datetime() repl maininfo_id with maininfo.maininfo_id *repl nospam with .F. select maininfo if type("goBotApp.oMainForm") = "O" goBotApp.oMainForm.txtDetails.value = "sending email for " + m.cphone goBotApp.oMainForm.refresh() endif if .F. && val(m.ccar_id) != 91575546 inkey(.5) repl cemailparams with lcCgiParams else *messagebox("starting actual send") *set step on * send the actual post mimicking behavior of a manual send * foxpro sometimes interprets the amperstand as a special char to avoid this I do a trick with substitution ccorrected = strtran("/dealer/send_email.jtmpl?link_type=results#search_type=#ac_afflt=none", "#", "&") * note the post is a bit weird it both includes command line parameters as well as normal post variables, this is * the way the actual auto-trader post works during form submission by the way. mstr2 = GetUrlStrFromPost( "www.autotrader.com", ccorrected, ; lcCgiParams) select maininfo repl lemailsent with .T. repl memail_rsp with mstr2 if "Thank you for contacting this seller" $ memail_rsp repl leconfirmed with .T. else repl leconfirmed with .F. *messagebox("confirmation receive possible issue") endif * some debug code if .F. k2 = select() if ! used("memo1") use memo1 in 0 endif select memo1 go top repl memo1 with mstr2 messagebox(mstr2) select (k2) endif endif else select maininfo repl lemailsent with .F. endif endif endif endif endif enddo return ***************************************************************************************************************************** function TestParam( thisval ) ***************************************************************************************************************************** thisval = "B" return ***************************************************************************************************************************** function SkipAhead( cthis_string, tcStartText, nMatchNo) ***************************************************************************************************************************** local k2 k2 = len( tcStartText) npos2 = at( tcStartText, cthis_string, nMatchNo) if npos2 == 0 return .F. else cthis_string = substr(cthis_string, npos2 + k2) endif return .T. ***************************************************************************************************************************** function ParseStr( tcstring, tcStartText, nMatchNo, tcEndText) ***************************************************************************************************************************** local k2 local cret local npos local nstartmark if empty( tcStartText ) npos = 1 else npos = at( tcStartText, tcstring, nMatchNo) if npos == 0 return "/N/F:1" endif endif nstartmark = npos tcstring = substr( tcstring, nstartmark + len( tcStartText) ) npos = at( tcEndText, tcstring) if npos == 0 return "/N/F:2" else cret = left(tcstring, npos - 1 ) tcstring = substr(tcstring, npos + len(tcEndText) ) endif return cret ***************************************************************************************************************************** function MB2( tcs1, tcn1, tcs2) ***************************************************************************************************************************** local nret if type("tcs2") != "L" nret = messagebox( tcs1, tcn1, tcs2 ) else if type("tcn1") != "L" nret = messagebox( tcs1, tcn1, "AutoBot") else nret = messagebox( tcs1, 0, "AutoBot") endif endif return nret ***************************************************************************************************************************** ***************************************************************************************************************************** ***************************************************************************************************************************** ***************************************************************************************************************************** ***************************************************************************************************************************** *****************************************************************************************************************************