Estou pensando que a melhor maneira de implementar o receptor de e-mail (já que já uso o AWS SES) é usar a API por meio de uma função lambda para chamar o endpoint da API /admin/email/handle_mail.
Existem tutoriais sobre como levar e-mail para o SES e passá-lo via S3 para uma função lambda. Existem algumas funções nodejs prontas para pegar esse e-mail e repassá-lo para um servidor de e-mail.
Mas, em vez de fazer isso, parece melhor simplesmente chamar a API diretamente de uma função lambda, passando o e-mail. (Eu não mantenho meu próprio servidor de e-mail local e não quero usar polling de qualquer maneira).
Então, eu poderia usar um pouco de ajuda, seja por meio de documentação mais detalhada (nenhuma página de documentação, até onde sei) sobre como chamar esse endpoint e o que exatamente enviar (é um endpoint REST? Existe uma alternativa websocket? Formato do que enviar?) ou algum código para compartilhar. Eu ainda preciso executar este contêiner extra? Configure direct-delivery incoming email for self-hosted sites with Mail-Receiver (essa postagem tem 6 anos)
@dltj aqui. Não posso ajudar com Node/JavaScript, mas posso oferecer um pseudo-código na forma do Python que estou usando para fazer isso acontecer:
""" AWS Lambda para receber mensagem do SES e postá-la no Discourse """
import json
import logging
import boto3
import requests
from base64 import b64decode
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.INFO)
# pylint: disable=C0301
ENCRYPTED_DISCOURSE_KEY = "{cyper-text}"
DISCOURSE_KEY = boto3.client("kms").decrypt(
CiphertextBlob=b64decode(ENCRYPTED_DISCOURSE_KEY)
)["Plaintext"]
DISCOURSE_SITE = "discuss.folio.org"
EMAIL_S3_BUCKET = "openlibraryfoundation-discourse-mail"
DISCOURSE_URL = f"https://{DISCOURSE_SITE}/admin/email/handle_mail"
DISCOURSE_API_HEADERS = {
"Api-Key": DISCOURSE_KEY,
"Api-Username": "system",
}
def log_lambda_context(context):
"""Registra os atributos do objeto de contexto recebido do invocador do Lambda"""
LOGGER.info(
"Tempo restante (ms): %s. Fluxo de log: %s. Grupo de log: %s. Limites de memória (MB): %s",
context.get_remaining_time_in_millis(),
context.log_stream_name,
context.log_group_name,
context.memory_limit_in_mb,
)
def lambda_handler(event, context):
""" Lida com o evento de entrega do SES """
log_lambda_context(context)
LOGGER.info(json.dumps(event))
processed = False
for record in event["Records"]:
mail = record["ses"]["mail"]
mailMessageId = mail["commonHeaders"]["messageId"]
sesMessageId = mail["messageId"]
mailSender = mail["source"]
LOGGER.info(
"Processando um SES com mID %s (%s) de %s",
mailMessageId,
sesMessageId,
mailSender,
)
deliveryRecipients = mail["destination"]
for recipientEmailAddress in deliveryRecipients:
LOGGER.info("Destinatário da entrega: %s", recipientEmailAddress)
s3resource = boto3.resource("s3")
bucket = s3resource.Bucket(EMAIL_S3_BUCKET)
obj = bucket.Object(sesMessageId)
LOGGER.info("Obtendo %s/%s", EMAIL_S3_BUCKET, sesMessageId)
post_data = {"email": obj.get()["Body"].read()}
LOGGER.info("Postando em %s", DISCOURSE_SITE)
r = requests.post(
DISCOURSE_URL, headers=DISCOURSE_API_HEADERS, data=post_data
)
if r.status_code == 200:
LOGGER.info("Email aceito pelo Discourse: %s", DISCOURSE_SITE)
obj.delete()
processed = True
else:
LOGGER.error(
"Email rejeitado por %s (%s): %s",
DISCOURSE_SITE,
r.status_code,
r.content,
)
return processed
Estou usando Serverless para gerenciar a implantação e a manutenção do Lambda. Caso queira seguir um caminho semelhante, sinta-se à vontade para começar com este arquivo serverless.yml:
Como se cria essa chave de API (criptografada)? Ela precisa ser criptografada?
Preciso marcar “habilitação de sondagem manual” (Push emails using the API for email replies) para que o endpoint da API fique ativo ou ele é ativo por padrão?
Algo a que prestar atenção especial em termos de permissões da AWS ou regras do SES?
A chave não precisa ser criptografada. O código de infraestrutura do nosso projeto está em um espaço compartilhado, então eu não queria codificar a chave diretamente no código-fonte.
Eu tenho “polling manual ativado” no meu site, embora eu não me lembre especificamente o que isso faz.
Nada incomum que eu possa lembrar com as permissões da AWS além de enviar e ler/escrever no bucket temporário.
Existe algum lugar na interface do usuário da web onde se gera a chave de API? Não consigo encontrar nenhum lugar nas configurações. Talvez você apenas a defina como quiser como variável de ambiente ao compilar? De qualquer forma, não tenho ideia de como gerar uma chave de API.
Sim, eu vi isso, mas não achei que estivesse certo porque “granular” não tinha permissão de handle_email e eu não tinha certeza se deveria escolher um usuário ou todos os usuários.
Então, gerei uma chave para o usuário “system” com permissão global. Foi isso que você fez?
Bem, eu queria usar meu aplicativo “postman” para aprender/experimentar, mas sem documentação, não sei o que POSTAR em qual(is) formato(s) e como passar a chave. Se eu estivesse mais familiarizado com python, talvez pudesse entender isso olhando seu código.
Eu sei que este é um projeto de código aberto, então é compreensível que não haja documentação real sobre o uso da API handle_mail, apenas alguma ajuda de pessoas como você… obrigado!
Não está sem documentação porque é de código aberto, está sem documentação porque ninguém (ou poucas pessoas) perguntaram.
Sugestões de que você quer POSTar o e-mail codificado em um email_encoded coisa. routes.rb mostra que é um POST e a rota, que é https://discourse.example.com/admin/email/handle_mail. Eu peguei do template de exemplo do receptor de e-mail (e eu recomendaria que você apenas usasse isso, pois se tivesse, já teria terminado), mas também está em discourse/config/routes.rb at main · discourse/discourse · GitHub (o que não é imediatamente claro o que está acontecendo, a menos que você entenda rals).
@dltj Consegui reescrever seu código para nodejs usando o aws-sdk e módulos get. Muito obrigado.
Houve vários obstáculos no caminho, desde AWS (ses, IAM, S3, lambda), mexendo com serverless, acertando a chamada da API, até configurar corretamente as definições do discourse.
O que vou fazer é escrever um tópico detalhado sobre exatamente o que fiz para fazer isso funcionar, com detalhes suficientes para que alguém possa habilitá-lo sem ter que codificar ou arrancar os cabelos. A verdadeira vantagem deste método é que, usando o ses, NÃO é necessário configurar um servidor de e-mail para o domínio. Isso deixa você livre para configurar um servidor de e-mail totalmente independente para o seu domínio.
Se você estiver interessado em como foi feito, volte mais tarde, postarei um link aqui quando estiver pronto (daqui a alguns dias).
Adicionando a isso, @dltj removeremos o parâmetro email da rota admin/email/handle_mail em breve, você precisa enviar o corpo do e-mail como uma string codificada em base64 em um parâmetro email_encoded para esta rota.
A codificação foi um dos problemas que não consegui resolver.
Não consegui fazer o e-mail codificado funcionar.
@martin, de acordo com sua postagem, email_encoded está incorreto. Tentei ambos, email_encoded gera um erro, mas encoded_email ainda gera o aviso.
body: 'warning: the email parameter is deprecated. all POST requests to this route should be sent with a base64 strict encoded encoded_email parameter instead. email has been received and is queued for processing',
Preciso converter meu e-mail de entrada para texto simples (o que estou fazendo agora) e de volta para base64, ou é provável que o que a AWS Ses armazena no bucket já esteja em base64? Se eu passar o corpo do e-mail como a SES o salvou, ele não aparece no log de e-mails recebidos. O comando POST não retorna erro, apenas o aviso. Então, basicamente, estou preso aqui.
Por favor, adie a remoção da chave email até que possamos fazer isso funcionar.
mais uma vez, se eu enviar texto simples com “emal”, a mesma mensagem é recebida e processada.
Você continua dizendo “strict”, isso significa alguma coisa? Nodejs/JS tem apenas dois sabores de base64: “base64” e “base64url”.
Por que a API continua dando o aviso se eu uso encoded_email, mesmo que eu tenha um erro na codificação.
Tentei usar o curl e ele não aceita encoded_email, mas funcionou com email_encoded… droga, então o aviso está incorreto!
Então, novamente, tentei meu código, mas com email_encoded, mas desta vez usei um módulo de codificação base64 em vez do integrado e está funcionando, ufa!
Isso é ótimo, obrigado por compartilhar.
Estávamos usando a versão pre-base64 descontinuada por três anos no Lambda e ela parou de funcionar há um mês.
Não podemos usar o SMTP :25, pois estamos hospedados atrás de um cloudflared tunnel e por que mexer com a porta 25 se a Amazon faz um ótimo trabalho bloqueando o ruído com o SES?
De qualquer forma, estou no fim da minha sanidade.
O código funciona com Postman e Python executando em um computador local. Algo parece ser corrompido ao ser enviado do AWS Lambda com código idêntico.
Forbidden
UTF-8
Bem, resolvi meu problema -
mesmo que as consultas estivessem sendo passadas, o ‘modo de combate a bots’ NÃO funciona bem com a API do Discourse. Talvez por causa da codificação base64?
Espero que isso poupe alguém 72 horas de sua vida:
Desligue isso: