זה הקוד עם זיהוי אוטומטי של המייל של המשתמש, לא צריך להזין מייל
// ============================================================
// מתמחים טופ - דיגסט יומי: תוכנות חדשות שנוצרו + עדכוני AI
// גרסה: 2.2 נקייה (רוטציית מפתחות + מודלי Gemini + עד 350 מילים)
// מקור: https://mitmachim.top/post/1264230
// ============================================================
// הוראות התקנה מהירות:
// 1. כנס ל- script.google.com > פרויקט חדש
// 2. הדבק את הקוד הזה
// 3. ערוך למטה את המייל וה-API KEY
// 4. שמור > הרץ את "הגדרת_טריגר_מתמחים" ואשר הרשאות
// 5. לבדיקה מיידית: הרץ "בדיקת_דיגסט_מתמחים"
// ============================================================
var MM_CONFIG = {
forumBaseUrl: 'https://mitmachim.top',
recentApiUrl: 'https://mitmachim.top/api/recent',
unreadApiUrl: 'https://mitmachim.top/api/unread',
// מזהה אשכול חדשות ה-AI הרשמי
aiTopicId: 95167,
aiTopicUrl: 'https://mitmachim.top/topic/95167/שיתוף-מה-חדש-בבינה-מלאכותית-נספח-עדכוני-פיצ-רים-ודיונים',
// הגבלות
maxTotalWords: 350,
maxSoftwareItems: 8,
maxAiPostsToScan: 15,
triggerHour: 22, // שעה לשליחה יומית
triggerTimeZone: 'Asia/Jerusalem',
email: {
senderName: 'מתמחים טופ - עדכונים יומיים',
subjectPrefix: 'מתמחים טופ: תוכנות חדשות ו-AI - '
},
// אם לא תגדיר מייל, זה ישלח אוטומטית לחשבון שמריץ את הסקריפט
defaultRecipients: '', // אפשר להשאיר ריק - יזהה אוטומטית, או לכתוב 'yourmail@gmail.com'
// מודלי Gemini לפי סדר עדיפות
geminiModels: [
'gemini-2.5-flash',
'gemini-2.5-flash-lite',
'gemini-1.5-flash',
'gemini-1.5-flash-8b'
],
// >>> כאן לערוך - חובה <<<
// אפשר להוסיף עד 6 מפתחות, אם אחד נכשל הוא עובר לבא
apiKeys: [
'כאן לרשום מפתח API - מ-GOOGLE AI STUDIO',
'', '', '', '', ''
],
maxOutputTokens: 1500,
disableThinking: true,
temperature: 0.2,
retryDelayMs: 600
};
// ========== הפונקציה הראשית ==========
function mm_digestMain() {
try {
Logger.log('=== מתמחים טופ - דיגסט יומי מתחיל ===');
if (mm_alreadySentToday()) {
Logger.log('כבר נשלח דיגסט להיום - מדלג');
return;
}
var recipients = mm_getRecipients();
if (recipients.length === 0) {
Logger.log('לא הוגדרו נמענים - מפסיק');
return;
}
var days = ['ראשון','שני','שלישי','רביעי','חמישי','שישי','שבת'];
var todayName = days[new Date().getDay()];
var hebrewDate = mm_hebrewDate();
var dateStr = new Date().toLocaleDateString('he-IL');
// 1. תוכנות חדשות מהיום
Logger.log('--- שלב 1: חיפוש תוכנות חדשות ---');
var softwareItems = mm_fetchAndFilterNewSoftware();
Logger.log('נמצאו %s תוכנות חדשות', softwareItems.length);
// 2. עדכוני AI מהיום
Logger.log('--- שלב 2: שליפת עדכוני AI מאשכול %s ---', MM_CONFIG.aiTopicId);
var aiUpdates = mm_fetchTodayAiThreadUpdates();
Logger.log('נמצאו %s עדכוני AI', aiUpdates.length);
if (softwareItems.length === 0 && aiUpdates.length === 0) {
Logger.log('אין תוכן חדש היום - לא נשלח מייל ריק');
return;
}
// 3. הגבלת מילים
var limited = mm_enforceTotalWordLimit(softwareItems, aiUpdates, MM_CONFIG.maxTotalWords);
softwareItems = limited.softwareItems;
aiUpdates = limited.aiUpdates;
Logger.log('סך מילים בדיגסט: %s / %s', limited.totalWords, MM_CONFIG.maxTotalWords);
// 4. עיצוב ושליחה
var htmlBody = mm_formatDigestEmail(softwareItems, aiUpdates, dateStr, todayName, hebrewDate, limited.totalWords);
var subject = MM_CONFIG.email.subjectPrefix + dateStr + ' (' + softwareItems.length + ' תוכנות, ' + aiUpdates.length + ' עדכוני AI)';
for (var r = 0; r < recipients.length; r++) {
MailApp.sendEmail({
to: recipients[r],
subject: subject,
htmlBody: htmlBody,
name: MM_CONFIG.email.senderName
});
Logger.log('מייל נשלח אל: %s', recipients[r]);
}
for (var s = 0; s < softwareItems.length; s++) {
mm_markTopicSent(softwareItems[s].tid || softwareItems[s].url);
}
mm_markSentToday();
Logger.log('=== דיגסט הסתיים בהצלחה! ===');
} catch (e) {
Logger.log('!!! שגיאה: %s', e.stack || e.message);
}
}
// ========== הגבלת מילים ==========
function mm_countWords(str) {
if (!str) return 0;
return str.trim().split(/\s+/).filter(function(w) { return w.length > 0; }).length;
}
function mm_enforceTotalWordLimit(softwareItems, aiUpdates, maxLimit) {
var limit = maxLimit || 350;
var currentWords = 0;
for (var i = 0; i < softwareItems.length; i++) {
currentWords += mm_countWords(softwareItems[i].softwareName) + mm_countWords(softwareItems[i].summary);
}
for (var j = 0; j < aiUpdates.length; j++) {
currentWords += mm_countWords(aiUpdates[j].headline) + mm_countWords(aiUpdates[j].bulletSummary);
}
if (currentWords <= limit) {
return { softwareItems: softwareItems, aiUpdates: aiUpdates, totalWords: currentWords };
}
Logger.log('חריגת מילים: %s מילים, מקצץ ל-%s', currentWords, limit);
var totalItems = softwareItems.length + aiUpdates.length;
var targetWordsPerItem = Math.floor((limit - 20) / Math.max(1, totalItems));
var trimmedSoftware = [];
var wordsUsed = 0;
for (var s = 0; s < softwareItems.length; s++) {
var item = softwareItems[s];
var words = (item.summary || '').split(/\s+/).filter(Boolean);
if (words.length > targetWordsPerItem) {
item.summary = words.slice(0, targetWordsPerItem).join(' ') + '.';
}
wordsUsed += mm_countWords(item.softwareName) + mm_countWords(item.summary);
trimmedSoftware.push(item);
}
var trimmedAi = [];
for (var a = 0; a < aiUpdates.length; a++) {
var ai = aiUpdates[a];
var remaining = limit - wordsUsed;
if (remaining <= 0) break;
var aiWords = (ai.bulletSummary || '').split(/\s+/).filter(Boolean);
if (aiWords.length > Math.min(targetWordsPerItem, remaining - 5)) {
ai.bulletSummary = aiWords.slice(0, Math.min(targetWordsPerItem, remaining - 5)).join(' ') + '.';
}
wordsUsed += mm_countWords(ai.headline) + mm_countWords(ai.bulletSummary);
trimmedAi.push(ai);
}
return { softwareItems: trimmedSoftware, aiUpdates: trimmedAi, totalWords: wordsUsed };
}
// ========== שליפת תוכנות חדשות ==========
function mm_fetchAndFilterNewSoftware() {
var qualifiedSoftware = [];
var rawTopics = mm_fetchRecentTopics();
var todayStart = mm_getTodayStartTimestamp();
for (var i = 0; i < rawTopics.length; i++) {
if (qualifiedSoftware.length >= MM_CONFIG.maxSoftwareItems) break;
var topic = rawTopics[i];
var tid = String(topic.tid || topic.slug);
var topicTimestamp = topic.timestamp || topic.lastposttime || 0;
if (topicTimestamp < todayStart || mm_isTopicSent(tid)) continue;
var title = (topic.title || '').trim();
if (mm_isObviouslyNotSoftware(title)) continue;
var firstPostContent = mm_fetchTopicFirstPost(topic.tid, topic.slug);
if (!firstPostContent || firstPostContent.length < 30) continue;
try {
var evaluation = mm_evaluateSoftwareWithGemini(title, firstPostContent, topic.categoryName || '');
if (evaluation && evaluation.isNewSoftware) {
qualifiedSoftware.push({
tid: tid,
title: title,
softwareName: evaluation.softwareName || title,
summary: evaluation.summary,
version: evaluation.version || '',
url: MM_CONFIG.forumBaseUrl + '/topic/' + (topic.slug || topic.tid),
author: topic.user ? (topic.user.username || topic.user.displayname) : 'חבר פורום'
});
Logger.log('✓ אושרה תוכנה: %s', title);
} else {
Logger.log('✗ סונן (לא תוכנה חדשה): %s', title);
}
Utilities.sleep(250);
} catch (err) {
Logger.log('שגיאה בניתוח "%s": %s', title, err.message);
}
}
return qualifiedSoftware;
}
function mm_isObviouslyNotSoftware(title) {
var lower = title.toLowerCase();
var skipPhrases = [
'עזרה', 'איך לעשות', 'מישהו יודע', 'למה לא עובד', 'בעיה ב', 'שגיאה',
'מחפש תוכנה', 'האם יש', 'בקשה:', 'דחוף', 'שאלה', 'לא מצליח', 'התקנה נכשלה',
'האם אפשר', 'בירור', 'חוות דעת', 'מחיר', 'קנייה', 'מכירה', 'מחשב נייד'
];
for (var i = 0; i < skipPhrases.length; i++) {
if (lower.indexOf(skipPhrases[i]) !== -1 && lower.indexOf('הורדה') === -1 && lower.indexOf('תוכנה חדשה') === -1) {
return true;
}
}
return false;
}
function mm_fetchRecentTopics() {
try {
var endpoints = [MM_CONFIG.recentApiUrl, MM_CONFIG.unreadApiUrl];
for (var e = 0; e < endpoints.length; e++) {
var jsonStr = mm_fetchUrl(endpoints[e]);
if (jsonStr) {
var data = JSON.parse(jsonStr);
if (data.topics && data.topics.length > 0) return data.topics;
}
}
return [];
} catch (e) {
Logger.log('שגיאה בשליפת נושאים: %s', e.message);
return [];
}
}
function mm_fetchTopicFirstPost(tid, slug) {
try {
var url = MM_CONFIG.forumBaseUrl + '/api/topic/' + (slug || tid);
var jsonStr = mm_fetchUrl(url);
if (!jsonStr) return '';
var data = JSON.parse(jsonStr);
if (data.posts && data.posts.length > 0) {
return mm_stripHtml(data.posts[0].content || '').substring(0, 3000);
}
return '';
} catch (e) {
return '';
}
}
function mm_evaluateSoftwareWithGemini(title, content, category) {
var prompt = 'אתה מסנן קפדני של עדכוני פורום מחשבים. המטרה: לזהות אך ורק תוכנות, אפליקציות, כלים או תוספים חדשים שנוצרו או פורסמו להורדה.\n'
+ 'כללים מחמירים:\n'
+ '1. אם הפוסט הוא שאלה, בקשת עזרה, בעיה טכנית, שאלת ייעוץ, בקשת תוכנה (למשל "מחפש תוכנה ל..."), או דיון כללי - החזר מיד isNewSoftware: false!\n'
+ '2. רק אם מדובר ביוצר שמשתף כלי/תוכנה שהוא פיתח, או שחרור של תוכנה/כלי חדש להורדה - החזר isNewSoftware: true.\n'
+ '3. ספק סיכום קצרצר ותמציתי ביותר בעברית (משפט אחד או שניים בלבד, עד 25 מילים). המייל כולו מוגבל ל-350 מילים!\n'
+ '4. החזר אך ורק JSON תקין במבנה הבא ללא שום טקסט נוסף:\n'
+ '{\n'
+ ' "isNewSoftware": true/false,\n'
+ ' "softwareName": "שם התוכנה או הכלי",\n'
+ ' "summary": "סיכום תמציתי של משפט עד שניים.",\n'
+ ' "version": "גרסה אם קיימת"\n'
+ '}\n\n'
+ 'כותרת הפוסט: ' + title + '\n'
+ 'קטגוריה: ' + category + '\n'
+ 'תוכן הפוסט:\n' + content.substring(0, 2000);
var res = mm_callGemini(prompt);
var cleanJson = res.text.replace(/```json/gi, '').replace(/```/g, '').trim();
try {
return JSON.parse(cleanJson);
} catch (err) {
var jsonMatch = cleanJson.match(/{[\s\S]*}/);
if (jsonMatch) return JSON.parse(jsonMatch[0]);
return null;
}
}
// ========== שליפת עדכוני AI ==========
function mm_fetchTodayAiThreadUpdates() {
try {
var todayStart = mm_getTodayStartTimestamp();
var topicApiUrl = MM_CONFIG.forumBaseUrl + '/api/topic/' + MM_CONFIG.aiTopicId;
var jsonStr = mm_fetchUrl(topicApiUrl);
if (!jsonStr) return [];
var data = JSON.parse(jsonStr);
var posts = data.posts || [];
var postCount = data.postcount || 0;
if (postCount > 20 && data.pagination && data.pagination.pageCount > 1) {
var lastPageUrl = topicApiUrl + '?page=' + data.pagination.pageCount;
var lastPageJson = mm_fetchUrl(lastPageUrl);
if (lastPageJson) {
var lastPageData = JSON.parse(lastPageJson);
if (lastPageData.posts && lastPageData.posts.length > 0) {
posts = lastPageData.posts;
}
}
}
var todayPosts = [];
for (var i = posts.length - 1; i >= 0 && todayPosts.length < MM_CONFIG.maxAiPostsToScan; i--) {
var p = posts[i];
var postTime = p.timestamp || 0;
if (postTime >= todayStart) {
var cleanText = mm_stripHtml(p.content || '');
if (cleanText.length > 40) {
todayPosts.push({
pid: p.pid,
index: p.index,
user: p.user ? (p.user.username || p.user.displayname) : 'משתמש',
content: cleanText.substring(0, 2500)
});
}
}
}
if (todayPosts.length === 0) return [];
var combinedContent = todayPosts.map(function(item, idx) {
return '[' + (idx + 1) + '] מאת ' + item.user + ':\n' + item.content;
}).join('\n\n--- פוסט הבא ---\n\n');
var prompt = 'להלן פוסטים חדשים שפורסמו היום בפורום מתמחים טופ באשכול עדכוני בינה מלאכותית (AI).\n'
+ 'משימתך:\n'
+ '1. סנן שיחות סרק, שאלות טכניות פרטיות או תודות.\n'
+ '2. חלץ וסכם את החדשות והפיצ\'רים האמיתיים של בינה מלאכותית שנחשפו היום.\n'
+ '3. מגבלת מילים קריטית: סך כל המילים בכל הדיגסט מוגבל לעד 350 מילים! כתוב סיכום של משפט אחד בלבד (עד 20 מילים לפריט).\n'
+ '4. החזר אך ורק מערך JSON תקין ללא שום טקסט נוסף:\n'
+ '[\n'
+ ' {\n'
+ ' "headline": "כותרת קצרה של החידוש",\n'
+ ' "bulletSummary": "משפט תמציתי ומדויק על מה שהתחדש.",\n'
+ ' "sourceUser": "שם המשתמש ששיתף"\n'
+ ' }\n'
+ ']\n\n'
+ combinedContent;
var res = mm_callGemini(prompt);
var cleanJson = res.text.replace(/```json/gi, '').replace(/```/g, '').trim();
var parsedList = [];
try {
parsedList = JSON.parse(cleanJson);
} catch (e) {
var match = cleanJson.match(/\[[\s\S]*\]/);
if (match) parsedList = JSON.parse(match[0]);
}
for (var k = 0; k < parsedList.length; k++) {
parsedList[k].url = MM_CONFIG.aiTopicUrl;
}
return parsedList;
} catch (err) {
Logger.log('שגיאה בשליפת אשכול AI: %s', err.message);
return [];
}
}
// ========== עיצוב המייל ==========
function mm_formatDigestEmail(softwareItems, aiUpdates, dateStr, todayName, hebrewDate, totalWords) {
var softwareHtml = '';
if (softwareItems.length > 0) {
softwareHtml += '<div style="margin-bottom:24px;">'
+ '<div style="display:flex; align-items:center; margin-bottom:12px; border-bottom:2px solid #3b82f6; padding-bottom:6px;">'
+ '<span style="background:#3b82f6; color:#ffffff; font-size:11px; font-weight:bold; padding:2px 8px; border-radius:4px; margin-left:6px;">חדש בקהילה</span>'
+ '<h2 style="color:#0f172a; font-size:17px; margin:0; font-weight:800;">🚀 תוכנות וכלים חדשים שפותחו/שוחררו היום (' + softwareItems.length + ')</h2>'
+ '</div>';
for (var i = 0; i < softwareItems.length; i++) {
var item = softwareItems[i];
softwareHtml += '<div style="background:#f8fafc; border:1px solid #e2e8f0; border-radius:8px; padding:14px 16px; margin-bottom:12px;">'
+ '<div style="display:flex; justify-content:space-between; align-items:flex-start; margin-bottom:4px;">'
+ '<h3 style="color:#1e3a8a; font-size:15px; font-weight:700; margin:0; line-height:1.4;">' + (i + 1) + '. ' + item.softwareName + '</h3>'
+ (item.version ? '<span style="background:#e0e7ff; color:#3730a3; font-size:11px; font-weight:700; padding:2px 6px; border-radius:4px;">' + item.version + '</span>' : '')
+ '</div>'
+ '<p style="color:#334155; font-size:14px; line-height:1.65; margin:0 0 8px;">' + item.summary + '</p>'
+ '<div style="display:flex; justify-content:space-between; align-items:center; font-size:12px; color:#64748b;">'
+ '<span>שותף ע"י: <strong>' + item.author + '</strong></span>'
+ '<a href="' + item.url + '" target="_blank" style="color:#2563eb; text-decoration:none; font-weight:bold;">לפוסט המלא ולהורדה ←</a>'
+ '</div></div>';
}
softwareHtml += '</div>';
} else {
softwareHtml += '<div style="padding:12px; background:#f8fafc; border-radius:8px; border:1px dashed #cbd5e1; color:#64748b; font-size:13px; margin-bottom:20px; text-align:center;">לא פורסמו היום תוכנות חדשות נוספות שפותחו בקהילה.</div>';
}
var aiHtml = '';
if (aiUpdates.length > 0) {
aiHtml += '<div style="margin-bottom:20px;">'
+ '<div style="display:flex; align-items:center; margin-bottom:12px; border-bottom:2px solid #8b5cf6; padding-bottom:6px;">'
+ '<span style="background:#8b5cf6; color:#ffffff; font-size:11px; font-weight:bold; padding:2px 8px; border-radius:4px; margin-left:6px;">אשכול AI</span>'
+ '<h2 style="color:#0f172a; font-size:17px; margin:0; font-weight:800;">🤖 מה חדש בבינה מלאכותית היום (' + aiUpdates.length + ')</h2>'
+ '</div>';
for (var j = 0; j < aiUpdates.length; j++) {
var ai = aiUpdates[j];
aiHtml += '<div style="border-right:3px solid #8b5cf6; background:#faf5ff; padding:10px 14px; margin-bottom:10px; border-radius:0 6px 6px 0;">'
+ '<h4 style="color:#581c87; font-size:14px; font-weight:700; margin:0 0 3px;">' + (j + 1) + '. ' + ai.headline + '</h4>'
+ '<p style="color:#334155; font-size:13.5px; line-height:1.6; margin:0 0 5px;">' + ai.bulletSummary + '</p>'
+ '<div style="font-size:12px; color:#7e22ce;">'
+ (ai.sourceUser ? '<span>משתף: ' + ai.sourceUser + ' • </span>' : '')
+ '<a href="' + ai.url + '" target="_blank" style="color:#7e22ce; font-weight:bold; text-decoration:none;">צפה באשכול ה-AI ←</a>'
+ '</div></div>';
}
aiHtml += '</div>';
} else {
aiHtml += '<div style="padding:12px; background:#f8fafc; border-radius:8px; border:1px dashed #cbd5e1; color:#64748b; font-size:13px; margin-bottom:20px; text-align:center;">לא היו עדכוני AI חדשים היום באשכול הייעודי.</div>';
}
return '<!DOCTYPE html><html dir="rtl" lang="he"><head><meta charset="UTF-8"></head>'
+ '<body style="font-family:Arial,Helvetica,sans-serif; direction:rtl; text-align:right; max-width:640px; margin:0 auto; padding:16px; background-color:#f1f5f9;">'
+ '<div style="background:linear-gradient(135deg,#0f172a 0%,#1e1b4b 100%); color:#ffffff; padding:24px 20px; border-radius:12px 12px 0 0; text-align:center;">'
+ '<span style="display:inline-block; background:rgba(59,130,246,0.3); color:#93c5fd; font-size:11px; font-weight:bold; padding:3px 10px; border-radius:999px; margin-bottom:8px;">מתמחים טופ • דיגסט יומי תמציתי</span>'
+ '<h1 style="margin:2px 0 5px; font-size:22px; font-weight:800;">חדשות תוכנה ובינה מלאכותית</h1>'
+ '<p style="margin:0; font-size:13px; opacity:0.85;">יום ' + todayName + ', ' + dateStr + ' • ' + totalWords + ' מילים בדיגסט</p>'
+ '</div>'
+ '<div style="background:#ffffff; padding:22px 20px; border:1px solid #e2e8f0; border-top:none;">'
+ softwareHtml + aiHtml
+ '</div>'
+ '<div style="background:#f8fafc; padding:14px 20px; border-radius:0 0 12px 12px; border:1px solid #e2e8f0; border-top:none; text-align:center; font-size:12px; color:#64748b;">'
+ 'נשלח אוטומטית ממתמחים טופ • ' + hebrewDate + ' • דיגסט קצר (עד 350 מילים) מבוסס Gemini'
+ '</div></body></html>';
}
// ========== קריאה ל-Gemini ==========
function mm_getAllApiKeys() {
var props = PropertiesService.getScriptProperties();
var scriptPropKey = props.getProperty('MM_GEMINI_API_KEY') || props.getProperty('AI7_GEMINI_API_KEY');
var allKeys = [];
if (scriptPropKey) allKeys.push(scriptPropKey);
if (MM_CONFIG.apiKeys && MM_CONFIG.apiKeys.length > 0) {
for (var i = 0; i < MM_CONFIG.apiKeys.length; i++) {
var k = MM_CONFIG.apiKeys[i];
if (k && allKeys.indexOf(k) === -1) allKeys.push(k);
}
}
return allKeys;
}
function mm_callGemini(prompt) {
var models = MM_CONFIG.geminiModels;
var keys = mm_getAllApiKeys();
var lastError = null;
if (keys.length === 0) throw new Error('לא נמצאו מפתחות Gemini מוגדרים');
for (var m = 0; m < models.length; m++) {
var model = models[m];
for (var k = 0; k < keys.length; k++) {
var apiKey = keys[k];
try {
var resultText = mm_geminiApiCall(prompt, model, apiKey);
Logger.log('הצלחה עם מודל %s מפתח #%s', model, (k + 1));
return { text: resultText, model: model, keyIndex: k };
} catch (err) {
lastError = err;
Logger.log('מפתח #%s נכשל עם מודל %s (%s)', (k + 1), model, err.message);
if (k < keys.length - 1) Utilities.sleep(MM_CONFIG.retryDelayMs);
}
}
Logger.log('כל המפתחות נכשלו עבור מודל %s, עובר למודל הבא', model);
}
throw new Error('כל המודלים וכל המפתחות נכשלו. שגיאה אחרונה: ' + (lastError ? lastError.message : 'לא ידועה'));
}
function mm_geminiApiCall(prompt, model, apiKey) {
if (!apiKey) throw new Error('מפתח API ריק');
var url = 'https://generativelanguage.googleapis.com/v1beta/models/' + model + ':generateContent?key=' + apiKey;
var generationConfig = {
temperature: MM_CONFIG.temperature || 0.2,
maxOutputTokens: MM_CONFIG.maxOutputTokens || 1500
};
if (MM_CONFIG.disableThinking) {
generationConfig.thinkingConfig = { thinkingBudget: 0 };
}
var body = {
contents: [{ parts: [{ text: prompt }] }],
generationConfig: generationConfig
};
var headers = {
'Content-Type': 'application/json',
'x-goog-api-key': apiKey
};
if (apiKey.indexOf('AQ.') === 0 || apiKey.indexOf('ya29.') === 0) {
headers['Authorization'] = 'Bearer ' + apiKey;
}
var options = {
method: 'post',
contentType: 'application/json',
headers: headers,
payload: JSON.stringify(body),
muteHttpExceptions: true
};
var r = UrlFetchApp.fetch(url, options);
var code = r.getResponseCode();
if (code >= 300) {
throw new Error('Gemini API HTTP ' + code + ': ' + r.getContentText().substring(0, 200));
}
var data = JSON.parse(r.getContentText());
var candidate = data.candidates && data.candidates[0];
if (!candidate || !candidate.content || !candidate.content.parts) {
throw new Error('תשובה ריקה מ-Gemini');
}
var textParts = [];
for (var p = 0; p < candidate.content.parts.length; p++) {
var part = candidate.content.parts[p];
if (part.text && !part.thought) textParts.push(part.text);
}
var out = textParts.join('').trim();
if (!out && candidate.content.parts[0] && candidate.content.parts[0].text) {
out = candidate.content.parts[0].text.trim();
}
if (!out) throw new Error('טקסט ריק מ-Gemini');
return out;
}
// ========== עזרים ==========
function mm_getTodayStartTimestamp() {
var now = new Date();
var start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
return start.getTime();
}
function mm_todayKey() {
return Utilities.formatDate(new Date(), MM_CONFIG.triggerTimeZone, 'yyyy-MM-dd');
}
function mm_alreadySentToday() {
var props = PropertiesService.getScriptProperties();
return props.getProperty('MM_LAST_SENT_DATE') === mm_todayKey();
}
function mm_markSentToday() {
PropertiesService.getScriptProperties().setProperty('MM_LAST_SENT_DATE', mm_todayKey());
}
function mm_getRecipients() {
var props = PropertiesService.getScriptProperties();
var raw = props.getProperty('MM_RECIPIENTS') || MM_CONFIG.defaultRecipients;
// אם לא הוגדר מייל או שעדיין יש פלסהולדר - קח אוטומטית את המייל של החשבון שמריץ
if (!raw || raw.trim() === '' || raw.indexOf('כאן לרשום') !== -1) {
try {
var autoEmail = Session.getEffectiveUser().getEmail();
if (!autoEmail) autoEmail = Session.getActiveUser().getEmail();
if (autoEmail) {
Logger.log('לא הוגדר מייל - משתמש אוטומטית במייל החשבון: %s', autoEmail);
return [autoEmail];
}
} catch(e) {
Logger.log('לא הצלחתי לזהות מייל אוטומטי: %s', e.message);
}
}
return raw.split(',').map(function(s) { return s.trim(); }).filter(function(s) { return s.length > 0 && s.indexOf('כאן לרשום') === -1; });
}
function mm_getSentTopics() {
var raw = PropertiesService.getScriptProperties().getProperty('MM_SENT_TOPICS');
if (!raw) return {};
try { return JSON.parse(raw); } catch (e) { return {}; }
}
function mm_markTopicSent(key) {
var sent = mm_getSentTopics();
sent[key] = new Date().getTime();
var cutoff = new Date().getTime() - (14 * 24 * 60 * 60 * 1000);
for (var k in sent) {
if (sent[k] < cutoff) delete sent[k];
}
PropertiesService.getScriptProperties().setProperty('MM_SENT_TOPICS', JSON.stringify(sent));
}
function mm_isTopicSent(key) {
return !!mm_getSentTopics()[key];
}
function mm_clearHistory() {
PropertiesService.getScriptProperties().deleteProperty('MM_SENT_TOPICS');
PropertiesService.getScriptProperties().deleteProperty('MM_LAST_SENT_DATE');
Logger.log('היסטוריה אופסה');
}
function mm_fetchUrl(url) {
try {
var r = UrlFetchApp.fetch(url, {
method: 'get',
muteHttpExceptions: true,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'Accept': 'application/json, text/html;q=0.9,*/*;q=0.8',
'Accept-Language': 'he-IL,he;q=0.9,en-US;q=0.8,en;q=0.7'
}
});
if (r.getResponseCode() >= 300) return null;
return r.getContentText('UTF-8');
} catch (e) {
return null;
}
}
function mm_stripHtml(s) {
if (!s) return '';
return s.replace(/<[^>]+>/g, ' ')
.replace(/ /g, ' ')
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, "'")
.replace(/&#(\d+);/g, function(_, n) { return String.fromCharCode(parseInt(n)); })
.replace(/\s+/g, ' ').trim();
}
function mm_hebrewDate() {
try {
var now = new Date();
var month = now.getMonth() + 1;
var hebrewMonths = ['שבט','אדר','ניסן','אייר','סיוון','תמוז','אב','אלול','תשרי','חשוון','כסלו','טבת'];
return now.getDate() + ' ב' + hebrewMonths[month - 1] + ' ' + (now.getFullYear() + 3760);
} catch (e) {
return new Date().toLocaleDateString('he-IL');
}
}
// ========== פונקציות הפעלה ידנית ==========
function בדיקת_דיגסט_מתמחים() {
PropertiesService.getScriptProperties().deleteProperty('MM_LAST_SENT_DATE');
Logger.log('מתחיל בדיקה ידנית...');
mm_digestMain();
return 'בדיקה הושלמה - בדוק לוג ואימייל!';
}
function איפוס_נעילת_מתמחים() {
mm_clearHistory();
return 'נעילת מתמחים טופ והיסטוריית הפוסטים אופסו';
}
function הגדרת_טריגר_מתמחים() {
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
if (triggers[i].getHandlerFunction() === 'mm_digestMain') {
ScriptApp.deleteTrigger(triggers[i]);
}
}
ScriptApp.newTrigger('mm_digestMain').timeBased().everyDays(1).atHour(MM_CONFIG.triggerHour).inTimezone(MM_CONFIG.triggerTimeZone).create();
Logger.log('נוצר טריגר יומי לשעה %s:00', MM_CONFIG.triggerHour);
return 'טריגר יומי נוצר בהצלחה לשעה ' + MM_CONFIG.triggerHour + ':00';
}