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 create_datagram_endpoint(self):
     self.transport, self.rcon_protocol = await asyncio.get_running_loop(
     ).create_datagram_endpoint(
         connection.ProtocolFactory(self.server_name,
                                    self.rcon_registrar).get,
         remote_addr=(config.get_server(self.server_name).ip,
                      config.get_server(self.server_name).rcon_port))
Exemple #3
0
async def start_server(server_name):
    await steam_service.get_service_manager(server_name).start()
    if config.get_server(server_name).rcon_password is not None:
        await rcon_service.get_service_manager(server_name).start()
        for i in range(len(config.get_server(server_name).scheduled_commands)):
            sm = scheduled_command.get_service_manager(server_name, i)
            await sm.start()
    if config.get_server(server_name).player_count_channel_id is not None:
        await player_count.get_service_manager(server_name).start()
 def connection_made(self, transport: asyncio.DatagramTransport):
     self.transport = transport
     data = protocol.Packet(
         protocol.Login(password=config.get_server(
             self.server_name).rcon_password)).generate()
     log.info(f'{self.server_name} sending login')
     self.send_rcon_datagram(data)
Exemple #5
0
    async def query_stats(self, server_name, steam64):
        server_api_id = config.get_server(server_name).cf_cloud_server_api_id
        request = await self.locking_request('GET', f'{API}/v1/server/{server_api_id}/lookup',
                                             payload=dict(identifier=steam64))
        if request.status_code != 200 or not request.json().get('status', False):
            return 'Lookup failed'
        result = request.json()
        cftools_id = result.get('cftools_id')

        request = await self.locking_request('GET', f'{API}/v1/server/{server_api_id}/player',
                                             payload=dict(cftools_id=cftools_id))
        if request.status_code != 200 or not request.json().get('status', False):
            return 'Query failed'
        result = request.json()
        log.info(f'result: {result}')
        user = result.get('user', dict())
        stats = user.get('stats', dict())

        response = dict(
            playtime=str(datetime.timedelta(seconds=user.get('playtime', 0))),
            sessions=user.get('sessions', 0),
            average_engagement_distance=f'{stats.get("average_engagement_distance", 0):0.2f}m',
            kills=stats.get('kills', 0),
            deaths=stats.get('deaths', 0),
            longest_kill_distance=f'{stats.get("longest_kill_distance", 0)}m',
            longest_kill_weapon=stats.get('longest_kill_weapon', '')
        )

        return json.dumps(response, indent=1, ensure_ascii=False)
 def __init__(self, server_name, index):
     super().__init__()
     self.server_name = server_name
     self.index = index
     self.command = config.get_server(
         self.server_name).scheduled_commands[self.index]
     self.command['next'] = 'unknown'
    async def query_stats(self, server_name, steam64):
        request = await self.locking_request('GET',
                                             f'{API}/v1/user/lookup',
                                             payload=dict(
                                                 identity=steam64,
                                                 identity_type='steam64'))
        result = request.json()
        if not result.get('status', False):
            return 'Invalid steam64'
        cftools_id = result.get('cftools_id')

        service_token = self.get_service_token(server_name)
        request = await self.locking_request(
            'GET',
            f'{API}/v1/user/{service_token.token}/service',
            payload=dict(platform='omega', cftools_id=cftools_id))

        result = request.json()
        user = result.get('user', dict()).get(
            config.get_server(server_name).cftools_service_id, dict())
        stats = user.get('stats', dict())

        response = dict(
            playtime=str(datetime.timedelta(seconds=user.get('playtime', 0))),
            sessions=user.get('sessions', 0),
            average_engagement_distance=
            f'{stats.get("average_engagement_distance", 0):0.2f}m',
            kills=stats.get('kills', 0),
            deaths=stats.get('deaths', 0),
            longest_kill_distance=f'{stats.get("longest_kill_distance", 0)}m',
            longest_kill_weapon=stats.get('longest_kill_weapon', ''))

        return json.dumps(response, indent=1)
 def process_packet(self, packet):
     if isinstance(packet.payload, protocol.Command) or isinstance(
             packet.payload, protocol.SplitCommand):
         asyncio.create_task(
             self.rcon_registrar.incoming(packet.payload.sequence_number,
                                          packet))
     elif isinstance(packet.payload, protocol.Message):
         message = packet.payload.message
         log.debug(f'{self.server_name} message: {message}')
         disconnect = re.compile(r'Player .* disconnected')
         if disconnect.match(message):
             parts = message.split()
             disconnect_message = ' '.join(parts[2:])
             log.info(
                 f'{self.server_name} login event {disconnect_message}')
             if config.get_server(
                     self.server_name).chat_show_connect_disconnect_notices:
                 asyncio.create_task(
                     discord_service.get_service_manager().send_message(
                         discord_service.Chat(self.server_name,
                                              disconnect_message)))
         connect = re.compile(r'Verified GUID .* of player .*')
         if connect.match(message):
             parts = message.split()
             name = ' '.join(parts[6:])
             login_message = f'{name} connected'
             log.info(f'{self.server_name} login event {login_message}')
             if config.get_server(
                     self.server_name).chat_show_connect_disconnect_notices:
                 asyncio.create_task(
                     discord_service.get_service_manager().send_message(
                         discord_service.Chat(self.server_name,
                                              login_message)))
         chat = re.compile(r'^\((Global|Side)\).*:.*')
         if chat.match(message):
             _, _, content = message.partition(' ')
             asyncio.create_task(
                 discord_service.get_service_manager().send_message(
                     discord_service.Chat(self.server_name, content)))
         if len(message) > 0:
             asyncio.create_task(
                 discord_service.get_service_manager().send_message(
                     discord_service.Log(self.server_name, message)))
         return protocol.Packet(
             protocol.Message(packet.payload.sequence_number)).generate()
     return None
 async def keep_alive(self):
     seq_number = await self.rcon_registrar.get_next_sequence_number()
     packet = protocol.Packet(protocol.Command(seq_number))
     future = asyncio.get_running_loop().create_future()
     await self.rcon_registrar.register(packet.payload.sequence_number,
                                        future)
     self.rcon_protocol.send_rcon_datagram(packet.generate())
     await future
     if config.get_server(self.server_name).log_rcon_keep_alive:
         await self.discord_log('keep alive')
 async def handle_message(self, message: Message):
     await self.client.wait_until_ready()
     if isinstance(message, PlayerCount):
         await self.handle_player_count_message(message)
     elif isinstance(message, Log):
         log.info(f'log {message.server_name}: {message.text}')
         self.log_rollup[message.server_name] += f'{message.text}'.split(
             '\n')
     elif isinstance(message, Response):
         log.info(f'response {message.server_name}: {message.text}')
         self.log_rollup[message.server_name] += f'{message.text}'.split(
             '\n')
     elif isinstance(message, Chat):
         log.info(f'chat {message.server_name}: {message.content}')
         channel_id = config.get_server(message.server_name).chat_channel_id
         if channel_id:
             if config.get_server(message.server_name).chat_ignore_regex:
                 r = re.compile(
                     config.get_server(
                         message.server_name).chat_ignore_regex)
                 if r.match(message.content):
                     log.info(
                         f'chat ignored {message.server_name}: {message.content}'
                     )
                     return
             channel: discord.TextChannel = self.client.get_channel(
                 channel_id)
             await channel.send(embed=discord.Embed(
                 description=message.content))
     elif isinstance(message, UserResponse):
         log.info(f'user message {message.channel_id}: {message.text}')
         channel: discord.TextChannel = self.client.get_channel(
             message.channel_id)
         for m in build_formatted_fields(message.title,
                                         message.text.split('\n')):
             embed_dict = {
                 'color': get_server_color(message.title),
                 'fields': m
             }
             await channel.send(embed=discord.Embed.from_dict(embed_dict))
