장소원

chatbot + search + cfr and connect

1 +var express = require('express');
2 +var client_id = 'v3M4wjolGLkrvNA3GUIW';
3 +var client_secret = 'fKF6vjkWhE';
4 +const request = require('request');
5 +const TARGET_URL = 'https://api.line.me/v2/bot/message/reply'
6 +const TOKEN = 'hMnfhBQc8nadyn5Ow6aejAVDUoUEp9N8YxOFxfOB2V83TOf0vjquT4cC8ll4Ccq4hkWJ8xHij53FzjMteqLLuUL6bZs+ZONI+f5aawIulRg4Y4FFBGp1O03awvgxGn503iyI7+5iQCEi80Kus6cRZQdB04t89/1O/w1cDnyilFU='
7 +const SECRET = '270103fd4cbd81695ceb6d0ed7f85f4b'
8 +const fs = require('fs');
9 +const path = require('path');
10 +const HTTPS = require('https');
11 +const domain = "www.osschatbot.tk"
12 +const sslport = 23023;
13 +const line = require('@line/bot-sdk');
14 +
15 +const bodyParser = require('body-parser');
16 +const { assert } = require('console');
17 +var app = express();
18 +app.use(bodyParser.json());
19 +
20 +var usingMessage = ''
21 +var content_id = ''
22 +var imgDownloaded = false;
23 +var downloadedImg = ''
24 +
25 +//search
26 +var search_client_id = 'cnS9zzj0OZ3xPgHqtaLJ';
27 +var search_client_secret = 'oQGaxdr7aq';
28 +
29 +//data parsing
30 +var data='';
31 +var title='';
32 +var link='';
33 +var category='';
34 +var address='';
35 +var roadAddress='';
36 +
37 +
38 +app.post('/hook', function (req, res) {
39 +
40 + var eventObj = req.body.events[0];
41 + var text = eventObj.message.text;
42 +
43 + // request log
44 + if (!(eventObj.message.type == 'image')) {
45 + console.log('======================', new Date() ,'======================');
46 + console.log('[request]', req.body);
47 + console.log('[request source] ', eventObj.source);
48 + console.log('[request message]', eventObj.message);
49 + }
50 +
51 + if (eventObj.message.type == 'location') {
52 +
53 + //위치 받아서 맛집 추천해주는 함수
54 + var chatbotaddress=eventObj.message;
55 + var chatbotdata1=chatbotaddress.address;
56 + var chatbotdata2=chatbotdata1.split(' ');
57 + var place=chatbotdata2[0];
58 +
59 + console.log(place);
60 + var menu='';
61 + var query= place + ' ' + menu + ' 맛집'; //검색 원하는 문자열
62 + RecommendationResult(eventObj.replyToken, query);
63 +
64 + res.sendStatus(200);
65 + } else if (text == 'Cfr:Yes') {
66 + QuickReplyCfrYes(eventObj.replyToken);
67 + res.sendStatus(200);
68 + } else if (text == 'Cfr:No') {
69 + QuickReplyCfrNo(eventObj.replyToken);
70 + res.sendStatus(200);
71 + } else if (text == '랜덤 추천' || text == '위치 기반 추천') {
72 + if (text == '랜덤 추천') {
73 +
74 + //랜덤으로 맛집 추천해주는 함수
75 + var query = '맛집';
76 + RecommendationResult(eventObj.replyToken, query);
77 +
78 + res.sendStatus(200);
79 + }
80 + else {
81 + SendingLocation(eventObj.replyToken);
82 + res.sendStatus(200);
83 + }
84 + } else if (eventObj.message.type == 'image') {
85 + content_id = eventObj.message.id;
86 + const downloadPath = path.join(__dirname, 'sample.jpg');
87 + downloadContent(content_id, downloadPath);
88 + //downloadedImg = content_id;
89 + //imgDownloaded = true;
90 + Checking(eventObj.replyToken);
91 + res.sendStatus(200);
92 + } else if (text == '계속 진행') {
93 + //사진으로 얼굴 인식해주는 함수
94 + imgtodata('sample.jpg');
95 + SendingLocation(eventObj.replyToken);
96 + res.sendStatus(200);
97 + }
98 + else {
99 + usingMessage = text;
100 + initReply(eventObj.replyToken);
101 + res.sendStatus(200);
102 + }
103 +
104 +});
105 +
106 +const quickReplyLocation = {
107 + "items": [
108 + {
109 + "type": "action",
110 + "action": {
111 + "type": "location",
112 + "label": "위치 입력"
113 + }
114 + }
115 + ]
116 +};
117 +
118 +const quickReplyCfrYes = {
119 + "items": [
120 + {
121 + "type": "action",
122 + "action": {
123 + "type": "cameraRoll",
124 + "label": "사진 가져오기"
125 + }
126 + },
127 + {
128 + "type": "action",
129 + "action": {
130 + "type": "camera",
131 + "label": "사진 찍기"
132 + }
133 + },
134 + ]
135 + };
136 +
137 +const quickReplyCfrNo = {
138 + items: [
139 + /*
140 + {
141 + "type": "action",
142 + "action": {
143 + "type": "message",
144 + "label": "weather",
145 + "text": "날씨"
146 + }
147 + },
148 + {
149 + "type": "action",
150 + "action": {
151 + "type": "message",
152 + "label": "menu",
153 + "text": "메뉴"
154 + }
155 + },
156 + */
157 + {
158 + "type": "action",
159 + "action": {
160 + "type": "message",
161 + "label": "random",
162 + "text": "랜덤 추천"
163 + }
164 + },
165 + {
166 + "type": "action",
167 + "action": {
168 + "type": "message",
169 + "label": "location",
170 + "text": "위치 기반 추천"
171 + }
172 + }
173 + ]
174 + };
175 +
176 +//기본 reply
177 +function initReply (replyToken) {
178 + request.post(
179 + {
180 + url: TARGET_URL,
181 + headers: {
182 + 'Authorization': `Bearer ${TOKEN}`
183 + },
184 + json: {
185 + "replyToken":replyToken,
186 + "messages":[
187 + {
188 + "type": "sticker",
189 + "packageId": "11537",
190 + "stickerId": "52002738"
191 + },
192 + {
193 + "type": "text",
194 + "text": "안녕하세요.\nCFR을 이용한 얼굴인식 맛집 추천봇입니다."
195 + },
196 + {
197 + "type": "template",
198 + "altText": "얼굴 인식을 위해 사진을 가져오시겠습니까?",
199 + "template": {
200 + "type": "confirm",
201 + "text": "얼굴 인식을 위해 사진을 가져오시겠습니까?",
202 + "actions": [
203 + {
204 + "type": "message",
205 + "label": "Cfr:Yes",
206 + "text": "Cfr:Yes"
207 + },
208 + {
209 + "type": "message",
210 + "label": "Cfr:No",
211 + "text": "Cfr:No"
212 + }
213 + ]
214 + }
215 + }
216 + ]
217 + }
218 + },(error, response, body) => {
219 + console.log(body)
220 + });
221 +}
222 +
223 +//위치 입력 reply
224 +function SendingLocation(replyToken) {
225 + request.post(
226 + {
227 + url: TARGET_URL,
228 + headers: {
229 + 'Authorization': `Bearer ${TOKEN}`
230 + },
231 + json: {
232 + "replyToken": replyToken,
233 + "messages": [
234 + {
235 + "type": "text",
236 + "text": "위치를 입력해주세요.",
237 + "quickReply": quickReplyLocation
238 + }
239 + ]
240 + },
241 + body: request
242 + },(error, response, body) => {
243 + console.log(body)
244 + });
245 +}
246 +
247 +//cfr 이용할 때 reply
248 +function QuickReplyCfrYes(replyToken) {
249 + request.post(
250 + {
251 + url: TARGET_URL,
252 + headers: {
253 + 'Authorization': `Bearer ${TOKEN}`
254 + },
255 + json: {
256 + "replyToken": replyToken,
257 + "messages": [
258 + {
259 + "type": "text",
260 + "text": "사진을 입력해주세요.",
261 + "quickReply": quickReplyCfrYes
262 + }
263 + ]
264 + }
265 + },(error, response, body) => {
266 + console.log(body)
267 + });
268 +}
269 +
270 +//cfr 이용하지 않을 때 reply
271 +function QuickReplyCfrNo (replyToken) {
272 + request.post(
273 + {
274 + url: TARGET_URL,
275 + headers: {
276 + 'Authorization': `Bearer ${TOKEN}`
277 + },
278 + json: {
279 + "replyToken": replyToken,
280 + "messages": [
281 + {
282 + "type": "text",
283 + "text": "랜덤 추천 옵션을 선택해주세요.",
284 + "quickReply": quickReplyCfrNo
285 + }
286 + ]
287 + }
288 + },(error, response, body) => {
289 + console.log(body)
290 + });
291 +}
292 +
293 +function Checking (replyToken) {
294 + request.post(
295 + {
296 + url: TARGET_URL,
297 + headers: {
298 + 'Authorization': `Bearer ${TOKEN}`
299 + },
300 + json: {
301 + "replyToken": replyToken,
302 + "messages": [
303 + {
304 + "type": "text",
305 + "label": "계속 진행",
306 + "text": "계속 진행하시려면 '계속 진행'을 입력해주세요."
307 + },
308 + ],
309 + }
310 + },(error, response, body) => {
311 + console.log(body)
312 + });
313 +}
314 +
315 +
316 +//추천 결과 + Search
317 +function RecommendationResult(replyToken, query) {
318 +
319 + console.log(query);
320 + var display = '1'; //검색 결과 출력 건수. 최대 5개
321 + var start = '1'; //검색 시작 위치. 1만 가능
322 + var sort = 'comment'; //정렬 옵션 (random : 유사도순, comment : 카페/블로그 리뷰 개수 순)
323 +
324 + var api_url = 'https://openapi.naver.com/v1/search/local?query=' + encodeURI(query) + '&display=' + encodeURI(display) + '&start=' + encodeURI(start) + '&sort=' + encodeURI(sort);
325 + var options = {
326 + url: api_url,
327 + headers: { 'X-Naver-Client-Id': search_client_id, 'X-Naver-Client-Secret': search_client_secret }
328 + };
329 + request.get(options, function (error, response, body) {
330 + if (!error && response.statusCode == 200) {
331 + //response.writeHead(200, {'Content-Type': 'text/json;charset=utf-8'});
332 + console.log(body);
333 +
334 + //데이터 parsing
335 + data = JSON.parse(body);
336 + title = data.items[0].title;
337 + console.log(title);
338 +
339 + link = data.items[0].link;
340 + console.log(link);
341 +
342 + category = data.items[0].category;
343 + console.log(category);
344 +
345 + address = data.items[0].address;
346 + console.log(address);
347 +
348 + roadAddress = data.items[0].roadAddress;
349 + console.log(roadAddress);
350 +
351 + request.post(
352 + {
353 + url: TARGET_URL,
354 + headers: {
355 + 'Authorization': `Bearer ${TOKEN}`
356 + },
357 + json: {
358 + "replyToken": replyToken,
359 + "messages": [
360 + /*{
361 + "type": "imagemap",
362 + // 이미지 불러올 수 없습니다 뜸
363 + "baseUrl": "https://www.flaticon.com/free-icon/food-store_2934069?term=restaurant&page=1&position=7&related_item_id=2934069",
364 + "altText": "이미지를 누르시면 해당 가게로 이동합니다.",
365 + "baseSize": {
366 + "width": 1040,
367 + "height": 1040
368 + },
369 + "actions": [
370 + {
371 + "type": "uri",
372 + "linkUri": link, // 가게 링크
373 + "area": {
374 + "x": 0,
375 + "y": 0,
376 + "width": 1040,
377 + "height": 1040
378 + }
379 + }
380 + ]
381 + },*/
382 + {
383 + "type": "text",
384 + "text": "맛집 이름: " + title
385 + },
386 + {
387 + "type":"text",
388 + "text": "맛집 주소: " + address
389 + }
390 + /*{
391 + "type":"location",
392 + "title":"맛집 주소",
393 + "address":address
394 + }*/
395 + ]
396 + }
397 + }, (error, response, body) => {
398 + console.log(body)
399 + });
400 +
401 + } else {
402 + res.status(response.statusCode).end();
403 + console.log('error = ' + response.statusCode);
404 + }
405 + })
406 +
407 +}
408 +
409 +imgtodata = function(dir){
410 + var api_url = 'https://openapi.naver.com/v1/vision/face'; // 얼굴 감지
411 +
412 + var _formData = {
413 + image:'image',
414 + image: fs.createReadStream(path.join(__dirname, dir)) // FILE 이름
415 + };
416 +
417 + request.post(
418 + { url:api_url,
419 + formData:_formData,
420 + headers: {'X-Naver-Client-Id':client_id,
421 + 'X-Naver-Client-Secret': client_secret}
422 + }, (err,response,body) =>{
423 + console.log(response.statusCode); // 200
424 + //console.log(response.headers['content-type'])
425 + console.log(body);
426 + var cfrdata=JSON.parse(body);
427 +
428 + var cfrgender=cfrdata.faces[0].gender.value; //CFR의 성별 데이터 (json)
429 + var cfremotion=cfrdata.faces[0].emotion.value; //CFR의 감정 데이터 (json)
430 +
431 + var gender = cfrgender; //사용자 성별
432 + var emotion = cfremotion; //사용자 감정
433 +
434 +
435 + console.log(gender);
436 + console.log(emotion);
437 +
438 + if(gender=='male'){
439 + if(emotion=='angry'){
440 + menu='술';
441 + }
442 + else if(emotion=='disgust'){
443 + menu='야식';
444 + }
445 + else if(emotion=='fear'){
446 + menu='한식';
447 + }
448 + else if(emotion=='laugh'){
449 + menu='치킨';
450 + }
451 + else if(emotion=='neutral'){
452 + menu='양식';
453 + }
454 + else if(emotion=='sad'){
455 + menu='중식';
456 + }
457 + else if(emotion=='surprise'){
458 + menu='일식';
459 + }
460 + else if(emotion=='smile'){
461 + menu='고기';
462 + }
463 + else if(emotion=='talking'){
464 + menu='술';
465 + }
466 + else{
467 + menu='';
468 + }
469 + }
470 + else if(gender=='female'){
471 + if(emotion=='angry'){
472 + menu='고기';
473 + }
474 + else if(emotion=='disgust'){
475 + menu='디저트';
476 + }
477 + else if(emotion=='fear'){
478 + menu='한식';
479 + }
480 + else if(emotion=='laugh'){
481 + menu='일식';
482 + }
483 + else if(emotion=='neutral'){
484 + menu='중식';
485 + }
486 + else if(emotion=='sad'){
487 + menu='야식';
488 + }
489 + else if(emotion=='surprise'){
490 + menu='중식';
491 + }
492 + else if(emotion=='smile'){
493 + menu='치킨';
494 + }
495 + else if(emotion=='talking'){
496 + menu='카페';
497 + }
498 + else{
499 + menu='';
500 + }
501 + }
502 + else{
503 + menu='';
504 + }
505 +
506 + console.log(menu);
507 +
508 +
509 + //return {gender:gender,emotion:emotion};
510 + });
511 + }
512 +
513 +//사용자가 보낸 사진 저장
514 +const config = ({
515 + channelAccessToken: `${TOKEN}`,
516 + channelSecret: `${SECRET}`,
517 +});
518 +
519 +const client = new line.Client(config);
520 +function downloadContent(messageId, downloadPath) {
521 + return client.getMessageContent(messageId)
522 + .then((stream) => new Promise((resolve, reject) => {
523 + const writable = fs.createWriteStream(downloadPath);
524 + stream.pipe(writable);
525 + stream.on('end', () => resolve(downloadPath));
526 + stream.on('error', reject);
527 + }));
528 +}
529 +
530 +
531 +try {
532 + const option = {
533 + ca: fs.readFileSync('/etc/letsencrypt/live/' + domain +'/fullchain.pem'),
534 + key: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain +'/privkey.pem'), 'utf8').toString(),
535 + cert: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain +'/cert.pem'), 'utf8').toString(),
536 + };
537 + if (usingMessage == '') {
538 + HTTPS.createServer(option, app).listen(sslport, () => {
539 + console.log(`[HTTPS] Server is started on port ${sslport}`);
540 + });
541 + }
542 + } catch (error) {
543 + console.log('[HTTPS] HTTPS 오류가 발생하였습니다. HTTPS 서버는 실행되지 않습니다.');
544 + console.log(error);
545 +}
...\ No newline at end of file ...\ No newline at end of file