【備忘録】結婚式でフォトコンテストをした時の設定事項メモ(LINE APIとGooglePhoto API使用)
最近全くブログを書くモチベがないですが,昔やった/できたことを完全に忘れるのは忍びないので備忘録だけでも放流しておきます。
基本的には技術的なメモを書くので運営的な反省については書かない予定。
フォトコンテストをする際に参考にしたもの
下記のブログを参考にLINE botとGooglePhotoの連携を試した。
Heroku上にサーバーを立ててそこからLINE APIとGooglePhotoAPIを叩いてLINE BOTとGooglePhotoをつなぐ形式である。
元記事のソースコードはいくつか挙動が合わなかったので改変したVerを自分のGithubにて管理している。
Heroku側の設定
Heroku上でのConfig設定
アクセストークンなどの値は直接サーバーにあげずにconfig値として送ると多少セキュアらしい。
heroku config:set <NAME>=<VAL>
.envファイルでまとめて入れても良い。 https://blog.44uk.net/2019/03/16/heroku-config-multiple-set-and-remove/
その場合は下記のコマンドでセットできる(bash限定)
heroku config:set $(cat .env)
DOSのコマンドプロンプトからは下記のどちらかを叩いた気がする。
FOR /F "usebackq" %i IN (`type env_heroku.txt`) DO heroku config:set %i FOR /F "usebackq" %i IN (`type .env_heroku`) DO set %i
Heroku上でのコミット設定
以下を参照
データベースURIってなんだっけ?
Flask初めてだったので上記の疑問がメモに残っていた。
データベースの記述フォーマットのことで
dialect+driver://username:password@host:port/database名
みたいな形式で書くらしい。
ここでは面倒だったので
DATABASE_URI=sqlite:////tmp/photocontest.db
とした。
db/photocontest.sqlite
でも良かったかも。
ngrokでデバッグ
heroku上でデバッグするのはちょっとしんどかったのでローカル上でデバッグするためにngrokというのを使った。
ローカルからwebhookのURLを獲得することができる。いろいろ初期設定したら下記を叩くだけだった。
ngrok http 5000
GooglePhotoやGoogleの認証まわり
Googleの認証周りはとにかく面倒だった。正直今でも何もわかっていない。
Credentialsを取得するPythonスクリプト
Googleの管理画面でアプリ用のIDとSECRETを取得した後,実際のAPIを叩くためにはCredentialsというものを取得しなければならない。
これが意外と面倒なのでスクリプトで一気にできるようにした。下記を適当なファイルで保存して実行すればOK。
- input: 同じフォルダに
client_id_secret.json
が必要 - output:
credentials.json
ができる
# google_photos.py import google.oauth2.credentials import google_auth_oauthlib.flow import json import os from datetime import datetime, date from matplotlib.pyplot import get #from googleapiclient.discovery import build from verify_reflesh_token import verify_credentials, get_accesstoken_from_refreshtoken SCOPES = ['https://www.googleapis.com/auth/photoslibrary'] API_SERVICE_NAME = 'photoslibrary' API_VERSION = 'v1' CLIENT_SECRET_FILE = 'client_id_secret.json' CREDENTIAL_FILE = 'credentials.json' def support_datetime_default(o): if isinstance(o, datetime): return o.isoformat() raise TypeError(repr(o) + " is not JSON serializable") def getNewCredentials(): flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file( CLIENT_SECRET_FILE, scopes=SCOPES) credentials = flow.run_console() with open(CREDENTIAL_FILE, mode='w') as f_credential_w: f_credential_w.write(json.dumps( vars(credentials), default=support_datetime_default, sort_keys=True)) return credentials def getCredentials(): if os.path.exists(CREDENTIAL_FILE): with open(CREDENTIAL_FILE) as f_credential_r: credentials_json = json.loads(f_credential_r.read()) credentials = google.oauth2.credentials.Credentials( credentials_json['token'], refresh_token=credentials_json['_refresh_token'], token_uri=credentials_json['_token_uri'], client_id=credentials_json['_client_id'], client_secret=credentials_json['_client_secret'] ) valid = get_accesstoken_from_refreshtoken( credentials_json['_client_id'], credentials_json['_client_secret'], credentials_json['_refresh_token']) print("Credentials is ", credentials.valid, valid) if not valid: print("Credential is Old!") credentials = getNewCredentials() else: print("Credential is not found!") getNewCredentials() return credentials def main(): credentials = getCredentials() # service = build( # API_SERVICE_NAME, # API_VERSION, # credentials=credentials # ) if __name__ == "__main__": main()
Credentialsが有効か確かめるスクリプト
上記で取得したCredentialsはすぐ有効期限が切れるのでこれが有効かどうか手っ取り早く試す作業もスクリプト化した。
- input :
credentials.json
- output: Credentialsが有効化どうか文が出力される
# google_photos.py import requests import json import google CREDENTIAL_FILE = "credentials.json" def load_credential_file(): with open(CREDENTIAL_FILE) as f_credential_r: credentials_json = json.loads(f_credential_r.read()) credentials_dict = { "client_id": credentials_json["_client_id"], "client_secret": credentials_json["_client_secret"], "refresh_token": credentials_json["_refresh_token"] } return credentials_dict def verify_credentials(credentials): client_id = credentials["client_id"] client_secret = credentials["client_secret"] refresh_token = credentials["refresh_token"] token = get_accesstoken_from_refreshtoken( client_id, client_secret, refresh_token) if token: print("Credentials is valid. Gained access token is:", token) return True else: print("Credentials is not valid!") return False def get_accesstoken_from_refreshtoken(client_id, client_secret, refresh_token): params = { "grant_type": "refresh_token", "client_id": client_id, "client_secret": client_secret, "refresh_token": refresh_token } authorization_url = "https://oauth2.googleapis.com/token" authorization_url = 'https://www.googleapis.com/oauth2/v4/token' r = requests.post(authorization_url, data=params) if r.ok: return r.json()['access_token'] else: # 失敗したときにRequestを表示 print("Failed to get Access token!", r) return None def main(): credentials = load_credential_file() verify_credentials(credentials) if __name__ == "__main__": main()
アップロードできない!?
後に気づいたことだがどうもAPIで作ったアルバムじゃないとAPIからアップロードできない仕様になっているっぽい。 ここがGooglePhotoを結婚式のフォトコンテストに一番勧めにくい点である。私のケースではCredentialsが万が一にも切れないよう,当日の朝にHerokuにPushして運用した。
https://www.sukerou.com/2020/10/gapino-permission-to-add-media-items-to.html
LINE まわり
本当に何もメモが残っていないので多分苦戦しなかったものと思われる。 多分なにかに使ったコードがあるのでメモする。
- なんか秘密鍵・公開鍵を作るときに使ったプログラム
from jwcrypto import jwk import json key = jwk.JWK.generate(kty='RSA', alg='RS256', use='sig', size=2048) private_key = key.export_private() public_key = key.export_public() print("=== private key ===\n"+json.dumps(json.loads(private_key),indent=2)) print("=== public key ===\n"+json.dumps(json.loads(public_key),indent=2))
- private_keyを読んでJWTを出すプログラム。何に使ったっけ?
import jwt from jwt.algorithms import RSAAlgorithm import time import json with open("privatekey.txt", 'r') as json_open: privateKey = json.load(json_open) headers = { "alg": "RS256", "typ": "JWT", "kid": "d173551d-2f74-4faa-81af-5ae3eb545c0a" # your key } payload = { "iss": "1657287856", # channel ID "sub": "1657287856", # channel ID "aud": "https://api.line.me/", "exp": int(time.time())+(60 * 30), "token_exp": 60 * 60 * 24 * 30 # token valid sec } key = RSAAlgorithm.from_jwk(privateKey) JWT = jwt.encode(payload, key, algorithm="RS256", headers=headers, json_encoder=None) print(JWT)