Exemple #11
0
 async def query_leaderboard(self, server_name, stat):
     cached = self.leaderboard_cache.get(server_name, stat)
     if cached is not None:
         log.debug('using cached value')
         return cached
     else:
         server_api_id = config.get_server(server_name).cf_cloud_server_api_id
         request = await self.locking_request('GET', f'{API}/v1/server/{server_api_id}/leaderboard',
                                              payload=dict(stat=stat, limit=20, order=-1))
         if request.status_code != 200 or not request.json().get('status', False):
             return 'Query failed'
         result = request.json()
         result['leaderboard'] = [{'rank': entry['rank'], 'latest_name': entry['latest_name'], stat: entry[stat]} for entry in result.get('leaderboard', list())]
         self.leaderboard_cache.set(server_name, stat, result)
         return result
Exemple #12
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)}'
                            )
 def get_service_token(self, server_name):
     try:
         return self.service_tokens[config.get_server(
             server_name).cftools_service_id]
     except IndexError:
         return None
 async def send_rolled_log(self, server_name, fields):
     channel: discord.TextChannel = self.client.get_channel(
         config.get_server(server_name).admin_channel_id)
     embed_dict = {'color': get_server_color(server_name), 'fields': fields}
     await channel.send(embed=discord.Embed.from_dict(embed_dict))
Exemple #15
0
async def process_admin_args(server_name, parsed_args, message):
    if parsed_args.list_priority:
        cf_message = omega_service.QueuePriorityList(server_name)
        await omega_service.get_service_manager().send_message(cf_message)
        try:
            result = await cf_message.result
            result = json.dumps(result, indent=1)
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.Response(
                        server_name, f'**Queue Priority**\n```{result}```')))
        except asyncio.CancelledError:
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.Response(
                        server_name, f'**Queue Priority**\nquery timed out')))
    if 'create_priority' in parsed_args:
        cftools_id, comment, days = parsed_args.create_priority
        try:
            days = int(days)
            if days != -1:
                expires_at = datetime.datetime.now(
                    tz=datetime.timezone.utc) + datetime.timedelta(days=days)
                expires_at = expires_at.timestamp()
            else:
                expires_at = -1
        except ValueError:
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.Response(server_name,
                                             f'Invalid days: {days}')))
            return
        cf_message = omega_service.QueuePriorityCreate(server_name, cftools_id,
                                                       comment, expires_at)
        await omega_service.get_service_manager().send_message(cf_message)
        try:
            result = await cf_message.result
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.Response(
                        server_name, f'**Create Priority**\n{result}')))
        except asyncio.CancelledError:
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.Response(
                        server_name, f'**Create Priority**\nquery timed out')))
    if 'revoke_priority' in parsed_args:
        cftools_id = parsed_args.revoke_priority
        cf_message = omega_service.QueuePriorityRevoke(server_name, cftools_id)
        await omega_service.get_service_manager().send_message(cf_message)
        try:
            result = await cf_message.result
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.Response(
                        server_name, f'**Revoke Priority**\n{result}')))
        except asyncio.CancelledError:
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.Response(
                        server_name, f'**Revoke Priority**\nquery timed out')))
    if 'command' in parsed_args:
        if parsed_args.command is None:
            command = 'commands'
        else:
            command = parsed_args.command
        service_message = rcon_service.Command(server_name, command)
        await rcon_service.get_service_manager(server_name).send_message(
            service_message)
        try:
            result = await service_message.result
            asyncio.create_task(discord_service.get_service_manager(
            ).send_message(
                discord_service.Response(
                    server_name,
                    f'**{command}**\n{str(result) if result else "success"}')))
        except asyncio.CancelledError:
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.Response(
                        server_name, f'**{command}**\nquery timed out')))
    if 'shutdown' in parsed_args:
        if parsed_args.shutdown is not None:
            delay = parsed_args.shutdown
        else:
            delay = 0
        service_message = rcon_service.SafeShutdown(server_name, delay)
        await rcon_service.get_service_manager(server_name).send_message(
            service_message)
    if parsed_args.status:
        commands_info = list()
        commands_config = config.get_server(server_name).scheduled_commands
        for i, command_config in enumerate(commands_config):
            sc = scheduled_command.get_service_manager(server_name, i)
            next_run = sc.command['next']
            if not isinstance(next_run, str):
                next_run = datetime.timedelta(seconds=next_run)
                next_run -= datetime.timedelta(
                    microseconds=next_run.microseconds)
                next_run = str(next_run)
            c_info = dict(index=i,
                          command=sc.command['command'],
                          interval=sc.command['interval'],
                          next_run=next_run)
            if sc.command.get('skip', False):
                c_info['skip_next'] = True
            commands_info.append(c_info)
        asyncio.create_task(discord_service.get_service_manager().send_message(
            discord_service.Response(
                server_name, f'```{json.dumps(commands_info, indent=1)}```')))
    if 'skip' in parsed_args:
        i = parsed_args.skip
        if not 0 <= i < len(scheduled_command.services.get(
                server_name, list())):
            asyncio.create_task(
                discord_service.get_service_manager().send_message(
                    discord_service.Response(server_name, 'Invalid index')))
        else:
            await scheduled_command.get_service_manager(
                server_name,
                i).send_message(scheduled_command.Skip(server_name))
    if parsed_args.kill:
        sys.exit(0)
    if parsed_args.version:
        asyncio.create_task(discord_service.get_service_manager().send_message(
            discord_service.Response(server_name,
                                     f'{carim_discord_bot.VERSION}')))
Exemple #16
0
 async def service(self):
     while True:
         await asyncio.sleep(
             config.get_server(
                 self.server_name).player_count_update_interval)
         await self.update_player_count()
 async def handle_message(self, message: managed_service.Message):
     if isinstance(message, Query):
         await query.query(config.get_server(self.server_name).ip,
                           config.get_server(self.server_name).steam_port,
                           message.result)