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.
267 lines
8.2 KiB
267 lines
8.2 KiB
import xmpp, os, sys, time, traceback
|
|
import threading
|
|
|
|
from config import *
|
|
|
|
VERSION = "0.2"
|
|
|
|
MIN_CONNECT_INTERVAL=300
|
|
|
|
last_connect = 0
|
|
commands = dict()
|
|
commands_private = dict()
|
|
group_listeners = []
|
|
private_listeners = []
|
|
status_listeners = []
|
|
|
|
users = dict()
|
|
|
|
STATUS_JOIN, STATUS_ONLINE, STATUS_OFFLINE, STATUS_RENAME, STATUS_XAWAY, STATUS_AWAY, STATUS_UNKNOWN = range(7)
|
|
|
|
def getUsers():
|
|
"""Return a dictionary of every user in the group, the values are status codes."""
|
|
return users
|
|
|
|
def addCommand(name,help,func):
|
|
"""Add a groupchat command.
|
|
|
|
@param name -- The name of the command, if the command keyword
|
|
"!name" is used at the start of a message the given
|
|
handler will be called
|
|
@param help -- Help string displayed on with the !help command
|
|
@param func -- Handler of the form func(msg,args) where msg is the
|
|
xmpppy representation of the message sent and args is
|
|
a list of arguments"""
|
|
commands[name]=(help,func)
|
|
|
|
def addPrivateCommand(name,help,func):
|
|
"""Add a private chat command.
|
|
|
|
@param name -- The name of the command, if the command keyword
|
|
"!name" is used at the start of a message the given
|
|
handler will be called
|
|
@param help -- Help string displayed on with the !help command
|
|
@param func -- Handler of the form func(msg,args) where msg is the
|
|
xmpppy representation of the message sent and args is
|
|
a list of arguments"""
|
|
commands_private[name]=(help,func)
|
|
|
|
def addGroupListener(func):
|
|
"""Add a groupchat listener.
|
|
|
|
@param func -- Handler of the form func(from,msg) where both arguments
|
|
are unicode strings"""
|
|
group_listeners.append(func)
|
|
|
|
def addPrivateListener(func):
|
|
"""Adds a private chat listener.
|
|
|
|
@param func -- Handler of the form func(from,msg) where both arguments
|
|
are unicode strings"""
|
|
private_listeners.append(func)
|
|
|
|
def addStatusListener(func):
|
|
"""Adds a user status listener.
|
|
|
|
@param func -- Handler of the form func(from,msg,status) where first two
|
|
arguments are unicode strings and status is one of STATUS_*
|
|
constants"""
|
|
status_listeners.append(func)
|
|
|
|
def sendMsg(to,body,mtype=None):
|
|
"""Send a message to the server.
|
|
|
|
@param to -- Recipient of the message
|
|
@param body -- Message body
|
|
@param mtype -- Type of the message, (e.g. "groupchat" for group
|
|
messages)"""
|
|
mymsg = xmpp.protocol.Message(body=body)
|
|
mymsg.setTo(to)
|
|
if mtype!=None:
|
|
mymsg.setType(mtype)
|
|
cl.send(mymsg)
|
|
|
|
def sendStyledMsg(to,body,mtype=None,style=""):
|
|
"""Send a CSS-styled message to the server.
|
|
|
|
@param to -- Recipient of the message
|
|
@param body -- Message body
|
|
@param mtype -- Type of the message, (e.g. "groupchat" for group
|
|
messages)
|
|
@param style -- Inline CSS used to format the message"""
|
|
mymsg = xmpp.protocol.Message(body=body)
|
|
mymsg.setTo(to)
|
|
if mtype!=None:
|
|
mymsg.setType(mtype)
|
|
c=mymsg.addChild('html', namespace='http://jabber.org/protocol/xhtml-im')
|
|
c=c.addChild('body',namespace='http://www.w3.org/1999/xhtml')
|
|
c=c.addChild('span',payload=body,attrs={'style':style})
|
|
cl.send(mymsg)
|
|
|
|
def sendMonoMsg(to,body,mtype=None):
|
|
"""Send a mono-spaced styled message to the server (e.g. ASCII art).
|
|
|
|
@param to -- Recipient of the message
|
|
@param body -- Message body
|
|
@param mtype -- Type of the message, (e.g. "groupchat" for group
|
|
messages)"""
|
|
mymsg = xmpp.protocol.Message(body=body)
|
|
mymsg.setTo(to)
|
|
mymsg.setType(mtype)
|
|
c=mymsg.addChild('html', namespace='http://jabber.org/protocol/xhtml-im')
|
|
c=c.addChild('body',namespace='http://www.w3.org/1999/xhtml')
|
|
c=c.addChild('span', attrs={'style':'font-family: Courier New, monospace;'})
|
|
c=c.addChild('pre',payload=body,attrs={'style':'background-color: #ffffff;'})
|
|
cl.send(mymsg)
|
|
|
|
def __help(msg,args):
|
|
sendMsg(ROOM,"Available commands:",'groupchat')
|
|
for cmd in commands:
|
|
sendMsg(ROOM," !"+cmd+" - "+commands[cmd][0],'groupchat')
|
|
sendMsg(ROOM,"Available private chat commands:",'groupchat')
|
|
for cmd in commands_private:
|
|
sendMsg(ROOM," !"+cmd+" - "+commands_private[cmd][0],'groupchat')
|
|
|
|
def __messageCB(conn,msg):
|
|
if msg.getType() == "groupchat":
|
|
#print unicode(msg.getFrom()) +": "+ unicode(msg.getBody()) +" " +unicode(msg.getChildren())
|
|
body = unicode(msg.getBody())
|
|
mfrom = unicode(msg.getFrom())
|
|
if NAME in body and not room_nick in mfrom:
|
|
sendMsg(ROOM,NAME+" --(brathuhn bot framework (v"+VERSION+"), type !help)",'groupchat')
|
|
for cmd in commands:
|
|
if body.startswith("!"+cmd) and not room_nick in mfrom:
|
|
args = body.split()
|
|
commands[cmd][1](msg,args)
|
|
for listener in group_listeners:
|
|
listener( unicode(msg.getFrom()), unicode(msg.getBody()))
|
|
|
|
if msg.getType() == "chat":
|
|
if msg.getBody() != None:
|
|
body = unicode(msg.getBody())
|
|
mfrom = unicode(msg.getFrom())
|
|
for cmd in commands_private:
|
|
if body.startswith("!"+cmd) and not room_nick in mfrom:
|
|
args = body.split()
|
|
commands_private[cmd][1](msg,args)
|
|
for listener in private_listeners:
|
|
listener( mfrom, body)
|
|
raise xmpp.NodeProcessed
|
|
|
|
def __presenceCB(conn,msg):
|
|
#print "Status",msg.getFrom(), msg.getStatus(), msg.getStatusCode(), msg.getType()
|
|
if msg.getType()=='unavailable':
|
|
nick=msg.getNick()
|
|
if nick==None:
|
|
del users[msg.getFrom()]
|
|
#Disconnect
|
|
if not msg.getFrom() in users:
|
|
for listener in status_listeners:
|
|
listener(msg.getFrom(),msg,STATUS_OFFLINE)
|
|
else:
|
|
del users[msg.getFrom()]
|
|
#Rename
|
|
if not msg.getFrom() in users:
|
|
for listener in status_listeners:
|
|
listener(msg.getFrom(),msg,STATUS_RENAME)
|
|
else:
|
|
show = msg.getShow()
|
|
if show==None:
|
|
if not msg.getFrom() in users:
|
|
for listener in status_listeners:
|
|
listener(msg.getFrom(),msg,STATUS_JOIN)
|
|
|
|
users[msg.getFrom()] = STATUS_ONLINE
|
|
#Online
|
|
else:
|
|
#print msg.getFrom(),"is now",show
|
|
if not msg.getFrom() in users:
|
|
for listener in status_listeners:
|
|
listener(msg.getFrom(),msg,STATUS_JOIN)
|
|
if show=="away":
|
|
users[msg.getFrom()] = STATUS_AWAY
|
|
elif show=="xa":
|
|
users[msg.getFrom()] = STATUS_XAWAY
|
|
else:
|
|
users[msg.getFrom()] = STATUS_UNKNOWN
|
|
# status change
|
|
|
|
status = STATUS_OFFLINE
|
|
if msg.getFrom() in users:
|
|
status = users[msg.getFrom()]
|
|
|
|
for listener in status_listeners:
|
|
listener(msg.getFrom(),msg.getStatus(),status)
|
|
|
|
jid=xmpp.protocol.JID(node=NAME,domain=SRV,resource='bot')
|
|
cl=xmpp.Client(jid.getDomain(),debug=[])
|
|
|
|
def connect_and_join():
|
|
"""Connect to the server and join the group chat room from the config."""
|
|
global room_nick
|
|
global room_to
|
|
global last_connect
|
|
last_connect=time.time()
|
|
#connect to server
|
|
con=cl.connect()
|
|
if not con:
|
|
print 'could not connect!'
|
|
raise Exception('could not connect!')
|
|
print 'connected'
|
|
|
|
cl.RegisterHandler('message',__messageCB)
|
|
cl.RegisterHandler('presence',__presenceCB)
|
|
|
|
#authenticate
|
|
auth=cl.auth(NAME,PWD,resource=jid.getResource())
|
|
if not auth:
|
|
print 'could not authenticate!'
|
|
raise Exception('could not authenticate!')
|
|
print 'auth'
|
|
|
|
|
|
room_to = "%s/%s" % (ROOM, NAME)
|
|
room_nick = ROOM+"/"+NAME
|
|
msg = xmpp.Presence(to=room_to)
|
|
if PWD_ROOM != "":
|
|
msg.addChild('x', namespace='http://jabber.org/protocol/muc').addChild('password',payload=PWD_ROOM)
|
|
|
|
#add the help command
|
|
addCommand("help","Prints this help message",__help)
|
|
|
|
#join the room
|
|
cl.send(msg)
|
|
#set status
|
|
cl.send(xmpp.Presence(to=room_to,status=NAME+" --(brathuhn bot framework (v"+VERSION+"), type !help)"))
|
|
|
|
def loop():
|
|
global last_connect
|
|
"""Loop forever and handle the event-queue."""
|
|
while True:
|
|
try:
|
|
cl.Process(1)
|
|
except xmpp.protocol.Conflict:
|
|
print "detected another connection, quitting..."
|
|
sys.exit(0)
|
|
except KeyboardInterrupt:
|
|
print "keyboard interrupt, quitting..."
|
|
sys.exit(0)
|
|
except:
|
|
print "-------ERROR OCCURED-------"
|
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
traceback.print_tb(exc_traceback, file=sys.stdout)
|
|
if not cl.connected:
|
|
s=MIN_CONNECT_INTERVAL-(time.time()-last_connect)
|
|
if s>0:
|
|
time.sleep(s)
|
|
connect_and_join()
|
|
print "---------------------------"
|
|
|
|
def callAt(func,_time,*args):
|
|
s=_time-time.time()
|
|
def run():
|
|
if s>0:
|
|
time.sleep(s)
|
|
func(*args)
|
|
threading.Thread(target=run).start()
|
|
|
|
|