WaniKani Please Check Spelling

Plural-accepting no-misspelling script (No Cigar)

  1. // ==UserScript==
  2. // @name WaniKani Please Check Spelling
  3. // @namespace http://www.wanikani.com
  4. // @version 0.4.1
  5. // @description Plural-accepting no-misspelling script (No Cigar)
  6. // @author polv
  7. // @match https://www.wanikani.com/extra_study/session*
  8. // @match https://www.wanikani.com/review/session*
  9. // @match https://www.wanikani.com/subjects/*
  10. // @match https://preview.wanikani.com/extra_study/session*
  11. // @match https://preview.wanikani.com/review/session*
  12. // @match https://preview.wanikani.com/subjects/*
  13. // @icon https://www.google.com/s2/favicons?sz=64&domain=wanikani.com
  14. // @license MIT
  15. // @require https://gf.qytechs.cn/scripts/470201-wanikani-answer-checker/code/WaniKani%20Answer%20Checker.js?version=1215595
  16. // @homepage https://gf.qytechs.cn/en/scripts/465750-wanikani-please-check-spelling
  17. // @supportURL https://community.wanikani.com/t/userscript-plz-check-spelling-no-cigar-but-accept-plural-and-no-space-variants/61763
  18. // @source https://github.com/patarapolw/wanikani-userscript/blob/master/userscripts/plz-check-spelling.user.js
  19. // @grant none
  20. // ==/UserScript==
  21.  
  22. // @ts-check
  23. /// <reference path="./types/answer-checker.d.ts" />
  24. (function () {
  25. 'use strict';
  26.  
  27. /**
  28. * !No cigar section
  29. * @see https://community.wanikani.com/t/userscript-plz-check-spelling-no-cigar-but-accept-plural-and-no-space-variants/61763
  30. * @see https://community.wanikani.com/t/userscript-prevent-your-answer-was-a-bit-off-answers-from-being-accepted-aka-close-but-no-cigar/7134
  31. */
  32. window.modAnswerChecker.register((e, tryCheck) => {
  33. if (isWrongAnswer) {
  34. return {
  35. action: 'fail',
  36. message: null,
  37. };
  38. }
  39.  
  40. if (e.questionType !== 'reading') {
  41. const result = tryCheck(e);
  42. if (isForcedAccept) return result;
  43.  
  44. console.log(result, e);
  45.  
  46. if (
  47. result.action === 'pass' &&
  48. result.message?.type === 'itemInfoException'
  49. ) {
  50. const { meanings = [], auxiliary_meanings = [] } = e.item;
  51. const { userSynonyms = [] } = e;
  52.  
  53. const re = new RegExp(
  54. `^\\W*(${[
  55. ...meanings,
  56. ...userSynonyms,
  57. ...auxiliary_meanings
  58. .filter((m) => m.type === 'whitelist')
  59. .map((m) => m.meaning),
  60. ]
  61. .map((m) => {
  62. m = m.toLocaleLowerCase();
  63.  
  64. const tokens = m.split(/\W+/g);
  65. const isVerb = tokens[0] === 'to';
  66.  
  67. const out = [];
  68.  
  69. tokens.map((t, i) => {
  70. let ed = '\\W*';
  71.  
  72. if (
  73. ['to', 'in', 'on', 'at', 'of', 'and', 'with', 'be'].includes(
  74. t,
  75. )
  76. ) {
  77. ed = '\\W+';
  78. } else if (['something', 'a', 'an', 'the'].includes(t)) {
  79. t = `(${t})?`;
  80. } else {
  81. t = makePlural(t);
  82. }
  83.  
  84. out.push(t);
  85. if (i < tokens.length - 1) {
  86. out.push(ed);
  87. }
  88. });
  89. return out.join('');
  90. })
  91. .join('|')})\\W*$`,
  92. 'i',
  93. );
  94. console.log(re);
  95.  
  96. if (!re.test(e.response.toLocaleLowerCase().trim())) {
  97. return {
  98. action: 'retry',
  99. message: {
  100. text: 'Close, but no cigar! Please try again',
  101. type: 'answerException',
  102. },
  103. };
  104. }
  105. }
  106. }
  107.  
  108. return null;
  109. });
  110.  
  111. /**
  112. *
  113. * @param {string} s
  114. * @returns
  115. */
  116. function makePlural(s) {
  117. if (s.length > 2) {
  118. const yPlural = ['y', 'ys', 'ies'];
  119. for (const p of yPlural) {
  120. if (s.endsWith(p)) {
  121. return s.substring(0, s.length - p.length) + `(${yPlural.join('|')})`;
  122. }
  123. }
  124.  
  125. const sPlural = ['s', 'es'];
  126. for (const p of sPlural) {
  127. if (s.endsWith(p)) {
  128. return s.substring(0, s.length - p.length) + `(${p})?`;
  129. }
  130. }
  131.  
  132. return s + `(${sPlural.join('|')})?`;
  133. }
  134.  
  135. return s;
  136. }
  137.  
  138. /** @type {HTMLInputElement | null} */
  139. let inputContainer = null;
  140. let qType = '';
  141. let isWrongAnswer = false;
  142. let isForcedAccept = false;
  143.  
  144. addEventListener('willShowNextQuestion', (e) => {
  145. // @ts-ignore
  146. const { questionType } = e.detail;
  147. qType = questionType;
  148.  
  149. isWrongAnswer = false;
  150.  
  151. if (!inputContainer) {
  152. inputContainer = document.querySelector('input[name="user-response"]');
  153. if (inputContainer) {
  154. const el = inputContainer;
  155. el.addEventListener('keydown', (ev) => {
  156. if (el.getAttribute('enabled') !== 'true') return;
  157. if (ev.key === 'Escape' || ev.code === 'Escape') {
  158. // https://community.wanikani.com/t/userscript-i-dont-know-button/7231
  159. const msg =
  160. qType === 'reading'
  161. ? 'えええーさっぱりわからないいいい'
  162. : 'Aargh! What does that even mean? (╯°□°)╯︵ ┻━┻';
  163.  
  164. if (el.value === msg) {
  165. el.value = '';
  166. isWrongAnswer = false;
  167. } else {
  168. el.value = msg;
  169. isWrongAnswer = true;
  170. }
  171.  
  172. // manual submit
  173. } else if (ev.key === 'Enter') {
  174. isForcedAccept = ev.shiftKey || ev.ctrlKey;
  175. } else if (ev.code.startsWith('Key')) {
  176. isWrongAnswer = false;
  177. }
  178. });
  179. }
  180. }
  181. });
  182. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址