コード例 #1
0
ファイル: admin.py プロジェクト: LeicaSimile/PROJECT-Ashe
async def get_inactive_members(context, progress_report=True):
    """Returns a list of inactive members."""
    senders = []
    inactive_members = []
    now = datetime.datetime.now()
    days_threshold = Settings.inactive_threshold(context.guild.id)
    time_boundary = now - datetime.timedelta(days=days_threshold)
    progress_msg = None
    include_reactions = Settings.include_reactions_inactivity(context.guild.id)

    included_channels = context.guild.text_channels
    channel_count = len(included_channels)

    if progress_report:
        progress_msg = await utils.say(context.channel, content=f"Scanning {channel_count} channels for inactive members.")
    
    for i, channel in enumerate(included_channels):
        try:
            async for m in channel.history(limit=None, after=(now - datetime.timedelta(days=days_threshold)), oldest_first=False):
                if m.author not in senders:
                    senders.append(m.author)

                if include_reactions:
                    for r in m.reactions:
                        async for u in r.users():
                            if u not in senders:
                                senders.append(u)

        except discord.errors.Forbidden:
            print(f"Can't access {channel.name}")
        else:
            if progress_msg:
                await progress_msg.edit(content=f"Scanned {i}/{channel_count} channels for inactive members.")
    
    if progress_msg:
        await progress_msg.edit(content=f"Scanned {channel_count} channels for inactive members.")
    
    results = [
        u for u in context.guild.members
        if u not in senders and u.joined_at < time_boundary and not u.bot
    ]
    db_inactive_members = [] # database.get_all_inactive_members(context.guild.id)

    for member in results:
        if member.id in db_inactive_members:
            m = db_inactive_members[member.id]
            m.user = member
            inactive_members.append(m)
        else:
            inactive_members.append(database.InactiveMember(context.guild.id, member.id, user=member))
    # update_inactive_members(db_inactive_members, {m.member_id: m for m in inactive_members})

    return inactive_members
コード例 #2
0
def run_game():
    ai_settings = Settings()
    pygame.init()
    screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")
    ship = Ship(ai_settings,screen)
    stats = GameStats(ai_settings)
    bullets = Group()
    aliens = Group()
    stars = Group()
    drops = Group()
    sb = Scoreboard(ai_settings, screen, stats)
    play_button = Button(ai_settings, screen,"Play")
    gf.create_fleet(ai_settings,screen,ship, aliens)
    gf.create_sky(ai_settings, screen, stars)
    #gf.create_rain(ai_settings, screen, drops)
    while True:
        gf.check_events(ai_settings,screen,stats,sb, play_button, ship,aliens, bullets)
        if stats.game_active:
            ship.update()
            bullets.update()
            gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets)
            gf.update_aliens(ai_settings, stats, screen, sb,ship, aliens, bullets)
            #gf.update_drops(ai_settings, drops)
        gf.update_screen(ai_settings,screen,stats, sb, stars, ship,aliens, bullets, drops, play_button)
コード例 #3
0
ファイル: admin.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def message(self, context):
        async def get_destination(context):
            def check_destination(msg):
                return msg.author.id == context.message.author.id and msg.channel.id == context.channel.id and msg.channel_mentions

            await utils.say(context.channel, content="Which channel should the message be sent to?")
            try:
                destination = await self.bot.wait_for("message", timeout=60, check=check_destination)
                return destination.content
            except asyncio.TimeoutError:
                return False

        async def get_message(context):
            def check_message(msg):
                return msg.author.id == context.message.author.id and msg.channel.id == context.channel.id

            await utils.say(context.channel, content="What's your message?")
            try:
                message = await self.bot.wait_for("message", timeout=120, check=check_message)
                return message.content
            except asyncio.TimeoutError:
                return False

        cmd_settings = Settings.command_settings("message", context.guild.id)
        if not cmd_settings.get("enabled"):
            return

        arguments = context.message.content.split(maxsplit=2)
        destination_id = None
        msg = ""

        try:
            destination_id = arguments[1]
        except IndexError:
            destination_id = await get_destination(context)
            if not destination_id:
                return CommandStatus.CANCELLED

        destination_id = destination_id.strip("<># ")
        destination = discord.utils.find(lambda c: str(c.id) == destination_id, context.guild.text_channels)
        if not destination:
            await utils.say(context.channel, content="I couldn't find that channel on this server.")
            return CommandStatus.INVALID

        try:
            msg = arguments[2]
        except IndexError:
            msg = await get_message(context)
            if not msg:
                return CommandStatus.CANCELLED
        
        try:
            sent = await utils.say(destination, content=msg)
        except discord.Forbidden:
            await utils.say(context.channel, content=f"I don't have permission to send messages to {destination.name}")
            return CommandStatus.FORBIDDEN
        else:
            await utils.say(context.channel, content=f"Message sent: {sent.jump_url}")
コード例 #4
0
def regular_lookup(word: str):
    """Looks up given word in Merriam-Webster Collegiate dictionary
    
    Args:
        word(str): Word to look up in dictionary.

    Returns:
        list: Can be a list of DictionaryEntry objects or a list of suggested strings to search up
              (in case of misspelling)

    """
    entries = []
    entries = regular_cache.get_entries(word)
    if not entries:
        api_url = Settings.command_settings("define")["base_api_url"]
        api_key = Settings.config["env"]["dict_regular_api_key"]
        req_location = f"{api_url}{word}?key={api_key}"
        response = requests.get(req_location, timeout=30)
        res_data = response.json()
        if not res_data:
            return None # Word not found

        original_found = False
        clean_word = re.sub(r"\s+", " ", word.strip().lower())
        for i, entry in enumerate(res_data):
            if isinstance(entry, str):
                entries.append(entry)
            elif re.match(
                    r"{word}\W?(?:\:[\d\w]+)?$".format(word=clean_word),
                    entry.get("meta", {"id": ""})["id"],
                    re.I):
                original_found = True
                try:
                    # Check for spelling variants
                    if not entry.get("shortdef") and entry.get("cxs"):
                        for variant in regular_lookup(entry["cxs"][0]["cxtis"][0]["cxt"]):
                            entries.append(variant)
                    else:
                        entries.append(DictionaryEntry(entry))
                except (IndexError, KeyError) as e:
                    continue
            elif 0 == i and not entries:
                try:
                    # Check for spelling variants
                    if not entry.get("shortdef") and entry.get("cxs"):
                        for variant in entry["cxs"][0]["cxtis"]:
                            if variant.get("cxt"):
                                entries.append(variant["cxt"])
                    else:
                        entries.append(DictionaryEntry(entry))
                except (IndexError, KeyError) as e:
                    continue
            elif not original_found:
                entries.append(DictionaryEntry(entry))

        regular_cache.add_entries(word, entries)
    
    return entries
コード例 #5
0
ファイル: admin.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def inactivelist(self, context):
        cmd_settings = Settings.command_settings(context.command.name, context.guild.id)
        if not cmd_settings.get("enabled"):
            return

        inactive_members = await get_inactive_members(context)
        inactive_list = []
        for i in inactive_members:
            last_notified = i.last_notified.strftime(" (%b %d, %Y %Z)") if i.last_notified else ""
            entry = f"{'**EXEMPT** ' if i.is_exempt else ''}{i.user.mention} [{i.user.display_name}]{last_notified}"
            inactive_list.append(entry)

        days_threshold = Settings.inactive_threshold(context.guild.id)
        embeds = utils.split_embeds(
            title=f"Inactive Members ({days_threshold}+ days since last message)",
            description="\n".join(inactive_list)
        )

        for i, embed in enumerate(embeds):
            if i < len(embeds) - 1:
                await utils.say(context.channel, embed=embed)
            else:
                # Final message
                inactivity_message = Settings.inactive_message(context.guild.id)
                if inactivity_message:
                    embed.set_footer(text="React 📧 below to notify them")
                report = await utils.say(context.channel, content=f"{context.author.mention}", embed=embed)
        
                if inactivity_message:
                    await report.add_reaction("📧")
                    def check(reaction, user):
                        return reaction.message.id == report.id and user.id == context.message.author.id \
                        and str(reaction.emoji) == "📧"

                    try:
                        await self.bot.wait_for("reaction_add", timeout=600, check=check)
                    except asyncio.TimeoutError:
                        embed.set_footer(text=discord.Embed.Empty)
                        await report.edit(embed=embed)
                        await report.clear_reactions()
                    else:
                        await self.notify_inactive_members(context, inactive_members)
        
        return CommandStatus.COMPLETED
コード例 #6
0
ファイル: bot.py プロジェクト: LeicaSimile/PROJECT-Ashe
 async def on_command_error(self, context, error):
     cmd_name = context.command.name
     cmd_settings = Settings.command_settings(cmd_name, context.guild.id)
     if isinstance(error, discord.ext.commands.MissingPermissions
                   ) and cmd_settings.get("visible"):
         notice = "You need the following permissions for this command: {}".format(
             ", ".join([f"`{p}`" for p in error.missing_perms]))
         await utils.say(context.channel, content=notice)
     elif isinstance(error, discord.ext.commands.NotOwner):
         return
     else:
         Logger.error(logger, error)
コード例 #7
0
ファイル: admin.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def notify_inactive_members(self, context, members=None):
        message = Settings.inactive_message(context.guild.id)
        if not message:
            await utils.say(context.channel, content="There is no inactivity message for this server.")
            return CommandStatus.FAILED

        if not members:
            members = await get_inactive_members(context)
        members = [i.user for i in members]
        
        await self.notify_members(context, members, message, use_case="inactivity")
        return CommandStatus.COMPLETED
コード例 #8
0
ファイル: admin.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def notify_members(self, context, members, message, use_case=None):
        success = []
        failed = []
        for member in members:
            notification = message

            inactivity_settings = Settings.inactivity_features(context.guild.id)
            if use_case == "inactivity" and inactivity_settings.get("message_invite_enabled"):
                # Attach invite to message
                invite_channel = None
                invite_channel_id = inactivity_settings.get("message_invite_channel")
                if invite_channel_id:
                    invite_channel = context.guild.get_channel(invite_channel_id)
                
                if not invite_channel:
                    invite_channel = context.guild.text_channels[0]

                # Translate hours to seconds for max_age
                max_age = 3600 * inactivity_settings.get("message_invite_hours")
                max_uses = inactivity_settings.get("message_invite_max_uses")
                reason = inactivity_settings.get("message_invite_reason")
                
                try:
                    invite = await invite_channel.create_invite(max_age=max_age, max_uses=max_uses, reason=reason)
                    notification = f"{notification}\n{invite.url}"
                except discord.HTTPException as e:
                    print(e)
                    failed.append(member)
                    await utils.say(context.channel, content=f"An error happened while creating invite: {e.text} (error code: {e.code})")
                    break

            try:
                await utils.say(member, context=context, parse=True, content=notification)
            except discord.DiscordException as e:
                failed.append(member)
                print(e)
            else:
                success.append(member)

        if success:
            messaged = "\n".join([f"{m.mention} [{m.display_name}]" for m in success])
            for embed in utils.split_embeds(
                title="Notified Members",
                description=messaged
            ):
                await utils.say(context.channel, embed=embed)
        if failed:
            not_messaged = "\n".join([f"{m.mention} [{m.display_name}]" for m in failed])
            for embed in utils.split_embeds(
                title="Failed to Notify",
                description=not_messaged
            ):
                await utils.say(context.channel, embed=embed)
コード例 #9
0
ファイル: admin.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def exempt(self, context):
        cmd_settings = Settings.command_settings("exempt", context.guild.id)
        if not cmd_settings.get("enabled"):
            return

        args = context.message.split(maxsplit=2)
        cmd_type = args[1].lower()
        if cmd_type == "add":
            pass
        elif cmd_type == "remove":
            pass
        elif cmd_type == "list":
            pass
コード例 #10
0
ファイル: general.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def define(self, context):
        args = context.message.clean_content.split(maxsplit=1)
        if 2 > len(args):
            await utils.say(
                context.channel,
                content=
                f"Type `{context.prefix}{context.command.name} {context.command.usage}` to look up a term in the dictionary."
            )
            return

        search_term = re.sub(r"\s+", " ", args[1].strip())
        search_results = utils.dictionary.regular_lookup(search_term)

        if search_results:
            base_url = Settings.command_settings("define")["base_url"]
            search_url = requote_uri(f"{base_url}{search_term}")
            reply = discord.Embed(title=f'Define "{search_term}"',
                                  url=search_url)
            reply.set_footer(text=f"Source: {search_url}")
            try:
                num_entries = len(search_results)
                definitions = []
                for i, entry in enumerate(search_results):
                    if i > 2:
                        break

                    is_offensive = " *(offensive)*" if entry.is_offensive else ""
                    term_type = entry.term_type
                    definitions.append(
                        f"**{search_term}** {i + 1}/{num_entries} *({term_type})*{is_offensive}"
                    )
                    definitions.append("".join(
                        ["*", "\n\n".join(entry.short_definitions), "*"]))
                    definitions.append("\n")

                reply.description = "\n".join(definitions)
            except AttributeError:
                # Suggested search terms
                reply.url = ""
                suggestions = "\n".join(search_results)
                reply.description = f"**Did you mean...**\n*{suggestions}*"

            await utils.say(context.channel, embed=reply)
        else:
            await utils.say(
                context.channel,
                content=f"Couldn't find a definition for `{search_term}`.")
コード例 #11
0
    def __init__(self):
        """Initialize the game, and create game resources."""
        pygame.init()
        self.settings = Settings()

        self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
        self.settings.screen_width = self.screen.get_rect().width
        self.settings.screen_height = self.screen.get_rect().height
        pygame.display.set_caption("Alien Invasion")

        # Create an instance to store game statistics.
        self.stats = GameStats(self)

        self.ship = Ship(self)
        self.bullets = pygame.sprite.Group()
        self.aliens = pygame.sprite.Group()

        self._create_fleet()
コード例 #12
0
ファイル: bot.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def on_member_update(self, before, after):
        milestones = Settings.on_member_update_features(
            after.guild.id, "role_message")
        if not milestones or not milestones.get("enabled"):
            return

        before_roles = [r.id for r in before.roles]
        after_roles = [r.id for r in after.roles]
        for role in milestones["roles"]:
            if role in after_roles and role not in before_roles:
                output_channel = discord.utils.get(
                    after.guild.channels,
                    name=milestones["roles"][role]["channel"])
                await utils.say(output_channel,
                                context=after,
                                parse=True,
                                content=milestones["roles"][role]["message"])

        return
コード例 #13
0
ファイル: bot.py プロジェクト: LeicaSimile/PROJECT-Ashe
    def set_commands(self, *cmds):
        self.add_cog(commands.Admin(self))
        self.add_cog(commands.General(self))
        self.add_cog(commands.Statistics(self))

        for c in cmds:
            self.add_cog(c)

        for command in self.commands:
            Logger.debug(logger, f"Setting up command '{command.name}'")
            cmd_settings = Settings.command_settings(command.name)
            if not cmd_settings:
                continue

            command.aliases = cmd_settings.get("aliases", [])
            command.hidden = not cmd_settings.get("visible", True)
            command.description = cmd_settings.get("description", "")
            command.help = cmd_settings.get("help", "")
            command.usage = cmd_settings.get("usage", "")
            Logger.debug(logger, f"Command '{command.name}' all set")
コード例 #14
0
ファイル: general.py プロジェクト: LeicaSimile/PROJECT-Ashe
def split_embeds(title: str, description: str, delimiter="\n", **kwargs):
    """Returns a list of embeds split according to Discord character limits.
    
    Args:
        title(str): Title of the embed
        description(str): Embed description (body text)
    """
    embeds = []
    char_count = len(description)
    max_chars = Settings.app_standards("embed")["description_limit"]
    current_count = 0

    # Minimum length of description as percentage of max_chars (0 to 1)
    # e.g. 0.5 = description length must be at least 50% of max_chars
    min_threshold = 0.25

    while current_count < char_count:
        current_description = description[current_count:]
        if len(current_description) > max_chars:
            end = current_count + max_chars
            current_description = description[current_count:end]
            split_description = current_description.rsplit(delimiter, maxsplit=1)
            if len(split_description) < 2 or len(split_description[0]) < (max_chars * min_threshold):
                split_description = current_description.rsplit(maxsplit=1)
                if len(split_description) > 1:
                    current_count += 1
            elif len(split_description) > 1:
                current_count += len(delimiter)

            current_description = split_description[0]
        
        current_count += len(current_description)
        embeds.append(discord.Embed(
            title=title,
            description=current_description,
            **kwargs
        ))

    return embeds
コード例 #15
0
ファイル: admin.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def purgeleaderboard(self, context):
        cmd_settings = Settings.command_settings("purgeleaderboard", context.guild.id)
        if not cmd_settings.get("enabled"):
            return

        mee6 = mee6_py_api.API(context.guild.id)
        member_ids = [str(m.id) for m in context.guild.members]
        try:
            leaderboard_pages = await mee6.levels.get_all_leaderboard_pages()
            absent_members = []
            for page in leaderboard_pages:
                players = page.get("players")
                absent_members.extend([f"**{p.get('username')}**#{p.get('discriminator')} — lv{p.get('level')}" for p in players if p.get("id") not in member_ids])
                if not absent_members:
                    continue

            for embed in utils.split_embeds(
                title=f"MEE6 leaderboard members who left the server",
                description="\n".join(absent_members)
            ):
                await utils.say(context.channel, embed=embed)

        except mee6_py_api.exceptions.HTTPRequestError:
            await utils.say(context.channel, content="I couldn't find this server's MEE6 leaderboard.")
コード例 #16
0
def main():
    command_prefix = Settings.app_defaults("cmd_prefix")
    description = Settings.app_defaults("description")
    ashe = Bot(command_prefix=command_prefix, description=description)
    ashe.run()
コード例 #17
0
ファイル: bot.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def on_message(self, message):
        async def check_content(message, check, custom_message):
            if check:
                content = message.clean_content
                try:
                    await message.delete()
                except (discord.Forbidden, discord.HTTPException) as e:
                    Logger.warn(
                        logger,
                        f"Unable to delete message at {message.jump_url}. {e}")
                else:
                    await utils.say(
                        message.author,
                        context=message,
                        parse=True,
                        content=
                        f"{custom_message}\nYour message: ```{content}```")
                    return True

            return False

        user = message.author
        if message.guild:
            Logger.info(
                logger,
                f"({message.guild.name} - {message.channel.name}) {user.name}: {message.content}"
            )
        else:
            try:
                Logger.info(
                    logger,
                    f"({message.channel.name}) {user.name}: {message.content}")
            except AttributeError:
                Logger.info(logger, f"({user.name}) {message.content}")
            finally:
                return

        mee6_level_up = Settings.on_message_features(message.guild.id,
                                                     "mee6_level_up")
        if mee6_level_up and mee6_level_up.get(
                "enabled") and user.id == mee6_level_up["bot_id"]:
            result = re.compile(r"{}".format(
                mee6_level_up["message_pattern"])).search(message.content)
            if result:
                mentioned = message.mentions[0]
                level = int(result.group(1))
                Logger.info(logger, f"{mentioned.name} reached level {level}")

                roles = mee6_level_up["roles"]
                for r in roles:
                    if level >= r:
                        role = discord.utils.get(message.guild.roles,
                                                 id=roles[r]["id"])
                        await mentioned.add_roles(
                            role, reason=f"User reached level {r}")

        pics_only = Settings.on_message_features(message.guild.id, "pics_only")
        if pics_only and pics_only.get("enabled"):
            channel = message.channel

            if channel.id in pics_only["channels"]:
                if await check_content(
                        message, not message.attachments,
                        pics_only["channels"][channel.id]["message"]):
                    return

        await self.process_commands(message)
コード例 #18
0
ファイル: bot.py プロジェクト: LeicaSimile/PROJECT-Ashe
 async def on_ready(self):
     prefix = Settings.app_defaults("cmd_prefix")
     Logger.info(logger,
                 f"{self.user.name} (ID: {self.user.id}) is now online.")
     status = Settings.app_defaults("status").format(prefix=prefix)
     await self.change_presence(activity=discord.Game(name=status))
コード例 #19
0
ファイル: admin.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def inactivenotify(self, context):
        cmd_settings = Settings.command_settings(context.command.name, context.guild.id)
        if not cmd_settings.get("enabled"):
            return

        await self.notify_inactive_members(context)
コード例 #20
0
ファイル: admin.py プロジェクト: LeicaSimile/PROJECT-Ashe
    async def edit(self, context):
        def check_id(msg):
            return msg.author.id == context.message.author.id and msg.channel.id == context.channel.id

        def check_message(msg):
            return msg.author.id == context.message.author.id and msg.channel.id == context.channel.id

        cmd_settings = Settings.command_settings("edit", context.guild.id)
        if not cmd_settings.get("enabled"):
            return

        # TODO: Let message ID be passed in first go - skip channel arg and loop through all?
        # arguments = context.message.content.split()
        message_id = 0
        if not context.message.channel_mentions:
            await utils.say(context.channel, content="To use, put: ```;edit #[channel name]```, where [channel name] is where the message is.")
            return CommandStatus.INVALID
        channel = context.message.channel_mentions[0]

        await utils.say(context.channel, content="Enter the message ID to be edited:")
        try:
            message_id = await self.bot.wait_for("message", timeout=300, check=check_id)
            message_id = message_id.content
        except asyncio.TimeoutError:
            await utils.say(context.channel, content="Time's up.")
            return CommandStatus.CANCELLED

        try:
            message_id = int(message_id)
        except ValueError:
            await utils.say(context.channel, content=f"{message_id} is not a valid message ID.")
            return CommandStatus.INVALID

        to_edit = await channel.fetch_message(message_id)
        if not to_edit:
            await utils.say(context.channel, content=f"Couldn't find message with ID #{message_id}.")
            return CommandStatus.INVALID
        elif to_edit.author.id != context.guild.me.id:
            await utils.say(context.channel, content="I can only edit messages I sent.")
            return CommandStatus.INVALID
        else:
            for preview in utils.split_embeds(
                title="Message Preview",
                description=discord.utils.escape_markdown(to_edit.content),
                url=to_edit.jump_url,
                timestamp=to_edit.edited_at if to_edit.edited_at else to_edit.created_at
            ):
                await utils.say(context.channel, embed=preview)
            await utils.say(context.channel, content="Enter the newly edited message below.")
            
            try:
                new_edit = await self.bot.wait_for("message", timeout=900, check=check_message)
            except asyncio.TimeoutError:
                await utils.say(context.channel, content="Time's up.")
                return CommandStatus.CANCELLED
            else:
                try:
                    await to_edit.edit(content=new_edit.content)
                except discord.Forbidden:
                    await utils.say(context.channel, content="I'm not allowed to edit this message.")
                    return CommandStatus.FORBIDDEN
                else:
                    await utils.say(context.channel, content=f"Message edited: {to_edit.jump_url}")

        return CommandStatus.COMPLETED
コード例 #21
0
        Can be a list of DictionaryEntry objects or a list of suggested strings to search.
        """
        if word in self._cache:
            self._cache[word]["requests"] += 1
            return self._cache[word]["entries"]
        
        return []

    def _check_entries(self):
        """Remove entry with least amount of requests if cache is at or over limit"""
        if len(self._cache) >= self.limit:
            sorted_cache = [k for k, v in sorted(self._cache.items(), key=lambda item: item[1]["requests"])]
            self._cache.pop(sorted_cache[0], None)


regular_cache = DictionaryCache(limit=Settings.command_settings("define")["cache_limit"])
format_tokens = [
    {
        "start_token": "{b}",
        "end_token": "{\/b}",
        "pattern": r"",
        "start_sub": "**",
        "end_sub": "**"
    }
]

def format_text(text: str):
    pass

def regular_lookup(word: str):
    """Looks up given word in Merriam-Webster Collegiate dictionary