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):