JS.36 GASでLINE BOTを作成し、各種IDを取得するで、LINE BOTを作成し、userIDを取得したり、そのuserID宛にメッセージを送る方法を理解したので、ここではそれらを自動化し、GASを用いて定期実行等を実現する。
変数定義
- スプレッドシート
WEBHOOK_URL
等の外部から隠すべき変数を保持するPropertiesService
は、global変数として定義する。
const prop = PropertiesService.getScriptProperties();
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('SHEET NAME');
send_all_table_messages
${getColName(col)}${row} : ${getColName(col+2)}
のテーブルデータを用いて各ユーザーにメッセージを送信し、各行の右端列(getColName(col+3)
)に結果を記録する。
/** Send all messages in table[`${getColName(col)}${row}:${getColName(col+3)}`] in "自動送信" tab.
* @param {Number} col Column index (1-based).
* @param {Number} row Row index (1-based)
*/
function send_all_table_messages(col, row=4) {
var table = sheet.getRange(`${getColName(col)}${row}:${getColName(col+2)}`).getValues();
var rowIdx = 0
while (true){
user = new User(rowData=table[rowIdx], result_cell=sheetn.getRange(`${getColName(col+3)}${4+rowIdx}`))
if (user.name=="") break
user.send_message()
rowIdx++;
}
}
User
上記の関数でやりたいことが実行できるよう以下の User
クラスを作成し、「メッセージの送信」や「結果の報告等」を楽に記述できるようにする。
なお、以下のような表形式でデータが格納されていることを想定している。
Name | userId | message | Result |
---|---|---|---|
iwasakishuto | Uxxx |
Hello world | OK |
tanakataro | Uxxx |
Hello world!! | OK |
// A class that handles sending messages and reporting results (to Slack and Spread Sheet)
class User {
/** Construct the user class.
* @param {list} rowData A row data that holds ``["name","userId","message"]`` in this order. Corresponds to one row in the spreadsheet.
* @param {Range} result_cell Location of the sheet that describes the result. ( ``Spreadsheet.getRange("")`` )
*/
constructor(rowData, result_cell){
this.name = rowData[0];
this.userId = rowData[1];
this.message = rowData[2];
this.result_cell = result_cell;
}
/** Set result to ``this.result_cell``
* @param {string} message Contents to be described in ``this.reault_cell``. (Function result)
*/
set_result(message){
this.result_cell.setValue(message)
}
/** Report the function result to the administrator.
* @param {string} message Message to report.
* @param {boolean} to_slack Whether to send a message to slack.
*/
report_result(message, to_slack=false){
this.set_result(message);
if (to_slack){
// postSlack(`${now_str()}\n${this.userId}: ${message}`)
postSlack(message, this.name);
}
}
// Get ``this.userId``'s profile information.
get_profile(){
try{
var options = {
"method" : "GET",
"headers" : {
"Content-Type" : "application/json",
"Authorization" : "Bearer " + prop.getProperty("CHANNEL_ACCESS_TOKEN")
},
};
var response = UrlFetchApp.fetch(`https://api.line.me/v2/bot/profile/${this.userId}`, options);
return JSON.parse(response.getContentText());
} catch(ex) {
return {
"displayName": "",
"userId": "",
"language": "",
"pictureUrl": "",
"statusMessage": ""
}
}
}
/** Send a message to ``this.userId``
* @param {string} message A message to send to ``this.userId``.
*/
send_message(message=undefined){
if (message == undefined){
message = this.message;
}
var postData = {
"to" : this.userId,
"messages" : [
{
"type" : "text",
"text" : this.message,
}
]
};
this.send_push_message(postData)
}
/** Post the data of ``postData``.
* @reference https://developers.line.biz/en/reference/messaging-api/#send-reply-message
* @param {Object} postData A data structure for "https://api.line.me/v2/bot/message/push"
*/
send_push_message(postData){
var displayName = this.get_profile().displayName;
if (this.name == displayName){
var options = {
"method" : "POST",
"headers" : {
"Content-Type" : "application/json",
"Authorization" : "Bearer " + prop.getProperty("CHANNEL_ACCESS_TOKEN")
},
"payload" : JSON.stringify(postData)
};
try{
UrlFetchApp.fetch("https://api.line.me/v2/bot/message/push", options);
this.report_result("OK", false);
} catch (ex){
this.report_result(ex.message, true)
}
}else{
this.report_result(`名前 != displayName (${this.name}!=${displayName})`, true);
}
}
}
postSlack
エラーや、実行結果などをslackに通知する関数。
/** Post a Message to Slack using Incoming Webhook
* @param {string} text A default message to send to ``this.userId``.
* @param {string} channel Where to report the result.
* @param {string} username Bot's user name.
* @param {string} icon_url Image url for Bot Icon
*/
function postSlack(text,
username="USERNAME",
channel="#CHANNEL_NAME",
icon_url="https://iwasakishuto.github.io/images/profile/twitter.png"){
var payload = {
"text" : text,
"channel" : channel,
"username" : username,
"icon_url" : icon_url,
}
var options = {
"method" : "POST",
"payload" : JSON.stringify(payload)
}
var url = prop.getProperty("WEBHOOK_URL");
var response = UrlFetchApp.fetch(url, options);
var content = response.getContentText("UTF-8");
}
now_str
エラー等の報告には、当時の時間情報が必要になるため、「現在の時間を取得し、文字列に整形する」関数を作成する。
/** Return the current time as a string.
* @return {string} The current time as a string.
*/
function now_str(){
/** Fill in the numbers with 0 to make it beautiful.
* @param {Number} no The input number.
* @return {string} 0 filled number.
*/
function padLeft(no){
no = String(no);
let len = (2-no.length)+1;
return len > 0? new Array(len).join('0')+no : no;
}
var d = new Date
return `${d.getFullYear()}/${padLeft(d.getMonth()+1)}/${padLeft(d.getDate())} ${padLeft(d.getHours())}:${padLeft(d.getMinutes())}:${padLeft(d.getSeconds())}`
}
getColName
スプレッドシート特有の列名を、列のindexから取得できるようにする関数。
/** Get the column name on the spreadsheet from index.
* @param {Number} idx Column name specified by 1-based index.
* @return {string} Column Name.
*/
function getColName(idx) {
var sheet = SpreadsheetApp.getActiveSheet();
var result = sheet.getRange(1, idx);
result = result.getA1Notation();
result = result.replace(/\d/,'');
return result;
}