תוצאות נוספות...

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
× Send

Google Apps Script: מה זה ומה אפשר לעשות איתו?

Google Apps Script: מה זה, מה אפשר לעשות איתו, ואיך בונים אוטומציה ראשונה
תוכן עניינים

יש לכם טבלה בגוגל שיטס עם תאריכים שאסור לפספס. חידוש ביטוח, טסט לרכב, מנוי שמתחדש, דומיין שפג. אתם יודעים שהיא שם, אתם אפילו נכנסים אליה מדי פעם, ובכל זאת פעם בשנה משהו נשכח. מה שחסר לכם זה לא עוד טבלה, אלא מישהו שיסתכל בה בשבילכם כל בוקר וישלח לכם מייל כשמשהו מתקרב. הכלי שעושה בדיוק את זה כבר קיים בחשבון גוגל שלכם. הוא נקרא Google Apps Script, הוא חינמי, לא דורש התקנה, ורוב האנשים מעולם לא פתחו אותו כי הוא נראה כמו כלי של מתכנתים. במדריך הזה נראה מה הוא יכול לעשות, מה הוא לא יכול, ונבנה איתו אוטומציה אמיתית מההתחלה ועד הסוף, כשמודל שפה כותב את כל הקוד במקומנו.

 

הישארו מעודכנים

רוצים לקבל עדכונים בלייב? רוצים מקום בו אתם יכולים להתייעץ עם מומחי AI, לשאול שאלות ולקבל תשובות? רוצים לשמוע על מבצעים והטבות לכלי ה-AI שמשנים את העולם? הצטרפו לקהילות ה-AI שלנו.

אפשר גם להרשם לניוזלטר שלנו

 

מה זה בעצם Apps Script

Apps Script היא סביבת פיתוח של גוגל שרצה על השרתים של גוגל עצמה. היא מבוססת על JavaScript, אבל החלק המעניין הוא לא השפה אלא הגישה: לסקריפט שאתם כותבים יש חיבור ישיר ומובנה לגוגל שיטס, ג'ימייל, דרייב, קלנדר, דוקס וגוגל פורמס. אין צורך במפתחות API, אין הרשמה לשירות חיצוני, ואין שרת שצריך לתחזק.

 

שלושה דברים שכדאי להבין לפני הכל:

  • זה חינם - לכל בעל חשבון גוגל, כולל חשבון פרטי, יש גישה מלאה.
  • הקוד רץ בענן, לא במחשב שלכם - אפשר לסגור את המחשב והאוטומציה תמשיך לעבוד.
  • הקוד רץ בהרשאות שלכם - זו העוצמה שלו וגם הסיכון שלו, ונחזור לזה בהמשך.

 

בהשוואה לכלי אוטומציה מוכרים כמו Make או Zapier, ההבדל המרכזי הוא מודל העלות. שם משלמים מנוי חודשי ולעיתים גם לפי כמות פעולות. כאן אין מנוי, אין ספירת פעולות, ויש גישה פשוטה ומהירה לשירותי גוגל.

 

מה אפשר לעשות עם זה

כמה דוגמאות לתהליכים שאפשר לבנות, כולם מבוססים רק על שירותי גוגל ובלי שום כלי חיצוני:

  • לשלוח מיילים אוטומטית על בסיס נתונים בטבלה, בדיוק מה שנבנה במדריך הזה
  • לשמור קבצים מצורפים מהמייל לתיקייה מסודרת בדרייב לפי חודש או לפי שולח
  • לייצר מסמכי Word או PDF מתבנית, כשכל שורה בטבלה הופכת למסמך משלה
  • לתייג, לארכב או לנקות מיילים בג'ימייל לפי כללים שאתם מגדירים
  • להוסיף תפריט מותאם אישית בתוך גוגל שיטס עם כפתורים שמריצים תהליכים
  • לקחת תשובות מטופס גוגל ולהפוך אותן למשימה, למייל אישור או לשורה במערכת
  • למשוך נתונים משירות חיצוני, למשל שער מטבע או נתוני מזג אוויר, ולעדכן איתם גיליון
  • להריץ כל אחד מאלה לפי לוח זמנים קבוע, בלי שאף אחד יפתח מחשב

 

ומה אי אפשר: המגבלות שכדאי להכיר מראש

Apps Script הוא לא שרת אמיתי ויש לו מספר מגבלות:

  • זמן ריצה מוגבל - פונקציה בודדת נעצרת אחרי שש דקות, וזה נכון גם בחשבון פרטי וגם בחשבון Workspace עסקי. זו מגבלה קשיחה שאי אפשר להרחיב, ולכן עיבוד של עשרות אלפי שורות בבת אחת פשוט לא יסתיים.
  • מכסת שליחת מיילים יומית - מאה נמענים ביום בחשבון פרטי, ואלף וחמש מאות בחשבון Workspace. שימו לב שהספירה היא לפי נמענים ולא לפי הודעות, כך שמייל אחד לחמישים אנשים עולה חמישים. לדיוור המוני זה לא הכלי.
  • זמן ריצה יומי כולל לתהליכים מתוזמנים - כתשעים דקות ביום בחשבון פרטי, וכשש שעות בחשבון Workspace.
  • תזמון הוא בערך ולא בדיוק - טריגר שמוגדר לשמונה בבוקר ירוץ אי שם בין שמונה לתשע, ולא בשמונה ואפס דקות. לתהליכים שדורשים דיוק לשנייה זה לא מתאים.
  • אין תגובה בזמן אמת - Apps Script מצוין לתהליכים שרצים כל כמה שעות, ופחות מתאים למשהו שצריך להגיב תוך שנייה.

המספרים האלה משתנים מדי פעם, וגוגל מפרסמת אותם בדף המכסות הרשמי. השורה התחתונה: לתהליכים אישיים ועסקיים בקנה מידה סביר זה יותר מספיק, ולמערכות כבדות צריך משהו אחר.

 

כאן נכנס ה-AI

עד כאן תיארנו כלי שדורש כתיבת קוד. הנקודה של המדריך הזה היא שאתם לא הולכים לכתוב אותו. מודלי שפה כמו קלוד, שממילא כבר יודע לעבוד מול שירותי גוגל, כותבים Apps Script ברמה גבוהה מאוד, מהסיבה הפשוטה שזו סביבה ותיקה, מתועדת היטב, ועם המון קוד לדוגמה. מה שנשאר לכם הוא לא הידע הטכני אלא היכולת לתאר בדיוק מה אתם רוצים ומה יש אצלכם על המסך. זה כל ההבדל, וזה גם החלק שרוב האנשים עושים לא נכון.

 

לפני שמתחילים

מה שצריך כדי לעבור את המדריך:

  • חשבון גוגל כלשהו, פרטי או עסקי
  • גיליון גוגל שיטס עם הנתונים שרוצים לעקוב אחריהם
  • גישה לקלוד או למודל שפה אחר (חינמי או בתשלום)
  • כרבע שעה

 

שלב 1: בונים את הטבלה

כל האוטומציה שנבנה מתבססת על טבלה אחת פשוטה עם ארבע עמודות:

  • עמודה A, "מה מתחדש" - שם הפריט
  • עמודה B, "תאריך" - מתי הוא מתחדש
  • עמודה C, "ימים מראש" - כמה ימים לפני התאריך תרצו לקבל התרעה
  • עמודה D, "סטטוס" - נשארת ריקה, הסקריפט ימלא אותה בעצמו

עמודה C היא הלב של האוטומציה שלנו. היא מאפשרת לכל שורה התרעה בתאריך הרצוי. על חידוש ביטוח מספיק שבוע מראש, על טסט שנתי אולי שבועיים, ועל חידוש דומיין תרצו חודש שלם.

 

הטבלה שממנה הכל מתחיל: ארבע עמודות, בלי נוסחאות ובלי הגדרות מיוחדות

הטבלה שממנה הכל מתחיל: ארבע עמודות, בלי נוסחאות ובלי הגדרות מיוחדות

 

נקודה טכנית קטנה: ודאו שהתאריכים בעמודה B הם באמת תאריכים ולא טקסט. הדרך הכי מהירה לבדוק היא להסתכל על היישור בתוך התא. תאריך אמיתי נצמד לימין התא באופן אוטומטי. אם התאריכים אצלכם נצמדים לצד השני, כדאי להיכנס ל-File ואז Settings, לוודא שה-Locale ואזור הזמן מוגדרים נכון, ואז להקליד את התאריכים מחדש.

 

שלב 2: פותחים את עורך הסקריפטים

מתוך הגיליון עצמו, לוחצים על תפריט Extensions ובוחרים Apps Script. שימו לב שנכנסים דווקא מכאן ולא דרך כתובת חיצונית, כי ככה הסקריפט מחובר לגיליון הזה ויודע לקרוא ממנו נתונים בלי שום הגדרה נוספת.

 

Extensions ואז Apps Script, מתוך הגיליון עצמו

Extensions ואז Apps Script, מתוך הגיליון עצמו

 

נפתחת לשונית חדשה עם עורך קוד ובתוכו שלוש שורות ריקות. זה בדיוק הרגע שבו רוב האנשים סוגרים את החלון - אך אין מה לפחד ואנחנו נעבור את זה ביחד!

 

המסך שמרתיע. מכאן והלאה רק מדביקים

המסך שמרתיע. מכאן והלאה רק מדביקים

 

שלב 3: מנסחים את הבקשה למודל

זה החלק החשוב ביותר במדריך. שימו לב שרוב הפרומפט שלמטה הוא לא בקשה אלא תיאור עובדתי של מה שקיים על המסך: שם הלשונית, אילו עמודות יש, מה יש בכל אחת, ומאיזו שורה מתחילים הנתונים. הסיבה פשוטה. המודל כותב Apps Script מצוין, אבל הוא לא רואה את הגיליון שלכם. בקשה בסגנון "תכתוב לי סקריפט שישלח תזכורות מטבלה" תייצר קוד תקין לחלוטין שפונה לעמודות הלא נכונות.

 

אני עובד ב-Google Apps Script, בפרויקט שמחובר לגיליון גוגל שיטס
(נפתח דרך Extensions ואז Apps Script מתוך הגיליון).

מבנה הגיליון:
- שם הלשונית: Sheet1
- שורה 1 היא שורת כותרות
- עמודה A: "מה מתחדש" (טקסט)
- עמודה B: "תאריך" (תאריך אמיתי, לא טקסט)
- עמודה C: "ימים מראש" (מספר שלם)
- עמודה D: "סטטוס" (ריק, או המילה "נשלח")
- הנתונים מתחילים בשורה 2 ונמשכים עד סוף הטבלה

מה שאני צריך שהסקריפט יעשה בכל הרצה:
1. לעבור על כל השורות מ-2 ומטה.
2. לדלג על כל שורה שבעמודה D כבר כתוב "נשלח", ועל כל שורה ריקה.
3. לחשב כמה ימים נשארו מהיום עד התאריך בעמודה B.
4. אם מספר הימים שנשארו קטן או שווה למספר שבעמודה C, ובו זמנית
   התאריך עוד לא עבר, לצרף את השורה לרשימת התזכורות.
5. אם נמצאה לפחות תזכורת אחת, לשלוח מייל אחד מרוכז לכתובת
   your-email@example.com שמכיל את כל הפריטים.
6. אחרי שליחה מוצלחת בלבד, לכתוב "נשלח" בעמודה D בכל אחת
   מהשורות שנכללו במייל.
7. אם לא נמצאה אף תזכורת, לא לשלוח כלום ולסיים בשקט.

דרישות נוספות:
- כל תוכן המייל בעברית, כולל הנושא. שם הפונקציה והערות בקוד באנגלית.
- אזור הזמן הוא Asia/Jerusalem. תקפיד שחישוב הימים יהיה לפי
  תחילת היום ולא לפי שעה מדויקת, כדי שלא ייפול ביום.
- תאריכים במייל בפורמט DD/MM/YYYY.
- לכל פריט במייל: מה מתחדש, התאריך, וכמה ימים נשארו.
- להוסיף לוג ברור עם Logger.log על כמה שורות נבדקו וכמה נשלחו.
- לעטוף את השליחה ב-try/catch, ואם השליחה נכשלה לא לסמן "נשלח".

תן לי פונקציה אחת ראשית שאפשר להריץ ידנית מהעורך, ותסביר לי
בסוף בשורה אחת מה שם הפונקציה שצריך לבחור בתפריט Run.

 

שני סעיפים בפרומפט שווים תשומת לב מיוחדת, כי בלעדיהם האוטומציה מעצבנת במקום לעזור. סעיף 2 וסעיף 6 יחד הם מה שגורם למערכת לזכור מה כבר נשלח. בלעדיהם תקבלו את אותה תזכורת בדיוק כל בוקר מחדש, ותכבו את כל התהליך אחרי שלושה ימים.

 

שלב 4: מדביקים ושומרים

מעתיקים את הקוד שהמודל החזיר, חוזרים לעורך, מוחקים לגמרי את שלוש השורות שהיו שם ומדביקים במקומן. לא מוסיפים מתחת. אחר כך לוחצים על אייקון השמירה, ובהזדמנות הזו נותנים שם לפרויקט במקום Untitled project.

 

הקוד בעורך אחרי ההדבקה. אף שורה ממנו לא נכתבה ידנית

הקוד בעורך אחרי ההדבקה. אף שורה ממנו לא נכתבה ידנית

 

שלב 5: מריצים ומאשרים הרשאות

בסרגל העליון בוחרים את שם הפונקציה, במקרה שלנו sendUpcomingReminders, ולוחצים Run. כאן מגיע השלב שמפיל הכי הרבה אנשים. גוגל תעצור אתכם ותבקש אישור, כי הסקריפט מבקש לקרוא את הגיליון ולשלוח מייל בשמכם.

 

בקשת ההרשאה הראשונה. מכאן ממשיכים ב-Review permissions

בקשת ההרשאה הראשונה. מכאן ממשיכים ב-Review permissions

 

לוחצים על Review permissions, בוחרים את החשבון, ואז מופיע מסך אדום למדי שמודיע ש-Google hasn't verified this app. זה נראה מפחיד וזה לגמרי תקין. גוגל מאמתת רק אפליקציות שפורסמו לציבור הרחב, וסקריפט אישי שכתבתם לעצמכם לפני שתי דקות מעולם לא עבר אימות כזה ולא אמור לעבור. כדי להמשיך לוחצים על Advanced ואז על השורה התחתונה שמתחילה ב-Go to, ומאשרים.

 

שווה לעצור כאן לרגע. האישור הזה הוא אמיתי ומשמעותי, והוא נותן לקוד גישה לתיבת המייל ולקבצים שלכם. כשמדובר בקוד שאתם ייצרתם בעצמכם לצורך שאתם מבינים, זה בסדר גמור. כשמדובר בסקריפט שמישהו שלח לכם בוואטסאפ, זו כבר החלטה אחרת לגמרי.

 

שלב 6: בודקים שזה עבד

אחרי ההרצה נפתח בתחתית המסך חלון Execution log, וזה המקום היחיד שאליו חוזרים כשרוצים לדעת מה קרה. במקרה שלנו הוא מדווח שנבדקו שש שורות ונמצאו שלוש תזכורות.

 

הלוג מדווח בדיוק מה קרה: שש שורות נבדקו, שלוש תזכורות נשלחו

הלוג מדווח בדיוק מה קרה: שש שורות נבדקו, שלוש תזכורות נשלחו

 

והמייל מגיע לתיבה, עם שלושת הפריטים ועם מספר הימים שנותר לכל אחד.

 

התוצאה בתיבת המייל

התוצאה בתיבת המייל

 

וכאן החלק שמוכיח שהלוגיקה באמת עובדת ולא סתם שלחה הכל: המקדמה למס הכנסה, שרחוקה שמונה עשר ימים והוגדרה עם התרעה של שלושה ימים בלבד, לא נכנסה למייל. הטסט השנתי, שרחוק אחד עשר ימים והוגדר עם התרעה של ארבעה עשר, כן נכנס. עמודה C עושה בדיוק את מה שהתכוונו.

 

במקביל, שלוש השורות שנשלחו קיבלו את הסימון "נשלח" בעמודה D, והשאר נשארו ריקות. מכאן, כל הרצה נוספת תדלג עליהן.

 

הטבלה אחרי ההרצה. הסקריפט סימן בעצמו מה כבר טופל

הטבלה אחרי ההרצה. הסקריפט סימן בעצמו מה כבר טופל

 

שלב 7: הופכים את זה לאוטומציה אמיתית

עד עכשיו הרצנו ידנית, וזו עדיין לא אוטומציה. ההפיכה לתהליך שרץ לבד היא הדבר הקצר ביותר בכל המדריך: בסרגל הצד השמאלי של העורך יש אייקון שעון מעורר בשם Triggers. נכנסים אליו, לוחצים על Add Trigger, בוחרים את הפונקציה שלנו, מגדירים Time-driven ואז Day timer ושעה, ושומרים.

 

מהרגע הזה הסקריפט רץ כל בוקר בעצמו, בודק את הטבלה, ושולח מייל רק אם באמת יש מה לשלוח. אתם לא צריכים לפתוח כלום. נרחיב על תזמונים, על סוגי טריגרים נוספים ועל מה שקורה כשטריגר נכשל במדריך נפרד. בינתיים, אם נושא התהליכים שרצים בלעדיכם מעניין אתכם, כדאי לקרוא גם על הפעלת משימות מרחוק דרך קלוד.

 

טיפים שיחסכו לכם זמן

  • כשמשהו לא עובד, אל תנחשו - פתחו את Execution log, העתיקו את הודעת השגיאה במלואה, והדביקו אותה בחזרה למודל באותה שיחה שבה קיבלתם את הקוד. הוסיפו משפט על מה שראיתם בפועל, ובקשו את הקובץ המלא המתוקן ולא רק את החלק ששונה. זו הלולאה שמחליפה את הידע לתכנת, והיא עובדת גם כשאין לכם מושג מה כתוב בקוד.
  • תארו מבנה, לא רק רצון - שמות עמודות, אותיות, שם הלשונית, מאיזו שורה מתחילים. זה מה שמפריד בין קוד שעובד לקוד שנראה מצוין ופונה למקום הלא נכון.
  • שנו בקטן - אחרי שהגרסה הראשונה עובדת, בקשו שיפור אחד בכל פעם והריצו שוב. שינוי אחד שנשבר קל לאתר, חמישה שינויים ביחד כבר לא.
  • התחילו ממשהו שלא מוחק - אוטומציה ראשונה שקוראת נתונים ושולחת מייל היא בטוחה. אוטומציה ראשונה שמוחקת מיילים או קבצים היא דרך מהירה לצרות.

 

לפני שאתם רצים לבנות

שתי אזהרות שחשוב שיישארו איתכם:

 

סקריפט מקבל את ההרשאות שלכם - הקוד רץ בשם החשבון שלכם ויכול לקרוא מיילים, למחוק קבצים ולשלוח הודעות בשמכם. לכן קוד שאתם מייצרים לעצמכם דרך מודל שפה, לצורך שאתם מבינים, הוא סיפור אחד. קוד מוכן שהגיע ממקור לא מוכר הוא סיפור אחר. אם אתם לא בטוחים מה קוד מסוים עושה, הדביקו אותו למודל ובקשו הסבר בעברית פשוטה לפני שאתם מריצים.

 

אל תבנו על זה מערכת עסקית קריטית לעסק - Apps Script מצוין לתהליכים אישיים וצוותיים בקנה מידה סביר. הוא לא מיועד להיות התשתית של מערכת שהעסק לא יכול לתפקד בלעדיה.

 

מה הלאה

מה שבנינו כאן הוא לא באמת מערכת תזכורות. זה תבנית. אותו תהליך בדיוק, עם שינוי בטבלה ובפרומפט, יכול לשלוח סיכום שבועי של משימות פתוחות, להתריע על לקוחות שלא שילמו, להזכיר לצוות למלא דוח שעות, או לעקוב אחרי כל דבר אחר שיושב אצלכם בגיליון ומחכה שמישהו יסתכל עליו.

 

קחו טבלה אחת שאתם כבר מנהלים, שאלו את עצמכם מה הייתם רוצים שיקרה איתה לבד כל בוקר, ותארו את זה למודל בדיוק כמו בפרומפט שלמעלה. הסקריפט הראשון שלכם ירוץ תוך רבע שעה.

 

ואם התיאבון נפתח, זו בדיוק נקודת הכניסה לעולם שלם של תהליכים שרצים לבד. אפשר להמשיך מכאן לסוכנים מנוהלים של קלוד, ומי שרוצה ללמוד את זה בצורה מסודרת ומעשית מוזמן לקורס קלוד קוד של Let's AI, שמיועד גם למי שאין לו רקע טכני.

 

הקוד המלא

לנוחיותכם, זה הקוד המלא שהתקבל והורץ במדריך הזה. שימו לב להחליף את כתובת המייל בשורה הראשונה בכתובת שלכם. אנחנו ממליצים בכל זאת לייצר אותו בעצמכם דרך הפרומפט, כי הערך האמיתי הוא בשיטה ולא בקובץ הזה.

/**
 * Main function - checks Sheet1 for upcoming items and sends a single
 * consolidated reminder email for all items due within their configured
 * lead time. Run this manually from the Apps Script editor, or attach
 * a time-driven trigger to it.
 *
 * Column B ("date") is supported in two forms:
 *  - a real Date object (as returned by Sheets when the cell is a true date)
 *  - a text string in DD/MM/YYYY format (as returned when the cell content
 *    is actually stored as text, even if it displays like a date)
 */
function sendUpcomingReminders() {
  var RECIPIENT_EMAIL = 'your-email@example.com';
  var TIMEZONE = 'Asia/Jerusalem';
  var SHEET_NAME = 'Sheet1';
  var STATUS_SENT = 'נשלח';

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
  if (!sheet) {
    Logger.log('Sheet "' + SHEET_NAME + '" not found.');
    return;
  }

  var lastRow = sheet.getLastRow();
  if (lastRow < 2) {
    Logger.log('No data rows found. Rows checked: 0, reminders sent: 0.');
    return;
  }

  // Read columns A:D from row 2 to the last row
  var numRows = lastRow - 1;
  var range = sheet.getRange(2, 1, numRows, 4);
  var values = range.getValues();

  // Compute "today" at midnight in the target timezone for a clean day-based diff
  var todayStr = Utilities.formatDate(new Date(), TIMEZONE, 'yyyy-MM-dd');
  var today = new Date(todayStr + 'T00:00:00');

  var reminders = []; // { rowIndex, title, dateObj, daysLeft }
  var rowsChecked = 0;

  for (var i = 0; i < values.length; i++) {
    var rowNumber = i + 2; // actual sheet row number
    var title = values[i][0];
    var dateValue = values[i][1];
    var daysAhead = values[i][2];
    var status = values[i][3];

    // Skip empty rows (no title and no date)
    if (!title && !dateValue) {
      continue;
    }

    rowsChecked++;

    // Skip rows already marked as sent
    if (status && String(status).trim() === STATUS_SENT) {
      continue;
    }

    // Determine the value type and parse it into a real Date accordingly
    var targetDate = null;
    var valueType = '';

    if (dateValue instanceof Date && !isNaN(dateValue.getTime())) {
      // Case 1: real Date object
      valueType = 'Date object';
      var dateStr = Utilities.formatDate(dateValue, TIMEZONE, 'yyyy-MM-dd');
      targetDate = new Date(dateStr + 'T00:00:00');

    } else if (typeof dateValue === 'string' && dateValue.trim() !== '') {
      // Case 2: text string, expected format DD/MM/YYYY
      valueType = 'text string';
      var trimmed = dateValue.trim();
      var match = trimmed.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);

      if (match) {
        var day = parseInt(match[1], 10);
        var month = parseInt(match[2], 10);
        var year = parseInt(match[3], 10);

        // Basic validity check before constructing the date
        if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
          var candidate = new Date(year, month - 1, day);
          // Verify the constructed date matches the input (guards against e.g. 31/02)
          if (candidate.getFullYear() === year &&
              candidate.getMonth() === month - 1 &&
              candidate.getDate() === day) {
            targetDate = candidate;
          }
        }
      }

      if (!targetDate) {
        Logger.log('Row ' + rowNumber + ' skipped: value type is ' + valueType +
          ' ("' + dateValue + '") but does not match DD/MM/YYYY format.');
        continue;
      }

    } else {
      // Case 3: neither a Date nor a usable string (empty, number, etc.)
      valueType = (dateValue === '' || dateValue === null) ? 'empty' : typeof dateValue;
      Logger.log('Row ' + rowNumber + ' skipped: invalid or missing date (value type: ' + valueType + ').');
      continue;
    }

    Logger.log('Row ' + rowNumber + ': date value type detected as ' + valueType + '.');

    var msPerDay = 24 * 60 * 60 * 1000;
    var daysLeft = Math.round((targetDate.getTime() - today.getTime()) / msPerDay);

    var leadDays = Number(daysAhead);
    if (isNaN(leadDays)) {
      Logger.log('Row ' + rowNumber + ' skipped: invalid "days ahead" value.');
      continue;
    }

    // Date must not have passed (daysLeft >= 0) and must be within the lead window
    if (daysLeft >= 0 && daysLeft <= leadDays) {
      reminders.push({
        rowNumber: rowNumber,
        title: title,
        dateObj: targetDate,
        daysLeft: daysLeft
      });
    }
  }

  Logger.log('Rows checked: ' + rowsChecked + ', reminders found: ' + reminders.length);

  if (reminders.length === 0) {
    Logger.log('No reminders to send. Exiting quietly.');
    return;
  }

  // Build the email content in Hebrew
  var subject = 'תזכורות: פריטים מתקרבים';
  var bodyLines = [];
  bodyLines.push('שלום,');
  bodyLines.push('');
  bodyLines.push('להלן רשימת הפריטים המתקרבים:');
  bodyLines.push('');

  for (var j = 0; j < reminders.length; j++) {
    var item = reminders[j];
    var formattedDate = Utilities.formatDate(item.dateObj, TIMEZONE, 'dd/MM/yyyy');
    var daysLabel = item.daysLeft === 0 ? 'היום' : (item.daysLeft + ' ימים');
    bodyLines.push('- ' + item.title + ' | תאריך: ' + formattedDate + ' | נותרו: ' + daysLabel);
  }

  bodyLines.push('');
  bodyLines.push('הודעה זו נשלחה אוטומטית.');

  var body = bodyLines.join('\n');

  // Send the email, wrapped in try/catch so a failure never marks rows as sent
  try {
    MailApp.sendEmail(RECIPIENT_EMAIL, subject, body);
    Logger.log('Email sent successfully to ' + RECIPIENT_EMAIL + '.');
  } catch (err) {
    Logger.log('Failed to send email: ' + err.message);
    return;
  }

  // Mark the included rows as sent only after a successful send
  for (var k = 0; k < reminders.length; k++) {
    sheet.getRange(reminders[k].rowNumber, 4).setValue(STATUS_SENT);
  }

  Logger.log('Rows checked: ' + rowsChecked + ', reminders sent: ' + reminders.length);
}
הישארו מעודכנים

רוצים לקבל עדכונים בלייב? רוצים מקום בו אתם יכולים להתייעץ עם מומחי AI, לשאול שאלות ולקבל תשובות? רוצים לשמוע על מבצעים והטבות לכלי ה-AI שמשנים את העולם? הצטרפו לקהילות ה-AI שלנו.

אפשר גם להרשם לניוזלטר שלנו

רוצים הרצאה או ייעוץ של ולדי פייסחין?
השאירו פרטים ונשמח לחזור אליכם עם המידע הרלוונטי
אולי יעניין אותך גם...

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

Let's update

רוצים לקבל עדכונים על כל מה שחדש ומעניין בעולם ה-AI? הרשמו לניוזלטר שלנו!

רוצים לראות יותר תכנים שלנו?

סמנו אותנו כמקור מועדף בגוגל וקבלו יותר כתבות, עדכונים ותובנות AI ישירות בתוצאות החיפוש!

הסבר קצר איך מוסיפים:

  • לוחצים על הכפתור "הוספה כמקור מועדף".
  • מסמנים את התיבה ליד Let’s AI.
  • סוגרים את החלון.
אירועי AI קרובים

תפריט נגישות

תוצאות נוספות...

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors