Пример #1
0
    async def hide(self, message: Message, location: str):
        config = State.get_config(message.guild.id)
        channel: TextChannel = message.channel
        connection = self.roleplay.get_connection(channel.name, location)
        if not connection:
            await channel.send(
                f'No connection from {channel.name} to {location}.',
                delete_after=60*60
            )
            return

        is_hidden = config['connections'][connection.name].get('h', False)
        if is_hidden:
            await channel.send(
                f'Connection from {channel.name} to {location} is already '
                'hidden.',
                delete_after=60*60
            )
        else:
            config['connections'][connection.name]['h'] = True
            State.save_config(message.guild.id, config)
            await self.bot.save_guild_config(message.guild, config)
            text = f'A connection between {channel.name} and {location} has ' \
                   f'been hidden.'
            await channel.send(text)
            await message.delete()
            await self.bot.get_chronicle(message.guild).log_announcement(
                channel, text
            )
Пример #2
0
 async def move_reset(self, message, user: Optional[str]):
     move_timers = State.get_var(message.guild.id, 'move_timers')
     if not move_timers:
         move_timers = defaultdict(lambda: datetime.min)
     for player in message.mentions:
         move_timers[player.id] = datetime.min
     State.set_var(message.guild.id, 'move_timers', move_timers)
Пример #3
0
    async def start_game(self, message: Message, roleplay: str, players: str):
        config = State.get_config(message.guild.id)
        if config and 'rp' in config and config['rp']:
            await message.channel.send(
                f'Cannot start roleplay, {config["rp"]} is already in progress.'
            )
            return

        if roleplay not in self.bot.roleplays:
            await message.channel.send(
                f'Cannot start roleplay, unknown roleplay name {roleplay}'
            )
            return

        loading_message = await message.channel.send(f'Setting up new game...')
        config = {'rp': roleplay, 'connections': {}}
        await self.bot.save_guild_config(message.guild, config)
        await self.bot.refresh_from_config(message.guild, config, True)
        await message.author.add_roles(State.get_admin_role(message.guild.id))

        self.roleplay = self.bot.roleplays[roleplay]
        player_role = State.get_player_role(message.guild.id)
        for member in message.mentions:
            await member.add_roles(player_role)
        await self.move_force(message, self.roleplay.starting_room, None)

        await message.channel.send(f'The game begins.')
        await loading_message.delete()
        await message.delete()
Пример #4
0
    async def toggle_key(
            self, message: Message, room1: str, room2: str, user: str
    ):
        connection = self.roleplay.get_connection(room1, room2)
        if not connection:
            await message.channel.send(
                f'No connection from {room1} to {room2}.'
            )
            return

        config = State.get_config(message.guild.id)
        keys = config['connections'][connection.name]['k']
        for member in message.mentions:
            if member.id not in keys:
                keys.append(member.id)
                await message.channel.send(
                    f'{member.mention} can now lock and unlock the connection '
                    f'from {room1} to {room2}.'
                )
            else:
                keys.remove(member.id)
                await message.channel.send(
                    f'{member.mention} can no longer lock and unlock the '
                    f'connection from {room1} to {room2}.'
                )
        State.save_config(message.guild.id, config)
        await self.bot.save_guild_config(message.guild, config)
        await message.delete()
Пример #5
0
    async def on_message(self, message: Message):
        # Ignore all DMs
        if message.guild is None:
            return

        # Ignore all messages from this bot
        if message.author == self.user:
            return

        plugins = self.get_all_plugins(message.guild)

        # Do a special case for !help, since we need an overview of all plugins
        # to make it complete
        if message.clean_content == PluginCommand.PREFIX + 'help':
            help_message = '\n'.join(
                p.get_help(State.is_admin(message.author)) for p in plugins
            )
            if not help_message:
                help_message = 'No commands are currently available.'
            else:
                help_message = \
                    'The following commands are currently available:\n' \
                    + help_message
            await reply(message, help_message)
            return

        for plugin in plugins:
            if await plugin.process_message(message):
                break
        else:
            roleplay = self.get_roleplay_for_guild(message.guild.id)
            if roleplay and message.channel.name in roleplay.rooms:
                await self.get_chronicle(message.guild).log_player_message(
                    message.author, message.channel, message.clean_content
                )
Пример #6
0
    async def _lock_or_unlock(
            self, message: Message, location: str, lock: bool = True
    ):
        config = State.get_config(message.guild.id)
        channel: TextChannel = message.channel
        connection = self.roleplay.get_connection(channel.name, location)
        if not connection:
            await channel.send(
                f'No connection from {channel.name} to {location}.',
                delete_after=60*60
            )
            return
        connection_config = config['connections'][connection.name]

        if not State.is_admin(message.author) \
                and message.author.id not in connection_config['k']:
            await channel.send(
                f'{message.author.mention} does not have the key to '
                f'{location}.',
                delete_after=60*60
            )
            return

        locked = connection_config['l']
        if locked and lock or not locked and not lock:
            await channel.send(
                f'Access to {location} is already '
                f'{"locked" if lock else "unlocked"}.',
                delete_after=60*60
            )
            return
        self._update_connection(
            config["connections"], channel.name, location, locked=lock
        )
        State.save_config(message.guild.id, config)
        await self.bot.save_guild_config(message.guild, config)
        await channel.send(
            f'{"Locked" if lock else "Unlocked"} access to {location}.'
        )
        await message.delete()
        name = 'the GM' \
            if State.is_admin(message.author) else message.author.display_name
        await self.bot.get_chronicle(message.guild).log_announcement(
            channel,
            f'Access from **{message.channel.name}** to **{location}** '
            f'{"" if lock else "un"}locked by **{name}**'
        )
Пример #7
0
 async def view_remove(self, message: Message, room: str) -> None:
     config = State.get_config(message.guild.id)
     if 'views' not in config:
         config['views'] = {}
     if room in config['views'] \
             and message.channel.name in config['views'][room]:
         config['views'][room].remove(message.channel.name)
         await self.bot.save_guild_config(message.guild, config)
         await message.channel.send(f'Remote view to {room} removed.')
     else:
         await message.channel.send(
             f'No remote view to {room} set up in this channel.'
         )
Пример #8
0
 async def view_add(self, message: Message, room: str) -> None:
     config = State.get_config(message.guild.id)
     if 'views' not in config:
         config['views'] = {}
     if room not in config['views']:
         config['views'][room] = []
     if message.channel.name not in config['views'][room]:
         config['views'][room].append(message.channel.name)
         await self.bot.save_guild_config(message.guild, config)
         await message.channel.send(f'Remote view to {room} added.')
     else:
         await message.channel.send(
             f'There is already a remote view to {room} in this channel.'
         )
Пример #9
0
async def character(request: Request):
    try:
        guild_id = int(request.match_info['guild_id'])
    except (KeyError, ValueError):
        guild_id = None
    char = None
    if guild_id is not None:
        user_id = request.match_info['user_id']
        character_id = request.match_info['character_id']
        config = State.get_config(guild_id)
        characters_data = config.get('characters', {})
        char = characters_data.get(user_id, {}).get('characters',
                                                    {}).get(character_id)
        if char:
            move_timers = State.get_var(guild_id, 'move_timers')
            move_countdown_time = None
            if move_timers:
                move_countdown_time = move_timers.get(int(user_id))
            char = {
                **char, "move_countdown_time":
                move_countdown_time.astimezone().isoformat()
                if move_countdown_time else ""
            }
    return {'character': char}
Пример #10
0
 async def view_list(self, message: Message) -> None:
     config = State.get_config(message.guild.id)
     all_views = []
     for user_id, views in config.get('views', {}).items():
         user: Member = message.guild.get_member(user_id)
         if user and views:
             all_views.append(
                 f'**{user.display_name}:** ' + ', '.join(views)
             )
     if all_views:
         await message.channel.send(
             'The following remote views are set up:\n'
             + '\n'.join(all_views)
         )
     else:
         await message.channel.send('No remote views are set up.')
Пример #11
0
async def global_variables(request: Request):
    bot: RoleplayBot = request.app['bot']
    roleplays = []
    for guild in bot.guilds:
        if bot.get_roleplay_for_guild(guild.id):
            roleplays.append({'id': guild.id, 'name': guild.name})

    try:
        guild_id = int(request.match_info['guild_id'])
    except (KeyError, ValueError):
        guild_id = None
    roleplay = None
    if guild_id is not None:
        config = State.get_config(guild_id)
        roleplay_data = bot.get_roleplay_for_guild(guild_id)
        if roleplay_data:
            guild = bot.get_guild(guild_id)
            roleplay = {
                'id':
                guild_id,
                'name':
                guild.name,
                'description':
                roleplay_data.description,
                'characters': [{
                    'user_id': user_id,
                    'id': character_id,
                    **character,
                }
                               for user_id, user_characters in config.get(
                                   'characters', {}).items()
                               for character_id, character in
                               user_characters['characters'].items()],
                'commands': [
                    command for plugin in bot.get_all_plugins(guild)
                    for command in sorted(plugin.commands.values(),
                                          key=lambda c: c.name)
                    if command.enabled and not command.requires_admin
                    and not command.hidden
                ]
            }
    return {
        'base_url': bot_config['rpbot']['base_url'],
        'roleplay': roleplay,
        'roleplays': sorted(roleplays, key=itemgetter('name')),
    }
