abdullahtarar commited on
Commit
29a5f5d
·
verified ·
1 Parent(s): 043e7e5

Update js/script.js

Browse files
Files changed (1) hide show
  1. js/script.js +667 -667
js/script.js CHANGED
@@ -1,668 +1,668 @@
1
- // Chat Configuration
2
- const DEFAULT_API_KEY = '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';
3
- const DEFAULT_MODEL = 'qwen/qwen3-14b:free';
4
-
5
- let chatConfig = {
6
- apiKey: DEFAULT_API_KEY,
7
- model: DEFAULT_MODEL,
8
- siteUrl: window.location.origin,
9
- siteName: 'Muhafiz AI Chat'
10
- };
11
-
12
- // Conversation history
13
- let conversationHistory = [];
14
-
15
- // DOM Elements
16
- const messageInput = document.getElementById('message-input');
17
- const sendButton = document.getElementById('send-btn');
18
- const chatMessages = document.getElementById('chat-messages');
19
- const moreButton = document.querySelector('.more-btn');
20
- const newChatButton = document.getElementById('new-chat-btn');
21
- const menuButton = document.querySelector('.menu-btn');
22
- const menuOverlay = document.getElementById('menuOverlay');
23
- const closeMenuButton = document.getElementById('closeMenuBtn');
24
- const conversationList = document.getElementById('conversationList');
25
- const seeMoreBtn = document.getElementById('seeMoreBtn');
26
- const themeToggleBtn = document.getElementById('themeToggleBtn');
27
-
28
- // Initialize chat
29
- function initChat() {
30
- // Load saved configuration and conversations
31
- loadChatConfig();
32
- loadConversations();
33
- loadTheme();
34
-
35
- // Add event listeners
36
- messageInput.addEventListener('input', handleInput);
37
- messageInput.addEventListener('keypress', handleKeyPress);
38
- sendButton.addEventListener('click', handleSend);
39
- moreButton.addEventListener('click', handleMore);
40
- newChatButton.addEventListener('click', handleNewChat);
41
- menuButton.addEventListener('click', handleMenuOpen);
42
- closeMenuButton.addEventListener('click', handleMenuClose);
43
- seeMoreBtn.addEventListener('click', handleSeeMore);
44
- themeToggleBtn.addEventListener('click', handleThemeToggle);
45
- menuOverlay.addEventListener('click', (e) => {
46
- if (e.target === menuOverlay) {
47
- handleMenuClose();
48
- }
49
- });
50
-
51
- // Disable send button initially
52
- sendButton.disabled = true;
53
- sendButton.style.opacity = '0.5';
54
-
55
- // Update status bar time
56
- updateTime();
57
- setInterval(updateTime, 60000);
58
- }
59
-
60
- // Load conversations from localStorage
61
- function loadConversations() {
62
- const savedConversations = localStorage.getItem('conversations');
63
- if (savedConversations) {
64
- conversationHistory = JSON.parse(savedConversations);
65
-
66
- // If there are saved conversations, update the chat interface
67
- if (conversationHistory.length > 0) {
68
- const currentConversation = conversationHistory[0];
69
- if (currentConversation.messages.length > 0) {
70
- // Clear welcome message
71
- while (chatMessages.firstChild) {
72
- chatMessages.removeChild(chatMessages.firstChild);
73
- }
74
-
75
- // Load messages from the current conversation
76
- currentConversation.messages.forEach(msg => {
77
- addMessage(msg.content, msg.type);
78
- });
79
- }
80
- }
81
- }
82
- updateConversationList();
83
- }
84
-
85
- // Save conversations to localStorage
86
- function saveConversations() {
87
- localStorage.setItem('conversations', JSON.stringify(conversationHistory));
88
- }
89
-
90
- // Update conversation list in menu
91
- function updateConversationList() {
92
- conversationList.innerHTML = '';
93
- const recentConversations = conversationHistory.slice(0, 4);
94
-
95
- if (recentConversations.length === 0) {
96
- const emptyState = document.createElement('div');
97
- emptyState.className = 'empty-conversations';
98
- emptyState.textContent = 'No conversations yet';
99
- conversationList.appendChild(emptyState);
100
- return;
101
- }
102
-
103
- recentConversations.forEach(conv => {
104
- const item = document.createElement('div');
105
- item.className = 'conversation-item';
106
- item.innerHTML = `
107
- <i class="fas fa-comment"></i>
108
- <span>${conv.title}</span>
109
- <span class="message-count">${conv.messages.length} messages</span>
110
- `;
111
- item.addEventListener('click', () => loadConversation(conv.id));
112
- conversationList.appendChild(item);
113
- });
114
- }
115
-
116
- // Handle new chat
117
- async function handleNewChat() {
118
- // If there's an existing conversation, generate a title for it
119
- if (conversationHistory.length > 0 && conversationHistory[0].messages.length > 0) {
120
- const currentConversation = conversationHistory[0];
121
- try {
122
- const messages = currentConversation.messages.map(msg => `${msg.type}: ${msg.content}`).join('\n');
123
- const titlePrompt = `Please summarize this conversation in 4-5 words:\n${messages}`;
124
-
125
- // Show loading state for the conversation title
126
- const tempTitle = 'Generating title...';
127
- currentConversation.title = tempTitle;
128
- updateConversationList();
129
-
130
- // Get AI-generated title
131
- const response = await fetchBotResponse(titlePrompt);
132
- currentConversation.title = response.slice(0, 40).trim();
133
- saveConversations();
134
- updateConversationList();
135
- } catch (error) {
136
- console.error('Error generating title:', error);
137
- // Fallback to first message if title generation fails
138
- currentConversation.title = currentConversation.messages[0].content.slice(0, 30) + '...';
139
- saveConversations();
140
- updateConversationList();
141
- }
142
- }
143
-
144
- // Create new conversation
145
- const newConversation = {
146
- id: Date.now(),
147
- title: 'New Chat',
148
- messages: []
149
- };
150
-
151
- // Add to conversations array
152
- conversationHistory.unshift(newConversation);
153
- saveConversations();
154
- updateConversationList();
155
-
156
- // Clear chat messages
157
- while (chatMessages.firstChild) {
158
- chatMessages.removeChild(chatMessages.firstChild);
159
- }
160
-
161
- // Add welcome message
162
- const welcomeMessage = document.createElement('div');
163
- welcomeMessage.className = 'welcome-message';
164
- welcomeMessage.innerHTML = `
165
- <img src="images/welcome-logo.png" alt="Muhafiz AI Logo" class="welcome-logo">
166
- <h2>Hi, I'm Muhafiz AI.</h2>
167
- <p>Your digital safety assistant dedicated to combating cyber threats and providing support.</p>
168
- <div class="context-info">Ask me about cyber security, online protection, or digital safety concerns.</div>
169
- `;
170
- chatMessages.appendChild(welcomeMessage);
171
-
172
- // Clear input
173
- messageInput.value = '';
174
- handleInput({ target: messageInput });
175
-
176
- // Focus input
177
- messageInput.focus();
178
- }
179
-
180
- // Handle see more button click
181
- function handleSeeMore() {
182
- // Create full conversation list overlay
183
- const overlay = document.createElement('div');
184
- overlay.className = 'conversation-overlay';
185
-
186
- const content = document.createElement('div');
187
- content.className = 'conversation-overlay-content';
188
-
189
- // Add header
190
- content.innerHTML = `
191
- <div class="overlay-header">
192
- <h2>All Conversations</h2>
193
- <button class="close-overlay-btn"><i class="fas fa-times"></i></button>
194
- </div>
195
- <div class="conversation-list-full"></div>
196
- `;
197
-
198
- // Add all conversations
199
- const listContainer = content.querySelector('.conversation-list-full');
200
- conversationHistory.forEach(conv => {
201
- const item = document.createElement('div');
202
- item.className = 'conversation-item';
203
- item.innerHTML = `
204
- <i class="fas fa-comment"></i>
205
- <div class="conversation-details">
206
- <span class="conversation-title">${conv.title}</span>
207
- <span class="message-count">${conv.messages.length} messages</span>
208
- <span class="conversation-date">${new Date(conv.id).toLocaleDateString()}</span>
209
- </div>
210
- `;
211
- item.addEventListener('click', () => {
212
- loadConversation(conv.id);
213
- overlay.remove();
214
- });
215
- listContainer.appendChild(item);
216
- });
217
-
218
- // Add close button handler
219
- overlay.appendChild(content);
220
- content.querySelector('.close-overlay-btn').addEventListener('click', () => {
221
- overlay.remove();
222
- });
223
-
224
- // Add overlay click handler
225
- overlay.addEventListener('click', (e) => {
226
- if (e.target === overlay) {
227
- overlay.remove();
228
- }
229
- });
230
-
231
- document.body.appendChild(overlay);
232
- }
233
-
234
- // Load specific conversation
235
- function loadConversation(id) {
236
- const conversation = conversationHistory.find(conv => conv.id === id);
237
- if (conversation) {
238
- // Clear current chat
239
- while (chatMessages.firstChild) {
240
- chatMessages.removeChild(chatMessages.firstChild);
241
- }
242
-
243
- // Load conversation messages
244
- conversation.messages.forEach(msg => {
245
- addMessage(msg.content, msg.type);
246
- });
247
-
248
- // Close menu
249
- handleMenuClose();
250
- }
251
- }
252
-
253
- // Handle input changes
254
- function handleInput(e) {
255
- const isEmpty = !e.target.value.trim();
256
- sendButton.style.opacity = isEmpty ? '0.5' : '1';
257
- sendButton.disabled = isEmpty;
258
- }
259
-
260
- // Handle enter key
261
- function handleKeyPress(e) {
262
- if (e.key === 'Enter' && !e.shiftKey) {
263
- e.preventDefault();
264
- handleSend();
265
- }
266
- }
267
-
268
- // Handle send button click
269
- async function handleSend() {
270
- const message = messageInput.value.trim();
271
- if (!message) return;
272
-
273
- // Add user message to chat and conversation history
274
- addMessage(message, 'user');
275
- if (conversationHistory.length === 0) {
276
- handleNewChat();
277
- }
278
-
279
- // Update current conversation
280
- const currentConversation = conversationHistory[0];
281
- currentConversation.messages.push({ type: 'user', content: message });
282
- if (currentConversation.messages.length === 1) {
283
- // Update conversation title with first message
284
- currentConversation.title = message.slice(0, 30) + (message.length > 30 ? '...' : '');
285
- updateConversationList();
286
- }
287
- saveConversations();
288
-
289
- messageInput.value = '';
290
- handleInput({ target: messageInput });
291
-
292
- try {
293
- // Show typing indicator
294
- showTypingIndicator();
295
-
296
- // Get bot response
297
- const response = await fetchBotResponse(message);
298
-
299
- // Remove typing indicator and add bot message
300
- removeTypingIndicator();
301
- addMessage(response, 'bot');
302
-
303
- // Update conversation history
304
- currentConversation.messages.push({ type: 'bot', content: response });
305
- saveConversations();
306
- } catch (error) {
307
- console.error('Error:', error);
308
- removeTypingIndicator();
309
- showError(error.message || 'Failed to get response. Please try again.');
310
- }
311
- }
312
-
313
- // Add message to chat
314
- function addMessage(content, type) {
315
- // Create message element
316
- const messageDiv = document.createElement('div');
317
- messageDiv.className = `message ${type}-message`;
318
-
319
- // Get current time
320
- const now = new Date();
321
- const hours = now.getHours().toString().padStart(2, '0');
322
- const minutes = now.getMinutes().toString().padStart(2, '0');
323
- const timeString = `${hours}:${minutes}`;
324
-
325
- if (type === 'user') {
326
- messageDiv.innerHTML = `
327
- <div class="question-bubble">${content}</div>
328
- <div class="message-time">${timeString}</div>
329
- `;
330
- } else {
331
- messageDiv.innerHTML = `
332
- <div class="answer-container">
333
- <div class="bot-icon">
334
- <img src="images/bot-message-logo.png" alt="Muhafiz AI">
335
- </div>
336
- <div class="answer-content">${formatBotResponse(content)}</div>
337
- </div>
338
- <div class="message-time">${timeString}</div>
339
- `;
340
- }
341
-
342
- // Remove welcome message if present
343
- const welcomeMessage = document.querySelector('.welcome-message');
344
- if (welcomeMessage) {
345
- welcomeMessage.remove();
346
- }
347
-
348
- chatMessages.appendChild(messageDiv);
349
- chatMessages.scrollTop = chatMessages.scrollHeight;
350
-
351
- // Add message to current conversation
352
- if (conversationHistory.length === 0) {
353
- handleNewChat();
354
- }
355
- conversationHistory[0].messages.push({ type, content });
356
- saveConversations();
357
- }
358
-
359
- // Format bot response with proper styling
360
- function formatBotResponse(content) {
361
- // Split content into sections
362
- const sections = content.split('\n\n');
363
-
364
- let formattedContent = '';
365
- let inList = false;
366
-
367
- sections.forEach(section => {
368
- // Handle main headings (e.g., "Types of AI:")
369
- if (section.match(/^[A-Z][^:]+:$/)) {
370
- if (inList) {
371
- formattedContent += '</div>';
372
- inList = false;
373
- }
374
- formattedContent += `<h2 class="response-heading">${section}</h2>`;
375
- return;
376
- }
377
-
378
- // Handle definitions (text with parentheses)
379
- if (section.includes('(') && section.includes(')')) {
380
- section = section.replace(/([A-Za-z\s]+)\s*\((.*?)\)/, '<div class="definition"><span class="term">$1</span> <span class="definition-text">($2)</span></div>');
381
- }
382
-
383
- // Handle numbered lists
384
- if (section.match(/^\d+\./)) {
385
- if (!inList) {
386
- formattedContent += '<div class="numbered-list">';
387
- inList = true;
388
- }
389
- const [number, ...rest] = section.split('.');
390
- const content = rest.join('.').trim();
391
-
392
- // Check if the list item has a dash/hyphen definition
393
- if (content.includes('–')) {
394
- const [term, definition] = content.split('–').map(s => s.trim());
395
- formattedContent += `
396
- <div class="list-item">
397
- <span class="number">${number}.</span>
398
- <div class="list-content">
399
- <span class="list-term">${term}</span>
400
- <span class="list-definition">– ${definition}</span>
401
- </div>
402
- </div>`;
403
- } else {
404
- formattedContent += `
405
- <div class="list-item">
406
- <span class="number">${number}.</span>
407
- <div class="list-content">${content}</div>
408
- </div>`;
409
- }
410
- return;
411
- }
412
-
413
- // Close list if we're not processing a list item
414
- if (inList) {
415
- formattedContent += '</div>';
416
- inList = false;
417
- }
418
-
419
- // Regular paragraphs
420
- formattedContent += `<p class="response-paragraph">${section}</p>`;
421
- });
422
-
423
- // Close any open list
424
- if (inList) {
425
- formattedContent += '</div>';
426
- }
427
-
428
- return formattedContent;
429
- }
430
-
431
- // Show typing indicator
432
- function showTypingIndicator() {
433
- const indicator = document.createElement('div');
434
- indicator.classList.add('message', 'bot-message', 'typing-indicator');
435
- indicator.innerHTML = '<span></span><span></span><span></span>';
436
- chatMessages.appendChild(indicator);
437
- chatMessages.scrollTop = chatMessages.scrollHeight;
438
- }
439
-
440
- // Remove typing indicator
441
- function removeTypingIndicator() {
442
- const indicator = document.querySelector('.typing-indicator');
443
- if (indicator) {
444
- indicator.remove();
445
- }
446
- }
447
-
448
- // Show error message
449
- function showError(message) {
450
- const errorDiv = document.createElement('div');
451
- errorDiv.classList.add('message', 'error-message');
452
- errorDiv.textContent = message;
453
- chatMessages.appendChild(errorDiv);
454
- chatMessages.scrollTop = chatMessages.scrollHeight;
455
-
456
- // Remove error after 5 seconds
457
- setTimeout(() => {
458
- errorDiv.remove();
459
- }, 5000);
460
- }
461
-
462
- // Fetch bot response from OpenRouter API
463
- async function fetchBotResponse(userMessage) {
464
- try {
465
- // Check for API key
466
- const apiKey = chatConfig.apiKey || localStorage.getItem('openRouterApiKey');
467
- if (!apiKey) {
468
- throw new Error('Please set up your API key in the admin panel');
469
- }
470
-
471
- // Format messages for the API
472
- const messages = [
473
- {
474
- role: 'system',
475
- content: `You are Muhafiz AI, a digital safety assistant by the Mohsin Kamil Foundation. Your role is strictly limited to:
476
- 1. Combating cyber threats
477
- 2. Providing emotional support for cybercrime victims
478
- 3. Guiding users through digital safety solutions
479
- 4. Handling cybersecurity incidents
480
- 5. Addressing online harassment and blackmail
481
- 6. Assisting with content removal and legal reporting
482
-
483
- IMPORTANT: You must NEVER respond to questions outside these areas. For any off-topic questions (e.g., general AI, weather, news, etc.), respond with:
484
- "I am Muhafiz AI, focused exclusively on digital safety and cybersecurity. I cannot assist with questions about [topic]. However, I'm here to help if you need support with:
485
- - Cybersecurity incidents
486
- - Online harassment or blackmail
487
- - Account hacking
488
- - Content removal
489
- - Digital safety guidance
490
- - Reporting cybercrimes
491
-
492
- Would you like assistance with any of these issues?"
493
-
494
- Response Framework:
495
- 1. Emotional Check-In:
496
- - Start with empathy: "This sounds incredibly stressful. How are you feeling right now?"
497
- - Reassure: "We'll tackle this together—you're not alone."
498
-
499
- 2. URL Safety Analysis:
500
- - If a user shares a suspicious link:
501
- - "Let's check this link safely. First check the Link looking formating feel Safe or Not. Avoid clicking it. Use tools like Google Safe Browsing or VirusTotal to scan it."
502
- - "If you've already clicked it, change your passwords immediately and enable 2FA."
503
-
504
- 3. Tech Support for Hacking:
505
- - Account Security:
506
- - "Let's secure your account: 1) Change passwords, 2) Enable 2FA, 3) Check login activity."
507
- - Phishing Guidance:
508
- - "Never share OTPs or passwords. Legitimate organizations won't ask for these."
509
- - Breach Response:
510
- - "Check if your data was leaked at Have I Been Pwned?"
511
-
512
- 4. Legal & Content Removal:
513
- - "Under Pakistan's PECA 2016, blackmail is punishable. Report to FIA Cyber Crime or call 1991."
514
- - "To remove content from platforms:
515
- - Google: Content Removal Request
516
- - Meta (FB/IG): Reporting Tool"
517
-
518
- 5. Founder's Role:
519
- - "Abdullah Tarar, our founder, is a Certified Ethical Hacker. He's helped many in your situation—no judgment, only solutions. Chat directly: WhatsApp or call 0342-4844392."
520
-
521
- Always end responses with:
522
- - "What else can I do to help you feel safer?"
523
- - "You're in control. Let's take the next step."`
524
- }
525
- ];
526
-
527
- // Add conversation history if available
528
- if (conversationHistory.length > 0) {
529
- const currentConversation = conversationHistory[0];
530
- if (currentConversation.messages) {
531
- // Get all messages from the current conversation for context
532
- currentConversation.messages.forEach(msg => {
533
- messages.push({
534
- role: msg.type === 'user' ? 'user' : 'assistant',
535
- content: msg.content
536
- });
537
- });
538
- }
539
- }
540
-
541
- // Add the new message
542
- messages.push({
543
- role: 'user',
544
- content: userMessage
545
- });
546
-
547
- console.log('Sending request to OpenRouter with context:', {
548
- model: chatConfig.model,
549
- messageCount: messages.length,
550
- apiKeySet: !!apiKey
551
- });
552
-
553
- const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
554
- method: 'POST',
555
- headers: {
556
- 'Authorization': `Bearer ${apiKey}`,
557
- 'HTTP-Referer': chatConfig.siteUrl,
558
- 'X-Title': chatConfig.siteName,
559
- 'Content-Type': 'application/json'
560
- },
561
- body: JSON.stringify({
562
- model: chatConfig.model || DEFAULT_MODEL,
563
- messages: messages,
564
- temperature: 0.7,
565
- max_tokens: 2000
566
- })
567
- });
568
-
569
- const data = await response.json();
570
-
571
- if (!response.ok) {
572
- console.error('OpenRouter API Error:', data);
573
- throw new Error(data.error?.message || `HTTP error! status: ${response.status}`);
574
- }
575
-
576
- console.log('OpenRouter API Response:', data);
577
-
578
- if (!data.choices?.[0]?.message?.content) {
579
- throw new Error('Invalid response format from API');
580
- }
581
-
582
- return data.choices[0].message.content;
583
-
584
- } catch (error) {
585
- console.error('API Error:', error);
586
- throw new Error(`Failed to get response: ${error.message}`);
587
- }
588
- }
589
-
590
- // Handle more options
591
- function handleMore() {
592
- // Implement more options menu
593
- console.log('More options clicked');
594
- }
595
-
596
- // Handle menu open
597
- function handleMenuOpen() {
598
- menuOverlay.classList.add('active');
599
- document.body.style.overflow = 'hidden';
600
- }
601
-
602
- // Handle menu close
603
- function handleMenuClose() {
604
- menuOverlay.classList.remove('active');
605
- document.body.style.overflow = '';
606
- }
607
-
608
- // Update time in status bar
609
- function updateTime() {
610
- const timeElement = document.querySelector('.time');
611
- const now = new Date();
612
- const hours = now.getHours().toString().padStart(2, '0');
613
- const minutes = now.getMinutes().toString().padStart(2, '0');
614
- timeElement.textContent = `${hours}:${minutes}`;
615
- }
616
-
617
- // Load chat configuration
618
- function loadChatConfig() {
619
- // Try to load API key from localStorage if no default is set
620
- if (!DEFAULT_API_KEY) {
621
- const storedApiKey = localStorage.getItem('openRouterApiKey');
622
- if (storedApiKey) {
623
- chatConfig.apiKey = storedApiKey;
624
- }
625
- }
626
-
627
- // Load model from localStorage or use default
628
- chatConfig.model = localStorage.getItem('selectedModel') || DEFAULT_MODEL;
629
-
630
- // Log configuration (without API key)
631
- console.log('Chat Configuration:', {
632
- ...chatConfig,
633
- apiKey: chatConfig.apiKey ? '[SET]' : '[NOT SET]',
634
- model: chatConfig.model
635
- });
636
- }
637
-
638
- // Load theme from localStorage
639
- function loadTheme() {
640
- const savedTheme = localStorage.getItem('theme') || 'light';
641
- document.documentElement.setAttribute('data-theme', savedTheme);
642
- updateThemeIcon(savedTheme);
643
- }
644
-
645
- // Handle theme toggle
646
- function handleThemeToggle() {
647
- const currentTheme = document.documentElement.getAttribute('data-theme');
648
- const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
649
-
650
- document.documentElement.setAttribute('data-theme', newTheme);
651
- localStorage.setItem('theme', newTheme);
652
- updateThemeIcon(newTheme);
653
- }
654
-
655
- // Update theme toggle icon
656
- function updateThemeIcon(theme) {
657
- const icon = themeToggleBtn.querySelector('i');
658
- if (theme === 'dark') {
659
- icon.className = 'fas fa-sun';
660
- themeToggleBtn.title = 'Switch to light mode';
661
- } else {
662
- icon.className = 'fas fa-moon';
663
- themeToggleBtn.title = 'Switch to dark mode';
664
- }
665
- }
666
-
667
- // Initialize chat when DOM is loaded
668
  document.addEventListener('DOMContentLoaded', initChat);
 
