#!/usr/bin/env python3 """Il Muro del Sanpierino dentro il gioco (Andrea, 31 ottobre): dal dump del muro vero riesumato (~/Workspaces/misc/spswall) prepara assets/muro/ per la pagina statica del telefonino del Barone. - messaggi.json: i messaggi veri (soprannome, data, testo), con gli accenti rimessi a posto. Niente email né IP. Solo quelli del dump (i primi 24, novembre 2007): i 654 ritrovati nella Wayback Machine restano nel muro riesumato (spswall/archivio/importa.py), fuori dal gioco (Andrea, 31 ottobre). - img/: solo gli smile usati e le icone della pagina; muro.css è il CSS originale; le due pagine di testo, com'erano. python3 tools/muro.py [percorso del dump] [cartella www del muro] """ import json, re, shutil, sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent SPS = Path.home() / 'Workspaces/misc/spswall' DUMP = Path(sys.argv[1]) if len(sys.argv) > 1 else SPS / 'db/01-spswall.sql' WWW = Path(sys.argv[2]) if len(sys.argv) > 2 else SPS / 'www' OUT = ROOT / 'assets/muro' # Le icone della pagina, oltre agli smile. PAGE = ['logo_trasparente_obliquo.gif', 'spacer.gif', 'pagefirst.gif', 'pageback.gif', 'postmessage.gif', 'refresh.gif', 'home.gif', 'pageahead.gif', 'pagelast.gif', 'logomanzolo.jpg'] def rows(sql, table): """Le righe di INSERT INTO `table` del dump di phpMyAdmin, come liste di valori.""" m = re.search(r'INSERT INTO `%s` \(([^)]*)\) VALUES\s*(.*?);\n' % table, sql, re.S) cols = [c.strip(' `') for c in m.group(1).split(',')] body, out, row, val, i, quoted = m.group(2), [], [], '', 0, False while i < len(body): ch = body[i] if quoted: if ch == '\\': val += {'n': '\n', 'r': '\r', 't': '\t', '0': ''}.get(body[i + 1], body[i + 1]); i += 2; continue if ch == "'" and body[i + 1:i + 2] == "'": val += "'"; i += 2; continue if ch == "'": quoted = False else: val += ch elif ch == "'": quoted = True elif ch == '(': row, val = [], '' elif ch == ',': row.append(val.strip()); val = '' elif ch == ')': row.append(val.strip()); out.append(dict(zip(cols, row))) elif not ch.isspace(): val += ch i += 1 return out def fix(text): """Il muro scriveva UTF-8 su una connessione latin1: nel dump gli accenti sono «Ã¨». Si rimettono a posto.""" for enc in ('latin1', 'cp1252'): try: return text.encode(enc).decode('utf8') except (UnicodeEncodeError, UnicodeDecodeError): pass return text def main(): sql = DUMP.read_text('utf8') smiles = {r['codsml']: r['linksml'] for r in rows(sql, 'tpsmiles')} # Le immagini prese da fuori (http://...) non si caricano: il vecchio dominio oggi può essere di chiunque. outside = lambda t: re.sub(r']*src="?http[^>]*>', '', t, flags=re.I) messages = [{'nick': fix(r['nikute']), 'date': r['datmsg'], 'text': outside(fix(r['desmsg']))} for r in rows(sql, 'tpmsgute')] messages.sort(key=lambda m: m['date'], reverse=True) # Gli smile usati: dai messaggi veri e da quelli del gioco (story.closeups.muro_sanpierino.messages). ours = (ROOT / 'src/story/story.js').read_text('utf8') used = {code: link for code, link in smiles.items() if code in ours or any(code in m['text'] or code in m['nick'] for m in messages)} shutil.rmtree(OUT / 'img', ignore_errors=True) (OUT / 'img').mkdir(parents=True) pictures = {n for m in messages for n in re.findall(r'src="\./img/([^"]+)"', m['text'] + m['nick'])} for name in PAGE + sorted(set(used.values()) | pictures): shutil.copy(WWW / 'imgwall' / name, OUT / 'img' / name) shutil.copy(WWW / 'class/thewall.css', OUT / 'muro.css') # Le due pagine di testo, com'erano; in fondo un link per tornare al muro (nel telefonino non c'è il «Indietro»). for page in ('Responsabilita.htm', 'guida_al_muro_del_sanpierino.htm'): html = (WWW / page).read_bytes() back = b'

« Torna al Muro

' (OUT / page).write_bytes(re.sub(rb'', back + b'', html, count=1, flags=re.I)) (OUT / 'messaggi.json').write_text(json.dumps({'smiles': used, 'messages': messages}, ensure_ascii=False, indent=1) + '\n') print(f'{len(messages)} messaggi, {len(used)} smile -> {OUT.relative_to(ROOT)}') if __name__ == '__main__': main()