Automatically completes Microsoft Rewards activities to earn points
Size
12.5 KB
Version
1.0.1
Created
Jan 11, 2026
Updated
24 days ago
1// ==UserScript==
2// @name Microsoft Rewards Auto-Complete
3// @description Automatically completes Microsoft Rewards activities to earn points
4// @version 1.0.1
5// @match https://*.rewards.bing.com/*
6// @icon https://rewards.bing.com/rewards.png
7// @grant GM.getValue
8// @grant GM.setValue
9// @grant GM.openInTab
10// ==/UserScript==
11(function() {
12 'use strict';
13
14 console.log('Microsoft Rewards Auto-Complete extension loaded');
15
16 // Debounce function to prevent excessive calls
17 function debounce(func, wait) {
18 let timeout;
19 return function executedFunction(...args) {
20 const later = () => {
21 clearTimeout(timeout);
22 func(...args);
23 };
24 clearTimeout(timeout);
25 timeout = setTimeout(later, wait);
26 };
27 }
28
29 // Main initialization function
30 async function init() {
31 console.log('Initializing Microsoft Rewards Auto-Complete...');
32
33 // Check if we're on the main dashboard
34 if (window.location.pathname === '/' || window.location.pathname.includes('dashboard')) {
35 await handleDashboard();
36 } else if (window.location.href.includes('bing.com/search')) {
37 // If we're on a Bing search page (activity completion)
38 await handleActivityCompletion();
39 }
40 }
41
42 // Handle the main dashboard - find and complete activities
43 async function handleDashboard() {
44 console.log('On Microsoft Rewards dashboard');
45
46 // Wait for the page to fully load
47 await waitForElement('mee-card');
48
49 // Add a control panel to the page
50 addControlPanel();
51
52 // Get all available activities
53 const activities = await getAvailableActivities();
54 console.log(`Found ${activities.length} activities`);
55
56 // Store activities for later use
57 await GM.setValue('pendingActivities', JSON.stringify(activities));
58 }
59
60 // Get all available activities from the page
61 async function getAvailableActivities() {
62 const activityCards = document.querySelectorAll('mee-card a.ds-card-sec');
63 const activities = [];
64
65 activityCards.forEach((card, index) => {
66 const title = card.querySelector('.c-heading')?.textContent?.trim() || '';
67 const description = card.querySelector('.c-paragraph-4')?.textContent?.trim() || '';
68 const pointsText = card.querySelector('.pointLink')?.textContent?.trim() || '';
69 const href = card.getAttribute('href') || '';
70
71 // Skip if already completed or if it's not a valid activity
72 const isCompleted = card.closest('mee-card')?.classList.contains('completed');
73 const isDisabled = card.getAttribute('aria-disabled') === 'true';
74
75 // Only include activities that link to Bing searches or rewards pages
76 if (!isCompleted && !isDisabled && href &&
77 (href.includes('bing.com/search') || href.includes('rewards.bing.com'))) {
78 activities.push({
79 index,
80 title,
81 description,
82 pointsText,
83 href
84 });
85 }
86 });
87
88 return activities;
89 }
90
91 // Add control panel to the page
92 function addControlPanel() {
93 // Check if panel already exists
94 if (document.getElementById('rewards-auto-complete-panel')) {
95 return;
96 }
97
98 const panel = document.createElement('div');
99 panel.id = 'rewards-auto-complete-panel';
100 panel.style.cssText = `
101 position: fixed;
102 top: 20px;
103 right: 20px;
104 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
105 color: white;
106 padding: 20px;
107 border-radius: 12px;
108 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
109 z-index: 10000;
110 min-width: 280px;
111 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
112 `;
113
114 panel.innerHTML = `
115 <div style="margin-bottom: 15px;">
116 <h3 style="margin: 0 0 10px 0; font-size: 18px; font-weight: 600;">🎯 Auto-Complete</h3>
117 <p style="margin: 0; font-size: 13px; opacity: 0.9;">Automatically complete activities</p>
118 </div>
119 <div style="margin-bottom: 15px;">
120 <div id="activity-status" style="font-size: 14px; margin-bottom: 10px;">
121 Ready to start
122 </div>
123 <div id="activity-progress" style="background: rgba(255,255,255,0.2); height: 6px; border-radius: 3px; overflow: hidden;">
124 <div id="progress-bar" style="width: 0%; height: 100%; background: #4ade80; transition: width 0.3s;"></div>
125 </div>
126 </div>
127 <button id="start-auto-complete" style="
128 width: 100%;
129 padding: 12px;
130 background: white;
131 color: #667eea;
132 border: none;
133 border-radius: 8px;
134 font-size: 14px;
135 font-weight: 600;
136 cursor: pointer;
137 transition: all 0.2s;
138 margin-bottom: 8px;
139 ">Start Auto-Complete</button>
140 <button id="stop-auto-complete" style="
141 width: 100%;
142 padding: 12px;
143 background: rgba(255,255,255,0.2);
144 color: white;
145 border: none;
146 border-radius: 8px;
147 font-size: 14px;
148 font-weight: 600;
149 cursor: pointer;
150 transition: all 0.2s;
151 display: none;
152 ">Stop</button>
153 `;
154
155 document.body.appendChild(panel);
156
157 // Add hover effects
158 const startBtn = document.getElementById('start-auto-complete');
159 const stopBtn = document.getElementById('stop-auto-complete');
160
161 startBtn.addEventListener('mouseenter', () => {
162 startBtn.style.transform = 'translateY(-2px)';
163 startBtn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';
164 });
165 startBtn.addEventListener('mouseleave', () => {
166 startBtn.style.transform = 'translateY(0)';
167 startBtn.style.boxShadow = 'none';
168 });
169
170 // Add click handlers
171 startBtn.addEventListener('click', startAutoComplete);
172 stopBtn.addEventListener('click', stopAutoComplete);
173 }
174
175 // Start auto-completing activities
176 async function startAutoComplete() {
177 console.log('Starting auto-complete...');
178
179 const startBtn = document.getElementById('start-auto-complete');
180 const stopBtn = document.getElementById('stop-auto-complete');
181 const statusDiv = document.getElementById('activity-status');
182
183 startBtn.style.display = 'none';
184 stopBtn.style.display = 'block';
185
186 // Set running flag
187 await GM.setValue('autoCompleteRunning', true);
188 await GM.setValue('currentActivityIndex', 0);
189
190 // Get activities
191 const activitiesJson = await GM.getValue('pendingActivities', '[]');
192 const activities = JSON.parse(activitiesJson);
193
194 if (activities.length === 0) {
195 statusDiv.textContent = 'No activities found';
196 startBtn.style.display = 'block';
197 stopBtn.style.display = 'none';
198 return;
199 }
200
201 statusDiv.textContent = `Starting... (${activities.length} activities)`;
202
203 // Start completing activities one by one
204 await completeNextActivity();
205 }
206
207 // Stop auto-completing
208 async function stopAutoComplete() {
209 console.log('Stopping auto-complete...');
210 await GM.setValue('autoCompleteRunning', false);
211
212 const startBtn = document.getElementById('start-auto-complete');
213 const stopBtn = document.getElementById('stop-auto-complete');
214 const statusDiv = document.getElementById('activity-status');
215
216 startBtn.style.display = 'block';
217 stopBtn.style.display = 'none';
218 statusDiv.textContent = 'Stopped';
219 }
220
221 // Complete the next activity in the queue
222 async function completeNextActivity() {
223 const isRunning = await GM.getValue('autoCompleteRunning', false);
224 if (!isRunning) {
225 console.log('Auto-complete stopped');
226 return;
227 }
228
229 const activitiesJson = await GM.getValue('pendingActivities', '[]');
230 const activities = JSON.parse(activitiesJson);
231 const currentIndex = await GM.getValue('currentActivityIndex', 0);
232
233 if (currentIndex >= activities.length) {
234 console.log('All activities completed!');
235 await completeAllActivities();
236 return;
237 }
238
239 const activity = activities[currentIndex];
240 console.log(`Completing activity ${currentIndex + 1}/${activities.length}: ${activity.title}`);
241
242 // Update UI
243 updateProgress(currentIndex, activities.length, activity.title);
244
245 // Open the activity in a new tab
246 await GM.setValue('completingActivity', true);
247 await GM.setValue('activityStartTime', Date.now());
248
249 // Open in background
250 GM.openInTab(activity.href, true);
251
252 // Wait a bit before moving to next activity
253 setTimeout(async () => {
254 await GM.setValue('currentActivityIndex', currentIndex + 1);
255 await completeNextActivity();
256 }, 8000); // Wait 8 seconds between activities
257 }
258
259 // Update progress UI
260 function updateProgress(current, total, activityTitle) {
261 const statusDiv = document.getElementById('activity-status');
262 const progressBar = document.getElementById('progress-bar');
263
264 if (statusDiv && progressBar) {
265 const percentage = ((current + 1) / total) * 100;
266 statusDiv.textContent = `Completing ${current + 1}/${total}: ${activityTitle.substring(0, 30)}...`;
267 progressBar.style.width = `${percentage}%`;
268 }
269 }
270
271 // All activities completed
272 async function completeAllActivities() {
273 const statusDiv = document.getElementById('activity-status');
274 const progressBar = document.getElementById('progress-bar');
275 const startBtn = document.getElementById('start-auto-complete');
276 const stopBtn = document.getElementById('stop-auto-complete');
277
278 if (statusDiv) {
279 statusDiv.textContent = '✅ All activities completed!';
280 }
281 if (progressBar) {
282 progressBar.style.width = '100%';
283 }
284 if (startBtn) {
285 startBtn.style.display = 'block';
286 }
287 if (stopBtn) {
288 stopBtn.style.display = 'none';
289 }
290
291 await GM.setValue('autoCompleteRunning', false);
292 await GM.setValue('currentActivityIndex', 0);
293
294 console.log('All activities completed successfully!');
295 }
296
297 // Handle activity completion on Bing search pages
298 async function handleActivityCompletion() {
299 const isCompleting = await GM.getValue('completingActivity', false);
300
301 if (isCompleting) {
302 console.log('Activity page loaded, waiting for completion...');
303
304 // Wait for the page to load
305 await new Promise(resolve => setTimeout(resolve, 3000));
306
307 // Mark as completed
308 await GM.setValue('completingActivity', false);
309
310 // Close this tab by navigating back (we can't actually close tabs)
311 console.log('Activity completed, you can close this tab');
312 }
313 }
314
315 // Wait for an element to appear
316 function waitForElement(selector, timeout = 10000) {
317 return new Promise((resolve, reject) => {
318 if (document.querySelector(selector)) {
319 return resolve(document.querySelector(selector));
320 }
321
322 const observer = new MutationObserver(debounce(() => {
323 if (document.querySelector(selector)) {
324 observer.disconnect();
325 resolve(document.querySelector(selector));
326 }
327 }, 100));
328
329 observer.observe(document.body, {
330 childList: true,
331 subtree: true
332 });
333
334 setTimeout(() => {
335 observer.disconnect();
336 reject(new Error(`Timeout waiting for element: ${selector}`));
337 }, timeout);
338 });
339 }
340
341 // Start the extension when the page is ready
342 if (document.readyState === 'loading') {
343 document.addEventListener('DOMContentLoaded', init);
344 } else {
345 init();
346 }
347
348})();