Skip to main content

Async framework for bots

Project description

# State [![Build Status](https://travis-ci.org/botstory/botstory.svg?branch=develop)](https://travis-ci.org/botstory/botstory) [![Coverage Status](https://coveralls.io/repos/github/botstory/botstory/badge.svg?branch=develop)](https://coveralls.io/github/botstory/botstory?branch=develop)

Under active development

# Idea

Easy reading API to describe dialogs (scenario) of bots in Python language.
Key problem is asyn nature of any dialog -
we can wait for are months answer from user and should store context
until that. As well dialog structure should be simply and clear
and show sequence of questions and reactions.

Sure dialog can be made in diagrams but my thought that code should
clear enough to show story of dialog and should be open for modification.

# Draft of API 0.2.2

## Simple example

```python
"""
v0.2.2
Bot asks user about destionation of space travelling.
- stateless story. it stores context of story (current question and results) somewhere (maybe DB)
"""
@story.on('lets go!')
def stateless_story(message):
@story.part()
def then(message):
return chat.ask_location(text='Where do you go?', user=message['user'])

@story.part()
def then(message):
store_destination(message['location'])
return chat.ask_location(text='Where do you now?', user=message['user'])

@story.part()
def then(message):
store_origin(message['location'])
return chat.say('Thanks! Give me a minute I will find you right spaceship!', user=message['user'])
```

## example with bifurcations

```python
"""
v0.2.2
Bot asks user about destionation of space travelling.
- stateless story. it stores context of story (current question and results) somewhere (maybe DB)
"""
@story.on('lets go!')
def stateless_story_with_bifurcation():
@story.part()
def request_destination(message):
return chat.ask_location(text='Where do you go?', user=message['user'])

@story.part()
def receive_destination(message):
location = message['location']]
return SwitchOnValue(location)

else:
store_destination(message['location'])
return request_origin(message)

@story.case(equal_to='stars')
def stars():
@story.part()
def receive_destination_options(message):
return chat.ask_location(text='Which star do you prefer?', then=receive_destination)

@story.case(equal_to='planets')
def planets():
@story.part()
def request_origin(message):
#cycle back
return ask_location(message['user'], text='Which planet do you prefer?', then=receive_destination)

@story.case(default=True)
def other():
@story.part()
def choose_from_top10_planets(message):
return choose_option(top10_planets,
text='Here is the most popular places. Maybe you would like to choose one?',
then=receive_destination_options)

@story.part()
def receive_origin(message):
store_origin(message['location'])
return chat.say('Thanks! Give me a minute I will find you right spaceship!', message['user'])

```

## example of callable function

```python

from ... import chat, story
from ...matchers.any import any
from ...matchers.location import location
from ...matchers.text import text


@story.callable()
def ask_location():
"""
v0.2.2

case/default/recursion version
based on indents and no any goto

:return:
"""
@story.begin()
def ask(body=None, options=None, user=None):
if not options:
# default aliases for current user
# like 'home', 'work', or other
options = default_aleases(user)
if not body:
body = default_question(user)
chat.say(body, options, user)
return Switch({
'location': location.Any(),
'option': option.Any(),
'text': text.Any(),
})

# 2 wait for answer
@story.case(match='location')
def location_case():
@story.part()
def return_location(message):
return EndOfStory({
'location': message,
})

@story.case(match='option')
def aliase():
@story.part()
def return_aliase(message):
# it can be location or any other message data
return EndOfStory({
'location': message['option']['data'],
})

@story.case(match='text')
def text_case():
@story.part()
def parse_text(message):
text_message = message['text']['raw']
# try aliases (common names like home, work, or other)
aliase = aliases.lookup(text_message)
if aliase:
return return EndOfStory({
'location': aliase['data'],
})

# if it is not alias maybe it is name of some place
options = googlemap.lookup_location_by_name(text_message)
if len(options) > 0:
return {
'args': 'many',
'wait': chat.choose_option(
body='We have few options',
options=[{'title': o.name, 'data': o.json()} for o in options],
user=message['user'],
),
}
else:
return {
'args': None,
}

@story.case(equal_to='many')
def have_options():
@story.part()
def choose_one_location_from_many(message):
location = message['option']['data']
if location:
return EndOfStory({
'location': location,
})
else:
# choose something else
pass

@story.case(equal_=None)
def not_any_option():
@story.check_alternative_stories()
@story.part()
def do_you_have_other_data(message):
text_message = message['text']['raw']
return chat.ask(
body='I can not find {} on map. Do you mean something else? Skip it?'.format(text_message),
options=[{
'title': 'skip',
'data': 'skip',
}],
user=message['user'],
)

@story.part()
def unknown_name(message):
if message['option']['data'] == 'skip':
return EndOfStory({
'location': None,
})
else:
# TODO: restart (tail recursion?)
return {
# ????
'recursion': location_case,
}

@story.case(default=True)
def default_case():
@story.check_alternative_stories()
@story.part()
def react_on_joke(message):
chat.say('Very funny! :)', message['user'])
return EndOfStory({
'location': None,
})


@story.callable()
def ask_date_time():
"""

ask date time from user

:return:
"""
@story.part()
def ask(body=None, options=None, user=None):
if not options:
# default aliases for current user
# like 'home', 'work', or other
options = default_aleases(user)
if not body:
body = default_question(user)
chat.say(body, options, user)
return SwitchOnValue({
'option': option.Any(),
'text': text.Any(),
})

@story.case(match='option')
def option_case():
@story.part()
def return_option(message):
return {
'return': message['option']['data']
}

@story.case(match='text')
def text_case():
@story.part()
def parse_text(message):
datetime_options = parse_text_to_date_time(message)
if len(datetime_options) == 0:
return EndOfStory({
'datetime': datetime_options,
})
elif len(datetime_options) == 1:
return EndOfStory({
'datetime': datetime_options,
})
else:
return chat.choose_option(
body='Hm what time do you mean?',
options=[{
'title': d['name'], 'data': {'datetime': d['value']}
} for d in datetime_options],
user=message['user'],
)

@story.part()
def return_option(message):
return EndOfStory({
'datetime': message['option']['data'],
})


```
[original sources](https://gist.github.com/hyzhak/b9adcc938abe9bfb4335cf31ef0abbee)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

botstory-0.0.58.tar.gz (60.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

botstory-0.0.58-py3-none-any.whl (92.4 kB view details)

Uploaded Python 3

File details

Details for the file botstory-0.0.58.tar.gz.

File metadata

  • Download URL: botstory-0.0.58.tar.gz
  • Upload date:
  • Size: 60.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for botstory-0.0.58.tar.gz
Algorithm Hash digest
SHA256 9be68e919415f842f44b82375596770770875edfc5678f4860f018b13bec948c
MD5 5ba145ff962cb48b3c20cf136bc86a64
BLAKE2b-256 51f0075c02e77adfb8edb5978993f0a286bd0deb61f8d23aa61709c516dac867

See more details on using hashes here.

File details

Details for the file botstory-0.0.58-py3-none-any.whl.

File metadata

File hashes

Hashes for botstory-0.0.58-py3-none-any.whl
Algorithm Hash digest
SHA256 fe55b7888e612a7b4d84b67534853d5a7b6ae18049a517980e65c446e4b686e8
MD5 8abaf79e7475f48a27de7bbd2e16c4d0
BLAKE2b-256 8923f81bb63f2b6d2525dab4fd8c4c6875cdd4164e2e91df02893327e3df4d6b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page