# -*- coding: utf-8 -*- # # Copyright 2010 Maurizio Porrato # See LICENSE.txt for copyright info from twisted.application.service import MultiService from zope.interface import implements from twisted.plugin import IPlugin from twisted.application.service import IServiceMaker from twisted.application import internet from twisted.python import usage from ConfigParser import ConfigParser from frn.protocol.client import FRNClient, FRNClientFactory from frn.user import FRNUser from twisted.python import log from os.path import curdir, join as pjoin cType = {'PC Only': 'O', 'Crosslink': 'C', 'Parrot': 'P'} cStatus = {'0': 'T', '1': 'R', '2': 'N'} clients = [] talking = None lastMessages = [] class FRNCrosslinkClient(FRNClient): def connectionMade(self): self.txOk = False self.txReq = False self.clientId = None from twisted.internet import reactor self.reactor = reactor FRNClient.connectionMade(self) def getClientName(self, client_id): if self.clientsById.has_key(client_id): return self.clientsById[client_id]['ON'] else: return client_id def sendMultiLineTextMessage(self, client, lines, maxLen=15): chunks = [lines[i*maxLen:(i+1)*maxLen] for i in range((len(lines)+maxLen-1)/maxLen)] for m in chunks: reply = '
'.join(m) self.sendTextMessage(client, reply) def textMessageReceived(self, client, message, target): global clients, talking, lastMessages if target == 'A': msg = "[%s] %s" % (self.getClientName(client), message) if client != self.clientId: for _, _, _, factory in clients: if factory != self.factory: factory.connection.sendTextMessage('', msg) else: if message.startswith('!'): cmd = message[1:] if cmd == "who": for server, port, u, factory in clients: cl = [] cl.append("%s@%s:%d" % (u.NT,server,port)) role = factory.connection.serverdata.get('AL', None) if role in ['OK', 'ADMIN', 'OWNER']: for c in factory.connection.clients: ss = cStatus.get(c['s'], '?') if c['M'] != '0': ss = ss.lower() cl.append(" %s%s %s " % ( cType.get(c['BC'], 'G'), ss, c['ON'].replace('<', '<'), )) else: cl.append(" :!: DISCONNECTED :!: ") self.sendMultiLineTextMessage(client, cl) elif cmd == "last": ml = [] ml.append("Last active talkers (most recent first):") for n in lastMessages: ml.append(" - "+n) self.sendMultiLineTextMessage(client, ml) def decodeTX(self, my_id): log.msg("Got TX ack for %s" % self.user.ON) self.txOk = True def stopTransmission(self): FRNClient.stopTransmission(self) log.msg("Stopped TX on %s" % self.user.ON) def goIdle(self): global talking, lastMessages self.txReq = False self.txOk = False talking = None self.stopTransmission() def audioFrameReceived(self, from_id, frames): global clients, talking, lastMessages if talking is None or talking == self: talking = self talkingUser = self.clients[from_id-1]['ON'] if len(lastMessages) > 0: if lastMessages[0] == talkingUser: talkingUser = None if talkingUser is not None: lastMessages.insert(0, talkingUser) lastMessages = lastMessages[:5] for _, _, _, factory in clients: conn = factory.connection if conn != self: role = conn.serverdata.get('al', None) if role in ['OK', 'ADMIN', 'OWNER']: if conn.txOk: conn.txReq = False conn.sendAudioFrame(frames) conn.busyTimer.reset(2.0) else: if not conn.txReq: log.msg("Requesting TX for %s" % conn.user.ON) conn.startTransmission() conn.txReq = True conn.busyTimer = self.reactor.callLater( 5.0, conn.goIdle) self.pong() def loginResponse(self, info): log.msg("%s login: %s" % (self.user.ON, info['AL'])) def clientsListUpdated(self, clients): self.clients = clients self.clientsById = dict([(i['ID'], i) for i in clients]) if self.clientId is None: for c in clients: if c['ON'] == self.user.ON and c['BC'] == self.user.BC: self.clientId = c['ID'] class FRNCrosslinkClientFactory(FRNClientFactory): protocol = FRNCrosslinkClient def buildProtocol(self, addr): p = FRNClientFactory.buildProtocol(self, addr) self.connection = p return p class Options(usage.Options): optParameters = [ ["confdir", "c", curdir, "Directory containing config files"] ] def parseArgs(self, *serverdefs): basedir = self['confdir'] acfg = ConfigParser() acfg.read(['/etc/grn/accounts.conf', pjoin(basedir,'accounts.conf'), 'accounts.conf']) scfg = ConfigParser() scfg.read(['/etc/grn/servers.conf', pjoin(basedir,'servers.conf'), 'servers.conf']) clients = [] for serverdef in serverdefs: account_name, server_name, network_name = serverdef.split(':', 2) sd = {} sd['server'] = scfg.get(server_name, 'server') sd['port'] = scfg.getint(server_name, 'port') sd['backup_server'] = scfg.get(server_name, 'backup_server') sd['backup_port'] = scfg.getint(server_name, 'backup_port') sd['operator'] = acfg.get(account_name, 'operator') sd['email'] = acfg.get(account_name, 'email') sd['password'] = acfg.get(account_name, 'password') sd['description'] = acfg.get(account_name, 'description') sd['country'] = acfg.get(account_name, 'country') sd['city'] = acfg.get(account_name, 'city') sd['network'] = network_name clients.append(sd) self['clients'] = clients class FRNCrosslinkServiceMaker(object): implements(IServiceMaker, IPlugin) tapname = "frncrosslink" description = "Freeradionetwork crosslink" options = Options def makeService(self, options): s = MultiService() global clients # FIXME: Get rid of this hack for c in options['clients']: user = FRNUser( EA=c['email'], PW=c['password'], ON=c['operator'], # BC=c['transmission'], DS=c['description'], BC="Crosslink", DS=c['description'], NN=c['country'], CT=c['city'], NT=c['network']) factory = FRNCrosslinkClientFactory(user) clients.append((c['server'], c['port'], user, factory)) s.addService( internet.TCPClient(c['server'], c['port'], factory)) return s serviceMaker = FRNCrosslinkServiceMaker() # vim: set et ai sw=4 ts=4 sts=4: