top of page
Blog SO Labs na Íntegra
Writer's pictureDaniel Sanches

AWS, Telegram bot on AWS, Datalake na AWS (API Gateway + Lambda + S3 Bucket + Athena)

Updated: Feb 24, 2022


Neste artigo, mostrarei como criar um bot echo Telegram simples em webhooks escritos em Python 3 e implantá-lo no AWS Lambda usando a estrutura Serverless.



Atualmente, diferentes processos são suportados por vários bots de bate-papo. Alguns são projetados para guiar os usuários pelas várias etapas de um fluxo de processo complexo, mas também há muitos bots pequenos criados para ajudar os usuários em tarefas rotineiras triviais – ou mesmo apenas por diversão. Neste post, mostrarei como construir um bot do Telegram que funcionará como um encurtador de URL usando serverless.



Sobre o Telegram


O Telegram fornece chamadas de voz e vídeo criptografadas de ponta a ponta e chats "secretos" criptografados de ponta a ponta opcionais. Os bate-papos e grupos na nuvem são criptografados entre o aplicativo e o servidor, para que os ISPs e outros terceiros na rede não possam acessar os dados, mas o servidor do Telegram pode. Os usuários podem enviar mensagens de texto e voz, fazer chamadas de voz e vídeo e compartilhar um número ilimitado de imagens, documentos (2 GB por arquivo), localizações de usuários, adesivos animados, contatos e arquivos de áudio. Em janeiro de 2021, o Telegram ultrapassou 500 milhões de usuários ativos mensais. Foi o aplicativo mais baixado em todo o mundo em janeiro de 2021 com 1 bilhão de downloads globalmente no final de agosto de 2021.



Mensagens na íntegra de diversos tipos (tipagem - textos, vídeos, imagens, etc.).


Ementa de Desenvolvimento


- Configurar estrutura Serverles

- Obtenha credenciais da AWS

- Escrever back-end do Telegram Bot no Python 3

- Implantar back-end no Lambda

- Conecte seu back-end ao Telegram Bot

- Feito!


__________________________________________________________________________________


Steps de Desenvolvimento


Step 1: Criação de solução e arquitetura AWS para AWS, Telegram bot on AWS, Datalake na AWS (API Gateway + Lambda + S3 Bucket + Athena)



a. Separação de tudas partes macros que se comunicam por webhook - OLTP e OLAP. Temos:


- O gerenciamento de dados transacionais usando sistemas de computador é conhecido como OLTP (processamento de transações online).


- O termo OLAP refere-se a um conjunto de ferramentas voltadas para acesso e análise de dados, com objetivo final de transformar dados em informações capazes de dar suporte às decisões gerenciais de forma amigável e flexível ao usuário em tempo hábil.


b. Escolha do app de comunicação Telegram


- Telegram é um aplicativo gratuito de troca de mensagens. Aqui no Brasil, é considerado rival do famoso WhatsApp. Tão versátil quanto o verdinho, o Telegram tem versões para uso profissional, para celulares com sistema operacional iOS, Android e Windows. Ele também conta com uma versão Web, para ser usado no desktop.


c. Raw via AWS Lambda


- Camada de armazenamento dos dados brutos. ... Essa camada “normalmente” é construída sob infraestrutura com banco de dados relacionais (SQL Server, Oracle, etc), devido a sua característica de consumo, que normalmente é feita via API's e sistemas transacionais.


d. Raw via AWS S3 Bucket


- No console do Amazon S3, escolha seu bucket do S3, escolha o arquivo que deseja abrir ou fazer download, escolha Actions e, em seguida, Open ou Download. Se você estiver baixando um objeto, especifique onde deseja salvá-lo. O procedimento para salvar o objeto depende do navegador e do sistema operacional que você está usando.


d. Raw via AWS S3 Lambda (Enriched - Enriquecido)


- Mesmo processo via AWS Lambda citado logo acima, de forma enriquecida.


e. Raw via AWS S3 Bucket


- Mesmo processo via AWS S3 Bucket citado logo acima, de forma enriquecida.


f. Athena


- Fluxo de dados direcionado para o Athena - O Amazon Athena é um serviço de consultas interativas que facilita a análise de dados diretamente no Amazon Simple Storage Service (Amazon S3) usando SQL padrão. ... O Athena é escalado automaticamente, executando consultas em paralelo, o que acelera os resultados mesmo em conjuntos de dados grandes e consultas complexas.


Step 2: Disponibilidade de uso para o Telegram


Após baixar e instalar. Basta abrir o aplicativo, que o processo de configurar sua conta é bem simples, basta cadastrar seu número de celular com DDD. Ative o seu Telegram ou o Telegram da sua empresa e deixe-o pronto para uso.



Step 3: GitHub workflows, deploy - addev-routine


### Deploy em AWS utilizando servless e pacotes NPM do Node - projeto para o Telegram


-----------------------------------------------------------------------------------------------------------------------------------------------
-- DEPLOY - ADDEDV-ROUTINE
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: FUNÇÃO DE DEPLOY NO PROJETO TELEGRAM
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Deploy em AWS utilizando servless e pacotes NPM do Node - projeto para o Telegram

name: serverless-deploy
on:
  push:
    branches:
      - main
jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest
    steps:
      - name: checkout code
        uses: actions/checkout@v2
        with:
          ref: main
      - name: install node engine
        uses: actions/setup-node@v2-beta
        with:
          node-version: '12'
      - name: install node dependencies
        run: npm install
      - name: configure aws credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: sa-east-1
      - name: serverless deploy
        env:
          RAW_BUCKET: addev-raw
          TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
          TELEGRAM_ACCESS_TOKEN: ${{ secrets.TELEGRAM_ACCESS_TOKEN }}
        run: npm run-script deploy

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

Step 4: docs - event, addev-routine.json


### Arquivo JSON referenciando o usuário administrador e suas informações para o contexto grupo de Telegram.


-----------------------------------------------------------------------------------------------------------------------------------------------
-- EVENT - ADDEDV-ROUTINE
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: CRIAÇÃO DE EVENTO PARA ROTINA JSON
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Arquivo JSON referenciando o usuário administrador e suas informações para o contexto grupo de Telegram.

{
    "ok":true,
    "result":[{
            "update_id":920664170,
            "message": {
                "message_id":2,
                "from": {
                    "id":<user-id>,
                    "is_bot":false,
                    "first_name":"Andr\u00e9",
                    "last_name":"Perez",
                    "username":"andremarcosperez"
                },
                "chat": {
                    "id":-790291911,
                    "title":"Ebac Cloud Demo \u2601\ufe0f",
                    "type":"group",
                    "all_members_are_administrators":true
                },
                "date":1639580077,
                "text":"Ol\u00e1, mundo!"
            }
    }]
}

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

Step 5: AWS Athena - create-table - create-table - addev-routine.sql / query-table - addev-routine.sql


### Arquivo JSON referenciando o usuário administrador e suas informações para o contexto grupo de Telegram.


-----------------------------------------------------------------------------------------------------------------------------------------------
-- AWS ATHENA - CREATE TABLE  - ADDEDV-ROUTINE
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: CRIAÇÃO DE TABELA NO AWS ATHENA PARA A FINALIZAÇÃO DO FLUXO DE DADOS.
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Criação da tabela externa adddev_enriched, para formatação e fluxo de dados para o AWS S3 Bucket.

CREATE EXTERNAL TABLE `addev_enriched`(
  `message_id` bigint,
  `user_id` bigint,
  `user_is_bot` boolean,
  `user_first_name` string,
  `user_last_name` string,
  `user_username` string,
  `user_language_code` string,
  `chat_id` bigint,
  `chat_title` string,
  `chat_type` string,
  `chat_all_members_are_administrators` boolean,
  `text` string,
  `timestamp` string)
PARTITIONED BY (
  `date` string)
ROW FORMAT SERDE
  'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat'
OUTPUTFORMAT
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
  's3://addev-enriched/'

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Levantamento de consultas por dia, por usuário e por dia e tamanho das mensagens por usuário por dia.


-----------------------------------------------------------------------------------------------------------------------------------------------
-- AWS ATHENA - QUERY TABLE  - ADDEDV-ROUTINE
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: CONSULTA NA TABELA NO AWS ATHENA PARA A FINALIZAÇÃO DO FLUXO DE DADOS.
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Levantamento de consultas por dia, por usuário e por dia e tamanho das mensagens por usuário por dia.

