MAPSTUDY Comment Bot

Auto-comment & delete in MAPSTUDY

// ==UserScript==
// @name         MAPSTUDY Comment Bot
// @namespace    http://tampermonkey.net/
// @version      0.0.0.0.5
// @description  Auto-comment & delete in MAPSTUDY
// @ Note:If the bot isn't detecting lesson/event properly, try opening DevTools (F12), then refresh the page
// @icon         https://mapstudy.edu.vn/assets/images/logo/logo-64.png
// @author       Quang
// @license      MIT
// @match        https://mapstudy.edu.vn/*
// @grant        GM_xmlhttpRequest
// @connect      api.mapstudy.edu.vn
// ==/UserScript==

(function () {
    'use strict';
    //Remove bottom line if you understand
    alert("If the bot isn't detecting lesson/event properly, try opening DevTools (F12), then refresh the page.");
    //Remove above line if you understand
    let detectedCourseId = null; let type = null; const userId = 410039; const maxTeacherId = 42; const AUTH_TOKEN = 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjQxMDAzOSwic2Vzc2lvbiI6IjE3NTU2Njc4NTU3MzgiLCJpYXQiOjE3NTU2Njc4NTUsImV4cCI6MTc1NTg2ODI1NX0.K72ufWffzyOKzvxXapeI5dLQw2e041nWwl0AB-kjrm8'; const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url, ...rest) { const match = url.match(/\/course\/(\d+)\//); const matchLesson = url.match(/\/lesson\/(\d+)\//); const matchEvent = url.match(/\/event-comment\/(\d+)/); if (match && match[1]) { detectedCourseId = parseInt(match[1], 10); console.log('[BOT] Detected courseId:', detectedCourseId); type = 'course'; } if (matchLesson && matchLesson[1]) { detectedCourseId = parseInt(matchLesson[1], 10); console.log('[BOT] Detected lessonId:', detectedCourseId); type = 'lesson'; } if (matchEvent && matchEvent[1]) { detectedCourseId = parseInt(matchEvent[1], 10); console.log('[BOT] Detected eventId:', detectedCourseId); type = 'event-comment'; } return originalOpen.call(this, method, url, ...rest); }; const openBtn = document.createElement('button'); openBtn.textContent = 'Open'; openBtn.style.cssText = ` position: fixed; display:none; top: 0; left: 0; z-index: 1100; border-radius: 0.5em; padding: 0.5em; background: #000000b5; color: #8dff92; backdrop-filter: blur(0.5em); cursor:grab; pointer-events: all !important; `; openBtn.onclick = () => { container.style.display = 'block'; openBtn.style.display = 'none'; }; const container = document.createElement('div'); container.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.5); color: white; backdrop-filter: blur(0.5em); padding: 1em; border-radius: 1em; text-align: center; user-select: none; z-index:1000; cursor:grab; pointer-events: all !important; `; const closeBtn = document.createElement('button'); closeBtn.textContent = 'Close'; closeBtn.style.cssText = `color: #ffb8b8;`; closeBtn.onclick = () => { container.style.display = 'none'; openBtn.style.display = 'block'; }; const title = document.createElement('h1'); title.textContent = 'MAPSTUDY COMMENT BOT'; const inputComment = document.createElement('input'); inputComment.placeholder = 'Enter comment (e.g. Hello)'; inputComment.style.cssText = ` background: transparent; border: 0.1em solid; display: block; padding: 0.5em; margin: 0.3em auto 1em auto; color: white; `; const inputAmount = document.createElement('input'); inputAmount.placeholder = 'Amount to send (Bugs)'; inputAmount.style.cssText = inputComment.style.cssText; const inputTeacher = document.createElement('input'); inputTeacher.placeholder = 'TeacherId (random default)'; inputTeacher.style.cssText = inputComment.style.cssText; const inputDate = document.createElement('input'); inputDate.type = 'datetime-local'; inputDate.style.cssText = inputComment.style.cssText; const selectTheme = document.createElement('select'); const placeholderOption = document.createElement('option'); placeholderOption.text = 'Select theme'; placeholderOption.value = ''; placeholderOption.disabled = true; placeholderOption.selected = true; selectTheme.appendChild(placeholderOption); const options = ['default', 'gradient-text','COMMENT_UNIVERSE', 'COMMENT_BACK_TO_SCHOOL_2025', 'COMMENT_NATIONAL_DAY_2025', 'COMMENT_BIRTHDAY_2025']; options.forEach(text => { const option = document.createElement('option'); option.value = text; option.text = text; selectTheme.appendChild(option); }); selectTheme.style.cssText = inputComment.style.cssText; selectTheme.style.color = 'red'; const btnContainer = document.createElement('div'); btnContainer.style.cssText = `text-align: center; margin: 0.5em 0;`; const sendBtn = document.createElement('button'); sendBtn.textContent = 'Send now'; sendBtn.style.cssText = `padding: 0.5em; border: 0.1em solid; margin: 0 0.5em;`; const deleteBtn = document.createElement('button'); deleteBtn.textContent = 'Delete All'; deleteBtn.style.cssText = sendBtn.style.cssText; btnContainer.appendChild(sendBtn); btnContainer.appendChild(deleteBtn); container.appendChild(closeBtn); container.appendChild(title); container.appendChild(inputComment); container.appendChild(inputAmount); container.appendChild(inputTeacher); container.appendChild(inputDate); container.appendChild(selectTheme); container.appendChild(btnContainer); document.body.appendChild(container); document.body.appendChild(openBtn); const style = document.createElement('style'); style.innerHTML = ` input::placeholder { color: #ccc; font-style: italic; } `; document.body.appendChild(style); let isDragging = false; let offsetX, offsetY; container.addEventListener("mousedown", (e) => { isDragging = true; offsetX = e.clientX - container.offsetLeft; offsetY = e.clientY - container.offsetTop; container.style.cursor = 'grabbing'; container.style.opacity = 0.7; document.body.style.pointerEvents = 'none'; }); container.addEventListener("mousemove", (e) => { if (isDragging) { container.style.left = (e.clientX - offsetX) + "px"; container.style.top = (e.clientY - offsetY) + "px"; }; }); container.addEventListener("mouseup", () => { isDragging = false; container.style.cursor = 'grab'; container.style.opacity = 1; document.body.style.pointerEvents = 'all'; }); openBtn.addEventListener("mousedown", (e) => { isDragging = true; offsetX = e.clientX - openBtn.offsetLeft; offsetY = e.clientY - openBtn.offsetTop; openBtn.style.cursor = 'grabbing'; openBtn.style.opacity = 0.7; document.body.style.pointerEvents = 'none'; }); openBtn.addEventListener("mousemove", (e) => { if (isDragging) { openBtn.style.left = (e.clientX - offsetX) + "px"; openBtn.style.top = (e.clientY - offsetY) + "px"; }; }); openBtn.addEventListener("mouseup", () => { isDragging = false; openBtn.style.cursor = 'grab'; openBtn.style.opacity = 1; document.body.style.pointerEvents = 'all'; }); function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }; async function sendComments(message, count, countTeach, date, theme) { if (!detectedCourseId) { alert('Course ID not detected. Interact with the course page first.'); return; }; function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }; if(countTeach!== null && countTeach >= 0 && countTeach <= 42){ for (let i = 1; i <= count; i++) { await sendOne(i, countTeach, message,date, theme); await delay(500); }; } else{ for (let i = 1; i <= count; i++) { const teacherId = i % maxTeacherId || maxTeacherId; await sendOne(i, teacherId, message,date, theme); await delay(500); }; }; alert("All comment send!"); location.reload(); } async function sendOne(index, teacherId, message, date, theme, retries = 2, retriedWithTeacher1 = false) { let payload; let data; let contentType; if (type === 'event-comment') { payload = { body: message, createdAt: date, resourceSid: "830fef63a054", parentComment: null, pinOrder: 1, resource: "course", resourceId: detectedCourseId, sid: "830fef63a054", sysId: 1, teacher: null, teacherId: teacherId, theme: theme, updatedAt: date }; data = JSON.stringify(payload); contentType = 'application/json'; } else { payload = { body: message, createdAt: date, resourceSid: "830fef63a054", images: [], parentComment: null, pinOrder: 1, resource: "course", resourceId: detectedCourseId, sid: "830fef63a054", sysId: 1, teacher: null, teacherId: teacherId, theme: theme, updatedAt: date }; data = `data=${encodeURIComponent(JSON.stringify(payload))}`; contentType = 'application/x-www-form-urlencoded'; }; let url = `https://api.mapstudy.edu.vn/v1/${type}/${detectedCourseId}/comment?sysId=1`; if(type === 'event-comment'){ url = `https://api.mapstudy.edu.vn/v1/event-comment/${detectedCourseId}?sysId=1`; }; return new Promise(resolve => { GM_xmlhttpRequest({ method: "POST", url: url, headers: { "Content-Type": contentType, "Authorization": AUTH_TOKEN }, data: data, onload: () => { console.log(`[BOT] Sent comment ${index} with teacherId ${teacherId}`); resolve(true); }, onerror: async (err) => { console.warn(`[BOT] Failed comment ${index} with teacherId ${teacherId}, retries left: ${retries}`, err); if (retries > 0) { await delay(500); await sendOne(index, teacherId, retries - 1, retriedWithTeacher1).then(resolve); } else if (!retriedWithTeacher1 && teacherId !== 1) { console.log(`[BOT] Retrying comment ${index} with teacherId 1`); await delay(500); await sendOne(index, 1, 2, true).then(resolve); } else { resolve(false); }; } }); }); } async function deleteAllComments(type) { if (!detectedCourseId) { alert('Course ID not detected.'); return; }; let url = `https://api.mapstudy.edu.vn/v1/${type}/${detectedCourseId}/comment?sysId=1`; if(type === 'event-comment'){ url = `https://api.mapstudy.edu.vn/v1/event-comment/${detectedCourseId}?sysId=1`; } GM_xmlhttpRequest({ method: "GET", url: url, headers: { "Authorization": AUTH_TOKEN }, onload: function(res) { const json = JSON.parse(res.responseText); const comments = json.data.comments; let myComments = []; if (type === 'event-comment') { myComments = comments.filter(c => c.userId === userId && c.user.username === 'quanghacker' ); } else { myComments = comments.filter(c => c.userId === userId && c.teacherId >= 0 && c.teacherId <= maxTeacherId ); } let deleted = 0; for (const cmt of myComments) { let deleteUrl = ''; if (type === 'event-comment') { deleteUrl = `https://api.mapstudy.edu.vn/v1/event-comment/${cmt.id}?sysId=1`; } else { deleteUrl = `https://api.mapstudy.edu.vn/v1/_/_/comment/${cmt.id}?sysId=1`; } GM_xmlhttpRequest({ method: "DELETE", url: deleteUrl, headers: { "Authorization": AUTH_TOKEN }, onload: () => { console.log(`[BOT] Deleted comment ID ${cmt.id}`); deleted++; if (deleted === myComments.length) { alert(`Deleted ${deleted} comments`); location.reload(); } } }); }; } }); } sendBtn.onclick = async () => { const message = inputComment.value.trim(); const count = parseInt(inputAmount.value.trim(), 10); const countTeach = inputTeacher.value ? parseInt(inputTeacher.value.trim(), 10) : null; const parsedDate = new Date(inputDate.value); const date = !isNaN(parsedDate.getTime()) ? parsedDate.toISOString() : new Date(new Date().getTime() + 7 * 60 * 60 * 1000).toISOString(); const theme = selectTheme.value || 'default'; if (!message || isNaN(count) || count <= 0) { alert('Please enter a valid message and amount.'); return; } if(countTeach !== null && countTeach >= 0 && countTeach <= 42){ await sendComments(message, count, countTeach , date, theme); } else{ await sendComments(message, count, null, date, theme); } }; deleteBtn.onclick = () => { deleteAllComments(type); };
})();