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

Commit

Permalink
added campfire interaction code
Browse files Browse the repository at this point in the history
  • Loading branch information
bmuller committed Apr 24, 2011
1 parent db13baa commit 1817d10
Show file tree
Hide file tree
Showing 7 changed files with 330 additions and 69 deletions.
16 changes: 0 additions & 16 deletions campfirer.tac
@@ -1,29 +1,13 @@
from twisted.application import service, internet, strports
from twisted.words.protocols.jabber import component
#from twisted.enterprise import adbapi
#from twisted.web2 import server, channel, log

#from twistar.registry import Registry
#from twistar.dbconfig.base import InteractionBase

#from gossipr import listener, website

from campfirer import muc

from config import CONFIG

# connect to DB
#Registry.DBPOOL = adbapi.ConnectionPool(CONFIG['db.driver'],
# user=CONFIG['db.user'],
# passwd=CONFIG['db.pass'],
# db=CONFIG['db.name'],
# host=CONFIG['db.host'])
#InteractionBase.LOG = CONFIG['db.debug']

# Application set up
application = service.Application("campfirer")


# Component
host = "tcp:%s:%s" % (CONFIG['xmpp.host'], CONFIG['xmpp.port'])
sm = component.buildServiceManager(CONFIG['xmpp.muc.host'], CONFIG['xmpp.muc.password'], (host))
Expand Down
188 changes: 188 additions & 0 deletions campfirer/DOMLight.py
@@ -0,0 +1,188 @@
"""
DOMLight is a little library for making DOM's more accessable in python.
Copyright (C) 2011 Brian Muller <bamuller@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""

## DOMLight is a little library for making DOM's more accessable in python.
##
## For instance, say you've got the following XML:
## <cheese with="spam">
## <block type="jack">
## A huge bunch of jack.
## </block>
## <block type="cheddar">
## I like cheddar.
## <color>yellow</color>
## It is tastey.
## </block>
## <additional stuff="eggs" />
## </cheese>
##
## Python code to access the objects:
##
## import DOMLight
## root = DOMLight.createModel(xml_string)
## print root["with"] # this accesses the attribute "with" in the root node
## print root["doesnt_exist"] # this attempts to access a non-existant attribute, returns ""
## block = root.block[0] # block now holds root's first child named block
## print block["type"] # -> "jack"
## print block.text[0] # Text children are named "text" - this prints the first text child
## block = root.block[1] # block now holds root's second child named block
## print block.color[0].text[0] # -> "yellow"
## print block.text[0] # -> "I like cheddar."
## print block.text[1] # -> "It is tastey."
## a = root.additional # "a" is now an list with one element
## print a[0]["stuff"] # -> "eggs"
##
## node.children is a hash. The keys are children names, and the values
## are lists of children with that name.
##
## for nodename, nodelist in root.children.items():
## print "There are %d children named %s" % (len(nodelist), nodename)
## for node in nodelist:
## # if it's a text node, just print the text
## if type(node).__name__ == 'unicode':
## print "text value is: \"%s\"" % str(node)
## else:
## # node.attrs is a hash. The keys are attribute names,
## # and the values are the attribute values.
## for attr_name,attr_value in node.attrs.items():
## print "%s = %s" % (attr_name, attr_value)

from xml.dom.minidom import *
import cgi
import re

def createModel(xml_string):
def makeObject(elem):
doml = DOMLightObject(elem.nodeName)
if elem.attributes:
for k,v in elem.attributes.items():
doml[k] = v
for child in elem.childNodes:
if child.nodeType == 3 and child.nodeValue.rstrip() != "":
doml.setAttr('text', child.nodeValue)
elif child.nodeType == 8:
doml.setAttr('comment', child.nodeValue)
elif child.nodeType != 3:
doml.setAttr(child.nodeName, makeObject(child))
return doml
doc = parseString(xml_string)
for kid in doc.childNodes:
if kid.nodeType == 1:
return makeObject(kid)
return None

class DOMLightObject:
def __init__(self, name):
self.children = {}
self.attrs = {}
self._name = name

def __getitem__(self, name):
if self.attrs.has_key(name):
return self.attrs[name]
return ""

def __setitem__(self, name, value):
self.attrs[name] = value

def setAttr(self, name, kid):
if not self.children.has_key(name):
self.children[name] = []
self.children[name].append(kid)

def __getattr__(self, name):
if self.children.has_key(name):
return self.children[name]
return []

def __str__(self):
return "<DOMLightObject %s>" % self._name


## This class is used to generate xml. It is used like:
# xml = XMLMaker()
# print xml.a({'href': 'http://google.com'}, xml.img({'src': 'http://www.google.com/intl/en/images/logo.gif'}), "Click the image")
# <a href="http://google.com"><img src="http://www.google.com/intl/en/images/logo.gif"/>Click the image</a>
class XMLMaker:
## Inner class representing an XML element.
class Elem:
def __init__(self,name):
self.name = name
def set(self,attr={},*args):
self.attr = attr
self.kids = args
return self
def cleanAttrs(self):
if len(self.attr)==0: return ""
return ' '+" ".join(["%s=\"%s\"" % (k,v) for (k,v) in self.attr.items()])
def __str__(self):
if len(self.kids)==0: return "<%s%s/>" % (self.name,self.cleanAttrs())
result = ""
kids = ""
try:
kids = "".join([str(kid) for kid in self.kids])
strmap = lambda *a: map(lambda x: str(x), a)
(self.name,cleanedAttrs,kids,self.name) = strmap(self.name,self.cleanAttrs(),kids,self.name)
result = "<%s%s>%s</%s>" % (self.name,cleanedAttrs,kids,self.name)
except Exception, e:
print "Error: %s\nkids: %s" % (str(e),kids)
return result
## Constructor.
# @param namespace The xmlns to use
def __init__(self, namespace=None):
self.namespace = namespace
## Manually create a function
def makeFunc(self,name):
return self.__getattr__(name)
## Make a new elemented
# @param name Name of the elem to make
def __getattr__(self,name):
if self.namespace!=None: elem = XMLMaker.Elem(self.namespace+':'+name)
else: elem = XMLMaker.Elem(name)
return elem.set


## This function first html escapes a string, and then converts
# entities to html entities to xml entities as much as possible.
# \return An XML entity escaped string
def XMLEscape(string):
s = cgi.escape(string, True)
return htmlToXmlEntities(s)

## XML only allows 5 entities (&lt; &gt; &quot; &amp; &apos;) and
# html tidy does not do any conversion when creating xml from html,
# so this must be done manually. This function will convert Unicode
# entities to html entities to xml entities as much as possible.
# (for example, "&#169;" becomes "&amp;copy;"
# @param s The string containing HTML entities
# \return A string only containing XML entities
def htmlToXmlEntities(s):
def convertentity(m):
if m.group(1)=='#':
try:
return "&amp;%s;" % htmlentitydefs.codepoint2name[int(m.group(2))]
except KeyError:
return "" # Must keep runing - even if we loose a uni char
elif m.group(2) in ['lt', 'gt', 'quot', 'amp', 'apos']:
return '&%s;' % m.group(2)
else:
return '&amp;%s;' % m.group(2)
return re.sub(r'&(#?)(.+?);', convertentity, s)


113 changes: 113 additions & 0 deletions campfirer/campfire.py
@@ -0,0 +1,113 @@
import base64

from twisted.internet.ssl import ClientContextFactory
from twisted.web import client

from campfirer.DOMLight import createModel

class WebClientContextFactory(ClientContextFactory):
def getContext(self):
return ClientContextFactory.getContext(self)


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


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

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

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



class CampfireClient:
def __init__(self, account):
self.account = account
self.contextFactory = WebClientContextFactory()
self.token = None


def getPage(self, url, username=None, password=None):
if username is None:
username = self.token
if password is None:
password = "X"
auth = "%s:%s" % (username, password)
headers = {'Authorization': base64.b64encode(auth) }
url = "https://%s.campfirenow.com/%s" % (self.account, url)
return client.getPage(url, self.contextFactory, headers=headers)


class CampfireRoom(CampfireClient):
def __init__(self, account, token, roomname, room_id):
CampfireClient.__init__(self, account)
self.roomname = roomname
self.room_id = room_id
self.token = token
self.participants = {}
self.topic = ""
self.msgs = MessageList()


def _updateRoom(self, response):
root = createModel(response)
self.participants = {}
for xmluser in root.users[0].user:
uid = xmluser.id[0].text[0]
self.participants[uid] = xmluser.name[0].text[0]
self.topic = root.topic[0].text[0]
return self.getPage("room/%s/recent.xml" % self.room_id).addCallback(self._updateMsgs)


def _updateMsgs(self, response):
root = createModel(response)
msgs = []
print 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])
body = xmlmsg.body[0].text[0]
msgs.append(Message(user, body, msgtype))
self.msgs.append(msgs)
return self


def update(self):
return self.getPage("room/%s.xml" % self.room_id).addCallback(self._updateRoom)


class Campfire(CampfireClient):
def getRoom(self, name):
def _getRoomID(response):
root = createModel(response)
room_id = None
for xmlroom in root.room:
if str(xmlroom.name[0].text[0]) == name:
room_id = str(xmlroom.id[0].text[0])
if room_id is None:
return None
return CampfireRoom(self.account, self.token, name, room_id).update()
return self.getPage("rooms.xml").addCallback(_getRoomID)


def initialize(self, username, password):
def _getTokenFailure(result):
self.token = None

def _getToken(response):
root = createModel(response)
self.token = root.children["api-auth-token"][0].text[0]
return self

return self.getPage("users/me.xml", username, password).addCallback(_getToken, _getTokenFailure)
33 changes: 0 additions & 33 deletions campfirer/models.py

This file was deleted.

0 comments on commit 1817d10

Please sign in to comment.