-- Quantidade de mensagens por dia

SELECT "date", count(1) AS "message_amount" FROM "default"."addev_enriched" GROUP BY "date" ORDER BY "date" DESC;

-- Quantidade de mensagens por usuário por dia

SELECT user_id, user_username, "date", count(1) AS "message_amount" FROM "default"."addev_enriched" GROUP BY user_id, user_username, "date" ORDER BY "date" DESC;

-- Média do tamanho das mensagens por usuário por dia

SELECT user_id, user_username, "date", CAST(AVG(length(text)) AS INT) AS "average_message_length" FROM "default"."addev_enriched" GROUP BY user_id, user_username, "date" ORDER BY "date" DESC;

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

Step 6: Settings - aws_settings - addev-routine.py / telegram_settings - addev-routine.py


#### Criação de settings para o AWS, utilizando init.



-----------------------------------------------------------------------------------------------------------------------------------------------
-- AWS SETTINGS
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: AWS SETTINGS
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

#### Criação de settings para o AWS, utilizando init.

from settings.settings import Settings


class AWSSettings(Settings):

    def __init__(self):
        super().__init__()
        self.raw_bucket = 'addev-raw'
        self.enriched_bucket = 'addev-enriched'

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

#### Criação de settings para o AWS, utilizando init e setup.


-----------------------------------------------------------------------------------------------------------------------------------------------
-- AWS SETTINGS
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: AWS SETTINGS
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Criação de settings para o AWS, utilizando a função init e setup.

from settings.settings import Settings


class TelegramSettings(Settings):       

    def __init__(self):
        super().__init__()
        self.chat_id = None
        self.access_token = None
        self.__setup()

    def __setup(self):
        self.chat_id = TelegramSettings._load_variable(variable='TELEGRAM_CHAT_ID', default=None, cast=int)
        self.access_token = TelegramSettings._load_variable(variable='TELEGRAM_ACCESS_TOKEN', default=None, cast=str)

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

Step 7: enriched - addev-routine.py


### Funções para processo enriched.



-----------------------------------------------------------------------------------------------------------------------------------------------
-- ENRICHED
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: ENRICHED
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Funções para processo enriched. 

import json
from datetime import datetime

import boto3
import pendulum
import pyarrow as pa
import pyarrow.parquet as pq
from botocore.exceptions import ClientError

from util.log import Log
from settings.aws_settings import AWSSettings


def lambda_handler(event: dict, context: dict) -> bool:

    log = Log.setup(name='logger')
    aws_settings = AWSSettings()

    timezone = pendulum.timezone('America/Sao_Paulo')
    date = datetime.now(tz=timezone).strftime('%Y-%m-%d')
    timestamp = datetime.now(tz=timezone).strftime('%Y%m%d%H%M%S')

    try:

        raw_key = event['Records'][0]['s3']['object']['key']
        raw_bucket = event['Records'][0]['s3']['bucket']['name']

        enriched_bucket = aws_settings.enriched_bucket
        root_path = aws_settings.root_path

        client = boto3.client('s3')
        client.download_file(raw_bucket, raw_key, f"{root_path}/{raw_key.split('/')[-1]}")

        with open(f"{root_path}/{raw_key.split('/')[-1]}", mode='r', encoding='utf8') as fp:
            data = json.load(fp)
            data = data["message"]

        parsed_data = dict()
        for key, value in data.items():
            if key == 'from' or key == 'chat':
                for k, v in data[key].items():
                    parsed_data[f"{key if key == 'chat' else 'user'}_{k}"] = [v]
            else:
                parsed_data[key] = [value]
        parsed_data['date'] = [date]
        parsed_data['timestamp'] = [timestamp]

        try:
            table = pa.Table.from_pydict(mapping=parsed_data)
            pq.write_table(table=table, where=f'{root_path}/{timestamp}.parquet')
            client.upload_file(f"{root_path}/{timestamp}.parquet", enriched_bucket, f"date={date}/{timestamp}.parquet")
        except ClientError as exc:
            raise exc

        return True
    except Exception as exc:
        log.error(msg=exc)
        return False

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

Step 8: package - addev-routine.json


