Skip to content
This repository has been archived by the owner on Dec 17, 2020. It is now read-only.

Commit

Permalink
MVP reached: msgs in campfire now go to xmpp and vice versa
Browse files Browse the repository at this point in the history
  • Loading branch information
bmuller committed Aug 21, 2011
1 parent e91df0c commit 98d4b0d
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 59 deletions.
87 changes: 74 additions & 13 deletions campfirer/campfire.py
Expand Up @@ -4,29 +4,72 @@
from twisted.python import log
from twisted.web import client

from campfirer.DOMLight import createModel
from campfirer.DOMLight import createModel, XMLMaker


class Message:
def __init__(self, user, body, msgtype, tstamp):
def __init__(self, id, user, body, msgtype, tstamp):
self.id = id
self.user = user
self.body = body
self.msgtype = msgtype
self.tstamp = tstamp


class ParticipantList:
def __init__(self):
self.participants = {}
self.recent = {}

def add(self, id, name):
self.participants[id] = name

def update(self, newpeople):
self.recent = {}
for uid, name in newpeople.items():
if not self.participants.has_key(uid):
self.recent[uid] = name
self.participants[uid] = name

def getName(self, id):
return self.participants.get(id, id)

def getJustJoined(self):
return self.recent

def __len__(self):
return len(self.participants)


class MessageList:
def __init__(self, maxsize=100):
self.maxsize = maxsize
self.msgs = []
self.last_msg_id = None
self.ignore = set()


def addIgnore(self, id):
self.ignore.add(id)


def append(self, msgs):
self.msgs = (self.msgs + msgs)[-self.maxsize:]


def reset(self, msgs):
self.msgs = []
for msg in msgs:
if msg.id in self.ignore:
self.ignore.discard(msg.id)
else:
self.msgs.append(msg)


def __iter__(self):
return self.msgs.__iter__()


def __len__(self):
return len(self.msgs)

Expand All @@ -52,11 +95,13 @@ def getPage(self, url, username=None, password=None):
return client.getPage(self.url(url), headers=headers)


def postPage(self, url):
def postPage(self, url, data=None):
log.msg("POST %s" % url)
auth = "%s:X" % self.token
headers = {'Authorization': base64.b64encode(auth) }
return client.getPage(self.url(url), method='POST', headers=headers)
if data is not None:
headers['Content-Type'] = 'application/xml'
return client.getPage(self.url(url), method='POST', headers=headers, postdata=data)


class CampfireRoom(CampfireClient):
Expand All @@ -65,7 +110,7 @@ def __init__(self, account, token, roomname, room_id, muc):
self.roomname = roomname
self.room_id = room_id
self.token = token
self.participants = {}
self.participants = ParticipantList()
self.topic = ""
self.msgs = MessageList()
self.muc = muc
Expand All @@ -82,10 +127,15 @@ def setJIDs(self, source_jid, participant_jid):

def _updateRoom(self, response):
root = createModel(response)
self.participants = {}

# update list of participants
participants = {}
for xmluser in root.users[0].user:
uid = xmluser.id[0].text[0]
self.participants[uid] = xmluser.name[0].text[0]
name = xmluser.name[0].text[0]
participants[uid] = name
self.participants.update(participants)

self.topic = root.topic[0].text[0]
if self.msgs.last_msg_id is not None:
url = "room/%s/recent.xml?since_message_id=%s" % (self.room_id, self.msgs.last_msg_id)
Expand All @@ -100,15 +150,26 @@ def _updateMsgs(self, response):
for xmlmsg in root.message:
msgtype = xmlmsg.type[0].text[0]
if msgtype in ["TextMessage", "PasteMessage"]:
user = self.participants.get(xmlmsg.children['user-id'][0].text[0], xmlmsg.children['user-id'][0].text[0])
user = self.participants.getName(xmlmsg.children['user-id'][0].text[0])
body = xmlmsg.body[0].text[0]
id = xmlmsg.id[0].text[0]
tstamp = xmlmsg.children["created-at"][0].text[0]
msgs.append(Message(user, body, msgtype, tstamp))
msgs.append(Message(id, user, body, msgtype, tstamp))
self.msgs.last_msg_id = xmlmsg.children['id'][0].text[0]
self.msgs.append(msgs)
self.msgs.reset(msgs)
return self


def say(self, msg):
xml = XMLMaker()
xmlmsg = xml.message({}, xml.body({}, msg))
# ignore this message next time we poll campfirenow.com
def addIgnore(result):
root = createModel(result)
self.msgs.addIgnore(root.id[0].text[0])
return self.postPage("room/%s/speak.xml" % self.room_id, data=str(xmlmsg)).addCallback(addIgnore)


def join(self):
return self.postPage("room/%s/join.xml" % self.room_id).addCallback(lambda _: self)

Expand Down Expand Up @@ -208,15 +269,15 @@ def key(self, account, user):
return "%s@%s" % (user, account)


def getCampfire(self, account, user, password):
key = self.key(account, user)
def getCampfire(self, account, jid, password=None):
key = self.key(account, jid.userhost())
if self.fires.has_key(key):
return defer.succeed(self.fires[key])
def save(result):
if result is not None:
self.fires[key] = result
return result
return Campfire(account, self.muc).initialize(user, password).addCallback(save)
return Campfire(account, self.muc).initialize(jid.resource, password).addCallback(save)


