From 082cdb4b93ea7f127b41e07c61e9ce72626e9d69 Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Thu, 6 Sep 2018 18:45:08 +0530 Subject: [PATCH] Moving functions from main run.py to their own social.py file. --- .gitignore | 0 LICENSE.md | 0 README.md | 0 config.json | 4 +- helpers.py | 0 requirements.txt | 0 run.py | 127 ++--------------------------------------------- social.py | 119 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 125 insertions(+), 125 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 LICENSE.md mode change 100644 => 100755 README.md mode change 100644 => 100755 config.json mode change 100644 => 100755 helpers.py mode change 100644 => 100755 requirements.txt create mode 100644 social.py diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/LICENSE.md b/LICENSE.md old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/config.json b/config.json old mode 100644 new mode 100755 index df007a9..4d67fa0 --- a/config.json +++ b/config.json @@ -1,8 +1,8 @@ { "gen.APP_NAME": "Tweet-Toot", "gen.log_timestamp": "%Y-%m-%d %H:%M:%S", - "tweets.source_account_url": "", - "toots.host_instance": "", + "tweets.source_account_url": "https://twitter.com/SarcasmMother", + "toots.host_instance": "https://botsin.space", "toots.app_secure_token": "", "toots.cache_path": "/tmp/" } \ No newline at end of file diff --git a/helpers.py b/helpers.py old mode 100644 new mode 100755 diff --git a/requirements.txt b/requirements.txt old mode 100644 new mode 100755 diff --git a/run.py b/run.py index 6e17218..1df61e1 100644 --- a/run.py +++ b/run.py @@ -1,125 +1,6 @@ import helpers +import social import sys -import requests -from bs4 import BeautifulSoup -from pathlib import Path - - -def isAlreadyTooted(tweet_id): - """ Check if a tweet has already been POSTed to Mastodon. If so, let's not do that again. - - This is important! - - Since this script will likely run as a cron, tweet-bombing our favorite Mastodon neighbourhood - will ruin things for everyone. - - Arguments: - tweet_id {string} -- Numerical tweet ID returned by getTweets(). - """ - - cache_path = helpers._config('toots.cache_path') - - my_file = Path(cache_path + tweet_id) - if my_file.is_file(): - - return True - else: - - return False - - -def getTweets(): - """ Get list of tweets, with tweet ID and content, from configured Twitter account URL. - - This function relies on BeautifulSoup to extract the tweet IDs and content of all tweets on the specified page. - - The data is returned as a list of dictionaries that can be used by other functions. - """ - - all_tweets = [] - - url = helpers._config('tweets.source_account_url') - - if not url: - - return False - - headers = {} - headers['accept-language'] = 'en-US,en;q=0.9' - headers['dnt'] = '1' - headers['user-agent'] = helpers._config('gen.APP_NAME') - - data = requests.get(url) - - html = BeautifulSoup(data.text, 'html.parser') - - timeline = html.select('#timeline li.stream-item') - - if timeline is None: - - return False - - helpers._info('getTweets => Fetching tweets for ' + url + '.') - - for tweet in timeline: - - tweet_id = tweet['data-item-id'] - tweet_text = tweet.select('p.tweet-text')[0].get_text() - - all_tweets.append({"id": tweet_id, "text": tweet_text}) - - return all_tweets if len(all_tweets) > 0 else None - - -def tootTheTweet(tweet): - """ Receieve a dictionary containing Tweet ID and text... and TOOT! - - This function relies on the requests library to post the content to your Mastodon account (human or bot). - - A boolean success status is returned. - - Arguments: - tweet {dictionary} -- Dictionary containing the "id" and "text" of a single tweet. - """ - - if isAlreadyTooted(tweet['id']): - - helpers._info('tootTheTweet => ' + - tweet['id'] + ' already tooted. Skipping.') - - return False - - host_instance = helpers._config('toots.host_instance') - token = helpers._config('toots.app_secure_token') - - headers = {} - headers['Authorization'] = 'Bearer ' + token - headers['Idempotency-Key'] = tweet['id'] - - data = {} - data['status'] = tweet['text'] - data['visibility'] = 'public' - - cache_path = '/tmp/' + tweet['id'] - new_days = open(cache_path, 'w') - new_days.write(tweet['text']) - new_days.close() - - response = requests.post( - url=host_instance + '/api/v1/statuses', data=data, headers=headers) - - if response.status_code == 200: - - helpers._info('tootTheTweet => OK (Response: ' + response.text + ')') - - return True - - else: - - helpers._error( - 'tootTheTweet => FAIL (Response: ' + response.text + ')') - - return False if __name__ == '__main__': @@ -130,12 +11,12 @@ if __name__ == '__main__': It will only toot once per invokation to avoid flooding the instance. """ - tweets = getTweets() + tweets = social.getTweets() if not tweets: helpers._error( - '__main__ => No tweets fetched. Please check the Twitter account URL "tweets.source_account_url" in the config.json file.') + '__main__ => No tweets fetched.') sys.exit() @@ -143,7 +24,7 @@ if __name__ == '__main__': for tweet in tweets: - if tootTheTweet(tweet): + if social.tootTheTweet(tweet): helpers._info('__main__ => Tooted "' + tweet['text'] + '"') helpers._info( diff --git a/social.py b/social.py new file mode 100644 index 0000000..5ca35ff --- /dev/null +++ b/social.py @@ -0,0 +1,119 @@ +import helpers +import requests +from bs4 import BeautifulSoup +from pathlib import Path + + +def getTweets(): + """ Get list of tweets, with tweet ID and content, from configured Twitter account URL. + + This function relies on BeautifulSoup to extract the tweet IDs and content of all tweets on the specified page. + + The data is returned as a list of dictionaries that can be used by other functions. + """ + + all_tweets = [] + + url = helpers._config('tweets.source_account_url') + + if not url: + + helpers._error('getTweets() => The source Twitter account URL (' + url + ') was incorrect. Could not retrieve tweets.') + + return False + + headers = {} + headers['accept-language'] = 'en-US,en;q=0.9' + headers['dnt'] = '1' + headers['user-agent'] = helpers._config('gen.APP_NAME') + + data = requests.get(url) + + html = BeautifulSoup(data.text, 'html.parser') + + timeline = html.select('#timeline li.stream-item') + + if timeline is None: + + helpers._error('getTweets() => Could not retrieve tweets from the page. Please make sure the source Twitter account URL (' + url + ') is correct.') + + return False + + helpers._info('getTweets => Fetched tweets for ' + url + '.') + + for tweet in timeline: + + tweet_id = tweet['data-item-id'] + tweet_text = tweet.select('p.tweet-text')[0].get_text() + + all_tweets.append({"id": tweet_id, "text": tweet_text}) + + return all_tweets if len(all_tweets) > 0 else None + + +def tootTheTweet(tweet): + """ Receieve a dictionary containing Tweet ID and text... and TOOT! + + This function relies on the requests library to post the content to your Mastodon account (human or bot). + + A boolean success status is returned. + + Arguments: + tweet {dictionary} -- Dictionary containing the "id" and "text" of a single tweet. + """ + + host_instance = helpers._config('toots.host_instance') + token = helpers._config('toots.app_secure_token') + + if not host_instance: + + helpers._error('tootTheTweet() => Your host Mastodon instance URL (' + host_instance + ') was incorrect.') + + return False + + if not token: + + helpers._error('tootTheTweet() => Your Mastodon access token was incorrect.') + + return False + + headers = {} + headers['Authorization'] = 'Bearer ' + token + headers['Idempotency-Key'] = tweet['id'] + + data = {} + data['status'] = tweet['text'] + data['visibility'] = 'public' + + tweet_check_file_path = helpers._config('toots.cache_path') + tweet['id'] + tweet_check_file = Path(tweet_check_file_path) + if tweet_check_file.is_file(): + + helpers._info('tootTheTweet() => This tweet has already been posted. Skipping...') + + return False + + else: + + tweet_check = open(tweet_check_file_path, 'w') + tweet_check.write(tweet['text']) + tweet_check.close() + + helpers._info('tootTheTweet() => Caching new tweet.') + + response = requests.post( + url=host_instance + '/api/v1/statuses', data=data, headers=headers) + + if response.status_code == 200: + + helpers._info('tootTheTweet() => OK. Posted tweet to Mastodon.') + helpers._info('tootTheTweet() => Response: ' + response.text) + + return True + + else: + + helpers._info('tootTheTweet() => FAIL. Could not post tweet to Mastodon.') + helpers._info('tootTheTweet() => Response: ' + response.text) + + return False \ No newline at end of file