בוט טלגרם שעוקב אחרי אתרי הדירות בישראל ושולח לך הודעה ברגע שעולה דירה שמתאימה לחיפוש שלך. רץ על המחשב שלך, בחינם.
- כל הערים והיישובים בישראל (1,178), מתל אביב ועד קריית שמונה ואילת. כותבים שם של עיר ב-
/addוזה עובד. ערים גדולות נקראות במלואן, לא מדגם. - הרבה מקורות במקביל: יד2, מדלן, ריאלטה, הומלס, OnMap, אתרים קטנים נוספים, ערוצי טלגרם ציבוריים ואם רוצים גם קבוצות פייסבוק. מתריע על דירות חדשות וירידות מחיר, עם תמונה בראש כל הודעה.
- הכל מתוך טלגרם:
/addמוביל אותך בין עיר, חדרים ותקציב, או שפשוט כותבים משפט כמו "3 חדרים ברחובות עד 6000". בכל התראה יש תאריך כניסה, כפתור מפה, וכפתור WhatsApp כשבמודעה יש מספר. - אף פעם לא אותה דירה פעמיים, גם כשהיא מתפרסמת בשלושה אתרים או מוקפצת לראש הרשימה אחרי שבוע. וגם בלי הצפה ביום הראשון.
1. התקנת תלויות (Node 20 ומעלה)
npm install2. יצירת קובץ .env
מעתיקים את .env.example ל-.env וממלאים את הטוקן של הבוט מ-@BotFather:
TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
את OWNER_CHAT_ID משאירים ריק בינתיים. הצ'אט הראשון שכותב לבוט הופך לבעלים, וכל השאר פשוט לא מקבלים תשובה. שלחו /start מיד אחרי ההפעלה הראשונה: בלוג תופיע השורה "registered bot owner" עם ה-chatId שלכם, ואותו מכניסים ל-.env כדי לנעול. הבעלים יכול להכניס חברים עם /invite, שיוצר קישור חד-פעמי.
GEMINI_API_KEY לא חובה. בלעדיו המקורות המובנים (יד2, מדלן, ריאלטה, הומלס, OnMap) עובדים כרגיל, ורק המקורות שנקראים בעזרת מודל שפה כבויים. מפתח חינמי מ-Google AI Studio מספיק.
SOURCES קובע מאילו אתרים לקרוא, למשל SOURCES=yad2,madlan. אם לא מוגדר, קוראים מכולם.
3. הפעלה
npm run devשולחים לבוט /start בטלגרם, ואז /add כדי ליצור חיפוש ראשון.
| פקודה | מה היא עושה |
|---|---|
/add |
חיפוש חדש: עיר, חדרים, תקציב, ואפשר גם רחובות ודרישות (חניה, מעלית, ללא תיווך, מינימום מ"ר, מילות מפתח) |
| טקסט חופשי | 3 חדרים במודיעין עד 6500 בלי תיווך: המודל ממלא את החיפוש ואתם מאשרים בלחיצה |
/list |
החיפושים השמורים |
/latest |
מה יש בשוק עכשיו: התאמות, כמעט-התאמות ומה שכבר נשלח, עם כרטיסים עם תמונות |
/remove |
מחיקת חיפוש |
/pause · /resume |
עצירה והפעלה מחדש של כל ההתראות |
/status |
זמן פעילות, הסבב האחרון ומצב כל מקור |
/now |
סריקה עכשיו |
/quiet 23:00-07:30 |
שעות שקט בלילה (/quiet off לביטול) |
/invite · /users |
לבעלים בלבד: קישור הזמנה חד-פעמי, ורשימת מי שיש לו גישה |
שום דבר לא הולך לאיבוד בשעות השקט: הדירות נשמרות בתור ונשלחות כשהחלון נגמר.
אף מקור לא דורש חשבון חוץ מפייסבוק, ואף אחד לא דורש API בתשלום. כל מקור יושב מאחורי ממשק אחד (SourceAdapter, בתיקייה src/sources/), כך שמקור שנשבר או נחסם מבודד: מקבלים הודעה אחת שהמקור נכשל, הוא נסוג לזמן מה, וכל השאר ממשיך לרוץ.
לאתרים האלה יש כבר מידע מובנה, אז לכל אחד יש פרסר קטן שנכתב ידנית. הם חינמיים, מדויקים ומהירים, ורצים בכל סבב.
| מקור | כתובת | איך נקרא | תאריך פרסום? |
|---|---|---|---|
| יד2 | gw.yad2.co.il/realestate-feed/rent/feed |
הרשימה בעמודים שהאתר עצמו קורא, לפי עדכון אחרון. קוראים מלמעלה עד עמוד שאין בו שום דבר חדש (בדרך כלל שני עמודים), כך שגם עיר גדולה מכוסה במלואה. | ✅ מתוך כתובות התמונות |
| מדלן | www.madlan.co.il/api3 |
ה-GraphQL של מדלן, מהחדש לישן בכל הארץ; העיר מסוננת אחרי השליפה. כל 15 דקות. | ✅ lastUpdated |
| ריאלטה | realta.co.il/api/v1/search/ |
API ציבורי שמאגד את יד2, מדלן, OnMap, קומו ו-Facebook Marketplace | ✅ publishedAt |
| OnMap | phoenix.onmap.co.il/v1/properties/mixed_search |
API ציבורי; הסינון לפי עיר הוא פוליגון גאוגרפי | ✅ |
| הומלס | m.homeless.co.il/rent |
האתר למובייל, HTML רגיל שמפוענח עם cheerio | - |
כל יישוב שיד2 מכירה (1,178 בבנייה האחרונה) זמין לחיפוש: כותבים את השם ב-/add או בתוך משפט. הרשימה נמצאת ב-cities.generated.ts ונבנית עם npm run build-cities מרשימת היישובים הרשמית ב-data.gov.il ומהחיפוש של יד2 עצמה, שנותן את קודי העיר והאזור שה-API שלה צריך. קוד אזור שגוי גורם ליד2 להחזיר עיר ריקה בלי שום שגיאה, ולכן הקודים אף פעם לא מוקלדים ידנית. פרטים שמתוחזקים ביד (שמות הערים באתרים אחרים, אזורים כמו "צפון ראשון") נמצאים ב-cities.ts וגוברים על הרשומה שנוצרה.
לרוב האתרים הקטנים אין API, וסקרייפר לכל אחד היה נשבר בכל עיצוב מחדש. במקום זה העמוד מצטמצם לטקסט, קישורים ותמונות, ו-Gemini מחזיר מודעות מובנות שעוברות אימות עם Zod. הוספת אתר היא כתובת ושם ב-sites.ts, ועיצוב מחדש של האתר פוגע באיכות במקום להחזיר שקט. הם רצים פעם בשעה: בערך 120 קריאות למודל ביום, שמינית מהמכסה החינמית.
מוגדרים כרגע: קומו, JAnglo, אנגלו סכסון, Homely MLS וכתובת מודיעין.
דירות פרטיות בלי תיווך מופיעות כאן לפעמים שעות לפני כל אתר, אבל כפוסטים חופשיים בעברית. הם נשלחים למודל עשרה פוסטים בקריאה, ורק פוסטים שלא נראו קודם, מה שמחזיק עשר קבוצות מתחת ל-100 קריאות ביום במקום 2,900.
- טלגרם: ערוצים ציבוריים דרך התצוגה המקדימה
t.me/s/<channel>, בלי חשבון. הערוצים מוגדרים לפי עיר ב-channels.ts; בודקים ערוץ חדש עםnpm run telegram-probe -- <channel>. כל 20 דקות. - קבוצות פייסבוק: פרופיל כרום אמיתי ומחובר שמופעל עם Playwright. כבוי כברירת מחדל, ראו הפעלת קבוצות פייסבוק. כל 30 דקות.
מספרי טלפון נמחקים מקומית לפני שטקסט כלשהו מגיע לגוגל ומוחזרים אחר כך, כך שפרטי קשר לא יוצאים מהמחשב.
- האתרים חסומים, ה-API לא.
www.yad2.co.ilעונה באתגר של Radware, והדפים של מדלן וה-/api2שלה מאחורי PerimeterX, אבל ה-gateway של יד2 וה-/api3של מדלן עונים לבקשה רגילה. יד2 מתעקשת עלregion(בלעדיו מקבלים 400) ועל כותרותOrigin/Referer, שבלעדיהן מקבלים חסימת captcha. - ה-API של המפה ביד2 מחזיר עד 200 מודעות לעיר, ולא את החדשות. מתל אביב חזרו 188 מתוך 4,769. הרשימה בעמודים מכילה את כולן, ממוינת לפי עדכון אחרון, כשגם הקפצה נחשבת עדכון: מודעה חדשה מתחילה למעלה ושוקעת כשאחרים מקפיצים. לכן הבוט קורא עד עמוד שאין בו שום דבר חדש. מספרי המודעות ביד2 רק עולים, וכך מבדילים בין מודעה ישנה שהוקפצה למודעה חדשה באמת. כל פרמטר מעבר ל-
region,cityו-pageמקבל רשומת חסימה של חומת האש במקום מידע, אז לא שולחים אף אחד. - קוד העיר ביד2 הוא ארבע ספרות עם אפסים מובילים. כפר יונה היא
0168; שליחה של168מחזירה עיר ריקה, עם תשובה תקינה לכל דבר. - טביעת ה-TLS הרגילה של Node חסומה ב-Cloudflare. הבקשות יוצאות ב-HTTP/2 עם סדר ההצפנות של כרום (src/util/http.ts); בלי זה כל בקשה חוזרת כמסך "Just a moment...", גם עם כותרות דפדפן מושלמות.
- דפי הגנה מבוטים חוזרים עם 200 OK. "אין מודעות חדשות" ו"נחסמת" נראים אותו דבר אם לא בודקים את התוכן, ולכן גוף התשובה נבדק לפני הסטטוס, ובקשה שנחסמה אף פעם לא נשלחת שוב.
- הומלס מציג את התוצאות במחשב בתוך iframe של JavaScript, אז הבוט קורא את האתר למובייל, שהוא HTML רגיל.
- האתרים עובדים כמו שצריך רק מכתובות IP ישראליות. מדלן לא מחזירה כלום לכתובות של חוות שרתים, ולכן מריצים מהבית.
מקורות שנפסלו, ולמה:
- ad.co.il ממיין לפי "פופולריות" בלי אפשרות למיין לפי תאריך, אז התוצאות הראשונות היו מודעות מ-2023. אתר שאי אפשר לבקש ממנו את החדש ביותר לא שווה כלום להתראות.
- LuxuryEstate נקרא טוב, אבל כל המודעות שם היו של משרדי תיווך בקצה היקר של השוק.
- Immo Israel, data.gov.il, century21, lagur, sublet, homes.co.il ועוד עשרות נבדקו ונמצאו מתים, ריקים או בלי מודעות.
Scheduler ──▶ PollCycle ──▶ SourceAdapter[] ──▶ filter ──▶ dedupe ──▶ Notifier ──▶ Telegram
דירה שנמצאת קצת מחוץ לגבולות, עד 10% מחוץ לטווח המחיר או חצי חדר פחות או יותר, נשלחת גם היא, עם הכותרת 🤏 כמעט מתאים ומה בדיוק חסר. הגבולות הם ניחוש על השוק, וכמעט-התאמה מסמנת מתי כדאי לעדכן אותם.
ההגנות שחלות על כל מקור, כל אחת נוספה אחרי שריצה אמיתית נכשלה בלעדיה:
- פורסם ב-30 הימים האחרונים, איפה שהמקור מפרסם תאריך. רוב האתרים לא מפרסמים, ולכן המלאי הקיים של מקור נרשם בשקט בפעם הראשונה, ורק מה שמופיע אחר כך מתריע.
- רק למגורים: מחסנים, חניות, משרדים וחנויות מסוננים.
- התראה אחת לכל דירה: אותה דירה מופיעה בכמה אתרים, אז מודעות מזוהות גם לפי מחיר, חדרים וגודל או רחוב. כשחסר מידע לא ממזגים: עדיף כפילות מאשר דירה אמיתית שנעלמה.
- בלי מחיר, בלי התראה. "מחיר לא צוין" הוא כמעט תמיד מתווך שמסתיר.
- מודעה ישנה שהוקפצה לא מתריעה: הבוט זוכר את מספר המודעה הגבוה ביותר מהרגע שהחיפוש התחיל, וכל מה שמתחתיו נרשם בשקט.
לפרטים על המבנה ועל הכללים בקוד: docs/ARCHITECTURE.md.
ב-ops/install-service.md יש הוראות להתקנה כשירות Windows שעולה עם המחשב ומתאושש לבד מקריסות.
npm run probe # every source, live, for Modi'in
npm run probe -- rishon # another cityשולף מכל מקור ומדפיס מה חזר, בלי לגעת במסד הנתונים או בטלגרם. זה הדבר הראשון לבדוק כשההתראות משתתקות. לא להריץ בלולאה: בדיקות חוזרות הן בדיוק מה שמפעיל הגבלות קצב.
npm test
npm run typecheckהפרסרים נבדקים מול תשובות אמיתיות שנשמרו ב-test/fixtures/ (אחרי שנוקו מפרטים אישיים), כך ששינוי באתר מופיע כבדיקה שנכשלה ולא כשקט.
בקבוצות מופיעות קודם הדירות הפרטיות בלי תיווך. קריאה שלהן דורשת את החשבון שלכם, אז זה כבוי עד שמפעילים:
1. מוסיפים מפתח Gemini ל-.env (GEMINI_API_KEY=...). הוא הופך פוסטים חופשיים בעברית למודעות מובנות.
2. מתחברים פעם אחת: מריצים npm run fb-login. נפתח חלון כרום עם הפרופיל של הבוט בתיקייה .fb-profile/ (שלא נכנסת ל-git). מתחברים ידנית, הבוט לא רואה את הסיסמה, וסוגרים את החלון. הסקריפט בודק את החיבור ומדפיס PASS או FAIL.
3. מפעילים: FACEBOOK_ENABLED=1 ב-.env, ומפעילים את הבוט מחדש.
4. בודקים את רשימת הקבוצות ב-src/sources/facebook/fbGroups.ts. הבוט יכול לקרוא רק קבוצות שהחשבון שלכם חבר בהן.
הקבוצות נקראות מהחדש לישן, בגלילה בקצב אנושי. פוסטים של "מחפש דירה" מזוהים ולא נשלחים. אם החיבור פג, הבוט שולח הודעה אחת עם הפקודה להרצה ונסוג עד שהחיבור חוזר.
כדאי לדעת: קריאה אוטומטית היא נגד תנאי השימוש של פייסבוק, והסיכון הוא על החשבון שהתחברתם איתו. הקצב האיטי והפרופיל האמיתי מקטינים אותו, אבל לא מאפסים.
MIT, ראו LICENSE.