anuisud1

Untitled

Apr 6th, 2019
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.17 KB | None | 0 0
  1. // ==UserScript==
  2. // @author Ross Hill <rosshill.ca>
  3. // @connect skribbler.herokuapp.com
  4. // @grant GM_xmlhttpRequest
  5. // @grant GM.xmlHttpRequest
  6. // @homepageURL https://github.com/rosslh/skribbler
  7. // @icon https://skribbl.io/res/favicon.png
  8. // @licence MIT
  9. // @match *://skribbl.io/*
  10. // @name Skribbler
  11. // @namespace https://rosshill.ca
  12. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
  13. // @supportURL https://github.com/rosslh/skribbler/issues
  14. // @version 2.6.3
  15. // ==/UserScript==
  16. const state = {
  17. content: document.createElement("span"),
  18. links: document.createElement("strong"),
  19. pattern: "",
  20. prevAnswer: "",
  21. prevClue: "",
  22. wordsList: $(document.createElement("ul"))
  23. };
  24. unsafeWindow.dictionary = {
  25. confirmed: [],
  26. guessed: [],
  27. oneOffWords: [],
  28. standard: [],
  29. validAnswers: []
  30. };
  31. function scrollDown() {
  32. if ($("#screenGame").is(":visible") &&
  33. $("html").scrollTop() < $("#screenGame").offset().top) {
  34. $("html, body").animate({
  35. scrollTop: $("#screenGame").offset().top
  36. }, 1000);
  37. }
  38. }
  39. function getPlayer() {
  40. const nameElem = $('.info .name[style="color: rgb(0, 0, 255);')[0];
  41. if (typeof nameElem !== "undefined") {
  42. return nameElem.innerText.split(" (")[0];
  43. }
  44. return "";
  45. }
  46. function validClue(clue, minCharsFound) {
  47. const someoneDrawing = $(".drawing").is(":visible");
  48. const charsFound = clue.replace(/_|-| /g, "").length;
  49. const noUnderscores = clue.replace(/_/g, "").length;
  50. if (someoneDrawing &&
  51. (unsafeWindow.dictionary.oneOffWords.length > 0 ||
  52. (charsFound >= minCharsFound && noUnderscores !== clue.length))) {
  53. return true;
  54. }
  55. if (!someoneDrawing) {
  56. unsafeWindow.dictionary.validAnswers = [];
  57. unsafeWindow.dictionary.guessed = [];
  58. unsafeWindow.dictionary.oneOffWords = [];
  59. }
  60. return false;
  61. }
  62. function wordGuessed() {
  63. if ($('.guessedWord .info .name[style="color: rgb(0, 0, 255);"]').length) {
  64. unsafeWindow.dictionary.validAnswers = [];
  65. unsafeWindow.dictionary.guessed = [];
  66. unsafeWindow.dictionary.oneOffWords = [];
  67. return true;
  68. }
  69. return false;
  70. }
  71. function missingChar(short, long) {
  72. for (let i = 1; i < long.length + 1; i++) {
  73. if (short === long.substring(0, i - 1) + long.substring(i, long.length)) {
  74. return true;
  75. }
  76. }
  77. return false;
  78. }
  79. function oneOff(listWord, guessedWord) {
  80. if (listWord.length === guessedWord.length) {
  81. let wrongLetters = 0;
  82. for (let i = 0; i < listWord.length; i++) {
  83. if (listWord.charAt(i) !== guessedWord.charAt(i)) {
  84. wrongLetters += 1;
  85. }
  86. if (wrongLetters > 1) {
  87. return false;
  88. }
  89. }
  90. return wrongLetters === 1;
  91. }
  92. if (listWord.length === guessedWord.length - 1) {
  93. return missingChar(listWord, guessedWord);
  94. }
  95. if (guessedWord.length === listWord.length - 1) {
  96. return missingChar(guessedWord, listWord);
  97. }
  98. return false;
  99. }
  100. function checkPastGuesses(notOBO, word) {
  101. if (unsafeWindow.dictionary.guessed.indexOf(word) !== -1) {
  102. return false;
  103. }
  104. for (const oneOffWord of unsafeWindow.dictionary.oneOffWords) {
  105. if (!oneOff(word, oneOffWord)) {
  106. return false;
  107. }
  108. }
  109. for (const str of notOBO) {
  110. if (oneOff(word, str)) {
  111. return false;
  112. }
  113. }
  114. return true;
  115. }
  116. function getRegex(clue) {
  117. return new RegExp(`^${clue.replace(/_/g, "[^- ]")}$`);
  118. }
  119. function filterWords(words, notOBO, clue) {
  120. return words
  121. .filter(word => word.length === clue.length &&
  122. state.pattern.test(word) &&
  123. checkPastGuesses(notOBO, word))
  124. .sort();
  125. }
  126. function getWords(clue) {
  127. const dict = unsafeWindow.dictionary;
  128. let words;
  129. if (dict.validAnswers.length === 0) {
  130. // && dict.guessed.length === 0
  131. words = dict.confirmed.slice();
  132. for (const item of dict.standard) {
  133. if (words.indexOf(item) === -1) {
  134. words.push(item);
  135. }
  136. }
  137. }
  138. else {
  139. words = dict.validAnswers;
  140. }
  141. state.pattern = getRegex(clue);
  142. const notOBO = [];
  143. for (const word of dict.guessed) {
  144. if (dict.oneOffWords.indexOf(word) === -1) {
  145. notOBO.push(word);
  146. }
  147. }
  148. if (!wordGuessed()) {
  149. dict.validAnswers = filterWords(words, notOBO, clue);
  150. }
  151. else {
  152. dict.validAnswers = [];
  153. }
  154. return dict.validAnswers;
  155. }
  156. function constructWordsList(clue) {
  157. const newList = $(document.createElement("ul"));
  158. if (validClue(clue, 0) && !wordGuessed()) {
  159. const words = getWords(clue);
  160. for (const word of words) {
  161. const item = document.createElement("li");
  162. const child = $(`<span onClick="submitGuess('${word}')">${word}</span>`);
  163. child.css({ cursor: 'pointer', textDecoration: 'underline', textDecorationStyle: 'dotted' });
  164. if (unsafeWindow.dictionary.confirmed.indexOf(word) > -1) {
  165. child.css({ fontWeight: 'bold' });
  166. }
  167. $(item).append(child);
  168. newList.append(item);
  169. }
  170. }
  171. state.wordsList.html(newList.html());
  172. state.wordsList.css({
  173. width: `${$(document).width() - $("#containerChat").width() - 40}px`
  174. });
  175. }
  176. function getClue() {
  177. return $("#currentWord");
  178. }
  179. function getClueText() {
  180. return getClue()[0].textContent.toLowerCase();
  181. }
  182. function findGuessedWords() {
  183. const player = getPlayer();
  184. if (player) {
  185. const guesses = $(`#boxMessages p[style='color: rgb(0, 0, 0);'] b:contains(${player}:)`)
  186. .parent()
  187. .find("span")
  188. .not(".skribblerHandled")
  189. .slice(-10);
  190. guesses.each((i, elem) => {
  191. const guessText = elem.innerText;
  192. if (unsafeWindow.dictionary.guessed.indexOf(guessText) === -1) {
  193. unsafeWindow.dictionary.guessed.push(guessText);
  194. elem.classList.add("skribblerHandled");
  195. constructWordsList(getClueText());
  196. }
  197. });
  198. }
  199. }
  200. function findCloseWords() {
  201. const close = $("#boxMessages p[style='color: rgb(204, 204, 0); font-weight: bold;'] span:contains( is close!)")
  202. .not(".skribblerHandled")
  203. .slice(-10);
  204. close.each((i, elem) => {
  205. const text = elem.innerText.split("'")[1];
  206. if (unsafeWindow.dictionary.oneOffWords.indexOf(text) === -1) {
  207. unsafeWindow.dictionary.oneOffWords.push(text);
  208. elem.classList.add("skribblerHandled");
  209. constructWordsList(getClueText());
  210. }
  211. });
  212. }
  213. unsafeWindow.getInput = () => $("#inputChat");
  214. function validateInput() {
  215. const word = getClueText();
  216. const input = unsafeWindow.getInput()[0];
  217. const remaining = word.length - input.value.length;
  218. state.content.textContent = remaining;
  219. state.content.style.color = "unset";
  220. if (remaining > 0) {
  221. state.content.textContent = `+${state.content.textContent}`;
  222. state.content.style.color = "green";
  223. }
  224. else if (remaining < 0) {
  225. state.content.style.color = "red";
  226. }
  227. state.pattern = getRegex(word);
  228. const short = getRegex(word.substring(0, input.value.length));
  229. if (state.pattern.test(input.value.toLowerCase())) {
  230. input.style.border = "3px solid green";
  231. }
  232. else if (short.test(input.value.toLowerCase())) {
  233. input.style.border = "3px solid orange";
  234. }
  235. else {
  236. input.style.border = "3px solid red";
  237. }
  238. }
  239. function showDrawLinks(clueText) {
  240. if (clueText.length > 0 && clueText.indexOf("_") === -1) {
  241. state.links.innerHTML = `<a style='color: blue' target='_blank'
  242. href='https://www.google.ca/search?tbm=isch&q=${clueText}'>Images</a>, `;
  243. state.links.innerHTML += `<a style='color: blue' target='_blank'
  244. href='https://www.google.ca/search?tbm=isch&tbs=itp:lineart&q=${clueText}'>Line art</a>`;
  245. }
  246. else {
  247. state.links.innerHTML = "";
  248. }
  249. }
  250. function clueChanged() {
  251. const clue = getClueText();
  252. if (clue !== state.prevClue) {
  253. state.prevClue = clue;
  254. validateInput();
  255. constructWordsList(clue);
  256. showDrawLinks(clue);
  257. }
  258. }
  259. function answerShown(username, password) {
  260. let answer = $("#overlay .content .text")[0].innerText;
  261. if (answer.slice(0, 14) === "The word was: ") {
  262. answer = answer.slice(14);
  263. if (answer !== state.prevAnswer) {
  264. state.prevAnswer = answer;
  265. unsafeWindow.dictionary.oneOffWords = [];
  266. unsafeWindow.dictionary.guessed = [];
  267. unsafeWindow.dictionary.validAnswers = [];
  268. if (admin) {
  269. handleWord(answer, username, password);
  270. }
  271. }
  272. }
  273. }
  274. function makeGuess(clue) {
  275. if (validClue(clue, 1) && !wordGuessed()) {
  276. const words = unsafeWindow.dictionary.validAnswers;
  277. const confWords = [];
  278. for (const item of words) {
  279. if (unsafeWindow.dictionary.confirmed.indexOf(item) > -1) {
  280. confWords.push(item);
  281. }
  282. }
  283. let guess;
  284. if (confWords.length > 0) {
  285. guess = confWords[Math.floor(Math.random() * confWords.length)];
  286. }
  287. else {
  288. guess = words[Math.floor(Math.random() * words.length)];
  289. }
  290. guessWord(guess, clue);
  291. }
  292. }
  293. unsafeWindow.submitGuess = (guess) => {
  294. const submitProp = Object.keys(unsafeWindow.formChat).filter((k) => ~k.indexOf("jQuery") // tslint:disable-line no-bitwise
  295. )[0];
  296. unsafeWindow.getInput().val(guess);
  297. unsafeWindow.formChat[submitProp].events.submit[0].handler();
  298. };
  299. function guessWord(guess, clue) {
  300. window.setTimeout(() => {
  301. if (unsafeWindow.getInput().val() === "" && validClue(clue, 1) && !wordGuessed()) {
  302. unsafeWindow.submitGuess(guess);
  303. }
  304. }, Math.floor(Math.random() * (Number($("#guessRate").val()) / 3)));
  305. }
  306. function toggleWordsList() {
  307. if ($(state.wordsList).is(":visible")) {
  308. if (state.wordsList.children().length === 0 ||
  309. wordGuessed() ||
  310. !validClue(getClueText(), 0)) {
  311. state.wordsList.hide();
  312. }
  313. }
  314. else if (state.wordsList.children().length > 0 &&
  315. !wordGuessed() &&
  316. validClue(getClueText(), 0)) {
  317. state.wordsList.show();
  318. }
  319. }
  320. function stillHere() {
  321. if (document.hidden &&
  322. $(".modal-dialog:contains(Are you still here?)").is(":visible")) {
  323. alert("Action required.");
  324. }
  325. }
  326. function main(username, password) {
  327. $("#audio").css({
  328. left: "unset",
  329. right: "0px"
  330. }); // so it doesn't cover timer
  331. window.setInterval(scrollDown, 2000);
  332. $(state.links).css({
  333. padding: "0 1em 0 1em"
  334. });
  335. getClue().after(state.links);
  336. const formArea = $("#formChat")[0];
  337. $(state.content).css({
  338. left: "295px",
  339. position: "relative",
  340. top: "-25px"
  341. });
  342. state.wordsList.css({
  343. "background-color": "#eee",
  344. "border-radius": "2px",
  345. columns: "4",
  346. "list-style-position": "inside",
  347. "margin-top": "10px",
  348. padding: "4px",
  349. width: "70%"
  350. });
  351. formArea.appendChild(state.content);
  352. $("#screenGame")[0].appendChild(state.wordsList[0]);
  353. const input = unsafeWindow.getInput()[0];
  354. input.style.border = "3px solid orange";
  355. window.setInterval(() => {
  356. clueChanged();
  357. answerShown(username, password);
  358. findCloseWords();
  359. findGuessedWords();
  360. toggleWordsList();
  361. stillHere();
  362. }, 1000);
  363. $("#boxChatInput").append($(`<div style="background-color:#eee; position:relative;
  364. top:-20px; padding:0 5px; width:auto; margin:0;">
  365. <input id="guessEnabled" name="guessEnabled" style="width:6px; height:6px;" type="checkbox">
  366. <label for="guessEnabled" style="all: initial; padding-left:5px;">Enable auto-guesser</label><br>
  367. <label for="guessRate" style="all: initial; padding-right:5px;">Guess frequency (seconds):</label>
  368. <input id="guessRate" name="guessRate" type="number" step="0.5" min="1" value="1.5" style="width:4em;"></div>`));
  369. let lastGuess = 0;
  370. let lastTyped = 0;
  371. window.setInterval(() => {
  372. if ($("#guessEnabled").is(":checked") &&
  373. Date.now() - lastTyped >= 1500 &&
  374. Date.now() - lastGuess >= 1000 * Number($("#guessRate").val())) {
  375. lastGuess = Date.now();
  376. makeGuess(getClueText());
  377. }
  378. }, 500);
  379. unsafeWindow.getInput().keyup(() => {
  380. lastTyped = Date.now();
  381. });
  382. unsafeWindow.getInput().keyup(validateInput);
  383. }
  384. function fetchWords(username, password) {
  385. GM.xmlHttpRequest({
  386. method: "GET",
  387. url: "https://skribbler.herokuapp.com/api/words",
  388. onload(res) {
  389. const response = JSON.parse(res.responseText);
  390. unsafeWindow.dictionary.standard = response.default;
  391. unsafeWindow.dictionary.confirmed = response.confirmed;
  392. const run = window.setInterval(() => {
  393. if (getClue()) {
  394. clearInterval(run);
  395. main(username, password);
  396. }
  397. }, 1000);
  398. }
  399. });
  400. }
  401. $(document).ready(() => {
  402. if (typeof GM === "undefined") {
  403. // polyfill GM4
  404. GM = {
  405. xmlHttpRequest: GM_xmlhttpRequest
  406. };
  407. }
  408. let activate;
  409. if (admin) {
  410. activate = $("<button>Activate skribbler (admin)</button>");
  411. }
  412. else
  413. activate = $("<button>Activate skribbler</button>");
  414. activate.css({
  415. "font-size": "0.6em"
  416. });
  417. $(".loginPanelTitle")
  418. .first()
  419. .append(activate);
  420. activate.click(() => {
  421. activate.hide();
  422. if (admin) {
  423. getLoginDetails();
  424. }
  425. else {
  426. fetchWords("", "");
  427. }
  428. });
  429. });
  430. const handleWord = (clue, username, password) => { };
  431. const getLoginDetails = () => { };
  432. const admin = false;
Add Comment
Please, Sign In to add comment