Удобный интерфейс для взаимодействия с AI моделями через OpenRouter API
Size
15.8 KB
Version
1.1.1
Created
Dec 4, 2025
Updated
3 months ago
1// ==UserScript==
2// @name OpenRouter AI Chat Assistant
3// @description Удобный интерфейс для взаимодействия с AI моделями через OpenRouter API
4// @version 1.1.1
5// @match *://*/*
6// @icon https://files.buildwithfern.com/openrouter.docs.buildwithfern.com/docs/9ea97c59357c47db235d0591b7ed936d40b94e3dd02030a36e26b406671f011d/content/assets/favicon.ico
7// @grant GM.getValue
8// @grant GM.setValue
9// @grant GM.xmlhttpRequest
10// ==/UserScript==
11(function() {
12 'use strict';
13
14 const API_KEY = '111222333';
15 const API_URL = 'https://openrouter.ai/api/v1/chat/completions';
16
17 // Доступные AI модели
18 const AI_MODELS = [
19 { id: 'openai/gpt-3.5-turbo', name: 'GPT-3.5 Turbo' },
20 { id: 'openai/gpt-4', name: 'GPT-4' },
21 { id: 'openai/gpt-4-turbo', name: 'GPT-4 Turbo' },
22 { id: 'anthropic/claude-3-opus', name: 'Claude 3 Opus' },
23 { id: 'anthropic/claude-3-sonnet', name: 'Claude 3 Sonnet' },
24 { id: 'anthropic/claude-3-haiku', name: 'Claude 3 Haiku' },
25 { id: 'google/gemini-pro', name: 'Gemini Pro' },
26 { id: 'meta-llama/llama-3-70b-instruct', name: 'Llama 3 70B' },
27 { id: 'mistralai/mistral-large', name: 'Mistral Large' },
28 { id: 'cohere/command-r-plus', name: 'Command R+' }
29 ];
30
31 // Добавляем стили для интерфейса
32 TM_addStyle(`
33 #ai-chat-container {
34 position: fixed;
35 bottom: 20px;
36 right: 20px;
37 width: 400px;
38 height: 600px;
39 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
40 border-radius: 16px;
41 box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
42 display: flex;
43 flex-direction: column;
44 z-index: 999999;
45 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
46 overflow: hidden;
47 }
48
49 #ai-chat-header {
50 background: rgba(255, 255, 255, 0.15);
51 backdrop-filter: blur(10px);
52 padding: 16px 20px;
53 display: flex;
54 justify-content: space-between;
55 align-items: center;
56 border-bottom: 1px solid rgba(255, 255, 255, 0.2);
57 }
58
59 #ai-chat-header h3 {
60 margin: 0;
61 color: white;
62 font-size: 18px;
63 font-weight: 600;
64 }
65
66 #ai-model-selector {
67 background: rgba(255, 255, 255, 0.9);
68 border: none;
69 color: #667eea;
70 padding: 8px 12px;
71 border-radius: 8px;
72 cursor: pointer;
73 font-size: 13px;
74 font-weight: 500;
75 outline: none;
76 transition: all 0.3s ease;
77 margin-right: 8px;
78 }
79
80 #ai-model-selector:hover {
81 background: white;
82 transform: scale(1.02);
83 }
84
85 #ai-chat-toggle {
86 background: rgba(255, 255, 255, 0.2);
87 border: none;
88 color: white;
89 width: 32px;
90 height: 32px;
91 border-radius: 8px;
92 cursor: pointer;
93 font-size: 18px;
94 display: flex;
95 align-items: center;
96 justify-content: center;
97 transition: all 0.3s ease;
98 }
99
100 #ai-chat-toggle:hover {
101 background: rgba(255, 255, 255, 0.3);
102 transform: scale(1.1);
103 }
104
105 #ai-chat-messages {
106 flex: 1;
107 overflow-y: auto;
108 padding: 20px;
109 display: flex;
110 flex-direction: column;
111 gap: 12px;
112 }
113
114 .ai-message {
115 max-width: 85%;
116 padding: 12px 16px;
117 border-radius: 12px;
118 word-wrap: break-word;
119 animation: slideIn 0.3s ease;
120 }
121
122 @keyframes slideIn {
123 from {
124 opacity: 0;
125 transform: translateY(10px);
126 }
127 to {
128 opacity: 1;
129 transform: translateY(0);
130 }
131 }
132
133 .ai-message.user {
134 align-self: flex-end;
135 background: white;
136 color: #333;
137 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
138 }
139
140 .ai-message.assistant {
141 align-self: flex-start;
142 background: rgba(255, 255, 255, 0.95);
143 color: #333;
144 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
145 }
146
147 .ai-message.system {
148 align-self: center;
149 background: rgba(255, 255, 255, 0.2);
150 color: white;
151 font-size: 13px;
152 font-style: italic;
153 }
154
155 #ai-chat-input-container {
156 padding: 16px 20px;
157 background: rgba(255, 255, 255, 0.15);
158 backdrop-filter: blur(10px);
159 border-top: 1px solid rgba(255, 255, 255, 0.2);
160 display: flex;
161 gap: 10px;
162 }
163
164 #ai-chat-input {
165 flex: 1;
166 padding: 12px 16px;
167 border: 2px solid rgba(255, 255, 255, 0.3);
168 border-radius: 12px;
169 background: rgba(255, 255, 255, 0.9);
170 color: #333;
171 font-size: 14px;
172 outline: none;
173 transition: all 0.3s ease;
174 }
175
176 #ai-chat-input:focus {
177 border-color: white;
178 background: white;
179 }
180
181 #ai-chat-send {
182 padding: 12px 20px;
183 background: white;
184 color: #667eea;
185 border: none;
186 border-radius: 12px;
187 cursor: pointer;
188 font-weight: 600;
189 font-size: 14px;
190 transition: all 0.3s ease;
191 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
192 }
193
194 #ai-chat-send:hover {
195 transform: translateY(-2px);
196 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
197 }
198
199 #ai-chat-send:disabled {
200 opacity: 0.5;
201 cursor: not-allowed;
202 transform: none;
203 }
204
205 #ai-chat-fab {
206 position: fixed;
207 bottom: 20px;
208 right: 20px;
209 width: 60px;
210 height: 60px;
211 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
212 border-radius: 50%;
213 display: none;
214 align-items: center;
215 justify-content: center;
216 cursor: pointer;
217 box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
218 z-index: 999999;
219 transition: all 0.3s ease;
220 }
221
222 #ai-chat-fab:hover {
223 transform: scale(1.1);
224 box-shadow: 0 6px 30px rgba(102, 126, 234, 0.6);
225 }
226
227 #ai-chat-fab svg {
228 width: 28px;
229 height: 28px;
230 fill: white;
231 }
232
233 .ai-chat-minimized #ai-chat-container {
234 display: none;
235 }
236
237 .ai-chat-minimized #ai-chat-fab {
238 display: flex;
239 }
240
241 #ai-chat-messages::-webkit-scrollbar {
242 width: 6px;
243 }
244
245 #ai-chat-messages::-webkit-scrollbar-track {
246 background: rgba(255, 255, 255, 0.1);
247 border-radius: 3px;
248 }
249
250 #ai-chat-messages::-webkit-scrollbar-thumb {
251 background: rgba(255, 255, 255, 0.3);
252 border-radius: 3px;
253 }
254
255 #ai-chat-messages::-webkit-scrollbar-thumb:hover {
256 background: rgba(255, 255, 255, 0.5);
257 }
258
259 .loading-dots {
260 display: inline-flex;
261 gap: 4px;
262 }
263
264 .loading-dots span {
265 width: 6px;
266 height: 6px;
267 background: #667eea;
268 border-radius: 50%;
269 animation: bounce 1.4s infinite ease-in-out both;
270 }
271
272 .loading-dots span:nth-child(1) {
273 animation-delay: -0.32s;
274 }
275
276 .loading-dots span:nth-child(2) {
277 animation-delay: -0.16s;
278 }
279
280 @keyframes bounce {
281 0%, 80%, 100% {
282 transform: scale(0);
283 }
284 40% {
285 transform: scale(1);
286 }
287 }
288 `);
289
290 // Создаем интерфейс чата
291 function createChatInterface() {
292 const container = document.createElement('div');
293 container.id = 'ai-chat-container';
294
295 // Создаем опции для селектора моделей
296 const modelOptions = AI_MODELS.map(model =>
297 `<option value="${model.id}">${model.name}</option>`
298 ).join('');
299
300 container.innerHTML = `
301 <div id="ai-chat-header">
302 <h3>🤖 AI Ассистент</h3>
303 <div style="display: flex; align-items: center; gap: 8px;">
304 <select id="ai-model-selector">
305 ${modelOptions}
306 </select>
307 <button id="ai-chat-toggle">−</button>
308 </div>
309 </div>
310 <div id="ai-chat-messages"></div>
311 <div id="ai-chat-input-container">
312 <input type="text" id="ai-chat-input" placeholder="Напишите сообщение...">
313 <button id="ai-chat-send">Отправить</button>
314 </div>
315 `;
316
317 const fab = document.createElement('div');
318 fab.id = 'ai-chat-fab';
319 fab.innerHTML = `
320 <svg viewBox="0 0 24 24">
321 <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/>
322 </svg>
323 `;
324
325 document.body.appendChild(container);
326 document.body.appendChild(fab);
327
328 return { container, fab };
329 }
330
331 // Добавляем сообщение в чат
332 function addMessage(text, type = 'user') {
333 const messagesContainer = document.getElementById('ai-chat-messages');
334 const messageDiv = document.createElement('div');
335 messageDiv.className = `ai-message ${type}`;
336 messageDiv.textContent = text;
337 messagesContainer.appendChild(messageDiv);
338 messagesContainer.scrollTop = messagesContainer.scrollHeight;
339
340 console.log(`[AI Chat] ${type}: ${text}`);
341 }
342
343 // Показываем индикатор загрузки
344 function showLoading() {
345 const messagesContainer = document.getElementById('ai-chat-messages');
346 const loadingDiv = document.createElement('div');
347 loadingDiv.className = 'ai-message assistant';
348 loadingDiv.id = 'loading-indicator';
349 loadingDiv.innerHTML = '<div class="loading-dots"><span></span><span></span><span></span></div>';
350 messagesContainer.appendChild(loadingDiv);
351 messagesContainer.scrollTop = messagesContainer.scrollHeight;
352 }
353
354 // Убираем индикатор загрузки
355 function hideLoading() {
356 const loadingIndicator = document.getElementById('loading-indicator');
357 if (loadingIndicator) {
358 loadingIndicator.remove();
359 }
360 }
361
362 // Отправляем сообщение в OpenRouter API
363 async function sendMessageToAI(message) {
364 try {
365 console.log('[AI Chat] Отправка запроса к OpenRouter API...');
366
367 // Получаем выбранную модель
368 const modelSelector = document.getElementById('ai-model-selector');
369 const selectedModel = modelSelector ? modelSelector.value : 'openai/gpt-3.5-turbo';
370
371 console.log('[AI Chat] Используется модель:', selectedModel);
372
373 const response = await GM.xmlhttpRequest({
374 method: 'POST',
375 url: API_URL,
376 headers: {
377 'Content-Type': 'application/json',
378 'Authorization': `Bearer ${API_KEY}`,
379 'HTTP-Referer': window.location.href,
380 'X-Title': 'AI Chat Assistant'
381 },
382 data: JSON.stringify({
383 model: selectedModel,
384 messages: [
385 {
386 role: 'user',
387 content: message
388 }
389 ]
390 }),
391 responseType: 'json'
392 });
393
394 console.log('[AI Chat] Получен ответ от API:', response.status);
395
396 if (response.status === 200) {
397 const data = response.response;
398 if (data.choices && data.choices.length > 0) {
399 return data.choices[0].message.content;
400 } else {
401 throw new Error('Некорректный формат ответа от API');
402 }
403 } else {
404 throw new Error(`Ошибка API: ${response.status} - ${response.statusText}`);
405 }
406 } catch (error) {
407 console.error('[AI Chat] Ошибка при отправке сообщения:', error);
408 throw error;
409 }
410 }
411
412 // Обработка отправки сообщения
413 async function handleSendMessage() {
414 const input = document.getElementById('ai-chat-input');
415 const sendButton = document.getElementById('ai-chat-send');
416 const message = input.value.trim();
417
418 if (!message) return;
419
420 // Добавляем сообщение пользователя
421 addMessage(message, 'user');
422 input.value = '';
423 sendButton.disabled = true;
424
425 // Показываем индикатор загрузки
426 showLoading();
427
428 try {
429 // Отправляем запрос к API
430 const aiResponse = await sendMessageToAI(message);
431
432 // Убираем индикатор загрузки
433 hideLoading();
434
435 // Добавляем ответ AI
436 addMessage(aiResponse, 'assistant');
437 } catch (error) {
438 hideLoading();
439 addMessage('Произошла ошибка при получении ответа. Проверьте API ключ и подключение.', 'system');
440 console.error('[AI Chat] Ошибка:', error);
441 } finally {
442 sendButton.disabled = false;
443 input.focus();
444 }
445 }
446
447 // Инициализация
448 async function init() {
449 console.log('[AI Chat] Инициализация расширения...');
450
451 // Создаем интерфейс
452 const { container, fab } = createChatInterface();
453
454 // Приветственное сообщение
455 addMessage('Привет! Я AI ассистент. Задайте мне любой вопрос!', 'system');
456
457 // Обработчики событий
458 const toggleButton = document.getElementById('ai-chat-toggle');
459 const input = document.getElementById('ai-chat-input');
460 const sendButton = document.getElementById('ai-chat-send');
461
462 // Сворачивание/разворачивание чата
463 toggleButton.addEventListener('click', () => {
464 document.body.classList.toggle('ai-chat-minimized');
465 });
466
467 fab.addEventListener('click', () => {
468 document.body.classList.remove('ai-chat-minimized');
469 input.focus();
470 });
471
472 // Отправка сообщения
473 sendButton.addEventListener('click', handleSendMessage);
474
475 input.addEventListener('keypress', (e) => {
476 if (e.key === 'Enter' && !e.shiftKey) {
477 e.preventDefault();
478 handleSendMessage();
479 }
480 });
481
482 console.log('[AI Chat] Расширение успешно загружено!');
483 }
484
485 // Запускаем после загрузки страницы
486 if (document.readyState === 'loading') {
487 document.addEventListener('DOMContentLoaded', init);
488 } else {
489 init();
490 }
491})();