LANG SELRCT

コードを書く場所についてはこちら

2019年5月25日土曜日

フォーカス時の青い枠を消したい


この青枠を消すには

スタイルに outline: none を設定する

たとえばこんなスタイルを用意する
  .outline_none {
      outline: none;
  }


デモ

通常のテキストエリア



フォーカス時の青枠を消す




コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
意訳
この機能がやること
指定したHTMLファイルを表示する




index.html
<!DOCTYPE html>
<html>

<head>
  <style>
  .outline_none {
    outline: none;
  }
  </style>
</head>

<body>
  <textarea class='outline_none'></textarea>
</body>
</html>



2019年5月24日金曜日

LiveChatのイベントログをシートに書き出したい


LiveChatでチャット終了後のタグ更新をwebhookでキャッチしたい
の改良版です。


イベントのログを残したい

Logger.logでは複数のログの記録を残せなくて
console.logでのStackdriver LoggingでGCPのプロジェクトと関連付ける程でもなくて

軽くログを見たいだけなのでSpreadsheetに書き出すようにしてみます
一行目は見出しを入れておきます



LiveChat側でWebhookの設定をして
以下のコード.gsの シートのURL を各自で使うものに変更すると
チャット開始、タグの更新、チャット終了のタイミングでシートにログが書き込まれます



コード.gs
function doPost(e) {
  var contents = e.postData.contents
  var jobj = JSON.parse(contents);
  var eventType = jobj['event_type'];
  var isEnded = judgeEnded(jobj);
  
  var message;
  switch(eventType) {
    case 'chat_started':
      message = 'not ended';
      break;
    case 'chat_changed':
      if(isEnded) {
        message = 'ended & changed';
      } else {
        message = 'not ended';
      }
      break;
    case 'chat_ended':
        message = 'ended';
      break;
  }
  
  var datetime = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd/HH:mm:ss:SSS');

  var values = [eventType, message, contents, datetime];
  set_values(values);
}

function judgeEnded(jobj) {
  var events = jobj['chat']['events'];
  if(events === undefined || events[0] === undefined) {
    return false;
  }
  return true;
}

function set_values(array){
  var ss = SpreadsheetApp.openByUrl("シートURL");
  var sh = ss.getSheets()[0];
  var last_row = sh.getLastRow();
  var start_row = last_row + 1;
  var start_col = 1;
  var num_rows = 1;
  var num_cols = array.length;
  var range = sh.getRange(start_row, start_col, 1, num_cols);
  range.setValues([array]); 
}

意訳
この機能がやること
飛んできたデータからcontentsを取得して
オブジェクトに変換して
event_typeを取得して
chatが終わっているか判定して

messageの入れ物を用意して
eventTypeによって分岐させる
chat_startedなら
 messageにnot endedを入れる
 switchの判定から抜ける
chat_changedで
 chatが終わっているなら
  messageにended & changedを入れる
 終わっていないなら
  messageにnot endedを入れる

 switchの判定から抜ける
chat_endedなら
 messageにendedを入れる
 switchの判定から抜ける


現在日時を取得してフォーマットして

配列にeventType, message, contents, datetimeを入れて
set_valuesに渡す


この機能がやること
chatのeventsを取得して
もしeventsが存在しないかeventsの0番目が存在しなければ
falseを返す

存在していればtrueを返す


この機能がやること
スプレッドシートを取得して
0番目のシートを取得して
値が入っている最終行を取得して
その行数に1足して
1列目から
1行だけ
配列の要素の列数
の範囲を取得して
その行に値を入れる シートは2次元配列なので[[1列目の値,2列目の値,3列目の値]]という形にする




関連記事

LiveChatでチャット終了後のタグ更新をwebhookでキャッチしたい


参考

Building webhook integration
https://developers.livechatinc.com/docs/build-integration/

2019年5月23日木曜日

GASのライブラリ内で参照されたくない関数は末尾に_(アンダーバー)をつける


Best practices
https://developers.google.com/apps-script/guides/libraries#best_practices
の2番目に記載されています

ライブラリ内だけで使うけど、外からは参照されたくない関数の末尾に_をつける


以下のコードをライブラリ化すると、get_token()を呼び出せる

コード.gs
function get_token() {
  return PropertiesService.getScriptProperties().getProperty('token')
}
意訳
この機能がやること
スクリプトのプロパティのtokenの値を返す



