예제 #1
0
    def on_analyze_command(self, event):
        if event.channel.name not in ALLOWED_CHANNELS:
            event.msg.reply(
                "{}, please run this command in #help.".format(event.author.mention))
            return

        if len(event.msg.attachments) < 1:
            event.msg.reply("{}, usage: Attach Xenia's logfile with your message (preferably compressed in a .zip).".format(
                event.author.mention))
            return

        # Fire off a typing event.
        self.client.api.channels_typing(event.channel.id)
        for _, attach in event.msg.attachments.items():
            s_file_name = sanitize(attach.filename, escape_codeblocks=True)
            if attach.size > SIZE_LIMIT_MB:
                event.msg.reply(event.author.mention, embed=MessageEmbed(title=s_file_name, color=COLOR_RED,
                                                                         description="**File above size limit, not analyzed**. Did you compress it?"))
                continue

            r = requests.get(attach.url)
            if r.status_code != 200:
                event.msg.reply(event.author.mention, embed=MessageEmbed(title=s_file_name, color=COLOR_RED,
                                                                         description="**Failed to fetch file from Discord**, status code {}".format(r.status_code)))
                continue

            mime = magic.from_buffer(r.content, mime=True)
            if mime == 'text/plain':
                # Plaintext, straight to the parser!
                event.msg.reply(event.author.mention, embed=self.parse_log_file(
                    attach.filename, io.StringIO(r.text)))
            elif mime == 'application/zip':
                z = zipfile.ZipFile(io.BytesIO(r.content))
                if len(z.namelist()) != 1:
                    event.msg.reply(event.author.mention, embed=MessageEmbed(
                        title=s_file_name, color=COLOR_RED, description="**Archives must contain only a single file**."))
                    continue

                # Parse every file in the zip file.
                for name in z.namelist():
                    # Check the guessed type as well. No voodoo embedding zip files inside one another.
                    mime = magic.from_buffer(z.open(name).read(1024), mime=True)
                    if mime != 'text/plain':
                        event.msg.reply(event.author.mention, embed=MessageEmbed(
                            title=s_file_name, color=COLOR_RED, description="**Contents not plaintext, ignored**."))
                        continue

                    event.msg.reply(event.author.mention, embed=self.parse_log_file(
                        name, z.open(name)))
                    
                z.close()
            else:
                event.msg.reply(event.author.mention, embed=MessageEmbed(
                    title=s_file_name, color=COLOR_RED, description="**Unsupported file type, not analyzed**."))
                continue
예제 #2
0
 def on_lyrics_command(self, event, content):
     """
     Api Return lyrics for a song.
     """
     self.pre_check("google_key", "google_cse_engine_ID")
     first_message = api_loop(
         event.channel.send_message,
         "Searching for lyrics...",
     )
     title, lyrics = get_lyrics(
         self.google_key,
         self.google_cse_engine_ID,
     ).get_lyrics(quote_plus(content))
     if len(lyrics) > 46300:
         return first_message.edit("I doubt that's a song.")
     if not lyrics:
         return first_message.edit("No Lyrics found for ``{}``".format(
             sanitize(
                 content,
                 escape_codeblocks=True,
             )
         ))
     lyrics_embed = bot.generic_embed_values(
         title=title,
         footer_text="Requested by {}".format(event.author),
         footer_img=event.author.get_avatar_url(size=32),
         timestamp=event.msg.timestamp.isoformat(),
     )
     first_message.delete()
     responses = 1
     while lyrics and responses < 4:
         lyrics_embed.description = lyrics[:2048]
         lyrics = lyrics[2048:]
         if lyrics:
             tmp_to_shift = lyrics_embed.description.splitlines()[-1]
             lyrics = tmp_to_shift + lyrics
             lyrics_embed.description = lyrics_embed.description[
                 :-len(tmp_to_shift)
             ]
         api_loop(event.channel.send_message, embed=lyrics_embed)
         responses += 1
예제 #3
0
    def parse_log_file(self, file_name, file):
        """
        Parses a log file, and returns a Discord MessageEmbed describing it.
        """
        embed = MessageEmbed()
        embed.title = "**{}**\n".format(sanitize(file_name,
                                                 escape_codeblocks=True))
        embed.description = ''
        embed.color = COLOR_BLUE

        build_info = {}
        message_levels = {
            'w': [],
            '!': [],
        }
        seen = set()
        lines = 0

        for line in file:
            # Process up to 500,000 lines
            if lines > 500000:
                break

            # Decode the line if it needs it.
            try:
                line = line.decode('utf-8')
            except AttributeError:
                pass

            sanitized_line = sanitize(line, escape_codeblocks=True).replace('\r\n', '').replace('\r', '')

            if 'date' not in build_info:
                # Scan for build info
                res = re.search(
                    r'^i> ([0-9a-fA-f]{8}) Build: (.*) / ([0-9a-fA-F]{40}) on (.*)$', sanitized_line)
                if res:
                    build_info.update({
                        "branch": res.group(2),
                        "commit": res.group(3),
                        "date": res.group(4),
                    })

            # See if we can find a game ID.
            if 'title_id' not in build_info:
                res = re.search(r'^\s*Title ID: ([0-9a-fA-F]{8})$', sanitized_line)
                if res:
                    build_info.update({"title_id": res.group(1)})

            if len(sanitized_line) > 1 and (sanitized_line[0] in message_levels):
                if sanitized_line not in seen:
                    seen.add(sanitized_line)
                    message_levels[sanitized_line[0]].append(sanitized_line)
            
            lines += 1

        if 'date' not in build_info:
            embed.color = COLOR_RED
            embed.description = "\t**Invalid file**. Could not find build information - is this a Xenia logfile?"
            return embed

        # Setup the description
        if 'branch' in build_info and 'date' in build_info and 'commit' in build_info:
            embed.description = "Branch: {branch}\nDate: {date}\nCommit: {commit}\n".format(
                **build_info)
        if 'title_id' in build_info:
            embed.description += "Title ID: {title_id}".format(**build_info)

        # Errors
        if len(message_levels['!']) > 0:
            errors = "```\n"
            for line in message_levels['!']:
                if len(errors) + len(line) > 997:
                    errors += '...'
                    break

                errors += "{}\n".format(line)
            errors += "```\n"
            embed.add_field(name="Errors", value=errors)

        # Warnings
        if len(message_levels["w"]) > 0:
            warnings = "```\n"
            for line in message_levels['w']:
                if len(warnings) + len(line) > 997:
                    warnings += '...'
                    break

                warnings += "{}\n".format(line)
            warnings += "```\n"
            embed.add_field(name="Warnings", value=warnings)

        return embed
예제 #4
0
 def on_alias_set_command(self, event, alias):
     """
     Last.fm Used to add or remove a user alias in a guild.
     Users are limited to 5 alises in a guild.
     Alises are limited to 20 characters and cannot contain Discord's reserved special characters (e.g. '@', '#').
     """
     if event.channel.is_dm:
         api_loop(event.channel.send_message,
                  "Alias commands are guild specific.")
     else:
         if len(alias) > 20 or sanitize(alias) != alias:
             api_loop(
                 event.channel.send_message,
                 "Aliasas are limited to 20 characters and cannot contain Discord's reserved special characters.",
             )
         else:
             data = handle_sql(
                 db_session.query(aliases).filter(
                     aliases.user_id == event.author.id,
                     aliases.guild_id == event.guild.id,
                     aliases.alias.like(alias),
                 ).first)
             if data is None:
                 if (handle_sql(
                         db_session.query(aliases).filter_by(
                             user_id=event.author.id,
                             guild_id=event.guild.id,
                         ).count) < 5):
                     payload = aliases(
                         user_id=event.author.id,
                         guild_id=event.guild.id,
                         alias=alias,
                     )
                     handle_sql(db_session.add, payload)
                     handle_sql(db_session.flush)
                     api_loop(
                         event.channel.send_message,
                         "Added alias ``{}``.".format(alias),
                     )
                 else:
                     api_loop(
                         event.channel.send_message,
                         "You've reached the 5 alias limit for this guild.".
                         format(data.alias, ),
                     )
             else:
                 if data.user_id == event.author.id:
                     handle_sql(
                         db_session.query(aliases).filter_by(
                             user_id=event.author.id,
                             guild_id=event.guild.id,
                             alias=data.alias,
                         ).delete)
                     handle_sql(db_session.flush)
                     api_loop(
                         event.channel.send_message,
                         "Removed alias ``{}``.".format(data.alias),
                     )
                 else:
                     api_loop(
                         event.channel.send_message,
                         "Alias ``{}`` is already taken in this guild.".
                         format(data.alias),
                     )
예제 #5
0
    def on_spotify_command(self, event, type, search=""):
        """
        Api Search for an item on Spotify.
        If the first argument is in the list
            "track", "album", "artist" or "playlist",
            then the relevant search point will be used.
        Otherwise, it will assume the user wants to find a track.
        """
        self.pre_check("spotify_ID", "spotify_secret")
        auth = urlsafe_b64encode("{}:{}".format(
            self.spotify_ID,
            self.spotify_secret
        ).encode()).decode()

        spotify_auth = getattr(self, "spotify_auth", None)
        if spotify_auth is not None and ((time() - self.spotify_auth_time)
                >= self.spotify_auth_expire):
            get_auth = True
        else:
            get_auth = False
        if spotify_auth is None or get_auth:
            self.get_spotify_auth(auth)
        if type not in ("track", "album", "artist", "playlist"):
            search = "{} {}".format(type, search)
            type = "track"
        elif search == "":
            return api_loop(
                event.channel.send_message,
                "Missing search argument.",
            )
        r = get(
            "https://api.spotify.com/v1/search?q={}&type={}".format(
                quote_plus(search),
                type,
            ),
            headers={
                "Authorization": "Bearer {}".format(self.spotify_auth),
                "User-Agent": self.user_agent,
                "Content-Type": "application/json",
            },
        )
        if r.status_code == 200:
            if not r.json()[type+"s"]["items"]:
                return api_loop(
                    event.channel.send_message,
                    "{}: ``{}`` not found.".format(
                        type,
                        sanitize(search, escape_codeblocks=True)
                    )
                )
            url = r.json()[type+"s"]["items"][0]["external_urls"]["spotify"]
            reply = api_loop(event.channel.send_message, url)
            if (len(r.json()[type+"s"]["items"]) > 1 and
                    not event.channel.is_dm):
                bot.reactor.init_event(
                    message=reply,
                    timing=30,
                    data=r.json()[type+"s"]["items"],
                    index=0,
                    amount=1,
                    edit_message=self.spotify_react,
                )
                bot.reactor.add_reactors(
                    self,
                    reply,
                    generic_react,
                    event.author.id,
                    "\N{leftwards black arrow}",
                    "\N{Cross Mark}",
                    "\N{black rightwards arrow}",
                )
        else:
            log.warning(r.text)
            api_loop(
                event.channel.send_message,
                "Error code {} returned".format(r.status_code),
            )