Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- # Version 24
- # POSTED ONLINE: https://pastebin.com/g10jVftx
- # Windows: $ pip install windows-curses pyperclip
- #
- # Linux: $ sudo pacman -S python-pyperclip
- # $ sudo pacman -S wl-clipboard <-- Pyperclip has issues with KDE + Wayland. Installing this seems to get it to work.
- #
- # NOTE: For Linux you also need to install a copy/paste mechanism if you don't have one.
- # KDE has /bin/klipper installed by default. For other window managers try [$ sudo pacman -S xclip] or [$ sudo pacman -S xsel].
- #
- # NOTE: If you get error "Cannot find '.setClipboardContents' in object /klipper at org.kde.klipper",
- # its because klipper isn't running on startup. Try Start > klipper to see if the problem goes away.
- #
- # NOTE: To display klipper in the taskbar, right click the ^ > Configure System Tray... > Entries > Clipboard: [Show when relevant].
- import curses
- import pyperclip
- # -----
- def init_colors():
- # Enable color functionality.
- curses.start_color()
- # Define all standard colors with black background.
- curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
- curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
- curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)
- curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK)
- curses.init_pair(5, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
- curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK)
- curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK)
- # Create variables for easier use.
- global COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE
- COLOR_RED = curses.color_pair(1)
- COLOR_GREEN = curses.color_pair(2)
- COLOR_YELLOW = curses.color_pair(3)
- COLOR_BLUE = curses.color_pair(4)
- COLOR_MAGENTA = curses.color_pair(5)
- COLOR_CYAN = curses.color_pair(6)
- COLOR_WHITE = curses.color_pair(7)
- # -----
- row = 0
- col = 0
- def writeline(stdscr, text='', color=None):
- global row, col
- lines = text.split('\n')
- for line in lines:
- try:
- stdscr.addstr(row, col, line, color or COLOR_WHITE)
- except curses.error: # Happens with off screen writing.
- pass
- row += 1
- col = 0
- def write(stdscr, text='', color=None):
- global row, col
- lines = text.split('\n')
- for i, line in enumerate(lines):
- try:
- stdscr.addstr(row, col, line, color or COLOR_WHITE)
- except curses.error: # Happens with off screen writing.
- pass
- if i < len(lines) - 1:
- row += 1
- col = 0
- else:
- col += len(line)
- def clear(stdscr):
- global row
- global col
- row = 0
- col = 0
- stdscr.clear()
- # -----
- def draw_menu(stdscr, menu_items, selected_index, title, highlight):
- # Clear screen.
- clear(stdscr)
- if title:
- # Draw title.
- writeline(stdscr, title)
- # Draw menu items.
- for i in range(len(menu_items)):
- menuItem = menu_items[i]
- if highlight in menuItem:
- left,right = menuItem.split(highlight)
- write(stdscr, f' {"●" if i == selected_index else "○"} {left}', COLOR_GREEN if i == selected_index else COLOR_CYAN)
- write(stdscr, f'{highlight}', COLOR_RED)
- writeline(stdscr, f'{right}', COLOR_GREEN if i == selected_index else COLOR_CYAN)
- else:
- writeline(stdscr, f' {"●" if i == selected_index else "○"} {menuItem}', COLOR_RED)
- # Draw usage information.
- writeline(stdscr)
- writeline(stdscr, ' Make → Space', COLOR_YELLOW)
- writeline(stdscr, ' Copy → Enter', COLOR_YELLOW)
- writeline(stdscr, ' Rise → Up', COLOR_YELLOW)
- writeline(stdscr, ' Fall → Down', COLOR_YELLOW)
- writeline(stdscr, ' Smol → Left', COLOR_YELLOW)
- writeline(stdscr, ' Swol → Right', COLOR_YELLOW)
- writeline(stdscr, ' Zoom → Ctrl+/-', COLOR_YELLOW)
- writeline(stdscr, ' Quit → Esc', COLOR_YELLOW)
- # Redraw screen.
- stdscr.refresh()
- # -----
- import string
- # Character groups to be used in the password.
- CHARACTER_GROUPS = [
- string.ascii_lowercase,
- string.ascii_uppercase,
- string.digits,
- #string.punctuation,
- '!@#$%^&*()', # PayPal allowed punctuation characters.
- ]
- PASSWORD_CHARACTERS = ''.join(CHARACTER_GROUPS)
- # Remove unwanted characters.
- removal_table = str.maketrans('', '', r'|\\/`\'\"')
- PASSWORD_CHARACTERS = PASSWORD_CHARACTERS.translate(removal_table)
- # Remove timestamp divider characters so they can be uniquely added to the password around the timestamp.
- TIMESTAMP_DIVIDER_LEFT = '('
- TIMESTAMP_DIVIDER_RIGHT = ')'
- PASSWORD_CHARACTERS = PASSWORD_CHARACTERS.replace(TIMESTAMP_DIVIDER_LEFT, '').replace(TIMESTAMP_DIVIDER_RIGHT, '')
- from datetime import datetime
- import secrets
- def generate_timestamped_passwords(password_length, count=5):
- dt = datetime.now()
- short_time_stamp = f"{TIMESTAMP_DIVIDER_LEFT}{dt.strftime('%y%m%d')}{TIMESTAMP_DIVIDER_RIGHT}"
- short_time_stamp_len = len(short_time_stamp)
- passwords = []
- retries = 100000
- for _ in range(count):
- for attempt in range(retries):
- # SOURCE: https://docs.python.org/3/library/secrets.html
- password = ''.join(secrets.choice(PASSWORD_CHARACTERS) for _ in range(password_length - short_time_stamp_len))
- # If the password has repeating characters.
- # NOTE: The non-timestamped password is tested because the timestamp might fail the unique-segments test depending on what day it is.
- if not has_unique_segments(password):
- continue
- # If the password uses characters from all categories.
- if has_representative_characters(password):
- timestamped_password = shuffle(password, short_time_stamp)
- passwords.append(timestamped_password)
- break
- else:
- passwords.append(f'ERROR: Failed to generate a valid password after {retries} attempts.')
- large_time_stamp = f' Created: {dt.strftime("%Y-%m-%d %H:%M:%S")}\n'
- large_time_stamp += f' Length: {password_length}\n'
- return passwords, large_time_stamp, short_time_stamp
- # -----
- def has_representative_characters(s):
- min_characters_per_group = min(4, len(s) // len(CHARACTER_GROUPS))
- for group in CHARACTER_GROUPS:
- if sum(char in group for char in s) < min_characters_per_group:
- return False
- return True
- def has_unique_segments(s):
- segment_length = 2 if len(s) > 80 else 10
- for i in range(0, len(s), segment_length):
- segment = s[i:i + segment_length]
- if len(set(segment)) != len(segment):
- return False
- return True
- # -----
- # Insert a word into a sentence randomly.
- def shuffle(sentence, word):
- max_word_index = len(sentence)
- # Generate a random integer in the range [0, n).
- random_index = secrets.randbelow(max_word_index + 1)
- # Insert the word into the sentence.
- return sentence[:random_index] + word + sentence[random_index:]
- # -----
- # def wait_for_enter_keypress(stdscr):
- # writeline(stdscr, ' Press ENTER to continue . . . ')
- # stdscr.refresh()
- #
- # while True:
- # # Get keyboard input.
- # key = stdscr.getch()
- #
- # # If Enter key is pressed.
- # if key in (curses.KEY_ENTER, ord('\r'), ord('\n')):
- # break
- def wait_for_any_keypress(stdscr):
- writeline(stdscr, ' Press any key to continue . . . ')
- stdscr.refresh()
- # Get keyboard input.
- key = stdscr.getch()
- # -----
- MIN_PASSWORD_LENGTH = 12 # 6 numbers (timestamp) + 2 uppercase letters + 2 lowercase letters + 2 punctuation (timestamp-dividers).
- def main(stdscr):
- init_colors()
- password_length = 20
- menu_items, large_time_stamp, short_time_stamp = generate_timestamped_passwords(password_length)
- selected_index = 0
- draw_menu(stdscr, menu_items, selected_index, large_time_stamp, short_time_stamp) # Initial drawing.
- while True:
- # Get keyboard input.
- key = stdscr.getch()
- # If Escape key is pressed.
- if key == ord('\x1b'):
- break
- # If Space key is pressed.
- elif key == ord(' '):
- menu_items, large_time_stamp, short_time_stamp = generate_timestamped_passwords(password_length)
- # If Up key is pressed.
- elif key == curses.KEY_UP:
- # Loop around backwards.
- selected_index = (selected_index - 1 + len(menu_items)) % len(menu_items)
- # If Down key is pressed.
- elif key == curses.KEY_DOWN:
- # Loop around forwards.
- selected_index = (selected_index + 1) % len(menu_items)
- # If Left key is pressed.
- elif key == curses.KEY_LEFT:
- # Reduce length of passwords.
- password_length = max(MIN_PASSWORD_LENGTH, password_length - 1)
- menu_items, large_time_stamp, short_time_stamp = generate_timestamped_passwords(password_length)
- # If Right key is pressed.
- elif key == curses.KEY_RIGHT:
- # Increase length of passwords.
- password_length += 1
- menu_items, large_time_stamp, short_time_stamp = generate_timestamped_passwords(password_length)
- # If Enter key is pressed.
- # NOTE: On Windows the enter key appears to be \r, and on Linux its \n.
- elif key in (curses.KEY_ENTER, ord('\r'), ord('\n')):
- pyperclip.copy(menu_items[selected_index])
- writeline(stdscr, f"\n Copied {menu_items[selected_index]} to clipboard.\n")
- # wait_for_enter_keypress(stdscr)
- wait_for_any_keypress(stdscr) # Works better.
- # Else an unexpected key is pressed.
- # else:
- # try:
- # writeline(stdscr, "\n The pressed key '{}' {} is not associated with a menu function.\n".format(chr(key), key))
- # except ValueError:
- # writeline(stdscr, "\n The pressed key {} is not associated with a menu function.\n".format(key))
- # wait_for_any_keypress(stdscr)
- draw_menu(stdscr, menu_items, selected_index, large_time_stamp, short_time_stamp)
- if __name__ == '__main__':
- curses.wrapper(main)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement