programming
[4차시/마지막]파이썬 플라스크 (Python + Flask) 로 자동 응답 슬랙 봇 만들기 최종
by Kitle · 2021. 01. 22.
(4/4)번째 마지막 강좌입니다. 지난 강좌에서는 slack을 이용하여 @botname 메세지를 입력하면 똑같이 대답해주는 앵무새 봇을 만들었습니다.
슬랙과 여러분이 만든 서버가 메세지를 주고 받고 할 수 있게 된 것이죠.
마지막 시간은 여러분이 원하는 응답을 사전식으로 만들어 놓고 이를 대답할 수 있게 만들어 보겠습니다. 물론 더 좋은 방법과 지능적인 방법이 있겠지만 여기서는 여러가지 답변들을 미리 등록해 놓고 대답하는 FAQ같은 기능이라고 보면 되겠습니다.
그리고 사용자들이 어떤 검색어를 입력했는지 로그를 남기고, 이를 토대로 봇을 발전시켜 나갈수도 있겠죠?
그러면 마지막 강좌를 시작하겠습니다.
앵무새 답변이 아닌 키워드 식으로 답변하기
예를들어 'hello' 라고 입력하면 'hi there' 이라고 답변하게 만들어주고 싶습니다.
python에서는 dict 자료구조를 활용하여 쉽게 만들 수 있죠. 수정할 부분은 지난번 앵무새 봇에서 만들어 두었던
def get_answer(user_query): 부분을 수정하겠습니다.
def get_answer(user_query):
raw_user_query = user_query
user_query = raw_user_query.replace(" ", "")
answer_dict = {
'hello': 'hi there',
}
def get_answer(user_query):
raw_user_query = user_query
user_query = raw_user_query.replace(" ", "")
answer_dict = {
'hello': 'hi there',
}
if user_query == '' or None:
return "앗.. 아무것도 안쓰셨거나.. 혹은 아직 해석 불가 글자에요. 아직 그정도로 똑똑하진 않아요."
elif user_query in answer_dict.keys(): # 결과 있으면 리턴
return answer_dict[user_query]
else:
return user_query+"은(는) 없는 질문입니다."
키워드 답변에 날짜/요일 알려주기
팀원분들께 테스트를 해보니 '오늘이 무슨 요일이니?' 등의 고급 질의를 ㅠ.ㅠ 하시더라구요. 그래서 이부분을 완벽히 처리할 수는 없지만 요일을 입력하면 간단히 처리할 수 있게 키워드를 등록해 보겠습니다. 현재 날짜 시간등 정보를 얻어오기위해 파이썬 pytz 를 사용하겠습니다.
터미널로 이동하여 설치하여주세요.
(venv) $ pip install pytz
from datetime import datetime
import time
import pytz
def get_datetime(weekday=None):
asia_seoul = datetime.fromtimestamp(time.time(), pytz.timezone('Asia/Seoul'))
t = ['월요일', '화요일', '수요일', '목요일', '금요일', '토요일', '일요일']
if weekday is None:
fmt = "%Y-%m-%d %H:%M:%S"
s = asia_seoul.strftime(fmt)
return s
else:
return t[asia_seoul.today().weekday()]
def get_answer(user_query):
raw_user_query = user_query
user_query = raw_user_query.replace(" ", "")
answer_dict = {
'hello': 'hi there',
'요일': 'Asia/Seoul 현재 ' + str(get_datetime()) + '입니다. 오늘은 ' + str(get_datetime('weekday')) + '입니다.',
}
# 뒷부분 생략..
answer_dict = {
'hello': 'hi there',
'요일': 'Asia/Seoul 현재 ' + str(get_datetime()) + '입니다. 오늘은 ' + str(get_datetime('weekday')) + '입니다.',
'전화번호': '\n회사 번호 : 02-000-000. \n 고객 콜센터: 080-1544-0000',
}
if user_query == '' or None:
return "앗.. 아무것도 안쓰셨거나.. 혹은 아직 해석 불가 글자에요. 아직 그정도로 똑똑하진 않아요."
elif user_query in answer_dict.keys(): # 결과 있으면 리턴
return answer_dict[user_query]
else:
for now_key in answer_dict.keys(): # 키에서 먼저 찾고
if now_key.find(user_query) != -1:
return "연관 단어인 '"+now_key+"'에 대한 답변입니다.\n"+now_key+' : '+answer_dict[now_key]
for now_key in answer_dict.keys(): # 키가 없으면 본문에 검색
if answer_dict[now_key].find(raw_user_query[1:]) != -1:
return "관련이 있나 모르겠지만 답변 내용에"+answer_dict[now_key]+'가 있네요.\n'+now_key+'에 대한 답변이에요.'
return user_query+"은(는) 없는 질문입니다."
사용자 검색어를 로그로 저장하기
마지막으로 사용자 검색어를 로그로 저장해 분석해보도록 하겠습니다. 간단하게 검색어를 파일로 해당 시간에 저장하면 끝입니다. 최종 코드까지 한번에 확인하시죠.
from slacker import Slacker
import json
from flask import Flask, request, make_response
from datetime import datetime
import time
import pytz
token = "YOUR_TOKEN" #이부분 api.slack.com에서 발급받은 부분 넣으세요
slack = Slacker(token)
app = Flask(__name__)
def get_datetime(weekday=None):
asia_seoul = datetime.fromtimestamp(time.time(), pytz.timezone('Asia/Seoul'))
t = ['월요일', '화요일', '수요일', '목요일', '금요일', '토요일', '일요일']
if weekday is None:
fmt = "%Y-%m-%d %H:%M:%S"
s = asia_seoul.strftime(fmt)
return s
else:
return t[asia_seoul.today().weekday()]
def get_answer(user_query):
with open('searchlog.log', 'a') as f: # 유저 검색어는 로그로 저장, 시간 / 검색어만 저장
f.write(str(get_datetime())+','+user_query+'\n')
raw_user_query = user_query
user_query = raw_user_query.replace(" ", "")
answer_dict = {
'hello': 'hi there',
'요일': 'Asia/Seoul 현재 ' + str(get_datetime()) + '입니다. 오늘은 ' + str(get_datetime('weekday')) + '입니다.',
'전화번호': '\n회사 번호 : 02-000-000. \n 고객 콜센터: 080-1544-0000',
}
if user_query == '' or None:
return "앗.. 아무것도 안쓰셨거나.. 혹은 아직 해석 불가 글자에요. 아직 그정도로 똑똑하진 않아요."
elif user_query in answer_dict.keys(): # 결과 있으면 리턴
return answer_dict[user_query]
else:
for now_key in answer_dict.keys(): # 키에서 먼저 찾고
if now_key.find(user_query) != -1:
return "연관 단어인 '" + now_key + "'에 대한 답변입니다.\n" + now_key + ' : ' + answer_dict[now_key]
for now_key in answer_dict.keys(): # 키가 없으면 본문에 검색
if answer_dict[now_key].find(raw_user_query[1:]) != -1:
return "관련이 있나 모르겠지만 답변 내용에" + answer_dict[now_key] + '가 있네요.\n' + now_key + '에 대한 답변이에요.'
return user_query + "은(는) 없는 질문입니다."
def event_handler(event_type, slack_event):
channel = slack_event["event"]["channel"]
string_slack_event = str(slack_event)
if string_slack_event.find("{'type': 'user', 'user_id': ") != -1: # 멘션으로 호출
try:
user_query = slack_event['event']['blocks'][0]['elements'][0]['elements'][1]['text']
answer = get_answer(user_query)
slack.chat.post_message(channel, answer)
return make_response("ok", 200, )
except IndexError:
pass
elif string_slack_event.find("'channel_type': 'im'") != -1: # 다이렉트로 호출
try:
if slack_event['event']['client_msg_id']:
user_query = slack_event['event']['text']
answer = get_answer(user_query)
slack.chat.post_message(channel, answer)
return make_response("ok", 200, )
except IndexError:
pass
except KeyError:
pass
message = "[%s] cannot find event handler" % event_type
return make_response(message, 200, {"X-Slack-No-Retry": 1})
@app.route('/', methods=['POST'])
def hello_there():
slack_event = json.loads(request.data)
if "challenge" in slack_event:
return make_response(slack_event["challenge"], 200, {"content_type": "application/json"})
if "event" in slack_event:
event_type = slack_event["event"]["type"]
return event_handler(event_type, slack_event)
return make_response("There are no slack request events", 404, {"X-Slack-No-Retry": 1})
if __name__ == '__main__':
app.run(debug=True)
일단 디버그용 코드이니 적절히 수정하셔서 사용하시면 될것 같습니다.
그러면 로그가 잘 남았나 마지막으로 확인하고 마치겠습니다.
소스 수정 후 누군가 봇에게 메세지를 남기면 searchlog.log 파일로 남게 됩니다. 확장자는 편하게 .csv 등으로 변경해도 문제가 없겠네요.
봇 다이렉트 응답 만들기
매번 @ 로 봇을 불러서 하기도 귀찮을 수 있습니다. 봇과 1:1로 직접 대화하는 것도 좋겠죠. 채널에 불필요한 알람도 줄이구요.
이부분은 위 소스 코드에 다이렉트 호출 부분에 적용되어 있습니다.
Apps -> 해당 봇에 바로 말을 걸면 멘션을 쓰지 않아도 훨 편리하게 됩니다. 봇과의 비밀스런(이라고 쓰고 로그 남음) 대화도 가능하겠네요.
이제 사용자 입력을 통해 키워드와 답변을 고도화 하면 될것 같습니다. 이상 마치겠습니다.