diff --git a/README.md b/README.md index 8a885fa..0dc6733 100644 --- a/README.md +++ b/README.md @@ -209,30 +209,47 @@ Open a **second terminal** in the same project directory. You can now run the `e After running a command, a new `.png` file (e.g., `wallstreetbets_daily_1690000000.png`) will be saved in the images-directory in the root directory of the project. - ## 4. Full Automation: Posting to Reddit via Cron Job -The final piece of the project is a script that automates the entire process: scraping data, generating an image, and posting it to a target subreddit like `r/rstat`. This is designed to be run via a scheduled task or cron job. +The final piece of the project is a script that automates the entire pipeline: scraping data, generating an image, and posting it to a target subreddit like `r/rstat`. This is designed to be run via a scheduled task or cron job. -### Prerequisites for Posting +### Prerequisites: One-Time Account Authorization (OAuth2) -The posting script needs to log in to your Reddit account. You must add your Reddit username and password to your `.env` file. +To post on your behalf, the script needs to be authorized with your Reddit account. This is done securely using OAuth2 and a `refresh_token`, which is compatible with 2-Factor Authentication (2FA). This is a **one-time setup process**. -**Add these two lines to your `.env` file:** -``` -REDDIT_USERNAME=YourRedditUsername -REDDIT_PASSWORD=YourRedditPassword -``` -*(For security, it's recommended to use a dedicated bot account for this, not your personal account.)* +**Step 1: Get Your Refresh Token** + +1. First, ensure the "redirect uri" in your [Reddit App settings](https://www.reddit.com/prefs/apps) is set to **exactly** `http://localhost:8080`. +2. Run the temporary helper script included in the project: + ```bash + python get_refresh_token.py + ``` +3. The script will print a unique URL. Copy this URL and paste it into your web browser. +4. Log in to the Reddit account you want to post from and click **"Allow"** when prompted. +5. You'll be redirected to a `localhost:8080` page that says "This site can’t be reached". **This is normal and expected.** +6. Copy the **full URL** from your browser's address bar. It will look something like `http://localhost:8080/?state=...&code=...`. +7. Paste this full URL back into the terminal where the script is waiting and press Enter. +8. The script will output your unique **refresh token**. + +**Step 2: Update Your `.env` File** + +1. Open your `.env` file. +2. Add a new line and paste your refresh token into it. +3. Ensure your file now contains the following (your username and password are no longer needed): + ``` + REDDIT_CLIENT_ID=your_client_id_from_reddit + REDDIT_CLIENT_SECRET=your_client_secret_from_reddit + REDDIT_USER_AGENT=A custom user agent string (e.g., python:rstat:v1.2) + REDDIT_REFRESH_TOKEN=the_long_refresh_token_string_you_just_copied + ``` +You can now safely delete the `get_refresh_token.py` script. Your application is now authorized to post on your behalf indefinitely. ### The `post_to_reddit.py` Script -This is a standalone script located in the project's root directory that finds the most recently generated image and posts it to Reddit. +This is the standalone script that finds the most recently generated image and posts it to Reddit using your new authorization. **Manual Usage:** -You can run this script manually from your terminal. This is great for testing or one-off posts. - * **Post the latest OVERALL summary image to `r/rstat`:** ```bash python post_to_reddit.py @@ -248,18 +265,13 @@ You can run this script manually from your terminal. This is great for testing o python post_to_reddit.py --subreddit wallstreetbets --weekly ``` -* **Post to a different target subreddit (e.g., a test subreddit):** - ```bash - python post_to_reddit.py --target-subreddit MyTestSub - ``` - -### Setting Up the Cron Job for Full Automation +### Setting Up the Cron Job To run the entire pipeline automatically every day, you can use a simple shell script controlled by `cron`. **Step 1: Create a Job Script** -Create a file named `run_daily_job.sh` in the root of your project directory. This script will run all the necessary commands in the correct order. +Create a file named `run_daily_job.sh` in the root of your project directory. **`run_daily_job.sh`:** ```bash @@ -274,49 +286,43 @@ source /path/to/your/project/reddit_stock_analyzer/.venv/bin/activate echo "--- Starting RSTAT Daily Job on $(date) ---" -# 1. Scrape data from the last 24 hours for all subreddits in the config. +# 1. Scrape data from the last 24 hours. echo "Step 1: Scraping new data..." -rstat --config subreddits.json --days 1 +rstat --days 1 -# 2. Start the dashboard in the background so the exporter can access it. +# 2. Start the dashboard in the background. echo "Step 2: Starting dashboard in background..." rstat-dashboard & DASHBOARD_PID=$! - -# Give the server a moment to start up. sleep 10 # 3. Export the overall summary image. echo "Step 3: Exporting overall summary image..." python export_image.py --overall -# 4. Post the newly created overall summary image to r/rstat. +# 4. Post the image to r/rstat. echo "Step 4: Posting image to Reddit..." python post_to_reddit.py --target-subreddit rstat -# 5. Clean up by stopping the background dashboard server. +# 5. Clean up by stopping the dashboard server. echo "Step 5: Stopping dashboard server..." kill $DASHBOARD_PID echo "--- RSTAT Daily Job Complete ---" -```**Before proceeding, you must edit the two absolute paths at the top of this script to match your system.** +``` +**Before proceeding, you must edit the two absolute paths at the top of this script to match your system.** -**Step 2: Make the Script Executable** - -In your terminal, run the following command: -```bash +**Step 2: Make the Script Executable**```bash chmod +x run_daily_job.sh ``` **Step 3: Schedule the Cron Job** -1. Open your crontab editor by running `crontab -e`. -2. Add a new line to the file to schedule the job. For example, to run the script **every day at 10:00 PM**, add the following line: +1. Run `crontab -e` to open your crontab editor. +2. Add the following line to run the script every day at 10:00 PM and log its output: ``` 0 22 * * * /path/to/your/project/reddit_stock_analyzer/run_daily_job.sh >> /path/to/your/project/reddit_stock_analyzer/cron.log 2>&1 ``` - * `0 22 * * *` means at minute 0 of hour 22, every day, every month, every day of the week. - * `>> /path/to/your/.../cron.log 2>&1` is highly recommended. It redirects all output (both standard and error) from the script into a log file, so you can check if the job ran successfully. -Your project is now fully automated to scrape, analyze, visualize, and post data every day. \ No newline at end of file +Your project is now fully and securely automated. \ No newline at end of file diff --git a/get_refresh_token.py b/get_refresh_token.py new file mode 100644 index 0000000..66bd12e --- /dev/null +++ b/get_refresh_token.py @@ -0,0 +1,68 @@ +# get_refresh_token.py +# A temporary, one-time-use script to get your OAuth2 refresh token. + +import praw +from dotenv import load_dotenv +import os +import random +import socket + +# --- IMPORTANT: Ensure this matches the "redirect uri" in your Reddit App settings --- +REDIRECT_URI = "http://localhost:5000" + +def main(): + print("--- RSTAT Refresh Token Generator ---") + load_dotenv() + client_id = os.getenv("REDDIT_CLIENT_ID") + client_secret = os.getenv("REDDIT_CLIENT_SECRET") + + if not all([client_id, client_secret]): + print("Error: REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET must be set in your .env file.") + return + + # 1. Initialize PRAW + reddit = praw.Reddit( + client_id=client_id, + client_secret=client_secret, + redirect_uri=REDIRECT_URI, + user_agent="rstat_token_fetcher (by u/YourUsername)" # Can be anything + ) + + # 2. Generate the authorization URL + # Scopes define what our script is allowed to do. 'identity' and 'submit' are needed. + scopes = ["identity", "submit", "read"] + state = str(random.randint(0, 65536)) + auth_url = reddit.auth.url(scopes, state, "permanent") + + print("\nStep 1: Open this URL in your browser:\n") + print(auth_url) + + print("\nStep 2: Log in to Reddit, click 'Allow', and you'll be redirected to a 'page not found'.") + print("Step 3: Copy the ENTIRE URL from your browser's address bar after the redirect.") + + # 3. Get the redirected URL from the user + redirected_url = input("\nStep 4: Paste the full redirected URL here and press Enter:\n> ") + + # 4. Exchange the authorization code for a refresh token + try: + # The state is used to prevent CSRF attacks, we're just checking it matches + assert state == redirected_url.split("state=")[1].split("&")[0] + code = redirected_url.split("code=")[1].split("#_")[0] + + print("\nAuthorization code received. Fetching refresh token...") + + # This is the line that gets the key! + refresh_token = reddit.auth.authorize(code) + + print("\n--- SUCCESS! ---") + print("Your Refresh Token is:\n") + print(refresh_token) + print("\nStep 5: Copy this token and add it to your .env file as REDDIT_REFRESH_TOKEN.") + print("Step 6: You can now delete your REDDIT_USERNAME and REDDIT_PASSWORD from the .env file.") + + except Exception as e: + print(f"\nAn error occurred: {e}") + print("Please make sure you copied the full URL.") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/post_to_reddit.py b/post_to_reddit.py index ab9c0f3..9b097fc 100644 --- a/post_to_reddit.py +++ b/post_to_reddit.py @@ -16,19 +16,17 @@ def get_reddit_instance(): client_id = os.getenv("REDDIT_CLIENT_ID") client_secret = os.getenv("REDDIT_CLIENT_SECRET") user_agent = os.getenv("REDDIT_USER_AGENT") - username = os.getenv("REDDIT_USERNAME") # <-- Add your Reddit username to .env - password = os.getenv("REDDIT_PASSWORD") # <-- Add your Reddit password to .env + refresh_token = os.getenv("REDDIT_REFRESH_TOKEN") - if not all([client_id, client_secret, user_agent, username, password]): - print("Error: Reddit API credentials (including username/password) not found in .env file.") + if not all([client_id, client_secret, user_agent, refresh_token]): + print("Error: Reddit API credentials (including REDDIT_REFRESH_TOKEN) must be set in .env file.") return None return praw.Reddit( client_id=client_id, client_secret=client_secret, user_agent=user_agent, - username=username, - password=password + refresh_token=refresh_token ) def find_latest_image(pattern):