def load_character(team_id: str, name: str) -> Tuple[Character, str]: """Load a character sheet Arg: team_id (str): ID of the Slack workspace name (str): Name of the character Returns: - (Character) Desired character sheet - (str): Absolute path to the character sheet, in case you must save it later """ config = get_config() sheet_path = config.get_character_sheet_path(team_id, name) return Character.from_yaml(sheet_path), os.path.abspath(sheet_path)
def test_sheet_path(): config = get_config() return config.get_character_sheet_path('TP3LCSL2Z', 'adrianna')
import logging import os from argparse import ArgumentParser, Namespace from datetime import datetime from typing import List, NoReturn import requests from modron.characters import list_available_characters, load_character from modron.config import get_config from modron.dice import DiceRoll, dice_regex from modron.interact import SlashCommandPayload from modron.interact.base import InteractionModule logger = logging.getLogger(__name__) config = get_config() def _render_dice_rolls(roll: DiceRoll) -> List[str]: """Render the dice values in an pretty HTML format Args: roll (DiceRoll): Dice roll to be rendered Returns: ([str]) Rendered form of each dice """ output = [] for (value, rolls), d in zip(roll.results, roll.dice): # Get whether the dice was used used_ix = rolls.index(value)
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