Jabber 机器人 - 如何获取联系人的在线状态?
我想用Python设置一个Jabber机器人,这个机器人可以根据几个联系人的在线或离线状态发送消息。
我查了一下pyxmpp和xmpppy这两个库,但没找到简单的方法来检查某个联系人的状态。
有没有什么建议可以帮我实现这个功能?
理想情况下,我希望能有类似于bot.status_of("contact1@gmail.com")
这样的代码,返回"online"
。
3 个回答
你需要做的事情是:
- 先连接上。
- 声明一个状态处理器。这个处理器会保存每个联系人的状态信息(下面会详细说明)。
- 把你的初始状态发送到服务器,这样就能收到你所有在线联系人的状态信息,这也会触发处理器的工作。
- status_of() 方法会读取缓存,立刻判断联系人的状态。
现在,这取决于你需要什么样的状态信息。为了简单起见,我们假设你只需要“在线”或“离线”的状态。缓存可以是一个字典,字典的键是没有资源的JID(就是联系人的唯一标识),值是这个JID连接的资源的集合。例如:
{'foo@bar.com': set(['work', 'notebook']), 'bob@example.net': set(['gtalk'])}
现在,当你收到某个JID/资源的“可用”状态时:
if jid not in cache:
cache[jid] = set()
cache[jid].add(resource)
相反,当你收到“不可用”状态时:
if jid in cache: # bad people send "unavailable" just to make your app crash
cache[jid].discard(resource)
if 0 == len(cache[jid]):
del cache[jid]
现在:
def is_online(jid):
return jid in cache
当然,如果你想要更详细的信息,你可以不仅保存联系人的可用资源列表,还可以保存每个资源的状态、状态消息、优先级等等。
你想要的功能可以通过一个叫做 <presence type="probe"/>
的东西来实现。这是由客户端代替完成的,客户端自己不应该去做(这是根据XMPP即时消息的标准规定的)。因为这是一个机器人,你可以实现这个存在探测功能,来获取某个实体的当前状态。记得要把探测请求发送到没有资源的裸JID,因为服务器会代表客户端来回应这些探测请求。这意味着你的工作流程应该是这样的:
<presence/> // I'm online! BOT
<presence from="juliet@capulet.org/balcony"/> RESPONSE
<presence from="romeo@montague.net/hallway"/> // and so on... RESPONSE
<presence type="probe" to="benvolio@montague.net"/> BOT
<presence from="benvoio@montague.net/hallway"> RESPONSE
<status>Huzzah!</status>
<priority>3</priority>
</presence>
想了解更多关于你调用流程应该如何运作的信息,可以查看这个RFC的相关部分。
我觉得你想要的方式可能行不通,因为联系人在线状态的信息是异步接收的,也就是说,机器人并不是实时收到这些信息。
你需要写一个处理在线状态的函数,并把它注册到连接上。每当收到某个联系人的在线状态时,这个函数就会被调用。调用时会传入一个参数,这个参数会告诉你这个联系人是否在线。根据这个信息,你就可以决定是否给这个联系人发送消息。
使用 xmpppy
的话,你可以这样做:
def connect(jid, password, res, server, proxy, use_srv):
conn = xmpp.Client(jid.getDomain())
if not conn.connect(server=server, proxy=proxy, use_srv=use_srv):
log( 'unable to connect to server.')
return None
if not conn.auth(jid.getNode(), password, res):
log( 'unable to authorize with server.')
return None
conn.RegisterHandler( 'presence', callback_presence)
return conn
conn = connect(...)
def callback_presence(sess, pres):
if pres.getStatus() == "online":
msg = xmpp.Message(pres.getFrom(), "Hi!")
conn.send(msg)
附注:我没有测试过这段代码,但应该和这个非常相似。