156 lines
5.7 KiB
Python

from django.shortcuts import render
from .models import Board, RestrictedIP
from datetime import datetime
from django.http import HttpResponse
from django.core.exceptions import ObjectDoesNotExist
from colour import Color
from pytz import UTC
# Change these for your board
BOARD_X = 64
BOARD_Y = 32
# Options for implementation of board update:
# 1) Serve at a special url which is a OTC generated by the server and the client (Hard-ish)
# 2) Serve at all times and have the client decide when it has changed(Easy)
# Internal validation
def validateBoard(x:int, y:int, board:dict, ip) -> bool:
try:
ip = RestrictedIP.objects.get(ip=ip)
return "RESTRICTED"
except ObjectDoesNotExist:
pass
key_counter = 0 # Not using len for optimization purposes
for row in board:
# Check if datatype is list
if not isinstance(board[row], list):
return "WRONG Y"
# If length of list is not equal to given width of matrix, it is incorrect
length = len(board[row])
if length != x:
return "WRONG LENGTH"
key_counter += 1
# Check if the keys are the same number as y
if key_counter != y:
return "WRONG X"
return "SUCCESS"
def restrict_or_unrestrict(ip):
try:
ip = RestrictedIP.objects.get(ip=ip)
timedelta = datetime.utcnow().replace(tzinfo=UTC) - ip.time_added
# if banned for more than 1 minute, unban
if timedelta.seconds > 60:
ip.delete()
return "ALLOWED"
return "STILL RESTRICTED"
except ObjectDoesNotExist:
instance = RestrictedIP(ip=ip, time_added=datetime.now())
instance.save()
return "RESTRICTION"
# Internal board class
class localBoard:
def __init__(self) -> None:
try:
self.board = Board.objects.latest("update_time")
except ObjectDoesNotExist:
# Create a blank board
blank_config = {int(i): [-1 for _ in range(64)] for i in range(32)}
# Time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format
time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
instance = Board(update_time=time, configuration=blank_config)
instance.save()
self.board = Board.objects.latest("update_time")
def getBoard(self) -> dict:
return dict(self.board.configuration)
def setBoard(self, new_configuration:dict, ip) -> bool:
# Validate the board
if err := validateBoard(x=BOARD_X, y=BOARD_Y, board=new_configuration, ip=ip) != "SUCCESS":
return err
# Save the board
instance = Board(update_time=datetime.now(), configuration=new_configuration)
instance.save()
self.board = Board.objects.latest("update_time")
return "SUCCESS"
def HTML(self) -> str:
board = self.getBoard()
style = """
<style>
table { border-collapse: collapse; width: 100%; height: 90vh; border: 1px solid white;}
td { width: 10px; height: 10px; border: 1px solid gray; }
td:onclick { border: 2px solid white; }
td:hover { border: 2px solid white; }
.color {
display: flex;
width: 100%;
justify-content: space-between;
}
</style>
"""
selected_pixel = """
<script>
async function selected_pixel(element) {
var color = document.getElementById("color_picker").value;
var arr = element.id.split("x")[1].split("y");
var x = arr[0];
var y = arr[1];
var url = "http://127.0.0.1:8000/board/" + x + "/" + y + "/" + color.replace("#", "%23");
let response = await fetch(url);
let result = await response.text();
console.log(result);
if (result != "RESTRICTED") {
element.style.backgroundColor = color;
}
else {
alert("You have placed your pixel. Please wait.");
}
}
</script>
"""
html = f"<html><head><title>Ballin</title></head>{style}<body>{selected_pixel}<table><form>"
for row in board:
html += "<tr>"
x = 0
for col in board[row]:
html += f"<td style='background-color:{col};' id={'x'+ str(x) + 'y'+ str(row)} onclick=selected_pixel(this)></td>"
x += 1
html += "</tr>"
html += "</table><div class='color'><input type='color' id='color_picker'><p color='white' ><------ pick ur color</p></div></body></html>"
return html
# Publicly visible functions
def update_board(request, x, y, color):
board = localBoard().getBoard()
try:
color = Color(color).hex
except Exception as e:
return HttpResponse(e)
board[str(y)][int(x)] = color
ip = restrict_or_unrestrict(request.META["REMOTE_ADDR"])
if ip == "STILL RESTRICTED":
return HttpResponse("RESTRICTED")
res = localBoard().setBoard(board, request.META["REMOTE_ADDR"])
return HttpResponse(f"board: {str(res)}, ip: {ip}")
def current_config(request):
html = localBoard().HTML()
return HttpResponse(html)