fix emojis
This commit is contained in:
parent
c45c6992ae
commit
287b1b154f
5 changed files with 39 additions and 32 deletions
|
|
@ -85,7 +85,7 @@ class ApplicationHandler:
|
|||
|
||||
company_label = company.capitalize() if company != "unknown" else "Wohnung"
|
||||
message = (
|
||||
f"🏠 <b>[{company_label}] Neue Wohnung!</b>\n\n"
|
||||
f"<b>[{company_label}] Neue Wohnung!</b>\n\n"
|
||||
f"🚪 <b>{listing['rooms']}</b>\n"
|
||||
f"📏 {listing['size']}\n"
|
||||
f"💰 {listing['price']}\n"
|
||||
|
|
@ -136,7 +136,7 @@ class ApplicationHandler:
|
|||
result = await self.apply(listing)
|
||||
results[listing["id"]] = result
|
||||
self.save_application(result)
|
||||
status = "✅" if result["success"] else "❌"
|
||||
status = "[SUCCESS]" if result["success"] else "[FAILED]"
|
||||
logger.info(f"{status} {listing['address'][:30]}... | {result['message'][:50]}")
|
||||
await asyncio.sleep(2)
|
||||
return results
|
||||
|
|
@ -409,7 +409,7 @@ class ApplicationHandler:
|
|||
else:
|
||||
stats_text = "🎯 Peak time: N/A"
|
||||
|
||||
stats_text = f"""📊 Summary Statistics
|
||||
stats_text = f"""<b>Summary Statistics</b>
|
||||
|
||||
Total listings tracked: {total_listings}
|
||||
|
||||
|
|
@ -632,15 +632,15 @@ Total listings tracked: {total_listings}
|
|||
try:
|
||||
listings = await self._fetch_listings_attempt()
|
||||
if attempt > 0:
|
||||
logger.info(f"✅ Fetch succeeded (attempt {attempt + 1})")
|
||||
logger.info(f"Fetch succeeded (attempt {attempt + 1})")
|
||||
return listings
|
||||
except Exception as e:
|
||||
if attempt < max_retries - 1:
|
||||
wait_time = retry_delay * (2 ** attempt) # Exponential backoff
|
||||
logger.warning(f"⚠️ Fetch failed (attempt {attempt + 1}/{max_retries}): {str(e)[:50]}... Retrying in {wait_time}s")
|
||||
logger.warning(f"Fetch failed (attempt {attempt + 1}/{max_retries}): {str(e)[:50]}... Retrying in {wait_time}s")
|
||||
await asyncio.sleep(wait_time)
|
||||
else:
|
||||
logger.error(f"❌ Fetch failed after {max_retries} attempts")
|
||||
logger.error(f"Fetch failed after {max_retries} attempts")
|
||||
return []
|
||||
|
||||
return []
|
||||
|
|
@ -782,14 +782,14 @@ Total listings tracked: {total_listings}
|
|||
listings = unique_listings
|
||||
|
||||
if not listings:
|
||||
logger.warning("⚠️ No listings parsed")
|
||||
logger.warning("No listings parsed")
|
||||
|
||||
await page.close()
|
||||
logger.info(f"📊 Fetched {len(listings)} listings")
|
||||
logger.info(f"Fetched {len(listings)} listings")
|
||||
return listings
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fetch error: {str(e)[:100]}")
|
||||
logger.error(f"Fetch error: {str(e)[:100]}")
|
||||
return []
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ class WBMHandler(BaseHandler):
|
|||
# Look for application button on detail page
|
||||
logger.info("[WBM] Looking for application button on detail page...")
|
||||
selectors = [
|
||||
'button:has-text("Anfrage absenden")',
|
||||
'a:has-text("Anfrage absenden")',
|
||||
'a[href*="expose-anfordern"]',
|
||||
'a[href*="bewerben"]',
|
||||
'a:has-text("Anfragen")',
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ class WGCompanyNotifier:
|
|||
if listing['id'] not in previous:
|
||||
new.append(listing)
|
||||
if new:
|
||||
logger.info(f"[WG] 🏠 {len(new)} new listing{'s' if len(new) > 1 else ''} detected")
|
||||
logger.info(f"[WG] {len(new)} new listing{'s' if len(new) > 1 else ''} detected")
|
||||
return new
|
||||
|
||||
def log_listing_times(self, new_listings):
|
||||
|
|
@ -185,7 +185,7 @@ class WGCompanyNotifier:
|
|||
for idx, listing in enumerate(new_listings, start=1):
|
||||
try:
|
||||
message = (
|
||||
f"🏠 <b>[WG-Company] Neues WG-Zimmer!</b>\n\n"
|
||||
f"<b>[WG-Company] Neues WG-Zimmer!</b>\n\n"
|
||||
f"🚪 <b>{listing['rooms']}</b>\n"
|
||||
f"📏 {listing['size']}\n"
|
||||
f"💰 {listing['price']}\n"
|
||||
|
|
@ -196,7 +196,7 @@ class WGCompanyNotifier:
|
|||
asyncio.run_coroutine_threadsafe(self.telegram_bot._send_message(message), loop)
|
||||
await asyncio.sleep(0.5)
|
||||
except Exception as e:
|
||||
logger.error(f"[WG] ❌ Telegram failed for listing {idx}: {str(e)[:50]}")
|
||||
logger.error(f"[WG] Telegram failed for listing {idx}: {str(e)[:50]}")
|
||||
|
||||
async def run(self):
|
||||
await self.init_browser()
|
||||
|
|
|
|||
23
main.py
23
main.py
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import asyncio
|
||||
from playwright.async_api import async_playwright
|
||||
from application_handler import ApplicationHandler
|
||||
|
|
@ -22,13 +21,13 @@ logging.basicConfig(
|
|||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)-5s] %(name)-20s | %(message)s",
|
||||
handlers=[
|
||||
RotatingFileHandler("data/monitor.log", maxBytes=1 * 1024 * 1024, backupCount=3), # 1 MB per file, 3 backups
|
||||
RotatingFileHandler("data/monitor.log", maxBytes=1 * 1024 * 1024, backupCount=3),
|
||||
logging.StreamHandler()
|
||||
],
|
||||
force=True # Enforce for all modules, Python 3.8+
|
||||
)
|
||||
logger = logging.getLogger(__name__) # Use named logger
|
||||
logger.info("🚀 Bot starting | Logs: data/monitor.log + console")
|
||||
logger.info("Bot starting | Logs: data/monitor.log + console")
|
||||
|
||||
# Interval (seconds) between checks for new listings
|
||||
CHECK_INTERVAL = int(os.getenv("CHECK_INTERVAL", 300)) # Default: 300 seconds
|
||||
|
|
@ -96,7 +95,7 @@ async def init_browser_context() -> tuple:
|
|||
return playwright, browser, browser_context
|
||||
|
||||
async def main() -> None:
|
||||
logger.info("🤖 Initializing wohn-bot...")
|
||||
logger.info("Initializing wohn-bot...")
|
||||
|
||||
# Validate configuration before starting
|
||||
if not validate_config():
|
||||
|
|
@ -106,7 +105,7 @@ async def main() -> None:
|
|||
state_manager = StateManager(Path("data/state.json"))
|
||||
|
||||
# --- Playwright browser/context setup with recovery ---
|
||||
logger.info("🌐 Initializing browser...")
|
||||
logger.info("Initializing browser...")
|
||||
playwright, browser, browser_context = await init_browser_context()
|
||||
|
||||
# Application handler manages browser/context
|
||||
|
|
@ -134,16 +133,16 @@ async def main() -> None:
|
|||
try:
|
||||
deleted = autoclean_debug_material()
|
||||
if deleted:
|
||||
logger.info(f"🧹 Cleaned {len(deleted)} debug files (48h)")
|
||||
logger.info(f"Cleaned {len(deleted)} debug files (48h)")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ Autoclean failed: {e}")
|
||||
logger.warning(f"Autoclean failed: {e}")
|
||||
last_clean = now
|
||||
|
||||
try:
|
||||
current_listings = await app_handler.fetch_listings()
|
||||
except Exception as e:
|
||||
logger.error(f"💥 Browser crash: {e}")
|
||||
logger.info("🔄 Recovering...")
|
||||
logger.info("Recovering...")
|
||||
try:
|
||||
await browser.close()
|
||||
await playwright.stop()
|
||||
|
|
@ -154,7 +153,7 @@ async def main() -> None:
|
|||
try:
|
||||
playwright, browser, browser_context = await init_browser_context()
|
||||
app_handler.context = browser_context
|
||||
logger.info("✅ Browser recovered")
|
||||
logger.info("Browser recovered")
|
||||
await asyncio.sleep(5)
|
||||
continue
|
||||
except Exception as recovery_error:
|
||||
|
|
@ -163,7 +162,7 @@ async def main() -> None:
|
|||
continue
|
||||
|
||||
if not current_listings:
|
||||
logger.warning("⚠️ No listings fetched")
|
||||
logger.warning("No listings fetched")
|
||||
await asyncio.sleep(CHECK_INTERVAL)
|
||||
_flush_rotating_file_handlers()
|
||||
continue
|
||||
|
|
@ -192,10 +191,10 @@ async def main() -> None:
|
|||
new_listings = app_handler.find_new_listings(current_listings, previous_listings)
|
||||
application_results = {}
|
||||
if new_listings:
|
||||
logger.info(f"\ud83c\udfe0 {len(new_listings)} new listing{'s' if len(new_listings) > 1 else ''} detected")
|
||||
logger.info(f"{len(new_listings)} new listing{'s' if len(new_listings) > 1 else ''} detected")
|
||||
app_handler.log_listing_times(new_listings)
|
||||
if app_handler.is_autopilot_enabled():
|
||||
logger.info("\ud83e\udd16 Autopilot active - applying...")
|
||||
logger.info("Autopilot active - applying...")
|
||||
application_results = await app_handler.apply_to_listings(new_listings)
|
||||
app_handler.notify_new_listings(new_listings, application_results)
|
||||
app_handler.save_listings(current_listings)
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class TelegramBot:
|
|||
await self._send_message(msg)
|
||||
except Exception as e:
|
||||
logger.error(f"Error resetting listings: {e}")
|
||||
await self._send_message(f"❌ Error resetting listings: {str(e)}")
|
||||
await self._send_message(f"[ERROR] Error resetting listings: {str(e)}")
|
||||
|
||||
def __init__(self, monitor, bot_token: str | None = None, chat_id: str | None = None, event_loop=None) -> None:
|
||||
self.monitor = monitor
|
||||
|
|
@ -174,9 +174,9 @@ class TelegramBot:
|
|||
and app.get("retries", 0) < max_retries
|
||||
and not app.get("deactivated", False)
|
||||
]
|
||||
await self._send_message(f"🔄 Retrying {len(failed)} failed applications (max retries: {max_retries})...")
|
||||
await self._send_message(f"[RETRY] Retrying {len(failed)} failed applications (max retries: {max_retries})...")
|
||||
if not failed:
|
||||
await self._send_message("✅ No failed applications to retry (or all reached max retries).")
|
||||
await self._send_message("[INFO] No failed applications to retry (or all reached max retries).")
|
||||
return
|
||||
results = {}
|
||||
details = []
|
||||
|
|
@ -197,7 +197,7 @@ class TelegramBot:
|
|||
result["timestamp"] = app.get("timestamp", result["timestamp"])
|
||||
self.app_handler.save_application(result)
|
||||
results[listing["id"]] = result
|
||||
status_emoji = "✅" if result["success"] else "❌"
|
||||
status_emoji = "[SUCCESS]" if result["success"] else "[FAILED]"
|
||||
details.append(
|
||||
f"{status_emoji} <b>{result.get('address', '')}</b> ({result.get('company', '')})\n"
|
||||
f"<code>{result.get('link', '')}</code>\n"
|
||||
|
|
@ -205,7 +205,7 @@ class TelegramBot:
|
|||
)
|
||||
n_success = sum(1 for r in results.values() if r["success"])
|
||||
n_fail = sum(1 for r in results.values() if not r["success"])
|
||||
summary = f"🔄 Retried {len(results)} failed applications.\n✅ Success: {n_success}\n❌ Still failed: {n_fail}"
|
||||
summary = f"[RETRY] Retried {len(results)} failed applications.\n[SUCCESS]: {n_success}\n[FAILED]: {n_fail}"
|
||||
if details:
|
||||
summary += "\n\n<b>Details:</b>\n" + "\n".join(details)
|
||||
await self._send_message(summary)
|
||||
|
|
@ -220,7 +220,7 @@ class TelegramBot:
|
|||
if action == "on":
|
||||
logger.info("Enabling autopilot mode")
|
||||
self.monitor.set_autopilot(True)
|
||||
await self._send_message("🤖 <b>Autopilot ENABLED</b>\n\nI will automatically apply to new listings!")
|
||||
await self._send_message("<b>Autopilot ENABLED</b>\n\nI will automatically apply to new listings!")
|
||||
elif action == "off":
|
||||
self.monitor.set_autopilot(False)
|
||||
await self._send_message("🛑 <b>Autopilot DISABLED</b>\n\nI will only notify you of new listings.")
|
||||
|
|
@ -231,7 +231,7 @@ class TelegramBot:
|
|||
state = self.app_handler.load_state()
|
||||
autopilot = state.get("autopilot", False)
|
||||
applications = self.app_handler.load_applications()
|
||||
status = "🤖 <b>Autopilot:</b> " + ("ON ✅" if autopilot else "OFF ❌")
|
||||
status = "<b>Autopilot:</b> " + ("ON" if autopilot else "OFF")
|
||||
status += f"\n📝 <b>Applications sent:</b> {len(applications)}"
|
||||
by_company: dict[str, int] = {}
|
||||
for app in applications.values():
|
||||
|
|
@ -264,7 +264,7 @@ class TelegramBot:
|
|||
logger.error(f"Error generating errorrate plot: {e}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
await self._send_message(f"❌ Error generating errorrate plot: {str(e)}")
|
||||
await self._send_message(f"[ERROR] Error generating errorrate plot: {str(e)}")
|
||||
|
||||
|
||||
async def _send_message(self, text: str) -> None:
|
||||
|
|
@ -274,6 +274,9 @@ class TelegramBot:
|
|||
logger.warning("Telegram bot token or chat ID not configured, cannot send message")
|
||||
return
|
||||
|
||||
# Clean text: remove invalid unicode surrogates
|
||||
text = text.encode('utf-8', errors='ignore').decode('utf-8')
|
||||
|
||||
url = f"https://api.telegram.org/bot{self.bot_token}/sendMessage"
|
||||
|
||||
# Split message into chunks if too long
|
||||
|
|
@ -340,6 +343,9 @@ class TelegramBot:
|
|||
logger.warning("Telegram bot token or chat ID not configured, cannot send photo")
|
||||
return
|
||||
|
||||
# Clean caption: remove invalid unicode surrogates
|
||||
caption = caption.encode('utf-8', errors='ignore').decode('utf-8')
|
||||
|
||||
url = f"https://api.telegram.org/bot{self.bot_token}/sendPhoto"
|
||||
max_retries = 3
|
||||
retry_delay = 1 # Initial delay in seconds
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue