winterjung / chatterbox

Python library for Kakaotalk chatbot
MIT License
77 stars 16 forks source link
chatbot chatterbox fsm kakaotalk kakaotalk-bot

Chatterbox

license pypi pyversions travis appveyor codecov


Chatterbox카카오톡 플러스친구 자동응답 API를 활용하여 챗봇을 만들 때 사용되는 파이썬 라이브러리입니다.

Table of Contents

:memo: Installation

$ pip install chatterbox.py

:rocket: Example

더 다양한 예제는 examples를 참고해주세요.

from flask import Flask, request, jsonify
from chatterbox import Chatter
from chatterbox.response import Text, Keyboard, MessageButton

app = Flask(__name__)
chatter = Chatter()

@chatter.base(name='홈')
def home_keyboard():
    home_buttons = ['자기소개', '사이트로 이동하기']
    return Keyboard(home_buttons)

@chatter.rule(action='자기소개', src='홈', dest='홈')
def intro(data):
    message = 'chatterbox를 통해 더 쉽게 카카오톡 봇을 만들 수 있습니다!'
    return Text(message) + chatter.home()

@chatter.rule(action='사이트로 이동하기', src='홈', dest='홈')
def web(data):
    text = Text('자세한 정보를 보고싶으면 사이트로 이동해주세요!')
    msg_button = MessageButton(label='이동하기',
                               url='https://github.com/jungwinter/chatterbox')
    keyboard = chatter.home()
    return text + msg_button + keyboard

@app.route('/keyboard', methods=['GET'])
def keyboard():
    return jsonify(chatter.home())

@app.route('/message', methods=['POST'])
def message():
    return jsonify(chatter.route(request.json))

if __name__ == '__main__':
    app.run(debug=True)

:gem: Usage

Chatter

FSM(finite-state machine)을 사용해 유저들의 state를 내부에 저장하고, 요청에 맞는 response를 반환합니다.

Chatter.route(data)

카카오톡 자동응답 api 명세에 맞는 json 데이터를 인자로 받습니다. user_key로 가져온 state와 content값을 action으로 적절한 rule을 찾아 등록된 함수를 실행한 후 api 명세에 맞는 response를 반환합니다. rule에 관해선 아래에 서술되어 있습니다.

예제
@app.route('/message', methods=['POST'])
def message():
    response = chatter.route(request.json)
    return jsonify(response)

input 데이터로 다음과 같은 형식의 dict 객체를 인자로 받습니다.

{
  "user_key": "encryptedUserKey",
  "type": "text",
  "content": "자기소개"
}

output 데이터로 다음과 같은 형식의 Response 객체(dict로 동작함)를 반환합니다.

{
  "message": {
    "text": "안녕하세요! 무엇을 도와드릴까요?"
  },
  "keyboard": {
    "buttons": [
      "오늘의 날씨",
      "취소"
    ],
    "type": "buttons"
  }
}

Response

chatterbox.response에서 카카오톡 response object 명세를 만족하는 클래스를 가져올 수 있습니다.

Text(text)

다음과 같은 dict like 객체를 반환합니다. 멤버 변수로 text, message를 갖습니다.

Text(text='안녕!')
{
  "message": {
    "text": "안녕!"
  }
}

Photo(url, width, height)

다음과 같은 dict like 객체를 반환합니다. 멤버 변수로 url, width, height, message를 갖습니다.

Photo(url='https://image/url.png',
      width=500,
      height=400)
{
  "message": {
    "photo": {
      "url": "https://image/url.png",
      "width": 500,
      "height": 400
    }
  }
}

MessageButton(label, url)

다음과 같은 dict like 객체를 반환합니다. 멤버 변수로 label, url, message를 갖습니다.

MessageButton(label='이동하기',
              url='https://github.com/jungwinter/chatterbox')
{
  "message": {
    "message_button": {
      "label": "이동하기",
      "url": "https://github.com/jungwinter/chatterbox"
    }
  }
}

