11

I want to review questions I have posted bounties on across the Stack Exchange network. I'd like to do this so I can check for active bounties with answers I need to review.

I can view my offered bounties for a specific site by appending ?tab=bounties to my user profile, but this isn't an option on my Network Profile.

Is there any way to view my offered bounties across Stack Exchange?

Related: How can I view all my unaccepted questions across SE?

2
  • Sounds like a job for SEDE, the API or a user script
    – Helmar
    Commented Jul 23, 2017 at 15:04
  • 1
    @Helmar SEDE is possible but it is only refreshed once a week, on Sunday. For bounties that period is just a bit too long to be useful. The API which uses live data is a better choice here.
    – rene
    Commented Jul 23, 2017 at 20:31

1 Answer 1

7

Here is a stacksnippet that given an accountid (found in the url of your network profile) will list all your active bounties. It uses the questions/featured StackAPI endpoint.

The scripts start with fetching for a given account all associated users. With that info we can iterate over the result and call for each site the info endpoint so we have the site icon and name. Then we proceed to call questions/featured for a user to get the questions that are bountied for that specific site user.

Once we have that result all that is needed is adding elements to the DOM so you can click on the links.

(function () {
  var api = {
    url: 'https://api.stackexchange.com/2.2/',
    endpoint: 'questions/featured',
    params: { 
      site: 'stackoverflow',
      pagesize: 100,
      order: 'desc',
      sort: 'activity',
      Filter: '!iCF4LsDYJt6oT_Y1qKBCOv',
      key: 'JBN9WXWxwrcnAj7WEEytmQ(('
      }
    },
    sites = {};
    
  function createUrl(ap) {
    var ep = ap.url + ap.endpoint +'?',
       nv = [];
    if (ap.params) {
      for(var p in ap.params) {
        if (ap.params.hasOwnProperty(p)) {
             nv.push(p + '=' + ap.params[p]);
        }
      }
    }
    // console.log(ep + nv.join('&'));
    return ep + nv.join('&');
  }
  
  function createSitesUrl() {
    return createUrl( 
      { url: api.url, 
        endpoint: 'sites', 
        params: { 
          pagesize: 500, 
          key: api.params.key, 
          filter: '!*l7_tSmzy1mF3bw9odQw*8ur' } 
       });
  }
  
  function createUserFeaturedUrl(userid, site) {
    return createUrl( 
      { url: api.url, 
        endpoint: 'users/'+ userid + '/questions/featured', 
        params: { 
          site: site,
          pagesize: 100, 
          key: api.params.key, 
          filter: '!*7PmgCF3rY3WihzNKHz4jURFN9X6' } 
       });
  }
  
  function createInfoUrl(site) {
    return createUrl( 
      { url: api.url, 
        endpoint: 'info', 
        params: { 
          site: site,
          key: api.params.key, 
          filter: '!39qVOEzAjeqnKS5bl7_puf_.GSZJG' } 
       });
  }
  
  function createAccountsUrl(accountid) {
    return createUrl( 
      { url: api.url, 
        endpoint: 'users/'+ accountid + '/associated', 
        params: { 
          pagesize: 100, 
          key: api.params.key, 
          filter: '!w*vp4lVBIX453e.b5Y' } 
       });
  }
  
  
  function find(id) {
    return document.getElementById(id);
  }
   
  function buildSite(site) {
     var img = document.createElement('img');
     img.setAttribute('src', site.icon_url);
     img.setAttribute('title', site.name);
     return img;
  }
  
  function buildProgress(site) {
    var li = document.createElement('li');
    li.setAttribute('data-site', site);
    li.textContent = site;
    return li;
  }
  
  function buildLink(post, site) {
    var link = document.createElement('a');
    link.setAttribute('href', site.site_url +'/q/' + post.question_id);
    link.setAttribute('title', new Date(post.last_activity_date * 1000));
    link.textContent = post.title 
      + ' ('
      + 'score:'
      + post.score
      + ', ans:'
      + post.answer_count
      + ' )' ;
    return link;
  }
   
  function buildQuestion(site, item) {
     var cont = document.createElement('div');
     cont.appendChild(buildSite(site));
     cont.appendChild(buildLink(item, site));
     return cont;
  }
 
  var queue = [];
  var backlog = []; // ms to backoff
  var backoffTimer;
  var boIsRunning = false;
  var isCalled = false;
  
  function get(url, callback) {
    queue.push(function() { realget(url, callback); });
    if (!isCalled) processQueue();
  }
  
  function processQueue() {
    console.log('bl ',backlog.length, 'ql' ,  queue.length);
    if (backlog.length > 0) {
      if (boIsRunning) {
         // backoffTimer running
      } else {
        console.log('bo set');
        backoffTimer = setTimeout(()=>{
          clearTimeout(backoffTimer);
          boIsRunning = false;
          processQueue();
        }, backlog.shift());
        boIsRunning = true;
      }
    } else {
      if (queue.length > 0) {
         isCalled = true;
         var action = queue.shift();
         if (action != null) { 
            // prevent CORS error so throttle pre-emptively
            backlog.push((queue.length + 100));
            action();
         }
         
      } else {
        backlog.push(100);
        isCalled = false;
      }
    }
  }
  
  var errors = 0;
  // used when we are good to call
  function realget(url, callback) {
     var xhr = new XMLHttpRequest();
     xhr.open('GET', url);
     xhr.addEventListener('load', function() {
       var data;
       if (xhr.readyState === 4 && xhr.status === 200) {
         data = JSON.parse(xhr.responseText);
         if (data.backoff) backlog.push((data.backoff || 0 ) * 1000); 
         if (errors > 0 ) errors--;
         processQueue();
         callback(data);
       } else {
         console.log('readystate and status ', xhr.readyState, xhr.status);  
       }
    });
    
    xhr.addEventListener('error', function(err) {
      console.log('error for ', url, err, xhr.status);
      errors++;
      err.preventDefault();
      document.getElementById('msg').textContent = '(throttled, waiting for ' + (errors * 20) + ' seconds)';
      backlog.unshift(errors * 20000);  
      queue.unshift(function() { 
        document.getElementById('msg').textContent = 'Sites being processed:';  
        realget(url, callback);
      });
      processQueue();
    });
    
    xhr.addEventListener('abort', function() {
      console.log('abort for ', url, xhr.status);
    });
    xhr.send();
  }


  function findProgressItem(site) {
    return document.querySelector(
      '#prog li[data-site="' + 
      site.site_url.replace('https://','') +
      '"]');
  }
  
  function updateProgress(site) {
      var item = findProgressItem(site);
      item.textContent = null;
      item.appendChild(buildSite(site));
  }
  
  function loadUserFeatured(userid, site) {
     updateProgress(site);
     get(createUserFeaturedUrl(userid, site.api_site_parameter), function(data) {
       console.log(site);
       data.items.forEach( function(item) {
           console.log(item);
           find('fav').appendChild(buildQuestion(site, item));
       }); 
       var item = findProgressItem(site);
       item.parentElement.removeChild(item);
    });
  }
  
  
  function loadUserFromAccount(item) {
    var uid = item.user_id;
    var site = item.site_url.replace('https://','');
    find('prog').appendChild(buildProgress(site));
    
    if (sites[site]) {
      loadUserFeatured(uid, sites[site]);
    } else {
      get(createInfoUrl(site), function(data) {
        sites[site] = data.items[0].site;
        loadUserFeatured(uid, sites[site]);
      });
    }
  }
  
  function loadAccounts(accountId) {
    get(createAccountsUrl(accountId), function(data) {
      var fav = find('fav');
      while (fav.firstChild) {
        fav.removeChild(fav.firstChild);
      }
      data.items.forEach( function(item) {
         loadUserFromAccount(item);
      });
    });
  }
    
  find('go').addEventListener(
    'click', 
    function() {
        loadAccounts(find('accountid').value);
    });
  
}());
img {
  height: 1em;
  }
<div id="error" style="display:none">
</div>
<div>
<label>Account id:</label><input type="text" id="accountid" />
</div>
<div>
<button id="go">Search</button>
</div>
<p id="msg">Sites being processed:</p>
<ul id="prog"></ul>
<p>Bounties:</p>
<div id="fav">
  
</div>

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .