diff --git a/ethermess.py b/ethermess.py index b56e19f..e908f0f 100644 --- a/ethermess.py +++ b/ethermess.py @@ -1,15 +1,36 @@ #!/usr/bin/env python3 libexec_dir = __LIBEXECDIR__ +import enum import select import subprocess import sys +import time + +class statuses(enum.Enum): + available = 0 + unavailable = 1 + offline = 2 own_nick = None own_status = None default_target_mac = None +peers = {} +class Peer: + def __init__(self, status, nick, lastseen): + self.status = status + self.nick = nick + self.lastseen = lastseen + + def __repr__(self): + r = 'Peer(%s, %s, %s)' % (repr(self.status), repr(self.nick), repr(self.lastseen)) + if __name__ != '__main__': + return '%s.%s' % (__name__, r) + else: + return r + def writeall(f, b): written = 0 while written < len(b): @@ -62,7 +83,7 @@ def send_status_request(backend, mac): def set_status_nick(backend, status, nick): encoded = nick.encode('utf-8') - writeall(backend, b's' + bytes([status, len(encoded)]) + encoded) + writeall(backend, b's' + bytes([status.value, len(encoded)]) + encoded) def handle_user_command(backend, line): global own_nick, own_status @@ -89,11 +110,11 @@ def handle_user_command(backend, line): send_status_request(backend, mac) elif command == '/available' and rest == '': - own_status = 0 + own_status = statuses.available set_status_nick(backend, own_status, own_nick) elif command == '/unavailable' and rest == '': - own_status = 1 + own_status = statuses.unavailable set_status_nick(backend, own_status, own_nick) elif command == '/nick': @@ -114,6 +135,63 @@ def handle_user_command(backend, line): else: send_message(backend, default_target_mac, line) +def handle_status(mac, status, nick): + global peers + + if mac not in peers: + # Never seen before + peers[mac] = Peer(nick = None, status = None, lastseen = None) + + + peers[mac].lastseen = time.monotonic() + + if peers[mac].nick is not None and peers[mac].status != statuses.offline and nick != peers[mac].nick: + print('=== ~%s -> ~%s [%s]' % (peers[mac].nick, nick, format_mac(mac))) + + peers[mac].nick = nick + + if status != peers[mac].status: + if status == statuses.offline: + print('<<< ~%s [%s]' % (nick, format_mac(mac))) + elif peers[mac].status is None or peers[mac].status == statuses.offline: + if status == statuses.available: + print('>>> ~%s [%s]' % (nick, format_mac(mac))) + else: + print('>>> ~%s (%s) [%s]' % (nick, status.name, format_mac(mac))) + else: + print('=== ~%s (%s) [%s]' % (nick, status.name, format_mac(mac))) + + peers[mac].status = status + +def handle_message(mac, message): + global peers + + if mac not in peers: + nick = format_mac(mac) + + else: + nick = peers[mac].nick + + # Ensure nicks are unique + unique = True + for peer_mac, peer in peers.items(): + if peer_mac == mac: continue + + if peer.nick == nick: + # Nick not unique + unique = False + break + + if unique: + # Unique nicks: ~nick + nick = '~' + nick + else: + # Non-unique nicks: [MAC]~nick + nick = '[%s]~%s' % (format_mac(mac), nick) + + for line in message.split('\n'): + print('<%s> %s' % (nick, ascii(line))) + def eventloop(proc): # Create unbuffered version of stdin unbuf_stdin = open(sys.stdin.buffer.fileno(), 'rb', buffering = 0) @@ -137,7 +215,7 @@ def eventloop(proc): nick_length, = readall(proc.stdout, 1) nick = readall(proc.stdout, nick_length,) - print('%s (%s) ~%s' % (format_mac(source_mac), format_status(status), nick.decode('utf-8'))) + handle_status(source_mac, statuses(status), nick.decode('utf-8')) elif event_type == b'i': # Msgid for message @@ -164,17 +242,10 @@ def eventloop(proc): message_length = readall_u16(proc.stdout) message = readall(proc.stdout, message_length) - print('<%s> %s' % (format_mac(source_mac), message.decode('utf-8'))) #debg + handle_message(source_mac, message.decode('utf-8')) else: - # Not sth we handle yet - data = proc.stdout.read(1023) - if data == b'': - data = b'[!] ' + event_type - else: - data = b'[!] ' + event_type + data - sys.stdout.buffer.write(data) - sys.stdout.buffer.flush() + raise ValueError('Unknown event type from backend: %s' % repr(event_type)) elif fd == proc.stdout.fileno() and event & select.POLLHUP: print('Backend exited') @@ -207,14 +278,14 @@ def main(): proc = subprocess.Popen(['sudo', libexec_dir + '/ethermess-backend', interface], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = sys.stderr, bufsize = 0) # Tell the backend the status and nick - own_status = 0 + own_status = statuses.available encoded = own_nick.encode('utf-8') - writeall(proc.stdin, bytes([own_status, len(encoded)]) + encoded) + writeall(proc.stdin, bytes([own_status.value, len(encoded)]) + encoded) # Read our MAC mac = readall(proc.stdout, 6) - print('Own mac: %s' % format_mac(mac)) + print('--- MAC: %s' % format_mac(mac)) eventloop(proc)