diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..201d810d8 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,29 @@ +name: Deploy Cloudflare Worker + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: Install dependencies + run: pnpm install + + - name: Build project + run: pnpm run build + + - name: Deploy to Cloudflare Workers + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + run: npx wrangler deploy --config=twilio-neon/wrangler.toml diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 000000000..c8d4e2a27 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +if [ -z "$CF_API_TOKEN" ]; then + echo "Error: CF_API_TOKEN is not set." + exit 1 +fi + +export CLOUDFLARE_API_TOKEN="$CF_API_TOKEN" +echo "Exported CLOUDFLARE_API_TOKEN from CF_API_TOKEN" + +# Run wrangler deploy with your config +pnpm exec wrangler deploy --config=twilio-neon/wrangler.toml diff --git a/twilio-neon/README.md b/twilio-neon/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/twilio-neon/cloudflare/inbox-handler.ts b/twilio-neon/cloudflare/inbox-handler.ts new file mode 100644 index 000000000..5894cbca3 --- /dev/null +++ b/twilio-neon/cloudflare/inbox-handler.ts @@ -0,0 +1,28 @@ +const handler = { + async fetch(request: Request): Promise { + const form = await request.formData(); + const message = (form.get("Body") || "").toString().toLowerCase(); + const sender = form.get("From") || "unknown"; + + let reply = "🤖 Neon Covenant AI: Reply with 'confirm', 'reschedule', or 'help'."; + + if (message.includes("confirm")) { + reply = "✅ Delivery confirmed for Dec 1 at 3pm. Thank you."; + } else if (message.includes("reschedule")) { + reply = "📆 Please reply with your new desired delivery time."; + } else if (message.includes("help")) { + reply = "🧙‍♂️ Support: Visit https://help.neoncovenant.com for assistance."; + } + + const twiml = `${reply}`; + + return new Response(twiml, { + headers: { + "Content-Type": "application/xml", + }, + }); + }, +}; + +export default handler; + diff --git a/twilio-neon/dist/inbox-handler.js b/twilio-neon/dist/inbox-handler.js new file mode 100644 index 000000000..25ca498c7 --- /dev/null +++ b/twilio-neon/dist/inbox-handler.js @@ -0,0 +1,24 @@ +const handler = { + async fetch(request) { + const form = await request.formData(); + const message = (form.get("Body") || "").toString().toLowerCase(); + const sender = form.get("From") || "unknown"; + let reply = "🤖 Neon Covenant AI: Reply with 'confirm', 'reschedule', or 'help'."; + if (message.includes("confirm")) { + reply = "✅ Delivery confirmed for Dec 1 at 3pm. Thank you."; + } + else if (message.includes("reschedule")) { + reply = "📆 Please reply with your new desired delivery time."; + } + else if (message.includes("help")) { + reply = "🧙‍♂️ Support: Visit https://help.neoncovenant.com for assistance."; + } + const twiml = `${reply}`; + return new Response(twiml, { + headers: { + "Content-Type": "application/xml", + }, + }); + }, +}; +export default handler; diff --git a/twilio-neon/git-auto.sh b/twilio-neon/git-auto.sh new file mode 100755 index 000000000..3d3299279 --- /dev/null +++ b/twilio-neon/git-auto.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Commit message (customize this) +COMMIT_MSG="chore: finalize modularization, add tests, linting, and deploy workflow" + +echo "Adding all changes..." +git add . + +echo "Committing with message: $COMMIT_MSG" +git commit -m "$COMMIT_MSG" + +echo "Pulling remote changes with rebase..." +git pull --rebase origin main + +if [ $? -ne 0 ]; then + echo "Rebase failed. Please resolve conflicts, then run 'git rebase --continue'." + exit 1 +fi + +echo "Pushing commits to origin/main..." +git push origin main + +if [ $? -eq 0 ]; then + echo "Push successful! 🎉" +else + echo "Push failed. Check your network or remote repository status." +fi + diff --git a/twilio-neon/inbox-handler.ts b/twilio-neon/inbox-handler.ts new file mode 100644 index 000000000..e2a86080b --- /dev/null +++ b/twilio-neon/inbox-handler.ts @@ -0,0 +1,28 @@ +import { createTwilioClient } from './twilioClient'; +import { generateReply } from './messageProcessor'; + +export default { + async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { + const { searchParams } = new URL(request.url); + const body = await request.text(); + + const params = new URLSearchParams(body); + const incomingMsg = params.get('Body') || ''; + const from = params.get('From') || ''; + + const twilio = createTwilioClient(env.TWILIO_SID, env.TWILIO_TOKEN); + const reply = generateReply(incomingMsg); + + try { + await twilio.messages.create({ + from: env.TO_WHATSAPP_NUMBER, + to: from, + body: reply, + }); + } catch (error) { + return new Response('Failed to send message', { status: 500 }); + } + + return new Response('OK', { status: 200 }); + }, +}; diff --git a/twilio-neon/messageProcessor.ts b/twilio-neon/messageProcessor.ts new file mode 100644 index 000000000..165c4ebba --- /dev/null +++ b/twilio-neon/messageProcessor.ts @@ -0,0 +1,7 @@ +export function generateReply(msg: string): string { + msg = msg.toLowerCase(); + if (msg.includes('confirm')) return '✅ Confirmed. Thank you!'; + if (msg.includes('reschedule')) return '📆 Let us know a new time.'; + if (msg.includes('help')) return '🧠 How can we assist you today?'; + return '🤖 Message received. A representative will reply shortly.'; +} diff --git a/twilio-neon/package.json b/twilio-neon/package.json new file mode 100644 index 000000000..cf1cf2aa0 --- /dev/null +++ b/twilio-neon/package.json @@ -0,0 +1,19 @@ +{ + "name": "twilio-neon", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "build": "tsc", + "deploy": "wrangler deploy", + "send": "node scripts/send-whatsapp.js" + }, + "dependencies": { + "dotenv": "^16.0.0", + "php": "^1.1.0", + "twilio": "^4.0.0" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20250618.0", + "typescript": "^5.8.3" + } +} diff --git a/twilio-neon/scripts/send-whatsapp.js b/twilio-neon/scripts/send-whatsapp.js new file mode 100644 index 000000000..e69de29bb diff --git a/twilio-neon/tsconfig.json b/twilio-neon/tsconfig.json new file mode 100644 index 000000000..91033c31b --- /dev/null +++ b/twilio-neon/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "outDir": "dist", + "moduleResolution": "Node", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true + }, + "include": ["cloudflare"] +} + diff --git a/twilio-neon/twilioClient.ts b/twilio-neon/twilioClient.ts new file mode 100644 index 000000000..43c073601 --- /dev/null +++ b/twilio-neon/twilioClient.ts @@ -0,0 +1,5 @@ +import { Twilio } from 'twilio'; + +export function createTwilioClient(accountSid: string, authToken: string) { + return new Twilio(accountSid, authToken); +} diff --git a/twilio-neon/wrangler.toml b/twilio-neon/wrangler.toml new file mode 100644 index 000000000..037377695 --- /dev/null +++ b/twilio-neon/wrangler.toml @@ -0,0 +1,18 @@ +name = "twilio-neon" +compatibility_date = "2025-06-18" +main = "./dist/index.mjs" # Or wherever your compiled entry point lives + +[build] +command = "npm run build" + +[vars] +TWILIO_ACCOUNT_SID = "your_sid_here" +TWILIO_AUTH_TOKEN = "your_token_here" +# Add other environment variables here + +[triggers] +# Optional: add for Git-based auto deploys, etc. + +[env.production] +name = "twilio-neon-prod" +# Add overrides if you need them in production only