Showing
6 changed files
with
181 additions
and
81 deletions
| 1 | -### 오픈소스sw개발 개인 프로젝트 | 1 | +README referred to [Github](https://github.com/othneildrew/Best-README-Template) |
| 2 | -## CHATBOT WITH CRAWLING | 2 | + |
| 3 | + | ||
| 4 | +<!-- PROJECT LOGO --> | ||
| 5 | +<br /> | ||
| 6 | +<p align="center"> | ||
| 7 | + <h3 align="center">SEARCH AND CHAT</h3> | ||
| 8 | + | ||
| 9 | + <p align="center"> | ||
| 10 | + 웹 페이지로 구현된 챗봇과 대화 및 '@'를 이용하여 최신 영상, 정확도 높은 영상, 소식을 카드로 제공합니다. | ||
| 11 | + "http://khuhub.khu.ac.kr/2017103084/oss-chatbot" | ||
| 12 | + <a href="http://khuhub.khu.ac.kr/2017103084/oss-chatbot/">View Demo</a> | ||
| 13 | + · | ||
| 14 | + <a href="http://khuhub.khu.ac.kr/2017103084/oss-chatbot/issues">Report Bug</a> | ||
| 15 | + · | ||
| 16 | + <a href="http://khuhub.khu.ac.kr/2017103084/oss-chatbot/merge_requests">Request Feature</a> | ||
| 17 | + </p> | ||
| 18 | +</p> | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +<!-- TABLE OF CONTENTS --> | ||
| 23 | +## Table of Contents | ||
| 24 | + | ||
| 25 | +* [About the Project](#about-the-project) | ||
| 26 | + * [Built With](#built-with) | ||
| 27 | +* [Getting Started](#getting-started) | ||
| 28 | + * [Prerequisites](#prerequisites) | ||
| 29 | + * [Installation](#installation) | ||
| 30 | +* [Usage](#usage) | ||
| 31 | +* [Roadmap](#roadmap) | ||
| 32 | +* [Contributing](#contributing) | ||
| 33 | +* [License](#license) | ||
| 34 | +* [Contact](#contact) | ||
| 35 | +* [Acknowledgements](#acknowledgements) | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +<!-- ABOUT THE PROJECT --> | ||
| 40 | +## About The Project | ||
| 41 | + | ||
| 42 | +[ | ||
| 43 | + | ||
| 44 | +카카오톡, 라인 등에서 제공하는 챗봇 어플리케이션과 같이 웹에서도 작동할 수 있는 챗봇을 만들어, 웹 사용자들과 대화할 수 있는 봇을 만들고 싶어 이 프로젝트를 진행하게 되었습니다. 간단한 대화와 더불어 좋아하는 가수, 또는 배우 등의 이름과 함께 검색어를 입력하면 봇이 대신 정보를 가져와주는 편의성을 더하였습니다. | ||
| 45 | + | ||
| 46 | + | ||
| 47 | +### Built With | ||
| 48 | + | ||
| 49 | +* [Nodejs](https://nodejs.org/en/) | ||
| 50 | +* [React](https://ko.reactjs.org/) | ||
| 51 | +* [Google Cloud API](https://cloud.google.com/apis) | ||
| 52 | +* [Dialogflow API](https://dialogflow.cloud.google.com/) | ||
| 53 | + | ||
| 54 | + | ||
| 55 | +<!-- GETTING STARTED --> | ||
| 56 | +## Getting Started | ||
| 57 | + | ||
| 58 | +로컬 컴퓨터에서 실행시킬 수 있는 방법입니다. | ||
| 59 | + | ||
| 60 | +### Prerequisites | ||
| 61 | + | ||
| 62 | +먼저, 이 프로젝트를 실행시키기 위해 필요한 요구사항입니다. | ||
| 63 | + | ||
| 64 | +* Node | ||
| 65 | +[Official Node.js Website](https://nodejs.org/)에서 Node.js를 설치합니다. | ||
| 66 | +또한 `npm` 의 설치도 필요합니다. | ||
| 67 | + | ||
| 68 | +### Installation | ||
| 69 | + | ||
| 70 | +1. 이 리포지토리를 Clone 합니다. | ||
| 71 | +```sh | ||
| 72 | +git clone http://khuhub.khu.ac.kr/2017103084/oss-chatbot/ | ||
| 73 | +``` | ||
| 74 | +2. root 폴더와 client 폴더에서 아래 명령을 실행합니다. | ||
| 75 | +```sh | ||
| 76 | +npm install | ||
| 77 | +``` | ||
| 78 | +3. [Google Developers](https://console.developers.google.com/project)에서 프로젝트를 생성한 뒤, API 키를 발급 받습니다. 이 때, 프로젝트 명(ID)과 API 키의 json 파일이 필요합니다. | ||
| 79 | + | ||
| 80 | +4. [Dialogflow](https://dialogflow.cloud.google.com/)에서 에이전트를 생성합니다. 이 때, GOOGLE PROJECT 탭의 Project ID는 앞서 (3)에서 생성한 프로젝트의 ID를 선택합니다. | ||
| 81 | + | ||
| 82 | +5. root 폴더에 .env 파일을 생성한 뒤, 아래 내용을 채워 넣습니다. | ||
| 83 | +``` | ||
| 84 | +googleProjectID = (3)에서 생성한 구글 프로젝트 ID | ||
| 85 | +dialogFlowSessionID = bot-session #원하는 것으로 입력. | ||
| 86 | +dialogFlowSessionLanguageCode = Dialogflow 에이전트 생성 시 설정한 언어 코드 (ex. 한글일 경우에는 "ko" 입니다.) | ||
| 87 | +googleClientEmail = 구글 프로젝트 생성 시 제공되는 이메일 (ex. [프로젝트 명]@[프로젝트명 2].iam.gserviceaccount.com) | ||
| 88 | +``` | ||
| 89 | + | ||
| 90 | +6. **중요** 로컬에서 실행하는 경우에는 root의 package.json 중 "@google-cloud/storage": "^5.0.1" 를 지웁니다. 이 패키지는 herokuapp에서 GOOGLE_APPLICATION_CREDENTIALS를 활용하기 위해 설치되어있는 패키지입니다. | ||
| 91 | + | ||
| 92 | +7. 모두 완료 되었다면, 아래 명령어를 입력하여 클라이언트와 서버를 모두 실행시킬 수 있습니다. | ||
| 93 | +``` | ||
| 94 | +npm run dev | ||
| 95 | +``` | ||
| 96 | + | ||
| 97 | + | ||
| 98 | +<!-- USAGE EXAMPLES --> | ||
| 99 | +## Usage | ||
| 100 | + | ||
| 101 | +해당 프로젝트의 실제 동작 화면은 [SEARCH-AND-CHAT](https://search-and-chat.herokuapp.com/)에서 확인하실 수 있습니다. | ||
| 102 | +React 의 특성 상 뒤로가기 및 Reload 시 오류가 발생할 수 있습니다. 이 때는 아래 URL로 재접속하시면 원활히 사용하실 수 있습니다. 또한 heroku 로 빌드되어 사용이 없는 경우에는 첫 접속 시 로딩 시간이 걸릴 수 있습니다. 조금만 기다려주시면 프로젝트가 실행될 것입니다 😊 | ||
| 103 | + | ||
| 104 | +* URL : <https://search-and-chat.herokuapp.com/> | ||
| 105 | + | ||
| 106 | + | ||
| 107 | +<!-- CONTRIBUTING --> | ||
| 108 | +## Contributing | ||
| 109 | + | ||
| 110 | +이 프로젝트를 더욱 발전시키고 싶으신 분들은 아래와 같은 절차를 따라주세요! | ||
| 111 | + | ||
| 112 | +1. Fork the Project | ||
| 113 | +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) | ||
| 114 | +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) | ||
| 115 | +4. Push to the Branch (`git push origin feature/AmazingFeature`) | ||
| 116 | +5. Open a Pull Request | ||
| 117 | + | ||
| 118 | +<!-- CONTACT --> | ||
| 119 | +## Contact | ||
| 120 | + | ||
| 121 | +프로젝트에 문제가 있거나 궁금하신 사항은 아래 메일로 연락주세요. | ||
| 122 | +* Email : <mathmjseo@khu.ac.kr> | ||
| 3 | 123 | ||
| 4 | --readme 수정하기 | ||
| 5 | --로그인 시 페이지렌더링 안됨 ..... 수정하기 | ... | ... |
| ... | @@ -8,18 +8,13 @@ import Card from "./Sections/Card"; | ... | @@ -8,18 +8,13 @@ import Card from "./Sections/Card"; |
| 8 | import CheckString from './Check'; | 8 | import CheckString from './Check'; |
| 9 | import { text } from 'body-parser'; | 9 | import { text } from 'body-parser'; |
| 10 | 10 | ||
| 11 | -let userKeyword = ""; | 11 | +function Chatbot(props) { |
| 12 | -let userName = "유저"; | 12 | + |
| 13 | -let autoSearch = 0; | 13 | + console.log("실행") |
| 14 | -if(sessionStorage.length){ | 14 | + var userName = props.userName; |
| 15 | - userKeyword = sessionStorage.getItem("Now_userKeyword"); | 15 | + var userKeyword = props.userKeyword; |
| 16 | - userName = sessionStorage.getItem("Now_userName"); | 16 | + var autoSearch = props.autoSearch; |
| 17 | - autoSearch = 1; | 17 | + |
| 18 | - sessionStorage.clear(); | ||
| 19 | -} | ||
| 20 | - | ||
| 21 | - | ||
| 22 | -function Chatbot() { | ||
| 23 | console.log("이름",userName); | 18 | console.log("이름",userName); |
| 24 | console.log("키워드",userKeyword); | 19 | console.log("키워드",userKeyword); |
| 25 | 20 | ||
| ... | @@ -32,6 +27,22 @@ function Chatbot() { | ... | @@ -32,6 +27,22 @@ function Chatbot() { |
| 32 | 27 | ||
| 33 | }, []) | 28 | }, []) |
| 34 | 29 | ||
| 30 | + if(autoSearch){ | ||
| 31 | + useEffect(() => { | ||
| 32 | + console.log("I am in autoSearch!!"); | ||
| 33 | + setTimeout(function(){ | ||
| 34 | + eventQuery('008_AutoSearch'); | ||
| 35 | + }, 500); | ||
| 36 | + | ||
| 37 | + setTimeout(function(){ | ||
| 38 | + textQuery(`@${userKeyword}_최신`); | ||
| 39 | + textQuery(`@${userKeyword}_정확도`); | ||
| 40 | + textQuery(`@${userKeyword}_소식`); | ||
| 41 | + }, 1000); | ||
| 42 | + | ||
| 43 | + }, []) | ||
| 44 | + } | ||
| 45 | + | ||
| 35 | const textQuery = async (text) => { | 46 | const textQuery = async (text) => { |
| 36 | // First Need to take care of the message I sent | 47 | // First Need to take care of the message I sent |
| 37 | let conversation = { | 48 | let conversation = { |
| ... | @@ -147,20 +158,14 @@ function Chatbot() { | ... | @@ -147,20 +158,14 @@ function Chatbot() { |
| 147 | 158 | ||
| 148 | } | 159 | } |
| 149 | 160 | ||
| 150 | - if(autoSearch === 1){ | 161 | + // if(autoSearch === 1){ |
| 151 | - setTimeout(function(){ | ||
| 152 | - eventQuery('008_AutoSearch'); | ||
| 153 | - }, 500); | ||
| 154 | 162 | ||
| 155 | - setTimeout(function(){ | 163 | + |
| 156 | - textQuery(`@${userKeyword}_최신`); | 164 | + |
| 157 | - textQuery(`@${userKeyword}_정확도`); | 165 | + |
| 158 | - textQuery(`@${userKeyword}_소식`); | 166 | + // autoSearch = 0; |
| 159 | - }, 1000); | 167 | + // console.log("I am in autoSearch!!"); |
| 160 | - | 168 | + // } |
| 161 | - autoSearch = 0; | ||
| 162 | - console.log("I am in autoSearch!!"); | ||
| 163 | - } | ||
| 164 | 169 | ||
| 165 | 170 | ||
| 166 | const keyPressHanlder = (e) => { | 171 | const keyPressHanlder = (e) => { | ... | ... |
| ... | @@ -3,16 +3,29 @@ import { Typography, Icon } from 'antd'; | ... | @@ -3,16 +3,29 @@ import { Typography, Icon } from 'antd'; |
| 3 | import Chatbot from '../Chatbot/Chatbot'; | 3 | import Chatbot from '../Chatbot/Chatbot'; |
| 4 | import { withRouter } from "react-router-dom"; | 4 | import { withRouter } from "react-router-dom"; |
| 5 | const { Title } = Typography; | 5 | const { Title } = Typography; |
| 6 | - | 6 | +let userKeyword = ""; |
| 7 | +let userName = "유저"; | ||
| 8 | +let autoSearch = 0; | ||
| 9 | +console.log("챗페이지 밖"); | ||
| 7 | 10 | ||
| 8 | function chatpage() { | 11 | function chatpage() { |
| 12 | + console.log("챗페이지 안") | ||
| 13 | + if(sessionStorage.length === 2){ | ||
| 14 | + console.log("세션스토리지 안") | ||
| 15 | + userKeyword = sessionStorage.getItem("Now_userKeyword"); | ||
| 16 | + userName = sessionStorage.getItem("Now_userName"); | ||
| 17 | + sessionStorage.clear(); | ||
| 18 | + | ||
| 19 | + autoSearch = 1; | ||
| 20 | + } | ||
| 21 | + | ||
| 9 | return ( | 22 | return ( |
| 10 | <div> | 23 | <div> |
| 11 | <div style={{ display: 'flex', justifyContent: 'center', marginTop: '1rem' }}> | 24 | <div style={{ display: 'flex', justifyContent: 'center', marginTop: '1rem' }}> |
| 12 | <Title level={2} >CHATBOT <Icon type="robot" /></Title> | 25 | <Title level={2} >CHATBOT <Icon type="robot" /></Title> |
| 13 | </div> | 26 | </div> |
| 14 | <div style={{ display: 'flex', justifyContent: 'center' }}> | 27 | <div style={{ display: 'flex', justifyContent: 'center' }}> |
| 15 | - <Chatbot /> | 28 | + <Chatbot userName = {userName} userKeyword = {userKeyword} autoSearch = {autoSearch}/> |
| 16 | </div> | 29 | </div> |
| 17 | </div> | 30 | </div> |
| 18 | ) | 31 | ) | ... | ... |
| ... | @@ -6,10 +6,13 @@ import Axios from 'axios'; | ... | @@ -6,10 +6,13 @@ import Axios from 'axios'; |
| 6 | const { Title } = Typography; | 6 | const { Title } = Typography; |
| 7 | 7 | ||
| 8 | console.log("start"); | 8 | console.log("start"); |
| 9 | +let url = "/"; | ||
| 9 | 10 | ||
| 10 | -const userInfo = async (info) => { | 11 | +async function userInfo(){ |
| 11 | const email = document.getElementById('email').value; | 12 | const email = document.getElementById('email').value; |
| 12 | const pw = document.getElementById('password').value; | 13 | const pw = document.getElementById('password').value; |
| 14 | + | ||
| 15 | + | ||
| 13 | if(email && pw){ | 16 | if(email && pw){ |
| 14 | const userVariables = { | 17 | const userVariables = { |
| 15 | email, | 18 | email, |
| ... | @@ -18,6 +21,7 @@ const userInfo = async (info) => { | ... | @@ -18,6 +21,7 @@ const userInfo = async (info) => { |
| 18 | 21 | ||
| 19 | const response = await Axios.post('/api/login/userInfo', userVariables); | 22 | const response = await Axios.post('/api/login/userInfo', userVariables); |
| 20 | if(response.data !== 'FAIL'){ | 23 | if(response.data !== 'FAIL'){ |
| 24 | + url = "/chat"; | ||
| 21 | // loginForm.action = `/chat?${response.data}`; | 25 | // loginForm.action = `/chat?${response.data}`; |
| 22 | // loginForm.submit(); | 26 | // loginForm.submit(); |
| 23 | var keyword = response.data.keyword; | 27 | var keyword = response.data.keyword; |
| ... | @@ -25,14 +29,16 @@ const userInfo = async (info) => { | ... | @@ -25,14 +29,16 @@ const userInfo = async (info) => { |
| 25 | 29 | ||
| 26 | sessionStorage.setItem("Now_userKeyword", keyword); | 30 | sessionStorage.setItem("Now_userKeyword", keyword); |
| 27 | sessionStorage.setItem("Now_userName", name); | 31 | sessionStorage.setItem("Now_userName", name); |
| 28 | - window.history.replaceState('login','','/chat'); | 32 | + // window.history.replaceState('login','','/chat'); |
| 29 | - window.history.go(); | 33 | + // window.history.go(); |
| 30 | // window.location.href = "/chat"; | 34 | // window.location.href = "/chat"; |
| 31 | } else{ | 35 | } else{ |
| 36 | + url = "/"; | ||
| 32 | alert("입력하신 정보와 일치하는 회원이 존재하지 않습니다 😥"); | 37 | alert("입력하신 정보와 일치하는 회원이 존재하지 않습니다 😥"); |
| 33 | } | 38 | } |
| 34 | } | 39 | } |
| 35 | else{ | 40 | else{ |
| 41 | + url = "/"; | ||
| 36 | alert("이메일과 패스워드를 입력해주세요!"); | 42 | alert("이메일과 패스워드를 입력해주세요!"); |
| 37 | } | 43 | } |
| 38 | } | 44 | } |
| ... | @@ -67,9 +73,11 @@ function loginpage() { | ... | @@ -67,9 +73,11 @@ function loginpage() { |
| 67 | 73 | ||
| 68 | <Form.Item> | 74 | <Form.Item> |
| 69 | <div> | 75 | <div> |
| 70 | - <Button type="primary" className="login-form-button" style={{ minWidth: '100%' }} onClick={userInfo}> | 76 | + <Link to= {`${url}`} onClick= {userInfo} name = "loginLink"> |
| 71 | - Log in | 77 | + <Button type="primary" className="login-form-button" style={{ minWidth: '100%' }}> |
| 72 | - </Button> | 78 | + Log in |
| 79 | + </Button> | ||
| 80 | + </Link> | ||
| 73 | </div> | 81 | </div> |
| 74 | <Link to ="/register">가입하기</Link> Or <Link to = "/chat">비회원으로 사용하기</Link> | 82 | <Link to ="/register">가입하기</Link> Or <Link to = "/chat">비회원으로 사용하기</Link> |
| 75 | </Form.Item> | 83 | </Form.Item> | ... | ... |
note.md
deleted
100644 → 0
| 1 | -1. dialogflow로 기본 기능 구현 | ||
| 2 | -2. React로 프론트엔드 구현 | ||
| 3 | -3. routing으로 @가수명 입력하면 해당 라우팅(크롤링) 함수로 들어가게 구현 | ||
| 4 | -4. 크롤링 구현 (네이버TV 최신 영상 3개 json으로 가져오기) | ||
| 5 | -5. json파일 카드로 넘겨주기 | ||
| 6 | - | ||
| 7 | - | ||
| 8 | ---- | ||
| 9 | -검색결과가 없을 때 -> 다시 dialogflow 라우터로 넘겨줘서 무슨 말인지 이해하지 못했어요 | ||
| 10 | -관련 영상이 3가지 미만일 때 -> 없다고 판단함. | ||
| 11 | ---- | ||
| 12 | - | ||
| 13 | - | ||
| 14 | -node 서버: localhost: 5000 | ||
| 15 | -프론트엔드/ localhost: 3000 | ||
| 16 | - | ||
| 17 | ----- 정리 | ||
| 18 | - | ||
| 19 | -리덕스 참고: https://velopert.com/3528 | ||
| 20 | - | ||
| 21 | -dialogflow | ||
| 22 | -화자의 의도 intent ,사용자가 말하는 것과 dialogflow가 수행해야할 작업간의 매핑을 나타내는 것. | ||
| 23 | -fallback intent : 사용자의 대화가 어떤 intent 와도 매칭되지 않을 때 선택되어지는 intent | ||
| 24 | - | ||
| 25 | -화자의 속성 entity | ||
| 26 | -ex. 내일 오후 2시 되나요? 에서 '내일', '오후 2시'를 파라미터로 뽑아내는 것. | ||
| 27 | - | ||
| 28 | -문맥 context | ||
| 29 | -내일 오후 2시 되나요? 에서 무엇을 위한 내일 오후 2시인가를 파악하기 위해서 그 전에 대화가 되었던 '수리'라는 것을 기억하는 것을 의미함 | ||
| 30 | - | ||
| 31 | -uuid 제대로 이해하고 다시 작성하기 | ||
| 32 | - | ||
| 33 | - | ||
| 34 | - | ||
| 35 | - | ||
| 36 | ------ | ||
| 37 | -001 - welcome | ||
| 38 | -002 - 인사 | ||
| 39 | -003 - @'가수명' | ||
| 40 | --> 해당 가수명으로 크롤링을 했을 때 null이 3개이상 나오면 가수명이 없다고 판단함. | ||
| 41 | --> 만약 가수가 아니더라도 크롤링 시 해당되는 내용이 3개 이상 나오면 해당 내용을 리턴함. | ||
| 42 | ------ | ||
| 43 | - | ||
| 44 | - |
projectScreenShot.PNG
0 → 100644
54 KB
-
Please register or login to post a comment