From 59458f1365fc2252239e08fe1f446991c91efd5d Mon Sep 17 00:00:00 2001 From: erkcet Date: Thu, 23 Jul 2026 11:27:26 +0300 Subject: [PATCH] Add maintainable referral code workflow --- .github/workflows/check-codes.yml | 31 ++++++++++++++ README.md | 53 ++++++++++++------------ data/codes.json | 8 ++++ package.json | 15 +++++++ scripts/check-links.mjs | 38 ++++++++++++++++++ scripts/lib.mjs | 40 ++++++++++++++++++ scripts/render-readme.mjs | 67 +++++++++++++++++++++++++++++++ 7 files changed, 224 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/check-codes.yml create mode 100644 data/codes.json create mode 100644 package.json create mode 100644 scripts/check-links.mjs create mode 100644 scripts/lib.mjs create mode 100644 scripts/render-readme.mjs diff --git a/.github/workflows/check-codes.yml b/.github/workflows/check-codes.yml new file mode 100644 index 0000000..cf065ad --- /dev/null +++ b/.github/workflows/check-codes.yml @@ -0,0 +1,31 @@ +name: Check referral codes + +on: + push: + branches: [main] + pull_request: + schedule: + - cron: "17 7 * * *" + workflow_dispatch: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Check data and generated README + run: npm run check:readme + + - name: Check active referral links + run: npm run check:links diff --git a/README.md b/README.md index be15723..23ac35e 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,37 @@ -# Raycast Pro — Free 30-Day Trial Codes +# Raycast Pro referral codes -Free **Raycast Pro** referral codes. Each code gives you a **30-day free trial** of Raycast Pro — no credit card required to start. +Active, one-time Raycast Pro referral codes. A successful referral gives a new user a 30-day Pro trial. -## What You Get +## Active codes -- Raycast AI (ChatGPT, GPT-4 built-in) -- Cloud Sync across multiple Macs -- Custom Themes -- Pro-only extensions and features +| # | Code | Link | Added | +|---:|---|---|---| +| 1 | `66957497` | [Claim 30 days free](https://www.raycast.com/hey/66957497) | 2026-02-22 | + +Each code can be claimed once. If a link no longer works, please [open an issue](https://github.com/erkcet/RayCast/issues/new). -## Referral Codes +## What the trial includes + +- Raycast AI +- Cloud Sync +- Custom Themes +- Other Raycast Pro features -Grab any unused code below. Each code works once — first come, first served. +## Maintaining this list -| # | Code | Link | -|---|------|------| -| 1 | `0b72d041` | [Claim](https://www.raycast.com/hey/0b72d041) | -| 2 | `21c44ecf` | [Claim](https://www.raycast.com/hey/21c44ecf) | -| 3 | `360d78b1` | [Claim](https://www.raycast.com/hey/360d78b1) | -| 4 | `52b57f8f` | [Claim](https://www.raycast.com/hey/52b57f8f) | -| 5 | `66957497` | [Claim](https://www.raycast.com/hey/66957497) | -| 6 | `7045f453` | [Claim](https://www.raycast.com/hey/7045f453) | -| 7 | `75b6805d` | [Claim](https://www.raycast.com/hey/75b6805d) | -| 8 | `b1927664` | [Claim](https://www.raycast.com/hey/b1927664) | -| 9 | `b83bc9c7` | [Claim](https://www.raycast.com/hey/b83bc9c7) | +`data/codes.json` is the source of truth. To add or remove a code: -## How It Works +1. Edit `data/codes.json`. +2. Run `npm run readme`. +3. Run `npm test`. +4. Commit both the data and generated README. -1. Click any **Claim** link above -2. Sign up or log in to Raycast -3. Your 30-day Pro trial activates automatically +GitHub Actions checks data and README consistency on every change. A daily scheduled check also verifies that each listed URL still serves a Raycast referral page. -## What is Raycast? +## Referral disclosure -[Raycast](https://www.raycast.com) is a productivity app for macOS that replaces Spotlight. It lets you control tools, scripts, and shortcuts from a single launcher. The Pro plan adds AI chat, cloud sync, custom themes, and more. +Raycast states that the person sharing a code may receive a $10 account credit if the referred user finishes the trial and becomes an active Pro subscriber. ---- +## About Raycast -Codes are updated periodically. Star this repo to get notified when new codes are added. +[Raycast](https://www.raycast.com) is a productivity launcher for macOS and Windows. Learn more on the [Raycast Pro page](https://www.raycast.com/pro). diff --git a/data/codes.json b/data/codes.json new file mode 100644 index 0000000..9b187ef --- /dev/null +++ b/data/codes.json @@ -0,0 +1,8 @@ +{ + "codes": [ + { + "code": "66957497", + "addedAt": "2026-02-22" + } + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3e3b8d6 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "raycast-referral-codes", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "readme": "node scripts/render-readme.mjs", + "check:readme": "node scripts/render-readme.mjs --check", + "check:links": "node scripts/check-links.mjs", + "test": "npm run check:readme && npm run check:links" + }, + "engines": { + "node": ">=20" + } +} diff --git a/scripts/check-links.mjs b/scripts/check-links.mjs new file mode 100644 index 0000000..6f779c5 --- /dev/null +++ b/scripts/check-links.mjs @@ -0,0 +1,38 @@ +import { loadCodes } from "./lib.mjs"; + +const codes = await loadCodes(); +const failures = []; + +for (const { code } of codes) { + const url = `https://www.raycast.com/hey/${code}`; + + try { + const response = await fetch(url, { + headers: { "user-agent": "erkcet/RayCast link checker" }, + redirect: "follow", + signal: AbortSignal.timeout(15_000), + }); + const body = await response.text(); + const isReferralPage = + response.ok && + /sent you one month free of Raycast Pro/i.test(body); + + if (!isReferralPage) { + failures.push(`${code}: HTTP ${response.status}, active referral marker missing`); + console.error(`✗ ${code}`); + } else { + console.log(`✓ ${code}`); + } + } catch (error) { + failures.push(`${code}: ${error.message}`); + console.error(`✗ ${code}`); + } +} + +if (failures.length > 0) { + console.error("\nInvalid or unavailable referral links:"); + failures.forEach((failure) => console.error(`- ${failure}`)); + process.exit(1); +} + +console.log(`\nChecked ${codes.length} active referral link(s).`); diff --git a/scripts/lib.mjs b/scripts/lib.mjs new file mode 100644 index 0000000..2fd8e90 --- /dev/null +++ b/scripts/lib.mjs @@ -0,0 +1,40 @@ +import { readFile } from "node:fs/promises"; + +export const CODES_FILE = new URL("../data/codes.json", import.meta.url); + +export async function loadCodes() { + const data = JSON.parse(await readFile(CODES_FILE, "utf8")); + + if (!data || !Array.isArray(data.codes)) { + throw new Error("data/codes.json must contain a codes array"); + } + + const seen = new Set(); + + return data.codes.map((entry, index) => { + if (!entry || typeof entry !== "object") { + throw new Error(`codes[${index}] must be an object`); + } + + const { code, addedAt } = entry; + + if (typeof code !== "string" || !/^[a-z0-9]{8}$/i.test(code)) { + throw new Error(`codes[${index}].code must be an 8-character code`); + } + + if (seen.has(code.toLowerCase())) { + throw new Error(`Duplicate code: ${code}`); + } + seen.add(code.toLowerCase()); + + if ( + typeof addedAt !== "string" || + !/^\d{4}-\d{2}-\d{2}$/.test(addedAt) || + Number.isNaN(Date.parse(`${addedAt}T00:00:00Z`)) + ) { + throw new Error(`codes[${index}].addedAt must be a valid YYYY-MM-DD date`); + } + + return { code, addedAt }; + }); +} diff --git a/scripts/render-readme.mjs b/scripts/render-readme.mjs new file mode 100644 index 0000000..6b11ff5 --- /dev/null +++ b/scripts/render-readme.mjs @@ -0,0 +1,67 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { loadCodes } from "./lib.mjs"; + +const README_FILE = new URL("../README.md", import.meta.url); +const codes = await loadCodes(); +const rows = + codes.length > 0 + ? codes + .map( + ({ code, addedAt }, index) => + `| ${index + 1} | \`${code}\` | [Claim 30 days free](https://www.raycast.com/hey/${code}) | ${addedAt} |`, + ) + .join("\n") + : "| — | — | No active codes right now | — |"; + +const content = `# Raycast Pro referral codes + +Active, one-time Raycast Pro referral codes. A successful referral gives a new user a 30-day Pro trial. + +## Active codes + +| # | Code | Link | Added | +|---:|---|---|---| +${rows} + +Each code can be claimed once. If a link no longer works, please [open an issue](https://github.com/erkcet/RayCast/issues/new). + +## What the trial includes + +- Raycast AI +- Cloud Sync +- Custom Themes +- Other Raycast Pro features + +## Maintaining this list + +\`data/codes.json\` is the source of truth. To add or remove a code: + +1. Edit \`data/codes.json\`. +2. Run \`npm run readme\`. +3. Run \`npm test\`. +4. Commit both the data and generated README. + +GitHub Actions checks data and README consistency on every change. A daily scheduled check also verifies that each listed URL still serves a Raycast referral page. + +## Referral disclosure + +Raycast states that the person sharing a code may receive a $10 account credit if the referred user finishes the trial and becomes an active Pro subscriber. + +## About Raycast + +[Raycast](https://www.raycast.com) is a productivity launcher for macOS and Windows. Learn more on the [Raycast Pro page](https://www.raycast.com/pro). +`; + +if (process.argv.includes("--check")) { + const current = await readFile(README_FILE, "utf8"); + + if (current !== content) { + console.error("README.md is out of date. Run: npm run readme"); + process.exit(1); + } + + console.log("README.md is up to date."); +} else { + await writeFile(README_FILE, content); + console.log(`README.md updated with ${codes.length} active code(s).`); +}