From b2b4a087417d19a3aba8b886df14fcab9a93c9a3 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Mon, 20 Jun 2022 01:34:46 +0300 Subject: [PATCH 1/5] updated checker.py, new .env.template syntax --- .env.template | 43 +++++++++++++----- main.py | 8 +++- setup.py | 67 +++++++--------------------- utils/checker.py | 76 ++++++++++++++++++++++++++++++++ utils/console.py | 39 ++++++++++++++++ utils/scripts/FileGrabber.ps1 | 9 ---- utils/scripts/FileGrabberenv.ps1 | 9 ---- 7 files changed, 168 insertions(+), 83 deletions(-) create mode 100755 utils/checker.py delete mode 100644 utils/scripts/FileGrabber.ps1 delete mode 100644 utils/scripts/FileGrabberenv.ps1 diff --git a/.env.template b/.env.template index bcd326d..6a4923c 100644 --- a/.env.template +++ b/.env.template @@ -1,31 +1,52 @@ REDDIT_CLIENT_ID="" +#EXPLANATION the ID of your Reddit app of SCRIPT type + REDDIT_CLIENT_SECRET="" +#EXPLANATION the SECRET of your Reddit app of SCRIPT type REDDIT_USERNAME="" +#EXPLANATION the username of your reddit account + REDDIT_PASSWORD="" +#EXPLANATION the password of your reddit account -# If no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: "no" +#OPTIONAL RANDOM_THREAD="no" +#EXPLANATION If set to no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: "no" -# Valid options are "yes" and "no" for the variable below REDDIT_2FA="" +#MATCH_REGEX ^(yes|no) +#EXPLANATION Whether you have Reddit 2FA enabled, Valid options are "yes" and "no" + SUBREDDIT="AskReddit" -# True or False ALLOW_NSFW="False" -# Used if you want to use a specific post. example of one is urdtfx +#EXPLANATION Whether to allow NSFW content, True or False +#MATCH_REGEX ^(True|False)$ + POST_ID="" -#set to either LIGHT or DARK +#MATCH_REGEX ^((?!://|://).)*$ +#EXPLANATION Used if you want to use a specific post. example of one is urdtfx + THEME="LIGHT" -# used if you want to run multiple times. set to an int e.g. 4 or 29 and leave blank for 1 +#EXPLANATION sets the Reddit theme, either LIGHT or DARK + TIMES_TO_RUN="" -# max number of characters a comment can have. -MAX_COMMENT_LENGTH="500" # default is 500 -# Range is 0 -> 1 recommended around 0.8-0.9 +#EXPLANATION used if you want to run multiple times. set to an int e.g. 4 or 29 and leave blank for 1 + +MAX_COMMENT_LENGTH="500" +#EXPLANATION max number of characters a comment can have. default is 500 + +#OPTIONAL OPACITY="1" +#EXPLANATION sets the opacity of the comments, Range is 0 -> 1 recommended around 0.8-0.9 # see different voice options: todo: add docs -VOICE="Matthew" # e.g. en_us_002 +VOICE="Matthew" +#EXPLANATION sets the voice the TTS uses, e.g. en_us_002 + TTsChoice="polly" +#EXPLANATION the backend used for TTS, default is polly -# IN-PROGRESS - not yet implemented +#OPTIONAL STORYMODE="False" +#EXPLANATION IN-PROGRESS - not yet implemented diff --git a/main.py b/main.py index 3ca7738..75863d9 100755 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python import time from subprocess import Popen @@ -6,11 +7,14 @@ from os import getenv, name from reddit.subreddit import get_subreddit_threads from utils.cleanup import cleanup from utils.console import print_markdown, print_step + # from utils.checker import envUpdate from video_creation.background import download_background, chop_background_video from video_creation.final_video import make_final_video from video_creation.screenshot_downloader import download_screenshots_of_reddit_posts from video_creation.voices import save_text_to_mp3 +from utils.checker import check_env + VERSION = 2.1 print( """ @@ -28,7 +32,6 @@ print_markdown( "### Thanks for using this tool! [Feel free to contribute to this project on GitHub!](https://lewismenelaws.com) If you have any questions, feel free to reach out to me on Twitter or submit a GitHub issue. You can find solutions to many common problems in the [Documentation](https://luka-hietala.gitbook.io/documentation-for-the-reddit-bot/)" ) -time.sleep(1) client_id = getenv("REDDIT_CLIENT_ID") client_secret = getenv("REDDIT_CLIENT_SECRET") @@ -38,7 +41,8 @@ reddit2fa = getenv("REDDIT_2FA") def main(): - #envUpdate() + if check_env() is not True: + exit() cleanup() def get_obj(): diff --git a/setup.py b/setup.py index a8d7f12..6063e5a 100755 --- a/setup.py +++ b/setup.py @@ -10,51 +10,14 @@ from utils.console import print_markdown from utils.console import print_step from rich.console import Console from utils.loader import Loader +from utils.console import handle_input console = Console() -def handle_input( - message: str = "", - check_type=False, - match: str = "", - err_message: str = "", - nmin=None, - nmax=None, - oob_error="", -): - match = re.compile(match + "$") - while True: - user_input = input(message + "\n> ").strip() - if re.match(match, user_input) is not None: - if check_type is not False: - try: - user_input = check_type(user_input) - if nmin is not None and user_input < nmin: - console.log("[red]" + oob_error) # Input too low failstate - continue - if nmax is not None and user_input > nmax: - console.log("[red]" + oob_error) # Input too high - continue - break # Successful type conversion and number in bounds - except ValueError: - console.log("[red]" + err_message) # Type conversion failed - continue - if nmin is not None and len(user_input) < nmin: # Check if string is long enough - console.log("[red]" + oob_error) - continue - if nmax is not None and len(user_input) > nmax: # Check if string is not too long - console.log("[red]" + oob_error) - continue - break - console.log("[red]" + err_message) - - return user_input - - if os.path.isfile(".setup-done-before"): - console.log( - "[red]Setup was already completed! Please make sure you have to run this script again. If that is such, delete the file .setup-done-before" + console.print( + "[red]WARNING: Setup was already completed! Please make sure you have to run this script again. If that is such, delete the file .setup-done-before" ) exit() @@ -89,15 +52,15 @@ if input("Are you sure you want to continue? > ").strip().casefold() != "yes": console.print("[bold green]Alright! Let's get started!") print() -console.log("Ensure you have the following ready to enter:") -console.log("[bold green]Reddit Client ID") -console.log("[bold green]Reddit Client Secret") -console.log("[bold green]Reddit Username") -console.log("[bold green]Reddit Password") -console.log("[bold green]Reddit 2FA (yes or no)") -console.log("[bold green]Opacity (range of 0-1, decimals are OK)") -console.log("[bold green]Subreddit (without r/ or /r/)") -console.log("[bold green]Theme (light or dark)") +console.print("Ensure you have the following ready to enter:") +console.print("[bold green]Reddit Client ID") +console.print("[bold green]Reddit Client Secret") +console.print("[bold green]Reddit Username") +console.print("[bold green]Reddit Password") +console.print("[bold green]Reddit 2FA (yes or no)") +console.print("[bold green]Opacity (range of 0-1, decimals are OK)") +console.print("[bold green]Subreddit (without r/ or /r/)") +console.print("[bold green]Theme (light or dark)") console.print( "[green]If you don't have these, please follow the instructions in the README.md file to set them up." ) @@ -117,7 +80,7 @@ console.print("[bold green]Alright! Let's get started!") # Begin the setup process. -console.log("Enter your credentials now.") +console.print("Enter your credentials now.") client_id = handle_input( "Client ID > ", False, @@ -178,7 +141,7 @@ theme = handle_input( ) loader = Loader("Attempting to save your credentials...", "Done!").start() # you can also put a while loop here, e.g. while VideoIsBeingMade == True: ... -console.log("Writing to the .env file...") +console.print("Writing to the .env file...") with open(".env", "w") as f: f.write( f"""REDDIT_CLIENT_ID="{client_id}" @@ -199,7 +162,7 @@ with open(".setup-done-before", "w") as f: loader.stop() -console.log("[bold green]Setup Complete! Returning...") +console.print("[bold green]Setup Complete! Returning...") # Post-Setup: send message and try to run main.py again. subprocess.call("python3 main.py", shell=True) diff --git a/utils/checker.py b/utils/checker.py new file mode 100755 index 0000000..75617b1 --- /dev/null +++ b/utils/checker.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +import os +from rich.console import Console +import re +import dotenv +from utils.console import handle_input + +console = Console() + +success = True + + +def check_env() -> bool: + if not os.path.exists(".env.template"): + console.print("[red]Couldn't find .env.template. Unable to check variables.") + return False + with open(".env.template", "r") as template: + # req_envs = [env.split("=")[0] for env in template.readlines() if "=" in env] + matching = {} + explanations = {} + req_envs = [] + var_optional = False + for line in template.readlines(): + if "=" in line and var_optional is not True: + req_envs.append(line.split("=")[0]) + elif "#OPTIONAL" in line: + var_optional = True + elif line.startswith("#MATCH_REGEX "): + matching[req_envs[-1]] = line.removeprefix("#MATCH_REGEX ")[:-1] + var_optional = False + elif line.startswith("#EXPLANATION "): + explanations[req_envs[-1]] = line.removeprefix("#EXPLANATION ")[:-1] + var_optional = False + else: + var_optional = False + missing = [] + incorrect = [] + dotenv.load_dotenv() + for env in req_envs: + value = os.getenv(env) + if value is None: + missing.append(env) + continue + if env in matching.keys(): + env, re.match(matching[env], value) is None and incorrect.append(env) + if len(missing): + for i in range(len(missing)): + try: + missing[i] = missing[i] + ": " + explanations[missing[i]] + except KeyError: + pass + console.print( + f"[red]{'These variables are'*(len(missing) > 1) or 'This variable is'} non-optional and missing: \n\n" + + "\n\n".join(missing) + ) + success = False + if len(incorrect): + console.print( + f"[red]{'These variables are'*(len(incorrect) > 1) or 'This variable is'} set incorrectly: " + + "\n".join(incorrect) + ) + success = False + # if success is True: + # return True + # console.print("[green]Do you want to enter the missing variables by hand(y/n)") + # if not input().casefold().startswith("y"): + # console.print("[red]Aborting: Unresolved missing variables") + # return success + # with open(".env", "a") as env_file: + # for env in missing: + # pass + return success + + +if __name__ == "__main__": + check_env() diff --git a/utils/console.py b/utils/console.py index 11ee429..83736ac 100644 --- a/utils/console.py +++ b/utils/console.py @@ -4,6 +4,7 @@ from rich.markdown import Markdown from rich.padding import Padding from rich.panel import Panel from rich.text import Text +import re console = Console() @@ -25,3 +26,41 @@ def print_step(text): def print_substep(text, style=""): """Prints a rich info message without the panelling.""" console.print(text, style=style) + + +def handle_input( + message: str = "", + check_type=False, + match: str = "", + err_message: str = "", + nmin=None, + nmax=None, + oob_error="", +): + match = re.compile(match + "$") + while True: + user_input = input(message + "\n> ").strip() + if re.match(match, user_input) is not None: + if check_type is not False: + try: + user_input = check_type(user_input) + if nmin is not None and user_input < nmin: + console.print("[red]" + oob_error) # Input too low failstate + continue + if nmax is not None and user_input > nmax: + console.print("[red]" + oob_error) # Input too high + continue + break # Successful type conversion and number in bounds + except ValueError: + console.print("[red]" + err_message) # Type conversion failed + continue + if nmin is not None and len(user_input) < nmin: # Check if string is long enough + console.print("[red]" + oob_error) + continue + if nmax is not None and len(user_input) > nmax: # Check if string is not too long + console.print("[red]" + oob_error) + continue + break + console.print("[red]" + err_message) + + return user_input diff --git a/utils/scripts/FileGrabber.ps1 b/utils/scripts/FileGrabber.ps1 deleted file mode 100644 index a820d2e..0000000 --- a/utils/scripts/FileGrabber.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -$envFile = Get-Content ".\.env.template" - -$envFile -split "=" | Where-Object {$_ -notmatch '\"'} | Set-Content ".\envVarsbefSpl.txt" -Get-Content ".\envVarsbefSpl.txt" | Where-Object {$_ -notmatch '\#'} | Set-Content ".\envVarsN.txt" -Get-Content ".\envVarsN.txt" | Where-Object {$_ -ne ''} | Set-Content ".\video_creation\data\envvars.txt" -Remove-Item ".\envVarsbefSpl.txt" -Remove-Item ".\envVarsN.txt" - -Write-Host $nowSplit diff --git a/utils/scripts/FileGrabberenv.ps1 b/utils/scripts/FileGrabberenv.ps1 deleted file mode 100644 index ffb021b..0000000 --- a/utils/scripts/FileGrabberenv.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -$envFile = Get-Content ".\.env" - -$envFile -split "=" | Where-Object {$_ -notmatch '\"'} | Set-Content ".\envVarsbefSpl.txt" -Get-Content ".\envVarsbefSpl.txt" | Where-Object {$_ -notmatch '\#'} | Set-Content ".\envVarsN.txt" -Get-Content ".\envVarsN.txt" | Where-Object {$_ -ne ''} | Set-Content ".\video_creation\data\envvars.txt" -Remove-Item ".\envVarsbefSpl.txt" -Remove-Item ".\envVarsN.txt" - -Write-Host $nowSplit From eef5ca32aaff327d39bcd5c8bd8fc1d5bb8326a7 Mon Sep 17 00:00:00 2001 From: CJthisis <99442347+CJthisis@users.noreply.github.com> Date: Tue, 21 Jun 2022 13:18:56 +0200 Subject: [PATCH 2/5] Sox and Path variables Many people seem to have problems with SoX and the associated path variables, this would (hopefully) fix the issue. --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 26b5814..c85b377 100644 --- a/README.md +++ b/README.md @@ -44,15 +44,18 @@ The only original thing being done is the editing and gathering of all materials 2b **Manual Install**: Rename `.env.template` to `.env` and replace all values with the appropriate fields. To get Reddit keys (**required**), visit [the Reddit Apps page.](https://www.reddit.com/prefs/apps) TL;DR set up an app that is a "script". Copy your keys into the `.env` file, along with whether your account uses two-factor authentication. 3. Install [SoX](https://sourceforge.net/projects/sox/files/sox/) + +4. Add the directory of your SoX installation to the Path system variables (see https://github.com/elebumm/RedditVideoMakerBot/issues/613) + and move the added directory all the way to the top of the list of directories. -4. Run `pip install -r requirements.txt` +5. Run `pip install -r requirements.txt` -5. Run `playwright install` and `playwright install-deps`. (if this fails try adding python -m to the front of the command) +6. Run `playwright install` and `playwright install-deps`. (if this fails try adding python -m to the front of the command) -6. Run `python main.py` (unless you chose automatic install, then the installer will automatically run main.py) +7. Run `python main.py` (unless you chose automatic install, then the installer will automatically run main.py) required\*\*), visit [the Reddit Apps page.](https://www.reddit.com/prefs/apps) TL;DR set up an app that is a "script". Copy your keys into the `.env` file, along with whether your account uses two-factor authentication. -7. Enjoy 😎 +8. Enjoy 😎 (Note if you got an error installing or running the bot try first rerunning the command with a three after the name e.g. python3 or pip3) ## Video From 629b74c5d476051a171bc07566aa712a6aa4ed05 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 21 Jun 2022 16:26:06 +0300 Subject: [PATCH 3/5] completely revampted .env.template syntax, shiny new checker, better formatting and more --- .env.template | 30 ++++- .github/workflows/codeql-analysis.yml | 97 +++++++-------- main.py | 13 +- utils/checker.py | 169 +++++++++++++++++++++----- utils/console.py | 5 +- 5 files changed, 220 insertions(+), 94 deletions(-) diff --git a/.env.template b/.env.template index 6a4923c..ed85764 100644 --- a/.env.template +++ b/.env.template @@ -1,24 +1,40 @@ REDDIT_CLIENT_ID="" #EXPLANATION the ID of your Reddit app of SCRIPT type +#RANGE 12:30 +#MATCH_REGEX [-a-zA-Z0-9._~+/]+=*$ +#OOB_ERROR The ID should be over 12 and under 30 characters, double check your input. REDDIT_CLIENT_SECRET="" #EXPLANATION the SECRET of your Reddit app of SCRIPT type +#RANGE 20:40 +#MATCH_REGEX [-a-zA-Z0-9._~+/]+=*$ +#OOB_ERROR The secret should be over 20 and under 40 characters, double check your input. REDDIT_USERNAME="" #EXPLANATION the username of your reddit account +#RANGE 3:20 +#MATCH_REGEX [_0-9a-zA-Z]+$ +#OOB_ERROR A username HAS to be between 3 and 20 characters REDDIT_PASSWORD="" #EXPLANATION the password of your reddit account +#RANGE 8:None +#OOB_ERROR Password too short #OPTIONAL RANDOM_THREAD="no" -#EXPLANATION If set to no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: "no" +# If set to no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: "no" REDDIT_2FA="" #MATCH_REGEX ^(yes|no) #EXPLANATION Whether you have Reddit 2FA enabled, Valid options are "yes" and "no" SUBREDDIT="AskReddit" +#EXPLANATION what subreddit to pull posts from, the name of the sub, not the URL +#RANGE 3:20 +#MATCH_REGEX [_0-9a-zA-Z]+$ +#OOB_ERROR A subreddit name HAS to be between 3 and 20 characters + ALLOW_NSFW="False" #EXPLANATION Whether to allow NSFW content, True or False #MATCH_REGEX ^(True|False)$ @@ -29,16 +45,22 @@ POST_ID="" THEME="LIGHT" #EXPLANATION sets the Reddit theme, either LIGHT or DARK +#MATCH_REGEX ^(dark|light|DARK|LIGHT)$ TIMES_TO_RUN="" #EXPLANATION used if you want to run multiple times. set to an int e.g. 4 or 29 and leave blank for 1 MAX_COMMENT_LENGTH="500" #EXPLANATION max number of characters a comment can have. default is 500 +#RANGE 0:10000 +#MATCH_TYPE int +#OOB_ERROR the max comment length should be between 0 and 10000 -#OPTIONAL OPACITY="1" -#EXPLANATION sets the opacity of the comments, Range is 0 -> 1 recommended around 0.8-0.9 +#EXPLANATION Sets the opacity of the comments when overlayed over the background +#RANGE 0:1 +#MATCH_TYPE float +#OOB_ERROR The opacity HAS to be between 0 and 1 # see different voice options: todo: add docs VOICE="Matthew" @@ -49,4 +71,4 @@ TTsChoice="polly" #OPTIONAL STORYMODE="False" -#EXPLANATION IN-PROGRESS - not yet implemented +# IN-PROGRESS - not yet implemented diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 835b4fb..238dad4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,3 +1,4 @@ + # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # @@ -12,61 +13,61 @@ name: "CodeQL" on: - push: - branches: [ "master" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "master" ] - schedule: - - cron: '16 14 * * 3' + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '16 14 * * 3' jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 - strategy: - fail-fast: false - matrix: - language: [ 'python' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. - steps: - - name: Checkout repository - uses: actions/checkout@v3 + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/main.py b/main.py index 75863d9..9998f44 100755 --- a/main.py +++ b/main.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -import time from subprocess import Popen from dotenv import load_dotenv @@ -26,23 +25,16 @@ print( ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ """ ) -load_dotenv() # Modified by JasonLovesDoggo print_markdown( "### Thanks for using this tool! [Feel free to contribute to this project on GitHub!](https://lewismenelaws.com) If you have any questions, feel free to reach out to me on Twitter or submit a GitHub issue. You can find solutions to many common problems in the [Documentation](https://luka-hietala.gitbook.io/documentation-for-the-reddit-bot/)" ) -client_id = getenv("REDDIT_CLIENT_ID") -client_secret = getenv("REDDIT_CLIENT_SECRET") -username = getenv("REDDIT_USERNAME") -password = getenv("REDDIT_PASSWORD") -reddit2fa = getenv("REDDIT_2FA") - - def main(): if check_env() is not True: exit() + load_dotenv() cleanup() def get_obj(): @@ -58,8 +50,7 @@ def main(): def run_many(times): - for x in range(times): - x = x + 1 + for x in range(1, times + 1): print_step( f'on the {x}{("st" if x == 1 else ("nd" if x == 2 else ("rd" if x == 3 else "th")))} iteration of {times}' ) # correct 1st 2nd 3rd 4th 5th.... diff --git a/utils/checker.py b/utils/checker.py index 75617b1..5735bda 100755 --- a/utils/checker.py +++ b/utils/checker.py @@ -1,75 +1,184 @@ #!/usr/bin/env python import os from rich.console import Console +from rich.table import Table +from rich import box import re import dotenv from utils.console import handle_input console = Console() -success = True - def check_env() -> bool: if not os.path.exists(".env.template"): console.print("[red]Couldn't find .env.template. Unable to check variables.") - return False + return True + if not os.path.exists(".env"): + console.print("[red]Couldn't find the .env file, creating one now.") + with open(".env", "x") as file: + file.write("") with open(".env.template", "r") as template: # req_envs = [env.split("=")[0] for env in template.readlines() if "=" in env] matching = {} explanations = {} + bounds = {} + types = {} + oob_errors = {} req_envs = [] var_optional = False for line in template.readlines(): - if "=" in line and var_optional is not True: + if line.startswith("#") is not True and "=" in line and var_optional is not True: req_envs.append(line.split("=")[0]) elif "#OPTIONAL" in line: var_optional = True elif line.startswith("#MATCH_REGEX "): matching[req_envs[-1]] = line.removeprefix("#MATCH_REGEX ")[:-1] var_optional = False + elif line.startswith("#OOB_ERROR "): + oob_errors[req_envs[-1]] = line.removeprefix("#OOB_ERROR ")[:-1] + var_optional = False + elif line.startswith("#RANGE "): + bounds[req_envs[-1]] = tuple( + map( + lambda x: float(x) if x != "None" else None, + line.removeprefix("#RANGE ")[:-1].split(":"), + ) + ) + var_optional = False + elif line.startswith("#MATCH_TYPE "): + types[req_envs[-1]] = eval(line.removeprefix("#MATCH_TYPE ")[:-1].split()[0]) + var_optional = False elif line.startswith("#EXPLANATION "): explanations[req_envs[-1]] = line.removeprefix("#EXPLANATION ")[:-1] var_optional = False else: var_optional = False - missing = [] - incorrect = [] + missing = set() + incorrect = set() dotenv.load_dotenv() for env in req_envs: value = os.getenv(env) if value is None: - missing.append(env) + missing.add(env) continue if env in matching.keys(): - env, re.match(matching[env], value) is None and incorrect.append(env) - if len(missing): - for i in range(len(missing)): + re.match(matching[env], value) is None and incorrect.add(env) + if env in bounds.keys() and env not in types.keys(): + len(value) >= bounds[env][0] or ( + len(bounds[env]) > 1 and bounds[env][1] >= len(value) + ) or incorrect.add(env) + continue + if env in types.keys(): try: - missing[i] = missing[i] + ": " + explanations[missing[i]] - except KeyError: - pass - console.print( - f"[red]{'These variables are'*(len(missing) > 1) or 'This variable is'} non-optional and missing: \n\n" - + "\n\n".join(missing) + temp = types[env](value) + if env in bounds.keys(): + (bounds[env][0] <= temp or incorrect.add(env)) and len(bounds[env]) > 1 and ( + bounds[env][1] >= temp or incorrect.add(env) + ) + except ValueError: + incorrect.add(env) + + if len(missing): + table = Table( + title="Missing variables", + highlight=True, + show_lines=True, + box=box.ROUNDED, + border_style="#414868", + header_style="#C0CAF5 bold", + title_justify="left", + title_style="#C0CAF5 bold", ) + table.add_column("Variable", justify="left", style="#7AA2F7 bold", no_wrap=True) + table.add_column("Explanation", justify="left", style="#BB9AF7", no_wrap=False) + table.add_column("Type", justify="center", style="#F7768E", no_wrap=True) + table.add_column("Min", justify="right", style="#F7768E", no_wrap=True) + table.add_column("Max", justify="left", style="#F7768E", no_wrap=True) + for env in missing: + table.add_row( + env, + explanations[env] if env in explanations.keys() else "No explanation given", + str(types[env].__name__) if env in types.keys() else "str", + str(bounds[env][0]) if env in bounds.keys() and bounds[env][1] is not None else "", + str(bounds[env][1]) + if env in bounds.keys() and len(bounds[env]) > 1 and bounds[env][1] is not None + else "", + ) + console.print(table) success = False if len(incorrect): - console.print( - f"[red]{'These variables are'*(len(incorrect) > 1) or 'This variable is'} set incorrectly: " - + "\n".join(incorrect) + table = Table( + title="Incorrect variables", + highlight=True, + show_lines=True, + box=box.ROUNDED, + border_style="#414868", + header_style="#C0CAF5 bold", + title_justify="left", + title_style="#C0CAF5 bold", ) - success = False - # if success is True: - # return True - # console.print("[green]Do you want to enter the missing variables by hand(y/n)") - # if not input().casefold().startswith("y"): - # console.print("[red]Aborting: Unresolved missing variables") - # return success - # with open(".env", "a") as env_file: - # for env in missing: - # pass - return success + table.add_column("Variable", justify="left", style="#7AA2F7 bold", no_wrap=True) + table.add_column("Current value", justify="left", style="#F7768E", no_wrap=False) + table.add_column("Explanation", justify="left", style="#BB9AF7", no_wrap=False) + table.add_column("Type", justify="center", style="#F7768E", no_wrap=True) + table.add_column("Min", justify="right", style="#F7768E", no_wrap=True) + table.add_column("Max", justify="left", style="#F7768E", no_wrap=True) + for env in incorrect: + table.add_row( + env, + os.getenv(env), + explanations[env] if env in explanations.keys() else "No explanation given", + str(types[env].__name__) if env in types.keys() else "str", + str(bounds[env][0]) if env in bounds.keys() else "None", + str(bounds[env][1]) if env in bounds.keys() and len(bounds[env]) > 1 else "None", + ) + missing.add(env) + console.print(table) + if success is True: + return True + console.print( + "[green]Do you want to automatically overwrite incorrect variables and add the missing variables? (y/n)" + ) + if not input().casefold().startswith("y"): + console.print("[red]Aborting: Unresolved missing variables") + return False + if len(incorrect): + with open(".env", "r+") as env_file: + lines = [] + for line in env_file.readlines(): + line.split("=")[0].strip() not in incorrect and lines.append(line) + env_file.seek(0) + env_file.write("\n".join(lines)) + env_file.truncate() + console.print("[green]Successfully removed incorrectly set variables from .env") + with open(".env", "a") as env_file: + for env in missing: + env_file.write( + env + + "=" + + ('"' if env not in types.keys() else "") + + str( + handle_input( + "[#F7768E bold]" + env + "[#C0CAF5 bold]=", + types[env] if env in types.keys() else False, + matching[env] if env in matching.keys() else ".*", + explanations[env] + if env in explanations.keys() + else "Incorrect input. Try again.", + bounds[env][0] if env in bounds.keys() else None, + bounds[env][1] if env in bounds.keys() and len(bounds[env]) > 1 else None, + oob_errors[env] if env in oob_errors.keys() else "Input too long/short.", + extra_info="[#C0CAF5 bold]âŽļ " + + ( + explanations[env] if env in explanations.keys() else "No info available" + ), + ) + ) + + ('"' if env not in types.keys() else "") + + "\n" + ) + return True if __name__ == "__main__": diff --git a/utils/console.py b/utils/console.py index 83736ac..905b2aa 100644 --- a/utils/console.py +++ b/utils/console.py @@ -36,10 +36,13 @@ def handle_input( nmin=None, nmax=None, oob_error="", + extra_info="", ): match = re.compile(match + "$") + console.print(extra_info, no_wrap=True) while True: - user_input = input(message + "\n> ").strip() + console.print(message, end="") + user_input = input("").strip() if re.match(match, user_input) is not None: if check_type is not False: try: From dd56fc87dfec93ddbc2a4f6033b5a34589b09641 Mon Sep 17 00:00:00 2001 From: CordlessCoder <42666308+CordlessCoder@users.noreply.github.com> Date: Tue, 21 Jun 2022 17:41:26 +0300 Subject: [PATCH 4/5] Added example support --- utils/checker.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/utils/checker.py b/utils/checker.py index 5735bda..07b1e15 100755 --- a/utils/checker.py +++ b/utils/checker.py @@ -18,6 +18,7 @@ def check_env() -> bool: console.print("[red]Couldn't find the .env file, creating one now.") with open(".env", "x") as file: file.write("") + success = True with open(".env.template", "r") as template: # req_envs = [env.split("=")[0] for env in template.readlines() if "=" in env] matching = {} @@ -25,11 +26,14 @@ def check_env() -> bool: bounds = {} types = {} oob_errors = {} + examples = {} req_envs = [] var_optional = False for line in template.readlines(): if line.startswith("#") is not True and "=" in line and var_optional is not True: req_envs.append(line.split("=")[0]) + if "#" in line: + examples[line.split("=")[0]] = "#".join(line.split("#")[1:]).strip() elif "#OPTIONAL" in line: var_optional = True elif line.startswith("#MATCH_REGEX "): @@ -92,14 +96,14 @@ def check_env() -> bool: ) table.add_column("Variable", justify="left", style="#7AA2F7 bold", no_wrap=True) table.add_column("Explanation", justify="left", style="#BB9AF7", no_wrap=False) - table.add_column("Type", justify="center", style="#F7768E", no_wrap=True) + table.add_column("Example", justify="center", style="#F7768E", no_wrap=True) table.add_column("Min", justify="right", style="#F7768E", no_wrap=True) table.add_column("Max", justify="left", style="#F7768E", no_wrap=True) for env in missing: table.add_row( env, explanations[env] if env in explanations.keys() else "No explanation given", - str(types[env].__name__) if env in types.keys() else "str", + examples[env] if env in examples.keys() else "", str(bounds[env][0]) if env in bounds.keys() and bounds[env][1] is not None else "", str(bounds[env][1]) if env in bounds.keys() and len(bounds[env]) > 1 and bounds[env][1] is not None @@ -121,7 +125,7 @@ def check_env() -> bool: table.add_column("Variable", justify="left", style="#7AA2F7 bold", no_wrap=True) table.add_column("Current value", justify="left", style="#F7768E", no_wrap=False) table.add_column("Explanation", justify="left", style="#BB9AF7", no_wrap=False) - table.add_column("Type", justify="center", style="#F7768E", no_wrap=True) + table.add_column("Example", justify="center", style="#F7768E", no_wrap=True) table.add_column("Min", justify="right", style="#F7768E", no_wrap=True) table.add_column("Max", justify="left", style="#F7768E", no_wrap=True) for env in incorrect: @@ -135,6 +139,7 @@ def check_env() -> bool: ) missing.add(env) console.print(table) + success = False if success is True: return True console.print( From eb0094a2ec52a9f3eecdd903de6eeeb73e562366 Mon Sep 17 00:00:00 2001 From: CordlessCoder <42666308+CordlessCoder@users.noreply.github.com> Date: Tue, 21 Jun 2022 17:41:45 +0300 Subject: [PATCH 5/5] Update .env.template --- .env.template | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.env.template b/.env.template index ed85764..fcfc739 100644 --- a/.env.template +++ b/.env.template @@ -1,22 +1,22 @@ -REDDIT_CLIENT_ID="" +REDDIT_CLIENT_ID="" #fFAGRNJru1FTz70BzhT3Zg #EXPLANATION the ID of your Reddit app of SCRIPT type #RANGE 12:30 #MATCH_REGEX [-a-zA-Z0-9._~+/]+=*$ #OOB_ERROR The ID should be over 12 and under 30 characters, double check your input. -REDDIT_CLIENT_SECRET="" +REDDIT_CLIENT_SECRET="" #fFAGRNJru1FTz70BzhT3Zg #EXPLANATION the SECRET of your Reddit app of SCRIPT type #RANGE 20:40 #MATCH_REGEX [-a-zA-Z0-9._~+/]+=*$ #OOB_ERROR The secret should be over 20 and under 40 characters, double check your input. -REDDIT_USERNAME="" +REDDIT_USERNAME="" #asdfghjkl #EXPLANATION the username of your reddit account #RANGE 3:20 #MATCH_REGEX [_0-9a-zA-Z]+$ #OOB_ERROR A username HAS to be between 3 and 20 characters -REDDIT_PASSWORD="" +REDDIT_PASSWORD="" #fFAGRNJru1FTz70BzhT3Zg #EXPLANATION the password of your reddit account #RANGE 8:None #OOB_ERROR Password too short @@ -25,7 +25,7 @@ REDDIT_PASSWORD="" RANDOM_THREAD="no" # If set to no, it will ask you a thread link to extract the thread, if yes it will randomize it. Default: "no" -REDDIT_2FA="" +REDDIT_2FA="" #no #MATCH_REGEX ^(yes|no) #EXPLANATION Whether you have Reddit 2FA enabled, Valid options are "yes" and "no" @@ -43,30 +43,30 @@ POST_ID="" #MATCH_REGEX ^((?!://|://).)*$ #EXPLANATION Used if you want to use a specific post. example of one is urdtfx -THEME="LIGHT" +THEME="LIGHT" #dark #EXPLANATION sets the Reddit theme, either LIGHT or DARK #MATCH_REGEX ^(dark|light|DARK|LIGHT)$ -TIMES_TO_RUN="" +TIMES_TO_RUN="" #2 #EXPLANATION used if you want to run multiple times. set to an int e.g. 4 or 29 and leave blank for 1 -MAX_COMMENT_LENGTH="500" +MAX_COMMENT_LENGTH="500" #500 #EXPLANATION max number of characters a comment can have. default is 500 #RANGE 0:10000 #MATCH_TYPE int #OOB_ERROR the max comment length should be between 0 and 10000 -OPACITY="1" +OPACITY="1" #.8 #EXPLANATION Sets the opacity of the comments when overlayed over the background #RANGE 0:1 #MATCH_TYPE float #OOB_ERROR The opacity HAS to be between 0 and 1 # see different voice options: todo: add docs -VOICE="Matthew" -#EXPLANATION sets the voice the TTS uses, e.g. en_us_002 +VOICE="Matthew" #en_us_002 +#EXPLANATION sets the voice the TTS uses -TTsChoice="polly" +TTsChoice="polly" #polly #EXPLANATION the backend used for TTS, default is polly #OPTIONAL