### Atribuições de desenvolvimento em GitHub


-----------------------------------------------------------------------------------------------------------------------------------------------
-- PACKAGE
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: PACKAGE
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Atribuições de desenvolvimento em GitHub

{
  "name": "addev-bot",
  "version": "1.0.0",
  "description": "Telegram bot for addev live demo",
  "repository": {
    "type": "git",
    "url": "git@github.com:daniel-sanches/addev-bot.git"
  },
  "scripts": {
    "deploy": "serverless deploy --verbose"
  },
  "author": "Daniel Sanches, danielnsanches@gmail.com",
  "dependencies": {
    "serverless": "2.42.0",
    "serverless-python-requirements": "5.1.1"
  }
}

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

Step 9: raw - addev-routine.py


### Configurações e script da camada raw.




-----------------------------------------------------------------------------------------------------------------------------------------------
-- RAW
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: CAMADA RAW
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Configurações e script da camada raw.

import json
import gzip
import requests
from datetime import datetime

import pendulum
import boto3
from botocore.exceptions import ClientError

from util.log import Log
from settings.aws_settings import AWSSettings
from settings.telegram_settings import TelegramSettings


def lambda_handler(event: dict, context: dict) -> dict:

    log = Log.setup(name='logger')
    aws_settings = AWSSettings()
    telegram_settings = TelegramSettings()

    timezone = pendulum.timezone('America/Sao_Paulo')
    date = datetime.now(tz=timezone).strftime('%Y-%m-%d')
    timestamp = datetime.now(tz=timezone).strftime('%Y%m%d%H%M%S')

    try:

        token = telegram_settings.access_token
        base_url = f"https://api.telegram.org/bot{token}"
        data = json.loads(event["body"])
        chat_id = data["message"]["chat"]["id"]

        if chat_id == telegram_settings.chat_id:

            client = boto3.client('s3')
            bucket = aws_settings.raw_bucket
            root_path = aws_settings.root_path

            try:
                with open(f"{root_path}/{timestamp}.json", mode='w', encoding='utf8') as fp:
                    json.dump(data, fp)
                client.upload_file(f"{root_path}/{timestamp}.json", bucket, f"{date}/{timestamp}.json")
            except ClientError as exc:
                raise exc

        else:

            text = "I can't talk to strangers, sorry mate!"
            data = {"text": text, "chat_id": chat_id}
            data = gzip.compress(json.dumps(data).encode('utf-8'))
            headers = {'content-type': 'application/json', 'content-encoding': 'gzip'}
            url = base_url + "/sendMessage"
            requests.post(url=url, data=data, headers=headers)

    except Exception as exc:
        log.error(msg=exc)

    finally:
        return dict(statusCode="200")

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

Step 10: README - addev-routine.txt


bot echo Telegram simples em webhooks escritos em Python 3 e implantá-lo no AWS Lambda usando a estrutura Serverless.


-----------------------------------------------------------------------------------------------------------------------------------------------
-- README
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------

Somos uma equipe enxuta de desenvolvedores da área de TI e Negócios (Business). A Space_One Labs, é o nosso repositório e laboratório virtual 
de desenvolvimento e uma comunidade virtual aberta (Open Community). Para acesso e consulta de recrutadores, clientes, parceiros e da 
comunidade de TI e Corporativa em geral.

-----------------------------------------------------------------------------------------------------------------------------------------------

DESCRIÇÃO

bot echo Telegram simples em webhooks escritos em Python 3 e implantá-lo no AWS Lambda usando a estrutura Serverless.

-----------------------------------------------------------------------------------------------------------------------------------------------

PLANO DE EXECUÇÕES:

- Configurar estrutura Serverles;
- Obtenha credenciais da AWS;
- Escrever back-end do Telegram Bot no Python 3;
- Implantar back-end no Lambda;
- Conecte seu back-end ao Telegram Bot.

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

Step 11: requirements - addev-routine.txt


### Bibliotecas Python usadas no projeto.


-----------------------------------------------------------------------------------------------------------------------------------------------
-- REQUERIMENTS
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: REQUERIMENTS
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Bibliotecas Python usadas no projeto.

boto3==1.17.76
pendulum==2.1.2
pyarrow==4.0.0
python-decouple==3.4
requests==2.25.1

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