関数の末尾にアンダーバーをつけると、get_token()を呼び出せなくなる

コード.gs
function get_token_() {
  return PropertiesService.getScriptProperties().getProperty('token')
}
意訳
この機能がやること
スクリプトのプロパティのtokenの値を返す



参考


Google Formの回答を{ 質問1 : 回答1 , 質問2 : 回答2 }の形で取得したい


このようなフォームを作って


USERNAME@gmail.com
{"Country":"オーストラリア","City":"シドニー"}

のように回答を取得したくて書いたコードです。



コード.gs
function getFormResponse(event) {
  var email = event.response.getRespondentEmail();
  var responseObj = getItemResponseObj(event);
  var json = JSON.stringify(responseObj);
  Logger.log(email);
  Logger.log(json);
}

function getItemResponseObj(event) {
  var itemResponses = event.response.getItemResponses();
  var obj = {};
  for (var i = 0; i < itemResponses.length; i++) {
    var itemResponse = itemResponses[i];
    var question = itemResponse.getItem().getTitle();
    var answer = itemResponse.getResponse();
    obj[question] = answer;
  }
  return obj;
}

意訳
この機能がやること
eventからユーザのEmailを取得する
getItemResponseObjにeventを渡して質問:回答を取得する
取得した質問:回答をJSON形式にする
Emailをログに出す
jsonをログに出す


この機能がやること
eventから質問と回答を取得する
空のオブジェクトを作る
質問:回答の数だけ繰り返す
質問:回答を一つずつ取得して
質問のタイトルを取得して
その回答を取得して
objに質問 : 回答 の形で追加していく

出来上がったobjを返す




実行結果


2019年5月21日火曜日

macOS MojaveにしたらChromeで音声入力できなくなった(解決済)


解決したいこと
音声で検索をクリックして
話しかけてみると
反応せずこうなった


ググってみるとAppleの公式サポートに記載がありました。
macOS Mojave のインストール後にオーディオを録音できなくなった場合

解決する手順
  1. システム環境設定
  2. セキュリティとプライバシー
  3. マイクを選択
  4. Chromeにチェック
  5. 今すぐ終了
  6. アプリを再起動


手順通りやったら解決しました

システム環境設定を開きます


セキュリティとプライバシーを選択します


左のリストからマイクを選択して
右に出てきたChromeのチェックボックスをクリックします


今すぐ終了をクリックします


再度Chromeを開くと音声入力できるようになりました。


参考

macOS Mojave のインストール後にオーディオを録音できなくなった場合
https://support.apple.com/ja-jp/HT209175

2019年5月20日月曜日

Google Apps Scriptのプロジェクトを削除したい


スプレッドシートに紐付いているプロジェクトが複数ある時
オーナー権限を持っていれば
ファイルメニューの「プロジェクトを削除」から削除できます



たとえば

スプレッドシートでスクリプトエディタを選択して


複数のプロジェクトがある場合はこのような選択画面が表示されます

無題のプロジェクトを開くと


無題のプロジェクト2を開くと

どちらも「プロジェクトを削除...」を選択できます


補足

自分がオーナーではない場合は「プロジェクトを削除...」の選択肢が表示されません

この場合はオーナーに削除してもらいましょう


スクリプトに登録されているトリガーを取得したい


自分がトリガーに登録している関数を取得するコードは



コード.gs
function getTriggesr() {
  var allTriggers = ScriptApp.getProjectTriggers();
  var triggers = [];
  for (var i = 0; i < allTriggers.length; i++) {
    var triggerName = allTriggers[i].getHandlerFunction();
    triggers.push(triggerName)
    }
  Logger.log(triggers);
}


補足

ScriptApp.getProjectTriggers()で
このスクリプトのプロジェクト内のトリガーをすべて取得できそうな気がしましたが
自分が登録している以外のトリガーは取得できませんでした。

チームで運用する場合の課題として
自分以外の誰かがトリガーを設定しているときに
誰が設定しているのか確認する方法が知りたいけれどその方法は不明。

誰かまではわからないまでも
どんなトリガーが登録されているかを知る方法は見つました。


その方法

1. 対象のプロジェクトでトリガーを開きます


2. オーナー:自分でフィルタがかかっているのでそのフィルタを外します
3. 自分以外のユーザが登録しているトリガーが表示されます

