import coding import scratchattach as scratch3 import threading import time import random import os global conn global projectid global endpoint global users projectid = os.getenv("ProjectID") users = {} session = scratch3.login(os.getenv("Username"), os.getenv("Password")) class User: def __init__(self, name, id): self.last_request = time.time() self.name = name self.id = id class UserStartup: def __init__(self): self.last_request = time.time() class Responder: def __init__(self, projectid, channel, can_respond, can_stream, value, user): user.last_request = time.time() self.user = user self.projectid = projectid self.can_respond = can_respond self.can_stream = can_stream self.channel = channel self.cooldown = time.time() self.wrote = False self.last_value = value def poll(self): value = scratch3.get_var(self.projectid, "channel "+str(self.channel)) if value != self.last_value and value != None: self.user.last_request = time.time() self.last_value = value binary = coding.decimal_to_binary(int(value)) if binary[1:3]=="01": value = str(coding.binary_to_decimal(binary[3:])) if binary[1:3]=="10": value = str("true" if binary[3:] == 1 else "false") if binary[1:3]=="11": value = coding.convert_to_text(binary[3:]) return value def close(self): if self.can_stream or not self.can_respond: conn.set_var("channel "+str(self.channel), "0") else: while str(scratch3.get_var(self.projectid, "channel "+str(self.channel))) !="0": pass def respond(self, response): global conn if self.wrote and not self.can_stream: raise Exception("Can't stream to this as a response") if not (self.can_respond or self.can_stream): raise Exception("Can't respond to this") while time.time() - self.cooldown < 0.5: time.sleep(0.5 - (time.time() - self.cooldown)) if self.can_respond or self.can_stream: payload = "1" if type(response) is int: payload+="01"+coding.decimal_to_binary(response) elif type(response) is bool: payload+="10"+"1" if response else "0" elif type(response) is str: payload+="11"+coding.convert_to_binary(response) self.last_value = str(coding.binary_to_decimal(payload)) conn.set_var("channel "+str(self.channel), str(coding.binary_to_decimal(payload))) t= time.time() times=0.2 while scratch3.get_var(self.projectid, "channel "+str(self.channel)) !=str(coding.binary_to_decimal(payload)): if time.time()-t>=times: print("Message not sent, retrying") times+=0.1 conn.set_var("channel "+str(self.channel), str(coding.binary_to_decimal(payload))) t=time.time() self.wrote = True class ConnectionEndpoint: def receivedMessage(self, message, user, responder): global users r=random.randrange(1, 2047) while r in users: r=random.randrange(1, 2047) users[r] = User(message, r) responder.respond(r) responder.close() class HeartbeatEndpoint: def receivedMessage(self, message, user, responder): responder.close() def thread(n): global users global conn global projectid global endpoint heartbeater = HeartbeatEndpoint() conn.set_var("channel "+str(n), "0") while True: value = scratch3.get_var(projectid, "channel "+str(n)) if str(value) != "0" and value != None: binary = coding.decimal_to_binary(int(value)) reqendpoint = coding.binary_to_decimal(binary[1:6]) header = coding.binary_to_decimal(binary[6:17]) staticpayload = binary[17] == '1' streamingpayload = binary[18] == '1' acceptstaticpayload = binary[19] == '1' acceptstreamingpayload = binary[20] == '1' payload = None if staticpayload and not streamingpayload: payloadformat = binary[21:23] if payloadformat == "01": payload = str(coding.binary_to_decimal(binary[23:])) if payloadformat == "10": payload = "true" if binary[23:]=='1' else "false" if payloadformat == "11": payload = coding.convert_to_text(binary[23:]) if header in users: user = users[header] else: user = UserStartup() respond = Responder(projectid, n, acceptstaticpayload, acceptstreamingpayload, value, user) if reqendpoint == 31: heartbeater.receivedMessage(payload, user, respond) else: endpoint[reqendpoint].receivedMessage(payload, user, respond) def monitor_users(): global users while True: time.sleep(1) for k, v in users.items(): if time.time() - v.last_request >= 300: del users[k] def start_server(endpoints): global projectid global conn global endpoint endpoints.insert(0, ConnectionEndpoint()) endpoint = endpoints conn = session.connect_cloud(projectid) threads = [threading.Thread(target=thread, args=(i+1,)) for i in range(10)] for t in threads: t.start() monitorusers = threading.Thread(target=monitor_users) monitorusers.start()