Skip to content

Commit

Permalink
Introduce @at_start (#135)
Browse files Browse the repository at this point in the history
* Add @at_start to trigger actions when plugins initialize

fixes #132

* Stylize python code in documentation

* Fix inconsistent indentation
  • Loading branch information
unode authored Aug 15, 2020
1 parent 4faa19a commit 41fe464
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 21 deletions.
36 changes: 32 additions & 4 deletions docs/decorators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Built-in decorators
===================


Allowed users::
Allowed users:

.. code-block:: python
from mmpy_bot.utils import allowed_users
from mmpy_bot.bot import respond_to
Expand All @@ -17,7 +19,9 @@ Allowed users::
Direct messages::
Direct messages:

.. code-block:: python
from mmpy_bot.utils import allow_only_direct_message
from mmpy_bot.bot import respond_to
Expand All @@ -30,17 +34,34 @@ Direct messages::
Actions just after plugin initilization:

.. code-block:: python
from mmpy_bot.bot import at_start
@at_start
def hello(client):
client.connect_websocket()
client.ping()
Real case
---------

For example we have some users on our chat. We want allow some functionality
to some users. We can create constants with allowed users on bot settings::
to some users. We can create constants with allowed users on bot settings:

.. code-block:: python
ADMIN_USERS = ['admin', 'root', '[email protected]']
SUPPORT_USERS = ['mike', 'nick', '[email protected]']
So now we can close access to some functions on plugins::
So now we can close access to some functions on plugins:

.. code-block:: python
from mmpy_bot.utils import allow_only_direct_message
from mmpy_bot.utils import allowed_users
Expand All @@ -61,3 +82,10 @@ So now we can close access to some functions on plugins::
def sms_to(message, name):
# some code
message.reply('Sms was sent to %s' % name)
@at_start
def hello(client):
team = client.api.get_team_by_name("TESTTEAM")
channel = client.api.get_channel_by_name("bot_test", team["id"])
client.channel_msg(channel["id"], "Hello, feels good to be alive!!")
8 changes: 8 additions & 0 deletions docs/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ To write a new plugin, simply create a function decorated by ``mmpy_bot.bot.resp

- A function decorated with ``respond_to`` is called when a message matching the pattern is sent to the bot (direct message or @botname in a channel/group chat)
- A function decorated with ``listen_to`` is called when a message matching the pattern is sent on a channel/group chat (not directly sent to the bot)
- A function decorated with ``at_start`` is called as soon as the plugin is initialized (when the bot starts)

.. code-block:: python
Expand Down Expand Up @@ -38,6 +39,13 @@ To write a new plugin, simply create a function decorated by ``mmpy_bot.bot.resp
# Message is sent on the channel
# message.send('I can help everybody!')
@at_start
def hello(client):
# Note that contrary to respond_to and listen_to @at_start
# receives a client object and not a message object
team = client.api.get_team_by_name("TESTTEAM")
channel = client.api.get_channel_by_name("bot_test", team["id"])
client.channel_msg(channel["id"], "Hello, feels good to be alive!!")
To extract params from the message, you can use regular expression:

Expand Down
20 changes: 20 additions & 0 deletions mmpy_bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def __init__(self):

def run(self):
self._plugins.init_plugins()
self._plugins.trigger_at_start(self._client)
self._dispatcher.start()
_thread.start_new_thread(self._keep_active, tuple())
_thread.start_new_thread(self._run_jobs, tuple())
Expand All @@ -58,6 +59,7 @@ class PluginsManager(object):
'respond_to': {},
'listen_to': {}
}
_at_start = []

def __init__(self, plugins=None):
self.plugins = plugins or []
Expand Down Expand Up @@ -107,6 +109,13 @@ def get_plugins(self, category, text):
if not has_matching_plugin:
yield None, None

def trigger_at_start(self, client):
for func in self._at_start:
try:
func(client)
except Exception as err:
logger.exception(err)


class Matcher(object):
"""This allows us to map the same regex to multiple handlers."""
Expand All @@ -132,3 +141,14 @@ def respond_to(regexp, flags=0):

def listen_to(regexp, flags=0):
return get_wrapper('listen_to', regexp, flags)


def at_start():
def wrapper(func):
PluginsManager._at_start.append(func)
logger.info(
'registered %s plugin "%s"',
"at_start", func.__name__)
return func

return wrapper
30 changes: 15 additions & 15 deletions mmpy_bot/mattermost.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def get_file_link(self, file_id):
return self.get('/files/{}/link'.format(file_id))

def get_team_by_name(self, team_name):
return self.get('/teams/name/{}'.format(team_name))
return self.get('/teams/name/{}'.format(team_name))

def get_team_id(self, channel_id):
for team_id, channels in self.teams_channels_ids.items():
Expand Down Expand Up @@ -125,21 +125,21 @@ def in_webhook(url, channel, text, username=None, as_user=None,
}, verify=ssl_verify)

def login(self, team, account, password):
props = {'login_id': account, 'password': password}
props = {'login_id': account, 'password': password}
response = self._login(props)
if response.status_code in [301, 302, 307]:
# reset self.url to the new URL
self.url = response.headers['Location'].replace(
'/users/login', '')
# re-try login if redirected
response = self._login(props)
if response.status_code in [301, 302, 307]:
# reset self.url to the new URL
self.url = response.headers['Location'].replace(
'/users/login', '')
# re-try login if redirected
response = self._login(props)
if response.status_code == 200:
self.token = response.headers["Token"]
self.load_initial_data()
user = json.loads(response.text)
return user
else:
response.raise_for_status()
if response.status_code == 200:
self.token = response.headers["Token"]
self.load_initial_data()
user = json.loads(response.text)
return user
else:
response.raise_for_status()

def _login(self, props):
return requests.post(
Expand Down
4 changes: 2 additions & 2 deletions mmpy_bot/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def _default_scheduler__once(self, trigger_time):

def _once(trigger_time=datetime.now()):
if not isinstance(trigger_time, datetime):
raise AssertionError(
"The trigger_time parameter should be a datetime object.")
raise AssertionError(
"The trigger_time parameter should be a datetime object.")
return default_scheduler.once(
self=default_scheduler,
trigger_time=trigger_time)
Expand Down

0 comments on commit 41fe464

Please sign in to comment.