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.
#BlogdeMembros #BlogSOLabs / #AWS #AWSLambds #AWSAthena #AWSAPIGateway #AWS #Tecnologia #BI #BusinessIntelligence #ConstructorSO
__________________________________________________________________________________
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
Comments