Save Player Drawings

Click on a user and then click Track to automatically download their drawings after their turn.

  1. // ==UserScript==
  2. // @name Save Player Drawings
  3. // @namespace https://greasyfork.org/users/281093
  4. // @match https://sketchful.io/
  5. // @grant none
  6. // @version 1.0
  7. // @author Bell
  8. // @license MIT
  9. // @copyright 2020, Bell
  10. // @description Click on a user and then click Track to automatically download their drawings after their turn.
  11. // ==/UserScript==
  12. /* jshint esversion: 6 */
  13.  
  14. const trackedPlayers = JSON.parse(localStorage.getItem('trackedPlayers')) || [];
  15. const userModalHeader = document.querySelector('#userModal div.modal-header');
  16. const chat = document.querySelector('#gameChatList');
  17. const buttonTextOff = 'Track';
  18. const buttonTextOn = 'Untrack';
  19. const buttonStyle = `width: 100px; top: 70px; margin-right:
  20. 16px; position: absolute; right: 0px`;
  21.  
  22. (function addButton() {
  23. const modalBody = document.querySelector('#userModal div.modal-body');
  24. const button = document.createElement('button');
  25. button.setAttribute('class', 'btn btn-primary');
  26. button.setAttribute('style', buttonStyle);
  27. button.textContent = buttonTextOff;
  28. button.id = 'trackButton';
  29. button.onclick = toggleTrackedPlayer;
  30. modalBody.appendChild(button);
  31. })();
  32.  
  33. function toggleTrackedPlayer() {
  34. const player = document.querySelector('#userModal h3').textContent;
  35. const button = document.querySelector('#trackButton');
  36. const tracked = trackedPlayers.includes(player);
  37.  
  38. if (tracked) {
  39. trackedPlayers.splice(trackedPlayers.indexOf(player), 1);
  40. button.textContent = buttonTextOff;
  41. }
  42. else {
  43. trackedPlayers.push(player);
  44. button.textContent = buttonTextOn;
  45. }
  46.  
  47. localStorage.setItem('trackedPlayers', JSON.stringify(trackedPlayers));
  48. }
  49.  
  50. const updateHeader = (mutations) => {
  51. mutations.forEach(mutation => {
  52. const newNodes = mutation.addedNodes;
  53. if (!newNodes.length) return;
  54. updateButtonState(newNodes[0]);
  55. });
  56. };
  57.  
  58. new MutationObserver(updateHeader)
  59. .observe(userModalHeader, { childList: true, subtree: true });
  60.  
  61. function updateButtonState(nameNode) {
  62. const player = nameNode.textContent;
  63. const button = document.querySelector('#trackButton');
  64. const tracked = trackedPlayers.includes(player);
  65. button.textContent = tracked ? buttonTextOn : buttonTextOff;
  66. }
  67.  
  68. const checkChat = (mutations) => {
  69. mutations.forEach(mutation => {
  70. const msgNode = mutation.addedNodes[0];
  71. if (!msgNode || !msgNode.classList.contains('chatAdmin')) return;
  72. parseSystemMessage(msgNode.textContent);
  73. });
  74. };
  75.  
  76. new MutationObserver(checkChat).observe(chat, { childList: true });
  77.  
  78. function downloadDrawing(name) {
  79. const a = document.createElement('a');
  80. a.download = name + '.png';
  81. a.href = document.querySelector('#canvas').toDataURL();
  82. a.click();
  83. }
  84.  
  85. function parseSystemMessage(message) {
  86. if (message.startsWith('The word was')) {
  87. const word = /: ([a-zA-Z0-9 -]+)/.exec(message)[1];
  88. const drawerNode = document.querySelector('.gameDrawing').parentElement;
  89. let drawerName = drawerNode.querySelector('.gameAvatarName').textContent;
  90.  
  91. if (!trackedPlayers.includes(drawerName)) return;
  92.  
  93. drawerName = drawerName.replaceAll(/[\/\\*:>?<|"]/, '');
  94. if (!drawerName.length) drawerName = 'anon';
  95. downloadDrawing(`${word} by ${drawerName}`);
  96. }
  97. }