NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @id KongImprovements // @name KongImprovements // @version 0.6dev7 // @author KurzedMetal // @description Stuff for Kongregate Chat // @updateUrl https://openuserjs.org/install/KurzedMetal/KongImprovements.user.js // @include http://www.kongregate.com/games/* // @run-at document-end // ==/UserScript== // ----- Stuff -------------------------------------------------------------------------- /* Features: ========= * Highlight Friends, Mods, Devs & Whispers * Convert some text URLs to real links * Display "Anti-Idle: The Game" Acronyms and Abbvs * Add a "!<command>" message shortcut to access AITGBot commands TODO: ===== * Improve the text to link conversion, add more sites * Define more Anti-Idle acronyms and abbvs Changelog: ========== Version 0.3 * configure automatic updates with userscripts.org Version 0.5 * skip v0.4 uploading v0.5 by mistake * add more glossary terms * fix a bug in highlightGlossaryTerms replacements by adding word delimiters * add a message filter that redirect messages starting with '!' to the AITGBot Version 0.6 * add automatic DEBUG mode only for the coolest guy ever * add more glossary terms * improve replacement of acronyms and abbreviations * add injection of a CSS StyleSheet * replace hardcoding of styles in tags with classes defined by injected CSS * avoid breaking links * add a regex to match any link * only replace acronyms in AITG rooms */ // ----- Notes -------------------------------------------------------------------------- /* // highscore categories // first do: (guessing, stuff that gets loaded when clicking "view highscore") holodeck.loadHighScores(); // then: game_id = holodeck.chatWindow().gameId() 66881 for Anti Idle statistic_id = list of objects with descriptions in holodeck._high_scores._holodeck._statistics // access to highscores: http://www.kongregate.com/high_scores.chat?game_id=66881&statistic_id=37911&friends_page=2 http://www.kongregate.com/high_scores.chat?game_id=66881&statistic_id=37911&all_time_page=2 http://www.kongregate.com/high_scores.chat?game_id=66881&statistic_id=37911&weekly_page=2 http://www.kongregate.com/high_scores.chat?game_id=66881&statistic_id=37911&today_page=2 */ // ----- Globals ------------------------------------------------------------------------ //// access DOM through Scriptish's unsafeWindow or directly var dom = (typeof(unsafeWindow) === 'undefined' ? window : unsafeWindow); //// control console output var DEBUG = false; //// define Regular Expression substitutions for Chat Filter var regexes = [ {regexp: RegExp("(\\*\\* Bot Alert \\*\\*)"), replace: "<span class='KI_bot_alert'>$1</span>"}, {regexp: RegExp("(\\bhttps?://prntscr\.com(?:/[^' <>]{6})?\\b/?)", 'gi'), replace: "<a target='_blank' href='$1'>$1</a>"}, {regexp: RegExp("(\\bhttps?://dl\.dropboxusercontent\.com(?:[^' <>]+)?\\b/?)", 'gi'), replace: "<a target='_blank' href='$1'>$1</a>"}, {regexp: RegExp("(\\bhttps?://anti-idle-the-game\.wikia\.com(?:/wiki/([^ '<>]+))?\\b/?)", 'gi'), replace: "<a target='_blank' href='$1'>AITG Wiki: $2</a>"}, {regexp: RegExp("(\\bhttps?://(?:[^' <>]+)\\b)", 'gi'), replace: "<a target='_blank' href='$1'>$1</a>"} ]; var glossary = { 'YC': "YC: Yellow Coins (AITG Resource)", 'GC': "GC: Green Coins (AITG Resource)", 'BC': "BC: Blue Coins (AITG Resource)", 'WC': "WC: White Coins (AITG Resource)", 'QT': "QT: Quest Tokens (AITG Resource)", 'HC': "HC: Hardcore Mode", 'APOC': "Apoc: Apocalypse Mode", 'WM': "WM: Worst Moon Mode", 'NM': "NM: Nightmare Buff", 'PX': "Px: Pixels (BA Crafting Resource)", 'CM': "CM: Crafting Material (BA Crafting Resource)", 'SCM': "SCM: Superior Crafting Material (BA Crafting Resource)", 'UNOB': "Unob: Unobtainium Enhacement Level (BA Crafting Resource)", 'COR': "CoR: Crystal of Rarity (BA Crafting Resource)", 'COUR': "CoUR: Crystal of Ultimate Rarity (BA Crafting Resource)", 'CFOC': "CFoC: Chaotic Fragments of Chaos (BA Crafting Resource)", '500/500': "500/500: Crafting with 500 Crystal of Rarity and 500 Crystal of Ultimate Rarity", 'QA': "QA: Quick Attack (BA Active Skill)", 'PA': "PA: Power Attack (BA Active Skill)", 'RH': "RHK: Roundhouse Kick (BA Active Skill)", 'RHK': "RHK: Roundhouse Kick (BA Active Skill)", 'SKB': "SKB: Super Knockback (BA Active Skill)", 'CDG': "CdG: Coup de Grace (BA Passive Skill)", 'GOC': "GoC: Gem of Constancy (BA Trinket)", 'HG': "HG: Holy Glory (BA Armor Set)", 'KM': "KM: Knightmare (BA Armor Set)", 'HBB': "HBB: Humblebee (BA Armor Set)", 'PB/ES':"PB/ES: Pyrabow + Evil Sword Fusion (BA Weapon)", 'PB': "PB: Pyrabow (BA Weapon)", 'ES': "ES: Evil Sword (BA Weapon)", 'G5': "G5: Glaive-Glaive-Glaive-Guisarme-Glaive (BA Weapon)", 'GS': "GS: Greatersword (BA Weapon)", 'ULS': "ULS: Ultimate Lightning Spear (BA Weapon)", 'BLS': "BLS: Blood Scimitar (BA Weapon)", 'LG': "LG: Light Glaive (BA Weapon)", 'DR': "DR: Dark Ruler (BA Weapon)", 'AURA': "AURA: CHAOS AURA (BA Weapon of CHAOS Set)", 'DH': "DH: Double Hit", 'CGT': "CGT: Corrupted Giant Treeman (WM Corruption Boss)", 'CN': "CN: Chuck Norris (2012: Ye Old Pub Boss", 'MB': "MB: Megaboss (Special Arena Boss)", 'FGM': "FGM: Fairy Godmother (Throne Room Boss)", 'FGF': "FGM: Fairy Godfather (Throne Room Boss)", 'TR': "TR: Throne Room (BA Area)", 'BB': "BB: Binary Battlefield (BA Area)", 'CS': "CS: Censor Ship (BA Area)", 'DC': "DC: Dragon Cave (BA Area)", 'SI': "SI: Smiley Island (BA Area)", 'TL': "TL: Triangle Land (BA Area)", 'NCI': "NCI: No Copyright Infringement (BA Area)", 'PSI': "PSI: Pirate Ship Interior (BA Area)", 'SC': "SC: Spooky Crypt (BA Area)", 'ED': "ED: Endless Dungeon (BA Raid)", 'TH': "TH: Triangle Hideout (BA Raid)", 'SC': "SC: Spooky Crypt (BA Raid)", 'SD': "SD: Secret Dungeon (BA Raid)", 'SA': "SA: Special Arena (BA Raid)", 'MBR': "MBR: Megaboss's Revenge (BA Raid)", 'DP': "DP: Dark Pyramid (BA Raid)", 'AA': "AA: Awesome Adventures (AITG Feature)", 'AG': "AG: Another Garden (AITG Feature)", 'BA': "BA: Battle Arena (AITG Feature)", 'RQ': "RQ: Random Quest (AITG Feature)", 'PBAR': "PBar: Progress Bar (AITG Feature)", 'PBE': "PBE: Progress Bar Extension (Awesome Adventure Reward)", 'UA': "UA: Ultimate Avoidance (Arcade Game)", 'TC': "TC: Triangle Count (Arcade Game)", 'MM': "MM: Math Master (Arcade Game)", 'MS': "MS: Mind Sweeper (Arcade Game)", 'UA': "UA: Ultimate Avoidance (Arcade Game)", 'W-A-G': "WaG: Whack-a-Greg (Arcade Game)", 'WAG': "WaG: Whack-a-Greg (Arcade Game)", 'B3': "B3: Balance 3 (Arcade Game)", 'LB': "LBox: Legendary Box (Mystery Box)", 'LBOX': "LBox: Legendary Box (Mystery Box)", 'PMB': "PMBox: Pixelated Mystery Box (Mystery Box)", 'PMBOX':"PMBox: Pixelated Mystery Box (Mystery Box)", 'PBOX': "PBox: Progress Box (Mystery Box)", 'IA': "IA: Invisible Ally", 'POT': "PoT: Proof of Training", 'POM': "PoM: Proof of Mission", 'SSF': "SSF: Secret Save File", 'IX': "IX: Invisible X (2012: Ye Olde Pub)", 'TBNG': "TBNG: To-Be-Nerfed Gems", 'DAS': "DAS: Doom Ant Sprayer", 'MAS': "DAS: Manual Ant Sprayer", 'SCS': "SCs: Secret Crystals", 'SC1': "SC1: Secret Crystal 1 (Bestiary page 3, row 4, col 10)", 'SC2': "SC2: Secret Crystal 2 (Bestiary page 4, row 4, col 3)", 'RCP': "RCP: Random Career Potions", 'MCP': "MCP: Mega Career Potions", 'AF': "AF: Awesome Face (TukkunFCG card)", 'AU': "AU: Awesome Update", 'BOD': "BoD: Boss of Doom", 'DM': "DM: Deathmatch (Stadium Mode)", 'EC': "EC: Emperor's Clothes Armor Set", 'FLC': "FLC: Four Leaf Clover (BA Medal)", 'GB': "GB: Gamblers Box", 'GP': "GP: Garden Points", 'IM': "IM: Item Maker (Career)", 'MK': "MK: Mission Kommander", 'PC': "PC: Pokayman City (BA Area)", 'PS': "PS: Pirate Ship", 'PSOD': "PSoD: Purple Screen of Death", 'SG': "SG: Scary Graveyard", 'KB': "KBR: Knockback Ring", 'KBR': "KBR: Knockback Ring", 'SL': "SL: Secret Lab", 'SMG': "SMG: Strong Machine Gun", 'SP': "SP: Skill Points", 'SPF': "SPF: Special Pet Food", 'ST': "ST: Stadium Tokens", 'TOD': "ToD: Tower of Doom", }; var glossaryTerms = listToRegExp(Object.keys(glossary)); var glossaryTermsRegex = RegExp(glossaryTerms, 'gi'); // ----- Functions ---------------------------------------------------------------------- //// check if the user is developer of the current game //// this ugly implementation was found in holodeck function isDeveloperOfGame(username) { return dom.location.pathname.split("/")[2].toLowerCase() == username.toLowerCase(); } //// check if the room is for AITG function isAITGRoom() { return dom.holodeck.chatWindow().activeRoom()._room.name.lastIndexOf('Anti-Idle: The Game', 0) === 0 } //// convert the list of glossary terms to a RegExp string function listToRegExp(list) { function reduceToRegExp(prev, curr, idx, arr) { return prev + '|' + curr; } return'(\\b(?:' + list.reduce(reduceToRegExp) + ')\\b)+'; } //// find glossary terms, highlight them, and add hover descriptions. function highlightGlossaryTerms(text) { function replaceTerm(term) { console.debug('Found match: "' + term + '"'); return '<acronym style="font-style: italic; text-decoration: underline" title="' + glossary[term.toUpperCase()] + '">' + term + '</acronym>'; } return text.replace(glossaryTermsRegex, replaceTerm); } //// substitute lines in Kong Chat using Regular Expressions //// note: can edit message text only function kongRegexFilter(text, func) { var foundLink = false; //// running the REs on the text for (x in regexes) { rr = regexes[x]; if (text.search(rr.regexp) > -1) { DEBUG && console.debug("Filtering '" + text + "'"); DEBUG && console.debug("Replacing " + rr.regexp.toString() + " with " + rr.replace); text = text.replace(rr.regexp, rr.replace); DEBUG && console.debug("Resulting text: '" + text + "'"); foundLink = true; //// as a workaround, avoid any further replacements when a link is found return text; } } //// as a workaround, simply don't define acronyms if a link is found if (foundLink == false && isAITGRoom()) { text = highlightGlossaryTerms(text); } ///// "returning" the modified text if (typeof(func) === 'undefined') { return text; } else { func(text, func); } } //// outgoing message filter, redirect messages starting with '!' to AITGBot as whispers function simplerAITGBotCommandsFilter(text, func) { if (text.charAt(0) === '!') { dom.holodeck.sendWhisper(dom.holodeck, '/w AITGBot ' + text.slice(1)); //// instead of calling func() as expected by Kong's Filter system, //// just exit the Filter's flow to prevent printing the message return true; } else { //// process as normal func(text, func); } } //// can replace text and templates of all //// note: can edit message and style, gets the name of the sender too function displayUnsanitizedMessageModified(username, message, htmlclasses, prefix) { var originalTemplate = dom.ChatDialogue.MESSAGE_TEMPLATE.template; var isFriend = false; var isModerator = false; var isDeveloper = false; var userObject = dom.holodeck.chatWindow().activeRoom().user(username); if (!htmlclasses) { htmlclasses = { class: '' }; } //// highlight me if (dom.holodeck.username() == username) { htmlclasses['class'] += ' KI_me'; } //// highlight developers if (isDeveloperOfGame(username)) { isDeveloper = true; htmlclasses['class'] += ' KI_dev'; } //// highlight moderators if (dom.holodeck.chatWindow().activeRoom().canUserModerate(userObject)) { isModerator = true; htmlclasses['class'] += ' KI_mod'; } //// highlight friends if (dom.holodeck.chatWindow().isFriend(username)) { isFriend = true; htmlclasses['class'] += ' KI_friend'; } /* // Kong's Template dom.ChatDialogue.MESSAGE_TEMPLATE.template = '<p class="#{classNames}">' + '<span username="#{username}" class="username #{userClassNames}">#{prefix}#{username}</span>' + '<span class="separator">: </span>' + '<span class="message hyphenate">#{message}</span>' + '<span class="clear"></span></p>'; */ dom.ChatDialogue.MESSAGE_TEMPLATE.template = '<p class="#{classNames}">' + '<span username="#{username}" class="username #{userClassNames}">' + (!isFriend && !isModerator && !isDeveloper ? '' : '<span class="user_row">') + (!isDeveloper ? '' : '<span class="spritesite developer_icon rank_icon" title="Developer">Developer</span>') + (!isModerator ? '' : '<span class="spritesite moderator_icon rank_icon" title="Moderator">Moderator</span>') + (!isFriend ? '' : '<span class="spritesite friend_icon" title="Friend">Friend</span>') + (!isFriend && !isModerator && !isDeveloper ? '' : '</span>') + '#{prefix}#{username}' + '</span>' + '<span class="separator">: </span>' + '<span class="message hyphenate">#{message}</span>' + '<span class="clear"></span></p>'; message = kongRegexFilter(message); htmlmsg = this.displayUnsanitizedMessageOriginal(username, message, htmlclasses, prefix); dom.ChatDialogue.MESSAGE_TEMPLATE.template = originalTemplate; return htmlmsg; } //// when Kong's Holodeck is loaded, add Filters to Kong Chat function injectKongHolodeck() { !DEBUG && (DEBUG = (dom.holodeck.username() === 'kurzedmetal')); DEBUG && console.debug('glossaryTerms: ' + glossaryTerms); DEBUG && console.debug("Holodeck defined? " + (typeof(unsafeWindow.holodeck) !== 'undefined')); //// injecting function to manipulate display of messages if (typeof(dom.ChatDialogue.prototype.displayUnsanitizedMessageOriginal) === 'undefined') { dom.ChatDialogue.prototype.displayUnsanitizedMessageOriginal = dom.ChatDialogue.prototype.displayUnsanitizedMessage; } dom.ChatDialogue.prototype.displayUnsanitizedMessage = displayUnsanitizedMessageModified; DEBUG && console.debug("Added Message Template manipulation!"); //// these filters modify incoming messages //// including the ones you type //dom.holodeck.addIncomingMessageFilter(kongRegexFilter); //DEBUG && console.debug("Regex Filter added to incomming messages!"); //// these filters modify your messsages //// other people see the changes too, not just you! dom.holodeck.addOutgoingMessageFilter(simplerAITGBotCommandsFilter); } //// injects a CSS StyleSheet into Kong's Chat function injectStyleSheet() { DEBUG && console.debug("Adding custom StyleSheet!"); var chatSelector = '#kong_game_ui .chat_message_window '; var cssCode = chatSelector + ".KI_bot_alert { font-weight: bold; color: orange; }\n" + chatSelector + ".username .user_row { padding: 0 !important; float: left; }\n" + chatSelector + ".username .user_row .spritesite { margin: 0 2px 0 0 !important; }\n" + chatSelector + ".sent_whisper { background-image: -moz-linear-gradient(left center , #FFFFFF 20%, #F9F9BB 100%); }\n" + chatSelector + ".received_whisper { background-image: -moz-linear-gradient(right center , #FFFFFF 20%, #F9F9BB 100%); }\n" + chatSelector + ".KI_friend .username { font-weight: bold; }\n" + chatSelector + ".KI_me .username { font-weight: bold; }\n" + chatSelector + ".KI_dev .username { font-weight: bold; color: #112244; }\n" + chatSelector + ".KI_dev { border: 2px none green; border-style: none solid; background-color: #ddffdd; }\n" + chatSelector + ".KI_mod .username { font-weight: bold; color: #330055; }\n" + chatSelector + ".KI_mod { border: 2px none #4499FF; border-style: none solid; background-color: #ebf5ff; }\n" + "\n"; var css = document.createElement('style'); css.appendChild(document.createTextNode(cssCode)); document.head.appendChild(css); } // ----- Main Code ---------------------------------------------------------------------- //// inject code into Kong's Holodeck when it is loaded function executeMain() { if (typeof(dom) === 'undefined') { DEBUG && console.error('No Document object!'); } else { injectStyleSheet(); dom.document.observe("holodeck:ready", injectKongHolodeck); DEBUG && console.debug('Waiting for Holodeck Object...'); } } executeMain();