diff --git a/BOTFATHER_COMMANDS.txt b/BOTFATHER_COMMANDS.txt
index 3fef6a9..328338c 100644
--- a/BOTFATHER_COMMANDS.txt
+++ b/BOTFATHER_COMMANDS.txt
@@ -1,6 +1,13 @@
-Copy this to BotFather when setting commands with /setcommands:
+Autopilot bot command list for @BotFather
-autopilot - Toggle automatic applications (on/off)
-status - Show current status and stats
-plot - Show weekly listing patterns
-help - Show available commands
+Use @BotFather -> /setcommands and paste the following lines exactly (one per line):
+
+/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)
+/errorrate - Show autopilot success vs failure plot (image)
+/help - Show help and command usage
+
+Example: send `/setcommands` to @BotFather, then paste the above lines and confirm.
+
+Security reminder: set your bot privacy and only allow the trusted chat id to issue commands. Keep your `TELEGRAM_BOT_TOKEN` secret.
diff --git a/README.md b/README.md
index 5c13578..2c6abf6 100644
--- a/README.md
+++ b/README.md
@@ -91,10 +91,13 @@ python monitor.py
## Telegram Commands
-- `/status` - Show current status and recent listings
-- `/autopilot` - Toggle auto-apply on/off
-- `/listings` - Show current listings
-- `/help` - Show available commands
+- `/autopilot on|off` - Enable or disable automatic applications (use `/autopilot on` or `/autopilot off`).
+- `/status` - Show current status and statistics (autopilot state, application counts by company).
+- `/plot` - Generate and send a weekly listing-patterns plot (`data/weekly_plot.png`).
+- `/errorrate` - Generate and send an autopilot success vs failure plot (`data/error_rate.png`).
+- `/help` - Show available commands and usage information.
+
+Note: The bot only processes commands from the configured `TELEGRAM_CHAT_ID`. Use `/autopilot off` while testing selector changes or after modifying configuration to avoid accidental submissions.
## Data files
diff --git a/monitor.py b/monitor.py
index 4f9cfb1..4be84e9 100644
--- a/monitor.py
+++ b/monitor.py
@@ -185,6 +185,8 @@ class TelegramBot:
self._handle_help_command()
elif text == "/plot":
self._handle_plot_command()
+ elif text == "/errorrate":
+ self._handle_error_rate_command()
elif text.startswith("/"):
self._handle_unknown_command(text)
@@ -235,6 +237,85 @@ When autopilot is ON, I will automatically apply to new listings."""
def _handle_unknown_command(self, text):
cmd = text.split()[0] if text else text
+
+ def _handle_error_rate_command(self):
+ """Generate and send a plot showing success vs failure ratio for autopilot applications."""
+ logger.info("Generating autopilot errorrate plot...")
+ try:
+ plot_path, summary = self._generate_error_rate_plot()
+ if plot_path:
+ caption = "📉 Autopilot Success vs Failure\n\n" + summary
+ self._send_photo(plot_path, caption)
+ else:
+ self._send_message("📉 Not enough application data to generate errorrate plot.")
+ except Exception as e:
+ logger.error(f"Error generating errorrate plot: {e}")
+ import traceback
+ logger.error(traceback.format_exc())
+ self._send_message(f"❌ Error generating errorrate plot: {str(e)}")
+
+ def _generate_error_rate_plot(self):
+ """Read applications.json and produce a plot image + summary text.
+
+ Returns (plot_path, summary_text) or (None, "") if insufficient data.
+ """
+ if not APPLICATIONS_FILE.exists():
+ logger.warning("No applications.json found for errorrate plot")
+ return None, ""
+
+ try:
+ with open(APPLICATIONS_FILE, 'r', encoding='utf-8') as f:
+ apps = json.load(f)
+ if not apps:
+ return None, ""
+
+ # Convert to DataFrame
+ rows = []
+ for _id, rec in apps.items():
+ ts = rec.get('timestamp')
+ try:
+ dt = pd.to_datetime(ts)
+ except Exception:
+ dt = pd.NaT
+ rows.append({'id': _id, 'company': rec.get('company'), 'success': bool(rec.get('success')), 'ts': dt})
+ df = pd.DataFrame(rows)
+ df = df.dropna(subset=['ts'])
+ if df.empty:
+ return None, ""
+
+ df['date'] = df['ts'].dt.floor('D')
+ grouped = df.groupby('date').agg(total=('id','count'), successes=('success', lambda x: x.sum()))
+ grouped['failures'] = grouped['total'] - grouped['successes']
+ grouped['error_rate'] = grouped['failures'] / grouped['total']
+
+ # Prepare plot
+ fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
+ grouped[['successes','failures']].plot(kind='bar', stacked=True, ax=ax1, color=['#2E8B57','#C44A4A'])
+ ax1.set_ylabel('Count')
+ ax1.set_title('Autopilot: Successes vs Failures (by day)')
+
+ ax2.plot(grouped.index, grouped['error_rate'], marker='o', color='#3333AA')
+ ax2.set_ylim(0,1)
+ ax2.set_ylabel('Error rate')
+ ax2.set_xlabel('Date')
+ ax2.set_title('Daily Error Rate (failures / total)')
+
+ plt.tight_layout()
+ plot_path = DATA_DIR / 'error_rate.png'
+ fig.savefig(plot_path)
+ plt.close(fig)
+
+ # Summary
+ total_attempts = int(grouped['total'].sum())
+ total_success = int(grouped['successes'].sum())
+ total_fail = int(grouped['failures'].sum())
+ overall_error = (total_fail / total_attempts) if total_attempts>0 else 0.0
+ summary = f"Total attempts: {total_attempts}\nSuccesses: {total_success}\nFailures: {total_fail}\nOverall error rate: {overall_error:.1%}"
+
+ return str(plot_path), summary
+ except Exception as e:
+ logger.exception(f"Failed to generate error rate plot: {e}")
+ return None, ""
self._send_message(f"❓ Unknown command: {cmd}\n\nUse /help to see available commands.")
def _handle_plot_command(self):