메이킹/메이킹 프로젝트

카카오톡 학식봇 만들기 - 2

트리맨스 2020. 2. 21. 23:43
반응형

 

카카오 오픈빌더 사용하기


학식을 알려주는 카카오톡 봇을 만들기 위해 필요한 것만 간단히 알아보자.

먼저 봇 제네릭 메뉴를 설정해 보자. 봇 제네릭 메뉴는 채팅창 하단에 고정되어 있는 버튼이다. 이것을 활성화 시키고, 식당을 선택하는 곳에 써 보자.

 

 

왼쪽 상단에 있는 시나리오 설정을 클릭하면, 봇 제네릭 메뉴가 뜬다. 봇 제네릭 메뉴는 채팅 입력 칸 상단에 고정적으로 떠 있는 버튼이다. 봇 제네릭 메뉴 목록을 원하는 대로 추가하면 채팅방에 하단 고정이 되어 계속 뜬다.

 

다음으로는 시나리오를 추가해야 한다. 챗봇을 위해 하는 이 작업은 사용자가 말한 내용을 기반으로 어떠한 정보를 출력해야 하는지 입력받는 것이다. 사용자 발화를 인식하게 하기 위해서는, 시나리오에 사용자 발화를 적어야 한다. 각 상황이 될 발화를 적어 두자.

 

 

스킬


챗봇에서 가장 핵심이 되는 기능인 스킬이다. 스킬은 외부 서비스와 연결하게 해주는 매우 유용한 도구이다.

 

 

스킬의 이름과 설명을 원하는 대로 적어 주자.

URL 칸에는 본인이 설정한 스킬 서버의 주소를 적어 주자.  나는 본인 아이피주소:포트번호/message 로 저장해 두었습니다. /message 가 들어간 이유는 스킬 서버의 코드와 관련이 있습니다.

 

 

여기서 스킬을 테스트 할 수 있습니다.

 

우측의 '요청할 파라미터 값 입력' 항목은 사용자가 발화하거나 특정 값을 보낼 수 있다. 좌측의 'JSON' 항목은 스킬 서버로 전송되는 실제 json 이다. 카카오톡 봇과 스킬 서버가 서로 통신을 할 때에는 json 파일을 주고 받는다. Flask를 실행할 때에 서버 메소드를 POST로 설정하는 이유이다. 위에 있는 json 파일은 카카오톡 봇에서 스킬 서버로 전송할 때 보내는 json 양식이다. 자세한 정보는 카카오톡 스킬 가이드에 들어가면 json 양식을 알 수 있다.

https://i.kakao.com/docs/getting-started-overview#%EC%98%A4%ED%94%88%EB%B9%8C%EB%8D%94-%EC%86%8C%EA%B0%9C

 

 

발화 (utterance) 에 시나리오에 적었던 문구를 넣고 (또는 key : uttreance , Value : 문구) 스킬서버로 전송을 하면, 정해진 양식의 json 파일이 수신된다. 수신된 json 파일은 스킬 서버에서 직접 코딩해야 한다. 이 양식도 마찬가지로, 스킬 제작 가이드에 나와 있다.

 

이 정보를 기반으로 파이썬을 이용해 코딩을 해 보았다. 여기 코딩에서는 웹 크롤러 기능을 추가했다. 핵심이 되는 부분은 카카오톡 서버랑 통신하는 구현부이기 때문에, 크롤러 기능은 따로 공부를 해야 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
from flask import Flask,request,jsonify
import bs4
import urllib.request
import time
 
import os
import sys
 
app = Flask(__name__)
 
#식당성정->날짜설정->아침점심저녁 설정->처음으로
 
Restaurant=["학생식당","푸름관","오름1동","오름3동","교직원 식당"]
 
ChoiceUrl=""
ChoiceDay=0
ChoiceRes=0
 
urlStudent="http://www.kumoh.ac.kr/ko/restaurant01.do"
urlProfess="http://www.kumoh.ac.kr/ko/restaurant02.do"
urlPorum="http://dorm.kumoh.ac.kr/dorm/restaurant_menu01.do"
urlorum1="http://dorm.kumoh.ac.kr/dorm/restaurant_menu02.do"
urlorum3="http://dorm.kumoh.ac.kr/dorm/restaurant_menu03.do"
 
'''
월요일~일요일 중식 : 0~6
월요일~일요일 석식 : 7~13
@@@ 예외적으로 오름 1동은 중식->조식 @@@
'''
 
jsonChoiceDay = {
    "version""2.0",
    "template": {"outputs": [{"simpleText": {"text""날짜를 선택해 주세요"}}],
                 "quickReplies": [{"label""오늘""action""message""messageText""오늘"},
                                  {"label""월""action""message""messageText""월"},
                                  {"label""화""action""message""messageText""화"},
                                  {"label""수""action""message""messageText""수"},
                                  {"label""목""action""message""messageText""목"},
                                  {"label""금""action""message""messageText""금"},
                                  {"label""토""action""message""messageText""토"},
                                  {"label""일""action""message""messageText""일"}
                                  ]
                 }
}
 
jsonChoiceRes = {
    "version""2.0",
    "template": {"outputs": [{"simpleText": {"text""식당을 선택해 주세요"}}],
                 "quickReplies": [{"label""학생식당""action""message""messageText""학생식당"},
                                  {"label""푸름관""action""message""messageText""푸름관"},
                                  {"label""오름1동""action""message""messageText""오름1동"},
                                  {"label""오름3동""action""message""messageText""오름3동"},
                                  {"label""교직원""action""message""messageText""교직원"},
                                  ]
                 }
}
 
 
jsonChoiceTime = {
    "version""2.0",
    "template": {"outputs": [{"simpleText": {"text""시간을 선택해 주세요"}}],
                 "quickReplies": [{"label""아침""action""message""messageText""아침"},
                                  {"label""점심""action""message""messageText""점심"},
                                  {"label""저녁""action""message""messageText""저녁"},
                                  ]
                 }
}
 
 
 
def returnMenu(url,num):  #식단을 보여줄수 있게 하는 함수 (링크,식단종류)
    html = bs4.BeautifulSoup(urllib.request.urlopen(url), "html.parser")
    menus=html.find("td")
    menu=str(menus.text)  #bs4 자료형을 String 형태로 변환, 식단의 존재 유무 판별
 
    if(menu=="등록된 메뉴가 없습니다."): #식단이 없을경우
        return menu
    else:                              #식단이 있을경우
        html = bs4.BeautifulSoup(urllib.request.urlopen(url), "html.parser")
        menu = html.findAll("ul", {"class""s-dot"})
        return menu[num].text.strip()
 
 
def returnAvaliableTimeDormitory(url):  #기숙사 식당 이용 시간을 리턴하는 함수
    html = bs4.BeautifulSoup(urllib.request.urlopen(url), "html.parser")
    Time=html.findAll("div",{"class":"contents-area"})
    return Time[3].text + Time[4].text
 
 
def returnAvaliableTime(url):  #전체식당 이용 시간을 리턴하는 함수
    html = bs4.BeautifulSoup(urllib.request.urlopen(url), "html.parser")
    Time=html.findAll("ul",{"class":"ul-h-list01"})
    return Time[1].text
 
 
@app.route('/message', methods=['POST'])  #json으로 들어온 사용자 요청을 보고 판단
def bob():
 
    content = request.get_json() #사용자가 보낸 메세지 입력
    content = content['userRequest']
    content = content['utterance']
 
    global ChoiceUrl
    global ChoiceDay
    global ChoiceRes
    global jsonChoiceDay
    global jsonChoiceRes
    global jsonChoiceTime
 
    if content==u"학생식당":
        response_data=jsonChoiceDay
        ChoiceUrl=urlStudent
        ChoiceRes=0
 
    elif content==u"푸름관":
        response_data=jsonChoiceDay
        ChoiceUrl=urlPorum
        ChoiceRes = 1
 
    elif content==u"오름1동":
        response_data=jsonChoiceDay
        ChoiceUrl=urlorum1
        ChoiceRes = 2
 
    elif content == u"오름3동":
        response_data=jsonChoiceDay
        ChoiceUrl=urlorum3
        ChoiceRes = 3
 
    elif content==u"교직원":
        response_data=jsonChoiceDay
        ChoiceUrl=urlProfess
        ChoiceRes = 4
 
    elif content==u"오늘":
        response_data=jsonChoiceTime
        ChoiceDay = time.localtime().tm_wday
 
    elif content==u"월":
        response_data=jsonChoiceTime
        ChoiceDay = 0
 
    elif content==u"화":
        response_data = jsonChoiceTime
        ChoiceDay = 1
 
    elif content==u"수":
        response_data = jsonChoiceTime
        ChoiceDay = 2
 
    elif content==u"목":
        response_data = jsonChoiceTime
        ChoiceDay = 3
 
    elif content==u"금":
        response_data = jsonChoiceTime
        ChoiceDay = 4
 
    elif content==u"토":
        response_data = jsonChoiceTime
        ChoiceDay = 5
 
    elif content==u"일":
        response_data = jsonChoiceTime
        ChoiceDay = 6
 
    elif content==u"아침":
        if(ChoiceUrl==urlorum1):  #오름1동 아침일경우 정상 출력
            response_data={
            "version""2.0",
            "template": {
                "outputs": [{"simpleText": {"text": returnMenu(ChoiceUrl,ChoiceDay)}}],
                "quickReplies": [{"label""처음으로""action""message""messageText""처음으로"},
                                 ]
                         }
            }
 
        else:   #오름1동 아침이 아닐경우 경고 메시지 출력
            response_data = {
                "version""2.0",
                "template": {
                    "outputs": [{"simpleText": {"text": Restaurant[ChoiceRes]+"은 아침이 없습니다. 다시 선택해 주세요."}}],
                    "quickReplies": [{"label""아침""action""message""messageText""아침"},
                                     {"label""점심""action""message""messageText""점심"},
                                     {"label""저녁""action""message""messageText""저녁"}, ]}
            }
 
 
    elif content == u"점심":
        if (ChoiceUrl != urlorum1):  #오름1동 점심이 아닐경우 정상출력
            response_data = {
                "version""2.0",
                "template": {
                    "outputs": [{"simpleText": {"text": returnMenu(ChoiceUrl, ChoiceDay)}}],
                    "quickReplies": [{"label""처음으로""action""message""messageText""처음으로"},
                                     ]
                }
            }
 
 
        else:  #오름1동 점심일경우 경고 메시지 출력
            response_data = {
                "version""2.0",
                "template": {
                    "outputs": [{"simpleText": {"text": Restaurant[ChoiceRes] + "은 아침이 없습니다. 다시 선택해 주세요."}}],
                    "quickReplies": [{"label""아침""action""message""messageText""아침"},
                                     {"label""점심""action""message""messageText""점심"},
                                     {"label""저녁""action""message""messageText""저녁"}, ]}
            }
 
    elif content==u"저녁":
        response_data={
        "version""2.0",
        "template": {
            "outputs": [{"simpleText": {"text": returnMenu(ChoiceUrl,ChoiceDay)}}],
            "quickReplies": [{"label""처음으로""action""message""messageText""처음으로"},]}
        }
 
 
    elif content==u"처음으로":
        response_data=jsonChoiceRes
 
    else :
        response_data = jsonChoiceRes
 
    return jsonify(response_data)
 
if __name__=="__main__":
     app.run(host="0.0.0.0", port=5000)
cs

 

결과값을 표시할 때 simpleText 와 quickReplies 를 이용했다. quickReplies를 이용한 이유는 사용자가 직접 타이핑을 하지 않고 버튼의 클릭으로 정보를 보고, 간단히 정보를 알 수 있어서 선택했다. 주변의 사용자들의 의견도 대화형 응답보다는 버튼을 이용한 대화다 편하다고 했다. 이것 외에 다른 출력 형태는 마찬가지로 카카오톡 스킬 가이드에 가면 여러가지 형태의 출력값을 알 수 있다.

 

96번째 줄에 @app.route('/message', methods=['POST']) 라는 코드가 있다. 스킬 서버에서 /message 로 json 파일을 보내기 때문에, 수신부인 스킬 코드에서도 /message 로 받아야 한다.

 

 

배포


최종적으로 배포를 해야지, 스킬을 적용한 봇이 배포가 된다.

 

이렇게 간단히 챗봇을 만들어 보았다. 카카오 공식 예제에서는 파이썬이 아닌 자바스크립트를 이용하여 만들어서 초반에는 어려움이 있었으나, 며칠 공부하고 나니 쉽게 만들어 보였다. 컴퓨터공학 비전공자도 간단히 만들 수 있었고, 조금만 더 노력해서 머신러닝을 이용한 실제 챗봇 개발고 가능할 것 같았다.

 

마지막으로 참고한 사이트와 학식봇 만들기 과정 올리며 포스팅 마칠까 한다.

 

 

참고한 사이트


https://has3ong.tistory.com/301?category=831354

 

[Flask/ChatBot] 카카오톡 챗봇 만들기 -4-

FLASK 코드 from flask import Flask, request, jsonify import sys app = Flask(__name__) @app.route('/keyboard') def Keyboard(): dataSend = { } return jsonify(dataSend) @app.route('/message', methods=[..

has3ong.tistory.com

https://vmpo.tistory.com/95?category=733430

 

카카오 오픈빌더 -4 : 스킬 QuickReplies(바로가기 응답 리스트만들기)

오늘은 카카오 오픈빌더의 "스킬"을 사용해 카카오톡에 말풍선리스트를 출력해보도록 하겠습니다. 아래와 같은 모습으로 출력을 해보도록 하겠습니다. 지난 포스팅에서 활용한 웹서버 구성을 활용해 보도록 하겠..

vmpo.tistory.com

 

 

 

학식봇 만들기 시리즈


https://tre2man.tistory.com/157

 

카카오톡 학식봇 만들기-1

카카오톡 채널 API가 2019년 12월 31부로 많은 것이 변화했습니다. 간단히 말하면, 오픈빌더 i 를 쓰지 않던 예전 API 형식의 카카오톡 봇은 사용을 할 수 없다는 얘기가 되어 버렸습니다. 채널 관리자 본인이 직..

tre2man.tistory.com

https://tre2man.tistory.com/158

 

카카오톡 학식봇 만들기-2

카카오 오픈빌더 사용하기 학식을 알려주는 카카오톡 봇을 만들기 위해 필요한 것만 간단히 설명해 보겠습니다. 먼저 봇 제네릭 메뉴를 설정해 봅시다. 봇 제네릭 메뉴는 채팅창 하단에 고정되어 있는 버튼입니다...

tre2man.tistory.com

https://tre2man.tistory.com/159

 

카카오톡 학식봇 만들기-json 파일이란?

카카오톡 학식봇을 만들기 위해 개인적으로 json에 대한 지식 및 정리를 하기 위한 포스팅입니다. json 이란? json은 데이터 저장 및 공유를 위한 개방평 표준 포맷이다. json의 구성은 "키:값" 으로 이루어져 있..

tre2man.tistory.com

 

반응형