Skip to content

Instantly share code, notes, and snippets.

@kazuooooo
Last active September 20, 2022 11:21
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kazuooooo/6acb1f20c0a3616edaeefa09f1626cc6 to your computer and use it in GitHub Desktop.
Save kazuooooo/6acb1f20c0a3616edaeefa09f1626cc6 to your computer and use it in GitHub Desktop.
お弁当ボットのソースコードです(Google App Script)
Const = {
spreadSheetId: 'xxxxxxxxxxxxxxxxxx',
obentColumnIdx: 6,
sheetStartDate: new Date(2017, 0, 19),
sheetTopMarginRows: 4,
scheduleOrderTime: new Date(2017, 0, 1),
scheduleRemindTime: new Date(2017, 0, 1),
triggers: {
triggerDays: [ScriptApp.WeekDay.SUNDAY,
ScriptApp.WeekDay.MONDAY,
ScriptApp.WeekDay.TUESDAY,
ScriptApp.WeekDay.WEDNESDAY,
ScriptApp.WeekDay.THURSDAY],
callTrigger: {
hour: 17,
minute: 0
},
reminderTrigger: {
hour: 21,
minute: 0
},
orderTrigger: {
hour: 22,
minute: 0
}
}
}
function doPost(e) {
var request = parse_request(e);
var commandar = loadCommander();
switch (true) {
case /^obento/.test(request.text):
commandar.command_order(request);
break;
case /^ノ/.test(request.text):
commandar.command_add(request);
break;
case /^_/.test(request.text):
commandar.command_cancel(request);
break;
default:
no_command(request);
}
// return dummy value
return ContentService.createTextOutput("hello world!(dummy_message)");
}
function parse_request(e) {
var request = {};
request.text = e.parameters["text"][0];
request.user = e.parameters["user_name"][0];
return request;
}
// Commandar
loadCommander = function() {
SpreadSheet = loadSpreadSheet();
Commandar = {};
Commandar.command_order = function (request) {
// prod
var fax_email_address = "xxxxxxxxxxxxx@efaxsend.com"
// dev
// var fax_email_address = "zzzzzzzzzzzzzzzzzz@gmail.com"
var params = parse_order_text(request.text);
var subject = "MAMORIO株式会社" + params.month + "月" + params.day + "日分注文"
var body = "いつもお世話になっております。MAMORIO株式会社です。" + params.month + "月" + params.day + "日、お弁当" + params.amount + "個お願い致します。"
// mailを送信
MailApp.sendEmail(fax_email_address,
subject,
body);
// 結果をslack通知
var slack_result = "<@" + request.user + ">" + params.month + "月" + params.day + "日分" + params.amount + "個注文しました。(FAXが送信できたらここに通知されます。)"
loadSlackModule().send_slack(slack_result)
}
Commandar.command_add = function(request) {
var date = loadDateUtils().parseDateTime(request.text);
SpreadSheet.write_sheet(request.user, date, 1);
var amount = loadSummerizer().summerizeOrder().orderAmount;
var slack_result = "<@" + request.user + "> \n" + (date.getMonth() + 1) + "月" + date.getDate() + "日分 注文で登録しました。 \n 現在合計" + amount + "個"
loadSlackModule().send_slack(slack_result)
}
Commandar.command_cancel = function(request) {
var date = loadDateUtils().parseDateTime(request.text);
SpreadSheet.write_sheet(request.user, date, -1);
var amount = loadSummerizer().summerizeOrder().orderAmount;
var slack_result = "<@" + request.user + "> \n" + (date.getMonth() + 1) + "月" + date.getDate() + "日分 注文をキャンセルしました。 \n 現在合計" + amount + "個"
loadSlackModule().send_slack(slack_result)
}
function parse_order_text(text) {
var hankaku_text = convert_hankaku(text)
var reg = /(\d{1,2})月\s*(\d{1,2})日\s*(\d{1,2})/
var result = hankaku_text.match(reg)
var params = {};
params.month = result[1];
params.day = result[2];
params.amount = result[3];
return params;
}
function convert_hankaku(text) {
return text.replace(/[A-Za-z0-9]/g, function(s) {
return String.fromCharCode(s.charCodeAt(0) - 0xFEE0)
});
}
return Commandar;
}
//SpreadSheet
loadSpreadSheet = function() {
SpreadSheet = {};
SpreadSheet.dateToIdx = function(date) {
var sday = Const.sheetStartDate;
var millisecondsPerDay = 24 * 60 * 60 * 1000;
var days = (date - sday) / millisecondsPerDay;
var result = Math.floor(days) + Const.sheetTopMarginRows;
//HACK: 日付指定した日時がちょうど0時になってMath.ceil(days)が効かないので1日足してfloorをかける
return Math.floor(days + 1) + Const.sheetTopMarginRows;
}
SpreadSheet.write_sheet = function(user, date, val) {
var timeSheet = SpreadsheetApp.openById(Const.spreadSheetId)
var userSheet = timeSheet.getSheetByName(user);
var row = SpreadSheet.dateToIdx(date);
var cell = userSheet.getRange(row, Const.obentColumnIdx);
var sum = cell.getValue() + val;
cell.setValue(sum);
}
return SpreadSheet;
}
//Summerizer
loadSummerizer = function() {
SpreadSheet = loadSpreadSheet();
Summerizer = {};
Summerizer.summerizeOrder = function() {
collectedOrder = collectOrder();
var summerizedOrder = {};
summerizedOrder.date = getTommorow(); //tmp 必要そうなら引数で渡せるようにする
summerizedOrder.orderAmount = 0;
summerizedOrder.orderUsers = [];
summerizedOrder.noOrderUsers = [];
collectedOrder.forEach(function(order) {
if (order.value >= 1) {
summerizedOrder.orderAmount += order.value;
summerizedOrder.orderUsers.push(order.name);
} else {
summerizedOrder.noOrderUsers.push(order.name);
}
});
return summerizedOrder;
}
function collectOrder() {
var collectedOrder = [];
var usersSheets = getUserSheets();
userSheets.forEach(function(sheet) {
collectedOrder.push(getUserOrder(sheet));
});
return collectedOrder
}
// ユーザーのシートを配列で取得
function getUserSheets() {
var timeSheet = SpreadsheetApp.openById(Const.spreadSheetId)
var allSheets = timeSheet.getSheets()
return userSheets = allSheets.filter(function(sheet) {
return !(/^\_/.test(sheet.getSheetName()))
});
}
// ユーザーの注文するしないを取得
function getUserOrder(sheet, date) {
var userOrder = {}
var date = (typeof date !== 'undefined') ? date : getTommorow();
var row = SpreadSheet.dateToIdx(date);
var cell = sheet.getRange(row, Const.obentColumnIdx);
userOrder.name = sheet.getSheetName();
userOrder.value = (cell.getValue() == '') ? 0 : cell.getValue()
return userOrder;
}
return Summerizer;
}
// Scheuduled Tasks(最大1時間に1回しか実行出来ないので注意)
function setUpTimeTriggers() {
// 今あるトリガーは削除
deleteAllTrigger();
// お弁当いる方〜?
setCallTrigger();
// リマインド
setReminderTrigger();
// 注文
setOrderTrigger();
}
// 注文
function scheduledOrder(){
var summerize = loadSummerizer().summerizeOrder();
var SlackModule = loadSlackModule();
var orderUsers = summerize.orderUsers;
//個数が0ならreturnさせる
if(summerize.orderAmount <= 0){
SlackModule.send_slack('今日は注文がありませんでした。');
return;
}
var notifyText = SlackModule.addAts(orderUsers).join(' ') + ' お弁当を注文します';
SlackModule.send_slack(notifyText);
SlackModule.send_slack('obento ' + (summerize.date.getMonth() + 1) + '月' + summerize.date.getDate() + '日' + summerize.orderAmount + '個');
}
// リマインダー
function scheduledRemind() {
var summerize = loadSummerizer().summerizeOrder();
var SlackModule = loadSlackModule();
var noOrderUsers = summerize.noOrderUsers;
var notifyText = SlackModule.addAts(noOrderUsers).join(' ') + 'まだお弁当を注文していません。夜10時に注文されます。'
SlackModule.send_slack(notifyText);
}
function setCallTrigger() {
for (var i = 0; i < Const.triggers.triggerDays.length; i++) {
ScriptApp.newTrigger("triggerCall")
.timeBased()
.onWeekDay(Const.triggers.triggerDays[i])
.atHour(Const.triggers.callTrigger.hour)
.nearMinute(Const.triggers.callTrigger.minute)
.create();
}
}
function setReminderTrigger() {
for (var i = 0; i < Const.triggers.triggerDays.length; i++) {
ScriptApp.newTrigger("triggerReminder")
.timeBased()
.onWeekDay(Const.triggers.triggerDays[i])
.atHour(Const.triggers.reminderTrigger.hour)
.nearMinute(Const.triggers.reminderTrigger.minute)
.create();
}
}
function setOrderTrigger() {
for (var i = 0; i < Const.triggers.triggerDays.length; i++) {
ScriptApp.newTrigger("triggerOrder")
.timeBased()
.onWeekDay(Const.triggers.triggerDays[i])
.atHour(Const.triggers.orderTrigger.hour)
.nearMinute(Const.triggers.orderTrigger.minute)
.create();
}
}
function triggerCall() {
loadSlackModule().send_slack('<!channel> 明日お弁当いる方〜?');
}
function triggerReminder() {
scheduledRemind();
}
function triggerOrder() {
scheduledOrder();
}
function deleteAllTrigger() {
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
function everyMinuteTrigger() {
ScriptApp.newTrigger("triggerTest")
.timeBased()
.onWeekDay(ScriptApp.WeekDay.SUNDAY)
.atHour(20)
.nearMinute(17)
.create();
}
//Slack
loadSlackModule = function() {
var SlackModule = {};
SlackModule.send_slack = function(message, options) {
//prod
var incomingURL = "https://hooks.slack.com/services/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";
//dev
//var incomingURL = "https://hooks.slack.com/services/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"
var options = (options || {});
options["text"] = message;
var send_options = {
method: "post",
payload: {
"payload": JSON.stringify(options)
}
};
UrlFetchApp.fetch(incomingURL, send_options);
return message;
}
SlackModule.addAts = function(users) {
return users.map((function(user) {
return '<@' + user + '>';
}));
}
return SlackModule;
}
//DateUtil
loadDateUtils = function() {
var DateUtils = {};
// 今を返す
var _now = new Date();
var now = function(datetime) {
if (typeof datetime != 'undefined') {
_now = datetime;
}
return _now;
};
DateUtils.now = now;
// テキストから時間を抽出
DateUtils.parseTime = function(str) {
str = String(str || "").toLowerCase().replace(/[A-Za-z0-9]/g, function(s) {
return String.fromCharCode(s.charCodeAt(0) - 0xFEE0);
});
var reg = /((\d{1,2})\s*[:時]{1}\s*(\d{1,2})\s*(pm|)|(am|pm|午前|午後)\s*(\d{1,2})(\s*[:時]\s*(\d{1,2})|)|(\d{1,2})(\s*[:時]{1}\s*(\d{1,2})|)(am|pm)|(\d{1,2})\s*時)/;
var matches = str.match(reg);
if (matches) {
var hour, min;
// 1時20, 2:30, 3:00pm
if (matches[2] != null) {
hour = parseInt(matches[2]);
min = parseInt(matches[3] ? matches[3] : '0');
if (_.contains(['pm'], matches[4])) {
hour += 12;
}
}
// 午後1 午後2時30 pm3
if (matches[5] != null) {
hour = parseInt(matches[6]);
min = parseInt(matches[8] ? matches[8] : '0');
if (_.contains(['pm', '午後'], matches[5])) {
hour += 12;
}
}
// 1am 2:30pm
if (matches[9] != null) {
hour = parseInt(matches[9]);
min = parseInt(matches[11] ? matches[11] : '0');
if (_.contains(['pm'], matches[12])) {
hour += 12;
}
}
// 14時
if (matches[13] != null) {
hour = parseInt(matches[13]);
min = 0;
}
return [hour, min];
}
return null;
};
// テキストから日付を抽出
DateUtils.parseDate = function(str) {
str = String(str || "").toLowerCase().replace(/[A-Za-z0-9]/g, function(s) {
return String.fromCharCode(s.charCodeAt(0) - 0xFEE0);
});
if (str.match(/(明日|tomorrow)/)) {
var tomorrow = new Date(now().getFullYear(), now().getMonth(), now().getDate() + 1);
return [tomorrow.getFullYear(), tomorrow.getMonth() + 1, tomorrow.getDate()]
}
if (str.match(/(今日|today)/)) {
return [now().getFullYear(), now().getMonth() + 1, now().getDate()]
}
if (str.match(/(昨日|yesterday)/)) {
var yesterday = new Date(now().getFullYear(), now().getMonth(), now().getDate() - 1);
return [yesterday.getFullYear(), yesterday.getMonth() + 1, yesterday.getDate()]
}
var reg = /((\d{4})[-\/年]{1}|)(\d{1,2})[-\/月]{1}(\d{1,2})/;
var matches = str.match(reg);
if (matches) {
var year = parseInt(matches[2]);
var month = parseInt(matches[3]);
var day = parseInt(matches[4]);
if (isNaN(year) || year < 1970) {
//
if ((now().getMonth() + 1) >= 11 && month <= 2) {
year = now().getFullYear() + 1;
} else if ((now().getMonth() + 1) <= 2 && month >= 11) {
year = now().getFullYear() - 1;
} else {
year = now().getFullYear();
}
}
return [year, month, day];
}
return null;
};
// 日付と時間の配列から、Dateオブジェクトを生成
DateUtils.normalizeDateTime = function(date, time) {
// 時間だけの場合は日付を補完する
if (date) {
if (!time) date = null;
} else {
date = [now().getFullYear(), now().getMonth() + 1, now().getDate()];
if (!time) {
time = [now().getHours(), now().getMinutes()];
}
}
// 日付を指定したけど、時間を書いてない場合は扱わない
if (date && time) {
return (new Date(date[0], date[1] - 1, date[2], time[0], time[1], 0));
} else {
return null;
}
};
// 日時をいれてparseする
DateUtils.parseDateTime = function(str) {
// add
if (str === "ノ" || str === "_") {
return getTommorow();
}
// add end
var date = DateUtils.parseDate(str);
var time = DateUtils.parseTime(str);
if (!date) return new Date();
if (time) {
return (new Date(date[0], date[1] - 1, date[2], time[0], time[1], 0));
} else {
return (new Date(date[0], date[1] - 1, date[2], 0, 0, 0));
}
};
// Dateから日付部分だけを取り出す
DateUtils.toDate = function(date) {
return (new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0));
};
// 曜日を解析
DateUtils.parseWday = function(str) {
str = String(str).replace(/曜日/g, '');
var result = [];
var wdays = [/(sun|日)/i, /(mon|月)/i, /(tue|火)/i, /(wed|水)/i, /(thu|木)/i, /(fri|金)/i, /(sat|土)/i];
for (var i = 0; i < wdays.length; ++i) {
if (str.match(wdays[i])) result.push(i);
}
return result;
}
var replaceChars = {
Y: function() {
return this.getFullYear();
},
y: function() {
return String(this.getFullYear()).substr(-2, 2);
},
m: function() {
return ("0" + (this.getMonth() + 1)).substr(-2, 2);
},
d: function() {
return ("0" + (this.getDate())).substr(-2, 2);
},
H: function() {
return ("0" + (this.getHours())).substr(-2, 2);
},
M: function() {
return ("0" + (this.getMinutes())).substr(-2, 2);
},
s: function() {
return ("0" + (this.getSeconds())).substr(-2, 2);
},
};
DateUtils.format = function(format, date) {
var result = '';
for (var i = 0; i < format.length; i++) {
var curChar = format.charAt(i);
if (replaceChars[curChar]) {
result += replaceChars[curChar].call(date);
} else {
result += curChar;
}
}
return result;
};
return DateUtils;
};
// Global Util
function getTommorow(){
var date = new Date();
date.setDate(date.getDate() + 1);
return date;
}
// For Debug
function debugSlack(){
loadSlackModule().send_slack('<!channel> 明日お弁当いる方〜?(<https://docs.google.com/spreadsheets/d/xxxxxxxxxxxx/edit#gid=475934721>に1を入力すれば未来分もまとめて注文できます)');
}
function callTrigger() {
loadSlackModule().send_slack('<!channel> 明日お弁当いる方〜?2');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment