Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- import os
- import uuid
- import json
- import re
- import struct
- import socket
- import configparser
- import logging as log
- import MySQLdb
- import tornado.ioloop
- import tornado.web
- from tornado import websocket
- from tornado.util import bytes_type
- from tornado.iostream import StreamClosedError
- ##########################################################################
- # Generate Configure Log System
- ##########################################################################
- logger = log.getLogger("WHNSMARCTI")
- logger.setLevel(log.INFO)
- handler = log.FileHandler('whnlog.log')
- handler.setLevel(log.INFO)
- formatter = log.Formatter(
- '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
- handler.setFormatter(formatter)
- logger.addHandler(handler)
- ##########################################################################
- # Generate configuration system (read file config.conf)
- ##########################################################################
- config = configparser.ConfigParser()
- config.read('config.conf')
- localport = config.getint('Server', 'Port')
- debug = config.getboolean('Server', 'debug')
- atghost = config.get('Atg', 'host')
- atgport = config.getint('Atg', 'port')
- appInstance = config.get('Applications', 'name')
- mysqlhost = config.get('db', 'host')
- mysqluser = config.get('db', 'user')
- mysqlpass = config.get('db', 'pass')
- mysqldbnm = config.get('db', 'dbnm')
- conn = MySQLdb.connect(host=mysqlhost,
- unix_socket='/tmp/mysql.sock',
- user=mysqluser,
- passwd=mysqlpass,
- db=mysqldbnm
- )
- dbconn = conn.cursor()
- dbconn.execute("SELECT VERSION()")
- versiondb = dbconn.fetchone()
- ##########################################################################
- # print info start app
- ##########################################################################
- if debug:
- print ("[INFO] >> load library sys... ")
- print ("[INFO] >> load library socket... ")
- print ("[INFO] >> load library time... ")
- print ("[INFO] >> load library traceback... ")
- print ("[INFO] >> load library treading ... ")
- print ("[INFO] >> load library multiprocessing... ")
- print ("[INFO] >> load library MySQLdb")
- print ("[INFO] >> load config local port %s " % (localport))
- print ("[INFO] >> load config ATG host %s " % (atghost))
- print ("[INFO] >> load config ATG port %s " % (atgport))
- print ("[INFO] >> load config AppName %s " % (appInstance))
- print ("[INFO] >> connect to db %s" % (mysqldbnm))
- print ("[INFO] >> Database version %s" % (versiondb))
- # print
- # "-----------------------------------------------------------------------------"
- logger.info('Loaded Module python')
- logger.info('Loaded configuration info from config.conf')
- logger.info('Connecting to db')
- MAX_ROOMS = 100
- MAX_USERS_PER_ROOM = 100
- MAX_LEN_ROOMNAME = 20
- MAX_LEN_NICKNAME = 20
- class RoomHandler(object):
- """Store data about connections, rooms, which users are in which rooms, etc."""
- def __init__(self):
- self.client_info = {} # for each client id we'll store {'wsconn': wsconn, 'room':room, 'nick':nick}
- self.room_info = {} # dict to store a list of {'cid':cid, 'nick':nick , 'wsconn': wsconn} for each room
- self.pending_cwsconn = {} #pending client ws connection
- self.roomates = {} # store a set for each room, each contains the connections of the clients in the room.
- def add_roomnick(self, room, nick):
- """Add nick to room. Return generated clientID"""
- # meant to be called from the main handler (page where somebody indicates a nickname and a room to join)
- if len(self.room_info) >= MAX_ROOMS:
- cid = -1
- else:
- if room in self.room_info and len(self.room_info[room]) >= MAX_USERS_PER_ROOM:
- cid = -2
- else:
- roomvalid= re.match(r'[\w-]+$', room)
- nickvalid= re.match(r'[\w-]+$', nick)
- if roomvalid == None :
- cid = -3
- else:
- if nickvalid == None :
- cid = -4
- else:
- cid = uuid.uuid4().hex # generate a client id.
- if not room in self.room_info: # it's a new room
- self.room_info[room] = []
- c = 1
- nn = nick
- nir = self.nicks_in_room(room)
- while True:
- if nn in nir:
- nn = nick + str(c)
- else:
- break
- c += 1
- self.add_pending(cid,room,nn)
- return cid
- def add_pending(self,cid,room,nick):
- logger.info("| ADD_PENDING | %s" % cid)
- self.pending_cwsconn[cid] = {'room': room, 'nick': nick} # we still don't know the WS connection for this client
- def remove_pending(self,client_id):
- logger.info("| REMOVE_PENDING | %s" % client_id)
- if client_id in self.pending_cwsconn:
- del(self.pending_cwsconn[client_id]) #no longer pending
- def add_client_wsconn(self, client_id, conn):
- """Store the websocket connection corresponding to an existing client."""
- # add complete client info to the data structures, remove from the pending dict
- self.client_info[client_id] = self.pending_cwsconn[client_id]
- self.client_info[client_id]['wsconn'] = conn
- room = self.pending_cwsconn[client_id]['room']
- nick= self.pending_cwsconn[client_id]['nick']
- self.room_info[room].append({'cid': client_id, 'nick': nick, 'wsconn': conn})
- self.remove_pending(client_id)
- cid_room = self.client_info[client_id]['room']
- if cid_room in self.roomates:
- self.roomates[cid_room].add(conn)
- else:
- self.roomates[cid_room] = {conn}
- for user in self.room_info[cid_room]:
- if user['cid'] == client_id:
- user['wsconn'] = conn
- break
- # send "join" and and "nick_list" messages
- self.send_join_msg(client_id)
- nick_list = self.nicks_in_room(room)
- cwsconns = self.roomate_cwsconns(client_id)
- self.send_nicks_msg(cwsconns, nick_list)
- def remove_client(self, client_id):
- """Remove all client information from the room handler."""
- cid_room = self.client_info[client_id]['room']
- nick = self.client_info[client_id]['nick']
- # first, remove the client connection from the corresponding room in self.roomates
- client_conn = self.client_info[client_id]['wsconn']
- if client_conn in self.roomates[cid_room]:
- self.roomates[cid_room].remove(client_conn)
- if len(self.roomates[cid_room]) == 0:
- del(self.roomates[cid_room])
- r_cwsconns = self.roomate_cwsconns(client_id)
- # filter out the list of connections r_cwsconns to remove clientID
- r_cwsconns = [conn for conn in r_cwsconns if conn != self.client_info[client_id]['wsconn']]
- self.client_info[client_id] = None
- for user in self.room_info[cid_room]:
- if user['cid'] == client_id:
- self.room_info[cid_room].remove(user)
- break
- self.send_leave_msg(nick, r_cwsconns)
- nick_list = self.nicks_in_room(cid_room)
- self.send_nicks_msg(r_cwsconns, nick_list)
- if len(self.room_info[cid_room]) == 0: # if room is empty, remove.
- del(self.room_info[cid_room])
- logger.info("| ROOM_REMOVED | %s" % cid_room)
- def nicks_in_room(self, rn):
- """Return a list with the nicknames of the users currently connected to the specified room."""
- nir = [] # nicks in room
- for user in self.room_info[rn]:
- nir.append(user['nick'])
- return nir
- def roomate_cwsconns(self, cid):
- """Return a list with the connections of the users currently connected to the room where
- the specified client (cid) is connected."""
- cid_room = self.client_info[cid]['room']
- r = []
- if cid_room in self.roomates:
- r = self.roomates[cid_room]
- return r
- def send_join_msg(self, client_id):
- """Send a message of type 'join' to all users connected to the room where client_id is connected."""
- nick = self.client_info[client_id]['nick']
- r_cwsconns = self.roomate_cwsconns(client_id)
- msg = {"msgtype": "join", "username": nick, "payload": " joined the chat room."}
- pmessage = json.dumps(msg)
- for conn in r_cwsconns:
- conn.write_message(pmessage)
- @staticmethod
- def send_nicks_msg(conns, nick_list):
- """Send a message of type 'nick_list' (contains a list of nicknames) to all the specified connections."""
- msg = {"msgtype": "nick_list", "payload": nick_list}
- pmessage = json.dumps(msg)
- for c in conns:
- c.write_message(pmessage)
- @staticmethod
- def send_leave_msg(nick, rconns):
- """Send a message of type 'leave', specifying the nickname that is leaving, to all the specified connections."""
- msg = {"msgtype": "leave", "username": nick, "payload": " left the chat room."}
- pmessage = json.dumps(msg)
- for conn in rconns:
- conn.write_message(pmessage)
- class MainHandler(tornado.web.RequestHandler):
- def initialize(self, room_handler):
- """Store a reference to the "external" RoomHandler instance"""
- self.__rh = room_handler
- def get(self, action = None):
- """Render chat.html if required arguments are present, render main.html otherwise."""
- if not action : # init startup sequence, won't be completed until the websocket connection is established.
- try:
- room = self.get_argument("room")
- nick = self.get_argument("nick")
- cid = self.__rh.add_roomnick(room, nick) # this alreay calls add_pending
- self.set_cookie("ftc_cid", cid)
- emsgs = ["The nickname provided was invalid. It can only contain letters, numbers, - and _.\nPlease try again.",
- "The room name provided was invalid. It can only contain letters, numbers, - and _.\nPlease try again.",
- "The maximum number of users in this room (%d) has been reached.\n\nPlease try again later." % MAX_USERS_PER_ROOM,
- "The maximum number of rooms (%d) has been reached.\n\nPlease try again later." % MAX_ROOMS]
- if cid == -1 or cid == -2:
- self.render("templates/maxreached.html",emsg=emsgs[cid])
- else:
- if cid < -2:
- self.render("templates/main.html",emsg = emsgs[cid])
- else:
- self.render("templates/cticlient.php", room_name=room)
- # self.render("templates/chat.html", room_name=room)
- except tornado.web.MissingArgumentError:
- self.render("templates/main.html",emsg = "")
- else:
- if action == "drop": # drop client from "pending" list. Client cannot establish WS connection.
- client_id = self.get_cookie("ftc_cid")
- if client_id:
- self.__rh.remove_pending(client_id)
- self.render("templates/nows.html")
- websockets = []
- class ClientWSConnection(websocket.WebSocketHandler):
- def initialize(self, room_handler):
- """Store a reference to the "external" RoomHandler instance"""
- self.__rh = room_handler
- self.atg = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.atg.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.atg.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
- self.atg.connect((atghost, atgport))
- def open(self):
- self.__clientID = self.get_cookie("ftc_cid")
- self.__rh.add_client_wsconn(self.__clientID, self)
- logger.info("| WS_OPENED | %s" % self.__clientID)
- def on_message(self, message):
- msg = json.loads(message)
- mlen = len(msg['payload'])
- msg['username'] = self.__rh.client_info[self.__clientID]['nick']
- # query data profile agent
- userid = msg['userid']
- try:
- conn = MySQLdb.connect(host=mysqlhost,
- user=mysqluser,
- passwd=mysqlpass,
- db=mysqldbnm)
- dbconn = conn.cursor()
- dbconn.execute(
- "SELECT cti_agentpabx, cti_password, cti_extension, cti_afterstatus, cti_vdn FROM m_user WHERE muser_id = %s", ([userid]))
- for i in range(dbconn.rowcount):
- row = dbconn.fetchone()
- pabx_agent = row[0]
- pabx_pass = row[1]
- pabx_ext = row[2]
- pabx_afsta = row[3]
- pabx_vdn = row[4]
- except MySQLdb.Error:
- print ("Error %d: %s" % (e.args[0], e.args[1]))
- sys.exit(1)
- finally:
- dbconn.close()
- varcommand = msg['payload']
- if varcommand == 'login':
- msg_do_login = msg['userid'] + ';do_user_login;' + \
- pabx_agent + ';' + pabx_pass + ';' + pabx_ext + ';' + pabx_vdn
- self.atg.send(msg_do_login)
- call_do_login = self.atg.recv(1024)
- logger.info("[MSG-CTI] >> Login to device %s" % (msg_do_login))
- logger.info("[RES-CTI] >> %s" % (call_do_login))
- msg_do_run_device = pabx_ext + ';do_run_device'
- self.atg.send(msg_do_run_device)
- call_do_run_device = self.atg.recv(1024)
- logger.info("[MSG-CTI] >> Run to device %s" % (msg_do_run_device))
- logger.info("[RES-CTI] >> %s" % (call_do_run_device))
- # msg_acd_login = msg['userid'] + ';do_ag_login;' + pabx_ext + \
- # ';' + pabx_agent + ';' + pabx_pass + ';' + pabx_afsta
- # self.atg.send(msg_acd_login)
- # call_acd_login = self.atg.recv(1024)
- # logger.info("[MSG-CTI] >> ACD Login %s" % (msg_acd_login))
- # logger.info("[RES-CTI] >> %s" % (call_acd_login))
- msg = {
- "msgtype": "login", "sec": msg['userid'], "agent_id": pabx_agent, "agent_ext": pabx_ext}
- # query insert to agent activity
- #dbconn.execute("INSERT INTO Writers(Name) VALUES('Jack London')")
- elif varcommand == 'ready':
- msg_acd_ready = msg['userid'] + ';do_ag_ready;' + \
- pabx_ext + ';' + pabx_agent + ';' + pabx_pass + ';0'
- self.atg.send(msg_acd_ready)
- call_acd_ready = self.atg.recv(1024)
- logger.info("[MSG-CTI] >> ACD Ready %s" % (msg_acd_ready))
- logger.info("[RES-CTI] >> %s" % (call_acd_ready))
- msg = {
- "msgtype": "ready", "sec": msg['userid'], "agent_id": pabx_agent, "agent_ext": pabx_ext}
- elif varcommand == 'notready':
- msg_acd_not_ready = msg['userid'] + ';do_ag_aux;' + \
- pabx_ext + ';' + pabx_agent + ';' + pabx_pass + ';0'
- self.atg.send(msg_acd_not_ready)
- call_acd_not_ready = self.atg.recv(1024)
- logger.info("[MSG-CTI] >> ACD Not Ready %s" % (msg_acd_not_ready))
- logger.info("[RES-CTI] >> %s" % (call_acd_not_ready))
- msg = {"msgtype": "not ready",
- "sec": msg['userid'], "agent_id": pabx_agent, "agent_ext": pabx_ext}
- elif varcommand == 'logout':
- msg_acd_shutdown = msg['userid'] + ';do_ag_logout;' + \
- pabx_ext + ';' + pabx_agent + ';' + pabx_pass + ';0'
- self.atg.send(msg_acd_shutdown)
- call_acd_shutdown = self.atg.recv(1024)
- logger.info("[MSG-CTI] >> ACD Not Ready %s" % (msg_acd_shutdown))
- logger.info("[RES-CTI] >> %s" % (call_acd_shutdown))
- msg = {"msgtype": "shutdown",
- "sec": msg['userid'], "agent_id": pabx_agent, "agent_ext": pabx_ext}
- elif varcommand == 'makecall':
- msg_do_make_call = msg['userid'] + ';do_dev_make_call;' + '202'
- self.atg.send(msg_do_make_call)
- call_do_make_call = self.atg.recv(1024)
- logger.info("[MSG-CTI] >> Make Call %s" % (msg_do_make_call))
- logger.info("[RES-CTI] >> %s" % (call_do_make_call))
- msg = {"msgtype": "makecall",
- "sec": msg['userid'], "agent_id": pabx_agent, "agent_ext": pabx_ext}
- else:
- msg = {"msgtype": "else"}
- pmessage = json.dumps(msg)
- rconns = self.__rh.roomate_cwsconns(self.__clientID)
- logger.info("| MSG_RECEIVED | %s | %d" % (self.__clientID,mlen) )
- frame = self.make_frame(pmessage)
- for conn in rconns:
- #conn.write_message(pmessage)
- conn.write_frame(frame)
- def make_frame(self, message):
- opcode = 0x1 # we know that binary is false, so opcode is s1
- message = tornado.escape.utf8(message)
- assert isinstance(message, bytes_type)
- finbit = 0x80
- mask_bit = 0
- frame = struct.pack("B", finbit | opcode)
- l = len(message)
- if l < 126:
- frame += struct.pack("B", l | mask_bit)
- elif l <= 0xFFFF:
- frame += struct.pack("!BH", 126 | mask_bit, l)
- else:
- frame += struct.pack("!BQ", 127 | mask_bit, l)
- frame += message
- return frame
- def write_frame(self, frame):
- try:
- #self._write_frame(True, opcode, message)
- self.stream.write(frame)
- except StreamClosedError:
- pass
- #self._abort()
- def on_close(self):
- cid = self.__clientID
- self.__rh.remove_client(self.__clientID)
- logger.info("| WS_CLOSED | %s" % cid)
- def allow_draft76(self):
- return True
- def receive(fd, events):
- """Receive a notify message from channel I listen."""
- for ws in websockets:
- ws.write_message("my message")
- tornado.ioloop.add_handler(connection.fileno(), receive, tornado.ioloop.WRITE)
- if __name__ == "__main__":
- rh = RoomHandler()
- app = tornado.web.Application([
- (r"/(|drop)", MainHandler, {'room_handler': rh}),
- (r"/ws", ClientWSConnection, {'room_handler': rh})
- ],
- static_path=os.path.join(os.path.dirname(__file__), "static"),
- debug=debug
- )
- app.listen(localport)
- print ('[INFO] >> start %s.' % (appInstance))
- print ('[INFO] >> client listening on port %s ...' % (localport))
- tornado.ioloop.IOLoop.instance().start()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement