Foxhole: the MAD voting script


  • Trolleybus Mechanic

    Okay, everyone. I finally got around to generalizing the voting script. It's fully configurable. In theory you can make it an Upvote script that only targets the Like topic. Or you can keep it as a Downvote script that occasionally downvotes specific users in the Garbage category.

    To use it, slap Greasemonkey or some other Userscript on your browser. Add this script. Do a hard refresh any time you make a configuration change Because SPA That's Why.

    Things to setup after you install:

    REMINDER all vote chance variables are decimals from 0-1. 50% is 0.5.

    MyUsername - Set it to your username. If I weren't lazy, I could probably extract this from #user_dropdown, but that would probably break, and I'm sure username != display name.

    GarbageCategoryID - Currently set to 49, which is the category Id of the Garbage. Please keep the script in there. Don't be a cunt. (TODO: Maybe allow it in the Likes category, too?)

    WhitelistTopicIDs - If this is blank, all topics (in the category) are fair game. If there is an entry, will only run on those topics. 1D array
    BlacklistTopicIDs - If this is not blank, topics in this 1D array will never be script-voted on.

    DefaultDownvoteChance - If a post is eligible to be downvoted, this is the default chance it will be. If you disagree with 50% of shit in a thread, set it to .5. If you don't want to downvote, set to 0.

    DefaultUpvoteChance - If a post is eligible to be upvoted, this is the default chance. If you agree with 65% of shit in a thread, set it to .65. If you want to upvote everything, set it to 1.

    TopicDownvoteChanceOverride
    TopicUpvoteChanceOverride - Override the up/downvote chance based on the specific topic. If you have upvote set to 0, but agree with 99% of stuff in one particular thread, set this to [tid]=.99

    UpvoteUsers - users who will only be targeted with upvotes. If in this list, they will be skipped when determining downvote eligibility.
    DownvoteUsers - users who will only be targeted with downvotes. If in this list, they will be skipped when determining upvote eligibility.

    OnlyDownvoteIfInList - If true, a post will be targeted for downvoting ONLY if the user is in the DownvoteUsers array. If false, everyone is eligible to be downvoted
    OnlyUpvoteIfInList - If true, a post will be targeted for upvoting ONLY if the user is in the UpvoteUsers array. If false, everyone is eligible to be upvoted.

    SO if you never want to downvote anyone, leave DownvoteUsers blank, and set OnlyDownvoteIfInList to True.

    TopicDownvoteChanceOverride
    TopicUpvoteChanceOverride - a multi-dim array. The key is "username". see format below. If a user exists in this array, then you can tweak their default up/down vote chance. There is an optional override value, which means you can further tweak their up/down vote chance on a per-topic basis.

    Construct:

    // voteChance: 0-1 REQUIRED, with 0 being 0%, and 1 being 100%
    // override: OPTIONAL, a set of objects in the form of
    //      tid: topic_ic // REQUIRED topic ID
    //      voteChance: 0-1 // REQUIRED, chance of the vote occuring.
    TopicDownvoteChanceOverride["username"] = {voteChance: 0.5, override: { {tid:1, voteChance: 0.25}, {tid:2, voteChance:1}   }
    

    ExtraSpecialRandomDownvoteChance
    ExtraSpecialRandomUpvoteChance - If set, after determining up and downvotes, will do another round of voting. Everyone is eligible. Used if "no matter what, 2% of what everyone says is complete bullshit and needs to be downvoted".

    And that's it. Install, configure, fire and forget. Post bug reports here so I can ban you.

    And remember, this is just a tool. It can be used for good or evil; as a laser or a nuke. I'm not responsible for how you behave. Fuck you, give me money.

    // ==UserScript==
    // @name        tdwtf - Foxhole
    // @namespace   Lorne
    // @include     https://what.thedailywtf.com/*
    // @version     42
    // @grant       all
    // @runat      document-idle
    // ==/UserScript==
    
    var DoLogging = false;
    
    if(DoLogging) {    console.log("downvote script start"); }
    var $ = unsafeWindow.jQuery;
    var MyUsername = "Lorne Kates"; // fill this in, idiot.  You can't vote on your own posts
    var GarbageCategoryID = "49"; // this is the flame category. If you change this value, you are a cunt.
    // TODO: I suppose we could extend this to also do 100% upvotes in official likes thread but fuck you give me money
    var WhitelistTopicIDs = []; // If you put any topic ids here, then the script will ONLY run on those topics. 1d array of tids
    var BlacklistTopicIDs = []; // If you put any topic ids here, then the script will NEVER run on those topics. 1d array of tids
    
    var IgnoredPostIDs = []; // if there are any posts you never want to run the script on, put them here. 
    
    var DefaultUpvoteChance = 0.8;  // default chance of a upvote happening. Can be overriden per user, and per user/topic
                                    // this is the chance for EVERYONE who is not in the Downvote list
    var DefaultDownvoteChance = 0.2; // default chance of an downvote happening. Can be overriden per user, and per user/topic
                                    // this is the chance for EVERYONE who is not in the Upvote list
                                    
    var TopicDownvoteChanceOverride = [];   // 2D array with topics to have different up/downvote chances. Will be overrided by user-specific ones
    var TopicUpvoteChanceOverride = [];     // 
    
    
    // Examples
    /*
    TopicDownvoteChanceOverride[1] = 0;
    TopicDownvoteChanceOverride[22089] = .1;
    TopicUpvoteChanceOverride[1] = 1;
    TopicUpvoteChanceOverride[22089] = .11;
    */
    
    // fill these 2D arrays to explicitly set someone to be upvoted or downvoted
    // key is their username, value is an object in the form of:
    // voteChance: 0-1 REQUIRED, with 0 being 0%, and 1 being 100%
    // override: OPTIONAL, a set of objects in the form of
    //      tid: topic_ic // REQUIRED topic ID
    //      voteChance: 0-1 // REQUIRED, chance of the vote occuring.
    
    // This means each user has X% chance of being Downvoted, unless they are in the Upvote array.
    // If the user is in the Downvote array, they have Y%, where y is the voteChance
    // If the user also has overrides, and the topic_id is being viewed, they have Z%, where Z is that specific votechance
    // The same is true for Downvotes
    
    var UpvoteUsers = [];
    var DownvoteUsers = [];
    
    // Examples
    /*
    UpvoteUsers["Lorne Kates"] = {voteChance: 1, override: {tid:1, voteChance:.99}};
    UpvoteUsers["anonymous234"] = {voteChance: .15, override: {tid:1, voteChance:.99}};
    UpvoteUsers["dkf"] = {voteChance: .02, override: {tid:22089, voteChance: .05}};
    
    
    DownvoteUsers["BatConley"] = {voteChance: 1};
    DownvoteUsers["Fox"] = {voteChance: 0.8};
    DownvoteUsers["Lorne Kates"] = {voteChance: 0.01};
    */
    
    var OnlyDownvoteIfInList = true; // Only downvote someone if they exist in the Downvoters list.  Effectively, deny/allow presedence
    var OnlyUpvoteIfInList = false; // Only upvote for someone if they exist in the Upvote list
    
    
    var ExtraSpecialRandomDownvoteChance = 0; // 0.02; // if > 0, will randomly do a downvote, ignoring the Upvote users, after all other up/down votes are done
    var ExtraSpecialRandomUpvoteChance = 0; // if > 0, will randomly do an upvote, ignoring the Downvote users
    
    function LaunchVoting()
    {
        if(DoLogging) {    console.log("in launch voting"); }
        var socket = unsafeWindow.socket;
        if(typeof socket == 'undefined' || socket == null)
        {
            if(DoLogging) {    console.log("Can't find socket, waiting"); }
            setTimeout(LaunchVoting, 500);
        }
        else
        {
    
            if(DoLogging) {    console.log("doing downvote"); }
            DoDownvote(data.tid);
        }
        if(DoLogging) {    console.log("leaving launch voting"); }
    }
    
    $(document).ready(function()
    {
       
       var $ = unsafeWindow.jQuery;
       if(DoLogging) {    console.log("document.ready in downvote script"); }
    
        // whenever a topic is loaded, we will do votes for that topic
        $(window).on('action:topic.loaded', function(event, data) 
        {
           // console.log("topic loaded");
            //console.log("data", data);
            //console.log("event", event);
            //console.log("test");
            // trolleybus garbage category id = 49
            if(DoLogging) {    console.log("in action:topic.loaded for downvote script"); }
            if(DoLogging) {    console.log("data.cid = ", data.cid); }
            var ShouldDoDownvote = false;
            
            if(data.cid == GarbageCategoryID)
            {   
                if(DoLogging) {    console.log("in Garbage category"); }
                // check for blacklisted topics
                if(BlacklistTopicIDs.length > 0 && inArray(data.tid, BlacklistTopicIDs))
                {
                    if(DoLogging) {    console.log("Topic id " + data.tid + " is blacklisted, and won't be voted on."); }
                }
                else
                {
                    // if the whitelist is populated, only
                    if(WhitelistTopicIDs.length > 0)
                    {
                        if(! inArray(data.tid, WhitelistTopicIDs))
                        {
                            if(DoLogging) {    console.log("topic ID " + data.tid + " is not in the whitelist"); }
                        }
                        else
                        {
                            if(DoLogging) {    console.log("topic ID " + data.tid + " is whitelisted"); }
                            ShouldDoDownvote = true;
                        }
                        
                    }
                    else
                    {
                        if(DoLogging) {    console.log("topic ID " + data.tid + " is not white or blacklisted"); }
                        ShouldDoDownvote = true;
                    }
                    
                }
            }
            else
            {
                if(DoLogging) {    console.log("Category is not the garbage category."); }
            }
            
            if(ShouldDoDownvote)
            {
                if(DoLogging) {    console.log("doing downvote for this topic"); }
                DoDownvote(data.tid);
            }
    
    
    
        });
        
        if(DoLogging) {    console.log("END document.ready in downvote script"); }
        
    });
    
    
    
    
    
    function DoDownvote(topic_id)
    {
        // each link will be voted on:
        // Upvote: NOT my username AND (not in downvote list)
        // Downvote: NOT my username AND (not in upvote list)
        // The vote will only happen based on a percentage.  The percentage is defaulted on DefaultUpvoteChance and DefaultDownvoteChance 
        // BUT the vote will be possibly overridden based on override in UpvoteUsers or DownvoteUsers based on topic id
        
        
        
        var ShouldDoDownload = false;
        // Need to do this manually since app.currentRoom is not set
        if(DoLogging) {    console.log("In DoDownvote for topic ", topic_id); }
        
        var $ = unsafeWindow.jQuery;
        if(DoLogging) {    console.log("Getting downvote links"); }
        
     
        
        $ToDownvote = $("a[component*=downvote]").not(".downvoted").filter(function()
        {
            var r = ShouldPerformVote(this); 
            return r;
    
        });
        
        if(DoLogging) {    console.log("Got downvte links ", $ToDownvote); }
        
        if(DoLogging) {    console.log("Getting upvote links"); }
        $ToUpvote = $("a[component*=upvote]").not(".upvoted").filter(function()
        {
            var r = ShouldPerformVote(this); 
            return r;
        });    
        
            if(DoLogging) {    console.log("Got upvote links ", $ToUpvote); }
    
        var vote_actual;
        
        // DOWNVOTES
        vote_actual = DefaultDownvoteChance;
        if(TopicDownvoteChanceOverride[topic_id] != null)
        {
            vote_actual = TopicDownvoteChanceOverride[topic_id];
        }
    
        if(DoLogging) {    console.log("doing downvotes with chance ", vote_actual); } 
        EmitVoting(topic_id, $ToDownvote, "posts.downvote", vote_actual, DownvoteUsers);
    
        // UPVOTES    
        vote_actual = DefaultUpvoteChance;
        if(TopicUpvoteChanceOverride[topic_id] != null)
        {
            vote_actual = TopicUpvoteChanceOverride[topic_id];
        }
    
        if(DoLogging) {    console.log("doing upvotes with chance ", vote_actual); }
        
        EmitVoting(topic_id, $ToUpvote, "posts.upvote", vote_actual, UpvoteUsers);
        
        if(ExtraSpecialRandomDownvoteChance > 0)
        {
            $ToDownvote = $("li[data-username]").not("[data-username='"+MyUsername+"']").find("a[component*=downvote]").not(".downvoted");
            EmitVoting(topic_id, $ToDownvote, "posts.downvote", ExtraSpecialRandomDownvoteChance, null);    
        }
    
        if(ExtraSpecialRandomUpvoteChance > 0)
        {
            $ToUpvote = $("li[data-username]").not("[data-username='"+MyUsername+"']").find("a[component*=upvote]").not(".upvoted");
            EmitVoting(topic_id, $ToDownvote, "posts.upvote", ExtraSpecialRandomUpvoteChance, null);    
        }
            
           
    }
    
    // "posts.downvote"
    // vote chance is 0 - 1
    function EmitVoting(topic_id, elms, vote_type, vote_chance, VoteChanceArray)
    {
        var $ToVote = $(elms);
        vote_chance = parseInt(vote_chance * 100);
        if (isNaN(vote_chance) || vote_chance > 100)
        {
            vote_chance = 100;
        }
        
        if(vote_chance < 0)
        {
            vote_chance = 0;
        }
        
        vote_chance = parseInt(vote_chance);
        
        if(vote_type != "posts.downvote" && vote_type != "posts.upvote")
        {
            if(DoLogging) {    console.log("invalid vote type of " + vote_type + " must be posts.downvote or posts.upvote"); }
            return;
        }
        
        $ToVote.each(function()
        {
            var original_vote_chance = vote_chance;
                e = $(this);
                var i = e.parents("[data-pid]");
               
    
                
                // See if this user, for this topic, has a different vote_chance
                var li = e.parents("li[data-username]:first");
                var username = li.attr("data-username");
                var user;
                var overrides;
    
                if(VoteChanceArray != null)
                {
     
                    // Check if user exists in the upvote or downvote users special consideration
                    user = VoteChanceArray[username];
                    if(user != null)
                    {
                        if(DoLogging) {    console.log("Checking special requirements for " + username); }
                        vote_chance = user.voteChance;
                        vote_chance = parseInt(vote_chance * 100);
                        
                        if(user.overrides != null)
                        {
                            for(var i = 0; i < user.overrides.length; i++)
                            {
                                // if the user has special requirements for this topic
                                if(user.overrides[i].tid == topic_id)
                                {
                                    
                                    vote_chance = user.overrides[i].voteChance;
                                    vote_chance = parseInt(vote_chance * 100);
                                   if(DoLogging) {console.log("for tid " + topic_id + " user has special chance of " + vote_chance + " for " + vote_type); }
                                    i = user.overrides.length;
                                }
                            }
                        }
                    }
                } // end user/topic specific overrides
                
                // check the random number
                var whatTheNumber = Math.floor((Math.random() * 100) + 1);
                if (isNaN(vote_chance) || vote_chance > 100)
                {
                    vote_chance = 100;
                }
                
                if(vote_chance < 0)
                {
                    vote_chance = 0;
                }
                
                vote_chance = parseInt(vote_chance);
                if(DoLogging) {    console.log("Whatthenumber = ", whatTheNumber, " vs. vote chance of ", vote_chance); }
                if(whatTheNumber <= vote_chance)
                {
                    if(DoLogging) {    console.log("Voting pid = ", i.attr("data-pid"), " topic ", topic_id, " vote type ", vote_type); }
                    var socket = unsafeWindow.socket;
                    socket.emit(vote_type, {pid: i.attr("data-pid"), room_id: "topic_" + topic_id}, function(e) { console.log("Did vote of " + vote_type + " for " + i.attr("data-pid"), e) });
    
                }
            // reset back to default vote chance
            vote_chance = original_vote_chance;
    
        });
    }
    
        // helper function if an element should be considered for downvoting based on user, vote status, and other settings
       function ShouldPerformVote(elm)
        {
            var li = $(elm).parents("li:first");
            var IsUpvoteElm = $(elm).attr("component") == "post/upvote";
            var IsDownvoteElm = ! IsUpvoteElm;
            var username = li.attr("data-username");
            var IsUpvoteUser = UpvoteUsers[username] != null;
            var IsDownvoteUser = DownvoteUsers[username] != null;
            var IsUpvotedAlready = li.find("a.upvoted").length > 0;
            var IsDownvotedAlready = li.find("a.upvoted").length > 0;
            var DoVote = true;
    
    
            // Only downvote someone if:
            // They are not in the opposite up/downvote list
            // have not already (opposite)voted
            // (OnlyDownvoteIfInList is false OR they are in the downvote list)
            // AND you have not Upvoted this post
            // broken into multiple ifs just for readability
            if(IsDownvoteElm)
            {
                if(IsUpvoteUser)
                {
                    DoVote = false;
                }
                
                if(IsUpvotedAlready) 
                {
                    DoVote = false;
                }            
                if(OnlyDownvoteIfInList && ! IsDownvoteUser)
                {
                    DoVote = false;
                }
            }
            
            if(IsUpvoteElm)
            {
                if(IsDownvoteUser)
                {
                    DoVote = false;
                }     
                
                if(IsDownvotedAlready)
                {
                    DoVote = false;
                }   
                
                if(OnlyUpvoteIfInList && ! IsUpvoteUser)
                {
                    DoVote = false;
                }              
            }
            
            var ielm = $(elm).parents("[data-pid]");
            var pid =  ielm.attr("data-pid")
            
            // obey ignored posts
            if(IgnoredPostIDs[pid] != null)
            {
                DoVote = false;
            }
            
    
            return DoVote // true means include. Exclude any link that is for an Upvote user OR that is already upvoted.       
        }
    
    // Yes, there is .includes. But fuck you, I'm using FF22
    function inArray(val, arr)
    {
        var found = false;
        
        for(var i = 0; i < arr.length; i++)
        {
            if(arr[i] == val)
            {
                found = true;
                i = arr.length;
            }
        }
        
        return found;
    }
    
    if(DoLogging) {    console.log("downvote script end"); } 
    
    
    


  • Real men hammer that downvote button like a masturbating bonobo.


  • Discourse touched me in a no-no place

    @Lorne-Kates said in Foxhole: the MAD voting script:

    I finally got around to generalizing the voting script

    You did, or the Indian bloke you outsourced it to for like $6CAD did?


  • Trolleybus Mechanic

    @loopback0 said in Foxhole: the MAD voting script:

    You did, or the Indian bloke you outsourced it to for like $6CAD did?

    Fivr, dude. Though $5USD ~= $219CAD.

    I know it isn't the most elegant code I've ever written, but I err'd on the side of "read and understand" vs. "clever generalization of up/down actions".


  • Discourse touched me in a no-no place

    @Lorne-Kates said in Foxhole: the MAD voting script:

    I err'd on the side of "read and understand"

    A new policy? 🎛



  • @loopback0 Outsourcing is the only economically logical option. All you people should be secretly hiring Indian guys to write the part of your code.

    Also:

    Foxhole

    :giggity:



  • A proper forum would rate limit votes, and not allow more than x/day.



  • @Lorne-Kates said in Foxhole: the MAD voting script:

    var DefaultUpvoteChance = 0.8; // default chance of a upvote happening.
    ...
    var DefaultDownvoteChance = 0.2; // default chance of an downvote happening.

    squirms


  • Trolleybus Mechanic

    @CarrieVS said in Foxhole: the MAD voting script:

    @Lorne-Kates said in Foxhole: the MAD voting script:

    var DefaultUpvoteChance = 0.8; // default chance of a upvote happening.
    ...
    var DefaultDownvoteChance = 0.2; // default chance of an downvote happening.

    squirms

    Can you tell where i copypasta'd documentation, realized the documentation up/down didn't match the variable up/down, then recopypastad part of it, then published it an anways?



  • @Lorne-Kates Actually I assumed you'd done it on purpose just to make :pendant:s uncomfortable.


  • BINNED

    @CarrieVS never attribute to malice that which you can attribute to laziness.



  • @Onyx said in Foxhole: the MAD voting script:

    @CarrieVS never attribute to malice that which you can attribute to laziness.

    Except here.



  • @CarrieVS
    Don't worry, that squirming feeling from not having your cast on will go away in a few weeks.


  • Garbage Person

    var ielm = $(elm(.parents("[data-pid]");

    The fuck kind of syntax is that?



  • @Weng error kind of syntax, that's what.

    $(elm).parents("[data-pid]") is jQuery kind of syntax.


  • Garbage Person

    @anotherusername I guess it's somehow valid in ff22


  • Trolleybus Mechanic

    @anotherusername said in Foxhole: the MAD voting script:

    @Weng error kind of syntax, that's what.

    $(elm).parents("[data-pid]") is jQuery kind of syntax.

    yes.

    I can't edit the OP so 🤷


  • :belt_onion:

    @Lorne-Kates this sounds hard


  • :belt_onion:

    @darkmatter also, which option to use to make it an auto-vote-balancer bot?


  • I survived the hour long Uno hand

    @Lorne-Kates But I can! FTFY


  • Trolleybus Mechanic

    @Yamikuronue said in Foxhole: the MAD voting script:

    @Lorne-Kates But I can! FTFY

    HAHAHAH YES NOW IT LOOKS LIKE I WAS RIGHT ALL ALONG AND ANOTHERUSERNAME IS A RAVING MORON LUNATIC!!!!!!!!!!!!!



  • @Lorne-Kates my post still makes perfect sense in response to what @Weng posted.


Log in to reply