from flask import Flask, render_template, request, jsonify import re import requests from urllib.parse import urlparse, parse_qs import json from datetime import datetime import os from typing import Dict, List, Tuple import time import random from openai import OpenAI app = Flask(__name__) class ScamSignalVerifier: def __init__(self): # Initialize OpenAI client self.openai_client = None self._init_openai() self.scam_keywords = [ 'urgent', 'limited time', 'act now', 'click here', 'guaranteed', 'free money', 'no risk', 'earn money fast', 'work from home', 'congratulations', 'you have won', 'claim now', 'verify account', 'suspended', 'confirm identity', 'update payment', 'bitcoin', 'cryptocurrency', 'investment opportunity', 'double your money', 'risk-free', 'government grant', 'tax refund', 'inheritance', 'lottery', 'sweepstakes', 'prince', 'transfer funds', 'logged out', 'share details', 'bank account', 'account details', 'provide details', 'get back', 'restore access' ] self.suspicious_domains = [ 'bit.ly', 'tinyurl.com', 'shortened.link', 'click.me', 'secure-bank.net', 'paypal-verify.com', 'amazon-update.net' ] self.legitimate_domains = [ 'google.com', 'facebook.com', 'amazon.com', 'paypal.com', 'microsoft.com', 'apple.com', 'linkedin.com', 'twitter.com', 'instagram.com', 'youtube.com', 'github.com', 'stackoverflow.com' ] def _init_openai(self): """Initialize OpenAI client with API key from Hugging Face secrets""" try: api_key = os.environ.get('OPENAI_API_KEY') if api_key: self.openai_client = OpenAI(api_key=api_key) print("✅ OpenAI API initialized successfully") else: print("⚠️ OpenAI API key not found - falling back to rule-based analysis only") self.openai_client = None except Exception as e: print(f"⚠️ Failed to initialize OpenAI API: {str(e)}") self.openai_client = None def ai_analyze_scam(self, text: str, url: str = None) -> Dict: """Use OpenAI to perform intelligent scam analysis""" if not self.openai_client: print("DEBUG: OpenAI client not available") return { 'ai_available': False, 'ai_risk_score': 0, 'ai_confidence': 0, 'ai_explanation': 'AI analysis unavailable - using rule-based detection only', 'scam_type': 'Unknown', 'social_engineering_tactics': [], 'sophistication_level': 'N/A' } try: print("DEBUG: Making OpenAI API call...") # Construct the analysis prompt analysis_prompt = f""" You are an expert cybersecurity analyst specializing in scam detection. Analyze the following message for scam indicators using advanced pattern recognition and social engineering analysis. MESSAGE TO ANALYZE: "{text}" {f'ASSOCIATED URL: {url}' if url else ''} Please provide a comprehensive analysis in JSON format with these exact fields: 1. "ai_risk_score": Integer from 0-100 (0=definitely legitimate, 100=definitely scam) 2. "ai_confidence": Integer from 0-100 (how confident you are in your assessment) 3. "scam_type": String identifying the primary scam type (e.g., "Phishing", "419 Scam", "Investment Fraud", "Romance Scam", "Tech Support Scam", "Lottery Scam", "Legitimate") 4. "social_engineering_tactics": Array of strings describing psychological manipulation techniques used 5. "sophistication_level": String ("Low", "Medium", "High") - how well-crafted the scam attempt is 6. "ai_explanation": String with detailed reasoning for your assessment 7. "grammar_quality": String ("Poor", "Fair", "Good", "Excellent") - grammar and writing quality assessment 8. "emotional_manipulation": String describing emotional appeals used (urgency, fear, greed, etc.) Focus on: - Subtle linguistic patterns that indicate deception - Psychological manipulation techniques - Context inconsistencies - Grammar/spelling patterns typical of scams - Social engineering red flags - Urgency and pressure tactics - Trust-building followed by requests - Too-good-to-be-true offers Respond ONLY with valid JSON format. """ # Make API call to OpenAI response = self.openai_client.chat.completions.create( model="gpt-4o-mini", messages=[ { "role": "system", "content": "You are an expert scam detection AI. You must respond only with valid JSON format as requested." }, { "role": "user", "content": analysis_prompt } ], max_tokens=800, temperature=0.3 # Lower temperature for more consistent analysis ) # Parse the AI response ai_response = response.choices[0].message.content.strip() print(f"DEBUG: OpenAI raw response: {ai_response}") # Try to parse JSON response try: ai_analysis = json.loads(ai_response) ai_analysis['ai_available'] = True print(f"DEBUG: AI analysis successful - risk score: {ai_analysis.get('ai_risk_score', 'N/A')}") return ai_analysis except json.JSONDecodeError as je: print(f"DEBUG: JSON parsing failed: {str(je)}") # Fallback if JSON parsing fails return { 'ai_available': True, 'ai_risk_score': 50, # Default moderate risk when parsing fails 'ai_confidence': 30, 'ai_explanation': f'AI analysis completed but response parsing failed. Raw response: {ai_response[:200]}...', 'scam_type': 'Analysis Error', 'social_engineering_tactics': [], 'sophistication_level': 'Unknown' } except Exception as e: print(f"DEBUG: OpenAI API error: {str(e)}") return { 'ai_available': False, 'ai_risk_score': 0, 'ai_confidence': 0, 'ai_explanation': f'AI analysis failed: {str(e)}', 'scam_type': 'Analysis Failed', 'social_engineering_tactics': [], 'sophistication_level': 'N/A' } def extract_claims(self, text: str, url: str = None) -> Dict: claims = { 'text_content': text, 'urls': [], 'phone_numbers': [], 'email_addresses': [], 'money_amounts': [], 'suspicious_phrases': [], 'urgency_indicators': [] } url_pattern = r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+' claims['urls'] = re.findall(url_pattern, text) if url: claims['urls'].append(url) phone_pattern = r'\b(?:\+?1[-.\s]?)?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})\b' claims['phone_numbers'] = re.findall(phone_pattern, text) email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' claims['email_addresses'] = re.findall(email_pattern, text) money_pattern = r'\$[\d,]+\.?\d*|\d+\s*(?:dollars?|USD|euros?|pounds?)' claims['money_amounts'] = re.findall(money_pattern, text, re.IGNORECASE) text_lower = text.lower() for keyword in self.scam_keywords: if keyword in text_lower: claims['suspicious_phrases'].append(keyword) high_risk_patterns = [ 'you won', 'you have won', 'winner', 'selected winner', 'send bank details', 'provide bank account', 'wire transfer', 'inheritance fund', 'beneficiary', 'claim your prize', 'processing fee', 'activation fee', 'clearance fee', 'verify your account', 'account suspended', 'account locked', 'logged out', 'share details', 'get your bank account back', 'bank account', 'share bank details', 'provide details' ] for pattern in high_risk_patterns: if pattern in text_lower: claims['suspicious_phrases'].append(pattern) urgency_patterns = [ r'within \d+\s*(?:hours?|days?|minutes?)', r'expires? (?:today|tomorrow|soon)', r'limited time', r'act (?:now|immediately|fast)', r'urgent.{0,20}(?:action|response|reply)' ] for pattern in urgency_patterns: matches = re.findall(pattern, text_lower) claims['urgency_indicators'].extend(matches) return claims def verify_claims(self, claims: Dict) -> Dict: verification_results = { 'domain_analysis': {}, 'keyword_score': 0, 'urgency_score': 0, 'contact_info_risk': 0, 'financial_risk': 0 } for url in claims['urls']: try: parsed = urlparse(url) domain = parsed.netloc.lower() if any(suspicious in domain for suspicious in self.suspicious_domains): verification_results['domain_analysis'][domain] = 'HIGH_RISK' elif any(legit in domain for legit in self.legitimate_domains): verification_results['domain_analysis'][domain] = 'LOW_RISK' elif len(domain.split('.')) > 3: verification_results['domain_analysis'][domain] = 'MEDIUM_RISK' else: verification_results['domain_analysis'][domain] = 'UNKNOWN' except: verification_results['domain_analysis'][url] = 'INVALID_URL' verification_results['keyword_score'] = min(100, len(claims['suspicious_phrases']) * 15) verification_results['urgency_score'] = min(100, len(claims['urgency_indicators']) * 25) if claims['phone_numbers'] and claims['email_addresses']: verification_results['contact_info_risk'] = 60 elif claims['phone_numbers'] or claims['email_addresses']: verification_results['contact_info_risk'] = 30 if claims['money_amounts']: verification_results['financial_risk'] = 70 if len(claims['money_amounts']) > 2 else 40 return verification_results def calculate_hybrid_risk_score(self, claims: Dict, verification: Dict, ai_analysis: Dict) -> Tuple[int, str]: """Calculate combined risk score using both rule-based and AI analysis""" text_lower = claims['text_content'].lower() print(f"DEBUG: Analyzing text: '{text_lower}'") # Debug line # Start with base rule-based scoring rule_based_score = 0 rule_based_score += verification['keyword_score'] * 0.4 rule_based_score += verification['urgency_score'] * 0.25 rule_based_score += verification['contact_info_risk'] * 0.25 rule_based_score += verification['financial_risk'] * 0.36 print(f"DEBUG: Base scores - keyword:{verification['keyword_score']}, urgency:{verification['urgency_score']}, contact:{verification['contact_info_risk']}, financial:{verification['financial_risk']}") # Domain analysis domain_risk = 0 for domain, risk in verification['domain_analysis'].items(): if risk == 'HIGH_RISK': domain_risk += 30 elif risk == 'MEDIUM_RISK': domain_risk += 15 elif risk == 'INVALID_URL': domain_risk += 10 rule_based_score += min(30, domain_risk) # CRITICAL SCAM PATTERNS - These should trigger HIGH scores immediately critical_patterns = [ ('send bank details', 80), ('provide bank details', 80), ('share bank details', 80), ('bank account details', 75), ('send details', 70), ('share details', 70), ('provide details', 65), ('logged out', 60), ('account suspended', 65), ('account locked', 65), ('verify account', 55), ('confirm identity', 55) ] pattern_bonus = 0 for pattern, score in critical_patterns: if pattern in text_lower: pattern_bonus = max(pattern_bonus, score) print(f"DEBUG: Found critical pattern '{pattern}' - adding {score} points") rule_based_score += pattern_bonus # High-risk combinations bank_terms = ['bank account', 'bank details', 'account details', 'account info'] action_terms = ['send', 'provide', 'share', 'give'] threat_terms = ['logged out', 'suspended', 'locked', 'blocked', 'expired'] has_bank = any(term in text_lower for term in bank_terms) has_action = any(term in text_lower for term in action_terms) has_threat = any(term in text_lower for term in threat_terms) if has_bank and has_action: rule_based_score += 40 print("DEBUG: Bank + Action combo detected - adding 40 points") if has_threat and (has_bank or has_action): rule_based_score += 30 print("DEBUG: Threat + Bank/Action combo detected - adding 30 points") # Lottery/prize scams if any(phrase in text_lower for phrase in ['you won', 'you have won', 'congratulations', 'winner']): if any(word in text_lower for word in ['send', 'provide', 'bank', 'details', 'account']): rule_based_score += 40 print("DEBUG: Prize + financial request detected - adding 40 points") # Investment/money scams if any(phrase in text_lower for phrase in ['double your money', 'guaranteed return', 'risk-free', 'investment opportunity']): rule_based_score += 35 print("DEBUG: Investment scam pattern detected - adding 35 points") rule_based_score = min(100, int(rule_based_score)) print(f"DEBUG: Final rule-based score: {rule_based_score}") # Handle AI analysis final_score = rule_based_score if ai_analysis['ai_available'] and ai_analysis.get('ai_risk_score', 0) > 0: ai_score = ai_analysis['ai_risk_score'] ai_confidence = ai_analysis.get('ai_confidence', 0) print(f"DEBUG: AI analysis - score: {ai_score}, confidence: {ai_confidence}") if ai_confidence > 30: # Combine scores, but don't let AI override obvious high-risk patterns ai_weight = min(0.4, ai_confidence / 100 * 0.6) # Reduced AI weight rule_weight = 1 - ai_weight combined_score = (rule_based_score * rule_weight) + (ai_score * ai_weight) # Take the higher of rule-based or combined score final_score = max(rule_based_score, int(combined_score)) print(f"DEBUG: Combined with AI - final score: {final_score}") final_score = min(100, final_score) # Determine classification if final_score >= 70: classification = "HIGH RISK" elif final_score >= 45: classification = "MEDIUM RISK" elif final_score >= 20: classification = "LOW RISK" else: classification = "MINIMAL RISK" print(f"DEBUG: Final classification: {classification} (score: {final_score})") return final_score, classification def generate_enhanced_explanation(self, claims: Dict, verification: Dict, ai_analysis: Dict, risk_score: int, classification: str) -> Dict: red_flags = [] recommendations = [] # Rule-based red flags if len(claims['suspicious_phrases']) > 0: red_flags.append(f"Contains {len(claims['suspicious_phrases'])} suspicious keywords/phrases") if verification['urgency_score'] > 20: red_flags.append("Uses high-pressure urgency tactics") for domain, risk in verification['domain_analysis'].items(): if risk == 'HIGH_RISK': red_flags.append(f"Uses suspicious domain: {domain}") elif risk == 'MEDIUM_RISK': red_flags.append(f"Domain structure looks suspicious: {domain}") elif risk == 'INVALID_URL': red_flags.append(f"Contains invalid/malformed URL: {domain}") if claims['money_amounts']: red_flags.append("Mentions money/financial transactions") if len(claims['urls']) > 3: red_flags.append("Contains multiple links (potential redirect chains)") text_lower = claims['text_content'].lower() if any(phrase in text_lower for phrase in ['you won', 'you have won', 'winner']): red_flags.append("Claims you won money/prizes (common lottery scam)") if any(phrase in text_lower for phrase in ['send bank', 'provide bank', 'bank details', 'account details', 'share details']): red_flags.append("Requests bank account details (major red flag)") if any(phrase in text_lower for phrase in ['logged out', 'account suspended', 'account locked', 'account blocked']): red_flags.append("Claims account issues to pressure immediate action") if 'processing fee' in text_lower or 'activation fee' in text_lower: red_flags.append("Requests upfront fees (advance fee fraud)") # Add AI-based insights if ai_analysis['ai_available']: if ai_analysis.get('scam_type') and ai_analysis['scam_type'] not in ['Legitimate', 'Analysis Error', 'Analysis Failed']: red_flags.append(f"🤖 AI detected: {ai_analysis['scam_type']} pattern") for tactic in ai_analysis.get('social_engineering_tactics', []): red_flags.append(f"🤖 Social engineering: {tactic}") if ai_analysis.get('sophistication_level') == 'High': red_flags.append("🤖 AI detected sophisticated scam techniques") # Enhanced recommendations based on AI analysis if classification == "HIGH RISK": recommendations = [ "🚫 This is very likely a SCAM - Do NOT respond or engage", "🚫 Do NOT click any links or provide personal information", "🚫 Do NOT send money or share financial details", "📧 Report this message as spam/phishing immediately", "🛡️ Block the sender and delete the message", "👥 Warn friends/family about this type of scam" ] if ai_analysis['ai_available'] and ai_analysis.get('scam_type'): recommendations.append(f"🤖 Specific threat: This appears to be a {ai_analysis['scam_type']} - research this scam type for more info") elif classification == "MEDIUM RISK": recommendations = [ "⚠️ HIGH CAUTION ADVISED - Verify independently before taking action", "🔍 Contact the organization directly using official channels", "🚫 Don't click links - navigate to official websites manually", "👨‍👩‍👧‍👦 Ask a trusted friend or family member for advice", "⏰ Take time to think - don't rush into decisions", "🛡️ Never share sensitive personal or financial information" ] elif classification == "LOW RISK": recommendations = [ "⚠️ Proceed with caution - some concerning elements detected", "🔍 Verify any claims through official sources", "🛡️ Avoid sharing sensitive personal information", "📞 When in doubt, contact the organization directly" ] else: recommendations = [ "✅ Message appears relatively safe", "🛡️ Always practice general online safety", "🔍 Verify important information independently" ] # Enhanced explanation with AI insights explanation = { 'risk_score': risk_score, 'classification': classification, 'summary': self._generate_enhanced_summary(classification, len(red_flags), ai_analysis), 'red_flags': red_flags, 'recommendations': recommendations, 'ai_insights': { 'available': ai_analysis['ai_available'], 'confidence': ai_analysis.get('ai_confidence', 0), 'scam_type': ai_analysis.get('scam_type', 'N/A'), 'sophistication': ai_analysis.get('sophistication_level', 'N/A'), 'explanation': ai_analysis.get('ai_explanation', 'AI analysis not available') }, 'report_template': self._generate_report_template(claims) } return explanation def _generate_enhanced_summary(self, classification: str, flag_count: int, ai_analysis: Dict) -> str: ai_suffix = "" if ai_analysis['ai_available']: confidence = ai_analysis.get('ai_confidence', 0) if confidence > 70: ai_suffix = f" AI analysis confirms with {confidence}% confidence." elif confidence > 30: ai_suffix = f" AI analysis supports this assessment ({confidence}% confidence)." if classification == "HIGH RISK": return f"This message shows {flag_count} major warning signs of a scam.{ai_suffix} Avoid all interaction and report immediately." elif classification == "MEDIUM RISK": return f"This message has {flag_count} concerning elements.{ai_suffix} Exercise extreme caution and verify independently." elif classification == "LOW RISK": return f"While not immediately dangerous, this message has {flag_count} minor concerns.{ai_suffix} Stay alert." else: return f"This message appears legitimate.{ai_suffix} Always practice good online safety habits." def _generate_report_template(self, claims: Dict) -> str: template = f""" SCAM REPORT TEMPLATE: Subject: Reporting Suspicious Message/Scam Attempt Dear [Platform/Authority], I received a suspicious message that appears to be a scam attempt. Details: Date Received: {datetime.now().strftime('%Y-%m-%d %H:%M')} Suspicious Elements: - Keywords: {', '.join(claims['suspicious_phrases'][:5])} - URLs: {', '.join(claims['urls'][:3])} - Contact Info: {', '.join([str(p) for p in claims['phone_numbers'][:2]])} Message Content: [Include original message here] Please investigate and take appropriate action. Best regards, [Your Name] """.strip() return template def analyze_message(self, text: str, url: str = None) -> Dict: """Enhanced analysis combining rule-based and AI methods""" # Step 1: Extract claims (rule-based) claims = self.extract_claims(text, url) # Step 2: Verify claims (rule-based) verification = self.verify_claims(claims) # Step 3: AI analysis (new intelligent layer) ai_analysis = self.ai_analyze_scam(text, url) # Step 4: Calculate hybrid risk score risk_score, classification = self.calculate_hybrid_risk_score(claims, verification, ai_analysis) # Step 5: Generate enhanced explanation explanation = self.generate_enhanced_explanation(claims, verification, ai_analysis, risk_score, classification) return { 'claims': claims, 'verification': verification, 'ai_analysis': ai_analysis, 'explanation': explanation, 'analysis_method': 'hybrid' if ai_analysis['ai_available'] else 'rule-based', 'timestamp': datetime.now().isoformat() } verifier = ScamSignalVerifier() @app.route('/') def index(): return render_template('index.html') @app.route('/analyze', methods=['POST']) def analyze(): try: data = request.get_json() message_text = data.get('message', '').strip() message_url = data.get('url', '').strip() debug_mode = data.get('debug', False) # Add debug parameter if not message_text and not message_url: return jsonify({'error': 'Please provide either a message or URL to analyze'}), 400 # Add small delay to prevent API abuse time.sleep(0.5) # Reduced delay since we now have AI processing time result = verifier.analyze_message(message_text, message_url if message_url else None) # Add debug information if requested if debug_mode: result['debug_info'] = { 'rule_based_score_breakdown': { 'keyword_score': result['verification']['keyword_score'], 'urgency_score': result['verification']['urgency_score'], 'domain_risk': result['verification'].get('domain_risk', 0), 'contact_info_risk': result['verification']['contact_info_risk'], 'financial_risk': result['verification']['financial_risk'] }, 'detected_phrases': result['claims']['suspicious_phrases'], 'ai_available': result['ai_analysis']['ai_available'], 'ai_risk_score': result['ai_analysis'].get('ai_risk_score', 0), 'ai_confidence': result['ai_analysis'].get('ai_confidence', 0) } return jsonify(result) except Exception as e: return jsonify({'error': f'Analysis failed: {str(e)}'}), 500 @app.route('/test', methods=['POST']) def test_analysis(): """Test endpoint with detailed debugging""" try: data = request.get_json() message_text = data.get('message', '').strip() if not message_text: return jsonify({'error': 'Please provide a message to test'}), 400 print(f"\n=== TESTING MESSAGE: '{message_text}' ===") # Test individual components claims = verifier.extract_claims(message_text) print(f"Claims extracted: {claims}") verification = verifier.verify_claims(claims) print(f"Verification results: {verification}") ai_analysis = verifier.ai_analyze_scam(message_text) print(f"AI analysis: {ai_analysis}") risk_score, classification = verifier.calculate_hybrid_risk_score(claims, verification, ai_analysis) print(f"Final score: {risk_score}, classification: {classification}") return jsonify({ 'message': message_text, 'claims': claims, 'verification': verification, 'ai_analysis': ai_analysis, 'final_score': risk_score, 'classification': classification, 'debug': 'Check server logs for detailed output' }) except Exception as e: print(f"Test failed with error: {str(e)}") return jsonify({'error': f'Test failed: {str(e)}'}), 500 @app.route('/health') def health(): """Health check endpoint that also reports AI availability""" ai_status = "available" if verifier.openai_client else "unavailable" api_key_present = "yes" if os.environ.get('OPENAI_API_KEY') else "no" return jsonify({ 'status': 'healthy', 'ai_status': ai_status, 'api_key_present': api_key_present, 'timestamp': datetime.now().isoformat() }) if __name__ == '__main__': port = int(os.environ.get('PORT', 7860)) app.run(host='0.0.0.0', port=port, debug=False)