Keyboard(buttons, type)

자세한 명세는 Keyboard object 문서에서 확인할 수 있습니다. 멤버 변수로 type, buttons, keyboard를 갖습니다.

주관식 입력
Keyboard(type='text')
{
  "keyboard": {
    "type": "text"
  }
}
버튼 입력
Keyboard(['버튼1', '버튼2'])  # type='buttons'는 생략할 수 있음
{
  "keyboard": {
    "buttons": [
      "버튼1",
      "버튼2"
    ],
    "type": "buttons"
  }
}

조합

def intro():
    text = Text('안녕!')
    photo = Photo('https://image/url.png', 500, 400)
    keyboard = Keyboard(['날씨', '시간'])
    return text + photo + keyboard

위 코드는 아래와 같은 dict 객체를 반환합니다.

{
  "message": {
    "text": "안녕!",
    "photo": {
        "url": "https://image/url.png",
        "width": 500,
        "height": 400
    }
  },
  "keyboard": {
    "buttons": [
      "날씨",
      "시간"
    ],
    "type": "buttons"
  }
}
관계
Response
├── Message
│   ├── Text
│   ├── Photo
│   └── MessageButton
└── Keyboard
    ├── ButtonType
    └── TextType

Message + Message = Message
Message + Keyboard = Response
Response + Message = Response

Base

Chatter.add_base(name, func)

name으로 유저가 시작할 state 이름을 지정할 수 있습니다.

func은 인자가 없어야하며 Keyboard를 반환해야합니다.

예제
def func():
    return Keyboard(['버튼1', '버튼2'])
chatter.add_base(name='홈', func=func)

@Chatter.base(name)

Chatter.add_base()의 wrapper입니다. 데코레이터로 사용할 수 있습니다.

예제
@chatter.base(name='홈')
def func():
    return Keyboard(['버튼1', '버튼2'])

Chatter.home()

Chatter.add_base()를 통해 등록된 함수 func을 실행해 Keyboard를 반환합니다.

예제
>>> chatter.home()
{
  "keyboard": {
    "buttons": [
      "버튼1",
      "버튼2"
    ],
    "type": "buttons"
  }
}

Rule

Chatter.add_rule(action, src, dest, func)

유저의 현재 state가 src이고 input으로 받은 데이터에서 content가 action일 때, func 함수를 실행하고 유저의 state를 dest로 변경합니다. state를 활용하여 1 depth 이상의 자동응답 시나리오를 구성할 수 있습니다.

func 함수는 반드시 data를 인자로 받아야하며 Response를 반환해야합니다.

예제
def intro(data):
    message = 'chatterbox를 통해 더 쉽게 카카오톡 봇을 만들 수 있습니다!'
    return Text(message) + chatter.home()

chatter.add_rule(action='자기소개', src='홈', dest='홈', func=intro)

@Chatter.rule(action, src, dest)

Chatter.add_rule()의 wrapper입니다. 데코레이터로 사용할 수 있습니다.

예제
@chatter.rule(action='자기소개', src='홈', dest='홈')
def intro(data):
    message = 'chatterbox를 통해 더 쉽게 카카오톡 봇을 만들 수 있습니다!'
    return Text(message) + chatter.home()

주관식 답변 처리하기

Keyboard(type='text')를 반환해 유저의 주관식 답변을 받는 경우 action='*'을 사용해 처리할 수 있습니다. 자세한 방법은 examples/flask_advance.py를 참고해주세요.

fallback 처리하기

src='*'를 사용해 유저가 어떤 state에 있더라도 특정 dest로 이동시킬 수 있습니다.

예제
@chatter.rule(action='취소', src='*', dest='홈')
def cancel(data):
    message = '취소하셨습니다.'
    return Text(message) + chatter.home()

:pray: Contribution

CONTRIBUTING.md을 참고해주세요.

:link: Related projects