Gmail API を使ってメールを操作(前編)

Gmail操作

こんにちは、サポート部のまさよしです。

皆さん、業務ではどんな電子メールを利用していますか?

弊社内では数年前から Gmail を利用しているのですが、メール検索や分類のためのラベル付け、予約送信など、とても便利な機能を備えているなと感じています。しかしながらまだまだ活用できていない機能も多く、もう少しカスタマイズすることで業務の効率化につながるのではと個人的に思っています。しかも Gmail の機能をプログラムで操作できるよう Gmail API も用意されているので、さまざまな用途でも利用可能となっています。

そこで今回は、この Gmail API を活用したメール操作についてご紹介します。


事前準備

PowerBuilder から Gmail API を利用するにあたって何から手をつけたらいいか分からないですよね?そこで明石先輩が以前執筆してくれたテクニカルブログ Google Sheets API を使ってスプレッドシートを操作 を確認しましょう!Google API を利用するにあたっての設定など、手続きに関してはこの記事とほぼ同じなのでまだ見ていない方はこの機会に是非ご一読ください。

さて前置きが長くなりましたが、Gmail API を利用するためには事前に Gmail API の設定が必要となります。Gmail API を有効にして認証情報を取得するのですが、手順としては Google Sheets API の利用時と同様で Google Sheets API の部分を Gmail API に読み替えるだけです。今回、おさらいも兼ねて改めて手順をご説明しますが、Gmail API に関する情報は Web 上でたくさん公開されていますので、そちらもあわせてご確認ください。

なお、今回も検証用に Google Cloud Platform の無料トライアルを利用しますが、Google Sheets API の利用時と違う部分は 56 ですね。

  • Google Cloud Platform にログイン後、プロジェクトを作成して Gmail API を有効にする
    • ナビゲーション メニューから「IAM と管理」-「リソースの管理」をクリック
    • リソースの管理画面で「プロジェクトを作成」をクリック
    • 新しいプロジェクト画面でプロジェクト名を入力し [作成] ボタンをクリック
    • 作成したプロジェクトがドロップダウンで選択されているのを確認後、ナビゲーション メニューから「API とサービス」-「ライブラリ」をクリック
    • API ライブラリ画面で表示されている Google Workspace カテゴリから Gmail API を選択
    • Gmail API 画面の[有効にする] ボタンをクリック
    API ライブラリ画面

    API ライブラリ画面

    Gmail API 画面

    Gmail API 画面

  • 有効にした Gmail API の認証情報を取得する
    • ナビゲーション メニューから「API とサービス」-「認証情報」をクリック
    • 認証情報画面で「認証情報を作成」をクリック後、「OAuth クライアント ID」をクリック
    • OAuth クライアント ID の作成画面でアプリケーションの種類の選択や OAuth クライアントの名前を入力後、[作成] ボタンをクリック

作成が完了すると「OAuth クライアントを作成しました」というメッセージとともにクライアント IDクライアント シークレットが表示されます。クライアント ID とクライアント シークレットはこの後の PowerBuilder の処理で必要となりますので、テキストエディタなどに残しておいてください。なお、忘れてしまった場合は認証情報画面で表示されている OAuth 2.0 クライアント ID リストから OAuth クライアントの名前をクリックすることで再表示されます。

認証情報画面  

認証情報画面

  OAuth クライアント作成完了画面   

OAuth クライアント作成完了画面

これにて事前準備は完了です。


Authorization Code の生成

ここからは PowerBuilder のターンです。Gmail API に限らずアプリケーションから Web API を利用するには、安全性やセキュリティの面からアクセストークンを提示しなければなりません。そうしないとユーザーの権限以上の操作が行えたり、悪意をもったユーザーが Web API を使って重要なデータを取得できてしまうからです。そのため、アプリケーションでは Access Token を持たせておく必要があるのですが、Gmail API では事前に Google にログインして Authorization Code を生成することで Access Token が取得できます。

ここでも Google Sheets API の記事と同じように、ShellExecuteW 関数を利用します。ShellExecuteW 関数の引数に URL を指定してデフォルトブラウザーを立ち上げて Authorization Code を生成するので、関数を Global External Functions に宣言します。

Global External Functions

FUNCTION Long ShellExecute ( Uint ihwnd, String lpszOp, String lpszFile, String lpszParams, String lpszDir, Int wShowCmd ) LIBRARY "Shell32.dll" ALIAS FOR "ShellExecuteW"

ウィンドウ上のボタンをクリックすることでブラウザーが立ち上がるよう実装します。以下がサンプルスクリプトです。

[コード取得] ボタン Clicked イベント

// 変数宣言
String ls_id
String ls_redirect
String ls_scope
String ls_url
String ls_null

// Null セット
SetNull( ls_null )

// クライアント ID と Redirect URI、Scope を設定
ls_id = "生成されたクライアント ID"
ls_redirect = "urn:ietf:wg:oauth:2.0:oob"
ls_scope = "https://www.googleapis.com/auth/gmail.readonly"

// 認証リクエスト用 URL 作成
ls_url = "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=" + ls_id + "&redirect_uri=" + ls_redirect + "&scope=" + ls_scope
// URL を引数としてブラウザーをオープン
ShellExecute( 0, "open", ls_url, ls_null, ls_null, 0 )

// 終了
Return

変数 ls_id には事前準備で OAuth クライアント作成時に取得したクライアント ID を設定し、Redirect の変数には urn:ietf:wg:oauth:2.0:oob を指定しています。 この値は Google の承認サーバーが承認コードをブラウザーのタイトル バーに返すことを意味しているようです。

ここで Google Sheets API の利用時と違う部分としては Gmail API の Scope となります。gmail.readonly の部分がスコープとなるのですが、今回はメールデータの取得を目的としているので、すべてのリソースとそのメタデータを読み取りだけ行えて書き込み操作は行えないスコープとしています。各パラメーターの詳細については Google の Using OAuth 2.0 to Access Google APIs を参考にしてください。

なお Access Token を取得するには先ほど生成した Authorization Code を保持しておく必要があるので、今回はウィンドウ上のシングルラインエディットにセットしておきましょう。

Gmail API を利用するまでの手続きが続いていますが、あとは Access Token の取得だけなのでもう少しだけお付き合いください。


Access Token の取得

Authorization Code の生成が完了したので Access Token を取得する処理を実装します。作りをシンプルにするために Gmail API の操作は別のイベントで処理して、ここでは Access Token を取得するまでの処理を実装します。インスタンス変数を用意して、取得した Access Token をセットしておきましょう。

Instance Variables

String is_access_token

OAuthClient オブジェクトを使用した Access Token を取得するサンプルスクリプトは以下のとおりです。Access Token 取得には、事前準備で OAuth クライアント作成時に取得したクライアント IDクライアント シークレットも必要となります。

[Access Token 取得] ボタン Clicked イベント

// 変数宣言
Integer li_rtn
String ls_id
String ls_secret
String ls_redirect
String ls_scope
String ls_authorization
OAuthClient loa_client
TokenRequest ltr_request
TokenResponse ltr_response

// Access Token 初期化
is_access_token = ""

// パラメーター設定
ls_id = "生成されたクライアント ID"
ls_secret = "生成されたクライアント シークレット"
ls_redirect = "urn:ietf:wg:oauth:2.0:oob"
ls_scope = "https://www.googleapis.com/auth/gmail.readonly"
// シングルラインエディットから Authorization Code 取得
ls_authorization = sle_1.Text
IF IsNull( ls_authorization ) OR Trim( ls_authorization ) = "" THEN

  Return

END IF

// OAuthClient 作成
loa_client = CREATE OAuthClient

