1 # -*- coding: utf-8 -*-
3 # Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
4 # See LICENSE.txt for copyright info
6 from twisted
.application
.service
import MultiService
7 from zope
.interface
import implements
8 from twisted
.plugin
import IPlugin
9 from twisted
.application
.service
import IServiceMaker
10 from twisted
.application
import internet
11 from twisted
.python
import usage
12 from ConfigParser
import ConfigParser
13 from frn
.protocol
.client
import FRNClient
, FRNClientFactory
14 from frn
.user
import FRNUser
15 from twisted
.python
import log
16 from os
.path
import curdir
, join
as pjoin
18 cType
= {'PC Only': 'O', 'Crosslink': 'C', 'Parrot': 'P'}
19 cStatus
= {'0': 'T', '1': 'R', '2': 'N'}
25 class FRNCrosslinkClient(FRNClient
):
27 def connectionMade(self
):
31 from twisted
.internet
import reactor
32 self
.reactor
= reactor
33 FRNClient
.connectionMade(self
)
35 def getClientName(self
, client_id
):
36 if self
.clientsById
.has_key(client_id
):
37 return self
.clientsById
[client_id
]['ON']
41 def sendMultiLineTextMessage(self
, client
, lines
, maxLen
=15):
42 chunks
= [lines
[i
*maxLen
:(i
+1)*maxLen
]
43 for i
in range((len(lines
)+maxLen
-1)/maxLen
)]
45 reply
= '<br>'.join(m
)
46 self
.sendTextMessage(client
, reply
)
48 def textMessageReceived(self
, client
, message
, target
):
49 global clients
, talking
, lastMessages
51 msg
= "[%s] %s" % (self
.getClientName(client
), message
)
52 if client
!= self
.clientId
:
53 for _
, _
, _
, factory
in clients
:
54 if factory
!= self
.factory
:
55 factory
.connection
.sendTextMessage('', msg
)
57 if message
.startswith('!'):
60 for server
, port
, u
, factory
in clients
:
62 cl
.append("<u>%s@%s:%d</u>" % (u
.NT
,server
,port
))
63 role
= factory
.connection
.serverdata
.get('AL', None)
64 if role
in ['OK', 'ADMIN', 'OWNER']:
65 for c
in factory
.connection
.clients
:
66 ss
= cStatus
.get(c
['S'], '?')
69 cl
.append(" %s%s %s " % (
70 cType
.get(c
['BC'], 'G'),
72 c
['ON'].replace('<', '<'),
75 cl
.append(" :!: DISCONNECTED :!: ")
76 self
.sendMultiLineTextMessage(client
, cl
)
79 ml
.append("Last active talkers (most recent first):")
80 for n
in lastMessages
:
82 self
.sendMultiLineTextMessage(client
, ml
)
84 def decodeTX(self
, my_id
):
85 log
.msg("Got TX ack for %s" % self
.user
.ON
)
88 def stopTransmission(self
):
89 FRNClient
.stopTransmission(self
)
90 log
.msg("Stopped TX on %s" % self
.user
.ON
)
93 global talking
, lastMessages
97 self
.stopTransmission()
99 def audioFrameReceived(self
, from_id
, frames
):
100 global clients
, talking
, lastMessages
101 if talking
is None or talking
== self
:
103 talkingUser
= self
.clients
[from_id
-1]['ON']
104 if len(lastMessages
) > 0:
105 if lastMessages
[0] == talkingUser
:
107 if talkingUser
is not None:
108 lastMessages
.insert(0, talkingUser
)
109 lastMessages
= lastMessages
[:5]
110 for _
, _
, _
, factory
in clients
:
111 conn
= factory
.connection
113 role
= conn
.serverdata
.get('AL', None)
114 if role
in ['OK', 'ADMIN', 'OWNER']:
117 conn
.sendAudioFrame(frames
)
118 conn
.busyTimer
.reset(2.0)
121 log
.msg("Requesting TX for %s" % conn
.user
.ON
)
122 conn
.startTransmission()
124 conn
.busyTimer
= self
.reactor
.callLater(
128 def loginResponse(self
, info
):
129 log
.msg("%s login: %s" % (self
.user
.ON
, info
['AL']))
131 def clientsListUpdated(self
, clients
):
132 self
.clients
= clients
133 self
.clientsById
= dict([(i
['ID'], i
) for i
in clients
])
134 if self
.clientId
is None:
136 if c
['ON'] == self
.user
.ON
and c
['BC'] == self
.user
.BC
:
137 self
.clientId
= c
['ID']
139 class FRNCrosslinkClientFactory(FRNClientFactory
):
140 protocol
= FRNCrosslinkClient
142 def buildProtocol(self
, addr
):
143 p
= FRNClientFactory
.buildProtocol(self
, addr
)
148 class Options(usage
.Options
):
151 ["confdir", "c", curdir
, "Directory containing config files"]
154 def parseArgs(self
, *serverdefs
):
155 basedir
= self
['confdir']
156 acfg
= ConfigParser()
157 acfg
.read(['/etc/grn/accounts.conf',
158 pjoin(basedir
,'accounts.conf'), 'accounts.conf'])
160 scfg
= ConfigParser()
161 scfg
.read(['/etc/grn/servers.conf',
162 pjoin(basedir
,'servers.conf'), 'servers.conf'])
165 for serverdef
in serverdefs
:
166 account_name
, server_name
, network_name
= serverdef
.split(':', 2)
169 sd
['server'] = scfg
.get(server_name
, 'server')
170 sd
['port'] = scfg
.getint(server_name
, 'port')
171 sd
['backup_server'] = scfg
.get(server_name
, 'backup_server')
172 sd
['backup_port'] = scfg
.getint(server_name
, 'backup_port')
173 sd
['operator'] = acfg
.get(account_name
, 'operator')
174 sd
['email'] = acfg
.get(account_name
, 'email')
175 sd
['password'] = acfg
.get(account_name
, 'password')
176 sd
['description'] = acfg
.get(account_name
, 'description')
177 sd
['country'] = acfg
.get(account_name
, 'country')
178 sd
['city'] = acfg
.get(account_name
, 'city')
179 sd
['network'] = network_name
181 self
['clients'] = clients
184 class FRNCrosslinkServiceMaker(object):
185 implements(IServiceMaker
, IPlugin
)
186 tapname
= "frncrosslink"
187 description
= "Freeradionetwork crosslink"
190 def makeService(self
, options
):
192 global clients
# FIXME: Get rid of this hack
193 for c
in options
['clients']:
196 PW
=c
['password'], ON
=c
['operator'],
197 # BC=c['transmission'], DS=c['description'],
198 BC
="Crosslink", DS
=c
['description'],
199 NN
=c
['country'], CT
=c
['city'], NT
=c
['network'])
200 factory
= FRNCrosslinkClientFactory(user
)
201 clients
.append((c
['server'], c
['port'], user
, factory
))
203 internet
.TCPClient(c
['server'], c
['port'], factory
))
207 serviceMaker
= FRNCrosslinkServiceMaker()
209 # vim: set et ai sw=4 ts=4 sts=4: