LANG SELRCT

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

2019年3月31日日曜日

LiveChat APIでタグのGET, POST, DELETEを試した備忘録




コード.gs
function getTags() {
  var method = 'get';
  var payload = null;
  var url = 'https://api.livechatinc.com/tags';
  var response = getResponse(method, url, payload);
  Logger.log(response)
}

function postTag() {
  var method = 'post';
  var tag = '追加タグ名';
  var payload = {
    'tag': tag,
    'author': 'NAME@gmail.com',
    'group': 0,
  }
  payload = JSON.stringify(payload);
  var url = 'https://api.livechatinc.com/tags';
  var response = getResponse(method, url, payload);
  Logger.log(response)
}

function deleteTag() {
  var method = 'delete';
  var tag = '追加タグ名';
  var payload = {
    'group': 0
  }
  payload = JSON.stringify(payload);
  var url = 'https://api.livechatinc.com/tags' + '/' + tag;
  var response = getResponse(method, url, payload);
  Logger.log(response);
}

function getResponse(method, url, payload) {
  var options = {
    'method': method,
    'headers': {
      'X-API-Version': 2,
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + getProp('token')
    },
    'payload': payload
  };
  var response = UrlFetchApp.fetch(url, options);
  return response;
}

function getProp(key) {
  return PropertiesService.getScriptProperties().getProperty(key);
}



関連記事
LiveChatのアクセストークンを取得したい
LiveChat APIでタグの一覧を取得したい
LiveChat APIでタグを追加したい
LiveChat APIでタグを削除したい


参考

Tags
https://developers.livechatinc.com/docs/rest-api/#tags

LiveChat APIでタグを削除したい


LiveChat APIで Delete a tag を試したときの備忘録



コード.gs
function deleteTag() {
  var tag = '追加タグ名';
  var payload = {
    "group": 0
  }
  var options = {
    "method": "delete",
    "headers": {
      "X-API-Version": 2,
      "Content-Type": "application/json",
      "Authorization": "Bearer " + getProp("token")
    },
    "payload": JSON.stringify(payload)
  };
  var url = "https://api.livechatinc.com/tags/" + tag;
  var response = UrlFetchApp.fetch(url, options);
  Logger.log(response)
}

function getProp(key) {
  return PropertiesService.getScriptProperties().getProperty(key);
}

getProp('token')はアクセストークンです
LiveChatのアクセストークンを取得したい


参考

Delete a tag
https://developers.livechatinc.com/docs/rest-api/#delete-a-tag

LiveChat APIでタグを追加したい


API で Tag の追加を試したときの備忘録



コード.gs
function postTag() {
  var payload = {
    "tag": "追加タグ名",
    "author": "NAME@gmail.com",
    "group": 0,
  }
  var options = {
    "method": "post",
    "headers": {
      "X-API-Version": 2,
      "Content-Type": "application/json",
      "Authorization": "Bearer " + getProp("token")
    },
    "payload": JSON.stringify(payload)
  };
  var url = "https://api.livechatinc.com/tags";
  var response = UrlFetchApp.fetch(url, options);
  Logger.log(response)
}


LiveChat APIでタグの一覧を取得したい


LiveChat APIでList all tagsを試したときの備忘録



コード.gs
function getTags() {
  var options = {
    "method" : "get",
    "headers" : {
      "X-API-Version": 2,
      "Content-Type" : "application/json",
      "Authorization" : "Bearer " + getProp('token')
    }
  };
  var url = "https://api.livechatinc.com/tags";
  var response = UrlFetchApp.fetch(url, options);
  Logger.log(response)
}

function getProp(key) {
  return PropertiesService.getScriptProperties().getProperty(key);
}


getProp('token')はアクセストークンです
LiveChatのアクセストークンを取得したい


参考

List all tags
https://developers.livechatinc.com/docs/rest-api/#list-all-tags


LiveChatのアクセストークンを取得したい


LiveChatのアクセストークンを取得した時の備忘録


LiveChat APIの公式リファレンス
https://developers.livechatinc.com/docs/sign-in-with-livechat/


上記のリンク先の手順で取得できるのが正しいやり方だと思うので、一度試してみてください。


