amit / Bing Rewards

// ==UserScript==
// @name         Bing Rewards
// @version      V1.0.2
// @description  Automatically complete the Microsoft Rewards daily search task.
// @author       Amit
// @match        https://www.bing.com/*
// @match        https://rewards.bing.com/*
// @license      MIT
// @icon         https://www.bing.com/favicon.ico
// @connect      inshorts.vercel.app
// @run-at       document-end
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @grant        GM_openInTab
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @require      file:///Users/amit/Documents/code/sideProjects/bing/index.js
// ==/UserScript==
'use strict';
var pid = Date.now();
if (GM_getValue('pid') > pid) return console.log('Older instance found. Exiting...');
GM_setValue('pid', pid);


var TASK_TIMEOUT = 60 * 1000 * 3;

var max_rewards = navigator?.platform === 'Android' ? 20 : 30;
var search_words = [];
var news_api = "https://inshorts.vercel.app/news/top";


function generateRandomString(length) {
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  let result = '';
  const charactersLength = characters.length;
  for (let i = 0; i < length; i++)
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  return result;
}


async function getSearchWords() {
  search_words = GM_getValue('search_words') ?? [];

  if (search_words.length === 0 || ((GM_getValue('search_time') ?? 0) < Date.now())) {
    console.log(`Will fetch new search words because ${search_words.length < 0 ? 'No search words' : 'Search words are expired'}`);
    let r = await fetch(news_api);
    console.log('Fetching news', r);
    let data = await r.json()
    console.log(data)
    search_words = [];
    for (let i = 0; i < data.data.articles.length; i++) {
      let item = data.data.articles[i];
      let content = item.content.split(' ');
      let len = content.length;
      search_words.push(item.title);
      search_words.push(content.slice(0, (len / 4) + rmax(-3,3) ).join(' '));
      search_words.push(content.slice(len / 4, (len / 2)+ rmax(-3,3)).join(' '));
      search_words.push(content.slice((len / 2)+ rmax(-3,3), (3 * len / 4)+ rmax(-3,3)).join(' '));
      search_words.push(content.slice((3 * len / 4) + rmax(-3,3), len).join(' '));

    }
    GM_setValue('search_words', search_words);
    GM_setValue('search_time', Date.now() + 24 * 60 * 60 * 1000);
    console.log(search_words)
    return search_words;
  } else {
    console.log('Using old search words');
    return search_words;
  }
}

function rmax(min,max){
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function isAlreadyRunningTask() {
  if ((GM_getValue('running_tasks') ?? '') !== '') {
    console.log('Task is running:', GM_getValue('running_tasks'));
    // get running since in milliseconds from now
    var running_since = Date.now() - (GM_getValue('running_since') ?? (Date.now() + TASK_TIMEOUT + 1));
    console.log('Running since:', running_since);
    if (running_since > TASK_TIMEOUT) {
      console.log('Task is running for more than 3 minutes. Clearing it');
      clearTask();
      return null;
    }
    console.log(`Task is running for ${running_since} milliseconds`);
    return running_since;
  }
  return null;
}

function startTask(task) {
  GM_setValue('running_tasks', task);
  GM_setValue('running_since', Date.now());
}

function extendTimeout() {
  GM_setValue('running_since', Date.now());
}

function clearTask() {
  GM_setValue('running_tasks', null);
  GM_setValue('running_since', null);
}

async function runSearchPageChecks() {
  if (window.location.href.includes('www.bing.com')) {
    console.log('We are on www.bing.com');
  } else return;


  if (GM_getValue('running_tasks') != 'search') {
    if (isAlreadyRunningTask() < 20000) {
      console.log('Fresh task is running:', GM_getValue('running_tasks'));
      return completeTask();
    } else if (isAlreadyRunningTask() > 20000) {
      console.log('Task is running for more than 20 seconds. Not running new task');
      return;
    }
  } else {
    if ((GM_getValue('Cnt') ?? max_rewards + 1) > max_rewards) return clearTask();
  }


  await getSearchWords();
  console.log('Executing search');
  GM_setValue('Cnt', GM_getValue('Cnt') + 1); // Increment the counter by 1
  let nowtxt = search_words[GM_getValue('Cnt')]; // Get the current search word
  fillTextbox(nowtxt, 'sb_form_q');
  await sleep(2, 'click search button');
  clickButton('sb_form_go');
};


// Define menu command: Start
let menu1 = GM_registerMenuCommand('Start Search', function () {
  GM_setValue('Cnt', 0);// Reset the counter to 0
  startTask('search');
  location.href = "https://www.bing.com/search?q=" + generateRandomString(5)
   ; //   Jump to Bing homepage
}, 'o');

//  Define menu command: Stop
let menu2 = GM_registerMenuCommand('Reset', function () {
  GM_setValue('Cnt', max_rewards + 1);
  clearTask();
}, 'o');


function log(text) {
  let tt = document.getElementsByTagName("title")[0];
  if (tt == null) return console.error('Title not found');
  tt.innerHTML = text;

}

function fillTextbox(text, id) {
  var e = document.getElementById(id);
  if (e == null) return console.error(`fillTextbox: Element with id ${id} not found`);
  e.value = text;
}


function clickButton(id) {
  var e = document.getElementById(id);
  if (e == null) return console.error(`clickButton: Element with id ${id} not found`);
  console.log('clicking on:' + id);
  e.click();
}

function setText(id, text) {
  var e = document.getElementById(id);
  if (e == null) return console.error(`setText: Element with id ${id} not found`);
  e.innerText = text;

}

function setinnerHTML(id, text) {
  var e = document.getElementById(id);
  if (e == null) return console.error(`setinnerHTML: Element with id ${id} not found`);
  e.innerHTML = text;
}

async function sleep(sec, message) {
  var ms = Math.floor(Math.random() * sec * 1000) + 5000;
  log(`${ms / 1000} sec to ${message}`);
  return new Promise(resolve => setTimeout(resolve, ms));
}



async function clickOnTask(index) {
  let containers = document.getElementsByClassName("rewards-card-container");
  containers = Array.from(containers);
  containers = containers.filter((el) => el.getElementsByClassName("mee-icon-AddMedium").length > 0);
  let task = containers[index].getElementsByTagName("a")[0];
  if (task == null) return console.error('Task not found');

  task.click();
  console.log('Clicked on task:', task);
  // await sleep(10, 'reload page');


  // location.reload();
}


async function listTasks() {
  // find all containers belonging to the class "mee-rewards-tile"
  let containers = document.getElementsByClassName("rewards-card-container");
  let tasks = [];
  for (let i = 0; i < containers.length; i++) {
    let container = containers[i];

    let title = container.getElementsByClassName("contentContainer")[0];
    if (title == null) continue;
    let points = container.getElementsByClassName("pointsString")[0];
    if (points == null) continue;
    let status = container.getElementsByClassName("mee-icon-AddMedium")[0];
    if (status == null) continue;
    let url = container.getElementsByTagName("a")[0];
    if (url == null) continue;
    tasks.push({ title: title.innerText, points: points.innerText, url: url.href });
  }

  tasks = tasks.filter((el) => !el.title.startsWith('You did it!'));

  console.log(tasks);
  GM_setValue('tasks', tasks);
  return tasks;
}


async function runRewardsPageChecks() {
  if (window.location.href.includes('rewards.bing.com')) {
    console.log('We are on rewards.bing.com');
  } else return;

  var tasks = await listTasks();
  if (tasks.length == 0) return console.log('No tasks found');

  console.log(`Checking if task is already running`);
  if (GM_getValue('running_tasks')) {
    let running_task = GM_getValue('running_tasks');
    let index = tasks.findIndex((el) => el.title === running_task);
    if (index !== -1) {
      let running_since = isAlreadyRunningTask() ?? 60 * 1000 * 5;
      if (running_since < TASK_TIMEOUT) {
        console.log('Task is running for less than 3 minutes. Will not start new task');
        return;
      }
    };
  }

  console.log(`No task is running for ${GM_getValue('running_tasks')}`);
  let task = tasks[0];
  startTask(task.title);
  console.log('Starting task:', task.title);
  if (task.url == '') {
    console.log('No URL found for task:', task.title);
    await clickOnTask(0);
  } else {
    console.log('Opening tab:', task.url);
    extendTimeout();
    let tabControl = GM_openInTab(task.url, { active: true });
    for (var i = 0; i < 60; i++) {
      var running_since = isAlreadyRunningTask() ?? 60 * 1000 * 5;
      if (running_since < TASK_TIMEOUT) {
        console.log('Waiting...');
      } else {
        sleep(15, 'reload page');
        i = 100;
      }
      await sleep(5, `Wait ${(TASK_TIMEOUT - running_since) / 1000}`);
    }
    console.log('Closing tab:', task.url);
    tabControl.close();
    sleep(5, 'reload page');
    location.reload();
  }
}

async function completeTask() {

  await completePuzzleTask();
  await completeQuizTask();
  await completePollTask();
  await completeQuiz2Task();
  await runActivityTask();

}

async function completePuzzleTask() {
  if (document.getElementsByClassName('skipPuzzle').length > 0) {
    console.log('Skipping puzzle');
    await sleep(5, 'Clicking on skip.');
    clearTask();
    document.getElementsByClassName('skipPuzzle')[0].getElementsByTagName('a')[0].click();
    await sleep(5, 'Reload. puzzle complete.');
  }
}


async function completePollTask() {
  if (document.getElementsByClassName('bt_selectedbar').length > 0) {
    console.log('Poll complete');
    clearTask();
    await sleep(5, 'Reload. Poll complete.');
    location.href = "https://rewards.bing.com/";
  }


  if (document.getElementById('btoption0') != null) {
    if (Math.random() > 0.5) clickButton('btoption0');
    else clickButton('btoption1');
  }
  if (document.getElementById('btoption0') != null) {
    if (Math.random() > 0.5) clickButton('btoption0');
    else clickButton('btoption1');
  }
}


async function completeQuizTask() {

  // Todo: Check and exit if quiz 1 is not found

  if (document.getElementById('quizCompleteContainer') != null) {
    console.log('Quiz complete');
    await sleep(5, 'Reload. Quiz complete.');
    clearTask();
    location.href = "https://rewards.bing.com/";
  }

  if (document.getElementById('rqStartQuiz') != null) clickButton('rqStartQuiz');
  await sleep(2, 'Start quiz-1');
  console.log('Completing task...');
  // initialize options and shuffle them
  var options = ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((el) => `rqAnswerOption${el}`)).sort(() => Math.random() - 0.5);
  for (var i = 0; i < 10; i++) {
    if (document.getElementById(options[i]) != null) {
      isAlreadyRunningTask();
      console.log(`Extending timeout for option ${i}...`);
      isAlreadyRunningTask();
      extendTimeout();
      await sleep(15, `Clicking option ${i}`);
      clickButton(options[i]);
    } else console.log(`${options[i]} not found`);
  }
}


async function completeQuiz2Task() {
  console.log('Checking Quiz2 task...');
  if (document.getElementById('wq_qna') == null) return console.log('Quiz2 not found');
  console.log('Quiz2 found');

  if (document.getElementsByClassName('wk_summary').length > 0) {
    console.log('Quiz2 complete');
    clearTask();
    await sleep(5, 'Reload. Quiz2 complete.');
    location.href = "https://rewards.bing.com/";
  }

  if (document.getElementsByClassName('wk_buttons').length > 0) {
    console.log(`button ${document.getElementsByClassName('wk_buttons')[0].innerHTML} found`);
    clearTask();
    let l = document.getElementsByClassName('wk_buttons')[0].getElementsByTagName('input')[0];
    console.log('Clicking on:', l.value);
    l.click();
  }


  let choices = document.getElementsByClassName('wk_choicesInstLink');
  if (choices.length == 0) {
    console.log('No choices found');
    return;
  }

  let choice = choices[Math.floor(Math.random() * choices.length)];
  extendTimeout();
  sleep(5, 'Load. Clicking choice');
  extendTimeout();
  choice.click();
  await sleep(5, 'Load. Clicked choice');
}

async function runActivityTask() {
  console.log('Running random activity....');
  // Generate random mouse movement and keyboard events on the page.
  // for 30 seconds
  for (var i = 0; i < 10; i++) {
    // debugger;
    console.log('Random activity:', i);
    var ids = ['b_header', 'b_content', 'id_rc', 'rewards_header_icon serp'];
    let id = ids[Math.floor(Math.random() * ids.length)];
    console.log('Clicking on:', id);
    let el = document.getElementById(id);
    console.log('Element:', el);
    if (el == null) {
      console.log('Element not found:', id);
      continue;
    }
    console.log('Clicking on:', id);
    el.click();

    await sleep(1, 'Random activity');

  }





}

if (document.getElementsByTagName("title")[0] == null) return;
console.log(`Started!!!! ${pid}`);
log('Waiting to start...');
await sleep(4, `Start ${pid}`);
await runSearchPageChecks();
await runRewardsPageChecks();

console.log('Complete!!!!');
log('halted...');