def putCampfireOut(self, account, user):
Expand Down
66 changes: 20 additions & 46 deletions campfirer/muc.py
Expand Up @@ -52,6 +52,7 @@ def componentConnected(self, xmlstream):
self.xmlstream = xmlstream
self.xmlstream.addObserver(DISCO_INFO, self.onDiscoInfo)
self.xmlstream.addObserver(PRESENCE, self.onPresence)
self.xmlstream.addObserver(MESSAGE, self.onMessage)
log.msg("muc component connected")


Expand All @@ -66,13 +67,18 @@ def onDiscoInfo(self, iq):
response.send(iq['from'])


def parseCampfireName(self, jid):
room_parts = jid.user.split(".")
roomname = ".".join(room_parts[1:])
account = room_parts[0]
return (account, roomname)


# <presence from='bmuller@butterfat.net/hm-min' to='testthree@muc.campfirer.com/bmuller'>
# <x xmlns='http://jabber.org/protocol/muc'><password>password</password></x></presence>
def onPresence(self, pres):
to = jid.JID(pres['to'])
room_parts = to.user.split(".")
roomname = ".".join(room_parts[1:])
account = room_parts[0]
account, roomname = self.parseCampfireName(to)

def handleAuth(campfire):
if campfire is None:
Expand All @@ -87,7 +93,7 @@ def handleAuth(campfire):
self.sendErrorPresence(pres, "not-authorized")
else:
log.msg("attempting to auth %s for room %s on account %s" % (to.resource, roomname, account))
self.smokey.getCampfire(account, to.resource, password).addCallback(handleAuth)
self.smokey.getCampfire(account, to, password).addCallback(handleAuth)


def initializeRoom(self, campfire, roomname, participant_jid, source_jid):
Expand All @@ -101,7 +107,7 @@ def initParticipants(room):


def handleRoomUpdate(self, room):
for username in room.participants.values():
for username in room.participants.getJustJoined().values():
mfrom = room.participant_jid.userhostJID()
mfrom.resource = username.replace(" ", "")
self.sendPresence(mfrom, room.source_jid)
Expand Down Expand Up @@ -139,45 +145,13 @@ def sendMessage(self, mfrom, msgBody, mto, tstamp):
self.xmlstream.send(m)


################## ################## ################## ################## ##################
def presence(self, sendto):
p = domish.Element((None, 'presence'))
p['to'] = sendto
p['from'] = self.jid
return p


def updateRoomList(self, roomlist):
rooms = []
for room in roomlist.firstChildElement().elements():
rooms.append((room['jid'], room['name']))

def setID(room):
self.rooms[unicode(room.jid)] = (room.id, room.name)

def addRooms(r):
room = r.pop()
d = Room.createIfNonexistant(room[0], room[1]).addCallback(setID)
if len(r) > 0:
return d.addCallback(lambda _: addRooms(r))
return d

addRooms(rooms).addCallback(lambda _: self.joinRooms())


def joinRooms(self):
for jid in self.rooms.keys():
if len(self.config['rooms']) == 0 or self.config['rooms'].has_key(jid):
p = self.presence("%s/%s" % (jid, 'gossipr'))
p.addElement('x', 'http://jabber.org/protocol/muc')
self.xmlstream.send(p)


def onMessage(self, msg):
jidparts = jid.parse(msg['from'])
userhost = "%s@%s" % (jidparts[0], jidparts[1])

if self.rooms.has_key(userhost):
room_id = self.rooms[userhost][0]
body = xpath.queryForString("/message/body", msg)
return Message(speaker=jidparts[2], message=body, room_id=room_id, created_at=datetime.datetime.now()).save()
to = jid.JID(msg['to'])
account, roomname = self.parseCampfireName(to)
def sendMsgRoom(room):
if room is not None:
room.say(xpath.queryForString("/message/body", msg))
def sendMsg(campfire):
if campfire is not None:
campfire.getRoom(roomname).addCallback(sendMsgRoom)
self.smokey.getCampfire(account, to).addCallback(sendMsg)
12 changes: 12 additions & 0 deletions test/client.py
Expand Up @@ -4,7 +4,9 @@
from twisted.names.srvconnect import SRVConnector
from twisted.internet import reactor
from twisted.python import log

import sys
import time

from config import CONFIG

Expand Down Expand Up @@ -67,7 +69,17 @@ def authenticated(self, xs):
x = presence.addElement('x', 'http://jabber.org/protocol/muc')
x.addElement('password', content=CONFIG['roompasswd'])
xs.send(presence)
reactor.callLater(10, self.say, "hello there %i" % time.time())


def say(self, msgtxt):
msg = domish.Element((None, 'message'))
msg['from'] = CONFIG['jid']
msg['to'] = jid.JID(CONFIG['room']).userhost()
msg['type'] = "groupchat"
msg.addElement('body', content=msgtxt)
self.xmlstream.send(msg)


def init_failed(self, failure):
log.err("Initialization failed.")
Expand Down

0 comments on commit 98d4b0d

Please sign in to comment.