From b2261d9192bf64403b850575d9062da3180e8a43 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 24 Jul 2026 16:38:47 +0300 Subject: [PATCH] Clean env enforcement & add Gemini AI Post Generator --- .env.example | 11 +++ scripts/ai-post-generator.js | 142 +++++++++++++++++++++++++++++++ scripts/meta-ads-manager.js | 25 ++++-- scripts/publish-facebook-post.js | 29 +++++-- scripts/publish-now.js | 29 +++++-- 5 files changed, 217 insertions(+), 19 deletions(-) create mode 100644 .env.example create mode 100644 scripts/ai-post-generator.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..76c7725 --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +# Tripz — Meta & Marketing Environment Variables +# Copy this file to .env on your server and update as needed. + +# Meta API Credentials +META_AD_ACCOUNT_ID=1945325153083644 +META_PAGE_ID=1047354938453381 +META_ACCESS_TOKEN=EAAM8GXpb76wBSCUHNxvd6PpSenb2HjkyyL8scTeocsmGRAk8kriqTqke85AVhQa36NExS0GC3DebnQJsgaXD01kh1oVbHZBRgpzI9RhtqZCmfyTonxwuD9cHngCkilrKiMVRh9YFbZBwWgBhiZB7THMYSIDJpOZAUXmcSjiBD1lvIcuM8HQ0oLkwq1ZBis063FiyQmPcy + +# Gemini AI Credentials +GEMINI_API_KEY=AIzaSyDHp0yXCGWqnd4ynlCCWzDz9Un1EJOKeW8 +GEMINI_MODEL=gemini-flash-lite-latest diff --git a/scripts/ai-post-generator.js b/scripts/ai-post-generator.js new file mode 100644 index 0000000..375536a --- /dev/null +++ b/scripts/ai-post-generator.js @@ -0,0 +1,142 @@ +#!/usr/bin/env node +/** + * Autonomous Gemini AI Post Generator & Facebook Publisher for Tripz + * Analyzes topic / prompt + marketing playbook rules and generates & publishes posts live! + */ + +const fs = require('fs'); +const path = require('path'); + +// Auto-load .env file if present +const envPath = path.resolve(__dirname, '../.env'); +if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf8'); + envContent.split('\n').forEach(line => { + const match = line.match(/^\s*([\w.-]+)\s*=\s*(.*)\s*$/); + if (match) { + const key = match[1]; + let value = match[2].trim().replace(/^['"]|['"]$/g, ''); + if (!process.env[key]) process.env[key] = value; + } + }); +} + +const META_ACCESS_TOKEN = process.env.META_ACCESS_TOKEN; +const META_PAGE_ID = process.env.META_PAGE_ID; +const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +const GEMINI_MODEL = process.env.GEMINI_MODEL || 'gemini-2.0-flash'; + +if (!META_ACCESS_TOKEN) { + console.error('ERROR: META_ACCESS_TOKEN is missing in .env environment file.'); + process.exit(1); +} + +if (!GEMINI_API_KEY) { + console.error('ERROR: GEMINI_API_KEY is missing in .env environment file.'); + process.exit(1); +} + +/** + * Generate Post Content using Gemini API + */ +async function generatePostWithGemini(topic = 'منشور تعريفي عن منصة Tripz ومزايا التملك والشفافية والتوفير') { + console.log(`Generating AI post content with Gemini (${GEMINI_MODEL}) for topic: "${topic}"...`); + + const systemInstruction = `أنت مدير تسويق ومحرر إعلاني محترف لمنصة Tripz (منصة SaaS عالمية لإطلاق تطبيقات نقل الركاب وإدارة أساطيل التكسي بعلامة المشغّل التجارية). +قواعد التسويق الحاكمة (من docs/32): +1. الملكية: علامتك أنت، وتطبيقك باسمك وشعارك، وبياناتك تبقى لك بعقد تصدير مكتوب. +2. الشفافية: أسعارنا معلنة بالكامل دون أسرار ولا مفاجآت في الفاتورة. +3. السرعة: إطلاق خلال 30 يوماً من التوقيع على آبل وجوجل. +4. الإثبات: محرك مجرّب منذ 2019 يخدم أكثر من 6,000 سائق نشط. +5. دعوة للإجراء (CTA): رابط حاسبة التوفير (https://tripz-egypt.com/apps/) ورابط واتساب المباشر (https://wa.me/962798583052). + +اكتب منشوراً جذاباً جداً باللغة العربية، منسقاً بالإيموجيات والعناوين والنقاط الواضحة والهاشتاغات المناسبة.`; + + const url = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_API_KEY}`; + + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + contents: [ + { + role: 'user', + parts: [{ text: `${systemInstruction}\n\nالموضوع المطلوب كتابة منشور عنه:\n${topic}` }] + } + ] + }) + }); + + const data = await res.json(); + if (data.error) { + throw new Error(`Gemini API Error: ${data.error.message}`); + } + + const generatedText = data.candidates?.[0]?.content?.parts?.[0]?.text; + if (!generatedText) { + throw new Error('Gemini API returned an empty response.'); + } + + return generatedText.trim(); +} + +/** + * Publish generated post to Meta Facebook Page + */ +async function publishToFacebook(message) { + console.log('Publishing generated post to Facebook Page...'); + + const url = `https://graph.facebook.com/v19.0/me/feed`; + let res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + access_token: META_ACCESS_TOKEN, + message: message + }) + }); + + let data = await res.json(); + + if (data.error && META_PAGE_ID) { + console.log('Direct /me/feed returned error, fallback to Page ID endpoint:', META_PAGE_ID); + const fallbackUrl = `https://graph.facebook.com/v19.0/${META_PAGE_ID}/feed`; + res = await fetch(fallbackUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + access_token: META_ACCESS_TOKEN, + message: message + }) + }); + data = await res.json(); + } + + if (data.error) { + throw new Error(`Meta Graph API Error [${data.error.code}]: ${data.error.message}`); + } + + return data; +} + +async function main() { + const topic = process.argv[2] || 'منشور تعريفي عن منصة Tripz ومزايا التملك والشفافية والتوفير وحاسبة الأرباح'; + try { + const postContent = await generatePostWithGemini(topic); + console.log('\n--- Generated Post Content ---\n'); + console.log(postContent); + console.log('\n------------------------------\n'); + + const result = await publishToFacebook(postContent); + console.log('🎉 Post Published Successfully to Facebook!'); + console.log('Meta Post ID:', result.id); + } catch (err) { + console.error('❌ Error:', err.message); + } +} + +if (require.main === module) { + main(); +} + +module.exports = { generatePostWithGemini, publishToFacebook }; diff --git a/scripts/meta-ads-manager.js b/scripts/meta-ads-manager.js index 3261a03..debf3db 100644 --- a/scripts/meta-ads-manager.js +++ b/scripts/meta-ads-manager.js @@ -1,11 +1,22 @@ -#!/usr/bin/env node -/** - * Meta Ads Automation Manager for Tripz - * Uses Meta Graph API v19.0 - */ +const fs = require('fs'); +const path = require('path'); -const AD_ACCOUNT_ID = process.env.META_AD_ACCOUNT_ID || '1945325153083644'; -const ACCESS_TOKEN = process.env.META_ACCESS_TOKEN || 'EAAM8GXpb76wBSP6SbqQ1ZAHCcmuPbk8J145t5ZBhVaQJByjEh3GzzpZAZApfVAf81lsiQPFK6oSlWwaZAbletLjQ2nEr1q8zNuNo0XQSbi31jf0VJEHwsW9ksosNlZBKP9KzPlgqt3wm7q5o1wmyUKFnnVu91d3z1b1CfKSSwQk1AfIo0HXZClt5eDL83C5G6ff7eH9CNy3oKUCQC9Wf239IJpYf2YbmvvXADoxKwvy0FTfMZCK0hPn4JMMkuGkqqQJDjDwSwEpszD3diCEGfWrN'; +// Auto-load .env file if present +const envPath = path.resolve(__dirname, '../.env'); +if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf8'); + envContent.split('\n').forEach(line => { + const match = line.match(/^\s*([\w.-]+)\s*=\s*(.*)\s*$/); + if (match) { + const key = match[1]; + let value = match[2].trim().replace(/^['"]|['"]$/g, ''); + if (!process.env[key]) process.env[key] = value; + } + }); +} + +const AD_ACCOUNT_ID = process.env.META_AD_ACCOUNT_ID; +const ACCESS_TOKEN = process.env.META_ACCESS_TOKEN; const API_VERSION = 'v19.0'; const BASE_URL = `https://graph.facebook.com/${API_VERSION}`; diff --git a/scripts/publish-facebook-post.js b/scripts/publish-facebook-post.js index f1f11e5..deb1d4d 100644 --- a/scripts/publish-facebook-post.js +++ b/scripts/publish-facebook-post.js @@ -1,10 +1,27 @@ -#!/usr/bin/env node -/** - * Publish Post to Facebook Page via Meta Graph API v19.0 - */ +const fs = require('fs'); +const path = require('path'); -const ACCESS_TOKEN = process.env.META_ACCESS_TOKEN || 'EAAM8GXpb76wBSP6SbqQ1ZAHCcmuPbk8J145t5ZBhVaQJByjEh3GzzpZAZApfVAf81lsiQPFK6oSlWwaZAbletLjQ2nEr1q8zNuNo0XQSbi31jf0VJEHwsW9ksosNlZBKP9KzPlgqt3wm7q5o1wmyUKFnnVu91d3z1b1CfKSSwQk1AfIo0HXZClt5eDL83C5G6ff7eH9CNy3oKUCQC9Wf239IJpYf2YbmvvXADoxKwvy0FTfMZCK0hPn4JMMkuGkqqQJDjDwSwEpszD3diCEGfWrN'; -const PAGE_ID = process.env.META_PAGE_ID || '61588173848643'; +// Auto-load .env file if present +const envPath = path.resolve(__dirname, '../.env'); +if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf8'); + envContent.split('\n').forEach(line => { + const match = line.match(/^\s*([\w.-]+)\s*=\s*(.*)\s*$/); + if (match) { + const key = match[1]; + let value = match[2].trim().replace(/^['"]|['"]$/g, ''); + if (!process.env[key]) process.env[key] = value; + } + }); +} + +const ACCESS_TOKEN = process.env.META_ACCESS_TOKEN; +const PAGE_ID = process.env.META_PAGE_ID; + +if (!ACCESS_TOKEN) { + console.error('ERROR: META_ACCESS_TOKEN is missing in .env file or environment variables.'); + process.exit(1); +} async function publishPost(message, imageUrl = null) { if (!PAGE_ID) { diff --git a/scripts/publish-now.js b/scripts/publish-now.js index fa9dfce..4e8f6a2 100644 --- a/scripts/publish-now.js +++ b/scripts/publish-now.js @@ -1,10 +1,27 @@ -#!/usr/bin/env node -/** - * Direct Live Publishing Script for Facebook Page - */ +const fs = require('fs'); +const path = require('path'); -const USER_TOKEN = process.env.META_ACCESS_TOKEN || 'EAAM8GXpb76wBSP6SbqQ1ZAHCcmuPbk8J145t5ZBhVaQJByjEh3GzzpZAZApfVAf81lsiQPFK6oSlWwaZAbletLjQ2nEr1q8zNuNo0XQSbi31jf0VJEHwsW9ksosNlZBKP9KzPlgqt3wm7q5o1wmyUKFnnVu91d3z1b1CfKSSwQk1AfIo0HXZClt5eDL83C5G6ff7eH9CNy3oKUCQC9Wf239IJpYf2YbmvvXADoxKwvy0FTfMZCK0hPn4JMMkuGkqqQJDjDwSwEpszD3diCEGfWrN'; -const PAGE_ID = process.env.META_PAGE_ID || '1047354938453381'; +// Auto-load .env file if present +const envPath = path.resolve(__dirname, '../.env'); +if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf8'); + envContent.split('\n').forEach(line => { + const match = line.match(/^\s*([\w.-]+)\s*=\s*(.*)\s*$/); + if (match) { + const key = match[1]; + let value = match[2].trim().replace(/^['"]|['"]$/g, ''); + if (!process.env[key]) process.env[key] = value; + } + }); +} + +const USER_TOKEN = process.env.META_ACCESS_TOKEN; +const PAGE_ID = process.env.META_PAGE_ID; + +if (!USER_TOKEN) { + console.error('ERROR: META_ACCESS_TOKEN is missing in .env file or environment variables.'); + process.exit(1); +} const message = `شركتك تحمل اسمك… وتطبيقك يحمل اسم غيرك؟ 🚘📱