Пример #1
0
    async def giveaway(self, ctx, duration_minutes: positive_integer, channel: discord.TextChannel):
        if duration_minutes > 1440:
            await ctx.send(embed=failure_embed("Maximum duration is 24h!"), delete_after=5)
            return

        description = "React to this message to enter the giveaway and a chance to win license!"
        event_title = "Giveaway!"
        emoji = "🎉"
        message = await channel.send(embed=info_embed(description, ctx.me, title=event_title))
        await message.add_reaction(emoji)

        await asyncio.sleep(duration_minutes*60)

        try:
            done_message = await channel.fetch_message(message.id)
        except NotFound:
            logger.info(f"Event message deleted! Event canceled! Guild:{ctx.guild.id} {ctx.guild.name}, "
                        f"channel: {channel.id} {channel.name}")
            return

        for reaction in done_message.reactions:
            if str(reaction.emoji) == emoji:
                choices = []
                async for user in reaction.users():
                    if not user.bot:
                        choices.append(user)
                if choices:
                    winner = random.choice(choices)
                    edit_description = f"Giveaway has finished,\n{winner.mention} has won the raffle!"
                    await message.edit(embed=info_embed(edit_description, ctx.me, title=event_title))
                    await channel.send(f"{winner.mention} has won the raffle.", delete_after=10)
                else:
                    edit_description = "Giveaway has finished, no one reacted :("
                    await message.edit(embed=info_embed(edit_description, ctx.me, title=event_title))
            break
Пример #2
0
    async def ping(self, ctx):
        """
        Show bot ping.

        First value is REST API latency.
        Second value is Discord Gateway latency.

        """
        before = time.monotonic()
        message = await ctx.send(embed=info_embed("Pong", ctx.me))
        ping = (time.monotonic() - before) * 1000
        content = (f":ping_pong:   |   {int(ping)}ms\n"
                   f":timer:   |   {self.bot.latency * 1000:.0f}ms")
        await message.edit(embed=info_embed(content, ctx.me, title="Results:"))
Пример #3
0
    async def uptime(self, ctx):
        """
        Time since boot.

        """
        await ctx.send(
            embed=info_embed(self.last_boot(), ctx.me, title="Booted:"))
Пример #4
0
    async def support(self, ctx):
        """
        Shows invite to the support server.

        """
        description = f"Join **[support server]({self.support_server_invite})** for questions, suggestions and support."
        await ctx.send(embed=info_embed(description, ctx.me, title="Ask away!")
                       )
Пример #5
0
    async def invite(self, ctx):
        """
        Shows bot invite link.

        """
        invite_link = self._get_bot_invite_link()
        description = f"Use this **[invite link]({invite_link})** to invite me."
        await ctx.send(
            embed=info_embed(description, ctx.me, title="Invite me :)"))
Пример #6
0
    async def quickstart(self, ctx):
        """
        Shortly explains first time bot usage.

        """
        description = (
            f"To avoid repeating see **[github link]({self.github_bot_quick_start})** where quickstart "
            f"is explained in detail.")
        await ctx.send(
            embed=info_embed(description, ctx.me, title="Quickstart :)"))
Пример #7
0
    async def member_data(self, ctx, member: discord.Member = None):
        """
        Shows active subscriptions of member.
        Sends result in DMs

        """

        if member is None:
            member = ctx.author
        else:
            # If called member is the same as author allow him to see since it's himself
            if ctx.author == member:
                pass
            # We require admin permissions if you want to see other members data
            elif not ctx.author.guild_permissions.administrator:
                await ctx.send(embed=failure_embed(
                    "You need administrator permission to see other members data."
                ))
                return

        table = texttable.Texttable(max_width=90)
        table.set_cols_dtype(["t", "t"])
        table.set_cols_align(["c", "c"])
        header = ("Licensed role", "Expiration date")
        table.add_row(header)

        all_active = await self.bot.main_db.get_member_data(
            ctx.guild.id, member.id)
        if not all_active:
            await ctx.send(embed=failure_embed("Nothing to show."))
            return

        for entry in all_active:
            # Entry is in form ("license_id", "expiration_date)
            try:
                role = ctx.guild.get_role(int(entry[0]))
                table.add_row((role.name, entry[1]))
            except (ValueError, AttributeError):
                # Just in case if error in case role is None (deleted from guild) just show IDs from database
                table.add_row(entry)

        local_time = datetime.now()
        title = (
            f"Server local time: {local_time}\n\n"
            f"{member.name} active subscriptions in guild '{ctx.guild.name}':\n\n"
        )

        await ctx.send(embed=info_embed("Sent in Dms!", ctx.me),
                       delete_after=5)
        await Paginator.paginate(self.bot,
                                 ctx.author,
                                 ctx.author,
                                 table.draw(),
                                 title=title,
                                 prefix="```DNS\n")
Пример #8
0
    async def faq(self, ctx):
        """
        Show common Q/A about bot and it's usage.

        """
        disclaimer = (
            "Disclaimer: Bot is currently in alpha phase.\n"
            "Please let me know which features/improvements you want so I can focus on those.\n"
            f"Type `{ctx.prefix}support` for invite to support server.")

        bot_faq = (
            "**1. If bot gets kicked/banned do I lose all my data?**\n"
            "All of the guild data is immediately deleted from the database.\n\n"
            "**2. What happens if I delete a role or remove it manually from a member?**\n"
            "If that role is tied to any licenses/active subscriptions "
            "they are immediately deleted/canceled from the database.\n\n"
            "**3. What is the precision of role expiration?**\n"
            "Bot checks for expired licenses on startup and each 60 seconds after startup.\n\n"
            "**4. Who can view/generate guild licenses?**\n"
            "Only those who have role administrator in the guild.\n\n"
            "**5. How are licenses generated, are they unique?**\n"
            "They are completely unique, comprised of 30 randomly generated characters.\n\n"
            "**6. Can I generate licenses for roles other than default guild role?**\n"
            f"Use `{ctx.prefix}generate` command with custom arguments.\n"
            f"See **[github link]({self.github_bot_quick_start})** for example "
            f"or just call `{ctx.prefix}help generate` for more info.\n\n"
            "**7. What's the maximum for role expire time?**\n"
            "Maximum time for expiry date is 12 months.\n\n"
            "**8. How many stored licenses per guild?**\n"
            "Limit for stored (unactivated) licenses is "
            f"{self.bot.config.get_maximum_unused_guild_licences()} per guild.\n\n"
            "**9. How many activated licenses per member?**\n"
            "Members can have unlimited subscriptions active at the same time! "
            "(only limited by the Discord role limit per member which is 250).\n\n"
            "**10. What are the bot permissions for?**\n"
            f"To avoid repeating see **[github link]({self.github_permissions_link})** where permissions "
            "are explained in detail.\n\n"
            "**11. What if I deny any of those permissions when inviting the bot?**\n"
            "Bot was over-engineered to deal with all sorts of exceptions but I don't guarantee the bot "
            "will function properly or at all in that case.\n\n"
            "**12. Does the bot get updated? Will it affect usage?**\n"
            "There will be no breaking changes in updates, only improvements. "
            "During the update you will see that bot has changed status to 'Update' or is offline. "
            "During that time the bot may stop responding to commands, but this is only for <5 minutes. "
            "After that everything is back to normal.")
        await ctx.send(embed=info_embed(bot_faq, ctx.me, title=disclaimer))
Пример #9
0
    async def activate_license(self, ctx, license, member):
        """
        :param ctx: invoked context
        :param license: license to add
        :param member: who to give role to
        """
        guild = ctx.guild
        if await self.bot.main_db.is_valid_license(license, guild.id):
            # Adding role to the member requires that role object
            # First we get the role linked to the license
            role_id = await self.bot.main_db.get_license_role_id(license)
            role = ctx.guild.get_role(role_id)
            if role is None:
                log_error_msg = (
                    f"Can't find role {role_id} in guild {guild.id} '{guild.name}' "
                    f"from license: '{license}' member to give the role to: {member.id} '{member.name}'"
                    "\n\nProceeding to delete this invalid license from database!"
                )
                logger.critical(log_error_msg)

                msg = (
                    "Well this is awkward...\n\n"
                    "The role that was supposed to be given out by this license has been deleted from this guild!"
                    f"\n\nError message:\n\n{log_error_msg}")
                await ctx.send(embed=failure_embed(msg))
                await self.bot.main_db.delete_license(license)
                return
            # Now before doing anything check if member already has the role
            # Beside for logic (why redeem already existing subscription?) if we don't check this we will get
            # sqlite3.IntegrityError:
            #   UNIQUE constraint failed:LICENSED_MEMBERS.MEMBER_ID,LICENSED_MEMBERS.LICENSED_ROLE_ID
            # when adding new licensed member to table LICENSED_MEMBERS if member already has the role (because in that
            # table the member id and role id is unique aka can only have uniques roles tied to member id)
            if role in member.roles:
                # We notify user that he already has the role, we also show him the expiration date
                try:
                    expiration_date = await self.bot.main_db.get_member_license_expiration_date(
                        member.id, role_id)
                except DatabaseMissingData as e:
                    # TODO print role name instead of ID (from e)
                    msg = e.message
                    msg += (
                        f"\nThe bot did not register {member.mention} in the database with that role but somehow they have it."
                        "\nThis probably means that they were manually assigned this role without using the bot license system."
                        "\nHave someone remove the role from them and call this command again."
                    )
                    await ctx.send(embed=failure_embed(msg))
                    await ctx.message.delete()
                    return

                remaining_time = get_remaining_time(expiration_date)
                msg = (
                    f"{member.mention} already has an active subscription for the {role.mention} role!"
                    f"\nIt's valid for another {remaining_time}")
                await ctx.send(embed=warning_embed(msg))
                await ctx.message.delete()
                return
            # We add the role to the member, we do this before adding/removing stuff from db
            # just in case the bot doesn't have perms and throws exception (we already
            # checked for bot_has_permissions(manage_roles=True) but it can happen that bot has
            # that permission and check is passed but it's still forbidden to alter role for the
            # member because of it's role hierarchy.) -> will raise Forbidden and be caught by cmd error handler
            await member.add_roles(role, reason="Redeemed license.")
            # We add entry to db table LICENSED_MEMBERS (which we will checked periodically for expiration)
            # First we get the license duration so we can calculate expiration date
            license_duration = await self.bot.main_db.get_license_duration_hours(
                license)
            expiration_date = construct_expiration_date(license_duration)
            # In case where you successfully redeemed the role and it's still in database(not expired)
            # BUT someone manually removed the role, in that case when you try to redeem a valid license
            # for the said role you will get IntegrityError because LICENSED_ROLE_ID and MEMBER_ID have to
            # be unique (and the entry still exists in database).
            # Even when caught by remove role event leave this
            try:
                await self.bot.main_db.add_new_licensed_member(
                    member.id, guild.id, expiration_date, role_id)
            except IntegrityError:
                # We remove the database entry because when role was remove the bot was
                # probably offline and couldn't register the role remove event
                await self.bot.main_db.delete_licensed_member(
                    member.id, role_id)
                await self.bot.main_db.add_new_licensed_member(
                    member.id, guild.id, expiration_date, role_id)
                msg = (
                    f"Someone removed the role manually from {member.mention} but no worries,\n"
                    "since the license is valid we're just gonna reactivate it :)"
                )
                await ctx.send(embed=info_embed(msg, ctx.me))

            # Remove guild license from database, so it can't be redeemed again
            await self.bot.main_db.delete_license(license)
            # Send message notifying user
            msg = f"License valid - adding role {role.mention} to {member.mention} in duration of {license_duration}h"
            await ctx.send(embed=success_embed(msg, ctx.me))
        else:
            await ctx.send(embed=failure_embed(
                "The license key you entered is invalid/deactivated."))