1
+ // Chat Configuration
2
+ const DEFAULT_API_KEY = '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';
3
+ const DEFAULT_MODEL = 'deepseek/deepseek-chat-v3-0324:free';
4
+
5
+ let chatConfig = {
6
+ apiKey: DEFAULT_API_KEY,
7
+ model: DEFAULT_MODEL,
8
+ siteUrl: window.location.origin,
9
+ siteName: 'Muhafiz AI Chat'
10
+ };
11
+
12
+ // Conversation history
13
+ let conversationHistory = [];
14
+
15
+ // DOM Elements
16
+ const messageInput = document.getElementById('message-input');
17
+ const sendButton = document.getElementById('send-btn');
18
+ const chatMessages = document.getElementById('chat-messages');
19
+ const moreButton = document.querySelector('.more-btn');
20
+ const newChatButton = document.getElementById('new-chat-btn');
21
+ const menuButton = document.querySelector('.menu-btn');
22
+ const menuOverlay = document.getElementById('menuOverlay');
23
+ const closeMenuButton = document.getElementById('closeMenuBtn');
24
+ const conversationList = document.getElementById('conversationList');
25
+ const seeMoreBtn = document.getElementById('seeMoreBtn');
26
+ const themeToggleBtn = document.getElementById('themeToggleBtn');
27
+
28
+ // Initialize chat
29
+ function initChat() {
30
+ // Load saved configuration and conversations
31
+ loadChatConfig();
32
+ loadConversations();
33
+ loadTheme();
34
+
35
+ // Add event listeners
36
+ messageInput.addEventListener('input', handleInput);
37
+ messageInput.addEventListener('keypress', handleKeyPress);
38
+ sendButton.addEventListener('click', handleSend);
39
+ moreButton.addEventListener('click', handleMore);
40
+ newChatButton.addEventListener('click', handleNewChat);
41
+ menuButton.addEventListener('click', handleMenuOpen);
42
+ closeMenuButton.addEventListener('click', handleMenuClose);
43
+ seeMoreBtn.addEventListener('click', handleSeeMore);
44
+ themeToggleBtn.addEventListener('click', handleThemeToggle);
45
+ menuOverlay.addEventListener('click', (e) => {
46
+ if (e.target === menuOverlay) {
47
+ handleMenuClose();
48
+ }
49
+ });
50
+
51
+ // Disable send button initially
52
+ sendButton.disabled = true;
53
+ sendButton.style.opacity = '0.5';
54
+
55
+ // Update status bar time
56
+ updateTime();
57
+ setInterval(updateTime, 60000);
58
+ }
59
+
60
+ // Load conversations from localStorage
61
+ function loadConversations() {
62
+ const savedConversations = localStorage.getItem('conversations');
63
+ if (savedConversations) {
64
+ conversationHistory = JSON.parse(savedConversations);
65
+
66
+ // If there are saved conversations, update the chat interface
67
+ if (conversationHistory.length > 0) {
68
+ const currentConversation = conversationHistory[0];
69
+ if (currentConversation.messages.length > 0) {
70
+ // Clear welcome message
71
+ while (chatMessages.firstChild) {
72
+ chatMessages.removeChild(chatMessages.firstChild);
73
+ }
74
+
75
+ // Load messages from the current conversation
76
+ currentConversation.messages.forEach(msg => {
77
+ addMessage(msg.content, msg.type);
78
+ });
79
+ }
80
+ }
81
+ }
82
+ updateConversationList();
83
+ }
84
+
85
+ // Save conversations to localStorage
86
+ function saveConversations() {
87
+ localStorage.setItem('conversations', JSON.stringify(conversationHistory));
88
+ }
89
+
90
+ // Update conversation list in menu
91
+ function updateConversationList() {
92
+ conversationList.innerHTML = '';
93
+ const recentConversations = conversationHistory.slice(0, 4);
94
+
95
+ if (recentConversations.length === 0) {
96
+ const emptyState = document.createElement('div');
97
+ emptyState.className = 'empty-conversations';
98
+ emptyState.textContent = 'No conversations yet';
99
+ conversationList.appendChild(emptyState);
100
+ return;
101
+ }
102
+
103
+ recentConversations.forEach(conv => {
104
+ const item = document.createElement('div');
105
+ item.className = 'conversation-item';
106
+ item.innerHTML = `
107
+ <i class="fas fa-comment"></i>
108
+ <span>${conv.title}</span>
109
+ <span class="message-count">${conv.messages.length} messages</span>
110
+ `;
111
+ item.addEventListener('click', () => loadConversation(conv.id));
112
+ conversationList.appendChild(item);
113
+ });
114
+ }
115
+
116
+ // Handle new chat
117
+ async function handleNewChat() {
118
+ // If there's an existing conversation, generate a title for it
119
+ if (conversationHistory.length > 0 && conversationHistory[0].messages.length > 0) {
120
+ const currentConversation = conversationHistory[0];
121
+ try {
122
+ const messages = currentConversation.messages.map(msg => `${msg.type}: ${msg.content}`).join('\n');
123
+ const titlePrompt = `Please summarize this conversation in 4-5 words:\n${messages}`;
124
+
125
+ // Show loading state for the conversation title
126
+ const tempTitle = 'Generating title...';
127
+ currentConversation.title = tempTitle;
128
+ updateConversationList();
129
+
130
+ // Get AI-generated title
131
+ const response = await fetchBotResponse(titlePrompt);
132
+ currentConversation.title = response.slice(0, 40).trim();
133
+ saveConversations();
134
+ updateConversationList();
135
+ } catch (error) {
136
+ console.error('Error generating title:', error);
137
+ // Fallback to first message if title generation fails
138
+ currentConversation.title = currentConversation.messages[0].content.slice(0, 30) + '...';
139
+ saveConversations();
140
+ updateConversationList();
141
+ }
142
+ }
143
+
144
+ // Create new conversation
145
+ const newConversation = {
146
+ id: Date.now(),
147
+ title: 'New Chat',
148
+ messages: []
149
+ };
150
+
151
+ // Add to conversations array
152
+ conversationHistory.unshift(newConversation);
153
+ saveConversations();
154
+ updateConversationList();
155
+
156
+ // Clear chat messages
157
+ while (chatMessages.firstChild) {
158
+ chatMessages.removeChild(chatMessages.firstChild);
159
+ }
160
+
161
+ // Add welcome message
162
+ const welcomeMessage = document.createElement('div');
163
+ welcomeMessage.className = 'welcome-message';
164
+ welcomeMessage.innerHTML = `
165
+ <img src="images/welcome-logo.png" alt="Muhafiz AI Logo" class="welcome-logo">
166
+ <h2>Hi, I'm Muhafiz AI.</h2>
167
+ <p>Your digital safety assistant dedicated to combating cyber threats and providing support.</p>
168
+ <div class="context-info">Ask me about cyber security, online protection, or digital safety concerns.</div>
169
+ `;
170
+ chatMessages.appendChild(welcomeMessage);
171
+
172
+ // Clear input
173
+ messageInput.value = '';
174
+ handleInput({ target: messageInput });
175
+
176
+ // Focus input
177
+ messageInput.focus();
178
+ }
179
+
180
+ // Handle see more button click
181
+ function handleSeeMore() {
182
+ // Create full conversation list overlay
183
+ const overlay = document.createElement('div');
184
+ overlay.className = 'conversation-overlay';
185
+
186
+ const content = document.createElement('div');
187
+ content.className = 'conversation-overlay-content';
188
+
189
+ // Add header
190
+ content.innerHTML = `
191
+ <div class="overlay-header">
192
+ <h2>All Conversations</h2>
193
+ <button class="close-overlay-btn"><i class="fas fa-times"></i></button>
194
+ </div>
195
+ <div class="conversation-list-full"></div>
196
+ `;
197
+
198
+ // Add all conversations
199
+ const listContainer = content.querySelector('.conversation-list-full');
200
+ conversationHistory.forEach(conv => {
201
+ const item = document.createElement('div');
202
+ item.className = 'conversation-item';
203
+ item.innerHTML = `
204
+ <i class="fas fa-comment"></i>
205
+ <div class="conversation-details">
206
+ <span class="conversation-title">${conv.title}</span>
207
+ <span class="message-count">${conv.messages.length} messages</span>
208
+ <span class="conversation-date">${new Date(conv.id).toLocaleDateString()}</span>
209
+ </div>
210
+ `;
211
+ item.addEventListener('click', () => {
212
+ loadConversation(conv.id);
213
+ overlay.remove();
214
+ });
215
+ listContainer.appendChild(item);
216
+ });
217
+
218
+ // Add close button handler
219
+ overlay.appendChild(content);
220
+ content.querySelector('.close-overlay-btn').addEventListener('click', () => {
221
+ overlay.remove();
222
+ });
223
+
224
+ // Add overlay click handler
225
+ overlay.addEventListener('click', (e) => {
226
+ if (e.target === overlay) {
227
+ overlay.remove();
228
+ }
229
+ });
230
+
231
+ document.body.appendChild(overlay);
232
+ }
233
+
234
+ // Load specific conversation
235
+ function loadConversation(id) {
236
+ const conversation = conversationHistory.find(conv => conv.id === id);
237
+ if (conversation) {
238
+ // Clear current chat
239
+ while (chatMessages.firstChild) {
240
+ chatMessages.removeChild(chatMessages.firstChild);
241
+ }
242
+
243
+ // Load conversation messages
244
+ conversation.messages.forEach(msg => {
245
+ addMessage(msg.content, msg.type);
246
+ });
247
+
248
+ // Close menu
249
+ handleMenuClose();
250
+ }
251
+ }
252
+
253
+ // Handle input changes
254
+ function handleInput(e) {
255
+ const isEmpty = !e.target.value.trim();
256
+ sendButton.style.opacity = isEmpty ? '0.5' : '1';
257
+ sendButton.disabled = isEmpty;
258
+ }
259
+
260
+ // Handle enter key
261
+ function handleKeyPress(e) {
262
+ if (e.key === 'Enter' && !e.shiftKey) {
263
+ e.preventDefault();
264
+ handleSend();
265
+ }
266
+ }
267
+
268
+ // Handle send button click
269
+ async function handleSend() {
270
+ const message = messageInput.value.trim();
271
+ if (!message) return;
272
+
273
+ // Add user message to chat and conversation history
274
+ addMessage(message, 'user');
275
+ if (conversationHistory.length === 0) {
276
+ handleNewChat();
277
+ }
278
+
279
+ // Update current conversation
280
+ const currentConversation = conversationHistory[0];
281
+ currentConversation.messages.push({ type: 'user', content: message });
282
+ if (currentConversation.messages.length === 1) {
283
+ // Update conversation title with first message
284
+ currentConversation.title = message.slice(0, 30) + (message.length > 30 ? '...' : '');
285
+ updateConversationList();
286
+ }
287
+ saveConversations();
288
+
289
+ messageInput.value = '';
290
+ handleInput({ target: messageInput });
291
+
292
+ try {
293
+ // Show typing indicator
294
+ showTypingIndicator();
295
+
296
+ // Get bot response
297
+ const response = await fetchBotResponse(message);
298
+
299
+ // Remove typing indicator and add bot message
300
+ removeTypingIndicator();
301
+ addMessage(response, 'bot');
302
+
303
+ // Update conversation history
304
+ currentConversation.messages.push({ type: 'bot', content: response });
305
+ saveConversations();
306
+ } catch (error) {
307
+ console.error('Error:', error);
308
+ removeTypingIndicator();
309
+ showError(error.message || 'Failed to get response. Please try again.');
310
+ }
311
+ }
312
+
313
+ // Add message to chat
314
+ function addMessage(content, type) {
315
+ // Create message element
316
+ const messageDiv = document.createElement('div');
317
+ messageDiv.className = `message ${type}-message`;
318
+
319
+ // Get current time
320
+ const now = new Date();
321
+ const hours = now.getHours().toString().padStart(2, '0');
322
+ const minutes = now.getMinutes().toString().padStart(2, '0');
323
+ const timeString = `${hours}:${minutes}`;
324
+
325
+ if (type === 'user') {
326
+ messageDiv.innerHTML = `
327
+ <div class="question-bubble">${content}</div>
328
+ <div class="message-time">${timeString}</div>
329
+ `;
330
+ } else {
331
+ messageDiv.innerHTML = `
332
+ <div class="answer-container">
333
+ <div class="bot-icon">
334
+ <img src="images/bot-message-logo.png" alt="Muhafiz AI">
335
+ </div>
336
+ <div class="answer-content">${formatBotResponse(content)}</div>
337
+ </div>
338
+ <div class="message-time">${timeString}</div>
339
+ `;
340
+ }
341
+
342
+ // Remove welcome message if present
343
+ const welcomeMessage = document.querySelector('.welcome-message');
344
+ if (welcomeMessage) {
345
+ welcomeMessage.remove();
346
+ }
347
+
348
+ chatMessages.appendChild(messageDiv);
349
+ chatMessages.scrollTop = chatMessages.scrollHeight;
350
+
351
+ // Add message to current conversation
352
+ if (conversationHistory.length === 0) {
353
+ handleNewChat();
354
+ }
355
+ conversationHistory[0].messages.push({ type, content });
356
+ saveConversations();
357
+ }
358
+
359
+ // Format bot response with proper styling
360
+ function formatBotResponse(content) {
361
+ // Split content into sections
362
+ const sections = content.split('\n\n');
363
+
364
+ let formattedContent = '';
365
+ let inList = false;
366
+
367
+ sections.forEach(section => {
368
+ // Handle main headings (e.g., "Types of AI:")
369
+ if (section.match(/^[A-Z][^:]+:$/)) {
370
+ if (inList) {
371
+ formattedContent += '</div>';
372
+ inList = false;
373
+ }
374
+ formattedContent += `<h2 class="response-heading">${section}</h2>`;
375
+ return;
376
+ }
377
+
378
+ // Handle definitions (text with parentheses)
379
+ if (section.includes('(') && section.includes(')')) {
380
+ section = section.replace(/([A-Za-z\s]+)\s*\((.*?)\)/, '<div class="definition"><span class="term">$1</span> <span class="definition-text">($2)</span></div>');
381
+ }
382
+
383
+ // Handle numbered lists
384
+ if (section.match(/^\d+\./)) {
385
+ if (!inList) {
386
+ formattedContent += '<div class="numbered-list">';
387
+ inList = true;
388
+ }
389
+ const [number, ...rest] = section.split('.');
390
+ const content = rest.join('.').trim();
391
+
392
+ // Check if the list item has a dash/hyphen definition
393
+ if (content.includes('–')) {
394
+ const [term, definition] = content.split('–').map(s => s.trim());
395
+ formattedContent += `
396
+ <div class="list-item">
397
+ <span class="number">${number}.</span>
398
+ <div class="list-content">
399
+ <span class="list-term">${term}</span>
400
+ <span class="list-definition">– ${definition}</span>
401
+ </div>
402
+ </div>`;
403
+ } else {
404
+ formattedContent += `
405
+ <div class="list-item">
406
+ <span class="number">${number}.</span>
407
+ <div class="list-content">${content}</div>
408
+ </div>`;
409
+ }
410
+ return;
411
+ }
412
+
413
+ // Close list if we're not processing a list item
414
+ if (inList) {
415
+ formattedContent += '</div>';
416
+ inList = false;
417
+ }
418
+
419
+ // Regular paragraphs
420
+ formattedContent += `<p class="response-paragraph">${section}</p>`;
421
+ });
422
+
423
+ // Close any open list
424
+ if (inList) {
425
+ formattedContent += '</div>';
426
+ }
427
+
428
+ return formattedContent;
429
+ }
430
+
431
+ // Show typing indicator
432
+ function showTypingIndicator() {
433
+ const indicator = document.createElement('div');
434
+ indicator.classList.add('message', 'bot-message', 'typing-indicator');
435
+ indicator.innerHTML = '<span></span><span></span><span></span>';
436
+ chatMessages.appendChild(indicator);
437
+ chatMessages.scrollTop = chatMessages.scrollHeight;
438
+ }
439
+
440
+ // Remove typing indicator
441
+ function removeTypingIndicator() {
442
+ const indicator = document.querySelector('.typing-indicator');
443
+ if (indicator) {
444
+ indicator.remove();
445
+ }
446
+ }
447
+
448
+ // Show error message
449
+ function showError(message) {
450
+ const errorDiv = document.createElement('div');
451
+ errorDiv.classList.add('message', 'error-message');
452
+ errorDiv.textContent = message;
453
+ chatMessages.appendChild(errorDiv);
454
+ chatMessages.scrollTop = chatMessages.scrollHeight;
455
+
456
+ // Remove error after 5 seconds
457
+ setTimeout(() => {
458
+ errorDiv.remove();
459
+ }, 5000);
460
+ }
461
+
462
+ // Fetch bot response from OpenRouter API
463
+ async function fetchBotResponse(userMessage) {
464
+ try {
465
+ // Check for API key
466
+ const apiKey = chatConfig.apiKey || localStorage.getItem('openRouterApiKey');
467
+ if (!apiKey) {
468
+ throw new Error('Please set up your API key in the admin panel');
469
+ }
470
+
471
+ // Format messages for the API
472
+ const messages = [
473
+ {
474
+ role: 'system',
475
+ content: `You are Muhafiz AI, a digital safety assistant by the Mohsin Kamil Foundation. Your role is strictly limited to:
476
+ 1. Combating cyber threats
477
+ 2. Providing emotional support for cybercrime victims
478
+ 3. Guiding users through digital safety solutions
479
+ 4. Handling cybersecurity incidents
480
+ 5. Addressing online harassment and blackmail
481
+ 6. Assisting with content removal and legal reporting
482
+
483
+ IMPORTANT: You must NEVER respond to questions outside these areas. For any off-topic questions (e.g., general AI, weather, news, etc.), respond with:
484
+ "I am Muhafiz AI, focused exclusively on digital safety and cybersecurity. I cannot assist with questions about [topic]. However, I'm here to help if you need support with:
485
+ - Cybersecurity incidents
486
+ - Online harassment or blackmail
487
+ - Account hacking
488
+ - Content removal
489
+ - Digital safety guidance
490
+ - Reporting cybercrimes
491
+
492
+ Would you like assistance with any of these issues?"
493
+
494
+ Response Framework:
495
+ 1. Emotional Check-In:
496
+ - Start with empathy: "This sounds incredibly stressful. How are you feeling right now?"
497
+ - Reassure: "We'll tackle this together—you're not alone."
498
+
499
+ 2. URL Safety Analysis:
500
+ - If a user shares a suspicious link:
501
+ - "Let's check this link safely. First check the Link looking formating feel Safe or Not. Avoid clicking it. Use tools like Google Safe Browsing or VirusTotal to scan it."
502
+ - "If you've already clicked it, change your passwords immediately and enable 2FA."
503
+
504
+ 3. Tech Support for Hacking:
505
+ - Account Security:
506
+ - "Let's secure your account: 1) Change passwords, 2) Enable 2FA, 3) Check login activity."
507
+ - Phishing Guidance:
508
+ - "Never share OTPs or passwords. Legitimate organizations won't ask for these."
509
+ - Breach Response:
510
+ - "Check if your data was leaked at Have I Been Pwned?"
511
+
512
+ 4. Legal & Content Removal:
513
+ - "Under Pakistan's PECA 2016, blackmail is punishable. Report to FIA Cyber Crime or call 1991."
514
+ - "To remove content from platforms:
515
+ - Google: Content Removal Request
516
+ - Meta (FB/IG): Reporting Tool"
517
+
518
+ 5. Founder's Role:
519
+ - "Abdullah Tarar, our founder, is a Certified Ethical Hacker. He's helped many in your situation—no judgment, only solutions. Chat directly: WhatsApp or call 0342-4844392."
520
+
521
+ Always end responses with:
522
+ - "What else can I do to help you feel safer?"
523
+ - "You're in control. Let's take the next step."`
524
+ }
525
+ ];
526
+
527
+ // Add conversation history if available
528
+ if (conversationHistory.length > 0) {
529
+ const currentConversation = conversationHistory[0];
530
+ if (currentConversation.messages) {
531
+ // Get all messages from the current conversation for context
532
+ currentConversation.messages.forEach(msg => {
533
+ messages.push({
534
+ role: msg.type === 'user' ? 'user' : 'assistant',
535
+ content: msg.content
536
+ });
537
+ });
538
+ }
539
+ }
540
+
541
+ // Add the new message
542
+ messages.push({
543
+ role: 'user',
544
+ content: userMessage
545
+ });
546
+
547
+ console.log('Sending request to OpenRouter with context:', {
548
+ model: chatConfig.model,
549
+ messageCount: messages.length,
550
+ apiKeySet: !!apiKey
551
+ });
552
+
553
+ const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
554
+ method: 'POST',
555
+ headers: {
556
+ 'Authorization': `Bearer ${apiKey}`,
557
+ 'HTTP-Referer': chatConfig.siteUrl,
558
+ 'X-Title': chatConfig.siteName,
559
+ 'Content-Type': 'application/json'
560
+ },
561
+ body: JSON.stringify({
562
+ model: chatConfig.model || DEFAULT_MODEL,
563
+ messages: messages,
564
+ temperature: 0.7,
565
+ max_tokens: 2000
566
+ })
567
+ });
568
+
569
+ const data = await response.json();
570
+
571
+ if (!response.ok) {
572
+ console.error('OpenRouter API Error:', data);
573
+ throw new Error(data.error?.message || `HTTP error! status: ${response.status}`);
574
+ }
575
+
576
+ console.log('OpenRouter API Response:', data);
577
+
578
+ if (!data.choices?.[0]?.message?.content) {
579
+ throw new Error('Invalid response format from API');
580
+ }
581
+
582
+ return data.choices[0].message.content;
583
+
584
+ } catch (error) {
585
+ console.error('API Error:', error);
586
+ throw new Error(`Failed to get response: ${error.message}`);
587
+ }
588
+ }
589
+
590
+ // Handle more options
591
+ function handleMore() {
592
+ // Implement more options menu
593
+ console.log('More options clicked');
594
+ }
595
+
596
+ // Handle menu open
597
+ function handleMenuOpen() {
598
+ menuOverlay.classList.add('active');
599
+ document.body.style.overflow = 'hidden';
600
+ }
601
+
602
+ // Handle menu close
603
+ function handleMenuClose() {
604
+ menuOverlay.classList.remove('active');
605
+ document.body.style.overflow = '';
606
+ }
607
+
608
+ // Update time in status bar
609
+ function updateTime() {
610
+ const timeElement = document.querySelector('.time');
611
+ const now = new Date();
612
+ const hours = now.getHours().toString().padStart(2, '0');
613
+ const minutes = now.getMinutes().toString().padStart(2, '0');
614
+ timeElement.textContent = `${hours}:${minutes}`;
615
+ }
616
+
617
+ // Load chat configuration
618
+ function loadChatConfig() {
619
+ // Try to load API key from localStorage if no default is set
620
+ if (!DEFAULT_API_KEY) {
621
+ const storedApiKey = localStorage.getItem('openRouterApiKey');
622
+ if (storedApiKey) {
623
+ chatConfig.apiKey = storedApiKey;
624
+ }
625
+ }
626
+
627
+ // Load model from localStorage or use default
628
+ chatConfig.model = localStorage.getItem('selectedModel') || DEFAULT_MODEL;
629
+
630
+ // Log configuration (without API key)
631
+ console.log('Chat Configuration:', {
632
+ ...chatConfig,
633
+ apiKey: chatConfig.apiKey ? '[SET]' : '[NOT SET]',
634
+ model: chatConfig.model
635
+ });
636
+ }
637
+
638
+ // Load theme from localStorage
639
+ function loadTheme() {
640
+ const savedTheme = localStorage.getItem('theme') || 'light';
641
+ document.documentElement.setAttribute('data-theme', savedTheme);
642
+ updateThemeIcon(savedTheme);
643
+ }
644
+
645
+ // Handle theme toggle
646
+ function handleThemeToggle() {
647
+ const currentTheme = document.documentElement.getAttribute('data-theme');
648
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
649
+
650
+ document.documentElement.setAttribute('data-theme', newTheme);
651
+ localStorage.setItem('theme', newTheme);
652
+ updateThemeIcon(newTheme);
653
+ }
654
+
655
+ // Update theme toggle icon
656
+ function updateThemeIcon(theme) {
657
+ const icon = themeToggleBtn.querySelector('i');
658
+ if (theme === 'dark') {
659
+ icon.className = 'fas fa-sun';
660
+ themeToggleBtn.title = 'Switch to light mode';
661
+ } else {
662
+ icon.className = 'fas fa-moon';
663
+ themeToggleBtn.title = 'Switch to dark mode';
664
+ }
665
+ }
666
+
667
+ // Initialize chat when DOM is loaded
668
  document.addEventListener('DOMContentLoaded', initChat);