From 6a5caad4d64cf37ad63ffda21c0bce39c4c864d1 Mon Sep 17 00:00:00 2001 From: josephernest Date: Wed, 9 Nov 2016 00:09:11 +0100 Subject: [PATCH] Many little improvements: admin user, no malicious unicode in username, etc. --- README.md | 3 +++ talktalktalk.html | 43 ++++++++++++++++++++++++++---------- talktalktalk.py | 55 ++++++++++++++++++++++++++++++++--------------- 3 files changed, 73 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index a664b58..8202daf 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,9 @@ FAQ Q: How to send messages? Is there a way to get username autocompletion like in IRC? A: Use `` to send messages. Use `` to autocomplete usernames, example: `us` + `` will give `user1`, then `user2`, etc. It also works with `@`. +Q: Is there a way to prevent a particular username from being used by anyone except me? +A: The username `admin` is available *if and only if* the username `adminxyz` is entered in the input box. Change `adminxyz` to a private password in the beginning of `talktalktalk.py`, and as a result **noone else than you will be able to use the username `admin`.** + Q: How to find the date and time of the messages? A: Hover over the messages, and a tooltip will show the date and time. diff --git a/talktalktalk.html b/talktalktalk.html index dfa6052..6802caa 100644 --- a/talktalktalk.html +++ b/talktalktalk.html @@ -27,16 +27,16 @@ #popup { z-index: 10; position: fixed; left: 20%; width:60%; top:40%; border: solid black 1px; background-color: white; text-align: center; padding: 30px 10px 30px 10px; color: black !important; } .hidden { display: none !important; } .opaque { color: #AAA; } -@media (max-width: 500px) { #right { width: 120px; left: calc(100% - 120px); } #wrapper { width: calc(100% - 130px); } -} +@media (max-width: 500px) { #right { width: 120px; left: calc(100% - 120px); } #wrapper { width: calc(100% - 130px); } #banner { display: none; } } +
@@ -49,6 +49,7 @@ + - \ No newline at end of file + diff --git a/talktalktalk.py b/talktalktalk.py index f259fdf..eeeeb10 100644 --- a/talktalktalk.py +++ b/talktalktalk.py @@ -10,7 +10,12 @@ # license: MIT license -import sys, time, json, bleach, time, threading, dumbdbm +PORT = 9000 +HOST = "127.0.0.1" +ADMINNAME = 'admin' # this username will be available if *and only if* the following username is entered in the input field: +ADMINHIDDENNAME = 'adminxyz' + +import sys, time, json, bleach, time, threading, dumbdbm, random, re import daemon from bottle import route, run, view, request, post, ServerAdapter, get, static_file from gevent import pywsgi @@ -40,6 +45,17 @@ def send_userlist(): for u in users.keys(): u.send(json.dumps({'type' : 'userlist', 'connected': users.values()})) + def clean_username(usr, ws): + username = bleach.clean(usr).encode('utf8') + username = re.sub('[‍ :]', '', username) # removes " ", ":", and this evil char http://unicode-table.com/fr/200D/ + if username.lower() == ADMINNAME or username == '': + username = 'user' + str(random.randint(0,1000)) + ws.send(json.dumps({'type' : 'usernameunavailable', 'username' : username})) + elif username.lower() == ADMINHIDDENNAME: + username = ADMINNAME + ws.send(json.dumps({'type' : 'displayeduser', 'username' : username})) + return username + def dbworker(): # when a user disappears during more than 30 seconds (+/- 10), remove him/her from the userlist while True: userlistchanged = False @@ -51,7 +67,6 @@ def dbworker(): # when a user disappears during more than 30 seconds (+/- userlistchanged = True if userlistchanged: send_userlist() - time.sleep(10) dbworkerThread = threading.Thread(target=dbworker) @@ -69,33 +84,39 @@ def chat(ws): pings[ws] = time.time() if receivedmsg == 'ping': # ping/pong packet to make sure connection is still alive ws.send('id' + str(idx-1)) # send the latest message id in return - if ws not in users: - users[ws] = username - send_userlist() + if ws not in users: # was deleted by dbworker + ws.send(json.dumps({'type' : 'username'})) else: msg = json.loads(receivedmsg) if msg['type'] == 'message': message = (bleach.clean(msg['message'])).strip().encode('utf8') - username = (bleach.clean(msg['username'])).strip().encode('utf8') - if message and username: - s = json.dumps({'type' : 'message', 'message': message, 'username': username, 'id': idx, 'datetime': int(time.time())}) + + if ws not in users: # is this really mandatory ? + username = clean_username(msg['username'], ws) + users[ws] = username + send_userlist() + + if message: + s = json.dumps({'type' : 'message', 'message': message, 'username': users[ws], 'id': idx, 'datetime': int(time.time())}) db[str(idx)] = s # Neither dumbdbm nor shelve module allow integer as key... I'm still looking for a better solution! idx += 1 for u in users.keys(): u.send(s) + elif msg['type'] == 'messagesbefore': idbefore = msg['id'] ws.send(json.dumps({'type' : 'messages', 'before': 1, 'messages': [db[str(i)] for i in range(max(0,idbefore - 100),idbefore)]})) + elif msg['type'] == 'messagesafter': idafter = msg['id'] ws.send(json.dumps({'type' : 'messages', 'before': 0, 'messages': [db[str(i)] for i in range(idafter,idx)]})) + elif msg['type'] == 'username': - username = (bleach.clean(msg['username'])).strip().encode('utf8') - if username: - if ws not in users: # welcome new user - ws.send(json.dumps({'type' : 'messages', 'before': 0, 'messages': [db[str(i)] for i in range(max(0,idx - 100),idx)]})) - users[ws] = username - send_userlist() + username = clean_username(msg['username'], ws) + if ws not in users: # welcome new user + ws.send(json.dumps({'type' : 'messages', 'before': 0, 'messages': [db[str(i)] for i in range(max(0,idx - 100),idx)]})) + users[ws] = username + send_userlist() else: break if ws in users: @@ -114,7 +135,7 @@ def index(): def popsound(): return static_file('popsound.mp3', root='.') - run(host="127.0.0.1", port=9000, debug=True, server=GeventWebSocketServer) + run(host=HOST, port=PORT, debug=True, server=GeventWebSocketServer) class talktalktalk(daemon.Daemon): def run(self): @@ -124,11 +145,11 @@ def run(self): main() elif len(sys.argv) == 2: # daemon mode - daemon = talktalktalk(pidfile='_.pid') + daemon = talktalktalk(pidfile='_.pid', stdout='log.txt', stderr='log.txt') if 'start' == sys.argv[1]: daemon.start() elif 'stop' == sys.argv[1]: daemon.stop() elif 'restart' == sys.argv[1]: - daemon.restart() \ No newline at end of file + daemon.restart()