Construindo um bot com Python RPA para interagir com sistemas desktop
Tags: braziliandevs, rpa, python, botcity
Published: 2023-09-20 23:14:50

Você já deve conhecer ferramentas e frameworks que podem te ajudar a construir automações para interagir com sistemas Web. Mas e quando se trata de **sistemas desktop**, como você faz? Vim te mostrar como utilizar o framework Open Source da [BotCity](https://botcity.dev/) com diversas funcionalidades para você construir a sua automação com Python RPA.

## Preparando o ambiente de desenvolvimento

- [Crie a sua conta gratuita](https://developers.botcity.dev/signup) na plataforma da BotCity;
- Acompanhe o passo a passo da instalação nas orientações da documentação de [pré-requisitos](https://documentation.botcity.dev/pt/getting-started/prerequisites/) e da [instalação do SDK BotCity Studio](https://documentation.botcity.dev/pt/getting-started/botcity-studio-sdk/);
- Instale o [Sicalc](https://drive.google.com/drive/folders/1gpmqV3l0VuLEg1d8kOFFFpzq2gopq66U?usp=sharing) (caso queira seguir o tutorial com o mesmo programa de exemplo utilizado).
    - o Sicalc desktop é utilizado de maneira offline, então não será gerada uma Nota Fiscal válida;
    - o Sicalc funciona para sistema operacional Windows, contudo todo o processo e uso do framework pode ser aplicado também para distribuições Linux e MacOS sem problemas;
    - ao abrir o Sicalc pela primeira vez, será solicitado um código de município. Para o nosso exemplo, vamos colocar o 7107 que representa São Paulo, mas poderia ser qualquer outro.

## Primeiros passos do desenvolvimento do nosso robô

### Criando o projeto a partir do template da BotCity

No terminal do seu computador, dentro da pasta em que você deseja criar o projeto, execute o seguinte comando para instalarmos o pacote [cookiecutter](https://cookiecutter.readthedocs.io/en/stable/) (porque os templates oferecidos foram criados a partir dele).

Para instalarmos o pacote do `cookiecutter`, vamos executar o seguinte comando:

```python
python -m pip install --upgrade cookiecutter
```

Para criarmos um projeto usando o modelo, vamos invocar o `cookiecutter` e fornecer como argumento a URL do repositório onde os templates da BotCity estão localizados:

```python
python -m cookiecutter https://github.com/botcity-dev/bot-python-template/archive/v2.zip
```

Durante a execução deste comando, você precisará informar duas coisas:

- **project_type:** refere-se ao tipo de projeto que deseja criar. O template fornecerá algumas opções, mas neste caso, vamos escolher o número 1, pois vamos construir um bot Desktop;
- **bot_id:** refere-se a uma identificação que você deseja dar para o seu projeto. Neste exemplo, chamaremos de bot-sicalc.

Após o término do processo acima, uma nova pasta chamada `bot-sicalc` estará disponível. Você pode entender um pouco mais sobre a estrutura e como ele funciona com as orientações na documentação.

### Instalando as dependências do seu projeto

Normalmente, projetos de desenvolvimento podem ter dependências como os frameworks utilizados, pacotes importantes, entre outros. O nosso também tem dependências e você poderá identificá-las no arquivo `requirements.txt`. Você notará que há pelo menos essas duas:

```powershell
botcity-framework-core # framework de desenvolvimento da automação
botcity-maestro-sdk # API do BotCity Maestro para orquestração da automação

```

Antes de rodar o seu projeto, é importante fazermos a instalação dessas dependências. E para isso, devemos executar o seguinte comando:

```powershell
pip install --upgrade -r requirements.txt
```

## Primeira execução do nosso robô

Se você entrar no arquivo bot.py, perceberá que já temos um código pré-pronto e alguns comentários com orientações importantes sobre como fazer alguns dos próximos passos.

### Entenda o que o código está fazendo

Identifique as linhas com os comandos abaixo. Elas estão importando as bibliotecas que mencionamos do arquivo `requirements.txt` para que consiga utilizá-las no seu código.

```python
from botcity.core import DesktopBot
from botcity.maestro import *

```

E nas linhas abaixo, estamos criando um objeto para o bot com o framework Desktop e pedindo para ele abrir um navegador no site da BotCity.

```python
bot = DesktopBot()
bot.browse("<http://www.botcity.dev>")

```

### Execute o robô

Para ver o robô sendo executado, você pode fazer isso pela IDE que estiver utilizando ou na linha de comando com o seguinte:

```python
python bot.py

```

Deu certo? Abriu o navegador do seu computador com o site da BotCity? Aqui está um exemplo de qual seria o resultado esperado após essa execução:


![Print da tela inicial do site da BotCity.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1f86m9xbdt9o1euc4n3s.png)

> 💡 Uma dica interessante é você testar se o que está acontecendo é o esperado. Tente fazer essa prática de testar aos poucos para você entender o que está acontecendo. Isso também facilitará no entendimento caso algum erro ocorra.

### Como lidar com os erros

Caso não tenha funcionado de primeira, não se preocupe. É normal você passar por alguns erros quando está desenvolvendo algo pela primeira vez ou aprendendo algo do zero. Te convido a acompanhar este [post sobre alguns erros](https://dev.to/botcitydev/os-desafios-do-hello-world-criando-meu-primeiro-bot-com-python-rpa-utilizando-botcity-1a0p) que podem acontecer e te encorajar a compartilhar os erros que você teve e como os resolveu.

Caso esteja com alguma outra dificuldade no desenvolvimento, fique à vontade para entrar em nosso [fórum](https://community.botcity.dev/) ou em nossa [comunidade no slack](https://join.slack.com/t/communitybotcitydev/shared_invite/zt-1yvjzlce9-esRSoWfAIXPse4L21bzjAQ) para pedir ajuda e tirar suas dúvidas.

## Desenvolvendo o robô desktop

Para esse desenvolvimento, vamos utilizar o BotCity Studio, que através de visão computacional, nos ajudará com o mapeamento dos itens na tela do sistema utilizado para que nosso robô possa interagir com ele.

### Carregando o projeto BotSicalc no BotCity Studio

Mantenha a sua IDE aberta com o seu projeto. Abra também o BotCity Studio e, no menu superior, escolha a opção "File" e, em seguida, "Load Project".

![print do menu file aberto dentro do BotCity Studio mostrando a opção "load project" em destaque](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1vr7ji29k5ouh0vm7c61.png)

Uma nova janela se abrirá e, nela, você deverá clicar em "browse" e identificar onde está a pasta do seu projeto. 

![print da janela "load project" aberta](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xyvj6jvmymhpi40nsch5.png)

Em seguida, identifique o arquivo com extensão `.botproj`. Selecione este arquivo para o BotCity Studio carregar o seu projeto. No caso deste exemplo, o arquivo será “bot-sicalc.botproj”. Após isso, clique em "Open" e, em seguida, "Launch". Pronto, você já tem seu projeto carregado no BotCity Studio.

### Prosseguindo no desenvolvimento

Para os próximos passos, vamos manter as duas ferramentas abertas, a sua IDE e o BotCity Studio, para que os códigos sejam sincronizados em ambas as plataformas.

Até este momento, mantivemos apenas o seguinte código no meu arquivo "bot.py". Perceba que deletamos a linha `bot.browse("http://www.botcity.dev")`, pois ela não será mais necessária.

```python
# Import for the Desktop Bot
from botcity.core import DesktopBot

# Import for integration with BotCity Maestro SDK
from botcity.maestro import *

# Disable errors if we are not connected to Maestro
BotMaestroSDK.RAISE_NOT_CONNECTED = False

def main():

    bot = DesktopBot()
        

def not_found(label):
    print(f"Element not found: {label}")

if __name__ == '__main__':
    main()
```

Ao salvar esse código na minha IDE, ao clicar novamente no BotCity Studio, você perceberá que também foi atualizado.

Abaixo da linha `bot = DesktopBot()`, vamos adicionar o comando execute para o robô abrir o Sicalc, conforme código abaixo:

```python
bot.execute(r"C:\Program Files (x86)\Programas RFB\Sicalc Auto Atendimento\SicalcAA.exe")
```

Uma dica interessante é você testar se o que está acontecendo é o esperado. Neste momento, por exemplo, podemos salvar o código e **executar o robô pela IDE** para verificarmos se, de fato, o comando funcionará. Tente fazer essa prática de testar aos poucos para você entender o que está acontecendo. Isso também facilitará no entendimento caso algum erro ocorra.

Se essa etapa funcionar, o robô executará e o Sicalc ficará nessa tela:

![print da tela inicial do sicalc aberta](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57gu0xywqgvomnuxxl5e.png)

> 💡 Antes de seguir em frente, lembre-se de deixar o cursor logo abaixo da linha de código que o bot abre o Sicalc no BotCity Studio. Isso é porque ele vai começar a gerar código para você utilizar em seu projeto. E o código será adicionado exatamente onde o cursor estiver posicionado.

### Identificando pop-up da tela inicial

Ao executarmos o aplicativo Sicalc pela primeira vez, será exibido um pop-up contendo um aviso. A tarefa será identificarmos essa janela e fechá-la logo em seguida.

A estratégia que vamos utilizar é procurar por uma âncora e realizar um clique relativo no botão **Continuar**.

Voltando ao BotCity Studio, no arquivo `bot.py` posicionamos o cursor na linha onde o código deve ser gerado.

Na aba `UI`, clicamos próximo ao elemento que queremos identificar e a ferramenta dará um zoom para selecionarmos o elemento. A seleção do nosso recorte é feita com o mouse, clicando e arrastando até selecionar dessa maneira:

![print da tela esclarecimento ao contribuinte e a janela solicitando as informações de nome, ação e modo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5l5ee8q06hrjwt7osid.png)

> 💡 Se o zoom não estiver adequado para você fazer a seleção necessária, basta pressionar a tecla `esc` no seu teclado e refazer o clique próximo ao elemento.

Após selecionado, vamos preencher os campos `Name` com o valor "popup-esclarecimento" e `Action` com o valor "Click_relative". O campo "mode" deve ser mantido com a opção "Image" selecionada. Em seguida, clicamos no botão **Submit**.

![print da tela para preenchimento de nome e ação ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/un6lirv0ge7gy6yg7m42.png)

Após clicar em Submit, ao arrastar o mouse, você perceberá que uma linha vermelha acompanhará o movimento. Nós devemos dizer para o BotCity Studio onde deverá ser o clique relativo. Neste caso, será o botão "Continuar". Então arrastamos o cursor do mouse até esse botão e clicamos nele.

Com essa etapa concluída, o seguinte trecho de código será gerado pelo BotCity Studio:

```python
# Abre o aplicativo do SiCalc
bot.execute(path_sicalc)

if not bot.find( "popup-esclarecimento", matching=0.97, waiting_time=10000):
    not_found("popup-esclarecimento")
bot.click_relative(195, 211)
```

Lembre-se de salvar o código no BotCity Studio, que pode ser pelo atalho "ctrl + s". Isso fará com que a sua IDE também seja atualizada com o novo código gerado.

Ao rodarmos novamente esse código pela IDE, depois de abrir o aplicativo Sicalc, o bot realizará o clique relativo no elemento que foi selecionado acima. A partir disso, conseguimos continuar o processo de preenchimento da DARF.

### Selecionando opções do menu

No aplicativo do Sicalc, precisaremos acessar a opção **Funções** do menu e, em seguida, a opção **Preenchimento de DARF**. Então devemos tirar um novo print da tela do Sicalc e, no BotCity Studio, podemos fazer o recorte desses elementos.

Vamos começar pelo item "Funções". E para essa opção, vamos dar o nome `funcoes` e a action será `click`. A opção mode deve continuar como `image`.

![print mostrando em destaque a parte de funções selecionada com nome e ação preenchidos conforme texto do tutorial](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r71zl30opwguzvl46nhp.png)

Com o sub-menu de opções de Funções aberto, vamos tirar um novo print para fazermos a seleção e ação Click na opção Preenchimento de DARF, chamando-a de preenchimento-darf.

![print mostrando em destaque a parte de preenchimento de DARF selecionada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmctoyv42tzi2dmhkygr.png)

O código gerado pelo BotCity Studio será parecido com o código abaixo:

```python
if not bot.find( "funcoes", matching=0.97, waiting_time=10000):
    not_found("funcoes")
bot.click()

if not bot.find( "preenchimento-darf", matching=0.97, waiting_time=10000):
    not_found("preenchimento-darf")
bot.click()
```

### Preenchendo do formulário inicial

Após a etapa anterior, será aberto um formulário inicial onde precisaremos preencher somente o código da receita.

Novamente, vamos tirar um novo print da tela atualizada para fazermos a seleção do elemento correto no BotCity Studio.

Nesse caso, vamos encontrar a âncora do input **Cód. Receita** e fazer um clique relativo dentro do campo, que fica logo na frente para ser preenchido:

![print com a tela em destaque para a parte de código da receira selecionada e a janela para preenchimento de nome e ação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qsdqjdqj58xtlvow0us5.png)

Após encontrar e clicar no campo referente a esse elemento, utilizamos o comando `paste()` para inserir nosso dado. Em seguida vamos utilizar o comando `tab()` para avançarmos para o próximo formulário.

Você pode adicionar o código com o comando `paste()` e com o comando `tab()` diretamente no BotCity Studio e salvar para atualizar na sua IDE, ou adicionar diretamente pela IDE. Apenas lembre-se de salvar o código para ambas as ferramentas seguirem atualizadas.

Abaixo, o exemplo de como poderá ficar esse trecho do código:

```python
if not bot.find( "codigo-receita", matching=0.97, waiting_time=10000):
    not_found("codigo-receita")
bot.click_relative(128, 10)

# Inserindo no campo um código fictício
bot.paste("5629")

# Tecla "tab" avança para o próximo formulário
bot.tab()
```

### Preenchendo os dados da DARF

Ao executarmos o código acima, será aberto um segundo formulário. E nele, devemos inserir os demais dados da DARF. Neste momento, a tela será parecida com essa:

![print da tela do sicalc para preenchimento de informações conforme descrito no tutorial](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shiq852wd42qa3ww6a78.png)

Atualizando o print da tela no BotCity Studio, vamos precisar encontrar os elementos referentes ao período de apuração (campo PA) e ao valor do documento (campo Valor em Reais) e fazer um clique relativo nos seus respectivos campos:

![print da tela do sicalc com destaque no campo PA](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6v6ngurqm7a7wxjoepnv.png)

![print da tela do sicalc com destaque no campo valor em reais](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r1kzol0ddykmtdtxsbzy.png)

E da mesma forma que fizemos anteriormente, vamos utilizar o comando `paste()` do framework para nos ajudar a digitar o valor dentro dos campos que estamos preenchendo neste momento. Perceba como o código ficará nessa etapa:

```python
if not bot.find( "periodo-apuracao", matching=0.97, waiting_time=10000):
    not_found("periodo-apuracao")
bot.click_relative(15, 25)
# Inserindo PA
bot.paste("310120")

if not bot.find( "valor", matching=0.97, waiting_time=10000):
    not_found("valor")
bot.click_relative(17, 27)
# Inserindo valor
bot.paste("10000")
```

O próximo passo é clicarmos no botão **Calcular**, para conseguirmos acessar o formulário final. Temos duas formas de resolver essa etapa. A primeira delas seria adicionar o comando `enter()`, pois essa ação no teclado de apertar a tecla enter, após o preenchimento dos campos, aciona o botão Calcular.

```python
bot.enter()
```

Outra forma é seguir pela visão computacional e apoio do BotCity Studio. Então tiramos um novo print, selecionamos o botão **Calcular** e informamos que a ação deverá ser a de `click`.

![print da tela do sicalc com destaque no botão calcular e a janela para preencher o nome do botão e a ação de clique](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fzytwxzrehfpcgem95d9.png)

```python
if not bot.find( "calcular", matching=0.97, waiting_time=10000):
    not_found("calcular")
bot.click()
```

### Preenchendo formulário final

Após o processo de cálculo ser finalizado, precisamos acessar o formulário final. Para isso, vamos clicar no botão **DARF**:

![print sicalc com destaque onde está o botão DARF e a janela para preencher o nome e ação para o botão](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ynfhq36iihoau1z85bfa.png)

```python
if not bot.find( "botao-darf", matching=0.97, waiting_time=10000):
    not_found("botao-darf")
bot.click()
```

Com o formulário final aberto, vamos atualizar o print da tela no BotCity Studio para fazermos a seleção dos últimos elementos:

![print do sicalc mostrando o calculo feito e a janela para preencher nome, telefone e outros dados](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apmaedchox1x02bsbmy7.png)

Para essa etapa, vamos seguir utilizando a mesma estratégia das etapas anteriores, procurando pela âncora e fazendo um clique relativo no campo que precisamos preencher.

Os dados que precisamos preencher nesse último formulário serão:

- Nome: Petrobras
- Telefone: 1199991234
- CNPJ: 33000167000101
- Referência: 0

Com todas as âncoras mapeadas, o código gerado será semelhante a esse:

```python
if not bot.find( "nome", matching=0.97, waiting_time=10000):
    not_found("nome")
bot.click_relative(0, 24)

# Inserindo nome no campo
bot.paste("Petrobras")

if not bot.find( "telefone", matching=0.97, waiting_time=10000):
    not_found("telefone")
bot.click_relative(1, 27)

# Inserindo telefone no campo
bot.paste("1199991234")

if not bot.find( "cnpj", matching=0.97, waiting_time=10000):
    not_found("cnpj")
bot.click_relative(123, 9)

# Inserindo CNPJ no campo
bot.paste("33000167000101")

if not bot.find( "referencia", matching=0.97, waiting_time=10000):
    not_found("referencia")
bot.click_relative(121, 8)

# Inserindo referência no campo
bot.paste("0")
```

### Salvando o arquivo

O último passo é salvarmos o arquivo da DARF que será gerado. Ainda na tela do último formulário exibido, após preencher os dados vamos clicar no botão **Imprimir**:

![print do sicalc com o botão imprimir em destaque e a janela para configurar a ação de clique](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z1aduay0n8cct8fj56ra.png)

```python
if not bot.find( "imprimir", matching=0.97, waiting_time=10000):
    not_found("imprimir")
bot.click()
```

Clicando no botão, será aberto a janela do gerenciador de arquivos do Windows, caso não tenha diretamente uma impressora configurada:

![print da tela para salvar o arquivo pdf da DARF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8g2153e8osgulmiuespn.png)

Neste caso, vamos encontrar a referência dessa janela e clicar para que ela fique em foco:

![print com zoom na tela "save print output as"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yrlo9z8e0dvsuninc3te.png)

Após isso, podemos utilizar o `paste()` para informar qual será o caminho do arquivo e o `enter()` para salvar. Abaixo temos o exemplo de como poderá ficar o código:

```python
if not bot.find( "janela-salvar", matching=0.97, waiting_time=10000):
    not_found("janela-salvar")
bot.click()

# Inserindo o caminho do arquivo
bot.paste(r"C:\Users\username\Documents\DARF.pdf")
bot.enter()
```

### Finalizando o processo

Após salvar o arquivo, nosso processo está finalizado!

Podemos agora utilizar o comando `wait()` para aguardar 2 segundos e garantir que tudo foi salvo e depois o comando `alt_f4()` para fecharmos o formulário e a janela do Sicalc.

```python
# Aguarda 2 segundos
bot.wait(2000)

# Fechando janela do formulário
bot.alt_f4()

# Fechando app do SiCalc
bot.alt_f4()
```

<aside>
💡 Essa estratégia do comando `wait()` pode ser útil em algumas ocasiões, quando algum processo ou comando pode levar um tempo para acontecer ou atualizar antes de ir para a próxima etapa do processo. Então analise se no seu projeto isso pode te ajudar e utilize da maneira que for melhor para você.

</aside>

### Código final

Caso queira validar como o código final deste tutorial poderá ficar, segue abaixo o conteúdo do arquivo `bot.py` do projeto:

```python
def main():
    bot = DesktopBot()

    # Implement here your logic...
    # Caminho onde está o executável SiCalc
    path_sicalc = r"C:\Program Files (x86)\Programas RFB\Sicalc Auto Atendimento\SicalcAA.exe"

    # Abre o aplicativo do SiCalc
    bot.execute(path_sicalc)

    if not bot.find( "popup-esclarecimento", matching=0.97, waiting_time=10000):
        not_found("popup-esclarecimento")
    bot.click_relative(195, 211)

    if not bot.find( "funcoes", matching=0.97, waiting_time=10000):
        not_found("funcoes")
    bot.click()

    if not bot.find( "preenchimento-darf", matching=0.97, waiting_time=10000):
        not_found("preenchimento-darf")
    bot.click()

    if not bot.find( "codigo-receita", matching=0.97, waiting_time=10000):
        not_found("codigo-receita")
    bot.click_relative(128, 10)

    # Inserindo no campo um código fictício
    bot.paste("5629")

    # Tecla "tab" avança para o próximo formulário
    bot.tab()

    if not bot.find( "periodo-apuracao", matching=0.97, waiting_time=10000):
        not_found("periodo-apuracao")
    bot.click_relative(15, 25)
    # Inserindo PA
    bot.paste("310120")

    if not bot.find( "valor", matching=0.97, waiting_time=10000):
        not_found("valor")
    bot.click_relative(17, 27)
    # Inserindo valor
    bot.paste("10000")

    if not bot.find( "calcular", matching=0.97, waiting_time=10000):
        not_found("calcular")
    bot.click()

    if not bot.find( "botao-darf", matching=0.97, waiting_time=10000):
        not_found("botao-darf")
    bot.click()

    if not bot.find( "nome", matching=0.97, waiting_time=10000):
        not_found("nome")
    bot.click_relative(0, 24)
    # Inserindo nome
    bot.paste("Petrobras")

    if not bot.find( "telefone", matching=0.97, waiting_time=10000):
        not_found("telefone")
    bot.click_relative(1, 27)
    # Inserindo telefone
    bot.paste("1199991234")

    if not bot.find( "cnpj", matching=0.97, waiting_time=10000):
        not_found("cnpj")
    bot.click_relative(123, 9)
    # Inserindo CNPJ
    bot.paste("33000167000101")

    if not bot.find( "referencia", matching=0.97, waiting_time=10000):
        not_found("referencia")
    bot.click_relative(121, 8)
    # Inserindo referência
    bot.paste("0")

    if not bot.find( "imprimir", matching=0.97, waiting_time=10000):
        not_found("imprimir")
    bot.click()

    if not bot.find( "janela-salvar", matching=0.97, waiting_time=10000):
        not_found("janela-salvar")
    bot.click()

    # Inserindo path do arquivo
    bot.paste(r"C:\Users\username\Documents\DARF.pdf")
    bot.enter()

    # Aguarda 2 segundos
    bot.wait(2000)

    # Fechando janela do formulário
    bot.alt_f4()

    # Fechando app do SiCalc
    bot.alt_f4()

def not_found(label):
    print(f"Element not found: {label}")

if __name__ == '__main__':
    main()
```

## Para explorar mais comandos do framework desktop
Tem vários outros comandos que você pode explorar e utilizar para construir suas automações desktop. Acompanhe pelo link da [documentação](https://documentation.botcity.dev/pt/frameworks/desktop/).

## Compartilhe com a comunidade o seu projeto
Após construir a sua automação, que tal compartilhar com a comunidade o que aprendeu e o robô que você construiu? Basta compartilhar com o [repositório de robôs da comunidade](https://repository.botcity.dev/).