Telegram 챗봇으로 DigitalOcean 제어하기

Minkang Heo
BotHub.Studio (ko)
Published in
15 min readJul 24, 2017

0. 시작에 앞서

챗봇을 만들어 보신 경험이 없으시다면 Python으로 Telegram 챗봇 시작하기를 먼저 읽어보시기 바랍니다.

어떤 언어를 사용하나요?
- Python을 이용합니다.

어떤 메신저를 이용하나요?
- Telegram 메신저를 이용합니다.

제작된 코드는 챗봇 호스팅 서비스인 BotHub.Studio를 이용해 실행됩니다.

1. 어떤 기능을 만들어 보나요?

DigitalOcean에서 제공하는 API를 활용하여 Droplet 을 관리하는 기능을 제작하게 됩니다. 본 예제를 통해 BotHub.Studio의 Property 기능과 Telegram Keyboards 를 활용해 볼 수 있습니다.

자주 사용하는 기능이 있다면 예제를 활용해 직접 추가해서 사용하실 수 있습니다.

2. 환경 준비하기

2.1 DigitalOcean에서 Access Token 생성하기

홈페이지 로그인 후 API 메뉴에서 Generate New Token을 진행합니다.

DigitalOcean Acces token 생성

생성한 Access token은 잘 기록해 둡니다.

2.2 Telegram에서 Access Token 생성하기

텔레그램 검색창에서 @BotFather 를 찾습니다. 또는 https://t.me/BotFather 으로 연결 하셔도 됩니다.

/newbot 명령을 이용해 새로운 봇을 생성합니다.

Telegram Bot 생성

생성된 Access token은 기록해 둡니다.

2.3 BotHub.Studio 등록 & CLI 설치하기

챗봇 호스팅 서비스 BotHub.Studio 회원 가입 후 아래와 같이 CLI 도구를 설치합니다.

pip install bothub-cli

계정 연결을 위해 configure 을 실행합니다.
(BotHub.Studio 계정이 필요합니다.)

bothub configure

프로젝트 폴더를 생성합니다.

mkdir DigitalOceanBot
cd DigitalOceanBot
bothub init

init 작업이 완료되면 기본 코드가 생성됩니다.

|-- bothub
| |-- bot.py
| `-- __init__.py
|-- bothub.yml
|-- requirements.txt
`-- tests

텔레그램 메신저를 연결합니다.

bothub channel add telegram --api-key=<api-key>

처음 프로젝트를 생성하면 기본으로 EchoBot 코드가 들어있어 바로 deploy를 통해 동작하는지 확인해 봅니다.

bothub deploy

3. API 연동하기

방금 생성한 챗봇 프로젝트 안에 bothub/doapi.py 라는 파일을 만들고 아래 클래스를 작성해봅시다.

DigitalOcean API를 이용해 간단하게 Droplet을 생성하고, 목록을 가져오고, 삭제할 수 있습니다.

import jsonfrom urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import Request
from urllib.request import urlopen
class DigitalOcean(object):
base_url = ‘https://api.digitalocean.com/v2/droplets'
def __init__(self, api_key):
self.headers = {
‘content-type’: ‘application/json’,
‘authorization’: ‘Bearer {}’.format(api_key)
}
def get_droplets(self):
req = Request(url=’{}page=1&per_page=5' \
.format(self.base_url),headers = self.headers)
try:
with urlopen(req) as response:
return json.loads(response.read().decode(‘utf8’))
except HTTPError as e:
return e.code
def delete_droplet(self, droplet_id):
req = Request(url=’{}/{}’.format(self.base_url,droplet_id),\
headers=self.headers, method=’DELETE’)
try:
with urlopen(req) as response:
return response.getcode()
except HTTPError as e:
return e.code
def create_droplet(self, name, region, image):
values = {‘name’: name, ‘region’: region, ‘size’: ‘512mb’, \
‘image’: ‘ubuntu-{}’.format(image)}
data = json.dumps(values).encode(‘utf8’)
req = Request(self.base_url, data=data,\
headers=self.headers)
with urlopen(req) as response:
return json.loads(response.read().decode(‘utf8’))
def simplify(self, result):
return [
{
‘id’: entry.get(‘id’),
‘name’: entry.get(‘name’),
‘status’: entry.get(‘status’)
}
for entry in result.get(‘droplets’)
]

Droplet 생성은 편의상 Name, Region, Image 만 선택하도록 되어 있으며, Size 는 512mb 로 지정해 두었습니다.

4. 챗봇 연동하기

4.1 Image, Region 정의하기

Droplet 생성을 위해 필요한 몇가지 정보를 사전에 Property에 저장해 둡니다.

bothub property set image [“14–04-x64”,”16–04–2-x64",”16–10-x64",”17–04-x64"]bothub property set region [ “nyc1”, “nyc3”, “sfo1”, “sfo2”, “sgp1” ]

4.2 Access token 등록

텔레그램의 경우 /start 메시지와 함께 봇이 시작됩니다. 첫 시작과 함께 Access token을 입력받아 사용하도록 하겠습니다.

bothub/bot.py 파일에서 Bot class의 handle_message 메소드를 아래와 같이 적용합니다.

from bothub_client.bot import BaseBot
from bothub_client.messages import Message
from .doapi import DigitalOcean
class Bot(BaseBot):
def handle_message(self, event, context):
message = event.get(‘content’)
if message == ‘/start’:
self.set_start_message(event)
else:
data = self.get_user_data()
api_key = data.get('api')
if api_key:
self.verify_api(message, event)
return
def set_start_message(self, event):
data = self.get_user_data()
data[‘api’] = True
self.set_user_data(data)
self.send_message(‘DigitalOcean Droplet 관리를 시작합니다.\n’ \
‘API사용을 위해 Access token을 입력해주세요.’)
def verify_api(self, api_key, event):
d = DigitalOcean(api_key)
res = d.get_droplets()
if res == 401:
self.send_message('Access token이 올바르지 않습니다.\n'\
'다시 입력해주세요.')
else:
data = self.get_user_data()
data['api'] = False
data['api_key'] = api_key
self.set_user_data(data)

message = Message(event).set_text('등록이 완료되었습니다.')
message.add_keyboard_button('Create Droplet')
message.add_keyboard_button('List all Droplets')
self.send_message(message)

Access token이 확인되면 텔레그램 키보드에 ‘Create Droplet’, ‘List all Droplets’ 가 생성됩니다.

4.3 Droplet 생성하기

등록된 Access token을 이용해 Droplet 생성을 해보겠습니다. (편의를 위해 위 코드중 일부는 생략해 두었습니다. 기존 코드를 삭제하지 말고 추가하시면 됩니다.)

class Bot(BaseBot):
def handle_message(self, event, context):
message = event.get(‘content’)
if message == ‘/start’:
self.set_start_message(event)
elif message == ‘Create Droplet’:
self.set_image(event)
elif message == ‘List all Droplets’:
self.list_droplets(event)
elif message.startswith(‘/delete ‘):
_, droplet_id = message.split()
self.delete_droplet(droplet_id, event)
elif message.startswith(‘/region ‘):
_, image = message.split()
self.set_data(‘image’, image)
self.set_region(event)
elif message.startswith(‘/name ‘):
_, region = message.split()
self.set_data(‘region’, region)
self.set_name(event)
# in case of natural language
else:
data = self.get_user_data()
api_key = data.get(‘api’)
set_name = data.get(‘set_name’)
if api_key:
self.verify_api(message, event)
return
elif set_name:
self.create_droplet(message, event)
return
def set_data(self, type, value):
data = self.get_user_data()
data[type] = value
self.set_user_data(data)
def set_image(self, event):
images = self.get_project_data()['image']
message = Message(event).set_text('이미지를 선택해주세요.')
for image in images:
message.add_postback_button(image, '/region {}' \
.format(image))
self.send_message(message)
def set_region(self, event):
regions = self.get_project_data()['region']
message = Message(event).set_text('Region을 선택해주세요.')
for region in regions:
message.add_postback_button(region, '/name {}' \
.format(region))
self.send_message(message)
def set_name(self, event):
self.send_message('hostname을 입력해주세요.')
data = self.get_user_data()
data['set_name'] = True
self.set_user_data(data)
def create_droplet(self, name, event):
d = DigitalOcean(self.api_key())
image = self.get_user_data()['image']
region = self.get_user_data()['region']
self.send_message(image, region)
droplet = d.create_droplet(name, region, image)
if not droplet:
self.send_message('Droplet 생성에 실패했습니다.')
data = self.get_user_data()
data['set_name'] = False
self.set_user_data(data)
return
self.send_message('id: {} name: {} status: {}\n' \
'Created successfully'\
.format(droplet['droplet']['id'], \
droplet['droplet']['name'], droplet['droplet']['status']))
data = self.get_user_data()
data['set_name'] = False
self.set_user_data(data)
def api_key(self):
api_key = self.get_user_data()['api_key']
return api_key

4.4 Droplet 목록 가져오기 & 삭제하기

삭제하기 버튼이 포함된 Droplet 목록을 가져오도록 아래의 내용을 추가합니다.

def list_droplets(self, event):
d = DigitalOcean(self.api_key())
droplets = d.simplify(d.get_droplets())
if not droplets:
self.send_message(‘생성된 Droplet이 없습니다.’)
return
for droplet in droplets:
msg = Message(event).set_text(‘id: {} name: {} status: {}’\
.format(droplet[‘id’],\
droplet[‘name’],\
droplet[‘status’])) \
.add_quick_reply(‘Delete’, ‘/delete {}’\
.format(droplet[‘id’]))
self.send_message(msg)def delete_droplet(self, droplet_id, event):
d = DigitalOcean(self.api_key())
res = d.delete_droplet(droplet_id)
if res == 204:
self.send_message(‘id: {} Droplet 삭제되었습니다.’\
.format(droplet_id))
elif res == 404:
self.send_message(‘id: {} Droplet이 존재하지 않습니다.’\
.format(droplet_id))
else:
self.send_message(‘에러 발생. Droplet 목록을 다시 확인해주세요.’)

작성된 최종 코드는 Github에서 확인하실 수 있습니다.

또 위에 작성된 코드로 동작하는 테스트 가능한 챗봇은 텔레그램에서 @Digital_Ocean_Bot 을 검색하시거나 t.me/Digital_Ocean_Bot 를 통해서 확인해 보실 수 있습니다.

5. 참고자료

본 아티클 관련 문의는 BotHub.Studio Community를 통해 지원받으실 수 있습니다. 챗봇 도입 및 제휴 문의는 bothub@bothub.sutdio로 연락해주세요.

--

--