「他のユーザー」が「どのユーザー」なのか特定する方法がわかったら追記します
(ここで表示されていない時点で、Adminじゃないとできない気もする)


参考

getProjectTriggers()
https://developers.google.com/apps-script/reference/script/script-app#getProjectTriggers()

flexboxとposition:fixedを使ったときにぶつかった壁


Flexboxとposition: fixedを組み合わせたときにぶつかった壁

margin-leftで左端からの位置を指定してやることで解決しました


ぶつかった壁

いちご、めろん、りんごのテキストが入った3つのdivをFlexboxで横に並べて


こうしたかったけれど


全部左に寄ってこうなってしまった


※fixedを使わず素直にFlexboxだけでやればこの壁にはぶつからないので、この記事は役に立ちません



コード.gs
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}


壁にぶつかったindex.html

index.html
<!DOCTYPE html>
<html>

<head>
  <style>
    .flex_row{
      display: flex;
      flex-direction: row;
    }
    
    .fixed {
     position: fixed;
    }

    #div1 {
      width: 10vw;
      border: solid 1px red;
    }
    
    #div2 {
      width: 15vw;
      border: solid 1px green;
    }

    #div3 {
      width: 20vw;
      border: solid 1px blue;
    }
  </style>
</head>

<body>
<div id="main_div" class="flex_row">

  <div id="div1" class="fixed">
    いちご
  </div>
  
  <div id="div2">
    めろん
  </div>
  
  <div id="div3" class="fixed">
    りんご
  </div>

</div>

</body>
</html>




以下のようにmargin-leftを追加して解決しました


解決したindex.html

index.html
<!DOCTYPE html>
<html>

<head>
  <style>
    .flex_row{
      display: flex;
      flex-direction: row;
    }
    
    .fixed {
     position: fixed;
    }

    #div1 {
      width: 10vw;
      border: solid 1px red;
    }
    
    #div2 {
      width: 15vw;
      border: solid 1px green;
      margin-left: 10vw;
    }

    #div3 {
      width: 20vw;
      border: solid 1px blue;
      margin-left: 25vw;
    }
  </style>
</head>

<body>
<div id="main_div" class="flex_row">

  <div id="div1" class="fixed">
    いちご
  </div>
  
  <div id="div2">
    めろん
  </div>
  
  <div id="div3" class="fixed">
    りんご
  </div>

</div>

</body>
</html>


POINT
 





















ここにdiv1のwidth分のmargineを入れる





ここにdiv1とdiv2のwidth分のmarginを入れる



























補足

margine-leftのイメージ


対象のdiv内でスクロールするにはoverflow: scrollも考えられますが
今回は対象のdiv以外を固定したくてfixedとmargin-leftを使いました


参考

position

overflow

flex

2019年5月19日日曜日

codelabsのactions-1を手順通りにやってみる


Build Actions for the Google Assistant (Level 1)
https://codelabs.developers.google.com/codelabs/actions-1/index.html?hl=ja#0

上記のアクションを手元でキャプチャを残しながらやっていきます。

ここではcodelabの通り言語は英語の設定でやっていきます。
日本語の場合は現在対応していない機能があるためです。
まず一通り英語でやってから日本語の設定でも別の記事でやってみようと思います。

コンテンツ
  1. 事前確認
  2. PROJECTを作成してACTIONを追加します
  3. AGENTを作成して起動時の質問を設定します
  4. INTENTを作成します
  5. WEBHOOKを作成します


このアクションがやること

要約
ユーザから好きな色を聞いて色に応じた数字を返す

必要なステップ
  1. ユーザに好きな色を聞く
  2. 入力された色に応じて数字を返す
  3. 色が入力されない場合は何度も聞く
  4. ユーザはいつでも会話をやめられる


事前確認

アクティビティ管理ページで以下が有効になっていることを確認します
https://myaccount.google.com/activitycontrols
  1. ウェブとアプリのアクティビティ
  2. デバイス情報
  3. 音声アクティビティ


PROJECTを作成してACTIONを追加します

Actions on Googleコンソールを開きます
https://console.actions.google.com/


Terms of ServiceでYesをチェックしてAGREE AND CONTINUEをクリックします


Project Nameを決めてCREATE PROJECTをクリックします


Conversationalをクリックします


BuildのActionsでADD YOUR FIRST ACTIONをクリックします


BUILDをクリックします



AGENTを作成してWelcome Intentを設定します

Dialogflowが起動した後AGENT作成画面が開きます
DEFAULT LANGUAGEとTIME ZONEを設定してCREATEをクリックします


Default Welcome Intentをクリックして開きます


Responsesの中の「Hi! How are you doing?」の右端にマウスを置くと
ゴミ箱アイコンが表示されるのでクリックして削除します
その他のtextも同じようにすべて削除します


「Welcome! What is your favorite color?」を入力してSAVEします


テスト環境で確認してみます
IntegrationsからINTEGRATION SETTINGSをクリックします



Auto-preview changesを有効にしてTESTをクリックします



Simulatorが起動するので、「Talk to my test app」と入力して確定します



このように、設定した「何色が好きですか?」と聞いてくれます


REQUESTやRESPONSEのJSONもここで見ることができます


TEST画面を閉じてCLOSEをクリックしてINTEGRATION SETTINGSも閉じておきます


INTENTSを作成します

DialogflowのIntentsを開いてCREATE INTENTをクリックします


Intent nameに「favorite color」と入力してSAVE後
Training phrasesのADD TRAINING PHRASESをクリックします


Training phrasesは「favorite color」をユーザが答える時のパターンで
考えられるいくつかのパターンを登録していくと
「色」の名前の単語が自動でハイライトされます(日本語だとハイライトされない)

Action and parametersのMANAGE PARAMETERS AND ACTIONをクリックします

PARAMETER NAME:color
ENTITY:@sys.color
ENTITYは自然言語入力からパラメータの値を抽出するために使用されるもので

  • 今回の場合はフレーズ内の「色」を抽出するためのcolorが自動検出されている
  • ENTITYはPHRASEに含まれる要素でcolorやdateやnumberなどのカテゴリがある
  • colorやdateなどはDialogflowにデフォルトで備わっている
  • 備わっていないものも新規で定義できる(現在日本語は対応していない)
  • 今回の場合は「color」というカテゴリーの中に「yellow」や「Pink」などが予め登録されていて「i love yellow」の中では「yellow」が「color」だと識別される

と理解しています


色が入力されたら数字を返す処理を実現するためにはWebhookを利用します
FulfillmentのENABLE FULFILLMENTをクリックします


 Enable webhook call for this intentを有効にしてSAVEします


WEBHOOKを実装します

左のメニューからFulfillmentを開いてInline EditorをENABLEDにします

index,jsを以下のコードで上書きします
(意訳はほぼ元のコードにあったコメントをGoogle翻訳したものです)


index.js
'use strict';

const {dialogflow} = require('actions-on-google');
const functions = require('firebase-functions');
const app = dialogflow({debug: true});

app.intent('favorite color', (conv, {color}) => {
    const luckyNumber = color.length;
    conv.close('Your lucky number is ' + luckyNumber);
});

exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
意訳
strictモード

GoogleクライアントライブラリのアクションからDialogflowモジュールをインポートします
デプロイ用にfirebase-functionsパッケージをインポートします
Dialogflowクライアントをインスタンス化します

「favorite color」という名前のインテントを処理して 'color'という名前のパラメータを収集します
colorのパラメータの文字数を取得してラッキーナンバーに入れます
ラッキーナンバーを返して会話を終了します


HTTPS POSTリクエストを処理するようにDialogflowAppオブジェクトを設定します



DEPLOYをクリックします

Simulatorを起動して確認します
https://console.actions.google.com/を開いてプロジェクトを選択して


左のメニューからSimulatorをクリックして
Inputに「Talk to my test app」を入力して確定します


「What is your favorite color?」と聞かれ
「red」と答えると
「Your lucky number is 3」とindex.jsの処理結果を返してくれました。


参考

Build Actions for the Google Assistant (Level 1)
https://codelabs.developers.google.com/codelabs/actions-1/index.html?hl=ja#0

Dialogflow Overview
https://dialogflow.com/

Dialogflow Docs
https://dialogflow.com/docs

Youtube
Intents
https://www.youtube.com/watch?v=9aHusGxntPw

Entities
https://www.youtube.com/watch?v=kzdL6GxJ_WY

Dialog control
https://www.youtube.com/watch?v=-tOamKtmxdY

Actions on Google Documentation
https://developers.google.com/actions/overview