Merge branch 'master' of https://github.com/tzakrajs/python-rtmbot into tzakrajs-master

Also fixes some PEP8 issues and a number of small bugs.
This commit is contained in:
Jeff Ammons 2016-04-17 16:41:39 -07:00
commit 4c7fa8b9ae
5 changed files with 105 additions and 79 deletions

2
.gitignore vendored
View file

@ -1,5 +1,7 @@
*.pyc *.pyc
/rtmbot.conf /rtmbot.conf
/plugins/** /plugins/**
/build/**
*.log
env env
.tox .tox

3
rtmbot/__init__.py Normal file
View file

@ -0,0 +1,3 @@
from core import *
site_config = {}

View file

@ -1,36 +1,58 @@
#!/usr/bin/env python #!/usr/bin/env python
import sys import sys
import glob import glob
import yaml
import os import os
import time import time
import logging import logging
from argparse import ArgumentParser
from slackclient import SlackClient from slackclient import SlackClient
sys.dont_write_bytecode = True sys.dont_write_bytecode = True
def dbg(debug_string):
if debug:
logging.info(debug_string)
class RtmBot(object): class RtmBot(object):
def __init__(self, token): def __init__(self, config):
# set the config object
self.config = config
global site_config
site_config = self.config
# set slack token
self.token = config.get('SLACK_TOKEN')
# set working directory for loading plugins or other files
working_directory = os.path.dirname(sys.argv[0])
self.directory = self.config.get('BASE_PATH', working_directory)
if not self.directory.startswith('/'):
path = '{}/{}'.format(os.getcwd(), self.directory)
self.directory = os.path.abspath(path)
# establish logging
log_file = config.get('LOGFILE', 'rtmbot.log')
logging.basicConfig(filename=log_file,
level=logging.INFO,
format='%(asctime)s %(message)s')
logging.info(self.directory)
if 'DEBUG' in self.config:
self.debug = self.config.get('DEBUG')
else:
self.debug = False
# initialize stateful fields
self.last_ping = 0 self.last_ping = 0
self.token = token
self.bot_plugins = [] self.bot_plugins = []
self.slack_client = None self.slack_client = None
def _dbg(self, debug_string):
if self.debug:
logging.info(debug_string)
def connect(self): def connect(self):
"""Convenience method that creates Server instance""" """Convenience method that creates Server instance"""
self.slack_client = SlackClient(self.token) self.slack_client = SlackClient(self.token)
self.slack_client.rtm_connect() self.slack_client.rtm_connect()
def start(self): def _start(self):
self.connect() self.connect()
self.load_plugins() self.load_plugins()
while True: while True:
@ -41,6 +63,14 @@ class RtmBot(object):
self.autoping() self.autoping()
time.sleep(.1) time.sleep(.1)
def start(self):
if 'DAEMON' in self.config:
if self.config.get('DAEMON'):
import daemon
with daemon.DaemonContext():
self._start()
self._start()
def autoping(self): def autoping(self):
# hardcode the interval to 3 seconds # hardcode the interval to 3 seconds
now = int(time.time()) now = int(time.time())
@ -51,7 +81,7 @@ class RtmBot(object):
def input(self, data): def input(self, data):
if "type" in data: if "type" in data:
function_name = "process_" + data["type"] function_name = "process_" + data["type"]
dbg("got {}".format(function_name)) self._dbg("got {}".format(function_name))
for plugin in self.bot_plugins: for plugin in self.bot_plugins:
plugin.register_jobs() plugin.register_jobs()
plugin.do(function_name, data) plugin.do(function_name, data)
@ -74,17 +104,18 @@ class RtmBot(object):
plugin.do_jobs() plugin.do_jobs()
def load_plugins(self): def load_plugins(self):
for plugin in glob.glob(directory + '/plugins/*'): for plugin in glob.glob(self.directory + '/plugins/*'):
sys.path.insert(0, plugin) sys.path.insert(0, plugin)
sys.path.insert(0, directory + '/plugins/') sys.path.insert(0, self.directory + '/plugins/')
for plugin in glob.glob(directory + '/plugins/*.py') + \ for plugin in glob.glob(self.directory + '/plugins/*.py') + \
glob.glob(directory + '/plugins/*/*.py'): glob.glob(self.directory + '/plugins/*/*.py'):
logging.info(plugin) logging.info(plugin)
name = plugin.split('/')[-1][:-3] name = plugin.split('/')[-1][:-3]
# try: if name in self.config:
self.bot_plugins.append(Plugin(name)) logging.info("config found for: " + name)
# except: plugin_config = self.config.get(name, {})
# print "error loading plugin %s" % name plugin_config['DEBUG'] = self.debug
self.bot_plugins.append(Plugin(name, plugin_config))
class Plugin(object): class Plugin(object):
@ -95,11 +126,10 @@ class Plugin(object):
self.name = name self.name = name
self.jobs = [] self.jobs = []
self.module = __import__(name) self.module = __import__(name)
self.module.config = plugin_config
self.debug = self.module.config.get('DEBUG')
self.register_jobs() self.register_jobs()
self.outputs = [] self.outputs = []
if name in config:
logging.info("config found for: " + name)
self.module.config = config[name]
if 'setup' in dir(self.module): if 'setup' in dir(self.module):
self.module.setup() self.module.setup()
@ -115,18 +145,18 @@ class Plugin(object):
def do(self, function_name, data): def do(self, function_name, data):
if function_name in dir(self.module): if function_name in dir(self.module):
# this makes the plugin fail with stack trace in debug mode # this makes the plugin fail with stack trace in debug mode
if not debug: if not self.debug:
try: try:
eval("self.module." + function_name)(data) eval("self.module." + function_name)(data)
except: except:
dbg("problem in module {} {}".format(function_name, data)) self._dbg("problem in module {} {}".format(function_name, data))
else: else:
eval("self.module." + function_name)(data) eval("self.module." + function_name)(data)
if "catch_all" in dir(self.module): if "catch_all" in dir(self.module):
try: try:
self.module.catch_all(data) self.module.catch_all(data)
except: except:
dbg("problem in catch all") self._dbg("problem in catch all")
def do_jobs(self): def do_jobs(self):
for job in self.jobs: for job in self.jobs:
@ -160,11 +190,11 @@ class Job(object):
def check(self): def check(self):
if self.lastrun + self.interval < time.time(): if self.lastrun + self.interval < time.time():
if not debug: if not debug: # TODO: This isn't in scope any more
try: try:
self.function() self.function()
except: except:
dbg("problem") self._dbg("problem")
else: else:
self.function() self.function()
self.lastrun = time.time() self.lastrun = time.time()
@ -173,54 +203,3 @@ class Job(object):
class UnknownChannel(Exception): class UnknownChannel(Exception):
pass pass
def main_loop():
if "LOGFILE" in config:
logging.basicConfig(
filename=config["LOGFILE"],
level=logging.INFO,
format='%(asctime)s %(message)s'
)
logging.info(directory)
try:
bot.start()
except KeyboardInterrupt:
sys.exit(0)
except:
logging.exception('OOPS')
def parse_args():
parser = ArgumentParser()
parser.add_argument(
'-c',
'--config',
help='Full path to config file.',
metavar='path'
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
directory = os.path.dirname(sys.argv[0])
if not directory.startswith('/'):
directory = os.path.abspath("{}/{}".format(os.getcwd(),
directory
))
config = yaml.load(open(args.config or 'rtmbot.conf', 'r'))
debug = config["DEBUG"]
bot = RtmBot(config["SLACK_TOKEN"])
site_plugins = []
files_currently_downloading = []
job_hash = {}
if 'DAEMON' in config:
if config["DAEMON"]:
import daemon
with daemon.DaemonContext():
main_loop()
main_loop()

13
setup.py Executable file
View file

@ -0,0 +1,13 @@
#!/usr/bin/env python
from distutils.core import setup
setup(
name='rtmbot',
version='0.10',
description='A Slack bot written in python that connects via the RTM API.',
author='Ryan Huber',
author_email='rhuber@gmail.com',
url='https://github.com/slackhq/python-rtmbot',
packages=['rtmbot'],
)

29
start_rtmbot.py Executable file
View file

@ -0,0 +1,29 @@
#!/usr/bin/env python
import sys
import logging
from argparse import ArgumentParser
import yaml
from rtmbot import RtmBot
def parse_args():
parser = ArgumentParser()
parser.add_argument(
'-c',
'--config',
help='Full path to config file.',
metavar='path'
)
return parser.parse_args()
# load args with config path
args = parse_args()
config = yaml.load(file(args.config or 'rtmbot.conf', 'r'))
bot = RtmBot(config)
try:
bot.start()
except KeyboardInterrupt:
sys.exit(0)
except:
logging.exception('OOPS')