You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
4.2 KiB
132 lines
4.2 KiB
#!/usr/bin/env python |
|
# -*- coding: utf-8 -*- |
|
# |
|
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com> |
|
# See LICENSE.txt for copyright info |
|
|
|
from __future__ import with_statement |
|
from frn.protocol.client import FRNClient, FRNClientFactory |
|
from frn.user import FRNUser |
|
from twisted.internet import reactor, task |
|
from twisted.internet.defer import DeferredList |
|
from twisted.python import log |
|
import os, string |
|
safe_chars = string.ascii_letters+string.digits+' :;.,+-=$@' |
|
|
|
PARROT_AUDIO_DELAY = 5.0 |
|
|
|
def sanitizeFilename(name): |
|
r = '' |
|
for c in name: |
|
if c in safe_chars: |
|
r += c |
|
return r |
|
|
|
|
|
class FRNParrot(FRNClient): |
|
|
|
def getClientName(self, client_id): |
|
if self.clientsById.has_key(client_id): |
|
return self.clientsById[client_id]['on'] |
|
else: |
|
return client_id |
|
|
|
def textMessageReceived(self, client, message, target): |
|
log.msg("Type %s message from %s: %s" % |
|
(target, self.getClientName(client), message)) |
|
if target == 'P': # Only reply to private messages |
|
if not message.startswith('play'): |
|
self.sendTextMessage(client, message) |
|
else: |
|
cmd = message.split() |
|
if len(cmd) > 1: |
|
message = sanitizeFilename(cmd[1]) |
|
else: |
|
message = 'monkeys' |
|
filename = 'sounds/%s.wav' % message |
|
if os.path.exists(filename): |
|
log.msg("Streaming file %s" % filename) |
|
with file(filename, 'rb') as sf: |
|
sf.seek(0x3c) # Skip wav header |
|
while True: |
|
b = sf.read(325) |
|
if len(b) < 325: |
|
break |
|
self.feedStreaming(b) |
|
self.factory.reactor.callLater(0.5, |
|
self.startStreaming) |
|
else: |
|
self.sendTextMessage(client, "File not found") |
|
|
|
def stopTransmission(self): |
|
FRNClient.stopTransmission(self) |
|
log.msg("Stopped playback.") |
|
|
|
def startRepeating(self, from_id): |
|
log.msg("%s stopped talking: starting playback." % |
|
self.clients[from_id-1]['on']) |
|
self.startStreaming() |
|
|
|
def audioFrameReceived(self, from_id, frames): |
|
recname = sanitizeFilename(self.clients[from_id-1]['on']) |
|
with file('recordings/%s.gsm' % recname, 'ab') as f: |
|
f.write(frames) |
|
self.feedStreaming(frames) |
|
try: |
|
self.parrot_timer.reset(PARROT_AUDIO_DELAY) |
|
except: |
|
log.msg("%s started talking" % |
|
self.clients[from_id-1]['on']) |
|
self.parrot_timer = self.factory.reactor.callLater( |
|
PARROT_AUDIO_DELAY, self.startRepeating, from_id) |
|
self.pong() |
|
|
|
def loginResponse(self, info): |
|
log.msg("Login: %s" % info['al']) |
|
|
|
def clientsListUpdated(self, clients): |
|
self.clients = clients |
|
self.clientsById = dict([(i['id'], i) for i in clients]) |
|
|
|
|
|
class FRNParrotFactory(FRNClientFactory): |
|
protocol = FRNParrot |
|
reactor = reactor |
|
|
|
|
|
if __name__ == '__main__': |
|
import sys |
|
from os.path import dirname, join as pjoin |
|
from ConfigParser import ConfigParser |
|
|
|
log.startLogging(sys.stderr) |
|
|
|
basedir = dirname(__file__) |
|
|
|
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']) |
|
|
|
argc = len(sys.argv) |
|
if argc >= 3: |
|
server_name, network_name = sys.argv[2].split(':',1) |
|
account_cfg = acfg.items(sys.argv[1])+[('network', network_name)] |
|
server_cfg = scfg.items(server_name) |
|
server = scfg.get(server_name, 'server') |
|
port = scfg.getint(server_name, 'port') |
|
|
|
d = dict(account_cfg) |
|
user = FRNUser( |
|
EA=d['email'], |
|
PW=d['password'], ON=d['operator'], |
|
BC=d['transmission'], DS=d['description'], |
|
NN=d['country'], CT=d['city'], NT=d['network']) |
|
reactor.connectTCP(server, port, FRNParrotFactory(user)) |
|
reactor.run() |
|
|
|
|
|
# vim: set et ai sw=4 ts=4 sts=4:
|
|
|