// Authorization Code から Access Token を取得する
ltr_request.Tokenlocation = "https://accounts.google.com/o/oauth2/token"
ltr_request.Method = "POST"
ltr_request.Granttype = "authorization_code"
ltr_request.Clientid = ls_id
ltr_request.Clientsecret = ls_secret
// パラメーターを追加
ltr_request.ClearParams()
ltr_request.AppendParam( "grant_type", "authorization_code" )
ltr_request.AppendParam( "client_id", ls_id )
ltr_request.AppendParam( "client_secret", ls_secret )
ltr_request.AppendParam( "scope", ls_scope )
ltr_request.AppendParam( "code", ls_authorization )
ltr_request.AppendParam( "redirect_uri", ls_redirect )
// Content-Type ヘッダーを設定
ltr_request.ClearHeaders()
ltr_request.SetHeader( "Content-type", "application/x-www-form-urlencoded" )

// トークン情報取得をリクエスト
li_rtn = loa_Client.AccessToken( ltr_request, ltr_response )

// 破棄
DESTROY loa_client

IF li_rtn = 1 AND ltr_response.GetStatusCode() = 200 THEN

  // Access Token 取得
  is_access_token = ltr_response.GetAccessToken()
  // メッセージ
  MessageBox( "確認", "Access Token を取得しました" )

ELSE

  MessageBox( "エラー", "Access Token の取得に失敗しました", StopSign! )

END IF

// 終了
Return

ここでも [コード取得] ボタンの Clicked イベントと同様、Google Sheets API の利用時と違う部分としては Gmail API の Scope となります。それ以外はコピー&ペーストで。

AccessToken 関数でリクエストを送信することで Access Token が取得できますが、注意点としては関数を呼び出す前までに認証サーバーの URL やプロセスの種類、HTTP メソッドなどを TokenRequest オブジェクトに設定しておく必要があります。


PowerBuilder から Gmail のデータを取得

Access Token の取得も完了しましたので、最後に Gmail API で Gmail のデータを取得します。

Gmail のデータを取得するにはまず https://www.googleapis.com/gmail/v1/users/{userId}/messages/ に GET リクエストを送信してメール一覧を取得します。{userId} は取得したいアカウントの ID を指定しますが、今回は認証されたアカウントなので me を指定します。また、URL にクエリパラメーター(q)を指定することでメールの絞り込みが行えます。送信者や件名など、クエリは Gmail の検索演算子(Gmail の検索ボックスで使用できる単語や記号)をセットしてフィルタリングします。クエリの詳細は Gmail Help でご確認ください。

そして GET リクエストを送信した結果 id と threadId の JSON 形式のデータが返ってきます(※1)。その id を https://www.googleapis.com/gmail/v1/users/{userId}/messages/ のパラメーターに付与して改めて GET リクエストを送信することで、その id に紐づいたメール詳細情報(件名や本文、送信元、送信先、送信日時など)が JSON 形式のデータで返ってきます(※2)。そこまでできればあとは JSONParser オブジェクトの GetRootItem や GetItemArray、GetItemString などの関数を駆使してデータを取得します。取得できるメールデータの詳細に関しては Gmail API リファレンスの users.messages などでご確認ください。

以下のサンプルスクリプトでは、メール一覧を取得する際にフィルタリング(送信された期間:2021/01/01 以降 且つ キーワード:メールマガジン)を行い、条件に該当した直近のメール id を取得して、その id に紐づいたメール詳細情報を取得します。そのメール詳細情報内から件名と本文を取得して、ウィンドウ上のシングルラインエディットやマルチラインエディットにセットする処理となります。

[Gmail データ取得] ボタン Clicked イベント

// 変数宣言
Integer li_rtn
Integer li_childcount
Integer li_index
Integer li_index_subject
Integer li_index_body
Long ll_root
Long ll_messages_array
Long ll_messages
Long ll_payload_object
Long ll_headers_array
Long ll_object
String ls_body
String ls_id
String ls_name
String ls_key
Blob lb_body
OAuthClient loa_client
OauthRequest loa_request
ResourceResponse lrr_response
JsonParser ljp_parser

// OAuthClient 作成
loa_client = CREATE OAuthClient

// 1. メール一覧を取得する(絞り込み:送信された期間=2021/01/01 以降 且つ キーワード=メールマガジン)
loa_request.Method = "GET"
loa_request.Url = "https://www.googleapis.com/gmail/v1/users/me/messages?q=after:2021/01/01 メールマガジン"
loa_request.SetAccessToken( is_access_token )
li_rtn = loa_client.RequestResource( loa_request, lrr_response )

IF li_rtn <> 1 THEN

  // 破棄
  DESTROY loa_client
  
  // 終了
  Return
  
END IF

// 2. IDを取得する
// JsonParser 作成
ljp_parser = CREATE JsonParser

// Body データ取得 ※1
ls_body = lrr_response.GetHeaders()
lrr_response.GetBody( ls_body )

// ルートアイテムのハンドルを取得
ljp_parser.LoadString( ls_body )
ll_root = ljp_parser.GetRootItem()

// キー「messages」の子項目のハンドルを取得(配列型)
ll_messages_array = ljp_parser.GetItemArray(ll_root, "messages")
// 最初の子項目のハンドル(受信日時が直近のメール)を取得
ll_messages = ljp_parser.GetChildItem(ll_messages_array, 1)
// キー「id」の項目を取得(String 型)
ls_id = ljp_parser.GetItemString(ll_messages, "id")

// 3. メール詳細情報を取得
loa_request.Url = "https://www.googleapis.com/gmail/v1/users/me/messages/" + ls_id
loa_request.SetAccessToken( is_access_token )
li_rtn = loa_client.RequestResource( loa_request, lrr_response )

IF li_rtn <> 1 THEN

  // 破棄
  DESTROY loa_client
  
  // 終了
  Return
  
END IF

// Body データ取得 ※2
ls_body = lrr_response.GetHeaders()
lrr_response.GetBody( ls_body )

// ルートアイテムのハンドルを取得
ljp_parser.LoadString( ls_body )
ll_root = ljp_parser.GetRootItem()

// キー「payload」の子項目のハンドルを取得(オブジェクト型)
ll_payload_object = ljp_parser.GetItemObject(ll_root, "payload")

// 4. 件名を取得
// キー「headers」の子項目のハンドルを取得(配列型)
ll_headers_array = ljp_parser.GetItemArray(ll_payload_object, "headers")

li_childcount = ljp_parser.GetChildCount(ll_headers_array)

// 配列の要素内からキー「name」の値が「Subject」となる Index を取得
FOR li_index = 1 TO li_childcount
	
	ll_object = ljp_parser.GetChildItem(ll_headers_array, li_index)
	ls_name = ljp_parser.GetItemString(ll_object, "name")
	
	IF ( ls_name = 'Subject') THEN
		
		li_index_subject = li_index
		
		EXIT
		
	END IF
	
NEXT

// value (件名) をシングルラインエディットにセット
sle_2.text = ljp_parser.GetItemString(ll_object, "value")

// 5. 本文を取得
li_childcount = ljp_parser.GetChildCount(ll_payload_object)

// キーが「body」となる Index を取得
FOR li_index = 1 TO li_childcount
	
	ls_key = ljp_parser.GetChildKey(ll_payload_object, li_index)
	
	IF ( ls_key = 'body') THEN
		
		li_index_body = li_index
		
		EXIT
		
	END IF
	
NEXT

// Indexを指定して子項目ハンドルを取得
ll_object = ljp_parser.GetChildItem(ll_payload_object, li_index_body)
// キー「data」の項目を取得(Blob 型)
lb_body = ljp_parser.GetItemBlob(ll_object, "data")
// エンコーディングに EncodingUTF8! を指定して Blob 型から String 型に変換後、マルチラインエディットにセット
mle_1.text = string(lb_body, EncodingUTF8!)

// 破棄
DESTROY ljp_parser

// メッセージ
MessageBox( "完了", "Gmail データ取得に成功しました" )

// 終了
Return

※1 メール一覧で返されるデータ(JSON 形式)

{
  "messages": [
    {
      "id": "xxxxxxxxxxxxxxxx",
      "threadId": "xxxxxxxxxxxxxxxx"
    },
    {
      "id": "xxxxxxxxxxxxxxxx",
      "threadId": "xxxxxxxxxxxxxxxx"
    },
    {
      "id": "xxxxxxxxxxxxxxxx",
      "threadId": "xxxxxxxxxxxxxxxx"
    },
    {
      "id": "xxxxxxxxxxxxxxxx",
      "threadId": "xxxxxxxxxxxxxxxx"
    },
・
・
・

※2 メール詳細情報で返されるデータ(JSON 形式)

{
  "id": "xxxxxxxxxxxxxxxx",
  "threadId": "xxxxxxxxxxxxxxxx",
  "labelIds": [xxxxxxxxxxxxxxxx],
  "snippet": "xxxxxxxxxxxxxxxx",
  "payload": {
    "partId": "",
    "mimeType": "text/plain",
    "filename": "",
    "headers": [
      {
        "name": "Delivered-To",
        "value": "xxxxxxxxxxxxxxxx"
      },
      {
        "name": "Received",
        "value": "xxxxxxxxxxxxxxxx"
      },
      {
        "name": "X-Google-Smtp-Source",
        "value": "xxxxxxxxxxxxxxxx"
      },
      {
        "name": "X-Received",
        "value": "xxxxxxxxxxxxxxxx"
      },
・
・
・

なお、これだけ有益な API なので利用する際に色々と制限があるようです。1 日に使えるポイントや 1 秒間で使えるポイントが制限されています。このポイントは扱う操作によって変わってくるので、場合によっては処理内に Sleep を入れるなど工夫が必要となることがありそうです。詳しくは Gmail API リファレンスの Usage limits を確認してくださいね。まぁ確かに大人数であまりにも利用しすぎるとサーバーに負荷がかかりまくりですもんね。


アプリケーションの実行

それでは締めに今回作成したアプリケーションを実行してみましょう。画面イメージは以下のようになっています。

Gmail 取得アプリ

最初に [コード取得] ボタンをクリックします。

ログイン

 

 

 

 

 

するとデフォルト設定されているブラウザーが起動し「Google にログイン(または Google アカウントの選択)」画面が表示されますので、取得する Gmail のアカウントでログインしてください。

 

 

 

 

 

 

 

ログインすると “(作成したプロジェクト) が Google アカウントへのアクセスをリクエストしています” と表示されます。 リクエストされた Gmail のアカウントやプロジェクトに問題がないことを確認後、[許可] ボタンをクリックします。

選択内容確認
Authorization Code取得

 

 

 

ログイン画面に遷移して画面上にコードが表示されるのでコピーしておきましょう。これが Authorization Code となります。PowerBuilder アプリケーションに戻って、コピーしたコードを Authorization Code のシングルラインエディットに貼り付けて [Access Token 取得] ボタンをクリックします。成功すると「Access Token を取得しました」のメッセージが表示されます。

最後に [Gmail データ取得] ボタンをクリックします。成功すると取得した Gmail のデータ(件名と本文)がアプリケーション上のシングルラインエディットやマルチラインエディットにセットされます。念のため、もうひとつブラウザーを立ち上げ Gmail を表示させてデータが一致しているか確認してみましょう。

Gmail 取得
Gmail 情報

無事 Gmail のデータを取得できました。サンキュー PowerBuilder!


まとめ

今回のテクニカルブログを執筆するにあたり試行錯誤の連続でしたが、PowerBuilder から Gmail のデータをここまで簡単に取得できるとは思ってもみませんでした。PowerBuilder の Web API 利用、これは機能拡張の可能性が広がりますね!

それと気づいた方もおられるかと思いますが、この「Gmail API を使ってメールを操作」には後編があります。メールデータの取得だけでは不完全燃焼ですよね。そこで後編では Gmail API を利用して PowerBuilder からメールの作成と送信を行う方法についてお伝えできればと思っています。

まだまだ試行錯誤の日々は続きますが、後編の公開に関してはもうしばらくお待ちください。

以上、まさよしでした。

テクニカルブログ 一覧を見る
PowerBuilder マイグレーション
PowerBuilderとは? ~デキる大人へ変身できる?ローコード開発ツール~