Skip to content
This repository has been archived by the owner on Jun 8, 2022. It is now read-only.

Commit

Permalink
Documentation init & slack app quickstart
Browse files Browse the repository at this point in the history
  • Loading branch information
ovv committed Mar 5, 2018
1 parent 3adcc62 commit e5a59b5
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 7 deletions.
6 changes: 6 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
python:
pip_install: true
extra_requirements:
- doc
conda:
file: docs/environment.yml
11 changes: 11 additions & 0 deletions docs/api/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
=============
API Reference
=============

Slack Plugin
------------

.. module:: sirbot.plugins.slack

.. autoclass:: sirbot.plugins.slack.SlackPlugin
:members:
4 changes: 4 additions & 0 deletions docs/assets/.directory
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[Dolphin]
PreviewsShown=true
Timestamp=2018,3,1,14,51,48
Version=4
Binary file added docs/assets/slack_app_permissions.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/slack_event_subscriptions.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/slack_oauth_token.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/slack_verification_token.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@
master_doc = 'index'

# General information about the project.
project = u'slack-sansio'
copyright = u"2017, Quentin Dawans"
author = 'Quentin Dawans'
project = u'Sir Bot-a-lot'
copyright = u"2017, Pyslackers"
author = 'Pyslackers'

# The version info for the project you're documenting, acts as replacement
# for |version| and |release|, also used in various other places throughout
Expand Down Expand Up @@ -229,7 +229,7 @@
latex_documents = [
(master_doc, 'sir-bot-a-lot-2.tex',
'sir-bot-a-lot-2 Documentation',
'Quentin Dawans', 'manual'),
'Pyslackers', 'manual'),
]

# The name of an image file (relative to this directory) to place at
Expand Down
3 changes: 3 additions & 0 deletions docs/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: sir-bot-a-lot-doc
dependencies:
- python=3.6
12 changes: 12 additions & 0 deletions docs/how_to/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

.. _howto:

======
How To
======

.. toctree::
:glob:
:maxdepth: 2

*
137 changes: 137 additions & 0 deletions docs/how_to/slack.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
=================
Slack Application
=================

Quickstart for creating a slack application with Sir bot-a-lot.

Ngrok setup
-----------

For ease of coding we will develop our app locally but in order to do so we need a tool to be reachable by slack. For
this we will use `ngrok <https://ngrok.com/>`_.

* `Install <https://ngrok.com/download>`_ ngrok.
* Start an HTTP tunnel on port ``8080``.

.. code:: console
$ ngrok http 8080
You should now have access to the ngrok web interface at `127.0.0.1:4040 <http://127.0.0.1:4040>`_ and see your http & https forwarding url (``http://xxxxxx.ngrok.io -> localhost:8080``).


Application setup
-----------------

* `Create a development slack team <https://slack.com/get-started#create>`_.
* `Create a new app <https://api.slack.com/apps>`_.
* Add the ``chat:write:bot`` & ``channels:read`` permissions to your application from the ``OAuth & Permissions`` page.

.. image:: ../assets/slack_app_permissions.png
:align: center

* Install the application to your workspace (Preferably on all public channels).
* Grab the ``Verification Token`` from the ``Basic Information`` page.

.. image:: ../assets/slack_verification_token.png
:align: center

* Grab the ``OAuth Access Token`` from the ``Install App`` page.

.. image:: ../assets/slack_oauth_token.png
:align: center

Sir Bot-a-lot installation
--------------------------

`Sir Bot-a-lot is on PyPI <https://pypi.org/project/sirbot/>`_.

.. code:: console
$ python3 -m venv .env # Create a virtual environment
$ source .env/bin/activate # Activate the virtual environment
$ pip3 install sirbot # Install Sir bot-a-lot
Starting the Bot
----------------

* Copy the code in a ``bot.py`` file and fill the ``OAuth Access Token`` & ``Verification Token``.

.. code:: python3
import logging
logging.basicConfig(level=logging.DEBUG)
from sirbot import SirBot
from sirbot.plugins.slack import SlackPlugin
OAUTH_ACCESS_TOKEN = ''
VERIFICATION_TOKEN = ''
bot = SirBot()
slack = SlackPlugin(token=OAUTH_ACCESS_TOKEN, verify=VERIFICATION_TOKEN)
bot.load(slack)
bot.start(host='0.0.0.0', port=8080)
* Start your bot

.. code:: console
$ (.env) python bot.py
Now that your bot is running we can start sending it events.

* Go back on your application configuration page (click `here <https://api.slack.com/apps>`_ to find it back).
* Add the ``Event Subscriptions`` feature. Set the url as your ngrok url (``http://xxxxxx.ngrok.io``).
* If everything is ok the url should be marked as ``Verified``.
* Add the ``message.channels`` event to the subscription.

.. image:: ../assets/slack_event_subscriptions.png
:align: center

* Type a message in your slack channel and you should see it coming in the logs.

.. code::
sirbot.plugins.slack.endpoints - DEBUG - Incoming message: Slack Message: {'type': 'message', 'user': 'U29163YQH', 'text': 'test message', 'ts': '1519907093.000242', 'channel': 'C5MKJ21QR', 'event_ts': '1519907093.000242'}
Answering messages
------------------

Once we receive message the next step is to start answering them. For that we need to add some code to ``bot.py``.

.. code:: python3
...
from slack import methods # Enum of the slack endpoints for better autocomplete
...
# We create function that will be called on incoming message
# The parameters are the incoming message and the aiohttp app
async def hello(message, app):
# We create a response object from the incoming message
response = message.response()
# We set the text of our message
response['text'] = 'Hello <@{user}>'.format(user=message['user'])
# We querry the slack api chat.postMessage with our response
await app['plugins']['slack'].api.query(url=methods.CHAT_POST_MESSAGE, data=response)
...
slack = SlackPlugin(token=OAUTH_ACCESS_TOKEN, verify=VERIFICATION_TOKEN)
# We register our function to respond to message starting with ``hello``
slack.on_message('^hello', hello)
bot.load(slack)
...
64 changes: 61 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,61 @@
===============
Sir-bot-a-lot-2
===============
==========================================
Sir Bot-a-lot | Asynchronous bot framework
==========================================

This project aims to provide a simple library to generate bots for various services. The initial goal was to implement
a bot for the slack team of the `pyslackers <https://www.pyslackers.com>`_ community.

Installation
------------

`Sir Bot-a-lot is on PyPI <https://pypi.org/project/sirbot/>`_.

.. code:: console
$ pip3 install sirbot
We recommend installing it in a `virtual environment <https://docs.python.org/3/tutorial/venv.html>`_.

To run the bot initialize it and call the ``bot.start`` method:

.. code:: python3
from sirbot import SirBot
bot = SirBot()
bot.start(host='0.0.0.0', port=80)
For examples take a look at the :ref:`howto` and the `pyslackers bot <https://github.com/pyslackers/sirbot-pyslackers>`_.

Plugins
-------

Sir Bot-a-lot connect to services with plugins, some are bundled by default:

* ``sirbot.plugins.github.GithubPlugin`` For `Github <https://www.github.com>`_.
* :class:`sirbot.plugins.slack.SlackPlugin` For `Slack <https://www.slack.com>`_.
* ``sirbot.plugins.postgres.PgPlugin`` For `PostgreSQL <https://www.postgresql.org/>`_.
* ``sirbot.plugins.apscheduler.APSchedulerPlugin`` For `APscheduler <https://apscheduler.readthedocs.io/en/latest/>`_.

To load a plugin call the ``bot.load`` method:

.. code:: python3
from sirbot import SirBot
from sirbot.plugins.slack import SlackPlugin
bot = SirBot()
slack = SlackPlugin(token='supersecureslacktoken')
bot.load(slack)
bot.start(host='0.0.0.0', port=80)
Navigation
----------

.. toctree::
:maxdepth: 2

how_to/index
api/index
49 changes: 49 additions & 0 deletions sirbot/plugins/slack/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@


class SlackPlugin:
"""
Slack plugin.
Handle communication from / to slack
Args:
token: slack authentication token (environment variable: `SLACK_TOKEN`).
verify: slack verification token (environment variable: `SLACK_VERIFY`).
bot_id: bot id (environment variable: `SLACK_BOT_ID`).
bot_user_id: user id of the bot (environment variable: `SLACK_BOT_USER_ID`).
admins: list of slack admins user id (environment variable: `SLACK_ADMINS`).
"""

__name__ = 'slack'

def __init__(self, *, token=None, verify=None, bot_id=None, bot_user_id=None, admins=None):
Expand Down Expand Up @@ -48,18 +61,45 @@ def load(self, sirbot):
sirbot.on_startup.append(self.find_bot_id)

def on_event(self, event_type, handler, wait=True):
"""
Register handler for an event
Args:
event_type: Incoming event type
handler: Handler to call
wait: Wait for handler execution before responding to the slack API.
"""

if not asyncio.iscoroutinefunction(handler):
handler = asyncio.coroutine(handler)
configuration = {'wait': wait}
self.routers['event'].register(event_type, (handler, configuration))

def on_command(self, command, handler, wait=True):
"""
Register handler for a command
Args:
command: Incoming command
handler: Handler to call
wait: Wait for handler execution before responding to the slack API.
"""
if not asyncio.iscoroutinefunction(handler):
handler = asyncio.coroutine(handler)
configuration = {'wait': wait}
self.routers['command'].register(command, (handler, configuration))

def on_message(self, pattern, handler, mention=False, admin=False, wait=True, **kwargs):
"""
Register handler for a message
Args:
pattern: Pattern on which to match incoming message.
handler: Handler to call
mention: Only trigger handler when the bot is mentioned
admin: Only trigger handler if posted by an admin
wait: Wait for handler execution before responding to the slack API.
"""
if not asyncio.iscoroutinefunction(handler):
handler = asyncio.coroutine(handler)

Expand All @@ -70,6 +110,15 @@ def on_message(self, pattern, handler, mention=False, admin=False, wait=True, **
self.routers['message'].register(pattern=pattern, handler=(handler, configuration), **kwargs)

def on_action(self, action, handler, name='*', wait=True):
"""
Register handler for an action
Args:
action: `callback_id` of the incoming action
handler: Handler to call
name: Choice name of the action
wait: Wait for handler execution before responding to the slack API.
"""
if not asyncio.iscoroutinefunction(handler):
handler = asyncio.coroutine(handler)
configuration = {'wait': wait}
Expand Down

0 comments on commit e5a59b5

Please sign in to comment.