예제 #1
0
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))
예제 #2
0
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
예제 #3
0
# 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)
예제 #4
0
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)
예제 #5
0
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