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:
commit
4c7fa8b9ae
5 changed files with 105 additions and 79 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,7 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
/rtmbot.conf
|
/rtmbot.conf
|
||||||
/plugins/**
|
/plugins/**
|
||||||
|
/build/**
|
||||||
|
*.log
|
||||||
env
|
env
|
||||||
.tox
|
.tox
|
3
rtmbot/__init__.py
Normal file
3
rtmbot/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from core import *
|
||||||
|
|
||||||
|
site_config = {}
|
|
@ -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
13
setup.py
Executable 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
29
start_rtmbot.py
Executable 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')
|
Loading…
Add table
Add a link
Reference in a new issue