Verwenden Sie zuvor den E-Mail-Dienst [^ iftttemail] von IFTTT als E-Mail-Empfänger, rufen Sie die GitHub-API [^ githubapi] über AWS Lambda auf und Fügen Sie GitHub per E-Mail ein neues Problem hinzu / items / ff516aa90eb87c5140e7) Ich habe eine Funktion erstellt. Es ist eigentlich sehr praktisch, ein GitHub-Problem mit einer einzigen E-Mail erstellen zu können, wenn Sie einen Fehler oder eine Verbesserung Ihres eigenen Dienstes bemerken. Ich denke, ich werde es auch in Zukunft weiter verwenden, daher habe ich es neu erstellt, damit es unter AWS funktioniert, einschließlich des E-Mail-Empfängers.
Verwenden Sie Amazon SES (Simple Email Service) [^ ses] als E-Mail-Empfänger. Indem Sie das E-Mail-Zustellungsziel der von Ihnen verwalteten Domäne an den empfangenden Endpunkt von SES weiterleiten, können Sie E-Mails an Amazon SES → Amazon S3 → AWS Lambda und Bucket Relay senden. Ich habe eine Lambda-Funktion implementiert, die dem GitHub-Repository ein Problem hinzufügt, indem ich die GitHub-API [^ githubapi] gemäß dem Inhalt der E-Mail unter Verwendung des Python-Frameworks AWS Chalice [^ chalice] aufrufe.
Das Implementierungsverfahren ist ungefähr wie folgt.
Informationen zu 1-3 finden Sie im AWS Developer Guide "Empfangen von E-Mails mit Amazon SES-Amazon Simple Email Service -email.html) “und Supportinformationen“ [E-Mails mit Amazon SES in Amazon S3 empfangen und speichern](https://aws.amazon.com/jp/premiumsupport/knowledge-center/ses- eingehende E-Mails erhalten /) “wird detailliert beschrieben. Für 4 wird das spezifische Verfahren unter "So legen Sie GitHub" Persönliche Zugriffstoken "- Qiita" erläutert.
In diesem Artikel fasse ich die Implementierungen von 5 und 6 unten zusammen.
Wenn Sie in den obigen Abschnitten 5 und 6 neue eingehende E-Mails im S3-Bucket speichern, lesen Sie die empfangenen E-Mails aus dem S3-Bucket und fügen Sie das Problem dem GitHub-Repository hinzu. Dieser Prozess, Chalice [^ chalice], ein Python-Framework für die Lambda-basierte Entwicklung, kann sehr einfach mit einem Dekorator namens "on_s3_event" erreicht werden.
Chalice.on_s3_event() S3 verfügt über einen Mechanismus zum Senden einer Benachrichtigung an Lambda usw., wenn sich Änderungen am Bucket ergeben. Um diesen Mechanismus verwenden zu können, muss ein Ereignis festgelegt werden, um die Benachrichtigung in S3 zu überspringen und eine Funktion zum Empfangen der Benachrichtigung in Lambda zu erstellen. Wenn Sie jedoch Chalice verwenden, werden diese Einstellungen fast automatisch vorgenommen.
Der Basiscode, der die Lambda-Funktion implementiert, die S3-Ereignisse in Chalice empfängt, lautet [^ on_s3_event].
app.py(sample)
from chalice import Chalice
app = chalice.Chalice(app_name='s3eventdemo')
app.debug = True
@app.on_s3_event(bucket='mybucket-name',
events=['s3:ObjectCreated:*'])
def handle_s3_event(event):
app.log.debug("Received event for bucket: %s, key: %s",
event.bucket, event.key)
Chalice.on_s3_event ()
Wenn Sie eine Funktion mit einem Dekorateur definieren und den Code schreiben, werden beim Bereitstellen der Funktion auf Lambda mit chalice deploy
alle Rollen und Ereignisse für S3 und Lambda automatisch festgelegt. Ich werde.
In dieser Funktion mit dem Dekorator "Chalice.on_s3_event ()" habe ich dieses Mal den Vorgang des Lesens der empfangenen E-Mails aus dem S3-Bucket [^ email] und des Hinzufügens des Problems zum GitHub-Repository beschrieben. Der Hauptcode von Chalice, app.py
, lautet wie folgt.
app.py
from chalice import Chalice
import logging, os, json, re
import boto3
from botocore.exceptions import ClientError
import email
from email.header import decode_header
from email.utils import parsedate_to_datetime
import urllib.request
# setup chalice
app = Chalice(app_name='mail2issue')
app.debug = False
# setup logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
logformat = (
'[%(levelname)s] %(asctime)s.%(msecs)dZ (%(aws_request_id)s) '
'%(filename)s:%(funcName)s[%(lineno)d] %(message)s'
)
formatter = logging.Formatter(logformat, '%Y-%m-%dT%H:%M:%S')
for handler in logger.handlers:
handler.setFormatter(formatter)
# on_s3_event
@app.on_s3_event(
os.environ.get('BUCKET_NAME'),
events = ['s3:ObjectCreated:*'],
prefix = os.environ.get('BUCKET_KEY_PREFIX')
)
def receive_mail(event):
logger.info('received key: {}'.format(event.key))
# read S3 object (email message)
obj = getS3Object(os.environ.get('BUCKET_NAME'), event.key)
if obj is None:
logger.warning('object not found!')
return
# read S3 object (config)
config = getS3Object(os.environ.get('BUCKET_NAME'), 'mail2issue-config.json')
if config is None:
logger.warning('mail2issue-config.json not found!')
return
settings = json.loads(config)
#E-Mail analysieren
msg = email.message_from_bytes(obj)
msg_from = get_header(msg, 'From')
msg_subject = get_header(msg, 'Subject')
msg_content = get_content(msg)
#E-Mail-Adresse extrahieren
pattern = "[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+"
adds = re.findall(pattern, msg_from)
#Extrahieren Sie die Einstellungen entsprechend der E-Mail-Adresse
config = None
for add in settings:
if add in adds:
config = settings[add]
break
if config is None:
logger.info('there is no config for {}'.format(', '.join(adds)))
return
#Holen Sie sich ein Repository
repos = getRepositories(config['GITHUB_ACCESS_TOKEN'])
logger.info('repositories: {}'.format(repos))
#Bestimmen Sie das Repository anhand des E-Mail-Titels
repo = config['GITHUB_DEFAULT_REPOSITORY']
title = msg_subject
spaceIdx = msg_subject.find(' ')
if spaceIdx > 0:
repo_tmp = msg_subject[0:spaceIdx]
if repo_tmp in repos:
title = msg_subject[spaceIdx+1:]
repo = repo_tmp
title = title.lstrip().rstrip()
logger.info("repository: '{}'".format(repo))
logger.info("title: '{}'".format(title))
#POST-Problem
postIssue(
config['GITHUB_ACCESS_TOKEN'],
config['GITHUB_OWNER'],
repo, title, msg_content
)
#Mail löschen
deleteS3Object(os.environ.get('BUCKET_NAME'), event.key)
#Objekt aus S3 abrufen
def getS3Object(bucket, key):
ret = None
s3obj = None
try:
s3 = boto3.client('s3')
s3obj = s3.get_object(
Bucket = bucket,
Key = key
)
except ClientError as e:
logger.warning('S3 ClientError: {}'.format(e))
if s3obj is not None:
ret = s3obj['Body'].read()
return ret
#S3-Objekt löschen
def deleteS3Object(bucket, key):
try:
s3 = boto3.client('s3')
s3.delete_object(
Bucket = bucket,
Key = key
)
except ClientError as e:
logger.warning('S3 ClientError: {}'.format(e))
#Mail-Header abrufen
def get_header(msg, name):
header = ''
if msg[name]:
for tup in decode_header(str(msg[name])):
if type(tup[0]) is bytes:
charset = tup[1]
if charset:
header += tup[0].decode(tup[1])
else:
header += tup[0].decode()
elif type(tup[0]) is str:
header += tup[0]
return header
#E-Mail-Text erhalten
def get_content(msg):
charset = msg.get_content_charset()
payload = msg.get_payload(decode=True)
try:
if payload:
if charset:
return payload.decode(charset)
else:
return payload.decode()
else:
return ""
except:
return payload
#Holen Sie sich eine Liste der Github-Repositorys
def getRepositories(token):
req = urllib.request.Request(
'https://api.github.com/user/repos',
method = 'GET',
headers = {
'Authorization': 'token {}'.format(token)
}
)
repos = []
try:
with urllib.request.urlopen(req) as res:
for repo in json.loads(res.read().decode('utf-8')):
repos.append(repo['name'])
except Exception as e:
logger.exception("urlopen error: %s", e)
return set(repos)
#Problem zum Github-Repository hinzufügen
def postIssue(token, owner, repository, title, content):
req = urllib.request.Request(
'https://api.github.com/repos/{}/{}/issues'.format(owner, repository),
method = 'POST',
headers = {
'Content-Type': 'application/json',
'Authorization': 'token {}'.format(token)
},
data = json.dumps({
'title': title,
'body': content
}).encode('utf-8'),
)
try:
with urllib.request.urlopen(req) as res:
logger.info(res.read().decode("utf-8"))
except Exception as e:
logger.exception("urlopen error: %s", e)
Die folgende Einstellungsdatei wird aus S3 gelesen, damit das Zugriffstoken für die Verwendung der GitHub-API entsprechend der E-Mail-Adresse des Absenders umgeschaltet werden kann.
mail2issue-config.json
{
"<Absender-E-Mail-Adresse>": {
"GITHUB_OWNER": "<GitHub-Benutzername>",
"GITHUB_ACCESS_TOKEN": "<GitHub-Zugriffstoken>",
"GITHUB_DEFAULT_REPOSITORY": "<Repository-Name, falls nicht im E-Mail-Titel angegeben>"
},
...
}
Wenn ich Amazon SES zu einem anderen Zweck berührt habe und E-Mails über AWS erhalten kann, habe ich mir dieses Refactoring ausgedacht. Es gibt immer noch viele E-Mail-ausgelöste Dienste, daher werden wir weiterhin überlegen, dieses Muster anzuwenden.