JavaScript · 11202 bytes Raw Blame History
1 // Screenshot Manager for Evidence Collection
2 // Captures and stores screenshots when scammers are detected
3
4 export class ScreenshotManager {
5 constructor() {
6 this.storage = 'screenshots';
7 this.maxScreenshots = 100;
8 this.compressionQuality = 0.8;
9 this.init();
10 }
11
12 async init() {
13 // Create storage structure if doesn't exist
14 const stored = await chrome.storage.local.get(this.storage);
15 if (!stored[this.storage]) {
16 await chrome.storage.local.set({ [this.storage]: [] });
17 }
18 }
19
20 async capture(tab, metadata = {}) {
21 try {
22 // Capture visible tab
23 const dataUrl = await chrome.tabs.captureVisibleTab(tab.windowId, {
24 format: 'jpeg',
25 quality: Math.round(this.compressionQuality * 100)
26 });
27
28 // Generate unique ID
29 const id = `screenshot_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
30
31 // Create screenshot object
32 const screenshot = {
33 id,
34 timestamp: new Date().toISOString(),
35 url: tab.url,
36 title: tab.title,
37 platform: this.detectPlatform(tab.url),
38 dataUrl: await this.compressImage(dataUrl),
39 thumbnail: await this.createThumbnail(dataUrl),
40 metadata: {
41 ...metadata,
42 tabId: tab.id,
43 windowId: tab.windowId,
44 incognito: tab.incognito
45 }
46 };
47
48 // Store screenshot
49 await this.store(screenshot);
50
51 // Notify dashboard
52 this.notifyDashboard(screenshot);
53
54 return {
55 success: true,
56 id: screenshot.id,
57 size: this.calculateSize(screenshot.dataUrl)
58 };
59 } catch (error) {
60 console.error('[ScreenshotManager] Capture error:', error);
61 return {
62 success: false,
63 error: error.message
64 };
65 }
66 }
67
68 detectPlatform(url) {
69 if (url.includes('whatsapp.com')) return 'whatsapp';
70 if (url.includes('telegram.org')) return 'telegram';
71 if (url.includes('messenger.com')) return 'messenger';
72 return 'unknown';
73 }
74
75 async compressImage(dataUrl) {
76 return new Promise((resolve) => {
77 const img = new Image();
78 img.onload = () => {
79 const canvas = document.createElement('canvas');
80 const ctx = canvas.getContext('2d');
81
82 // Set max dimensions
83 const maxWidth = 1920;
84 const maxHeight = 1080;
85
86 let width = img.width;
87 let height = img.height;
88
89 // Calculate new dimensions
90 if (width > maxWidth || height > maxHeight) {
91 const ratio = Math.min(maxWidth / width, maxHeight / height);
92 width *= ratio;
93 height *= ratio;
94 }
95
96 canvas.width = width;
97 canvas.height = height;
98
99 // Draw and compress
100 ctx.drawImage(img, 0, 0, width, height);
101 resolve(canvas.toDataURL('image/jpeg', this.compressionQuality));
102 };
103
104 img.src = dataUrl;
105 });
106 }
107
108 async createThumbnail(dataUrl) {
109 return new Promise((resolve) => {
110 const img = new Image();
111 img.onload = () => {
112 const canvas = document.createElement('canvas');
113 const ctx = canvas.getContext('2d');
114
115 // Thumbnail dimensions
116 const thumbWidth = 320;
117 const thumbHeight = 180;
118
119 canvas.width = thumbWidth;
120 canvas.height = thumbHeight;
121
122 // Draw thumbnail
123 ctx.drawImage(img, 0, 0, thumbWidth, thumbHeight);
124 resolve(canvas.toDataURL('image/jpeg', 0.6));
125 };
126
127 img.src = dataUrl;
128 });
129 }
130
131 calculateSize(dataUrl) {
132 // Estimate size in bytes
133 const base64Length = dataUrl.length - 'data:image/jpeg;base64,'.length;
134 const sizeInBytes = base64Length * 0.75;
135 return Math.round(sizeInBytes / 1024); // Return in KB
136 }
137
138 async store(screenshot) {
139 const stored = await chrome.storage.local.get(this.storage);
140 let screenshots = stored[this.storage] || [];
141
142 // Add new screenshot
143 screenshots.unshift(screenshot);
144
145 // Enforce max limit
146 if (screenshots.length > this.maxScreenshots) {
147 screenshots = screenshots.slice(0, this.maxScreenshots);
148 }
149
150 // Update storage
151 await chrome.storage.local.set({ [this.storage]: screenshots });
152
153 // Also store metadata separately for quick access
154 await this.storeMetadata(screenshot);
155
156 return screenshot;
157 }
158
159 async storeMetadata(screenshot) {
160 const metaKey = `${this.storage}_metadata`;
161 const stored = await chrome.storage.local.get(metaKey);
162 let metadata = stored[metaKey] || [];
163
164 metadata.unshift({
165 id: screenshot.id,
166 timestamp: screenshot.timestamp,
167 platform: screenshot.platform,
168 url: screenshot.url,
169 scammerScore: screenshot.metadata.scammerScore || 0
170 });
171
172 // Keep only last 500 metadata entries
173 if (metadata.length > 500) {
174 metadata = metadata.slice(0, 500);
175 }
176
177 await chrome.storage.local.set({ [metaKey]: metadata });
178 }
179
180 async get(id) {
181 const stored = await chrome.storage.local.get(this.storage);
182 const screenshots = stored[this.storage] || [];
183 return screenshots.find(s => s.id === id);
184 }
185
186 async getAll(options = {}) {
187 const stored = await chrome.storage.local.get(this.storage);
188 let screenshots = stored[this.storage] || [];
189
190 // Apply filters
191 if (options.platform) {
192 screenshots = screenshots.filter(s => s.platform === options.platform);
193 }
194
195 if (options.startDate) {
196 screenshots = screenshots.filter(s =>
197 new Date(s.timestamp) >= new Date(options.startDate)
198 );
199 }
200
201 if (options.endDate) {
202 screenshots = screenshots.filter(s =>
203 new Date(s.timestamp) <= new Date(options.endDate)
204 );
205 }
206
207 if (options.limit) {
208 screenshots = screenshots.slice(0, options.limit);
209 }
210
211 return screenshots;
212 }
213
214 async getMetadata() {
215 const metaKey = `${this.storage}_metadata`;
216 const stored = await chrome.storage.local.get(metaKey);
217 return stored[metaKey] || [];
218 }
219
220 async delete(id) {
221 const stored = await chrome.storage.local.get(this.storage);
222 let screenshots = stored[this.storage] || [];
223
224 screenshots = screenshots.filter(s => s.id !== id);
225 await chrome.storage.local.set({ [this.storage]: screenshots });
226
227 // Also remove from metadata
228 const metaKey = `${this.storage}_metadata`;
229 const metaStored = await chrome.storage.local.get(metaKey);
230 let metadata = metaStored[metaKey] || [];
231 metadata = metadata.filter(m => m.id !== id);
232 await chrome.storage.local.set({ [metaKey]: metadata });
233
234 return { success: true };
235 }
236
237 async cleanup(maxAge = 24 * 60 * 60 * 1000) {
238 const stored = await chrome.storage.local.get(this.storage);
239 let screenshots = stored[this.storage] || [];
240
241 const cutoff = Date.now() - maxAge;
242 const before = screenshots.length;
243
244 screenshots = screenshots.filter(s =>
245 new Date(s.timestamp).getTime() > cutoff
246 );
247
248 await chrome.storage.local.set({ [this.storage]: screenshots });
249
250 const deleted = before - screenshots.length;
251 console.log(`[ScreenshotManager] Cleaned up ${deleted} old screenshots`);
252
253 return { deleted };
254 }
255
256 async export(ids = null) {
257 const stored = await chrome.storage.local.get(this.storage);
258 let screenshots = stored[this.storage] || [];
259
260 if (ids) {
261 screenshots = screenshots.filter(s => ids.includes(s.id));
262 }
263
264 // Create export data
265 const exportData = {
266 version: '1.0',
267 timestamp: new Date().toISOString(),
268 count: screenshots.length,
269 screenshots: screenshots.map(s => ({
270 id: s.id,
271 timestamp: s.timestamp,
272 platform: s.platform,
273 url: s.url,
274 metadata: s.metadata,
275 thumbnail: s.thumbnail // Include thumbnail only
276 }))
277 };
278
279 return exportData;
280 }
281
282 async getStatistics() {
283 const stored = await chrome.storage.local.get(this.storage);
284 const screenshots = stored[this.storage] || [];
285
286 const stats = {
287 total: screenshots.length,
288 byPlatform: {},
289 byDay: {},
290 totalSize: 0,
291 oldestTimestamp: null,
292 newestTimestamp: null
293 };
294
295 screenshots.forEach(s => {
296 // By platform
297 stats.byPlatform[s.platform] = (stats.byPlatform[s.platform] || 0) + 1;
298
299 // By day
300 const day = new Date(s.timestamp).toDateString();
301 stats.byDay[day] = (stats.byDay[day] || 0) + 1;
302
303 // Size
304 stats.totalSize += this.calculateSize(s.dataUrl);
305
306 // Timestamps
307 const timestamp = new Date(s.timestamp).getTime();
308 if (!stats.oldestTimestamp || timestamp < stats.oldestTimestamp) {
309 stats.oldestTimestamp = s.timestamp;
310 }
311 if (!stats.newestTimestamp || timestamp > stats.newestTimestamp) {
312 stats.newestTimestamp = s.timestamp;
313 }
314 });
315
316 return stats;
317 }
318
319 notifyDashboard(screenshot) {
320 // Send to dashboard if open
321 chrome.runtime.sendMessage({
322 type: 'DASHBOARD_UPDATE',
323 data: {
324 event: 'screenshot_captured',
325 screenshot: {
326 id: screenshot.id,
327 timestamp: screenshot.timestamp,
328 platform: screenshot.platform,
329 thumbnail: screenshot.thumbnail
330 }
331 }
332 }).catch(() => {
333 // Dashboard might not be open
334 });
335 }
336
337 async annotate(id, annotations) {
338 const screenshot = await this.get(id);
339 if (!screenshot) {
340 throw new Error('Screenshot not found');
341 }
342
343 // Add annotations
344 screenshot.annotations = {
345 ...screenshot.annotations,
346 ...annotations,
347 updatedAt: new Date().toISOString()
348 };
349
350 // Update storage
351 const stored = await chrome.storage.local.get(this.storage);
352 let screenshots = stored[this.storage] || [];
353
354 const index = screenshots.findIndex(s => s.id === id);
355 if (index !== -1) {
356 screenshots[index] = screenshot;
357 await chrome.storage.local.set({ [this.storage]: screenshots });
358 }
359
360 return screenshot;
361 }
362
363 async createEvidence Report(screenshotIds, conversationId) {
364 const screenshots = await this.getAll();
365 const selected = screenshots.filter(s => screenshotIds.includes(s.id));
366
367 const report = {
368 id: `report_${Date.now()}`,
369 timestamp: new Date().toISOString(),
370 conversationId,
371 screenshots: selected.map(s => ({
372 id: s.id,
373 timestamp: s.timestamp,
374 platform: s.platform,
375 url: s.url,
376 annotations: s.annotations
377 })),
378 summary: {
379 totalScreenshots: selected.length,
380 platforms: [...new Set(selected.map(s => s.platform))],
381 timeRange: {
382 start: selected[selected.length - 1]?.timestamp,
383 end: selected[0]?.timestamp
384 }
385 }
386 };
387
388 // Store report
389 const reportKey = 'evidence_reports';
390 const stored = await chrome.storage.local.get(reportKey);
391 let reports = stored[reportKey] || [];
392 reports.unshift(report);
393
394 // Keep only last 50 reports
395 if (reports.length > 50) {
396 reports = reports.slice(0, 50);
397 }
398
399 await chrome.storage.local.set({ [reportKey]: reports });
400
401 return report;
402 }
403 }
404
405 // Export for use in service worker
406 if (typeof module !== 'undefined' && module.exports) {
407 module.exports = ScreenshotManager;
408 }