# standard python libs from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG import re # related import uuid import struct from indra.base import llsd # pyogp from pyogp.lib.base.settings import Settings from pyogp.lib.base.message.packets import * from pyogp.lib.base.datatypes import * from pyogp.lib.base.exc import * # pyogp utilities from pyogp.lib.base.utilities.enums import ImprovedIMDialogue # initialize logging logger = getLogger('pyogp.lib.base.inventory') log = logger.log # ToDo: handle library inventory properly. right now, it's treated as regular inv. store it in self.library_folders class Inventory(object): """ is an inventory container Initialize the event queue client class >>> inventory = Inventory() Sample implementations: agent.py Tests: tests/test_inventory.py """ def __init__(self, agent = None, settings = None): """ set up the inventory manager """ # allow the settings to be passed in # otherwise, grab the defaults if settings != None: self.settings = settings else: from pyogp.lib.base.settings import Settings self.settings = Settings() self.agent = agent # For now, store the inventory contents in a list # of folders with it's contents list containing it's inventory items # Ditto the library self.folders = [] self.library_folders = [] # the root inventory and library root folder are special cases self.inventory_root = None self.library_root = None if self.settings.LOG_VERBOSE: log(INFO, "Initializing inventory storage") def enable_callbacks(self): """ enable monitors for certain inventory related packet events """ onInventoryDescendents_received = self.agent.region.packet_handler._register('InventoryDescendents') onInventoryDescendents_received.subscribe(self.onInventoryDescendents) onFetchInventoryReply_received = self.agent.region.packet_handler._register('FetchInventoryReply') onFetchInventoryReply_received.subscribe(self.onFetchInventoryReply) onBulkUpdateInventory_received = self.agent.region.packet_handler._register('BulkUpdateInventory') onBulkUpdateInventory_received.subscribe(self.onBulkUpdateInventory) def onInventoryDescendents(self, packet): raise NotImplemented("onInventoryDescendents") def onFetchInventoryReply(self, packet): raise NotImplemented("onFetchInventoryReply") def onBulkUpdateInventory(self, packet): """ handle the inventory data being delivered in the BullkUpdateInventory packet """ for FolderData_block in packet.message_data.blocks['FolderData']: _FolderID = FolderData_block.get_variable('FolderID').data _ParentID = FolderData_block.get_variable('ParentID').data _Type = FolderData_block.get_variable('Type').data _Name = FolderData_block.get_variable('Name').data folder = InventoryFolder( _Name, _FolderID, _ParentID, None, _Type) self._store_inventory_folder(folder) for ItemData_block in packet.message_data.blocks['ItemData']: # what is CallbackID??? not doing anything with it. _ItemID = ItemData_block.get_variable('ItemID').data _FolderID = ItemData_block.get_variable('FolderID').data _CreatorID = ItemData_block.get_variable('CreatorID').data _OwnerID = ItemData_block.get_variable('OwnerID').data _GroupID = ItemData_block.get_variable('GroupID').data _BaseMask = ItemData_block.get_variable('BaseMask').data _OwnerMask = ItemData_block.get_variable('OwnerMask').data _GroupMask = ItemData_block.get_variable('GroupMask').data _EveryoneMask = ItemData_block.get_variable('EveryoneMask').data _NextOwnerMask = ItemData_block.get_variable('NextOwnerMask').data _GroupOwned = ItemData_block.get_variable('GroupOwned').data _AssetID = ItemData_block.get_variable('AssetID').data _Type = ItemData_block.get_variable('Type').data _InvType = ItemData_block.get_variable('InvType').data _Flags = ItemData_block.get_variable('Flags').data _SaleType = ItemData_block.get_variable('SaleType').data _SalePrice = ItemData_block.get_variable('SalePrice').data _Name = ItemData_block.get_variable('Name').data _Description = ItemData_block.get_variable('Description').data _CreationDate = ItemData_block.get_variable('CreationDate').data _CRC = ItemData_block.get_variable('CRC').data inventory_item = InventoryItem(_ItemID, _FolderID, _CreatorID, _OwnerID, _GroupID, _BaseMask, _OwnerMask, _GroupMask, _EveryoneMask, _NextOwnerMask, _GroupOwned, _AssetID, _Type, _InvType, _Flags, _SaleType, _SalePrice, _Name, _Description, _CreationDate, _CRC) self._store_inventory_item(inventory_item) def _parse_folders_from_login_response(self): """ the login response may contain inventory information, append data to our folders list """ if self.settings.LOG_VERBOSE: log(DEBUG, 'Parsing the login response for inventory folders') if self.agent.login_response.has_key('inventory-skeleton'): [self._store_inventory_folder(folder, 'inventory') for folder in self.agent.login_response['inventory-skeleton']] if self.agent.login_response.has_key('inventory-skel-lib'): [self._store_inventory_folder(folder, 'library') for folder in self.agent.login_response['inventory-skel-lib']] def _store_inventory_item(self, inventory_item, destination = 'inventory'): """ inventory items comes from packets, or from caps """ if destination == 'inventory': container = self.folders elif destination == 'library': container = self.library_folders else: log(WARNING, 'Not storing folder data, as it\'s not bound for a valid destination.') return # replace an existing list member, else, append # for now, we are assuming the parent folder exists. # if it doesn't we'll know via traceback index = [container.index(folder) for folder in container if str(folder.FolderID) == str(inventory_item.FolderID)] # did not find a folder for this item # this is probably a case we are not handling correctly if len(index) == 0: if self.settings.LOG_VERBOSE: log(DEBUG, 'Did not find parent folder %s for Inventory Item %s: %s' % (inventory_item.ItemID, inventory_item.FolderID, inventory_item.Name)) return try: inventory_index = [container[index].index(item) for item in container[index].inventory] container[index[0]].inventory[inventory_index[0]] = inventory_item if self.settings.LOG_VERBOSE: log(DEBUG, 'Replacing a stored inventory item: %s for agent \'%s\'' % (inventory_item.ItemID, self.agent.agent_id)) except: container[index[0]].inventory.append(inventory_item) if self.settings.LOG_VERBOSE: log(DEBUG, 'Storing a new inventory item: %s in agent \'%s\'' % (inventory_item.ItemID, self.agent.agent_id)) def search_inventory(self, folder_list = [], item_id = None, name = None, match_list = []): """ search through all inventory folders for an id(uuid) or Name, and return a list of matching InventoryItems or InventoryFolders recursive search, based on folder_id. if no folder id is specified look through everything in self.folders This does not request inventory from the grid. It could, were we to go about enabling this... """ # search our inventory storage if we aren't told what to look in if folder_list == []: search_folders = self.folders else: search_folders = folder_list if item_id != None: item_id = UUID(str(item_id)) for item in search_folders: if isinstance(item, InventoryItem): if str(item.ItemID) == str(item_id): match_list.append(item) elif isinstance(item, InventoryFolder): matches = self.search_inventory_folder(item.FolderID, _id = item_id) match_list.extend(matches) return match_list elif name != None: for item in search_folders: pattern = re.compile(name) if isinstance(item, InventoryItem): if pattern.match(item.Name): match_list.append(item) elif isinstance(item, InventoryFolder): matches = self.search_inventory_folder(item.FolderID, name = name.strip()) match_list.extend(matches) return match_list else: # return an empty list return [] def search_inventory_folder(self, folder_id, _id = None, name = None): """ search an inventory folder for _id or name return a list of matches """ match_list = [] search_folder = [folder for folder in self.folders if str(folder.FolderID) == str(folder_id)][0] for item in search_folder.inventory: if _id != None: if isinstance(item, InventoryItem): if str(item.ItemID) == str(_id): match_list.append(item) elif isinstance(item, InventoryFolder): if item.FolderID== _id: match_list.append(item) elif name != None: pattern = re.compile(name) if pattern.match(item.Name): match_list.append(item) return match_list def _store_inventory_folder(self, folder_data, destination = 'inventory'): """ inventory folder data comes from either packets or login, or from caps. destination is either 'inventory' or 'library' """ if destination == 'inventory': container = self.folders elif destination == 'library': container = self.library_folders else: log(WARNING, 'Not storing folder data, as it\'s not bound for a valid destination.') return # if it's a dict, we are parsing login response data or a caps response # transform to an InventoryFolder() instance and store it if we havne't already stored it, otherwise, update the params if type(folder_data) == dict: if folder_data.has_key('category_id'): # this is from caps folder = InventoryFolder(folder_data['name'], folder_data['category_id'], folder_data['parent_id'], folder_data['version'], folder_data['type_default'], folder_data['agent_id']) else: # this is from login inv skeleton folder = InventoryFolder(folder_data['name'], folder_data['folder_id'], folder_data['parent_id'], folder_data['version'], folder_data['type_default']) elif isinstance(folder_data, InventoryFolder): folder = folder_data # see if we have already stored this folder index = [container.index(target_folder) for target_folder in container if str(folder.FolderID) == str(target_folder.FolderID)] # skip storing folders we already know about if len(index) == 1: # we may be receiving an update to the folder attributes, if so, save off the contents, swap in the new representation, then append the contents if self.settings.LOG_VERBOSE: log(DEBUG, 'Replacing a stored inventory folder: %s for agent \'%s\'' % (folder.FolderID, self.agent.agent_id)) contents = container[index[0]].inventory container[index[0]] = folder container[index[0]].inventory = contents else: if self.settings.LOG_VERBOSE: log(DEBUG, "Storing inventory folder %s" % (folder.Name)) if folder_data['parent_id'] == '00000000-0000-0000-0000-000000000000' and folder_data['name'] == 'My Inventory': self.inventory_root = folder elif folder_data['parent_id'] == '00000000-0000-0000-0000-000000000000' and folder_data['name'] == 'Library': self.library_root = folder container.append(folder) def display_folder_contents(self, folder_id = None): """ returns a list of the local representation of a folder's contents """ if folder_id != None: return [member for member in self.folders if str(member.ParentID) == str(folder_id)] # the name case is not handled at this time ''' elif name != None: folder_ids = [member.folder_id for member in self.contents if member.name == name] return [member for member in self.contents if member.name == name] ''' def request_known_folder_contents(self, folder_id = None): """ send requests to the server for contents of all known folders """ [self.inventory._request_folder_contents(folder.FolderID) for folder in self.inventory.folders if str(folder.ParentID) == str(self.inventory.inventory_root.FolderID)] def _request_folder_contents(self, folder_id = None, source = 'inventory'): """ send a request to the server for folder contents, wraps sendFetchInventoryDescendentsPacket """ if source == 'inventory': self.sendFetchInventoryDescendentsRequest(folder_id = folder_id) elif source == 'library': self.sendFetchLibDescendentsRequest(folder_id = folder_id) def request_inventory_by_id(self, id_list = None): """ ask for inventory data by id via a list """ if id_list != None: packet = FetchInventoryPacket() # AgentData block packet.AgentData['AgentID'] = self.agent.agent_id # MVT_LLUUID packet.AgentData['SessionID'] = self.agent.session_id # MVT_LLUUID for inventory_id in id_list: InventoryData = {} InventoryData['OwnerID'] = self.agent.agent_id # MVT_LLUUID InventoryData['ItemID'] = inventory_id # MVT_LLUUID packet.InventoryDataBlocks.append(InventoryData) # enqueue the message self.region.enqueue_message(packet()) def give_inventory(self, ItemID = None, agent_id = None): """ offers another agent the specified inventory item searches the local inventory for the specified ItemID given a match, sends an ImprovedInventoryMessage packet to the specified AgentID """ if not (ItemID or agent_id): log(WARNING, "ItemID and agent_id are required in Inventory().give_inventory()") return item_id = self.search_inventory(item_id = ItemID) if item_id == []: log(WARNING, "ItemID %s not found in inventory of %s" % (ItemID, self.agent.Name())) return elif len(item_id) > 1: log(WARNING, "Multiple matches in inventory for ItemID %s, using the first one in Inventory().give_inventory()" % (ItemID)) inv_item = item_id[0] inv_item.give(self.agent, agent_id) def handle_inventory_offer(self, packet): """ parses and handles an incoming inventory offer """ FromAgentID = packet.message_data.blocks['AgentData'][0].get_variable('AgentID').data FromAgentName = packet.message_data.blocks['MessageBlock'][0].get_variable('FromAgentName').data InventoryName = packet.message_data.blocks['MessageBlock'][0].get_variable('Message').data ID = packet.message_data.blocks['MessageBlock'][0].get_variable('ID').data Message = packet.message_data.blocks['MessageBlock'][0].get_variable('Message').data ToAgentID = packet.message_data.blocks['MessageBlock'][0].get_variable('ToAgentID').data BinaryBucket = packet.message_data.blocks['MessageBlock'][0].get_variable('BinaryBucket').data # parse the binary bucket AssetType = struct.unpack(">b", BinaryBucket[0:1])[0] ItemID = UUID(BinaryBucket[1:17]) if str(FromAgentID) == str(self.agent.agent_id): log(INFO, "%s offered inventory to %s" % (self.agent.Name(), ToAgentID)) else: log(INFO, "%s received an inventory offer from %s for %s." % (self.agent.Name(), FromAgentName, InventoryName)) if self.settings.LOG_VERBOSE: log(DEBUG, "Inventory offer data. \n\tFromAgentID: %s \n\tFromAgentName: %s \n\tInventoryName: %s \n\tID: %s \n\tAssetType: %s \n\tItemID: %s \n\tMessage: %s" % (FromAgentID, FromAgentName, InventoryName, ID, AssetType, ItemID, Message)) if self.agent.settings.ACCEPT_INVENTORY_OFFERS: accept = True else: accept = False self.confirm_inventory_offer(FromAgentID, FromAgentName, InventoryName, ID, AssetType, ItemID, accept) def confirm_inventory_offer(self, FromAgentID, FromAgentName, InventoryName, ID, AssetType, ItemID, accept): """ sends an ImprovedInstantMessage packet accepting or declining an inventory offer """ if accept: accept_key = ImprovedIMDialogue.InventoryAccepted log(INFO, "Sending an inventory accepted message to %s for item %s(%s)" % (FromAgentName, InventoryName, ID)) else: accept_key = ImprovedIMDialogue.InventoryDeclined log(INFO, "Sending an inventory declined message to %s for item %s(%s)" % (FromAgentName, InventoryName, ID)) self.agent.send_ImprovedInstantMessage(self.agent.agent_id, self.agent.session_id, 0, FromAgentID, 0, UUID(), self.agent.Position, 0, accept_key, ID, 0, self.agent.Name(), '', '') def sendFetchInventoryDescendentsRequest(self, folder_id = None): """ send a request to the grid for folder contents """ raise NotImplemented("sendFetchInventoryDescendentsRequest") def sendFetchInventoryRequest(self, folder_id = None): """ send a request to the grid for inventory items in folder """ raise NotImplemented("sendFetchInventoryRequest") def sendFetchLibRequest(self, item_id = None): """ send a request to the grid for library items """ raise NotImplemented("sendFetchLibRequest") def sendFetchLibDescendentsRequest(self, item_id = None): """ send a request to the grid for library folder contents """ raise NotImplemented("sendFetchDescendentsRequest") class AIS(Inventory): """ AIS specific inventory manager """ def __init__(self, agent, capabilities, settings = None): super(AIS, self).__init__(agent, settings) self.capabilities = capabilities def sendFetchInventoryRequest(self, item_ids = []): """ send a request to the grid for inventory item attributes """ if len(item_ids) == 0: log(WARNING, "sendFetchInventoryRequest requires > 0 item_ids, 0 passed in") return elif type(item_ids) != list: log(WARNING, "sendFetchInventoryRequest requires a list of item_ids, %s passed in" % (type(item_ids))) return cap = self.capabilities['FetchInventory'] post_body = {'items': [{'item_id': str(item_id)} for item_id in item_ids]} custom_headers={'Accept' : 'application/llsd+xml'} try: result = cap.POST(post_body, custom_headers) except ResourceError, error: log(ERROR, error) return except ResourceNotFound, error: log(ERROR, "404 calling: %s" % (error)) return for item in response['folders']: self._store_caps_items(item, folder_id) def sendFetchLibRequest(self, folder_id, item_ids = []): """ send a request to the grid for library item attributes """ if len(item_ids) == 0: log(WARNING, "sendFetchLibRequest requires > 0 item_ids, 0 passed in") return elif type(item_ids) != list: log(WARNING, "sendFetchLibRequest a list of item_ids, %s passed in" % (type(item_ids))) return cap = self.capabilities['FetchLib'] post_body = {'items': [{'item_id': str(item_id)} for item_id in item_ids]} custom_headers={'Accept' : 'application/llsd+xml'} try: result = cap.POST(post_body, custom_headers) except ResourceError, error: log(ERROR, error) return except ResourceNotFound, error: log(ERROR, "404 calling: %s" % (error)) return for item in response['folders']: self._store_caps_items(item, folder_id, 'library') def sendFetchInventoryDescendentsRequest(self, folder_id): """ send a request to the server for inventory folder contents """ cap = self.capabilities['WebFetchInventoryDescendents'] post_body = {'folders': [{'folder_id': str(folder_id), 'owner_id': str(self.agent.agent_id)}]} custom_headers={'Accept' : 'application/llsd+xml'} try: response = cap.POST(post_body, custom_headers) except ResourceError, error: log(ERROR, error) return except ResourceNotFound, error: log(ERROR, "404 calling: %s" % (error)) return ''' Response shape {'folders':[ {'category': CATEGORY_SHAPE, 'categories': [CATEGORY_SHAPE,], 'items': [ITEM_SHAPE,] }]} ''' for member in response['folders']: if member.has_key('category'): self._store_inventory_folder(member['category']) if member.has_key('categories'): [self._store_inventory_folder(folder_info) for folder_info in member['categories']] # pass a tuple of (item_info, category_id) if member.has_key('items'): self._store_caps_items(member['items'], member['category']['category_id']) def _store_caps_items(self, items, folder_id, destination = 'inventory'): """ transform an AIS caps response's inventory items and folder_id to InventoryItems and store it """ for item in items: inventory_item = InventoryItem(item['item_id'], folder_id, item['permissions']['creator_id'], item['permissions']['owner_id'], item['permissions']['group_id'], item['permissions']['base_mask'], item['permissions']['owner_mask'], item['permissions']['group_mask'], item['permissions']['everyone_mask'], item['permissions']['next_owner_mask'], None, item['asset_id'], item['type'], item['inv_type'], item['flags'], item['sale_info']['sale_type'], item['sale_info']['sale_price'], item['name'], item['desc'], item['created_at'], None, item['permissions']['last_owner_id']) self._store_inventory_item(inventory_item, destination) def sendFetchLibDescendentsRequest(self, folder_id): """ send a request to the server for folder contents """ cap = self.capabilities['FetchLibDescendents'] post_body = {'folders': [{'folder_id': str(folder_id), 'owner_id': self.settings.ALEXANDRIA_LINDEN}]} custom_headers={'Accept' : 'application/llsd+xml'} try: response = cap.POST(post_body, custom_headers) except ResourceError, error: log(ERROR, error) return except ResourceNotFound, error: log(ERROR, "404 calling: %s" % (error)) return ''' Response shape {'folders':[ {'category': CATEGORY_SHAPE, 'categories': [CATEGORY_SHAPE,], 'items': [ITEM_SHAPE,] }]} ''' for member in response['folders']: if member.has_key('category'): self._store_inventory_folder(member['category'], 'library') if member.has_key('categories'): [self._store_inventory_folder(folder_info, 'library') for folder_info in member['categories']] if member.has_key('items'): self._store_caps_items(member['items'], member['category']['category_id'], 'library') class UDP_Inventory(Inventory): def __init__(self, agent, settings = None): super(UDP_Inventory, self).__init__(agent, settings) def sendFetchInventoryRequest(self, folder_id = None): """ send a request to the grid for folder attributes """ raise NotImplemented("sendFetchInventoryRequest") def sendFetchLibRequest(self, item_id = None): """ send a request to the grid for library items """ raise NotImplemented("sendFetchInventoryRequest") def sendFetchInventoryDescendentsRequest(self, folder_id = None): """ send a request to the grid for folder contents """ # the name case is not handled at this time packet = FetchInventoryDescendentsPacket() # AgentData block packet.AgentData['AgentID'] = self.agent.agent_id # MVT_LLUUID packet.AgentData['SessionID'] = self.agent.session_id # MVT_LLUUID # InventoryData block packet.InventoryData['FolderID'] = UUID(str(folder_id)) # MVT_LLUUID packet.InventoryData['OwnerID'] = self.agent.agent_id # MVT_LLUUID packet.InventoryData['SortOrder'] = 0 # MVT_S32, 0 = name, 1 = time packet.InventoryData['FetchFolders'] = True # MVT_BOOL packet.InventoryData['FetchItems'] = True # MVT_BOOL self.agent.region.enqueue_message(packet()) def sendFetchLibDescendentsRequest(self, folder_id = None): """ send a request to the grid for library items """ raise NotImplemented("sendFetchInventoryRequest") def onFetchInventoryReply(self, packet): _agent_id = packet.message_data.blocks['AgentData'][0].get_variable('AgentID') for InventoryData_block in packet.message_data.blocks['InventoryData']: _ItemID = InventoryData_block.get_variable('ItemID').data _FolderID = InventoryData_block.get_variable('FolderID').data _CreatorID = InventoryData_block.get_variable('CreatorID').data _OwnerID = InventoryData_block.get_variable('OwnerID').data _GroupID = InventoryData_block.get_variable('GroupID').data _BaseMask = InventoryData_block.get_variable('BaseMask').data _OwnerMask = InventoryData_block.get_variable('OwnerMask').data _GroupMask = InventoryData_block.get_variable('GroupMask').data _EveryoneMask = InventoryData_block.get_variable('EveryoneMask').data _NextOwnerMask = InventoryData_block.get_variable('NextOwnerMask').data _GroupOwned = InventoryData_block.get_variable('GroupOwned').data _AssetID = InventoryData_block.get_variable('AssetID').data _Type = InventoryData_block.get_variable('Type').data _InvType = InventoryData_block.get_variable('InvType').data _Flags = InventoryData_block.get_variable('Flags').data _SaleType = InventoryData_block.get_variable('SaleType').data _SalePrice = InventoryData_block.get_variable('SalePrice').data _Name = InventoryData_block.get_variable('Name').data _Description = InventoryData_block.get_variable('Description').data _CreationDate = InventoryData_block.get_variable('CreationDate').data _CRC = InventoryData_block.get_variable('CRC').data inventory_item = InventoryItem(_ItemID, _FolderID, _CreatorID, _OwnerID, _GroupID, _BaseMask, _OwnerMask, _GroupMask, _EveryoneMask, _NextOwnerMask, _GroupOwned, _AssetID, _Type, _InvType, _Flags, _SaleType, _SalePrice, _Name, _Description, _CreationDate, _CRC) self._store_inventory_item(inventory_item) def onInventoryDescendents(self, packet): if packet.message_data.blocks['AgentData'][0].get_variable('Descendents') > 0: _agent_id = packet.message_data.blocks['AgentData'][0].get_variable('AgentID') _folder_id = packet.message_data.blocks['AgentData'][0].get_variable('FolderID') _owner_id = packet.message_data.blocks['AgentData'][0].get_variable('OwnerID') _version = packet.message_data.blocks['AgentData'][0].get_variable('Version') _descendents = packet.message_data.blocks['AgentData'][0].get_variable('Descendents') # _descendents is not dealt with in any way here if str(packet.message_data.blocks['ItemData'][0].get_variable('ItemID').data) != str(UUID()): for ItemData_block in packet.message_data.blocks['ItemData']: _ItemID = ItemData_block.get_variable('ItemID').data _FolderID = ItemData_block.get_variable('FolderID').data _CreatorID = ItemData_block.get_variable('CreatorID').data _OwnerID = ItemData_block.get_variable('OwnerID').data _GroupID = ItemData_block.get_variable('GroupID').data _BaseMask = ItemData_block.get_variable('BaseMask').data _OwnerMask = ItemData_block.get_variable('OwnerMask').data _GroupMask = ItemData_block.get_variable('GroupMask').data _EveryoneMask = ItemData_block.get_variable('EveryoneMask').data _NextOwnerMask = ItemData_block.get_variable('NextOwnerMask').data _GroupOwned = ItemData_block.get_variable('GroupOwned').data _AssetID = ItemData_block.get_variable('AssetID').data _Type = ItemData_block.get_variable('Type').data _InvType = ItemData_block.get_variable('InvType').data _Flags = ItemData_block.get_variable('Flags').data _SaleType = ItemData_block.get_variable('SaleType').data _SalePrice = ItemData_block.get_variable('SalePrice').data _Name = ItemData_block.get_variable('Name').data _Description = ItemData_block.get_variable('Description').data _CreationDate = ItemData_block.get_variable('CreationDate').data _CRC = ItemData_block.get_variable('CRC').data inventory_item = InventoryItem(_ItemID, _FolderID, _CreatorID, _OwnerID, _GroupID, _BaseMask, _OwnerMask, _GroupMask, _EveryoneMask, _NextOwnerMask, _GroupOwned, _AssetID, _Type, _InvType, _Flags, _SaleType, _SalePrice, _Name, _Description, _CreationDate, _CRC) self._store_inventory_item(inventory_item) if str(packet.message_data.blocks['FolderData'][0].get_variable('FolderID').data) != str(UUID()): for FolderData_block in packet.message_data.blocks['FolderData']: _FolderID = FolderData_block.get_variable('FolderID').data _ParentID = FolderData_block.get_variable('ParentID').data _Type = FolderData_block.get_variable('Type').data _Name = FolderData_block.get_variable('Name').data folder = InventoryFolder( _Name, _FolderID, _ParentID, None, _Type, _agent_id) self._store_inventory_folder(folder) class InventoryFolder(object): """ represents an Inventory folder Initialize the event queue client class >>> inventoryfolder = InventoryFolder() Sample implementations: inventory.py Tests: tests/test_inventory.py """ def __init__(self, Name = None, FolderID = None, ParentID = None, Version = None, Type = None, AgentID = UUID(), Descendents = 0): """ initialize the inventory folder """ self.type = 'InventoryFolder' self.Name = Name self.FolderID = UUID(str(FolderID)) self.ParentID = UUID(str(ParentID)) self.Version = Version self.Type = Type self.Descendents = Descendents self.AgentID = UUID(str(AgentID)) self.inventory = [] def purge_descendents(self, agent): """ removes inventory from this folder, by unlinking the inventory from this parent folder """ packet = PurgeInventoryDescendentsPacket() packet.AgentData['AgentID'] = agent.agent_id packet.AgentData['SessionID'] = agent.session_id packet.InventoryData['FolderID'] = self.FolderID agent.region.enqueue_message(packet) def move(self, agent, parent_id, restamp = False): """ reparents this inventory folder """ packet = MoveInventoryFolderPacket() packet.AgentData['AgentID'] = agent.agent_id packet.AgentData['SessionID'] = agent.session_id packet.AgentData['Stamp'] = restamp # this packet allows for variable instances of the InventoryData block # populate a dict and append to the list packet.InventoryData['FolderID'] = self.FolderID packet.InventoryData['ParentID'] = UUID(str(parent_id)) packet.InventoryDataBlocks.append(packet.InventoryData) agent.region.enqueue_message(packet) def remove(self, agent): """ removes this inventory folder """ packet = RemoveInventoryFolderPacket() packet.AgentData['AgentID'] = agent.agent_id packet.AgentData['SessionID'] = agent.session_id packet.AgentData['Stamp'] = restamp # this packet allows for variable instances of the InventoryData block # populate a dict and append to the list packet.InventoryData['FolderID'] = self.FolderID packet.InventoryDataBlocks.append(packet.InventoryData) agent.region.enqueue_message(packet) class InventoryItem(object): """ represents an Inventory item Initialize the InventoryItem class >>> inventoryitem = InventoryItem() Sample implementations: inventory.py Tests: tests/test_inventory.py """ def __init__(self, ItemID = None, FolderID = None, CreatorID = None, OwnerID = None, GroupID = None, BaseMask = None, OwnerMask = None, GroupMask = None, EveryoneMask = None, NextOwnerMask = 0, GroupOwned = None, AssetID = None, Type = None, InvType = None, Flags = None, SaleType = None, SalePrice = None, Name = None, Description = None, CreationDate = None, CRC = None, LastOwnerID = UUID()): """ initialize the inventory item """ self.type = 'InventoryItem' self.ItemID = UUID(str(ItemID)) # LLUUID self.FolderID = UUID(str(FolderID)) # LLUUID self.CreatorID = UUID(str(CreatorID)) # LLUUID self.OwnerID = UUID(str(OwnerID)) # LLUUID self.GroupID = UUID(str(GroupID)) # LLUUID self.BaseMask = BaseMask # U32 self.OwnerMask = OwnerMask # U32 self.GroupMask = GroupMask # U32 self.EveryoneMask = EveryoneMask # U32 self.NextOwnerMask = NextOwnerMask self.GroupOwned = GroupOwned # Bool self.AssetID = UUID(str(AssetID)) # LLUUID self.Type = Type # S8 self.InvType = InvType # S8 self.Flags = Flags # U32 self.SaleType = SaleType # U8 self.SalePrice = SalePrice # S32 self.Name = Name # Variable 1 self.Description = Description # Variable 1 self.CreationDate = CreationDate # S32 self.CRC = CRC # U32 self.LastOwnerID = UUID(str(LastOwnerID)) def rez_object(self, agent, relative_position = (1, 0, 0)): # self.agent.Position holds where we are. we need to add this Vector3 to the incoming tuple (vector to a vector) location_to_rez_x = agent.Position.X + relative_position[0] location_to_rez_y = agent.Position.Y + relative_position[1] location_to_rez_z = agent.Position.Z + relative_position[2] location_to_rez = (location_to_rez_x, location_to_rez_y, location_to_rez_z) sendRezObject(agent, self, location_to_rez, location_to_rez) def update(agent, name = None, value = None): """ allow arbitraty update to any data in the inventory item accepts a dictionary of key:value pairs which will update the stored inventory items and then send an UpdateInventoryItem packet """ if self.__dict__.has_key(name): self.setattr(self, name, value) sendUpdateInventoryItem(agent, [self]) else: raise DataParsingError('Inventory item attribute update failes, no field named \'%s\'' % (name)) def give(self, agent, to_agent_id): """ sends the target agent an inventory offer of this """ # prep variables and send the message _AgentID = agent.agent_id _SessionID = agent.session_id _FromGroup = False _ToAgentID = to_agent_id _ParentEstateID = 0 _RegionID = agent.region.RegionID _Position = agent.Position _Offline = 0 _Dialog = ImprovedIMDialogue.InventoryOffered _ID = uuid.uuid4() _Timestamp = 0 _FromAgentName = agent.Name() _Message = self.Name _BinaryBucket = struct.pack(">b", self.Type) + self.ItemID.get_bytes() # binary of asset type and id log(INFO, "Sending inventory offer of %s from %s to %s" % (self.Name, agent.agent_id, to_agent_id)) agent.send_ImprovedInstantMessage(_AgentID, _SessionID, _FromGroup, _ToAgentID, _ParentEstateID, _RegionID, _Position, _Offline, _Dialog, _ID, _Timestamp, _FromAgentName, _Message, _BinaryBucket) # ~~~~~~~~~~~~~~~ # Packet Wrappers # ~~~~~~~~~~~~~~~ def sendUpdateInventoryItem(agent, inventory_items = [], ): """ sends an UpdateInventoryItem packet to a region this function expects an already transformed InventoryItem instance """ packet - UpdateInventoryItemPacket() packet.AgentData["AgentID"] = agent.agent_id packet.AgentData["SessionID"] = agent.session_id packet.AgentData["TransactionID"] = UUID() for item in inventory_items: packet.InventoryData['ItemID'] = item.ItemID packet.InventoryData['FolderID'] = item.FolderID packet.InventoryData['CallbackID'] = UUID() packet.InventoryData['CreatorID'] = item.CreatorID packet.InventoryData['OwnerID'] = item.OwnerID packet.InventoryData['GroupID'] = item.GroupID packet.InventoryData['BaseMask'] = item.BaseMask packet.InventoryData['OwnerMask'] = item.OwnerMask packet.InventoryData['GroupMask'] = item.GroupMask packet.InventoryData['EveryoneMask'] = item.EveryoneMask packet.InventoryData['NextOwnerMask'] = item.NextOwnerMask packet.InventoryData['GroupOwned'] = item.GroupOwned packet.InventoryData['TransactionID'] = UUID() packet.InventoryData['Type'] = item.Type packet.InventoryData['InvType'] = item.InvType packet.InventoryData['Flags'] = item.Flags packet.InventoryData['SaleType'] = item.SaleType packet.InventoryData['SalePrice'] = item.SalePrice packet.InventoryData['Name'] = item.Name packet.InventoryData['Description'] = item.Description packet.InventoryData['CreationDate'] = item.CreationDate packet.InventoryData['CRC'] = item.CRC agent.region.enqueue_message(packet()) def sendRezObject(agent, inventory_item, RayStart, RayEnd, FromTaskID = UUID(), BypassRaycast = 1, RayTargetID = UUID(), RayEndIsIntersection = False, RezSelected = False, RemoveItem = False, ItemFlags = 0, GroupMask = 0, EveryoneMask = 0, NextOwnerMask = 0): """ sends a RezObject packet to a region """ packet = RezObjectPacket() packet.AgentData['AgentID'] = agent.agent_id packet.AgentData['SessionID'] = agent.session_id packet.AgentData['GroupID'] = agent.ActiveGroupID packet.RezData['FromTaskID'] = UUID(str(FromTaskID)) packet.RezData['BypassRaycast'] = BypassRaycast # ??? packet.RezData['RayStart'] = RayStart packet.RezData['RayEnd'] = RayEnd packet.RezData['RayTargetID'] = UUID(str(RayTargetID)) packet.RezData['RayEndIsIntersection'] = RayEndIsIntersection packet.RezData['RezSelected'] = RezSelected packet.RezData['RemoveItem'] = RemoveItem packet.RezData['ItemFlags'] = ItemFlags packet.RezData['GroupMask'] = GroupMask packet.RezData['EveryoneMask'] = EveryoneMask packet.RezData['NextOwnerMask'] = inventory_item.NextOwnerMask packet.InventoryData['ItemID'] = inventory_item.ItemID packet.InventoryData['FolderID'] = inventory_item.FolderID packet.InventoryData['CreatorID'] = inventory_item.CreatorID packet.InventoryData['OwnerID'] = inventory_item.OwnerID packet.InventoryData['GroupID'] = inventory_item.GroupID packet.InventoryData['BaseMask'] = inventory_item.BaseMask packet.InventoryData['OwnerMask'] = inventory_item.OwnerMask packet.InventoryData['GroupMask'] = inventory_item.GroupMask packet.InventoryData['EveryoneMask'] = inventory_item.EveryoneMask packet.InventoryData['GroupOwned'] = inventory_item.GroupOwned packet.InventoryData['TransactionID'] = UUID() packet.InventoryData['Type'] = inventory_item.Type packet.InventoryData['InvType'] = inventory_item.InvType packet.InventoryData['Flags'] = inventory_item.Flags packet.InventoryData['SaleType'] = inventory_item.SaleType packet.InventoryData['SalePrice'] = inventory_item.SalePrice packet.InventoryData['Name'] = inventory_item.Name packet.InventoryData['Description'] = inventory_item.Description packet.InventoryData['CreationDate'] = inventory_item.CreationDate packet.InventoryData['CRC'] = inventory_item.CRC packet.InventoryData['NextOwnerMask'] = inventory_item.NextOwnerMask agent.region.enqueue_message(packet()) """ Contributors can be viewed at: http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt $LicenseInfo:firstyear=2008&license=apachev2$ Copyright 2009, Linden Research, Inc. Licensed under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 or in http://svn.secondlife.com/svn/linden/projects/2008/pyogp/LICENSE.txt $/LicenseInfo$ """