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
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
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
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), )
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), )