<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://gw1wiki.ezgame.cc/index.php?action=history&amp;feed=atom&amp;title=Widget%3ARollback_robot</id>
	<title>Widget:Rollback robot - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://gw1wiki.ezgame.cc/index.php?action=history&amp;feed=atom&amp;title=Widget%3ARollback_robot"/>
	<link rel="alternate" type="text/html" href="https://gw1wiki.ezgame.cc/index.php?title=Widget:Rollback_robot&amp;action=history"/>
	<updated>2026-10-17T20:53:15Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.39.17</generator>
	<entry>
		<id>https://gw1wiki.ezgame.cc/index.php?title=Widget:Rollback_robot&amp;diff=3155&amp;oldid=prev</id>
		<title>gww&gt;Chieftain Alex at 21:54, 11 September 2026</title>
		<link rel="alternate" type="text/html" href="https://gw1wiki.ezgame.cc/index.php?title=Widget:Rollback_robot&amp;diff=3155&amp;oldid=prev"/>
		<updated>2026-09-11T21:54:40Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;noinclude&amp;gt;{{#Widget:Rollback robot}}&lt;br /&gt;
== Description ==&lt;br /&gt;
This widget facilitates the automation of rollbacks. If you are doing lots of rollbacks, make sure to login to a bot account before using this page.&lt;br /&gt;
&lt;br /&gt;
You will need the &amp;quot;rollback&amp;quot; permission on your account to use this page.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
This widget has no parameters.&lt;br /&gt;
&lt;br /&gt;
[[Category:Widgets]]&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&amp;lt;includeonly&amp;gt;&amp;lt;div id=&amp;quot;widgettarget&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;style&amp;gt;&lt;br /&gt;
/* CSS for bulk edit tool */&lt;br /&gt;
#listwrapper {&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  clear: both;&lt;br /&gt;
}&lt;br /&gt;
#listwrapper &amp;gt; table {&lt;br /&gt;
  width: 100%;&lt;br /&gt;
}&lt;br /&gt;
#titlebar {&lt;br /&gt;
  border: 1px solid #555;&lt;br /&gt;
  background-color: #777;&lt;br /&gt;
  padding: 3px 0px;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  color: #EEE;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 12pt;&lt;br /&gt;
}&lt;br /&gt;
.contentwrapper {&lt;br /&gt;
  border: 1px solid #555;&lt;br /&gt;
  margin-top: 2px;&lt;br /&gt;
  background-color: #DDD;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
}&lt;br /&gt;
.contentwrapper &amp;gt; table {&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  border-spacing: 5px;&lt;br /&gt;
}&lt;br /&gt;
.contentwrapper td {&lt;br /&gt;
  border: 1px solid #555;&lt;br /&gt;
  background-color: #EEE;&lt;br /&gt;
  padding: 5px;&lt;br /&gt;
  font-family: consolas;&lt;br /&gt;
  vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
#listoperators {&lt;br /&gt;
  min-width: 222px;&lt;br /&gt;
  width: 50%;&lt;br /&gt;
}&lt;br /&gt;
#fullwrapper input[type=text] {&lt;br /&gt;
  width: 99%;&lt;br /&gt;
  padding: 3px 2px;&lt;br /&gt;
}&lt;br /&gt;
#fullwrapper input[type=button] {&lt;br /&gt;
  min-width: 70px;&lt;br /&gt;
  margin: 0px 2px;&lt;br /&gt;
}&lt;br /&gt;
#fullwrapper select {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  height: 27px;&lt;br /&gt;
}&lt;br /&gt;
#listinput textarea {&lt;br /&gt;
  overflow-y: scroll;&lt;br /&gt;
  height: 515px;&lt;br /&gt;
}&lt;br /&gt;
#listsucceed {&lt;br /&gt;
  background-color: #9F9;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#listsucceed div {&lt;br /&gt;
  height: 500px;&lt;br /&gt;
  overflow-y: scroll;&lt;br /&gt;
}&lt;br /&gt;
#listfailed {&lt;br /&gt;
  background-color: #F99;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#listfailed div {&lt;br /&gt;
  max-height: 100px;&lt;br /&gt;
  overflow-y: scroll;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;/* SCRIPT: ROLLBACK VANDALISM */&lt;br /&gt;
function mwRollbackScript (){&lt;br /&gt;
    // Make the GUI.&lt;br /&gt;
    function makeTaskGUI () {&lt;br /&gt;
        // Initialise frame with required options.&lt;br /&gt;
        var UIframe = $('&amp;lt;div id=&amp;quot;fullwrapper&amp;quot;&amp;gt;'&lt;br /&gt;
                            +'&amp;lt;div id=&amp;quot;titlebar&amp;quot;&amp;gt;AJAX contributions and rollback script&amp;lt;/div&amp;gt;'&lt;br /&gt;
                            +'&amp;lt;div class=&amp;quot;contentwrapper&amp;quot;&amp;gt;'&lt;br /&gt;
                                +'&amp;lt;table&amp;gt;&amp;lt;tbody&amp;gt;'&lt;br /&gt;
                                    +'&amp;lt;tr&amp;gt;'&lt;br /&gt;
                                        +'&amp;lt;td colspan=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;textarea id=&amp;quot;listuser&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Insert username to revert here.&amp;quot; rows=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;/td&amp;gt;'&lt;br /&gt;
                                    +'&amp;lt;/tr&amp;gt;'&lt;br /&gt;
                                    +'&amp;lt;tr&amp;gt;'&lt;br /&gt;
                                        +'&amp;lt;td id=&amp;quot;listinput&amp;quot; rowspan=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;textarea&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;/td&amp;gt;'&lt;br /&gt;
                                        +'&amp;lt;td id=&amp;quot;listsucceed&amp;quot;&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;'&lt;br /&gt;
                                    +'&amp;lt;/tr&amp;gt;'&lt;br /&gt;
                                    +'&amp;lt;tr&amp;gt;'&lt;br /&gt;
                                        +'&amp;lt;td id=&amp;quot;listfailed&amp;quot;&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;'&lt;br /&gt;
                                    +'&amp;lt;/tr&amp;gt;'&lt;br /&gt;
                                    +'&amp;lt;tr&amp;gt;'&lt;br /&gt;
                                        +'&amp;lt;td id=&amp;quot;listprogress&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;'&lt;br /&gt;
                                        +'&amp;lt;td id=&amp;quot;listoperators&amp;quot;&amp;gt;'&lt;br /&gt;
                                            +'&amp;lt;input name=&amp;quot;contribs&amp;quot; type=&amp;quot;button&amp;quot; value=&amp;quot;Get contributions&amp;quot;&amp;gt;'&lt;br /&gt;
                                            +'&amp;lt;input name=&amp;quot;rollback&amp;quot; type=&amp;quot;button&amp;quot; value=&amp;quot;Rollback contributions&amp;quot;&amp;gt;'&lt;br /&gt;
                                            +'&amp;lt;input name=&amp;quot;reset&amp;quot; type=&amp;quot;button&amp;quot; value=&amp;quot;Reset&amp;quot;&amp;gt;'&lt;br /&gt;
                                        +'&amp;lt;/td&amp;gt;'&lt;br /&gt;
                                    +'&amp;lt;/tr&amp;gt;'&lt;br /&gt;
                                +'&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;'&lt;br /&gt;
                            +'&amp;lt;/div&amp;gt;'&lt;br /&gt;
                        +'&amp;lt;/div&amp;gt;');&lt;br /&gt;
        $('#widgettarget').html(UIframe);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetch all the contributions&lt;br /&gt;
    function apiQueryContributions (token) {&lt;br /&gt;
        // Prepare URL: action = query, list = user contributions, limit = 500&lt;br /&gt;
        var url = 'https://wiki.guildwars.com/api.php?action=query&amp;amp;format=json&amp;amp;list=usercontribs&amp;amp;uclimit=500&amp;amp;ucdir=newer&amp;amp;ucuser=' + user;&lt;br /&gt;
        if (token) {&lt;br /&gt;
            url += '&amp;amp;uccontinue=' + token&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Make the requests&lt;br /&gt;
        $.getJSON(url).done(function(data){&lt;br /&gt;
            datadrop.push(data['query']['usercontribs']);&lt;br /&gt;
&lt;br /&gt;
            // Check if more requests are necessary&lt;br /&gt;
            if (data['continue']) {&lt;br /&gt;
                apiQueryContributions(data['continue']['uccontinue']);&lt;br /&gt;
            } else {&lt;br /&gt;
                console.log('Contributions query complete.');&lt;br /&gt;
                getTitles(datadrop);&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Collate all results&lt;br /&gt;
    function getTitles (datadrop) {&lt;br /&gt;
        // Nested array of request objects&lt;br /&gt;
        var contribs = [];&lt;br /&gt;
        $.each(datadrop, function(k,v){&lt;br /&gt;
            $.each(v, function(kk,vv){&lt;br /&gt;
                contribs.push(vv);&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // Focus on titles&lt;br /&gt;
        var titles = $.map(contribs, function(v){&lt;br /&gt;
            return v.title;&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // Helper function: Removes duplicates, blanks and undefined, then sorts. Usage: a.unique()&lt;br /&gt;
        Array.prototype.unique = function() {&lt;br /&gt;
            return this.filter(function (el, i, self) {&lt;br /&gt;
                return self.indexOf(el) === i;&lt;br /&gt;
            });&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        // Remove duplicates&lt;br /&gt;
        titles = titles.unique();&lt;br /&gt;
&lt;br /&gt;
        // Return titles to UI&lt;br /&gt;
        $('#listinput textarea').text( titles.join('\n') );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Rollback an edit&lt;br /&gt;
    function rollbackContributions () {&lt;br /&gt;
&lt;br /&gt;
        // Obtain the list of potential pages&lt;br /&gt;
        var allpages = [];&lt;br /&gt;
        allpages = $('#listinput textarea').val().split('\n');&lt;br /&gt;
&lt;br /&gt;
        // Take the first line away from the potential pages and add to the processing box.&lt;br /&gt;
        var currentline = allpages.shift();&lt;br /&gt;
        $('#listprogress').text(currentline);&lt;br /&gt;
&lt;br /&gt;
        // Return allpages back to the form without the top line&lt;br /&gt;
        $('#listinput textarea').val(allpages.join('\n'));&lt;br /&gt;
&lt;br /&gt;
        // Get single page from stack&lt;br /&gt;
        var pagetitle = currentline;&lt;br /&gt;
&lt;br /&gt;
        // Do a rollback&lt;br /&gt;
        api.rollback( pagetitle, user )&lt;br /&gt;
        .done( function() {&lt;br /&gt;
            $('#listsucceed div').html( $('#listprogress').text() + '&amp;lt;br&amp;gt;' + $('#listsucceed div').html());&lt;br /&gt;
            $('#listprogress').text('');&lt;br /&gt;
&lt;br /&gt;
            // More on to next page&lt;br /&gt;
            if (allpages.length &amp;gt; 0) {&lt;br /&gt;
              rollbackContributions();&lt;br /&gt;
            } else {&lt;br /&gt;
              console.log('Finished rollbacks.')&lt;br /&gt;
            }&lt;br /&gt;
        })&lt;br /&gt;
        .fail( function(code, response1, response2, jqXHR) {&lt;br /&gt;
            // Actually it didn't &amp;quot;fail&amp;quot; per-se if it was rolledback by somebody else&lt;br /&gt;
            if ( code == 'alreadyrolled') {&lt;br /&gt;
                var pattern = new RegExp(&amp;quot;The last edit to the page was by (.*) [\(]&amp;quot;);&lt;br /&gt;
                var hero = response1.error.info.match(pattern);&lt;br /&gt;
                if (hero) {&lt;br /&gt;
                    $('#listsucceed div').html( $('#listprogress').text() + ' - &amp;lt;i&amp;gt;already reverted by and/or conflicting edits by ' + hero[1] + '&amp;lt;/i&amp;gt;' + '&amp;lt;br&amp;gt;' + $('#listsucceed div').html());&lt;br /&gt;
                    console.log( pagetitle + &amp;quot;: API error: &amp;quot; + code + &amp;quot; by &amp;quot; + hero[1]);&lt;br /&gt;
                } else {&lt;br /&gt;
                    $('#listsucceed div').html( $('#listprogress').text() + '&amp;lt;br&amp;gt;' + $('#listsucceed div').html());&lt;br /&gt;
                    console.log( pagetitle + &amp;quot;: API error: &amp;quot; + code);&lt;br /&gt;
                }&lt;br /&gt;
                $('#listprogress').text('');&lt;br /&gt;
&lt;br /&gt;
                // Continue&lt;br /&gt;
                if (allpages.length &amp;gt; 0) {&lt;br /&gt;
                  rollbackContributions();&lt;br /&gt;
                } else {&lt;br /&gt;
                  console.log('Finished rollbacks.');&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                // Other legitimate errors&lt;br /&gt;
                $('#listfailed div').html( $('#listprogress').text() + '&amp;lt;br&amp;gt;' + $('#listfailed div').html());&lt;br /&gt;
                $('#listprogress').text('');&lt;br /&gt;
&lt;br /&gt;
                // Full detail in console&lt;br /&gt;
                if ( code == 'http' ) {&lt;br /&gt;
                    console.log( pagetitle + &amp;quot;: HTTP error: &amp;quot; + jqXHR.textStatus );&lt;br /&gt;
                } else if ( code == 'ok-but-empty' ) {&lt;br /&gt;
                    console.log( pagetitle + &amp;quot;: Empty response from server.&amp;quot; );&lt;br /&gt;
                } else {&lt;br /&gt;
                    console.log( pagetitle + &amp;quot;: API error: &amp;quot; + code );&lt;br /&gt;
                }&lt;br /&gt;
            } &lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Respond to button pushes&lt;br /&gt;
    function mainTaskFunction () {&lt;br /&gt;
        // When user clicks, get contributions&lt;br /&gt;
        $('input[name=&amp;quot;contribs&amp;quot;]').click(function() {&lt;br /&gt;
            // Check user input box isn't blank&lt;br /&gt;
            if ( $('#listuser').val() == '' ) {&lt;br /&gt;
                console.log('You did not select a user to revert. Abort!');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Get username from the UI&lt;br /&gt;
            user = $('#listuser').val().trim();&lt;br /&gt;
&lt;br /&gt;
            // Fetch contributions&lt;br /&gt;
            console.log('User pressed button. Getting contributions for User:' + user + '...');&lt;br /&gt;
            apiQueryContributions();&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // When user clicks, rollback contributions&lt;br /&gt;
        $('input[name=&amp;quot;rollback&amp;quot;]').click(function() {&lt;br /&gt;
            // Check user input box isn't blank&lt;br /&gt;
            if ( $('#listuser').val() == '' ) {&lt;br /&gt;
                console.log('You did not select a user to revert. Abort!');&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Get username from the UI&lt;br /&gt;
            user = $('#listuser').val().trim();&lt;br /&gt;
&lt;br /&gt;
            // Get contributions from the UI&lt;br /&gt;
            var pages = $('#listinput textarea').val().split('\n');&lt;br /&gt;
&lt;br /&gt;
            // Fetch contributions&lt;br /&gt;
            console.log('User pressed button. Rolling back contributions for User:' + user + '...');&lt;br /&gt;
            rollbackContributions();&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // When user clicks reset, blank all fields&lt;br /&gt;
        $('input[name=&amp;quot;reset&amp;quot;]').click(function() {&lt;br /&gt;
            console.log('user clicked reset.');&lt;br /&gt;
            $('#listuser').val('');&lt;br /&gt;
            $('#listinput textarea').text('');&lt;br /&gt;
            $('#listsucceed div').html('');&lt;br /&gt;
            $('#listfailed div').html('');&lt;br /&gt;
            $('#listprogress').text('');&lt;br /&gt;
            $('#listsummary').val('');&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Initialise variables&lt;br /&gt;
    var api = new mw.Api();&lt;br /&gt;
    var user;&lt;br /&gt;
    var datadrop = [];&lt;br /&gt;
&lt;br /&gt;
    // Create the user interface&lt;br /&gt;
    makeTaskGUI();&lt;br /&gt;
&lt;br /&gt;
    // Add functions to the interface&lt;br /&gt;
    mainTaskFunction();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// DEFER LOADING SCRIPT UNTIL JQUERY IS READY. WAIT 40MS BETWEEN ATTEMPTS.&lt;br /&gt;
function defer(method) {&lt;br /&gt;
    if (window.jQuery) {&lt;br /&gt;
        method();&lt;br /&gt;
    } else {&lt;br /&gt;
        setTimeout(function() { defer(method) }, 240);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// INITIALISATION&lt;br /&gt;
defer(function () {&lt;br /&gt;
    mw.loader.using(['mediawiki.api'], function () {&lt;br /&gt;
        var userrights = mw.config.get('wgUserGroups');&lt;br /&gt;
        if (userrights.indexOf('sysop') &amp;gt; -1 || userrights.indexOf('bot') &amp;gt; -1 || userrights.indexOf('patrol') &amp;gt; -1 ) {&lt;br /&gt;
            mwRollbackScript();&lt;br /&gt;
        } else {&lt;br /&gt;
            console.log('Insufficient user rights: '+JSON.stringify(userrights));&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/script&amp;gt;&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>gww&gt;Chieftain Alex</name></author>
	</entry>
</feed>