- Il risveglio (src/engine/risveglio.js): primo piano sopra la scena, con le battute mentre le palpebre si aprono e i vecchietti che danno la notizia del palio; «Salta» lo salta. - Il telefonino del Barone (src/engine/telefono.js): sale dal basso come il libro, la home di sanpiero50037.it dalla Wayback Machine nello schermo, il mattone del muro apre il muro. - Il muro (assets/muro, tools/muro.py): la pagina vera rifatta statica dal muro riesumato, coi 24 messaggi del dump (senza email né IP) e quelli del gioco, che arrivano con la storia; «Scrivi» spento. - Il gioco sgancia i service worker di altre app rimasti sul suo indirizzo (la Bussola su localhost:8080). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiECd9RWGMKV7n3K1mdy8T
81 lines
4.3 KiB
Python
81 lines
4.3 KiB
Python
#!/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'<img[^>]*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'<p align="center"><a href="index.html" style="font: bold 14px Tahoma; color: #800000">« Torna al Muro</a></p>'
|
|
(OUT / page).write_bytes(re.sub(rb'</body>', back + b'</body>', 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()
|