Ejemplo n.º 1
0
class Audit(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

    async def print_audit(self, author, type, message):
        self._logger.trace("print_audit")
        usr = cassandra.get_user((author.guild.id, author.id))
        # TODO: Passed user class

        # TODO: Check for user not found

        # Check if the server audit log is enabled
        if usr.server.audit_channel is None:
            return False

        # Check if the audit log channel exists
        ch = self.client.get_channel(usr.server.audit_channel)
        if ch is None:
            return False

        # TODO: Check if audits of this type are enabled

        # Send audit log
        await ch.send("<@!%s>: %s" % (usr.ID, message))

        return True
Ejemplo n.º 2
0
class Main(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

    @commands.command(name="profile", help="Shows the profile.")
    async def profile(self, ctx, name=None):
        self._logger.trace("profile")
        # Do not do anything on private messages
        if ctx.guild == None:
            return

        uid = ctx.author.id
        if name is not None:
            if name.startswith("<@!") and name.endswith(">"):
                uid = name[3:len(name) - 1]

        usr = cassandra.get_user((ctx.guild.id, uid))
        if usr is None:
            await ctx.send("User not found.")
            return

        discord_user = self.client.get_user(usr.ID)
        if discord_user is None:
            await ctx.send("User not found.")
            return

        content = ""
        cecon = self.client.get_cog('Economy')
        if cecon is not None:
            content = content + ":moneybag: Coins: %s" % cecon.get_balance(usr)

        cxp = self.client.get_cog('Xp')
        if cxp is not None:
            content = content + "\n:star: XP:    %s" % cxp.get_xp(usr)
            content = content + "\n:star2: Level: %s" % cxp.get_level(usr)

        embed = discord.Embed(title="Profile for %s on server %s" %
                              (discord_user.name, ctx.guild.name),
                              description=content,
                              colour=discord.Colour.gold())
        embed.set_thumbnail(url=discord_user.avatar_url)

        stats = self.client.get_cog('Stats')
        if stats is not None:
            embed.add_field(name=":writing_hand: Messages",
                            value="Total: %s" % stats.get_msg(usr),
                            inline=True)
            embed.add_field(name=":heart: Reactions",
                            value="Total: %s" % stats.get_reaction(usr),
                            inline=True)
            embed.add_field(name=":microphone2: Voice",
                            value="Time: %s" %
                            util.sec2human(stats.get_voice(usr)),
                            inline=True)

        await ctx.send(embed=embed)
Ejemplo n.º 3
0
class Economy(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

    def get_balance(self, u):
        self._logger.trace("get_balance")
        usr = cassandra.get_user(u)
        return usr.balance  # Balance

    def set_balance(self, u, amt):
        self._logger.trace("set_balance")
        usr = cassandra.get_user(u)
        old = usr.balance

        resp = cassandra.dispatch("balance-change", {
            "old": old,
            "new": amt,
            "user": usr
        })

        usr.balance = resp.new
        return old  # Old balance

    def add_balance(self, u, amt):
        self._logger.trace("add_balance")
        usr = cassandra.get_user(u)
        old = usr.balance

        resp = cassandra.dispatch("balance-change", {
            "old": old,
            "new": old + amt,
            "user": usr
        })

        usr.balance = resp.new
        return usr.balance  # New balance

    def has_balance(self, u, amt):
        self._logger.trace("has_balance")
        usr = cassandra.get_user(u)
        return usr.balance >= amt
Ejemplo n.º 4
0
class LevelRoles(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

        self._cache_xp_upate = []  # Holds tuples for users to update

    def get_help_page(self):
        return {
            "title": "LevelRoles Commands",
            "description":
            "Some of these commands require moderation or administrator permissions.",
            "content": {
                "lvroles":
                "Lists all reward roles that can be gained by leveling up.",
                "lvrole <level>":
                "Removes a reward from the ladder. - Admin Only",
                "lvrole <level> <role>":
                "Adds a reward level to the ladder. - Admin Only"
            }
        }

    @commands.command(name="lvroles")
    async def lvroles(self, ctx):
        srv = cassandra.get_server(ctx.guild.id)
        if len(srv.level_roles) > 0:
            text = "**Level Roles**\n\n"
            lvrolecache = self._build_level_rank_cache(ctx.guild, srv)
            for lv, role in lvrolecache.items():
                text = text + ("%s: <@&%s>\n" % (lv, role.id))
            await ctx.send(text)
        else:
            await ctx.send("This server has no level roles configured.")

    @commands.command(name="lvrole")
    @commands.has_permissions(manage_roles=True)
    async def lvrole(self, ctx, lvstr, rolestr=None):
        lv = None
        try:
            lv = int(lvstr)
        except Exception:
            await ctx.send("<level> must be a positive number.")
            return

        if lv < 0:
            await ctx.send("<level> must be a positive number.")
            return

        srv = cassandra.get_server(ctx.guild.id)
        if rolestr is None:
            # Remove Level role
            if lv in srv.level_roles:
                srv.level_roles.pop(lv, None)
                await ctx.send("Removed level role for level %s." % lv)
            else:
                await ctx.send("No level role is set for level %s." % lv)
        else:
            if lvstr in srv.level_roles:
                await ctx.send(
                    "Level %s currently has role %s.\nPlease remove it manualy first, if you want another role at this level."
                )
                return

            if len(rolestr) == 22 and rolestr.startswith(
                    "<@&") and rolestr.endswith(">"):
                try:
                    roleid = int(rolestr[3:21])
                except:
                    await ctx.send("Role not found %s!" % rolestr)
                    return

                role = get(ctx.guild.roles, id=int(roleid))
                if role is None:
                    await ctx.send("Role not found <@&%s>!" % roleid)
                    return

                srv.level_roles[lvstr] = roleid
                cassandra.save_server(srv)
                await ctx.send("Role <@&%s> set for level %s." % (roleid, lv))
            else:
                ctx.send("%s is not a vaild role!" % rolestr)
                return

        pass

    def _build_level_rank_cache(self, guild, srv=None):
        self._logger.trace("_build_level_rank_cache")
        if srv is None:
            srv = cassandra.get_server(guild.id)

        lv_role_cache = {}
        for lv in srv.level_roles:
            role = get(guild.roles, id=srv.level_roles[lv])
            if role is None:
                continue
            lv_role_cache[int(lv)] = role
        return dict(sorted(lv_role_cache.items(), key=lambda item: item[0]))

    async def update_user(self, guild, user, lv_role_cache=None):
        self._logger.trace("update_user(%s)" % user.id)
        if lv_role_cache is None:
            lv_role_cache = self._build_level_rank_cache(guild)

        # If user is a bot, ignore
        if user.bot:
            return

        level = self.client.get_cog('Xp').get_level((guild.id, user.id))

        highest_role = None
        for lv in lv_role_cache:
            role = lv_role_cache[lv]
            if level < lv:
                break
            highest_role = role

        if highest_role is None:
            return

        if highest_role in user.roles:
            return

        for urole in user.roles:
            if urole in lv_role_cache.values():
                await user.remove_roles(
                    urole, reason="Userlevel rank"
                )  # TODO: There must be a more efficient way to do this
        await user.add_roles(highest_role, reason="Userlevel change")

        # Dispatch event
        cassandra.dispatch(
            "lvrole-change", {
                "duser": user,
                "user": cassandra.get_user((guild.id, user.id)),
                "role": highest_role
            })

        # TODO: Anounce

    async def update_guild(self, guild):
        self._logger.trace("update_guild(%s)" % guild.id)
        # Do not do anything for unavailable guilds
        if guild.unavailable == True:
            return

        srv = cassandra.load_server(guild.id)

        # If no level roles are configured, ignore
        if len(srv.level_roles) == 0:
            return

        # Get all groups into a dictionary
        lv_role_cache = self._build_level_rank_cache(guild, srv)

        for member in guild.members:
            await self.update_user(guild, member, lv_role_cache)

    @commands.Cog.listener()
    async def on_ready(self):
        self.update_slow.start()
        self.update_fast.start()

        cassandra.add_event_listener("xp-change", self, self.on_xp_change)

    def on_xp_change(self, args):
        self._logger.trace("on_xp_change")
        if args.old >= args.new:
            return

        if args.user in self._cache_xp_upate:
            return

        self._cache_xp_upate.append(args.user)

    @tasks.loop(seconds=UPDATE_TIMEOUT_FAST)
    async def update_fast(self):
        if len(self._cache_xp_upate) == 0:
            return

        self._logger.trace("update_fast")

        for entry in self._cache_xp_upate:
            guild = get(self.client.guilds, id=entry.server.ID)
            if guild is None:
                continue

            user = get(guild.members, id=entry.ID)
            if user is None:
                continue

            await self.update_user(guild, user)

        self._cache_xp_upate.clear()

    @tasks.loop(seconds=UPDATE_TIMEOUT_SLOW)
    async def update_slow(self):
        self._logger.trace("update_slow")

        for guild in self.client.guilds:
            await self.update_guild(guild)
Ejemplo n.º 5
0
class Fun(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

        self.wyr_data = []
        self.whatami_data = []
        self.eightball_data = []
        self.fortunes = {}

        self._load_wyr_assets()
        self._load_fortune_assets()
        self._load_whatami_assets()
        self._load_8ball_assets()

    def _load_wyr_assets(self):
        path = "assets/wyr.json"

        if not os.path.exists(path):
            return False

        with open(path) as f:
            jdata = json.load(f)

            self.wyr_data = jdata["questions"]

            self._logger.info("Loaded WYR Data")
            return True

    def _load_fortune_assets(self):
        dir_path = "assets/fortunes"

        if not os.path.exists(dir_path):
            return False

        for filename in os.listdir(dir_path):
            if not filename.endswith(".json"):
                continue

            path = dir_path + "/" + filename
            with open(path) as f:
                jdata = json.load(f)

                self.fortunes[filename[:-5]] = jdata["fortunes"]
                self._logger.info("Loaded fortune database %s" % filename)

        self._logger.info("Loaded fortune Data")
        return True

    def _load_whatami_assets(self):
        path = "assets/whatami.json"

        if not os.path.exists(path):
            return False

        with open(path) as f:
            jdata = json.load(f)

            self.whatami_data = jdata["quotes"]

        self._logger.info("Loaded whatami Data")
        return True

    def _load_8ball_assets(self):
        path = "assets/8ball.json"

        if not os.path.exists(path):
            return False

        with open(path) as f:
            jdata = json.load(f)

            self.eightball_data = jdata["answers"]

        self._logger.info("Loaded 8ball Data")
        return True

    def get_help_page(self):
        return {
            "title": "Fun Commands",
            "description": None,
            "content": {
                "8ball <Question ...>": "Ask the magic 8ball.",
                #"conn <User>": "Starts a game of connect-4 against the mentioned user.",
                "fortune": "Opens a fortune cookie.",
                #"rusr": "******",
                #"tord": "Play a game of truth or dare.",
                #"ttt <User>": "Starts a game of Tick-Tack-Toe.",
                "wyr": "Displays a would you rather question.",
                "whatami": "You better have thick skin...or a thick skull!"
            }
        }

    @commands.command(name="whatami")
    async def whatami(self, ctx):
        self._logger.trace("whatami")
        quote = random.choice(self.whatami_data)
        await ctx.send(quote)

    @commands.command(name="conn")
    async def conn(self, ctx):
        self._logger.trace("conn")
        await ctx.send("Not implemented")

    @commands.command(name="fortune")
    async def fortune(self, ctx):
        self._logger.trace("fortune")

        # TODO: Handle dictionary is empty

        db = random.choice(list(self.fortunes.keys()))

        # TODO: Handle db is empty

        line = random.choice(self.fortunes[db])

        await ctx.send(line)

    @commands.command(name="tord")
    async def tord(self, ctx):
        self._logger.trace("tord")
        await ctx.send("Not implemented")

    @commands.command(name="ttt")
    async def ttt(self, ctx):
        self._logger.trace("ttt")
        await ctx.send("Not implemented")

    @commands.command(name="wyr")
    async def wyr(self, ctx):
        self._logger.trace("wyr")
        line = random.choice(self.wyr_data)

        embed = discord.Embed(title="Would You Rather",
                              description="**" + line["question"] +
                              "**\n\n\n" + EMOJI_A + " " + line["answer_1"] +
                              "\n\n" + EMOJI_B + " " + line["answer_2"],
                              colour=discord.Colour.red())

        msg = await ctx.send(embed=embed)

        await msg.add_reaction(EMOJI_A)
        await msg.add_reaction(EMOJI_B)

    @commands.command(name="8ball")
    async def eightball(self, ctx):
        self._logger.trace("8ball")
        await ctx.send(EMOJI_8BALL + " " + random.choice(self.eightball_data))
Ejemplo n.º 6
0
class Stats(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

    def get_msg(self, u):
        self._logger.trace("get_msg")
        usr = cassandra.get_user(u)
        return usr.msg_count

    def get_reaction(self, u):
        self._logger.trace("get_reaction")
        usr = cassandra.get_user(u)
        return usr.reaction_count

    def get_voice(self, u):
        self._logger.trace("get_voice")
        usr = cassandra.get_user(u)
        return usr.voice_time

    @commands.Cog.listener()
    async def on_ready(self):
        self.update_voice.start()

    @commands.Cog.listener()
    async def on_reaction_add(self, reaction, user):
        self._logger.trace("on_reaction_add")
        # Ignore reactions from bots
        if user.bot == True:
            return

        # Ignore private messages
        if reaction.message.guild is None:
            return

        # Ignore reactions made to messages written by bots
        if reaction.message.author.bot == True:
            return

        usr = cassandra.get_user((reaction.message.guild.id, user.id))

        # TODO: Handle user not found

        ts = util.getts()
        usr.reaction_last = ts

        # if reaction timeout is reached award coins
        if ts - usr.reaction_awarded > REACTION_TIMEOUT:
            usr.reaction_awarded = ts

            # Add balance if economy cog is loaded
            cecon = self.client.get_cog('Economy')
            if cecon is not None:
                cecon.add_balance(usr, REACTION_BONUS_COINS)

            # Add xp if xp cog is loaded
            cxp = self.client.get_cog('Xp')
            if cxp is not None:
                cxp.add_xp(usr, REACTION_BONUS_XP)

        usr.reaction_count = usr.reaction_count + 1

        cassandra.save_user(usr)

    @commands.Cog.listener()
    async def on_message(self, message):
        # Ignore messages from bots
        if message.author.bot == True:
            return

        # Ignore direct messages
        if message.guild is None:
            return

        self._logger.trace("on_message(%s)" % message.content)

        srv = cassandra.get_server(message.guild.id)
        # TODO: Check if server is not found

        # Ignore commands to cassandra
        if message.content.startswith(srv.prefix_used):
            return

        # Ignore commands to other bots
        for prefix in srv.prefix_blocked:
            if message.content.startswith(prefix):
                return

        usr = cassandra.get_user((srv, message.author.id))
        # TODO: Check if user is not found

        ts = util.getts()
        usr.msg_last = ts
        usr.msg_count = usr.msg_count + 1

        #if msg timeout is reached award coins
        if ts - usr.msg_awarded > MSG_TIMEOUT:
            usr.msg_awarded = ts

            # Add balance if economy cog is loaded
            cecon = self.client.get_cog('Economy')
            if cecon is not None:
                cecon.add_balance(usr, MSG_BONUS_COINS)

            # Add xp if xp cog is loaded
            cxp = self.client.get_cog('Xp')
            if cxp is not None:
                cxp.add_xp(usr, MSG_BONUS_XP)

        cassandra.save_user(usr)

    @tasks.loop(seconds=VOICE_TIMEOUT)
    async def update_voice(self):
        self._logger.trace("update_voice")

        # For all voice channels on each guild
        for guild in self.client.guilds:

            # Do not do anything for unavailable guilds
            if guild.unavailable == True:
                continue

            # For each voice channel in each guild
            for vchannel in guild.voice_channels:
                vchmem = len(vchannel.members)

                # No need to look into a voice channel with one or no active members
                if vchmem < 2:
                    continue

                # For each current member
                for member in vchannel.members:
                    if member.voice.afk:
                        continue
                    elif member.voice.deaf or member.voice.self_deaf:
                        continue
                    elif member.voice.mute or member.voice.self_mute:
                        continue

                    usr = cassandra.get_user((guild.id, member.id))
                    usr.voice_time = usr.voice_time + VOICE_TIMEOUT

                    # Add balance if economy cog is loaded
                    cecon = self.client.get_cog('Economy')
                    if cecon is not None:
                        cecon.add_balance(usr, VOICE_BONUS_COINS)

                    # Add xp if xp cog is loaded
                    cxp = self.client.get_cog('Xp')
                    if cxp is not None:

                        # Give an xp bonus for more people in the voicechat
                        xp = VOICE_BONUS_XP + (vchmem - 2)
                        cxp.add_xp(usr, xp)

                    cassandra.save_user(usr)
Ejemplo n.º 7
0
class Cassino(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

    def get_help_page(self):
        return {
            "title": "Casino Commands",
            "description": None,
            "content": {
                #"lottery": "Buy a lottery ticket.",
                "coinflip <heads|tails> [bet = 0]":
                "Flip a coin and bet on the outcome.",
                #"slots [bet = 0]": "Spin the slots for a chance to win the jackpot!",
                # "spin": "Spin the wheel every 5 minutes for a reward.",
                #"scratch": "Scratch a card for a chance to win a reward.",
                #"dice": Roll the dice, if you roll a 6 you'll get your bet x5!",
                # "snakeeyes": "Two dice are rolled. Roll a 1 on either one to win. Get both on 1 to recieve a special bonus reward.",
                # "blackjack": "Play a game of blackjack.",
            }
        }

    def _get_bet(self, bet):
        if bet is None:
            return 0

        if not bet.isdigit():
            return None

        # At this point the number can not be negative
        return int(bet)

    async def _can_afford(self, ctx, u, bet):
        cecon = self.client.get_cog('Economy')
        if cecon is None:
            await ctx.send(EMOJI_ERROR +
                           " Betting for coins is not configured!")
            return False
        if not cecon.has_balance(u, bet):
            await ctx.send(EMOJI_ERROR + " You can't afford this bet!")
            return False

        return True

    async def _usage_coinflip(self, ctx):
        await ctx.send("Usage: $coinflip <heads|tails> [amt=0]")

    @commands.command(name="coinflip",
                      help="Flip a coin and bet on the outcome.")
    async def coinflip(self, ctx, side=None, bet=None):
        self._logger.trace("coinflip")
        if side is None:
            await self._usage_coinflip(ctx)
            return

        bet = self._get_bet(bet)
        if bet is None:
            await self._usage_coinflip(ctx)
            return

        usr = None
        if bet > 0:
            usr = cassandra.get_user((ctx.guild.id, ctx.author.id))

            #  Check if the user of this command has that much money
            if not await self._can_afford(ctx, usr, bet):
                return

        rng_side = -1
        if side == "heads":
            rng_side = 0
        elif side == "tails":
            rng_side = 1
        else:
            await self._usage_coinflip(ctx)
            return

        flip = random.randint(0, 1)

        thumbnail = None
        flip_str = None
        if flip == 1:
            flip_str = "tails"
            thumbnail = IMAGE_TAILS
        else:
            flip_str = "heads"
            thumbnail = IMAGE_HEADS

        won = (flip == rng_side)

        color = None
        description = "It's **%s**\n" % flip_str

        if won:
            color = discord.Colour.green()
            description = description + "You won"
        else:
            color = discord.Colour.red()
            description = description + "You lost"

        if bet > 0:
            cecon = self.client.get_cog(
                'Economy'
            )  # This can always be found because of the _can_afford check

            if won:
                cecon.add_balance(usr, bet)
            else:
                cecon.add_balance(usr, -bet)

            description = description + (" %s " % bet) + EMOJI_MONEY + (
                "\nYou now have %s " % usr.balance) + EMOJI_MONEY

        embed = discord.Embed(title="Coinflip",
                              colour=color,
                              description=description)
        embed.set_thumbnail(url=thumbnail)

        await ctx.send(embed=embed)

        if usr:
            cassandra.save_user(usr)

    @commands.command(name="lottery")
    async def lottery(self, ctx):
        self._logger.trace("lottery")
        await ctx.send("Not implemented")

    @commands.command(name="slots")
    async def slots(self, ctx):
        self._logger.trace("slots")
        await ctx.send("Not implemented")

    @commands.command(name="scratch")
    async def scratch(self, ctx):
        self._logger.trace("scratch")
        await ctx.send("Not implemented")

    @commands.command(name="dice")
    async def dice(self, ctx):
        self._logger.trace("dice")
        await ctx.send("Not implemented")

    @commands.command(name="snakeeyes")
    async def snakeeyes(self, ctx):
        self._logger.trace("snakeeyes")
        await ctx.send("Not implemented")

    @commands.command(name="blackjack")
    async def blackjack(self, ctx):
        self._logger.trace("blackjack")
        await ctx.send("Not implemented")

    # Casino
    @commands.command(name="spin",
                      help="Spin the wheel every 5 minutes for a reward.")
    async def spin(self, ctx):
        self._logger.trace("spin")
        ods = [{
            "symbol": "<:c500:710482148305403924>",
            "propbability": 1,
            "ods": 500,
            "name": "MYTHIC"
        }, {
            "symbol": "<:c100:710482154500653056>",
            "propbability": 2,
            "ods": 100,
            "name": "IMORTAL"
        }, {
            "symbol": "<:c50:710482156895469691>",
            "propbability": 4,
            "ods": 50,
            "name": "LEGENDARY"
        }, {
            "symbol": "<:c25:710482155310022758>",
            "propbability": 8,
            "ods": 25,
            "name": "EPIC"
        }, {
            "symbol": "<:c10:710482151484817439>",
            "propbability": 16,
            "ods": 10,
            "name": "RARE"
        }, {
            "symbol": "<:c5:710482137895403520>",
            "propbability": 24,
            "ods": 5,
            "name": "UNCOMMON"
        }, {
            "symbol": "<:c1:710482104705613845>",
            "propbability": 48,
            "ods": 1,
            "name": "COMMON"
        }]

        usr = cassandra.get_user((ctx.guild.id, ctx.author.id))

        ts = util.getts()
        if ts - usr.casino_last_spin < 300:
            await ctx.send("You have to wait " +
                           util.sec2human(300 - (ts - usr.casino_last_spin)) +
                           " for your next chance of a reward.")
            return

        total = 0
        for outcome in ods:
            total = total + outcome["propbability"]

        rand = random.randint(0, total)
        index = 0
        out = None
        while rand > 0:
            outcome = ods[index]
            rand = rand - outcome["propbability"]

            if rand <= 0:
                out = outcome
                break
            index = index + 1

        # FIX: This is a drity fix, as we should never end up here
        if out is None:
            out = ods[0]

        embed = discord.Embed(
            title=ctx.author.display_name + " spins the wheel ...",
            description="<a:spin:710491435006165032> | Spinning ...",
            colour=discord.Colour.green())

        msg = await ctx.send(embed=embed)

        # FIX: Replace this with a timer, because this forces only one player (in al servers) to be able to spin at once
        time.sleep(1)

        description = "%s | Landed on **%s**" % (out["symbol"], out["name"])

        cecon = self.client.get_cog('Economy')
        if cecon is not None:
            reward = out["ods"]

            # Multiply reward by user level
            cxp = self.client.get_cog('Xp')
            if cxp is not None:
                level = cxp.get_level(usr)
                if level > 1:
                    reward = reward * level

            description = description + ("\nYou got a reward of %s coins" %
                                         reward)
            cecon.add_balance(usr, reward)
        else:
            description = description + (
                "\n\n%s You did not get a reward, because the economy is not setup!"
                % EMOJI_WARN)

        embed = discord.Embed(title=ctx.author.display_name +
                              " Spun the wheel",
                              description=description,
                              colour=discord.Colour.green())

        await msg.edit(embed=embed)

        usr.casino_last_spin = ts
        cassandra.save_user(usr)
Ejemplo n.º 8
0
class AutoChannel(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

    async def create_autochannel(self, s, dguild, duser=None):
        srv = cassandra.get_server(s)

        # Check if autochannels are configured
        if srv.autochannel_router is None:
            self._logger.debug("router channel not configured")
            return False

        # Find router channel
        router = get(dguild.channels, id=srv.autochannel_router)
        if router is None:
            self._logger.debug("router channel not found")
            return False

        # Get category
        cat = router.category
        if cat is None:
            self._logger.debug("router channel has no category")
            return False

        name = "Autochannel"
        if duser is not None:
            name = "AC %s" % (duser.display_name)
        channel = await cat.create_voice_channel(name,
                                                 reason="New AutoChannel")

        # Save changes
        srv.autochannel_channels.append(channel.id)
        cassandra.save_server(srv)

        # Move member to new channel
        if duser is not None:
            await duser.edit(voice_channel=channel)

        return True

    async def delete_autochannel(self, s, dguild, channel):
        srv = cassandra.get_server(s)

        if not channel.id in srv.autochannel_channels:
            self._logger.debug("channel not an autochannel")
            return False

        # Check if the channel is empty
        if len(channel.members) > 0:
            self._logger.debug("still users in channel")
            return False

        # Remove from server data
        srv.autochannel_channels.remove(channel.id)
        cassandra.save_server(srv)

        # Delete empty channel
        await channel.delete(reason="Empty AutoChannel")

        return True

    async def cleanup(self, guild=None):
        self._logger.trace("cleanup(%s)" % guild)
        if guild is None:
            # For all guilds
            for g in self.client.guilds:
                # Do not do anything for unavailable guilds
                if g.unavailable == True:
                    continue

                await self.cleanup(g)

        else:
            srv = cassandra.get_server(guild.id)
            for ac in srv.autochannel_channels:
                channel = get(guild.channels, id=ac)
                if channel is None:
                    continue

                await self.delete_autochannel(srv, guild, channel)

    @commands.command(name="autochannel")
    @commands.has_permissions(manage_channels=True)
    async def autochannel(self, ctx, action, router=None):
        self._logger.trace("autochannel")

        srv = cassandra.get_server(ctx.guild.id)
        if action in ["disable", "stop"]:
            srv.autochannel_router = None
            cassandra.save_server(srv)

            await self.cleanup()

            await ctx.send(
                "Autochannels are no longer active.\nIf you want to use them in the future, you need to configure them again."
            )

        elif action in ["setup", "start", "enable"]:
            if router is None:
                return  # TODO: Print help

            routerid = None
            try:
                routerid = int(router)
            except Exception:
                return  # TODO: User feedback

            routerch = get(ctx.guild.channels, id=routerid)
            if routerch is None:
                return  # TODO: User feedback

            if routerch.category is None:
                await ctx.send(
                    "The autochannel router must be part of a channel category!"
                )
                return

            srv.autochannel_router = routerid
            cassandra.save_server(srv)

            await ctx.send("Autochannels successfully configured.")

    @commands.Cog.listener()
    async def on_voice_state_update(self, member, before, after):
        srv = cassandra.get_server(member.guild.id)

        if after.channel is not None:
            if after.channel.id == srv.autochannel_router:
                self._logger.debug("user join router")
                await self.create_autochannel(srv, member.guild, member)

        if before.channel is not None:
            if before.channel.id in srv.autochannel_channels:
                self._logger.debug("user left autochannel")
                await self.delete_autochannel(srv, member.guild,
                                              before.channel)

    @commands.Cog.listener()
    async def on_ready(self):
        self._logger.trace("on_ready")
        await self.cleanup()
Ejemplo n.º 9
0
class Tracker(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

    def get_nicknames(self, u):
        self._logger.trace("get_nicknames")
        usr = cassandra.get_user(u)
        return usr.nicknames

    def get_names(self, u):
        self._logger.trace("get_names")
        usr = cassandra.get_user(u)
        return usr.names

    def add_nickname(self, u, dusr):
        self._logger.trace("add_nickname")
        usr = cassandra.get_user(u)

        if dusr.nick is None:
            return False  # Has no nick

        if dusr.nick in usr.nicknames:
            # Dispatch event
            cassandra.dispatch("nickname-change", {
                "user": usr,
                "nick": dusr.nick,
                "known": True
            })
            return False  # Known nick

        # Dispatch event
        cassandra.dispatch("nickname-change", {
            "user": usr,
            "nick": dusr.nick,
            "known": False
        })

        usr.nicknames.append(dusr.nick)

        return True  # New nick

    def add_name(self, u, dusr):
        self._logger.trace("add_name")
        usr = cassandra.get_user(u)

        name = dusr.name + "#" + dusr.discriminator

        if name in usr.names:
            # Dispatch event
            cassandra.dispatch("name-change", {
                "user": usr,
                "name": name,
                "known": True
            })
            return False  # Known name

        # Dispatch event
        cassandra.dispatch("name-change", {
            "user": usr,
            "name": dusr.nick,
            "known": False
        })

        usr.names.append(name)

        return True  # New name

    @commands.Cog.listener()
    async def on_ready(self):
        self.update.start()

    @commands.command(name="whois")
    @commands.has_permissions(manage_messages=True)
    async def whois(self, ctx, member: discord.Member):
        self._logger.trace("whois")
        usr = cassandra.get_user((ctx.guild.id, member.id))
        if usr is None:
            await ctx.send("Couldn't get user information")
            return

        title = "%s#%s" % (member.name, member.discriminator)
        if member.nick is not None:
            title = title + ("(%s)" % member.nick)

        nicknames = "No nicknames tracked"
        nicklist = self.get_nicknames(usr)
        if len(nicklist) > 0:
            nicknames = "```" + ', '.join(nicklist) + '```'

        names = "No names tracked"
        namelist = self.get_names(usr)
        if len(namelist) > 0:
            names = "```" + ', '.join(namelist) + '```'

        embed = discord.Embed(title=title)
        embed.set_thumbnail(url=member.avatar_url)

        embed.add_field(name="ID", value=str(member.id))
        embed.add_field(name="Account Created",
                        value=str(member.created_at),
                        inline=True)
        embed.add_field(name="Joined At",
                        value=str(member.joined_at),
                        inline=True)
        embed.add_field(name="Nicknames", value=nicknames)
        embed.add_field(name="Names", value=names)

        await ctx.send(embed=embed)

    @commands.Cog.listener()  # Username, discriminator
    async def on_member_join(self, member):
        self._logger.trace("on_member_join")
        usr = cassandra.get_user((member.guild.id, member.id))

        update = False

        if self.add_name(usr, member):
            update = True

        if self.add_nickname(usr, member):
            update = True

        if update:
            cassandra.save_user(usr)

    @commands.Cog.listener()  # Nickname
    async def on_member_update(self, before, after):
        self._logger.trace("on_member_update")
        usr = cassandra.get_user((after.guild.id, after.id))

        if self.add_nickname(usr, after):
            cassandra.save_user(usr)

    @commands.Cog.listener()  # Username, discriminator
    async def on_user_join(self, member):
        usr = cassandra.get_user((member.guild.id, member.id))

        if self.add_nickname(usr, member):
            cassandra.save_user(usr)

    @tasks.loop(seconds=UPDATE_TIMEOUT)
    async def update(self):
        self._logger.trace("update")

        for guild in self.client.guilds:
            # Do not do anything for unavailable guilds
            if guild.unavailable == True:
                continue

            for member in guild.members:
                # If member is a bot, ignore
                if member.bot:
                    continue

                usr = cassandra.get_user((guild.id, member.id))
                self.add_nickname(usr, member)
                self.add_name(usr, member)
Ejemplo n.º 10
0
class Moderation(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._logger = Logger(self)

    def get_help_page(self):
        return {
            "title": "Moderation Commands",
            "description":
            "These commands require moderation or administrator permissions.",
            "content": {
                "clear [count = 10]": "Clears the chat.",
                "ban <user> [reason...]": "Bans a user from the server.",
                "kick <user> [reason...]": "Kicks a user from the server.",
                "warn <user> [reason...]": "Warns a user.",
                "quarantine <user> [reason...]":
                "Strips a user from all roles.",
                "unquarantine <user>": "Restores a users roles.",
                # "history <user>": "Gets the moderation history for a user.",
                "whois <user>":
                "Displays some extended information about a user."
            }
        }

    def is_user_quarantined(self, u):
        usr = cassandra.get_user(u)
        return usr.quarantine_status

    @commands.command(name="warn")
    @commands.has_permissions(manage_messages=True)
    async def warn(self, ctx, member: discord.Member, *, reason=None):
        self._logger.trace("warn")
        # Audit log
        caudit = self.client.get_cog('Audit')
        if caudit is not None:
            if reason is None:
                await caudit.print_audit(
                    ctx.author, 'warning', "Warned %s#%s for no reason." %
                    (member.name, member.discriminator))
            else:
                await caudit.print_audit(
                    ctx.author, 'warning', "Warned %s#%s for **\"%s\"**." %
                    (member.name, member.discriminator, reason))

        reason_str = reason
        if reason is None:
            reason_str = "No reason given"

        embed = discord.Embed(title="%s %s: You have been warned!" %
                              (EMOJI_WARN, ctx.guild.name),
                              description="Reason: %s" % reason_str,
                              colour=discord.Colour.gold())

        await member.send(embed=embed)

        usr = cassandra.get_user((guild.id, member.id))

        # Dispatch event
        cassandra.dispatch("mod-warning", {
            "dauthor": ctx.author,
            "reason": reason,
            "duser": member,
            "user": usr
        })

        # TODO: Append to user history
        # usr = load_user(ctx.guild.id, member.id)
        # histentry = {
        #     "type": "warning",
        #     "time": getts(),
        #     "by": ctx.author.id,
        #     "reason": reason
        # }
        # usr["history"].append(histentry)
        # save_user(usr)

    @commands.command(name="ban")
    @commands.has_permissions(ban_members=True)
    async def ban(self, ctx, member: discord.Member, *, reason=None):
        self._logger.trace("ban")
        # TODO: Tempban

        # Audit log
        caudit = self.client.get_cog('Audit')
        if caudit is not None:
            if reason is None:
                await caudit.print_audit(
                    ctx.author, 'ban', "Baned %s#%s for no reason." %
                    (member.name, member.discriminator))
            else:
                await caudit.print_audit(
                    ctx.author, 'ban', "Baned %s#%s for **\"%s\"**." %
                    (member.name, member.discriminator, reason))

        if reason is None:
            reason = "No reason given"

        embed = discord.Embed(title="%s %s: The BAN-HAMMER has spoken!" %
                              (EMOJI_BAN, ctx.guild.name),
                              description="Reason: %s" % reason,
                              colour=discord.Colour.red())

        # We need to send the message first because discord does not let you send messages to users that have no servers in common
        await member.send(embed=embed)

        usr = cassandra.get_user((guild.id, member.id))

        # Dispatch event
        cassandra.dispatch("mod-ban", {
            "dauthor": ctx.author,
            "reason": reason,
            "duser": member,
            "user": usr
        })

        await member.ban(reason=reason)

        # TODO: User history
        # usr = load_user(ctx.guild.id, member.id)
        # histentry = {
        #     "type": "ban",
        #     "time": getts(),
        #     "by": ctx.author.id,
        #     "reason": reason
        # }
        # usr["history"].append(histentry)
        # save_user(usr)

    @commands.command(name="qrole")
    @commands.has_permissions(manage_channels=True)
    async def qrole(self, ctx, action, role: discord.Role = None):
        self._logger.trace("qrole")

        srv = cassandra.get_server(ctx.guild.id)
        if action in ["unset", "remove", "delete", "del", "rem"]:
            srv.quarantine_role = None
            cassandra.save_server(srv)

            await ctx.send("Quarantine role unset.")

        elif action in ["set", "add"]:
            if role is None:
                return  # TODO: Print help

            srv.quarantine_role = role.id
            cassandra.save_server(srv)

            await ctx.send("Quarantine role set.")

    @commands.command(name="quarantine")
    @commands.has_permissions(manage_roles=True)
    async def quarantine(self, ctx, member: discord.Member, *, reason=None):
        self._logger.trace("quarantine")

        usr = cassandra.get_user((ctx.guild.id, member.id))
        if usr.quarantine_status == True:
            await ctx.send("This user is already in quarantine!")
            return

        # Audit log
        caudit = self.client.get_cog('Audit')
        if caudit is not None:
            if reason is None:
                await caudit.print_audit(
                    ctx.author, 'quarantine',
                    "Quarantined %s#%s for no reason." %
                    (member.name, member.discriminator))
            else:
                await caudit.print_audit(
                    ctx.author, 'quarantine',
                    "Quarantined %s#%s for **\"%s\"**." %
                    (member.name, member.discriminator, reason))

        old_roles = []
        for role in member.roles:
            if role == ctx.guild.default_role:
                continue
            old_roles.append(role.id)
            try:
                await member.remove_roles(role, reason="Quarantine")
            except Exception as ex:
                self._logger.warn(
                    "Failed to remove role %s from member %s: %s" %
                    (role.name, member.name, ex))

        if usr.server.quarantine_role is not None:
            qrole = get(ctx.guild.roles, id=usr.server.quarantine_role)
            if qrole is not None:
                try:
                    await member.add_roles(qrole, reason="Quarantine")
                except Exception as ex:
                    self._logger.warn(
                        "Failed to add role %s to member %s: %s" %
                        (qrole.name, member.name, ex))

        # Dispatch event
        cassandra.dispatch("mod-quarantine", {
            "dauthor": ctx.author,
            "reason": reason,
            "duser": member,
            "user": usr
        })

        usr.quarantine_status = True
        usr.quarantine_roles = old_roles
        cassandra.save_user(usr)

        await ctx.send("User %s#%s has been quarantined." %
                       (member.name, member.discriminator))

    @commands.command(name="unquarantine")
    @commands.has_permissions(manage_roles=True)
    async def unquarantine(self, ctx, member: discord.Member):
        self._logger.trace("unquarantine")

        usr = cassandra.get_user((ctx.guild.id, member.id))
        if usr.quarantine_status == False:
            await ctx.send("This user is not in quarantine!")
            return


# Audit log
        caudit = self.client.get_cog('Audit')
        if caudit is not None:
            if reason is None:
                await caudit.print_audit(
                    ctx.author, 'unquarantine', "Unquarantined %s#%s." %
                    (member.name, member.discriminator))

        if usr.server.quarantine_role is not None:
            qrole = get(ctx.guild.roles, id=usr.server.quarantine_role)
            if qrole is not None:
                try:
                    await member.remove_roles(qrole, reason="Unquarantine")
                except Exception as ex:
                    self._logger.warn(
                        "Failed to remove role %s from member %s: %s" %
                        (qrole.name, member.name, ex))

        for roleid in usr.quarantine_roles:
            role = get(ctx.guild.roles, id=roleid)
            if role is None:
                continue
            if role == ctx.guild.default_role:
                continue
            try:
                await member.add_roles(role, reason="Unquarantine")
            except Exception as ex:
                self._logger.warn("Failed to add role %s to member %s: %s" %
                                  (role.name, member.name, ex))

        # Dispatch event
        cassandra.dispatch("mod-unquarantine", {
            "dauthor": ctx.author,
            "duser": member,
            "user": usr
        })

        usr.quarantine_status = False
        usr.quarantine_roles = []
        cassandra.save_user(usr)

        await ctx.send("User %s#%s has been released form quarantine." %
                       (member.name, member.discriminator))

    @commands.command(name="kick")
    @commands.has_permissions(kick_members=True)
    async def kick(self, ctx, member: discord.Member, *, reason=None):
        self._logger.trace("kick")

        # Audit log
        caudit = self.client.get_cog('Audit')
        if caudit is not None:
            if reason is None:
                await caudit.print_audit(
                    ctx.author, 'kick', "Kicked %s#%s for no reason." %
                    (member.name, member.discriminator))
            else:
                await caudit.print_audit(
                    ctx.author, 'kick', "Kicked %s#%s for **\"%s\"**." %
                    (member.name, member.discriminator, reason))

        if reason is None:
            reason = "No reason given"

        embed = discord.Embed(title="%s %s: You have beed kicked!" %
                              (EMOJI_INFO, ctx.guild.name),
                              description="Reason: %s" % reason,
                              colour=discord.Colour.dark_orange())

        # We need to send the message first because discord does not let you send messages to users that have no servers in common
        await member.send(embed=embed)

        usr = cassandra.get_user((guild.id, member.id))

        # Dispatch event
        cassandra.dispatch("mod-kick", {
            "dauthor": ctx.author,
            "reason": reason,
            "duser": member,
            "user": usr
        })

        await member.kick(reason=reason)

        # TODO: User history
        # usr = load_user(ctx.guild.id, member.id)
        # histentry = {
        #     "type": "kick",
        #     "time": getts(),
        #     "by": ctx.author.id,
        #     "reason": reason
        # }
        # usr["history"].append(histentry)
        # save_user(usr)

    @commands.command(name="clear")
    @commands.has_permissions(manage_messages=True)
    async def clear(self, ctx, amt=10):
        self._logger.trace("clear")
        if not type(amt) is int:
            try:
                amt = int(amt, 10)
            except ValueError:
                await ctx.send("Invalid number of messages given")
                return

        if amt < 1:
            await ctx.send("Can not remove %s messages" % amt)
            return
        elif amt > 100:
            amt = 100

        await ctx.channel.purge(limit=amt + 1)

        # Audit log
        caudit = self.client.get_cog('Audit')
        if caudit is not None:
            await caudit.print_audit(
                ctx.author, 'clear', "Cleared %s messages from channel <#%s>" %
                (amt, ctx.channel.id))

        # Dispatch event
        cassandra.dispatch("mod-clear", {
            "dauthor": ctx.author,
            "amount": amt,
            "channel": ctx.channel
        })