A framework for receiving and interacting with events from Slack's RTM API

slackapi, updated 🕥 2022-01-21 18:58:46

python-rtmbot

This project is no longer under active development. If you’re just getting started, we recommend taking a look at the Python SDK first. If you’ve been using this project, only critical issues (such as security issues) will be addressed, but we advise planning to migrate to the Python SDK. You can still file an issue and ask us for help doing that!

:bulb: If you’re interested in maintaining this package in the future, please get in touch

Build Status Coverage Status

A Slack bot written in Python that connects via the RTM API.

Python-rtmbot is a bot engine. The plugins architecture should be familiar to anyone with knowledge of the Slack API and Python. The configuration file format is YAML.

This project is currently pre-1.0. As such, you should plan for it to have breaking changes from time to time. For any breaking changes, we will bump the minor version while we are pre-1.0. (e.g. 0.2.4 -> 0.3.0 implies breaking changes). If stability is important, you'll likely want to lock in a specific minor version)

Some differences to webhooks:

  1. Doesn't require a webserver to receive messages
  2. Can respond to direct messages from users
  3. Logs in as a slack user (or bot)
  4. Bot users must be invited to a channel

Dependencies

  • websocket-client https://pypi.python.org/pypi/websocket-client/
  • python-slackclient https://github.com/slackhq/python-slackclient

Installation

  1. Create your project

mkdir myproject cd myproject

  1. Install rtmbot (ideally into a virtualenv)

pip install rtmbot

  1. Create an rtmbot.conf file and create a bot for your team

# Add the following to rtmbot.conf DEBUG: True # make this False in production SLACK_TOKEN: "xoxb-11111111111-222222222222222" ACTIVE_PLUGINS: - plugins.repeat.RepeatPlugin

DEBUG will adjust logging verbosity and cause the runner to exit on exceptions, generally making debugging more pleasant.

SLACK_TOKEN is needed to authenticate with your Slack team.

ACTIVE_PLUGINS RTMBot will attempt to import any Plugin specified in ACTIVE_PLUGINS (relative to your python path) and instantiate them as plugins. These specified classes should inherit from the core Plugin class.

For example, if your python path includes '/path/to/myproject' and you include plugins.repeat.RepeatPlugin in ACTIVE_PLUGINS, it will find the RepeatPlugin class within /path/to/myproject/plugins/repeat.py and instantiate it, then attach it to your running RTMBot.

A Word on Structure

To give you a quick sense of how this library is structured, there is a RtmBot class which does the setup and handles input and outputs of messages. It will also search for and register Plugins within the specified directory(ies). These Plugins handle different message types with various methods and can also register periodic Jobs which will be executed by the Plugins.

RtmBot ├── Plugin | ├── Job | └── Job ├── Plugin └── Plugin └── Job

Add Plugins

Plugins can live within any python module, but we recommend just putting them in ./plugins. (Don't forget to add an __init__.py file to your directory to make it a module -- use touch __init__.py within your plugin directory to create one)

To add a plugin, create a file within your plugin directory (./plugins is a good place for it).

mkdir plugins touch plugins/__init__.py cd plugins vi myplugin.py

Add your plugin content into this file. Here's an example that will just print all of the requests it receives to the console. See below for more information on available methods.

```python from future import print_function from rtmbot.core import Plugin

class MyPlugin(Plugin):

    def catch_all(self, data):
        print(data)

```

You can install as many plugins as you like, and each will handle every event received by the bot independently.

To create an example 'repeat' plugin:

Open plugins/repeat.py

Add the following:

```python from future import print_function from future import unicode_literals

from rtmbot.core import Plugin


class RepeatPlugin(Plugin):

    def process_message(self, data):
        if data['channel'].startswith("D"):
            self.outputs.append(
                [data['channel'], 'from repeat1 "{}" in channel {}'.format(
                    data['text'], data['channel']
                )]
            )

```

The repeat plugin will now be loaded by the bot on startup. Run rtmbot from console to start your RtmBot.

rtmbot

Create Plugins

Incoming data

All events from the RTM websocket are sent to the registered plugins. To act on an event, create a function definition, inside your Plugin class, called process_(api_method) that accepts a single arg for data. For example, to handle incoming messages:

python def process_message(self, data): print data

This will print the incoming message json (dict) to the screen where the bot is running.

Plugins having a method defined as catch_all(self, data) will receive ALL events from the websocket. This is useful for learning the names of events and debugging.

For a list of all possible API Methods, look here: https://api.slack.com/rtm

Note: If you're using Python 2.x, the incoming data should be a unicode string, be careful you don't coerce it into a normal str object as it will cause errors on output. You can add from __future__ import unicode_literals to your plugin file to avoid this.

Outgoing data

RTM Output

Plugins can send messages back to any channel or direct message. This is done by appending a two item array to the Plugin's output array (myPluginInstance.output). The first item in the array is the channel or DM ID and the second is the message text. Example that writes "hello world" when the plugin is started:

```python class myPlugin(Plugin):

    def process_message(self, data):
        self.outputs.append(["C12345667", "hello world"])

```

SlackClient Web API Output

Plugins also have access to the connected SlackClient instance for more complex output (or to fetch data you may need).

python def process_message(self, data): self.slack_client.api_call( "chat.postMessage", channel="#general", text="Hello from Python! :tada:", username="pybot", icon_emoji=":robot_face:"

Timed jobs

Plugins can also run methods on a schedule. This allows a plugin to poll for updates or perform housekeeping during its lifetime. Jobs define a run() method and return any outputs to be sent to channels. They also have access to a SlackClient instance that allows them to make calls to the Slack Web API.

For example, this will print "hello world" every 10 seconds. You can output multiple messages to the same or different channels by passing multiple pairs of [Channel, Message] combos.

```python from rtmbot.core import Plugin, Job

class myJob(Job):

    def run(self, slack_client):
        return [["C12345667", "hello world"]]


class myPlugin(Plugin):

    def register_jobs(self):
        job = myJob(10)
        self.jobs.append(job)

```

Plugin misc

The data within a plugin persists for the life of the rtmbot process. If you need persistent data, you should use something like sqlite or the python pickle libraries.

Issues

Bump ipython from 4.1.2 to 7.16.3

opened on 2022-01-21 18:58:46 by dependabot[bot]

Bumps ipython from 4.1.2 to 7.16.3.

Commits


Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language - `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language - `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language - `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/slackapi/python-rtmbot/network/alerts).

RFC: Python 2.7 Support Timeline

opened on 2018-12-20 20:00:12 by RodneyU215

Why are we doing this? Python 2.7 will reach it’s official ‘end of life’ (EOL) on December 31st, 2019. This means that it will no longer be supported by its maintainers. Accordingly, we will retire python-rtmbot support for Python 2.7.

“Python 2.x is legacy, Python 3.x is the present and future of the language.“

How will this change the project? All new python-rtmbot features and bug fixes will continue to be supported in Python 2.7 until Dec 31st, 2019.

After January 1, 2020 we’ll be creating a new version of the python-rtmbot. This version will remove any Python 2.x specific code. We’ll also immediately stop fixing any bugs and security vulnerabilities that are specific to this older version.

We’d love your feedback! Please let us know what you think by commenting on this issue. We’d love feedback on this timeline; does it work for you?

Fix bot output when it's to a user

opened on 2018-11-03 04:08:20 by awentzonline

This seems to return a dict rather than a string. Looks like there was a similar fix for group messaging here: https://github.com/slackapi/python-rtmbot/commit/bba41f43d99dd5dd3ed513e69291ac5953946629#diff-933761be909a15a181706d770d010687

Problems with importing plugins

opened on 2018-10-04 18:54:49 by quulah
  • [x] I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • [x] I've read and agree to the Code of Conduct.
  • [x] I've searched for any related issues and avoided creating a duplicate issue.

Description

Something causes problems in importing plugins from the bot's directory. Since the pip install installs plugins/repeat.py in Python's library directory, it overrides the one in the bot's directory.

My custom plugins are in /opt/rtmbot/plugins. I set the BASE_PATH to /opt/rtmbot, but that didn't help.

I tried installing in a virtualenv and with the system Python. System Python seems to work better for some reason.

I added a debug print of sys.path before the module loading code.

This is from the system Python install: ['/usr/local/bin', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages', '/opt/rtmbot']

And this is from the virtualenv: ['/opt/rtmbot/venv/bin', '/opt/rtmbot/venv/lib/python36.zip', '/opt/rtmbot/venv/lib/python3.6', '/opt/rtmbot/venv/lib/python3.6/lib-dynload', '/usr/lib/python3.6', '/opt/rtmbot/venv/lib/python3.6/site-packages', '/opt/rtmbot'] If I change /opt/rtmbot/plugins to /opt/rtmbot/more-plugins, for example, I can get the plugin to load.

Reproducible in:

  • [ ] This is reproducible in the sample project. RTMBot version: 0.4.1 Python version: 3.6.6 OS Version: Ubuntu 18.04

Expected result:

No import errors.

Actual result:

Rtmbot fails to start due to not finding my plugins.

Attachments:

The actual error: Traceback (most recent call last): File "/opt/rtmbot/venv/bin/rtmbot", line 11, in <module> sys.exit(main()) File "/opt/rtmbot/venv/lib/python3.6/site-packages/rtmbot/bin/run_rtmbot.py", line 31, in main bot.start() File "/opt/rtmbot/venv/lib/python3.6/site-packages/rtmbot/core.py", line 100, in start self._start() File "/opt/rtmbot/venv/lib/python3.6/site-packages/rtmbot/core.py", line 75, in _start self.load_plugins() File "/opt/rtmbot/venv/lib/python3.6/site-packages/rtmbot/core.py", line 160, in load_plugins cls = import_string(plugin_path) File "/opt/rtmbot/venv/lib/python3.6/site-packages/rtmbot/utils/module_loading.py", line 17, in import_string module = import_module(module_path) File "/opt/rtmbot/venv/lib/python3.6/importlib/__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 994, in _gcd_import File "<frozen importlib._bootstrap>", line 971, in _find_and_load File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked ModuleNotFoundError: No module named 'plugins.cve'

Implement passing configuration over environment variables

opened on 2018-05-11 20:10:22 by kpaleniu
  • [x] I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • [x] I've read and agree to the Code of Conduct.
  • [x] I've been mindful about doing atomic commits, adding documentation to my changes, not refactoring too much.
  • [x] I've a descriptive title and added any useful information for the reviewer. Where appropriate, I've attached a screenshot and/or screencast (gif preferably).
  • [x] I've written tests to cover the new code and functionality included in this PR.
  • [x] I've read, agree to, and signed the Contributor License Agreement (CLA).

PR Summary

This pull request adds functionality for overriding configuration file content from environment variables. For example:

$ SLACK_TOKEN="xoxb-foo-bar" REPEAT_PLUGIN_DUMMY_VARIABLE="true" rtmbot

This will result in effectively the following rtmbot.conf:

SLACK_TOKEN: "xoxb-foo-bar"
plugins.repeat.RepeatPlugin:
    dummy_variable: "true"

All rtmbot-specific configuration variables can overridden. For plugin-specific configuration I left the limitation of treating all values as strings to keep code from getting overly complex.

All this is mentioned in the README.md that is also updated by this pull request.

Related Issues

Implements at least the environment variable portion of #89.

Test strategy

tests/test_rtmbot_runner.py includes unit tests that cover most usual cases that came to mind.

Added instructions on programmatically running bots

opened on 2018-04-09 05:35:01 by MeLight

Added explanation to README regarding issue #28

Releases

0.4.1 Release 2018-02-05 23:14:14

  • Fixes crashes due to missing job_output
  • Support for private channels (Group IDs)
  • Fixed example code in README
  • Stability improvements
  • Various docs updates

Refactor plugin architecture 2016-11-02 17:29:44

BREAKING CHANGES!

Plugins are now easier to handle from the python REPL and tests. See the README for usage instructions. This also makes it possible to install and use this from PyPi.

Adds Web API access into plugin context and fixed Windows plugin imports 2016-08-26 02:51:20

There will be a future release with some refactoring to make the inclusion of the SlackClient a bit easier, but for now this should work!

Unicode should now work 2016-06-05 20:52:46

We should be using Unicode strings everywhere in Python 2.7 and 3.x now.

Note, I also changed the versioning to use x.x.x instead of x.xx for better semantic versioning.

Refactor to pull RTMBot class to make it more isolated 2016-04-30 00:33:17

This repo still has a fair ways to go to get it up to a production release quality, including lots of testing. This marks the beginning point of working towards better quality and testing.

SlackAPI

https://api.slack.com

GitHub Repository Homepage