diff --git a/money.py b/money.py new file mode 100644 index 0000000..7808745 --- /dev/null +++ b/money.py @@ -0,0 +1,53 @@ +from sanic import Blueprint, exceptions, response +from time import time +from asyncio import Future +import asyncio +from datetime import datetime + +bp = Blueprint("money", url_prefix="/money") + +@bp.post("/pos") +async def do_transaction(request): + + message = '' + tx_id = request.app.ctx.money.execute('INSERT INTO tx(tag_id, amount, ts) VALUES (?,?,?) RETURNING id', (request.form.get('nfc_id'), request.form.get('total'), time())).fetchone()[0] + + for item, qty in request.form.items(): + if not item.startswith('itm_'): continue + if qty[0] == '0': continue + request.app.ctx.money.execute('INSERT INTO tx_items(tx_id, item_id, qty) VALUES (?,?,?)', (tx_id, item[4:], qty[0])) + + request.app.ctx.money.commit() + return await show_transactions(request, message='Transazione eseguita con successo!') + +@bp.get("/pos") +async def show_transactions(request, message=None): + tpl = request.app.ctx.tpl.get_template('pos.html') + items = request.app.ctx.money.execute('SELECT * FROM item') + + tx_info = {} + last_tx = request.app.ctx.money.execute('SELECT * FROM tx WHERE amount < 0 ORDER BY ts DESC LIMIT 3').fetchall() + for tx in last_tx: + tx_info[tx['id']] = {'items': request.app.ctx.money.execute('SELECT * FROM tx_items JOIN item ON item_id = item.id AND tx_id = ?', (tx['id'],)).fetchall(), 'order': await request.app.ctx.om.get_order(nfc_id=tx['tag_id']), 'time': datetime.fromtimestamp(tx['ts'] or 0).strftime('%H:%M')} + + return response.html(tpl.render(items=items, message=message, last_tx=last_tx, tx_info=tx_info)) + +@bp.get("/poll_barcode") +async def give_barcode(request): + + request.app.ctx.nfc_reads[request.ip] = Future() + + try: + bcd = await asyncio.wait_for(request.app.ctx.nfc_reads[request.ip], 20) + except asyncio.TimeoutError: + if not request.ip in request.app.ctx.nfc_reads: + del request.app.ctx.nfc_reads[request.ip] + return response.json({'error': 'no_barcode'}) + + info = request.app.ctx.money.execute("SELECT count(*), coalesce(sum(amount), 0) FROM tx WHERE coalesce(is_canceled, 0) != 1 AND tag_id = ?", (bcd['id'],)).fetchone() + + order = await request.app.ctx.om.get_order(nfc_id=bcd['id']) + + desc = ("โš ๏ธ" if not bcd['is_secure'] else '') + (f"๐Ÿ‘ค {order.code} {order.name}" if order else f"๐Ÿช™ {bcd['id']}") + f" ยท Transazioni: {info[0]}" + + return response.json({**bcd, 'txamt': info[0], 'balance': info[1], 'desc': desc}) diff --git a/nfc.py b/nfc.py new file mode 100644 index 0000000..4f98447 --- /dev/null +++ b/nfc.py @@ -0,0 +1,72 @@ +from sanic import Blueprint, exceptions, response +from random import choice +from ext import * +from config import headers, PROPIC_DEADLINE +from PIL import Image +from os.path import isfile +from os import unlink +from io import BytesIO +from hashlib import sha224 +from time import time +from urllib.parse import unquote +from base64 import b16decode +from asyncio import Future +import json +import re +import asyncio + +bp = Blueprint("nfc") + +@bp.post("/nfc/read") +async def handle_reading(request): + payload = request.json + if payload['count']: + request.app.ctx.nfc_counts.execute('INSERT INTO read_count(tag_id, ts, count) VALUES (?,?,?)', (payload['id'], int(time()), payload['count'])) + request.app.ctx.nfc_counts.commit() + + if payload['boopbox_id']: + await request.app.ctx.boopbox_queue.put(payload) + return response.text('ok') + + if not request.ip in request.app.ctx.nfc_reads: + return response.text('wasted read') + + try: + request.app.ctx.nfc_reads[request.ip].set_result(payload) + except asyncio.exceptions.InvalidStateError: + del request.app.ctx.nfc_reads[request.ip] + + return response.text('ok') + +@bp.get("/fz23/") +async def handle_nfc_tag(request, tag_id): + + tag = re.match('([bc])([0-9A-F]{14})x([0-9A-F]{6})', tag_id) + + # Store the read count + read_count = int.from_bytes(b16decode(tag.group(3))) + request.app.ctx.nfc_counts.execute('INSERT INTO read_count(tag_id, ts, count, is_web) VALUES (?,?,?,1)', (tag.group(2), int(time()), read_count)) + request.app.ctx.nfc_counts.commit() + + # If it's a coin, just show the coin template + if tag.group(1) == 'c': + return response.redirect(f"coin/{tag.group(2)}") + + # If it's a badge, look for the corresponding order + for o in request.app.ctx.om.cache.values(): + if o.nfc_id == tag.group(2): + return response.redirect(o.code) + + raise exceptions.NotFound("Unknown tag :(") + +@bp.get("/fz23/coin/") +async def show_coin(request, tag_id): + balance = request.app.ctx.money.execute('SELECT sum(amount) FROM tx WHERE tag_id = ?', (tag_id,)).fetchone()[0] + + tpl = request.app.ctx.tpl.get_template('coin.html') + return response.html(tpl.render(balance=balance)) + +@bp.get("/fz23/") +async def show_order(request, code): + return response.html(f"

Badge

{code}

") + diff --git a/tpl/badge.html b/tpl/badge.html new file mode 100644 index 0000000..6e820f7 --- /dev/null +++ b/tpl/badge.html @@ -0,0 +1,37 @@ + + + + + {{order.name}} + + + + +
+ +
+

{{order.name}}

+

Is an attendee of Furizon 2023 :D

+


+ +

Stay cool~
Stay Furizon

+
+ + diff --git a/tpl/coin.html b/tpl/coin.html new file mode 100644 index 0000000..20f752c --- /dev/null +++ b/tpl/coin.html @@ -0,0 +1,44 @@ + + + + + You found a coin! + + + + +
+ + {% if balance < 0 %} +

You found a
{{balance}}FZ
coin!

+ {% else %} +

You found a coin!
(but sadly, it's already spent)

+ {% endif %} +
+ + + + diff --git a/tpl/pos.html b/tpl/pos.html new file mode 100644 index 0000000..1df431b --- /dev/null +++ b/tpl/pos.html @@ -0,0 +1,294 @@ + + + + Fz vending + + + +
{{message or 'Scannerizza un NFC...'}}
+
+ + +
+ + {% for item in items %} + 0 %}class="secret"{% endif %}> + + + + + {% endfor %} +
{{item.name}}{{item.price}}FZ + + + +
+
+ +
+ + +
+
+ + +