ググってみるとWeb上にほとんど情報がなかったため、試行錯誤して取得した方法を書き残しておきます。


(※以下、公式の手順ではないため、同じ方法でやる場合は自己責任で)


ここに直接アクセスする
https://accounts.livechatinc.com/client_id=CLIENT_ID

(CLIENT_IDの取得方法は下の方に手順を書きました)



「Sign in」をクリックする


アドレスバーにaccess_tokenが表示される



CLIENT_IDを取得する手順


コンソールを開く
https://developers.livechatinc.com/console/


「Go to Apps」


「New app +」


「Continue」

「Client Id」をコピー



Google APIのスコープ一覧を知りたい


Google Apps Scriptのスコープを設定したくて調べた備忘録


一覧はここに書かれていた

OAuth 2.0 Scopes for Google APIs
https://developers.google.com/identity/protocols/googlescopes



Google Apps Scriptのスコープを設定してみる


「スコープ」は書かれたコードから自動判定されるようですが、広すぎるようです


「スコープ」って何?

  • スクリプトの実行に必要なアクセス権
  • スクリプトを実行するユーザのデータに対して、どんな操作を行うか
  • 例えば「データを読むだけ」とか「アプリで作ったデータの読み書きだけ」とか



どこに書かれているか
「ファイル > スクリプトのプロパティ > スコープ」 の中で確認できる


どこで設定できるか
「表示 > マニフェスト ファイルを表示」でappsscript.jsonが開くので

  "oauthScopes": [
    "https://www.googleapis.com/auth/drive.file"
  ],

このように追加してやる




manifest.json
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
  },
  "oauthScopes": [
    "https://www.googleapis.com/auth/drive.file"
  ],
  "exceptionLogging": "STACKDRIVER"
}


参考

Authorization Scopes (manifest.jsonに書くScope)
https://developers.google.com/apps-script/concepts/scopes


OAuth 2.0 Scopes for Google APIs (Scopeの一覧)
https://developers.google.com/identity/protocols/googlescopes


Authorize Requests (スプレッドシートのスコープ)
https://developers.google.com/sheets/api/guides/authorizing

Sourcetreeのアンインストール→インストール→Bitbucket連携


Sourcetreeからbitbucketへのpushができなくなり
一度sourcetreeをアンインストールしてみる手順を書き残しました

アンインストールは公式サイトのこれをやってみます
How to Wipe SourceTree Preferences
https://confluence.atlassian.com/sourcetreekb/how-to-wipe-sourcetree-preferences-412484640.html
MacThe following files need to be deleted after they've been backed up.
  1. Make sure SourceTree is closed.
  2. Make sure the files are backed up in the following folders before removing the files
  3. Remove everything in ~/Library/Application Support/SourceTree/
  4. If you're using an OS X version before Mavericks: Remove ~/Library/Preferences/com.torusknot.SourceTreeNotMAS.plist (you should be using the direct version of SourceTree, hence "NotMAS")
  5. If you're using Mavericks or later, run the following in the terminal defaults delete com.torusknot.SourceTreeNotMAS
  6. Open SourceTree again to test if it worked or not.
If this didn't fix your problems then close SourceTree and restore the files, then re-open SourceTree again (if at all possible).


アンインストール手順

  1. Sourcetreeを閉じる
  2. ファイルのバックアップをとっておく
  3. ~/Library/Application Support/SourceTree/の中身を全部削除する
  4. OSはhighsierraなので対象外
  5. defaults delete com.torusknot.SourceTreeNotMAS
  6. Sourcetreeを再度開けるか試す

インストール手順


Sourcetreeを開くとインストール画面が起動します
ライセンスに同意して「続ける」をクリックします


「My Atlassianを開く」をクリックします


「Continue with Google」をクリックします


自分のアカウントを選択します


ログインして管理画面が開きます


Sourcetreeに戻って「既存のアカウントを使用」をクリックします


「Log in with Google」をクリックします


自分のGmailアドレスを入力して
次にパスワードを入力します


登録が完了しました!と出るので「続ける」をクリックします


接続したいサービスを選択します
ここではBitbucketクラウドを選択します
「接続アカウント」を選択します


「Log in with Google」をクリックします


Gmailアドレスを入力して
パスワードを入力します


このような画面が出る場合はバージョンをアップデートしてみます
sourcetreeのOAuth認証で、bitbucketへログインができない


セットアップをスキップしてアップデートを確認します


「アップデートをインストール」をクリックします


ダウンロードが終わるまで待ちます


「インストールして再起動」をクリックします


再起動後にバージョンを確認してみます


この時はもう一つアップデートをダウンロードして3.0.1にしました


Bitbucketと接続する


環境設定を開きます


アカウントで「追加」をクリックします


「接続アカウント」をクリックします


こういうポップアップが表示されたら
「Sourcetreeを開く」をクリックします


このような入力を求められて
パスワードが分からず結局「拒否」しました


その後に再度「アカウント」で「追加」を開くと
SSHキーをクリップボードにコピーできるようになっていました
「クリップボードにコピー」を選択します
「保存」を選択します


Bitbucketにアクセスしてダッシュボードを開きます
https://bitbucket.org/dashboard/overview
左下の自分のアイコンをクリックして
「Bitbucket settings」を選択します


設定画面が開くので
①「SSH 鍵」をクリックします
②「鍵を追加」をクリックします


Labelに任意の名前を入力して
KeyにSourcetreeのSSHキー(先ほどクリップボードにコピーしたもの)を貼り付けます
右下の「鍵を追加」をクリックします


鍵が追加されます


Sourcetreeに戻ってリモート > 新規 の中で「URLからクローン」を選択します

こういう入力画面が開きます
ソース URLにBitbucketの対象のURLを貼り付けます

そのためにBitbucketに戻って対象のURLを取得します
Bitbucketのダッシュボードを開いて
https://bitbucket.org/dashboard/overview
対象のリポジトリを選択します


「クローンの作成」をクリックします






参考

2019年3月19日火曜日

var result = array.push("value");はresultに要素数が入る


var array = [1,2,3];
という配列に

var result = array.push(4);
とした場合


Logger.log(result);// 要素数がログに出る

Logger.log(array);// 要素がログに出る



コード.gs
function myFunction() {
  var array = [1,2,3];
  var result = array.push(4);
  Logger.log(result);
  Logger.log(array);
}

意訳
この機能がやること
配列の初期値
配列の末尾に4を追加した要素数をresultに入れて
ログに出す
配列の要素をログに出す




実行結果


2019年3月17日日曜日

ストップウォッチを作る実験(start→stop→resume)


こういうストップウォッチを作ってみる







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




index.html
<!DOCTYPE html>
<html>
  <head>
    <style>
      #tb {
        color: white;
        background-color: dodgerblue;
        font-size: 24px;
        border: none;
        border-radius: 5px;
        padding: 5px;
      }
      
      #start_bt {
        width: 55px;
      }
    </style>
  </head>
  <body>
    <input type="text" id="tb" value="00 : 00 : 00 : 00">
    <br>
    <button id="start_bt">start</button>
    <button id="reset">reset</button>
    <input type="text" id="tb_none" value="0">
    
<script>
var startTime;
var intervalTime;
var pastTime = 0;

function elem(id) {
  return document.getElementById(id);
}

elem('start_bt').onclick = stopWatch;
elem('reset').onclick = resetWatch;

function stopWatch() {
  var value = elem('start_bt').textContent;
  switch(value) {
    case "start": 
      startTime = new Date();
      elem('start_bt').textContent = 'stop'; 
      intervalTime = setInterval(culcTime, 10); 
      break;
    case "stop": 
      elem('start_bt').textContent = 'resume';
      pastTime = new Date().getTime() - startTime.getTime();
      console.log(pastTime);
      clearInterval(intervalTime);
      break;
    case "resume":
      pastTime = parseInt(elem("tb_none").value);
      
      startTime = new Date();
      elem('start_bt').textContent = 'stop';
      addTime = parseInt(elem("tb_none").value);
      intervalTime = setInterval(culcTime, 10);
      break;
  } 
} 

function culcTime() { 
  var stopTime = new Date();
  var milsec = stopTime - startTime + pastTime;//差分のミリ秒
  elem("tb_none").value = milsec;
  var hour = Math.floor(milsec / 1000 / 60 / 60);//差分のミリ秒を/秒にして/分にして/時にして小数点以下切り捨てる
  
  var milsecM = milsec - (hour * 60 * 60 * 1000);//差分のミリ秒からhour分のミリ秒を引いた残りのミリ秒
  var min = Math.floor(milsecM / 1000 / 60);//それを/秒にして/分にして小数点以下を切り捨てる
  
  var milsecS = milsecM - (min * 60 * 1000);//差分のミリ秒からhour分のミリ秒を引いた残りのミリ秒からmin分のミリ秒を引いた残りのミリ秒
  var sec = Math.floor(milsecS / 1000);//それを/秒にして小数点以下を切り捨てる
  
  var milsecMS = milsecS % 1000;//差分のミリ秒からhour分のミリ秒を引いた残りのミリ秒からmin分のミリ秒を引いた残りのミリ秒を秒にした余りのミリ秒
  var milsec = milsecMS.toString().slice(0, 2);//それを2桁にする
  var time = add0([hour, min, sec, milsec]);
  elem('tb').value = time.join(' : ');
}

function add0(times) {
  for(var i = 0; i < times.length; i++) {
    if(times[i] < 10) {
      times[i] = "0" + times[i];
    }
  }
  return times;
}

function resetWatch() {
  elem('start_bt').textContent = "start";
  elem('tb').value = "00 : 00 : 00 : 00";
  clearInterval(intervalTime);
  pastTime = 0;
}
</script>
  </body>
</html>


2019年3月13日水曜日

開いているページのURLを取得したい(location.href)


location.hrefで取得できる。



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




index.html
<!DOCTYPE html>
<html>
<body>
<script>
  alert(location.href);
</script>
</body>
</html>

意訳
 



現在のURLをアラートに出す






2019年3月11日月曜日

speechSynthesis.getVoices()で配列が返ってこなくて困った。


speechSynthesis.getVoices()で配列が返ってくるらしいので、

  var text = "hello";
  var msg = new SpeechSynthesisUtterance();
  msg.text = text;
  msg.voice = "Alex";
  speechSynthesis.speak(msg);

というコードを書いてみましたがmsg.voice = "Alexがうまくいかずエラーが出る。



いろいろ試してみると、speechSynthesis.onvoiceschanged で解決したので、備忘録として短いコードを書き残しておこう。

(ブラウザによるのかも ちなみに手元のブラウザはChrome)


デモ

以下のボタンをクリックすると Alex の声で hello と言ってくれる。



これをGoogle Apps ScriptのWebアプリで実現するコードを以下に残しました。



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




index.html
<!DOCTYPE html>
<html>
<body>
<button id="bt">Say hello</button>
<script>
speechSynthesis.onvoiceschanged = getVoices;

document.getElementById('bt').onclick = speak;

var voices;

function getVoices() {
  voices = speechSynthesis.getVoices();
}

function speak() {
  var text = "hello";
  var msg = new SpeechSynthesisUtterance();
  msg.text = text;
  msg.voice = voices[1];//Alex
  speechSynthesis.speak(msg);
}

</script>
</body>
</html>


補足:失敗したコード

以下のようにindex.htmlを書くと、こんなエラーが出た
Uncaught TypeError: Failed to set the 'voice' property on 'SpeechSynthesisUtterance': The provided value is not of type 'SpeechSynthesisVoice'.at HTMLTextAreaElement.speak


index.html
<!DOCTYPE html>
<html>
<body>
  <button id="bt">Say hello</button>
<script>
document.getElementById('bt').onclick = speak;

function speak() {
  var text = "hello";
  var msg = new SpeechSynthesisUtterance();
  msg.text = text;
  msg.voice = "Alex";
  speechSynthesis.speak(msg);
}
</script>
</body>
</html>


speechSynthesis.onvoiceschanged = getVoices;
を実行しないとだめらしい。


2019年3月10日日曜日

使える音声を取得してみる(speechSynthesis.getVoices())


speechSynthesis.getVoices()で取得してみました。




getVoices()だけでは配列を取得できず、参考サイトにあるように
speechSynthesis.onvoiceschanged = getVoices;
とすると取得できました。



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




index.html
<!DOCTYPE html>
<html>
<body>
<select id="voiceSelect"></select>
<textarea id="ta"></textarea>
<script>
speechSynthesis.onvoiceschanged = getVoices;

function getVoices() {
  var voices = speechSynthesis.getVoices();
  var select = document.getElementById("voiceSelect");
  var ta = document.getElementById("ta");
  var values = "";
  for(var i = 0; i < voices.length ; i++) {
    var option = document.createElement('option');
    var lang = voices[i].lang;
    var value = voices[i].name + ' / ' + lang
    option.textContent = value;
    option.setAttribute('data-lang', voices[i].lang);
    option.setAttribute('data-name', voices[i].name);
    select.appendChild(option);
    values += value + "\n";
  }
  select.setAttribute('size', 12);
  ta.setAttribute('rows', 12);
  ta.value = values;
}
</script>
</body>
</html>




SpeechSynthesisVoiceを手元の環境で出力してみた結果
0: SpeechSynthesisVoice {voiceURI: "Kyoko", name: "Kyoko", lang: "ja-JP", localService: true, default: true}
1: SpeechSynthesisVoice {voiceURI: "Alex", name: "Alex", lang: "en-US", localService: true, default: false}
2: SpeechSynthesisVoice {voiceURI: "Alice", name: "Alice", lang: "it-IT", localService: true, default: false}
3: SpeechSynthesisVoice {voiceURI: "Alva", name: "Alva", lang: "sv-SE", localService: true, default: false}
4: SpeechSynthesisVoice {voiceURI: "Amelie", name: "Amelie", lang: "fr-CA", localService: true, default: false}
5: SpeechSynthesisVoice {voiceURI: "Anna", name: "Anna", lang: "de-DE", localService: true, default: false}
6: SpeechSynthesisVoice {voiceURI: "Carmit", name: "Carmit", lang: "he-IL", localService: true, default: false}
7: SpeechSynthesisVoice {voiceURI: "Damayanti", name: "Damayanti", lang: "id-ID", localService: true, default: false}
8: SpeechSynthesisVoice {voiceURI: "Daniel", name: "Daniel", lang: "en-GB", localService: true, default: false}
9: SpeechSynthesisVoice {voiceURI: "Diego", name: "Diego", lang: "es-AR", localService: true, default: false}
10: SpeechSynthesisVoice {voiceURI: "Ellen", name: "Ellen", lang: "nl-BE", localService: true, default: false}
11: SpeechSynthesisVoice {voiceURI: "Fiona", name: "Fiona", lang: "en", localService: true, default: false}
12: SpeechSynthesisVoice {voiceURI: "Fred", name: "Fred", lang: "en-US", localService: true, default: false}
13: SpeechSynthesisVoice {voiceURI: "Ioana", name: "Ioana", lang: "ro-RO", localService: true, default: false}
14: SpeechSynthesisVoice {voiceURI: "Joana", name: "Joana", lang: "pt-PT", localService: true, default: false}
15: SpeechSynthesisVoice {voiceURI: "Jorge", name: "Jorge", lang: "es-ES", localService: true, default: false}
16: SpeechSynthesisVoice {voiceURI: "Juan", name: "Juan", lang: "es-MX", localService: true, default: false}
17: SpeechSynthesisVoice {voiceURI: "Kanya", name: "Kanya", lang: "th-TH", localService: true, default: false}
18: SpeechSynthesisVoice {voiceURI: "Karen", name: "Karen", lang: "en-AU", localService: true, default: false}
19: SpeechSynthesisVoice {voiceURI: "Laura", name: "Laura", lang: "sk-SK", localService: true, default: false}
20: SpeechSynthesisVoice {voiceURI: "Lekha", name: "Lekha", lang: "hi-IN", localService: true, default: false}
21: SpeechSynthesisVoice {voiceURI: "Luca", name: "Luca", lang: "it-IT", localService: true, default: false}
22: SpeechSynthesisVoice {voiceURI: "Luciana", name: "Luciana", lang: "pt-BR", localService: true, default: false}
23: SpeechSynthesisVoice {voiceURI: "Maged", name: "Maged", lang: "ar-SA", localService: true, default: false}
24: SpeechSynthesisVoice {voiceURI: "Mariska", name: "Mariska", lang: "hu-HU", localService: true, default: false}
25: SpeechSynthesisVoice {voiceURI: "Mei-Jia", name: "Mei-Jia", lang: "zh-TW", localService: true, default: false}
26: SpeechSynthesisVoice {voiceURI: "Melina", name: "Melina", lang: "el-GR", localService: true, default: false}
27: SpeechSynthesisVoice {voiceURI: "Milena", name: "Milena", lang: "ru-RU", localService: true, default: false}
28: SpeechSynthesisVoice {voiceURI: "Moira", name: "Moira", lang: "en-IE", localService: true, default: false}
29: SpeechSynthesisVoice {voiceURI: "Monica", name: "Monica", lang: "es-ES", localService: true, default: false}
30: SpeechSynthesisVoice {voiceURI: "Nora", name: "Nora", lang: "nb-NO", localService: true, default: false}
31: SpeechSynthesisVoice {voiceURI: "Paulina", name: "Paulina", lang: "es-MX", localService: true, default: false}
32: SpeechSynthesisVoice {voiceURI: "Samantha", name: "Samantha", lang: "en-US", localService: true, default: false}
33: SpeechSynthesisVoice {voiceURI: "Sara", name: "Sara", lang: "da-DK", localService: true, default: false}
34: SpeechSynthesisVoice {voiceURI: "Satu", name: "Satu", lang: "fi-FI", localService: true, default: false}
35: SpeechSynthesisVoice {voiceURI: "Sin-ji", name: "Sin-ji", lang: "zh-HK", localService: true, default: false}
36: SpeechSynthesisVoice {voiceURI: "Tessa", name: "Tessa", lang: "en-ZA", localService: true, default: false}
37: SpeechSynthesisVoice {voiceURI: "Thomas", name: "Thomas", lang: "fr-FR", localService: true, default: false}
38: SpeechSynthesisVoice {voiceURI: "Ting-Ting", name: "Ting-Ting", lang: "zh-CN", localService: true, default: false}
39: SpeechSynthesisVoice {voiceURI: "Veena", name: "Veena", lang: "en-IN", localService: true, default: false}
40: SpeechSynthesisVoice {voiceURI: "Victoria", name: "Victoria", lang: "en-US", localService: true, default: false}
41: SpeechSynthesisVoice {voiceURI: "Xander", name: "Xander", lang: "nl-NL", localService: true, default: false}
42: SpeechSynthesisVoice {voiceURI: "Yelda", name: "Yelda", lang: "tr-TR", localService: true, default: false}
43: SpeechSynthesisVoice {voiceURI: "Yuna", name: "Yuna", lang: "ko-KR", localService: true, default: false}
44: SpeechSynthesisVoice {voiceURI: "Yuri", name: "Yuri", lang: "ru-RU", localService: true, default: false}
45: SpeechSynthesisVoice {voiceURI: "Zosia", name: "Zosia", lang: "pl-PL", localService: true, default: false}
46: SpeechSynthesisVoice {voiceURI: "Zuzana", name: "Zuzana", lang: "cs-CZ", localService: true, default: false}
47: SpeechSynthesisVoice {voiceURI: "Google Deutsch", name: "Google Deutsch", lang: "de-DE", localService: false, default: false}
48: SpeechSynthesisVoice {voiceURI: "Google US English", name: "Google US English", lang: "en-US", localService: false, default: false}
49: SpeechSynthesisVoice {voiceURI: "Google UK English Female", name: "Google UK English Female", lang: "en-GB", localService: false, default: false}
50: SpeechSynthesisVoice {voiceURI: "Google UK English Male", name: "Google UK English Male", lang: "en-GB", localService: false, default: false}
51: SpeechSynthesisVoice {voiceURI: "Google español", name: "Google español", lang: "es-ES", localService: false, default: false}
52: SpeechSynthesisVoice {voiceURI: "Google español de Estados Unidos", name: "Google español de Estados Unidos", lang: "es-US", localService: false, default: false}
53: SpeechSynthesisVoice {voiceURI: "Google français", name: "Google français", lang: "fr-FR", localService: false, default: false}
54: SpeechSynthesisVoice {voiceURI: "Google हिन्दी", name: "Google हिन्दी", lang: "hi-IN", localService: false, default: false}
55: SpeechSynthesisVoice {voiceURI: "Google Bahasa Indonesia", name: "Google Bahasa Indonesia", lang: "id-ID", localService: false, default: false}
56: SpeechSynthesisVoice {voiceURI: "Google italiano", name: "Google italiano", lang: "it-IT", localService: false, default: false}
57: SpeechSynthesisVoice {voiceURI: "Google 日本語", name: "Google 日本語", lang: "ja-JP", localService: false, default: false}
58: SpeechSynthesisVoice {voiceURI: "Google 한국의", name: "Google 한국의", lang: "ko-KR", localService: false, default: false}
59: SpeechSynthesisVoice {voiceURI: "Google Nederlands", name: "Google Nederlands", lang: "nl-NL", localService: false, default: false}
60: SpeechSynthesisVoice {voiceURI: "Google polski", name: "Google polski", lang: "pl-PL", localService: false, default: false}
61: SpeechSynthesisVoice {voiceURI: "Google português do Brasil", name: "Google português do Brasil", lang: "pt-BR", localService: false, default: false}
62: SpeechSynthesisVoice {voiceURI: "Google русский", name: "Google русский", lang: "ru-RU", localService: false, default: false}
63: SpeechSynthesisVoice {voiceURI: "Google 普通话(中国大陆)", name: "Google 普通话(中国大陆)", lang: "zh-CN", localService: false, default: false}
64: SpeechSynthesisVoice {voiceURI: "Google 粤語(香港)", name: "Google 粤語(香港)", lang: "zh-HK", localService: false, default: false}
65: SpeechSynthesisVoice {voiceURI: "Google 國語(臺灣)", name: "Google 國語(臺灣)", lang: "zh-TW", localService: false, default: false}



