FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour Whatsapp
Posts: 22
Joined: Thu Apr 23, 2020 12:41 PM
Whatsapp
Posted: Sat Apr 05, 2025 02:46 PM

Hello, I would like to know how I can send a Whatsapp message with attachment from within an xHarbour program.

Thanks.

Posts: 182
Joined: Tue Oct 18, 2005 10:01 AM
Re: Whatsapp
Posted: Sat Apr 05, 2025 03:16 PM
Hi, I use easyChat

https://easychat.seacweb.com/docs/1.0/introduction

https://cloudhotels.com.br/

call he on whatsapp if you like.

regards.
Posts: 22
Joined: Thu Apr 23, 2020 12:41 PM
Re: Whatsapp
Posted: Wed May 28, 2025 12:08 PM

Hello, I would like to know if there is a way to download and read messages and attachments received on Whatsapp within an Xharbour program.

Thanks.

Posts: 9020
Joined: Thu Oct 06, 2005 08:17 PM
Re: Whatsapp
Posted: Sat Jun 14, 2025 05:09 PM

We are trying to get whatsapp.prg sample to work, but the text message never shows up on whatsapp Windows app. The phone number is received by the app, the text message is not. Any hints?

Posts: 1344
Joined: Wed Nov 16, 2005 09:14 PM
Re: Whatsapp
Posted: Tue Jun 17, 2025 10:30 PM
Hola Enrico:
Para la aplicacion de escritorio de Whatsapp, usamos esto:
IF lWhatsapp_escr //Si configurĂł que tiene whatsapp de escritorio
        ShellExecute( oDlg:hwnd,'open',"whatsapp://send?phone="+ALLTRIM(oQryComWeb:prefijo)+ALLTRIM(cTelefono)+"&text=")
        Sleep(1000)
        ShellExecute( oDlg:hwnd,'open',"whatsapp://send?phone="+ALLTRIM(oQryComWeb:prefijo)+ALLTRIM(cTelefono)+"&text="+cTexto)
    ELSE 
        // Whatsapp Web
        ShellExecute( oDlg:hwnd,'open',"https://api.whatsapp.com/send?phone="+ALLTRIM(oQryComWeb:prefijo)+ALLTRIM(cTelefono)+"&text="+cTexto)
ENDIF
En definitiva, para escritorio hacemos que intente conectarse con el programa, con un texto vacio, y luego de un segundo, enviamos el mensaje.
Posts: 9020
Joined: Thu Oct 06, 2005 08:17 PM
Re: Whatsapp
Posted: Wed Jun 18, 2025 06:59 AM

Thank you. Can you provide a little complete sample, please?

Posts: 1344
Joined: Wed Nov 16, 2005 09:14 PM
Re: Whatsapp
Posted: Wed Jun 18, 2025 11:13 AM
Si, aqui va:
#include "FiveWin.ch"
FUNCTION Main()
LOCAL oDlg1, oBrw, oBot1, lWhatsapp_escr := .f., oChe, aCols := {;
      {'5492324463274','Jose','Venezuela'},;
      {'5492324463274','Enrico','Italia'},;
      {'5492324463274','Cesar','Argentina'}}
DEFINE DIALOG oDlg1 TITLE "Enviar Mensaje por Whatsapp" FROM 05,15 TO 22,72
   @ 25, 05 XBROWSE oBrw SIZE 200,90 pixel OF oDlg1 ARRAY aCols  ;
      HEADERS "Telefono","Nombre", "Texto";
      COLUMNS 1,2,3;
      SIZES 100, 100,200;
      CELL LINES NOBORDER
   WITH OBJECT oBrw
      :CreateFromCode()
   END
@  5,  5 BUTTON oBot1 PROMPT "&Enviar" OF oDlg1 SIZE 50,15 ;
         ACTION EnviarWhatsapp(aCols[oBrw:nRowSel],lWhatsapp_escr,oDlg1) PIXEL 
@  5,160 CHECKBOX oChe VAR lWhatsapp_escr PROMPT "Whatsapp Escritorio" OF oDlg1 SIZE 100,12  PIXEL   
ACTIVATE DIALOG oDlg1 CENTERED 
RETURN nil

STATIC FUNCTION EnviarWhatsapp(a,lWhatsapp_escr, oDlg)
LOCAL cTexto

    cTexto:="Hola *"+ALLTRIM(a[2])+"*!"+CHR(13)+;
        "Queremos avisarte que tu detectamos que eres de *"+ALLTRIM(a[3])+"* "    
    cTexto := STRTRAN(cTexto," ","%20")
    cTexto := STRTRAN(cTexto,"&","%26")
    cTexto := STRTRAN(cTexto,chr(13),"%0D%0A")
    IF lWhatsapp_escr
        ShellExecute( oDlg:hwnd,'open',"whatsapp://send?phone="+ALLTRIM(a[1])+"&text=")
        Sleep(1000)
        ShellExecute( oDlg:hwnd,'open',"whatsapp://send?phone="+ALLTRIM(a[1])+"&text="+cTexto)
    ELSE 
        ShellExecute( oDlg:hwnd,'open',"https://api.whatsapp.com/send?phone="+ALLTRIM(a[1])+"&text="+cTexto)
    ENDIF

RETURN nil
Posts: 1816
Joined: Wed Oct 26, 2005 02:49 PM
Re: Whatsapp
Posted: Wed Jun 18, 2025 01:49 PM
Que buen aporte Cesar, muchas gracias por compartirlo :D :D :D
Saludos
LEANDRO AREVALO
Bogotá (Colombia)
https://hymlyma.com
https://hymplus.com/
leandroalfonso111@gmail.com
leandroalfonso111@hotmail.com

[ Turbo Incremental Link64 6.98 Embarcadero 7.70 ] [ FiveWin 25.01 ] [ xHarbour 64 bits) ]
Posts: 6983
Joined: Fri Oct 07, 2005 07:07 PM
Re: Whatsapp
Posted: Wed Jun 18, 2025 02:15 PM
Hello,
I did some tests with it a while ago. I'll repost the ChatGPT summary.
Best regards,
Otto

You need a mobile phone for this to work!





### 📱 **Do you need a mobile phone for this to work?**

**Yes, you do.** The FiveWin program you've shown works by launching WhatsApp either through the **WhatsApp Desktop App** or the **WhatsApp Web API**, and both require a **linked mobile phone with WhatsApp installed and active**.

---

### 🔗 How the system works

The code uses one of two methods to send a message:

#### 1. `https://api.whatsapp.com/send?phone=...&text=...`

* Opens **WhatsApp Web** in your browser.
* Requires the user to have previously scanned a QR code using their phone to link it with the browser session.
* A real phone with WhatsApp is needed for this to work.

#### 2. `whatsapp://send?phone=...&text=...`

* Opens the **WhatsApp Desktop App** (if installed).
* Again, this desktop app must be linked to a **real phone via QR code pairing**.

---

### âś… What does WhatsApp provide?

* The “clearing” or delivery mechanism is **WhatsApp itself**, via the URL scheme or API entry point.
* There is **no external clearing server** involved.
* WhatsApp ensures that messages are sent from a real phone account – it **does not allow sending messages without a linked mobile device**.

---

### 🔄 Can it be automated?

**No, not fully**, unless you use the **WhatsApp Business API**. Here's a comparison:

| Method | Requires Phone | Fully Automated | Easy Setup | For Business Use |
| ------------------------------- | ----------------------------------- | --------------- | ---------- | --------------------------- |
| `https://api.whatsapp.com/send` | ✅ Yes | ❌ No | ✅ Yes | ✅ Personal / Small Business |
| `whatsapp://send` | ✅ Yes | ❌ No | ✅ Yes | ✅ Personal / Small Business |
| WhatsApp Business API | ❌ No (but requires official number) | ✅ Yes | ❌ Complex | ✅ Medium / Large Business |

---

### ⚠️ Important Notes

* The number must be in **international format** (e.g. `5492324...` for Argentina).
* The phone must be **online** and have WhatsApp active.
* Users will usually need to **click “Send”** manually after the message is opened, unless advanced automation tools are used (which are often against WhatsApp's terms).

---

### 💡 Summary

You **cannot send WhatsApp messages without a real phone** — whether using the browser, the desktop app, or even via automation. WhatsApp's infrastructure requires all messages to go through a **verified phone account** unless you use the official Business API with approval and a provider like Twilio or 360dialog.

Would you like a version that cycles through a list and opens WhatsApp for each contact with a delay and user guidance? I can create that as well.


Great question — and yes, **some degree of automation is possible**, *but only partially and with important limitations*.

---

### âś… **What you *can* automate using WhatsApp Desktop + Timer**

You can:

* Automatically **launch the WhatsApp Desktop app** using `ShellExecute`.
* Automatically **open a chat with a specific phone number** using `whatsapp://send?phone=...&text=...`.
* Use a **timer or delay** (`Sleep()`) between launches.
* Potentially **simulate user input** (like keyboard events) via:

* Windows API (e.g., `keybd_event`, `SendInput`),
* AutoHotKey scripts,
* `INPUT` struct via low-level Windows automation.

---

### ⚠️ **What you *cannot* reliably automate**

WhatsApp Desktop is:

* **Not designed to be automated** (there is no official API for the desktop app).
* Subject to **UI changes** (button positions, window titles, etc.).
* Dependent on **human interaction** — WhatsApp intentionally makes it difficult to *fully* automate sending messages to prevent spam and abuse.

Also:

* You **cannot detect if the message was actually sent**.
* If the chat is already open, `whatsapp://send?phone=...` won't pre-fill the message reliably.
* **Simulating the Enter key to send** is fragile (it may send the wrong message or fail if focus is lost).

---

### 🧪 âś… Feasible hybrid solution for semi-automation

You can build a **loop with a timer** like this:

```xbase
STATIC FUNCTION LoopSendWhatsApp( aData, oDlg )
LOCAL i, cTexto, lWhatsapp_escr := .t.

FOR i := 1 TO LEN(aData)
cTexto := "Hello *" + aData[2] + "* from *" + aData[3] + "*!"
cTexto := STRTRAN(cTexto, " ", "%20")
cTexto := STRTRAN(cTexto, CHR(13), "%0D%0A")

ShellExecute( oDlg:hwnd, "open", ;
"whatsapp://send?phone=" + ALLTRIM(aData[1]) + "&text=" + cTexto )

Sleep(3000) // wait before next
NEXT
```

Then combine it with user instructions like:

> *"Click 'Send' manually for each message. The next message will open in 3 seconds."*

---

### 🛠 Advanced Option: AutoHotKey + WhatsApp Desktop

With [AutoHotKey](https://www.autohotkey.com/) you can:

* Wait for the WhatsApp window.
* Send keys (`Send Hello!{Enter}`).
* Close the window, go to the next.

But this is brittle and **not officially supported**.

---

### âś… Summary

| Approach | Fully Automated? | Stable? | WhatsApp-compliant? | Notes |
| ------------------------------------- | ---------------- | --------- | ------------------- | ------------------------- |
| WhatsApp Web / Desktop + Timer | ❌ Partial | ✅ Yes | ✅ Yes | Requires user interaction |
| Simulated keyboard/mouse (AutoHotKey) | ✅ Mostly | ❌ Fragile | ❌ Risky | Can break on updates |
| WhatsApp Business API | âś… Yes | âś… Yes | âś… Official | Paid, complex setup |

---



Posts: 6983
Joined: Fri Oct 07, 2005 07:07 PM
Re: Whatsapp
Posted: Wed Jun 18, 2025 02:26 PM
I'm just noting this here because SMS is still often needed besides WhatsApp or apart from WhatsApp for two-way authentication. This could still work as follows:
🔁 Explanation: Sending and Receiving SMS via USB GSM Modem
You can use a USB GSM modem or dongle (like Huawei E3372 or SIM800C) for both sending and receiving SMS, including two-factor authentication (2FA) codes.

These devices support AT commands (standard modem instructions), such as:

Receiving:

AT+CMGF=1 – Set text mode

AT+CMGL="ALL" – List all messages

AT+CMGR=1 – Read specific message

Sending:

AT+CMGS="+43660..." – Send SMS to a number

Followed by the message text and Ctrl+Z (\x1A) to send

💡 What your script or application can do:
Monitor the COM port (e.g., COM3 or /dev/ttyUSB0)

Detect incoming messages (e.g., with 2FA codes)

Extract codes using pattern matching (e.g., Your code is 123456)

Send SMS replies or notifications, for example:

Confirm registration

Send alerts

Notify users of login attempts or system messages

âś… Key Benefits:
Works without internet

Fully scriptable (Harbour, PHP, Python, etc.)

Uses a regular SIM card — no third-party provider needed

Reliable fallback when smartphones or internet are not available
Posts: 9020
Joined: Thu Oct 06, 2005 08:17 PM
Re: Whatsapp
Posted: Wed Jun 18, 2025 06:23 PM
cmsoft wrote: Si, aqui va:
Thank you. And what about attachments?
Posts: 1344
Joined: Wed Nov 16, 2005 09:14 PM
Re: Whatsapp
Posted: Wed Jun 18, 2025 10:23 PM
Las pruebas con documentos adjuntos no fueron del todo exitosas, hay que trabajarlas un poco mas (sobre todo los tiempos de espera)
Igualmente te dejo hasta donde probé y funciona (aunque no todas las veces) pero puede ser un buen punto de partida.
#include "FiveWin.ch"
FUNCTION Main()
//SendToWhatsApp('5492324463274')
//SendToWhatsApp('5492324463274','Esto es una prueba')
SendToWhatsApp('5492324463274','Primer Prueba, un archivo adjunto y una foto de la web',;
               {'c:\fwh21\samples\whatsapp3.prg'})
SendToWhatsApp('5492324463274','Segunda Prueba, 2 adjunotos en un array, y dos imagenes desde la web en un array',;
              {'c:\fwh21\bitmaps\agua2.bmp','c:\fwh21\bitmaps\cara3.bmp'})
RETURN nil




FUNC SendToWhatsApp( cPhone, cMsg, aAttach )
LOCAL oShell, aFiles := {}, aOthers  := {}, lRet
 If (lRet := !Empty( cPhone ))
  If !Empty( aAttach )
   If( !HB_ISARRAY( aAttach ), aAttach := { aAttach }, )
   AEval( aAttach, {|cFile| If( HB_ISSTRING( cFile ) .AND. File( cFile ), AAdd( aFiles, cFile), AAdd( aOthers, cFile ) ) } )
  EndIf
  cMsg  := StrTran( cMsg, CRLF, "%0D%0A" )
  oShell := CreateObject( "WScript.Shell" )
  ShellExecute( 0, "Open", "whatsapp://send?phone="+cPhone+"&text="+cMsg)
  SysWait( 0.2 )
  If !Empty( aFiles )
   If FW_1CopyToClipBoard( aFiles, 15 )
    SysWait( 2 )
    oShell:SendKeys( "^v" )
    SysWait( 1 )
   EndIf
  EndIf
  AEval( aOthers, <|u|
                      If FW_1CopyToClipBoard( u, 2 ) .OR. FW_CopyToClipBoard( u )
                       SysWait( 1 )
                       oShell:SendKeys( "^v" )
                       SysWait( 1 )
                      EndIf
                      RETURN NIL
                  > )
   oShell:SendKeys("~")
 EndIf
RETURN lRet

function FW_1CopyToClipBoard( uValue, nFormat )

   local lCopied  := .f.
   local aBmp

   if HB_ISPOINTER( uValue ) // assumes GDI+ Image object
      GDIPLUSImageToClipBoard( uValue, GetDesktopWindow() )
      return .t.
   endif

   if uValue != nil

      if nFormat == 15
         if !HB_ISARRAY( uValue ); uValue := { uValue }; endif
         _fwhtmp_ := uValue
         uValue   := {}
         AEval( _fwhtmp_, { |c| If( File( c ), AAdd( uValue, TrueName( c ) ), nil ) } )
         _fwhtmp_ := nil
         if Empty( uValue )
            return .f.
         endif
      endif

      if Empty( nFormat ) .and. HB_ISARRAY( uValue ) .and. ;
         AScan( uValue, { |u| !( ValType( u ) == "C" .and. File( u ) ) } ) == 0
            AEval( uValue, { |c,i| uValue[ i ] := TrueName( c ) } )
            nFormat  := 15
      endif

      if nFormat == 2 .and. HB_ISSTRING( uValue )
         aBmp  := FW_ReadImage( nil, uValue, , .f. )
         if !Empty( aBmp[ 1 ] )
            uValue := aBmp[ 1 ]
         else
            return .f.
         endif
      endif

      if Empty( nFormat )
         if HB_ISSTRING( uValue )
            if IsBinaryData( uValue )
               if Left( MemoryBufferType( uValue ), 4 ) == "IMG."
                  aBmp := FW_ReadImage( nil, uValue, , .f. )
                  if !Empty( aBmp[ 1 ] )
                     uValue   := aBmp[ 1 ]
                     nFormat  := 2
                  endif
               endif
               if Empty( nFormat )
                  return .f.
               endif
            elseif Left( uValue, 4 ) == "http"
               aBmp := FW_ReadImage( nil, uValue, , .f. )
               if !Empty( aBmp[ 1 ] )
                  uValue   := aBmp[ 1 ]
                  nFormat  := 2
               else
                  nFormat  := 1
               endif
            else
               nFormat  := 1
            endif
         elseif HB_ISNUMERIC( uValue )
            if ISHBITMAP( uValue )
               nFormat := 2
            elseif ISENHMETA( uValue )
               nFormat  := 14
            endif
         endif
      endif

      if Empty( nFormat )
         if ValType( uValue ) $ "AH"
            uValue   := FW_ValToExp( uValue )
         elseif ValType( uValue ) $ "DT"
            uValue   := CharRem( "'", FW_DateToSQL( uValue ) )
         else
            uValue   := cValToChar( uValue )
         endif
         nFormat  := 1
      endif

      if nFormat != nil
         if nFormat == 2
            if HB_ISNUMERIC( uValue ) .and. ISHBITMAP( uValue )
               GDIPLUSHBITMAPTOCLIPBOARD( uValue, GetDeskTopWindow() )
               lCopied  := .t.
            endif
         elseif OpenClipBoard( GetDeskTopWindow() )
            EmptyClipboard()
            if nFormat == 1 .and. IsUtf8( uValue )
               uValue    := utf8toutf16( uValue )
               if !( Right( uValue, 2 ) == Chr( 0 ) + Chr( 0 ) )
                  uValue    += ( Chr( 0 ) + Chr( 0 ) )
               endif
               lCopied  := SetClipboardData( 13, uValue )
            elseif nFormat == 15
               lCopied  := SetClipboardData( 15, uValue )
            else
               lCopied  := SetClipboardData( nFormat, uValue )
            endif
            CloseClipboard()
         endif
      endif
   endif

   if aBmp != nil
      PalBmpFree( aBmp )
   endif

return lCopied
En este caso habĂ­a tenido colaboraciĂłnd de Mr. Rao con las funciones de copiado al portapapeles
Posts: 410
Joined: Sun Jan 31, 2010 03:30 PM
Re: Whatsapp
Posted: Thu Jun 19, 2025 04:05 PM

Buen dia,,,

Para whatsapp de escritorio lo he trabajado sin problemas con los ejemplos publicados en el foro,,,

pero como en windows server, no funciona esta version, creo que deberĂ­amos pensar en la opciĂłn web usando el api de whatsapp

Saludos

JONSSON RUSSI

Posts: 9020
Joined: Thu Oct 06, 2005 08:17 PM
Re: Whatsapp
Posted: Sat Jun 28, 2025 03:43 PM

Let's make a little step back: we are still not able to send a simple text message through Whatsapp (only phone number and text). Can you provide a little working sample? Or some configuration is needed on the Whatsapp side?

Posts: 410
Joined: Sun Jan 31, 2010 03:30 PM
Re: Whatsapp
Posted: Sun Jun 29, 2025 11:52 AM
Buen dĂ­a..

Este CĂłdigo me funciona --- TOMADO DEL FORO
// REQUIERE VERSION WhatsApp - Escritorio
 
FUNCTION PRUEBA__WhatsApp()


LOCAL cPhone := "57"+"325874510"  // NO HACER PRUEBAS CON EL MISMO NUMERO DE QUIEN ENVIA
LOCAL cMenEnv := "Anexo factura de venta en formato PDF"
LOCAL cNomFil := "e:\temp\FACTURA_SP1012.PDF"
       
enviaLinkWhatsApp(ALLTRIM(cPhone),cMenEnv,cNomFil)

RETURN 


//***********************************************************

function enviaLinkWhatsApp(cPhone,cMsg,cFilPdf)

   LOCAL aImgEnv := {}


   IF !FILE(cFilPdf)
      ALERTA("Aviso. Archivo a transferir no existe : "+cFilPdf)
   ENDIF

   IF cFilPdf <> NIL .AND. !EMPTY(cFilPdf)
      aImgEnv := {cFilPdf}
   ENDIF

   IF !EMPTY(cPhone)

      _SendToWhatsApp( AllTrim( cPhone ), AllTrim( cMsg ),  aImgEnv )


   ELSE
      ALERTA("Aviso. Falto el nÂŁmero para WhatSapp ")
   ENDIF


return nil


//****************************************************

function _SendToWhatsApp( cPhone, cMsg, aAttach )

   local oShell
   local aFiles   := {}
   local aOthers  := {}


   if Empty( cPhone ); return .f.; endif

   DEFAULT cMsg := "Hello"

   if !Empty( aAttach )

      if !HB_ISARRAY( aAttach ); aAttach := { aAttach }; endif

      AEval( aAttach, {|c| if( HB_ISSTRING( c ) .and. File( c ), ;
            AAdd( aFiles, c ), AAdd( aOthers, c ) ) } )

   endif

   cMsg  := StrTran( cMsg, CRLF, "%0D%0A" )

   oShell := CreateObject( "WScript.Shell" )
   ShellExecute( 0, "Open", "whatsapp://send?phone="+cPhone+"&text="+cMsg )

   SysWait( 0.2 )


   if !Empty( aFiles )
      if FW_CopyToClipBoard( aFiles, 15 )
         SysWait( 2 ); oShell:SendKeys( "^v" ); SysWait( 1 )
      endif
   endif


   AEval( aOthers, <|u|
      if FW_CopyToClipBoard( u, 2 ) .or. FW_CopyToClipBoard( u )
         SysWait( 1 ); oShell:SendKeys( "^v" ); SysWait( 1 )
      endif
      return nil
      > )
   SysWait( 1 )
   oShell:SendKeys("~")


return nil

//********************************************