Step 12: serverless - addev-routine


### Estrutura serverless - atributos do projeto.



-----------------------------------------------------------------------------------------------------------------------------------------------
-- SERVERLESS
-----------------------------------------------------------------------------------------------------------------------------------------------
-- Função: SERVERLESS
-- Autor: Daniel Sanches
-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

### Estrutura serverless - atributos do projeto.

service:
  name: addev-bot
provider:
  name: aws
  stackName: ${self:service.name}
  region: sa-east-1
  runtime: python3.8
  environment:
    IS_LOCAL: False
    TELEGRAM_CHAT_ID: ${env:TELEGRAM_CHAT_ID}
    TELEGRAM_ACCESS_TOKEN: ${env:TELEGRAM_ACCESS_TOKEN}
  deploymentBucket:
    name: ${self:service.name}
package:
  individually: false
  exclude:
    - package.json
    - package-log.json
    - node_modules/**
functions:
  addev-raw:
    handler: raw.lambda_handler
    name: addev-raw
    memorySize: 128
    timeout: 30
    events:
      - http:
          path: addev-bot
          method: post
          cors: true
  addev-enriched:
    handler: enriched.lambda_handler
    name: addev-enriched
    memorySize: 128
    timeout: 30
    events:
      - s3:
          bucket: ${env:RAW_BUCKET}
          event: s3:ObjectCreated:Put
          rules:
            - suffix: .json
          existing: true
plugins:
  - serverless-python-requirements
custom:
  pythonRequirements:
    dockerizePip: true

-----------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------------

__________________________________________________________________________________


O Artefato


Um artefacto (Pt/Pt) ou artefato (Pt/Br) é um dos vários tipos de subprodutos concretos produzido durante o desenvolvimento de software. Alguns artefatos (por exemplo, casos de uso, diagramas de classes, requisitos e documentos de projeto) ajudam a descrever a função, arquitetura e o design do software.



__________________________________________________________________________________


Repositório para Download


> Download em:



Disponibilidade de download direto para cada arquivo ou .rar (pacote inteiro).


__________________________________________________________________________________


Conclusivo do Caso de Uso


Em alguns casos, a abordagem sem servidor é muito conveniente porque permite criar uma solução muito escalável. Também muda a maneira como você pensa em gerenciar e pagar por servidores.


As estruturas sem servidor fornecem uma ferramenta muito simples para implantar suas funções sem conhecer a AWS ou qualquer outro provedor de nuvem. A AWS oferece um bom período de nível gratuito para o serviço, para que você possa criar um MVP, entrar em operação e só começar a pagar quando atingir um determinado número de usuários de forma absolutamente gratuita.


__________________________________________________________________________________


Notas e Relacionados


> Inicialmente o nosso propósito com o Blog é efetuar postagens diversas, porém teremos a área separada para as postagens relacionadas ao Constructor SO, que é o nosso Portfólio de Projetos, Agiles e Scrum, em que cada membro do Constructor SO possui a sua área para os seus desenvolvimentos. Dessa forma, cada atualização da área do Constructor SO é seguida de uma postagem no blog do profissional, informando os nossos leitores e criando assim um panorama extensivo de tal trabalho lançado ou versionado;


> A priori em relação aos desenvolvimentos da Space_One Labs, a nossa ideia é lançar e trabalhar de forma aleatória vários projetos da área específica relacionada, não nos tornando assim limitados por apps ou softwares específicos;


> Todos os casos aqui descritos e desenvolvidos, para este blog, no qual me pertence, que seja da categoria "BI Case", são casos de empresas fictícias, criadas e inventadas, para contextualizar e deixar o trabalho mais vivo e realista possível.



__________________________________________________________________________________


Daniel Sanches


Engenheiro de Produção, Universo - Universidade Salgado de Oliveira, especializado em Analytics e Data, Business e Tecnologia.


SO Labs Developer Member of Research, Business Intelligence Analyst, Data

Analyst and Business Analyst, Data Engineer, Blogger DS Space_One Labs | Space Members


Membro SO Labs Desenvolvedor de Pesquisas, Business Intelligence, Data Engineer, Data Analyst e Negócios

97 views0 comments

Comments


bottom of page