Exemple #1
0
def friends(
    screen_name: Optional[str] = None,
    limit: Optional[int] = None,
    output: Optional[str] = None,
):
    """Get JSON array of friends

    Args:
        screen_name (Optional[str], optional): Target user's screen name (i.e., Twitter handle). If none is given, authenticated user is used. Defaults to None.
        limit (Optional[int], optional): Max number of users to fetch. Defaults to None.
        output (Optional[str], optional): Output path for JSON file. Defaults to None.
    """
    # get api and user object
    api = pu.get_api()
    source_user = pu.get_user(screen_name=screen_name)

    # check limit for progress bar
    if not limit:  # pragma: no cover
        limit = source_user.friends_count

    # ensure output location
    fname = f"{source_user.screen_name}-friends.json"
    path = pu.set_output(fname=fname, path=output)

    # get users
    LOGGER.info(f"Fetching {limit} friends")
    pu.get_tweepy_objects(func=api.friends,
                          screen_name=screen_name,
                          output=path,
                          total=limit)
Exemple #2
0
def check_config():  # pragma: no cover
    """Check and validate the current configuation.
    """
    # check env variables
    LOGGER.info("Checking config settings")
    is_valid_config = True
    for k in settings.default.config:
        if k in settings.as_dict():
            LOGGER.info(f"{k.upper()} found in settings")
        else:
            LOGGER.warning(f"{k.upper()} not found in settings!")
            is_valid_config = False

    # special message if a bad config is detected
    bad_config_message = (
        "Invalid configuration. "
        f"Please visit {settings.project_homepage} for more info. "
        f"Please visit {settings.twitter_dev_page} for your API tokens.")
    if not is_valid_config:
        LOGGER.warning(bad_config_message)
        return

    # check authentication
    LOGGER.info("Checking Twitter authentication")
    try:
        api = pu.get_api()
        me = api.me()
        LOGGER.info(f"Successfully authenticated as {me.screen_name}")
    except tweepy.error.TweepError:
        LOGGER.warning("Invalid authentication values!")
        LOGGER.warning(bad_config_message)
Exemple #3
0
def view_user(user: str):
    """View a user's raw JSON

    Args:
        user (str): User's screen name
    """
    user = pu.get_api().get_user(user)
    print(
        json.dumps(
            user._json,
            indent=4,
            sort_keys=True,
            default=lambda o: "<not serializable>",
        ))
Exemple #4
0
def audit_tweets(  # noqa C901
    path: str,
    days: Optional[int] = None,
    min_likes: Optional[int] = None,
    max_likes: Optional[int] = None,
    min_retweets: Optional[int] = None,
    max_retweets: Optional[int] = None,
    min_ratio: Optional[float] = None,
    max_ratio: Optional[float] = None,
    self_favorited: Optional[bool] = None,
    prune: bool = False,
    favorite: bool = False,
    bool_or: bool = False,
):
    """Audit and review tweets given criteria

    Args:
        days (Optional[int], optional): Days since tweeted. Defaults to None.
        min_likes (Optional[int], optional): Min number of favourites. Defaults to None.
        max_likes (Optional[int], optional): Max number of favourites. Defaults to None.
        min_retweets (Optional[int], optional): Min number of retweets. Defaults to None.
        max_retweets (Optional[int], optional): Max number of retweets. Defaults to None.
        min_ratio (Optional[float], optional): Min Twitter like-retweet ratio. Defaults to None.
        max_ratio (Optional[float], optional): Max Twitter like-retweet ratio. Defaults to None.
        self_favorited (Optional[bool], optional): Check if tweet is self-liked. Defaults to None.
        prune (bool, optional): Prune and destroy identified tweets. Defaults to False.
        favorite (bool, optional): Like identified tweets. Defaults to False.
        bool_or (bool, optional): Switch to boolean OR for conditions. Defaults to False.
    """
    # load data
    path = Path(path)
    with open(path) as f:
        tweets = json.load(f)
    LOGGER.info(f"Loaded {len(tweets)} tweets")

    # init set of users to prune
    identified_tweets = set()

    # iterate and identify prunable users
    for t in tweets:
        text = textwrap.shorten(t["text"], width=settings.textwrap_width)
        failed_clauses = []

        if days is not None:
            today = datetime.date.today()
            limit = today - datetime.timedelta(days=days)
            tweet_dt = eu.parsedate_to_datetime(t["created_at"])
            failed_clauses.append(tweet_dt.date() < limit)

        if min_likes is not None:
            failed_clauses.append(t["favorite_count"] < min_likes)

        if max_likes is not None:
            failed_clauses.append(t["favorite_count"] > max_likes)

        if min_retweets is not None:
            failed_clauses.append(t["retweet_count"] < min_retweets)

        if max_retweets is not None:
            failed_clauses.append(t["retweet_count"] > max_retweets)

        if min_ratio is not None:
            failed_clauses.append(
                pu.calculate_like_retweet_ratio(likes=t["favorite_count"],
                                                retweets=t["retweet_count"]) <
                min_ratio)

        if max_ratio is not None:
            failed_clauses.append(
                pu.calculate_like_retweet_ratio(likes=t["favorite_count"],
                                                retweets=t["retweet_count"]) >
                max_ratio)

        if self_favorited is not None:
            failed_clauses.append(t["favorited"] == self_favorited)

        if len(failed_clauses) > 0:
            if (bool_or and any(failed_clauses)) or all(failed_clauses):
                LOGGER.info(f'Identified "{text}"')
                identified_tweets.add(t["id_str"])

    LOGGER.info(f"Identified {len(identified_tweets)} tweets")
    if prune:  # pragma: no cover
        for t in identified_tweets:
            LOGGER.info(f"Deleting {t}")

            try:
                pu.get_api().destroy_status(t)
            except tweepy.error.TweepError as e:
                LOGGER.error(e)
    if favorite:  # pragma: no cover
        for t in identified_tweets:
            LOGGER.info(f"Favoriting {t}")
            pu.get_api().create_favorite(t)
Exemple #5
0
def audit_users(  # noqa C901
    path: str,
    min_followers: Optional[int] = None,
    max_followers: Optional[int] = None,
    min_friends: Optional[int] = None,
    max_friends: Optional[int] = None,
    days: Optional[int] = None,
    min_tweets: Optional[int] = None,
    max_tweets: Optional[int] = None,
    min_favourites: Optional[int] = None,
    max_favourites: Optional[int] = None,
    min_ratio: Optional[float] = None,
    max_ratio: Optional[float] = None,
    prune: bool = False,
    befriend: bool = False,
    bool_or: bool = False,
):
    """Audit and review users given criteria

    Args:
        path (str): Path to JSON file of users (e.g., output of friends())
        min_followers (Optional[int], optional): Min number of followers. Defaults to None.
        max_followers (Optional[int], optional): Max number of followers. Defaults to None.
        min_friends (Optional[int], optional): Min number of friends. Defaults to None.
        max_friends (Optional[int], optional): Max number of friends. Defaults to None.
        days (Optional[int], optional): Days since last tweet. Defaults to None.
        min_tweets (Optional[int], optional): Min number of tweets. Defaults to None.
        max_tweets (Optional[int], optional): Max number of tweets. Defaults to None.
        min_favourites (Optional[int], optional): Min number of favourites. Defaults to None.
        max_favourites (Optional[int], optional): Max number of favourites. Defaults to None.
        min_ratio (Optional[float], optional): Min Twitter follower-friend (TFF) ratio. Defaults to None.
        max_ratio (Optional[float], optional): Max Twitter follower-friend (TFF) ratio. Defaults to None.
        prune (bool, optional): Unfollow identified users. Defaults to False.
        befriend (bool, optional): Follow identified users. Defaults to False.
        bool_or (bool, optional): Switch to boolean OR for conditions. Defaults to False.
    """

    # load users data
    path = Path(path)
    with open(path) as f:
        users = json.load(f)
    LOGGER.info(f"Loaded {len(users)} users")

    # init list of users to prune
    identified_users = set()

    # iterate and identify prunable users
    for u in sorted(users, key=lambda x: x["screen_name"].lower()):
        failed_clauses = []

        if min_followers is not None:
            failed_clauses.append(u["followers_count"] < min_followers)

        if max_followers is not None:
            failed_clauses.append(u["followers_count"] > max_followers)

        if min_friends is not None:
            failed_clauses.append(u["friends_count"] < min_friends)

        if max_friends is not None:
            failed_clauses.append(u["friends_count"] > max_friends)

        if days is not None:
            today = datetime.date.today()
            limit = today - datetime.timedelta(days=days)
            try:
                last_tweet = eu.parsedate_to_datetime(
                    u["status"]["created_at"])
                failed_clauses.append(last_tweet.date() < limit)
            except KeyError:
                # no tweets
                failed_clauses.append(True)

        if min_tweets is not None:
            failed_clauses.append(u["statuses_count"] < min_tweets)

        if max_tweets is not None:
            failed_clauses.append(u["statuses_count"] > max_tweets)

        if min_favourites is not None:
            failed_clauses.append(u["favourites_count"] < min_favourites)

        if max_favourites is not None:
            failed_clauses.append(u["favourites_count"] > max_favourites)

        if min_ratio is not None:
            failed_clauses.append(
                pu.calculate_tff_ratio(followers=u["followers_count"],
                                       friends=u["friends_count"]) < min_ratio)

        if max_ratio is not None:
            failed_clauses.append(
                pu.calculate_tff_ratio(followers=u["followers_count"],
                                       friends=u["friends_count"]) > max_ratio)

        if len(failed_clauses) > 0:
            if (bool_or and any(failed_clauses)) or all(failed_clauses):
                LOGGER.info(f"Identified {u['screen_name']}")
                identified_users.add(u["screen_name"])

    LOGGER.info(f"Identified {len(identified_users)} users")
    if prune:  # pragma: no cover
        for u in identified_users:
            LOGGER.info(f"Unfollowing {u}")

            try:
                pu.get_api().destroy_friendship(u)
            except tweepy.error.TweepError as e:
                LOGGER.error(e)
    if befriend:  # pragma: no cover
        for u in identified_users:
            LOGGER.info(f"Following {u}")
            try:
                pu.get_api().create_friendship(u)
            except tweepy.error.TweepError as e:
                LOGGER.error(e)