A note of a person who is learning programming, SakaTaQ

ロック好きのプログラミング学習

Railsでのプロジェクトの作成

Ruby on Railsというフレームワークを使用したアプリケーションを作成する際に、どういった手順でコマンドを入力して行ったか毎回忘れがちなので、曖昧理解なまま当たり前にやっていることも含め、色々と調べながら書き残していくことにしました。

Railsプロジェクトの作成

% rails new APP_PATH [option]
% rails new {アプリの名前} {オプション}

例)
% rails new app-name

当初はアプリの名前と聞いてもよく理解できなかったですが、とりあえず起こることはapp-nameというディレクトリが作成され、railsでアプリを作成するのに必要なファイルなどがそのディレクトリの中に生成されるということ。
オプションについては後述。


参考にさせていただいたこちらの記事ではシステム側のgemに入れるのではなく別に格納した方がいいという旨の記事を見かけました。
自分はvendor/bundle指定をプロジェクトディレクトリ毎に行ったことはないので、基本的にインストールされたgemはシステム側に入っていたのですね。
因みに使用していなくてもnewした時点でvendorディレクトリは生成されているようです。
この辺りのキーワードで調べれば細かいことを知ることができそうですが...今はいいかな😅

この方法では以降のrailsコマンドはbundle execを介して呼び出しています。

% bundle exec rails new . -B

newの後にアプリケーションディレクトリ名ではなく.を記述することで、先ほどの参考記事のように、gemの設定も含めて作成したディレクトリにそのままrailsのアプリケーションのファイルを生成してくれるようです。
-Bもしくは--skip-bundleの部分は後述するオプション。これを書かないとbundle installが実行されてしまい、Ruby環境にgemがインストールされてしまう。システム側に入れない意図を考えると、こちらの方法ではこのオプションは必須っぽい。

プロジェクト作成時のオプション

プロジェクトを作成する際にはコマンドにオプションを記述することで、使用するデータベースを決めることができる。
また、検索するまで知りませんでしたが、予めturbolinksを切る方法やテストで使用するデフォルトのgemも切り替えできるようです。
% rails new -hでヘルプを表示し、オプションで使用できるコマンドが確認できます。
とりあえずザッと見てよく使いそうなオプションを書いてみました。

  • -d DATABASE もしくは --database=DATABASE
    DATABASEには使用するデータベース名を指定。自分はよくmysqlを使うので-d mysqlのように書く。指定なしの場合、Railsのデフォルトデータベースsqliteが指定される(後で変更可能)。
  • --skip-turbolinks
    turbolinksをオフにすることができる。jQueryを使う時にハマったことが多かったので、入れることがあるかも。
  • --skip-test
    railsではデータベース同様にテストもデフォルトのものが用意されており、Minitestというテストが生成される。自分はRSpecを使うことが多いので、生成しなくても良い時には記述した方が良い。
  • -Bもしくは--skip-bundle
    上で少し触れた通り、rails newの際にbundle installを行わないようにする。
    これについてはRails tutorialにも書いてあり、実際に% rails newコマンドを打った時にrun bundle installと出てくることから実行されることが分かると書かれている。
  • --skip-coffee coffeescriptを使うことがほぼないのでこれも入れてもいいのかも?

プロジェクトのバージョン指定

バージョンによってはそれまで使えたものがなくなっていたり、名前が変わっていたりしてエラーになることがあるので、指定しておいたほうが無難...だと思う。
バージョン指定をしない場合にはPCに入っているrailsバージョン(% rails -vで確認可能)の最新のバージョンで生成される。
但し、上述のオプション-Bor--skip-bundleの記述がない場合は、% rails new _5.2.3_をのように指定していても、プロジェクト生成後にbundle installが行われるため、PCの中にインストールされている最新バージョンが入ってしまうので注意する。
この場合は% rails new -Bでプロジェクトディレクトリ生成後
Gemfileのgem 'rails', '~>5.2.3''= 5.2.3'に書き換えてから% bundle installする
'~> 5.2.3'のままの場合は5.2.3以上をインストールしてしまう。

% rails _5.2.3_ new myapp -B -d mysql --skip-turbolinks --skip-coffee

グローバルにgemが既に入りまくってしまっている自分はこんな感じのコマンドでプロジェクト作るのかな...って感じ。
ただ、--skip-coffeeはなくても-Bbundle install止めてる時点でGemfileのgem 'coffee-rails'をコメントアウトしてから手動bundle installでも結果は同じのような。


参考にさせていただいた記事
Rails tutorial 1.3 最初のアプリケーション
Railsドキュメント オプションについて一通り説明あり。

新規Railsプロジェクトの作成手順まとめ
rails newするときによく使うオプションと、rails newした後によく行う設定
新規Railsプロジェクト作成時のコマンド


問題なく行われてしまっている自動生成コマンド一つ一つ、何が起きているかくらいは確認した方がいいですね❗️
正直、公式ドキュメントは単体だと取っつきづらいけど、どのように使っているか少し調べてから見てみるとそもそもの書き方などや説明が詳細に載っているので色々と知ることが出来ます。

したらな❗️ 👋

Twitter × GASで検索したキーワードをslackに定期投稿する

前回の記事で開発者としてのアカウント申請を終えましたので、今回は実装したTwitter botについて再び手順を残していこうと思います。
...色々あって当初予定していたものとは違うものを実装することにしました。

アプリケーションの作成

ヘッダーバーに公式ドキュメントがあります。
まずはTwitter Developerの自分のアカウント名のプルダウンメニューから「Apps」を選択。
f:id:saka20taku43:20200626115657p:plain
何も作ってないのでこの画面が出ます。「Create an app」でアプリケーションの作成を行います。
f:id:saka20taku43:20200626144745p:plain
作成するアプリケーションの詳細を入力していきます。
どこかの記事で日本語だと申請が通りにくい、みたいな感じの書き込みをみた気がしたので、念のため英語で入力。

  • App name(必須、最大32文字):アプリの名前
  • Application description(必須、10〜200文字):アプリの説明
    • ユーザーに表示されるアプリの説明文。どういった用途の物なのかを書いておくと良い。

実際に書いた文章 => It is an application that regularly posts the results of searching with fixed keywords on Twitter to Slack.
(前回記事同様、自分の言葉が望ましいようです。日本語で書いて英語に翻訳)
f:id:saka20taku43:20200626162623p:plain

  • Website URL(必須):ウェブサイトのURL
    • [原文訳]ウェブサイトのURLは、アプリによって作成されたツイートのアトリビューションのソースとして使用され、ユーザー向けの承認画面に表示されます。
      • ブログの新着記事投稿の自動ツイートであればブログのURLでいいんでしょうが、今回は参考サイトを例に作成する予定のGASスクリプトURL(現状は空のGAS)を新規作成し、そのURLを記載。
  • Allow this application to be used to sign in with Twitter:このアプリケーションを使用してTwitterでサインインすることを許可するか(チェックボックス)
    • 意味不明だったので詳細リンクに助けを求める←
      • オンにするとTwitterのサインインボタンが設置できるとあるので、認証を楽にするための何かっぽいのですが、それくらいしか分からん。後、今回のアプリは自動で検索したキーワードをSlackに投稿するものなのでその度にサインインするの?ってなわけで無視。
  • Callback URLs:コールバックした時に表示するURL
    • これも言葉だけではサッパリ意味不明でした。が、Webアプリでたまに見かける○○を使ってサインイン(or ログイン)の時に変遷したページから戻ってくる時のURLを入力する、ってくらいは分かりました。今回はログインもクソもないので無視。

  • Terms of Service URL:利用規約のURL
  • Privacy policy URL:プライバシーポリシーのURL
  • Organization nam:組織名
  • Organization website URL:組織のウェブサイトURL

この4項目は特にないので無視。必須でもない。

f:id:saka20taku43:20200626165422p:plain

  • Tell us how this app will be used(必須、100文字以上):アプリの使用方法
    • この部分はTwitter者の従業員のみが見る部分らしいです。このアプリの使用方法と、これを使用することによる自分と自分の顧客が何をすることを可能にするのか?とあります。
      アプリの要件さえ決まっていれば何となくかけます。勿論、翻訳は使いまくりですが💦

一通り必須項目を入力したら、Createを押す。 f:id:saka20taku43:20200626171710p:plain とりあえず注意事項っぽいので再び翻訳。 f:id:saka20taku43:20200626171739p:plain

同じ名前のアプリケーションは作れない、URLが長い、とあり失敗したので記入し直す。
で、アプリ名はすぐに通るものにできたけど、URLは流石に代用できないので困った...。 websiteの指定先としての定義みたいなものを色々探してみたところこちらの記事を参考にさせていただました。
とりあえず自分のTwitterアカウントのURLを入力。

作成完了したら詳細一覧が表示されます。
ここの「keys and tokens」でアプリケーションで使用するConsumer API keysが生成され、Access tokenの発行ができるようです。
また、PermissionsタブのAccess permissions はツイートの読み込みだけの場合は Read onlyになってるか確認する必要があるようです。
自動ツイート(書き込み)も行う場合は「Read and write」するみたいですね。

この時点でTwitter APIを呼び出す準備は整っているようです。
Googleアカウント(GAS)とSlackアカウントは既に準備済みなので省略します。
これ以降の手順として

  • Slackのwebhookの口(Incoming webhook)を用意
  • GASからそのwebhookを呼び出すスクリプトを書く
  • それを定期実行させるトリガーをGASに設定させる

のようにしていくようです、順番にやっていきます。

Slack側の設定

検索した結果をメッセージとして送信した時の投稿先のチャンネル開設。
Slack での Incoming Webhook の利用を参考に設定をしていく。
f:id:saka20taku43:20200627143537p:plain
メッセージを投稿するワークスペースでAppを選択 f:id:saka20taku43:20200627150939p:plain
検索して出てきた項目を選択し、
f:id:saka20taku43:20200627151119p:plain
一応、ヘッダー右上のワークスペース名を確認。
投稿するチャンネルをセレクトボックスから選択。
選択後は設定画面に飛ぶ。ページ上部の有効/無効で有効になっているか確認。下の方にはJSONデータでテキストを投稿する際の送信先Webhook URLなどが記載されている。
このアプリ設定ページへのアクセスはヘッダーバー「管理」の「カスタムインテグレーション」にある。

後は、ワークスペースを一度再読み込みしてみて、追加したチャンネルに連携した旨の投稿がされていればOK。

GASスクリプトファイルの準備

Googleドライブから開くやつかその他で直接用意する。
Slack API 公式ドキュメントを参考に、SlackのWebhook URLを呼び出すコードを書いていく

var slackWebhookUrl = 'https://hooks.slack.com/services/XXXXXXXXXXX/YYYYYZZZZZAAAAABBBBBCCCCCDDDDDEEEEEE'function firstApp() {
  var data = {
    'text': 'Hello, this message is from my GAS App',
  };
  
  var options = {
    'method': 'post', 
    'contentType': 'application/json', 
    'payload': JSON.stringify(data)
  };
  
  UrlFetchApp.fetch(slackWebhookUrl, options);
}

'payload'はslack appの設定ページに出てきましたが、webhook URLにJSON payloadでテキストを送信する、とあるのでこの形式になるようです。
LINE APIの時もそうでしたが、GASからwebhook URLを呼び出すにはUrlFetchAppclassが持っているfetchを使い、引数には投稿先であるwebhook URLとHTTPヘッダの内容やテキストなどをJSON形式に変換したオブジェクトにして渡す。

記述が完了したら、GASスクリプトを実行して投稿できているかを確認する。
実行したい関数を選択肢、再生マークのボタンで実行。
f:id:saka20taku43:20200627161600p:plain
※ 初めて送信する場合は、前回と同様に送信することをGoogleアカウントが許可するか別窓で出てくる。詳細からプライベートページに行くように促すと、今回はブラウザではないので外部サービスへ直接送信するか許可するか聞かれる。のでこれを承認する。

ランタイムV8が悪さしてエラー吐くかもしれないと見かけたので、無効にして送信→成功。
続けて有効にして送信→成功。
f:id:saka20taku43:20200627161719p:plain
送信自体は問題なくできるようです。

トリガーの設定

一定時間ごとに動かす設定をコードで書く必要はないみたい。
先ほどのGASのツールバーの再生ボタンの左にある時計みたいなアイコンをクリック。
f:id:saka20taku43:20200627162118p:plain
トリガーを追加をクリックすると、モーダル状の設定画面が出てくる。
f:id:saka20taku43:20200627162415p:plain
イベントのソースを選択を押すと「時間主導型」「カレンダーから」など出てくるが、ここで時間主導を選択。
特定の時間や日付ベースを選んだりすることで、そのカテゴリーによって何日とか何時とか何時間おきに、とか選べる。
今回はお昼頃に検索をかけて定期的に投稿をして欲しいので f:id:saka20taku43:20200627162725p:plain
のように選択肢、保存。
実際に来るか確認したいなら1時間おきなどにしておいて確認しておいたほうがいいかもしれない。
f:id:saka20taku43:20200628132643p:plain
キチンと日付を跨いで投稿されているのが確認できました。

ひとまずGASとSlackで連動させる作業は一旦終了。

TwitterとSlackを連携させる

Slack側のドキュメントです。
まずSlackのワークスペースのサイドバー上部の「App」を押して出てくる画面の右上の「Appディレクトリ」をクリック。
立ち上がった別窓の検索欄にそのまま「Twitter」と入れて検索
f:id:saka20taku43:20200628144514p:plain
Slackに追加→Twitterインテグレーションの追加→連携アプリを認証(Authorize app)をクリック
今回はTwitter上でGASを利用して自動検索されたツイートをSlackに投稿するので、ツイート追跡は特に書きません。投稿するチャンネルは既に作ってあるのでそれを指定。
インテグレーションが発言をした際の明示として、「自動キーワード検索」のような形で名前をつけておきます。最後に設定を保存(Save Setting)。

Twitter Bearerトークン取得

特定のアカウントのデータにアクセスする場合(ユーザー情報読み込み、ユーザーとしての書き込み)にはアクセストークン認証が必要。
公開されている情報にアプリケーションとしてのアクセスを行う場合にはアプリケーション認証であるBearerトークンが必要とのこと。
(Bearerトークン = ベアラートークン = 無記名トークン)
今回の要件では個人としての、ではなくてアプリケーション上で公開されている情報にアクセスするためBearerトークンを用いる。
認証処理の流れはドキュメントの図が利用する構文もついているので見やすいです。

とりあえず公式のApplication-only authentication and OAuth 2.0 Bearer Tokenを見ます。

  • Bearerトークンを使用するとユーザーコンテキストなしでアプリケーションに代わってAPIリクエストを行うことができる、これはアプリケーションのみの認証と呼ばれる。
  • Bearerトークンは、oauth2 / invalidate_tokenを使用して無効化できます。Bearerトークンが無効になると、新しい作成の試行で別のBearerトークンが生成され、以前のトークンの使用は許可されなくなります。
  • アプリケーションに対して未処理で存在することができるBearerトークンは1つだけであり、このメソッドへのリクエストが繰り返されると、無効になるまで同じ既存のトークンが生成されます。
  • 成功した応答には、授与されたBearerトークンを説明するJSON構造が含まれます。
  • このメソッドによって受信されたトークンはキャッシュされるべきです。試行回数が多すぎる場合、リクエストはコード99のHTTP 403で拒否されます。

    公式ドキュメントより翻訳して引用

1回きりの使い捨てできるトークンが使えるみたいなものかな。
Accessトークンは免許証、Bearerトークンは映画のチケットや駐車券みたいなイメージらしいっす。

Twitterリファレンス、GASドキュメントを参考にTwitterのBearerトークンを取得するプログラムを書く。

// Twitter Developer でアプリを作成した時に生成されたcomsumer-keyとsecret_keyのキーペアを入力(機密です、注意)
var consumer_key = 'xxxxxxxxxxxxxxxxx';
var consumer_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

function searchTweetsApps() { 
  // Twitter Bearerトークンの取得(検索APIの呼び出しに必要)
  // POST oauth2/token  https://developer.twitter.com/en/docs/basics/authentication/api-reference/token
  var blob = Utilities.newBlob(consumer_key + ':' + consumer_secret);
  var credential = Utilities.base64Encode(blob.getBytes());

  var formData = {
    'grant_type': 'client_credentials'
  };

  var basic_auth_header = {
    'Authorization': 'Basic ' + credential
  };

  var options = {
    'method': 'post',
    'contentType': 'application/x-www-form-urlencoded;charset=UTF-8',
    'headers':  basic_auth_header,
    'payload': formData,
  };

  var oauth2_response = UrlFetchApp.fetch('https://api.twitter.com/oauth2/token', options);  
  var bearer_token = JSON.parse(oauth2_response).access_token; 

// Todo:Twitter 検索APIの呼び出し

// Todo:Slackに通知  Incoming Webhook

}

STEP1:まず、コンシューマとシークレットキーを用意し、その2つのキーをURLエンコードする必要があるそうです。
で、連結する際に:で繋げるとあります。Utilities.newblob()構文とUtilities.base64Encode('blob obj' + getBytes())GASでbase64にエンコードするを参考に作る。

STEP2:STEP1で用意したエンコードされた文字列は、POST oauth2/tokenにリクエスト発行することにより、Bearerトークンと交換する必要がある...専門用語だけでは分かりませんが、何をすべきかは書いてあります。

  • リクエストはHTTP POSTであること。
  • リクエストはSTEP1で用意した{Basic エンコードの値}を含むAuthorization:ヘッダーを含める。(Basicの後に半角のスペースが必要なので注意)
  • リクエストはapplication/x-www-form-urlencoded;charset=UTF-8の値を持つcontentTypeヘッダーを含める。
  • リクエストの本文'payload'はgrant_type=client_credentialsであること。

今回のコードではoptionsの内容はそれぞれ上記の項目を一つのオブジェクトとしてまとめたもの。

GASからURLを呼ぶ時の書き方は上記の通り。今回のリソース先はAPIリファレンス POST oauth/tokenを参考にhttps://api.twitter.com/oauth2/tokenと記入し、ヘッダーはoptionsで用意したJSON形式のものを引数に渡している。
次のJSON.parse()では文字列をJSONとして解析し、オブジェクトを構築する構文。引数にはリソース先のURLにFetchした際のレスポンスを渡している。

HTTP/1.1 200 OK
Status: 200 OK
Content-Type: application/json; charset=utf-8
...
Content-Encoding: gzip
Content-Length: 140

