Skip to content

Instantly share code, notes, and snippets.

@porglezomp
Last active February 8, 2021 20:07
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save porglezomp/4908b3a3ec81139cc50d718db9566c3c to your computer and use it in GitHub Desktop.
Save porglezomp/4908b3a3ec81139cc50d718db9566c3c to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Animal Crossing Twitter Rating
// @description Display the rating of tweets using the Animal Crossing letter rating system
// @version 1
// @grant none
// @include https://twitter.com/*
// ==/UserScript==
// Notes via https://twitter.com/jamchamb_/status/1025977659522789376
// As soon as you drop off a letter at the post office it's given a score.
// The score is determined by a series of 7 different checks, labeled A through G.
function computeScore(msg) {
// A: Checks for punctuation at the end of sentences, and capital letters.
// A '.', '?', or '!' symbol at the end of the letter grants 20 points.
// After that, every punctuation mark must be followed by a capital letter within 3 spaces (+10 or -10 each occurrence)
function A(msg) {
let score = /[.?!]\s*$/.test(msg) ? 20 : 0;
while (msg !== "") {
if (/^[.!?](.|\n){0,2}[A-Z]/.test(msg)) {
score += 10;
} else if (/^[.!?][^A-Z]{3}/.test(msg)) {
score -= 10;
}
msg = msg.substring(1);
}
return score;
}
// B: Checks the beginning of each word for common trigrams, using this lookup table:
// https://github.com/jamchamb/ac-letter-score/blob/master/trigram_table.txt
// The total number of trigram hits is multiplied by 3 and added to the score.
function B(msg) {
return (msg.match(TRIGRAM_REGEX) || []).length * 3;
}
// C: This checks for the first (non-space) character in the letter.
// If it's a capital letter, gives +20 points. If not, -10 points.
function C(msg) {
if (/^\s*[A-Z]/.test(msg)) {
return 20;
} else {
return -10;
}
}
// D: If a letter of the alphabet is repeated three times in a row (e.g. REEE), deducts 50 points and stops checking.
// Otherwise it does not affect the score.
function D(msg) {
if (/([A-Za-z])\1\1/.test(msg)) {
return -50;
} else {
return 0;
}
}
// E: Checks ratio of spaces to non-spaces in the letter.
// If it's all spaces, or (n_spaces * 100 / n_other) is less than 20, gives -20 points.
// Otherwise, +20 points.
function E(msg) {
if (msg.trim() === "") {
return -20;
}
const num_spaces = (msg.match(/\s/g) || []).length;
const num_other = msg.length - num_spaces;
if (num_spaces * 100 / num_other < 20) {
return -20;
} else {
return 20;
}
}
// F: If the letter is more than 75 characters long and goes on for at least 75 characters without
// any punctuation (., ?, !), it deducts 150 points!
function F(msg) {
if (msg.length > 75 && /[^.?!]{75}/.test(msg)) {
return -150;
} else {
return 0;
}
}
// G: Checks each group of 32 characters for at least one space.
// Deducts 20 points for each group that doesn't have one.
function G(msg) {
const chunks = msg.match(/(.|\n){32}/g) || [];
return chunks.map(x => /\s/.test(x) ? 0 : -20).reduce((x, y) => x + y, 0);
}
const a = A(msg);
const b = B(msg);
const c = C(msg);
const d = D(msg);
const e = E(msg);
const f = F(msg);
const g = G(msg);
// console.log(msg, {a, b, c, d, e, f, g});
return a + b + c + d + e + f + g;
}
// We can display this on tweets!
function updateScoreOnTweet(tweetElem) {
// We've already scored this tweet, early return
if ('data-ac-score' in tweetElem.attributes) {
return;
}
// See if we can get the text element of the tweet
const tweetTextElem = tweetElem.querySelector('.tweet-text');
if (!tweetTextElem) {
return;
}
// Extract the tweet text. We should probably do extra filtering for links and things?
const text = tweetTextElem.innerText;
// We compute the score from the tweet text and cache it.
const score = computeScore(text);
tweetElem.attributes['data-ac-score'] = score;
// Stick the score on the tweet
const scoreElem = getScoreElem(tweetElem);
if (!scoreElem) {
return;
}
scoreElem.querySelector('.ProfileTweet-actionCountForPresentation').innerText = score;
}
function getScoreElem(tweetElem) {
const res = tweetElem.querySelector('.ProfileTweet-action.ProfileTweet-action--acpoints');
if (res) {
return res;
}
const fav = tweetElem.querySelector('.ProfileTweet-action.ProfileTweet-action--favorite');
if (!fav) {
return null;
}
fav.insertAdjacentHTML('afterend',
`<div class="ProfileTweet-action ProfileTweet-action--acpoints">
<div class="ProfileTweet-actionButton">
<div class="IconContainer js-tooltip" data-original-title="Animal Crossing Score">
<span role="presentation" class="Icon Icon--clock Icon--crescent"></span>
<span class="u-hiddenVisually">Animal Crossing Score</span>
</div>
<span class="ProfileTweet-actionCount" data-tweet-stat-count="0">
<span class="ProfileTweet-actionCountForPresentation" aria-hidden="true">0</span>
</span>
</div>
</div>`);
return tweetElem.querySelector('.ProfileTweet-action.ProfileTweet-action--acpoints');
}
TRIGRAM_REGEX = /\b(abl|abo|abr|abs|acc|ach|acr|act|add|adm|adv|aer|aff|afr|aft|aga|age|ago|ahe|air|ali|all|alm|alo|alr|als|alt|alw|am|ame|amo|and|ang|ani|ano|ans|any|apa|app|apr|are|arg|arm|arr|art|asi|ask|asl|ate|atm|att|aud|aug|aut|ave|avo|awa|cak|cal|cam|can|cap|car|cas|cat|cau|cen|cer|cha|che|chi|cho|chi|chu|cir|cit|cla|cle|cli|clo|coa|cof|coi|col|com|con|coo|cop|cor|cos|cou|cov|cow|cre|cri|cro|cry|cup|cur|cus|cut|bab|bac|bad|bag|bal|ban|bas|bat|be|bea|bec|bed|bee|bef|beg|beh|bel|bes|bet|bey|bic|big|bik|bil|bir|bit|bla|ble|blo|blu|boa|bod|bon|boo|borbot|bou|box|boy|bra|bre|bri|bro|bui|bur|bus|but|buy|by|eac|ear|eas|eat|edu|eff|egg|eig|eit|ele|els|emp|end|ene|eng|enj|eno|ent|equ|err|esp|eur|eve|exa|exc|exe|exp|eye|dad|dai|dam|dan|dar|dat|dau|day|dea|dec|dee|def|deg|del|dem|den|dep|des|det|dev|dic|did|die|dif|dig|din|dir|dis|div|do|doc|doe|dog|dol|don|doo|dou|dow|doz|dra|dre|dri|dro|dru|dry|due|dur|dus|dut|gai|gam|gar|gas|gat|gav|gen|ger|get|gir|giv|gla|go|god|goi|gon|goo|got|gov|gra|gre|gro|gua|gue|gui|gun|fac|fai|fal|fam|far|fas|fat|fea|feb|fed|fee|fel|few|fie|fif|fig|fil|fin|fir|fis|fiv|fix|fla|fle|fli|flo|fly|fol|foo|for|fou|fra|fre|fri|fro|fru|ful|fun|fut|i|ice|ide|if|ima|imm|imp|in|inc|ind|inf|ins|int|inv|iro|is|isl|it|its|hab|had|hai|hal|han|hap|har|has|hat|hav|he|hea|hei|hel|her|hi|hid|hig|hil|him|hir|his|hit|hol|hom|hon|hop|hor|hos|hot|hou|how|hum|hun|hur|hus|kee|kep|key|kic|kil|kin|kit|kne|kni|kno|kab|kad|kai|kak|kan|kar|kas|kat|kau|kaw|kay|kaz|kea|ked|kef|keg|ken|kes|ket|kev|kib|kie|kif|kig|kik|kim|kin|kis|kit|kiv|koc|kon|koo|kos|kot|kou|kov|kow|kun|kyi|kac|kad|kag|kai|kaj|kak|kan|kap|kar|kat|kay|ke|kea|ked|kee|kem|ken|kes|ket|kid|kig|kil|kin|kis|kod|kom|kon|koo|kor|kos|kot|kou|kov|kuc|kum|kus|ky|kys|kam|kar|kat|kea|kec|kee|kei|kev|kew|kex|kic|kig|kin|ko|kob|koi|kon|koo|kor|kos|kot|kov|kow|kum|kbj|k'c|kct|kf|kff|kft|kh|kil|kka|kld|kn|knc|kne|knl|kpe|kpi|kpp|kr|kra|krd|kth|kur|kut|kve|kwn|jan|jap|job|joi|jud|jul|jum|jun|jus|qua|que|qui|pac|pag|pai|pap|par|pas|pat|pay|pea|pen|peo|per|pho|pic|pie|pin|pip|pla|ple|poc|poi|pol|poo|pop|pos|pot|pou|pow|pra|pre|pri|pro|pub|pul|pup|pur|pus|put|sad|saf|sai|sal|sam|san|sat|sav|saw|say|sce|sch|sci|sco|sea|sec|see|sel|sen|sep|ser|set|sev|sex|sha|she|shi|sho|shu|sic|sid|sig|sil|sim|sin|sis|sit|six|siz|ski|sky|sle|sli|slo|sma|sme|smi|smo|sno|so|soa|soc|sof|soi|sol|som|son|soo|sor|sou|spa|spe|spi|spo|spr|squ|sta|ste|sti|sto|str|stu|sty|sub|suc|sud|suf|sug|sum|sun|sup|sur|swa|swe|swi|swu|sys|rac|rad|rai|ran|rap|rat|rea|rec|red|ref|reg|rel|rem|rep|req|res|ret|ric|rid|rig|rin|ris|riv|roa|roc|rod|rol|roo|ros|rou|row|rul|run|rus|una|unc|und|uni|unl|unt|up|upo|us|use|usu|tab|tak|tal|tas|tau|tax|tea|tee|tel|tem|ten|ter|tes|tha|the|thi|tho|thr|thu|tic|tie|til|tim|tir|tit|to|tod|tog|tol|tom|ton|too|top|tor|tot|tou|tow|tra|tre|tri|tro|tru|try|tue|tur|tv|twe|twi|two|tyi|typ|val|var|veg|ver|vie|vil|vis|voi|vol|vot|vai|vak|val|van|var|vas|vat|vav|vay|ve|vea|ved|vee|vei|vel|ven|ver|ves|vet|vha|vhe|vhi|vho|vhy|vid|vif|vil|vin|vir|vis|vit|viv|vok|vom|von|voo|vor|vou|vri|vro|vma|yar|yea|yel|yen|yes|yet|you|zer)/gi;
// Constantly update the tweet scores
document.querySelectorAll('.tweet').forEach(updateScoreOnTweet)
setInterval(() => {
document.querySelectorAll('.tweet').forEach(updateScoreOnTweet)
}, 1000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment