아래 포스트는 SSAFY 스타트 캠프의 수업내용을 정리한 글입니다.
텔레그램 키 재발급하기
/revoke -> 봇 선택해서 토큰없애기
/token -> 다시 재발급하기
환경변수
code .bashrc
(.은 숨김파일을 만들 때 쓰이는것임)
-> 리눅스 커맨드를 통해서 환경변수를 설정할 것임.
export TELEGRAM_TOKEN=#텔레그램 초기설정으로부터 받은 토큰
export NAME="Dong+Chan"
alias d5="cd ~/chatbot"
(환경변수는 ALL UPPER CASE로 하는게 관례이다.)
-> source .bashrc : 환경설정을 다시 갱신해줌.
-> cat .bashrc : 안에 들어간 내용을 출력해줌.
-> 파일 있는지 확인하려면 ls -al | grep bashrc
alias는 별칭을 붙여줌 : d5라고하기만 하면 cd ~/chatbot가 실행이 되는것임.
(예를 들어 d5 & jupyter notebook 하면 자동으로 주피터나옴.)
git 환경변수 확인
cat .gitconfig
echo $TELEGRAM_TOKEN
echo는 쉘에서는 print와 다름없음.
환경변수는 보통 $키를 통해서 확인함.
파이썬을 통해서도 환경변수 확인 가능함
import os
print(os.getenv('NAME'))
print(os.getenv('TELEGRAM_TOKEN'))
그외 꿀팁들
requests.get(url).text() 말고
requests.get(url).json() 으로 한다면 자동으로 딕셔너리로 변환되어서 나옴!
vscode : ctrl + shift + p -> select default shell -> git bash 를 vscode에서 사용가능!
UC 버클리 교수님의 말 : 컴퓨터에서 제일 중요한 것은 추상화이다!
이 세상을 어떻게 간단하게 컴퓨터의 언어로 이해할 수 있게끔 요약할 수 있는가???
한국에는 ‘엔트리’ : 스크래치 비슷한 툴이 있음.
앞으로 배울 쟝고라는 툴은 추상화가 엄청나게 구성되어있음!!
하나의 함수는 하나의 기능만 쓰도록 작성하라!
함수가 얽히고 얽히는 구조는 크게 좋지 않다
추상화를 잘 따르기 위해서는 모듈을 기능단위로 분류하는게 좋다.
해본 것 : 티몬 사이트에서 [오늘의 추천 상품] 중 가장 잘 팔리는 5개를 뽑아주는 코드
import os
import requests
from pprint import pprint as pp
import json
from bs4 import BeautifulSoup
import re
from selenium import webdriver
import time
# step1
def chat_url(token,method="sendMessage",chat_id=None,text=None):
if method == "getUpdates":
return f'https://api.telegram.org/bot{token}/{method}'
else:
return f'https://api.telegram.org/bot{token}/{method}?chat_id={my_id}&text={text}'
# step2
token = os.getenv('TELEGRAM_TOKEN')
method = "getUpdates"
url = chat_url(token,method)
res = requests.get(url)
res_dict = res.json()
my_id = res_dict['result'][0]['message']['chat']['id']
# webhook을 달게되면 getUpdates를 쓰지 못함
method = "sendMessage"
# step3
def crawl_hot_items():
browser = webdriver.Chrome('chromedriver.exe')
timon_url = 'http://www.ticketmonster.co.kr/home'
browser.get(timon_url)
page_source = browser.page_source
soup = BeautifulSoup(page_source,'html.parser')
titles = soup.select('.title_name')[:24]
prices = soup.select('.price_area > .price > .price')[:24]
buy_counts = soup.select('.buy_count')[:24]
links = soup.select('.item > .anchor')[:24]
whole_tags = [titles,prices,buy_counts,links]
whole_items = [[titles[x].text,prices[x].text,buy_counts[x].text,links[x]['href']]for x in range(len(titles))]
for x in range(len(whole_items)):
whole_items[x][0] = re.sub('[/&?]',' ',str(whole_items[x][0]))
# 실제 url에서 쓰이는 특수문자들을 제거해야 손상없이 전달될 수 있음.
whole_items[x][1] = ''.join(re.findall('[0-9]',str(whole_items[x][1])))
whole_items[x][2] = ''.join(re.findall('[0-9]',str(whole_items[x][2])))
hot_items = sorted(whole_items, key=lambda whole_items : int(whole_items[2]),reverse=True)[:5]
browser.close()
return hot_items
hot_items = crawl_hot_items()
print(*hot_items, sep='\n')
def item_to_string(item):
[title,price,count,link] = item
return title + " " + price + "원 " + count +"개 판매 " + link
for item in hot_items:
url = chat_url(token,chat_id=my_id,text=item_to_string(item))
requests.get(url)
네이버 얼굴인식(클로바)
post 앱 만들기 -> 유명인 사진 -> 클로버 api -> 유명인의 사람을 뽑아냄.
- naver develops에서 api등록
-
api_id secret_key를 찾아와서 환경변수에 등록
유명인 사진을 다운로드하고 네이버 클로바 예제코드에 집어넣어서 예측값 뽑아내기
import os
import sys
import requests
client_id = os.getenv("NAVER_ID")
client_secret = os.getenv("NAVER_SECRET")
# url = "https://openapi.naver.com/v1/vision/face" // 얼굴감지
url = "https://openapi.naver.com/v1/vision/celebrity" #// 유명인 얼굴인식
files = {'image': open('iu.jpg', 'rb')}
headers = {'X-Naver-Client-Id': client_id, 'X-Naver-Client-Secret': client_secret }
response = requests.post(url, files=files, headers=headers)
rescode = response.status_code
if(rescode==200):
print (response.json()["faces"][0]["celebrity"]["value"])
else:
print("Error Code:" + rescode)
플라스크를 통해서 여태껏 서버를 만들어줬음
각각의 클라이언트들이 url을 통해서 요청을 주면 그에 따라서 다른 응답을 줌.
그런데 요청은 두가지 방식이 있음.
-
get 방식 : 가져오는것.
- POST : 게시하다, 작성하다, 데이터를 보내다
예를 들어 get방식으로 회원가입사이트를 만든다면
할 때마다 id와 password가 url에 보여지므로 매우 좋지 않다.
=> 데이터를 보여주는게 아니라 서버에 게시하는것이 필요함.
플라스크를 이용해서 POST를 한번 실습해보자.
-
기본적으로 플라스크에서 app.route(‘/’)는 GET으로 인지한다
-
app.route(‘/’, methods=[‘POST’]) 이런식으로 명시해야함.
-
form 태그에 method를 POST로 바꿔주어야한다.
-
그러면 flask에서 post로 날라온 데이터는 어디서 받아서쓰는가?
: 새로운 route를 만들어야한다.(action에 지정된 사이트)
request.form.get()를 통해서 받을수있음.
특이한점
브라우저에 해당하는 url을 직접 치면 접근할 수 없다.
-
해당하는 url을 직접 치는 것은 GET 방식이다(브라우저로는 POST가 불가능)
-
하지만, FLASK app에서 POST만 허용했기 때문에 접근하는것은 불가능함.
ex) https://uni.likelion.org/users/sign_up : form태그에서 어디로 보내주는지 유추가능
https://www.codecademy.com/ : 여기는 form태그가 철저해서 유추도 못함.
-
from flask import Flask,render_template,request
import requests
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/signup', methods=['POST'])
def signup():
email = request.form.get('email')
password = request.form.get('password')
adminEmail = "qwer@qwer.com"
adminPassword = "12341234"
if email == adminEmail and password == adminPassword:
msg = "관리자님 환영합니다"
elif email == adminEmail:
msg = "관리자님 비밀번호 틀리셨어요"
else:
msg = "관리자님 아니시잖아요"
return render_template('signup.html',msg=msg)
app.run()
postman이라는 어플리케이션을 통해서 post에 접근가능(테스트하기 좋음)
쟝고같은 프레임워크가 csrf공격(?)같은 방법에 안정적이다.
-> 검증된 프레임워크를 쓰는 사람이 많아짐.
Interactive한 챗봇
내가 보낸 메시지를 상대방이 실시간으로 알 수 있어야함.
여태까지는 getUpdate를 사용함으로써 수동적으로 텔레그램에 요청해서 알림을 받았는데,
이제부터는 항상 돌아가는 서버(알림이)를 만들고, 자동으로 텔레그램의 알림을 받을 수 있음.
(텔레그램 서버가 webhook을 통해서 c9 서버에 무언가를 보내주면서 실시간으로 볼 수 있는것.)
-
webhook 서버의 주소를 텔레그램 서버에 알려주어야함
(webhook 세팅 : 우리가 텔레그램의 요청을 받을 수 있는 라우트 + 텔레그램에 요청할 수 있는 c9서버의 주소를 알려주는 라우트)
- 텔레그램 서버의 method : setWebhook -> 인자 : 알려줄 곳을 말해줘.
- 그런데 알려줄 주소를 getWebhookData같이 흔한거로 한다면 누군가가 탈취해서 이상한 응답을 보낼 수도 있음 -> 우리의 토큰을 라우트로 지정하는게 관례임.추천
- 역시 c9서버에도 환경변수를 지정해줘야함.
- 텔레그램 서버는 항상 잘 됬다는 신호(200 status)를 json과 함께 받아야함.
- 그러면 텔레그램 서버에서 준 json파일을 분석하여 메시지에 따라 각기다른 방법을 보내는 것이다.(POST방식으로 줌)
-
request : 요청을 보낸 사람에 대한 정보 -> json으로 파싱 : request.json()
-
webhook을 걸어놓고 봇의 상태가 바뀌면 “메세지를 받았다”라는 메세지를 주는거임.
-
그러면 “알았어 잘 받았어” 라는 말과 동치인 200을 내뱉어야 다시 묻지않음.
-> return render_template(‘telegram.html’), 200
from flask import Flask,render_template,request
import os
import requests
from pprint import pprint as pp
app = Flask(__name__)
token = os.getenv("TOKEN")
base_url = "https://api.hphk.io/telegram"
my_url = "https://webhook-start666.c9users.io"
@app.route('/{}'.format(token),methods=["POST"])
def telegram():
doc = request.get_json()
my_id = doc['message']['chat']['id']
text = doc['message']['text']
chat_url = "{}/bot{}/sendMessage?chat_id={}&text={}".format(base_url,token,my_id,reply)
requests.get(chat_url)
return '',200
@app.route('/setwebhook')
def setwebhook():
url = "{}/bot{}/setWebhook?url={}/{}".format(base_url,token,my_url,token)
print(url)
res = requests.get(url)
return ('{}'.format(res), 200)
과제 : 이미지가 들어왔을 때 클로바 api를 이용하여 얼굴인식결과를 제공해주기
주의 - 만약 텍스트가 들어오지 않았다면 text변수가 잘 출력이 안됨
-> get method를 사용한다면 에러를 잡지 않으므로
자료타입이 동적으로 변하는 경우에 대처할 수 있음.
- 내가 텔레그램에 사진을 보냄 -> 텔레그램이 사진을 클라우드에 저장함 -> 일단 텔레그램이 반환한 파일 id를 우리가 받아서 다시 get으로 클라우드에 요청하면 파일이 있는 정확한 주소를 반환함.
- 주소를 받고 네이버에 요청을 보내는데 POST로 요청을 하며, 헤더를 반드시 보내야함.
from flask import Flask,render_template,request
import os
import requests
from pprint import pprint as pp
import random
app = Flask(__name__)
token = os.getenv("TOKEN")
naver_id = os.getenv("NAVER_ID")
naver_secret = os.getenv("NAVER_SECRET")
base_url = "https://api.hphk.io/telegram"
my_url = "https://webhook-start666.c9users.io"
@app.route('/{}'.format(token),methods=["POST"])
def telegram():
doc = request.get_json()
pp(doc)
my_id = doc['message']['chat']['id']
text = doc['message'].get('text')
img = False
if doc['message'].get('photo') is not None:
img = True
if img:
file_id = doc.get('message').get('photo')[-1].get('file_id')
file = requests.get("{}/bot{}/getFile?file_id={}".format(base_url, token, file_id))
# getfile 메서드를 통해 파일의 아이디를 얻는다.
file_url = "{}/file/bot{}/{}".format(base_url, token, file.json().get('result').get('file_path'))
# 네이버로 요청
res = requests.get(file_url, stream=True)
clova_res = requests.post('https://openapi.naver.com/v1/vision/celebrity',
headers={
'X-Naver-Client-Id':naver_id,
'X-Naver-Client-Secret':naver_secret
},
files={
'image':res.raw.read()
})
if clova_res.json().get('info').get('faceCount'):
print(clova_res.json().get('faces'))
text = "{}".format(clova_res.json().get('faces')[0].get('celebrity').get('value'))
else:
text = "인식된 사람이 없습니다."
else:
# text 처리
text = doc['message']['text']
chat_url = "{}/bot{}/sendMessage?chat_id={}&text={}".format(base_url,token,my_id,text)
requests.get(chat_url)
return '',200
IBM 왓슨
자연어처리를 하는 챗봇 : 사람이 가진 intent를 가져올 수 있음.
(여러가지 단어군 -> 하나의 intent -> 각 intent에 대해서 이런 답안을 주겠다.
생각해보면 로직자체는 룰베이스와 크게 다르지는 않다고함.)