MAPSTUDY Comment Bot

Auto-comment & delete in MAPSTUDY

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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); };
})();