add start stop
This commit is contained in:
parent
c68ee12d4e
commit
29a3f629e2
4 changed files with 69 additions and 3 deletions
|
|
@ -1,8 +1,27 @@
|
||||||
Autopilot bot command list for @BotFather
|
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):
|
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
|
autopilot - Enable or disable automatic applications. Usage: autopilot on or autopilot off
|
||||||
status - Show current status and statistics (autopilot state, application counts by company)
|
status - Show current status and statistics (autopilot state, application counts by company)
|
||||||
plot - Show weekly listing patterns (image)
|
plot - Show weekly listing patterns (image)
|
||||||
|
|
|
||||||
7
main.py
7
main.py
|
|
@ -139,6 +139,13 @@ async def main() -> None:
|
||||||
last_clean = now
|
last_clean = now
|
||||||
|
|
||||||
try:
|
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()
|
current_listings = await app_handler.fetch_listings()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"💥 Browser crash: {e}")
|
logger.error(f"💥 Browser crash: {e}")
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class StateManager:
|
||||||
if self.state_file.exists():
|
if self.state_file.exists():
|
||||||
with open(self.state_file, "r") as f:
|
with open(self.state_file, "r") as f:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
return {"autopilot": False}
|
return {"autopilot": False, "monitoring_enabled": True}
|
||||||
|
|
||||||
def save_state(self, state: dict) -> None:
|
def save_state(self, state: dict) -> None:
|
||||||
"""Save persistent state"""
|
"""Save persistent state"""
|
||||||
|
|
@ -43,6 +43,17 @@ class StateManager:
|
||||||
"""Check if autopilot mode is enabled"""
|
"""Check if autopilot mode is enabled"""
|
||||||
return self.load_state().get("autopilot", False)
|
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:
|
def set_logged_in(self, status: bool) -> None:
|
||||||
"""Set the logged_in status"""
|
"""Set the logged_in status"""
|
||||||
self.logged_in = status
|
self.logged_in = status
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ class TelegramBot:
|
||||||
"""Send a help message with available commands."""
|
"""Send a help message with available commands."""
|
||||||
help_text = (
|
help_text = (
|
||||||
"<b>Available commands:</b>\n"
|
"<b>Available commands:</b>\n"
|
||||||
|
"/start - Resume monitoring\n"
|
||||||
|
"/stop - Pause monitoring\n"
|
||||||
"/autopilot on|off - Enable/disable autopilot\n"
|
"/autopilot on|off - Enable/disable autopilot\n"
|
||||||
"/status - Show current status\n"
|
"/status - Show current status\n"
|
||||||
"/plot - Show weekly listing pattern plot\n"
|
"/plot - Show weekly listing pattern plot\n"
|
||||||
|
|
@ -38,6 +40,25 @@ class TelegramBot:
|
||||||
)
|
)
|
||||||
await self._send_message(msg)
|
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(
|
||||||
|
"▶️ <b>Monitoring RESUMED</b>\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(
|
||||||
|
"⏸️ <b>Monitoring PAUSED</b>\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:
|
async def _handle_reset_listings_command(self) -> None:
|
||||||
"""Move listings.json to data/old/ with a timestamp, preserving statistics and application history."""
|
"""Move listings.json to data/old/ with a timestamp, preserving statistics and application history."""
|
||||||
import shutil
|
import shutil
|
||||||
|
|
@ -133,7 +154,11 @@ class TelegramBot:
|
||||||
return
|
return
|
||||||
logger.info(f"Received Telegram command: {text}")
|
logger.info(f"Received Telegram command: {text}")
|
||||||
loop = self.event_loop
|
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)
|
asyncio.run_coroutine_threadsafe(self._handle_autopilot_command(text), loop)
|
||||||
elif text == "/status":
|
elif text == "/status":
|
||||||
asyncio.run_coroutine_threadsafe(self._handle_status_command(), loop)
|
asyncio.run_coroutine_threadsafe(self._handle_status_command(), loop)
|
||||||
|
|
@ -230,9 +255,13 @@ class TelegramBot:
|
||||||
async def _handle_status_command(self) -> None:
|
async def _handle_status_command(self) -> None:
|
||||||
state = self.app_handler.load_state()
|
state = self.app_handler.load_state()
|
||||||
autopilot = state.get("autopilot", False)
|
autopilot = state.get("autopilot", False)
|
||||||
|
monitoring = state.get("monitoring_enabled", True)
|
||||||
applications = self.app_handler.load_applications()
|
applications = self.app_handler.load_applications()
|
||||||
status = "<b>Autopilot:</b> " + ("ON" if autopilot else "OFF")
|
|
||||||
|
status = "<b>Monitoring:</b> " + ("▶️ RUNNING" if monitoring else "⏸️ PAUSED")
|
||||||
|
status += "\n<b>Autopilot:</b> " + ("✅ ON" if autopilot else "🛑 OFF")
|
||||||
status += f"\n📝 <b>Applications sent:</b> {len(applications)}"
|
status += f"\n📝 <b>Applications sent:</b> {len(applications)}"
|
||||||
|
|
||||||
by_company: dict[str, int] = {}
|
by_company: dict[str, int] = {}
|
||||||
for app in applications.values():
|
for app in applications.values():
|
||||||
company = app.get("company", "unknown")
|
company = app.get("company", "unknown")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue