MAPSTUDY Comment Bot

Auto-comment & delete in MAPSTUDY

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

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