본문 바로가기

파이썬 Tip

[챗봇] 파이썬 텔레그램 챗봇, 이것만 따라하면 20분 완성 (코로나 알리미 봇)

텔레그램은 챗봇을 만들기 매우매우 쉬운 편입니다.

이 포스팅을 따라하면,

다른 것 볼 필요 없이 챗봇을 만들어 볼 수 있습니다.

 

A. 텔레그램 앱 설치

안드로이드의 구글 플레이스토어나 iOS의 앱 스토어에 들어가서 텔레그램을 검색합니다.

텔레그램 앱을 설치하고, 실행하면 문자 인증 등의 절차를 거쳐 가입이 완료됩니다.

그럼 아래와 같은 화면이 나옵니다.

 

B. 새로운 챗봇 생성

화면 우측 상단에 있는 돋보기를 눌러, BotFather 라고 검색해줍니다.

아래 사진과 같이 생긴 애를 눌러줍니다.

이 봇파더가 우리의 챗봇을 새로 만드는데 도와줍니다.

이게 엄청나게 편합니다.

BotFather.. 이름 참 잘 지었습니다. 챗봇들의 아빠.

그 다음, '시작' 버튼을 눌러주고 나면,

아래와 같이 봇파더에게 말할 수 있는 여러 명령어들을 안내해줍니다.

우리는 여기서 /newbot 이라고 입력해줍니다.

그럼 아래와 같이 새로 만들 봇을 위한 이름을 정하라고 합니다.

쉽게 말해 카톡으로 치면 채팅방의 이름을 짓는다고 생각하면 됩니다.

저는 여기서 코로나 확진자 일일 알리미 봇을 만들 것입니다.

따라서 '코로나 알리미 봇' 이라고 채팅방 이름을 짓겠습니다.

그럼 아래와 같이 봇의 계정명을 만들어 주라고 합니다.

아까 지어준 이름은 '채팅방 이름'인 것이고,

지금 지어주는 이름이 그 채팅방에 들어가는 진짜 '봇의 이름'인 것이죠.

이 봇의 이름은 'bot' 으로 끝나야 한다고 합니다.

아마 실제 유저와 봇을 구분시켜 비매너 행위를 방지하려는 게 아닐까 싶습니다.

저는 'Covid19_daily_alarm_bot' 이라고 이름 지었습니다.

중복되는 이름이 있다면 이름을 다시 지으라고 합니다.

다행히 제가 지은 이름은 중복되는 이름이 없었네요.

성공적으로 챗봇이 생성되면 아래와 같은 메시지가 옵니다.

이 때, 중요한 것이 저 메시지의 중간에 보면,

아래 사진에서 제가 표시한 것처럼 API 토큰이 있습니다. 이 토큰을 복사를 해와야 합니다.

안드로이드의 경우 저 부분을 길게 터치하면 자동으로 딱 토큰 부분만 복사가 되는데,

아이폰의 경우는 잘 모르겠네요 ㅠㅠ. 아마 비슷하지 않을까 싶습니다.

 

저는 이 복사한 토큰을 스마트폰 카톡에 보내고,

다시 PC 카톡에서 복사하여, 파이썬 파일에 붙여 넣었습니다.

아래 사진 처럼요!

위 그림의 파이썬 파일 부분 잘 보시면,

토큰 문자열을 token 이라는 변수에 문자열 형태로 넣어준 것을 잘 확인해주세요!

 

 

C. 내가 만든 봇에게 처음으로 말 걸어보기

아까 봇 이름 지어줬던 것 기억나시죠.

저의 경우 'Covid19_daily_alarm_bot' 이라고 지었는데

이 봇 이름을 검색창에 검색합니다.

그럼 아래와 같이 내가 만든 그 봇이 뜨는데, 걔를 눌러주세요.

그 다음, '시작' 버튼을 눌러준 뒤

아무말이나 걸어봅니다.

당연히 챗봇은 아무 답도 안합니다. 우리가 아직 아무 기능도 안 넣어 줬으니까요!

그치만 이 작업이 꼭 필요합니다!

아무 답도 안한다고해서 그냥 이 과정을 넘어가지 마시고,

꼭 아무말이나 걸어주세요..!

 

D. 내 대화 id 알아내기

우선 모듈을 설치해줍니다.

파이참의 터미널 창에 아래 명령어를 입력해주세요.

pip install python-telegram-bot

그 다음, 파이참에 잠시 파이썬 파일 하나를 생성하여, 아래 코드를 넣고 실행해주세요.

1
2
3
4
5
6
7
import telegram
 
token = '아까_발급_받은_API_토큰을_넣어주세요'
bot = telegram.Bot(token=token)
updates = bot.getUpdates()
for u in updates:
    print(u.message)
cs

그럼 아마 어떤 실행 결과가 나오셨을 수도 있고,

아래처럼 에러가 뜨거나

혹은

에러는 안뜨는데 실행 결과가 안나올 수도 있습니다.

이렇게 에러가 떠도 당황하지 마시고, 아래 설명을 읽어주세요!

에러가 뜨거나, 실행 결과가 아무것도 안나온다면

챗봇에게 다시 몇 마디 더 건네보세요.

그 후, 다시 코드를 실행해보는 겁니다.

그러면 아래와 같은 실행 결과가 나옵니다.

각각이 뜻하는 것이 뭐냐..

간단히 말하자면 각 메시지에 대한 자세한 정보들입니다.

자세히 뜯어보자면.. 아래와 같습니다.

 

 
'message_id' : 3 3번째 메시지
'date' : 1615563765  1615563765 시간에 전송됨
'id' : 1618277280 사용자 대화 id
'type' : 'private' 개인 채팅
'first_name' : '원준'  사용자 이름

 

Q. 'date' : 1615563765 에서 왜 시간이 저따구에요? 무슨 날짜/시/분/초 같은게 안보이는데 저게 시간이에요?

A. 하.. 이거에 대해서 너무 다른 길로 샐까봐 얘기 안하려고 했는데, 손가락이 근질 거려서 참을 수가 없네요 ㅠㅠ..
컴퓨터는 날짜/시간을 샐 때 사실, 인간처럼 3월 13일 1시 2분 3초와 같이, 단위를 여러 개로 나누어 새지 않아요.
컴퓨터는 희안하게도 1970년 1월 1일 0분 0초로부터 지금까지 흘러온 '초' 만을 카운트 합니다.
그래서 저 1615563765 라는 숫자가 1970년 1월 1일 0분 0초에서 지금까지 흘러온 '초' 인 것이죠.
그래서 날짜/시간을 제대로 표시하고 싶을 땐 컴퓨터는 저 '초'를 기준으로 '분'/'시'/'일'/'월'/'년' 을 환산하게 됩니다.
왜 컴퓨터가 이러한 방식으로 시간을 카운트하게 되었는지 궁금하신 분들은 구글에 '1970년 1월 1일 0분 0초' 라고 검색하시면
나름 재밌는(?) 얘기들 많이 보실 수 있어요!

암튼 우리는 여기서 저 'id'(사용자 대화 id) 값을 복사해 올 겁니다!

저의 경우는 1618277280 이렇게 되어있죠.

 

복사해서, 파이썬 파일에 넣어줍니다.

현재 제 파이썬 파일에는 코드가 이렇게 되어있어요.

여기까지 해주셨으면 아주 잘 따라오고 계신 겁니다..!

 

E. 봇이 나에게 메세지 보내게 하기

봇이 나에게 메세지 보내게 하는 것은 쉽습니다!

그냥 sendMessage 함수만 써주면 되죠.

아래와 같이 한번 코드를 작성하고, 실행해보세요!

1
2
3
4
5
6
7
import telegram
 
token = "여기에_토큰을_넣어주세요."
id = "여기에_id를_넣어주세요."
 
bot = telegram.Bot(token)
bot.sendMessage(chat_id=id, text="테스트 중입니다.")
cs

그럼 아래와 같이 메세지가 옵니다.

 

어떤 느낌인지 아시겠죠?

봇이 나에게 메세지를 보내게 하려면

그저 sendMessage() 함수를 써주기만 하면 됩니다.

 

F. 내가 보낸 메세지에 봇이 답장하게 만들기

내가 메세지를 보냈을 때, 이를 봇이 읽어들이도록 만들고,

그 메세지에 맞게 답장을 보내도록 할거에요.

어렵게 생각할 것 없고 아래 코드에서 그저, handler 함수만 입맛에 맞게 수정해주면 됩니다.

아래 코드를 파이썬 파일에 넣고 실행해보세요.

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
import telegram
from telegram.ext import Updater
from telegram.ext import MessageHandler, Filters
 
token = "여기에_토큰을_넣어주세요."
id = "여기에_id를_넣어주세요."
 
bot = telegram.Bot(token)
bot.sendMessage(chat_id=id, text="테스트 중입니다.")
 
# updater
updater = Updater(token=token, use_context=True)
dispatcher = updater.dispatcher
updater.start_polling()
 
# 사용자가 보낸 메세지를 읽어들이고, 답장을 보내줍니다.
# 아래 함수만 입맛에 맞게 수정해주면 됩니다. 다른 것은 건들 필요없어요.
def handler(update, context):
    user_text = update.message.text # 사용자가 보낸 메세지를 user_text 변수에 저장합니다.
    if user_text == "안녕"# 사용자가 보낸 메세지가 "안녕"이면?
        bot.send_message(chat_id=id, text="어 그래 안녕"# 답장 보내기
    elif user_text == "뭐해"# 사용자가 보낸 메세지가 "뭐해"면?
        bot.send_message(chat_id=id, text="그냥 있어"# 답장 보내기
 
echo_handler = MessageHandler(Filters.text, handler)
dispatcher.add_handler(echo_handler)
cs

한번 '안녕' 이나 '뭐해' 라고 메세지를 보내보면 답장이 오는 것을 볼 수 있습니다.

센치한 챗봇..

E. 코로나 확진자 챗봇 완성

[전체 코드]

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
import telegram
from telegram.ext import Updater
from telegram.ext import MessageHandler, Filters
from bs4 import BeautifulSoup
from selenium import webdriver
import urllib.request as req
import os
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
 
######## 크롤링 관련 함수 ########
def covid_num_crawling():
    code = req.urlopen("https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=%ED%99%95%EC%A7%84%EC%9E%90")
    soup = BeautifulSoup(code, "html.parser")
    info_num = soup.select("div.status_today em")
    result = int(info_num[0].string) + int(info_num[1].string)
    return result
 
def covid_news_crawling():
    code = req.urlopen("https://search.naver.com/search.naver?where=news&sm=tab_jum&query=%EC%BD%94%EB%A1%9C%EB%82%98")
    soup = BeautifulSoup(code, "html.parser")
    title_list = soup.select("a.news_tit")
    output_result = ""
    for i in title_list:
        title = i.text
        news_url = i.attrs["href"]
        output_result += title + "\n" + news_url + "\n\n"
        if title_list.index(i) == 2:
            break
    return output_result
 
def covid_image_crawling(image_num=5):
    if not os.path.exists("./코로나이미지"):
        os.mkdir("./코로나이미지")
 
    browser = webdriver.Chrome("./chromedriver")
    browser.implicitly_wait(3)
    wait = WebDriverWait(browser, 10)
 
    browser.get("https://search.naver.com/search.naver?where=image&section=image&query=%EC%BD%94%EB%A1%9C%EB%82%98&res_fr=0&res_to=0&sm=tab_opt&color=&ccl=0&nso=so%3Ar%2Cp%3A1d%2Ca%3Aall&datetype=1&startdate=&enddate=&gif=0&optStr=d&nq=&dq=&rq=&tq=")
    wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.photo_group._listGrid div.thumb img")))
    img = browser.find_elements_by_css_selector("div.photo_group._listGrid div.thumb img")
    for i in img:
        img_url = i.get_attribute("src")
        req.urlretrieve(img_url, "./코로나이미지/{}.png".format(img.index(i)))
        if img.index(i) == image_num-1:
            break
    browser.close()
####################################
 
######## 텔레그램 관련 코드 ########
token = "여기에_토큰을_넣어주세요."
id = "여기에_id를_넣어주세요."
 
bot = telegram.Bot(token)
info_message = '''- 오늘 확진자 수 확인 : "코로나" or "ㅋㄹㄴ" 입력
- 코로나 관련 뉴스 : "뉴스" or "ㄴㅅ" 입력
- 코로나 관련 이미지 : "이미지" or "ㅇㅁㅈ" 입력'''
bot.sendMessage(chat_id=id, text=info_message)
 
updater = Updater(token=token, use_context=True)
dispatcher = updater.dispatcher
updater.start_polling()
 
### 챗봇 답장
def handler(update, context):
    user_text = update.message.text # 사용자가 보낸 메세지를 user_text 변수에 저장합니다.
    # 오늘 확진자 수 답장
    if (user_text == "코로나"or (user_text == "ㅋㄹㄴ"):
        covid_num = covid_num_crawling()
        bot.send_message(chat_id=id, text="오늘 확진자 수 : {} 명".format(covid_num))
        bot.sendMessage(chat_id=id, text=info_message)
    # 코로나 관련 뉴스 답장
    elif (user_text == "뉴스"or (user_text == "ㄴㅅ"):
        covid_news = covid_news_crawling()
        bot.send_message(chat_id=id, text=covid_news)
        bot.sendMessage(chat_id=id, text=info_message)
    # 코로나 관련 이미지 답장
    elif (user_text == "이미지"or (user_text == "ㅇㅁㅈ"):
        bot.send_message(chat_id=id, text="최신 이미지 크롤링 중...")
        covid_image_crawling(image_num=10)
        # 이미지 한장만 보내기
        # bot.send_photo(chat_id=id, photo=open("./코로나이미지/0.png", 'rb'))
        # 이미지 여러장 묶어서 보내기
        photo_list = []
        for i in range(len(os.walk("./코로나이미지").__next__()[2])): # 이미지 파일 개수만큼 for문 돌리기
            photo_list.append(telegram.InputMediaPhoto(open("./코로나이미지/{}.png".format(i), "rb")))
        bot.sendMediaGroup(chat_id=id, media=photo_list)
        bot.sendMessage(chat_id=id, text=info_message)
 
echo_handler = MessageHandler(Filters.text, handler)
dispatcher.add_handler(echo_handler)
####################################
 
cs

handler() 함수 안에 이미지 답장을 하는 부분을 보면,

텍스트가 아닌 이미지를 전송하는 문장도 있습니다.

이미지를 한 장만 보낼 땐, send_photo() 라는 함수를 사용하지만

이미지 여러 장을 묶어서 보낼 땐, sendMediaGroup() 함수를 사용하게 됩니다.

 

실행 결과는 다음과 같습니다.

 

 

 

 

 

✔️ [온라인 Live 강의] taling.me/Talent/Detail/4466

 

[선착순 6명 남음] 코딩으로 회사에서 '에이스'되기 #파이썬 | 탈잉

——————— 공 지 사 항 ——————— ✔️ 심화반 OPEN! (기본반 / 심화반 선택 가능) ✔️ 기본반 / 심화반 커리큘럼 확인 필수! ✔️ 온라인 Live 수업 (3시간씩 4회 일정) ✔️ 수업 날짜 -

taling.me:443

✔️ [VOD 강의] taling.me/vod/view/31196

 

직장인을 위한 파이썬 기초

스마트한 업무의 필수 조건

taling.me