Last active
October 25, 2023 06:27
-
-
Save miku-orb/a312cc35b56284fb889dde8863474acc to your computer and use it in GitHub Desktop.
Batch-add your library symbol to selected bibliographic records in Virtua VTLS system (more reliable than AutoIt scripts and whatever)
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
| // Created with the following Virtua system versions (by reverse engineering the network protocol): | |
| // Server: 16.1.SP3.HF8(163939d)-189 | |
| // Client: 16.1.SP3.HF3(16.1.SP3-8-gbdfe5ec)-189 | |
| const VIRTUA_HOST = "0.0.0.0"; | |
| const VIRTUA_PORT = 1234; | |
| const VIRTUA_USERNAME = ""; | |
| const VIRTUA_PASSWORD = ""; | |
| const LIBRARY_SYMBOL = "TEST_123"; // !!! CHANGE TO YOURS !!! | |
| const LIBRARY_SYMBOL_ADD = true; // adding true, removing false | |
| const IDs_FILE = "ids.txt"; // internal Virtua VTLS id numbers to which you wanna add your library symbol, one per line | |
| const COMPUTER_NAME = "bot"; | |
| const USER_AGENT_1 = "Virtua-Client"; | |
| const USER_AGENT_2 = "VTLS Virtua Z39.50 Client"; | |
| const USER_AGENT_VERSION = "16.1.SP3.HF3(16.1.SP3-8-gbdfe5ec)-189"; // probably better not to change this without some forethought, in case they change the protocol format...? | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| const net = require("net"); | |
| const fs = require("fs"); | |
| const IDS = fs.readFileSync(IDs_FILE, "utf-8").toString().split("\n").map(l => l.trim()); | |
| const authMsg = Buffer.from( | |
| "\xb4" + String.fromCodePoint(34 + VIRTUA_USERNAME.length + VIRTUA_PASSWORD.length + USER_AGENT_1.length + USER_AGENT_2.length + USER_AGENT_VERSION.length) | |
| + `\x83\x02\x05\xe0` | |
| + `\x84\x03\x01\xc3\xa0` | |
| + `\x85\x02\x20\x00` | |
| + `\x86\x02\x40\x00` | |
| + `\xa7${String.fromCodePoint(6+VIRTUA_USERNAME.length+VIRTUA_PASSWORD.length)}\x30${String.fromCodePoint(4+VIRTUA_USERNAME.length+VIRTUA_PASSWORD.length)}` | |
| + `\x81${String.fromCodePoint(VIRTUA_USERNAME.length)}${VIRTUA_USERNAME}` | |
| + `\x82${String.fromCodePoint(VIRTUA_PASSWORD.length)}${VIRTUA_PASSWORD}` | |
| + `\x9f\x6e${String.fromCodePoint(USER_AGENT_1.length)}${USER_AGENT_1}` | |
| + `\x9f\x6f${String.fromCodePoint(USER_AGENT_2.length)}${USER_AGENT_2}` | |
| + `\x9f\x70${String.fromCodePoint(USER_AGENT_VERSION.length)}${USER_AGENT_VERSION}` | |
| , "ascii"); | |
| assert(authMsg.byteLength === 2 + 34 + VIRTUA_USERNAME.length + VIRTUA_PASSWORD.length + USER_AGENT_1.length + USER_AGENT_2.length + USER_AGENT_VERSION.length); | |
| function createSymbolPacket(id) { | |
| let buf = Buffer.from( | |
| `\xbf\x81\x48` | |
| + String.fromCodePoint(11 + 3 + LIBRARY_SYMBOL.length) // len | |
| + `\xa6` | |
| + String.fromCodePoint(9 + 3 + LIBRARY_SYMBOL.length) // len | |
| + `\xa0` | |
| + String.fromCodePoint(7 + 3 + LIBRARY_SYMBOL.length) // len | |
| + `\x80\x01${LIBRARY_SYMBOL_ADD ? "\x01" : "\x00"}` | |
| + `\x81` | |
| + `\x03` // len of next three | |
| + String.fromCodePoint(((+id) & 0xff0000) >> 16) | |
| + String.fromCodePoint(((+id) & 0x00ff00) >> 8) | |
| + String.fromCodePoint(((+id) & 0x0000ff) >> 0) | |
| + `\x82` | |
| + String.fromCodePoint(LIBRARY_SYMBOL.length) // len of symbol | |
| + LIBRARY_SYMBOL | |
| , "ascii"); | |
| assert(buf.byteLength === 4 + 11 + 3 + LIBRARY_SYMBOL.length); | |
| return buf; | |
| } | |
| const client = new net.Socket(); | |
| client.connect(VIRTUA_PORT, VIRTUA_HOST, function() { | |
| console.log('Connected, authenticating... (may take several seconds)'); | |
| client.write(authMsg); | |
| }); | |
| let di = 0; | |
| let ndi = 0; | |
| let started = false; | |
| let sentOutSymbols = false; | |
| function addSymbols() { | |
| console.log("Gonna add symbols..."); | |
| let i = 0; | |
| for (const id of IDS) { | |
| if (+id) { | |
| console.log(`[#${++i}] Adding symbol for record id ${id}...`); | |
| client.write(createSymbolPacket(+id)); | |
| } | |
| } | |
| sentOutSymbols = true; | |
| } | |
| client.on('data', function(data) { | |
| // hardcoded server response to auth packet (it takes several seconds to finally respond...) | |
| if (data.toString("hex") === "bc3ca03a2838060a2a8648ce130887681803a02a30283016810101a203820101a40c060a2a8648ce130d8768182f300e810104a203820101a40402022328") | |
| { | |
| console.log("Authed!!!"); | |
| if (!started) { | |
| started = true; | |
| setTimeout(() => client.write(Buffer.from(`\xbd${String.fromCodePoint(COMPUTER_NAME.length + 3)}\x9f\x26${String.fromCodePoint(COMPUTER_NAME.length)}${COMPUTER_NAME}`, "ascii")), 500); | |
| setTimeout(() => addSymbols(), 3000); | |
| } | |
| } | |
| //console.log('Received: ' + data); | |
| if (started && sentOutSymbols && (data.byteLength % 30) !== 0 && (data.byteLength % 11) !== 0) { | |
| console.log(`Odd length packet (${data.byteLength} bytes)`, data.toString("hex").match(/../g).join(' ')); | |
| } | |
| // example confirmation: bf814807a605a103800101 | |
| // example rejection: bf81481aa618a116800100a111300f06072a8648ce1304010202157c1b00 | |
| // that's a kinda simple loop and far from being perfectly valid | |
| // but still better than the first iteration :) | |
| let o = 0; | |
| while (true) { | |
| if (data.byteLength - o <= 4) break; | |
| if (data.readUInt8(o+0) == 0xbf | |
| && data.readUInt8(o+1) == 0x81 | |
| && data.readUInt8(o+2) == 0x48) { | |
| const chunklen = data.readUInt8(o+3); | |
| if (chunklen >= 7) { | |
| if (data.readUInt8(o+8) == 0x80 && data.readUInt8(o+9) == 0x01) { | |
| const added = !!data.readUInt8(o+10); | |
| if (added) { | |
| console.log(`Added #${++di}`); | |
| } else { | |
| console.log(`Not added #${++ndi}`); // cause was already added | |
| } | |
| } | |
| } | |
| o += 4 + chunklen; | |
| continue; | |
| } | |
| break; // well shit | |
| } | |
| }); | |
| client.on('close', function() { | |
| console.log('Connection closed'); | |
| }); |
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
| // Skrypt, który wyszukuje rekordy bibliograficzne w internetowym katalogu NUKAT po polu kontrolnym 035 | |
| // a następnie wypisuje ich mapowanie do wewnętrznego ID rekordu w nukatowskim systemie Virtua. | |
| // | |
| // Lista owych wewnętrznych ID jest potrzebna do działania skryptu "!virtua_add_library_symbol.js". | |
| // | |
| // Wypisanie samych numerów ID może się odbyć poprzez np.: | |
| // $ node nukat_035_to_001vtls.js > kontrolne_do_dopisania_mapping.txt | |
| // <w tym momencie sprawdzić czy nie ma żadnych problemów w pliku wyszukując frazę "PROBLEM" i poprawić jeśli jest...> | |
| // $ cat kontrolne_do_dopisania_mapping.txt | cut -d' ' -f3 | sort --numeric-sort --unique > ids.txt | |
| const assert = require("assert"); | |
| const fs = require("fs"); | |
| const controls = fs.readFileSync("kontrolne_do_dopisania.txt", "utf-8").toString().split("\n").map(l => l.trim()); | |
| function splitToNChunks(array, n) { | |
| let result = []; | |
| for (let i = n; i > 0; i--) { | |
| result.push(array.splice(0, Math.ceil(array.length / i))); | |
| } | |
| return result; | |
| } | |
| const chunks = splitToNChunks([...controls], 30); | |
| for (const chunk of chunks) { | |
| (async (chunk) => { | |
| for (const control of chunk) { | |
| try { | |
| const html = await fetch(`http://katalog.nukat.edu.pl/search/query?match_1=PHRASE&field_1=control&term_1=${control}&theme=nukat`).then(r => r.text()); | |
| assert(html.includes("Bieżące wyszukiwanie:")); | |
| assert(html.includes("<span>Opisy od 1 do 1 z 1</span>")); | |
| assert(!html.includes("Brak wyników dla wyszukiwania")); | |
| const vtls = /item\?id=chamo:([0-9]+)\&/.exec(html)[1]; | |
| assert(vtls && vtls.length); | |
| console.log(`${control} => ${vtls}`); | |
| } catch (e) { | |
| console.log(`PROBLEM(${control}): ${e+''}`); | |
| } | |
| } | |
| })(chunk); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment