示例#1
0
 def barf(self) -> None:
     console.info('{0:>25}   {1:>5}   {2:>9}  {3:>9}  {4:>6}\n'.format(
         self.catname,
         self.number_of_races,
         self.mean_str,
         self.stdev_str,
         int(self.winrate * 100)))
示例#2
0
    async def post_login_init(self, client: discord.Client, server_id: int,
                              load_config_fn) -> None:
        """Initializes object; call after client has been logged in to discord"""
        self._load_config_fn = load_config_fn

        # Find the correct server
        the_guild = None  # type: Optional[discord.Guild]
        for s in client.guilds:
            if s.id == server_id:
                the_guild = s

        if the_guild is None:
            console.warning('Could not find guild with ID {guild_id}.'.format(
                guild_id=server_id))
            exit(1)

        server.init(client, the_guild)

        if not self._initted:
            await self._load_config_fn(self)
            self._initted = True
            for manager in self._managers:
                await manager.initialize()
        else:
            await self.refresh()

        console.info('\n'
                     '-Logged in---------------\n'
                     '   User name: {0}\n'
                     ' Server name: {1}\n'
                     '-------------------------'.format(
                         the_guild.me.display_name, the_guild.name))
示例#3
0
    async def _countdown_to_match_start(self, warn: bool = False) -> None:
        """Does things at certain times before the match
        
        Posts alerts to racers in this channel, and sends NecroEvents at alert times. Begins the match
        at the appropriate time. This is stored as a future in this object, and is meant to be canceled
        if this object closes.
        """
        try:
            if not self.match.is_scheduled:
                return

            time_until_match = self.match.time_until_match

            # Begin match now if appropriate
            if time_until_match < datetime.timedelta(seconds=0):
                if not self.played_all_races:
                    if warn:
                        await self.write(
                            'I believe that I was just restarted; an error may have occurred. I am '
                            'beginning a new race and attempting to pick up this match where we left '
                            'off. If this is an error, or if there are unrecorded races, please contact '
                            'an admin.')
                    await self._begin_new_race()
                return

            # Wait until the first warning
            if time_until_match > Config.MATCH_FIRST_WARNING:
                await asyncio.sleep(
                    (time_until_match -
                     Config.MATCH_FIRST_WARNING).total_seconds())
                await self.alert_racers()
                await NEDispatch().publish('match_alert',
                                           match=self.match,
                                           final=False)

            # Wait until the final warning
            time_until_match = self.match.time_until_match
            if time_until_match > Config.MATCH_FINAL_WARNING:
                await asyncio.sleep(
                    (time_until_match -
                     Config.MATCH_FINAL_WARNING).total_seconds())

            # At this time, we've either just passed the FINAL_MATCH_WARNING or the function was just called
            # (happens if the call comes sometime after the FINAL_MATCH_WARNING but before the match).
            await self.alert_racers()
            await NEDispatch().publish('match_alert',
                                       match=self.match,
                                       final=True)

            await asyncio.sleep(self.match.time_until_match.total_seconds())
            await self._begin_new_race()
        except asyncio.CancelledError:
            console.info(
                'MatchRoom._countdown_to_match_start() was cancelled.')
            raise
示例#4
0
    async def initialize(self):
        self._main_channel = server.find_channel(channel_id=Config.MAIN_CHANNEL_ID)
        self._notifications_channel = server.find_channel(channel_name=Config.NOTIFICATIONS_CHANNEL_NAME)
        self._schedule_channel = server.find_channel(channel_name=Config.SCHEDULE_CHANNEL_NAME)
        self._client = server.client

        if Config.LEAGUE_NAME:
            try:
                await self.set_event(Config.LEAGUE_NAME)
                console.info('Event recovered: "{0}"'.format(self._event.schema_name))
            except necrobot.exception.SchemaDoesNotExist:
                console.warning('League "{0}" does not exist.'.format(Config.LEAGUE_NAME))
        else:
            console.warning('No league given in Config.')
示例#5
0
    async def execute(self, cmd: Command) -> None:
        """If the Command's command is this object's command, calls the (virtual) method _do_execute on it

        Parameters
        ----------
        cmd: Command
            The command to maybe execute.
        """
        if cmd.command in self.command_name_list \
                and ((not self.admin_only) or self.bot_channel.is_admin(cmd.author)) \
                and (not self.testing_command or Config.testing()):
            async with self.execution_id_lock:
                self.execution_id += 1
                this_id = self.execution_id

            if isinstance(cmd.channel, discord.TextChannel):
                # noinspection PyUnresolvedReferences
                channel_name = cmd.channel.name
            elif isinstance(cmd.channel, discord.DMChannel):
                # noinspection PyUnresolvedReferences
                channel_name = 'DM-{}'.format(
                    cmd.channel.recipient.display_name)
            else:
                channel_name = "<Unknown channel>"

            console.info(
                'Call {0}: <ID={1}> <Caller={2}> <Channel={3}> <Message={4}>'.
                format(
                    type(self).__name__, this_id, cmd.author.name,
                    channel_name, cmd.content))

            try:
                await self._do_execute(cmd)
                console.info('Exit {0}: <ID={1}>'.format(
                    type(self).__name__, this_id))
            except Exception as e:
                console.warning(
                    'Error exiting {name} <ID={id}>: {error_msg}'.format(
                        name=type(self).__name__,
                        id=this_id,
                        error_msg=repr(e)))
                asyncio.ensure_future(
                    cmd.channel.send(
                        "Unexpected error while executing command `{mention}`."
                        .format(mention=self.mention)))
                raise
示例#6
0
    async def post_login_init(
            self,
            client: discord.Client,
            server_id: int,
            load_config_fn
    ) -> None:
        """Initializes object; call after client has been logged in to discord"""
        self._load_config_fn = load_config_fn

        # Find the correct server
        try:
            int(server_id)
            id_is_int = True
        except ValueError:
            id_is_int = False

        the_server = None  # type: discord.Server
        for s in client.servers:
            if id_is_int and s.id == server_id:
                the_server = s
            elif s.name == server_id:
                the_server = s

        if the_server is None:
            console.warning('Could not find the server.')
            exit(1)

        server.init(client, the_server)

        if not self._initted:
            await self._load_config_fn(self)
            self._initted = True
            for manager in self._managers:
                await manager.initialize()
        else:
            await self.refresh()

        console.info(
            '\n'
            '-Logged in---------------\n'
            '   User name: {0}\n'
            ' Server name: {1}\n'
            '-------------------------'.format(the_server.me.display_name, the_server.name)
        )
示例#7
0
    async def execute(self, command: Command) -> None:
        """If the Command's command is this object's command, calls the (virtual) method _do_execute on it
        
        Parameters
        ----------
        command: Command
            The command to maybe execute.
        """
        if command.command in self.command_name_list \
                and ((not self.admin_only) or self.bot_channel.is_admin(command.author)) \
                and (not self.testing_command or Config.testing()):
            async with self.execution_id_lock:
                self.execution_id += 1
                this_id = self.execution_id

            console.info(
                'Call {0}: <ID={1}> <Caller={2}> <Channel={3}> <Message={4}>'.
                format(
                    type(self).__name__, this_id, command.author.name,
                    command.channel.name, command.content))
            await self._do_execute(command)
            console.info('Exit {0}: <ID={1}>'.format(
                type(self).__name__, this_id))
示例#8
0
    async def execute(self, command: Command) -> None:
        """If the Command's command is this object's command, calls the (virtual) method _do_execute on it
        
        Parameters
        ----------
        command: Command
            The command to maybe execute.
        """
        if command.command in self.command_name_list \
                and ((not self.admin_only) or self.bot_channel.is_admin(command.author)) \
                and (not self.testing_command or Config.testing()):
            async with self.execution_id_lock:
                self.execution_id += 1
                this_id = self.execution_id

            console.info(
                'Call {0}: <ID={1}> <Caller={2}> <Channel={3}> <Message={4}>'.
                format(
                    type(self).__name__, this_id, command.author.name,
                    command.channel.name, command.content))

            try:
                await self._do_execute(command)
                console.info('Exit {0}: <ID={1}>'.format(
                    type(self).__name__, this_id))
            except Exception as e:
                console.warning(
                    'Error exiting {name} <ID={id}>: {error_msg}'.format(
                        name=type(self).__name__,
                        id=this_id,
                        error_msg=repr(e)))
                asyncio.ensure_future(
                    self.client.send_message(
                        command.channel,
                        "Unexpected error while executing command `{mention}`."
                        .format(mention=self.mention)))
                raise
示例#9
0
 async def _recover_stored_match_rooms() -> None:
     """Recover MatchRoom objects on bot init
     
     Creates MatchRoom objects for `Match`es in the database which are registered (via their `channel_id`) to
     some discord.Channel on the server.
     """
     console.info('Recovering stored match rooms------------')
     for row in await matchdb.get_channeled_matches_raw_data():
         channel_id = int(row[13])
         channel = server.find_channel(channel_id=channel_id)
         if channel is not None:
             match = await matchutil.make_match_from_raw_db_data(row=row)
             new_room = MatchRoom(match_discord_channel=channel, match=match)
             Necrobot().register_bot_channel(channel, new_room)
             await new_room.initialize()
             console.info('  Channel ID: {0}  Match: {1}'.format(channel_id, match))
         else:
             console.info('  Couldn\'t find channel with ID {0}.'.format(channel_id))
     console.info('-----------------------------------------')
示例#10
0
    async def _do_execute(self, cmd: Command):
        if len(cmd.args) != 1:
            await self.client.send_message(
                cmd.channel,
                'Wrong number of arguments for `{0}`.'.format(self.mention))
            return

        wks_name = cmd.args[0]
        await self.client.send_message(
            cmd.channel,
            'Creating matches from worksheet `{0}`...'.format(wks_name))
        await self.client.send_typing(cmd.channel)

        match_info = LeagueMgr().league.match_info

        console.info('MakeFromSheet: Getting GSheet info...')
        try:
            matchup_sheet = await sheetlib.get_sheet(
                gsheet_id=LeagueMgr().league.gsheet_id,
                wks_name=wks_name,
                sheet_type=sheetlib.SheetType.MATCHUP)  # type: MatchupSheet
            matches = await matchup_sheet.get_matches(register=False,
                                                      match_info=match_info)
        except (googleapiclient.errors.Error,
                necrobot.exception.NecroException) as e:
            await self.client.send_message(
                cmd.channel, 'Error while making matchups: `{0}`'.format(e))
            return

        console.info('MakeFromSheet: Creating Match objects...')
        not_found_matches = matchup_sheet.uncreated_matches()
        matches_with_channels = await matchutil.get_matches_with_channels()
        channeled_matchroom_names = dict()
        for match in matches_with_channels:
            if match.matchroom_name in channeled_matchroom_names:
                channeled_matchroom_names[match.matchroom_name] += 1
            else:
                channeled_matchroom_names[match.matchroom_name] = 1

        console.info('MakeFromSheet: Removing duplicate matches...')
        # Remove matches that have the same name as current channels (but only one per channel)
        unchanneled_matches = []
        for match in matches:
            channeled_name = match.matchroom_name in channeled_matchroom_names
            if not channeled_name or channeled_matchroom_names[
                    match.matchroom_name] <= 0:
                unchanneled_matches.append(match)
            if channeled_name:
                channeled_matchroom_names[match.matchroom_name] -= 1

        console.info('MakeFromSheet: Sorting matches...')
        # Sort the remaining matches
        unchanneled_matches = sorted(unchanneled_matches,
                                     key=lambda m: m.matchroom_name)

        console.debug(
            'MakeFromSheet: Matches to make: {0}'.format(unchanneled_matches))
        console.info('MakeFromSheet: Creating match channels...')
        for match in unchanneled_matches:
            console.info('MakeFromSheet: Creating {0}...'.format(
                match.matchroom_name))
            new_room = await matchutil.make_match_room(match=match,
                                                       register=True)
            await new_room.send_channel_start_text()

        uncreated_str = ''
        for match_str in not_found_matches:
            uncreated_str += match_str + ', '
        if uncreated_str:
            uncreated_str = uncreated_str[:-2]

        if uncreated_str:
            report_str = 'Done creating matches. The following matches were not made: {0}'.format(
                uncreated_str)
        else:
            report_str = 'All matches created successfully.'

        await self.client.send_message(cmd.channel, report_str)
示例#11
0
    async def publish(self, event_type: str, **kwargs):
        ev = NecroEvent(event_type, **kwargs)
        console.info('Processing event of type {0}.'.format(ev.event_type))

        for subber in self._subscribers:
            await subber.ne_process(ev)
示例#12
0
    async def _do_execute(self, cmd: Command):
        console.info("--CHANNEL LIST:---------------------------------------")
        for channel in server.guild.channels:
            console.info(str(channel))
        console.info("--END CHANNEL LIST------------------------------------")

        console.info("--MEMBER LIST:---------------------------------------")
        for member in server.guild.members:
            console.info(str(member))
        console.info("--END MEMBER LIST------------------------------------")
示例#13
0
def logon(
        config_filename: str,
        logging_prefix: str,
        load_config_fn: types.FunctionType,
        on_ready_fn: types.FunctionType = None
) -> None:
    """Log on to Discord. Block until logout.

    Parameters
    ----------
    config_filename: str
        The filename of the config file to use.
    logging_prefix: str
        A prefix to append to all logfile outputs.
    load_config_fn: [coro] (Necrobot) -> None
        A coroutine to be called after first login, which should set up the Necrobot with the desired
        BotChannels and Managers.
    on_ready_fn: [coro] (Necrobot) -> None
        A coroutine to be called after every login. Useful for unit testing.
    """
    # Initialize config file----------------------------------
    config.init(config_filename)

    # Asyncio debug setup-------------------------------------
    if config.Config.testing():
        asyncio.get_event_loop().set_debug(True)
        warnings.simplefilter("always", ResourceWarning)

    # Logging--------------------------------------------------
    log_timestr_format = '%Y-%m-%d-%H-%M-%S'
    log_file_format = '{prefix}-{timestr}.log'
    log_output_filename = os.path.join(
        'logging',
        log_file_format.format(prefix=logging_prefix, timestr=datetime.datetime.utcnow().strftime(log_timestr_format))
    )

    # Set up logger
    if config.Config.full_debugging():
        asyncio_level = logging.DEBUG
        discord_level = logging.DEBUG
        necrobot_level = logging.DEBUG
    elif config.Config.debugging():
        asyncio_level = logging.INFO
        discord_level = logging.INFO
        necrobot_level = logging.DEBUG
    elif config.Config.testing():
        asyncio_level = logging.INFO
        discord_level = logging.INFO
        necrobot_level = logging.INFO
    else:  # if config.Config.TEST_LEVEL == config.TestLevel.RUN:
        asyncio_level = logging.WARNING
        discord_level = logging.WARNING
        necrobot_level = logging.INFO

    stream_formatter = logging.Formatter('%(levelname)s:%(name)s: %(message)s')
    file_formatter = logging.Formatter('[%(asctime)s] %(levelname)s:%(name)s: %(message)s')

    stdout_handler = logging.StreamHandler(stream=sys.stdout)
    stderr_handler = logging.StreamHandler()
    file_handler = logging.FileHandler(filename=log_output_filename, encoding='utf-8', mode='w')

    # stdout_handler.setLevel(logging.INFO)
    # stderr_handler.setLevel(logging.INFO)

    stdout_handler.setFormatter(stream_formatter)
    stderr_handler.setFormatter(stream_formatter)
    file_handler.setFormatter(file_formatter)

    logging.getLogger('discord').setLevel(discord_level)
    logging.getLogger('discord').addHandler(file_handler)
    logging.getLogger('discord').addHandler(stderr_handler)
    logging.getLogger('asyncio').setLevel(asyncio_level)
    logging.getLogger('asyncio').addHandler(file_handler)
    logging.getLogger('asyncio').addHandler(stderr_handler)

    logger = logging.getLogger('necrobot')
    logger.setLevel(necrobot_level)
    logger.addHandler(file_handler)
    logger.addHandler(stdout_handler)

    console.info('Initializing necrobot...')

    # Run client---------------------------------------------
    retry = backoff.ExponentialBackoff()

    try:
        while True:
            logger.info('Beginning main loop.')
            # Create the discord.py Client object and the Necrobot----
            client = discord.Client()
            the_necrobot = Necrobot()
            the_necrobot.clean_init()
            the_necrobot.ready_client_events(client=client, load_config_fn=load_config_fn, on_ready_fn=on_ready_fn)

            while not client.is_logged_in:
                try:
                    asyncio.get_event_loop().run_until_complete(client.login(config.Config.LOGIN_TOKEN))
                except (discord.HTTPException, aiohttp.ClientError):
                    logger.exception('Exception while logging in.')
                    asyncio.get_event_loop().run_until_complete(asyncio.sleep(retry.delay()))
                else:
                    break

            while client.is_logged_in:
                if client.is_closed:
                    # noinspection PyProtectedMember
                    client._closed.clear()
                    client.http.recreate()

                try:
                    logger.info('Connecting.')
                    asyncio.get_event_loop().run_until_complete(client.connect())

                except (discord.HTTPException,
                        aiohttp.ClientError,
                        discord.GatewayNotFound,
                        discord.ConnectionClosed,
                        websockets.InvalidHandshake,
                        websockets.WebSocketProtocolError) as e:

                    if isinstance(e, discord.ConnectionClosed) and e.code == 4004:
                        raise  # Do not reconnect on authentication failure

                    logger.exception('Exception while running.')

                finally:
                    for task in asyncio.Task.all_tasks(asyncio.get_event_loop()):
                        task.cancel()

                    asyncio.get_event_loop().run_until_complete(asyncio.sleep(retry.delay()))

            if the_necrobot.quitting:
                break

    finally:
        asyncio.get_event_loop().close()
        config.Config.write()
示例#14
0
 def register_manager(self, manager: Manager) -> None:
     """Register a manager"""
     console.info('Registering a manager of type {0}.'.format(
         type(manager).__name__))
     self._managers.append(manager)
示例#15
0
async def _makematches_from_pairs(cmd, league, desired_match_pairs):
    status_message = await cmd.channel.send(
        'Creating matches... (Checking usernames)')

    match_info = league.match_info
    async with cmd.channel.typing():
        # Find all racers
        all_racers = dict()  # type: Dict[str, List[NecroUser]]
        for racerpair in desired_match_pairs:
            all_racers[racerpair[0]] = []
            all_racers[racerpair[1]] = []

        await userlib.fill_user_dict(all_racers)
        console.debug(
            '_makematches_from_pairs: Filled user dict: {}'.format(all_racers))

        not_found_racers = []
        doublename_racers = []
        for username, userlist in all_racers.items():
            if len(userlist) == 0:
                not_found_racers.append(username)
            elif len(userlist) > 1:
                doublename_racers.append(username)

        # Create Match objects
        matches = []
        not_found_matches = []  # type: List[Tuple[str, str]]

        async def make_single_match(racers):
            console.debug(
                '_makematches_from_pairs: Making match {0}-{1}'.format(
                    racers[0], racers[1]))
            racer_1 = all_racers[racers[0]][0] if len(
                all_racers[racers[0]]) == 1 else None
            racer_2 = all_racers[racers[1]][0] if len(
                all_racers[racers[1]]) == 1 else None
            if racer_1 is None or racer_2 is None:
                console.warning(
                    'Couldn\'t find racers for match {0}-{1}.'.format(
                        racers[0], racers[1]))
                not_found_matches.append((racers[0], racers[1]))
                return

            new_match = await matchutil.make_match(register=True,
                                                   racer_1_id=racer_1.user_id,
                                                   racer_2_id=racer_2.user_id,
                                                   match_info=match_info,
                                                   league_tag=league.tag,
                                                   autogenned=True)
            if new_match is None:
                console.debug(
                    '_makematches_from_pairs: Match {0}-{1} not created.'.
                    format(racers[0], racers[1]))
                not_found_matches.append((racers[0], racers[1]))
                return

            matches.append(new_match)
            console.debug('_makematches_from_pairs: Created {0}-{1}'.format(
                new_match.racer_1.matchroom_name,
                new_match.racer_2.matchroom_name))

        for racer_pair in desired_match_pairs:
            await make_single_match(racer_pair)
            await asyncio.sleep(0)

        matches = sorted(matches, key=lambda m: m.matchroom_name)

        await status_message.edit(
            content='Creating matches... (Creating race rooms)')
        console.debug(
            '_makematches_from_pairs: Matches to make: {0}'.format(matches))

        # Create match channels
        for match in matches:
            console.info('MakeMatchesFromFile: Creating {0}...'.format(
                match.matchroom_name))
            new_room = await matchchannelutil.make_match_room(match=match,
                                                              register=False)
            await new_room.send_channel_start_text()

        # Report on uncreated matches
        if not_found_matches:
            filename = leagueutil.get_unmade_matches_filename(
                league_tag=league.tag)
            with open(filename, 'w') as file:
                for r1, r2 in not_found_matches:
                    file.write(f'{r1},{r2}\n')

            uncreated_str = ', '.join(f'`{t[0]}-{t[1]}`'
                                      for t in not_found_matches)
            report_str = f'The following matches were not made: {uncreated_str}. These matches were written to ' \
                         f'`{filename}`. Call `.make-unmade-matches` to attempt to remake these easily.'
        else:
            report_str = 'All matches created successfully.'

    unfound_racers_str = ', '.join(f'`{n}`' for n in not_found_racers)
    doubled_racers_str = ', '.join(f'`{n}`' for n in doublename_racers)
    if not_found_racers:
        report_str += \
            f'\n\nThe following racers could not be found: {unfound_racers_str}.'
    if doubled_racers_str:
        report_str += \
            f'\n\nThe following names were associated to more than one Discord account: {doubled_racers_str}.'

    await status_message.edit(
        content=f'Creating matches... done.\n\n{report_str}')
示例#16
0
def logon(config_filename: str,
          logging_prefix: str,
          load_config_fn: types.FunctionType,
          on_ready_fn: types.FunctionType = None) -> None:
    """Log on to Discord. Block until logout.
    
    Parameters
    ----------
    config_filename: str
        The filename of the config file to use.
    logging_prefix: str
        A prefix to append to all logfile outputs.
    load_config_fn: [coro] (Necrobot) -> None
        A coroutine to be called after first login, which should set up the Necrobot with the desired
        BotChannels and Managers.
    on_ready_fn: [coro] (Necrobot) -> None
        A coroutine to be called after every login. Useful for unit testing.
    """
    # Initialize config file----------------------------------
    config.init(config_filename)

    # Asyncio debug setup-------------------------------------
    if config.Config.testing():
        asyncio.get_event_loop().set_debug(True)
        warnings.simplefilter("always", ResourceWarning)

    # Logging--------------------------------------------------
    log_timestr_format = '%Y-%m-%d-%H-%M-%S'
    log_file_format = '{prefix}-{timestr}.log'
    log_output_filename = os.path.join(
        'logging',
        log_file_format.format(
            prefix=logging_prefix,
            timestr=datetime.datetime.utcnow().strftime(log_timestr_format)))

    # Set up logger
    if config.Config.full_debugging():
        asyncio_level = logging.DEBUG
        discord_level = logging.DEBUG
        necrobot_level = logging.DEBUG
    elif config.Config.debugging():
        asyncio_level = logging.INFO
        discord_level = logging.INFO
        necrobot_level = logging.DEBUG
    elif config.Config.testing():
        asyncio_level = logging.INFO
        discord_level = logging.INFO
        necrobot_level = logging.INFO
    else:  # if config.Config.TEST_LEVEL == config.TestLevel.RUN:
        asyncio_level = logging.WARNING
        discord_level = logging.WARNING
        necrobot_level = logging.INFO

    stream_formatter = logging.Formatter('%(levelname)s:%(name)s: %(message)s')
    file_formatter = logging.Formatter(
        '[%(asctime)s] %(levelname)s:%(name)s: %(message)s')

    stdout_handler = logging.StreamHandler(stream=sys.stdout)
    stderr_handler = logging.StreamHandler()
    file_handler = logging.FileHandler(filename=log_output_filename,
                                       encoding='utf-8',
                                       mode='w')

    # stdout_handler.setLevel(logging.INFO)
    # stderr_handler.setLevel(logging.INFO)

    stdout_handler.setFormatter(stream_formatter)
    stderr_handler.setFormatter(stream_formatter)
    file_handler.setFormatter(file_formatter)

    logging.getLogger('discord').setLevel(discord_level)
    logging.getLogger('discord').addHandler(file_handler)
    logging.getLogger('discord').addHandler(stderr_handler)
    logging.getLogger('asyncio').setLevel(asyncio_level)
    logging.getLogger('asyncio').addHandler(file_handler)
    logging.getLogger('asyncio').addHandler(stderr_handler)

    logger = logging.getLogger('necrobot')
    logger.setLevel(necrobot_level)
    logger.addHandler(file_handler)
    logger.addHandler(stdout_handler)

    console.info('Initializing necrobot...')

    # Seed the random number generator------------------------
    seedgen.init_seed()

    # Run client---------------------------------------------
    try:
        logger.info('Beginning main loop.')
        # Create the discord.py Client object and the Necrobot----
        client = discord.Client()
        the_necrobot = Necrobot()
        the_necrobot.clean_init()
        the_necrobot.ready_client_events(client=client,
                                         load_config_fn=load_config_fn,
                                         on_ready_fn=on_ready_fn)

        client.run(config.Config.LOGIN_TOKEN)

    finally:
        # VodRecorder().end_all_async_unsafe()
        config.Config.write()
示例#17
0
    async def _do_execute(self, cmd: Command):
        if len(cmd.args) != 1:
            await cmd.channel.send(
                'Wrong number of arguments for `{0}`.'.format(self.mention))
            return

        wks_name = cmd.args[0]
        status_message = await cmd.channel.send(
            'Creating matches from worksheet `{0}`... (Getting GSheet info)'.
            format(wks_name))

        async with cmd.channel.typing():
            match_info = LeagueMgr().league.match_info

            console.info('MakeFromSheet: Getting GSheet info...')
            try:
                matchup_sheet = await sheetlib.get_sheet(
                    gsheet_id=LeagueMgr().league.gsheet_id,
                    wks_name=wks_name,
                    sheet_type=sheetlib.SheetType.MATCHUP
                )  # type: MatchupSheet
                matches = await matchup_sheet.get_matches(
                    register=True, match_info=match_info)
            except (googleapiclient.errors.Error,
                    necrobot.exception.NecroException) as e:
                await cmd.channel.send(
                    'Error while making matchups: `{0}`'.format(e))
                return

            console.info('MakeFromSheet: Creating Match objects...')
            await status_message.edit(
                content=
                'Creating matches from worksheet `{0}`... (Creating match list)'
                .format(wks_name))
            not_found_matches = matchup_sheet.uncreated_matches()
            matches_with_channels = await matchchannelutil.get_matches_with_channels(
            )

            console.info('MakeFromSheet: Removing duplicate matches...')
            # Remove matches from the list that already have channels
            unchanneled_matches = []
            for match in matches:
                found = False
                for channeled_match in matches_with_channels:
                    if match.match_id == channeled_match.match_id:
                        found = True
                if not found:
                    unchanneled_matches.append(match)

            console.info('MakeFromSheet: Sorting matches...')
            # Sort the remaining matches
            unchanneled_matches = sorted(unchanneled_matches,
                                         key=lambda m: m.matchroom_name)

            await status_message.edit(
                content=
                'Creating matches from worksheet `{0}`... (Creating race rooms)'
                .format(wks_name))
            console.debug('MakeFromSheet: Matches to make: {0}'.format(
                unchanneled_matches))
            console.info('MakeFromSheet: Creating match channels...')
            for match in unchanneled_matches:
                console.info('MakeFromSheet: Creating {0}...'.format(
                    match.matchroom_name))
                new_room = await matchchannelutil.make_match_room(
                    match=match, register=False)
                await new_room.send_channel_start_text()

            uncreated_str = ''
            for match_str in not_found_matches:
                uncreated_str += match_str + ', '
            if uncreated_str:
                uncreated_str = uncreated_str[:-2]

            if uncreated_str:
                report_str = 'The following matches were not made: {0}'.format(
                    uncreated_str)
            else:
                report_str = 'All matches created successfully.'

        await status_message.edit(
            content='Creating matches from worksheet `{0}`... done. {1}'.
            format(wks_name, report_str))
示例#18
0
    async def _do_execute(self, cmd):
        if len(cmd.args) != 1:
            await cmd.channel.send(
                'Wrong number of arguments for `{0}`.'.format(self.mention)
            )
            return

        filename = cmd.args[0]
        if not filename.endswith('.csv'):
            await cmd.channel.send(
                'Matchup file should be a `.csv` file.'
            )
            return
        file_path = os.path.join(filename)
        if not os.path.isfile(file_path):
            await cmd.channel.send(
                'Cannot find file `{}`.'.format(filename)
            )
            return

        match_info = LeagueMgr().league.match_info
        status_message = await cmd.channel.send(
            'Creating matches from file `{0}`... (Reading file)'.format(filename)
        )

        async with cmd.channel.typing():
            # Store file data
            desired_match_pairs = []
            with open(file_path) as file:
                for line in file:
                    racernames = line.rstrip('\n').split(',')
                    desired_match_pairs.append((racernames[0].lower(), racernames[1].lower(),))

            # Find all racers
            all_racers = dict()
            for racerpair in desired_match_pairs:
                all_racers[racerpair[0]] = None
                all_racers[racerpair[1]] = None

            await userlib.fill_user_dict(all_racers)
            console.debug('MakeMatchesFromFile: Filled user dict: {}'.format(all_racers))

            # Create Match objects
            matches = []
            not_found_matches = []

            async def make_single_match(racers):
                console.debug('MakeMatchesFromFile: Making match {0}-{1}'.format(racers[0], racers[1]))
                racer_1 = all_racers[racers[0]]
                racer_2 = all_racers[racers[1]]
                if racer_1 is None or racer_2 is None:
                    console.warning('Couldn\'t find racers for match {0}-{1}.'.format(
                        racers[0], racers[1]
                    ))
                    not_found_matches.append('`{0}`-`{1}`'.format(racers[0], racers[1]))
                    return

                new_match = await matchutil.make_match(
                    register=True,
                    racer_1_id=racer_1.user_id,
                    racer_2_id=racer_2.user_id,
                    match_info=match_info,
                    autogenned=True
                )
                if new_match is None:
                    console.debug('MakeMatchesFromFile: Match {0}-{1} not created.'.format(racers[0], racers[1]))
                    not_found_matches.append('{0}-{1}'.format(racers[0], racers[1]))
                    return

                matches.append(new_match)
                console.debug('MakeMatchesFromFile: Created {0}-{1}'.format(
                    new_match.racer_1.rtmp_name, new_match.racer_2.rtmp_name)
                )

            for racer_pair in desired_match_pairs:
                await make_single_match(racer_pair)

            matches = sorted(matches, key=lambda m: m.matchroom_name)

            await status_message.edit(
                content='Creating matches from file `{0}`... (Creating race rooms)'.format(filename)
            )
            console.debug('MakeMatchesFromFile: Matches to make: {0}'.format(matches))

            # Create match channels
            for match in matches:
                console.info('MakeMatchesFromFile: Creating {0}...'.format(match.matchroom_name))
                new_room = await matchchannelutil.make_match_room(match=match, register=False)
                await new_room.send_channel_start_text()

            # Report on uncreated matches
            uncreated_str = ''
            for match_str in not_found_matches:
                uncreated_str += match_str + ', '
            if uncreated_str:
                uncreated_str = uncreated_str[:-2]

            if uncreated_str:
                report_str = 'The following matches were not made: {0}'.format(uncreated_str)
            else:
                report_str = 'All matches created successfully.'

        await status_message.edit(
            content='Creating matches from file `{0}`... done. {1}'.format(filename, report_str)
        )