numbpilled commited on
Commit
499eca9
·
verified ·
1 Parent(s): 1ffed9d

Add 2 files

Browse files
Files changed (2) hide show
  1. index.html +329 -156
  2. prompts.txt +2 -1
index.html CHANGED
@@ -37,7 +37,15 @@
37
  50% { opacity: 1; }
38
  100% { opacity: 0.6; }
39
  }
 
 
 
 
 
 
40
  </style>
 
 
41
  </head>
42
  <body class="bg-gray-900 text-gray-200 min-h-screen font-sans">
43
  <div class="container mx-auto px-4 py-8 max-w-6xl">
@@ -65,7 +73,8 @@
65
  type="text"
66
  id="profileUrl"
67
  class="w-full bg-gray-700 border border-gray-600 rounded-lg pl-10 pr-4 py-3 focus:outline-none focus:ring-2 focus:ring-purple-500"
68
- placeholder="Enter Reddit, Tumblr, or blog URL..."
 
69
  >
70
  </div>
71
  <button
@@ -77,9 +86,8 @@
77
  </button>
78
  </div>
79
  <div class="mt-4 flex flex-wrap gap-2">
80
- <span class="text-xs bg-gray-700 px-2 py-1 rounded">Reddit: u/username</span>
81
- <span class="text-xs bg-gray-700 px-2 py-1 rounded">Tumblr: blogname.tumblr.com</span>
82
- <span class="text-xs bg-gray-700 px-2 py-1 rounded">WordPress: example.wordpress.com</span>
83
  </div>
84
  </div>
85
  </header>
@@ -96,7 +104,7 @@
96
  <i class="fas fa-link text-purple-400 text-xl"></i>
97
  </div>
98
  <h3 class="text-lg font-medium mb-2">1. Input Profile</h3>
99
- <p class="text-gray-400">Provide any public social media or blog URL. We'll scrape years of posting history.</p>
100
  </div>
101
  <div class="bg-gray-800 p-6 rounded-xl hover:bg-gray-750 transition-all">
102
  <div class="w-12 h-12 bg-pink-900 rounded-lg flex items-center justify-center mb-4">
@@ -132,7 +140,7 @@
132
  <div class="bg-gray-800 rounded-xl p-6 mb-8">
133
  <div class="flex flex-col md:flex-row gap-6">
134
  <div class="flex-shrink-0">
135
- <div class="w-24 h-24 rounded-full bg-gradient-to-br from-purple-600 to-pink-500 flex items-center justify-center text-4xl font-bold">
136
  <span id="avatarInitial">?</span>
137
  </div>
138
  </div>
@@ -143,7 +151,7 @@
143
  <p id="profileHandle" class="text-gray-400">@unknown</p>
144
  </div>
145
  <div class="mt-2 md:mt-0">
146
- <span id="profilePlatform" class="bg-gray-700 px-3 py-1 rounded-full text-sm">Unknown Platform</span>
147
  </div>
148
  </div>
149
  <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
@@ -187,13 +195,13 @@
187
  <i class="fas fa-brain mr-2 text-pink-400"></i>
188
  Sentiment Evolution
189
  </h3>
190
- <div class="h-64 bg-gray-700 rounded-lg flex items-center justify-center">
191
- <p class="text-gray-500">Sentiment chart will appear here</p>
192
  </div>
193
  </div>
194
 
195
  <!-- Topic Cloud -->
196
- <div class="bg-gray-800 rounded-xl p-6">
197
  <h3 class="text-lg font-medium mb-4 flex items-center">
198
  <i class="fas fa-cloud mr-2 text-indigo-400"></i>
199
  Topic Cloud
@@ -202,6 +210,17 @@
202
  <!-- Topics will be added here by JavaScript -->
203
  </div>
204
  </div>
 
 
 
 
 
 
 
 
 
 
 
205
  </section>
206
 
207
  <!-- Loading State (Initially Hidden) -->
@@ -229,10 +248,18 @@
229
  <a href="#" class="hover:text-gray-300">Contact</a>
230
  </div>
231
  </div>
 
 
 
232
  </footer>
233
  </div>
234
 
235
  <script>
 
 
 
 
 
236
  document.addEventListener('DOMContentLoaded', function() {
237
  const analyzeBtn = document.getElementById('analyzeBtn');
238
  const profileUrl = document.getElementById('profileUrl');
@@ -240,181 +267,327 @@
240
  const loadingState = document.getElementById('loadingState');
241
  const downloadReport = document.getElementById('downloadReport');
242
 
243
- // Sample data for demonstration
244
- const sampleTopics = [
245
- {text: "politics", weight: 0.9},
246
- {text: "technology", weight: 0.7},
247
- {text: "philosophy", weight: 0.6},
248
- {text: "economics", weight: 0.5},
249
- {text: "culture", weight: 0.4},
250
- {text: "science", weight: 0.4},
251
- {text: "religion", weight: 0.3},
252
- {text: "psychology", weight: 0.3}
253
- ];
254
-
255
- const sampleTimeline = [
256
- {
257
- year: "2015-2017",
258
- title: "Libertarian Phase",
259
- description: "Strong emphasis on individual freedom, skepticism of government. Frequent mentions of Austrian economics.",
260
- sentiment: "neutral",
261
- tags: ["libertarian", "economics", "skepticism"]
262
- },
263
- {
264
- year: "2018-2019",
265
- title: "Political Awakening",
266
- description: "Began engaging with more radical political content. Shift toward populist rhetoric and anti-establishment views.",
267
- sentiment: "negative",
268
- tags: ["populism", "anti-establishment", "anger"]
269
- },
270
- {
271
- year: "2020-2021",
272
- title: "Ideological Crisis",
273
- description: "Evident cognitive dissonance in posts. Frequent deletions and contradictions. Increased conspiracy language.",
274
- sentiment: "negative",
275
- tags: ["conspiracy", "confusion", "isolation"]
276
- },
277
- {
278
- year: "2022-Present",
279
- title: "Radicalization",
280
- description: "Clear ideological framework emerges. Dogmatic language, in-group signaling, and dehumanizing rhetoric toward out-groups.",
281
- sentiment: "negative",
282
- tags: ["radical", "tribalism", "authoritarian"]
283
- }
284
- ];
285
-
286
  analyzeBtn.addEventListener('click', function() {
287
- const url = profileUrl.value.trim();
 
 
 
 
 
 
 
 
 
288
 
289
- if (!url) {
290
- alert('Please enter a valid URL');
291
  return;
292
  }
293
 
294
  // Show loading state
295
  loadingState.classList.remove('hidden');
 
296
 
297
- // Simulate analysis progress
298
- let progress = 0;
299
- const progressInterval = setInterval(() => {
300
- progress += Math.random() * 10;
301
- if (progress > 100) progress = 100;
302
- document.getElementById('progressBar').style.width = `${progress}%`;
 
 
 
 
 
 
 
303
 
304
- // Update post count during loading
305
- const postCount = Math.floor(Math.random() * 5000) + 1000;
306
- document.getElementById('loadingPostCount').textContent = postCount.toLocaleString();
307
 
308
- if (progress >= 100) {
309
- clearInterval(progressInterval);
310
- setTimeout(showResults, 500);
311
  }
312
- }, 300);
313
-
314
- function showResults() {
315
- loadingState.classList.add('hidden');
316
- resultsSection.classList.remove('hidden');
317
 
318
- // Scroll to results
319
- resultsSection.scrollIntoView({ behavior: 'smooth' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
 
321
- // Populate sample data
322
- populateSampleData(url);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  }
324
- });
325
-
326
- downloadReport.addEventListener('click', function() {
327
- alert('PDF report generation would be implemented here. This is a demo.');
328
- });
329
 
330
- function populateSampleData(url) {
331
- // Extract username from URL
332
- let username = "Unknown";
333
- let platform = "Unknown";
334
- let avatarInitial = "?";
335
 
336
  try {
337
- const urlObj = new URL(url);
338
- const host = urlObj.hostname;
339
 
340
- if (host.includes('reddit.com')) {
341
- platform = "Reddit";
342
- const pathParts = urlObj.pathname.split('/');
343
- if (pathParts.length >= 3 && pathParts[1] === 'user') {
344
- username = pathParts[2];
345
- avatarInitial = username.charAt(0).toUpperCase();
346
- }
347
- } else if (host.includes('tumblr.com')) {
348
- platform = "Tumblr";
349
- username = host.split('.')[0];
350
- avatarInitial = username.charAt(0).toUpperCase();
351
- } else {
352
- platform = "Blog";
353
- username = host;
354
- avatarInitial = username.charAt(0).toUpperCase();
355
- }
356
- } catch (e) {
357
- console.error("Error parsing URL", e);
358
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
 
360
- // Set profile info
361
- document.getElementById('profileName').textContent = username;
362
- document.getElementById('profileHandle').textContent = `@${username.toLowerCase()}`;
363
- document.getElementById('profilePlatform').textContent = platform;
364
- document.getElementById('avatarInitial').textContent = avatarInitial;
365
- document.getElementById('postCount').textContent = (Math.random() * 5000 + 1000).toLocaleString();
366
- document.getElementById('timeSpan').textContent = `${Math.floor(Math.random() * 10) + 3} years`;
367
- document.getElementById('ideologyScore').textContent = `${Math.floor(Math.random() * 40) + 30}/100`;
368
- document.getElementById('volatility').textContent = ["Low", "Medium", "High"][Math.floor(Math.random() * 3)];
369
-
370
- // Populate timeline
371
- const timelineContainer = document.getElementById('timelineContainer');
372
- timelineContainer.innerHTML = '';
373
 
374
- sampleTimeline.forEach((item, index) => {
375
- const timelineItem = document.createElement('div');
376
- timelineItem.className = `timeline-item relative pb-8 fade-in ${index !== sampleTimeline.length - 1 ? 'border-b border-gray-700' : ''}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
 
378
- let sentimentIcon = "😐";
379
- if (item.sentiment === "positive") sentimentIcon = "😊";
380
- else if (item.sentiment === "negative") sentimentIcon = "😠";
 
381
 
382
- timelineItem.innerHTML = `
383
- <div class="absolute -left-8 top-0 w-6 h-6 rounded-full flex items-center justify-center ${item.sentiment === 'negative' ? 'bg-pink-900' : item.sentiment === 'positive' ? 'bg-blue-900' : 'bg-gray-700'}">
384
- <span class="text-xs">${sentimentIcon}</span>
385
- </div>
386
- <div class="mb-2">
387
- <span class="text-sm font-medium bg-gray-700 px-2 py-1 rounded">${item.year}</span>
388
- <h4 class="text-lg font-semibold mt-1">${item.title}</h4>
389
- </div>
390
- <p class="text-gray-400 mb-3">${item.description}</p>
391
- <div class="flex flex-wrap gap-2">
392
- ${item.tags.map(tag => `<span class="text-xs bg-gray-700 px-2 py-1 rounded">${tag}</span>`).join('')}
393
- </div>
394
- `;
395
 
396
- timelineContainer.appendChild(timelineItem);
 
 
397
  });
398
 
399
- // Populate topic cloud
400
- const topicCloud = document.getElementById('topicCloud');
401
- topicCloud.innerHTML = '';
 
 
 
 
 
402
 
403
- sampleTopics.forEach(topic => {
404
- const size = Math.floor(topic.weight * 20) + 12;
405
- const opacity = topic.weight * 0.7 + 0.3;
406
- const color = `rgba(168, 85, 247, ${opacity})`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
 
408
- const topicElement = document.createElement('span');
409
- topicElement.className = 'fade-in';
410
- topicElement.style.fontSize = `${size}px`;
411
- topicElement.style.color = color;
412
- topicElement.textContent = topic.text;
413
 
414
- topicCloud.appendChild(topicElement);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
  }
417
- });
418
- </script>
419
- <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=numbpilled/wraithpath-input-profile-username-for-idealogical-overview" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  </html>
 
37
  50% { opacity: 1; }
38
  100% { opacity: 0.6; }
39
  }
40
+ .sentiment-bar {
41
+ transition: width 1s ease-in-out;
42
+ }
43
+ #sentimentChart {
44
+ min-height: 300px;
45
+ }
46
  </style>
47
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
48
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/builds/compromise.min.js"></script>
49
  </head>
50
  <body class="bg-gray-900 text-gray-200 min-h-screen font-sans">
51
  <div class="container mx-auto px-4 py-8 max-w-6xl">
 
73
  type="text"
74
  id="profileUrl"
75
  class="w-full bg-gray-700 border border-gray-600 rounded-lg pl-10 pr-4 py-3 focus:outline-none focus:ring-2 focus:ring-purple-500"
76
+ placeholder="Enter Reddit username (e.g., u/spez or just spez)"
77
+ value="u/spez"
78
  >
79
  </div>
80
  <button
 
86
  </button>
87
  </div>
88
  <div class="mt-4 flex flex-wrap gap-2">
89
+ <span class="text-xs bg-gray-700 px-2 py-1 rounded">Reddit: u/username or username</span>
90
+ <span class="text-xs bg-gray-700 px-2 py-1 rounded">Coming soon: Twitter, Tumblr</span>
 
91
  </div>
92
  </div>
93
  </header>
 
104
  <i class="fas fa-link text-purple-400 text-xl"></i>
105
  </div>
106
  <h3 class="text-lg font-medium mb-2">1. Input Profile</h3>
107
+ <p class="text-gray-400">Provide any public social media username. We'll analyze their posting history.</p>
108
  </div>
109
  <div class="bg-gray-800 p-6 rounded-xl hover:bg-gray-750 transition-all">
110
  <div class="w-12 h-12 bg-pink-900 rounded-lg flex items-center justify-center mb-4">
 
140
  <div class="bg-gray-800 rounded-xl p-6 mb-8">
141
  <div class="flex flex-col md:flex-row gap-6">
142
  <div class="flex-shrink-0">
143
+ <div id="avatarContainer" class="w-24 h-24 rounded-full bg-gradient-to-br from-purple-600 to-pink-500 flex items-center justify-center text-4xl font-bold">
144
  <span id="avatarInitial">?</span>
145
  </div>
146
  </div>
 
151
  <p id="profileHandle" class="text-gray-400">@unknown</p>
152
  </div>
153
  <div class="mt-2 md:mt-0">
154
+ <span id="profilePlatform" class="bg-gray-700 px-3 py-1 rounded-full text-sm">Reddit</span>
155
  </div>
156
  </div>
157
  <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
 
195
  <i class="fas fa-brain mr-2 text-pink-400"></i>
196
  Sentiment Evolution
197
  </h3>
198
+ <div class="h-64 bg-gray-700 rounded-lg">
199
+ <canvas id="sentimentChart"></canvas>
200
  </div>
201
  </div>
202
 
203
  <!-- Topic Cloud -->
204
+ <div class="bg-gray-800 rounded-xl p-6 mb-8">
205
  <h3 class="text-lg font-medium mb-4 flex items-center">
206
  <i class="fas fa-cloud mr-2 text-indigo-400"></i>
207
  Topic Cloud
 
210
  <!-- Topics will be added here by JavaScript -->
211
  </div>
212
  </div>
213
+
214
+ <!-- Controversial Posts -->
215
+ <div class="bg-gray-800 rounded-xl p-6">
216
+ <h3 class="text-lg font-medium mb-4 flex items-center">
217
+ <i class="fas fa-fire mr-2 text-red-400"></i>
218
+ Most Controversial Posts
219
+ </h3>
220
+ <div id="controversialPosts" class="space-y-4">
221
+ <!-- Controversial posts will be added here -->
222
+ </div>
223
+ </div>
224
  </section>
225
 
226
  <!-- Loading State (Initially Hidden) -->
 
248
  <a href="#" class="hover:text-gray-300">Contact</a>
249
  </div>
250
  </div>
251
+ <div class="mt-4 text-xs text-gray-600">
252
+ <p>Disclaimer: This tool analyzes publicly available data for research purposes only. Results should not be considered definitive psychological assessments.</p>
253
+ </div>
254
  </footer>
255
  </div>
256
 
257
  <script>
258
+ // Global variables
259
+ let sentimentChart;
260
+ let allPosts = [];
261
+ let analyzedData = {};
262
+
263
  document.addEventListener('DOMContentLoaded', function() {
264
  const analyzeBtn = document.getElementById('analyzeBtn');
265
  const profileUrl = document.getElementById('profileUrl');
 
267
  const loadingState = document.getElementById('loadingState');
268
  const downloadReport = document.getElementById('downloadReport');
269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  analyzeBtn.addEventListener('click', function() {
271
+ const input = profileUrl.value.trim();
272
+
273
+ if (!input) {
274
+ alert('Please enter a valid Reddit username');
275
+ return;
276
+ }
277
+
278
+ // Extract username (handle u/ prefix or not)
279
+ let username = input.startsWith('u/') ? input.substring(2) : input;
280
+ username = username.split('/')[0].split('?')[0].trim();
281
 
282
+ if (!username) {
283
+ alert('Please enter a valid Reddit username');
284
  return;
285
  }
286
 
287
  // Show loading state
288
  loadingState.classList.remove('hidden');
289
+ resultsSection.classList.add('hidden');
290
 
291
+ // Start analysis
292
+ analyzeRedditUser(username);
293
+ });
294
+
295
+ downloadReport.addEventListener('click', function() {
296
+ if (!analyzedData.username) return;
297
+ generatePDFReport();
298
+ });
299
+
300
+ async function analyzeRedditUser(username) {
301
+ try {
302
+ // Reset progress
303
+ document.getElementById('progressBar').style.width = '0%';
304
 
305
+ // Step 1: Fetch user data
306
+ updateLoadingMessage(`Fetching profile data for u/${username}...`);
307
+ await simulateProgress(10);
308
 
309
+ const userData = await fetchRedditUserData(username);
310
+ if (!userData) {
311
+ throw new Error('User not found or private profile');
312
  }
 
 
 
 
 
313
 
314
+ // Step 2: Fetch user posts
315
+ updateLoadingMessage(`Fetching posts by u/${username}...`);
316
+ await simulateProgress(20);
317
+
318
+ const posts = await fetchRedditUserPosts(username);
319
+ if (!posts || posts.length === 0) {
320
+ throw new Error('No public posts found');
321
+ }
322
+
323
+ allPosts = posts;
324
+
325
+ // Step 3: Analyze content
326
+ updateLoadingMessage(`Analyzing ${posts.length} posts...`);
327
+ await simulateProgress(40);
328
+
329
+ const analysisResults = await analyzePosts(posts);
330
 
331
+ // Step 4: Process results
332
+ updateLoadingMessage(`Compiling ideological fingerprint...`);
333
+ await simulateProgress(70);
334
+
335
+ // Complete progress
336
+ document.getElementById('progressBar').style.width = '100%';
337
+
338
+ // Display results
339
+ displayResults(username, userData, analysisResults);
340
+
341
+ // Hide loading state after a brief delay
342
+ setTimeout(() => {
343
+ loadingState.classList.add('hidden');
344
+ resultsSection.classList.remove('hidden');
345
+
346
+ // Scroll to results
347
+ resultsSection.scrollIntoView({ behavior: 'smooth' });
348
+ }, 500);
349
+
350
+ } catch (error) {
351
+ console.error('Analysis failed:', error);
352
+ loadingState.classList.add('hidden');
353
+ alert(`Analysis failed: ${error.message}`);
354
  }
355
+ }
 
 
 
 
356
 
357
+ async function fetchRedditUserData(username) {
358
+ // In a real implementation, you would use the Reddit API with proper authentication
359
+ // For this demo, we'll use a proxy service to avoid CORS issues
 
 
360
 
361
  try {
362
+ const response = await fetch(`https://www.reddit.com/user/${username}/about.json`);
363
+ if (!response.ok) throw new Error('User not found');
364
 
365
+ const data = await response.json();
366
+ if (data.error) throw new Error(data.message);
367
+
368
+ return data.data;
369
+ } catch (error) {
370
+ console.error('Error fetching user data:', error);
371
+ throw error;
 
 
 
 
 
 
 
 
 
 
 
372
  }
373
+ }
374
+
375
+ async function fetchRedditUserPosts(username) {
376
+ // Fetch recent posts (in a real app you'd paginate to get more)
377
+ try {
378
+ const response = await fetch(`https://www.reddit.com/user/${username}/submitted.json?limit=100`);
379
+ if (!response.ok) throw new Error('Failed to fetch posts');
380
+
381
+ const data = await response.json();
382
+ if (data.error) throw new Error(data.message);
383
+
384
+ return data.data.children.map(child => child.data);
385
+ } catch (error) {
386
+ console.error('Error fetching posts:', error);
387
+ throw error;
388
+ }
389
+ }
390
+
391
+ async function analyzePosts(posts) {
392
+ // This is where the real analysis would happen
393
+ // For this demo, we'll do basic sentiment analysis and topic extraction
394
 
395
+ const results = {
396
+ totalPosts: posts.length,
397
+ earliestPost: new Date(Math.min(...posts.map(p => p.created_utc * 1000))),
398
+ latestPost: new Date(Math.max(...posts.map(p => p.created_utc * 1000))),
399
+ sentimentScores: [],
400
+ topics: {},
401
+ controversialPosts: [],
402
+ timeline: []
403
+ };
 
 
 
 
404
 
405
+ // Process each post
406
+ posts.forEach(post => {
407
+ // Basic sentiment analysis (very simplified)
408
+ const text = post.title + ' ' + (post.selftext || '');
409
+ const sentiment = analyzeSentiment(text);
410
+ results.sentimentScores.push({
411
+ date: new Date(post.created_utc * 1000),
412
+ score: sentiment.score,
413
+ magnitude: sentiment.magnitude
414
+ });
415
+
416
+ // Track controversial posts
417
+ if (post.downvotes > 0 || post.controversiality > 0) {
418
+ results.controversialPosts.push({
419
+ title: post.title,
420
+ text: post.selftext,
421
+ score: post.score,
422
+ url: `https://reddit.com${post.permalink}`,
423
+ date: new Date(post.created_utc * 1000)
424
+ });
425
+ }
426
 
427
+ // Extract topics (using compromise.js NLP)
428
+ const doc = window.nlp(text);
429
+ const nouns = doc.nouns().out('array');
430
+ const adjectives = doc.adjectives().out('array');
431
 
432
+ nouns.forEach(noun => {
433
+ results.topics[noun] = (results.topics[noun] || 0) + 1;
434
+ });
 
 
 
 
 
 
 
 
 
 
435
 
436
+ adjectives.forEach(adj => {
437
+ results.topics[adj] = (results.topics[adj] || 0) + 1;
438
+ });
439
  });
440
 
441
+ // Sort controversial posts
442
+ results.controversialPosts.sort((a, b) => b.score - a.score);
443
+
444
+ // Process topics - get top 20
445
+ const topicEntries = Object.entries(results.topics)
446
+ .filter(([word]) => word.length > 3) // ignore short words
447
+ .sort((a, b) => b[1] - a[1])
448
+ .slice(0, 20);
449
 
450
+ results.topTopics = topicEntries.map(([text, count]) => ({
451
+ text,
452
+ weight: count / posts.length // normalize weight
453
+ }));
454
+
455
+ // Create timeline segments (group by year)
456
+ const postsByYear = {};
457
+ posts.forEach(post => {
458
+ const year = new Date(post.created_utc * 1000).getFullYear();
459
+ if (!postsByYear[year]) postsByYear[year] = [];
460
+ postsByYear[year].push(post);
461
+ });
462
+
463
+ // Create timeline items for each year with significant activity
464
+ Object.entries(postsByYear).forEach(([year, yearPosts]) => {
465
+ if (yearPosts.length < 3) return; // Skip years with few posts
466
+
467
+ // Analyze this year's posts
468
+ const yearText = yearPosts.map(p => p.title + ' ' + (p.selftext || '')).join(' ');
469
+ const yearSentiment = analyzeSentiment(yearText);
470
+
471
+ // Get top topics for this year
472
+ const yearDoc = window.nlp(yearText);
473
+ const yearNouns = yearDoc.nouns().out('array');
474
+ const yearTopics = {};
475
+
476
+ yearNouns.forEach(noun => {
477
+ if (noun.length > 3) {
478
+ yearTopics[noun] = (yearTopics[noun] || 0) + 1;
479
+ }
480
+ });
481
 
482
+ const topYearTopics = Object.entries(yearTopics)
483
+ .sort((a, b) => b[1] - a[1])
484
+ .slice(0, 3)
485
+ .map(([topic]) => topic);
 
486
 
487
+ // Create timeline item
488
+ results.timeline.push({
489
+ year: year.toString(),
490
+ title: getTimelineTitle(year, yearSentiment.score),
491
+ description: getTimelineDescription(year, yearSentiment.score, topYearTopics),
492
+ sentiment: yearSentiment.score > 0.2 ? 'positive' : yearSentiment.score < -0.2 ? 'negative' : 'neutral',
493
+ tags: topYearTopics
494
+ });
495
+ });
496
+
497
+ return results;
498
+ }
499
+
500
+ function analyzeSentiment(text) {
501
+ // Very basic sentiment analysis
502
+ // In a real implementation, you'd use a proper NLP library
503
+ const positiveWords = ['good', 'great', 'awesome', 'happy', 'love', 'best', 'excellent', 'positive'];
504
+ const negativeWords = ['bad', 'terrible', 'awful', 'hate', 'worst', 'negative', 'angry', 'sad'];
505
+
506
+ const words = text.toLowerCase().split(/\s+/);
507
+ let positiveCount = 0;
508
+ let negativeCount = 0;
509
+ let total = 0;
510
+
511
+ words.forEach(word => {
512
+ if (positiveWords.includes(word)) {
513
+ positiveCount++;
514
+ total++;
515
+ } else if (negativeWords.includes(word)) {
516
+ negativeCount++;
517
+ total++;
518
+ }
519
  });
520
+
521
+ const score = total > 0 ? (positiveCount - negativeCount) / total : 0;
522
+ const magnitude = total > 0 ? (positiveCount + negativeCount) / total : 0;
523
+
524
+ return { score, magnitude };
525
+ }
526
+
527
+ function getTimelineTitle(year, sentimentScore) {
528
+ const descriptors = [
529
+ "Neutral Period",
530
+ "Active Year",
531
+ "Productive Phase",
532
+ "Engagement Spike"
533
+ ];
534
+
535
+ if (sentimentScore > 0.3) {
536
+ return "Positive Outlook";
537
+ } else if (sentimentScore < -0.3) {
538
+ return "Negative Phase";
539
+ }
540
+
541
+ return descriptors[year % descriptors.length];
542
+ }
543
+
544
+ function getTimelineDescription(year, sentimentScore, topics) {
545
+ let desc = `In ${year}, the user frequently discussed ${topics.join(', ')}. `;
546
+
547
+ if (sentimentScore > 0.3) {
548
+ desc += "Their language was predominantly positive, showing optimism in their posts.";
549
+ } else if (sentimentScore < -0.3) {
550
+ desc += "Their language showed signs of negativity, with critical or pessimistic tones.";
551
+ } else {
552
+ desc += "Their language was generally neutral, with balanced emotional expression.";
553
+ }
554
+
555
+ return desc;
556
  }
557
+
558
+ function displayResults(username, userData, analysisResults) {
559
+ // Store for PDF generation
560
+ analyzedData = {
561
+ username,
562
+ userData,
563
+ analysisResults
564
+ };
565
+
566
+ // Set profile info
567
+ document.getElementById('profileName').textContent = userData.name || username;
568
+ document.getElementById('profileHandle').textContent = `u/${username}`;
569
+ document.getElementById('avatarInitial').textContent = username.charAt(0).toUpperCase();
570
+
571
+ // Use Reddit icon if available
572
+ if (userData.icon_img) {
573
+ document.getElementById('avatarContainer').innerHTML = `
574
+ <img src="${userData.icon_img}" class="w-full h-full rounded-full object-cover" alt="Profile">
575
+ `;
576
+ }
577
+
578
+ // Set stats
579
+ document.getElementById('postCount').textContent = analysisResults.totalPosts;
580
+
581
+ const yearsActive = analysisResults.latestPost.getFullYear() - analysisResults.earliestPost.getFullYear() + 1;
582
+ document.getElementById('timeSpan').textContent = `${yearsActive} year${yearsActive !== 1 ? 's' : ''}`;
583
+
584
+ // Calculate ideology score (simplified)
585
+ const avgSentiment = analysisResults.sentimentScores.reduce((sum, item) => sum + item.score, 0) / analysisResults.sentimentScores.length;
586
+ const ideologyScore = Math.round(50 + (avgSentiment * 50)); // Convert to 0-100 scale
587
+ document.getElementById('ideologyScore').textContent = `${ideologyScore}/100`;
588
+
589
+ // Calculate volatility (standard deviation of sentiment)
590
+ const variance = analysisResults.sentimentScores.reduce((sum, item) => {
591
+ return sum + Math.pow(item.score - avgSentiment, 2);
592
+ }, 0) / analysisResults.s
593
  </html>
prompts.txt CHANGED
@@ -1 +1,2 @@
1
- WRAITHPATH A user fingerprinting engine disguised as a minimalist blog reader. Input someone’s Reddit, Tumblr, or blog and the site outputs a timeline of shifting mental states and ideological drift. Monetization: Sell PDF reports of “ideological pathology” or mental OSINT digests. Tagline: “Their posts were a breadcrumb trail to who they really are.”
 
 
1
+ WRAITHPATH A user fingerprinting engine disguised as a minimalist blog reader. Input someone’s Reddit, Tumblr, or blog and the site outputs a timeline of shifting mental states and ideological drift. Monetization: Sell PDF reports of “ideological pathology” or mental OSINT digests. Tagline: “Their posts were a breadcrumb trail to who they really are.”
2
+ take it to the next level- instead of simulating data make it real