No artigo de hoje, vou mostrar para vocês como integrar facilmente com WhatsApp no Protheus usando a API da ConnectZap.
Atualização em 2025
Pessoal, foi feito um novo artigo, inclusive com um vídeo atualizado no YouTube demonstrando como integrar o Protheus com a plataforma da ConnectZap.
Segue o link: https://terminaldeinformacao.com/2025/01/17/integrando-o-protheus-com-whatsapp-atraves-da-connectzap/
Artigo original em 2021
Recentemente o grande Jader veio conversar comigo, explicando sobre uma ótima forma de integrar o Protheus com WhatsApp via AdvPL / TL++ usando a API da ConnectZap ( https://www.connectzap.com.br/ ).
Ele preparou dois códigos fontes de exemplo, que já estão prontos para qualquer um usar a API da ConnectZap.
Dos dois fontes, o ConnZap.prw é uma tela de leitura, que faz a integração entre o ConnectZap e o Protheus (também pode ser feito no portal). Já o MsgZap.prw é a função que vai ser acionada para o disparo da mensagem, que recebe 3 parâmetros:
- cCelular – Número de celular que será recebido, por exemplo, 5521992584067
- cMsg – Mensagem que será enviada
- cAnexo – Caminho do arquivo que será enviado como anexo (esse é um parâmetro opcional)
Nesses dois fontes, também é necessário que você crie 2 parâmetros na sua base:
- MV_ZAPURL – URL da ConnectZap
- MV_ZSESSAO – Token / Sessão usado na integração da ConnectZap
Além de tudo isso, o pessoal da ConnectZap disponibilizou um excelente manual com as funcionalidades da API, veja em https://www.connectzap.com.br/manual.pdf.
Abaixo um vídeo de como funciona a integração com o Protheus via AdvPL.
Código fonte completo do ConnZap.prw:
//Bibliotecas
#Include 'TOTVS.ch'
#Include 'ParmType.ch'
/*/{Protheus.doc} User Function ConnZap
Tela de leitura do QRCODE - WhatsApp
@type Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/
User Function ConnZap()
Local aArea := GetArea()
Local oNewPag
Local oStepWiz := Nil
Local oPanelBkg
Private cMsg := Space(50)
Private oDlg := Nil
Private cNome
Private cStatus := ""
Private oSay1
Private cGet1 := Space(150)
Private cGet2 := Space(14)
Private cGet3 := Space(250)
//Se a pasta qrcode não existir na Protheus Data, cria
If ! ExistDir("\qrcode")
MakeDir("\qrcode")
EndIf
//Busca o Token / Sessão
cNome := Alltrim(GetMv("MV_ZSESSAO"))
//Criando a dialog da integração
oDlg:= MSDialog():New(0, 0, 300, 300, 'Conecte o WhatsApp com seu SmartPhone', , , , nOr(WS_VISIBLE, WS_POPUP), CLR_BLACK, CLR_WHITE, , , .T., , , , .T.)
oDlg:nWidth := 600
oDlg:nHeight := 600
//Cria um painel de fundo, e vincula um Wizard a ele
oPanelBkg:= tPanel():New(0, 0, "", oDlg, , , , , , 300, 300)
oStepWiz:= FWWizardControl():New(oPanelBkg)
oStepWiz:ActiveUISteps()
//Página 1 do Wizard
oNewPag := oStepWiz:AddStep("1")
oNewPag:SetStepDescription("Seleção de Token")
oNewPag:SetConstruction({|oPanel| Cria_Pg1(oPanel, @cNome)})
oNewPag:SetNextAction({|| Valida_Pg1(@cNome)})
oNewPag:SetCancelAction({||, fCancel()})
//Página 2 do Wizard
oNewPag := oStepWiz:AddStep("2", {|oPanel| Cria_Pg2(oPanel)})
oNewPag:SetStepDescription("Leitura de QrCode")
oNewPag:SetNextAction({||Valida_Pg2(@cNome)})
oNewPag:SetCancelAction({|| fCancel()})
//Página 3 do Wizard
oNewPag := oStepWiz:AddStep("3", {||oDlg:End()})
oNewPag:SetStepDescription("Teste de envio")
oNewPag:SetNextAction({||.T.})
//Define o bloco ao clicar no botão Voltar
oNewPag:SetCancelAction({|| fCancel()})
oNewPag:SetPrevAction({||, .T.})
oStepWiz:Activate()
ACTIVATE DIALOG oDlg CENTER
oStepWiz:Destroy()
RestArea(aArea)
Return
//Construção da Página 1
Static Function Cria_Pg1(oPanel, cNome)
Local aItems := {}
//Monta o say e o combo para definir o Token
oSay1:= TSay():New(10, 10, {||'Escolha o seu token de acesso'}, oPanel, , , , , , .T., , , 200, 20)
cNome := PADR(cNome, 30)
aAdd(aItems, Alltrim(GetMV("MV_ZSESSAO")))
oCombo := tComboBox():new(20, 10, {|u|if(pcount()>0, cNome:=u, cNome)}, aItems, 100, 20, oPanel, , , , , , .t., , , , , , , , , )
Return
//Validação da página 1
Static Function Valida_Pg1(cNome)
Local cStrJson := ""
Local oObj
Local lRet := .T.
cNome := Alltrim(cNome)
Processa({||cStrJson := U_fStatus(cNome)})
//Busca o JSON e deserializa
FWJsonDeserialize(cStrJson, @oObj)
//Se o objeto estiver nulo, mostra um help, senão busca o status da integração
If oObj == Nil
Help(" ", 1, "Help", , "O serviço do Whatsapp está desconectado, favor reportar ao administrador!", 1, 0)
lRet := .F.
Else
cStatus := oObj:Status:state
EndIf
Return lRet
//Construção da página 2
Static Function Cria_Pg2(oPanel)
Local oTimer
Local oObj
Local oFont
//Define a fonte, e cria um Say
oFont := TFont():New('Courier new', , -14, .T.)
oSay1 := TSay():New( 10, 10, {|u| if( PCount() > 0, cMsg := u, cMsg ) } , oPanel, , oFont, , , , .T., CLR_BLUE, CLR_WHITE, 300, 009)
//Verifica o status da conexão
cStrJson := U_fStatus(cNome)
FWJsonDeserialize(cStrJson, @oObj)
cStatus := oObj:Status:state
//Se for não encontrado, inicializa a conexão
If cStatus == "NOTFOUND"
cStrJson := U_fStart(cNome)
Sleep(15000)
cStrJson := U_fStatus(cNome)
FWJsonDeserialize(cStrJson, @oObj)
cStatus := oObj:Status:state
EndIf
//Se já estiver conectado
If cStatus == "CONNECTED"
oSay1:SetText('Seu WhatsApp já está conectado!')
oTBitmap1 := TBitmap():New(40, 80, 260, 184, , '\qrcode\whatsapp.png', .T., oPanel, {||}, , .F., .F., , , .F., , .T., , .F.)
//Senão cria um temporizador, e mostra a mensagem para apontar para o QRCode
Else
oTimer := TTimer():New(1, {|| BuscaQR(oPanel) }, oDlg )
oTimer:Activate()
oSay1:SetText('Abra o WhatsApp Web do seu SmartPhone e aponte a camera para o QRCODE:')
EndIf
Return
//Validação da página 2
Static Function valida_pg2(cNome)
Local lRet := .T.
//Se o status não estiver conectado
If cStatus != "CONNECTED"
Help(" ", 1, "Help", , "O serviço do Whatsapp não está sincronizado, faça a leitura do Qrcode!", 1, 0)
lRet := .F.
EndIf
Return lRet
Static Function BuscaQR(oPanel)
Local cStrJson := ""
Local oObj
Local cFile
Local lRet := .T.
//Define o nome do arquivo png do qrcode, e faz a conexão com a API
cNome := alltrim(cNome)
cFile := "qrcode_" + cNome + ".png"
cStrJson := U_fStatus(cNome)
FWJsonDeserialize(cStrJson, @oObj)
cStatus := oObj:Status:state
//Se o status estiver como conectado
If cStatus == "CONNECTED"
oSay1:SetText('Seu WhatsApp foi conectado!')
oTBitmap1 := TBitmap():New(40, 80, 260, 184, , '\qrcode\whatsapp.png', .T., oPanel, {||}, , .F., .F., , , .F., , .T., , .F.)
lRet := .T.
//Senão, define a imagem do QRCode
Else
fQrCode(cNome)
Sleep(1000)
oTBitmap1 := TBitmap():New(40, 80, 260, 184, , '\qrcode\'+cFile, .T., oPanel, {||}, , .F., .F., , , .F., , .T., , .F.)
cStrJson := U_fStatus(cNome)
lRet := ("CONNECTED" $ cStrJson)
EndIf
Return lRet
// Construção da página 3
Static Function cria_pg3(oPanel, cNome)
Local lHasButton := .T.
//Get da Mensagem
oSay0:= TSay():New(10, 10, {||'Mensagem:'}, oPanel, , , , , , .T., , , 200, 20)
TGet():New( 20, 10, { | u | If( PCount() == 0, cGet1, cGet1 := u ) }, oPanel, ;
250, 010, "!@", , 0, 16777215, , .F., , .T., , .F., , .F., .F., , .F., .F. , , "cGet1", , , , lHasButton )
//Get do Número de Celular
oSay2:= TSay():New(40, 10, {||'Celular:'}, oPanel, , , , , , .T., , , 70, 20)
TGet():New( 50, 10, { | u | If( PCount() == 0, cGet2, cGet2 := u ) }, oPanel, ;
60, 010, "@R (99) 99999-9999", , 0, 16777215, , .F., , .T., , .F., , .F., .F., , .F., .F. , , "cGet2", , , , lHasButton )
//Get do Arquivo (opcional)
oSay3:= TSay():New(70, 10, {||'Escolha um arquivo (opcional):'}, oPanel, , , , , , .T., , , 200, 20)
TGet():New( 80, 10, { | u | If( PCount() == 0, cGet3, cGet3 := u ) }, oPanel, ;
250, 010, "!@", , 0, 16777215, , .F., , .T., , .F., , .F., .F., , .F., .F. , , "cGet3", , , , lHasButton )
//Botões
oTButton1 := TButton():New( 81, 260, "Abrir", oPanel, {||zChooseFile()}, 25, 10, , , .F., .T., .F., , .F., , , .F. )
oTButton2 := TButton():New( 100, 10, "Enviar", oPanel, {||Iif(u_MsgZap('55' + cGet2 , cGet1, cGet3), MSGINFO( "Sucesso!", "Resultado do Envio" ), MSGINFO( "Mensagem não enviada.", "Resultado do Envio" ))}, 25, 10, , , .F., .T., .F., , .F., , , .F. )
Return
Static Function zChooseFile()
Local aArea := GetArea()
Local cDirIni := GetTempPath()
Local cTipArq := "Todas extensões (*.*)"
Local cTitulo := "Seleção de Arquivos para Processamento"
Local lSalvar := .F.
Local cArqSel := ""
//Se não estiver sendo executado via job
If ! IsBlind()
//Chama a função para buscar arquivos
cArqSel := tFileDialog(;
cTipArq, ; // Filtragem de tipos de arquivos que serão selecionados
cTitulo, ; // Título da Janela para seleção dos arquivos
, ; // Compatibilidade
cDirIni, ; // Diretório inicial da busca de arquivos
lSalvar, ; // Se for .T., será uma Save Dialog, senão será Open Dialog
; // Se não passar parâmetro, irá pegar apenas 1 arquivo; Se for informado GETF_MULTISELECT será possível pegar mais de 1 arquivo; Se for informado GETF_RETDIRECTORY será possível selecionar o diretório
)
If ! Empty(cArqSel)
cGet3 := cArqSel
EndIf
EndIf
Return cArqSel
//Função que fecha a dialog
Static Function fCancel()
oDlg:End()
Return .T.
Static Function fQrCode(cNome)
Local cReturn
Local nTimeOut := 120
Local aHeadOut := {}
Local cHeadRet := ""
Local cPostParms:= ""
Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
Local cSession := Alltrim(cNome)
Local cFile := "qrcode_"+alltrim(cNome)+".png"
//Realiza a integração e busca o QRCode
cUrl += "/QRCode"
aAdd(aHeadOut, "User-Agent: Chrome/65.0 (compatible; Protheus " + GetBuild() + ")")
aAdd(aHeadOut, 'Content-Type: application/json')
cPostParms := '{"SessionName": "'+cSession+'", "View": true }'
cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
//Grava a imagem
MemoWrite('\qrcode\'+cFile, cReturn)
Return cReturn
/*/{Protheus.doc} User Function ConnZap
Fecha a conexão da Sessão / Token com ConnectZap
@type Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/
User Function fClose(cNome)
Local aArea := GetArea()
Local cReturn
Local nTimeOut := 120
Local aHeadOut := {}
Local cHeadRet := ""
Local cPostParms:= ""
Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
Local cSession := Alltrim(cNome)
//Define a operação para fechar e faz a integração
cUrl += "/Close"
aAdd(aHeadOut, 'Content-Type: application/json')
cPostParms := '{"SessionName":"'+cSession+'"}'
cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
RestArea(aArea)
Return cReturn
/*/{Protheus.doc} User Function ConnZap
Inicializa a conexão da Sessão / Token com ConnectZap
@type Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/
User Function fStart(cNome)
Local aArea := GetArea()
Local cReturn
Local nTimeOut := 120
Local aHeadOut := {}
Local cHeadRet := ""
Local cPostParms:= ""
Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
Local cSession := Alltrim(cNome)
//Define a operação para inicializar e faz a integração
cUrl += "/Start"
aAdd(aHeadOut, 'Content-Type: application/json')
cPostParms := '{"SessionName":"'+cSession+'"}'
cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
Sleep(2000)
RestArea(aArea)
Return cReturn
/*/{Protheus.doc} User Function ConnZap
Busca o status da Sessão / Token com ConnectZap
@type Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/
User Function fStatus(cNome)
Local aArea := GetArea()
Local cReturn
Local nTimeOut := 120
Local aHeadOut := {}
Local cHeadRet := ""
Local cPostParms:= ""
Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
Local cSession := Alltrim(cNome)
//Define a operação para buscar o status e faz a integração
cUrl += "/Status"
aAdd(aHeadOut, "User-Agent: Chrome/65.0 (compatible; Protheus " + GetBuild() + ")")
aAdd(aHeadOut, 'Content-Type: application/json')
cPostParms := '{"SessionName": "'+cSession+'"}'
cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
RestArea(aArea)
Return cReturn
/*/{Protheus.doc} User Function ConnZap
Busca informações do dispositivo conectado da Sessão / Token com ConnectZap
@type Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/
User Function fHard(cNome)
Local aArea := GetArea()
Local cReturn
Local nTimeOut := 120
Local aHeadOut := {}
Local cHeadRet := ""
Local cPostParms:= ""
Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
Local cSession := Alltrim(cNome)
//Define a operação para buscar informações do dispositivo conectado e faz a integração
cUrl += "/getHostDevice"
aAdd(aHeadOut, "User-Agent: Chrome/65.0 (compatible; Protheus " + GetBuild() + ")")
aAdd(aHeadOut, 'Content-Type: application/json')
cPostParms := '{"SessionName": "'+cSession+'"}'
cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
RestArea(aArea)
Return cReturn
Código fonte completo do MsgZap.prw:
//Bibliotecas
#Include 'TOTVS.ch'
#Include 'FWMVCDef.ch'
#Include 'RestFul.ch'
#Include 'FileIO.ch'
/*/{Protheus.doc} User Function MsgZap
Envio de WHATSAPP via Protheus
@type Function
@author Jader Berto
@since 13.08.2019
@version version
@param cCelular, Caractere, Número de Celular que irá receber a mensagem
@param cMsg, Caractere, Mensagem que será enviada
@param cAnexo, Caractere, Caminho do anexo (parâmetro opcional)
@return lRet, Lógico, Retorna se houve sucesso ou não no envio
@example
Exemplos abaixo:
// Com Anexo
U_MsgZap('5521992584067' ,'Teste Protheus', 'TSTPOLX01_210505_112556_Administrador.txt')
// Sem Anexo
U_MsgZap('5521975267383' ,'Teste Protheus')
@see https://www.connectzap.com.br/manual.pdf
/*/
User Function MsgZap(cCelular, cMsg, cAnexo)
Local aArea := GetArea()
Local lRet := .F.
Default cCelular := ""
Default cMsg := ""
Default cAnexo := ""
Private lAuto := IsBlind()
//Retirando caracteres especiais do numero
cCelular := Alltrim(cCelular)
cCelular := Replace(cCelular, "(", "")
cCelular := Replace(cCelular, ")", "")
cCelular := Replace(cCelular, "-", "")
cCelular := Replace(cCelular, " ", "")
//Se o celular tiver vazio, ou o tamanho for menor que 8
If Empty(cCelular) .Or. Len(cCelular) < 8
If lAuto
fConOut("CONNECTZAP = Favor enviar o número do celular WhatsApp.")
Else
MsgInfo("Favor enviar o número do celular WhatsApp.", "Problemas ao enviar" )
EndIf
lRet := .F.
EndIf
//Se não tiver mensagem para enviar
If Empty(cMsg)
If lAuto
fConOut("CONNECTZAP = Favor enviar a mensagem a ser enviada.")
Else
MsgInfo("Favor enviar a mensagem a ser enviada.", "Problemas ao enviar" )
EndIf
lRet := .F.
EndIf
//Se houver anexo, verifica se a extensão jpg, bmp ou png
If ! Empty(cAnexo)
If ! (".jpg" $ Lower(cAnexo) .Or. ".bmp" $ Lower(cAnexo) .Or. ".png" $ Lower(cAnexo))
lRet := .F.
EndIf
EndIf
//Se passou pela validação do celular e da mensagem, Faz o envio da mensagem
If lRet
lRet := SetZap(cCelular, cMsg, cAnexo)
EndIf
RestArea(aArea)
Return lRet
Static function SetZap(cCelular ,cMsg, cAnexo)
Local oOBJ
Local cStrJson
Local lRet := .F.
Local nTimeOut := 120
Local aHeadOut := {}
Local cHeadRet := ""
Local cPostParms := ""
Local cURL := SuperGetMV("MV_ZAPURL", .T., "http://api.connectzap.com.br/sistema")
Local cSession := Alltrim(SuperGetMV("MV_ZSESSAO", .T., "SEUTOKEN"))
Local xNome
Local nZ := 0
cMsg := EncodeUTF8(Replace(cMsg, Chr(10), ""))
cCelular := Alltrim(cCelular)
//Se a pasta temporária não existir, cria
If ! ExistDir("C:\temp")
MakeDir("C:\temp")
EndIf
//Se a pasta de arquivos não existir, cria
If ! ExistDir("C:\temp\files")
MakeDir("C:\temp\files")
EndIf
//Faz a conexão conforme o token do parâmetro
cStrJson := U_fStatus(cSession)
Sleep(2000)
FWJsonDeserialize(cStrJson, @oOBJ)
cStatus := oOBJ:Status:state
//Se não estiver conectado
If cStatus $ "NOTFOUND|CLOSED|" .OR. cStatus == "DISCONNECTED"
//Enquanto não estiver checado a informação de QRCode, reinicializa
While !("QRCODE" $ U_fStatus(cSession))
cStrJson := U_fStart(cSession)
Sleep(3000)
//Se estiver conectado, encerra o laço
If "CONNECTED" $ cStrJson
Exit
EndIf
EndDo
EndIf
//Retirando o -enter- da mensagem
cMsg := Alltrim(Replace(cMsg, CRLF, ""))
//Se houver anexo
If ! Empty(cAnexo)
cUrl += "/sendFileBase64"
aAdd(aHeadOut, 'Accept: application/json')
aAdd(aHeadOut, 'Content-Type: application/json')
//Se tiver barra no anexo
If "/" $ cAnexo
xNome := SubStr(cAnexo, RAt("/", cAnexo) + 1, Len(cAnexo))
__CopyFile(cAnexo, "C:\temp\files" + SubStr(cAnexo, RAt("/", cAnexo), Len(cAnexo)))
cAnexo := Encode64(, "C:\\temp\\files\\" + SubStr(cAnexo, RAt("/", cAnexo) + 1, Len(cAnexo)), .F., .F.)
Else
xNome := SubStr(cAnexo, RAt("\", cAnexo) + 1, Len(cAnexo))
__CopyFile(cAnexo, "C:\temp\files" + SubStr(cAnexo, RAt("\", cAnexo), Len(cAnexo)))
//Percorre o nome do arquivo, e define \ na última \
For nZ:= 1 to Len(cAnexo)
If subs(cAnexo,nZ,1) == '\'
nPosIni := nZ + 1
EndIf
Next nZ
cAnexo := Encode64(,"C:\\temp\\files\\" + SubStr(cAnexo, nPosIni, Len(cAnexo)), .F., .F.)
EndIf
//Define os parâmetros do Post
cPostParms := '{'
cPostParms += '"SessionName":"' + cSession + '",'
cPostParms += '"phonefull":"' + cCelular + '",'
cPostParms += '"base64":"' + cAnexo + '",'
cPostParms += '"originalname":"' + xNome + '",'
cPostParms += '"caption":"' + cMsg + '"'
cPostParms += '}'
//Senão, define como mensagem de envio de texto
Else
cUrl += "/sendText"
aAdd(aHeadOut, 'Accept: application/json')
aAdd(aHeadOut, 'Content-Type: application/json')
cPostParms := '{'
cPostParms += '"SessionName":"'+ cSession + '",'
cPostParms += '"phonefull":"' + cCelular + '",'
cPostParms += '"msg":"' + Alltrim(cMsg) + '"'
cPostParms += '}'
EndIf
//Usa o Post enviando as parametrizações acima
cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
sleep(3000)
//Se o retorno não for nulo, verifica se o status é conectado ou fechado
If cReturn != Nil
lRet := (oOBJ:Status:state == "CONNECTED") .OR. (oOBJ:Status:state == "CLOSED")
Else
lRet := .F.
EndIf
//Se o retorno não estiver correto
If !lRet
//Se não for forma automática, mostra a tela de conexão novamente, senão mostra mensagem no console.log
If !lAuto
u_ConnZap()
Else
fConOut("CONNECTZAP = Problema ao sincronizar com o Connectzap. Utilize o Painel de integração no protheus ou no site (painel.connectzap.com.br).")
EndIf
//Realiza um novo envio
cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
Sleep(3000)
//Verifica se teve sucesso no envio
FWJsonDeserialize(cReturn, @oOBJ)
lRet := "success" $ cReturn
EndIf
Return lRet
// https://centraldeatendimento.totvs.com/hc/pt-br/articles/360041301114-MP-ADVPL-Como-Ativar-a-fun%C3%A7%C3%A3o-FWLogMsg-
Static Function fConOut(cTexto)
FWLogMsg("INFO", /*cTransactionId*/, "CONOUT", /*cCategory*/, /*cStep*/, /*cMsgId*/, cTexto, /*nMensure*/, /*nElapseTime*/, /*aMessage*/)
Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.