This commit is contained in:
committed by
Salad Dais
parent
57f8cc2898
commit
5bb135727d
4
README.txt
Normal file
4
README.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
|
||||
8
docs/HISTORY.txt
Normal file
8
docs/HISTORY.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
1.0 - Unreleased
|
||||
----------------
|
||||
|
||||
* Initial release
|
||||
|
||||
6
pyogp/__init__.py
Normal file
6
pyogp/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
|
||||
try:
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
||||
except ImportError:
|
||||
from pkgutil import extend_path
|
||||
__path__ = extend_path(__path__, __name__)
|
||||
6
pyogp/lib/__init__.py
Normal file
6
pyogp/lib/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
|
||||
try:
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
||||
except ImportError:
|
||||
from pkgutil import extend_path
|
||||
__path__ = extend_path(__path__, __name__)
|
||||
0
pyogp/lib/base/__init__.py
Normal file
0
pyogp/lib/base/__init__.py
Normal file
46
pyogp/lib/base/agent.py
Normal file
46
pyogp/lib/base/agent.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from zope.interface import implements
|
||||
from zope.component import adapts
|
||||
|
||||
from interfaces import IAgent, IPlaceAvatarAdapter
|
||||
|
||||
class Agent(object):
|
||||
"""an OGP agent"""
|
||||
|
||||
implements(IAgent)
|
||||
|
||||
def __init__(self, agentdomain):
|
||||
"""initialize this agent"""
|
||||
self.agentdomain = agentdomain
|
||||
|
||||
|
||||
|
||||
class PlaceAvatarAdapter(object):
|
||||
"""handles placing an avatar for an agent object"""
|
||||
implements(IPlaceAvatarAdapter)
|
||||
adapts(IAgent)
|
||||
|
||||
def __init__(self, agent):
|
||||
"""initialize this adapter"""
|
||||
self.agent = agent
|
||||
|
||||
# let's retrieve the cap we need
|
||||
self.seed_cap = self.agent.agentdomain.seed_cap # ISeedCapability
|
||||
self.place_avatar_cap = self.seed_cap.get(['place_avatar'])['place_avatar']
|
||||
|
||||
def __call__(self, region):
|
||||
"""initiate the placing process"""
|
||||
region_uri = region.uri
|
||||
payload = {'region_url' : region_uri }
|
||||
result = self.place_avatar_cap(payload)
|
||||
|
||||
return result
|
||||
|
||||
# now we register this adapter so it can be used later:
|
||||
from zope.component import provideAdapter
|
||||
|
||||
# register adapters for the HTML node
|
||||
provideAdapter(PlaceAvatarAdapter)
|
||||
|
||||
|
||||
|
||||
|
||||
43
pyogp/lib/base/agentdomain.py
Normal file
43
pyogp/lib/base/agentdomain.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from agent import Agent
|
||||
from interfaces import ICredentialSerializer
|
||||
from caps import SeedCapability
|
||||
import urllib2
|
||||
|
||||
|
||||
# URL Opener for the agent domain login
|
||||
#
|
||||
class RedirectHandler(urllib2.HTTPRedirectHandler):
|
||||
|
||||
def http_error_302(self, req, fp, code, msg, headers):
|
||||
#ignore the redirect, grabbing the seed cap url from the headers
|
||||
# TODO: add logging and error handling
|
||||
return headers['location']
|
||||
|
||||
|
||||
# post to auth.cgi, ignoring the built in redirect
|
||||
AgentDomainLoginOpener = urllib2.build_opener(RedirectHandler())
|
||||
|
||||
|
||||
class AgentDomain(object):
|
||||
"""an agent domain endpoint"""
|
||||
|
||||
def __init__(self,uri):
|
||||
"""initialize the agent domain endpoint"""
|
||||
self.uri = uri
|
||||
|
||||
def login(self, credentials):
|
||||
"""login to the agent domain and return an agent object"""
|
||||
serializer = ICredentialSerializer(credentials) # convert to string via adapter
|
||||
payload = serializer.serialize()
|
||||
headers = serializer.headers
|
||||
print payload, headers
|
||||
|
||||
# now create the request. We assume for now that self.uri is the login uri
|
||||
# TODO: make this pluggable so we can use other transports like eventlet in the future
|
||||
# TODO: add logging and error handling
|
||||
request = urllib2.Request(self.uri,payload,headers)
|
||||
seed_cap_url = AgentDomainLoginOpener.open(request)
|
||||
self.seed_cap = SeedCapability('seed_cap', seed_cap_url)
|
||||
return Agent(self)
|
||||
|
||||
|
||||
58
pyogp/lib/base/caps.py
Normal file
58
pyogp/lib/base/caps.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from zope.interface import implements
|
||||
|
||||
import urllib2
|
||||
from indra.base import llsd
|
||||
|
||||
from interfaces import ICapability, ISeedCapability
|
||||
|
||||
class Capability(object):
|
||||
"""models a capability"""
|
||||
|
||||
implements(ICapability)
|
||||
|
||||
def __init__(self, name, private_url):
|
||||
"""initialize the capability"""
|
||||
|
||||
self.name = name
|
||||
self.private_url = private_url
|
||||
|
||||
def __call__(self,payload,custom_headers={}):
|
||||
"""call this capability, return the parsed result"""
|
||||
|
||||
headers = {"Content-type" : "application/llsd+xml"}
|
||||
headers.update(custom_headers)
|
||||
llsd_payload = llsd.format_xml(payload)
|
||||
|
||||
# TODO: better errorhandling with own exceptions
|
||||
try:
|
||||
request = urllib2.Request(self.private_url, llsd_payload, headers)
|
||||
result = urllib2.urlopen(request).read()
|
||||
except urllib2.HTTPError, e:
|
||||
print "** failure while calling cap:",
|
||||
print e.read()
|
||||
raise
|
||||
return llsd.parse(result)
|
||||
|
||||
|
||||
class SeedCapability(Capability):
|
||||
"""a seed capability which is able to retrieve other capabilities"""
|
||||
|
||||
implements(ISeedCapability)
|
||||
|
||||
def get(self, names=[]):
|
||||
"""if this is a seed cap we can retrieve other caps here"""
|
||||
payload = {'caps':names}
|
||||
parsed_result = self(payload)['caps']
|
||||
|
||||
caps = {}
|
||||
for name in names:
|
||||
# TODO: some caps might be seed caps, how do we know?
|
||||
caps[name]=Capability(name, parsed_result[name])
|
||||
|
||||
return caps
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
54
pyogp/lib/base/credentials.py
Normal file
54
pyogp/lib/base/credentials.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from zope.interface import implements
|
||||
from zope.component import adapts
|
||||
|
||||
from indra.base import llsd
|
||||
|
||||
|
||||
from interfaces import IPlainPasswordCredential, ICredentialSerializer
|
||||
|
||||
class PlainPasswordCredential(object):
|
||||
"""a plain password credential"""
|
||||
|
||||
implements(IPlainPasswordCredential)
|
||||
|
||||
def __init__(self, firstname, lastname, password):
|
||||
"""initialize this credential"""
|
||||
self.firstname = firstname
|
||||
self.lastname = lastname
|
||||
self.password = password
|
||||
|
||||
|
||||
# an adapter to serialize this to LLSD
|
||||
|
||||
class PlainPasswordLLSDSerializer(object):
|
||||
"""converts a plain password credential to LLSD"""
|
||||
|
||||
implements(ICredentialSerializer)
|
||||
adapts(IPlainPasswordCredential)
|
||||
|
||||
def __init__(self, context):
|
||||
"""initialize this adapter by storing the context (the credential)"""
|
||||
self.context = context
|
||||
|
||||
def serialize(self):
|
||||
"""return the credential as a string"""
|
||||
|
||||
loginparams={
|
||||
'password' : self.context.password,
|
||||
'lastname' : self.context.lastname,
|
||||
'firstname' : self.context.firstname
|
||||
}
|
||||
|
||||
llsdlist = llsd.format_xml(loginparams)
|
||||
return llsdlist
|
||||
|
||||
@property
|
||||
def headers(self):
|
||||
"""return HTTP headers needed here"""
|
||||
return {"Content-type" : "application/llsd+xml"}
|
||||
|
||||
# now we register this adapter so it can be used later:
|
||||
from zope.component import provideAdapter
|
||||
|
||||
# register adapters for the HTML node
|
||||
provideAdapter(PlainPasswordLLSDSerializer)
|
||||
46
pyogp/lib/base/example.py
Normal file
46
pyogp/lib/base/example.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from pyogp.lib.base.credentials import PlainPasswordCredential
|
||||
from pyogp.lib.base.agentdomain import AgentDomain
|
||||
from pyogp.lib.base.regiondomain import Region
|
||||
|
||||
from pyogp.lib.base.interfaces import IPlaceAvatarAdapter
|
||||
|
||||
import getpass, sys
|
||||
from optparse import OptionParser
|
||||
|
||||
|
||||
class ExampleLogin(object):
|
||||
|
||||
def login(self):
|
||||
parser = OptionParser()
|
||||
|
||||
parser.add_option("-a", "--agentdomain", dest="loginuri", default="https://login1.aditi.lindenlab.com/cgi-bin/auth.cgi",
|
||||
help="URI of Agent Domain")
|
||||
parser.add_option("-r", "--region", dest="regionuri", default="http://sim1.vaak.lindenlab.com:13000",
|
||||
help="URI of Region to connect to")
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
firstname = args[0]
|
||||
lastname = args[1]
|
||||
password = getpass.getpass()
|
||||
|
||||
credentials = PlainPasswordCredential(firstname, lastname, password)
|
||||
|
||||
agentdomain = AgentDomain(options.loginuri)
|
||||
agent = agentdomain.login(credentials)
|
||||
|
||||
print "logged in, we now have an agent: ", agent
|
||||
|
||||
place = IPlaceAvatarAdapter(agent)
|
||||
region = Region(options.regionuri)
|
||||
|
||||
print "now we try to place the avatar on a region"
|
||||
avatar = place(region)
|
||||
|
||||
#avatar.establish_presence()
|
||||
#
|
||||
def main():
|
||||
return ExampleLogin().login()
|
||||
|
||||
if __name__=="__main__":
|
||||
main()
|
||||
69
pyogp/lib/base/interfaces.py
Normal file
69
pyogp/lib/base/interfaces.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from zope.interface import Interface, Attribute
|
||||
|
||||
class ICredential(Interface):
|
||||
"""base interface for credentials"""
|
||||
|
||||
class IPlainPasswordCredential(ICredential):
|
||||
"""a plain password credential"""
|
||||
|
||||
firstname = Attribute("""first name of avatar""")
|
||||
lastname = Attribute("""last name of avatar""")
|
||||
password = Attribute("""plain password""")
|
||||
|
||||
class ICredentialSerializer(Interface):
|
||||
"""converts a credential to a serialized format for sending it over the network"""
|
||||
|
||||
def serialize():
|
||||
"""return a serialized string"""
|
||||
|
||||
def headers():
|
||||
"""return headers eventually needed for sending it out"""
|
||||
|
||||
|
||||
class IAgent(Interface):
|
||||
"""models an agent"""
|
||||
|
||||
agentdomain = Attribute("""the agent domain endpoint""")
|
||||
|
||||
|
||||
class IRegion(Interface):
|
||||
"""a region endpoint"""
|
||||
|
||||
def place_avatar(agent):
|
||||
"""place an avatar on this region, returns IAvatar"""
|
||||
|
||||
class IAvatar(Interface):
|
||||
"""an OGP avatar (region representation of an agent)"""
|
||||
|
||||
def establish_presence():
|
||||
"""for now it will do a loop to establish a presence on a region"""
|
||||
|
||||
class IPlaceAvatarAdapter(Interface):
|
||||
"""adapts an agents to a method which can place an avatar on a region"""
|
||||
|
||||
def __call__(region):
|
||||
"""takes a region objects and tries to place the agent there as an avatar
|
||||
|
||||
return an IAvatar"""
|
||||
|
||||
class ICapability(Interface):
|
||||
"""a capability"""
|
||||
|
||||
name = Attribute('''name of the capability''')
|
||||
private_url = Attribute('''private url of this capability''')
|
||||
|
||||
def __call__(payload):
|
||||
"""call this capability
|
||||
|
||||
payload -- the payload as python dictionary
|
||||
returns a python dictionary with the results
|
||||
|
||||
"""
|
||||
|
||||
class ISeedCapability(ICapability):
|
||||
"""a seed capability which is able to retrieve further capabilities"""
|
||||
|
||||
def get(names=[]):
|
||||
"""retrieve the given set of named capabilities
|
||||
|
||||
returns a dict of ICapabilty objects keyed by their name"""
|
||||
14
pyogp/lib/base/regiondomain.py
Normal file
14
pyogp/lib/base/regiondomain.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from zope.interface import implements
|
||||
|
||||
from interfaces import IRegion
|
||||
|
||||
class Region(object):
|
||||
"""models a region endpoint"""
|
||||
|
||||
implements(IRegion)
|
||||
|
||||
def __init__(self, uri):
|
||||
"""initialize the region with the region uri"""
|
||||
self.uri = uri
|
||||
|
||||
|
||||
30
pyogp/lib/base/tests/login.txt
Normal file
30
pyogp/lib/base/tests/login.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
Login
|
||||
=====
|
||||
|
||||
>>> from pyogp.lib.base.credentials import PlainPasswordCredential
|
||||
>>> from pyogp.lib.base.agentdomain import AgentDomain
|
||||
>>> from pyogp.lib.base.regiondomain import Region
|
||||
|
||||
First we create some credentials:
|
||||
>>> credentials = PlainPasswordCredential('Firstname', 'Lastname', 'password')
|
||||
|
||||
Then we need some agent domain to connect to. This might automatically retrieve some XRDS file to get the actual login endpoint:
|
||||
>>> agentdomain = AgentDomain('http://agent.domain')
|
||||
|
||||
Now we can use both to get an agent object (which transparently handles capabilities etc.):
|
||||
>>> agent = AgentDomain.login(credentials)
|
||||
|
||||
The next step is to use this agent to actually place the avatar somewhere. We therefor need a region:
|
||||
>>> region = Region('http://region.uri')
|
||||
|
||||
Note that we can also first retrieve a RegionDomain object and ask this for possible regions and a map etc.
|
||||
|
||||
|
||||
So let's place the agent in form of an avatar there (or try it at least):
|
||||
>>> avatar = region.place_avatar(agent)
|
||||
|
||||
Now we should establish a presence there:
|
||||
|
||||
avatar.establish_presence()
|
||||
|
||||
As this is an infinite loop the question is how this could be handled. Maybe in a different thread?
|
||||
3
setup.cfg
Normal file
3
setup.cfg
Normal file
@@ -0,0 +1,3 @@
|
||||
[egg_info]
|
||||
tag_build = dev
|
||||
tag_svn_revision = true
|
||||
37
setup.py
Normal file
37
setup.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from setuptools import setup, find_packages
|
||||
import os
|
||||
|
||||
version = '1.0'
|
||||
|
||||
setup(name='pyogp.lib.base',
|
||||
version=version,
|
||||
description="basic pyogp library package",
|
||||
long_description=open("README.txt").read() + "\n" +
|
||||
open(os.path.join("docs", "HISTORY.txt")).read(),
|
||||
# Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
|
||||
classifiers=[
|
||||
"Programming Language :: Python",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
],
|
||||
keywords='pyogp login awg virtualworlds',
|
||||
author='Christian Scholz',
|
||||
author_email='mrtopf@gmail.com',
|
||||
url='',
|
||||
license='GPL',
|
||||
packages=find_packages(exclude=['ez_setup']),
|
||||
namespace_packages=['pyogp', 'pyogp.lib'],
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
install_requires=[
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
'zope.component'
|
||||
# -*- Extra requirements: -*-
|
||||
],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'login = pyogp.lib.base.example:main',
|
||||
],
|
||||
},
|
||||
|
||||
)
|
||||
1
zopeskel.txt
Normal file
1
zopeskel.txt
Normal file
@@ -0,0 +1 @@
|
||||
nested_namespace
|
||||
Reference in New Issue
Block a user