JavaScript · 5764 bytes Raw Blame History
1 // Background script for LooseCannon
2 console.log('[LooseCannon Background] Initialized');
3
4 class LooseCannonBackground {
5 constructor() {
6 this.serverUrl = 'http://localhost:8765'; // Local server URL
7 this.isConnected = false;
8 this.activeTabs = new Map();
9 this.personalities = [];
10 this.currentPersonality = 'default';
11 this.init();
12 }
13
14 init() {
15 this.setupMessageListeners();
16 this.checkServerConnection();
17 this.loadSettings();
18 }
19
20 setupMessageListeners() {
21 browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
22 console.log('[LooseCannon Background] Received message:', message.type);
23
24 switch (message.type) {
25 case 'NEW_MESSAGE':
26 this.handleNewMessage(message.data, sender.tab.id)
27 .then(sendResponse)
28 .catch(error => {
29 console.error('Error handling message:', error);
30 sendResponse({ error: error.message });
31 });
32 return true; // Keep channel open for async response
33
34 case 'TOGGLE_ACTIVE':
35 this.handleToggleActive(sender.tab.id, message.data.isActive);
36 sendResponse({ success: true });
37 break;
38
39 case 'GET_SERVER_STATUS':
40 // Check connection immediately when asked
41 this.checkServerConnection().then(() => {
42 sendResponse({ connected: this.isConnected });
43 });
44 return true; // Keep channel open for async response
45
46 case 'GET_PERSONALITIES':
47 sendResponse({ personalities: this.personalities });
48 break;
49
50 case 'SET_PERSONALITY':
51 this.currentPersonality = message.data.personality;
52 this.saveSettings();
53 sendResponse({ success: true });
54 break;
55
56 default:
57 console.warn('Unknown message type:', message.type);
58 }
59 });
60 }
61
62 async handleNewMessage(data, tabId) {
63 if (!this.isConnected) {
64 console.warn('[LooseCannon] Server not connected, cannot process message');
65 return { error: 'Server not connected' };
66 }
67
68 try {
69 // Send message to local server for LLM processing
70 const response = await fetch(`${this.serverUrl}/generate`, {
71 method: 'POST',
72 headers: {
73 'Content-Type': 'application/json',
74 },
75 body: JSON.stringify({
76 message: data.text,
77 personality: this.currentPersonality,
78 chatId: data.chatId,
79 timestamp: data.timestamp
80 })
81 });
82
83 if (!response.ok) {
84 throw new Error(`Server error: ${response.status}`);
85 }
86
87 const result = await response.json();
88 console.log('[LooseCannon] Generated response:', result.reply);
89
90 // Log conversation for debugging
91 this.logConversation(data.chatId, data.text, result.reply);
92
93 return { reply: result.reply };
94 } catch (error) {
95 console.error('[LooseCannon] Error generating response:', error);
96 return { error: error.message };
97 }
98 }
99
100 handleToggleActive(tabId, isActive) {
101 if (isActive) {
102 this.activeTabs.set(tabId, {
103 activated: new Date().toISOString(),
104 messageCount: 0
105 });
106 console.log(`[LooseCannon] Activated for tab ${tabId}`);
107 } else {
108 this.activeTabs.delete(tabId);
109 console.log(`[LooseCannon] Deactivated for tab ${tabId}`);
110 }
111 }
112
113 async checkServerConnection() {
114 try {
115 console.log('[LooseCannon] Checking server connection to:', this.serverUrl);
116 const response = await fetch(`${this.serverUrl}/status`);
117
118 if (response.ok) {
119 const data = await response.json();
120 this.isConnected = true;
121 this.personalities = data.personalities || [];
122 console.log('[LooseCannon] ✅ Server connected successfully');
123 console.log('[LooseCannon] Available personalities:', this.personalities);
124 } else {
125 this.isConnected = false;
126 console.warn('[LooseCannon] ❌ Server responded with error:', response.status);
127 }
128 } catch (error) {
129 this.isConnected = false;
130 console.error('[LooseCannon] ❌ Could not connect to server:', error.message);
131 }
132
133 // Retry connection every 5 seconds if not connected
134 if (!this.isConnected) {
135 setTimeout(() => this.checkServerConnection(), 5000);
136 }
137 }
138
139 async loadSettings() {
140 try {
141 const settings = await browser.storage.local.get(['personality', 'serverUrl']);
142 if (settings.personality) {
143 this.currentPersonality = settings.personality;
144 }
145 if (settings.serverUrl) {
146 this.serverUrl = settings.serverUrl;
147 }
148 console.log('[LooseCannon] Settings loaded:', settings);
149 } catch (error) {
150 console.error('[LooseCannon] Error loading settings:', error);
151 }
152 }
153
154 async saveSettings() {
155 try {
156 await browser.storage.local.set({
157 personality: this.currentPersonality,
158 serverUrl: this.serverUrl
159 });
160 console.log('[LooseCannon] Settings saved');
161 } catch (error) {
162 console.error('[LooseCannon] Error saving settings:', error);
163 }
164 }
165
166 logConversation(chatId, userMessage, botReply) {
167 const log = {
168 timestamp: new Date().toISOString(),
169 chatId,
170 userMessage,
171 botReply
172 };
173
174 // Store conversation logs (with limit to prevent storage overflow)
175 browser.storage.local.get('conversationLogs').then(result => {
176 let logs = result.conversationLogs || [];
177 logs.push(log);
178
179 // Keep only last 100 messages
180 if (logs.length > 100) {
181 logs = logs.slice(-100);
182 }
183
184 browser.storage.local.set({ conversationLogs: logs });
185 });
186 }
187 }
188
189 // Initialize background script
190 const looseCannonBg = new LooseCannonBackground();