המדריך השלם למתחילים - מאיסוף נתונים פשוט ועד שיח חכם עם בינה מלאכותית (AI) דרך הטלפון.
צרו Repository חדש בגיטאהב והעלו לתוכו את הקבצים הבאים (שימו לב לשמות המדויקים):
const express = require('express');
const cors = require('cors');
const { GoogleSpreadsheet } = require('google-spreadsheet');
const { JWT } = require('google-auth-library');
const path = require('path');
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));
const SHEET_ID = process.env.SHEET_ID;
const GOOGLE_SERVICE_ACCOUNT = process.env.GOOGLE_SERVICE_ACCOUNT;
async function getDoc() {
if (!GOOGLE_SERVICE_ACCOUNT || !SHEET_ID) {
throw new Error("חסרים נתוני התחברות לגוגל");
}
const credentials = JSON.parse(GOOGLE_SERVICE_ACCOUNT);
const serviceAccountAuth = new JWT({
email: credentials.client_email,
key: credentials.private_key,
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
});
const doc = new GoogleSpreadsheet(SHEET_ID, serviceAccountAuth);
await doc.loadInfo();
return doc;
}
app.all('/api/ymotAddText', async (req, res) => {
try {
const id = req.query.new_id;
const content = req.query.new_content;
if (!id || !content) return res.send("id_list_message=t-שגיאה, חסרים נתונים");
const doc = await getDoc();
const sheet = doc.sheetsByIndex[0];
await sheet.addRow({ ID: id, Content: content });
res.send("id_list_message=t-הנתונים נשמרו בהצלחה למערכת");
} catch (error) { res.send("id_list_message=t-חלה שגיאה בשמירת הנתונים"); }
});
app.get('/api/ymotGetText', async (req, res) => {
try {
const id = req.query.id_to_search;
if (!id) return res.send("id_list_message=t-לא התקבל מזהה לחיפוש");
const doc = await getDoc();
const sheet = doc.sheetsByIndex[0];
const rows = await sheet.getRows();
const row = rows.find(r => r.get('ID') === String(id));
if (row) res.send(`id_list_message=t-${row.get('Content')}`);
else res.send("id_list_message=t-המזהה לא נמצא באקסל");
} catch (error) { res.send("id_list_message=t-חלה שגיאת מערכת בחיפוש"); }
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
module.exports = app;
{
"name": "ymot-texts-api",
"version": "1.0.0",
"main": "server.js",
"scripts": { "start": "node server.js" },
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"google-auth-library": "^9.0.0",
"google-spreadsheet": "^4.1.1"
}
}
{
"version": 2,
"builds": [{ "src": "server.js", "use": "@vercel/node" }],
"rewrites": [{ "source": "/(.*)", "destination": "/server.js" }]
}
SHEET_ID : המזהה מהשלב הראשון.GOOGLE_SERVICE_ACCOUNT : כל הטקסט שמופיע בקובץ ה-JSON (המפתח).עברו לניהול הקו שלכם והגדירו בקובץ ext.ini:
type=api api_link=https://YOUR-APP.vercel.app/api/ymotAddText api_000=new_id,yes,10,1,10,HebrewKeyboard,no,no,no,no,no,no,no,no,yes api_001=new_content,yes,100,1,10,HebrewKeyboard,no,no,no,no,no,no,no,no,yes
type=api api_link=https://YOUR-APP.vercel.app/api/ymotGetText api_000=id_to_search,yes,10,1,10,HebrewKeyboard,no,no,no,no,no,no,no,no,yes
YOUR-APP.vercel.app בכתובת שקיבלתם מ-Vercel!
במערכת הזו, המאזין יכנס לשלוחה אחת ויקליד שאלה (למשל: "מי היה הרמבם?"). השאלה נשלחת לגיליון גוגל שלכם. ברגע שהשאלה נוחתת בגיליון, מופעל קוד סודי ששולח את השאלה למוח של הבינה המלאכותית (Gemini). הבינה המלאכותית כותבת תשובה בגיליון, והמאזין עובר לשלוחה השנייה כדי לשמוע את התשובה שמקריא לו הרובוט של ימות המשיח!
// הכניסו כאן את מפתח ה-API שלכם מ-Google AI Studio
const GEMINI_API_KEY = "הכניסו_כאן_את_המפתח_שלכם";
function processNewQuestions(e) {
// פונקציה זו תופעל בכל פעם ש-Vercel מכניס שאלה חדשה
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("מענה AI");
if (!sheet) return;
const lastRow = sheet.getLastRow();
// אם אין שאלות, לא לעשות כלום
if (lastRow <= 1) return;
// נקרא את השאלה האחרונה שנכנסה
const questionRange = sheet.getRange(lastRow, 3); // עמודה C היא השאלה
const answerRange = sheet.getRange(lastRow, 4); // עמודה D היא התשובה המלאה
const cleanedAnswerRange = sheet.getRange(lastRow, 5); // עמודה E היא התשובה הנקייה
const question = questionRange.getValue();
const currentAnswer = answerRange.getValue();
// אם יש שאלה ועדיין אין תשובה - נפעיל את ה-AI
if (question && !currentAnswer) {
try {
const prompt = "אתה עוזר חכם. ענה בקצרה ובשפה ברורה על השאלה הבאה: " + question;
const aiResponseText = callGemini(prompt);
const cleanedText = cleanText(aiResponseText); // ניקוי טקסט לימות המשיח
// כתיבת התשובות חזרה לגיליון
answerRange.setValue(aiResponseText);
cleanedAnswerRange.setValue(cleanedText);
logEntry(lastRow, prompt, "הצלחה", "תשובה התקבלה", aiResponseText);
} catch (error) {
answerRange.setValue("שגיאה בקבלת תשובה.");
cleanedAnswerRange.setValue("שגיאה.");
logEntry(lastRow, question, "שגיאה", error.toString(), "");
}
}
}
// התחברות למוח של גוגל - Gemini
function callGemini(prompt) {
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`;
const payload = { "contents": [{ "parts": [{ "text": prompt }] }] };
const options = {
method: "post",
contentType: "application/json",
payload: JSON.stringify(payload)
};
const response = UrlFetchApp.fetch(url, options);
const json = JSON.parse(response.getContentText());
return json.candidates[0].content.parts[0].text;
}
// מנקה כוכביות וסימנים כדי שהרובוט הטלפוני יקריא ברור
function cleanText(text) {
return text.replace(/[*#_`]/g, "").trim();
}
function logEntry(rowNumber, prompt, status, response, aiText) {
let logSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Logs");
if (!logSheet) { logSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("Logs"); }
logSheet.appendRow([new Date(), rowNumber, prompt, status, response, aiText]);
}
processNewQuestionsמגיליון אלקטרוני (From spreadsheet)בעת שינוי (On change)עכשיו נכין את קוד השרת (server.js) שיקבל את ההקשה מימות המשיח ויעביר אותה לגיליון. שימו לב שקוד זה שונה מהמדריך הבסיסי ומותאם למערכת הבינה המלאכותית שלנו.
const express = require('express');
const cors = require('cors');
const { GoogleSpreadsheet } = require('google-spreadsheet');
const { JWT } = require('google-auth-library');
const path = require('path');
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const SHEET_ID = process.env.SHEET_ID;
const GOOGLE_SERVICE_ACCOUNT = process.env.GOOGLE_SERVICE_ACCOUNT;
async function getDoc() {
if (!GOOGLE_SERVICE_ACCOUNT || !SHEET_ID) {
throw new Error("שגיאת קונפיגורציה: חסרים סודות במשתני הסביבה");
}
const credentials = JSON.parse(GOOGLE_SERVICE_ACCOUNT);
const serviceAccountAuth = new JWT({
email: credentials.client_email,
key: credentials.private_key,
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
});
const doc = new GoogleSpreadsheet(SHEET_ID, serviceAccountAuth);
await doc.loadInfo();
return doc;
}
// שלוחה 1: קבלת השאלה מימות המשיח והכנסתה לגיליון
app.all('/api/ymotAskAI', async (req, res) => {
try {
const question = req.query.question_text;
const phone = req.query.ApiPhone || "לא ידוע";
if (!question) return res.send("id_list_message=t-לא זוהתה הקשה");
const doc = await getDoc();
const sheet = doc.sheetsByTitle["מענה AI"]; // חובה שהגיליון יקרא כך
// מכניסים את השאלה לעמודה C (הגיליון מסדר את העמודות לפי הכותרות)
await sheet.addRow({ "זמן": new Date().toLocaleString(), "מספר שורה": phone, "טקסט השאלה": question });
res.send("id_list_message=t-שאלתך התקבלה המתן מספר שניות לתשובה");
} catch (error) { res.send("id_list_message=t-חלה שגיאה במערכת"); }
});
// שלוחה 2: חיפוש התשובה הנקייה בגיליון והקראתה
app.all('/api/ymotReadAI', async (req, res) => {
try {
const phone = req.query.ApiPhone; // נחפש לפי מספר הטלפון
const doc = await getDoc();
const sheet = doc.sheetsByTitle["מענה AI"];
const rows = await sheet.getRows();
// מחפשים מהסוף להתחלה כדי למצוא את השאלה האחרונה של המשתמש הזה
let userRow = null;
for (let i = rows.length - 1; i >= 0; i--) {
if (rows[i].get("מספר שורה") === String(phone)) {
userRow = rows[i];
break;
}
}
if (userRow && userRow.get("טקסט מסונן")) {
const answer = userRow.get("טקסט מסונן");
res.send(`id_list_message=t-${answer}`);
} else {
res.send("id_list_message=t-התשובה עדיין לא מוכנה נסה שוב");
}
} catch (error) { res.send("id_list_message=t-חלה שגיאה במשיכת התשובה"); }
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
module.exports = app;
קובץ ה- package.json וקובץ ה- vercel.json נשארים בדיוק אותו דבר כמו במדריך הבסיסי (לשונית קודמת).
התהליך זהה לחלוטין למה שלמדנו: מייבאים את המאגר ל-Vercel, שמים את ה-SHEET_ID ואת ה-GOOGLE_SERVICE_ACCOUNT, ומריצים Deploy.
שימו לב: הערך העשירי הושאר ריק כדי לאפשר הקשת טקסט חופשית כבקשתכם, והוספנו המתנה ותיבת סימון כדי להרוויח זמן עד שהבינה המלאכותית תסיים לכתוב בגיליון.
type=api api_link=https://YOUR-APP.vercel.app/api/ymotAskAI ; קבלת הטקסט מהמשתמש - שינוי הערך העשירי לריק כדי לאפשר הקשה api_000=question_text,yes,100,1,10,HebrewKeyboard,no,no,no,,no,no,no,no,yes ; קבלת החלטה לגבי תיבת הסימון ; שינוי זמן המתנה ל-7 שניות, סוג השמעה ל-Digits, וריק בערך העשירי api_001=checkbox_choice,yes,1,1,7,Digits,no,no,no,,no,no,no,no,no api_timeout=20 api_hangup_send=no
אחרי שהמאזין חיכה קצת בשלוחה 1, הלקוח יעבור לשלוחה זו (יש להגדיר מעבר אוטומטי בימות המשיח) וישמע את התשובה.
type=api api_link=https://YOUR-APP.vercel.app/api/ymotReadAI
YOUR-APP.vercel.app לכתובת האמיתית שוורסל נתן לכם!