async def sarcasm(message): """gEt a LiTtLe WeIrD""" text = list(utils.raw(message.message)) if not text: text = list(utils.raw((await message.get_reply_message()).message)) text[1::2], text[::2] = map(str.lower, text[1::2]), map(str.upper, text[::2]) await message.edit("".join(text))
def flatten_log(log_path, preserve_renditions=True, show_esc=False): """ Given a log file at *log_path*, return a str of log lines contained within. If *preserve_renditions* is True, CSI escape sequences for renditions will be preserved as-is (e.g. font color, background, etc). This is to make the output appear as close to how it was originally displayed as possible. Besides that, it looks really nice =) If *show_esc* is True, escape sequences and control characters will be visible in the output. Trailing whitespace and escape sequences will not be removed. NOTE: Converts our standard recording-based log format into something that can be used with grep and similar search/filter tools. """ import gzip lines = gzip.open(log_path).read().decode('utf-8') out = "" for frame in lines.split(SEPARATOR)[1:]: # Skip the first frame (metadata) try: frame_time = float(frame[:13]) # First 13 chars is the timestamp # Convert to datetime object frame_time = datetime.fromtimestamp(frame_time/1000) if '\n' in frame[14:]: # Skips the colon frame_lines = frame[14:].splitlines() for i, fl in enumerate(frame_lines): if len(fl): # NOTE: Have to put a rendition reset (\x1b[m) at the # start of each line in case the previous line didn't # reset it on its own. if show_esc: out += u"%s %s\n" % ( # Standard Unix log format frame_time.strftime(u'\x1b[m%b %m %H:%M:%S'), raw(fl)) else: out += u"%s %s\n" % ( # Standard Unix log format frame_time.strftime(u'\x1b[m%b %m %H:%M:%S'), escape_escape_seq(fl, rstrip=True).rstrip() ) elif i:# Don't need this for the first empty line in a frame out += frame_time.strftime(u'\x1b[m%b %m %H:%M:%S \n') elif show_esc: if len(out) and out[-1] == u'\n': out = u"%s%s\n" % (out[:-1], raw(frame[14:])) else: escaped_frame = escape_escape_seq(frame[14:], rstrip=True).rstrip() if len(out) and out[-1] == u'\n': out = u"%s%s\n" % (out[:-1], escaped_frame) elif escaped_frame: # This is pretty much always going to be the first line out += u"%s %s\n" % ( # Standard Unix log format frame_time.strftime(u'\x1b[m%b %m %H:%M:%S'), escaped_frame.rstrip() ) except ValueError as e: pass # End of file. No biggie. return out
def flatten_log(log_path, preserve_renditions=True, show_esc=False): """ Given a log file at *log_path*, return a str of log lines contained within. If *preserve_renditions* is True, CSI escape sequences for renditions will be preserved as-is (e.g. font color, background, etc). This is to make the output appear as close to how it was originally displayed as possible. Besides that, it looks really nice =) If *show_esc* is True, escape sequences and control characters will be visible in the output. Trailing whitespace and escape sequences will not be removed. NOTE: Converts our standard recording-based log format into something that can be used with grep and similar search/filter tools. """ import gzip lines = gzip.open(log_path).read().decode('utf-8') out = "" for frame in lines.split(SEPARATOR)[1:]: # Skip the first frame (metadata) try: frame_time = float(frame[:13]) # First 13 chars is the timestamp # Convert to datetime object frame_time = datetime.fromtimestamp(frame_time / 1000) if '\n' in frame[14:]: # Skips the colon frame_lines = frame[14:].splitlines() for i, fl in enumerate(frame_lines): if len(fl): # NOTE: Have to put a rendition reset (\x1b[m) at the # start of each line in case the previous line didn't # reset it on its own. if show_esc: out += u"%s %s\n" % ( # Standard Unix log format frame_time.strftime(u'\x1b[m%b %m %H:%M:%S'), raw(fl)) else: out += u"%s %s\n" % ( # Standard Unix log format frame_time.strftime(u'\x1b[m%b %m %H:%M:%S'), escape_escape_seq(fl, rstrip=True).rstrip()) elif i: # Don't need this for the first empty line in a frame out += frame_time.strftime(u'\x1b[m%b %m %H:%M:%S \n') elif show_esc: if len(out) and out[-1] == u'\n': out = u"%s%s\n" % (out[:-1], raw(frame[14:])) else: escaped_frame = escape_escape_seq(frame[14:], rstrip=True).rstrip() if len(out) and out[-1] == u'\n': out = u"%s%s\n" % (out[:-1], escaped_frame) elif escaped_frame: # This is pretty much always going to be the first line out += u"%s %s\n" % ( # Standard Unix log format frame_time.strftime(u'\x1b[m%b %m %H:%M:%S'), escaped_frame.rstrip()) except ValueError as e: pass # End of file. No biggie. return out
def playback_log(log_path, file_like, show_esc=False): """ Plays back the log file at *log_path* by way of timely output to *file_like* which is expected to be any file-like object with write() and flush() methods. If *show_esc* is True, escape sequences and control characters will be escaped so they can be seen in the output. """ log = gzip.open(log_path).read() prev_frame_time = None # Skip first frame for i, frame in enumerate(log.split(SEPARATOR.encode('UTF-8'))[1:]): try: frame_time = float(frame[:13]) # First 13 chars is the timestamp frame = frame[14:] # Skips the colon if i == 0: # Write it out immediately file_like.write(frame.decode('UTF-8')) prev_frame_time = frame_time else: # Wait until the time between the previous frame and now has passed wait_time = (frame_time - prev_frame_time) / 1000.0 sleep(wait_time) # frame times are in milliseconds prev_frame_time = frame_time if show_esc: frame = raw(frame) file_like.write(frame.decode('UTF-8')) file_like.flush() except ValueError: # End of file. No biggie. return
def escape_escape_seq(text, preserve_renditions=True, rstrip=True): """ Escapes escape sequences so they don't muck with the terminal viewing *text* Also replaces special characters with unicode symbol equivalents (e.g. so you can see what they are without having them do anything to your running shell) If *preserve_renditions* is True, CSI escape sequences for renditions will be preserved as-is (e.g. font color, background, etc). If *rstrip* is true, trailing escape sequences and whitespace will be removed. """ esc_sequence = re.compile( r'\x1b(.*\x1b\\|[ABCDEFGHIJKLMNOQRSTUVWXYZa-z0-9=]|[()# %*+].)') csi_sequence = re.compile(r'\x1B\[([?A-Za-z0-9;@:\!]*)([A-Za-z@_])') #esc_rstrip = re.compile('[ \t]+\x1b.+$') out = u"" esc_buffer = u"" # If this seems confusing it is because text parsing is a black art! ARRR! for char in text: if not esc_buffer: if char == u'\x1b': # Start of an ESC sequence esc_buffer = char # TODO: Determine if we should bring this back: #elif ord(char) in replacement_map: #out += replacement_map[ord(char)] else: # Vanilla char. Booooring. out += raw(char) else: # Something interesting is going on esc_buffer += char if char == u'\x07' or esc_buffer.endswith(u'\x1b\\'): # Likely title esc_buffer = u'' # Nobody wants to see your naked ESC sequence continue elif esc_buffer.endswith('\x1b\\'): esc_buffer = u'' # Ignore continue # Nobody wants to see plain ESC sequences in the buf... match_obj = esc_sequence.match(esc_buffer) if match_obj: seq_type = match_obj.group(1) esc_buffer = u'' # Just when you thought you've ESC'd... continue # CSI ESC sequences... These are worth a second look match_obj = csi_sequence.match(esc_buffer) if match_obj: csi_type = match_obj.group(2) if csi_type == 'm' and preserve_renditions: # mmmmmm! out += esc_buffer # Ooh, naked viewing of pretty things! elif csi_type == 'C': # Move cursor right (we want to do this) # Will be something like this: \x1b[208C spaces = int(match_obj.group(1)) out += u' ' * spaces # Add an equivalent amount of spaces esc_buffer = u'' # Make room for more! continue if rstrip: # Remove trailing whitespace + trailing ESC sequences return out.rstrip() else: # All these trailers better make for a good movie return out
async def shuffle(message): """hsulfses tufsf""" a = list(utils.raw(message.message)) if not a: a = list((await message.get_reply_message()).message) random.shuffle(a) await message.edit("".join(a))
async def terminal(event): """Execute stuff in your os terminal""" command = utils.raw(event.message) await event.edit(f"**Running command:**\n`{command}`") result = subprocess.getoutput(command) await event.edit( f"**Running command:**\n`{command}`\n**Result:**\n`{result}`")
def playback_log(log_path, file_like, show_esc=False): """ Plays back the log file at *log_path* by way of timely output to *file_like* which is expected to be any file-like object with write() and flush() methods. If *show_esc* is True, escape sequences and control characters will be escaped so they can be seen in the output. """ log = gzip.open(log_path).read().decode('utf-8') prev_frame_time = None for i, frame in enumerate(log.split(SEPARATOR)[1:]): # Skip first frame try: frame_time = float(frame[:13]) # First 13 chars is the timestamp frame = frame[14:] # Skips the colon if i == 0: # Write it out immediately file_like.write(frame) prev_frame_time = frame_time else: # Wait until the time between the previous frame and now has passed wait_time = (frame_time - prev_frame_time)/1000.0 sleep(wait_time) # frame times are in milliseconds prev_frame_time = frame_time if show_esc: frame = raw(frame) file_like.write(frame) file_like.flush() except ValueError: # End of file. No biggie. return
def escape_escape_seq(text, preserve_renditions=True, rstrip=True): """ Escapes escape sequences so they don't muck with the terminal viewing *text* Also replaces special characters with unicode symbol equivalents (e.g. so you can see what they are without having them do anything to your running shell) If *preserve_renditions* is True, CSI escape sequences for renditions will be preserved as-is (e.g. font color, background, etc). If *rstrip* is true, trailing escape sequences and whitespace will be removed. """ esc_sequence = re.compile( r'\x1b(.*\x1b\\|[ABCDEFGHIJKLMNOQRSTUVWXYZa-z0-9=]|[()# %*+].)') csi_sequence = re.compile(r'\x1B\[([?A-Za-z0-9;@:\!]*)([A-Za-z@_])') #esc_rstrip = re.compile('[ \t]+\x1b.+$') out = u"" esc_buffer = u"" # If this seems confusing it is because text parsing is a black art! ARRR! for char in text: if not esc_buffer: if char == u'\x1b': # Start of an ESC sequence esc_buffer = char # TODO: Determine if we should bring this back: #elif ord(char) in replacement_map: #out += replacement_map[ord(char)] else: # Vanilla char. Booooring. out += raw(char) else: # Something interesting is going on esc_buffer += char if char == u'\x07' or esc_buffer.endswith( u'\x1b\\'): # Likely title esc_buffer = u'' # Nobody wants to see your naked ESC sequence continue elif esc_buffer.endswith('\x1b\\'): esc_buffer = u'' # Ignore continue # Nobody wants to see plain ESC sequences in the buf... match_obj = esc_sequence.match(esc_buffer) if match_obj: seq_type = match_obj.group(1) esc_buffer = u'' # Just when you thought you've ESC'd... continue # CSI ESC sequences... These are worth a second look match_obj = csi_sequence.match(esc_buffer) if match_obj: csi_type = match_obj.group(2) if csi_type == 'm' and preserve_renditions: # mmmmmm! out += esc_buffer # Ooh, naked viewing of pretty things! esc_buffer = u'' # Make room for more! continue if rstrip: # Remove trailing whitespace + trailing ESC sequences #return esc_rstrip.sub('', out).rstrip() return out.rstrip() else: # All these trailers better make for a good movie return out
async def deactivate(event): mod = utils.raw(event.message) if not mod: await event.edit("__Provide a module to deactivate!__") return try: md = modules[mod] for i in md: __main__.commands.pop(i) await __main__.db.set("Modules", mod, False) await event.edit("**Module deactivated!**") except KeyError: await event.edit("__No such module__")
async def activate(event): mod = utils.raw(event.message) if not mod: await event.edit("__Provide a module to activate!__") return try: __main__.commands.update(modules[mod]) await __main__.db.set("Modules", mod, True) await event.edit("**Module activated! Try using `.help`**") except KeyError: text = "**No such module. Available modules:**\n" for i in modules.keys(): text += f"`{i}`\n" await event.edit(text)
def playback_log(log_path, file_like, show_esc=False): """ Plays back the log file at *log_path* by way of timely output to *file_like* which is expected to be any file-like object with write() and flush() methods. If *show_esc* is True, escape sequences and control characters will be escaped so they can be seen in the output. There will also be no delay between the output of frames (under the assumption that if you want to see the raw log you want it to output all at once so you can pipe it into some other app). """ prev_frame_time = None try: for count, frame in enumerate(get_frames(log_path)): frame_time = float(frame[:13]) # First 13 chars is the timestamp frame = frame[14:] # [14:] Skips the timestamp and the colon if count == 0: # Write it out immediately if show_esc: frame = raw(frame) file_like.write(frame) prev_frame_time = frame_time else: if show_esc: frame = raw(frame) else: # Wait until the time between the previous frame and now # has passed wait_time = (frame_time - prev_frame_time)/1000.0 sleep(wait_time) # frame times are in milliseconds file_like.write(frame) prev_frame_time = frame_time file_like.flush() except IOError: # Something wrong with the file return
def playback_log(log_path, file_like, show_esc=False): """ Plays back the log file at *log_path* by way of timely output to *file_like* which is expected to be any file-like object with write() and flush() methods. If *show_esc* is True, escape sequences and control characters will be escaped so they can be seen in the output. There will also be no delay between the output of frames (under the assumption that if you want to see the raw log you want it to output all at once so you can pipe it into some other app). """ prev_frame_time = None try: for count, frame in enumerate(get_frames(log_path)): frame_time = float(frame[:13]) # First 13 chars is the timestamp frame = frame[14:] # [14:] Skips the timestamp and the colon if count == 0: # Write it out immediately if show_esc: frame = raw(frame) file_like.write(frame) prev_frame_time = frame_time else: if show_esc: frame = raw(frame) else: # Wait until the time between the previous frame and now # has passed wait_time = (frame_time - prev_frame_time) / 1000.0 sleep(wait_time) # frame times are in milliseconds file_like.write(frame) prev_frame_time = frame_time file_like.flush() except IOError: # Something wrong with the file return
async def cowsay(message): """Корова говорит""" text = utils.raw(message.message) if not text: await message.edit('Нет текста после команды :c') return else: cowsay = ("`" f"< {text} >\n" "\n" " \ ^__^\n" " \ (oo)\_______\n" " (__)\ )\/\n" " ||----w||\n" " || ||`") await message.edit(cowsay)
def _request_term_info(request, until, out_stream=None, in_stream=None, timeout=0.5): _out = out_stream or sys.stdout _in = in_stream or sys.stdin with raw(_in): with nonblocking(_in): _out.write(request) _out.flush() resp = c = '' start = time() while not resp.endswith(until): c = _in.read(1) if c: resp += c elif (time() - start) > timeout: raise Exception("Request timed out") return resp
async def imps(message): """Используй .imps <@ или реплай>. Может быть он импостер?""" reply = await message.get_reply_message() args = utils.raw(message.message) if not args and not reply: user = await message.client.get_me() if reply: user = await utils.get_user(await message.get_reply_message()) if args: user = await message.client.get_entity(args) imps = ['wasn`t the impostor', 'was the impostor'] imp = ("`. 。 • ゚ . . 。\n" " . . . . 。 。 .\n" " . 。 ඞ 。 . . • .\n" f"• {user.first_name} {choice(imps)} 。 .\n" f" 。 {randint(1, 5)} impostor(s) remains. . .\n" ", . . . • • 。.\n" "。 • . ゚ • ゚ . . .`") await message.edit(imp)
async def vjuh(message): """Вжух!""" text = utils.raw(message.message) if not text: await message.edit('Нет текста после команды :c') return else: vjuh = ("`.∧_∧\n" "( ・ω・。)つ━☆・*。\n" "⊂ ノ ・゜ .\n" "しーJ °。 *´¨)\n" " .· ´¸.·*´¨) ¸.·*¨)\n" " (¸.·´ (¸.·'* ☆\n\n" "Вжух и ты `" + f"`{text}`") await message.edit(vjuh) if text == "podpiska": await message.edit("<`.∧_∧\n" "( ・ω・。)つ━☆・*。\n" "⊂ ノ ・゜ .\n" "しーJ °。 *´¨)\n" " .· ´¸.·*´¨) ¸.·*¨)\n" " (¸.·´ (¸.·'* ☆\n\n" "Вжух и ты подпишешься ->` @Popugasha_Arkasha")
async def shout(message): """SHOUT!""" await message.edit(utils.raw(message.message).upper())
async def reverse(message): """Reverses ffuts""" if not utils.raw(message.message): await message.edit((await message.get_reply_message()).message[::-1]) await message.edit(utils.raw(message.message)[::-1])
def flatten_log(log_path, preserve_renditions=True, show_esc=False): """ Given a log file at *log_path*, return a str of log lines contained within. If *preserve_renditions* is True, CSI escape sequences for renditions will be preserved as-is (e.g. font color, background, etc). This is to make the output appear as close to how it was originally displayed as possible. Besides that, it looks really nice =) If *show_esc* is True, escape sequences and control characters will be visible in the output. Trailing whitespace and escape sequences will not be removed. NOTE: Converts our standard recording-based log format into something that can be used with grep and similar search/filter tools. """ import gzip lines = gzip.open(log_path).read() out = "" out_line = "" cr = False # We skip the first frame, [1:] because it holds the recording metadata for frame in lines.split(SEPARATOR.encode('UTF-8'))[1:]: frame = frame.decode('UTF-8', 'ignore') try: frame_time = float(frame[:13]) # First 13 chars is the timestamp # Convert to datetime object frame_time = datetime.fromtimestamp(frame_time / 1000) if show_esc: frame_time = frame_time.strftime(u'\x1b[0m%b %m %H:%M:%S') else: # Renditions preserved == I want pretty. Make the date bold: frame_time = frame_time.strftime( u'\x1b[0;1m%b %m %H:%M:%S\x1b[m') for char in frame[14:]: if '\x1b[H\x1b[2J' in out_line: # Clear screen sequence # Handle the clear screen (usually ctrl-l) by outputting # a new log entry line to avoid confusion regarding what # happened at this time. out_line += "^L" # Clear screen is a ctrl-l or equivalent if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) out += frame_time + ' %s\n' % adjusted out_line = "" continue if char == u'\n': if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) out += frame_time + ' %s\n' % adjusted out_line = "" cr = False elif char in u'\r': # Carriage returns need special handling. Make a note of it cr = True else: # \r without \n means that characters were (likely) # overwritten. This usually happens when the user gets to # the end of the line (which would create a newline in the # terminal but not necessarily the log), erases their # current line (e.g. ctrl-u), or an escape sequence modified # the line in-place. To clearly indicate what happened we # insert a '^M' and start a new line so as to avoid # confusion over these events. if cr: out_line += "^M" out += frame_time + ' ' if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) out += adjusted out += '\n' out_line = "" out_line += char cr = False except ValueError as e: pass # End of file. No biggie. return out
def flatten_log(log_path, file_like, preserve_renditions=True, show_esc=False): """ Given a log file at *log_path*, write a string of log lines contained within to *file_like*. Where *file_like* is expected to be any file-like object with write() and flush() methods. If *preserve_renditions* is True, CSI escape sequences for renditions will be preserved as-is (e.g. font color, background, etc). This is to make the output appear as close to how it was originally displayed as possible. Besides that, it looks really nice =) If *show_esc* is True, escape sequences and control characters will be visible in the output. Trailing whitespace and escape sequences will not be removed. NOTE: Converts our standard recording-based log format into something that can be used with grep and similar search/filter tools. """ import gzip encoded_separator = SEPARATOR.encode('UTF-8') out_line = "" cr = False # We skip the first frame, [1:] because it holds the recording metadata for count, frame in enumerate(get_frames(log_path)): if count == 0: # Skip the first frame (it's just JSON-encoded metadata) continue frame = frame.decode('UTF-8', 'ignore') frame_time = float(frame[:13]) # First 13 chars is the timestamp # Convert to datetime object frame_time = datetime.fromtimestamp(frame_time/1000) if show_esc: frame_time = frame_time.strftime(u'\x1b[0m%b %m %H:%M:%S') else: # Renditions preserved == I want pretty. Make the date bold: frame_time = frame_time.strftime( u'\x1b[0;1m%b %m %H:%M:%S\x1b[m') for char in frame[14:]: if '\x1b[H\x1b[2J' in out_line: # Clear screen sequence # Handle the clear screen (usually ctrl-l) by outputting # a new log entry line to avoid confusion regarding what # happened at this time. out_line += "^L" # Clear screen is a ctrl-l or equivalent if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) file_like.write(frame_time + ' %s\n' % adjusted) out_line = "" continue if char == u'\n': if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) file_like.write(frame_time + ' %s\n' % adjusted) out_line = "" cr = False elif char == u'\r': # Carriage returns need special handling. Make a note of it cr = True else: # \r without \n means that characters were (likely) # overwritten. This usually happens when the user gets to # the end of the line (which would create a newline in the # terminal but not necessarily the log), erases their # current line (e.g. ctrl-u), or an escape sequence modified # the line in-place. To clearly indicate what happened we # insert a '^M' and start a new line so as to avoid # confusion over these events. if cr: out_line += "^M" file_like.write(frame_time + ' ') if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) file_like.write(adjusted + '\n') out_line = "" out_line += char cr = False file_like.flush()
def flatten_log(log_path, file_like, preserve_renditions=True, show_esc=False): """ Given a log file at *log_path*, write a string of log lines contained within to *file_like*. Where *file_like* is expected to be any file-like object with write() and flush() methods. If *preserve_renditions* is True, CSI escape sequences for renditions will be preserved as-is (e.g. font color, background, etc). This is to make the output appear as close to how it was originally displayed as possible. Besides that, it looks really nice =) If *show_esc* is True, escape sequences and control characters will be visible in the output. Trailing whitespace and escape sequences will not be removed. ..note:: Converts our standard recording-based log format into something that can be used with grep and similar search/filter tools. """ import gzip from terminal import Terminal, SPECIAL metadata = get_log_metadata(log_path) rows = metadata.get('rows', 24) cols = metadata.get('columns', None) if not cols: # Try the old metadata format which used 'cols': cols = metadata.get('cols', 80) term = Terminal(rows=rows, cols=cols, em_dimensions=0) encoded_separator = SEPARATOR.encode('UTF-8') out_line = u"" cr = False # We skip the first frame, [1:] because it holds the recording metadata for count, frame in enumerate(get_frames(log_path)): if count == 0: # Skip the first frame (it's just JSON-encoded metadata) continue # First 13 chars is the timestamp: frame_time = float(frame.decode('UTF-8', 'ignore')[:13]) # Convert to datetime object frame_time = datetime.fromtimestamp(frame_time/1000) if show_esc: frame_time = frame_time.strftime(u'\x1b[0m%b %m %H:%M:%S') else: # Renditions preserved == I want pretty. Make the date bold: frame_time = frame_time.strftime(u'\x1b[0;1m%b %m %H:%M:%S\x1b[m') if not show_esc: term.write(frame[14:]) if term.capture: # Capturing a file... Keep feeding it frames until complete continue elif term.captured_files: for line in term.screen: # Find all the characters that come before/after the capture for char in line: if ord(char) >= SPECIAL: adjusted = escape_escape_seq(out_line, rstrip=True) adjusted = frame_time + u' %s\n' % adjusted file_like.write(adjusted.encode('utf-8')) out_line = u"" if char in term.captured_files: captured_file = term.captured_files[char].file_obj captured_file.seek(0) file_like.write(captured_file.read()) file_like.write(b'\n') del captured_file term.clear_screen() term.close_captured_fds() # Instant cleanup else: out_line += char if not out_line: continue adjusted = frame_time + u' %s\n' % out_line.strip() file_like.write(adjusted.encode('utf-8')) out_line = u"" continue else: term.clear_screen() frame = frame.decode('UTF-8', 'ignore') for char in frame[14:]: if '\x1b[H\x1b[2J' in out_line: # Clear screen sequence # Handle the clear screen (usually ctrl-l) by outputting # a new log entry line to avoid confusion regarding what # happened at this time. out_line += u"^L" # Clear screen is a ctrl-l or equivalent if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) adjusted = frame_time + u' %s\n' % adjusted file_like.write(adjusted.encode('utf-8')) out_line = u"" continue if char == u'\n': if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) if not adjusted: out_line = u"" # Skip empty lines continue adjusted = frame_time + u' %s\n' % adjusted file_like.write(adjusted.encode('utf-8')) out_line = u"" cr = False elif char == u'\r': # Carriage returns need special handling. Make a note of it cr = True else: # \r without \n means that characters were (likely) # overwritten. This usually happens when the user gets to # the end of the line (which would create a newline in the # terminal but not necessarily the log), erases their # current line (e.g. ctrl-u), or an escape sequence modified # the line in-place. To clearly indicate what happened we # insert a '^M' and start a new line so as to avoid # confusion over these events. if cr: out_line += "^M" file_like.write((frame_time + u' ').encode('utf-8')) if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) file_like.write((adjusted + u'\n').encode('utf-8')) out_line = u"" out_line += char cr = False file_like.flush() del term
def flatten_log(log_path, file_like, preserve_renditions=True, show_esc=False): """ Given a log file at *log_path*, write a string of log lines contained within to *file_like*. Where *file_like* is expected to be any file-like object with write() and flush() methods. If *preserve_renditions* is True, CSI escape sequences for renditions will be preserved as-is (e.g. font color, background, etc). This is to make the output appear as close to how it was originally displayed as possible. Besides that, it looks really nice =) If *show_esc* is True, escape sequences and control characters will be visible in the output. Trailing whitespace and escape sequences will not be removed. NOTE: Converts our standard recording-based log format into something that can be used with grep and similar search/filter tools. """ import gzip from terminal import Terminal, SPECIAL term = Terminal(rows=100, cols=300, em_dimensions=0) encoded_separator = SEPARATOR.encode('UTF-8') out_line = u"" cr = False # We skip the first frame, [1:] because it holds the recording metadata for count, frame in enumerate(get_frames(log_path)): if count == 0: # Skip the first frame (it's just JSON-encoded metadata) continue # First 13 chars is the timestamp: frame_time = float(frame.decode('UTF-8', 'ignore')[:13]) # Convert to datetime object frame_time = datetime.fromtimestamp(frame_time / 1000) if show_esc: frame_time = frame_time.strftime(u'\x1b[0m%b %m %H:%M:%S') else: # Renditions preserved == I want pretty. Make the date bold: frame_time = frame_time.strftime(u'\x1b[0;1m%b %m %H:%M:%S\x1b[m') if not show_esc: term.write(frame[14:]) if term.capture: # Capturing a file... Keep feeding it frames until complete continue elif term.captured_files: for line in term.screen: # Find all the characters that come before/after the capture for char in line: if ord(char) >= SPECIAL: adjusted = escape_escape_seq(out_line, rstrip=True) adjusted = frame_time + u' %s\n' % adjusted file_like.write(adjusted.encode('utf-8')) out_line = u"" if char in term.captured_files: captured_file = term.captured_files[char].file_obj captured_file.seek(0) file_like.write(captured_file.read()) file_like.write(b'\n') del captured_file term.clear_screen() term.close_captured_fds() # Instant cleanup #term = Terminal(rows=100, cols=300, em_dimensions=0) else: out_line += char adjusted = frame_time + u' %s\n' % out_line.strip() file_like.write(adjusted.encode('utf-8')) out_line = u"" continue else: term.clear_screen() frame = frame.decode('UTF-8', 'ignore') for char in frame[14:]: if '\x1b[H\x1b[2J' in out_line: # Clear screen sequence # Handle the clear screen (usually ctrl-l) by outputting # a new log entry line to avoid confusion regarding what # happened at this time. out_line += u"^L" # Clear screen is a ctrl-l or equivalent if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) adjusted = frame_time + u' %s\n' % adjusted file_like.write(adjusted.encode('utf-8')) out_line = u"" continue if char == u'\n': if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) adjusted = frame_time + u' %s\n' % adjusted file_like.write(adjusted.encode('utf-8')) out_line = u"" cr = False elif char == u'\r': # Carriage returns need special handling. Make a note of it cr = True else: # \r without \n means that characters were (likely) # overwritten. This usually happens when the user gets to # the end of the line (which would create a newline in the # terminal but not necessarily the log), erases their # current line (e.g. ctrl-u), or an escape sequence modified # the line in-place. To clearly indicate what happened we # insert a '^M' and start a new line so as to avoid # confusion over these events. if cr: out_line += "^M" file_like.write((frame_time + u' ').encode('utf-8')) if show_esc: adjusted = raw(out_line) else: adjusted = escape_escape_seq(out_line, rstrip=True) file_like.write((adjusted + u'\n').encode('utf-8')) out_line = u"" out_line += char cr = False file_like.flush() del term