From d62bb460a601d9616cde702907249d912442c622 Mon Sep 17 00:00:00 2001 From: Antoine Caron Date: Tue, 28 May 2024 14:49:08 +0200 Subject: [PATCH 01/13] ci(reviewBot): implement review bot in python to verify reviews --- .github/workflows/docs-review-bot.yml | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/docs-review-bot.yml diff --git a/.github/workflows/docs-review-bot.yml b/.github/workflows/docs-review-bot.yml new file mode 100644 index 0000000000..14909de4e9 --- /dev/null +++ b/.github/workflows/docs-review-bot.yml @@ -0,0 +1,40 @@ +name: 📖 Review Documentation Bot + +on: + workflow_dispatch: + inputs: + dryRun: + description: 'Run the bot without sending notifications' + required: false + default: 'false' + + schedule: + - cron: '0 8 * * 1' + +jobs: + review-docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run Documentation Review Bot in dry run mode + if: github.event_name == 'workflow_dispatch' && github.event.inputs.dryRun == 'true' + run: | + python path/to/your_review_bot_script.py --dryRun + + - name: Run Documentation Review Bot in normal mode + if: github.event_name != 'workflow_dispatch' || github.event.inputs.dryRun != 'true' + run: | + python path/to/your_review_bot_script.py \ No newline at end of file From 2fef9cbe5cdde79a2cafe2f65c0c28ea467adc7b Mon Sep 17 00:00:00 2001 From: Antoine Caron Date: Tue, 28 May 2024 14:51:21 +0200 Subject: [PATCH 02/13] ci: add debug mode during dev of workflow --- .github/workflows/docs-review-bot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs-review-bot.yml b/.github/workflows/docs-review-bot.yml index 14909de4e9..72e3570d3e 100644 --- a/.github/workflows/docs-review-bot.yml +++ b/.github/workflows/docs-review-bot.yml @@ -11,6 +11,9 @@ on: schedule: - cron: '0 8 * * 1' + # TODO: Remove this before merge + pull_request: + jobs: review-docs: runs-on: ubuntu-latest From 5b5b567d44d53af5b705eb50b273400d4de2b30e Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 15:26:44 +0200 Subject: [PATCH 03/13] feat(docs): review bot foundations --- .github/workflows/docs-review-bot.yml | 6 +- bin/check-review-dates.py | 122 ++++++++++++++++++++++++++ requirements.txt | 1 + 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 bin/check-review-dates.py create mode 100644 requirements.txt diff --git a/.github/workflows/docs-review-bot.yml b/.github/workflows/docs-review-bot.yml index 72e3570d3e..ebd11bc68c 100644 --- a/.github/workflows/docs-review-bot.yml +++ b/.github/workflows/docs-review-bot.yml @@ -34,10 +34,12 @@ jobs: - name: Run Documentation Review Bot in dry run mode if: github.event_name == 'workflow_dispatch' && github.event.inputs.dryRun == 'true' + env: + DRY_RUN: true run: | - python path/to/your_review_bot_script.py --dryRun + python bin/check-review-dates.py - name: Run Documentation Review Bot in normal mode if: github.event_name != 'workflow_dispatch' || github.event.inputs.dryRun != 'true' run: | - python path/to/your_review_bot_script.py \ No newline at end of file + python bin/check-review-dates.py \ No newline at end of file diff --git a/bin/check-review-dates.py b/bin/check-review-dates.py new file mode 100644 index 0000000000..0c96158421 --- /dev/null +++ b/bin/check-review-dates.py @@ -0,0 +1,122 @@ +import os +import logging +from slack_sdk import WebClient +from datetime import timedelta, date, datetime + +DEFAULT_VAL_FREQ = 6 + +def convert_to_date_and_delta(val_date, val_freq): + logging.info("Converts validation date string to datetime and validation frequency string (months) to timedelta.") + try: + val_date_conv = datetime.strptime(val_date.rstrip('\n'), '%Y-%m-%d').date() + val_freq_conv = timedelta(days=int(val_freq) * 30.4) + return val_date_conv, val_freq_conv + except ValueError: + # handles the case where validation format is incorrect + return None, None + +def needs_review(val_date, val_freq): + logging.info("Returns true if doc needs to be reviewed, based on val date and frequency") + val_date_conv, val_freq_conv = convert_to_date_and_delta(val_date, val_freq) + if val_date_conv is None or val_freq_conv is None: + return False + today = date.today() + # calculate how long since doc was reviewed, in days + delta = today - val_date_conv + # return true or false depending on evaluation of data + return delta >= val_freq_conv + +def extract_metadata(filepath): + logging.info("Extracts validation date and validation frequency from a document.") + with open(filepath) as doc: + meta_limiters = 0 + has_val_date = False + val_freq = DEFAULT_VAL_FREQ + + for line in doc: + if "validation: " in line: + val_date = line.split(": ", 1)[1].strip() + has_val_date = True + if "validation-frequency:" in line: + val_freq = line.split(": ", 1)[1].strip() + if "---" in line: + meta_limiters += 1 + # once two --- strings are found, it is the end of the meta section, stop checking file + if meta_limiters >= 2: + break + + return has_val_date, val_date if has_val_date else None, val_freq + +def process_files(directory): + logging.info("Processes files in the content directory to check for those needing review.") + docs_to_review=[] + for subdir, dirs, files in os.walk(directory): + for file in files: + filepath = os.path.join(subdir, file) + if filepath.endswith(".mdx"): + has_val_date, val_date, val_freq = extract_metadata(filepath) + if has_val_date and needs_review(val_date, val_freq): + docs_to_review.append(filepath) + return docs_to_review + +def get_doc_cat_name(filepath): + logging.info("Returns a document-to-review's category and tidied-up filepath, based on its raw filepath.") + trimmed_filepath = filepath[11:-4] + filepath_list = trimmed_filepath.split("/") + + if filepath_list[0] == "tutorials": + category = filepath_list[0] + elif filepath_list[0] == "faq": + category = filepath_list[1] + else: + category = ' '.join(filepath_list[0:2]) + + return category, trimmed_filepath + +def organize_docs_by_category(docs_to_review): + logging.info("Organizes docs to review by category into a dictionary.") + dict_by_cat = {} + + for filepath in docs_to_review: + category, trimmed_filepath = get_doc_cat_name(filepath) + + if category not in dict_by_cat: + dict_by_cat[category] = [trimmed_filepath] + else: + dict_by_cat[category].append(trimmed_filepath) + + # sort the dictionary alphabetically by category + dict_by_cat_sorted = {key: value for key, value in sorted(dict_by_cat.items())} + + return dict_by_cat_sorted + +def prep_message(docs_to_review_by_cat): + logging.info("Prepares the message to sent to the Slack channel, containing the docs to review") + message = ":wave: Hi doc team, here are some docs to review: \n \n" + + for key in docs_to_review_by_cat: + message += "*" + key.title() + "*" + "\n" + for doc in docs_to_review_by_cat[key]: + message += doc + "\n" + message += "\n" + logging.info(message) + return(message) + +def send_message(message): + logging.info("Sends the message containing docs to review to the Slack channel") + client = WebClient(token=os.environ['SLACK_BOT_TOKEN']) + client.chat_postMessage( + channel = "#review-doc", + text = message, + username = "DocReviewBot" + ) + +def main(): + docs_to_review = process_files(".") + docs_to_review_by_cat = organize_docs_by_category(docs_to_review) + message = prep_message(docs_to_review_by_cat) + if not os.environ["DRY_RUN"]: + send_message(message) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..2c3c16c016 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +slack_sdk == 3.27.2 \ No newline at end of file From 9d477b24092fff4ea931b7493b967638e8430034 Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 15:28:59 +0200 Subject: [PATCH 04/13] fix(docs): fix dry run --- bin/check-review-dates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/check-review-dates.py b/bin/check-review-dates.py index 0c96158421..7f3859de35 100644 --- a/bin/check-review-dates.py +++ b/bin/check-review-dates.py @@ -115,7 +115,7 @@ def main(): docs_to_review = process_files(".") docs_to_review_by_cat = organize_docs_by_category(docs_to_review) message = prep_message(docs_to_review_by_cat) - if not os.environ["DRY_RUN"]: + if os.environ["DRY_RUN"] != "true": send_message(message) if __name__ == "__main__": From e7b8a4b9ca3f38d455d535a92a5ab95aa6dbc15e Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 15:31:52 +0200 Subject: [PATCH 05/13] fix(docs): dryrun fix --- bin/check-review-dates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/check-review-dates.py b/bin/check-review-dates.py index 7f3859de35..b4c303e221 100644 --- a/bin/check-review-dates.py +++ b/bin/check-review-dates.py @@ -115,7 +115,7 @@ def main(): docs_to_review = process_files(".") docs_to_review_by_cat = organize_docs_by_category(docs_to_review) message = prep_message(docs_to_review_by_cat) - if os.environ["DRY_RUN"] != "true": + if os.environ.get("DRY_RUN") != "true": send_message(message) if __name__ == "__main__": From 240495a5d721243b2cb62459f44b8b808ae22135 Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 15:34:43 +0200 Subject: [PATCH 06/13] fix(docs): fix slack bot token --- .github/workflows/docs-review-bot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs-review-bot.yml b/.github/workflows/docs-review-bot.yml index ebd11bc68c..18668e09f8 100644 --- a/.github/workflows/docs-review-bot.yml +++ b/.github/workflows/docs-review-bot.yml @@ -14,6 +14,9 @@ on: # TODO: Remove this before merge pull_request: +env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + jobs: review-docs: runs-on: ubuntu-latest From 471886dc3c11246c15d8d5fb09852fba2b619734 Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 15:39:07 +0200 Subject: [PATCH 07/13] fix(docs): add logging --- .github/workflows/docs-review-bot.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs-review-bot.yml b/.github/workflows/docs-review-bot.yml index 18668e09f8..c69689a1b6 100644 --- a/.github/workflows/docs-review-bot.yml +++ b/.github/workflows/docs-review-bot.yml @@ -11,11 +11,12 @@ on: schedule: - cron: '0 8 * * 1' - # TODO: Remove this before merge + # TODO: Remove these two lines before merge pull_request: env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + DRY_RUN: true jobs: review-docs: @@ -40,9 +41,9 @@ jobs: env: DRY_RUN: true run: | - python bin/check-review-dates.py + python bin/check-review-dates.py --log=INFO - name: Run Documentation Review Bot in normal mode if: github.event_name != 'workflow_dispatch' || github.event.inputs.dryRun != 'true' run: | - python bin/check-review-dates.py \ No newline at end of file + python bin/check-review-dates.py --log=INFO \ No newline at end of file From 0f94019ba8ffbbbed40499478811837982e4b27f Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 15:41:59 +0200 Subject: [PATCH 08/13] fix(docs): logging --- .github/workflows/docs-review-bot.yml | 4 ++-- bin/check-review-dates.py | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docs-review-bot.yml b/.github/workflows/docs-review-bot.yml index c69689a1b6..681dec4bdd 100644 --- a/.github/workflows/docs-review-bot.yml +++ b/.github/workflows/docs-review-bot.yml @@ -41,9 +41,9 @@ jobs: env: DRY_RUN: true run: | - python bin/check-review-dates.py --log=INFO + python bin/check-review-dates.py - name: Run Documentation Review Bot in normal mode if: github.event_name != 'workflow_dispatch' || github.event.inputs.dryRun != 'true' run: | - python bin/check-review-dates.py --log=INFO \ No newline at end of file + python bin/check-review-dates.py \ No newline at end of file diff --git a/bin/check-review-dates.py b/bin/check-review-dates.py index b4c303e221..48c158bb2c 100644 --- a/bin/check-review-dates.py +++ b/bin/check-review-dates.py @@ -6,7 +6,7 @@ DEFAULT_VAL_FREQ = 6 def convert_to_date_and_delta(val_date, val_freq): - logging.info("Converts validation date string to datetime and validation frequency string (months) to timedelta.") + print("Converts validation date string to datetime and validation frequency string (months) to timedelta.") try: val_date_conv = datetime.strptime(val_date.rstrip('\n'), '%Y-%m-%d').date() val_freq_conv = timedelta(days=int(val_freq) * 30.4) @@ -16,7 +16,7 @@ def convert_to_date_and_delta(val_date, val_freq): return None, None def needs_review(val_date, val_freq): - logging.info("Returns true if doc needs to be reviewed, based on val date and frequency") + print("Returns true if doc needs to be reviewed, based on val date and frequency") val_date_conv, val_freq_conv = convert_to_date_and_delta(val_date, val_freq) if val_date_conv is None or val_freq_conv is None: return False @@ -27,7 +27,7 @@ def needs_review(val_date, val_freq): return delta >= val_freq_conv def extract_metadata(filepath): - logging.info("Extracts validation date and validation frequency from a document.") + print("Extracts validation date and validation frequency from a document.") with open(filepath) as doc: meta_limiters = 0 has_val_date = False @@ -48,7 +48,7 @@ def extract_metadata(filepath): return has_val_date, val_date if has_val_date else None, val_freq def process_files(directory): - logging.info("Processes files in the content directory to check for those needing review.") + print("Processes files in the content directory to check for those needing review.") docs_to_review=[] for subdir, dirs, files in os.walk(directory): for file in files: @@ -60,7 +60,7 @@ def process_files(directory): return docs_to_review def get_doc_cat_name(filepath): - logging.info("Returns a document-to-review's category and tidied-up filepath, based on its raw filepath.") + print("Returns a document-to-review's category and tidied-up filepath, based on its raw filepath.") trimmed_filepath = filepath[11:-4] filepath_list = trimmed_filepath.split("/") @@ -74,7 +74,7 @@ def get_doc_cat_name(filepath): return category, trimmed_filepath def organize_docs_by_category(docs_to_review): - logging.info("Organizes docs to review by category into a dictionary.") + print("Organizes docs to review by category into a dictionary.") dict_by_cat = {} for filepath in docs_to_review: @@ -91,7 +91,7 @@ def organize_docs_by_category(docs_to_review): return dict_by_cat_sorted def prep_message(docs_to_review_by_cat): - logging.info("Prepares the message to sent to the Slack channel, containing the docs to review") + print("Prepares the message to sent to the Slack channel, containing the docs to review") message = ":wave: Hi doc team, here are some docs to review: \n \n" for key in docs_to_review_by_cat: @@ -99,11 +99,11 @@ def prep_message(docs_to_review_by_cat): for doc in docs_to_review_by_cat[key]: message += doc + "\n" message += "\n" - logging.info(message) + print(message) return(message) def send_message(message): - logging.info("Sends the message containing docs to review to the Slack channel") + print("Sends the message containing docs to review to the Slack channel") client = WebClient(token=os.environ['SLACK_BOT_TOKEN']) client.chat_postMessage( channel = "#review-doc", From f3d22c1ff34169cde9090b89eae2598868e15af0 Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 16:29:42 +0200 Subject: [PATCH 09/13] fix(docs): fix logging and filepath --- bin/check-review-dates.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/bin/check-review-dates.py b/bin/check-review-dates.py index 48c158bb2c..82f87620ef 100644 --- a/bin/check-review-dates.py +++ b/bin/check-review-dates.py @@ -6,7 +6,7 @@ DEFAULT_VAL_FREQ = 6 def convert_to_date_and_delta(val_date, val_freq): - print("Converts validation date string to datetime and validation frequency string (months) to timedelta.") + "Converts validation date string to datetime and validation frequency string (months) to timedelta." try: val_date_conv = datetime.strptime(val_date.rstrip('\n'), '%Y-%m-%d').date() val_freq_conv = timedelta(days=int(val_freq) * 30.4) @@ -16,7 +16,7 @@ def convert_to_date_and_delta(val_date, val_freq): return None, None def needs_review(val_date, val_freq): - print("Returns true if doc needs to be reviewed, based on val date and frequency") + "Returns true if doc needs to be reviewed, based on val date and frequency" val_date_conv, val_freq_conv = convert_to_date_and_delta(val_date, val_freq) if val_date_conv is None or val_freq_conv is None: return False @@ -27,7 +27,7 @@ def needs_review(val_date, val_freq): return delta >= val_freq_conv def extract_metadata(filepath): - print("Extracts validation date and validation frequency from a document.") + "Extracts validation date and validation frequency from a document." with open(filepath) as doc: meta_limiters = 0 has_val_date = False @@ -48,7 +48,8 @@ def extract_metadata(filepath): return has_val_date, val_date if has_val_date else None, val_freq def process_files(directory): - print("Processes files in the content directory to check for those needing review.") + "Processes files in the content directory to check for those needing review." + print("Processing files to check for those needing review") docs_to_review=[] for subdir, dirs, files in os.walk(directory): for file in files: @@ -60,8 +61,8 @@ def process_files(directory): return docs_to_review def get_doc_cat_name(filepath): - print("Returns a document-to-review's category and tidied-up filepath, based on its raw filepath.") - trimmed_filepath = filepath[11:-4] + "Returns a document-to-review's category and tidied-up filepath, based on its raw filepath." + trimmed_filepath = filepath[3:-4] filepath_list = trimmed_filepath.split("/") if filepath_list[0] == "tutorials": @@ -74,7 +75,8 @@ def get_doc_cat_name(filepath): return category, trimmed_filepath def organize_docs_by_category(docs_to_review): - print("Organizes docs to review by category into a dictionary.") + "Organizes docs to review by category into a dictionary." + print("Organizing docs by category") dict_by_cat = {} for filepath in docs_to_review: @@ -91,7 +93,8 @@ def organize_docs_by_category(docs_to_review): return dict_by_cat_sorted def prep_message(docs_to_review_by_cat): - print("Prepares the message to sent to the Slack channel, containing the docs to review") + "Prepares the message to sent to the Slack channel, containing the docs to review" + print("Preparing message") message = ":wave: Hi doc team, here are some docs to review: \n \n" for key in docs_to_review_by_cat: @@ -103,7 +106,8 @@ def prep_message(docs_to_review_by_cat): return(message) def send_message(message): - print("Sends the message containing docs to review to the Slack channel") + "Sends the message containing docs to review to the Slack channel" + print("Sending message") client = WebClient(token=os.environ['SLACK_BOT_TOKEN']) client.chat_postMessage( channel = "#review-doc", @@ -115,8 +119,8 @@ def main(): docs_to_review = process_files(".") docs_to_review_by_cat = organize_docs_by_category(docs_to_review) message = prep_message(docs_to_review_by_cat) - if os.environ.get("DRY_RUN") != "true": - send_message(message) + # if os.environ.get("DRY_RUN") != "true": + # send_message(message) if __name__ == "__main__": main() \ No newline at end of file From 4f4cd3079a1cee5fd6d4298f6d52bd1f05654164 Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 16:31:46 +0200 Subject: [PATCH 10/13] fix(docs): troubleshoot filepath --- bin/check-review-dates.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/check-review-dates.py b/bin/check-review-dates.py index 82f87620ef..e028daf540 100644 --- a/bin/check-review-dates.py +++ b/bin/check-review-dates.py @@ -62,7 +62,9 @@ def process_files(directory): def get_doc_cat_name(filepath): "Returns a document-to-review's category and tidied-up filepath, based on its raw filepath." + print(filepath) trimmed_filepath = filepath[3:-4] + print(trimmed_filepath) filepath_list = trimmed_filepath.split("/") if filepath_list[0] == "tutorials": From 85dd5f97997e11274476b974adce33fde3e468c0 Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 16:33:37 +0200 Subject: [PATCH 11/13] fix(docs): filepath fixed --- bin/check-review-dates.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bin/check-review-dates.py b/bin/check-review-dates.py index e028daf540..582ce64b28 100644 --- a/bin/check-review-dates.py +++ b/bin/check-review-dates.py @@ -62,9 +62,7 @@ def process_files(directory): def get_doc_cat_name(filepath): "Returns a document-to-review's category and tidied-up filepath, based on its raw filepath." - print(filepath) - trimmed_filepath = filepath[3:-4] - print(trimmed_filepath) + trimmed_filepath = filepath[2:-4] filepath_list = trimmed_filepath.split("/") if filepath_list[0] == "tutorials": From 7c2b96ae971119f8e3724c6fefb057f7e878458b Mon Sep 17 00:00:00 2001 From: Rowena Date: Tue, 28 May 2024 17:00:17 +0200 Subject: [PATCH 12/13] fix(docs): final modifs --- .github/workflows/docs-review-bot.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/docs-review-bot.yml b/.github/workflows/docs-review-bot.yml index 681dec4bdd..544a70f95e 100644 --- a/.github/workflows/docs-review-bot.yml +++ b/.github/workflows/docs-review-bot.yml @@ -11,9 +11,6 @@ on: schedule: - cron: '0 8 * * 1' - # TODO: Remove these two lines before merge - pull_request: - env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} DRY_RUN: true From 24e61ea0018108587f5909c1635858468256b088 Mon Sep 17 00:00:00 2001 From: Rowena Jones <36301604+RoRoJ@users.noreply.github.com> Date: Tue, 28 May 2024 17:07:08 +0200 Subject: [PATCH 13/13] Apply suggestions from code review Co-authored-by: Antoine Caron --- .github/workflows/docs-review-bot.yml | 2 +- bin/check-review-dates.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs-review-bot.yml b/.github/workflows/docs-review-bot.yml index 544a70f95e..79ed713c80 100644 --- a/.github/workflows/docs-review-bot.yml +++ b/.github/workflows/docs-review-bot.yml @@ -13,7 +13,7 @@ on: env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} - DRY_RUN: true + DRY_RUN: false jobs: review-docs: diff --git a/bin/check-review-dates.py b/bin/check-review-dates.py index 582ce64b28..a7eadc7112 100644 --- a/bin/check-review-dates.py +++ b/bin/check-review-dates.py @@ -119,8 +119,8 @@ def main(): docs_to_review = process_files(".") docs_to_review_by_cat = organize_docs_by_category(docs_to_review) message = prep_message(docs_to_review_by_cat) - # if os.environ.get("DRY_RUN") != "true": - # send_message(message) + if os.environ.get("DRY_RUN") != "true": + send_message(message) if __name__ == "__main__": main() \ No newline at end of file