Language
한국어

[slack] 파이선을 이용한 api 서버

2017.10.07 10:38

lispro06 조회 수:13

처음엔 챗봇급으로 개발하려 했으나, 단순 API 서버로 지칭하는 것이 맞다.

Ubuntu16.04 에서 진행하였으며, python 2.7이 기본으로 설치되어 있어 python 3.5를 설치했다.

sudo add-apt-repository ppa:fkrull/deadsnakes
sudo apt-get update
sudo apt-get install virtualenv
sudo apt-get install python3-pip

pip를 이용해 슬랙관련 라이브러리를 설치할 때, pip2가 동작하므로 pip3를 python3-pip 로 설치하는 것이 반드시 필요하다. 그런데 대다수의 튜토리얼에는 해당 설명이 없어 1시간 정도 낭비했다.

pip3 install websocket-client
pip3 install slackbot
pip3 install slackclient

여기까지 하면 python 관련 서버 세팅은 마친 것이다.

virtualenv starterbot
을 통해 프롬프트가 원하는 제약된 가상 환경으로 설정되도록 한 뒤, 예제 코드를 실행하기 위해 슬랙 봇의 토큰을 환경 변수에 설정한다.
export SLACK_BOT_TOKEN="xoxb-슬랙 봇 토큰"

예제에서는 curl을 이용해 BOT_ID를 확인하고 환경 변수에 설정하는데 이것 보다는 python이 잘 동작하고, 토큰도 이상없는지 확인하는 코드를 실행하는 것이 더 나아보였다.

[print_bot_id.py]
import os
from slackclient import SlackClient

BOT_NAME = 'ai'
slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))

if __name__ == "__main__":
    api_call = slack_client.api_call("users.list")
    if api_call.get('ok'):
        # retrieve all users so we can find our bot
        users = api_call.get('members')
        for user in users:
            if 'name' in user and user.get('name') == BOT_NAME:
                print("Bot ID for '" + user['name'] + "' is " + user.get('id'))
    else:
        print("could not find bot user with the name " + BOT_NAME)
---------------------------------------------------------------------------------------------------
BOT_NAME 부분에 슬랙에 생성한 봇 이름을 넣으면 Bot ID for '봇네임' is '봇ID' 를 출력하여 알려준다.

curl https://그룹.slack.com/api/auth.test?token=xoxb-슬랙 봇 토큰
으로 확인하는 것과 동일한 결과를 얻을 수 있다.
export BOT_ID='봇ID'

까지 설정하면, 슬랙과 연동할 python API 서버 환경 설정이 완료된다.

[ai.py]
import os
import datetime
import time
from slackclient import SlackClient
# starterbot's ID as an environment variable
BOT_ID = os.environ.get("BOT_ID")
# constants
AT_BOT = "<@" + BOT_ID + ">"
EXAMPLE_COMMAND = "날짜"
# instantiate Slack & Twilio clients
slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))

def handle_command(command, channel):
    """
        Receives commands directed at the bot and determines if they
        are valid commands. If so, then acts on the commands. If not,
        returns back what it needs for clarification.
    """
    response = "Not sure what you mean. Use the *" + EXAMPLE_COMMAND + \
               "* command with numbers, delimited by spaces."
    if command.find(EXAMPLE_COMMAND) >= 0:
        now = time.localtime()
        s = "%04d-%02d-%02d" % (now.tm_year, now.tm_mon, now.tm_mday)
        response = "날짜는 "+s
    elif command.find("시간") >= 0:
        now = time.localtime()
        s = "%02d:%02d:%02d" % (now.tm_hour, now.tm_min, now.tm_sec)
        response = "시간은 "+s
    else:
        response = "개발중입니다."
    slack_client.api_call("chat.postMessage", channel=channel,
                          text=response, as_user=True)

def parse_slack_output(slack_rtm_output):
    """
        The Slack Real Time Messaging API is an events firehose.
        this parsing function returns None unless a message is
        directed at the Bot, based on its ID.
    """
    output_list = slack_rtm_output
    if output_list and len(output_list) > 0:
        for output in output_list:
            if output and 'text' in output and AT_BOT in output['text']:
                # return text after the @ mention, whitespace removed
                return output['text'].split(AT_BOT)[1].strip().lower(), \
                       output['channel']
    return None, None

if __name__ == "__main__":
    READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
    if slack_client.rtm_connect():
        print("StarterBot connected and running!")
        while True:
            command, channel = parse_slack_output(slack_client.rtm_read())
            if command and channel:
                handle_command(command, channel)
            time.sleep(READ_WEBSOCKET_DELAY)
    else:
        print("Connection failed. Invalid Slack token or bot ID?")
-------------------------------------------------------------------------------------------------------------------------------

예제 코드는 startwith를 통해 특정 키워드에 대해 반응하게 되어 있는데, find로 고쳤다.
단순 조건문으로 시간과 날짜를 출력한다.

챗봇은 훨씬더 고도화된 언어처리기를 갖춰야 가능하므로 예제는 단순 API 서버로 활용할 예정이다.

챗봇을 위한 언어 처리기를 제공해 주고, 서버를 구축할 필요 없는(serverless) 아마존의 lambda 등의 서비스가 넘쳐나고 있다.
https://calyfactory.github.io/api.ai-chatbot/
http://www.usefulparadigm.com/2016/04/06/creating-a-slack-bot-with-aws-lambda-and-api-gateway/

이제 이런 따라하기식 환경설정은 별 의미기 없고, 창의성을 발휘하여 어떤 효율적인 서비스를 만들것인가가 더 중요하다.

도구와 재료는 완료되었다. 정말 건강하고 맛있는 음식을 만드는데만 집중하면 된다. 고차원적인 사고가 필요하다.

api.jpg