Por que a BotCity?

A BotCity é uma empresa brasileira, que teve a ideia de criar um framework que junta diversos tipos de automações em apenas um framework, mas mais que isso, oferece um gerenciador gráfico e web para que seja possível criar suas automações e tem soluções que facilitam o monitoramento e gerenciamento das automações usadas.

Isso facilita muito para gerenciar um projeto, pois atualizar um projeto é sempre algo trabalhoso e no caso a preocupação se torna apenas os pacotes da BotCity.

No caso, vamos usar para verificar os filmes que estão sendo divulgados no site do Cinema em POA.

0 - Preparando o ambiente
1 - Código Completo
2 - Como executar pela BotCity?
3 - Conclusão

0 - Preparando o ambiente

Antes de tudo, precisamos criar nosso ambiente virtual, utilizando o comando virtualenv e então ativá-lo:

virtualenv --python=3 venv
source venv

Depois disso, podemos clonar o repositório onde o código está:

git clone https://github.com/lcsvillela/cinema-em-poa-botcity.git

E então instalar no nosso ambiente venv as dependências:

pip install -r requirements.txt

1 - Análise do Código Completo

No trecho abaixo importamos as bibliotecas que iremos utilizar:

1
2
3
4
5
6
7
8
from botcity.plugins.http import BotHttpPlugin
from botcity.plugins.telegram import BotTelegramPlugin
from bs4 import BeautifulSoup as bs
from requests.exceptions import ConnectionError
from datetime import datetime
import os
from botcity.maestro import *
from time import sleep

No código abaixo, criamos uma classe chamada CinemaEmPoa, que tem a sua inicialização definida com três parâmetros, o self que é um objeto que é acessível em toda a classe, a url que iremos utilizar e o token de acesso do Telegram (obviamente este token não funciona, use o seu).

Aproveitamos para declarar as variáveis que precisaremos em algum momento utilizando o self para que sejam globais e também executamos métodos quando a classe é instanciada para que seja executado o bot. Podemos observar a linha 14 extraindo os dados do site do Cinema em Poa, na 15 o método que cria a estrutura da mensagem e por fim o início da execução completa com o método start.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CinemaEmPoa:
    def __init__(
        self,
        url="https://cinemaempoa.com.br/program",
        token="8123421335:ABFwVGLoyMxTpPXWTtG7Ugl136L\_1W8s9wE",
    ):
        self.http = BotHttpPlugin(url)
        self.telegram = telegram = BotTelegramPlugin(token=token)
        self.page = None
        self.message\_films = None
        self.films = {}
        self.last\_time = None
        self.last\_update\_id = self.load\_offset()
        self.get\_data()
        self.create\_message()
        self.start()

O telegram não faz o controle da fila de requisições que o bot recebe, então em alguns frameworks precisamos nós mesmos fazer o controle, salvando o último ID processado em um arquivo, no caso o last_id.data:

1
2
3
4
5
6
7
8
9
    def save\_offset(self, offset):
        with open("last\_id.data", "w") as f:
            f.write(str(offset))

    def load_offset(self):
        if os.path.exists("last_id.data"):
            with open("last_id.data", "r") as f:
                return int(f.read())
        return None

Aqui apenas pegamos o código da página e colocamos no parser do BeautifulSoup para facilitar a extração futura dos dados:

1
2
3
4
5
6
    def get\_page(self):
        try:
            source = self.http.get().content
            self.page = bs(source, "html.parser")
        except ConnectionError:
            print("error: connection")

Quando abrimos a página do Cinema em POA e vamos na programação, vemos que temos a estrutura de datas e depois filmes do dia, então a ideia aqui é basicamente extrair os blocos que englobam a data e os filmes relacionados naquela data e então iterar com um for para conseguir estruturar a informação em dois níveis: um dicionário tem a chave da data e ele então armazena um vetor de dicionários que tem as informações de cada filme.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    def get\_films(self):
        _dates = self.page.find_all(class_="col-md-8")

        for _date in _dates:
            _date_text = _date.find(class_="mb-2 fs-5").getText()
            self.films[f"{_date_text}"] = []
            _films = _date.find_all(class_="mb-1")
            for _film in _films:
                _location = _film.getText().strip().split(" ")[-1]
                _title_film = _film.getText().strip().split(" ")[1:-1]
                _hour = _film.getText().strip().split(":")[0]
                _title_film = " ".join(_title_film)
                self.films[f"{_date_text}"].append(
                    {"location": _location, "title": _title_film, "hour": _hour}
                )

Como temos a informação dos filmes estruturada, podemos agora organizá-la do jeito que queremos, usando o join podemos juntar todas as strings que temos e ter uma mensagem para o usuário.

1
2
3
4
5
6
7
8
9
10
11
12
    def create\_message(self):

        self.message_films = ""
        for films in self.films.keys():
            for film in self.films[f"{films}"]:
                location = film["location"]
                title = film["title"]
                hour = film["hour"]

                self.message_films += " ".join(
                    [films, "-", hour, "-", title, "-", location, "\n"]
                )

Aqui temos a execução da get_data, que ao fim da execução, armazena a data de sua execução, que utilizaremos para economizar nossa banda.

Na execução do método bot_telegram, temos na linha 8 o comando que verifica as novas requisições e utiliza o ID que salvamos anteriormente para que não fique repetindo as requisições anteriores.

Na linha 10 verificamos se temos alguma requisição, caso contrário retornamos ao ponto de chamada e não é executado o resto do código, na linha 13, temos um for que iterará por todas as requisições, verificando o seu conteúdo e casando as strings de comando que foram especificadas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    def get\_data(self):
        self.get\_page()
        self.get\_films()
        self.last\_time = datetime.now()

    def bot_telegram(self):

        updates = self.telegram.bot.get_updates(offset=self.last_update_id)

        if len(updates) == 0:
            return

        for update in updates:
            message = update.message
            chat_id = message.chat.id
            text = message.text

            if text == "/start":
                self.telegram.bot.send_message(
                    chat_id,
                    "Este é o bot do Cinema em POA feito com o BotCity! Agora você pode ver todos os filmes que passarão na cidade :D\n/filmes - para ver a lista completa de filmes\n/ajuda - veja o que este bot pode fazer!\n\n Mais informações em www.lcsvillela.com",
                )

            elif text == "/status":
                self.telegram.bot.send_message(
                    chat_id, "O sistema está rodando perfeitamente! 🚀"
                )

            elif text == "/ajuda":
                self.telegram.bot.send_message(
                    chat_id, "Use /filmes para ver todos os filmes que estão passando!"
                )

            elif text == "/filmes":
                self.telegram.bot.send_message(chat_id, self.message_films)

            self.last_update_id = update.update_id + 1
            self.save_offset(self.last_update_id)

O inicializador tem este método, que tem um loop indefinido e que verifica se faz mais de um dia que o site do Cinema em POA foi extraído, executa sempre o método do bot do telegram e espera um segundo para o próximo loop.

1
2
3
4
5
6
7
8
    def start(self):

        while True:
            if (datetime.now() - self.last_time).days:
                self.get_data()
                self.create_message()
            self.bot_telegram()
            sleep(1)

2 - Como Executar pela BotCity?

3 - Conclusão

A BotCity é uma plataforma completa, que oferece diversos tipos de RPA possíveis em apenas um framework e possibilita o gerenciamento e monitoramento, de forma intuitiva para que usuários possam executar automações existentes. Para mais informações, veja a documentação da BotCity