Beispiel #1
0
def cli(ctx, config, verbose):
    """Decentralised, minimalist microblogging service for hackers."""
    init_logging(debug=verbose)

    if ctx.invoked_subcommand == "quickstart":
        return  # Skip initializing config file

    try:
        if config:
            conf = Config.from_file(config)
        else:
            conf = Config.discover()
    except ValueError as e:
        if "Error in config file." in str(e):
            click.echo(
                "✗ Please correct the errors mentioned above an run twtxt again."
            )
        else:
            click.echo(
                "✗ Config file not found or not readable. You may want to run twtxt quickstart."
            )
        sys.exit()

    ctx.default_map = conf.build_default_map()
    ctx.obj = {'conf': conf}
Beispiel #2
0
def quickstart(ctx):
    """Quickstart wizard for setting up twtxt."""
    width = click.get_terminal_size()[0]
    width = width if width <= 79 else 79

    click.secho("twtxt - quickstart", fg="cyan")
    click.secho("==================", fg="cyan")
    click.echo()

    help = "This wizard will generate a basic configuration file for twtxt with all mandatory options set. " \
           "Have a look at the README.rst to get information about the other available options and their meaning."
    click.echo(textwrap.fill(help, width))

    click.echo()
    nick = click.prompt("➤ Please enter your desired nick", default=os.environ.get("USER", ""))
    twtfile = click.prompt("➤ Please enter the desired location for your twtxt file", "~/twtxt.txt", type=click.Path())

    click.echo()
    add_news = click.confirm("➤ Do you want to follow the twtxt news feed?", default=True)

    conf = Config(None)
    conf.create_config(nick, twtfile, add_news)
    open(os.path.expanduser(twtfile), 'a').close()

    click.echo()
    click.echo("✓ Created config file at '{}'.".format(click.format_filename(conf.config_file)))
Beispiel #3
0
def quickstart(ctx):
    """Quickstart wizard for setting up twtxt."""
    width = click.get_terminal_size()[0]
    width = width if width <= 79 else 79

    click.secho("twtxt - quickstart", fg="cyan")
    click.secho("==================", fg="cyan")
    click.echo()

    help = "This wizard will generate a basic configuration file for twtxt with all mandatory options set. " \
           "Have a look at the README.rst to get information about the other available options and their meaning."
    click.echo(textwrap.fill(help, width))

    click.echo()
    nick = click.prompt("➤ Please enter your desired nick",
                        default=os.environ.get("USER", ""))
    twtfile = click.prompt(
        "➤ Please enter the desired location for your twtxt file",
        "~/twtxt.txt",
        type=click.Path())

    click.echo()
    add_news = click.confirm("➤ Do you want to follow the twtxt news feed?",
                             default=True)

    conf = Config(None)
    conf.create_config(nick, twtfile, add_news)
    open(os.path.expanduser(twtfile), 'a').close()

    click.echo()
    click.echo("✓ Created config file at '{}'.".format(
        click.format_filename(conf.config_file)))
Beispiel #4
0
def test_build_default_map():
    empty_cfg = configparser.ConfigParser()
    empty_conf = Config("foobar", empty_cfg)

    default_map = {
        "following": {
            "check": empty_conf.check_following,
            "timeout": empty_conf.timeout,
            "porcelain": empty_conf.porcelain,
        },
        "tweet": {
            "twtfile": empty_conf.twtfile,
        },
        "timeline": {
            "pager": empty_conf.use_pager,
            "cache": empty_conf.use_cache,
            "limit": empty_conf.limit_timeline,
            "timeout": empty_conf.timeout,
            "sorting": empty_conf.sorting,
            "porcelain": empty_conf.porcelain,
            "twtfile": empty_conf.twtfile,
        },
        "view": {
            "pager": empty_conf.use_pager,
            "cache": empty_conf.use_cache,
            "limit": empty_conf.limit_timeline,
            "timeout": empty_conf.timeout,
            "sorting": empty_conf.sorting,
            "porcelain": empty_conf.porcelain,
        }
    }

    assert empty_conf.build_default_map() == default_map
Beispiel #5
0
def test_build_default_map():
    empty_cfg = configparser.ConfigParser()
    empty_conf = Config("foobar", empty_cfg)

    default_map = {
        "following": {
            "check": empty_conf.check_following,
            "timeout": empty_conf.timeout,
            "porcelain": empty_conf.porcelain,
        },
        "tweet": {
            "twtfile": empty_conf.twtfile,
        },
        "timeline": {
            "pager": empty_conf.use_pager,
            "cache": empty_conf.use_cache,
            "limit": empty_conf.limit_timeline,
            "timeout": empty_conf.timeout,
            "sorting": empty_conf.sorting,
            "porcelain": empty_conf.porcelain,
            "twtfile": empty_conf.twtfile,
        },
        "view": {
            "pager": empty_conf.use_pager,
            "cache": empty_conf.use_cache,
            "limit": empty_conf.limit_timeline,
            "timeout": empty_conf.timeout,
            "sorting": empty_conf.sorting,
            "porcelain": empty_conf.porcelain,
        }
    }

    assert empty_conf.build_default_map() == default_map
Beispiel #6
0
def test_from_file(config_dir):
    with pytest.raises(ValueError) as e:
        Config.from_file("invalid")
    assert "Config file not found." in str(e.value)

    with open(str(config_dir.join("empty")), "a") as fh:
        fh.write("XXX")
    with pytest.raises(ValueError) as e:
        Config.from_file(str(config_dir.join("empty")))
    assert "Config file is invalid." in str(e.value)

    conf = Config.from_file(str(config_dir.join(Config.config_name)))
    check_cfg(conf)
Beispiel #7
0
def test_from_file(config_dir):
    with pytest.raises(ValueError) as e:
        Config.from_file("invalid")
    assert "Config file not found." in str(e.value)

    with open(str(config_dir.join("empty")), "a") as fh:
        fh.write("XXX")
    with pytest.raises(ValueError) as e:
        Config.from_file(str(config_dir.join("empty")))
    assert "Config file is invalid." in str(e.value)

    conf = Config.from_file(str(config_dir.join(Config.config_name)))
    check_cfg(conf)
Beispiel #8
0
def test_check_config_file_sanity(capsys, config_dir):
    with pytest.raises(ValueError) as e:
        Config.from_file(str(config_dir.join("config_sanity")))
    assert "Error in config file." in str(e.value)

    out, err = capsys.readouterr()
    for line in ["✗ Config error on limit_timeline - invalid literal for int() with base 10: '2t0'",
                 "✗ Config error on check_following - Not a boolean: TTrue",
                 "✗ Config error on porcelain - Not a boolean: Faltse",
                 "✗ Config error on disclose_identity - Not a boolean: Ftalse",
                 "✗ Config error on timeout - could not convert string to float: '5t.0'",
                 "✗ Config error on use_pager - Not a boolean: Falste",
                 "✗ Config error on use_cache - Not a boolean: Trute"]:
        assert line in out
Beispiel #9
0
def test_check_config_file_sanity(capsys, config_dir):
    with pytest.raises(ValueError) as e:
        Config.from_file(str(config_dir.join("config_sanity")))
    assert "Error in config file." in str(e.value)

    out, err = capsys.readouterr()
    for line in [
            "✗ Config error on limit_timeline - invalid literal for int() with base 10: '2t0'",
            "✗ Config error on check_following - Not a boolean: TTrue",
            "✗ Config error on porcelain - Not a boolean: Faltse",
            "✗ Config error on disclose_identity - Not a boolean: Ftalse",
            "✗ Config error on timeout - could not convert string to float: '5t.0'",
            "✗ Config error on use_pager - Not a boolean: Falste",
            "✗ Config error on use_cache - Not a boolean: Trute"
    ]:
        assert line in out
Beispiel #10
0
def check_if_following(follower):
    config = Config.discover()

    if config.get_source_by_nick(follower):
        return True

    return False
Beispiel #11
0
def check_if_following(follower):
    config = Config.discover()

    if config.get_source_by_nick(follower):
        return True

    return False
Beispiel #12
0
def test_create_config(config_dir):
    config_dir_old = Config.config_dir
    Config.config_dir = str(config_dir.join("new"))
    conf_w = Config.create_config("bar", "batz.txt", True)
    conf_r = Config.discover()
    assert conf_r.nick == "bar"
    assert conf_r.twtfile == "batz.txt"
    assert conf_r.following[0].nick == "twtxt"
    assert conf_r.following[0].url == "https://buckket.org/twtxt_news.txt"
    assert set(conf_r.options.keys()) == {"nick", "twtfile"}

    conf_r.cfg.remove_section("twtxt")
    assert conf_r.options == {}

    conf_r.cfg.remove_section("following")
    assert conf_r.following == []
    Config.config_dir = config_dir_old
Beispiel #13
0
def cli(ctx, config, verbose):
    """Decentralised, minimalist microblogging service for hackers."""
    init_logging(debug=verbose)

    try:
        if config:
            conf = Config.from_file(config)
        else:
            conf = Config.discover()
    except ValueError:
        click.echo("Error loading config file.")
        if not config:
            if click.confirm("Do you want to run the twtxt quickstart wizard?", abort=True):
                pass

    ctx.default_map = conf.build_default_map()
    ctx.obj = {'conf': conf}
Beispiel #14
0
def cli(ctx, config, verbose):
    """Decentralised, minimalist microblogging service for hackers."""
    init_logging(debug=verbose)

    if ctx.invoked_subcommand == "quickstart":
        return

    try:
        if config:
            conf = Config.from_file(config)
        else:
            conf = Config.discover()
    except ValueError:
        click.echo("✗ Config file not found or not readable. You may want to run twtxt quickstart.")
        sys.exit()

    ctx.default_map = conf.build_default_map()
    ctx.obj = {'conf': conf}
Beispiel #15
0
def cli(ctx, config, verbose):
    """Decentralised, minimalist microblogging service for hackers."""
    init_logging(debug=verbose)

    try:
        if config:
            conf = Config.from_file(config)
        else:
            conf = Config.discover()
    except ValueError:
        click.echo("Error loading config file.")
        if not config:
            if click.confirm("Do you want to run the twtxt quickstart wizard?",
                             abort=True):
                pass

    ctx.default_map = conf.build_default_map()
    ctx.obj = {'conf': conf}
Beispiel #16
0
def test_create_config(config_dir):
    config_dir_old = Config.config_dir
    Config.config_dir = str(config_dir.join("new"))
    conf_w = Config.create_config("bar", "batz.txt", False, True)
    conf_r = Config.discover()
    assert conf_r.nick == "bar"
    assert conf_r.twtfile == "batz.txt"
    assert conf_r.character_limit == 140
    assert conf_r.following[0].nick == "twtxt"
    assert conf_r.following[0].url == "https://buckket.org/twtxt_news.txt"
    assert set(conf_r.options.keys()) == {"nick", "twtfile", "disclose_identity", "character_limit"}

    conf_r.cfg.remove_section("twtxt")
    assert conf_r.options == {}

    conf_r.cfg.remove_section("following")
    assert conf_r.following == []
    Config.config_dir = config_dir_old
Beispiel #17
0
def cli(ctx, config, verbose):
    """Decentralised, minimalist microblogging service for hackers."""
    init_logging(debug=verbose)

    if ctx.invoked_subcommand == "quickstart":
        return

    try:
        if config:
            conf = Config.from_file(config)
        else:
            conf = Config.discover()
    except ValueError:
        click.echo(
            "✗ Config file not found or not readable. You may want to run twtxt quickstart."
        )
        sys.exit()

    ctx.default_map = conf.build_default_map()
    ctx.obj = {'conf': conf}
Beispiel #18
0
def cli(ctx, config, verbose):
    """Decentralised, minimalist microblogging service for hackers."""
    init_logging(debug=verbose)

    if ctx.invoked_subcommand == "quickstart":
        return  # Skip initializing config file

    try:
        if config:
            conf = Config.from_file(config)
        else:
            conf = Config.discover()
    except ValueError as e:
        if "Error in config file." in str(e):
            click.echo("✗ Please correct the errors mentioned above an run twtxt again.")
        else:
            click.echo("✗ Config file not found or not readable. You may want to run twtxt quickstart.")
        sys.exit()

    ctx.default_map = conf.build_default_map()
    ctx.obj = {'conf': conf}
Beispiel #19
0
def quickstart():
    """Quickstart wizard for setting up twtxt."""
    width = click.get_terminal_size()[0]
    width = width if width <= 79 else 79

    click.secho("twtxt - quickstart", fg="cyan")
    click.secho("==================", fg="cyan")
    click.echo()

    help_text = "This wizard will generate a basic configuration file for twtxt with all mandatory options set. " \
                "You can change all of these later with either twtxt itself or by editing the config file manually. " \
                "Have a look at the docs to get information about the other available options and their meaning."
    click.echo(textwrap.fill(help_text, width))

    click.echo()
    nick = click.prompt("➤ Please enter your desired nick", default=os.environ.get("USER", ""))

    def overwrite_check(path):
        if os.path.isfile(path):
            click.confirm("➤ '{0}' already exists. Overwrite?".format(path), abort=True)

    cfgfile = click.prompt("➤ Please enter the desired location for your config file",
                           os.path.join(Config.config_dir, Config.config_name),
                           type=click.Path(readable=True, writable=True, file_okay=True))
    cfgfile = os.path.expanduser(cfgfile)
    overwrite_check(cfgfile)

    twtfile = click.prompt("➤ Please enter the desired location for your twtxt file",
                           os.path.expanduser("~/twtxt.txt"),
                           type=click.Path(readable=True, writable=True, file_okay=True))
    twtfile = os.path.expanduser(twtfile)
    overwrite_check(twtfile)

    twturl = click.prompt("➤ Please enter the URL your twtxt file will be accessible from",
                          default="https://example.org/twtxt.txt")

    disclose_identity = click.confirm("➤ Do you want to disclose your identity? Your nick and URL will be shared when "
                                      "making HTTP requests", default=False)

    click.echo()
    add_news = click.confirm("➤ Do you want to follow the twtxt news feed?", default=True)

    conf = Config.create_config(cfgfile, nick, twtfile, twturl, disclose_identity, add_news)

    twtfile_dir = os.path.dirname(twtfile)
    if not os.path.exists(twtfile_dir):
        os.makedirs(twtfile_dir)
    open(twtfile, "a").close()

    click.echo()
    click.echo("✓ Created config file at '{0}'.".format(click.format_filename(conf.config_file)))
    click.echo("✓ Created twtxt file at '{0}'.".format(click.format_filename(twtfile)))
Beispiel #20
0
def quickstart():
    """Quickstart wizard for setting up twtxt."""
    width = click.get_terminal_size()[0]
    width = width if width <= 79 else 79

    click.secho("twtxt - quickstart", fg="cyan")
    click.secho("==================", fg="cyan")
    click.echo()

    help_text = "This wizard will generate a basic configuration file for twtxt with all mandatory options set. " \
                "You can change all of these later with either twtxt itself or by editing the config file manually. " \
                "Have a look at the docs to get information about the other available options and their meaning."
    click.echo(textwrap.fill(help_text, width))

    click.echo()
    nick = click.prompt("➤ Please enter your desired nick", default=os.environ.get("USER", ""))

    def overwrite_check(path):
        if os.path.isfile(path):
            click.confirm("➤ '{0}' already exists. Overwrite?".format(path), abort=True)

    cfgfile = click.prompt("➤ Please enter the desired location for your config file",
                           os.path.join(Config.config_dir, Config.config_name),
                           type=click.Path(readable=True, writable=True, file_okay=True))
    cfgfile = os.path.expanduser(cfgfile)
    overwrite_check(cfgfile)

    twtfile = click.prompt("➤ Please enter the desired location for your twtxt file",
                           os.path.expanduser("~/twtxt.txt"),
                           type=click.Path(readable=True, writable=True, file_okay=True))
    twtfile = os.path.expanduser(twtfile)
    overwrite_check(twtfile)

    twturl = click.prompt("➤ Please enter the URL your twtxt file will be accessible from",
                          default="https://example.org/twtxt.txt")

    disclose_identity = click.confirm("➤ Do you want to disclose your identity? Otherwise your nick and URL will be shared when "
                                      "making HTTP requests", default=False)

    click.echo()
    add_news = click.confirm("➤ Do you want to follow the twtxt news feed?", default=True)

    conf = Config.create_config(cfgfile, nick, twtfile, twturl, disclose_identity, add_news)

    twtfile_dir = os.path.dirname(twtfile)
    if not os.path.exists(twtfile_dir):
        os.makedirs(twtfile_dir)
    open(twtfile, "a").close()

    click.echo()
    click.echo("✓ Created config file at '{0}'.".format(click.format_filename(conf.config_file)))
    click.echo("✓ Created twtxt file at '{0}'.".format(click.format_filename(twtfile)))
Beispiel #21
0
def test_defaults():
    empty_cfg = configparser.ConfigParser()
    empty_conf = Config("foobar", empty_cfg)

    assert empty_conf.nick == os.environ.get("USER", "")
    assert empty_conf.twtfile == "twtxt.txt"
    assert empty_conf.twturl is None
    assert empty_conf.check_following is True
    assert empty_conf.use_pager is False
    assert empty_conf.porcelain is False
    assert empty_conf.limit_timeline == 20
    assert empty_conf.timeout == 5.0
    assert empty_conf.sorting == "descending"
    assert empty_conf.post_tweet_hook is None
Beispiel #22
0
def test_create_config(config_dir):
    config_dir_old = Config.config_dir
    Config.config_dir = str(config_dir.join("new"))
    conf_w = Config.create_config(
        os.path.join(Config.config_dir, Config.config_name), "bar", "batz.txt",
        False, True)
    conf_r = Config.discover()
    assert conf_r.nick == "bar"
    assert conf_r.twtfile == "batz.txt"
    assert conf_r.character_limit == 140
    assert conf_r.character_warning == 140
    assert conf_r.following[0].nick == "twtxt"
    assert conf_r.following[0].url == "https://buckket.org/twtxt_news.txt"
    assert set(conf_r.options.keys()) == {
        "nick", "twtfile", "disclose_identity", "character_limit",
        "character_warning"
    }

    conf_r.cfg.remove_section("twtxt")
    assert conf_r.options == {}

    conf_r.cfg.remove_section("following")
    assert conf_r.following == []
    Config.config_dir = config_dir_old
Beispiel #23
0
def test_add_get_remove_source():
    conf = Config.discover()
    conf.cfg.remove_section("following")

    assert conf.remove_source_by_nick("foo") is False
    assert conf.get_source_by_nick("baz") is None

    conf.add_source(Source("foo", "bar"))
    source = conf.get_source_by_nick("foo")
    assert source.nick == "foo"
    assert source.url == "bar"

    assert conf.remove_source_by_nick("baz") is False
    assert conf.remove_source_by_nick("foo") is True
    assert conf.following == []
Beispiel #24
0
def test_add_get_remove_source():
    conf = Config.discover()
    conf.cfg.remove_section("following")

    assert conf.remove_source_by_nick("foo") is False
    assert conf.get_source_by_nick("baz") is None

    conf.add_source(Source("foo", "bar"))
    source = conf.get_source_by_nick("foo")
    assert source.nick == "foo"
    assert source.url == "bar"

    assert conf.remove_source_by_nick("baz") is False
    assert conf.remove_source_by_nick("foo") is True
    assert conf.following == []
Beispiel #25
0
def test_defaults():
    empty_cfg = configparser.ConfigParser()
    empty_conf = Config("foobar", empty_cfg)

    assert empty_conf.nick == os.environ.get("USER", "")
    assert empty_conf.twtfile == "twtxt.txt"
    assert empty_conf.twturl is None
    assert empty_conf.check_following is True
    assert empty_conf.use_pager is False
    assert empty_conf.use_cache is True
    assert empty_conf.porcelain is False
    assert empty_conf.character_limit is None
    assert empty_conf.character_warning is None
    assert empty_conf.disclose_identity is False
    assert empty_conf.limit_timeline == 20
    assert empty_conf.timeline_update_interval == 10
    assert empty_conf.timeout == 5.0
    assert empty_conf.sorting == "descending"
    assert empty_conf.post_tweet_hook is None
    assert empty_conf.pre_tweet_hook is None
Beispiel #26
0
def test_discover():
    conf = Config.discover()
    check_cfg(conf)
Beispiel #27
0
def retrieve_file(client, source, limit, cache):
    is_cached = cache.is_cached(source.url) if cache else None
    headers = {"If-Modified-Since": cache.last_modified(source.url)} if is_cached else {}

    try:
        response = yield from client.request("get",source.url, headers=headers,allow_redirects=False)
        content = yield from response.text()
    except Exception as e:
        if is_cached:
            logger.debug("{}: {} - using cached content".format(source.url, e))
            return cache.get_tweets(source.url, limit)
    #comp490
        elif e==ssl.CertificateError:

            click.echo("Warning the source: "+source.nick+" is unsafe: Hostname does not match name on SSL certificate")
            return []
        elif e==aiohttp.errors.ClientOSError:

            if "[[SSL: CERTIFICATE_VERIFY_FAILED" in str(e):
                click.echo("Warning the source: "+source.nick+" is unsafe: The ssl certificate has expired")
                return []
            elif "[SSL: EXCESSIVE_MESSAGE_SIZE]" in str(e):
                click.echo("Warning the source: "+source.nick+" is unsafe: source has sent an invalid response")
    #COMP490
        else:
            logger.debug(e)
            return []

    if response.status == 200:
        tweets = parse_tweets(content.splitlines(), source)

        if cache:
            last_modified_header = response.headers.get("Last-Modified")
            if last_modified_header:
                logger.debug("{} returned 200 and Last-Modified header - adding content to cache".format(source.url))
                cache.add_tweets(source.url, last_modified_header, tweets)
            else:
                logger.debug("{} returned 200 but no Last-Modified header - can’t cache content".format(source.url))
        else:
            logger.debug("{} returned 200".format(source.url))

        return sorted(tweets, reverse=True)[:limit]
#comp490
    elif response.status==301:
        cache = Cache.discover()
        conf=Config.discover()
        tweets=cache.get_tweets(source.url)

        conf.remove_source_by_nick(source.nick)
        url=response.headers["Location"]
        conf.add_source(Source(source.nick,url))
        for tweet in tweets:
            cache.add_tweet(url,0,tweet)
#comp490
    elif response.status == 410 and is_cached:
        # 410 Gone:
        # The resource requested is no longer available,
        # and will not be available again.
        logger.debug("{} returned 410 - deleting cached content".format(source.url))
        cache.remove_tweets(source.url)
        return []

    elif is_cached:
        logger.debug("{} returned {} - using cached content".format(source.url, response.status))
        return cache.get_tweets(source.url, limit)

    else:
        logger.debug("{} returned {}".format(source.url, response.status))
        return []
Beispiel #28
0
def test_discover():
    conf = Config.discover()
    check_cfg(conf)