Skip to content

Instantly share code, notes, and snippets.

@charleyXuTO
Created January 12, 2026 03:23
Show Gist options
  • Select an option

  • Save charleyXuTO/8c7bd9a2b5e9caca0f7b7e9fd9ae2793 to your computer and use it in GitHub Desktop.

Select an option

Save charleyXuTO/8c7bd9a2b5e9caca0f7b7e9fd9ae2793 to your computer and use it in GitHub Desktop.
import React, { useState } from 'react';
import { AlertCircle, MessageSquare, Sparkles } from 'lucide-react';
export default function BaitDetector() {
const [statement, setStatement] = useState('');
const [analysis, setAnalysis] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const analyzeStatement = async () => {
if (!statement.trim()) {
setError('Please enter a statement to analyze');
return;
}
setLoading(true);
setError(null);
setAnalysis(null);
try {
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'claude-sonnet-4-20250514',
max_tokens: 1000,
messages: [
{
role: 'user',
content: `Analyze the following statement and determine if it's "bait" (intentionally provocative, trolling, designed to get a reaction) or if it appears to be a sincerely held belief.
Statement: "${statement}"
Respond with a JSON object (and ONLY JSON, no markdown formatting) with this structure:
{
"classification": "bait" or "sincere",
"confidence": a number from 0-100,
"reasoning": "brief explanation of why you classified it this way",
"indicators": ["list", "of", "specific", "indicators"]
}`
}
]
})
});
const data = await response.json();
if (data.error) {
throw new Error(data.error.message || 'API request failed');
}
const textContent = data.content
.filter(item => item.type === 'text')
.map(item => item.text)
.join('');
// Clean up any markdown formatting
const cleanedText = textContent.replace(/```json\n?|\n?```/g, '').trim();
const result = JSON.parse(cleanedText);
setAnalysis(result);
} catch (err) {
setError(err.message || 'Failed to analyze statement');
console.error('Error:', err);
} finally {
setLoading(false);
}
};
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
analyzeStatement();
}
};
const getClassificationColor = (classification) => {
return classification === 'bait' ? 'text-red-600' : 'text-green-600';
};
const getClassificationBg = (classification) => {
return classification === 'bait' ? 'bg-red-50 border-red-200' : 'bg-green-50 border-green-200';
};
return (
<div className="min-h-screen bg-gradient-to-br from-indigo-50 via-white to-purple-50 p-6">
<div className="max-w-3xl mx-auto">
<div className="text-center mb-8">
<div className="flex items-center justify-center gap-2 mb-2">
<MessageSquare className="w-8 h-8 text-indigo-600" />
<h1 className="text-4xl font-bold text-gray-800">🎣 Bait Detector 🎣</h1>
</div>
<p className="text-gray-600">Analyze whether a statement is genuine or just trolling πŸ˜€πŸ˜‚ (for Charley's groupchat)</p>
</div>
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
Enter a statement to analyze:
</label>
<textarea
value={statement}
onChange={(e) => setStatement(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="e.g., 'I genuinely think pineapple belongs on pizza 😀' or 'Climate change is fake news lmao πŸ˜‚'"
className="w-full h-32 px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent resize-none"
/>
{error && (
<div className="mt-3 p-3 bg-red-50 border border-red-200 rounded-lg flex items-start gap-2">
<AlertCircle className="w-5 h-5 text-red-600 flex-shrink-0 mt-0.5" />
<p className="text-sm text-red-800">{error}</p>
</div>
)}
<button
onClick={analyzeStatement}
disabled={loading}
className="mt-4 w-full bg-indigo-600 hover:bg-indigo-700 disabled:bg-gray-400 text-white font-semibold py-3 px-6 rounded-lg transition-colors flex items-center justify-center gap-2"
>
{loading ? (
<>
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
Analyzing... πŸ€”
</>
) : (
<>
<Sparkles className="w-5 h-5" />
Analyze Statement πŸ”
</>
)}
</button>
</div>
{analysis && (
<div className={`bg-white rounded-lg shadow-lg p-6 border-2 ${getClassificationBg(analysis.classification)}`}>
<div className="flex items-center justify-between mb-4">
<h2 className="text-2xl font-bold text-gray-800">Analysis Result πŸ“Š</h2>
<div className={`px-4 py-2 rounded-full font-bold ${getClassificationColor(analysis.classification)} ${getClassificationBg(analysis.classification)} border-2`}>
{analysis.classification === 'bait' ? '😀 BAIT 😀' : 'πŸ’― SINCERE πŸ’―'}
</div>
</div>
<div className="space-y-4">
<div>
<h3 className="text-sm font-semibold text-gray-700 mb-1">Confidence Level πŸ’ͺ</h3>
<div className="flex items-center gap-3">
<div className="flex-1 bg-gray-200 rounded-full h-3 overflow-hidden">
<div
className={`h-full transition-all duration-500 ${
analysis.classification === 'bait' ? 'bg-red-500' : 'bg-green-500'
}`}
style={{ width: `${analysis.confidence}%` }}
/>
</div>
<span className="font-bold text-gray-700">{analysis.confidence}%</span>
</div>
</div>
<div>
<h3 className="text-sm font-semibold text-gray-700 mb-2">Reasoning 🧠</h3>
<p className="text-gray-800">{analysis.reasoning}</p>
</div>
<div>
<h3 className="text-sm font-semibold text-gray-700 mb-2">Key Indicators 🚩</h3>
<ul className="space-y-1">
{analysis.indicators.map((indicator, idx) => (
<li key={idx} className="flex items-start gap-2">
<span className="text-indigo-600 mt-1">β€’</span>
<span className="text-gray-700">{indicator}</span>
</li>
))}
</ul>
</div>
</div>
</div>
)}
<div className="mt-6 text-center text-sm text-gray-500">
<p>Powered by Claude AI πŸ€– β€’ Don't take the bait πŸ˜‚</p>
</div>
</div>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment