fork of the original source from Google Code SVN
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.svn/
|
||||||
23
LICENSE
Normal file
23
LICENSE
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Copyright (c) 2008 Jose Carlos Luna, Luis Peralta
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
20
contrib/access-log-client
Normal file
20
contrib/access-log-client
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# ibuddy access log client
|
||||||
|
# contributed by pablo muñoz-chapuli
|
||||||
|
#
|
||||||
|
|
||||||
|
HOST=nowhere.com
|
||||||
|
APACHE_LOG=/var/log/apache2/access.log
|
||||||
|
|
||||||
|
tail -f $APACHE_LOG | awk '
|
||||||
|
/ 200 / {
|
||||||
|
system("echo MACRO_GREEN|nc -u -q 0 $HOST 8888")
|
||||||
|
}
|
||||||
|
/ 404 / {
|
||||||
|
system("echo MACRO_RED|nc -u -q 0 $HOST 8888")
|
||||||
|
}
|
||||||
|
/ 500 / {
|
||||||
|
system("echo MACRO_BEAT2|nc -u -q 0 $HOST 8888")
|
||||||
|
}'
|
||||||
|
|
||||||
37
contrib/weblog.pl
Normal file
37
contrib/weblog.pl
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
my $account='lala@lalalala.com';
|
||||||
|
my $log_path=' /var/log/httpd/combined_log';
|
||||||
|
|
||||||
|
|
||||||
|
use IO::Socket;
|
||||||
|
my $sock = IO::Socket::INET->new(PeerPort => 8888,
|
||||||
|
PeerAddr => "127.0.0.1",
|
||||||
|
Proto => "udp",
|
||||||
|
LocalAddr => 'localhost'
|
||||||
|
)
|
||||||
|
or die "Can't bind : $@\n";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
open(LOG,"ssh $account tail -f $log_path |");
|
||||||
|
|
||||||
|
while(<LOG>) {
|
||||||
|
chomp;
|
||||||
|
if(/HTTP\/\d.\d\"\s200/) {
|
||||||
|
print "OK:$_\n";
|
||||||
|
$sock->send("MACRO_GREEN");
|
||||||
|
} elsif (/HTTP\/\d.\d\"\s404/) {
|
||||||
|
print "NOTFOUND:$_\n";
|
||||||
|
$sock->send("MACRO_RED");
|
||||||
|
} elsif (/HTTP\/\d.\d\"\s403/) {
|
||||||
|
print "FORBIDDEN:$_\n";
|
||||||
|
$sock->send("MACRO_RED");
|
||||||
|
} elsif(/HTTP\/\d.\d\"\s500/) {
|
||||||
|
print "ERROR:$_\n";
|
||||||
|
$sock->send("MACRO_BEAT");
|
||||||
|
} else {
|
||||||
|
print "RECVD:$_\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
17
pybuddy.cfg
Normal file
17
pybuddy.cfg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[network]
|
||||||
|
# 127.0.0.1 is the default
|
||||||
|
# change it to 0.0.0.0 to bind to all interfaces
|
||||||
|
address: 127.0.0.1
|
||||||
|
port: 8888
|
||||||
|
|
||||||
|
[system]
|
||||||
|
# user to setuid to after daemon is started
|
||||||
|
user: nobody
|
||||||
|
# usb product id (do a lspci to check)
|
||||||
|
usbproduct: 0001
|
||||||
|
|
||||||
|
# logging
|
||||||
|
# loglevel: info/debug
|
||||||
|
loglevel: info
|
||||||
|
# logfile: console for console logging, set to filename to log to file
|
||||||
|
logfile: console
|
||||||
412
src/pybuddy-daemon.py
Normal file
412
src/pybuddy-daemon.py
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# pybuddy
|
||||||
|
# python ibuddy daemon - http://code.google.com/p/pybuddy
|
||||||
|
#
|
||||||
|
# Jose.Carlos.Luna@gmail.com
|
||||||
|
# luis.peralta@gmail.com
|
||||||
|
#
|
||||||
|
# Most of the code comes from http://cuntography.com/blog/?p=17
|
||||||
|
# Which is based on http://scott.weston.id.au/software/pymissile/
|
||||||
|
|
||||||
|
import usb
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import os
|
||||||
|
import pwd
|
||||||
|
import logging
|
||||||
|
from ConfigParser import RawConfigParser
|
||||||
|
|
||||||
|
|
||||||
|
################
|
||||||
|
#Configuration
|
||||||
|
################
|
||||||
|
tsleep = 0.1
|
||||||
|
|
||||||
|
|
||||||
|
################
|
||||||
|
# IBUDDY class
|
||||||
|
################
|
||||||
|
|
||||||
|
class BuddyDevice:
|
||||||
|
SETUP = (0x22, 0x09, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00)
|
||||||
|
MESS = (0x55, 0x53, 0x42, 0x43, 0x00, 0x40, 0x02)
|
||||||
|
|
||||||
|
LEFT = 0
|
||||||
|
RIGHT = 1
|
||||||
|
|
||||||
|
UP = 0
|
||||||
|
DOWN = 1
|
||||||
|
|
||||||
|
finalMess = 0xFF
|
||||||
|
battery = 0
|
||||||
|
product = 0
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, battery, buddy_product):
|
||||||
|
try:
|
||||||
|
self.dev=UsbDevice(0x1130, buddy_product, battery)
|
||||||
|
self.dev.open()
|
||||||
|
self.dev.handle.reset()
|
||||||
|
self.resetMessage()
|
||||||
|
self.pumpMessage()
|
||||||
|
self.battery=battery
|
||||||
|
self.product=buddy_product
|
||||||
|
except NoBuddyException, e:
|
||||||
|
raise NoBuddyException()
|
||||||
|
|
||||||
|
# Commands are sent as disabled bits
|
||||||
|
def setReverseBitValue(self,num,value):
|
||||||
|
if (value==1):
|
||||||
|
temp = 0xFF - (1<<num)
|
||||||
|
self.finalMess = self.finalMess & temp
|
||||||
|
elif (value==0):
|
||||||
|
temp = 1 << num
|
||||||
|
self.finalMess = self.finalMess | temp
|
||||||
|
|
||||||
|
def getReverseBitValue(self,num):
|
||||||
|
temp = self.finalMess
|
||||||
|
temp = temp >> num
|
||||||
|
res = not(temp&1)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def setHeadColor(self, red, green, blue):
|
||||||
|
self.setReverseBitValue(4,red)
|
||||||
|
self.setReverseBitValue(5,green)
|
||||||
|
self.setReverseBitValue(6,blue)
|
||||||
|
|
||||||
|
def setHeart(self, status):
|
||||||
|
self.setReverseBitValue(7,status)
|
||||||
|
|
||||||
|
def pumpMessage(self):
|
||||||
|
self.send(self.finalMess)
|
||||||
|
|
||||||
|
def resetMessage(self):
|
||||||
|
self.finalMess = 0xFF
|
||||||
|
|
||||||
|
def flick(self, direction):
|
||||||
|
if (direction == self.RIGHT):
|
||||||
|
self.setReverseBitValue(1,1)
|
||||||
|
self.setReverseBitValue(0,0)
|
||||||
|
elif(direction == self.LEFT):
|
||||||
|
self.setReverseBitValue(1,0)
|
||||||
|
self.setReverseBitValue(0,1)
|
||||||
|
|
||||||
|
def wing(self, direction):
|
||||||
|
if (direction == self.UP):
|
||||||
|
self.setReverseBitValue(3,1)
|
||||||
|
self.setReverseBitValue(2,0)
|
||||||
|
elif(direction == self.DOWN):
|
||||||
|
self.setReverseBitValue(3,0)
|
||||||
|
self.setReverseBitValue(2,1)
|
||||||
|
|
||||||
|
def getColors (self):
|
||||||
|
return self.getReverseBitValue(4), self.getReverseBitValue(5), self.getReverseBitValue(6)
|
||||||
|
|
||||||
|
def getHeart(self):
|
||||||
|
return self.getReverseBitValue(7)
|
||||||
|
|
||||||
|
def getWing(self):
|
||||||
|
return self.getReverseBitValue(2)
|
||||||
|
|
||||||
|
def getDirection(self):
|
||||||
|
return self.getReverseBitValue(1)
|
||||||
|
|
||||||
|
def send(self, inp):
|
||||||
|
try:
|
||||||
|
self.dev.handle.controlMsg(0x21, 0x09, self.SETUP, 0x02, 0x01)
|
||||||
|
self.dev.handle.controlMsg(0x21, 0x09, self.MESS+(inp,), 0x02, 0x01)
|
||||||
|
except usb.USBError:
|
||||||
|
self.__init__(self.battery,self.product)
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# USB class
|
||||||
|
######################
|
||||||
|
|
||||||
|
class UsbDevice:
|
||||||
|
def __init__(self, vendor_id, product_id, skip):
|
||||||
|
busses = usb.busses()
|
||||||
|
self.handle = None
|
||||||
|
count = 0
|
||||||
|
for bus in busses:
|
||||||
|
devices = bus.devices
|
||||||
|
for dev in devices:
|
||||||
|
|
||||||
|
if dev.idVendor==vendor_id and dev.idProduct==product_id:
|
||||||
|
if count==skip:
|
||||||
|
log.info("ibuddy found! vend: %s prod: %s",dev.idVendor, dev.idProduct)
|
||||||
|
self.dev = dev
|
||||||
|
|
||||||
|
self.conf = self.dev.configurations[0]
|
||||||
|
self.intf = self.conf.interfaces[0][0]
|
||||||
|
self.endpoints = []
|
||||||
|
for endpoint in self.intf.endpoints:
|
||||||
|
self.endpoints.append(endpoint)
|
||||||
|
log.info("endpoint")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
count=count+1
|
||||||
|
raise NoBuddyException()
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
if self.handle:
|
||||||
|
self.handle = None
|
||||||
|
self.handle = self.dev.open()
|
||||||
|
#We need to detach HID interface
|
||||||
|
try:
|
||||||
|
self.handle.detachKernelDriver(0)
|
||||||
|
self.handle.detachKernelDriver(1)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.handle.setConfiguration(self.conf)
|
||||||
|
self.handle.claimInterface(self.intf)
|
||||||
|
self.handle.setAltInterface(self.intf)
|
||||||
|
|
||||||
|
class NoBuddyException(Exception): pass
|
||||||
|
|
||||||
|
|
||||||
|
#########################################
|
||||||
|
# Decoding macros
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
def macro_color (red,green,blue):
|
||||||
|
message = "C:%d:%d:%d" % (red,green,blue)
|
||||||
|
message += "\nX\nS\nZ\nZ\n"
|
||||||
|
return message
|
||||||
|
|
||||||
|
def macro_heart ():
|
||||||
|
message = "H:1"
|
||||||
|
message += "\nX\nS\nZ\nZ\n"
|
||||||
|
return message
|
||||||
|
|
||||||
|
def macro_move_flap():
|
||||||
|
message = "ML\nX\nS\nS\n"
|
||||||
|
message += "MR\nX\nS\nS\n"
|
||||||
|
message += "ML\nX\nS\nS\n"
|
||||||
|
return message + macro_flap()
|
||||||
|
|
||||||
|
def macro_flap (heart=0):
|
||||||
|
message = ""
|
||||||
|
for i in range(2):
|
||||||
|
message += "L\n"
|
||||||
|
message += "WU\n"
|
||||||
|
if heart:
|
||||||
|
message += "H:1\n"
|
||||||
|
message += "X\nS\nL\n"
|
||||||
|
message += "WD\nX\nS\n"
|
||||||
|
return message
|
||||||
|
|
||||||
|
def macro_heart2():
|
||||||
|
message = "H:1\nX\nS\n"
|
||||||
|
message += "Z\nS\n"
|
||||||
|
message += "H:1\nX\nS\n"
|
||||||
|
message += "Z\nS\n\nZ\nZ\n"
|
||||||
|
return message
|
||||||
|
|
||||||
|
def macro_demo(num=1):
|
||||||
|
message = ""
|
||||||
|
for i in range(num):
|
||||||
|
message += "C:1:0:0\nH:0\nML\nWD\nX\nS\nS\n"
|
||||||
|
message += "C:0:1:0\nH:1\nMR\nWU\nX\nS\nS\n"
|
||||||
|
message += "C:0:0:1\nH:0\nML\nWD\nX\nS\nS\n"
|
||||||
|
message += "C:1:0:1\nH:1\nMR\nWU\nX\nS\nS\n"
|
||||||
|
message += "C:1:1:0\nH:0\nML\nWD\nX\nS\nS\n"
|
||||||
|
message += "C:0:1:1\nH:1\nMR\nWU\nX\nS\nS\n"
|
||||||
|
message += "C:1:1:1\nH:0\nML\nWD\nX\nS\nS\n"
|
||||||
|
message += "Z\nZ\n"
|
||||||
|
return message
|
||||||
|
|
||||||
|
def do_color(buddy,red,green,blue):
|
||||||
|
r,g,b = buddy.getColors()
|
||||||
|
try:
|
||||||
|
param1=int(red)
|
||||||
|
param2=int(green)
|
||||||
|
param3=int(blue)
|
||||||
|
except:
|
||||||
|
param1=-1
|
||||||
|
param2=-1
|
||||||
|
param3=-1
|
||||||
|
|
||||||
|
if(param1==-1):
|
||||||
|
param1=r
|
||||||
|
|
||||||
|
if(param2==-1):
|
||||||
|
param2=g
|
||||||
|
|
||||||
|
if(param3==-1):
|
||||||
|
param3=b
|
||||||
|
|
||||||
|
buddy.setHeadColor(param1,param2,param3)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def decode_buddy (buddy,msg):
|
||||||
|
orders = msg.split("\n")
|
||||||
|
for str in (orders):
|
||||||
|
cod = str.split(":")
|
||||||
|
param=0
|
||||||
|
if cod[0] == 'RED' or cod[0]=='R':
|
||||||
|
try: do_color(buddy,cod[1],-1,-1)
|
||||||
|
except: pass
|
||||||
|
elif cod[0] == 'GREEN' or cod[0]=='G':
|
||||||
|
try: do_color(buddy,-1,cod[1],-1)
|
||||||
|
except: pass
|
||||||
|
elif cod[0] == 'BLUE' or cod[0]=='B':
|
||||||
|
try: do_color(buddy,-1,-1,cod[1])
|
||||||
|
except: pass
|
||||||
|
elif cod[0] == 'YELLOW':
|
||||||
|
try: do_color(buddy,cod[1],cod[1],-1)
|
||||||
|
except: pass
|
||||||
|
continue
|
||||||
|
elif cod[0] == 'SAPHIRE':
|
||||||
|
try: do_color(buddy,-1,cod[1],cod[1])
|
||||||
|
except: pass
|
||||||
|
elif cod[0] == 'VIOLET':
|
||||||
|
try: do_color(buddy,cod[1],-1,cod[1])
|
||||||
|
except: pass
|
||||||
|
elif cod[0] == 'HEART' or cod[0]=='H':
|
||||||
|
try:
|
||||||
|
param=int(cod[1])
|
||||||
|
except:
|
||||||
|
param=0
|
||||||
|
buddy.setHeart(param)
|
||||||
|
elif cod[0] == 'C':
|
||||||
|
try: do_color(buddy,cod[1],cod[2],cod[3])
|
||||||
|
except: pass
|
||||||
|
elif cod[0] == 'MR':
|
||||||
|
buddy.flick(buddy.RIGHT)
|
||||||
|
continue
|
||||||
|
elif cod[0] == 'ML':
|
||||||
|
buddy.flick(buddy.LEFT)
|
||||||
|
elif cod[0] == 'SLEEP' or cod[0]=='S':
|
||||||
|
time.sleep(0.1)
|
||||||
|
elif cod[0] == 'WU':
|
||||||
|
buddy.wing(buddy.UP)
|
||||||
|
elif cod[0]== 'WD':
|
||||||
|
buddy.wing(buddy.DOWN)
|
||||||
|
elif cod[0]== 'EXEC' or cod[0]=='X':
|
||||||
|
buddy.pumpMessage()
|
||||||
|
elif cod[0] == 'CLEAR' or cod[0]=='L':
|
||||||
|
buddy.resetMessage()
|
||||||
|
elif cod[0]== 'RESET' or cod[0]=='Z':
|
||||||
|
buddy.resetMessage()
|
||||||
|
buddy.pumpMessage()
|
||||||
|
elif cod[0] == 'MACRO_FLAP':
|
||||||
|
decode_buddy(buddy,macro_move_flap())
|
||||||
|
elif cod[0] == 'MACRO_FLAP2':
|
||||||
|
decode_buddy(buddy,macro_flap())
|
||||||
|
elif cod[0] == 'MACRO_RED':
|
||||||
|
decode_buddy(buddy,macro_color(1,0,0))
|
||||||
|
elif cod[0] == 'MACRO_GREEN':
|
||||||
|
decode_buddy(buddy,macro_color(0,1,0))
|
||||||
|
elif cod[0] == 'MACRO_BLUE':
|
||||||
|
decode_buddy(buddy,macro_color(0,0,1))
|
||||||
|
elif cod[0] == 'MACRO_YELLOW':
|
||||||
|
decode_buddy(buddy,macro_color(1,1,0))
|
||||||
|
elif cod[0] == 'MACRO_VIOLET':
|
||||||
|
decode_buddy(buddy,macro_color(1,0,1))
|
||||||
|
elif cod[0] == 'MACRO_SAPHIRE':
|
||||||
|
decode_buddy(buddy,macro_color(0,1,1))
|
||||||
|
elif cod[0] == 'MACRO_LBLUE':
|
||||||
|
decode_buddy(buddy,macro_color(1,1,1))
|
||||||
|
elif cod[0] == 'MACRO_HEART':
|
||||||
|
decode_buddy(buddy,macro_heart())
|
||||||
|
elif cod[0] == 'MACRO_HEART2':
|
||||||
|
decode_buddy(buddy,macro_heart2())
|
||||||
|
elif cod[0] == 'DEMO':
|
||||||
|
decode_buddy(buddy,macro_demo())
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# MAIN program
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
log = logging.getLogger('pybuddy')
|
||||||
|
|
||||||
|
#Default config
|
||||||
|
config = RawConfigParser(
|
||||||
|
{ 'port': 8888,
|
||||||
|
'address': '127.0.0.1',
|
||||||
|
'user': 'nobody',
|
||||||
|
'loglevel': 'info',
|
||||||
|
'logfile': 'console',
|
||||||
|
'usbproduct': 0002,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
config._sections = {'network':{}, 'system':{}}
|
||||||
|
|
||||||
|
config_files = [ "~/.pybuddy.cfg",
|
||||||
|
"/etc/pybuddy/pybuddy.cfg",
|
||||||
|
"/usr/local/etc/pybuddy.cfg"
|
||||||
|
]
|
||||||
|
|
||||||
|
#Parse config
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
config_files.append(sys.argv[1])
|
||||||
|
|
||||||
|
config_read = config.read(config_files)
|
||||||
|
|
||||||
|
if config.get("system", "logfile") != "console":
|
||||||
|
logging.basicConfig(
|
||||||
|
filename=config.get("system", "logfile"),
|
||||||
|
format='%(asctime)s %(levelname)-8s %(message)s',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(
|
||||||
|
stream=sys.stderr,
|
||||||
|
format='%(asctime)s %(levelname)-8s %(message)s',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if config.get("system", "loglevel") == "debug":
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
elif config.get("system", "loglevel") == "info":
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
if config_read:
|
||||||
|
log.info("Read config file: %s", config_read[0])
|
||||||
|
|
||||||
|
#Initialize device
|
||||||
|
log.info("Starting search...")
|
||||||
|
try:
|
||||||
|
buddy=BuddyDevice(0, int(config.get("system", "usbproduct")))
|
||||||
|
except NoBuddyException, e:
|
||||||
|
log.error("Not found!")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
#Daemonize
|
||||||
|
log.info("Starting daemon...")
|
||||||
|
if os.fork()==0:
|
||||||
|
os.setsid()
|
||||||
|
else:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
#Create server socket
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
s.bind((config.get("network", "address"), int(config.get("network", "port"))))
|
||||||
|
|
||||||
|
#Drop privileges
|
||||||
|
try:
|
||||||
|
uid = pwd.getpwnam(config.get("system", "user"))[2]
|
||||||
|
except KeyError:
|
||||||
|
log.error("Username %s not found, exiting...", config.get("system", "user"))
|
||||||
|
sys.exit(1)
|
||||||
|
os.setuid(uid)
|
||||||
|
|
||||||
|
|
||||||
|
#Main message loop
|
||||||
|
while 1:
|
||||||
|
try:
|
||||||
|
message, address = s.recvfrom(8192)
|
||||||
|
log.debug("Got data from %s", address)
|
||||||
|
decode_buddy(buddy, message)
|
||||||
|
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user