diff --git a/BOTFATHER_COMMANDS.txt b/BOTFATHER_COMMANDS.txt
index 5ffec20..3fabcc7 100644
--- a/BOTFATHER_COMMANDS.txt
+++ b/BOTFATHER_COMMANDS.txt
@@ -1,8 +1,27 @@
Autopilot bot command list for @BotFather
+=== BOT DESCRIPTION (for @BotFather /setdescription) ===
+
+π Monitors 6 Berlin housing companies (HOWOGE, Gewobag, Degewo, Gesobau, Stadt und Land, WBM) + WG-Company for new apartments.
+
+β
Instant Telegram notifications
+π€ Autopilot: auto-fill & submit applications
+π Weekly listing patterns & error tracking
+π Retry failed applications with one command
+βΈοΈ Pause/resume monitoring anytime
+
+Perfect for Berlin apartment hunters who want to apply first!
+
+=== BOT ABOUT (for @BotFather /setabouttext) ===
+
+Automated Berlin apartment monitoring bot with auto-apply functionality for inberlinwohnen.de and wgcompany.de listings.
+
+=== COMMANDS (for @BotFather /setcommands) ===
Use @BotFather -> setcommands and paste the following lines exactly (one per line):
+start - Resume monitoring for new listings
+stop - Pause monitoring (bot stays running, commands still work)
autopilot - Enable or disable automatic applications. Usage: autopilot on or autopilot off
status - Show current status and statistics (autopilot state, application counts by company)
plot - Show weekly listing patterns (image)
diff --git a/main.py b/main.py
index 942f063..21d2e28 100644
--- a/main.py
+++ b/main.py
@@ -139,6 +139,13 @@ async def main() -> None:
last_clean = now
try:
+ # Check if monitoring is enabled before fetching listings
+ if not state_manager.is_monitoring_enabled():
+ logger.debug("Monitoring is paused, skipping listing check")
+ await asyncio.sleep(CHECK_INTERVAL)
+ _flush_rotating_file_handlers()
+ continue
+
current_listings = await app_handler.fetch_listings()
except Exception as e:
logger.error(f"π₯ Browser crash: {e}")
diff --git a/state_manager.py b/state_manager.py
index 30c8bfe..15337a5 100644
--- a/state_manager.py
+++ b/state_manager.py
@@ -25,7 +25,7 @@ class StateManager:
if self.state_file.exists():
with open(self.state_file, "r") as f:
return json.load(f)
- return {"autopilot": False}
+ return {"autopilot": False, "monitoring_enabled": True}
def save_state(self, state: dict) -> None:
"""Save persistent state"""
@@ -43,6 +43,17 @@ class StateManager:
"""Check if autopilot mode is enabled"""
return self.load_state().get("autopilot", False)
+ def set_monitoring_enabled(self, enabled: bool) -> None:
+ """Enable or disable monitoring"""
+ state = self.load_state()
+ state["monitoring_enabled"] = enabled
+ self.save_state(state)
+ logger.info(f"Monitoring {'enabled' if enabled else 'disabled'}")
+
+ def is_monitoring_enabled(self) -> bool:
+ """Check if monitoring is enabled"""
+ return self.load_state().get("monitoring_enabled", True)
+
def set_logged_in(self, status: bool) -> None:
"""Set the logged_in status"""
self.logged_in = status
diff --git a/telegram_bot.py b/telegram_bot.py
index 6830dc2..8843a3a 100644
--- a/telegram_bot.py
+++ b/telegram_bot.py
@@ -20,6 +20,8 @@ class TelegramBot:
"""Send a help message with available commands."""
help_text = (
"Available commands:\n"
+ "/start - Resume monitoring\n"
+ "/stop - Pause monitoring\n"
"/autopilot on|off - Enable/disable autopilot\n"
"/status - Show current status\n"
"/plot - Show weekly listing pattern plot\n"
@@ -38,6 +40,25 @@ class TelegramBot:
)
await self._send_message(msg)
+ async def _handle_start_command(self) -> None:
+ """Resume monitoring for new listings."""
+ self.monitor.state_manager.set_monitoring_enabled(True)
+ await self._send_message(
+ "βΆοΈ Monitoring RESUMED\n\n"
+ "Bot will now check for new listings and notify you."
+ )
+ logger.info("Monitoring resumed via /start command")
+
+ async def _handle_stop_command(self) -> None:
+ """Pause monitoring without stopping the bot."""
+ self.monitor.state_manager.set_monitoring_enabled(False)
+ await self._send_message(
+ "βΈοΈ Monitoring PAUSED\n\n"
+ "Bot will not check for new listings until you send /start.\n"
+ "Commands like /status, /plot, and /retryfailed still work."
+ )
+ logger.info("Monitoring paused via /stop command")
+
async def _handle_reset_listings_command(self) -> None:
"""Move listings.json to data/old/ with a timestamp, preserving statistics and application history."""
import shutil
@@ -133,7 +154,11 @@ class TelegramBot:
return
logger.info(f"Received Telegram command: {text}")
loop = self.event_loop
- if text.startswith("/autopilot"):
+ if text == "/start":
+ asyncio.run_coroutine_threadsafe(self._handle_start_command(), loop)
+ elif text == "/stop":
+ asyncio.run_coroutine_threadsafe(self._handle_stop_command(), loop)
+ elif text.startswith("/autopilot"):
asyncio.run_coroutine_threadsafe(self._handle_autopilot_command(text), loop)
elif text == "/status":
asyncio.run_coroutine_threadsafe(self._handle_status_command(), loop)
@@ -230,9 +255,13 @@ class TelegramBot:
async def _handle_status_command(self) -> None:
state = self.app_handler.load_state()
autopilot = state.get("autopilot", False)
+ monitoring = state.get("monitoring_enabled", True)
applications = self.app_handler.load_applications()
- status = "Autopilot: " + ("ON" if autopilot else "OFF")
+
+ status = "Monitoring: " + ("βΆοΈ RUNNING" if monitoring else "βΈοΈ PAUSED")
+ status += "\nAutopilot: " + ("β
ON" if autopilot else "π OFF")
status += f"\nπ Applications sent: {len(applications)}"
+
by_company: dict[str, int] = {}
for app in applications.values():
company = app.get("company", "unknown")