テキストエリアの表示行数を指定したい(rows=行数)


3行まで表示してみる



textareaに直接書く場合
<textarea rows="3">

JavaScriptでやる場合
ta.setAttribute('rows', 3);



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



JavaScriptでやる例でやってみる

index.html
<!DOCTYPE html>
<html>
<body>
  <textarea id="ta">
blue
red
white
yelow
green
black
</textarea>
<script>
  var ta = document.getElementById("ta");
  ta.setAttribute('rows', 3);
</script>
</body>
</html>


行の値が編集された時、特定のヘッダの列に日時を入力したい


特定のヘッダの列に日時を入力したくて書いたコード

この記事でやること
  • ヘッダの値を見て、dateならその列に日時を入力する



日時を入力する条件
  • dataの列が編集された時は入力しない
  • 1行目が編集された時は入力しない
if(col !== targetCol && row > 1)



コード.gs
function onEdit() {
  var header = "date";
  
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var range = sheet.getActiveRange();
  var row = range.getRow();
  var col = range.getColumn();
  var date = new Date();
  set_value(sheet, row, col, date, header)
}


function set_value(sheet, row, col, value, header) {
  var headers = get_headers(sheet)[0];
  var targetCol = headers.indexOf(header) + 1;
  if(col !== targetCol && row > 1){
    sheet.getRange(row, targetCol).setValue(value);
  }
}

function get_headers(sheet) {
  var last_col = sheet.getLastColumn();
  var range = sheet.getRange(1, 1, 1, last_col);
  var headers = range.getValues();
  return headers;
}



関連記事

2019年3月9日土曜日

LiveChatのwebhookを使ってみる


LiveChatのwebhookについて調べたときの備忘録

LiveChat
https://www.livechatinc.com/jp/


この記事では、WebhookのTargetURLをGoogle Apps Script で作ったWebアプリのURLで試します。



  1. Webhookでデータを受け取るWebAppを作ります
  2. LiveChat側でWebhookの設定をします


1. Webhookでデータを受け取るWebAppを作ります

Google Apps Scriptのエディタに以下のコード.gsを書いて保存します。



コード.gs
function doPost(e) {
  var contents = e.postData.contents
  console.log(contents);
}
意訳
この機能がやること
受け取ったデータのcontentsを取得して
ログに出す



公開 > ウェブアプリケーションとして導入...を選択します

  1. バージョンの説明を入力して
  2. アプリケーションの実行ユーザは「自分」
  3. アクセスはできるのは「全員(匿名ユーザーを含む)」
で「導入」をクリックします。
(LiveChat側からこのアプリにアクセスできて実行できるように2, 3の設定は必須)

現在のウェブアプリケーションのURLのhttps://〜をコピーします


2. Webhook側の設定をします

Settings > Integrations > Webhooks を開きます

いつ=どのEventが起きた時に
どのデータ=Data type
どこに=Target URL(STEP1でコピーしたURL)
を設定して「Add a webhook」をクリックします

すると下部に以下のようなエリアが追加されます

これで設定したEventが起きた時にTarget URLにデータが飛んできます


Eventの chat starts と chat ends を試したときに取得したデータのメモ


chat startsのwebhook
{
  "event_type": "chat_started",
  "event_unique_id": "7ddaabb",
  "token": "31aae354",
  "license_id": "1067",
  "lc_version": "2",
  "chat": {
    "id": "PO13",
    "started_timestamp": 1552126412,
    "url": "URL",
    "referer": "",
    "messages": [{
      "user_type": "agent",
      "author_name": "NAME",
      "agent_id": "name@gmail.com",
      "text": "Hello. How may I help you?",
      "json": "",
      "timestamp": 1552126412
    }],
    "attachments": [],
    "events": [],
    "agents": [{
      "name": "AGENT NAME",
      "login": "name@gmail.com"
    }],
    "tags": [],
    "groups": [0]
  },
  "visitor": {
    "id": "S1551",
    "name": "NAME",
    "email": "name@gmail.com",
    "country": "Japan",
    "city": "",
    "language": "en",
    "page_current": "URL",
    "timezone": ""
  },
  "pre_chat_survey": [{
    "id": "2001",
    "type": "name",
    "label": "Name:",
    "answer": "NAME"
  }, {
    "id": "2002",
    "type": "email",
    "label": "E-mail:",
    "answer": "name@gmail.com"
  }]
}




















chat endsのwebhook
{
  "event_type": "chat_ended",
  "event_unique_id": "349a820",
  "token": "f791db42",
  "license_id": "1067",
  "lc_version": "2",
  "chat": {
    "id": "PO13",
    "started_timestamp": 1552126412,
    "ended_timestamp": 1552126503,
    "url": "URL",
    "referer": "",
    "messages": [{
      "user_type": "agent",
      "author_name": "NAME",
      "agent_id": "name@gmail.com",
      "text": "Hello. How may I help you?",
      "json": "",
      "timestamp": 1552126412
    }, {
      "user_type": "visitor",
      "author_name": "NAME",
      "text": "hello! how's it going?",
      "json": "",
      "timestamp": 1552126451
    }, {
      "user_type": "agent",
      "author_name": "AGENT NAME",
      "agent_id": "name@gmail.com",
      "text": "Hi, good.",
      "json": "{}",
      "timestamp": 1552126490
    }],
    "attachments": [],
    "events": [{
      "user_type": "visitor",
      "text": "NAME closed the chat.",
      "timestamp": 1552126503,
      "type": "closed"
    }],
    "agents": [{
      "name": "NAME",
      "login": "name@gmail.com"
    }],
    "tags": [],
    "groups": [0]
  },
  "visitor": {
    "id": "S1551",
    "name": "NAME",
    "email": "name@gmail.com",
    "country": "Japan",
    "city": "",
    "language": "en",
    "page_current": "URL",
    "timezone": ""
  },
  "pre_chat_survey": [{
    "id": "2001",
    "type": "name",
    "label": "Name:",
    "answer": "NAME"
  }, {
    "id": "2002",
    "type": "email",
    "label": "E-mail:",
    "answer":"name@gmail.com"
  }]
}