Created
August 27, 2024 06:58
-
-
Save notarikon-nz/2342cfaad7e1883daa7faf2610452416 to your computer and use it in GitHub Desktop.
HackMUD T2 Scraper including QR Decoder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function(context, args){// t:#s.weyland.members | |
| function getActiveT2() { | |
| let aa = "bunnybat_hut,cyberdine,setec_gas,soylentbean,suborbital_airlines,tandoori,tyrell,weyland".split(","), // known corp locations, without .public | |
| bb = "members,memberlogin,members_only,member_access,private,internal,priv,employees,intern,employee_login,emplogin".split(","), // known t2/t3 suffixes, e.g. memberz | |
| t2 = new Array() | |
| for (let first of aa) { | |
| for (let second of bb) { | |
| let domain = first + "." + second | |
| let r = #fs.scripts.get_level({ name: domain }) | |
| if (Number.isInteger(r)) { | |
| if (r == 2 || r == 3) | |
| t2.push(domain) | |
| } | |
| } | |
| } | |
| return t2; | |
| } | |
| // OOG CLIENT LISTING | |
| if (args && args.portal && !args.t) { | |
| let a = new Array(); | |
| const list = #fs.nokiraton.status({portal:true}) | |
| list.forEach(n => | |
| a.push(n.domain) | |
| ) | |
| return a.reverse().join("\n"); | |
| } | |
| if (!args || !args.t || args.t == null) { | |
| const shift = getActiveT2() | |
| return "\n`HAPERTURE SCIENCE TIER 2`\n\nUsage: aperture.t2 { t: #s.weyland.members }\n\nAvailable corps:\n\n" + shift; | |
| } | |
| let debug = ""; | |
| let t = args.t, | |
| u = args.u, | |
| r = "", | |
| o = "\n`HAPERTURE SCIENCE TIER 2`\n\n" | |
| let safe = t.call({}) | |
| if (safe.ok == false) | |
| return {ok:false,msg:"Location does not exist"} | |
| let rating_short = "_jr,_dd,_wb,_pr,_ls".split(","), | |
| rating_long = "`0junk`,`1deck`,`2wrck`,`3phrk`,`4leet`".split(","), | |
| class_short = "wvr_,ttl_,wlf_,rvn_,stg_".split(","), | |
| class_long = "`Narch`,`Olock`,`Pinfl`,`Qscav`,`Sexec`".split(","), | |
| s = ["NUL","LOW ","MID ","HIG","FUL"], | |
| rst = "????", | |
| // um = "", | |
| lm = "", | |
| eo = 0 | |
| // first, find the username type | |
| var p1 = decorrupt(t) | |
| u = p1.indexOf("username") > -1 ? "username" : p1.indexOf("user") > -1 ? "user" : "u" | |
| let users = new Set( | |
| "_123lt_jack,_3rd_3y3_grill,3rd_3y3_grill,ad4m4,ada_love,aeryn_s,amelie,arr_too_d_to,b_sisko,b3nd3r,b4cca,b4rry_vv,bassy_thecount,be_lavar,bella_swan,bella_swann,boba_the_hutt,bobranator,boris,brohda_99,bus_and_parks,c_vader,carrie_on_,catness,chad_bose,cheechfiend91,childishg4mb,cking,computer_blue,corg_train,corgitruthsayer,crichton_j,curtfields0fmay,d_bowman,d_jackson,d4ft,d4ria,daa_freak,daurmith,delete_me_first,derek_zoo,diamond_dogz,do_u_need_hal,doc_brown,dr00gy_lex,du_boyz,duke_ell,e_ponine,etceteraco,firebreathingdragon,foxy_guy,frank_einstein,frantimike,free_man_morg,geyser_sore,geyser_soze,ginnypig,gwashc,h_jimi,h4chguy,hand_solo,hermione,htubman,huey_n,hypati4_370,i_am_the_eggman,ice_ventura,indie_jones,inigo,ireneadler,jack_sparrow,jamesb,janeway,jermaine,jim_c_kirk,journer_of_truth,juno_macguff,killa_kara,king_in_yellow,king_luther,lass_doug,leia_it_ontheline,leon,lion_eyes,lizzie_regna,lt_col_j_shep,lt_col_j_shepp,luke_5kywalker,m_ali,m_c_fly,m_clarke_dunk,m_poppins,madthugpug,marc_garv,mary_shell,med_evarz,med_evzrz,mh_hamilton,mjay_m_walker,muld0r,neoone,oz,pick4rluc,poitier_27,pugluv4vr,purple1,q_bey,r0bertm4rley,rain3y,raygun_chandler,renaldos_malc,revolution808,rey_tr4cer,ripley_or_not,rob_rob_taylor,robo_deckard,rocky_b,runningman23,sam_cart,sammy_l_jack,scook,seria_mau_g,seven_out_of_9,shareef_j,shawn_aa,sidney_prescott,sp0ck_08,sportsfan2031,tam_riv,tchalla,terrance_cruz,thadd_0s,thedude,theformalartist,thegreat,thegreatvandross,thepowerful,theshrillery,troy_cole,turn_a_nat,turner_t,uhur4,universe,wc_handy,whois_hermano,wiley_curry,will_de_vaughn,wonderous_steve,x_mal,youngtwokay,yung_lespaul,zap_dweezil,zap_franscisco,zap_moon,zoe_wash".split(",") | |
| ), | |
| active = new Set(), | |
| verified = new Set(), | |
| regex_key = /[a-z]/g, | |
| a = "", | |
| key = "", | |
| first = 1 | |
| users.forEach(handle => { | |
| const args = {username:handle} | |
| const r = decorrupt (t, args) | |
| if (r.includes("faq")) { | |
| if (first) { | |
| key = r.split("\n")[2] | |
| key = key.substring(2,key.length-1) | |
| first = 0 | |
| } | |
| active.add(handle) | |
| } | |
| }) | |
| const user_list = [...active].map((handle, index) => { | |
| const paddedHandle = handle.padEnd(20, " ") | |
| return (index % 4 === 3) ? ` ${paddedHandle}\n` : ` ${paddedHandle}` | |
| }).join(""); | |
| let loc_set = new Set(), | |
| early = false | |
| let count = 0, qr_count = 0, call_count = 0 | |
| for (const handle of active) { | |
| a = {} // username:handle,key:"faq"} | |
| a[u] = handle | |
| a[key] = "order_qrs" | |
| let jsonArray = fast_qr(t, a) // r = #fs.notarikon.qr_decode ( {t:t,a:a} ) // dtr is faster for now | |
| var idArray = [] | |
| jsonArray.forEach(jsonObject => { | |
| if (typeof jsonObject == 'string') idArray.push(jsonObject) | |
| qr_count++ | |
| }); | |
| for (var i in idArray) { | |
| a[key] = "cust_service" | |
| a["order_id"] = idArray[i] | |
| var locs = decorrupt(t, a).split(" ") | |
| const locationRegex = /^[\w]+\.[\w]+$/ | |
| for (const location of locs) { | |
| if (!location.match(locationRegex)) continue | |
| verified.add(location) | |
| //const sl = #fs.scripts.get_level({ name: location }) | |
| //const sec = s[sl] | |
| const rat = get_rating(location) | |
| const cls = get_class(location) | |
| loc_set.add({ loc: location, rat, cls }) | |
| isNPC(location) ? insertNPC(location) : insertPlayer(location); | |
| } | |
| if ((Date.now() - _START) > 4750) break; | |
| } | |
| if ((Date.now() - _START) > 4750) break; | |
| } | |
| if (args.portal) | |
| return `Found: ${loc_set.size} NPCs` | |
| let output = get_return_string() | |
| if (context.caller == 'aperture') output += debug | |
| return output | |
| function get_return_string() { | |
| let et = Date.now()-_START | |
| o += "Found " + verified.size + " NPCs from " + qr_count + " QR Codes in " + et + "ms\n\n" | |
| // Convert the Set to an Array for sorting | |
| let arrayData = Array.from(loc_set); | |
| // Sort the Array | |
| arrayData.sort((a, b) => { | |
| //if (a.sec < b.sec) return -1; | |
| //if (a.sec > b.sec) return 1; | |
| if (a.rat < b.rat) return -1; | |
| if (a.rat > b.rat) return 1; | |
| if (a.cls < b.cls) return -1; | |
| if (a.cls > b.cls) return 1; | |
| return 0; | |
| }) | |
| // If you need the sorted data back as a Set (this is uncommon since Sets are not ordered) | |
| loc_set = new Set(arrayData); | |
| o += convertSetToJsonTable(loc_set) | |
| if (early) o+= "\n\nWarning: Script terminated early to avoid timeout, more locs available\n" | |
| return o | |
| } | |
| function decorrupt(target, args) { | |
| const corruption = /`.[¡¢Á¤Ã¦§¨©ª]`/g | |
| let r1 = target.call(args), // t is a global var, so no need to pass it as an argument | |
| r2 = target.call(args) // could probably do the same with b but oh well | |
| if (Array.isArray(r1)) r1 = r1.join("\n"); | |
| if (Array.isArray(r2)) r2 = r2.join("\n"); | |
| r1 = r1.replace(corruption, "§"); | |
| r2 = r2.replace(corruption, "§"); | |
| const timeout = 100; | |
| while (Date.now() - _START < timeout) { | |
| let cor_index = r1.indexOf("§") | |
| if (cor_index === -1) { | |
| return r1 | |
| } | |
| let r2char = r2[cor_index] | |
| if (r2char === "§") { | |
| r2 = target.call(args); | |
| if (Array.isArray(r2)) r2 = r2.join("\n"); | |
| r2 = r2.replace(corruption, "§"); | |
| continue | |
| } | |
| r1 = r1.slice(0, cor_index) + r2char + r1.slice(cor_index + 1); | |
| } | |
| return r1 | |
| } | |
| function get_rating(um) { | |
| let rst = "----" | |
| for (var x = 0; x < 5; x++) { | |
| var r = rating_short[x]; | |
| var sp = um.split(".")[0]; | |
| if (!sp.includes("_") && (rst = "`5plyr`")) continue; | |
| if (sp.includes(r) && (rst = rating_long[x])) continue; | |
| } | |
| return rst; | |
| } | |
| function get_class(um) { | |
| let rst = "----" | |
| for (var x = 0; x < 5; x++) { | |
| var r = class_short[x]; | |
| var sp = um.split(".")[0]; | |
| if (!sp.includes("_") && (rst = "")) continue; | |
| if (sp.includes(r) && (rst = class_long[x])) continue; | |
| } | |
| return rst; | |
| } | |
| function convertSetToJsonTable(set) { | |
| if (set.size === 0) return "No data available"; | |
| // Initialize headers and column width trackers | |
| let headers = []; | |
| let columnWidths = {}; | |
| // First, determine headers and initialize widths by examining the first object in the set | |
| set.forEach(item => { | |
| headers = Object.keys(item); | |
| headers.forEach(header => { | |
| let l = header.length | |
| let i = ("" + decolor(item[header] || '')) | |
| columnWidths[header] = Math.max(l, i.length) | |
| }); | |
| return true; // Exit after processing the first item | |
| }); | |
| // Update columnWidths based on all data in the set | |
| set.forEach(item => { | |
| headers.forEach(header => { | |
| const dataLength = ("" + decolor(item[header] || '')).length; | |
| if (dataLength > columnWidths[header]) { | |
| columnWidths[header] = dataLength; | |
| } | |
| }); | |
| }); | |
| // ━━━━━━━━━╋ | |
| // Create header row | |
| let headerRow = headers.map(header => ("`1" + header.padEnd(columnWidths[header])).toUpperCase() + "`").join(' ┃ ') | |
| let rows = [headerRow]; | |
| // Separator row | |
| let separatorRow = headers.map(header => '━'.repeat(columnWidths[header])).join('━━╋━━'); | |
| rows.push(separatorRow); | |
| // Data rows | |
| set.forEach(item => { | |
| let row = headers.map(header => ("" + (item[header] || '')).padEnd(columnWidths[header])).join(' ┃ '); | |
| rows.push(row); | |
| }); | |
| // Join all rows into a single string with new lines | |
| return rows.join('\n'); | |
| } | |
| function decolor (val) { | |
| return val.replaceAll(/`\w(.*?)`/g, '$1') | |
| } | |
| function printColumns(arr, width) { | |
| const spacing = 6; // fixed spacing between columns | |
| let maxStrLength = 0; | |
| let output = "" | |
| // Calculate the maximum string length | |
| for (let i = 0; i < arr.length; i++) { | |
| if (arr[i].length > maxStrLength) { | |
| maxStrLength = arr[i].length; | |
| } | |
| } | |
| const colWidth = maxStrLength + spacing; // width of each column including spacing | |
| const numCols = Math.floor(width / colWidth); // number of columns that fit in the given width | |
| const numRows = Math.ceil(arr.length / numCols); // number of rows needed | |
| for (let row = 0; row < numRows; row++) { | |
| let rowStr = ''; | |
| for (let col = 0; col < numCols; col++) { | |
| const index = row + col * numRows; | |
| if (index < arr.length) { | |
| rowStr += arr[index].padEnd(colWidth, ' '); | |
| } | |
| } | |
| output += (rowStr) + "\n"; | |
| } | |
| return output | |
| } | |
| function fast_qr(target, _args) { | |
| let sa = _args | |
| const MASK = [ | |
| (i, j) => (i + j) % 2 == 0, // 0 | |
| (i, j) => i % 2 == 0, // 1 | |
| (i, j) => j % 3 == 0, // 2 | |
| (i, j) => (i + j) % 3 == 0, // 3 | |
| (i, j) => (((i / 2) | 0) + ((j / 3) | 0)) % 2 == 0, // 4 | |
| (i, j) => (i * j) % 2 + (i * j) % 3 == 0, // 5 | |
| (i, j) => ((i * j) % 3 + (i * j)) % 2 == 0, // 6 | |
| (i, j) => ((i * j) % 3 + i + j) % 2 == 0 // 7 | |
| ], | |
| REG = /[<>]/; | |
| let tot_calls = 1; | |
| let rsp = target.call(_args); | |
| let CALLS; | |
| try { | |
| CALLS = [rsp.filter(o => !REG.test(o)).map(o => o.replace(/`.([^`])`/g, '@'))]; | |
| } catch (e) { | |
| CALLS = []; | |
| } | |
| function bit(ind, w, x, y) { | |
| const z = (y / 2 | 0) * (w + 1) + x, | |
| ch = y % 2 ? '▄' : '▀' | |
| for (let i = 0; i < CALLS.length; ++i) { | |
| const c = CALLS[i][ind][z]; | |
| if (c == '@') continue; | |
| return c == '█' || c == ch | |
| } | |
| while (true) { | |
| ++tot_calls; | |
| const t = target.call(_args).filter(o => !REG.test(o)).map(o => o.replace(/`.([^`])`/g, '@')); | |
| CALLS.push(t); | |
| const c = t[ind][z]; | |
| if (c == '@') continue; | |
| return c == '█' || c == ch | |
| } | |
| } | |
| function eitherbit(ind, w, x, y, x1, y1) { | |
| const z = (y / 2 | 0) * (w + 1) + x, | |
| z1 = (y1 / 2 | 0) * (w + 1) + x1, | |
| ch = y % 2 ? '▄' : '▀', | |
| ch1 = y1 % 2 ? '▄' : '▀' | |
| for (let i = 0; i < CALLS.length; ++i) { | |
| let c = CALLS[i][ind][z]; | |
| if (c == '@') { | |
| c = CALLS[i][ind][z1]; | |
| if (c == '@') continue; | |
| return c == '█' || c == ch1 | |
| } | |
| return c == '█' || c == ch | |
| } | |
| while (true) { | |
| ++tot_calls; | |
| const t = target.call(_args).filter(o => !REG.test(o)).map(o => o.replace(/`.([^`])`/g, '@')); | |
| CALLS.push(t); | |
| let c = t[ind][z]; | |
| if (c == '@') { | |
| c = t[ind][z1]; | |
| if (c == '@') continue; | |
| return c == '█' || c == ch1 | |
| } | |
| return c == '█' || c == ch | |
| } | |
| } | |
| const LOCS = { | |
| /*9*/53: [ | |
| /* 7*/[[39, 40], 0, [39, 39], [40, 10], [39, 10], [40, 9], [39, 9]], | |
| /* 8*/[[39, 8], 0, [39, 7], [38, 23], [37, 23], [38, 24], [37, 24]], | |
| /* 9*/[[37, 25], 0, [37, 26], [36, 50], [35, 50], [36, 49], [35, 49]], | |
| /*10*/[[35, 48], 0, [35, 47], [52, 48], [51, 48], [52, 47], [51, 47]], | |
| /*11*/[[51, 46], 0, [51, 45], [52, 16], [51, 16], [52, 15], [51, 15]], | |
| /*12*/[[51, 14], 0, [51, 13], [50, 33], [49, 33], [50, 34], [49, 34]] | |
| ], | |
| /*8*/49: [ | |
| /* 7*/[[37, 21], 0, [37, 22], [38, 43], [37, 43], [38, 44], [37, 44]], | |
| /* 8*/[[37, 45], 0, [37, 46], [36, 30], [35, 30], [36, 29], [35, 29]], | |
| /* 9*/[[35, 28], 0, [35, 27], [36, 5], [35, 5], [36, 4], [35, 4]], | |
| /*10*/[[35, 3], 0, [35, 2], [34, 19], [33, 19], [34, 20], [33, 20]], | |
| /*11*/[[33, 21], 0, [33, 22], [34, 43], [33, 43], [34, 44], [33, 44]], | |
| /*12*/[[33, 45], 0, [33, 46], [48, 44], [47, 44], [48, 43], [47, 43]] | |
| ], | |
| /*7*/45: [ | |
| /* 7*/[[33, 9], 0, [33, 10], [34, 27], [33, 27], [34, 28], [33, 28]], | |
| /* 8*/[[33, 29], 0, [33, 30], [32, 42], [31, 42], [32, 41], [31, 41]], | |
| /* 9*/[[31, 40], 0, [31, 39], [32, 22], [31, 22], [32, 21], [31, 21]], | |
| /*10*/[[31, 20], 0, [31, 19], [32, 1], [31, 1], [32, 0], [31, 0]], | |
| /*11*/[[29, 0], 0, [29, 1], [44, 40], [43, 40], [44, 39], [43, 39]], | |
| /*12*/[[43, 38], 0, [43, 37], [44, 20], [43, 20], [44, 19], [43, 19]] | |
| ] | |
| } | |
| const SM = { 1058: 45, 1250: 49, 1458: 53 } | |
| function qr(ind) { | |
| const ver = SM[CALLS[0][ind].length], | |
| m = ((eitherbit(ind, ver, 2, 8, 8, ver - 3) << 2) | (eitherbit(ind, ver, 3, 8, 8, ver - 4) << 1) | eitherbit(ind, ver, 4, 8, 8, ver - 5)) ^ 5 | |
| let answer = String.fromCharCode(...LOCS[ver].map(byte => '0b' + byte.map(a => +!a || bit(ind, ver, a[0], a[1]) ^ MASK[m](a[1], a[0])).join(''))) | |
| // debug += `\n${JSON.stringify(sa)} : ${answer} (${ver})` | |
| return answer | |
| } | |
| let res = Array(CALLS[0].length) | |
| for (let i = 0; i < res.length; ++i) { | |
| res[i] = qr(i) | |
| } | |
| return res; | |
| } | |
| function isNPC(handle) { | |
| const npcs = "abandoned,abndnd,anon,anonymous,derelict,uknown,unidentified,unknown".split(",") | |
| for (var ih = 0; ih < npcs.length; ih++) { | |
| if (handle.includes(npcs[ih])) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| function insertPlayer(pvpLocation) { | |
| let level = String(#fs.scripts.get_level({name:pvpLocation})) | |
| if (!isNumeric(level)) return false; | |
| const pvpUsername = pvpLocation.split(".")[0] | |
| const query = { group: "pvp", pvpUsername }; | |
| const update = { $set: { pvpLocation,source:'aperture.t2' } }; | |
| try { | |
| #db.us( | |
| query, | |
| update | |
| ); | |
| } catch (e) {} | |
| } | |
| function isNumeric(str) { | |
| if (typeof str != "string") return false // we only process strings! | |
| return !isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)... | |
| !isNaN(parseFloat(str)) // ...and ensure strings of whitespace fail | |
| } | |
| function insertNPC(location) { | |
| // const tier = #fs.scripts.get_level({ name: location }) | |
| const timestamp = Date.now() | |
| const _id = 'locs'; | |
| const query = { tier:2, location }; | |
| const update = { $set: { timestamp,source:'aperture.t2' } }; | |
| try { | |
| #db.us( | |
| query, | |
| update //, { upsert: true } | |
| ); | |
| } catch (e) { } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment