gnuradionetwork/twisted/plugins/frncrosslink.py

210 lines
7.7 KiB
Python
Executable File

# -*- coding: utf-8 -*-
#
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
# 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 = '<br>'.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("<u>%s@%s:%d</u>" % (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('<', '&lt;'),
))
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: