Advertisement
Lux-Ferre

DH2MB-M91

Nov 3rd, 2020
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         DH2 mod bot
  3. // @namespace    FileFace
  4. // @description  A moderator bot to assist mod team for Diamond Hunt online
  5. // @version      0.13
  6. // @author       you, and ktnn
  7. // @include      *.diamondhunt.co/*
  8. // @match        dh2.diamondhunt.co
  9. // @grant        none
  10. // ==/UserScript==
  11.  
  12.  
  13. /*
  14. CHANGELOG 0.13:
  15. - Fixed typo in new mute command
  16. - Added a list of words that instantly mute
  17.  
  18. CHANGELOG 0.12a:
  19. - Added Fireblade as mod
  20. - Updated to new smute command
  21. - Fixed a couple of minor syntax errors
  22.  
  23. CHANGELOG 0.12:
  24. - Added an option to enable/disable the autoMute feature. It is disabled by default and can be found in the customizable section.
  25. You should only enable it when chat really needs strictly moderation.
  26.  
  27. CHANGELOG 0.11a:
  28. - small fixed syntax
  29.  
  30. CHANGELOG 0.11:
  31.  
  32. - Move down some important paremeters that have been tested carefully, from customizable to non-customizable section
  33. - Min pts of excessive cursing words is from 7 to 8
  34. - Added an insult directly word into heavy list
  35. - Raise the trigger lines of flooding to 1 line of each.
  36. - Added level and flooding lines of flooder to the console log, for QA moderation
  37.  
  38.  
  39. END of Changelog
  40. */
  41.  
  42. // INSTRUCTION: using the script with tampermonkey. Turn on your browser's console to see the detector's log.
  43. (function (){
  44.     'use strict';
  45.     console.info('DH2 Mod bot is up and running');
  46.  
  47.     // Customizable parameters section
  48.     var modList = ['smitty', 'eegos', 'ted120', 'martlands', 'mattyboy', 'luxferre', 'ktnn', 'zack', 'morgan91']; // mods are unaffected by the script
  49.     var lightWords = ['dick', 'asshole', 'arsehole', 'arse', 'ass', 'bitch', 'twat', 'fuck', 'fucked', 'fucking', 'fuckers', 'fucker',
  50.                       'wanker', 'goddamn', 'bastard', 'choad', 'kys']; // add 1 pts
  51.     var heavyWords = ['fuck you', 'niger', 'anal', 'cunt', 'whore', 'motherfucker',
  52.                       'slut', 'douch', 'cocksucker']; // add 2 pts
  53.     var instaWords = ['nigger', 'nigga', 'fag', 'faggot', 'nibba', 'fags', 'niggers', 'faggots'];
  54.  
  55.     var autoMute = true; // WARNING: this feature is disabled by default, change it as true to enable
  56.     // It will inform/alert about the situation instead of automatically mute.
  57.     // DO NOT turn it on unless the chat needs to be moderated strictly
  58.     var minLevelAlt = 30; // min global level to detect an alt, the detector will be more sensisitve
  59.     var muteTimeSpam = 1; // mute duration for spamming/flooding
  60.     var muteTimeSwear = 1; // mute duration for excessive swearing/cursing
  61.     var notifKeywords = ['#mods', '#mod', 'morgan', 'morgan91', '@mods', '@morgan'];
  62.  
  63.  
  64.     // Not customizable anymore!! DO NOT change it if you have no idea how they work, or without carefully testing
  65.     var check = (autoMute == false) ? 'disabled' : 'enabled';
  66.     console.info('Automute feature is '+check);
  67.     var minSpamWords = 6; // min duplicate words in a single chat line, to be considered as a spam
  68.     var resetTime = 15000; // time to reset a check phase of spamming and swearing, the bigger it is, the more sensitive to trigger
  69.     var minPoints = 8; // min points of excessive cursing/swearing to be triggered
  70.     var badWordsList = lightWords.concat(heavyWords).concat(instaWords);
  71.     var swearUserObj = {name:'', msg:[], pts:0};
  72.     var spamUserObj = {name:'', number:0};
  73.     var floodCount = {};
  74.     var countDupWords = {};
  75.     var notifSpam = ' just got muted for spamming. Go review it.'; // need to review to see if the mute was justified by the bot. If not, unmute manually
  76.     var notifFlood = ' just got muted for flooding. Go review it.';
  77.     var notifSwear = ' just got muted for cursing. Go review it.';
  78.     var notifGeneral = ' is calling out the mod team. Or there may be issues in chat.';
  79.     var notifAlertSpam = ' seems to be spamming. Go check it';
  80.     var notifAlertFlood = ' seems to be flooding. Go check it';
  81.     var notifAlertSwear = ' seems to be swearing. Go check it';
  82.     //var resultDup = [];
  83.     //var allMsgArray = [];
  84.  
  85.     var target = document.getElementById('div-chat-area');
  86.     var textBox = document.getElementById("chat-input-text");
  87.     var chatSendButton = document.getElementById("chat-send-button");
  88.  
  89.  
  90.     // Utility functions
  91.  
  92.     // check if messageAuthor is in mod list, if yes return true
  93.     function checkModsException(username, list){
  94.         var result = false;
  95.         if (username.length <= 20){
  96.             for (var i = 0; i < list.length; i++){
  97.                 if (username != list[i]){
  98.                     continue;
  99.                 } else if (username == list[i]){
  100.                     result = true;
  101.                 }
  102.             }
  103.         } else{
  104.             result = undefined;
  105.         }
  106.         return result;
  107.     }
  108.     function resetFlood(){
  109.         floodCount = {};
  110.         //countDupWords = {};
  111.     }
  112.     setInterval(resetFlood, 2000); // time to reset a flooding check
  113.  
  114.     function resetCheck(){
  115.         countDupWords = {};
  116.         spamUserObj.name = '';
  117.         spamUserObj.number = 0;
  118.         swearUserObj.pts = 0;
  119.         swearUserObj.name = '';
  120.     }
  121.     var timer = setInterval(resetCheck, resetTime);
  122.  
  123.     /*
  124. function findDuplicates(data) {
  125.     data.forEach(function(element, index) {
  126.         // Find if there is a duplicate or not
  127.         if (data.indexOf(element, index + 1) > -1) {
  128.             // Find if the element is already in the result array or not
  129.             if (resultDup.indexOf(element) === -1) {
  130.                 resultDup.push(element);
  131.             }
  132.         }
  133.     });
  134.     return resultDup;
  135. }
  136.  
  137. function random(num1, num2){
  138.     return Math.floor(Math.random() * (num2-num1 + 1) + num1);
  139. }
  140.  
  141. function swearWarning (username){
  142.     var rdm = random(1, 3);
  143.     var warning = '';
  144.     var text = textBox.value = '/pm ' + username + ' ';
  145.     var text1 = 'You could use nicer words';
  146.     var text2 = 'Excuse me, would you please keep the language G rated?';
  147.     var text3 = 'You are going to violate the chat rule by excessive swearings';
  148.     if (rdm === 1){
  149.         warning = text.concat(text1);
  150.     } else if (rdm === 2){
  151.         warning = text.concat(text2);
  152.     } else if (rdm === 3){
  153.         warning = text.concat(text3);
  154.     }
  155.     return warning;
  156. }
  157.  
  158. function spamWarning (username){
  159.     var rdm = random(1, 3);
  160.     var warning = '';
  161.     var text = textBox.value = '/pm ' + username + ' ';
  162.     var text1 = 'Hey, do not spam';
  163.     var text2 = 'Would you please stop spamming?';
  164.     var text3 = 'You are going to violate the chat rule by spamming';
  165.     if (rdm === 1){
  166.         warning = text.concat(text1);
  167.     } else if (rdm === 2){
  168.         warning = text.concat(text2);
  169.     } else if (rdm === 3){
  170.         warning = text.concat(text3);
  171.     }
  172.     return warning;
  173. }
  174. */
  175.  
  176.     // check if a string contains certain words, e.g.: 'lovely' or 'aslove' can match 'love'
  177.     function checkWords(string, array){
  178.         for (var i = 0; i < array.length; i++){
  179.             if (string.includes(array[i])){
  180.                 return true;
  181.             }
  182.         }
  183.     }
  184.     // compare two arrays, strict word to word, e.g.: only 'love' can match 'love'
  185.     function scanWords(array, words) {
  186.         for (var i =0; i< array.length; i++){
  187.             for (var u =0; u< words.length;u++){
  188.                 if (array[i] === words[u]){
  189.                     return true;
  190.                 }
  191.             }
  192.         }
  193.     }
  194.     // compare bad words list to msg content, light words add 1 pts, heavy words add 2 pts, insta words add 8 pts
  195.     function countWords(array, words) {
  196.         var count = 0;
  197.         for(var i = 0; i < array.length; i++)
  198.         {
  199.             for (var u = 0; u < words.length; u++){
  200.                 if (array[i] == lightWords[u]) {
  201.                     count++;
  202.                 }else if (array[i] == heavyWords[u]) {
  203.                     count +=2;
  204.                 }else if (array[i] == instaWords[u]) {
  205.                     count +=8;}
  206.             }
  207.         }
  208.         return count;
  209.     }
  210.  
  211.     // remove link from message, unless the link length is too long
  212.     function removeLink(array){
  213.         for (var i = 0; i < array.length;i++){
  214.             if (array[i].indexOf('https://') !== -1 && array[i].length < 150){
  215.                 array.splice(i, 1);
  216.             }else if (array[i].indexOf('www.') !== -1 && array[i].length < 150){
  217.                 array.splice(i, 1);
  218.             }else if (array[i].indexOf('.com') !== -1 && array[i].length < 150){
  219.                 array.splice(i, 1);
  220.             }
  221.         }
  222.         return array;
  223.     }
  224.     function getLongestCharacters(array){
  225.         var longest = 0;
  226.         for (var i = 0; i < array.length;i++){
  227.             if (longest < array[i].length){
  228.                 longest = array[i].length;
  229.             }
  230.         }
  231.         return longest;
  232.     }
  233.  
  234.     function getKeyByValue(object, value) {
  235.         return Object.keys(object).find(key => object[key] === value);
  236.     }
  237.  
  238.     function sendChat(text){
  239.         cBytes('CHAT='+text);
  240.     }
  241.  
  242.     function mutePlayer(name, duration, reason){ // reason: 0 = other, 1 = flood/spam, 4 = excessively swear/curse
  243.         if (reason==1) {
  244.             cBytes('SMUTE='+name+'~'+duration+'~'+'Modbot: Flood/Spam'+'~'+0);}
  245.         else if (reason==4) {
  246.             cBytes('SMUTE='+name+'~'+duration+'~'+'Modbot: Excessive offensive language'+'~'+0);}
  247.         else {
  248.             cBytes('SMUTE='+name+'~'+duration+'~'+'Modbot: Other'+'~'+0);}
  249.     }
  250.     // notification
  251.     function notif(username, message){
  252.  
  253.         if (!window.Notification) {
  254.             alert("Sorry, Notification is not supported in this Browser!");
  255.         } else {
  256.             if (Notification.permission === 'default') {
  257.                 Notification.requestPermission(function(p) {
  258.                     if (p === 'denied') {
  259.                         alert('You have denied Notification'); }
  260.                     else {
  261.                         var notify = new Notification('Beep Boop, Beep Bop', {
  262.                             body: username + message,
  263.                             requireInteraction: true,
  264.                             icon: 'images/lumberjack.png'
  265.                         });
  266.                     }
  267.                 });
  268.             } else {
  269.                 var notify = new Notification('Beep Boop, Beep Bop', {
  270.                     body: username + message,
  271.                     icon: 'images/lumberjack.png',
  272.                     requireInteraction: true
  273.                 });
  274.             }
  275.         }
  276.     }
  277.  
  278.     function analyzeMutation(mutation){
  279.         //console.log(mutation.addedNodes.length);
  280.         if(mutation.addedNodes.length == 2){
  281.             var isPM = false;
  282.             var messageAuthor = "";
  283.             var messageText = "";
  284.             var messageContent = "";
  285.             var globalLevel = -1;
  286.  
  287.             messageText = mutation.addedNodes[1].innerText; //Retrieve the contents of the message. This is everything included the name and level
  288.             messageContent = messageText.slice(messageText.indexOf(":")+2); // getting the string after the colon and space
  289.             messageContent = messageContent.toLowerCase(); // all msg content to lower
  290.  
  291.             if(messageText.startsWith("[")){ // All PMs start with an opening square bracket
  292.                 isPM = true;
  293.             }
  294.  
  295.             if(isPM){ //Retrieve the username based on if it's a PM or not, since they are structured differently.
  296.                 messageAuthor = messageText.slice(9, messageText.indexOf("]")); // [PM from username <= index 9 at username
  297.             }else{
  298.                 messageAuthor = messageText.slice(0, messageText.indexOf("(")-1); // getting the string before the opening bracket and the space that appears before it
  299.             }
  300.  
  301.             if(!isPM){ //Global level is only shown in normal chat
  302.                 globalLevel = parseInt(messageText.slice(messageText.indexOf("(")+1, messageText.indexOf(")")));
  303.             }
  304.             return {
  305.                 isPM: isPM,
  306.                 messageAuthor: messageAuthor,
  307.                 messageContent: messageContent,
  308.                 globalLevel: globalLevel,
  309.             }; // return an object to make it easy to access the variable
  310.  
  311.         }else{
  312.             return false;
  313.         }
  314.     }
  315.  
  316.     var observer = new MutationObserver(function(mutations) {
  317.         mutations.forEach(function(mutation){
  318.             var messageInfo = analyzeMutation(mutation); // each msg, including name, lvl, content, is pm or not
  319.             if (!messageInfo){
  320.                 return;
  321.             }
  322.  
  323.             //var sendMessage = true;
  324.             if (websocketReady === true && !checkModsException(username, modList)){
  325.                 observer.disconnect();
  326.                 console.info('DH2 Mod bot is disabled. Current user is not a moderator');
  327.             }
  328.             var msg = messageInfo.messageContent;
  329.             var messageArray = msg.split(" "); // split into each word in a msg content, by a space between
  330.             var user = messageInfo.messageAuthor;
  331.             var isPM = messageInfo.isPM;
  332.             var level = messageInfo.globalLevel;
  333.  
  334.             //checkModsException(user, modList);
  335.             //console.log(spamUserObj);
  336.             //console.log(floodCount[user]);
  337.             //console.log(swearUserObj);
  338.             //console.log(countDupWords);
  339.  
  340.             // MODERATING SECTION
  341.             if(!isPM && !checkModsException(user, modList)){ //not a pm and user is not a mod
  342.                 if (scanWords(messageArray, notifKeywords)){
  343.                     notif(user, notifGeneral);
  344.                 }
  345.                 // SPAM DETECTOR:
  346.                 messageArray.forEach(function(i) {
  347.                     countDupWords[i] = (countDupWords[i] || 0) + 1; // count words of msg and store the duplicate times in countDupWords
  348.                 });
  349.                 var countValueObj = Object.values(countDupWords);
  350.                 var max = Math.max(...countValueObj); // most repetitive word
  351.                 var getWord = getKeyByValue(countDupWords, max);
  352.                 var spamTrigger = (getLongestCharacters(removeLink(messageArray)) > 30 || // word has more than 30 chars and not an URL is spam
  353.                                    (max >= minSpamWords) || // repeat word more than minSpamWords
  354.                                    (max >= 3 && max < minSpamWords && getWord.length >= 6)); // lesser repeat word but the length is longer than usual
  355.                 countDupWords = {};
  356.  
  357.                 if (spamTrigger){// can always view timestamp in console: SETTING > SHOW TIMESTAMPS
  358.                     //console.log('spam '+user);
  359.  
  360.                     if(spamUserObj.name == ''){
  361.                         spamUserObj.name = user;
  362.                     }
  363.                     if(spamUserObj.name === user){
  364.                         ++spamUserObj.number;
  365.                         clearInterval(timer); // reset timer between the check whenever the marked user fouls
  366.                         timer = setInterval(resetCheck, resetTime);
  367.                     }
  368.                     if(spamUserObj.number >= 3 || ((spamUserObj.number === 2) && (level < minLevelAlt))){
  369.                         //if(spamUserObj.number >= 3){
  370.                         if (autoMute == false){
  371.                             console.log(user + ' was spamming. Last msg is "'+msg+'"');
  372.                             notif(user, notifAlertSpam);
  373.                             resetCheck();
  374.                         } else if (autoMute == true){
  375.                             mutePlayer(user, muteTimeSpam, 1);
  376.                             console.log(user + ' has been muted for spamming for '+muteTimeSpam+' hrs. Last msg is "'+msg+'"');
  377.                             notif(user, notifSpam);
  378.                             resetCheck();
  379.                         }
  380.                     }
  381.                 }
  382.  
  383.                 // FLOODING DETECTOR: check if one is typing too many lines in short period of time
  384.                 if(floodCount[user]){
  385.                     //console.log('flood user is '+user+', line is ' +floodCount[user]);
  386.                     if(++floodCount[user] >= 4 || (++floodCount[user] === 3 && level < minLevelAlt)){
  387.                         //if(++floodCount[user] >= 4){
  388.                         if (autoMute == false){
  389.                             console.log(user + ' was flooding. Last msg is "'+msg+'"');
  390.                             notif(user, notifAlertFlood);
  391.                             floodCount = {};
  392.                         } else if (autoMute == true){
  393.                             mutePlayer(user, muteTimeSpam, 1);
  394.                             console.log(user + ' has been muted for flooding for '+muteTimeSpam+' hrs (level '+level+', lines '+floodCount[user]+'). Last msg is "'+msg+'"');
  395.                             notif(user, notifFlood);
  396.  
  397.                             floodCount = {};
  398.                         }
  399.                     }
  400.                 } else {
  401.                     floodCount[user] = 1;
  402.                 }
  403.                 // CURSING/SWEARING DETECTOR: check if one is swearing too much
  404.                 if(scanWords(messageArray, badWordsList)){
  405.                     //console.log('swear '+user);
  406.                     clearInterval(timer);
  407.                     timer = setInterval(resetCheck, resetTime);
  408.                     if(swearUserObj.name == ''){
  409.                         swearUserObj.name = user;
  410.                     }
  411.                     if(user === swearUserObj.name){
  412.                         swearUserObj.msg.push(msg);
  413.                         var swearArray = swearUserObj.msg[0].split(" ");
  414.                         swearUserObj.pts += countWords(swearArray, badWordsList);
  415.                         //console.log(swearUserObj.pts + ' ' + user);
  416.                         swearArray = [];
  417.                         swearUserObj.msg = [];
  418.                         clearInterval(timer);
  419.                         timer = setInterval(resetCheck, resetTime);
  420.                     }
  421.                     //if (swearUserObj.pts >= minPoints){
  422.                     if (swearUserObj.pts >= minPoints || ((swearUserObj.pts >= (minPoints - 5)) && (level < minLevelAlt))){
  423.                         if (autoMute == false){
  424.                             console.log(user + ' was cursing excessively. Last msg is "'+msg+'"');
  425.                             notif(user, notifAlertSwear);
  426.                             resetCheck();
  427.                         } else if (autoMute == true){
  428.                             mutePlayer(user, muteTimeSwear, 4);
  429.                             console.log(user + ' has been muted for excessive swearing for '+muteTimeSwear+' hrs ('+swearUserObj.pts+' pts). Last msg is "'+msg+'"');
  430.                             notif(user, notifSwear);
  431.  
  432.                             resetCheck();
  433.                         }
  434.                     }
  435.                 }
  436.             }
  437.         });
  438.     });
  439.  
  440.     // configuration of the observer:
  441.     var config = { attributes: true, childList: true, characterData: true };
  442.  
  443.     // pass in the target node, as well as the observer options
  444.     observer.observe(target, config);
  445.  
  446.     //Stop observing
  447.     //observer.disconnect();
  448. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement