class Slackbot: def __init__(self, SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET, config): # configuration self.log = logging.getLogger(__name__) self.config = config self.victorops = VictorOps(os.getenv('VICTOROPS_API_ID', ''), os.getenv('VICTOROPS_API_KEY', '')) # Login to slack self.adapter = SlackEventAdapter(SLACK_SIGNING_SECRET, "/") self.client = WebClient(token=SLACK_BOT_TOKEN) # Listen for events # -> this is alternative for decorators self.adapter.on("app_mention", self.handle_mention) def run(self, port=8080, host='127.0.0.1'): self.adapter.start(port=port, host=host) def print_message(self, message, channel): self.client.chat_postMessage(channel=channel, text=message) def not_found(self, cmd): return ("I don't know this command: '%s', try 'help'" % cmd) def process_message(self, message, channel): # command can have sub-commands # so let's split it on spaces commands = re.split('\s+', message) c = commands.pop(0) # get first command for cmd in COMMANDS: if (cmd == c): self.print_message( # generate the output from function ref COMMANDS[cmd]['func']( # inject victorops object self.victorops, # send the configuration for this command # (only if the configuration exist) self.config[cmd] if cmd in self.config else {}, # send sub-command(s) commands), channel) return # in case we don't know this command self.print_message(self.not_found(message), channel) def handle_mention(self, payload): event = payload.get('event', {}) self.log.debug("Got mention: %s" % event) m = re.match('<@\w+>\s(.*)', event.get('text')) self.process_message(m.group(1), event.get('channel')) @staticmethod def help(a, b, c): message = ["I know following commands:"] for cmd in COMMANDS: message.append('- %s: %s' % (cmd, COMMANDS[cmd]['help'])) return ("\n".join(message))
def create_app(config): app = Flask("web") app.config.from_pyfile((Path(__file__).parent.parent / "config" / config.lower()).with_suffix(".py")) with app.app_context(): from .executor import executor from .model import db, migrate executor.init_app(app) if app.config.get("SQLALCHEMY_DATABASE_URI") is not None: db.init_app(app) migrate.init_app(app, db) # hook up the low-level raw event handlers; high-level config is done in base.py if app.config.get("SLACK_SIGNING_SECRET") is not None: # connect the events adapter to the flask app slack_events_adapter = SlackEventAdapter( app.config["SLACK_SIGNING_SECRET"], "/slack/events", app) # wire up individual event handlers for event_handler, args, kwargs in events.EVENT_HANDLERS: slack_events_adapter.on(*args, **kwargs)(event_handler) # add routes for slash commands as specified in base.py for command, command_handler in app.config["SLASH_COMMANDS"].items(): app.add_url_rule( f"/slash/{command}", methods=["POST"], endpoint=command, view_func=command_handler.handle, ) # Turn off until we do something vaguely secure here or are # actively working on this again. for url, methods, api, api_handler in app.config["APIS"]: app.add_url_rule(url, methods=methods, endpoint=api, view_func=api_handler.handle) @app.route("/health") def health(): return "hi!" return app
# coding:utf-8 import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from flask import Flask from slackeventsapi import SlackEventAdapter from settings import HOST, PORT, SQLALCHEMY_DATABASE_URI, SQLALCHEMY_TRACK_MODIFICATIONS, SLACK_SIGNING_SECRET, CHATBOT_ENDPOINT, CHATBOT_SECRET_KEY from models import database as db from modules import authorize, handle_message app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = SQLALCHEMY_TRACK_MODIFICATIONS # Create a dictionary to represent a database to store our token db.init_app(app) db.create_all(app=app) # Route for Oauth flow to redirect to after user accepts scopes app.route('/authorize', methods=['GET', 'POST'])(authorize) # Bind the Events API route to your existing Flask app by passing the server # instance as the last param, or with `server=app`. slack_events_adapter = SlackEventAdapter(SLACK_SIGNING_SECRET, '/slack/events', app) slack_events_adapter.on('message')(handle_message) if __name__ == '__main__': print('start application') app.run(host=HOST, port=PORT, debug=True)
from dotenv import load_dotenv from redis import Redis from slackeventsapi import SlackEventAdapter load_dotenv() redis_client = Redis(host='redis_db', port=6379, db=0) slack_events_adapter = SlackEventAdapter(os.environ['SLACK_SIGNING_SECRET'], endpoint='/slack/events') def message(event_data): if 'text' not in event_data['event']: return if 'username' in event_data['event'] and event_data['event']['username'] == 'webslack': return msg = { 'author': 'site admin', 'date': str(datetime.datetime.now()), 'message': event_data['event']['text'] } redis_client.publish('slack-to-web', json.dumps(msg)) slack_events_adapter.on('message', message) slack_events_adapter.start(host='0.0.0.0', port=3000, debug=True)
def create_app(test_config=None): """Create the flask app""" logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(threadName)s - %(name)s - %(levelname)s - %(message)s', handlers=[RotatingFileHandler('modron.log', mode='a', maxBytes=1024 * 1024 * 2, backupCount=1), logging.StreamHandler(sys.stdout)]) logger = logging.getLogger(__name__) # Load the system configuration config = get_config() # Record the start time, used for status information start_time = datetime.now() # Get the secure tokens OAUTH_ACCESS_TOKENS = os.environ.get('OAUTH_ACCESS_TOKENS') SIGNING_SECRET = os.environ.get('SLACK_SIGNING_SECRET') CLIENT_SECRET = os.environ.get('CLIENT_SECRET') CLIENT_ID = os.environ.get('CLIENT_ID') # Make the Flask app app = Flask('modron', template_folder='./views/templates', static_folder="./views/static") app.jinja_env.filters['quote_plus'] = quote_plus app.secret_key = CLIENT_SECRET def get_netloc(url): p = urlparse(url) return f'{p.scheme}://{p.netloc}' app.jinja_env.filters['get_netloc'] = get_netloc app.jinja_env.filters['humanize_td'] = humanize.naturaldelta app.jinja_env.filters['humanize_size'] = humanize.naturalsize # Store some details about the runtime configuration app.config['start_time'] = start_time app.config['team_config'] = config app.config['CLIENT_SECRET'] = CLIENT_SECRET app.config['CLIENT_ID'] = CLIENT_ID # Register the views from .views import status, auth, players app.register_blueprint(status.bp) app.register_blueprint(auth.bp) app.register_blueprint(players.bp) # Store the clients clients = {} app.config['clients'] = clients # Make the Slack client and Events adapter if OAUTH_ACCESS_TOKENS is None: logger.warning('OAUTH_ACCESS_TOKENS was unset. Skipping all Slack-related functionality') return app for token in OAUTH_ACCESS_TOKENS.split(":"): client = BotClient(token=token) client.team_info() clients[client.team_id] = client event_adapter = SlackEventAdapter(SIGNING_SECRET, "/slack/events", app) logger.info(f'Finished initializing {len(clients)} Slack clients') # Check that we have configurations for each team authed_teams = set(clients.keys()) missing_config = authed_teams.difference(config.team_options.keys()) if len(missing_config) > 0: raise ValueError(f'Missing configuration data for {len(missing_config)} teams: {", ".join(missing_config)}') # Make the services app.config['services'] = {'reminder': {}, 'backup': {}} reminder_threads = {} for team_id, team_config in config.team_options.items(): if team_id not in clients: logging.warning(f'Missing OAuth Token for {team_id}') continue client = clients[team_id] # Start the reminder thread if team_config.reminders: reminder = ReminderService(clients[team_id], team_config.reminder_channel, team_config.watch_channels) reminder.start() reminder_threads[team_id] = reminder app.config['services']['reminder'][team_config.name] = reminder else: logger.info(f'No reminders for {team_config.name}') # Start the backup thread if team_config.backup_channels is not None: backup = BackupService(client, frequency=timedelta(days=1), channel_regex=team_config.backup_channels) backup.start() app.config['services']['backup'][team_config.name] = backup else: logger.info(f'No backup for {team_config.name}') # Generate the slash command responder modules = [ DiceRollInteraction(clients), StatisticModule(clients), ReminderModule(clients, reminder_threads), NPCGenerator(clients), CharacterSheet(clients) ] modron_cmd_parser = assemble_parser(modules) @app.route('/modron', methods=('POST',)) def modron_slash_cmd(): payload = SlashCommandPayload(**request.form.to_dict()) return handle_slash_command(payload, parser=modron_cmd_parser) @app.route('/oauth', methods=('GET',)) def slack_auth(): # Get the request code from the user code = request.args.get('code', None) logger.info('Received an authorization code. About to exchange it for a token') # Query Slack to get the token res = requests.post( url="https://slack.com/api/oauth.v2.access", data={ 'code': code, 'client_secret': CLIENT_SECRET, 'client_id': CLIENT_ID, 'redirect_uri': request.base_url } ) with open('received-tokens.json', 'w') as fp: json.dump(res.json(), fp) return "Success!" # Register the events event_adapter.on('message', f=partial(status_check, clients=clients, start_time=start_time)) return app