리그캣의 개발놀이터

롯데 스펙태클 프로그래밍 직무 시험. 본문

팀 활동/프로젝트

롯데 스펙태클 프로그래밍 직무 시험.

리그캣 2018. 1. 25. 19:46

Programming_곽승혁(940128)


상황분석 (주제1)

  • 글로벌 기업은 협업을 위한 메신저의 활용을 중요시 그로 인하여 롯데 그룹은 메시징을 통한 협업 및 커뮤니티 플랫폼 구축

  • 롯데그룹 및 파트너사의 이메일과 도메인을 자동으로 묶이게 하는 시스템

  • 업무용 자동응답 챗봇 및 사용자 간 대화가 가능한 멀티 협업 환경 지원

문제 정의

  • 여러 사람이 이용할 수 있는 커뮤니케이션 포탈 제작 - (챗봇 먼저 구현 후 추후 개발)

  • 보안 인증을 통한 사용자 등록과 관리 - (django user auth form 사용하도록)

  • 단순 반복적 행정 처리 - (챗봇 시스템 구현 가능 - POLLING 기능으로 메시지를 통한 명령어 처리)

  • 모바일 PC 환경에서 모두 사용 가능토록 구현 - (모바일, PC 환경에서 이미 구축되어 있는 플랫폼 이용)

왜 챗봇 인가?

  • 나의 가장 많이 구현 했던 프로젝트 챗봇 나의 강점

일정 관리 봇

  • 알림 기능

    • 내용

      • 출근 후 10:00 그날 일정을 알림

      • 각 일정 10분 전에 알림

      • 일정 추가나 삭제될 시 해당 채팅방에 알림

      • 18:00 퇴근 30분 전에 내일 일정 자동 출력

    • 구현 방법

      • 해당 구글 캘린더와 SLACK 자체를 연동하여 Push 알람을 받게끔 설정. 체크박스로 얼마든지 쉽게 구현슬랙 봇이 폴링 기법을 사용하여 시간을 지속해서 체크 그 후 지정한 시간이 되었을 때 내일 일정

      • 명령어 호출출력

    • 이슈

      • 해당 명령어를 호출할 때 채널의 이름이 필요하여 소스 안에서 해당 채널로 변경해 주어야 함

  • 일정 안내

    • 내용

      • 모든 일정 확인 (일정-모두)

      • 당일 일정 확인 (일정-오늘)

      • 내일 일정 확인 (일정-내일)

    • 구현 방법

      • 해당 명령어를 챗봇이 인식하고 각 명령어에 따라 해당하는 명령어를 unionCalendar.py에서 호출

    • 이슈

      • 함수 지향적으로 프로그램을 구현하기 번거로웠지만, 막상 구현하니 유지보수가 간단하다고 생각

  • 일정 추가

    • 내용

      • 새로운 일정 추가 [일정-추가 (-D|-d) 날짜 (-T|-t) 시작:시간~끝나는:시간 (-S:-s) 일정제목]

    • 구현방법

      • 해당 명령어를 챗봇이 인식하고 각 명령어에 따라 해당하는 명령어를 unionCalendar.py에서 호출

      • 날짜, 시작시각, 끝나는 시간은 일정한 포맷에 맞추어야 하므로 포맷을 맞추어주는 함수 따로 구현

      • 정규식을 사용하여 구현하여서 중간에 띄어쓰기나 대소문자가 바뀌어도 상관없음

      • 명령어 순서는 일치하여야 하며 하나라도 빠뜨릴 시 실행 안 되게 구현

    • 이슈

      • 일정을 추가할 때 참여자의 이메일이 필요하므로 소스 안에 미리 각자의 이메일 기재 필요

      • 인턴 일정이 한 달 정도 제한, 날짜 기재 만이 오히려 효율적일 것으로 예상하여 月 기재는 없앰

  • 일정 삭제

    • 내용

      • 기존 일정 삭제 [일정-삭제 (-D|-d) 날짜 (-S:-s) 일정제목]

    • 구현방법

      • 해당 명령어를 챗봇이 인식하고 각 명령어에 따라 해당하는 명령어를 unionCalendar.py에서 호출

      • 일정에 해당하는 Id를 갖고 있어야 해당 일정을 지울 수 있어. 일정 제목으로 해당 id를 뽑아내는 함수를 따로 구현

      • 정규식을 사용하여 구현 하여서 중간에 띄어쓰기나 대소문자가 바뀌어도 상관 없음

      • 해당 날짜에 해당하는 제목의 일정이 없으면 구현이 안되게 개발

      • 명령어 순서는 일치하여야 하며 하나라도 빠뜨릴 시 실행안되게 구현

    • 이슈

      • 같은 날 같은 제목의 일정이 있으면 for문을 돌리기 때문에 맨 마지막 일정 하나만 없어짐

      • 추 후에 시간과 같이 지정해서 제거를 시키던지, 한꺼번에 여러개의 일정을 제거할 수 있는 명령어 구현 필요하다 생각함


시스템 구성도





소스코드

게시판

Django 프레임워크를 사용. 소스코드는 .zip로 제출하겠습니다.

챗봇

dailybot.py

# -*- coding: utf-8 -*-


import calendarunion

import os

import time

import datetime

import json

import urllib

from slackclient import SlackClient

from slacker import Slacker

import re



# starterbot's ID as an environment variable

#BOT_ID = os.environ.get("BOT_ID")

BOT_ID = "U89P3Q9T3"


# constants

AT_BOT = "<@" + BOT_ID + ">"

EXAMPLE_COMMAND = u"일정-도와줘"

EXAMPLE_COMMAND1 = u"안녕"

LIST_ALL = u"일정-모두"

LIST_TOMORROW = u"일정-내일"

LIST_TODAY = u"일정-오늘"

INSERT_EVENT = u"일정-추가"

DELETE_EVENT = u"일정-삭제"

BL = True;


# instantiate Slack & Twilio clients


#slack = Slacker(os.environ.get('SLACK_BOT_TOKEN'))

#slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))

slack = Slacker("xoxb-281785825921-Cvg6DimfiXwQJgnGf1vPao9r")

slack_client = SlackClient("xoxb-281785825921-Cvg6DimfiXwQJgnGf1vPao9r")



def post_to_channel(message, channel):

  slack.chat.post_message(channel, message, as_user=True)


def parse_slack(msg):

  output_list = msg

  if output_list and len(output_list) > 0:

      for output in output_list:

          if output and 'text' in output and BOT_ID not in output:

              channel = output['channel']

              command = output['text']    # Get text in JSON

              answer = slack_answer(command, channel)    # Go to desk

              if answer :

                  post_to_channel(answer, channel)

  return None


def calendar_report():

 now = datetime.datetime.now()

 hour = now.hour

 min = now.minute

 sec = now.second;

 global BL

 if hour == 17 and min == 55 and BL:

    payload = {"text": "내일 일정확인해볼래?"}

    url = "https://hooks.slack.com/services/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"

    req = urllib2.Request(url)

    req.add_header('Content-Type', 'application/json')

    urllib2.urlopen(req, json.dumps(payload))

    BL = False

 if hour == 18 and min == 00:

    BL = True

 return None



def slack_answer(txt, channel):

  if txt == EXAMPLE_COMMAND1:

      answer = "안녕? 나는 일정봇이야. 명령어를 입력해줘! 명령어를 모른다면 '일정-도와줘'를 검색해봐:)"

  elif txt == EXAMPLE_COMMAND:

      answer = "자 한번 사용해보자"

      calendarunion.help_command(channel)

  elif txt.find(LIST_ALL) != -1:

      answer = "검색이 모두 완료되었어:smile:"

      calendarunion.get_AllList(channel)

  elif txt.find(LIST_TOMORROW) != -1:

      answer = "미리 준비하자>__<"

      calendarunion.get_TomorrowList(channel)

  elif txt.find(LIST_TODAY) != -1:

      answer = "오늘 하루도 힘내^__^"

      calendarunion.get_TodayList(channel)

  elif txt.find(INSERT_EVENT) != -1:

      cmd = re.compile(r"((^\S+)+\s*(-D|-d)+\s*(\d{2})+\s*(-T|-t)+\s*((\d+):(\d+))(-|~)((\d+):(\d+))+\s*(-S|-s)+\s*(\D+))")

      if re.match(cmd,txt) is not None:

          answer = "*★*일정 추가 완료*★*:smile:"

          matchobj = cmd.search(txt)

          E_Day=matchobj.group(4)

          s_Time=matchobj.group(6)

          e_Time=matchobj.group(10)

          event_Name = matchobj.group(14)

          s_dateTime = calendarunion.convert(E_Day, s_Time)

          e_dateTime = calendarunion.convert(E_Day, e_Time)

          calendarunion.insert_Event(event_Name, s_dateTime, e_dateTime, channel)

      else:

          answer = "정확하게 입력바랍니다.ex)일정추가 날짜 00:00~00:00 일정제목"

          return None

  elif txt.find(DELETE_EVENT) != -1:

      cmd = re.compile(r"((^\S+)+\s*(-D|-d)+\s*(\d{2})+\s*(-S|-s)+\s*(\D+))")

      if re.match(cmd, txt) is not None:

          answer = "*☆*일정 제거 완료*☆*:smile:"

          matchobj = cmd.search(txt)

          D_Day=matchobj.group(4)

          E_Summary=matchobj.group(6)


          Event_Id=calendarunion.get_EventID(D_Day,E_Summary,channel)

          print(Event_Id)

          if Event_Id=='error':

              return None

          calendarunion.delete_Event(Event_Id,channel)

      else:

          answer = "정확하게 입력바랍니다.ex)일정제거 날짜 일정제목"

          return None

 #payload = {"text": "내일일정 알림!!!"}

 #url = "https://hooks.slack.com/services/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"

 #req = urllib2.Request(url)

 #req.add_header('Content-Type', 'application/json')

 #urllib2.urlopen(req, json.dumps(payload))

 #return None

  else:

      return None

  return answer


if __name__ == "__main__":

  if slack_client.rtm_connect():

      print("Connected!")

      while True:

          calendar_report()

          parse_slack(slack_client.rtm_read())

          time.sleep(1)

  else:

      print("Connection failed.")



calendarunion.py

# -*- coding: utf-8 -*-


from __future__ import print_function

import httplib2

import os


from slackclient import SlackClient

from apiclient import discovery

from oauth2client import client

from oauth2client import tools

from oauth2client import file

from httplib2 import Http

from oauth2client.file import Storage

from slacker import Slacker


import datetime

#library???


#slack = Slacker(os.environ.get('SLACK_BOT_TOKEN'))

slack = Slacker("xoxb-281785825921-Cvg6DimfiXwQJgnGf1vPao9r")


def post_to_channel(message, channel):

  slack.chat.post_message(channel, message, as_user=True)


try:

  import argparse

  flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()

except ImportError:

  flags = None


SCOPES = 'https://www.googleapis.com/auth/calendar'#, 'https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/plus.login'

CLIENT_SECRET_FILE = 'client_secret.json'

APPLICATION_NAME = 'Google Calendar API Python Quickstart'

CA_NAME='skuniv.ac.kr_86in1lo83oq31vht666aotr7kk@group.calendar.google.com'#our calendar

EMAIL = 'league3236@skuniv.ac.kr','subeen2150@skuniv.ac.kr','shs4161@skuniv.ac.kr','jebigbang18@skuniv.ac.kr','mynadahun@skuniv.ac.kr','sports1014@skuniv.ac.kr','twinpapa2003@skuniv.ac.kr'




def get_credentials():

  """Gets valid user credentials from storage.

  If nothing has been stored, or if the stored credentials are invalid,

  the OAuth2 flow is completed to obtain the new credentials.

  Returns:

      Credentials, the obtained credential.

  """

  home_dir = os.path.expanduser('~')

  credential_dir = os.path.join(home_dir, '.credentials')

  if not os.path.exists(credential_dir):

      os.makedirs(credential_dir)

  credential_path = os.path.join(credential_dir,

                                 'calendar-python-quickstart.json')


  store = Storage(credential_path)

  credentials = store.get()

  if not credentials or credentials.invalid:

      flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)

      flow.user_agent = APPLICATION_NAME

      if flags:

          credentials = tools.run_flow(flow, store, flags)

      else: # Needed only for compatibility with Python 2.6

          credentials = tools.run(flow, store)

      answer='Storing credentials to ' + credential_path

  return credentials


def get_AllList(channel):

  """Shows basic usage of the Google Calendar API.

      Creates a Google Calendar API service object and outputs a list of the next

      10 events on the user's calendar.

      """

  credentials = get_credentials()

  http = credentials.authorize(httplib2.Http())

  service = discovery.build('calendar', 'v3', http=http)


  now1 = datetime.datetime.utcnow().isoformat() + 'Z'  # 'Z' indicates UTC time

  #print('Getting the upcoming 10 events')

  eventsResult = service.events().list(

      calendarId=CA_NAME, timeMin=now1, maxResults=10, singleEvents=True,

      orderBy='startTime').execute()

  events = eventsResult.get('items', [])


  #now1 = datetime.datetime.now()

  answer=u'날짜                 시간       제목\n'

  #nowDate = now.strftime('%Y-%m-%d')

  #print(nowDate)  # 2015-04-19


  if not events:

      answer = u'모든 일정이 비었어:( \n일정추가 명령어로 일정을 등록해보는건 어때?'

      attachments = [

          {

              "text": answer,

              "fallback": "ALL daily",

              "callback_id": "ALL_daily",

              "color": "#f44182",

              "attachment_type": "default",

          }

      ]

  for event in events:

      start = event['start'].get('dateTime', event['start'].get('date'))

      answer=answer+start.split('+')[0].split('T')[0]+u'    '+start.split('+')[0].split('T')[1].split(':')[0]+(':')+start.split(':')[1]+u'    '+event['summary']+u"\n"

      attachments = [

          {

              "text": answer,

              "fallback": "You are unable to choose a game",

              "callback_id": "wopr_game",

              "color": "#f4eb41",

              "attachment_type": "default",

          }

      ]

  slack.chat.post_message(channel, attachments=attachments, as_user=True)

  return None

def get_TodayList(channel):

  """Shows basic usage of the Google Calendar API.

      Creates a Google Calendar API service object and outputs a list of the next

      10 events on the user's calendar.

      """

  credentials = get_credentials()

  http = credentials.authorize(httplib2.Http())

  service = discovery.build('calendar', 'v3', http=http)


  now1 = datetime.datetime.utcnow().isoformat() + 'Z'  # 'Z' indicates UTC time

  #print('Getting the upcoming 10 events')

  eventsResult = service.events().list(

      calendarId=CA_NAME, timeMin=now1, maxResults=10, singleEvents=True,

      orderBy='startTime').execute()

  events = eventsResult.get('items', [])


  now=datetime.datetime.now()

  nowDate = now.strftime('%Y-%m-%d')

  answer =u"오늘 일정이야\n"

  post_to_channel(answer,channel)

  answer=u'시간                   제목\n'

  if not events:

      answer = u'오늘 일정이 비었어:( \n일정추가 명령어로 일정을 등록해보는건 어때?'

      post_to_channel(answer, channel)

      return None

  for event in events:

      start = event['start'].get('dateTime')

      end = event['end'].get('dateTime')

      today = start.split('T')[0]

      if today == nowDate:

          answer = answer + start.split('+')[0].split('T')[1].split(':')[0]+(':')+start.split(':')[1]+"~"+end.split('+')[0].split('T')[1].split(':')[0]+(':')+start.split(':')[1]+u"    "+event['summary']+u"\n"

      attachments = [

          {

              "text": answer,

              "fallback": "Today daily",

              "callback_id": "Today_daily",

              "color": "#3AA3E3",

              "attachment_type": "default",

          }

      ]

  slack.chat.post_message(channel, attachments=attachments, as_user=True)

          #post_to_channel(answer,channel)

          #print(event['id'])

  return None


def get_TomorrowList(channel):

  """Shows basic usage of the Google Calendar API.

          Creates a Google Calendar API service object and outputs a list of the next

          10 events on the user's calendar.

          """

  credentials = get_credentials()

  http = credentials.authorize(httplib2.Http())

  service = discovery.build('calendar', 'v3', http=http)


  now1 = datetime.datetime.utcnow().isoformat() + 'Z'  # 'Z' indicates UTC time

  # print('Getting the upcoming 10 events')

  eventsResult = service.events().list(

      calendarId=CA_NAME, timeMin=now1, maxResults=10, singleEvents=True,

      orderBy='startTime').execute()

  events = eventsResult.get('items', [])


  now = datetime.datetime.now()

  tomorrowDate = str(now + datetime.timedelta(days=1)).split(' ')[0]


  answer = u"내일 일정이야\n"

  post_to_channel(answer, channel)

  answer = u'시간                   제목\n'

  if not events:

      answer = u'내일 일정이 비었어:( \n일정추가 명령어로 일정을 등록해보는건 어때?'

      post_to_channel(answer, channel)

      return None

  for event in events:

      start = event['start'].get('dateTime')

      end = event['end'].get('dateTime')

      tomorrow = start.split('T')[0]

      if tomorrow == tomorrowDate:

          answer = answer + start.split('+')[0].split('T')[1].split(':')[0] + (':') + start.split(':')[1] + "~" + \

                   end.split('+')[0].split('T')[1].split(':')[0] + (':') + start.split(':')[1] + u"    " + event[

                       'summary'] + u"\n"

      attachments = [

          {

              "text": answer,

              "fallback": "Tomorrow_List",

              "callback_id": "Tomorrow_List",

              "color": "#9541f4",

              "attachment_type": "default",

          }

      ]

  slack.chat.post_message(channel, attachments=attachments, as_user=True)

  return None


def insert_Event(EventName,StartTime,EndTime,channel):

  store = file.Storage('storage.json')

  creds = store.get()

  if not creds or creds.invalid:

      flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)

      creds = tools.run_flow(flow, store)

  GCAL = discovery.build('calendar', 'v3', http=creds.authorize(Http()))


  GMT_OFF = '+09:00'  # PDT/MST/GMT-7

  EVENT = {

      'summary': EventName,

      'start': {'dateTime': StartTime+'%s' % GMT_OFF},#2017-07-12T14:00:00

      'end': {'dateTime': EndTime+'%s' % GMT_OFF},#2017-07-12T14:30:00

      'attendees': [

          {'email': EMAIL[0]},

          {'email': EMAIL[1]},

          {'email': EMAIL[2]},

          {'email': EMAIL[3]},

          {'email': EMAIL[4]},

          {'email': EMAIL[5]},

          {'email': EMAIL[6]},

      ],

  }


  e = GCAL.events().insert(calendarId=CA_NAME, sendNotifications=True, body=EVENT).execute()


  answer=u'''*** *[%s]* 일정을 추가중입니다 ---

         •시작시간:   %s

         •끝나는시간: %s''' % (e['summary'],

                         e['start']['dateTime'].split('T')[0] + " " + e['start']['dateTime'].split('+')[0].split('T')[1].split(':')[0]+(':')+e['start']['dateTime'].split(':')[1],

                         e['end']['dateTime'].split('T')[0] + " " + e['end']['dateTime'].split('+')[0].split('T')[1].split(':')[0]+(':')+e['start']['dateTime'].split(':')[1])

  post_to_channel(answer, channel)


  return 0

#print('DAY:'+start.split('+')[0].split('T')[0],'Time:'+start.split('+')[0].split('T')[1].split(':')[0]+(':')+start.split(':')[1],'Summary:'+event['summary'])


def convert(date,time):

  now = datetime.datetime.now()

  result = now.strftime('%Y-%m-') + date + 'T' + time + ':00'


  return result


def delete_Event(Event_Id,channel):

  credentials = get_credentials()

  http = credentials.authorize(httplib2.Http())

  service = discovery.build('calendar', 'v3', http=http)


  now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time

  #print('Getting the upcoming 10 events')

  eventsResult = service.events().list(

      calendarId=CA_NAME, timeMin=now, maxResults=10, singleEvents=True,

      orderBy='startTime').execute()

  events = eventsResult.get('items', [])


  answer=u'***일정 제거중***'

  post_to_channel(answer, channel)

  service.events().delete(calendarId=CA_NAME, eventId=Event_Id).execute()


  return 0


def get_EventID(D_Day,E_Summary,channel):

  credentials = get_credentials()

  http = credentials.authorize(httplib2.Http())

  service = discovery.build('calendar', 'v3', http=http)


  now1 = datetime.datetime.utcnow().isoformat() + 'Z'  # 'Z' indicates UTC time

  # print('Getting the upcoming 10 events')

  eventsResult = service.events().list(

      calendarId=CA_NAME, timeMin=now1, maxResults=10, singleEvents=True,

      orderBy='startTime').execute()

  events = eventsResult.get('items', [])


  now = datetime.datetime.now()

  WhatDate = now.strftime('%Y-%m-'+D_Day)


  for event in events:

      start = event['start'].get('dateTime')

      today = start.split('T')[0]

      if ((today == WhatDate) and (event['summary']==E_Summary)):

          event_id = event['id']

          return event_id

  return 'error'


def help_command(channel):

  answer = "*도움말*:smile:\n*명령어*\n\t일정-도와줘\t일정봇의 명령어들을 볼 수 있습니다.\n\t일정-모두\t모든 일정을 확인할 수 있습니다.\n\t일정-오늘\t오늘 일정을 확인할 수 있습니다.\n\t일정-내일\t내일 일정을 확인할 수 있습니다.\n\t일정-추가\t일정을 추가할 수 있습니다.\n\t\t[일정-추가 (-D|-d) 날짜 (-T|-t) 시작:시간~끝나는:시간 (-S:-s)일정제목]\n\t\t일정-추가 -d 18 -t 12:00~13:00 -S 점심시간\n\t일정-삭제\t\t일정을 삭제 할 수 있습니다.\n\t\t[일정-삭제 (-D|-d) 날짜 (-S:-s)일정제목]\n\t\t일정-삭제 -d 18 -S 점심시간"

  attachments = [

      {

          "text": answer,

          "fallback": "Help_Command",

          "callback_id": "Help_Command",

          "color": "#42f459",

          "attachment_type": "default",

      }

  ]

  slack.chat.post_message(channel, attachments=attachments, as_user=True)

  return None


def main():

  print('실행되는중')

  #command("event all")

  #get_AllList(channel)

  #command("event today")

  #get_TodayList()

  #command("Tomorrow")

  #get_TomorrowList()


  #insert

  #command("insert 15 13:00-14:00 summary")

  #((^\D+)\s(\d+)\s((\d+):(\d+))(-|~)((\d+):(\d+))\s(\D+))



  #E_Day='14'

  #s_Time='13:00'

  #e_Time='14:00'

  #event_Name = 'test'

  #s_dateTime = convert(E_Day,s_Time)

  #e_dateTime = convert(E_Day,e_Time)

  #insert_Event(event_Name,s_dateTime,e_dateTime,channel)



  #combine start delete


  #command(ex:delete 12 melong) (day"(\d[+2])")(summary"(^\D+)\s+(\d+2)\s+(\D+)"3GROUP USE)


  #D_Day='12'

  #E_Summary='melong'

  #Event_Id=get_EventID(D_Day,E_Summary)

  #delete_Event(Event_Id)


if __name__ == '__main__':

  main()



화면 캡처














Comments