Skip to content

Instantly share code, notes, and snippets.

@kevinshu1995
Last active July 27, 2022 14:50
Show Gist options
  • Save kevinshu1995/adfa87203def23a99b474c7391e5b81f to your computer and use it in GitHub Desktop.
Save kevinshu1995/adfa87203def23a99b474c7391e5b81f to your computer and use it in GitHub Desktop.

Google calendar X Line Notify

Let your Line remind you your everyday tasks on Google calendar

  1. Generate Line Notify Token Token
  2. Create/Grab the Google calendar Id which you want to get notify from
  3. Create a new Google app script project
  4. Copy and paste the codes into your project (make sure the order of your gs files is the same as the sample below)
  5. configs.gs
  6. utils.gs
  7. api_notify.gs
  8. sendTodayCalendarInfoByNotify.gs
  9. main.gs
  10. setup script properties (google app scripts)
  11. go to project settings, fill out the properties and value with all your tokens (notify & google calendar), you can name the property names whatever you want.
  12. go to configs.gs file, and replace all the property keys
  13. deploy with web application
  14. create new trigger (the invoked function will be TriggerNotify)
  15. there you go!
function notifySendMsg(Authorization, msg) {
if (!msg) return
const data = UrlFetchApp.fetch( NOTIFY_ENDPOINT_URL + "/notify", {
"method" : "post",
"payload" : { "message" : msg },
"headers" : { Authorization }
});
console.log('Notify response /api/notify')
console.log(data.toString())
console.log(data.getHeaders())
}
/**
* @return {Object} response
*
* @return {Number} response.status - 200 || 401
* @return {String} response.message
* @return {String} response.targetType - "USER" || "GROUP"
* @return {String|Null} response.target - user name
*/
function notifyStatus (Authorization) {
try {
//抓取status資料
const response = UrlFetchApp.fetch( NOTIFY_ENDPOINT_URL + "/status", {
"contentType" : "application/json",
"headers" : {
"method" : "get",
Authorization,
},
});
return JSON.parse(response.getContentText())
} catch(error){
console.error(error)
return {
status: 404
}
}
}
const NOTIFY_ENDPOINT_URL = "https://notify-api.line.me/api";
const properties = PropertiesService.getScriptProperties();
// general
// Line Notify token
const NOTIFY_TOKEN_GENERAL = properties.getProperty('NOTIFY_TOKEN_GENERAL')
// google calendar ID
const CALENDAR_ID_GENERAL = properties.getProperty('CALENDAR_ID_GENERAL')
// second notify and calendar
// Line Notify token
// const NOTIFY_TOKEN_JOB = properties.getProperty('NOTIFY_TOKEN_JOB')
// google calendar ID
// const CALENDAR_ID_JOB = properties.getProperty('CALENDAR_ID_JOB')
function TriggerNotify (){
// first calendar
sendTodayCalendarInfoByNotify({
notify_token: NOTIFY_TOKEN_GENERAL,
calendarID: CALENDAR_ID_GENERAL
})
// second calendar maybe?
// sendTodayCalendarInfoByNotify({
// notify_token: NOTIFY_TOKEN_JOB,
// calendarID: CALENDAR_ID_JOB
//})
}
// Dealing time
const getTimeDetail = (calendar) => {
// 全天事件
if (calendar.isAllDayEvent()){
const startDate = getDateFormat(calendar.getAllDayStartDate())
const endDate = getDateFormat(calendar.getAllDayEndDate(), true)
const isSameYear = startDate.year === endDate.year
const isSameDate = startDate.text() === endDate.text()
// 判斷顯示哪種格式
if (isSameDate) return `${startDate.text()} 整天`
if (isSameYear) return `${startDate.text('MD')} ~ ${endDate.text('MD')}`
return `${startDate.text('YMD')} ~ ${endDate.text('YMD')}`
}
// 非全天事件
const startTimeObj = calendar.getStartTime()
const endTimeObj = calendar.getEndTime()
const startTimeText = getHourAndMin(startTimeObj)
const endTimeText = getHourAndMin(endTimeObj)
return startTimeText && startTimeText ? `${startTimeText} ~ ${endTimeText}` : ''
}
function sendTodayCalendarInfoByNotify({ notify_token, calendarID }) {
const Authorization = "Bearer " + notify_token
const {dateStart, dateEnd} = getNowStartAndEndDate()
const calendarData = CalendarApp.getCalendarById(calendarID).getEvents(dateStart, dateEnd);
const emptyContentText = '空'
const noEventMsg = '今天很空~'
// 判斷 notify server status
const {status, message, target} = notifyStatus(Authorization)
if (status !== 200){
console.error('Notify failed: ', message)
return
}
if (target === null){
console.error('Notify failed: target is null. ', message)
return
}
// 組合訊息
const taskMsgs = [
`Hi! ${target}\n`,
...[...calendarData].map((calendar, index) => {
const month = calendar.getStartTime().getMonth() + 1
const date = calendar.getStartTime().getDate()
const title = calendar.getTitle() || emptyContentText
const description = calendar.getDescription() || emptyContentText
const location = calendar.getLocation() || emptyContentText
const msgSentDateText = index === 0 ? `> ${month} 月 ${date} 號\n`: ''
return [
'',
msgSentDateText,
`--------- 任務 ${index + 1} ---------`,
`| ${title} `,
'',
`| 地點:`,
`${location}`,
'',
`| 內容:`,
`${description}`,
'',
`| 時間:`,
getTimeDetail(calendar),
''
].join('\n')
})
]
// 判斷是否有活動
taskMsgs.length <= 1 && taskMsgs.push(`\n ${noEventMsg}`)
// 送訊息
console.log(taskMsgs.join(''))
notifySendMsg(Authorization, taskMsgs.join(''))
}
const padStartMinute = (value) => String(value).padStart(2, '0')
const getDateFormat = (dateObj, isEnd = false) => {
if (isEnd) dateObj.setDate(dateObj.getDate() - 1)
const year = dateObj.getFullYear()
const month = dateObj.getMonth() + 1
const date = dateObj.getDate()
return {
text: (type) => {
switch(type){
case 'D':
return`${date}`
case 'MD':
return`${month}/${date}`
case 'YMD':
default:
return`${year}/${month}/${date}`
}
},
year,
month,
date
}
}
const getHourAndMin = (dateObj) => dateObj
? `${dateObj.getHours()} : ${padStartMinute(dateObj.getMinutes())}`
: null
const getNowStartAndEndDate = () => {
const now = new Date();
return {
dateStart: new Date(now.getFullYear(), now.getMonth(),now.getDate(), 0, 0, 0, 0),
dateEnd: new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment