laughk / TIL

2 stars 0 forks source link

Gmail の内容を Slack 通知したい #10

Open laughk opened 5 years ago

laughk commented 5 years ago

(第99回)Python mini Hack-a-thon - connpass

いろいろ調べながら試行錯誤することになりそうなので Issue にしとく とりあえず以下のことやりたい

laughk commented 5 years ago

参考になりそうなリンク

GCP Cloud functions

Gmail Push Notification API をお使いいただくことで、Gmail イベントを Cloud Pub/Sub トピックに送り、Cloud Functions と一緒に呼び出すことができます。

Gmail の API

laughk commented 5 years ago

:eyes: この辺にサンプルがある

https://github.com/gsuitedevs/python-samples/tree/master/gmail

laughk commented 5 years ago

:thought_balloon: quickstart.py は Gmail の label 一覧を表示するやつっぽい

laughk commented 5 years ago

初回起動の際に認証を求められた

laughk commented 5 years ago

cloud functions に乗っけるスクリプトには CLI モードを有効にしてローカルでデバッグ、行けそうなら cloud functions へデプロイという感じでいいだろうか。

laughk commented 5 years ago

この辺をいじればやりたいことができるかも?

https://github.com/gsuitedevs/python-samples/blob/master/gmail/quickstart/quickstart.py#L49-L53

laughk commented 5 years ago

Gmail の API にどうアクセスしていくかのイメージが Quickstart だけだと掴めなかったけどここの記事のコードでちょっとイメージできたかも。

ref. Pythonを使ってGmail APIからメールを取得する - Qiita

laughk commented 5 years ago

例えば特定の人とのやり取りと取りたい時

    query = '(from OR to OR cc):u****@*******.co.jp'
    results = service.users()\
        .messages()\
        .list(userId='me', q=query)\
        .execute()
    print(results)

結果 (雰囲気)

{
  'messages': [{'id': '16*********', 'threadId': '16************'},
              {'id': '16*********', 'threadId': '16************'},
              {'id': '16*********', 'threadId': '16************'},
              {'id': '16*********', 'threadId': '16************'},
              {'id': '16*********', 'threadId': '16************'},
              {'id': '16*********', 'threadId': '16************'},
              {'id': '16*********', 'threadId': '16************'},
              {'id': '16*********', 'threadId': '16************'},
              {'id': '16*********', 'threadId': '16************'}],
 'resultSizeEstimate': 23
}

ここまでだと、あくまで個別にアクセスするための ID 情報だけが取れるっぽい

laughk commented 5 years ago

特定のメッセージのとり方

Users.messages: get | Gmail API | Google Developers

laughk commented 5 years ago

message.get したときのレスポンス

Users.messages | Gmail API | Google Developers

laughk commented 5 years ago

snippet だとメッセージ本文全体が取れないんだよなー payload.body からじゃないとダメそう

laughk commented 5 years ago

payload.body, base64url って形式で encode されてるみたい

https://developers.google.com/gmail/api/v1/reference/users/messages/attachments#resource

The body data of a MIME message part as a base64url encoded string. May be empty for MIME container types that have no message body or when the body data is sent as a separate attachment. An attachment ID is present if the body data is contained in a separate attachment.

laughk commented 5 years ago

なんか文字コード壊れてる... ? なんか上手く decode できない :sob:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.7/encodings/utf_8_sig.py", line 23, in decode
    (output, consumed) = codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 12-13: invalid continuation byte

ちなみに `.decode('utf-8', 'ignore') してみるも、ほとんどがぐっちゃぐちゃで読めたものではない...

laughk commented 5 years ago

なんか文字コード壊れてる... ? なんか上手く decode できない sob

base64url が曲者だったっぽい。 https://github.com/ploopkazoo/python-base64url/blob/master/base64url.py のコードを一部流用させてもらったら行けそうな気配。

body_data に payload.body.data が入ってる前提

before

            try:
                body_64decoded = base64.b64decode(body_data)
                print(f'payload.body.data(64decoded): {body_64decoded}')
            except:
                print(f'payload.body.data(64decoded): (passed)')

            try:
                body_decoded = bytes(body_64decoded).decode()
                print(f'payload.body.data(decoded): {body_decoded}')
            except:
                print(f'payload.body.data(decoded): (passed)')

after

            try:
                body_64decoded = base64.b64decode(base64urldecode(body_data))
                print(f'payload.body.data(64decoded): {body_64decoded}')
            except:
                print(f'payload.body.data(64decoded): (passed)')

            try:
                body_decoded = bytes(body_64decoded).decode()
                print(f'payload.body.data(decoded): {body_decoded}')
            except:
                print(f'payload.body.data(decoded): (passed)')
laughk commented 5 years ago

https://github.com/ploopkazoo/python-base64url/blob/master/base64url.py のコードを一部流用させてもらったら行けそうな気配。

公式ライブラリでこのへんやってくれるのがあるらしいので調べて差し替える

laughk commented 5 years ago

公式ライブラリでこのへんやってくれるのがあるらしいので調べて差し替える

@HayaoSuzuki が教えてくれた thx!!

https://docs.python.org/ja/3/library/base64.html#base64.urlsafe_b64decode

ref. https://twitter.com/CardinalXaro/status/1132213056098820101

laughk commented 5 years ago

Webhook が最近新しいものが出てきている。

これが結構ややこしい。形式は以下のツールで色々手探りしてる https://api.slack.com/tools/block-kit-builder

https://github.com/laughk/TIL/blob/0cd7906efcfa97cfd2b44099a707d89ec303f494/gmail_tool/modules/fetch.py#L35-L57