File size: 32,413 Bytes
945d0f8
 
 
 
 
 
 
 
 
 
 
 
 
 
6a6337e
945d0f8
 
 
 
6a6337e
945d0f8
 
6a6337e
 
 
 
 
 
 
 
 
 
 
 
945d0f8
 
 
6a6337e
 
 
 
 
945d0f8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a6337e
945d0f8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a6337e
 
 
945d0f8
 
3936714
 
 
 
 
 
 
 
 
 
 
 
 
 
34c461b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3936714
 
945d0f8
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
"""

Professional Telegram Bot for Climate-Resilient Agriculture

============================================================

Real-time notifications, farmer dashboard, and interactive commands

"""

import json
import logging
import asyncio
import os
import sys
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes
from telegram.constants import ParseMode, ChatAction
from farm_controller import run_farm_dashboard
from database import db

# Fix Unicode on Windows (safe check for buffer attribute)
if sys.platform == "win32":
    import io
    try:
        if hasattr(sys.stdout, 'reconfigure'):
            sys.stdout.reconfigure(encoding='utf-8')
        if hasattr(sys.stderr, 'reconfigure'):
            sys.stderr.reconfigure(encoding='utf-8')
    except (AttributeError, io.UnsupportedOperation):
        if hasattr(sys.stdout, 'buffer'):
            sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
        if hasattr(sys.stderr, 'buffer'):
            sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')

# Use child logger instead of basicConfig to play nice with app.py
logger = logging.getLogger("TelegramBot")

# Configuration
TOKEN: str = os.getenv("TELEGRAM_BOT_TOKEN", "8589326773:AAERc6eyATYmb8-Dr9yttiDKK9LJGa47-0M")
if os.getenv("TELEGRAM_BOT_TOKEN"):
    logger.info("βœ… TELEGRAM_BOT_TOKEN loaded from environment variable")
else:
    logger.warning("⚠️ TELEGRAM_BOT_TOKEN not in env, using hardcoded fallback token")

# API URLs - Works both locally AND in Docker
# In Docker: services communicate via localhost (same container)
# In HF Spaces: main app runs on port 7860, services integrated internally
WEATHER_API_URL = os.getenv("WEATHER_API_URL", "http://localhost:8001/weather")
PEST_API_URL = os.getenv("PEST_API_URL", "http://localhost:8000/api/predict")
WATER_API_URL = os.getenv("WATER_API_URL", "http://localhost:8002/predict")

# ════════════════════════════════════════════════════════════════════════════
# PERFORMANCE: CACHING LAYER
# ════════════════════════════════════════════════════════════════════════════

class DashboardCache:
    """Fast in-memory cache with TTL (Time-To-Live)"""
    def __init__(self, ttl_seconds: int = 120):  # 2-minute cache
        self.data: Dict[str, Any] = {}
        self.timestamp: Dict[str, datetime] = {}
        self.ttl = ttl_seconds
    
    def get(self) -> Optional[Dict[str, Any]]:
        """Get cached dashboard data if available and not expired"""
        if "dashboard" in self.data:
            age = (datetime.now() - self.timestamp["dashboard"]).total_seconds()
            if age < self.ttl:
                logger.debug(f"βœ… Cache HIT (age: {age:.1f}s)")
                return self.data["dashboard"]
        return None
    
    def set(self, data: Dict[str, Any]) -> None:
        """Cache dashboard data with timestamp"""
        self.data["dashboard"] = data
        self.timestamp["dashboard"] = datetime.now()
        logger.debug("πŸ’Ύ Dashboard cached")
    
    def clear(self) -> None:
        """Clear cache"""
        self.data.clear()
        self.timestamp.clear()

dashboard_cache = DashboardCache(ttl_seconds=120)

async def get_dashboard_data() -> Dict[str, Any]:
    """Get dashboard data with intelligent caching"""
    # Try cache first (50x faster than API call)
    cached = dashboard_cache.get()
    if cached:
        return cached
    
    # If cache miss, fetch fresh data
    try:
        dashboard_data = await run_farm_dashboard()
        dashboard_cache.set(dashboard_data)
        return dashboard_data
    except Exception as e:
        logger.error(f"Dashboard fetch error: {e}")
        return {}

# ════════════════════════════════════════════════════════════════════════════
# STARTUP HANDLERS
# ════════════════════════════════════════════════════════════════════════════

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Bot startup - Show main dashboard (OPTIMIZED)"""
    user = update.effective_user
    chat_id = update.effective_chat.id
    
    # Save user subscription (non-blocking)
    try:
        with open("user.json", "r") as f:
            farmer = json.load(f)
        # Fire and forget (don't await)
        asyncio.create_task(asyncio.to_thread(
            db.save_subscription, 
            farmer.get("farmer_id", "DEFAULT"), 
            str(chat_id)
        ))
    except:
        pass
    
    # Get cached or fresh dashboard data
    dashboard_data = await get_dashboard_data()
    
    farmer_profile = dashboard_data.get("farmer_profile", {})
    weather_intel = dashboard_data.get("weather_intelligence", {})
    alerts = dashboard_data.get("alerts", [])
    
    # Calculate risk level (fast)
    risk_score = 0
    for alert in alerts:
        if alert.get("severity") == "CRITICAL":
            risk_score += 35
        elif alert.get("severity") == "HIGH":
            risk_score += 20
        elif alert.get("severity") == "MEDIUM":
            risk_score += 10
    risk_score = min(100, risk_score)
    risk_level = "🟒 STABLE" if risk_score < 30 else "🟑 ALERT" if risk_score < 70 else "πŸ”΄ CRITICAL"
    
    # Main Dashboard Message
    welcome_text = f"""

🌾 <b>CLIMATE-RESILIENT FARM DASHBOARD</b> 🌾

{'═' * 50}



πŸ‘¨β€πŸŒΎ <b>Farmer:</b> {farmer_profile.get('name', 'Unknown')}

πŸ“ <b>Location:</b> {farmer_profile.get('village', 'N/A')}

🌱 <b>Crop:</b> {farmer_profile.get('crop_type', 'N/A')} | <b>Season:</b> {farmer_profile.get('season', 'N/A')}

πŸ“ <b>Farm Size:</b> {farmer_profile.get('farm_size_hectares', 1.0)} Ha



{'─' * 50}

πŸ“Š <b>CLIMATE RESILIENCE INDEX: {risk_score:.0f}/100</b>

Status: <b>{risk_level}</b>

Critical Alerts: <b>{len([a for a in alerts if a.get('severity') == 'CRITICAL'])}</b>

High Priority: <b>{len([a for a in alerts if a.get('severity') == 'HIGH'])}</b>



{'─' * 50}

🌑️ <b>CURRENT CONDITIONS:</b>

Temp: <b>{weather_intel.get('current', {}).get('temp', 'N/A')}Β°C</b> | Humidity: <b>{weather_intel.get('current', {}).get('humidity', 'N/A')}%</b>

Wind: <b>{weather_intel.get('current', {}).get('wind', 'N/A')} km/h</b>

Condition: <b>{weather_intel.get('current', {}).get('desc', 'N/A')}</b>



{'─' * 50}



<i>Select an option below for detailed information:</i>

"""
    
    keyboard = [
        [
            InlineKeyboardButton("πŸ”΄ CRITICAL ALERTS", callback_data="critical_alerts"),
            InlineKeyboardButton("🌦️ WEATHER INTEL", callback_data="weather_detail")
        ],
        [
            InlineKeyboardButton("πŸ› PEST MONITORING", callback_data="pest_detail"),
            InlineKeyboardButton("πŸ’§ WATER MANAGEMENT", callback_data="water_detail")
        ],
        [
            InlineKeyboardButton("πŸ“ˆ RESILIENCE SCORE", callback_data="resilience_score"),
            InlineKeyboardButton("🌍 SUSTAINABILITY", callback_data="sustainability")
        ],
        [
            InlineKeyboardButton("βš™οΈ SETTINGS", callback_data="settings"),
            InlineKeyboardButton("πŸ“ž SUPPORT", callback_data="support")
        ]
    ]
    
    await update.message.reply_html(
        welcome_text,
        reply_markup=InlineKeyboardMarkup(keyboard)
    )
    
    logger.info(f"βœ… Dashboard sent to {user.first_name} (Chat ID: {chat_id})")

# ════════════════════════════════════════════════════════════════════════════
# CALLBACK HANDLERS
# ════════════════════════════════════════════════════════════════════════════

async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Handle inline button clicks (OPTIMIZED - no API calls)"""
    query = update.callback_query
    await query.answer()  # Acknowledge immediately
    
    action = query.data
    
    # Get cached dashboard data (no API calls!)
    dashboard_data = await get_dashboard_data()
    
    weather_intel = dashboard_data.get("weather_intelligence", {})
    pest_intel = dashboard_data.get("pest_intelligence", {})
    water_intel = dashboard_data.get("water_intelligence", {})
    alerts = dashboard_data.get("alerts", [])
    
    # ────────────────────────────────────────────────────────────────────────
    if action == "critical_alerts":
        critical_alerts: list[dict] = [a for a in alerts if a.get("severity") in ["CRITICAL", "HIGH"]]
        
        if not critical_alerts:
            text = "βœ… <b>NO CRITICAL ALERTS</b>\n\nFarm conditions are stable. Continue monitoring."
        else:
            text = "πŸ”΄ <b>CRITICAL & HIGH PRIORITY ALERTS</b>\n\n"
            for idx, alert in enumerate(critical_alerts[:5], 1):
                severity_icon = "πŸ”΄" if alert.get("severity") == "CRITICAL" else "🟠"
                text += f"\n{severity_icon} <b>{alert.get('title', 'Alert')} (ID: {alert.get('id', 'N/A')})</b>\n"
                text += f"Message: {alert.get('message', 'N/A')}\n"
                
                if alert.get("action_items"):
                    text += "<b>Action Items:</b>\n"
                    for item in alert.get("action_items", [])[:3]:
                        text += f"  β€’ {item}\n"
                text += "\n"
        
        keyboard = [[InlineKeyboardButton("⬅️ Back", callback_data="back_main")]]
        await query.edit_message_text(text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
    
    # ────────────────────────────────────────────────────────────────────────
    elif action == "weather_detail":
        current = weather_intel.get("current", {})
        forecast = weather_intel.get("forecast", [])
        
        text = f"""

🌦️ <b>DETAILED WEATHER INTELLIGENCE</b>



<b>🌑️ CURRENT CONDITIONS:</b>

Temperature: <b>{current.get('temp', 'N/A')}Β°C</b>

Humidity: <b>{current.get('humidity', 'N/A')}%</b>

Wind Speed: <b>{current.get('wind', 'N/A')} km/h</b>

Condition: <b>{current.get('desc', 'N/A')}</b>

Last Updated: <b>{current.get('updated', 'N/A')}</b>



<b>πŸ“… 7-DAY FORECAST:</b>

"""
        for day in forecast[:7]:
            text += f"\n<b>{day.get('day_name', 'N/A')}:</b> "
            text += f"{day.get('temp_min', 'N/A')}Β°C - {day.get('temp_max', 'N/A')}Β°C | "
            text += f"{day.get('description', 'N/A')}"
        
        text += f"\n\n<b>🎯 RECOMMENDATION:</b>\n"
        text += f"{weather_intel.get('ai_recommendations', 'Monitor weather conditions regularly.')}"
        
        keyboard = [[InlineKeyboardButton("⬅️ Back", callback_data="back_main")]]
        await query.edit_message_text(text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
    
    # ────────────────────────────────────────────────────────────────────────
    elif action == "pest_detail":
        pest_table = pest_intel.get("pest_prediction_table", [])
        
        text = "πŸ› <b>PEST & DISEASE MONITORING</b>\n\n"
        
        if not pest_table:
            text += "βœ… <b>No significant pests predicted</b>\n\nContinue preventive measures."
        else:
            for pest in pest_table[:5]:
                severity_icon = "πŸ”΄" if pest.get("severity", "").upper() == "CRITICAL" else "🟑" if pest.get("severity", "").upper() == "HIGH" else "🟒"
                text += f"{severity_icon} <b>{pest.get('pest_name', 'Unknown')}</b>\n"
                text += f"   Severity: <b>{pest.get('severity', 'N/A')}</b>\n"
                text += f"   Confidence: <b>{pest.get('confidence', 'N/A')}%</b>\n"
                text += f"   Action: {pest.get('action', 'Monitor crop regularly')}\n\n"
        
        keyboard = [[InlineKeyboardButton("⬅️ Back", callback_data="back_main")]]
        await query.edit_message_text(text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
    
    # ────────────────────────────────────────────────────────────────────────
    elif action == "water_detail":
        water = water_intel.get("water_m3_sqm", 0)
        irrigation_mins = water_intel.get("irrigation_minutes", 0)
        
        text = f"""

πŸ’§ <b>IRRIGATION & WATER MANAGEMENT</b>



<b>πŸ“Š WATER REQUIREMENTS:</b>

Water Needed: <b>{water:.4f} mΒ³/mΒ²</b>

Irrigation Duration: <b>{irrigation_mins:.1f} minutes</b>

Weather Condition: <b>{water_intel.get('weather', {}).get('cond', 'N/A')}</b>

Temperature: <b>{water_intel.get('weather', {}).get('temp', 'N/A')}Β°C</b>



<b>πŸ’‘ RECOMMENDATIONS:</b>

β€’ Irrigate using <b>drip irrigation</b> for 50% water savings

β€’ Best time: <b>5-7 AM</b> or <b>6-8 PM</b> (minimal evaporation)

β€’ Apply mulch to reduce water loss by 30-40%

β€’ Monitor soil moisture with meter



<b>🌱 OPTIMAL SCHEDULE:</b>

β€’ Morning irrigation: 5-7 AM

β€’ Evening irrigation: 6-8 PM

β€’ Avoid midday (high evaporation)

"""
        
        keyboard = [[InlineKeyboardButton("⬅️ Back", callback_data="back_main")]]
        await query.edit_message_text(text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
    
    # ────────────────────────────────────────────────────────────────────────
    elif action == "resilience_score":
        critical_days = weather_intel.get("critical_days", [])
        warning_days = weather_intel.get("warning_days", [])
        
        risk_score = len(critical_days) * 25 + len(warning_days) * 15
        risk_score = min(100, risk_score)
        resilience_level = "🟒 GREEN" if risk_score < 30 else "🟑 YELLOW" if risk_score < 70 else "πŸ”΄ RED"
        
        text = f"""

πŸ“ˆ <b>CLIMATE RESILIENCE INDEX: {risk_score:.0f}/100</b>

Status: <b>{resilience_level}</b>



<b>πŸ” RISK ANALYSIS:</b>

Critical Weather Days: <b>{len(critical_days)}</b>

Warning Days: <b>{len(warning_days)}</b>



<b>πŸ›‘οΈ RESILIENCE STRATEGIES:</b>

1. <b>Diversify Crops:</b> Reduces single pest risk by 40%

2. <b>Mulching:</b> Reduces water loss 30-40%

3. <b>Intercropping:</b> Fixes nitrogen naturally

4. <b>Windbreaks:</b> Protects against extreme wind

5. <b>Drip Irrigation:</b> Saves 50% water vs. flood irrigation

6. <b>Soil Testing:</b> Optimize fertilizer use



<b>πŸ“Š NEXT ACTIONS:</b>

β€’ Implement at least 2 strategies this month

β€’ Monitor soil health monthly

β€’ Update crop rotation annually

"""
        
        keyboard = [[InlineKeyboardButton("⬅️ Back", callback_data="back_main")]]
        await query.edit_message_text(text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
    
    # ────────────────────────────────────────────────────────────────────────
    elif action == "sustainability":
        text = """

🌍 <b>SUSTAINABLE AGRICULTURE PRACTICES</b>



<b>πŸ’§ WATER CONSERVATION:</b>

βœ“ Mulching: Reduce evaporation 30-40%

βœ“ Drip Irrigation: Save 50% water vs. flood

βœ“ Rainwater Harvesting: Capture monsoon excess

βœ“ Early morning irrigation: Minimize evaporation



<b>🌱 SOIL HEALTH:</b>

βœ“ Intercropping with legumes: Fix nitrogen naturally

βœ“ Crop rotation: Prevent soil depletion

βœ“ Compost: Make from crop residue (don't burn)

βœ“ No-till farming: Preserve soil structure



<b>πŸ› PEST MANAGEMENT:</b>

βœ“ Beneficial insects: Ladybugs, parasitoid wasps

βœ“ Biopesticides: Neem oil over synthetic chemicals

βœ“ Crop monitoring: Scout weekly for pests

βœ“ Companion planting: Marigolds, mint deter pests



<b>♻️ CIRCULAR BIOECONOMY:</b>

βœ“ Crop residue composting

βœ“ Biogas from farm waste

βœ“ Integrated livestock farming

βœ“ On-farm seed saving



<b>πŸ”‹ RENEWABLE ENERGY:</b>

βœ“ Solar pumps: 0 fuel cost, reliable

βœ“ Biogas generators: Waste β†’ Energy

βœ“ Wind power: If available in region



<b>πŸ’° ECONOMIC BENEFITS:</b>

β€’ 30-40% reduction in input costs

β€’ 15-20% yield increase with soil health

β€’ Premium market prices for organic/sustainable

β€’ Government subsidies for green agriculture

"""
        
        keyboard = [[InlineKeyboardButton("⬅️ Back", callback_data="back_main")]]
        await query.edit_message_text(text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
    
    # ────────────────────────────────────────────────────────────────────────
    elif action == "settings":
        text = """

βš™οΈ <b>SETTINGS & PREFERENCES</b>



πŸ“± <b>CURRENT CONFIGURATION:</b>

β€’ Bot Status: <b>ACTIVE</b>

β€’ Alert Frequency: <b>REAL-TIME</b>

β€’ Weather Alerts: <b>ENABLED</b>

β€’ Pest Alerts: <b>ENABLED</b>

β€’ Water Alerts: <b>ENABLED</b>



<b>πŸ“ CUSTOMIZE (Coming Soon):</b>

β€’ Set alert thresholds

β€’ Choose notification times

β€’ Select alert types

β€’ Language preference



<b>πŸ’‘ TIPS:</b>

β€’ Check dashboard every morning

β€’ Respond to critical alerts within 1 hour

β€’ Keep your profile updated

β€’ Share feedback to improve alerts

"""
        
        keyboard = [
            [InlineKeyboardButton("πŸ”” Alert Settings", callback_data="alert_settings")],
            [InlineKeyboardButton("πŸ“ Location", callback_data="location_settings")],
            [InlineKeyboardButton("⬅️ Back", callback_data="back_main")]
        ]
        await query.edit_message_text(text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
    
    # ────────────────────────────────────────────────────────────────────────
    elif action == "support":
        text = """

πŸ“ž <b>SUPPORT & RESOURCES</b>



<b>❓ FREQUENTLY ASKED QUESTIONS:</b>



<b>Q: When should I irrigate?</b>

A: Early morning (5-7 AM) or evening (6-8 PM) when evaporation is low.



<b>Q: How do I reduce water usage?</b>

A: Use drip irrigation, mulching, and monitor rainfall to skip irrigation.



<b>Q: What does High Pest Risk mean?</b>

A: Conditions are favorable for pest reproduction. Scout crops and spray if needed.



<b>Q: How often should I update my profile?</b>

A: Update crop age monthly and location if you change fields.



<b>πŸ“š USEFUL RESOURCES:</b>

β€’ Agritech Documentation

β€’ Pest Management Guidelines

β€’ Sustainable Farming Manual

β€’ Government Agricultural Schemes



<b>🎯 EMERGENCY CONTACTS:</b>

β€’ Agricultural Officer: +91-XX-XXXXX

β€’ Extension Services: AGRI-HELPLINE

β€’ Pest Control Hotline: PEST-HOTLINE



<b>πŸ’¬ FEEDBACK:</b>

Have suggestions? Reply to any message with "feedback"

"""
        
        keyboard = [[InlineKeyboardButton("⬅️ Back", callback_data="back_main")]]
        await query.edit_message_text(text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
    
    # ────────────────────────────────────────────────────────────────────────
    elif action == "back_main":
        # Go back to main dashboard (for callback query, not message)
        dashboard_data = await get_dashboard_data()
        
        farmer_profile = dashboard_data.get("farmer_profile", {})
        weather_intel = dashboard_data.get("weather_intelligence", {})
        alerts = dashboard_data.get("alerts", [])
        
        risk_score = 0
        for alert in alerts:
            if alert.get("severity") == "CRITICAL":
                risk_score += 35
            elif alert.get("severity") == "HIGH":
                risk_score += 20
            elif alert.get("severity") == "MEDIUM":
                risk_score += 10
        risk_score = min(100, risk_score)
        risk_level = "🟒 STABLE" if risk_score < 30 else "🟑 ALERT" if risk_score < 70 else "πŸ”΄ CRITICAL"
        
        welcome_text = f"""

🌾 <b>CLIMATE-RESILIENT FARM DASHBOARD</b> 🌾

{'═' * 50}



πŸ‘¨β€πŸŒΎ <b>Farmer:</b> {farmer_profile.get('name', 'Unknown')}

πŸ“ <b>Location:</b> {farmer_profile.get('village', 'N/A')}

🌱 <b>Crop:</b> {farmer_profile.get('crop_type', 'N/A')} | <b>Season:</b> {farmer_profile.get('season', 'N/A')}

πŸ“ <b>Farm Size:</b> {farmer_profile.get('farm_size_hectares', 1.0)} Ha



{'─' * 50}

πŸ“Š <b>CLIMATE RESILIENCE INDEX: {risk_score:.0f}/100</b>

Status: <b>{risk_level}</b>

Critical Alerts: <b>{len([a for a in alerts if a.get('severity') == 'CRITICAL'])}</b>

High Priority: <b>{len([a for a in alerts if a.get('severity') == 'HIGH'])}</b>



{'─' * 50}

🌑️ <b>CURRENT CONDITIONS:</b>

Temp: <b>{weather_intel.get('current', {}).get('temp', 'N/A')}Β°C</b> | Humidity: <b>{weather_intel.get('current', {}).get('humidity', 'N/A')}%</b>

Wind: <b>{weather_intel.get('current', {}).get('wind', 'N/A')} km/h</b>

Condition: <b>{weather_intel.get('current', {}).get('desc', 'N/A')}</b>



{'─' * 50}



<i>Select an option below for detailed information:</i>

"""
        
        keyboard = [
            [
                InlineKeyboardButton("πŸ”΄ CRITICAL ALERTS", callback_data="critical_alerts"),
                InlineKeyboardButton("🌦️ WEATHER INTEL", callback_data="weather_detail")
            ],
            [
                InlineKeyboardButton("πŸ› PEST MONITORING", callback_data="pest_detail"),
                InlineKeyboardButton("πŸ’§ WATER MANAGEMENT", callback_data="water_detail")
            ],
            [
                InlineKeyboardButton("πŸ“ˆ RESILIENCE SCORE", callback_data="resilience_score"),
                InlineKeyboardButton("🌍 SUSTAINABILITY", callback_data="sustainability")
            ],
            [
                InlineKeyboardButton("βš™οΈ SETTINGS", callback_data="settings"),
                InlineKeyboardButton("πŸ“ž SUPPORT", callback_data="support")
            ]
        ]
        
        await query.edit_message_text(welcome_text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))

# ════════════════════════════════════════════════════════════════════════════
# UTILITY COMMANDS
# ════════════════════════════════════════════════════════════════════════════

async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Show help information"""
    help_text = """

πŸ€– <b>BOT COMMANDS:</b>



/start - Show main dashboard

/help - Show this help message

/alerts - Get current alerts

/weather - Quick weather check

/water - Irrigation status

/pest - Pest monitoring

/farm - Farm profile

/feedback - Send feedback



<b>πŸ’‘ TIPS:</b>

β€’ Use inline buttons for quick navigation

β€’ Check alerts at least twice daily

β€’ Act on CRITICAL alerts within 1 hour

β€’ Share this bot with neighboring farmers

"""
    
    await update.message.reply_html(help_text)

async def alerts_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Quick alerts summary (OPTIMIZED)"""
    dashboard_data = await get_dashboard_data()
    alerts = dashboard_data.get("alerts", [])
    
    if not alerts:
        await update.message.reply_text("βœ… No alerts at this time!")
        return
    
    text = "🚨 <b>CURRENT ALERTS SUMMARY:</b>\n\n"
    
    for alert in alerts[:10]:
        severity = alert.get("severity", "INFO")
        icon = "πŸ”΄" if severity == "CRITICAL" else "🟠" if severity == "HIGH" else "🟑" if severity == "MEDIUM" else "ℹ️"
        text += f"{icon} <b>{alert.get('title', 'Alert')}</b>\n"
        text += f"   {alert.get('message', '')}\n\n"
    
    await update.message.reply_html(text)

async def feedback_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Feedback handler"""
    await update.message.reply_text(
        "πŸ“ <b>FEEDBACK RECEIVED!</b>\n\n"
        "Thank you for your feedback. Our team will review and improve the bot.\n\n"
        "πŸ‘‰ Reply with your suggestions and we'll implement them!",
        parse_mode=ParseMode.HTML
    )

# ════════════════════════════════════════════════════════════════════════════
# BACKGROUND JOBS
# ════════════════════════════════════════════════════════════════════════════

async def send_daily_alerts(context: ContextTypes.DEFAULT_TYPE):
    """Send daily alerts to all subscribed farmers (OPTIMIZED)"""
    subscriptions = db.get_active_subscriptions()
    dashboard_data = await get_dashboard_data()  # Single fetch for all
    
    critical_count = len([a for a in dashboard_data.get("alerts", []) if a.get("severity") == "CRITICAL"])
    
    for sub in subscriptions:
        chat_id = sub.get("telegram_chat_id")
        try:
            # Send only if there are critical alerts
            if critical_count > 0:
                await context.bot.send_message(
                    chat_id,
                    f"🚨 <b>CRITICAL ALERT DETECTED!</b>\n\n"
                    f"You have <b>{critical_count}</b> critical alerts.\n"
                    f"Use /start to view your dashboard immediately.",
                    parse_mode=ParseMode.HTML
                )
            else:
                await context.bot.send_message(
                    chat_id,
                    "βœ… <b>FARM STATUS NORMAL</b>\n\n"
                    "All conditions are within safe parameters..\n"
                    "Check dashboard with /start for full details.",
                    parse_mode=ParseMode.HTML
                )
        except Exception as e:
            logger.debug(f"Alert delivery to {chat_id}: {e}")

# ════════════════════════════════════════════════════════════════════════════
# MAIN ENTRY POINT
# ════════════════════════════════════════════════════════════════════════════

def main():
    """Start the bot (OPTIMIZED)"""
    app = Application.builder().token(TOKEN).build()
    
    # Handlers
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CommandHandler("help", help_command))
    app.add_handler(CommandHandler("alerts", alerts_command))
    app.add_handler(CommandHandler("feedback", feedback_command))
    app.add_handler(CallbackQueryHandler(button_handler))
    
    # Jobs
    job_queue = app.job_queue
    job_queue.run_daily(send_daily_alerts, time=datetime.strptime("06:00", "%H:%M").time(), name="daily_alerts")
    
    # Periodic cache refresh (every 2 minutes) - keeps data fresh
    async def refresh_cache(context):
        dashboard_cache.clear()
        await get_dashboard_data()
        logger.debug("πŸ”„ Cache refreshed")
    
    job_queue.run_repeating(refresh_cache, interval=120, first=60, name="cache_refresh")
    
    logger.info("πŸ€– Climate-Resilient Agriculture Bot Started!")
    logger.info(f"βœ… Dashboard caching enabled (2-min TTL)")
    logger.info(f"βœ… Auto-cache refresh every 2 minutes")
    
    safe_token = str(TOKEN)[:20] if TOKEN else "MISSING"
    logger.info(f"Token: {safe_token}...")
    
    # Use allowed_updates to reduce unnecessary processing
    # Note: When running in Docker with threading, signal handlers may not work
    try:
        app.run_polling(allowed_updates=["message", "callback_query"], close_loop=False)
    except ValueError as e:
        if "add_signal_handler()" in str(e):
            logger.warning("⚠️ Running in non-main thread (Docker), using fallback polling")
            # Fallback for non-main thread execution
            import asyncio
            try:
                loop = asyncio.get_running_loop()
            except RuntimeError:
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
            
            retry_count = 0
            max_retries = 3  # Reduced from 5 to 3 for faster feedback
            while retry_count < max_retries:
                try:
                    loop.run_until_complete(app.initialize())
                    loop.run_until_complete(app.start())
                    logger.info("πŸ€– Bot polling started (non-signal mode)")
                    try:
                        loop.run_until_complete(app.updater.start_polling(allowed_updates=["message", "callback_query"]))
                    except KeyboardInterrupt:
                        pass
                    finally:
                        loop.run_until_complete(app.stop())
                    break
                except Exception as init_error:
                    retry_count += 1
                    if retry_count >= max_retries:
                        error_type = type(init_error).__name__
                        logger.error(f"Bot initialization failed ({error_type})")
                        logger.info("This is expected if there's no internet/network access.")
                        logger.info("REST APIs will continue working without the Telegram bot.")
                        raise  # Re-raise so app.py can catch it
                    wait_time = min(2 ** retry_count, 16)  # Exponential backoff
                    logger.warning(f"⚠️ Bot init failed (attempt {retry_count}/{max_retries}), retrying in {wait_time}s...")
                    import time
                    time.sleep(wait_time)
        else:
            raise

if __name__ == "__main__":
    main()