diff --git a/boop.py b/boop.py new file mode 100644 index 0000000..a619384 --- /dev/null +++ b/boop.py @@ -0,0 +1,86 @@ +from sanic import Blueprint, exceptions, response +from time import time +from asyncio import Future +import asyncio +from datetime import datetime +from asyncio import Queue +from random import randint +from boop_process import boop_process + +bp = Blueprint("boop", url_prefix="/boop") + +bp.ctx.boopbox_queue = {'a': Queue(), 'b': Queue(), 'c': Queue()} +bp.ctx.busy = {'a': False, 'b': False, 'c': False} +bp.ctx.last_tag = {'a': None, 'b': None, 'c': None} +bp.ctx.repeats = {'a': None, 'b': None, 'c': None} + +def enable_nfc(enable, boopbox_id): + log.info('NFC is ' + ('enabled' if enable else 'disabled')) + app.ctx.queue.put_nowait({'_': 'nfc', 'enabled': enable}) + app.ctx.nfc_enabled = enable; + +@bp.get("/refresh") +async def refresh_boops(request): + await boop_process(request.app.ctx.om.cache.values(), request.app.ctx.boop) + return response.text('ok') + +@bp.get("/") +async def show_boopbox(request): + tpl = request.app.ctx.tpl.get_template('boopbox.html') + return response.html(tpl.render()) + +@bp.get("/getqueue/") +async def boop_queue(request, boopbox_id): + + items = [] + queue = bp.ctx.boopbox_queue[boopbox_id] + while 1: + try: + item = queue.get_nowait() + except asyncio.queues.QueueEmpty: + if items: + break + + # Try one last time to get a task, then fail. + bp.ctx.busy[boopbox_id] = False + try: + item = await asyncio.wait_for(queue.get(), timeout=5) + except asyncio.exceptions.TimeoutError: + break + + items.append(item) + + if len(items): + bp.ctx.busy[boopbox_id] = True + return response.json(items) + +@bp.post("/read") +async def handle_boop(request): + payload = request.json + queue = bp.ctx.boopbox_queue[payload['boopbox_id']] + + if bp.ctx.busy[payload['boopbox_id']]: return response.text('busy') + + + await queue.put({'_': 'play', 'src': f"/res/snd/error.wav"}) + + if bp.ctx.last_tag[payload['boopbox_id']] == payload['id']: + bp.ctx.repeats[payload['boopbox_id']] += 1 + else: + bp.ctx.last_tag[payload['boopbox_id']] = payload['id'] + bp.ctx.repeats[payload['boopbox_id']] = 0 + + if bp.ctx.repeats[payload['boopbox_id']] > 5: + await queue.put({'_': 'play', 'src': f"/res/snd/ratelimit.wav"}) + await queue.put({'_': 'bye'}) + return response.text('ok') + + if bp.ctx.repeats[payload['boopbox_id']] > 10: + await queue.put({'_': 'talk', 'who': 'tiger', 'msg': f"Hey! Stop that! You're not the only one here!"}) + await queue.put({'_': 'bye'}) + return response.text('ok') + + request.app.ctx.boop.execute('INSERT INTO boop(tag_id, station, ts) VALUES (?,?,?)', (payload['id'], payload['boopbox_id'], int(time()))) + request.app.ctx.boop.commit() + + return response.text('ok') diff --git a/boop_process.py b/boop_process.py new file mode 100644 index 0000000..db70c4d --- /dev/null +++ b/boop_process.py @@ -0,0 +1,42 @@ +from datetime import datetime +from random import randint +from hashlib import md5 +from base64 import b16encode + +fibonacci = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233] +current_date = datetime.today() + +async def boop_process(orders, db): + print(f'Processing {len(orders)} orders') + db.execute('DELETE FROM player') + + for o in orders: + + tags = [] + code = o.code + tag_id = o.nfc_id or b16encode(md5(code.encode()).digest()).decode()[:14] + birthday = o.birth_date + badge_id = o.badge_id or randint(0,200) + room = o.actual_room or randint(100,400) + country = o.country + name = o.first_name.lower().strip() + + birth_date = datetime.strptime(birthday, "%Y-%m-%d").date() + + age = current_date.year - birth_date.year + if (current_date.month, current_date.day) < (birth_date.month, birth_date.day): + age -= 1 + + # Check if the birthday is today + if (current_date.month, current_date.day) == (birth_date.month, birth_date.day): + tags.append('birthday') + + # Check if the badge is a fib number + if badge_id in fibonacci: + tags.append('fibonacci') + + db.execute('INSERT INTO player(tag_id, code, tags, birthday, badge_id, age, name, room, country) VALUES (?,?,?,?,?,?,?,?,?)', + (tag_id, code, ','.join(tags), birthday, badge_id, age, name, room, country) + ) + + db.commit() diff --git a/boops.py b/boops.py new file mode 100644 index 0000000..e69de29 diff --git a/res/bg.png b/res/bg.png new file mode 100644 index 0000000..d44611e Binary files /dev/null and b/res/bg.png differ diff --git a/res/boopbox.css b/res/boopbox.css new file mode 100644 index 0000000..14326e1 --- /dev/null +++ b/res/boopbox.css @@ -0,0 +1,95 @@ +/* Fonts */ +@font-face {font-family: 'Alegreya';src: url("font/alegreya-latin-600-normal.woff2") format('woff2');} +@font-face {font-family: 'Nexa Rust Sans'; src: url("font/NexaRustSans-Black.otf");} +@font-face {font-family: 'Ticketing'; src: url("font/ticketing.otf");} +@font-face {font-family: 'Trade Winds'; src: url("font/TradeWinds-Regular.ttf");} + +* {margin:0;padding:0;border:0;box-sizing:border-box;} +body {font-family: 'Alegreya', serif;background:#000;color:#fff;overflow:hidden; /*cursor: none;*/} +em {color:#fc0;} +#border {border:red 1px dotted;position:absolute;top:50%;left:50%;transform:translateX(-640px) translateY(-400px);width:1280px;height:800px;z-index:1000;} +#main {position:absolute;top:50%;left:50%;transform:translateX(-640px) translateY(-400px);background:linear-gradient( + rgba(0, 0, 0, 0.5), + rgba(0, 0, 0, 0.5) + ),url('bg.png');background-size:cover;background-repeat:no-repeat;width:1280px;height:800px;border:1px solid #000;overflow:hidden;} +#commands a {color:#fff;display:inline-block;padding:0.2em;margin:0 0.25em;background:#009;font-family:monospace;font-size:2em;line-height:1.2em;} + +#debug {font-family:monospace;} + +#error {font-family:'Ticketing';font-size:2em;position:absolute;top:50%;left:50%;transform:translateX(-640px) translateY(-400px);background:#333;width:1280px;height:800px;background:rgba(0,0,0,0.75);z-index:200;box-sizing:border-box;} +#error p {display:block;margin-top:10em;text-align:center;background:#00c;padding:0.5em 0;} + +/* Personaggi */ +.char {position:absolute;transform:translateX(0px);transition: all 0.3s;z-index:500;} +#wolf {width:400px;left:880px;top:240px;} +#tiger {width:500px;left:-60px;top:90px;} +#wolf[disabled] {transform:translateX(398px);} +#tiger[disabled] {transform:translateX(-440px);} + +/* Nfc status in basso a dx */ +#nfcstat {background:#090;position:absolute;right:32px;width:320px;top:716px;font-size:1.7em;padding: 8px 16px;text-align:center;border-radius:8px} +#nfcstat[disabled] {background:#a00;} +#nfcstat img {margin-left:0.2em;margin-top:-4px;height:0.9em;display:inline-block;vertical-align:middle; filter: invert(100%);} + +.hidden {opacity: 0;} +.welcome-back {transition: all .25s;} + +/* Message box */ +#msgbox {backdrop-filter: blur(3px);position:absolute;width:1180px;height:190px;background:rgba(0.7,0,0,0.5);padding:26px;left:20px;bottom:20px;border-radius:8px;color:#fff;font-weight:bold;font-size:32px;opacity:1;transition: opacity .3s;z-index:600;} +#msgbox[disabled] {opacity:0;} +#msgbox img {height:60px;display:inline-block;vertical-align:middle; filter: invert(100%)} +#msgbox span {display:block;} + +@keyframes touch {from{opacity:0.3;} to{opacity:0.9;}} +#touch {animation-name:touch;animation-duration:0.7s;animation-iteration-count:infinite;animation-direction:alternate;} +#touch[disabled] {display:none;} + +/* Events */ + +#events > p {margin-bottom:1em;} +#events > p > a {display:inline-block;border-radius:16px;padding:0.1em 0.7em;margin-right:0.3em;text-decoration: none;color:#fff;font-size:1.3em;background:rgba(100,0,200,0.2);border:4px solid rgba(0,0,0,0);} +#events > p > a:hover {border:4px solid darkorchid;} + +#eventsbox div, #column {backdrop-filter: blur(6px);background:rgba(0,0,0,0.2);border-radius:16px;text-shadow: 0 0 5px rgba(0,0,0,0.5);} + +#events {transition: all .25s;position:absolute;left:32px;height:740px;width:870px;top:32px;} + +#eventsbox span {float:right;background:#300;border-radius:6px;font-size:0.9em;padding:0.3em 0.5em;} +#eventsbox span[data-location~="Reception"] {background:#613583;} +#eventsbox span[data-location~="Restaurant"] {background:#26a269;} +#eventsbox span[data-location~="Disco"] {background:#e5a50a;} +#eventsbox span[data-location~="Dealers"] {background:#1a5fb4;} +#eventsbox span[data-location~="Tent"] {background:#a51d2d;} +#eventsbox span[data-location~="Stage"] {background:#63452c;} + +#eventsbox > div {padding:1em 1em 0.5em 1em;font-size:1.5em;margin-bottom:1em;border-bottom:5px solid;border-radius:8px 8px 0 0;border-image: linear-gradient(to right, darkblue, darkorchid 50%, rgba(0,0,0,0) 50.1%) 1;transition:all 0.5s;} + +#eventsbox {height:690px;overflow:auto;overflow:auto;scrollbar-width: none;-webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 5%, #000 95%, transparent);} + +.eventmeter {box-sizing:border-box;display:inline-block;background:#090;height:4px;width:100%;margin-left:-1em;} + +/* Side column and tv */ +#tv {position:absolute;background:#000;right:32px;width:320px;height:180px;top:32px;border-radius:16px;} +#column {transition: all .25s;text-align:center;position:absolute;right:32px;width:320px;height:460px;top:230px;padding:1em;} +#column p {font-size:1.3em;} + +h1,h2,h3 {font-family: 'Trade Winds';} +h1 {text-align:center;font-size:2em;} + +hr {border: 1px solid white;opacity:0.2;width:320px;max-width:60%;margin:0.5em auto;} + +@keyframes shake { + 0% { transform: translate(1px, 1px); } + 10% { transform: translate(-1px, -2px); } + 20% { transform: translate(-3px, 0px); } + 30% { transform: translate(3px, 2px); } + 40% { transform: translate(1px, -1px); } + 50% { transform: translate(-1px, 2px); } + 60% { transform: translate(-3px, 1px); } + 70% { transform: translate(3px, 1px); } + 80% { transform: translate(-1px, -1px); } + 90% { transform: translate(1px, 2px); } + 100% { transform: translate(1px, -2px); } +} + +.yell {line-height:1.1em;font-size:2.5em;animation: shake 0.5s;animation-iteration-count: infinite; } diff --git a/res/boopbox.js b/res/boopbox.js new file mode 100644 index 0000000..055110f --- /dev/null +++ b/res/boopbox.js @@ -0,0 +1,246 @@ +const box = document.getElementById("msgbox"); +const msgbox = document.getElementById("msgcontent"); +const wolf = document.getElementById("wolf"); +const tiger = document.getElementById("tiger"); +const touch = document.getElementById("touch"); +const bye_elements = document.getElementsByClassName('bye'); +const welcome_back_elements = document.getElementsByClassName('welcome-back'); + +const apiUrl = "/manage/api/events.json"; +let currentTime = new Date(); + +const searchParams = new URL(window.location.href).searchParams; +const boopboxId = searchParams.get('id'); + +if(!boopboxId) { + alert("Boopbox id is not set!"); +} + +window.onload = function() { + updateEvents(); + loop(); + clock(); + //fastForward(); +} + +let cachedData = null; + +function updateEvents() { + fetch(apiUrl) + .then(response => response.json()) + .then(data => { + cachedData = data; + updateDivs(data); + }) + .catch(error => { + if (cachedData) { + updateDivs(cachedData); + } else { + console.error('Request failed and no cached data available:', error); + } + }); +} + +function updateDivs(data) { + const eventsContainer = document.getElementById("eventsbox"); + const tenMinutesFromNow = new Date(currentTime.getTime() + 10 * 60000); + const tenMinutesAgo = new Date(currentTime.getTime() - 10 * 60000); + let visibleEvents = 0; + + data.forEach(event => { + const eventStart = new Date(event.start); + const eventEnd = new Date(event.end); + + const eventPercent = Math.max(0, Math.min(100, ((currentTime - eventStart) / (eventEnd - eventStart)) * 100)); + + if (eventStart <= tenMinutesFromNow && eventEnd >= tenMinutesAgo) { + + visibleEvents++; + let eventDiv = document.getElementById(`event-${event.id}`); + + if (!eventDiv) { + eventDiv = document.createElement('div'); + eventDiv.id = `event-${event.id}`; + eventsContainer.appendChild(eventDiv); + } + + eventDiv.innerHTML = ` + ${event.location} +

${event.title}

+

${event.about}

+

${Math.ceil(Math.max(0, (eventEnd - currentTime) / 60000))} minutes left

+ `; + + eventDiv.style.borderImage = `linear-gradient(to right, darkblue, darkorchid ${eventPercent}%, rgba(0,0,0,0) ${eventPercent+0.1}%) 1`; + + } else { + const eventDiv = document.getElementById(`event-${event.id}`); + + if (eventDiv) { + eventDiv.remove(); + } + } + }); + + if(visibleEvents == 0) { + document.getElementById("nothing-happening").style.display = 'block'; + } else { + document.getElementById("nothing-happening").style.display = 'none'; + } +} + +function clock() { + setInterval(() => { + currentTime = new Date(); + let ts = currentTime.toString(); + ts = ts.replace("GMT+0200 (Central European Summer Time)", ""); + ts = ts.replace("2023", "
"); + document.getElementById("clock").innerHTML = ts; + }, 1000); +} + +function fastForward() { + currentTime = new Date("2023-05-29T18:00Z"); + setInterval(() => { + updateDivs(cachedData); + currentTime.setMinutes(currentTime.getMinutes()+1); + }, 100); +} + +async function loop() { + while (true) { + try { + document.getElementById("nfcstat").removeAttribute('disabled'); + const response = await fetch("/boop/getqueue/"+boopboxId); + const json = await response.json(); + document.getElementById("nfcstat").setAttribute('disabled', 'true'); + for(i = 0; i < json.length; i++) { + console.log(json[i]); + document.getElementById('debug').innerHTML = JSON.stringify(json[i]); + try { + await window[json[i]["_"]](json[i]); + } catch (e) { + document.getElementById('debug').innerHTML = e; + } + } + } catch (e) { + await new Promise(r => setTimeout(r, 2000)); + console.error(e); + } + } +} + +/*window.oncontextmenu = function(event) { + event.preventDefault(); + event.stopPropagation(); + return false; +};*/ + +async function talk(dict) { + + for (const c of welcome_back_elements) { + c.classList.add('hidden'); + } + + character = document.getElementById(dict.who); + character.removeAttribute('disabled'); + box.removeAttribute('disabled'); + for(j = 0;j < dict.msg.length; j++) { + to = 20; + msgbox.innerHTML += dict.msg[j]; + + if(dict.msg[j] == '.') {to = 400;} + if(dict.msg[j] == ',') {to = 200;} + + await new Promise(r => setTimeout(r, to)); + } + await new Promise(r => setTimeout(r, 1000)); +} + +async function bye(dict) { + + msgbox.innerHTML = ''; + + for (const c of bye_elements) { + c.setAttribute('disabled', 'true'); + } + + for (const c of welcome_back_elements) { + c.classList.remove('hidden'); + } +} + +async function play(dict) { + const audio = new Audio(dict.src); + await audio.play(); +} + +async function wait(dict) { + return new Promise((resolve) => { + setTimeout(resolve, dict.time * 1000); + }); +} + +async function orchestrate(thing) { + for(var i = 0; i < msg.length; i++) { + task = msg[i] + + // It's a command + if(task.length == 3) { + switch(task) { + case '!w-': + wolf.setAttribute('disabled', 'true'); + break; + case '!t-': + tiger.setAttribute('disabled', 'true'); + break; + case '!w+': + wolf.removeAttribute('disabled'); + break; + case '!t+': + tiger.removeAttribute('disabled'); + break; + case '!s+': + msgbox.classList.add('yell'); + break; + case '!s-': + msgbox.classList.remove('yell'); + break; + default: + alert('Invalid command received.' + task); + } + + continue; + } + + + touch.setAttribute("disabled", "true"); + + // It's a text message + if (task.startsWith('!w:') || task.startsWith('!t:')) { + + if(task[1] == 'w' && wolf.getAttribute('disabled') == 'true') { + wolf.removeAttribute('disabled'); + } + + if(task[1] == 't' && tiger.getAttribute('disabled') == 'true') { + tiger.removeAttribute('disabled'); + } + + msgbox.style.color = (task[1] == 'w')?'#FAEBD7':'#ADD8E6'; + + txt = task.substr(3); + + + + await new Promise(r => setTimeout(r, 500)); + //touch.removeAttribute("disabled"); + await new Promise(r => setTimeout(r, 1000)); + msgbox.innerHTML = ''; + + console.log(task); + } + } + + box.setAttribute('disabled', 'true'); +} diff --git a/res/botbg2.png b/res/botbg2.png new file mode 100644 index 0000000..632657c Binary files /dev/null and b/res/botbg2.png differ diff --git a/res/error_openbox.png b/res/error_openbox.png new file mode 100644 index 0000000..b54a081 Binary files /dev/null and b/res/error_openbox.png differ diff --git a/res/font/BALLSONTHERAMPAGE.ttf b/res/font/BALLSONTHERAMPAGE.ttf new file mode 100644 index 0000000..f28ba6c Binary files /dev/null and b/res/font/BALLSONTHERAMPAGE.ttf differ diff --git a/res/font/NexaRustExtras-Free.otf b/res/font/NexaRustExtras-Free.otf new file mode 100644 index 0000000..c9f13b4 Binary files /dev/null and b/res/font/NexaRustExtras-Free.otf differ diff --git a/res/font/NexaRustSans-Black.otf b/res/font/NexaRustSans-Black.otf new file mode 100644 index 0000000..2afcbc9 Binary files /dev/null and b/res/font/NexaRustSans-Black.otf differ diff --git a/res/font/TradeWinds-Regular.ttf b/res/font/TradeWinds-Regular.ttf new file mode 100644 index 0000000..6986abf Binary files /dev/null and b/res/font/TradeWinds-Regular.ttf differ diff --git a/res/font/alegreya-latin-600-normal.woff2 b/res/font/alegreya-latin-600-normal.woff2 new file mode 100644 index 0000000..9d85c3f Binary files /dev/null and b/res/font/alegreya-latin-600-normal.woff2 differ diff --git a/res/font/ticketing.otf b/res/font/ticketing.otf new file mode 100644 index 0000000..e90451a Binary files /dev/null and b/res/font/ticketing.otf differ diff --git a/res/icons/keypad.svg b/res/icons/keypad.svg new file mode 100644 index 0000000..cbbfe47 --- /dev/null +++ b/res/icons/keypad.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/icons/lock.svg b/res/icons/lock.svg index cdd7193..391061e 100644 --- a/res/icons/lock.svg +++ b/res/icons/lock.svg @@ -1,46 +1 @@ - - - - - - - - - + \ No newline at end of file diff --git a/res/icons/nfc.svg b/res/icons/nfc.svg new file mode 100644 index 0000000..b7ab5ed --- /dev/null +++ b/res/icons/nfc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/icons/refresh.svg b/res/icons/refresh.svg new file mode 100644 index 0000000..5148a97 --- /dev/null +++ b/res/icons/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/icons/touch.svg b/res/icons/touch.svg new file mode 100644 index 0000000..a0a317a --- /dev/null +++ b/res/icons/touch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/orig/FurizonBanner2023_BG.png b/res/orig/FurizonBanner2023_BG.png new file mode 100644 index 0000000..966a173 Binary files /dev/null and b/res/orig/FurizonBanner2023_BG.png differ diff --git a/res/orig/TIGER.png b/res/orig/TIGER.png new file mode 100644 index 0000000..e3956b7 Binary files /dev/null and b/res/orig/TIGER.png differ diff --git a/res/orig/wolf.png b/res/orig/wolf.png new file mode 100644 index 0000000..7d2773f Binary files /dev/null and b/res/orig/wolf.png differ diff --git a/res/snd/01.mp3 b/res/snd/01.mp3 new file mode 100644 index 0000000..b26cf0b Binary files /dev/null and b/res/snd/01.mp3 differ diff --git a/res/snd/02.mp3 b/res/snd/02.mp3 new file mode 100644 index 0000000..d4db4da Binary files /dev/null and b/res/snd/02.mp3 differ diff --git a/res/snd/03.mp3 b/res/snd/03.mp3 new file mode 100644 index 0000000..b9b22f4 Binary files /dev/null and b/res/snd/03.mp3 differ diff --git a/res/snd/04.mp3 b/res/snd/04.mp3 new file mode 100644 index 0000000..925d60d Binary files /dev/null and b/res/snd/04.mp3 differ diff --git a/res/snd/05.mp3 b/res/snd/05.mp3 new file mode 100644 index 0000000..59b83b8 Binary files /dev/null and b/res/snd/05.mp3 differ diff --git a/res/snd/06.mp3 b/res/snd/06.mp3 new file mode 100644 index 0000000..28464c5 Binary files /dev/null and b/res/snd/06.mp3 differ diff --git a/res/snd/07.mp3 b/res/snd/07.mp3 new file mode 100644 index 0000000..0fcd952 Binary files /dev/null and b/res/snd/07.mp3 differ diff --git a/res/snd/08.mp3 b/res/snd/08.mp3 new file mode 100644 index 0000000..904f3d5 Binary files /dev/null and b/res/snd/08.mp3 differ diff --git a/res/snd/09.mp3 b/res/snd/09.mp3 new file mode 100644 index 0000000..6c29a7b Binary files /dev/null and b/res/snd/09.mp3 differ diff --git a/res/snd/10.mp3 b/res/snd/10.mp3 new file mode 100644 index 0000000..36288d3 Binary files /dev/null and b/res/snd/10.mp3 differ diff --git a/res/snd/11.mp3 b/res/snd/11.mp3 new file mode 100644 index 0000000..650d3a9 Binary files /dev/null and b/res/snd/11.mp3 differ diff --git a/res/snd/12.mp3 b/res/snd/12.mp3 new file mode 100644 index 0000000..b5fb15e Binary files /dev/null and b/res/snd/12.mp3 differ diff --git a/res/snd/13.mp3 b/res/snd/13.mp3 new file mode 100644 index 0000000..cf4338d Binary files /dev/null and b/res/snd/13.mp3 differ diff --git a/res/snd/14.mp3 b/res/snd/14.mp3 new file mode 100644 index 0000000..21468d8 Binary files /dev/null and b/res/snd/14.mp3 differ diff --git a/res/snd/15.mp3 b/res/snd/15.mp3 new file mode 100644 index 0000000..2bb0e41 Binary files /dev/null and b/res/snd/15.mp3 differ diff --git a/res/snd/16.mp3 b/res/snd/16.mp3 new file mode 100644 index 0000000..a97c6fe Binary files /dev/null and b/res/snd/16.mp3 differ diff --git a/res/snd/17.mp3 b/res/snd/17.mp3 new file mode 100644 index 0000000..dde9052 Binary files /dev/null and b/res/snd/17.mp3 differ diff --git a/res/snd/18.mp3 b/res/snd/18.mp3 new file mode 100644 index 0000000..ca97513 Binary files /dev/null and b/res/snd/18.mp3 differ diff --git a/res/snd/19.mp3 b/res/snd/19.mp3 new file mode 100644 index 0000000..d3db922 Binary files /dev/null and b/res/snd/19.mp3 differ diff --git a/res/snd/20.mp3 b/res/snd/20.mp3 new file mode 100644 index 0000000..56e6e49 Binary files /dev/null and b/res/snd/20.mp3 differ diff --git a/res/snd/21.mp3 b/res/snd/21.mp3 new file mode 100644 index 0000000..0cdc661 Binary files /dev/null and b/res/snd/21.mp3 differ diff --git a/res/snd/22.mp3 b/res/snd/22.mp3 new file mode 100644 index 0000000..01b9224 Binary files /dev/null and b/res/snd/22.mp3 differ diff --git a/res/snd/23.mp3 b/res/snd/23.mp3 new file mode 100644 index 0000000..f8f283e Binary files /dev/null and b/res/snd/23.mp3 differ diff --git a/res/snd/24.mp3 b/res/snd/24.mp3 new file mode 100644 index 0000000..7770625 Binary files /dev/null and b/res/snd/24.mp3 differ diff --git a/res/snd/25.mp3 b/res/snd/25.mp3 new file mode 100644 index 0000000..d564cf9 Binary files /dev/null and b/res/snd/25.mp3 differ diff --git a/res/snd/error.wav b/res/snd/error.wav new file mode 100644 index 0000000..16d679e Binary files /dev/null and b/res/snd/error.wav differ diff --git a/res/snd/ratelimit.wav b/res/snd/ratelimit.wav new file mode 100644 index 0000000..dc71ed6 Binary files /dev/null and b/res/snd/ratelimit.wav differ diff --git a/res/tiger.png b/res/tiger.png new file mode 100644 index 0000000..2f76330 Binary files /dev/null and b/res/tiger.png differ diff --git a/res/tr.webm b/res/tr.webm new file mode 100644 index 0000000..34dc9eb Binary files /dev/null and b/res/tr.webm differ diff --git a/res/tv.jpg b/res/tv.jpg new file mode 100644 index 0000000..2e8faa8 Binary files /dev/null and b/res/tv.jpg differ diff --git a/res/video/coin-intro.webm b/res/video/coin-intro.webm new file mode 100644 index 0000000..db3e757 Binary files /dev/null and b/res/video/coin-intro.webm differ diff --git a/res/video/coin-loop.webm b/res/video/coin-loop.webm new file mode 100644 index 0000000..fa5a381 Binary files /dev/null and b/res/video/coin-loop.webm differ diff --git a/res/video/coin.webp b/res/video/coin.webp new file mode 100644 index 0000000..1624f85 Binary files /dev/null and b/res/video/coin.webp differ diff --git a/res/wolf.png b/res/wolf.png new file mode 100644 index 0000000..be22f19 Binary files /dev/null and b/res/wolf.png differ diff --git a/res/wood.jpg b/res/wood.jpg new file mode 100644 index 0000000..81bcf95 Binary files /dev/null and b/res/wood.jpg differ diff --git a/tpl/boopbox.html b/tpl/boopbox.html new file mode 100644 index 0000000..5f5bd66 --- /dev/null +++ b/tpl/boopbox.html @@ -0,0 +1,85 @@ + + + + + + + +
+ Fs + Wolf hide + Wolf show + Tiger hide + Tiger show + Chatbox show + Chatbox hide + Debug command +
+ + + + +
+ + + +
(touch the screen to continue )
+ +
+
+

It looks like nothing is happening now~
_-¯¯-¯_
See you later :)

+
+

See more events with our app!

+ +
+ + + + + +
+

Useful Info

+

CALL SECURITY

+

Dial (+39) 375 732 4734

+
+

Reception Open

+

Every day 8:00~19:00

+
+

NFC issues?

+

Search for Foxo (Badge ID 3) or write to @unfoxo on Telegram

+
+

+
+ +
Dealer's Den
+
+ + + +