Mattermost에서 Google Calendar 일정 받기 (Serverless)
Google Calendar 의 일정 정보(생성, 삭제, 시작)를 다른 메신저나 서비스로 전달하는 방법은 매우 다양하지만, 결과를 보여주고자 하는 환경에서 별도의 인터페이스를 제공하지 않는 경우 중간에 릴레이 역할을 하는 매개체(서버)가 반드시 필요하다. 그런 의미에서 자체적으로 연결을 지원하는 Jandi 나 외부 플러그인을 사용하는 Slack 은 매우 편리하다.
현재 업무에 사용하는 메신저는 Slack-Alternative 인 Mattermost 로, Jandi 나 Slack 처럼 쉬운 연결을 위한 UI 를 제공하고 있지 않다. Server-Less 구조로 Google Calendar 의 일정 정보를 받을 수 있는 방법을 만들어보자.
STEP1. Mattermost Webhook URL 생성
Google Calendar 에서 발생한 Event 정보를 수신할 수 있는 웹훅 URL 을 생성해야한다. 햄버거 메뉴를 클릭하고, 통합기능에 위치한 수신 웹훅 메뉴를 클릭하자.
제목과 설명은 웹훅 설정 화면에 보이는 내용이기 때문에 간단히 입력하면 된다. 중요한건.
- 웹훅 메시지를 받을 ‘채널’ 을 정확하게 선택하고 ‘이 채널로 고정’ 을 선택할 것.
- 사용자 ID 는 화면에 노출되는 (웹훅 메시지를 전달하는) ID 로 실제 존재하는 가 아니어도 무관하다.
- 프로필 이미지를 보여주는 부분. 구글링을 통해 적당한 png 파일 URL 을 넣자.
WebHook URL 이 생성됐다. 이 URL 은 외부로 유출되어선 위험하다. 본인 확인을 위한 별도의 토큰 정보가 없기 때문에 URL 만 알아도 내가 지정한 채널에 메시지를 보낼 수 있기 때문.
STEP2. Google Drive, Spreadsheet Script 편집기 열기
Google Calendar 에서 발행하는 이벤트 정보를 Spreadsheet 의 Script 에서 받아 앞서 생성한 WebHook URL 로 보내는 작업을 만든다. 정확히 이 Script 은 GAS(Google Apps Script)라 부리며, Excel 의 VBS(Visual Basic Script)과 개념은 비슷하다. 물론 구문은 Javascript 이기 때문에 이질감 없는 코딩이 가능하다는게 장점.
새로운 Google Spreadsheet 를 생성하자. 그리고 도구 메뉴의 <> 스크립트 편집기를 클릭하자.
편집기가 열렸다.
STEP3. Google Calendar, Calendar ID 얻기
정보를 갖고올 Calendar ID 를 알아야한다. 사용 할 캘린더를 선택해 ‘설정 및 공유 > 캘린더 통합’ 메뉴의 캘린더 ID 를 복사하자.
STEP4. GAS 코딩
Google Calendar 를 위한 GAS 관련 내용은 https://developers.google.com/apps-script/reference/calendar/calendar-app 를 참고하자. 내가 구현하고자 한 기능은 다음 세가지다.
- 이벤트 15분 전 알려줄 것
- 새로 생성되었거나 수정된 이벤트가 있으면 알려줄 것
- 매일 오전 8시 그날 전체 일정을 요약해서 알려줄 것
var calendarId = "{캘린더ID}"; var webhookUrl = "{웹훅URL}"; function myFunction() { Logger.log("\n Event Notification Begin \n"); begin15minAfterEvents(calendarId); // 이벤트 시작전 listupNewEvents(calendarId); // 31일 이내 // 매일 오전 8시에 그날 전체 일정을 요약해서 보낸다. Logger.log("\n Finished! \n"); } function listupNewEvents(cal_id){ // 한달치 신규/수정된 일정을 검사한다. var events, msgBody, lastUpdatedStamp, currentDateStamp; var checkTermMonth = 31, checkTermMin = 15; // Condition Init. var cal = CalendarApp.getCalendarById(cal_id); var currentDate = new Date(); var modifiedDate = new Date(); for(var i=0; i<checkTermMonth; i++){ // 31일치 데이터를 갖고온다 (0=오늘) modifiedDate.setDate(modifiedDate.getDate()+i); events = cal.getEventsForDay(modifiedDate); // 그 날짜의 전체이벤트 for(var k=0; k<events.length; k++){ lastUpdatedStamp = parseInt(Utilities.formatDate(events[k].getLastUpdated(),"GMT+0900","YYYYMMddHHmm")); currentDateStamp = parseInt(Utilities.formatDate(currentDate,"GMT+0900","YYYYMMddHHmm")); if(currentDateStamp-lastUpdatedStamp < checkTermMin){ // 최종수정일 비교 Logger.log(currentDateStamp+":"+lastUpdatedStamp+"\n"); msgBody = "##### "+ events[k].getTitle()+"\n"; if (events[k].isAllDayEvent()) { // 전일 msgBody += "- "+Utilities.formatDate(events[k].getStartTime(),"GMT+0900","YYYY년 MM월 dd일")+"\n"; } else { msgBody += "- "+Utilities.formatDate(events[k].getStartTime(),"GMT+0900","YYYY년 MM월 dd일 HH시 mm분")+" ~ "+Utilities.formatDate(events[k].getEndTime(),"GMT+0900","MM월 dd일 HH시 mm분")+"\n"; } msgBody += "- "+((events[k].getDescription())?events[k].getDescription():"내용 없음")+"\n\n"; msgBody += "일정이 생성(수정) 되었습니다." // 새로운 일정은 건단위로 발송한다. postMmost(msgBody); Logger.log(msgBody); } } } } function begin15minAfterEvents(cal_id){ // 15분뒤 시작되는 일정 var currentDate = new Date(); var cal = CalendarApp.getCalendarById(cal_id); var events = cal.getEventsForDay(currentDate); for(var i=0; i < events.length; i++){ if(!events[i].isAllDayEvent() && Utilities.formatDate(events[i].getStartTime(),"GMT+0900","YYYMMddHHmm")-Utilities.formatDate(currentDate,"GMT+0900","YYYMMddHHmm") == 15) { // 종일제외. 15분정각 msgBody = "##### "+ events[i].getTitle()+"\n"; msgBody += "- "+Utilities.formatDate(events[i].getStartTime(),"GMT+0900","YYYY년 MM월 dd일 HH시 mm분")+" ~ "+Utilities.formatDate(events[i].getEndTime(),"GMT+0900","MM월 dd일 HH시 mm분")+"\n"; msgBody += "- "+((events[i].getDescription())?events[i].getDescription():"내용 없음")+"\n\n"; msgBody += "###### 일정이 15분 후 시작 됩니다!"; postMmost(msgBody); Logger.log(msgBody); } } } function postMmost(msgBody){ var payload = { "text" : msgBody, "icon_url" : "https://img.icons8.com/color/420/google-calendar.png", "username" : "MTWORKS", } var options = { "method" : "POST", "contentType" : "application/json", "payload" : JSON.stringify(payload) } var response = UrlFetchApp.fetch(webhookUrl, options); var content = response.getContentText("UTF-8"); }
위 코드에서 매일 아침 8시에 그날 일정을 알려주는 부분은 만들지 않았지만 개발 방법은 간단하니 응용을. 위 코드가 정상적으로 동작하는지 확인하려면 메인 함수를 선택해 실행하면 된다.
내가 지정한 채널에 정상적으로 메시지가 온다면 성공적!
STEP5. 트리거 등록
Google Spreadsheet 의 수정 > 현재프로젝트의 트리거 메뉴를 선택하면 트리거를 생성할 수 있다. 난 15분 주기, 매일 오전 8시에 체크하도록 설정했다.
개발 자체는 어렵지 않다. 자, 마지막 Trigger 조건을 보면 이벤트 발생 시점을 캘린더로 지정할 수 있다. 이경우 새로운 이벤트가 발생하면 바로바로 알림을 줄 수 있게 되지만 왜 이걸 사용하지 않았냐고? 사실 이 스크립트는 사내 범용적으로 사용할 스크립트로 개발한 것으로 배포해 비 개발자도 사용하려고 했기 때문이다. 즉, 메뉴 설명 요소를 최소화 하기 위함에서 작업한 것이기 때문에.
만약 개발자 혹은 본인 전용 목적이라면 적절한 트리거를 응용해 호출 요소를 크게 줄일 수 있을 것이다.