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()
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()
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))
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)}' )
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')))
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')))
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)