def seat(message: dict): if authenticated: name = names[int(message['nameIndex'])] si = message['seatIndex'] ip = request.remote_addr persister.seat_indexes_by_ip[ip] = si persister.save() logger.info(f'{ip} seat {name} to {si}') existing_different_index = [i for i, station in enumerate(stations) if station.get('name') == name and i != si] if existing_different_index: stations[existing_different_index[0]] = {} if len(stations[si]): name_at_new_station = stations[si].get('name') if name_at_new_station and name != name_at_new_station: emit('disconnect_station', si, broadcast=True, namespace=TEACHER_NS) msg = f'Someone at {ip} claiming to be {name} has moved to {station_name(si)}, ' \ f'displacing {name_at_new_station}' logger.warning(msg) relay_chat(RH3K_ID, msg) station = {'ip': ip, 'sid': request.sid, 'name': name, 'connected': True} stations[si] = station broadcast_seated(station, si) return OK
def relay_chat(sender: str, msg: str) -> None: r = request logger.info('Chat message from %s at %s: %s', sender, r.remote_addr, msg) html = markdown(strftime('%H:%M:%S') + ' ' + sender + ': ' + msg) for ns in ALL_NS: if settings['chatEnabled'] or ns == TEACHER_NS: emit('chat_msg', html, namespace=ns, broadcast=True)
def teacher(): r = request logger.info('Teacher page requested from %s, %s', r.remote_addr, r.user_agent) return render_template('teacher.html', settings=json.dumps(settings), stationJson=json.dumps(stations))
def seat(message: dict): if authenticated: name = message['name'] si = message['seatIndex'] ip = request.remote_addr persister.seat_indexes_by_ip[ip] = si persister.save() logger.info('%s seat %s to %d', ip, name, si) existing_different_index = [ i for i, station in enumerate(stations) if station.get('name') == name and i != si ] if existing_different_index: stations[existing_different_index[0]] = {} if len(stations[si]): name_at_new_station = stations[si].get('name') if name_at_new_station and name != name_at_new_station: msg = 'Someone at %s claiming to be %s has moved to %s, displacing %s' % ( ip, name, station_name(si), name_at_new_station) logger.warn(msg) relay_chat('RH3K', msg) station = {'ip': ip, 'sid': request.sid, 'name': name} stations[si] = station broadcast_seated(station, si)
def set_names(message: dict) -> None: if authenticated: r = request ip = r.remote_addr logger.info('set_names from %s, %s', ip, r.sid) emit('set_names', message, broadcast=True, namespace=STUDENT_NS) global names names = [] assign_seats: bool = message['assignSeats'] def skip_missing(start: int) -> int: while start in settings['missingSeatIndexes']: start += 1 return start si = skip_missing(0) for line in message['names'].split('\n'): name = line.strip() if name: names.append(name) if assign_seats: station = {'ip': ip, 'name': name} stations[si] = station broadcast_seated(station, si) si = skip_missing(si + 1)
def index(): r = request seat_index = persister.seat_indexes_by_ip.get(r.remote_addr, -1) logger.info('Student page requested from %s (last seat index: %d), %s', r.remote_addr, seat_index, r.user_agent) return render_template('student.html', settings=json.dumps(settings), names=names, lastSeatIndex=seat_index)
def random_call(anyone: bool) -> int: if authenticated: eligible = [(k, v) for k, v in enumerate(stations) if v.get('callsLeft', 0) > 0 and (anyone or v.get('haveAnswer', False))] if eligible: chosen = choice(eligible) chosen[1]['callsLeft'] -= 1 logger.info(f'{chosen[1]["name"]} called randomly') return chosen[0] return -1
def relay_shares(sender: str, possible_url: str, allow_any=False) -> None: r = request logger.info('Shares message from %s at %s: %s', sender, r.remote_addr, possible_url) parts = urlparse(possible_url) if allow_any or parts.hostname in settings['allowedSharesDomains']: html = '<p>%s %s: <a href="%s" target="_blank">%s</a></p>' % ( strftime('%H:%M:%S'), sender, possible_url, possible_url) for ns in ALL_NS: if settings['sharesEnabled'] or ns == TEACHER_NS: emit('shares_msg', html, namespace=ns, broadcast=True)
def relay_chat(sender_id: int, raw_msg: str) -> None: 'Relay chat message, escaping student messages and processing teacher messages with Markdown' r = request sender: str = sender_from_id(sender_id) logger.info(f'Chat message from {sender} at {r.remote_addr}: {raw_msg}') msg = raw_msg if sender_id == TEACHER_ID else escape(raw_msg) + '<br/>' prefixed_msg = strftime('%H:%M:%S') + f' {sender} : {msg}' html = markdown(prefixed_msg) if sender_id == TEACHER_ID else prefixed_msg for ns in ALL_NS: if settings['chatEnabled'] or ns == TEACHER_NS: emit('chat_msg', html, namespace=ns, broadcast=True)
def set_status(message: dict) -> None: if authenticated: si = message['seatIndex'] station = stations[si] if station: logger.info('set_status: %s', message) now = time() for st in settings['statuses']: id = st[0] station[id] = now if message.get(id, False) else None ss_msg = {'seatIndex': si, 'station': station} emit('status_set', ss_msg, broadcast=True, namespace=TEACHER_NS)
def relay_shares(sender_id: str, possible_url: str, allow_any=False) -> None: r = request sender: str = sender_from_id(sender_id) logger.info(f'Shares message from {sender} at {r.remote_addr}: {possible_url}') parts = urlparse(possible_url) if allow_any or parts.hostname in settings['allowedSharesDomains']: escaped_url = escape(possible_url) html = f'<p>{strftime("%H:%M:%S")} {sender}: <a href="{escaped_url}" target="_blank">{escaped_url}</a></p>' settings['shares'].append(html) for ns in ALL_NS: if settings['sharesEnabled'] or ns == TEACHER_NS: emit('shares_msg', html, namespace=ns, broadcast=True)
def disconnect_request() -> None: r = request logger.info('Disconnected: %s, %s', r.remote_addr, r.sid) matches = [ item for item in enumerate(stations) if r.sid == item[1].get('sid') ] if matches: station_index, station = matches[0] clear_station(station) emit('clear_station', station_index, broadcast=True, namespace=TEACHER_NS)
def set_status(message: dict) -> any: if authenticated: seat_index = message['seatIndex'] station: Dict[str, Any] = stations[seat_index] student_name = station.get('name') if student_name: key, value = message['status'] logger.info(f'{student_name} status: {key}: {value}') # Temporarily log haveAnswer toggles until reliability problem is solved if key == 'haveAnswer': chat_log_msg = student_name + ' ' + ('is' if value else 'is not') + ' ready' relay_chat(RH3K_ID, chat_log_msg) station[key] = time() if value else None emit('status_set', {'seatIndex': seat_index, 'key': key, 'value': station[key]}, broadcast=True, namespace=TEACHER_NS) return OK r = request logger.warning(f'set_status from disconnected user {r.remote_addr}, {seat_index}, {r.sid}') return DISCONNECTED
def set_names(message: dict) -> None: if authenticated: r = request ip = r.remote_addr logger.info(f'set_names from {ip}, {r.sid}') emit('set_names', message, broadcast=True, namespace=STUDENT_NS) global names names = [] assign_seats: bool = message['assignSeats'] def skip_missing(start: int) -> int: new_si = start while new_si in settings['missingSeatIndexes']: new_si += 1 return new_si si = skip_missing(0) for name in message['names']: names.append(name) if assign_seats: station = {'ip': ip, 'name': name, 'connected': True} stations[si] = station broadcast_seated(station, si) si = skip_missing(si + 1)
def log_connection(r): logger.info(f'Connection from {r.remote_addr}, {r.sid}, {r.user_agent}')
def disconnect_request() -> None: r = request logger.info(f'Disconnected: {r.remote_addr}, {r.sid}') connect_or_disconnect(False, r)
def index(): r = request seat_index = persister.seat_indexes_by_ip.get(r.remote_addr, -1) logger.info(f'Student page requested from {r.remote_addr} (last seat index: {seat_index})') return render_template('student.html', settings=json.dumps(settings), names=names, lastSeatIndex=seat_index)
def connect(): r = request logger.info('Connection from %s, %s, %s', r.remote_addr, r.sid, r.user_agent)
def random_set(random_calls_limit: int) -> None: if authenticated: logger.info('Random calls limit set to %d', random_calls_limit) for station in stations: if station.get('name'): station['callsLeft'] = random_calls_limit
def relay_teacher_msg(msg: str) -> None: r = request logger.info(f'Teacher message from {r.remote_addr}: {msg}') html = markdown(msg) for ns in ALL_NS: emit('teacher_msg', html, namespace=ns, broadcast=True)