Compare commits

...

4 Commits

Author SHA1 Message Date
naomi 059c24be31 feat: add linting
Node.js CI / CI (push) Failing after 3s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 47s
2025-12-22 17:08:18 -08:00
naomi f5dbe93168 feat: makefile 2025-12-22 17:08:03 -08:00
naomi 4fd9b41c67 feat: rework scripts to be platform/user agnostic 2025-12-22 17:07:36 -08:00
naomi b377d24277 chore: add new gifs 2025-12-22 17:07:34 -08:00
14 changed files with 672 additions and 220 deletions
+1
View File
@@ -0,0 +1 @@
Stores backups of all deleted configs.
+6 -27
View File
@@ -16,32 +16,11 @@ jobs:
- name: Checkout Source Files
uses: actions/checkout@v4
- name: Use Node.js v24
uses: actions/setup-node@v4
with:
node-version: 24
- name: Install yamllint
run: sudo apt-get install yamllint
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10
- name: Run Linter
run: make lint
- name: Ensure Dependencies are Pinned
uses: naomi-lgbt/dependency-pin-check@main
with:
language: javascript
dev-dependencies: true
peer-dependencies: true
optional-dependencies: true
- name: Install Dependencies
run: pnpm install
- name: Lint Source Files
run: pnpm run lint
- name: Verify Build
run: pnpm run build
- name: Run Tests
run: pnpm run test
- name: Run Tests
run: make test
+2
View File
@@ -0,0 +1,2 @@
.backup/*
!.backup/.gitkeep
+69
View File
@@ -0,0 +1,69 @@
yaml-files:
- '*.yaml'
- '*.yml'
- '.yamllint'
rules:
anchors:
forbid-undeclared-aliases: true
forbid-duplicated-anchors: true
forbid-unused-anchors: true
braces:
forbid: true
brackets:
forbid: true
colons:
max-spaces-before: 0
max-spaces-after: 1
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
comments:
require-starting-space: true
ignore-shebangs: true
min-spaces-from-content: 2
comments-indentation: enable
document-end:
present: false
document-start:
present: false
empty-lines:
max: 2
max-start: 0
max-end: 0
empty-values:
forbid-in-block-mappings: true
forbid-in-flow-mappings: true
forbid-in-block-sequences: true
rules:
float-values:
forbid-inf: true
forbid-nan: true
forbid-scientific-notation: true
require-numeral-before-decimal: true
hyphens:
max-spaces-after: 1
indentation:
spaces: 2
indent-sequences: true
check-multi-line-strings: true
key-duplicates: enable
key-ordering: enable
line-length: disable
new-line-at-end-of-file: enable
new-lines:
type: unix
octal-values:
forbid-implicit-octal: true
forbid-explicit-octal: false
quoted-strings:
quote-type: double
required: true
extra-required: []
extra-allowed: []
allow-quoted-quotes: false
trailing-spaces: enable
truthy:
allowed-values: ['true', 'false']
check-keys: true
+40
View File
@@ -0,0 +1,40 @@
.PHONY: help push pull test lint
# Colors for pretty output
CYAN := \033[0;36m
GREEN := \033[0;32m
YELLOW := \033[1;33m
MAGENTA := \033[0;35m
BOLD := \033[1m
NC := \033[0m
# Default target - show help
.DEFAULT_GOAL := help
help: ## Show this help message
@echo ""
@echo "$(CYAN)$(BOLD)╔═══════════════════════════════════════════════╗$(NC)"
@echo "$(CYAN)$(BOLD)$(NC) $(MAGENTA)$(BOLD)✨ Espanso Config Makefile ✨$(NC) $(CYAN)$(BOLD)$(NC)"
@echo "$(CYAN)$(BOLD)╚═══════════════════════════════════════════════╝$(NC)"
@echo ""
@echo "$(GREEN)$(BOLD)📋 Available Commands:$(NC)"
@echo ""
@echo " $(YELLOW)$(BOLD)make push$(NC) $(CYAN)$(NC) Push the local match directory to the remote Espanso config"
@echo " $(YELLOW)$(BOLD)make pull$(NC) $(CYAN)$(NC) Pull the remote match directory to the local Espanso config"
@echo " $(YELLOW)$(BOLD)make test$(NC) $(CYAN)$(NC) Test the Espanso config for duplicate triggers and replace values"
@echo " $(YELLOW)$(BOLD)make lint$(NC) $(CYAN)$(NC) Lint YAML files in the match directory"
@echo ""
@echo "$(CYAN)$(BOLD)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$(NC)"
@echo ""
push: ## Push the local match directory to the remote Espanso config
./push.sh
pull: ## Pull the remote match directory to the local Espanso config
./pull.sh
test: ## Test the Espanso config for duplicate triggers and replace values
./test.sh
lint: ## Lint YAML files in the match directory
./lint.sh
Executable
+125
View File
@@ -0,0 +1,125 @@
#!/bin/bash
# Color definitions
RESET='\033[0m'
BOLD='\033[1m'
RED='\033[91m'
GREEN='\033[92m'
YELLOW='\033[93m'
BLUE='\033[94m'
MAGENTA='\033[95m'
CYAN='\033[96m'
# Directory containing YAML files
MATCH_DIR="match"
# Check if directory exists
if [ ! -d "$MATCH_DIR" ]; then
echo -e "${RED}${BOLD}Error:${RESET} ${RED}Directory '$MATCH_DIR' not found!${RESET}"
exit 1
fi
# Find all YAML files
YAML_FILES=$(find "$MATCH_DIR" -name "*.yml" -o -name "*.yaml" | sort)
if [ -z "$YAML_FILES" ]; then
echo -e "${YELLOW}${BOLD}Warning:${RESET} ${YELLOW}No YAML files found in $MATCH_DIR directory!${RESET}"
exit 0
fi
# Function to check if yamllint is available
check_yamllint() {
if command -v yamllint &> /dev/null; then
echo "yamllint"
return 0
fi
if command -v pipx &> /dev/null && pipx list | grep -q yamllint; then
echo "pipx run yamllint"
return 0
fi
if python3 -m yamllint --version &> /dev/null 2>&1; then
echo "python3 -m yamllint"
return 0
fi
return 1
}
# Function to use Python's yaml module for basic validation
validate_with_python() {
local file="$1"
python3 << EOF
import yaml
import sys
try:
with open("$file", 'r') as f:
yaml.safe_load(f)
sys.exit(0)
except yaml.YAMLError as e:
print(f"$file: {e}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"$file: Error reading file - {e}", file=sys.stderr)
sys.exit(1)
EOF
}
# Try to find yamllint
YAMLLINT_CMD=$(check_yamllint)
if [ -n "$YAMLLINT_CMD" ]; then
echo -e "${CYAN}${BOLD}Using yamllint for YAML linting...${RESET}"
echo ""
# Run yamllint on all files
ERRORS=0
for file in $YAML_FILES; do
echo -e "${BLUE}Linting:${RESET} ${BOLD}$(basename "$file")${RESET}"
if ! $YAMLLINT_CMD "$file"; then
ERRORS=$((ERRORS + 1))
fi
done
if [ $ERRORS -eq 0 ]; then
echo ""
echo -e "${GREEN}${BOLD}✓ Success:${RESET} ${GREEN}All YAML files passed linting!${RESET}"
exit 0
else
echo ""
echo -e "${RED}${BOLD}✗ Error:${RESET} ${RED}Found linting errors in $ERRORS file(s)${RESET}"
exit 1
fi
else
echo -e "${YELLOW}${BOLD}Warning:${RESET} ${YELLOW}yamllint not found. Falling back to basic YAML syntax validation...${RESET}"
echo ""
echo -e "${CYAN}${BOLD}Tip:${RESET} ${CYAN}Install yamllint for better linting:${RESET}"
echo -e " ${BLUE}${RESET} ${MAGENTA}pipx install yamllint${RESET} (recommended - avoids system package conflicts)"
echo -e " ${BLUE}${RESET} ${MAGENTA}pip install --user yamllint${RESET}"
echo ""
# Fall back to Python yaml validation
ERRORS=0
for file in $YAML_FILES; do
echo -e "${BLUE}Validating:${RESET} ${BOLD}$(basename "$file")${RESET}"
if ! validate_with_python "$file" 2>&1; then
ERRORS=$((ERRORS + 1))
else
echo -e " ${GREEN}${RESET} Valid YAML syntax"
fi
done
if [ $ERRORS -eq 0 ]; then
echo ""
echo -e "${GREEN}${BOLD}✓ Success:${RESET} ${GREEN}All YAML files have valid syntax!${RESET}"
echo -e "${YELLOW}${BOLD}Note:${RESET} ${YELLOW}Install yamllint for style and best-practice checks${RESET}"
exit 0
else
echo ""
echo -e "${RED}${BOLD}✗ Error:${RESET} ${RED}Found syntax errors in $ERRORS file(s)${RESET}"
exit 1
fi
fi
+12 -12
View File
@@ -1,13 +1,13 @@
matches:
- trigger: "d.promo"
replace: "Please do not self-promote. This includes advertising your social media, projects, blogs, and anything else where you are gaining benefits from leveraging our community to drive traffic to your site."
- trigger: "d.modping"
replace: "Please do not ping the entire moderation team for non-urgent situations."
- trigger: "d.everyone"
replace: "Please do not try to mention everyone in the server. We have users all around the world, across many timezones. Trying to mention all of them to address your query is self-centred and rude."
- trigger: "d.dm"
replace: "Hello there! Before continuing this conversation, please note that direct messages are billed at $25 per incoming message as indicated in our communication policy: https://docs.nhcarrigan.com/about/contact/#8-direct-messages. Continuing to contact us through this channel will trigger your first invoice. If you prefer a free method of communication, join our public server: https://chat.nhcarrigan.com"
- trigger: "d.invoice"
replace: "Thank you for confirming these charges. Here is your invoice, including all previously sent messages and an additional buffer of 5 messages:"
- trigger: "d.block"
replace: "Valued consumer, we appreciate your business. However, due to outstanding invoice charges for our DM services, your access has been suspended until payment is made in full. You may still reach us via our chat server: https://chat.nhcarrigan.com"
- replace: "Please do not self-promote. This includes advertising your social media, projects, blogs, and anything else where you are gaining benefits from leveraging our community to drive traffic to your site."
trigger: "d.promo"
- replace: "Please do not ping the entire moderation team for non-urgent situations."
trigger: "d.modping"
- replace: "Please do not try to mention everyone in the server. We have users all around the world, across many timezones. Trying to mention all of them to address your query is self-centred and rude."
trigger: "d.everyone"
- replace: "Hello there! Before continuing this conversation, please note that direct messages are billed at $25 per incoming message as indicated in our communication policy: https://docs.nhcarrigan.com/about/contact/#8-direct-messages. Continuing to contact us through this channel will trigger your first invoice. If you prefer a free method of communication, join our public server: https://chat.nhcarrigan.com"
trigger: "d.dm"
- replace: "Thank you for confirming these charges. Here is your invoice, including all previously sent messages and an additional buffer of 5 messages:"
trigger: "d.invoice"
- replace: "Valued consumer, we appreciate your business. However, due to outstanding invoice charges for our DM services, your access has been suspended until payment is made in full. You may still reach us via our chat server: https://chat.nhcarrigan.com"
trigger: "d.block"
+10 -10
View File
@@ -1,11 +1,11 @@
matches:
- trigger: "docs.contribute"
replace: "https://docs.nhcarrigan.com/dev/contributing/"
- trigger: "docs.coc"
replace: "https://docs.nhcarrigan.com/community/coc/"
- trigger: "docs.terms"
replace: "https://docs.nhcarrigan.com/legal/terms/"
- trigger: "docs.privacy"
replace: "https://docs.nhcarrigan.com/legal/privacy/"
- trigger: "docs.community"
replace: "https://docs.nhcarrigan.com/community/guide/"
- replace: "https://docs.nhcarrigan.com/dev/contributing/"
trigger: "docs.contribute"
- replace: "https://docs.nhcarrigan.com/community/coc/"
trigger: "docs.coc"
- replace: "https://docs.nhcarrigan.com/legal/terms/"
trigger: "docs.terms"
- replace: "https://docs.nhcarrigan.com/legal/privacy/"
trigger: "docs.privacy"
- replace: "https://docs.nhcarrigan.com/community/guide/"
trigger: "docs.community"
+104 -88
View File
@@ -1,89 +1,105 @@
matches:
- trigger: "gif.waltz"
replace: "https://c.tenor.com/kO5ruOuFkiYAAAAC/tenor.gif"
- trigger: "gif.kiss"
replace: "https://c.tenor.com/hdFfH-A8UwoAAAAd/tenor.gif"
- trigger: "gif.jinx"
replace: "https://c.tenor.com/rgPmLy-LrrgAAAAC/tenor.gif"
- trigger: "gif.fabulous"
replace: "https://c.tenor.com/jKIq-XwBErgAAAAC/tenor.gif"
- trigger: "gif.wake"
replace: "https://c.tenor.com/feKcxglGTmEAAAAd/tenor.gif"
- trigger: "gif.fix"
replace: "https://c.tenor.com/rbmuthchA8cAAAAC/tenor.gif"
- trigger: "gif.comfort"
replace: "https://c.tenor.com/Hgt-mT0KXN0AAAAd/tenor.gif"
- trigger: "gif.error"
replace: "https://c.tenor.com/9p1NW-ml_rYAAAAC/tenor.gif"
- trigger: "gif.tired"
replace: "https://c.tenor.com/EAKYKQFu4BAAAAAC/tenor.gif"
- trigger: "gif.bored"
replace: "https://c.tenor.com/GX7e2p31RlQAAAAC/tenor.gif"
- trigger: "gif.praise"
replace: "https://c.tenor.com/4-yCmKYkVgoAAAAC/tenor.gif"
- trigger: "gif.wait"
replace: "https://c.tenor.com/gEpKdawu7moAAAAd/tenor.gif"
- trigger: "gif.reverse"
replace: "https://c.tenor.com/0d_aB3xxWroAAAAd/tenor.gif"
- trigger: "gif.annoyed"
replace: "https://c.tenor.com/MvKZZ7JCkUMAAAAC/tenor.gif"
- trigger: "gif.sword"
replace: "https://c.tenor.com/EWhFGCTfmucAAAAC/tenor.gif"
- trigger: "gif.blush"
replace: "https://c.tenor.com/CEkiOjpsylwAAAAd/tenor.gif"
- trigger: "gif.smart"
replace: "https://c.tenor.com/hPsNhgYicFMAAAAC/tenor.gif"
- trigger: "gif.ignore"
replace: "https://c.tenor.com/AlUkiGkR2j8AAAAC/tenor.gif"
- trigger: "gif.shh"
replace: "https://c.tenor.com/P32p3dPsEgkAAAAC/tenor.gif"
- trigger: "gif.giggle"
replace: "https://c.tenor.com/3_Yh8BaRLAIAAAAC/tenor.gif"
- trigger: "gif.wave"
replace: "https://c.tenor.com/FvthnLepGgAAAAAC/tenor.gif"
- trigger: "gif.salute"
replace: "https://c.tenor.com/NahzVADyncsAAAAd/tenor.gif"
- trigger: "gif.thank"
replace: "https://c.tenor.com/xITmP6QW3woAAAAC/tenor.gif"
- trigger: "gif.peek"
replace: "https://c.tenor.com/X7tqC1BVu4sAAAAC/tenor.gif"
- trigger: "gif.pout"
replace: "https://c.tenor.com/03VCLMyKfL4AAAAC/tenor.gif"
- trigger: "gif.sing"
replace: "https://c.tenor.com/D4Z50s-YFkoAAAAC/tenor.gif"
- trigger: "gif.objection"
replace: "https://c.tenor.com/kHS2x_pJVrwAAAAC/tenor.gif"
- trigger: "gif.dance"
replace: "https://c.tenor.com/g9xOFVXmNx0AAAAd/tenor.gif"
- trigger: "gif.dancy"
replace: "https://c.tenor.com/m21RwoBHceEAAAAd/tenor.gif"
- trigger: "gif.cheer"
replace: "https://c.tenor.com/fKjC8KynhWYAAAAC/tenor.gif"
- trigger: "gif.hype"
replace: "https://c.tenor.com/_eWYkZnqjwcAAAAC/tenor.gif"
- trigger: "gif.noted"
replace: "https://c.tenor.com/tHswBoYy_HIAAAAd/tenor.gif"
- trigger: "gif.smug"
replace: "https://c.tenor.com/amMMuaTS1bsAAAAC/tenor.gif"
- trigger: "gif.vibe"
replace: "https://c.tenor.com/_aiabJq9CdoAAAAd/tenor.gif"
- trigger: "gif.laugh"
replace: "https://c.tenor.com/jz9aJI4fys4AAAAC/tenor.gif"
- trigger: "gif.shrug"
replace: "https://c.tenor.com/0GOwPHgcUj0AAAAC/tenor.gif"
- trigger: "gif.block"
replace: "https://c.tenor.com/Fym_4yzyu0IAAAAC/tenor.gif"
- trigger: "gif.pray"
replace: "https://c.tenor.com/d8vq5hthkAoAAAAC/tenor.gif"
- trigger: "gif.psychic"
replace: "https://c.tenor.com/ETmw31CyCmQAAAAd/tenor.gif"
- trigger: "gif.wiggle"
replace: "https://c.tenor.com/6bGvizlex6oAAAAd/tenor.gif"
- trigger: "gif.lurk"
replace: "https://c.tenor.com/Bh5FPIsK2xEAAAAd/tenor.gif"
- trigger: "gif.stare"
replace: "https://c.tenor.com/VC3EDnfJmKEAAAAd/tenor.gif"
- trigger: "gif.bow"
replace: "https://c.tenor.com/lpxKv7slwfgAAAAd/tenor.gif"
- trigger: "gif.excited"
replace: "https://c.tenor.com/CtJ_SVFoh4cAAAAd/tenor.gif"
- replace: "https://c.tenor.com/kO5ruOuFkiYAAAAC/tenor.gif"
trigger: "gif.waltz"
- replace: "https://c.tenor.com/hdFfH-A8UwoAAAAd/tenor.gif"
trigger: "gif.kiss"
- replace: "https://c.tenor.com/rgPmLy-LrrgAAAAC/tenor.gif"
trigger: "gif.jinx"
- replace: "https://c.tenor.com/jKIq-XwBErgAAAAC/tenor.gif"
trigger: "gif.fabulous"
- replace: "https://c.tenor.com/feKcxglGTmEAAAAd/tenor.gif"
trigger: "gif.wake"
- replace: "https://c.tenor.com/rbmuthchA8cAAAAC/tenor.gif"
trigger: "gif.fix"
- replace: "https://c.tenor.com/Hgt-mT0KXN0AAAAd/tenor.gif"
trigger: "gif.comfort"
- replace: "https://c.tenor.com/9p1NW-ml_rYAAAAC/tenor.gif"
trigger: "gif.error"
- replace: "https://c.tenor.com/EAKYKQFu4BAAAAAC/tenor.gif"
trigger: "gif.tired"
- replace: "https://c.tenor.com/GX7e2p31RlQAAAAC/tenor.gif"
trigger: "gif.bored"
- replace: "https://c.tenor.com/4-yCmKYkVgoAAAAC/tenor.gif"
trigger: "gif.praise"
- replace: "https://c.tenor.com/gEpKdawu7moAAAAd/tenor.gif"
trigger: "gif.wait"
- replace: "https://c.tenor.com/0d_aB3xxWroAAAAd/tenor.gif"
trigger: "gif.reverse"
- replace: "https://c.tenor.com/MvKZZ7JCkUMAAAAC/tenor.gif"
trigger: "gif.annoyed"
- replace: "https://c.tenor.com/EWhFGCTfmucAAAAC/tenor.gif"
trigger: "gif.sword"
- replace: "https://c.tenor.com/CEkiOjpsylwAAAAd/tenor.gif"
trigger: "gif.blush"
- replace: "https://c.tenor.com/hPsNhgYicFMAAAAC/tenor.gif"
trigger: "gif.smart"
- replace: "https://c.tenor.com/AlUkiGkR2j8AAAAC/tenor.gif"
trigger: "gif.ignore"
- replace: "https://c.tenor.com/P32p3dPsEgkAAAAC/tenor.gif"
trigger: "gif.shh"
- replace: "https://c.tenor.com/3_Yh8BaRLAIAAAAC/tenor.gif"
trigger: "gif.giggle"
- replace: "https://c.tenor.com/FvthnLepGgAAAAAC/tenor.gif"
trigger: "gif.wave"
- replace: "https://c.tenor.com/NahzVADyncsAAAAd/tenor.gif"
trigger: "gif.salute"
- replace: "https://c.tenor.com/oEFpzFipXvcAAAAd/tenor.gif"
trigger: "gif.thank"
- replace: "https://c.tenor.com/X7tqC1BVu4sAAAAC/tenor.gif"
trigger: "gif.peek"
- replace: "https://c.tenor.com/03VCLMyKfL4AAAAC/tenor.gif"
trigger: "gif.pout"
- replace: "https://c.tenor.com/D4Z50s-YFkoAAAAC/tenor.gif"
trigger: "gif.sing"
- replace: "https://c.tenor.com/kHS2x_pJVrwAAAAC/tenor.gif"
trigger: "gif.objection"
- replace: "https://c.tenor.com/g9xOFVXmNx0AAAAd/tenor.gif"
trigger: "gif.dance"
- replace: "https://c.tenor.com/m21RwoBHceEAAAAd/tenor.gif"
trigger: "gif.dancy"
- replace: "https://c.tenor.com/fKjC8KynhWYAAAAC/tenor.gif"
trigger: "gif.cheer"
- replace: "https://c.tenor.com/_eWYkZnqjwcAAAAC/tenor.gif"
trigger: "gif.hype"
- replace: "https://c.tenor.com/tHswBoYy_HIAAAAd/tenor.gif"
trigger: "gif.noted"
- replace: "https://c.tenor.com/amMMuaTS1bsAAAAC/tenor.gif"
trigger: "gif.smug"
- replace: "https://c.tenor.com/_aiabJq9CdoAAAAd/tenor.gif"
trigger: "gif.vibe"
- replace: "https://c.tenor.com/jz9aJI4fys4AAAAC/tenor.gif"
trigger: "gif.laugh"
- replace: "https://c.tenor.com/0GOwPHgcUj0AAAAC/tenor.gif"
trigger: "gif.shrug"
- replace: "https://c.tenor.com/Fym_4yzyu0IAAAAC/tenor.gif"
trigger: "gif.block"
- replace: "https://c.tenor.com/d8vq5hthkAoAAAAC/tenor.gif"
trigger: "gif.pray"
- replace: "https://c.tenor.com/ETmw31CyCmQAAAAd/tenor.gif"
trigger: "gif.psychic"
- replace: "https://c.tenor.com/6bGvizlex6oAAAAd/tenor.gif"
trigger: "gif.wiggle"
- replace: "https://c.tenor.com/Bh5FPIsK2xEAAAAd/tenor.gif"
trigger: "gif.lurk"
- replace: "https://c.tenor.com/VC3EDnfJmKEAAAAd/tenor.gif"
trigger: "gif.stare"
- replace: "https://c.tenor.com/lpxKv7slwfgAAAAd/tenor.gif"
trigger: "gif.bow"
- replace: "https://c.tenor.com/CtJ_SVFoh4cAAAAd/tenor.gif"
trigger: "gif.excited"
- replace: "https://c.tenor.com/keB3oG-he3AAAAAC/tenor.gif"
trigger: "gif.think"
- replace: "https://c.tenor.com/ai682TGdyQAAAAAC/tenor.gif"
trigger: "gif.paper"
- replace: "https://c.tenor.com/4o4SzmfxiBAAAAAd/tenor.gif"
trigger: "gif.thonk"
- replace: "https://c.tenor.com/uPAvHo2xmN8AAAAC/tenor.gif"
trigger: "gif.money"
- replace: "https://c.tenor.com/fNkIorwhEacAAAAC/tenor.gif"
trigger: "gif.nod"
- replace: "https://c.tenor.com/VWi9_CJrUc8AAAAd/tenor.gif"
trigger: "gif.oops"
- replace: "https://c.tenor.com/aRhrMeBzkd0AAAAd/tenor.gif"
trigger: "gif.lewd"
- replace: "https://c.tenor.com/lwyR-BEUeD4AAAAd/tenor.gif"
trigger: "gif.exhausted"
+6 -6
View File
@@ -1,7 +1,7 @@
matches:
- trigger: "hf.ps"
replace: "Please keep all projects to <#1280522731963547759>."
- trigger: "hf.invalid"
replace: "That project has been reported by the community for violating Hacktoberfest's values. Repositories which are designed to facilitate easy pull requests for the sake of 'winning' Hacktoberfest, including arbitrary collections of content like algorithms and code snippets, are ineligible to participate."
- trigger: "hf.spam"
replace: "Maintainers are free to use the `spam` label to indicate that a pull request is low effort and does not follow their contributing guide. If you receive two or more `spam` labels across any of your pull requests, you will be permanently ineligible to participate in Hacktoberfest."
- replace: "Please keep all projects to <#1280522731963547759>."
trigger: "hf.ps"
- replace: "That project has been reported by the community for violating Hacktoberfest's values. Repositories which are designed to facilitate easy pull requests for the sake of 'winning' Hacktoberfest, including arbitrary collections of content like algorithms and code snippets, are ineligible to participate."
trigger: "hf.invalid"
- replace: "Maintainers are free to use the `spam` label to indicate that a pull request is low effort and does not follow their contributing guide. If you receive two or more `spam` labels across any of your pull requests, you will be permanently ineligible to participate in Hacktoberfest."
trigger: "hf.spam"
+30 -30
View File
@@ -1,31 +1,31 @@
matches:
- trigger: "img.bad"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-13-52-26-Render.png"
- trigger: "img.magic"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-20-40-Render.png"
- trigger: "img.dance"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-24-02-Render.png"
- trigger: "img.smart"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-34-17-Render.png"
- trigger: "img.nom"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-35-27-Render.png"
- trigger: "img.fight"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-16-29-08-Render.png"
- trigger: "img.shh"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-16-45-58-Render.png"
- trigger: "img.hug"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-15-36-09-Render.png"
- trigger: "img.point"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-16-56-41-Render.png"
- trigger: "img.work"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-17-02-27-Render.png"
- trigger: "img.innocent"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-16-15-56-Render.png"
- trigger: "img.read"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-55-30-Render.png"
- trigger: "img.peek"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-23-32-Render.png"
- trigger: "img.queen"
replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-15-18-49-Render.png"
- trigger: "img.stonks"
replace: "https://cdn.nhcarrigan.com/stonks.png"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-13-52-26-Render.png"
trigger: "img.bad"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-20-40-Render.png"
trigger: "img.magic"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-24-02-Render.png"
trigger: "img.dance"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-34-17-Render.png"
trigger: "img.smart"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-35-27-Render.png"
trigger: "img.nom"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-16-29-08-Render.png"
trigger: "img.fight"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-16-45-58-Render.png"
trigger: "img.shh"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-15-36-09-Render.png"
trigger: "img.hug"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-16-56-41-Render.png"
trigger: "img.point"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-17-02-27-Render.png"
trigger: "img.work"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-16-15-56-Render.png"
trigger: "img.innocent"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-55-30-Render.png"
trigger: "img.read"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-14-23-32-Render.png"
trigger: "img.peek"
- replace: "https://cdn.nhcarrigan.com/koikatsu/CharaStudio-2025-04-06-15-18-49-Render.png"
trigger: "img.queen"
- replace: "https://cdn.nhcarrigan.com/stonks.png"
trigger: "img.stonks"
+82 -2
View File
@@ -1,4 +1,84 @@
#! /usr/bin/bash
rm -r match
cp -r ~/.config/espanso/match .
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
BOLD='\033[1m'
NC='\033[0m' # No Color
# Prompt for username
echo -e "${CYAN}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${CYAN}${BOLD} Espanso Config Pull${NC}"
echo -e "${CYAN}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
prompt_username="${YELLOW}Enter your system/account username: ${NC}"
read -p "$(printf '%b' "$prompt_username")" username
# Prompt for platform
echo ""
echo -e "${CYAN}${BOLD}Select the platform for your Espanso config:${NC}"
echo -e " ${GREEN}1)${NC} Linux: ${MAGENTA}/home/<user>/.config/espanso${NC}"
echo -e " ${GREEN}2)${NC} WSL: ${MAGENTA}/mnt/c/Users/<user>/AppData/Roaming/espanso${NC}"
echo -e " ${GREEN}3)${NC} Windows: ${MAGENTA}C:\\Users\\<user>\\AppData\\Roaming\\espanso${NC}"
echo -e " ${GREEN}4)${NC} Mac: ${MAGENTA}/Users/<user>/Library/Application Support/espanso${NC}"
echo -e " ${GREEN}5)${NC} Other (custom absolute path)"
echo ""
prompt_choice="${YELLOW}Enter your choice (1-5): ${NC}"
read -p "$(printf '%b' "$prompt_choice")" choice
# Build the path based on selection
case "$choice" in
1)
espanso_path="/home/$username/.config/espanso"
;;
2)
espanso_path="/mnt/c/Users/$username/AppData/Roaming/espanso"
;;
3)
espanso_path="/mnt/c/Users/$username/AppData/Roaming/espanso"
;;
4)
espanso_path="/Users/$username/Library/Application Support/espanso"
;;
5)
prompt_custom="${YELLOW}Enter the custom absolute path to espanso config: ${NC}"
read -p "$(printf '%b' "$prompt_custom")" espanso_path
;;
*)
echo -e "${RED}${BOLD}✗ Invalid choice. Exiting.${NC}"
exit 1
;;
esac
# Verify the path exists
if [ ! -d "$espanso_path" ]; then
echo -e "${RED}${BOLD}✗ Error: Path '$espanso_path' does not exist!${NC}"
exit 1
fi
# Verify the match directory exists
if [ ! -d "$espanso_path/match" ]; then
echo -e "${RED}${BOLD}✗ Error: Match directory not found at '$espanso_path/match'!${NC}"
exit 1
fi
echo ""
echo -e "${BLUE}${BOLD}→ Pulling from: ${CYAN}$espanso_path/match${NC}"
# Create backup of local match directory if it exists
if [ -d "match" ]; then
backup_date=$(date +%Y-%m-%d-%H%M%S)
backup_dir=".backup/${backup_date}-match"
echo -e "${YELLOW}${BOLD}→ Backing up local match directory to: ${CYAN}$backup_dir${NC}"
mkdir -p "$backup_dir"
cp -r match "$backup_dir/"
echo -e "${GREEN}${BOLD}✓ Backup created successfully${NC}"
fi
rm -rf match
cp -r "$espanso_path/match" .
echo -e "${GREEN}${BOLD}✓ Done! Match directory pulled successfully.${NC}"
+94 -2
View File
@@ -1,4 +1,96 @@
#! /usr/bin/bash
rm -r /mnt/c/Users/accou/AppData/Roaming/espanso/match
cp -r match /mnt/c/Users/accou/AppData/Roaming/espanso
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
BOLD='\033[1m'
NC='\033[0m' # No Color
# Verify local match directory exists
if [ ! -d "match" ]; then
echo -e "${RED}${BOLD}✗ Error: Local 'match' directory not found!${NC}"
exit 1
fi
# Prompt for username
echo -e "${CYAN}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${CYAN}${BOLD} Espanso Config Push${NC}"
echo -e "${CYAN}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
prompt_username="${YELLOW}Enter your system/account username: ${NC}"
read -p "$(printf '%b' "$prompt_username")" username
# Prompt for platform
echo ""
echo -e "${CYAN}${BOLD}Select the platform for your Espanso config:${NC}"
echo -e " ${GREEN}1)${NC} Linux: ${MAGENTA}/home/<user>/.config/espanso${NC}"
echo -e " ${GREEN}2)${NC} WSL: ${MAGENTA}/mnt/c/Users/<user>/AppData/Roaming/espanso${NC}"
echo -e " ${GREEN}3)${NC} Windows: ${MAGENTA}C:\\Users\\<user>\\AppData\\Roaming\\espanso${NC}"
echo -e " ${GREEN}4)${NC} Mac: ${MAGENTA}/Users/<user>/Library/Application Support/espanso${NC}"
echo -e " ${GREEN}5)${NC} Other (custom absolute path)"
echo ""
prompt_choice="${YELLOW}Enter your choice (1-5): ${NC}"
read -p "$(printf '%b' "$prompt_choice")" choice
# Build the path based on selection
case "$choice" in
1)
espanso_path="/home/$username/.config/espanso"
;;
2)
espanso_path="/mnt/c/Users/$username/AppData/Roaming/espanso"
;;
3)
espanso_path="/mnt/c/Users/$username/AppData/Roaming/espanso"
;;
4)
espanso_path="/Users/$username/Library/Application Support/espanso"
;;
5)
prompt_custom="${YELLOW}Enter the custom absolute path to espanso config: ${NC}"
read -p "$(printf '%b' "$prompt_custom")" espanso_path
;;
*)
echo -e "${RED}${BOLD}✗ Invalid choice. Exiting.${NC}"
exit 1
;;
esac
# Verify the espanso config directory exists, or ask to create it
if [ ! -d "$espanso_path" ]; then
echo ""
echo -e "${YELLOW}${BOLD}⚠ Directory '$espanso_path' does not exist.${NC}"
prompt_create="${YELLOW}Would you like to create it? (y/n): ${NC}"
read -p "$(printf '%b' "$prompt_create")" create_dir
case "$create_dir" in
[Yy]|[Yy][Ee][Ss])
mkdir -p "$espanso_path"
echo -e "${GREEN}${BOLD}✓ Directory created successfully${NC}"
;;
*)
echo -e "${RED}${BOLD}✗ Cannot proceed without the directory. Exiting.${NC}"
exit 1
;;
esac
fi
echo ""
echo -e "${BLUE}${BOLD}→ Pushing to: ${CYAN}$espanso_path${NC}"
# Create backup of remote match directory if it exists
if [ -d "$espanso_path/match" ]; then
backup_date=$(date +%Y-%m-%d-%H%M%S)
backup_dir=".backup/${backup_date}-match"
echo -e "${YELLOW}${BOLD}→ Backing up remote match directory to: ${CYAN}$backup_dir${NC}"
mkdir -p "$backup_dir"
cp -r "$espanso_path/match" "$backup_dir/"
echo -e "${GREEN}${BOLD}✓ Backup created successfully${NC}"
fi
rm -rf "$espanso_path/match"
cp -r match "$espanso_path"
echo -e "${GREEN}${BOLD}✓ Done! Match directory pushed successfully.${NC}"
+91 -43
View File
@@ -1,88 +1,136 @@
#!/bin/bash
# Color definitions
RESET='\033[0m'
BOLD='\033[1m'
RED='\033[91m'
GREEN='\033[92m'
YELLOW='\033[93m'
BLUE='\033[94m'
MAGENTA='\033[95m'
CYAN='\033[96m'
# Directory containing YAML files
MATCH_DIR="match"
# Check if directory exists
if [ ! -d "$MATCH_DIR" ]; then
echo "Error: Directory '$MATCH_DIR' not found!"
echo -e "${RED}${BOLD}Error:${RESET} ${RED}Directory '$MATCH_DIR' not found!${RESET}"
exit 1
fi
# Temporary file to store all triggers
ALL_TRIGGERS=$(mktemp)
ALL_REPLACES=$(mktemp)
# Temporary files to store triggers and replaces with metadata
# Format: trigger|replace|file|trigger_line|replace_line
ALL_TRIGGERS_DATA=$(mktemp)
ALL_REPLACES_DATA=$(mktemp)
# Find all YAML files and extract triggers
echo "Scanning YAML files in $MATCH_DIR directory..."
# Find all YAML files and extract triggers with their replace values and line numbers
echo -e "${CYAN}${BOLD}Scanning YAML files in $MATCH_DIR directory...${RESET}"
for file in "$MATCH_DIR"/*.y*ml; do
if [ -f "$file" ]; then
echo "Processing file: $(basename "$file")"
# Extract triggers using grep and sed
# This looks for lines with "trigger:" and captures the value
grep -E '^\s*-\s*trigger:' "$file" | sed 's/.*trigger:\s*"\([^"]*\)".*/\1/' >> "$ALL_TRIGGERS"
# Also extract replace values if they exist
grep -E '^\s*replace:' "$file" | sed 's/.*replace:\s*"\([^"]*\)".*/\1/' >> "$ALL_REPLACES"
echo -e "${BLUE}Processing file:${RESET} ${BOLD}$(basename "$file")${RESET}"
# Process file line by line to track trigger-replace pairs
trigger_line=""
trigger_value=""
replace_value=""
replace_line=""
line_num=0
while IFS= read -r line; do
line_num=$((line_num + 1))
# Check if this is a trigger line
if echo "$line" | grep -qE '^\s*-\s*trigger:'; then
trigger_line=$line_num
trigger_value=$(echo "$line" | sed 's/.*trigger:\s*"\([^"]*\)".*/\1/')
replace_value="" # Reset replace value for new trigger
replace_line="" # Reset replace line
# Check if this is a replace line (should be right after trigger)
elif echo "$line" | grep -qE '^\s*replace:'; then
replace_value=$(echo "$line" | sed 's/.*replace:\s*"\([^"]*\)".*/\1/')
replace_line=$line_num
# Save trigger data: trigger|replace|file|trigger_line|replace_line
if [ -n "$trigger_value" ]; then
echo "$trigger_value|$replace_value|$(basename "$file")|$trigger_line|$replace_line" >> "$ALL_TRIGGERS_DATA"
fi
fi
done < "$file"
else
echo "Warning: No YAML files found in $MATCH_DIR directory!"
echo -e "${YELLOW}${BOLD}Warning:${RESET} ${YELLOW}No YAML files found in $MATCH_DIR directory!${RESET}"
continue
fi
done
# Also create a reverse mapping for replace values: replace|trigger|file|trigger_line|replace_line
awk -F'|' '{print $2"|"$1"|"$3"|"$4"|"$5}' "$ALL_TRIGGERS_DATA" > "$ALL_REPLACES_DATA"
# Check if we found any triggers
if [ ! -s "$ALL_TRIGGERS" ]; then
echo "No triggers found in YAML files. Check file format or directory."
rm "$ALL_TRIGGERS"
if [ ! -s "$ALL_TRIGGERS_DATA" ]; then
echo -e "${RED}${BOLD}Error:${RESET} ${RED}No triggers found in YAML files. Check file format or directory.${RESET}"
rm "$ALL_TRIGGERS_DATA" "$ALL_REPLACES_DATA"
exit 1
fi
# Extract just triggers to find duplicates
ALL_TRIGGERS=$(mktemp)
cut -d'|' -f1 "$ALL_TRIGGERS_DATA" > "$ALL_TRIGGERS"
# Sort triggers and find duplicates
echo -e "\nChecking for duplicate triggers..."
echo -e "\n${CYAN}${BOLD}Checking for duplicate triggers...${RESET}"
DUPLICATE_TRIGGERS=$(sort "$ALL_TRIGGERS" | uniq -d)
# Display results
if [ -z "$DUPLICATE_TRIGGERS" ]; then
echo "Success: All triggers are unique!"
echo -e "${GREEN}${BOLD}✓ Success:${RESET} ${GREEN}All triggers are unique!${RESET}"
TOTAL=$(wc -l < "$ALL_TRIGGERS")
echo "Total number of triggers found: $TOTAL"
echo -e "${CYAN}Total number of triggers found:${RESET} ${BOLD}$TOTAL${RESET}"
else
echo "Error: Duplicate triggers found:"
echo "$DUPLICATE_TRIGGERS"
echo -e "${RED}${BOLD}✗ Error:${RESET} ${RED}Duplicate triggers found:${RESET}"
echo -e "${YELLOW}$DUPLICATE_TRIGGERS${RESET}"
# Optional: Show which files contain each duplicate
echo -e "\nDuplicates found in these files:"
# Show detailed information for each duplicate
echo -e "\n${CYAN}${BOLD}Detailed information for duplicates:${RESET}"
for dupe in $DUPLICATE_TRIGGERS; do
echo "Trigger \"$dupe\" found in:"
grep -l "trigger: \"$dupe\"" "$MATCH_DIR"/*.y*ml
echo -e "\n${MAGENTA}${BOLD}Trigger${RESET} ${BOLD}\"$dupe\"${RESET} ${MAGENTA}found:${RESET}"
grep "^$dupe|" "$ALL_TRIGGERS_DATA" | while IFS='|' read -r trigger replace file trigger_line replace_line; do
echo -e " ${YELLOW}${RESET} Line ${MAGENTA}${BOLD}$trigger_line${RESET} in ${BLUE}$file${RESET}: ${CYAN}replace=${RESET}\"${GREEN}$replace${RESET}\""
done
done
rm "$ALL_TRIGGERS" "$ALL_TRIGGERS_DATA" "$ALL_REPLACES_DATA"
exit 1
fi
# Extract just replace values to find duplicates
ALL_REPLACES=$(mktemp)
cut -d'|' -f1 "$ALL_REPLACES_DATA" > "$ALL_REPLACES"
# Sort replace values and find duplicates
echo -e "\nChecking for duplicate replace values..."
echo -e "\n${CYAN}${BOLD}Checking for duplicate replace values...${RESET}"
DUPLICATE_REPLACES=$(sort "$ALL_REPLACES" | uniq -d)
# Display results
if [ -z "$DUPLICATE_REPLACES" ]; then
echo "Success: All replace values are unique!"
echo -e "${GREEN}${BOLD}✓ Success:${RESET} ${GREEN}All replace values are unique!${RESET}"
TOTAL_REPLACES=$(wc -l < "$ALL_REPLACES")
echo "Total number of replace values found: $TOTAL_REPLACES"
echo -e "${CYAN}Total number of replace values found:${RESET} ${BOLD}$TOTAL_REPLACES${RESET}"
else
echo "Error: Duplicate replace values found:"
echo "$DUPLICATE_REPLACES"
echo -e "${RED}${BOLD}✗ Error:${RESET} ${RED}Duplicate replace values found:${RESET}"
echo -e "${YELLOW}$DUPLICATE_REPLACES${RESET}"
# Optional: Show which files contain each duplicate replace value
echo -e "\nDuplicates found in these files:"
# Show detailed information for each duplicate
echo -e "\n${CYAN}${BOLD}Detailed information for duplicates:${RESET}"
for dupe in $DUPLICATE_REPLACES; do
echo "Replace value \"$dupe\" found in:"
grep -l "replace: \"$dupe\"" "$MATCH_DIR"/*.y*ml
echo -e "\n${MAGENTA}${BOLD}Replace value${RESET} ${BOLD}\"$dupe\"${RESET} ${MAGENTA}found:${RESET}"
grep "^$dupe|" "$ALL_REPLACES_DATA" | while IFS='|' read -r replace trigger file trigger_line replace_line; do
echo -e " ${YELLOW}${RESET} Line ${MAGENTA}${BOLD}$replace_line${RESET} in ${BLUE}$file${RESET}: ${CYAN}trigger=${RESET}\"${GREEN}$trigger${RESET}\""
done
done
rm "$ALL_TRIGGERS" "$ALL_REPLACES" "$ALL_TRIGGERS_DATA" "$ALL_REPLACES_DATA"
exit 1
fi
# Clean up
rm "$ALL_TRIGGERS"
rm "$ALL_REPLACES"
rm "$ALL_TRIGGERS" "$ALL_REPLACES" "$ALL_TRIGGERS_DATA" "$ALL_REPLACES_DATA"
### Now we test that all gifs.yml replace values match "c.tenor.com".
@@ -90,31 +138,31 @@ rm "$ALL_REPLACES"
REPLACE_VALUES=$(mktemp)
# Read gifs.yml file and extract replace values
echo "Scanning gifs.yml for replace values..."
echo -e "${CYAN}${BOLD}Scanning gifs.yml for replace values...${RESET}"
if [ -f "$MATCH_DIR/gifs.yml" ]; then
# We match lines like: ` replace: "https://c.tenor.com/some-value/tenor.gif"`
grep -E '^\s*replace:' "$MATCH_DIR/gifs.yml" | sed 's/.*replace:\s*"\([^"]*\)".*/\1/' >> "$REPLACE_VALUES"
else
echo "Error: gifs.yml file not found in $MATCH_DIR directory!"
echo -e "${RED}${BOLD}Error:${RESET} ${RED}gifs.yml file not found in $MATCH_DIR directory!${RESET}"
rm "$REPLACE_VALUES"
exit 1
fi
# Check if we found any replace values
if [ ! -s "$REPLACE_VALUES" ]; then
echo "No replace values found in gifs.yml. Check file format."
echo -e "${RED}${BOLD}Error:${RESET} ${RED}No replace values found in gifs.yml. Check file format.${RESET}"
rm "$REPLACE_VALUES"
exit 1
fi
# Check if all replace values start with "https://c.tenor.com/"
echo -e "\nChecking replace values in gifs.yml..."
echo -e "\n${CYAN}${BOLD}Checking replace values in gifs.yml...${RESET}"
INVALID_REPLACE=$(grep -v '^https://c\.tenor\.com/' "$REPLACE_VALUES")
if [ -z "$INVALID_REPLACE" ]; then
echo "Success: All replace values in gifs.yml are valid!"
echo -e "${GREEN}${BOLD}✓ Success:${RESET} ${GREEN}All replace values in gifs.yml are valid!${RESET}"
else
echo "Error: Invalid replace values found in gifs.yml:"
echo "$INVALID_REPLACE"
echo -e "${RED}${BOLD}✗ Error:${RESET} ${RED}Invalid replace values found in gifs.yml:${RESET}"
echo -e "${YELLOW}$INVALID_REPLACE${RESET}"
exit 1
fi