<commit_before>#!/usr/bin/env python

# Import system libraries
import base64
import json
import os

# Import PyPi libraries
from appdirs import AppDirs
import click
import pyotp
import pyperclip

# Set app information
appname = 'iotp'
appauthor = '<NAME>'
appversion = '0.1.0'

# Setup appdirs
dirs = AppDirs(appname, appauthor)
keyFile = os.path.join(dirs.user_data_dir, 'keys.json')


def setup_keys():
    """
    Check for data file and directory and create if is doesn't exist

    Returns a dictionary of saved keys
    """
    if not os.path.isdir(dirs.user_data_dir):
        os.makedirs(dirs.user_data_dir)

    try:
        with open(keyFile, 'r') as f:
            keys = json.load(f)
    except ValueError:
        keys = {}
    except IOError:
        open(keyFile, 'w').close()
        keys = {}

    return keys


def save_keys(keys):
    """Takes a dictionary of keys and then saves them to the keys file"""
    with open(keyFile, 'w') as f:
        json.dump(keys, f, indent=True)


def get_totp(key):
    """
    Accepts a BASE32 encoded key and returns the TOTP if it is valid and None
    if it is invalid
    """
    try:
        totp = pyotp.TOTP(key)
        return totp.now()
    except TypeError:
        return None


@click.group()
@click.version_option(appversion)
def cli():
    """
    iotp is a Google Authenicator/RFC 6238 compatible application for
    generating Time based One Time Passwords
    """
    pass


@cli.command()
@click.argument('service', required=False)
@click.option('--copy', '-c', is_flag=True, help="Copy TOTP to the clipboard")
def get(copy, service=None):
    """
    Gets TOTP codes for service specified. If no service is specified it
    prints codes for all services
    """
    keys = setup_keys()
    if not service:
        for service in keys:
            value = keys[service]
            totp = get_totp(value)
            if totp:
                click.echo('{}: {}'.format(service, totp))

    elif service in keys:
        totp = get_totp(keys[service])
        if totp:
            click.echo('{}: {}'.format(service, totp))
            if copy:
                pyperclip.copy(totp)
        else:
            click.echo('Key is invalid, please reset key')
    else:
        click.echo('{} does not exist'.format(service))


@cli.command(help="Sets the service key")
@click.argument('service')
@click.argument('key')
def set(service, key):
    """Accepts a service and a key and saves it out to the keyFile."""<commit_msg>Add a time remaining progress bar<commit_after>#!/usr/bin/env python

# Import system libraries
import base64
import json
import os
import time
from datetime import datetime

# Import PyPi libraries
from appdirs import AppDirs
import click
import pyotp
import pyperclip

# Set app information
appname = 'iotp'
appauthor = '<NAME>'
appversion = '0.1.0'

# Setup appdirs
dirs = AppDirs(appname, appauthor)
keyFile = os.path.join(dirs.user_data_dir, 'keys.json')


def setup_keys():
    """
    Check for data file and directory and create if is doesn't exist

    Returns a dictionary of saved keys
    """
    if not os.path.isdir(dirs.user_data_dir):
        os.makedirs(dirs.user_data_dir)

    try:
        with open(keyFile, 'r') as f:
            keys = json.load(f)
    except ValueError:
        keys = {}
    except IOError:
        open(keyFile, 'w').close()
        keys = {}

    return keys


def save_keys(keys):
    """Takes a dictionary of keys and then saves them to the keys file"""
    with open(keyFile, 'w') as f:
        json.dump(keys, f, indent=True)


def get_totp(key, keyTime):
    """
    Accepts a BASE32 encoded key and returns the TOTP if it is valid and None
    if it is invalid
    """
    try:
        totp = pyotp.TOTP(key)
        return totp.at(keyTime)
    except TypeError:
        return None


def display_remaining(keyTime):
    """
    Displays a progress bar with the time remaining till the next 30 second
    """
    r = range(30, 0, -1)

    # Seconds remaining in block
    if keyTime.second > 30:
        secRemain = 30 - (keyTime.second - 30)
    else:
        secRemain = 30 - keyTime.second

    with click.progressbar(r,
                           label='Seconds Remaining',
                           width=30,
                           show_percent=False,
                           show_eta=False,
                           item_show_func=str) as bar:
        for i in bar:
            # Only wait a second if the countdown is less than the Seconds
            # remaining
            if i <= secRemain:
                time.sleep(1)


@click.group()
@click.version_option(appversion)
def cli():
    """
    iotp is a Google Authenicator/RFC 6238 compatible application for
    generating Time based One Time Passwords
    """
    pass


@cli.command()
@click.argument('service', required=False)
@click.option('--copy', '-c', is_flag=True, help="Copy TOTP to the clipboard")
def get(copy, service=None):
    """
    Gets TOTP codes for service specified. If no service is specified it
    prints codes for all services
    """
    keyTime = datetime.now()
    keys = setup_keys()
    if not service:
        for service in keys:
            value = keys[service]
            totp = get_totp(value, keyTime)
            if totp:
                click.echo('{}: {}'.format(service, totp))
                display_remaining(keyTime)

    elif service in keys:
        totp = get_totp(keys[service], keyTime)
        if totp:
            click.echo('{}: {}'.format(service, totp))
            if copy:
                pyperclip.copy(totp)
        else:
            click.echo('Key is invalid, please reset key')
        display_remaining(keyTime)
    else:
        click.echo('{} does not exist'.format(service))


@cli.command(help="Sets the service key")
@click.argument('service')
@click.argument('key')
def set(service, key):
    """Accepts a service and a key and saves it out to the keyFile."""
{'fraction_non_alphanumeric': 0.10647603027754415, 'fraction_numerical': 0.00656013456686291, 'mean_word_length': 2.365025466893039, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '427268', 'n_tokens_mistral': 1867, 'n_tokens_neox': 1813, 'n_words': 588}