Stripchat Chat Logger

Extracts chat messages and saves them as JSON files every minute

Size

10.1 KB

Version

1.1.2

Created

Oct 17, 2025

Updated

about 1 month ago

1// ==UserScript==
2// @name		Stripchat Chat Logger
3// @description		Extracts chat messages and saves them as JSON files every minute
4// @version		1.1.2
5// @match		https://*.stripchat.com/*
6// @icon		https://assets.striiiipst.com/assets/icons/favicon-32x32.png?v=9670c787
7// @grant		GM.xmlhttpRequest
8// @grant		GM.setClipboard
9// ==/UserScript==
10(function() {
11    'use strict';
12
13    console.log('[Stripchat Chat Logger] Extension started');
14
15    let isLogging = false;
16    let loggingInterval = null;
17    let controlButton = null;
18    let autoHideTimeout = null;
19
20    // Extract username from URL
21    function getUsername() {
22        const pathParts = window.location.pathname.split('/');
23        return pathParts[1] || 'unknown';
24    }
25
26    // Format date as YYMMDD_HHMM
27    function formatDateTime() {
28        const now = new Date();
29        const yy = String(now.getFullYear()).slice(-2);
30        const mm = String(now.getMonth() + 1).padStart(2, '0');
31        const dd = String(now.getDate()).padStart(2, '0');
32        const hh = String(now.getHours()).padStart(2, '0');
33        const min = String(now.getMinutes()).padStart(2, '0');
34        return `${yy}${mm}${dd}_${hh}${min}`;
35    }
36
37    // Extract chat messages from the DOM
38    function extractChatMessages() {
39        const messagesContainer = document.querySelector('#app > div > div.main-layout-main-content > div > div > div > div.page > div > div.ViewCamWrapper__chatLayout\\#n4.chat > div.ViewCamWrapper__chat\\#MZ.model-chat.model-chat-normal.model-chat-size-medium > div > div.model-chat-container.public > div > div.model-chat-content > div.scroll-bar-container > div > div.messages');
40        
41        if (!messagesContainer) {
42            console.error('[Stripchat Chat Logger] Messages container not found');
43            return [];
44        }
45
46        const messageElements = messagesContainer.querySelectorAll('.message');
47        const messages = [];
48
49        messageElements.forEach((msgElement, index) => {
50            try {
51                const messageBody = msgElement.querySelector('.message-body');
52                if (!messageBody) return;
53
54                // Extract username
55                const usernameElement = msgElement.querySelector('.user-levels-username-text, .message-username');
56                const username = usernameElement ? usernameElement.textContent.trim() : 'Unknown';
57
58                // Extract message text (everything after username)
59                const messageText = messageBody.textContent.replace(username, '').trim();
60
61                // Determine message type
62                let messageType = 'regular';
63                if (msgElement.classList.contains('tip-message')) {
64                    messageType = 'tip';
65                } else if (msgElement.classList.contains('system-message')) {
66                    messageType = 'system';
67                }
68
69                // Extract user level/league if available
70                const userLevel = msgElement.querySelector('[class*="color-league-"]');
71                const league = userLevel ? Array.from(userLevel.classList).find(c => c.startsWith('color-league-'))?.replace('color-league-', '') : null;
72
73                messages.push({
74                    index: index,
75                    timestamp: new Date().toISOString(),
76                    username: username,
77                    message: messageText,
78                    type: messageType,
79                    league: league
80                });
81            } catch (error) {
82                console.error('[Stripchat Chat Logger] Error extracting message:', error);
83            }
84        });
85
86        console.log(`[Stripchat Chat Logger] Extracted ${messages.length} messages`);
87        return messages;
88    }
89
90    // Save messages to JSON file and clipboard
91    async function saveMessagesToFile() {
92        const username = getUsername();
93        const dateTime = formatDateTime();
94        const filename = `sc_${username}_${dateTime}.json`;
95        
96        const messages = extractChatMessages();
97        
98        if (messages.length === 0) {
99            console.log('[Stripchat Chat Logger] No messages to save');
100            return;
101        }
102
103        const jsonData = {
104            metadata: {
105                username: username,
106                url: window.location.href,
107                captureTime: new Date().toISOString(),
108                messageCount: messages.length
109            },
110            messages: messages
111        };
112
113        const jsonString = JSON.stringify(jsonData, null, 2);
114        
115        // Copy to clipboard
116        try {
117            await GM.setClipboard(jsonString);
118            console.log('[Stripchat Chat Logger] Copied to clipboard');
119        } catch (error) {
120            console.error('[Stripchat Chat Logger] Failed to copy to clipboard:', error);
121        }
122
123        // Save to file
124        const blob = new Blob([jsonString], { type: 'application/json' });
125        const url = URL.createObjectURL(blob);
126
127        const downloadLink = document.createElement('a');
128        downloadLink.href = url;
129        downloadLink.download = filename;
130        downloadLink.style.display = 'none';
131        document.body.appendChild(downloadLink);
132        downloadLink.click();
133        document.body.removeChild(downloadLink);
134        URL.revokeObjectURL(url);
135
136        console.log(`[Stripchat Chat Logger] Saved ${messages.length} messages to ${filename}`);
137        console.log(`[Stripchat Chat Logger] Recommended path: ~/Downloads/${username}/${filename}`);
138    }
139
140    // Update button appearance based on logging state
141    function updateButtonState() {
142        if (!controlButton) return;
143        
144        if (isLogging) {
145            controlButton.style.backgroundColor = '#00ff00';
146            controlButton.title = 'Chat Logger: Active (Click to stop)';
147        } else {
148            controlButton.style.backgroundColor = '#ff0000';
149            controlButton.title = 'Chat Logger: Inactive (Click to start)';
150        }
151    }
152
153    // Start logging
154    function startLogging() {
155        if (isLogging) return;
156        
157        isLogging = true;
158        console.log('[Stripchat Chat Logger] Starting logging...');
159        
160        // Save immediately
161        saveMessagesToFile();
162        
163        // Then save every minute
164        loggingInterval = setInterval(saveMessagesToFile, 60000);
165        
166        updateButtonState();
167        console.log('[Stripchat Chat Logger] Logger active - saving every 60 seconds');
168    }
169
170    // Stop logging
171    function stopLogging() {
172        if (!isLogging) return;
173        
174        isLogging = false;
175        console.log('[Stripchat Chat Logger] Stopping logging...');
176        
177        if (loggingInterval) {
178            clearInterval(loggingInterval);
179            loggingInterval = null;
180        }
181        
182        updateButtonState();
183        console.log('[Stripchat Chat Logger] Logger stopped');
184    }
185
186    // Toggle logging state
187    function toggleLogging() {
188        if (isLogging) {
189            stopLogging();
190        } else {
191            startLogging();
192        }
193    }
194
195    // Auto-hide button after delay
196    function scheduleAutoHide() {
197        if (autoHideTimeout) {
198            clearTimeout(autoHideTimeout);
199        }
200        
201        controlButton.style.opacity = '1';
202        
203        autoHideTimeout = setTimeout(() => {
204            controlButton.style.opacity = '0.3';
205        }, 2000);
206    }
207
208    // Create floating control button
209    function createControlButton() {
210        const videoArea = document.querySelector('#app > div > div.main-layout-main-content > div > div > div > div.page > div > div.ViewCamWrapper__chatLayout\\#n4.chat');
211        
212        if (!videoArea) {
213            console.error('[Stripchat Chat Logger] Video area not found');
214            return;
215        }
216
217        controlButton = document.createElement('button');
218        controlButton.innerHTML = '📹';
219        controlButton.style.cssText = `
220            position: fixed;
221            bottom: 10px;
222            right: 10px;
223            width: 50px;
224            height: 50px;
225            border-radius: 50%;
226            border: 2px solid #ffffff;
227            background-color: #ff0000;
228            color: #ffffff;
229            font-size: 24px;
230            cursor: pointer;
231            z-index: 10000;
232            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
233            transition: opacity 0.3s ease, transform 0.2s ease;
234            opacity: 0.3;
235            display: flex;
236            align-items: center;
237            justify-content: center;
238        `;
239
240        // Click to toggle logging
241        controlButton.addEventListener('click', (e) => {
242            e.stopPropagation();
243            toggleLogging();
244        });
245
246        // Hover effects
247        controlButton.addEventListener('mouseenter', () => {
248            controlButton.style.opacity = '1';
249            controlButton.style.transform = 'scale(1.1)';
250            if (autoHideTimeout) {
251                clearTimeout(autoHideTimeout);
252            }
253        });
254
255        controlButton.addEventListener('mouseleave', () => {
256            controlButton.style.transform = 'scale(1)';
257            scheduleAutoHide();
258        });
259
260        document.body.appendChild(controlButton);
261        updateButtonState();
262        scheduleAutoHide();
263
264        console.log('[Stripchat Chat Logger] Control button created');
265    }
266
267    // Initialize the logger
268    function init() {
269        console.log('[Stripchat Chat Logger] Initializing...');
270        
271        // Wait for page to load
272        const checkPageLoaded = setInterval(() => {
273            const videoArea = document.querySelector('#app > div > div.main-layout-main-content > div > div > div > div.page > div > div.ViewCamWrapper__chatLayout\\#n4.chat');
274            
275            if (videoArea) {
276                clearInterval(checkPageLoaded);
277                console.log('[Stripchat Chat Logger] Page loaded, creating control button');
278                
279                createControlButton();
280            }
281        }, 1000);
282
283        // Stop checking after 30 seconds if page doesn't load
284        setTimeout(() => {
285            clearInterval(checkPageLoaded);
286        }, 30000);
287    }
288
289    // Start when page is ready
290    if (document.readyState === 'loading') {
291        document.addEventListener('DOMContentLoaded', init);
292    } else {
293        init();
294    }
295
296})();
Stripchat Chat Logger | Robomonkey