Esempio n. 1
0
class WikiaLookup(commands.Cog):

    def __init__(self, bot):
        self.bot = bot
        self.wikia_list = JsonFileObject('wikia.json')

    @commands.command()
    async def wikia(self, ctx, *, term=''):
        '''Searches for and grabs a link to the page of the given title from the set wiki sites.'''

        if not self.wikia_list.get(str(ctx.guild.id)):
            await ctx.send(f'No wikias have been set for this guild. Use the `{self.bot.command_prefix}wikialist` command to add some to the list')
            return
        if term:
            async with ctx.channel.typing():
                w = 0
                log.info(f'retrieve "{term}" from wikia...')

                for sub in self.wikia_list.get(str(ctx.guild.id)):
                    try:
                        w = wikia_module.page(sub.strip(' []'), term)
                        log.info('page found on {0}...'.format(sub))
                        break  # page found, exit the for loop
                    except:
                        w = 0
                        log.info(f'page not found in {sub}')

                if w is not 0:
                    await ctx.send(w.url.replace(' ', '_'))
                else:
                    await ctx.send(f':sweat: Sorry, I couldnt find a page titled "{term}"...')
        else:
            log.info('no search term...')
            await ctx.send('Use **!wiki <Page Title>** to search and grab a link to the page of that title on the following wiki sites: {}'.format(self.wikia_list[ctx.guild.id]))

    @commands.command()
    @commands.has_permissions(administrator=True)
    async def wikialist(self, ctx, *, wikias=''):
        '''Set the list of wikias that will be searched using the !wikia command. List wikias in a space separated list'''
        if wikias:
            self.wikia_list.update({str(ctx.guild.id): wikias.split(' ')})
            self.wikia_list.save()
            await ctx.send(f'Wikia list updated! :thumbsup: ```{self.wikia_list[str(ctx.guild.id)]}```')
        else:
            await ctx.send(f'The following wikias are searched in order until a match is found: ```{self.wikia_list[str(ctx.guild.id)]}```')
Esempio n. 2
0
class Economy(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.global_economy = JsonFileObject('economy.json')

    def get_local_tokens(self, ctx):
        tokens = self.global_economy.get(ctx.server.id)

        if not tokens:
            self.global_economy.update(ctx.server.id, {})
            tokens = self.global_economy[ctx.server.id]
            self.global_economy.save()

        return tokens

    def get_token(self, ctx, name):
        tokens = self.get_local_tokens(ctx)
        token = tokens.get(name)

        if not token:
            tokens.update(name, {})
            token = tokens[name]
            token.update('ledger', {})
            self.global_economy.save()

        return token

    @commands.Cog.listener()
    async def on_message(self, message):
        if message.author == self.bot.user \
        or message.content.startswith(self.bot.command_prefix):
            return
        
        #detect edge rolls and deduct edge automatically
        term = re.search(r'[.+!]')

    @commands.command()
    @commands.has_permissions(administrator = True)
    async def economy(self, ctx, name = None, param = None, value = None): 
        '''Manage server currencies, use empty command to list available tokens and properties. Name only to create a new token or list all player balances.'''

        if not name:
            msg = '```'
            for token_name, token in self.get_local_tokens(ctx):
                msg += token_name + '\n'
                for key, item in token:
                    if not key == 'ledger':
                        msg += '\t{0}: \t{1}'.format(key, item)
                msg += '\n\n'
            msg += '```'
        elif name and not param:
            msg = '```'
            msg += name.capitalize() + '\n\n'
            for player, history in self.get_local_tokens(ctx)[name].get("ledger"):
                try:
                    player = 
                except:
                    pass
                msg += f''
                msg += '\n'
            msg += '```'
        else:
            token = self.get_token(ctx, name)

            if param and value:
                token.update(param, value)
                self.global_economy.save()

    @commands.command()
    @commands.has_role('gm')
    async def setmax(self, ctx, user, unit, max_value): 
        '''Set the maximum amount of a token for a single user.
        
        setmax <user> <token_name> <max_value>'''
        
        tokens = self.get_local_tokens(ctx)

        token = tokens.get(unit)

        if not token:
            return "Currency not found"

        token_user =  token.get(user.strip('<@!>'))

        token_user.update('max', max_value)

    @commands.command()
    @commands.has_role('gm')
    async def take(self, ctx, amount, unit, user, max_value): 
        '''GM Only. Remove an amount from a player\'s inventory'''
        

    @commands.command()
    async def give(self, ctx, user, amount, unit):
        '''WIP. Transfer an amount to another player. Currency must be tradable.'''
        pass

    @commands.command()
    async def burn(self, ctx, user, amount, unit):
        '''WIP. Spends tokens and lowers maximum by the same amount'''
        pass

    @commands.command()
    async def balance(self, ctx, unit, player = None):
        '''WIP. Shows account balance for self or another player'''
        pass

    @commands.command()
    async def history(self, ctx, unit_name, lines = 5, user = None):
        '''Work in Progress'''
        pass
Esempio n. 3
0
class Tracker(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.initiative = JsonFileObject('initiative.json')

    def _is_waiting_msg(self, m):
        return m.content.startswith(mstr.PBP_WAITING)

    @commands.Cog.listener()
    async def on_message(self, message):
        #ignore self and command messages
        if message.author == self.bot.user:
            return
        elif message.content.startswith(self.bot.command_prefix):
            await self.update_tracking_message(message)
            return

        ini = self.initiative.get(str(message.channel.id))

        if not ini:
            return

        next = await self.get_next_turn(message)

        author = message.author.mention.replace("!", "")

        if not next:
            pass
        elif author in next:
            ini['entries'][author]['turns taken'] += 1
            self.initiative.save()
        elif author not in next:
            await message.delete()
            await message.channel.send(
                f'Sorry {message.author.mention}, you can only send a message when it is your turn! :slight_smile:',
                delete_after=15)

        await self.update_tracking_message(message)

    async def update_tracking_message(self, ctx):
        ini = self.initiative[str(ctx.channel.id)]
        mode = ini['mode']

        await ctx.channel.purge(check=self._is_waiting_msg)

        if mode == 'off':
            return

        _round = str(ini['round'] + 1)
        _pass = str(ini['pass'] + 1) if mode == 'sr5' else 0

        turn = await self.get_next_turn(ctx)
        log.debug(f'Turn = {turn}')
        if isinstance(turn, list):
            turn = '    '.join(turn)

        msg = mstr.PBP_WAITING
        msg += '\n' + f'`Round: {_round}'
        if _pass: msg += f' | Pass: {_pass}'
        msg += f' | Mode: {mode}`'
        msg += '\n \n' + turn
        await ctx.channel.send(msg)

    async def get_next_turn(self, ctx: discord.ext.commands.Context):
        try:
            ini = self.initiative[str(ctx.channel.id)]
        except KeyError:
            return None

        mode = ini['mode']

        entries = ini['entries']

        if mode == 'off':
            next = ''

        elif mode == 'sr5':
            highest = ('', 0)
            for i, e in entries.items():
                mod_ini = e['roll'] - e['spent'] - (10 * e['turns taken'])
                if mod_ini > 0 and mod_ini > highest[1] and e[
                        'turns taken'] <= ini['pass']:
                    if not e.get('name'):
                        member = ctx.guild.get_member(i.strip("<@!>"))
                        name = (getattr(member, 'nick', None)
                                or getattr(member, 'name', None)) or i
                        e.update({'name': name})
                    highest = (i, mod_ini)
            if highest == ('', 0):  #increment pass
                ini['pass'] += 1
                for i, e in entries.items():
                    mod_ini = e['roll'] - e['spent'] - (10 * e['turns taken'])
                    if mod_ini > 0 and mod_ini > highest[1] and e[
                            'turns taken'] <= ini['pass']:
                        highest = (i, mod_ini)
                self.initiative.save()
            if highest == ('', 0):  #increment round
                await self._roll(ctx)
                ini['round'] += 1
                ini['pass'] = 0
                for i, e in entries.items():
                    e['turns taken'] = 0
                    e['spent'] = 0
                    if e['roll'] > highest[1]:
                        highest = (i, e['roll'])
                self.initiative.save()
            if highest == ('', 0):  #wtf?
                next = r'¯\_(ツ)_/¯'
            else:
                next = f'{highest[0]} ({str(highest[1])})'

        elif mode == 'roundrobin':
            next = []
            for i, e in entries.items():
                if not e.get('name'):
                    member = ctx.guild.get_member(i.strip("<@!>"))
                    name = (getattr(member, 'nick', None)
                            or getattr(member, 'name', None)) or i
                    e.update({'name': name})
                if e['turns taken'] <= ini['round']:
                    next.append(i)
            if not next:
                ini['round'] += 1
                if e['turns taken'] <= ini['round']:
                    next.append(i)
                self.initiative.save()
            if not next:
                next = [r'¯\_(ツ)_/¯']

        else:
            raise commands.CommandInvokeError(
                'Invalid Initiative mode, use "off", "sr5", or "roundrobin"')

        return next

    @commands.command()
    @commands.has_role('gm')
    async def init(self, ctx, mode, *initiative_list):
        '''Add user to initiative order with the given initiative result
        Initiative list should be formatted in a comma separated list:
        (e.g @user#1234 10, @member#4321 15, Big Monster 12, ...)
        
        Available modes are: 
        off - Deactivate tracking on this channel
        roundrobin - Everyone gets exactly one turn
        sr5 - One turn for every 10 points of initiative rolled'''

        #sanity check
        valid_modes = ['sr5', 'roundrobin', 'off']
        if mode not in valid_modes:
            await ctx.send(
                f'Invalid mode used, please use one of the following: {valid_modes}. (eg. `{self.bot.command_prefix}init roundrobin @user#1234...`)',
                delete_after=10)
            return

        log.debug(f'{len(initiative_list)} Entries passed: {initiative_list}')

        entries = {}
        if mode == 'sr5':
            simple_list = [
                x.strip()
                for x in ' '.join(initiative_list).replace('!', '').split(',')
            ]
            for e in simple_list:
                t = e.split(' ')
                entries.update({
                    ' '.join(t[0:-1]): {
                        'formula': t[-1],
                        'roll': 0,
                        'spent': 0,
                        'turns taken': 0
                    }
                })
            log.debug(simple_list)
        elif mode == 'roundrobin':
            for e in initiative_list:
                entries.update({e: {'turns taken': 0}})

        log.debug(f'--> {entries}')

        self.initiative.update({
            str(ctx.channel.id): {
                'mode': mode,
                'round': 0,
                'pass': 0,
                'entries': entries
            }
        })

        log.debug(f'----> {self.initiative[str(ctx.channel.id)]}')

        await self._roll(ctx)
        await self.update_tracking_message(ctx)

    @commands.command()
    @commands.has_role('gm')
    async def setinit(self, ctx, user, formula=None):
        '''Set formula for or add a user to turn tracking'''

        player = user.replace('!', '')
        ini = self.initiative.get(str(ctx.channel.id))

        #sanity check
        if not ini or ini['mode'] == 'off':
            await ctx.send(
                f'Unable to update user initiative because turn tracking is not active on this channel. Use `{self.bot.command_prefix}init` to activate turn tracking.',
                delete_after=15)
            return

        entries = ini['entries']

        if ini['mode'] == 'sr5':
            if formula == None:
                await ctx.send(
                    'Unable to update user initiative. A formula is required for sr5 initiative in the format `x+yd6`',
                    delete_after=15)
            else:
                if entries.get(player):
                    entries[player]['formula'] = formula
                    await ctx.send(f'{player} initiative formula updated!')
                else:
                    entries.update({
                        player: {
                            'formula': formula,
                            'roll': 0,
                            'spent': 0,
                            'turns taken': 0
                        }
                    })
                    await ctx.send(f'{player} added to initiative!')

        if ini['mode'] == 'roundrobin':
            entries.update({
                player: {
                    'formula': formula,
                    'roll': 0,
                    'spent': 0,
                    'turns taken': ini['round'] - 1
                }
            })
            await ctx.send(f'{player} added to initiative!')

        await self._roll(ctx, player)
        await self.update_tracking_message(ctx)

    @commands.command()
    @commands.has_role('gm')
    async def reroll(self, ctx, *users):
        '''[sr5 only] Re-roll initiative for any selected user without disturbing other entries in the order. Name no players to reroll everyone.'''
        players = [x.replace('!', '') for x in users]
        await self._roll(ctx, *players)

    async def _roll(self, ctx, *players):
        ini = self.initiative[str(ctx.channel.id)]
        done = []
        failed = []

        try:
            log.debug(f'_roll called by {ctx.command}')
        except Exception as e:
            log.debug(f'_roll called by a process: {e}')

        #only need to re-roll when mode is Shadowrun 5th edition
        if ini['mode'] != 'sr5':
            return

        #if no entries are listed, reroll all of them
        if not players: entries = ini['entries']
        else: entries = {p: ini['entries'].get(p) for p in players}

        for k, e in entries.items():
            try:
                log.debug(f'Rolling for {k} ---> {e}')
                formula = e['formula']
                log.debug(f'Formula: {formula}')
                mod, pattern = formula.split('+')
                count, die = pattern.split('d')
                rolls = random.choices([x + 1 for x in range(int(die))],
                                       k=int(count))
                log.debug(f'Dice Rolls: {rolls}')
                roll_sum = sum(rolls)
                grand_total = int(mod) + roll_sum
                log.debug(f'Final roll: {mod} + {roll_sum} = {grand_total}')
                e['roll'] = grand_total
                done.append(f"{k}\t{e['roll']}")
            except Exception as f:
                failed.append(f"{k}, {f}")

        await ctx.channel.send('```Initiative has been rolled:```{0}'.format(
            '\n'.join(done)))

        if failed:
            await ctx.channel.send(
                '```The following entries had errors and could not be rolled:``` {0}'
                .format('\n'.join(failed)))

        #TODO: Send confirmation message with list of initiative rolls

        self.initiative.save()
        await self.update_tracking_message(ctx)

    @commands.command()
    async def skip(self, ctx, *users):
        '''Skips the named user(s) in initiative. If no users named, skips self'''

        author_roles = [r.name for r in ctx.author.roles]

        if users and 'gm' not in author_roles:
            await ctx.send(
                f'Only a GM can name another person to skip, players should only use `{self.bot.command_prefix}skip` on it\'s own to skip themselves :smile:',
                delete_after=15)
            return
        elif not users:
            users = [ctx.author.mention]

        ch_id = str(ctx.channel.id)
        ini = self.initiative[ch_id]
        c_ini = await self.get_next_turn(ctx)
        user_list = [u.replace('!', '') for u in users]

        for user in user_list:
            if user in c_ini:
                ini['entries'][user]['turns taken'] += 1
                self.initiative.save()
                await ctx.send(f'`Skipped` {user}')

        await ctx.message.delete()
        await self.update_tracking_message(ctx)

    @commands.command()
    async def spendinit(self, ctx, amount: int, *, action_taken):
        '''Spend initiative points to inturrupt or take a special action'''

        author = ctx.author.mention.replace('!', '')
        ini = self.initiative[str(ctx.channel.id)]
        entries = ini['entries']
        entry = entries[author]
        cpass = ini['pass']
        roll = entry['roll']
        spent = entry['spent']
        net_init = roll - spent - (10 * cpass)

        if net_init >= amount:
            entry['spent'] += amount
            new_init = net_init - amount
            self.initiative.save()
            await ctx.send(
                f'```{ctx.author.name} spent [{amount}] initiative to take the following action. Their initiative is now [{new_init}]``` {action_taken}'
            )
        else:
            await ctx.send(
                f'Sorry, {author}, you do not have the enough initiative to spend. Your current score is **{net_init}**.',
                delete_after=15)

        await ctx.message.delete()
        await self.update_tracking_message(ctx)

    @commands.command()
    @commands.has_role('gm')
    async def takeinit(self, ctx, user, amount: int, *, action_taken):
        '''Same as spendinit but used on a user or NPC other than self'''

        author = user.replace('!', '')
        ini = self.initiative[str(ctx.channel.id)]
        entries = ini['entries']
        entry = entries[author]
        cpass = ini['pass']
        roll = entry['roll']
        spent = entry['spent']
        net_init = roll - spent - (10 * cpass)

        if net_init >= amount:
            entry['spent'] += amount
            new_init = net_init - amount
            self.initiative.save()
            await ctx.send(
                f'```{ctx.author.name} spent {amount} initiative to take the following action. Their initiative is now [{new_init}]``` {action_taken}'
            )
        else:
            await ctx.send(
                f'Sorry, {author} does not have the enough initiative to spend. Their current score is **{net_init}**.',
                delete_after=15)

        await ctx.message.delete()
        await self.update_tracking_message(ctx)

    @commands.command()
    async def taketurn(self, ctx, user, *, action_taken):
        '''Allows a person to take a turn for an NPC or player. User argument only required for roundrobin'''

        ini = self.initiative.get(str(ctx.channel.id))
        cini = await self.get_next_turn(ctx)

        #if mode is not roundrobin, attach the user argument to the beginning of action string since it should be ignored
        if ini['mode'] != 'roundrobin':
            action_taken = f'{user} {action_taken}'
            user = cini.split(
                '(')[0].strip()  #current initiative minus the initiative score

        player = user.replace('!', '')
        entries = ini['entries']
        entry = entries[player]

        if player in cini:
            entry['turns taken'] += 1
            self.initiative.save()
            embeddable = discord.Embed(
                description=f"{user}'s turn was taken by {ctx.author.name}")
            await ctx.send(embed=embeddable)
        else:
            await ctx.send(
                f'Sorry, {player} does not have a turn to take right now',
                delete_after=15)

        await self.update_tracking_message(ctx)

    @commands.command()
    @commands.has_role('gm')
    async def setroll(self, ctx, user, roll: int = 0):
        '''Set an individuals initiative roll. If roundrobin, this will cause the player to join the next round'''

        player = user.replace('!', '')
        ini = self.initiative[str(ctx.channel.id)]
        entries = ini['entries']

        if ini['mode'] == 'off':
            await ctx.send(
                f'{player} could not be added because turn tracking is turned off. Use `{self.bot.command_prefix}init` to start turn tracking. Type `{self.bot.command_prefix}help init` for more detailed information',
                delete_after=15)
            return

        if ini['mode'] == 'sr5':
            entries.update(
                {player: {
                    'roll': roll,
                    'spent': roll,
                    'turns taken': 0
                }})
        elif ini['mode'] == 'roundrobin':
            entries.update({player: {'turns taken': ini['round']}})

        await ctx.send(
            f'{player} has been added to initiative. They will join in starting next round.',
            delete_after=15)

        self.initiative.save()

        await ctx.message.delete()
        await self.update_tracking_message(ctx)
Esempio n. 4
0
class GameCommands(commands.Cog):
    def __init__(self, bot):

        self.bot = bot
        self.npcs = JsonFileObject('npcs.json')

    @commands.command()
    @commands.has_role('gm')
    async def open(self, ctx):
        '''Post open message and add player role to allow posting in channel'''

        await ctx.message.delete()
        player_roles = [
            x for x in ctx.guild.roles if x.name.lower().startswith('player')
        ]
        for role in player_roles:
            await ctx.channel.set_permissions(role,
                                              send_messages=True,
                                              read_messages=True)
        await ctx.send('```This log is now open for player entries```')

    @commands.command()
    @commands.has_role('gm')
    async def close(self, ctx):
        '''Post close message and sync channel to category permissions'''

        await ctx.message.delete()
        await ctx.send(
            '```This log is closed, The next chapter will open soon```')
        await ctx.channel.edit(sync_permissions=True)

    @commands.command()
    @commands.has_role('gm')
    async def mimic(self, ctx, user, *, message):
        '''The bot will post a message imitating another user or an NPC'''

        await ctx.message.delete()

        person = {}

        if user.startswith('<'):
            m = ctx.guild.get_member(int(user.strip('<!@>')))
            avatar = m.avatar_url if not m.avatar_url == '' else m.default_avatar_url
            person.update({'name': m.name, 'avatar_url': avatar})
            log.debug(f'{ctx.author.name} is impersonating {user}, {m.name}')
        else:
            npc_list = self.npcs.get(str(ctx.guild.id))
            if user in npc_list:
                npc = npc_list.get(user)
                person.update({
                    'name': npc['display_name'],
                    'avatar_url': npc['avatar_url']
                })
                log.debug(
                    f'{ctx.author.name} is impersonating NPC, {npc["display_name"]}'
                )

        if person:
            embedable = discord.Embed(
                description=message)  #, timestamp = ctx.timestamp)
            #embedable.set_footer(text = 'sent by GM')
            embedable.set_author(
                name=person['name'])  #, icon_url= person['avatar_url'])
            embedable.set_thumbnail(url=person['avatar_url'])
            await ctx.send(embed=embedable)

    @commands.command()
    @commands.has_role('gm')
    async def addnpc(self, ctx, name, display_name, avatar_url):
        '''Adds an NPC for use with the mimic command'''

        await ctx.message.delete()

        npc_list = self.npcs.get(str(ctx.guild.id))
        if npc_list:
            npc_list.update({
                name: {
                    'display_name': display_name,
                    'avatar_url': avatar_url
                }
            })
            self.npcs.save()
        else:
            self.npcs.update({
                str(ctx.guild.id): {
                    name: {
                        'display_name': display_name,
                        'avatar_url': avatar_url
                    }
                }
            })
            self.npcs.save()

        npc = self.npcs[str(ctx.guild.id)][name]

        log.debug(npc)

        embedable = discord.Embed(
            description=
            f'NPC profile has been saved to {ctx.guild.name}. Make sure everything looks as you expect it to here :smile:'
        )  #, timestamp = ctx.message.timestamp)
        embedable.set_footer(text=f'!mimic "{name}"')
        embedable.set_author(
            name=npc.get('display_name'))  #, icon_url= npc.get('avatar_url'))
        embedable.set_thumbnail(url=npc.get('avatar_url'))

        await ctx.author.send(embed=embedable)

    @commands.command()
    @commands.has_role('gm')
    async def deletenpc(self, ctx, name):
        '''Remove NPC from server list'''

        await ctx.message.delete()

        npc_list = self.npcs.get(str(ctx.guild.id))
        if npc_list:
            npc_list.remove(name)
            self.npcs.save()
            await ctx.author.send(
                f'{name} has been removed from the list of NPCs in {ctx.guild.name}'
            )
        else:
            await ctx.author.send(
                f'{name} does not exist in the list of NPCs in {ctx.guild.name}'
            )

    @commands.command()
    @commands.has_role('gm')
    async def listnpcs(self, ctx):
        '''list of available NPCs in fancy boxes'''

        await ctx.message.delete()

        npc_list = self.npcs.get(str(ctx.guild.id))

        for name, npc in npc_list.items():
            embedable = discord.Embed(description='Lorem impsum sic amor emet'
                                      )  #, timestamp = ctx.message.timestamp)
            embedable.set_footer(text=f'!mimic "{name}"')
            embedable.set_author(name=npc.get('display_name'),
                                 icon_url=npc.get('avatar_url'))
            #embedable.set_thumbnail(url = npc.get('avatar_url'))
            await ctx.send(embed=embedable)