|
| 1 | + |
| 2 | +import os |
| 3 | +import logging |
| 4 | +from datetime import datetime, timedelta, time |
| 5 | +from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup |
| 6 | +from telegram.constants import ParseMode |
| 7 | +from telegram.ext import ( |
| 8 | + ContextTypes, |
| 9 | + CommandHandler, |
| 10 | + MessageHandler, |
| 11 | + filters, |
| 12 | + CallbackQueryHandler, |
| 13 | + ConversationHandler, |
| 14 | +) |
| 15 | +from config import ADMIN_IDS |
| 16 | +from utils.badge_manager import check_badges |
| 17 | +from utils import loan_utils |
| 18 | +import database |
| 19 | +from decimal import Decimal |
| 20 | +import psycopg2.extras |
| 21 | + |
| 22 | +logger = logging.getLogger(__name__) |
| 23 | + |
| 24 | +# Conversation states |
| 25 | +SELECT_LOAN_BUTTON = 1 |
| 26 | +LOAN_CONFIRM = 2 |
| 27 | + |
| 28 | +# Constants |
| 29 | +UNVERIFIED = 0 |
| 30 | +VERIFIED = 1 |
| 31 | +LOAN_PAID = 'paid' |
| 32 | +LOAN_OVERDUE = 'overdue' |
| 33 | +LOAN_CREDITED = 'credited' |
| 34 | +LOAN_PENDING_VERIFICATION = 'pending_payment_verification' |
| 35 | + |
| 36 | +LEVEL_SPECIFIC_AMOUNTS = { |
| 37 | + 'L1': Decimal('5'), |
| 38 | + 'L2': Decimal('10'), |
| 39 | + 'L3': Decimal('15'), |
| 40 | + 'L4': Decimal('20'), |
| 41 | + 'L5': Decimal('25'), |
| 42 | +} |
| 43 | + |
| 44 | +async def get_or_create_user(user_id, username): |
| 45 | + current_time = datetime.now() |
| 46 | + with database.get_db() as conn: |
| 47 | + cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) |
| 48 | + cursor.execute("SELECT username, verification_status FROM users WHERE user_id = %s", (user_id,)) |
| 49 | + existing_user = cursor.fetchone() |
| 50 | + |
| 51 | + if existing_user: |
| 52 | + if existing_user['username'] != username: |
| 53 | + cursor.execute("UPDATE users SET username = %s WHERE user_id = %s", (username, user_id)) |
| 54 | + return existing_user |
| 55 | + else: |
| 56 | + cursor.execute(""" |
| 57 | + INSERT INTO users (user_id, username, total_rep, level, join_date, verification_status, last_active) |
| 58 | + VALUES (%s, %s, 0, 1, %s, %s, %s) |
| 59 | + """, (user_id, username, current_time, UNVERIFIED, current_time)) |
| 60 | + return {'username': username, 'verification_status': UNVERIFIED} |
| 61 | + |
| 62 | +async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): |
| 63 | + """Handle /start command""" |
| 64 | + if update.message.chat.type == 'private': |
| 65 | + # Handle verification if coming from group |
| 66 | + if context.args and context.args[0].startswith('verify_'): |
| 67 | + await handle_start_verification(update, context) |
| 68 | + return |
| 69 | + |
| 70 | + await update.message.reply_text( |
| 71 | + "🌟 DegenCred Bot - Private Chat 🌟\n\n" |
| 72 | + "This bot helps manage reputation in groups.\n" |
| 73 | + "Use these commands in group chats:\n" |
| 74 | + "/plusrep - Give reputation to others\n" |
| 75 | + "/reputation - Check your stats\n" |
| 76 | + "/top - See leaderboard" |
| 77 | + ) |
| 78 | + else: |
| 79 | + await update.message.reply_text( |
| 80 | + "🌟 DegenCred Bot 🌟\n\n" |
| 81 | + "Give reputation with /plusrep\n" |
| 82 | + "Check stats with /reputation\n" |
| 83 | + "See leaderboard with /top" |
| 84 | + ) |
| 85 | + |
| 86 | +async def handle_start_verification(update: Update, context: ContextTypes.DEFAULT_TYPE): |
| 87 | + """Handle verification process""" |
| 88 | + if not update.message or update.message.chat.type != 'private': |
| 89 | + return |
| 90 | + |
| 91 | + args = context.args |
| 92 | + effective_user = update.effective_user |
| 93 | + |
| 94 | + if args and args[0].startswith('verify_'): |
| 95 | + try: |
| 96 | + _, user_id_str, chat_id_str = args[0].split('_') |
| 97 | + user_id = int(user_id_str) |
| 98 | + chat_id = int(chat_id_str) |
| 99 | + |
| 100 | + if effective_user.id != user_id: |
| 101 | + await update.message.reply_text("❌ You can only verify your own account!") |
| 102 | + return |
| 103 | + |
| 104 | + # Update verification status |
| 105 | + with database.get_db() as conn: |
| 106 | + cursor = conn.cursor() |
| 107 | + cursor.execute("UPDATE users SET verification_status = %s WHERE user_id = %s", (VERIFIED, user_id)) |
| 108 | + |
| 109 | + # Remove verification message if exists |
| 110 | + cursor.execute("DELETE FROM verification_messages WHERE user_id = %s", (user_id,)) |
| 111 | + |
| 112 | + await update.message.reply_text("✅ Account verified successfully! You can now use bot commands.") |
| 113 | + |
| 114 | + except Exception as e: |
| 115 | + logger.error(f"Error in verification: {e}") |
| 116 | + await update.message.reply_text("❌ Verification failed. Please try again.") |
| 117 | + |
| 118 | +async def plusrep(update: Update, context: ContextTypes.DEFAULT_TYPE): |
| 119 | + """Handle /plusrep command""" |
| 120 | + if update.message.chat.type not in ('group', 'supergroup'): |
| 121 | + await update.message.reply_text("This command only works in groups!") |
| 122 | + return |
| 123 | + |
| 124 | + if not update.message.reply_to_message: |
| 125 | + await update.message.reply_text("🔍 Reply to someone to give them reputation!") |
| 126 | + return |
| 127 | + |
| 128 | + giver = update.effective_user |
| 129 | + target_user = update.message.reply_to_message.from_user |
| 130 | + |
| 131 | + if giver.id == target_user.id: |
| 132 | + await update.message.reply_text("❌ You can't give reputation to yourself!") |
| 133 | + return |
| 134 | + |
| 135 | + # Create or get users |
| 136 | + await get_or_create_user(giver.id, giver.username or giver.first_name) |
| 137 | + await get_or_create_user(target_user.id, target_user.username or target_user.first_name) |
| 138 | + |
| 139 | + # Check if giver is verified |
| 140 | + with database.get_db() as conn: |
| 141 | + cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) |
| 142 | + cursor.execute("SELECT verification_status FROM users WHERE user_id = %s", (giver.id,)) |
| 143 | + giver_data = cursor.fetchone() |
| 144 | + |
| 145 | + if not giver_data or giver_data['verification_status'] != VERIFIED: |
| 146 | + await send_verification_prompt(context, update.effective_chat.id, giver.id, giver.username or giver.first_name) |
| 147 | + return |
| 148 | + |
| 149 | + # Add reputation |
| 150 | + cursor.execute("UPDATE users SET total_rep = total_rep + 1 WHERE user_id = %s", (target_user.id,)) |
| 151 | + |
| 152 | + # Record transaction |
| 153 | + cursor.execute(""" |
| 154 | + INSERT INTO reputation_transactions (from_user_id, to_user_id, chat_id, amount, timestamp) |
| 155 | + VALUES (%s, %s, %s, 1, %s) |
| 156 | + """, (giver.id, target_user.id, update.effective_chat.id, datetime.now())) |
| 157 | + |
| 158 | + await update.message.reply_text(f"✅ +1 rep to @{target_user.username or target_user.first_name}!") |
| 159 | + |
| 160 | +async def send_verification_prompt(context: ContextTypes.DEFAULT_TYPE, chat_id: int, user_id: int, username: str): |
| 161 | + """Send verification prompt to user""" |
| 162 | + keyboard = [ |
| 163 | + [InlineKeyboardButton("✅ Verify Account", url=f"https://t.me/{context.bot.username}?start=verify_{user_id}_{chat_id}")] |
| 164 | + ] |
| 165 | + reply_markup = InlineKeyboardMarkup(keyboard) |
| 166 | + |
| 167 | + try: |
| 168 | + await context.bot.send_message( |
| 169 | + chat_id=chat_id, |
| 170 | + text=f"👋 @{username}, please verify your account to use bot commands!\n" |
| 171 | + "Click the button below to verify:", |
| 172 | + reply_markup=reply_markup |
| 173 | + ) |
| 174 | + except Exception as e: |
| 175 | + logger.error(f"Error sending verification prompt: {e}") |
| 176 | + |
| 177 | +async def reputation(update: Update, context: ContextTypes.DEFAULT_TYPE): |
| 178 | + """Handle /reputation command""" |
| 179 | + target_user = update.effective_user |
| 180 | + |
| 181 | + if update.message.reply_to_message: |
| 182 | + target_user = update.message.reply_to_message.from_user |
| 183 | + |
| 184 | + await get_or_create_user(target_user.id, target_user.username or target_user.first_name) |
| 185 | + |
| 186 | + with database.get_db() as conn: |
| 187 | + cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) |
| 188 | + cursor.execute("SELECT * FROM users WHERE user_id = %s", (target_user.id,)) |
| 189 | + user_data = cursor.fetchone() |
| 190 | + |
| 191 | + if not user_data: |
| 192 | + await update.message.reply_text("❌ User not found!") |
| 193 | + return |
| 194 | + |
| 195 | + rep_text = ( |
| 196 | + f"👤 <b>@{user_data['username']}</b>\n" |
| 197 | + f"⭐ Reputation: {user_data['total_rep']}\n" |
| 198 | + f"🏆 Level: {user_data['level']}\n" |
| 199 | + f"📅 Joined: {user_data['join_date'].strftime('%Y-%m-%d')}\n" |
| 200 | + f"✅ Status: {'Verified' if user_data['verification_status'] == VERIFIED else 'Unverified'}" |
| 201 | + ) |
| 202 | + |
| 203 | + await update.message.reply_text(rep_text, parse_mode=ParseMode.HTML) |
| 204 | + |
| 205 | +async def leaderboard(update: Update, context: ContextTypes.DEFAULT_TYPE): |
| 206 | + """Handle /top command""" |
| 207 | + with database.get_db() as conn: |
| 208 | + cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) |
| 209 | + cursor.execute(""" |
| 210 | + SELECT user_id, username, total_rep, level |
| 211 | + FROM users |
| 212 | + WHERE verification_status = %s |
| 213 | + ORDER BY total_rep DESC |
| 214 | + LIMIT 10 |
| 215 | + """, (VERIFIED,)) |
| 216 | + top_users = cursor.fetchall() |
| 217 | + |
| 218 | + if not top_users: |
| 219 | + await update.message.reply_text("📊 No users found on the leaderboard yet!") |
| 220 | + return |
| 221 | + |
| 222 | + leaderboard_text = "🏆 <b>Top 10 Degens</b> 🏆\n\n" |
| 223 | + for i, user in enumerate(top_users): |
| 224 | + medal = ["🥇", "🥈", "🥉"][i] if i < 3 else f"▫️ {i+1}." |
| 225 | + leaderboard_text += f"{medal} @{user['username']} - ⭐ {user['total_rep']} (Level {user['level']})\n" |
| 226 | + |
| 227 | + await update.message.reply_text(leaderboard_text, parse_mode=ParseMode.HTML) |
| 228 | + |
| 229 | +def setup_handlers(application): |
| 230 | + """Set up all bot handlers""" |
| 231 | + # Basic commands |
| 232 | + application.add_handler(CommandHandler("start", start)) |
| 233 | + application.add_handler(CommandHandler("plusrep", plusrep)) |
| 234 | + application.add_handler(CommandHandler("reputation", reputation)) |
| 235 | + application.add_handler(CommandHandler("top", leaderboard)) |
| 236 | + |
| 237 | + logger.info("All handlers registered successfully") |
| 238 | + |
0 commit comments