Пример #12
0
 async def log(
     self,
     source_channel: Optional[str],
     message: str,
     extra_only: bool = False,
 ) -> None:
     config = State.get_config(self.guild.id)
     if not config:
         return
     views = config.get('views', {})
     dest_channels = set() if extra_only else {self.channel_name}
     if source_channel in views:
         dest_channels.update(views[source_channel])
     for channel in self.guild.text_channels:
         if channel.name in dest_channels:
             await channel.send(message[:MAX_MESSAGE_LENGTH])
             if message[MAX_MESSAGE_LENGTH:]:
                 await channel.send(message[MAX_MESSAGE_LENGTH:])
Пример #13
0
    async def status(self, message: Message) -> None:
        statuses = []
        for member in message.channel.members:
            if State.is_player(member):
                try:
                    character = await self._get_active_character(member)
                except CommandException:
                    continue
                if character['status']:
                    status = f'**{character["name"]}:** {character["status"]}'
                else:
                    status = f'**{character["name"]}**'
                statuses.append(status)
        for npc in self._get_npcs_in_room(message.guild, message.channel.name):
            statuses.append(f'**{npc["name"]}:** {npc["status"]}')

        if statuses:
            text = 'The following people are here:'
            for status in sorted(statuses):
                text += f'\n{status}'
            await reply(message, text)
        else:
            await reply(message, 'Nobody is here.')
Пример #14
0
    async def end_game(self, message: Message):
        loading_message = await message.channel.send(f'Ending game...')
        config = {}
        guild: Guild = message.guild
        State.save_config(guild.id, config)
        State.save_plugins(guild.id, [])
        await self.bot.save_guild_config(guild, config)

        player_role = State.get_player_role(guild.id)
        observer_role = State.get_observer_role(guild.id)
        for player in player_role.members:
            await player.remove_roles(player_role)
            await player.add_roles(observer_role)

        await message.channel.send(f'The game ends.')
        await loading_message.delete()
        await message.delete()

        await self.bot.refresh_from_config(guild, config)
Пример #15
0
 async def mark_observer(self, message: Message, user: str):
     await self._mark_with_role(
         message, State.get_observer_role(message.guild.id), 'an observer'
     )
Пример #16
0
 def _get_config(guild: Guild) -> Dict[str, Any]:
     config = State.get_config(guild.id)
     if not config or not config.get('rp'):
         raise CommandException(
             'No active roleplay, characters not available.')
     return config
Пример #17
0
 def get_all_plugins(self, guild: Guild) -> List[Plugin]:
     plugins = [
         BasePlugin(self, self.get_roleplay_for_guild(guild.id))
     ]
     plugins += State.get_plugins(guild.id)
     return plugins
Пример #18
0
    async def refresh_from_config(
            self, guild: Guild, config: Dict[str, Any], force_reset=False
    ):
        logging.info(f'Refreshing server state from config for {guild.name}')

        State.save_config(guild.id, config)
        if 'rp' not in config or not config['rp']:
            return

        modified_config = False
        roleplay = self.roleplays[config['rp']]
        if 'connections' not in config:
            config['connections'] = {}
            modified_config = True
        for connection in roleplay.connections:
            if connection.name not in config['connections']:
                config['connections'][connection.name] = {
                    'h': connection.hidden,
                    'l': connection.locked,
                    'k': []
                }
                modified_config = True
        if modified_config:
            State.save_config(guild.id, config)
            await self.save_guild_config(guild, config)

        # Pick all the plugins required by the roleplay
        plugins = []
        plugin_configs = roleplay.plugins
        if plugin_configs:
            for plugin_id, plugin_config \
                    in plugin_configs.items():
                # noinspection PyArgumentList
                plugins.append(
                    self.plugins[plugin_id](
                        self,
                        roleplay,
                        **plugin_config
                    )
                )
        State.save_plugins(guild.id, plugins)

        # Ensure the server state matches
        gm_role = await self.create_or_update_role(guild, roleplay.roles['gm'])
        player_role = await self.create_or_update_role(
            guild, roleplay.roles['player']
        )
        observer_role = await self.create_or_update_role(
            guild, roleplay.roles['observer']
        )
        State.save_roles(guild.id, gm_role, player_role, observer_role)

        sections: Dict[str, CategoryChannel] = {
            c.name: c for c in guild.categories
        }
        for channel_id, room in roleplay.rooms.items():
            source = guild
            if room.section:
                if room.section not in sections:
                    sections[room.section] = await guild.create_category(
                        room.section,
                    )
                section = sections[room.section]
                source = section
            channel: TextChannel = next(
                (c for c in source.text_channels if c.name == channel_id),
                None
            )
            overwrites = {
                guild.default_role: PermissionOverwrite(
                    read_messages=False
                ),
                self.user: PermissionOverwrite(read_messages=True),
                gm_role: PermissionOverwrite(
                    read_messages=True,
                    send_messages=True
                ),
                observer_role: PermissionOverwrite(
                    read_messages=True,
                    send_messages=False
                )
            }
            if channel:
                if not force_reset:
                    continue
                await channel.edit(topic=room.description)
                for target, overwrite in overwrites.items():
                    await channel.set_permissions(target, overwrite=overwrite)
            else:
                await source.create_text_channel(
                    channel_id, topic=room.description, overwrites=overwrites
                )
Пример #19
0
 async def reload(self, message: Message) -> None:
     self.bot.reload()
     await self.bot.refresh_from_config(
         message.guild, State.get_config(message.guild.id)
     )
     await message.channel.send('Roleplay definition and plugins reloaded.')
Пример #20
0
 def get_roleplay_for_guild(self, guild_id: int):
     config = State.get_config(guild_id)
     return self.roleplays[config['rp']] \
         if config and 'rp' in config and config['rp'] else None
Пример #21
0
 async def _save_config(self, guild: Guild) -> None:
     await self.bot.save_guild_config(guild, State.get_config(guild.id))
Пример #22
0
 async def move_all(self, message: Message, room: str):
     for player in State.get_player_role(message.guild.id).members:
         await self._move_player(player, room)
Пример #23
0
    async def move(self, message: Message, room: Optional[str]):
        connections = State.get_config(message.guild.id)['connections']
        channel: TextChannel = message.channel
        destinations = self._list_destinations(connections, channel.name)

        if not room:
            destination_list = '\n'.join(
                destination + (' *(locked)*' if locked else '')
                for destination, locked in destinations
            )
            await channel.send(
                (
                    'The following destinations are available from here:\n'
                    + destination_list
                ) if destination_list
                else 'No destinations are currently available.',
                delete_after=60*60
            )
            await message.delete()
            return

        for destination, locked in destinations:
            if destination == room:
                if locked:
                    await channel.send(
                        f'{room} is currently locked off.',
                        delete_after=60*60
                    )
                    await message.delete()
                    return
                break
        else:
            await channel.send(
                f'Cannot reach {room} from here.',
                delete_after=60*60
            )
            return

        move_timers = State.get_var(message.guild.id, 'move_timers')
        if not move_timers:
            move_timers = defaultdict(lambda: datetime.min)
        time_remaining = (
                move_timers[message.author.id] - datetime.now()
        ).total_seconds()
        if time_remaining > 0:
            minutes = time_remaining // 60
            await channel.send(
                'Must wait '
                + (
                    f'{int(minutes)} minutes' if minutes
                    else f'{int(time_remaining)} seconds'
                )
                + ' before moving again.',
                delete_after=60*60
            )
            return

        connection = self.roleplay.get_connection(channel.name, room)
        new_channel = await self._move_player(
            message.author, room, message.channel
        )
        move_timers[message.author.id] = datetime.now() + timedelta(
            minutes=connection.timer
        )
        State.set_var(message.guild.id, 'move_timers', move_timers)
        await channel.send(f'{message.author.mention} moves to {room}')
        await new_channel.send(
            f'{message.author.mention} moves in from {channel.name}'
        )
        await message.delete()
Пример #24
0
 async def mark_gm(self, message: Message, user: str):
     await self._mark_with_role(
         message, State.get_admin_role(message.guild.id), 'an admin'
     )