Automatically navigates through friend profiles and pokes users on Remilia
Size
11.7 KB
Version
1.0.1
Created
Oct 19, 2025
Updated
5 days ago
1// ==UserScript==
2// @name Remilia Profile Poker
3// @description Automatically navigates through friend profiles and pokes users on Remilia
4// @version 1.0.1
5// @match https://*.remilia.com/*
6// @icon https://remilia.com/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('Remilia Profile Poker: Extension loaded');
12
13 // State management
14 let visitedProfiles = new Set();
15 let isRunning = false;
16 let currentCycle = 0;
17
18 // Debounce helper
19 function debounce(func, wait) {
20 let timeout;
21 return function executedFunction(...args) {
22 const later = () => {
23 clearTimeout(timeout);
24 func(...args);
25 };
26 clearTimeout(timeout);
27 timeout = setTimeout(later, wait);
28 };
29 }
30
31 // Get current profile username from URL
32 function getCurrentProfile() {
33 const match = window.location.pathname.match(/\/~([^\/]+)/);
34 return match ? match[1] : null;
35 }
36
37 // Find and click the Poke button
38 async function clickPokeButton() {
39 console.log('Attempting to click Poke button...');
40
41 // Wait a bit for page to fully load
42 await new Promise(resolve => setTimeout(resolve, 2000));
43
44 // Try multiple selectors for the Poke button
45 const pokeSelectors = [
46 'button.btn.poke:not(.poke-cooldown)',
47 'button.btn.poke[data-state="not-poked"]',
48 'button.btn.poke:not([data-state="already-poked"])',
49 '.friendship-row button.poke:not(.poke-cooldown)'
50 ];
51
52 for (const selector of pokeSelectors) {
53 const pokeButton = document.querySelector(selector);
54 if (pokeButton) {
55 console.log('Found Poke button, clicking...');
56 pokeButton.click();
57 await new Promise(resolve => setTimeout(resolve, 1000));
58 return true;
59 }
60 }
61
62 // Check if already poked
63 const alreadyPoked = document.querySelector('button.btn.poke.poke-cooldown, button.btn.poke[data-state="already-poked"]');
64 if (alreadyPoked) {
65 console.log('User already poked (on cooldown)');
66 return false;
67 }
68
69 console.log('Poke button not found');
70 return false;
71 }
72
73 // Get list of friend profile links
74 function getFriendLinks() {
75 const friendsSection = document.querySelector('.friends-grid');
76 if (!friendsSection) {
77 console.log('Friends section not found');
78 return [];
79 }
80
81 const links = Array.from(friendsSection.querySelectorAll('a.profile-link[href^="/~"]'));
82 console.log(`Found ${links.length} friend links`);
83 return links;
84 }
85
86 // Navigate to next unvisited friend
87 async function navigateToNextFriend() {
88 console.log('Looking for next friend to visit...');
89
90 const friendLinks = getFriendLinks();
91
92 // Filter out already visited profiles
93 const unvisitedLinks = friendLinks.filter(link => {
94 const username = link.getAttribute('href').replace('/~', '');
95 return !visitedProfiles.has(username);
96 });
97
98 console.log(`Unvisited friends: ${unvisitedLinks.length}`);
99
100 if (unvisitedLinks.length > 0) {
101 const nextLink = unvisitedLinks[0];
102 const username = nextLink.getAttribute('href').replace('/~', '');
103 console.log(`Navigating to friend: ${username}`);
104 visitedProfiles.add(username);
105 nextLink.click();
106 return true;
107 }
108
109 console.log('No unvisited friends found');
110 return false;
111 }
112
113 // Navigate to notifications and click a random profile
114 async function navigateToNotifications() {
115 console.log('Navigating to notifications to find new profiles...');
116
117 // Click notifications bell
118 const notificationBell = document.querySelector('.nav-item-wrapper.notifications, .nav-item.notifications');
119 if (!notificationBell) {
120 console.log('Notification bell not found');
121 return false;
122 }
123
124 console.log('Clicking notification bell...');
125 notificationBell.click();
126
127 // Wait for notifications panel to open
128 await new Promise(resolve => setTimeout(resolve, 2000));
129
130 // Find profile links in notifications
131 const notificationLinks = Array.from(document.querySelectorAll('.notification a[href^="/~"], .notifications-panel a[href^="/~"]'));
132
133 if (notificationLinks.length === 0) {
134 console.log('No profile links found in notifications');
135 // Close notifications and try to go back
136 const closeButton = document.querySelector('.notifications-panel .close, .modal-close');
137 if (closeButton) closeButton.click();
138 return false;
139 }
140
141 // Pick a random profile
142 const randomLink = notificationLinks[Math.floor(Math.random() * notificationLinks.length)];
143 const username = randomLink.getAttribute('href').replace('/~', '');
144 console.log(`Clicking random notification profile: ${username}`);
145
146 // Clear visited profiles for new cycle
147 visitedProfiles.clear();
148 currentCycle++;
149 console.log(`Starting new cycle #${currentCycle}`);
150
151 randomLink.click();
152 return true;
153 }
154
155 // Main automation loop
156 async function automationLoop() {
157 if (!isRunning) return;
158
159 console.log('=== Automation Loop Iteration ===');
160
161 const currentProfile = getCurrentProfile();
162 console.log(`Current profile: ${currentProfile}`);
163
164 // Step 1: Try to poke current user
165 await clickPokeButton();
166
167 // Wait before navigating
168 await new Promise(resolve => setTimeout(resolve, 2000));
169
170 // Step 2: Try to navigate to next friend
171 const foundFriend = await navigateToNextFriend();
172
173 if (foundFriend) {
174 // Wait for navigation and continue loop
175 await new Promise(resolve => setTimeout(resolve, 3000));
176 automationLoop();
177 } else {
178 // Step 3: Dead-end reached, go to notifications
179 console.log('Dead-end reached, switching to notifications...');
180 await new Promise(resolve => setTimeout(resolve, 2000));
181
182 const foundNotification = await navigateToNotifications();
183
184 if (foundNotification) {
185 // Wait for navigation and continue loop
186 await new Promise(resolve => setTimeout(resolve, 3000));
187 automationLoop();
188 } else {
189 console.log('Could not find new profiles, stopping automation');
190 stopAutomation();
191 }
192 }
193 }
194
195 // Start automation
196 function startAutomation() {
197 if (isRunning) {
198 console.log('Automation already running');
199 return;
200 }
201
202 console.log('Starting Remilia Profile Poker automation...');
203 isRunning = true;
204 visitedProfiles.clear();
205 currentCycle = 0;
206
207 // Update button
208 const startButton = document.getElementById('remilia-poker-start');
209 if (startButton) {
210 startButton.textContent = '⏸️ STOP';
211 startButton.style.backgroundColor = '#ef4444';
212 }
213
214 automationLoop();
215 }
216
217 // Stop automation
218 function stopAutomation() {
219 console.log('Stopping automation...');
220 isRunning = false;
221
222 // Update button
223 const startButton = document.getElementById('remilia-poker-start');
224 if (startButton) {
225 startButton.textContent = '▶️ START';
226 startButton.style.backgroundColor = '#10b981';
227 }
228 }
229
230 // Toggle automation
231 function toggleAutomation() {
232 if (isRunning) {
233 stopAutomation();
234 } else {
235 startAutomation();
236 }
237 }
238
239 // Create control panel UI
240 function createControlPanel() {
241 // Check if already exists
242 if (document.getElementById('remilia-poker-panel')) return;
243
244 const panel = document.createElement('div');
245 panel.id = 'remilia-poker-panel';
246 panel.style.cssText = `
247 position: fixed;
248 top: 80px;
249 right: 20px;
250 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
251 color: white;
252 padding: 16px;
253 border-radius: 12px;
254 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
255 z-index: 999999;
256 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
257 min-width: 200px;
258 backdrop-filter: blur(10px);
259 `;
260
261 panel.innerHTML = `
262 <div style="font-weight: bold; font-size: 14px; margin-bottom: 12px; text-align: center;">
263 🎯 Profile Poker
264 </div>
265 <button id="remilia-poker-start" style="
266 width: 100%;
267 padding: 10px;
268 background-color: #10b981;
269 color: white;
270 border: none;
271 border-radius: 8px;
272 font-weight: bold;
273 cursor: pointer;
274 font-size: 13px;
275 transition: all 0.2s;
276 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
277 ">▶️ START</button>
278 <div id="remilia-poker-stats" style="
279 margin-top: 12px;
280 font-size: 11px;
281 text-align: center;
282 opacity: 0.9;
283 line-height: 1.6;
284 ">
285 <div>Visited: <span id="visited-count">0</span></div>
286 <div>Cycle: <span id="cycle-count">0</span></div>
287 </div>
288 `;
289
290 document.body.appendChild(panel);
291
292 // Add button hover effect
293 const startButton = document.getElementById('remilia-poker-start');
294 startButton.addEventListener('mouseenter', () => {
295 startButton.style.transform = 'scale(1.05)';
296 });
297 startButton.addEventListener('mouseleave', () => {
298 startButton.style.transform = 'scale(1)';
299 });
300
301 // Add click handler
302 startButton.addEventListener('click', toggleAutomation);
303
304 console.log('Control panel created');
305 }
306
307 // Update stats display
308 function updateStats() {
309 const visitedCountEl = document.getElementById('visited-count');
310 const cycleCountEl = document.getElementById('cycle-count');
311
312 if (visitedCountEl) visitedCountEl.textContent = visitedProfiles.size;
313 if (cycleCountEl) cycleCountEl.textContent = currentCycle;
314 }
315
316 // Update stats periodically
317 setInterval(updateStats, 1000);
318
319 // Initialize when page loads
320 function init() {
321 console.log('Initializing Remilia Profile Poker...');
322
323 // Wait for page to be ready
324 if (document.readyState === 'loading') {
325 document.addEventListener('DOMContentLoaded', init);
326 return;
327 }
328
329 // Create control panel after a short delay
330 setTimeout(createControlPanel, 1000);
331
332 // Observe DOM changes to recreate panel if needed
333 const observer = new MutationObserver(debounce(() => {
334 if (!document.getElementById('remilia-poker-panel')) {
335 createControlPanel();
336 }
337 }, 1000));
338
339 observer.observe(document.body, {
340 childList: true,
341 subtree: true
342 });
343 }
344
345 // Start initialization
346 init();
347})();