您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Полный набор для 100% посещаемости дистанционных лекций
- // ==UserScript==
- // @name Moodle AutoPilot
- // @namespace https://t.me/johannmosin
- // @version 1.0.5
- // @description Полный набор для 100% посещаемости дистанционных лекций
- // @author Johann Mosin
- // @match https://edu.vsu.ru/mod/bigbluebuttonbn/view.php*
- // @match https://*.edu.vsu.ru/html5client/*
- // @match https://www.cs.vsu.ru/brs/att_marks_report_student/*
- // @match https://edu.vsu.ru/mod/attendance/*
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_addStyle
- // @license MIT
- // @icon https://upload.wikimedia.org/wikipedia/commons/thumb/9/96/Glagolitic_ljudi.svg/47px-Glagolitic_ljudi.svg.png
- // ==/UserScript==
- (function() {
- 'use strict';
- // --- Settings Keys ---
- const SETTINGS_KEYS = {
- autoConnect: 'autoConnectEnabled',
- autoHello: 'autoHelloEnabled',
- autoLeave: 'autoLeaveEnabled',
- autoBRS: 'autobrsEnabled',
- autoAttendance: 'autoAttendanceEnabled'
- };
- // --- Styles ---
- GM_addStyle(`
- .moodle-autotool-button {
- cursor: pointer;
- border-radius: 7px;
- padding: 8px 15px !important;
- transition: background 0.3s;
- color: black !important;
- border: none !important;
- margin: 5px;
- text-align: center;
- font-size: 1rem;
- line-height: 1.5;
- }
- .moodle-autotool-button:hover {
- opacity: 0.9;
- }
- .moodle-autotool-button.off {
- background: rgba(255, 193, 7, 0.25);
- }
- .moodle-autotool-button.on {
- background: rgba(0, 128, 0, 0.25) !important;
- }
- .autoTool-controls {
- display: flex;
- margin: 10px 0;
- flex-wrap: wrap;
- }
- .autoTool-button {
- width: 180px;
- }
- #toggleBRS {
- width: 150px;
- margin-bottom: 2px;
- }
- #toggleAttendance {
- margin-top: 2px;
- margin-bottom: 2px;
- }
- .moodle-autotool-nav-item {
- display: flex;
- align-items: center;
- }
- `);
- function createToggleButton(id, textPrefix, settingKey, initialState = false, onClickCallback = null) {
- const button = document.createElement('button');
- button.id = id;
- button.className = `moodle-autotool-button ${id === 'autoConnectBtn' || id === 'autoHelloBtn' || id === 'autoLeaveBtn' ? 'autoTool-button' : ''}`;
- button.dataset.settingKey = settingKey;
- const updateButtonState = (btn, enabled) => {
- btn.textContent = `${textPrefix}: ${enabled ? 'ВКЛ' : 'ВЫКЛ'}`;
- if (enabled) {
- btn.classList.remove('off');
- btn.classList.add('on');
- } else {
- btn.classList.remove('on');
- btn.classList.add('off');
- }
- };
- let isEnabled = GM_getValue(settingKey, initialState);
- updateButtonState(button, isEnabled);
- button.addEventListener('click', (e) => {
- e.preventDefault();
- isEnabled = !isEnabled;
- GM_setValue(settingKey, isEnabled);
- updateButtonState(button, isEnabled);
- if (onClickCallback) {
- onClickCallback(isEnabled);
- }
- });
- return button;
- }
- const AutoTools = {
- settings: {
- autoConnect: GM_getValue(SETTINGS_KEYS.autoConnect, false),
- autoHello: GM_getValue(SETTINGS_KEYS.autoHello, false),
- autoLeave: GM_getValue(SETTINGS_KEYS.autoLeave, false)
- },
- intervals: {
- connect: null,
- hello: null,
- leave: null,
- bbbButtonCheck: null
- },
- timeouts: {
- reload: null
- },
- flags: {
- connectCheckStarted: false,
- helloMessageSent: false
- },
- initUI(isConnectPage, isConferencePage) {
- if (document.querySelector('.autoTool-controls')) return;
- const controlPanel = document.createElement('div');
- controlPanel.className = 'autoTool-controls';
- const createAndAppend = (id, text, key, callback) => {
- const btn = createToggleButton(id, text, key, this.settings[key.replace('Enabled','')], callback);
- controlPanel.appendChild(btn);
- };
- if (isConnectPage) {
- createAndAppend('autoConnectBtn', 'AutoConnect', SETTINGS_KEYS.autoConnect, (enabled) => {
- this.settings.autoConnect = enabled;
- enabled ? this.startAutoConnect() : this.stopAutoConnect();
- });
- }
- if (isConnectPage || isConferencePage) {
- createAndAppend('autoHelloBtn', 'AutoHello', SETTINGS_KEYS.autoHello, (enabled) => {
- this.settings.autoHello = enabled;
- enabled ? this.startAutoHello() : this.stopAutoHello();
- });
- createAndAppend('autoLeaveBtn', 'AutoLeave', SETTINGS_KEYS.autoLeave, (enabled) => {
- this.settings.autoLeave = enabled;
- enabled ? this.startAutoLeave() : this.stopAutoLeave();
- });
- }
- if (controlPanel.hasChildNodes()) {
- if (isConnectPage) {
- const targetElement = document.querySelector('[class*="custom-select"]') || document.querySelector('#region-main') || document.body;
- if(targetElement === document.body) targetElement.insertBefore(controlPanel, targetElement.firstChild);
- else targetElement.parentNode.insertBefore(controlPanel, targetElement.nextSibling);
- } else if (isConferencePage) {
- const userListContent = document.querySelector('[data-test="userList"]');
- const chatInputArea = document.querySelector('#message-input')?.parentNode;
- const targetParent = userListContent?.parentNode || chatInputArea || document.body;
- const referenceNode = userListContent || (chatInputArea ? chatInputArea.firstChild : null) || document.body.firstChild;
- const observer = new MutationObserver((mutations, obs) => {
- let inserted = false;
- const userList = document.querySelector('[data-test="userList"]');
- const chatInput = document.querySelector('#message-input')?.parentNode;
- if (userList) {
- userList.parentNode.insertBefore(controlPanel, userList);
- inserted = true;
- } else if (chatInput) {
- chatInput.parentNode.insertBefore(controlPanel, chatInput);
- inserted = true;
- }
- if (inserted) {
- obs.disconnect();
- }
- });
- if (userListContent) {
- userListContent.parentNode.insertBefore(controlPanel, userListContent);
- } else if (chatInputArea) {
- chatInputArea.parentNode.insertBefore(controlPanel, chatInputArea);
- } else {
- observer.observe(document.body, { childList: true, subtree: true });
- setTimeout(() => {
- observer.disconnect();
- if (!document.querySelector('.autoTool-controls')) {
- document.body.insertBefore(controlPanel, document.body.firstChild);
- }
- }, 15000);
- }
- }
- }
- },
- startAutoConnect() {
- if (!this.flags.connectCheckStarted) {
- this.flags.connectCheckStarted = true;
- this.timeouts.reload = setTimeout(() => {
- this.checkForSessionLink();
- }, 10000);
- }
- },
- stopAutoConnect() {
- clearInterval(this.intervals.connect);
- clearTimeout(this.timeouts.reload);
- this.intervals.connect = null;
- this.timeouts.reload = null;
- this.flags.connectCheckStarted = false;
- },
- resetReloadTimeout() {
- clearTimeout(this.timeouts.reload);
- this.timeouts.reload = setTimeout(() => {
- if (this.settings.autoConnect) {
- location.reload();
- }
- }, 10000);
- },
- checkForSessionLink() {
- const sessionLink = Array.from(document.querySelectorAll('a')).find(a =>
- a.textContent.includes("Подключиться к сеансу"));
- if (sessionLink && sessionLink.href) {
- window.open(sessionLink.href, '_blank');
- this.stopAutoConnect();
- } else {
- this.resetReloadTimeout();
- }
- },
- handleHtml5ClientPage() {
- this.intervals.bbbButtonCheck = setInterval(() => {
- const joinButton = document.querySelector('button[aria-label="Только слушать"]');
- if (joinButton) {
- joinButton.click();
- }
- const connectButton = document.querySelector('button[aria-label="Проиграть звук"]');
- if (connectButton) {
- connectButton.click();
- clearInterval(this.intervals.bbbButtonCheck);
- this.intervals.bbbButtonCheck = null;
- }
- }, 2000);
- setTimeout(() => {
- if (this.intervals.bbbButtonCheck) {
- clearInterval(this.intervals.bbbButtonCheck);
- this.intervals.bbbButtonCheck = null;
- }
- }, 60000);
- },
- startAutoHello() {
- if (this.intervals.hello) return;
- this.flags.helloMessageSent = false;
- this.intervals.hello = setInterval(() => {
- if (this.flags.helloMessageSent) {
- this.stopAutoHello();
- return;
- }
- const greetings = ["здравствуйте", "здравстуйте", "добрый день", "доброе утро"];
- const pageText = document.body.innerText.toLowerCase();
- if (greetings.some(greet => pageText.includes(greet))) {
- const messageInput = document.querySelector('#message-input');
- const sendButton = document.querySelector('button[aria-label="Отправить сообщение"]');
- if (messageInput && sendButton) {
- const message = "Здравствуйте";
- let reactProps = null;
- try { reactProps = this.findReactProps(messageInput); } catch (e) {}
- if (reactProps && reactProps.onChange) {
- const syntheticEvent = { target: { value: message }, currentTarget: { value: message } };
- reactProps.onChange(syntheticEvent);
- sendButton.click();
- this.flags.helloMessageSent = true;
- } else {
- messageInput.value = message;
- messageInput.dispatchEvent(new Event('input', { bubbles: true }));
- setTimeout(() => {
- sendButton.click();
- this.flags.helloMessageSent = true;
- }, 100);
- }
- }
- }
- }, 2000);
- },
- stopAutoHello() {
- clearInterval(this.intervals.hello);
- this.intervals.hello = null;
- },
- findReactProps(dom) {
- for (const key in dom) {
- if (key.startsWith('__reactInternalInstance$') || key.startsWith('__reactFiber$')) {
- let fiber = dom[key];
- if (fiber.return) {
- let current = fiber.return;
- while(current) {
- if (current.stateNode && current.stateNode.props) return current.stateNode.props;
- current = current.return;
- }
- }
- if (fiber._currentElement && fiber._currentElement._owner && fiber._currentElement._owner._instance) {
- return fiber._currentElement._owner._instance.props;
- }
- }
- }
- return null;
- },
- startAutoLeave() {
- if (this.intervals.leave) return;
- this.intervals.leave = setInterval(() => {
- this.checkLeaveText();
- }, 5000);
- },
- stopAutoLeave() {
- clearInterval(this.intervals.leave);
- this.intervals.leave = null;
- },
- disablePopups() {
- var beforeScript = document.createElement('script');
- beforeScript.textContent = `
- Window.prototype.addEventListener2 = Window.prototype.addEventListener;
- Window.prototype.addEventListener = function(type, listener, useCapture) {
- if (type != "beforeunload") {
- addEventListener2(type, listener, useCapture);
- }
- }
- `;
- (document.head||document.documentElement).insertBefore(beforeScript, (document.head||document.documentElement).firstChild);
- beforeScript.onload = function() {
- this.parentNode.removeChild(this);
- };
- var afterScript = document.createElement('script');
- afterScript.textContent = `
- function letmeout() {
- var all = document.getElementsByTagName("*");
- for (var i=0, max=all.length; i < max; i++) {
- if(all[i].getAttribute("onbeforeunload")) {
- all[i].setAttribute("onbeforeunload", null);
- }
- }
- window.onbeforeunload = null;
- }
- letmeout();
- setInterval(letmeout, 500);
- `;
- (document.head||document.documentElement).appendChild(afterScript);
- afterScript.onload = function() {
- this.parentNode.removeChild(this);
- };
- },
- checkLeaveText() {
- var text = document.body.innerText.toLowerCase();
- if (text.includes('до свидания') || text.includes('досвидания')) {
- this.disablePopups();
- window.close();
- }
- },
- run(isConnectPage, isConferencePage) {
- this.initUI(isConnectPage, isConferencePage);
- if (isConnectPage && this.settings.autoConnect) {
- this.startAutoConnect();
- }
- if (isConferencePage) {
- this.handleHtml5ClientPage();
- if (this.settings.autoHello) this.startAutoHello();
- if (this.settings.autoLeave) this.startAutoLeave();
- }
- }
- };
- const AutoBRS = {
- settings: {
- enabled: GM_getValue(SETTINGS_KEYS.autoBRS, false)
- },
- intervals: {
- check: null
- },
- timeouts: {
- reload: null
- },
- buttonId: 'modalCurrentLessonForMarkButtonOK',
- toggleButtonId: 'toggleBRS',
- init() {
- this.insertToggleButton();
- if (this.settings.enabled) {
- this.start();
- }
- },
- insertToggleButton() {
- const navbar = document.querySelector('ul.navbar-nav.nav-tabs');
- if (!navbar || document.getElementById(this.toggleButtonId)) return;
- const navItem = document.createElement('li');
- navItem.className = 'nav-item moodle-autotool-nav-item';
- const toggleBtn = createToggleButton(
- this.toggleButtonId,
- 'AutoBRS',
- SETTINGS_KEYS.autoBRS,
- this.settings.enabled,
- (enabled) => {
- this.settings.enabled = enabled;
- enabled ? this.start() : this.stop();
- }
- );
- toggleBtn.classList.add('nav-link');
- navItem.appendChild(toggleBtn);
- navbar.appendChild(navItem);
- },
- checkAndClick() {
- const button = document.getElementById(this.buttonId);
- if (button) {
- button.click();
- }
- },
- start() {
- if (this.intervals.check) return;
- this.settings.enabled = true;
- const toggleBtn = document.getElementById(this.toggleButtonId);
- if (toggleBtn && !toggleBtn.classList.contains('on')) {
- toggleBtn.classList.remove('off');
- toggleBtn.classList.add('on');
- toggleBtn.textContent = 'AutoBRS: ВКЛ';
- }
- this.intervals.check = setInterval(() => this.checkAndClick(), 1000);
- this.timeouts.reload = setTimeout(() => {
- if (this.settings.enabled && !document.getElementById(this.buttonId)) {
- location.reload();
- }
- }, 10000);
- },
- stop() {
- clearInterval(this.intervals.check);
- clearTimeout(this.timeouts.reload);
- this.intervals.check = null;
- this.timeouts.reload = null;
- this.settings.enabled = false;
- const toggleBtn = document.getElementById(this.toggleButtonId);
- if (toggleBtn && !toggleBtn.classList.contains('off')) {
- toggleBtn.classList.remove('on');
- toggleBtn.classList.add('off');
- toggleBtn.textContent = 'AutoBRS: ВЫКЛ';
- }
- }
- };
- const AutoFAC = {
- interval: null,
- buttonSelector: '[aria-label="Проверка"]',
- init() {
- this.start();
- },
- autoClick() {
- const facButton = document.querySelector(this.buttonSelector);
- if (facButton) {
- facButton.click();
- }
- },
- start() {
- if (this.interval) return;
- this.interval = setInterval(() => this.autoClick(), 5000);
- },
- stop() {
- if (this.interval) {
- clearInterval(this.interval);
- this.interval = null;
- }
- }
- };
- const AutoAttendance = {
- settings: {
- enabled: GM_getValue(SETTINGS_KEYS.autoAttendance, false)
- },
- intervals: {
- check: null
- },
- timeouts: {
- reload: null
- },
- toggleButtonId: 'toggleAttendance',
- init() {
- this.insertToggleButton();
- if (this.settings.enabled) {
- this.start();
- }
- },
- insertToggleButton() {
- const navBar = document.querySelector('ul.nav.nav-tabs');
- if (!navBar || document.getElementById(this.toggleButtonId)) return;
- const navItem = document.createElement('li');
- navItem.className = 'nav-item moodle-autotool-nav-item';
- const toggleBtn = createToggleButton(
- this.toggleButtonId,
- 'AutoAttendance',
- SETTINGS_KEYS.autoAttendance,
- this.settings.enabled,
- (enabled) => {
- this.settings.enabled = enabled;
- enabled ? this.start() : this.stop();
- }
- );
- toggleBtn.classList.add('nav-link');
- navItem.appendChild(toggleBtn);
- navBar.appendChild(navItem);
- },
- processPage() {
- const submitButton = document.querySelector('input[type="submit"][value="Сохранить"].btn.btn-primary');
- if (submitButton) {
- const radioInput = document.querySelector('input[type="radio"].form-check-input[name="status"]');
- if (radioInput) {
- radioInput.click();
- submitButton.click();
- this.stop();
- return true;
- }
- } else {
- const attendanceTd = Array.from(document.querySelectorAll('td')).find(td => td.textContent.includes("Отметить свое присутствие"));
- if (attendanceTd) {
- const attendanceLink = attendanceTd.querySelector('a');
- if (attendanceLink) {
- window.location.href = attendanceLink.href;
- this.stop();
- return true;
- }
- } else {
- if (this.settings.enabled && !this.timeouts.reload) {
- this.timeouts.reload = setTimeout(() => {
- location.reload();
- }, 10000);
- }
- }
- }
- return false;
- },
- start() {
- if (this.intervals.check) return;
- this.settings.enabled = true;
- const toggleBtn = document.getElementById(this.toggleButtonId);
- if (toggleBtn && !toggleBtn.classList.contains('on')) {
- toggleBtn.classList.remove('off');
- toggleBtn.classList.add('on');
- toggleBtn.textContent = 'AutoAttendance: ВКЛ';
- }
- if (this.processPage()) return;
- this.intervals.check = setInterval(() => {
- this.processPage();
- }, 1000);
- },
- stop() {
- clearInterval(this.intervals.check);
- clearTimeout(this.timeouts.reload);
- this.intervals.check = null;
- this.timeouts.reload = null;
- this.settings.enabled = false;
- const toggleBtn = document.getElementById(this.toggleButtonId);
- if (toggleBtn && !toggleBtn.classList.contains('off')) {
- toggleBtn.classList.remove('on');
- toggleBtn.classList.add('off');
- toggleBtn.textContent = 'AutoAttendance: ВЫКЛ';
- }
- }
- };
- function run() {
- const href = window.location.href;
- if (href.includes('/mod/bigbluebuttonbn/view.php')) {
- AutoTools.run(true, false);
- } else if (href.includes('/html5client/')) {
- AutoTools.run(false, true);
- AutoFAC.init();
- }
- else if (href.includes('/brs/att_marks_report_student/')) {
- AutoBRS.init();
- }
- else if (href.includes('/mod/attendance/')) {
- AutoAttendance.init();
- }
- }
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', run);
- } else {
- run();
- }
- })();