{"token_type":"bearer","access_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}

アプリケーションは、返されたオブジェクトのtoken_typeキーに関連付けられた値が無記名であることを確認する必要があります。 access_tokenキーに関連付けられている値は、Bearerトークンです。

公式リファレンスより引用

このレスポンスの中から.access_tokenと書くことで、最初に連結してエンコードされたBearerトークンを取得できるようです。accessトークンを渡しているならそのままaccessトークンを取得できる...と解釈。
デバッグとして関数最下にLogger.log(bearer_token);と記述し、関数を実行。ツールバーの「表示(View)」→「ログ(Logs)」で確認できる。
f:id:saka20taku43:20200628190102p:plain

Search Tweets(Twitter検索API)

Search Tweetsにはプランが3種類あって無料使用できるのはStandard search APIのみ。
f:id:saka20taku43:20200628190824p:plain
翻訳。 f:id:saka20taku43:20200628190707p:plain

無料版は過去7日文迄しか遡れないようですが...まぁ十分。

参照するページがあっちこっち行って分かりにくいですが、Application-only authentication and OAuth 2.0 Bearer TokenのSTEP 3(ようは続き)を見ていくと、
「Bearer Tokenを使用して、アプリケーションのみの認証をサポートするAPIエンドポイントにリクエストを発行できます。 Bearer Tokenを使用するには、通常のHTTPSリクエストを作成し、AuthorizationヘッダーにBearer <手順2のbase64 bearer token value>の値を含めます。署名は必要ありません。」(引用を翻訳し、抜粋)
とあるので、'Authorization': 'Bearer ' + bearer_tokenの形でヘッダーに渡してあげてリクエストをする。

  // Twitter 検索APIの呼び出し 
  // GET https://api.twitter.com/1.1/search/tweets.json
  var search_keyword = 'xxxxx -rt';

  var bearer_auth_header = {
    'Authorization': 'Bearer ' + bearer_token
  };

  var search_response = UrlFetchApp.fetch(
    'https://api.twitter.com/1.1/search/tweets.json?q=' + search_keyword + '&lang=ja&result_type=recent&count=10',
    { 'headers': bearer_auth_header });
  result = JSON.parse(search_response);

このコードでは検索URLの準備と、それを利用してのリクエストを行っている。 URLではエンコードされた文字を使用する必要があるようです。

search_keywordに入れてある文字列は検索用語で、Using the standard search endpoint公式ドキュメントのテーブルの辺りに書き方が書いてありますね。
タグを検索したい場合、「#」は検索演算子として使用する場合は「%23」のように書く、また複数検索する場合の区切りとして使用するキーワード間のスペースは「%20」か「+」で書くようです。
-rtリツイートを検索対象から外すことができるようです。

最後には検索APIのリクエストをしている。URLはドキュメントのBest Practices にいくつか例が載っているのでそちらを参考にする。

  • #superbowl で検索APIを使用する際のURL
    https://api.twitter.com/1.1/search/tweets.json?q=%23superbowl&result_type=recent
  • さらに、特定の言語(今回は日本語)のみを検索結果としてリクエストする場合
    https://api.twitter.com/1.1/search/tweets.json?q=%23superbowl&lang=ja&result_type=recent

これらlangresult_typeなどの使用できるパラメータ(公式リファレンス)について
recentは最新の投稿の取得。countはデフォルトでは15件、最大100件。今回は毎日定刻に検索を行うので10件を指定。

Twitterの検索結果をSlackに投稿する

Twitter検索を行った場合の結果の取得についてはExample Response(公式リファレンス)

statusesにオブジェクトが入っているのでkeyを使って取り出すことができる。
今回の検索結果はresultに代入されているのでそちらからstatusesで取り出してあげればいいようです。

また、GASからSlackに投稿するコードは前述したコードが利用できそうです。

  // Slackに通知  Incoming Webhook
  result.statuses.forEach(function(status) {    
    var data = { 
      'text': '-----------------------------------------\n' +
      status.text + 
      '\n----------------------------------------\n' +
      status.created_at + 
      '\n ' + 
    };

    var options = {
      'method' : 'post',
      'contentType': 'application/json',
      'payload' : JSON.stringify(data)
    };

    UrlFetchApp.fetch(slackWebhookUrl, options);
  });

dataの部分は投稿される本文になりますが、今回はstatusesには複数データが入っているので、forEach文を使用して1件ずつ繰り返して投稿するような流れになっています。

最後にトリガーで定期実行するように設定。前回の関数は今後使用しないため、こちらをそのまま書き換えていく。

検索キーワード 'rails+%23Google Apps Script -rt'
実行してみたが反応せず。Logger.logでデバッグしたらnullだったので取得すら失敗してるみたい。
検索URLにエンコード以外でスペース入れてるのが問題なのかもと思い、'%23GoogleAppsScript -rt'で検索 (本当はrailsでもやってみたのですが災害情報ばっかり引っかかるので変えました(笑)) f:id:saka20taku43:20200629151330p:plain
リツイートが表示されている...とりあえず公式リファレンスを参考に記事、実装コード共に修正。
後、意外と一つの単語に対して調べる場合、大分前まで遡ってるので件数は3件くらいでもいいのかも?
もしくは検索用語増やすか。

  • var search_responsecount=3に修正
  • var search_keyword'%23GoogleAppsScript -filter:retweets'に修正
  • 細かいが区切り線を変数postSplitなどの名前として用意 f:id:saka20taku43:20200629152658p:plain

何か格言みたいなのばっかりになったな...


参考にさせていただいた記事
公式ドキュメント/Twitter APIリファレンス一覧
GASを使って、Twitter検索した結果をSlackへ定期投稿してみた
Google Apps ScriptからSlackへ定期投稿してみた
Slack API 公式ドキュメント
Twitter REST APIの使い方
Twitterの検索結果のAPIが返すオブジェクトの公式リファレンス


先人にただただ感謝!! APIの利用は構文の組み立て方とか、どこのドキュメント見ればいいかとか、正直難しかったけども普段使いしているアプリと直結させたりできるので使い方次第ではとても便利。
LINE API共々慣れていければいいな...

したらな❗️ 👋

Twitter botの作成に向けてその1(開発者アカウントの申請)

Twitter botを触ってみたいと思った背景について

ブログを書いた際にTwitterに自動でブログのリンクをツイートするアプリを実装したいと考えていました。はてなブログではTwitterアカウントと連携することで投稿後のページにてそのままツイートすることができます。
これは他のこと(例えば、自分でブログサイトを解説した際など)にも応用が効くので、今回は目先でTwitterを利用していることを理由としています。

そんなわけで、いつも通り目的が曖昧ですが手順を書いていきます。(6/24/'20 時点の方法です)

自分が使っているアカウントを「Developer申請」する

新しいアカウントを作成するのではなく、現在使用しているアカウントに開発者として権限を持たせる為の申請を行う。
Twitter Developerにアクセスし、サインイン(自分の場合はブラウザでログイン状態になっていたのでここは飛ばしてます)
サインインしたらアカウントアイコン左にある「Apply」をクリックする。
変遷先のページで「Appry for a developer account(開発者用アカウントを申請する)」をクリック。

使用中のアカウントに電話番号が登録されてない場合はAdd a valid phone numberで電話番号を登録する必要があります。自分は既に登録済みだったので、この登録用の画面は出ていません。

開発者アカウントの使用する理由を入力

f:id:saka20taku43:20200624144651p:plain
今回はbot作成が目的のため、「BOTを作る」を選択して次へをクリック。一つしか選べないようです。

選択すると「This is you, right?(これはあなたですか?)」という確認画面が出てきます。
自分のアカウントの情報が出てきていれば大丈夫です。
ここでも入力する項目があり

  • どこの国に住んでいるか(セレクトボックス)
  • なんと呼ぶか(ニックネームを手入力)
  • Twitter APIの更新についてメールを送る必要があるか(チェックボックス)

など出てきて聞いてきますので入力して次へ。

その次の画面で利用目的、理由を入力

In your words(自分の言葉で)」「In English, please describe how you plan to use Twitter data and/or APIs. The more detailed the response, the easier it is to review and approve.(英語で、TwitterデータやAPIをどのように使用するかを説明してください。応答が詳細であるほど、レビューと承認が容易になります。)」とあります。
...正直単語が軽く分かる、読める程度の英語力しかない自分ではこれが一番しんどい。
自分が申請を行う動機を記述し、Google翻訳を使用して書いていきます。

  1. When I wrote what I learned on a blog(https://seramay419plog.hatenablog.com/), I manually entered the link destination in Twitter and tweeted it, but I want to automate it using the Twitter API.
    (学習した内容をブログ(https://seramay419plog.hatenablog.com/)に執筆した際に、Twitterに手動でリンク先を入力してツイートしていますが、Twitter APIを使用して自動化したい。)
  2. Considering the frequency of current tweets, the maximum frequency of automatic tweets is about twice a day.
    (現在のツイートの頻度から考えると、自動ツイートの頻度は、最大で1日2回程度です。)
  3. The reason why I am disseminating technology on the blog is that I think that the information on this blog will be useful if someone who is learning as an engineer tries to learn the same content.
    (ブログで技術発信をしている理由としては、これからエンジニアとして学習を始める方が同じ内容を学習しようとしたときに、このブログでの情報が役に立つと思っているからです。)

※ 3番に関しては無くても特に良いみたいなのですが、ブログ発信の目的などを書いておくとストーリーが分かりやすく、承認されやすいとのことです。
自分の場合は目先の目的は備忘録ですが、最終的に見返す人が自分だけではないかもしれないので、このように書いてます。綺麗事とか言わないでね、お願い!

さらに詳細な質問に答えていきます

Are you planning to analyze Twitter data?(Twitterデータを分析する予定ですか?)」
Twitterデータを取得し、分析するか聞いてるようです。トレンドのある内容を取得して学習のモチベーションの一つにしたいと考えているのでYES。100文字以上で理由を説明。
=> Since I am studying programming, I would like to analyze tweets of the most popular programming languages ​​and other technologies used by users and use them in my next learning.
(プログラミングの学習をしているので、ユーザーが利用している技術で最も人気のあるプログラミング言語などのツイートを分析し、次の学習に活かせるようにしたい。)

Will your app use Tweet, Retweet, like, follow, or Direct Message functionality?(アプリはツイート、リツイート、いいね、フォロー、ダイレクトメッセージ機能を使用しますか?)」
ツイートを自動化したいので当然YES。これも100文字以上。
=> Every time you post what you learned on the blog, you will tweet the link of that article, so the application uses the tweet function
(学習した内容をブログに投稿するたびに、その記事のリンクをツイートするので、アプリではツイート機能を利用します)

Do you plan to display Tweets or aggregate data about Twitter content outside of Twitter?(ツイッターを表示したり、ツイッター以外のツイッターコンテンツに関するデータを集計したりする予定はありますか?)」
次に学習する内容についてトレンドのある言語や技術などを分析するのでこれもYES。同様に書いていきます。
=> From the data collected on Twitter, we will display the analysis result on the blog in order to incorporate the trending technology as a keyword into the road map as the content to be learned next.
(Twitterで収集したデータを基に、分析結果をブログに表示し、トレンドテクノロジーをキーワードとし、次に学ぶ内容としてロードマップに組み込んでいきます。)

Will your product, service or analysis make Twitter content or derived information available to a government entity?(あなたの製品、サービスまたは分析は、政府機関がツイッターのコンテンツまたは派生情報を利用できるようにしますか?)」
流石にこれはないのでNO。

一通り入力したら

Is everything correct?(全て正しいですか?)というページに出ます。
問題ないようであれば「Look good!」をクリック。

次に進むと確認して同意してくださいというページに進みます。
開発者契約とポリシー、よくある利用規約みたいなものですね。
チェックボックスを押して同意したら「submit application」が押せるようになるのでクリックする。
f:id:saka20taku43:20200624173204p:plain
登録しているメールアドレスにメールが届き、確認してくださいとあるので「Confirm your Email」をクリック。
これで申請完了です。

後は審査が通ればAPIを利用した開発ができるようになります。
審査結果が届くには1日ほどかかるそうですが、説明文などを適当に書いたりすると書き直しを要求されたり、最悪2度と申請するな!という忠告、警告文が返ってくることもあるのだそう...。

とのことで戦々恐々としていたですが...

自分は「confirm your Email」を押した瞬間にブラウザが立ち上がり、申請が通...ったのかな、これは。
f:id:saka20taku43:20200624174719p:plain

何はともあれ開発が進められるようで安心しました😅


参考にさせていただいた記事
【2020年版】Twitter APIの申請が1時間で完了した英文を申請方法と共に徹底解説!

今回は技術的な操作や記入は特になかったので公式ヘルプは見なかったですね...。


次回はGASを利用したBOTの作成について書いていこうと思います。

したらな❗️ 👋

railsのパンくず機能[gem 'gretel']

スクールの最終課題で気になっていたgemその2。
Webサイトで複数のページ構造をしている時によくある、TOP > 概要 > 会社理念みたいな足跡を表示するパンくず機能と言うのですが、これを実現する際に使用するgem 'gretel'について少し調べました。

由来は、グレーテルといえばたしか童話ですよね。センスとボキャブラリー豊富。

導入、準備

gem導入方法は公式GitHub

「Gemfile」
gem 'gretel'

// ターミナル
% bundle install

パンくずリスト構成ファイル、と言うものを生成するコマンド

$ rails g gretel:install

config/breadcrumbs.rbファイルが生成されます。
(生成されたファイルの下の方の注釈に、パンくずリスト設定ファイルを分割する場合にはconfigディレクトリ下にbreadcrumbsディレクトリを置いてその中に設定ファイルを置いてができるそうですが、それはまた別の機会に...)

設定ファイルbreadcrumbs.rbを追記

# ルート
crumb :root do
  link "トップページ", root_path
end

# マイページ
crumb :user_show do
  link "マイページ", user_path
end

# 出品商品詳細
crumb :product_show do
  link "出品商品詳細", products_path
  parent :user_show
end

一番上のcrumbの後ろは(viewから)設定ファイルで記述したリストを呼び出す際に使用する引数みたいなもの(というイメージ、後述)。 linkの部分の""の中はパンくずリストに表示されるリンクの名称、その後に記述されているのはそのページ(呼び出し元)のprefix。% rails routesを参照して書いていく。
(クリックした際にリンク先に移動するという実装をしない場合はprefixは省略してしまっても問題ないみたいです)
parentでは親の設定を行っている。ここで記入されている:user_showも先ほど同様に設定したものを書いている。マイページの上のルートは親として設定する必要はないみたいです。

今回の設定ファイルの構成での最終的な表示イメージは
トップページ > マイページ > 出品商品詳細
マイページにそのユーザーの出品した商品一覧があり、名前をクリックするとその商品の詳細が見れる、そんな変遷イメージです。

view側を追加していく

パンくずリストを表示されるページごとに記述する場合は

-# 「app/views/users/show.html.haml」
- breadcrumb :user_show
= breadcrumbs separator: " &rsaquo; "

1行目にある:user_showは先ほど設定ファイルにて記述した呼び出し用の引数みたいなもの。
こちらは設定されたパンくず- breadcrumb :{各crumb名}を表示する場所に記載します。
separatorオプションで、パンくずリストを区切る際に使います。
" &rsoquo; "はブラウザ上で出力されると > となります。HTMLにあるメタ文字&gt;のように使えます。前後に入れている空白がそのまま適用されないなら&nbspを前後に入れてあげると幸せになれるのかもしれない。

= breadcrumbs pretext: "閲覧場所:", separator: " &rsaquo; "のように書くと、パンくずリストの前に閲覧場所:というテキストを挿入することができます。指定がない場合はデフォルトでは表示されないようになっているので、特に必要ないのであれば省略してもOK。

パンくずリストは殆どのページにて表示されるので呼び出し部分をテンプレート化してrenderを使用して呼び出すようにする、とよりいいのかもしれない。

-# 下記コードを「layouts/_breadcrumbs.html.haml」に移行
= breadcrumbs separator: " &rsaquo; "

-# 「app/views/users/show.html.haml」移行した部分の書き換え
- breadcrumb :user_show
= render "breadcrumbs"

今回既に用意している「app/views/products/show.html.haml」などでも

- breadcrumb :product_show
= render "breadcrumbs"

と書くことで部分テンプレートを追記する必要なく、呼び出すことができる。

renderを繰り返し書いているのでリファクタリング

今回は一例として用意してるので2つしかないですが、パンくずリストはその仕様としてこれより多くの複数のページに表示されるので、この書き込みも省略できるように書き換えていく。

パンくずリストlayouts/application.html.hamlなどで記述されるヘッダーの直下によく見かけるので各ビューで用意していたrenderをこちらに移行させる。

-# 「layouts/application.html.haml」
= render "breadcrumbs"

-# 「app/views/users/show.html.haml」、「app/views/products/show.html.haml」の render部分を削除

application.html.hamlの記述によってはパンくずリストの表示の書き込みが、各ビューより上に来てしまうので、設定ファイルから読み込む記述が後になるとエラーが出ると思います。
ので、case文などでその時使用しているコントローラー、アクションを条件分岐するための引数として渡してあげるようにして、パンくずリストの設定ファイルの呼び出しも部分テンプレート内で書き換えます、こちらの記事を参考にさせていただきました。

-# 「app/views/layouts/_breadcrumbs.html.haml」 (部分テンプレート)
- case params.value_at :controller, :action

- when ['users', 'show'] 
  - breadcrumb :user_show

- when ['products', 'show']
  -breadcrumb :product_show

= breadcrumbs separator: " &rsaquo; "

-# 「app/views/users/show.html.haml」、「app/views/products/show.html.haml」の - breadcrumb :〜 を削除

使用する(paramsから取得された)インスタンスやオブジェクトは設定ファイル側かview側で書かれていたので、ユーザー毎の商品詳細ページに飛ぶ際にエラーがでるのであれば
-breadcrumb :product_show, @product
と書くか、あるいは link "出品商品詳細", product_path(product) のように書いてDBからの情報を渡してあげる必要があります。


参考にさせていただいた記事
公式GitHub
【Rails】gretelを使ってパンくずリストを作成
一番わかりやすいパンくずの実装(gem 'gretel')


今回はここまで。

プロジェクトによって見ることも知ることもないgemがたくさん存在するので、よく使われているgemを一通り調べて見るのも面白いのかもしれません。
でもFWばっかり使っていて基本のrubyの部分が理解できているのか怪しい時があるため、あんまり固執しすぎない程度に学んで行ければな、と思います。

したらな❗️ 👋

gem 'ransack'について

最終課題で追加実装として用意されていた項目で、実際のアプリケーションに落とし込むところまで行けなかったのですが、気になったまま放置していたものがあったので、今回はそれについて書いてみようと思います。
gem 'ransack'ですが、ザックリと言うと検索機能用に用意されたgemのようです。
何がしかのwebアプリで検索は使うことがあるので、使えるようになっておくと良いのかなと思います。

導入方法

gemの導入はいつもの流れ
公式GitHubによると互換性のあるバージョンはRuby2.3以降のRails5.0, 5.1, 5.2, 6.0のようです。

# Gemfile
gem 'ransack'

Gemfileに記入後にターミナルで

% bundle install

を実行。

ransackは検索機能なので実装時にはテスト用にダミーデータが必要。
実装において検索機能はアクションで記述していく。

通常手書きで検索機能を実装する時、ヘッダーバー(application.html.erb)などのように複数のページに跨いで検索機能が実行される場合は、DBのデータを直接操作するのでモデルにメソッドを書くことが多かったですが、ransackを使用した時はこの辺りは少し変わってます。

GitHubによればSimpleモードの利用においては特にセットアップは殆ど必要なくそのまま使える。
メタサーチの場合はいくつかのことに注意する必要がある。

  1. 検索パラメーターのデフォルトのパラメーターキーは、:searchではなく:qになりました。これは主にクエリ文字列を短くするためですが、高度なクエリ(下記)は、ほとんどのブラウザでURLの長さの制限に違反するため、HTTP POSTリクエストへの切り替えが必要です。このキーは構成可能です。
  2. form_forはsearch_form_forになり、Ransack :: Searchオブジェクトが渡されていることを検証します
  3. Common ActiveRecord :: Relationメソッドは、検索オブジェクトによって委任されなくなりました。代わりに、Ransack#resultを呼び出して検索結果(ActiveRecordアダプターの場合はActiveRecord :: Relation)を取得します。

※ 公式GitHubより引用

# 検索を行う対象controller
def search
  @search = Product.ransack(params[:q])
  @search_products = @search.result(distinct: true)
end

ransackメソッドを利用するにはparamsでは[:q]と指定してやる必要がある、これは引用によれば元々は[:search]だったとありますね。
取得された変数に対してresultメソッドを使用することで検索結果を出力することができる。
distinct: trueオプションをつけることで重複する内容を省きます。
関連する別のテーブルでソートする場合は @search_products = @search.result(distinct: true).includes(:user)のように記述しページネーションを利用している場合はさらに.page(params[:page]).per(5)で1ページ辺り5件表示されるようになる。

viewではform_forヘルパーではなく、search_form_forと言うのを使用する。
ransackでは主要で使えるビューヘルパーが2種類あり、もう一つはsort_linkというそうです。

.search_wrapper
  = search_form_for @search do |f|
    = f.label :name_cont
    = f.search_field :name_cont, placeholder: 検索するキーワードを入力
    = f.submit '検索'

検索フィールドで使用しているsearch_fieldの引数は:属性名の形か、:name_or_email_contのように指定することで複数のカラムを検索することもできる。
公式ドキュメントにある_predicateの部分は検索する時のSearch Matchersという検索述語のこと。
今回使用している_contは検索したキーワードを含む(部分一致)か、という条件。

今回のように基本アクション以外を使っている場合はルーティングは別途用意する

「route.rb」
resources :product do
  collection do
    get :search
  end
end

sort_linkヘルパーはテーブルヘッダーを作成する。並べ替えの条件が複雑な場合はmodelクラスでscopeを使って設定するみたいです。
ソートが必要ない場合は使わなくても大丈夫ですね。
@search_productsを表示できるようにviewに書いておけばOK。 検索をかけて該当した場合、該当しない場合、検索してない場合で用意する。


参考にさせていただいた記事
公式GitHub
[Rails5]ransackってなんぞ?
【Ruby on Rails】gem 'ransack' を利用した検索機能/フォームの実装


色々なマッチャが使用できるので検索機能を使う時は利用していきたいです。

したらな❗️ 👋

Google Apps Scriptを利用する

今回の記事では前回実際に記述したGASで利用できるメソッドや関数について書いていきます。

※ GASを利用したBOT(LINE, Twitterなど)の作成について、GAS以外での環境の準備についてはこちら

GASでのデバッグ

GASの開発環境ではJavaScriptデバッグの時に使用しているconsole.logが使用できないようです。その代わりにLoggerというものが用意されているようなのでこれを使用します。

function test(){
 Logger.log("test");
}

console.logのように.logで文字列を入力するとログを残すことができる。
この関数を作成し保存、メニューの「関数を選択」でtestを選択し、その左にある三角を押す。
これにより関数を実行することができる。実行後には何も表示されないが、そのまま MacOSで「⌘ + return」でログの表示ができる。
そこに実行時間とtestの文字列が表示されていたら正しいログが出力できている。

GASにアクセス(HTTP method: GET)するための関数の作成

function doGet() {
  return ContentService.createTextOutput("success");
}

説明は前の記事
メソッドの名前からの想像と、ブラウザで開いた時に文字列を確認できることから引数で指定した文字列をページに表示できる。

スプレッドシートに書き込むための関数

LINE BOTAPIでは、ユーザーから送られたメッセージがPOSTでWebhookAPIに送信される。
デバッグで使っていたLoggerは実行環境での確認には使用できるが、実際にLINEのAPIを通して送られてくるWebhookのデータの中身は閲覧できない。
その為、データをスプレッドシートに書き込む関数を作成。

GASで任意のテキストをシートにまとめるための関数を作成

var spreadsheet = SpreadsheetApp.openById("{シートID}");
function appendToSheet(text){
 var sheet = spreadsheet.getSheetByName('webhook');
 sheet.appendRow([text]);
}

appendTosheet(text)を実行することで、スプレッドシートに送信されたテキストを書き込むことができる。変数spreadsheet自体は他の関数でも使用することがあるため、関数の外で宣言して一番先にしておく方が取り回しがしやすいです。
変数spreadsheetsheetはそれぞれこれまで準備してきた中で設定してきた名前を用いているので、それらを呼び出して取得し、変数に代入していることがわかります。
appendRow()では変数sheet(webhookシート)に引数で渡された値をシートのレコードに追加している。

動作確認するためにdoGet関数でappendTodsheet()関数を呼び出し。

function doGet() {
 appendToSheet("test");
}

メニューバーの「実行」から「関数を実行」→「doGet」をクリックすると、その場でコード動作確認ができる。
※ この時「アプリが確認されていない」という別ウィンドウが表示されてしまった場合は「詳細」→「{BOT名}(安全ではない)に移動」をクリックすることでGoogleアカウントへのアクセスに対してドライブのスプレッドシートなどの編集や削除ができるようにリクエストをする画面が出てきます(今回、BOTは自分で作っているものなので許可しています)。

許可した後にID指定先のスプレッドシート(今回はwebhookという名前のシート)を開いて、シートに文字列が入力されていれば成功(今回は関数呼び出しの引数に"test"という指定された文字列を送信しているので testと入力されている)。

POSTに反応する関数の作成

GETと同じようにdoPostという関数で作成することができます。

  • POSTで送信されたデータの中で、LINE APIで利用できる部分の取得webhookData
  • そこから送信されたテキストを取得message
  • メッセージに対してBOTがメッセージを返信するのに、送られてきたメッセージのtokenの取得が必須replyToken
    • BOTが自発的にメッセージを送信する場合には機能に差がある。

これらを利用してPOSTが実行された場合に必要な情報を返すコード

function doPost(e) {
 var webhookData = JSON.parse(e.postData.contents).events[0];
 var message,replyToken;
 message = webhookData.message.text;
 replyToken = webhookData.replyToken;
 return sendLineMessageFromReplyToken(replyToken,message);
}

returnでの返り値にはAPIを叩く関数が記述されており、そちらの実行結果が返り値となる。
このsendLineMessageFromReplyToken()では取得したトークreplyTokenとテキストmessageを引数として渡している。
受け取った情報を元に送信元に返信を行う関数sendLineMessageFromReplyToken()では

var channel_access_token = "管理画面から取得したアクセストークン";

function sendLineMessageFromReplyToken(token,replyText){
 var url = "https://api.line.me/v2/bot/message/reply";
 var headers = {
   "Content-Type" : "application/json; charset=UTF-8",
   "Authorization" : "Bearer " + channel_access_token
 };
 var postData = {
   "replyToken" : token,
   "messages" : [{
     "type" : "text",
     "text" : replyText + "なるほど、やりますねぇ!"
   }]
 };
 var options = {
   "method" : "POST",
   "headers" : headers,
   "payload" : JSON.stringify(postData)
 };
 return UrlFetchApp.fetch(url, options);  
}

コードの参考はLINE公式APIリファレンス-Messaging APIに載っています。
今回は応答メッセージを送るの辺りが該当箇所に近いのかな。

  • 1行目は変数宣言し、発行したトークンを代入。
  • 2行目のfunctionから下は、先ほどのAPIが呼び出された時の処理の内容が書かれている。
    • 変数urlには応答メッセージ用のAPI URLを代入。これは決まったURLのようです。
    • 変数headersJSONリクエストについて。Content-Typeヘッダにはtext/jsonもしくはapplication/jsonというMIMEタイプを指定する必要がある。charset=UTF-8はHTMLとかでもよく見る文字コードってやつ。"Authorization" : "Bearer "の部分はAuthorizationヘッダを用いた認証(BASIC, Digest, Bearer)。Bearerでの認証はトークンを用いて認証をすることを想定して行うことが多いようです。HTTPでのヘッダにスキームとして指定でき、今回のようにトークンをセットにして指定する。形式はtoken68と決められているようです。
    • 変数postDataには引数で受け取ったreplyToken(送信元のwebhookDataのトークン)や本文が代入されている。
    • 変数optionsにはHTTPメソッドの形式と最初に宣言したHTTPヘッダの内容、トークンや本文などをJSONの文字列に変換されたものが代入されています
    • 最後のreturnではGASから外部APIにデータを送信する場合のUrlFetchAppfetchメソッドを利用。

これで、LINEの管理画面から取得するアクセストークンと前述のリプライトークン、送りたいテキストをLINE APIの形式に沿って送信することで返信できる。自分形の要約では普通にLINEで投稿するとbotがその形式にそって返信するって感じ
この時、リプライトークンを利用して送信先を指定しているため、返信相手の指定は必要ない 。

完了したら

一通り入力できたら内容を保存し、更新して公開。LINE BOTが実際に動作するか、確認してみる。(ここでもアカウント外部へのアクセスに対して許可するための画面が出てます)

うまく動作しない場合

※ Loggerやスプレッドシート書き込みを利用して送信されている内容などを確認し、デバッグを行っていく。

function appendToSheet(text){
 var sheet = spreadsheet.getSheetByName('webhook');
 sheet.appendRow([text]);
}

function doGet(test) {
 appendToSheet(test);
}

// 調べたい変数などを関数を利用して書き出す
〜省略〜
  var webhookData = JSON.parse(e.postData.contents).events[0];
  doGET(webhookeData);
〜省略〜

実際に送信された内容をスプレッドシートに書き出すならこんな感じか。
複数ある場合は


参考にさせていただいた記事
プログラミング初心者でも無料で簡単にLINE BOTが作れるチュートリアル

一つ一つ説明が丁寧で、実装にあたりとても助かりました。


基本的な使い方や使用できるメソッド等をもっと学習して色々と機能を追加したい所存。

したらな❗️ 👋

LINE botの作成 (準備)

最近はご時世的なものもあって余り使わなくなってしまいましたが、仲間との飲み会などで連絡ツールとしてよく使っていたLINE。
このLINEのbotを作ってみて、これから便利な機能をつけたいなって思った時の自分の中の参入障壁みたいなものを少し取り除きたいなーと思って少しだけ調べてみました。
いつもどーり調べたことや行ってったことをツラツラと書いていきます。

bot作成にあたって必要なもの

  • Googleアカウント
  • LINE Developersアカウント
  • JavaScript, Node.jsの学習(自分はあまり長く付き合ってませんが...)

ちな、自分はMacBook OS(Catalina)を使用しています。

LINE Developersのアカウント作成

これまでは開発者としてLINEを使用することはなかったので、アカウントを持ってません。ので作成から。
LINE Developersにアクセスして右上のログインボタンをクリック。
公式のマニュアルに一通り流れが書いてあります。
正直、現状使っているLINEアカウントの情報と分けるメリットが今は感じられないので、LINEアカウントを使用してログインしていきます。
開発者の名前、メアドを入力するとコンソールホームページが開かれます。
f:id:saka20taku43:20200614135132p:plain
このコンソールページへは自身のアカウントアイコンをクリックしてサイドバーにある「コンソールホーム」を押すと飛ぶことができます。

次にプロバイダー(提供者)を作成する必要があります。クリックすると提供者名を入力するフォームが出てくるので、そこにプロバイダー名を入力。自分はLINE Develpersのアカウント名をそのまま入れました。

その次に Channelを作成します。
6/14/'20現在では、LINEログイン、Messaging API、Clova skill、β版ブロックチェーンサービスの4種類のチャンネルタイプが存在します。
f:id:saka20taku43:20200614141814p:plain
bot作成においてはMessaging APIを選択します。選択してページ変遷後はいくつかの必要事項を入力します。

  • チャンネルタイプ(事前選択済み)
  • プロバイダー名(事前選択済み)
  • チャンネルアイコン(オプション、なしでもOK):画像の拡張子はpng, jpg, gif, bmpなど決まっています。また、3MB以内限定。
  • チャンネル名:アプリの名前
  • チャンネルの説明:どういったアプリかの説明文、空欄はダメですが内容は任意。
  • カテゴリー:セレクトボックスから「飲食」「ゲーム」などのカテゴリーを選択。
  • サブカテゴリー:上記カテゴリー選択後にセレクトボックスから細分化されたサブカテゴリーの選択。
  • メールアドレス:自身のメールアドレスを入力
  • プライバシーポリシーURL(オプション、なしでもOK):HTTPS URLであれば登録可能。
  • サービス利用規約などのURL:HTTPS URLであれば登録可能。

設定自体にはアプリの動作には関係ないが、とりあえずはなるべく分かりやすくしたり、関連性を持たせたりした方がいざ使う時に困らない。
今回は仕組みを知るためなので余り凝ったことは考えてません。入力したものに対してオウム返ししつつ褒めてるようで煽ってくるbotを作成します。

色々と調べていくとLINE DevelopersではSSLを利用する必要があり、HerokuでデプロイしてSSLを利用するには $7/月 の支払いが必要とのこと。
GASでは無料でSSLサーバにてbot処理が利用できるみたいなので、今回はそちらを利用します。

GAS(Google Apps Script)って何?

GASとは、Googleが提供するJavaScript互換のサーバーサイド•スクリプト環境。
通常はブラウザにJavascriptの実行環境があるけど、GASの場合はGoogleのサーバー上に実行環境がある。そのため、よく使われていた「window」「document」といったオブジェクトの指定などはできないようです。その代わりにGoogleの便利機能を利用できるオブジェクトが存在するようです。
また、Node.jsなどのようなサーバーサイド言語であれば開発環境などは自分で揃えなければならないけれど、GASではこの環境自体がサーバー上にあるため、JavaScriptについて少し知っていればとっつきやすいというのがメリットの一つのようです。

使用可能オブジェクト、バージョンについてはこちらこちらを参考にしました。
割と最近になって、ES6で利用できる機能がサポートされたようです。
これまではlet, const, アロー関数や他の便利な構文も使用できなかったみたいですね。

利用するにはまず当たり前ですが、Googleアカウントが必要です。とりあえず自分は作成済み。
ログインしたらブックマークバーにある「Googleアプリ」から「Google ドライブ」を選択し、変遷先で「新規」ボタンをクリックした後「その他」に合わせる。
GASがインストールされていればプルダウンリストの中に表示されているようです。
ない場合はメニューの「アプリを追加」からGoogle Apps Scriptを検索し、インストールを行う。

メニューに追加されていたらインストールに成功しています。クリックするとエディタが立ち上がるので、そちらにコードを書いていく。
もしくは、新規のスプレッドシートを作成して、メニューバーの「ツール」→「スクリプト エディタ」でも起動できるようです。これによりスプレッドシートに付随してGASファイルが管理されるようです。
スプレッドシートをデータベースのように使用するため、こちらの方法を採るという記事も見かけました。

今回はスプレッドシートからエディタを立ち上げています。
エディタが立ち上がったら左上の「プロジェクト名」を入力。 「公開」→「ウェブアプリケーションとして導入」をクリック。マニュアルはこちら ※ リンク先も英語です
(こちらはコードを書いた後でも問題ないですが、今回は先に行います)

モーダルが出現
f:id:saka20taku43:20200616001954p:plain

  • Project version: New(新規作成)
  • Execute the app as(次のユーザーとしてアプリケーションを実行): Me(自分)
    • URLを配布する前にスクリプトを承認する必要がある、と注意書きがあります
  • Who has access to the app(アプリにアクセスできるユーザー): Anyone, even anonymous(誰でも、匿名でも)
    • 2つ目の選択肢にある全ユーザーはドメインメンバー(Googleアカウントを使用)とあるのである程度限定された範囲のようです。

...なんか見辛くなっちゃった😅
これでWebAPIとしてGASプログラムを公開することができます。
f:id:saka20taku43:20200616002648p:plain
ここで発行されたURLをLINE DevelopersでWebhook URLに登録する。
URLを取得できたらLINE Developersで先ほど作ったチャンネル(bot)のMessaging APIタブの中にWebhookURLを設定できる項があるのでそちらに入力し、Webhookを使用するようにしておく。
LINEではアプリでBOTへメッセージが送られたタイミングなどで、その内容をこのURLに送信する。
また、同タブ内にてチャンネルアクセストークンの発行を行う。
bot本体の開発にはこのアクセストークンと基本設定タブのChannel Secret(チャンネルの秘密)が必要になるので控えておく。

さらに、LINE BOTには固定のメッセージ自動で返信(応答、友達追加時)する機能があるみたいなので、そちらをOFFにしておく。
f:id:saka20taku43:20200616011346p:plain

「編集する」をクリックすると別ウィンドウが開くのでそちらで該当項目をOFFにしていく。

f:id:saka20taku43:20200616011407p:plain

一通り設定が終わったらMessaging APIタブにあるQRコードで友達登録をしておく。公式アカウントの方に追加されている。

GASにアクセスするための関数の作成

これまでの設定では外部にGASを公開する設定してきましたが、これだけではGASにはアクセスできないようです。HTTP通信のmethod: GETに対して反応する関数が必要とのこと。

// 既存のコードに上書き
function doGet() {
  return ContentService.createTextOutput("success");
}

このdoGetというのはGAS側で設定されており、この名前で関数を作成した場合、自動的にGETに対してこの関数が実行されるようです。

動作を確認する為、入力したらメニューバーのファイルから保存するか、⌘ + Sで保存。
「公開」→「ウェブアプリケーションとして導入」でプロジェクトバージョンは前回同様「New」。
コードを更新するたびに前述のように設定しないとbotの動作内容は更新されないようです。
更新したら「表示されたURL」に「ブラウザのシークレットモードでアクセス」してみると、「success」の文字が表示されている。
(Chromeの場合は、デスクトップのメニューバーのファイルか、ブラウザのその他ボタンの新しいシークレットウィンドウで開くことができる。)

GETでのレスポンスの確認ができたら、LINE Developersの管理画面でWebhookURLへの接続確認を行ってみる。成功と出れば、LINE APIからのGASが確認できたことになる。


参考にさせていただいた記事
LINEのBot開発 超入門(前編) ゼロから応答ができるまで
映画と旅行とエンジニア
Google Apps ScriptでLINE BOTつくったら30分で動かせた件
Line BotをGoogle App Scriptで無料で手軽に試してみる。


今回はここまで。

GASで利用できるメソッドなどは別途記事にしたいと思います。
したらな❗️ 👋