BikoRiko commited on
Commit
191bbfd
Β·
verified Β·
1 Parent(s): 06abf02

Upload index.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.js +150 -76
index.js CHANGED
@@ -1,13 +1,14 @@
1
  /**
2
- * DuckDuckGo AI Chatbot
3
  * Uses DuckDuckGo Instant Answer API for real-time responses
4
  */
5
 
6
  // Configuration
7
  const CONFIG = {
8
  DDG_API_BASE: 'https://api.duckduckgo.com/',
9
- MAX_TOPICS: 6,
10
- DEBOUNCE_MS: 300
 
11
  };
12
 
13
  // DOM Elements
@@ -77,64 +78,63 @@ function addMessage(content, isUser = false) {
77
  return messageDiv;
78
  }
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  /**
81
  * Format DuckDuckGo response for display
82
  */
83
  function formatDuckDuckGoResponse(data) {
84
  const container = document.createElement('div');
85
-
86
- // Check for Answer
87
- if (data.Answer) {
88
- const answerSection = document.createElement('div');
89
- answerSection.className = 'answer-section answer';
90
- answerSection.innerHTML = `
91
- <h4>πŸ’¬ Answer</h4>
92
- <p>${data.Answer}</p>
93
- `;
 
 
94
  container.appendChild(answerSection);
 
95
  }
96
 
97
  // Check for Definition
98
- if (data.Definition) {
99
- const defSection = document.createElement('div');
100
- defSection.className = 'answer-section definition';
101
- defSection.innerHTML = `
102
- <h4>πŸ“– Definition</h4>
103
- <p>${data.Definition}</p>
104
- `;
105
  container.appendChild(defSection);
106
-
107
- if (data.DefinitionSource) {
108
- const source = document.createElement('p');
109
- source.style.fontSize = '0.75rem';
110
- source.style.color = 'var(--text-muted)';
111
- source.style.marginTop = '0.5rem';
112
- source.textContent = `Source: ${data.DefinitionSource}`;
113
- defSection.querySelector('p').appendChild(source);
114
- }
115
  }
116
 
117
  // Check for Abstract (main topic info)
118
- if (data.Abstract) {
119
- const abstractSection = document.createElement('div');
120
- abstractSection.className = 'answer-section abstract';
121
- abstractSection.innerHTML = `
122
- <h4>πŸ“ ${data.AbstractTitle || 'Topic'}</h4>
123
- <p>${data.Abstract}</p>
124
- `;
125
  container.appendChild(abstractSection);
126
-
127
- if (data.AbstractSource) {
128
- const source = document.createElement('p');
129
- source.style.fontSize = '0.75rem';
130
- source.style.color = 'var(--text-muted)';
131
- source.style.marginTop = '0.5rem';
132
- source.textContent = `Source: ${data.AbstractSource}`;
133
- abstractSection.querySelector('p').appendChild(source);
134
- }
135
  }
136
 
137
- // Check for Related Topics
138
  if (data.RelatedTopics && data.RelatedTopics.length > 0) {
139
  const topicsSection = document.createElement('div');
140
  topicsSection.className = 'related-topics';
@@ -148,24 +148,39 @@ function formatDuckDuckGoResponse(data) {
148
 
149
  data.RelatedTopics.slice(0, CONFIG.MAX_TOPICS).forEach(topic => {
150
  if (topic.Text && topic.FirstURL) {
151
- const tag = document.createElement('span');
152
  tag.className = 'topic-tag';
153
- tag.textContent = topic.Text;
154
  tag.addEventListener('click', () => {
155
- elements.userInput.value = topic.Text;
156
  elements.chatForm.dispatchEvent(new Event('submit'));
157
  });
158
  tagsContainer.appendChild(tag);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  }
160
  });
161
 
162
  if (tagsContainer.children.length > 0) {
163
  topicsSection.appendChild(tagsContainer);
164
  container.appendChild(topicsSection);
 
165
  }
166
  }
167
 
168
- // Check for Results
169
  if (data.Results && data.Results.length > 0) {
170
  const resultsSection = document.createElement('div');
171
  resultsSection.className = 'related-topics';
@@ -179,9 +194,10 @@ function formatDuckDuckGoResponse(data) {
179
 
180
  data.Results.slice(0, CONFIG.MAX_TOPICS).forEach(result => {
181
  if (result.Text && result.FirstURL) {
182
- const tag = document.createElement('span');
183
  tag.className = 'topic-tag';
184
- tag.textContent = result.Text.replace(/<[^>]*>/g, '');
 
185
  tag.addEventListener('click', () => {
186
  window.open(result.FirstURL, '_blank');
187
  });
@@ -192,13 +208,31 @@ function formatDuckDuckGoResponse(data) {
192
  if (tagsContainer.children.length > 0) {
193
  resultsSection.appendChild(tagsContainer);
194
  container.appendChild(resultsSection);
 
195
  }
196
  }
197
 
198
- // If no data found
199
- if (container.children.length === 0) {
200
- const noResult = document.createElement('p');
201
- noResult.textContent = "I couldn't find a specific answer for that. Try asking differently or search for something more specific!";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  container.appendChild(noResult);
203
  }
204
 
@@ -213,24 +247,57 @@ async function fetchDuckDuckGoAnswer(query) {
213
  q: query,
214
  format: 'json',
215
  no_html: '1',
216
- skip_disambig: '1'
 
217
  });
218
 
219
  const url = `${CONFIG.DDG_API_BASE}?${params}`;
220
-
221
- try {
222
- const response = await fetch(url);
223
-
224
- if (!response.ok) {
225
- throw new Error(`HTTP error! status: ${response.status}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  }
227
-
228
- const data = await response.json();
229
- return data;
230
- } catch (error) {
231
- console.error('DuckDuckGo API Error:', error);
232
- throw error;
233
  }
 
 
234
  }
235
 
236
  /**
@@ -255,7 +322,7 @@ async function handleSubmit(event) {
255
  elements.chatMessages.appendChild(loadingMessage);
256
  elements.chatMessages.parentElement.scrollTop = elements.chatMessages.parentElement.scrollHeight;
257
 
258
- updateStatus('Searching DuckDuckGo...', 'searching');
259
 
260
  try {
261
  // Fetch from DuckDuckGo
@@ -268,16 +335,23 @@ async function handleSubmit(event) {
268
  const formattedResponse = formatDuckDuckGoResponse(data);
269
  addMessage(formattedResponse, false);
270
 
271
- updateStatus('Ready to chat', 'ready');
272
  } catch (error) {
273
  // Remove loading message
274
  loadingMessage.remove();
275
 
276
- // Show error message
277
- const errorMsg = addMessage('πŸ˜• Sorry, I encountered an error while searching. Please try again or check your internet connection.', false);
 
 
 
 
 
 
 
278
 
279
  updateStatus('Error occurred', 'error');
280
- console.error('Error:', error);
281
  } finally {
282
  // Reset loading state
283
  isLoading = false;
@@ -321,10 +395,10 @@ function init() {
321
  // Focus input
322
  elements.userInput.focus();
323
 
324
- updateStatus('Ready to chat', 'ready');
325
 
326
- console.log('πŸ¦† DuckDuckGo AI Chatbot initialized!');
327
- console.log('Powered by transformers.js and DuckDuckGo Instant Answer API');
328
  }
329
 
330
  // Start the app
 
1
  /**
2
+ * AI Web Search Assistant
3
  * Uses DuckDuckGo Instant Answer API for real-time responses
4
  */
5
 
6
  // Configuration
7
  const CONFIG = {
8
  DDG_API_BASE: 'https://api.duckduckgo.com/',
9
+ MAX_TOPICS: 8,
10
+ // Use a CORS proxy for better compatibility
11
+ CORS_PROXY: 'https://corsproxy.io/?'
12
  };
13
 
14
  // DOM Elements
 
78
  return messageDiv;
79
  }
80
 
81
+ /**
82
+ * Create an answer section
83
+ */
84
+ function createAnswerSection(title, content, className = '', source = null) {
85
+ const section = document.createElement('div');
86
+ section.className = `answer-section ${className}`;
87
+
88
+ let html = `<h4>${title}</h4><p>${content}</p>`;
89
+
90
+ if (source) {
91
+ html += `<p class="source-link">Source: ${source}</p>`;
92
+ }
93
+
94
+ section.innerHTML = html;
95
+ return section;
96
+ }
97
+
98
  /**
99
  * Format DuckDuckGo response for display
100
  */
101
  function formatDuckDuckGoResponse(data) {
102
  const container = document.createElement('div');
103
+ let hasContent = false;
104
+
105
+ // Debug: Log the raw response
106
+ console.log('DuckDuckGo Response:', data);
107
+
108
+ // Check for Instant Answer (most prominent)
109
+ if (data.Answer && data.Answer.trim()) {
110
+ const answerSection = createAnswerSection('πŸ’¬ Answer', data.Answer, 'answer');
111
+ if (data.AnswerType) {
112
+ answerSection.querySelector('h4').textContent += ` (${data.AnswerType})`;
113
+ }
114
  container.appendChild(answerSection);
115
+ hasContent = true;
116
  }
117
 
118
  // Check for Definition
119
+ if (data.Definition && data.Definition.trim()) {
120
+ const defSection = createAnswerSection('πŸ“– Definition', data.Definition, 'definition', data.DefinitionSource);
 
 
 
 
 
121
  container.appendChild(defSection);
122
+ hasContent = true;
 
 
 
 
 
 
 
 
123
  }
124
 
125
  // Check for Abstract (main topic info)
126
+ if (data.Abstract && data.Abstract.trim()) {
127
+ const abstractSection = createAnswerSection(
128
+ `πŸ“ ${data.AbstractTitle || 'Topic Overview'}`,
129
+ data.Abstract,
130
+ 'abstract',
131
+ data.AbstractSource
132
+ );
133
  container.appendChild(abstractSection);
134
+ hasContent = true;
 
 
 
 
 
 
 
 
135
  }
136
 
137
+ // Check for Related Topics (from main entity)
138
  if (data.RelatedTopics && data.RelatedTopics.length > 0) {
139
  const topicsSection = document.createElement('div');
140
  topicsSection.className = 'related-topics';
 
148
 
149
  data.RelatedTopics.slice(0, CONFIG.MAX_TOPICS).forEach(topic => {
150
  if (topic.Text && topic.FirstURL) {
151
+ const tag = document.createElement('button');
152
  tag.className = 'topic-tag';
153
+ tag.textContent = topic.Text.replace(/<[^>]*>/g, '');
154
  tag.addEventListener('click', () => {
155
+ elements.userInput.value = topic.Text.replace(/<[^>]*>/g, '');
156
  elements.chatForm.dispatchEvent(new Event('submit'));
157
  });
158
  tagsContainer.appendChild(tag);
159
+ } else if (topic.Name && topic.Topics) {
160
+ // Grouped topics (e.g., for famous people)
161
+ topic.Topics.slice(0, 3).forEach(subTopic => {
162
+ if (subTopic.Text && subTopic.FirstURL) {
163
+ const tag = document.createElement('button');
164
+ tag.className = 'topic-tag';
165
+ tag.textContent = subTopic.Text.replace(/<[^>]*>/g, '');
166
+ tag.addEventListener('click', () => {
167
+ elements.userInput.value = subTopic.Text.replace(/<[^>]*>/g, '');
168
+ elements.chatForm.dispatchEvent(new Event('submit'));
169
+ });
170
+ tagsContainer.appendChild(tag);
171
+ }
172
+ });
173
  }
174
  });
175
 
176
  if (tagsContainer.children.length > 0) {
177
  topicsSection.appendChild(tagsContainer);
178
  container.appendChild(topicsSection);
179
+ hasContent = true;
180
  }
181
  }
182
 
183
+ // Check for Results (additional web results)
184
  if (data.Results && data.Results.length > 0) {
185
  const resultsSection = document.createElement('div');
186
  resultsSection.className = 'related-topics';
 
194
 
195
  data.Results.slice(0, CONFIG.MAX_TOPICS).forEach(result => {
196
  if (result.Text && result.FirstURL) {
197
+ const tag = document.createElement('button');
198
  tag.className = 'topic-tag';
199
+ tag.textContent = result.Text.replace(/<[^>]*>/g, '').substring(0, 50);
200
+ tag.title = result.FirstURL;
201
  tag.addEventListener('click', () => {
202
  window.open(result.FirstURL, '_blank');
203
  });
 
208
  if (tagsContainer.children.length > 0) {
209
  resultsSection.appendChild(tagsContainer);
210
  container.appendChild(resultsSection);
211
+ hasContent = true;
212
  }
213
  }
214
 
215
+ // Check for Redirect (when query needs different search)
216
+ if (data.Redirect && data.Redirect.trim()) {
217
+ const redirectMsg = document.createElement('p');
218
+ redirectMsg.innerHTML = `πŸ”€ For better results, try searching for: <strong>${data.Redirect}</strong>`;
219
+ container.appendChild(redirectMsg);
220
+ hasContent = true;
221
+ }
222
+
223
+ // If no data found - provide helpful message
224
+ if (!hasContent) {
225
+ const noResult = document.createElement('div');
226
+ noResult.innerHTML = `
227
+ <p>πŸ˜• I couldn't find specific information for that query.</p>
228
+ <p style="margin-top: 0.5rem; font-size: 0.875rem; color: var(--text-secondary);">
229
+ Try:</p>
230
+ <ul style="margin-left: 1.25rem; margin-top: 0.25rem; color: var(--text-secondary); font-size: 0.875rem;">
231
+ <li>Using different keywords</li>
232
+ <li>Asking a factual question</li>
233
+ <li>Searching for a specific person, place, or concept</li>
234
+ </ul>
235
+ `;
236
  container.appendChild(noResult);
237
  }
238
 
 
247
  q: query,
248
  format: 'json',
249
  no_html: '1',
250
+ skip_disambig: '1',
251
+ pretty: '1'
252
  });
253
 
254
  const url = `${CONFIG.DDG_API_BASE}?${params}`;
255
+
256
+ // Try direct first, then with CORS proxy
257
+ const urlsToTry = [url, CONFIG.CORS_PROXY + encodeURIComponent(url)];
258
+
259
+ let lastError = null;
260
+
261
+ for (const attemptUrl of urlsToTry) {
262
+ try {
263
+ console.log('Trying URL:', attemptUrl);
264
+
265
+ const controller = new AbortController();
266
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
267
+
268
+ const response = await fetch(attemptUrl, {
269
+ method: 'GET',
270
+ headers: {
271
+ 'Accept': 'application/json',
272
+ },
273
+ signal: controller.signal
274
+ });
275
+
276
+ clearTimeout(timeoutId);
277
+
278
+ if (!response.ok) {
279
+ throw new Error(`HTTP error! status: ${response.status}`);
280
+ }
281
+
282
+ const data = await response.json();
283
+
284
+ // Check if we got any meaningful response
285
+ if (data && (data.Abstract || data.Answer || data.Definition || data.RelatedTopics || data.Results)) {
286
+ return data;
287
+ }
288
+
289
+ // If response exists but is empty, try next URL
290
+ console.log('Empty response, trying next URL');
291
+ lastError = new Error('Empty response');
292
+
293
+ } catch (error) {
294
+ console.log('Error with URL:', attemptUrl, error.message);
295
+ lastError = error;
296
+ continue;
297
  }
 
 
 
 
 
 
298
  }
299
+
300
+ throw lastError || new Error('Failed to fetch results');
301
  }
302
 
303
  /**
 
322
  elements.chatMessages.appendChild(loadingMessage);
323
  elements.chatMessages.parentElement.scrollTop = elements.chatMessages.parentElement.scrollHeight;
324
 
325
+ updateStatus('Searching the web...', 'searching');
326
 
327
  try {
328
  // Fetch from DuckDuckGo
 
335
  const formattedResponse = formatDuckDuckGoResponse(data);
336
  addMessage(formattedResponse, false);
337
 
338
+ updateStatus('Ready to search', 'ready');
339
  } catch (error) {
340
  // Remove loading message
341
  loadingMessage.remove();
342
 
343
+ // Show error message with retry suggestion
344
+ const errorContent = document.createElement('div');
345
+ errorContent.innerHTML = `
346
+ <p>πŸ˜• Sorry, I couldn't find results for that query.</p>
347
+ <p style="margin-top: 0.5rem; font-size: 0.875rem; color: var(--text-secondary);">
348
+ ${error.message.includes('abort') ? 'Request timed out. Please try again.' : 'Try using different keywords or check your internet connection.'}
349
+ </p>
350
+ `;
351
+ addMessage(errorContent, false);
352
 
353
  updateStatus('Error occurred', 'error');
354
+ console.error('Search Error:', error);
355
  } finally {
356
  // Reset loading state
357
  isLoading = false;
 
395
  // Focus input
396
  elements.userInput.focus();
397
 
398
+ updateStatus('Ready to search', 'ready');
399
 
400
+ console.log('πŸ” AI Web Search Assistant initialized!');
401
+ console.log('Powered by DuckDuckGo Instant Answer API');
402
  }
403
 
404
  // Start the app