From 5314497769f58cb32649cecd5f4427a473a45756 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Dec 2024 03:18:09 -0500 Subject: [PATCH 1/3] Bump tqdm from 4.66.6 to 4.67.1 (#217) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3a69e5e..4304dd2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pillow==10.2.0 psycopg2-binary==2.9.10 requests==2.32.2 SQLAlchemy==2.0.24 -tqdm==4.66.6 +tqdm==4.67.1 tweepy==4.14.0 python-dotenv==1.0.1 urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability From 6f6fecb43a981bd48e2ec087bd1d9436877f3e13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Dec 2024 03:18:28 -0500 Subject: [PATCH 2/3] Bump alembic from 1.13.3 to 1.14.0 (#214) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4304dd2..636a2d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -alembic==1.13.3 +alembic==1.14.0 black==24.10.0 openai==1.30.3 pillow==10.2.0 From 4f18910b861767851a2a51ec185eb8672694e203 Mon Sep 17 00:00:00 2001 From: Anderson Nelson Date: Sat, 21 Dec 2024 22:31:27 -0500 Subject: [PATCH 3/3] Blue sky implementation (#220) --- main.py | 9 ++--- nearquake/config/__init__.py | 3 ++ nearquake/data_processor.py | 9 ++--- nearquake/post_manager.py | 59 ++++++++++++++++++++++++++++++ nearquake/tweet_processor.py | 69 ------------------------------------ requirements.txt | 1 + 6 files changed, 71 insertions(+), 79 deletions(-) create mode 100644 nearquake/post_manager.py delete mode 100644 nearquake/tweet_processor.py diff --git a/main.py b/main.py index b4f12e9..621e218 100644 --- a/main.py +++ b/main.py @@ -16,6 +16,7 @@ UploadEarthQuakeLocation, get_date_range_summary, ) +from nearquake.post_manager import post_to_all_platforms from nearquake.open_ai_client import generate_response from nearquake.utils import convert_datetime, format_earthquake_alert from nearquake.utils.db_sessions import DbSessionManager @@ -98,7 +99,7 @@ post_type="fact", message=message, ) - tweet.run_tweet_operator(tweet_text=tweet_text, conn=conn) + post_to_all_platforms(tweet_text) if args.weekly: run.upload(url=generate_time_period_url("week")) @@ -122,7 +123,7 @@ post_type="fact", message=message, ) - tweet.run_tweet_operator(tweet_text=tweet_text, conn=conn) + post_to_all_platforms(tweet_text) if args.monthly: run.upload(url=generate_time_period_url("month")) @@ -146,7 +147,7 @@ post_type="fact", message=message, ) - tweet.run_tweet_operator(tweet_text=tweet_text, conn=conn) + post_to_all_platforms(tweet_text) if args.initialize: create_database(url=POSTGRES_CONNECTION_URL, schema=["earthquake", "tweet"]) @@ -159,7 +160,7 @@ post_type="fact", message=message, prompt=prompt ) - tweet.run_tweet_operator(tweet_text=tweet_text, conn=conn) + post_to_all_platforms(tweet_text) if args.backfill: start_date = input("Type Start Date:") diff --git a/nearquake/config/__init__.py b/nearquake/config/__init__.py index bb0822d..d5dbdf5 100644 --- a/nearquake/config/__init__.py +++ b/nearquake/config/__init__.py @@ -84,6 +84,9 @@ def generate_time_period_url(time_period: int) -> str: "BEARER_TOKEN": os.environ.get("BEARER_TOKEN"), } +BLUESKY_USER_NAME = os.environ.get("BLUESKY_USER_NAME") +BLUESKY_PASSWORD = os.environ.get("BLUESKY_PASSWORD") + TWEET_CONCLUSION = [ "How do you prepare? Share tips and stay safe! #earthquakePrep. Data provided by #usgs", "Were you near the epicenter? Share your experience. #earthquake. Data provided by #usgs", diff --git a/nearquake/data_processor.py b/nearquake/data_processor.py index 48691ff..2883b32 100644 --- a/nearquake/data_processor.py +++ b/nearquake/data_processor.py @@ -16,7 +16,7 @@ generate_coordinate_lookup_detail_url, generate_time_range_url, ) -from nearquake.tweet_processor import TweetOperator +from nearquake.post_manager import post_to_all_platforms from nearquake.utils import ( convert_timestamp_to_utc, fetch_json_data_from_url, @@ -272,7 +272,7 @@ def backfill( ) -class TweetEarthquakeEvents(BaseDataUploader, TweetOperator): +class TweetEarthquakeEvents(BaseDataUploader): def _extract(self) -> List: query = ( @@ -319,10 +319,7 @@ def upload(self): ) try: - self.run_tweet_operator(tweet_text=tweet_text, conn=self.conn) - _logger.info( - "Recorded recent tweet posted in the database recent into the Database " - ) + post_to_all_platforms(post_text=tweet_text) except Exception as e: _logger.error( diff --git a/nearquake/post_manager.py b/nearquake/post_manager.py new file mode 100644 index 0000000..14c14a2 --- /dev/null +++ b/nearquake/post_manager.py @@ -0,0 +1,59 @@ +import logging +from abc import ABC, abstractmethod +import tweepy +from atproto import Client +from nearquake.config import TWITTER_AUTHENTICATION, BLUESKY_PASSWORD, BLUESKY_USER_NAME + +_logger = logging.getLogger(__name__) + + +class PlatformPoster(ABC): + def __init__(self): + self.client = None + + @abstractmethod + def post(self, post_text): + pass + + +class TwitterPost(PlatformPoster): + def __init__(self): + self.client = tweepy.Client( + bearer_token=TWITTER_AUTHENTICATION["BEARER_TOKEN"], + consumer_key=TWITTER_AUTHENTICATION["CONSUMER_KEY"], + consumer_secret=TWITTER_AUTHENTICATION["CONSUMER_SECRET"], + access_token=TWITTER_AUTHENTICATION["ACCESS_TOKEN"], + access_token_secret=TWITTER_AUTHENTICATION["ACCESS_TOKEN_SECRET"], + ) + _logger.info("Successfully authenticated with Twitter") + + def post(self, post_text: str) -> bool: + try: + self.client.create_tweet(text=post_text) + _logger.info(f"Successfully posted to Twitter: {post_text}") + return True + except Exception as e: + _logger.error(f"Failed to post to Twitter: {post_text}. Error: {e}") + return False + + +class BlueSkyPost(PlatformPoster): + def __init__(self): + self.client = Client() + self.client.login(BLUESKY_USER_NAME, BLUESKY_PASSWORD) + _logger.info("Successfully authenticated with BlueSky") + + def post(self, post_text: str) -> bool: + try: + self.client.send_post(text=post_text) + _logger.info(f"Successfully posted to BlueSky: {post_text}") + return True + except Exception as e: + _logger.error(f"Failed to post to BlueSky: {post_text}. Error: {e}") + return False + + +def post_to_all_platforms(post_text: str) -> dict: + platforms = [TwitterPost(), BlueSkyPost()] + for platform in platforms: + platform.post(post_text.get("post")) diff --git a/nearquake/tweet_processor.py b/nearquake/tweet_processor.py deleted file mode 100644 index 39e1447..0000000 --- a/nearquake/tweet_processor.py +++ /dev/null @@ -1,69 +0,0 @@ -import logging - -import tweepy - -from nearquake.app.db import Post -from nearquake.config import TWITTER_AUTHENTICATION - -_logger = logging.getLogger(__name__) - - -class TweetOperator: - """ - Class to perform operations on Twitter such as posting tweets. - """ - - def _tweepy_connect(self) -> tweepy.client: - """ - Initialize a Tweepy client using authenticated credentials. - - """ - - client = tweepy.Client( - bearer_token=TWITTER_AUTHENTICATION["BEARER_TOKEN"], - consumer_key=TWITTER_AUTHENTICATION["CONSUMER_KEY"], - consumer_secret=TWITTER_AUTHENTICATION["CONSUMER_SECRET"], - access_token=TWITTER_AUTHENTICATION["ACCESS_TOKEN"], - access_token_secret=TWITTER_AUTHENTICATION["ACCESS_TOKEN_SECRET"], - ) - - self.client = client - - def __init__(self) -> None: - self._tweepy_connect() - - def post_tweet(self, tweet_text: dict) -> None: - """ - Post a tweet to twitter - :param tweet_text: The content of the tweet to be posted. - """ - - try: - self.client.create_tweet(text=tweet_text.get("post")) - _logger.info(f"Latest post to twitter {tweet_text}") - except Exception as e: - _logger.error(f"Did not post {tweet_text}. {e} ") - - def save_tweet_to_db(self, tweet_text: dict, conn) -> None: - """ - Save the posted tweet data into the database. - :param tweet_data: The content of the tweet to be saved. - :param conn: Database connection object. - """ - try: - conn.insert(Post(**tweet_text)) - _logger.info(f"Tweet saved to database: {tweet_text}") - except Exception as e: - _logger.error(f"Failed to save tweet to database {tweet_text}. Error: {e}") - raise - - def run_tweet_operator(self, tweet_text: dict, conn) -> None: - """ - Execute the tweet posting and saving operation. - - :param tweet_text: A dictionary containing the tweet data to be posted and saved. - :param conn: A database connection object used to insert the tweet data into the database. - """ - self._tweepy_connect() - self.post_tweet(tweet_text=tweet_text) - self.save_tweet_to_db(tweet_text=tweet_text, conn=conn) diff --git a/requirements.txt b/requirements.txt index 636a2d2..3a12fc6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ alembic==1.14.0 +atproto==0.0.56 black==24.10.0 openai==1.30.3 pillow==10.2.0