def __init__(self):
     super().__init__()
     self.user_agent = config.get().cftools_application_id
     self.client_id = config.get().cftools_client_id
     secret = config.get().cftools_secret
     self.hashed_secret = hashlib.sha256(secret.encode('utf-8')).hexdigest()
     self.logged_in = False
     self.logged_in_time = None
     self.access_token = None
     self.refresh_token = None
     self.service_tokens = dict()
     self.request_lock = asyncio.Lock()
     self.leaderboard_cache = LeaderboardCache()
Example #2
0
 async def update_member_count(self):
     if config.get().discord_member_count_channel_id:
         client: discord.Client = discord_service.get_service_manager(
         ).client
         if not client or not client.is_ready():
             log.warning('client not ready')
             return
         channel: discord.VoiceChannel = client.get_channel(
             config.get().discord_member_count_channel_id)
         count = channel.guild.member_count
         discord_member_count_string = config.get(
         ).discord_member_count_format.format(count=count)
         await channel.edit(name=discord_member_count_string)
         log.info(f'Update member count: {discord_member_count_string}')
 async def set_presence(self):
     if config.get().presence is not None and len(
             config.get().presence) > 0:
         if config.get().presence_type == 'watching':
             activity_type = discord.ActivityType.watching
         elif config.get().presence_type == 'listening':
             activity_type = discord.ActivityType.listening
         else:
             activity_type = discord.ActivityType.playing
         activity = discord.Activity(type=activity_type,
                                     name=config.get().presence)
     else:
         activity = None
     await self.client.wait_until_ready()
     await self.client.change_presence(activity=activity)
 async def handle_player_count_message(self, message: PlayerCount):
     if config.get_server(message.server_name).player_count_channel_id:
         player_count_string = config.get_server(
             message.server_name).player_count_format.format(
                 players=message.players,
                 slots=message.slots,
                 queue=message.queue,
                 time=message.time)
         if message.queue != '0':
             player_count_string += config.get_server(
                 message.server_name).player_count_queue_format.format(
                     players=message.players,
                     slots=message.slots,
                     queue=message.queue,
                     time=message.time)
         if self.player_counts[message.server_name] != player_count_string:
             if datetime.timedelta(minutes=6) < \
                     datetime.datetime.now() - self.last_player_count_update[message.server_name]:
                 # Rate limit is triggered when updating a channel name too often, so that's why we
                 # put a hard limit on how often the player count channel gets updated
                 channel: discord.TextChannel = self.client.get_channel(
                     config.get_server(
                         message.server_name).player_count_channel_id)
                 await channel.edit(name=player_count_string)
                 self.last_player_count_update[
                     message.server_name] = datetime.datetime.now()
                 self.player_counts[
                     message.server_name] = player_count_string
                 log.info(
                     f'log {message.server_name}: Update player count: {player_count_string}'
                 )
                 if config.get().log_player_count_updates:
                     self.log_rollup[message.server_name].append(
                         f'Update player count: {player_count_string}')
 async def service(self):
     self.client = client.CarimClient()
     await self.client.login(config.get().token)
     asyncio.create_task(self.client.connect())
     await self.set_presence()
     while True:
         await asyncio.sleep(1)
         await self.flush_log()
Example #6
0
async def start_service_managers():
    await discord_service.get_service_manager().start()
    await member_count.get_service_manager().start()

    if config.get().cftools_application_id is not None:
        await omega_service.get_service_manager().start()

    for server_name in config.get_server_names():
        asyncio.create_task(start_server(server_name))
Example #7
0
    async def on_message(self, message: discord.Message):
        if message.author == self.user:
            return

        for server_name in config.get_server_names():
            if config.get_server(server_name).rcon_password is not None:
                if message.channel.id == config.get_server(
                        server_name).chat_channel_id:
                    await arguments.process_chat(server_name, message)
                elif message.channel.id == config.get_server(server_name).admin_channel_id \
                        and message.content.startswith('--'):
                    args = shlex.split(message.content, comments=True)
                    try:
                        parsed_args, remaining_args = arguments.message_parser.parse_known_args(
                            args)
                    except (ValueError, argparse.ArgumentError):
                        log.info(f'invalid command {message.content}')
                        return
                    if remaining_args == args:
                        log.info(f'invalid command {message.content}')
                        asyncio.create_task(
                            discord_service.get_service_manager().send_message(
                                discord_service.Response(
                                    server_name,
                                    f'invalid command\n`{message.content}`')))
                    else:
                        await arguments.process_message_args(
                            server_name, parsed_args, message)

        if message.channel.id in config.get(
        ).user_channel_ids and message.content.startswith('--'):
            args = shlex.split(message.content, comments=True)
            try:
                parsed_args, remaining_args = arguments.user_message_parser.parse_known_args(
                    args)
                if remaining_args == args:
                    return
            except (ValueError, argparse.ArgumentError):
                log.info(f'invalid command {message.content}')
                asyncio.create_task(
                    discord_service.get_service_manager().send_message(
                        discord_service.UserResponse(
                            message.channel.id,
                            f'invalid command\n`{message.content}`')))
                return
            await arguments.process_user_message_args(message.channel.id,
                                                      parsed_args)

        for custom_command in config.get().custom_commands:
            custom_command: message_builder.Response = custom_command
            if custom_command.enabled:
                if message.channel.id in custom_command.channels or len(
                        custom_command.channels) == 0:
                    if re.match(custom_command.command, message.content):
                        embed = custom_command.generate()
                        if len(embed) <= 6000:
                            await message.channel.send(embed=embed)
                        else:
                            await message.channel.send(
                                f'Message longer than 6000 character limit: {len(embed)}'
                            )
Example #8
0
async def process_user_message_args(channel_id, parsed_args):
    if 'leaderboard' in parsed_args:
        query_stat = parsed_args.leaderboard[0]
        server_index = parsed_args.leaderboard[1]
        try:
            server_name = config.get().server_names[server_index]
        except IndexError:
            index_names = json.dumps(
                {k: v
                 for k, v in enumerate(config.get().server_names)})
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(
                        channel_id, 'Leaderboard',
                        f'Invalid index. Valid options:\n{index_names}')))
            return
        if config.get().cf_cloud_application_id is not None:
            stat_options = ('deaths', 'kills', 'playtime', 'longest_kill',
                            'longest_shot', 'suicides', 'kdratio')
        else:
            stat_options = ('deaths', 'kills', 'playtime', 'damage_dealt',
                            'damage_taken', 'hits', 'hitted',
                            'longest_kill_distance', 'kdratio')
        if query_stat not in stat_options:
            asyncio.create_task(discord_service.get_service_manager(
            ).send_message(
                discord_service.UserResponse(
                    channel_id, 'Leaderboard',
                    f'Invalid leaderboard stat. Valid options:\n{stat_options}'
                )))
            return

        if config.get().cf_cloud_application_id is not None:
            message = cf_cloud_service.Leaderboard(server_name, query_stat)
            await cf_cloud_service.get_service_manager().send_message(message)
        else:
            message = omega_service.Leaderboard(server_name, query_stat)
            await omega_service.get_service_manager().send_message(message)

        try:
            result = await message.result

            result_data = []

            if config.get().cf_cloud_application_id is not None:
                stats = set()
                for r in result.get('leaderboard', list()):
                    stats |= set(k for k in r.keys()
                                 if k not in ('cftools_id', 'rank',
                                              'latest_name'))
                stats = tuple(stats)
                result_data.append(
                    [stat for stat in ('#', ) + ('name', ) + stats])
                log.debug(result_data)
                for r in result.get('leaderboard', list()):
                    line_items = [r['rank']]
                    line_items += [r['latest_name']]
                    for stat in stats:
                        value = r.get(stat, '0')
                        if isinstance(value, float):
                            line_items += [f'{value:.2f}']
                        elif stat == 'playtime':
                            line_items += [
                                str(datetime.timedelta(seconds=value))
                            ]
                        else:
                            line_items += [value]
                    result_data.append(line_items)
            else:
                stats = None
                for r in result.get('users', list()):
                    if stats is None:
                        stats = tuple(k for k in r.keys()
                                      if k not in ('cftools_id', 'rank',
                                                   'latest_name'))
                        result_data.append(
                            [stat for stat in ('#', ) + ('name', ) + stats])
                    line_items = [r['rank']]
                    line_items += [r['latest_name']]
                    for stat in stats:
                        if isinstance(r[stat], float):
                            line_items += [f'{r[stat]:.2f}']
                        elif stat == 'playtime':
                            line_items += [
                                str(datetime.timedelta(seconds=r[stat]))
                            ]
                        else:
                            line_items += [r[stat]]
                    result_data.append(line_items)
            s = [[str(e) for e in row] for row in result_data]
            lens = [max(map(len, col)) for col in zip(*s)]
            fmt = ' '.join('{{:{}}}'.format(x) for x in lens)
            table = [fmt.format(*row) for row in s]
            formatted_result = '\n'.join(table)
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(channel_id, 'Leaderboard',
                                                 formatted_result)))
        except (AttributeError, asyncio.CancelledError):
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(channel_id, 'Leaderboard',
                                                 'query failed')))
    if 'stats' in parsed_args:
        steam64 = parsed_args.stats[0]
        server_index = parsed_args.stats[1]
        try:
            server_name = config.get().server_names[server_index]
        except IndexError:
            index_names = json.dumps(
                {k: v
                 for k, v in enumerate(config.get().server_names)})
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(
                        channel_id, 'Stats',
                        f'Invalid index. Valid options:\n{index_names}')))
            return

        if config.get().cf_cloud_application_id is not None:
            message = cf_cloud_service.Stats(server_name, steam64)
            await cf_cloud_service.get_service_manager().send_message(message)
        else:
            message = omega_service.Stats(server_name, steam64)
            await omega_service.get_service_manager().send_message(message)

        try:
            result = await message.result
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(channel_id, 'Stats', result)))
        except asyncio.CancelledError:
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(channel_id, 'Stats',
                                                 'query timed out')))
Example #9
0
async def process_user_message_args(channel_id, parsed_args):
    if 'leaderboard' in parsed_args:
        stat = parsed_args.leaderboard[0]
        server_index = parsed_args.leaderboard[1]
        try:
            server_name = config.get().server_names[server_index]
        except IndexError:
            index_names = json.dumps(
                {k: v
                 for k, v in enumerate(config.get().server_names)})
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(
                        channel_id, 'Leaderboard',
                        f'Invalid index. Valid options:\n{index_names}')))
            return
        stat_options = ('deaths', 'kills', 'playtime', 'damage_dealt',
                        'damage_taken', 'hits', 'hitted',
                        'longest_kill_distance', 'kdratio')
        if stat not in stat_options:
            asyncio.create_task(discord_service.get_service_manager(
            ).send_message(
                discord_service.UserResponse(
                    channel_id, 'Leaderboard',
                    f'Invalid leaderboard stat. Valid options:\n{stat_options}'
                )))
            return
        omega_message = omega_service.Leaderboard(server_name, stat)
        await omega_service.get_service_manager().send_message(omega_message)
        try:
            result = await omega_message.result
            result_data = []
            stats = None
            for r in result.get('users', list()):
                if stats is None:
                    stats = tuple(k for k in r.keys()
                                  if k not in ('cftools_id', 'rank',
                                               'latest_name'))
                    result_data.append(
                        [stat for stat in ('rank', ) + stats + ('name', )])
                line_items = [r['rank']] + [
                    f'{r[stat]:.2f}' if isinstance(r[stat], float) else r[stat]
                    for stat in stats
                ] + [r['latest_name']]
                result_data.append(line_items)
            s = [[str(e) for e in row] for row in result_data]
            lens = [max(map(len, col)) for col in zip(*s)]
            fmt = ' '.join('{{:{}}}'.format(x) for x in lens)
            table = [fmt.format(*row) for row in s]
            formatted_result = '```\n' + '\n'.join(table) + '\n```'
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(channel_id, 'Leaderboard',
                                                 formatted_result)))
        except asyncio.CancelledError:
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(channel_id, 'Leaderboard',
                                                 'query timed out')))
    if 'stats' in parsed_args:
        steam64 = parsed_args.stats[0]
        server_index = parsed_args.stats[1]
        try:
            server_name = config.get().server_names[server_index]
        except IndexError:
            index_names = json.dumps(
                {k: v
                 for k, v in enumerate(config.get().server_names)})
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(
                        channel_id, 'Stats',
                        f'Invalid index. Valid options:\n{index_names}')))
            return
        omega_message = omega_service.Stats(server_name, steam64)
        await omega_service.get_service_manager().send_message(omega_message)
        try:
            result = await omega_message.result
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(channel_id, 'Stats', result)))
        except asyncio.CancelledError:
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.UserResponse(channel_id, 'Stats',
                                                 'query timed out')))
Example #10
0
def set_log_verbosity(verbosity):
    if config.get().debug:
        verbosity = max(verbosity, 1)

    if verbosity > 0:
        logging.getLogger(carim_discord_bot.__name__).setLevel(logging.DEBUG)