def subscribe(context, request): """ Subscribe user to context """ actor = request.actor rest_params = {'object': context, 'verb': 'subscribe'} # Initialize a Activity object from the request newactivity = Activity.from_request(request, rest_params=rest_params) # Check if user is already subscribed subscribed_contexts_hashes = [a['hash'] for a in actor['subscribedTo']] if newactivity['object'].getHash() in subscribed_contexts_hashes: # If user already subscribed, send a 200 code and retrieve the original subscribe activity # post when user was subscribed. This way in th return data we'll have the date of subscription code = 200 activities = MADMaxCollection(request, 'activity') query = {'verb': 'subscribe', 'object.url': newactivity['object']['url'], 'actor.username': actor['username']} newactivity = activities.last(query) # Pick the last one, so we get the last time user subscribed (in cas a unsbuscription occured sometime...) else: actor.addSubscription(context) # If user wasn't created, 201 will show that the subscription has just been added code = 201 newactivity_oid = newactivity.insert() # Insert a subscribe activity newactivity['_id'] = newactivity_oid handler = JSONResourceEntity(request, newactivity.flatten(), status_code=code) return handler.buildResponse()
def like(activity, request): """ Like activity """ if activity.has_like_from(request.actor): code = 200 activities = MADMaxCollection(request, 'activity') query = {'verb': 'like', 'object._id': activity['_id'], 'actor.username': request.actor['username']} newactivity = activities.last(query) # Pick the last one, so we get the last time user liked this activity else: code = 201 # Prepare rest parameters to be merged with post data rest_params = { 'verb': 'like', 'object': { '_id': ObjectId(activity['_id']), 'objectType': activity['objectType'], } } # Initialize a Activity object from the request newactivity = Activity.from_request(request, rest_params=rest_params) newactivity_oid = newactivity.insert() newactivity['_id'] = newactivity_oid activity.add_like_from(request.actor) newactivity['object']['likes'] = activity['likes'] # Return the current likes of the activity newactivity['object']['likesCount'] = activity['likesCount'] # Return the current likes of the activity newactivity['object']['liked'] = activity.has_like_from(request.actor) handler = JSONResourceEntity(request, newactivity.flatten(), status_code=code) return handler.buildResponse()
def removeActivities(self, logical=False): """ Removes all activity posted to a context. If logical is set to True Activities are not actually deleted, only marked as not visible """ activitydb = MADMaxCollection(self.request, self.activity_storage) which_to_delete = {"contexts.{}".format(self.unique.lstrip("_")): self.getIdentifier()} activitydb.remove(which_to_delete, logical=logical)
def getInfo(self): actor = self.flatten() if self.has_field_permission('talkingIn', 'view'): actor.setdefault('talkingIn', []) if actor['talkingIn']: conversation_objectids = [ObjectId(conv['id']) for conv in actor['talkingIn']] conversations_collection = MADMaxCollection(self.request, 'conversations') conversations = conversations_collection.search({'_id': {'$in': conversation_objectids}}) conversations_by_id = {str(conv['_id']): conv for conv in conversations} messages_collection = MADMaxCollection(self.request, 'messages').collection pipeline = [ {"$match": {"contexts.id": {"$in": conversations_by_id.keys()}}}, {"$sort": {"_id": 1}}, {"$group": { "_id": "$contexts.id", "object": {"$last": "$object"}, "published": {"$last": "$published"} }} ] messages = messages_collection.aggregate(pipeline)['result'] def format_message(db_message): message_object = db_message.get('object', {}) message = dict( objectType=message_object.get('objectType', 'note'), content=message_object.get('content', ''), published=db_message.get('published', '') ) if isinstance(message['published'], datetime.datetime): try: message['published'] = message['published'].isoformat() except: message['published'] = message['published'] # Set object urls for media types if message['objectType'] in ['file', 'image']: message['fullURL'] = message_object.get('fullURL', '') if message['objectType'] == 'image': message['thumbURL'] = message_object.get('thumbURL', '') return message messages_by_conversation = {message['_id'][0]: format_message(message) for message in messages} for subscription in actor['talkingIn']: conversation_object = conversations_by_id[subscription['id']] subscription['displayName'] = conversation_object.realDisplayName(self['username']) subscription['lastMessage'] = messages_by_conversation[subscription['id']] subscription['participants'] = conversation_object['participants'] subscription['tags'] = conversation_object['tags'] subscription['messages'] = 0 actor['talkingIn'] = sorted(actor['talkingIn'], reverse=True, key=lambda conv: conv['lastMessage']['published']) return actor
def removeUserSubscriptions(self, users_to_delete=[]): """ Removes all users subscribed to the context, or only specifiyed user if userd_to_delete is provided """ usersdb = MADMaxCollection(self.request, "users") criteria = {"{}.{}".format(self.user_subscription_storage, self.unique.lstrip("_")): self.getIdentifier()} users = usersdb.search(criteria) for user in users: if users_to_delete == [] or user["username"] in users_to_delete: user.removeSubscription(self)
def delete_tokens(self, platform=None): """ Deletes tokens from a user, filtered by platform if any """ tokens = MADMaxCollection(self.request, 'tokens') query = { '_owner': self['_owner'], } if platform is not None: query['platform'] = platform tokens.remove(query)
def joinConversation(conversation, request): """ Join conversation """ actor = request.actor cid = request.matchdict['id'] # Check if user is already subscribed if conversation.subscription: # If user already subscribed, send a 200 code and retrieve the original subscribe activity # post when user was subscribed. This way in th return data we'll have the date of subscription code = 200 activities = MADMaxCollection(request, 'activity') query = {'verb': 'subscribe', 'object.id': cid, 'actor.username': actor['username']} newactivity = activities.last(query) # Pick the last one, so we get the last time user subscribed (in cas a unsbuscription occured sometime...) else: if len(conversation['participants']) == CONVERSATION_PARTICIPANTS_LIMIT: raise Forbidden('This conversation is full, no more of {} participants allowed'.format(CONVERSATION_PARTICIPANTS_LIMIT)) if 'group' not in conversation.get('tags', []): raise Forbidden('This is not a group conversation, so no one else is allowed'.format(CONVERSATION_PARTICIPANTS_LIMIT)) if not request.creator.is_allowed_to_see(actor): raise Forbidden('User {} is not allowed to have a conversation with {}'.format(request.creator['username'], actor['username'])) conversation['participants'].append(actor.flatten(preserve=['displayName', 'objectType', 'username'])) actor.addSubscription(conversation) # If we add anyone to a conversation, we remove the archive tag, no matter how many participants have if 'archive' in conversation.get('tags', []): conversation['tags'].remove('archive') conversation.save() # If user wasn't created, 201 will show that the subscription has just been added code = 201 # Initialize a Activity object from the request rest_params = {'actor': actor, 'verb': 'subscribe', 'object': {'objectType': 'conversation', 'id': cid, 'participants': conversation['participants']} } newactivity = Activity.from_request(request, rest_params=rest_params) newactivity_oid = newactivity.insert() # Insert a subscribe activity newactivity['_id'] = newactivity_oid handler = JSONResourceEntity(request, newactivity.flatten(), status_code=code) return handler.buildResponse()
def ModifyContext(context, request): """ """ urlHash = request.matchdict['urlHash'] contexts = MADMaxCollection(context.db.contexts) maxcontext = contexts.getItemsByurlHash(urlHash) if maxcontext: maxcontext = maxcontext[0] else: raise ObjectNotFound, 'Unknown context: %s' % urlHash properties = maxcontext.getMutablePropertiesFromRequest(request) maxcontext.modifyContext(properties) maxcontext.updateUsersSubscriptions() handler = JSONResourceEntity(maxcontext.flatten()) return handler.buildResponse()
def get_tokens(self, platform=None): """ Returns all the tokens from a user, filtered by platform if any """ tokens = MADMaxCollection(self.request, 'tokens') query = { '_owner': self['_owner'], } if platform is not None: query['platform'] = platform user_tokens = tokens.search(query) result = [] for token in user_tokens: result.append(dict(token=token['token'], platform=token['platform'], username=token['_owner'])) return result
def subscribe(context, request): """ /people/{username}/subscriptions """ # XXX For now only one context can be subscribed at a time actor = request.actor rest_params = {'actor': actor, 'verb': 'subscribe'} # Initialize a Activity object from the request newactivity = Activity() newactivity.fromRequest(request, rest_params=rest_params) #Check if user is already subscribed subscribed_contexts_hashes = [a['urlHash'] for a in actor.subscribedTo['items']] if sha1(newactivity.object['url']).hexdigest() in subscribed_contexts_hashes: # If user already subscribed, send a 200 code and retrieve the original subscribe activity # post when user was susbcribed. This way in th return data we'll have the date of subscription code = 200 activities = MADMaxCollection(context.db.activity) query = {'verb': 'subscribe', 'object.url': newactivity.object['url'], 'actor.username': actor.username} newactivity = activities.search(query)[-1] # Pick the last one, so we get the last time user subscribed (in case a unsbuscription occured sometime...) else: # If user wasn't created, 201 indicates that the subscription has just been added code = 201 newactivity_oid = newactivity.insert() # Insert a subscribe activity newactivity['_id'] = newactivity_oid #Register subscription to the actor contexts = MADMaxCollection(context.db.contexts, query_key='urlHash') scontext = contexts[sha1(newactivity['object']['url']).hexdigest()] actor.addSubscription(scontext) handler = JSONResourceEntity(newactivity.flatten(), status_code=code) return handler.buildResponse()
def __getitem__(self, userid): """ Overrides __getitem__ to avoid fetching the user twice , when user that we're going to request is the same actor on the request. Because the actor on the request won't be in the traversal chain, provide it a parent before returning. If the userid doesn't match the request actor fallback to query database. If the actor doesn't exists, raise a not found. """ userid = userid.lower() if userid == self.request.actor_username: actor = self.request.actor if actor is None: raise UnknownUserError("Object with %s %s not found inside %s" % (self.query_key, userid, self.collection.name)) actor.__parent__ = self return actor return MADMaxCollection.__getitem__(self, userid)
def processTweet(twitter_username, content, tweetID='---'): """ Process inbound tweet """ logger.info("(INFO) Processing tweet %s from @%s with content: %s" % (str(tweetID), twitter_username, content)) conn = pymongo.Connection(mongodb_url) db = conn[mongodb_db_name] users = MADMaxCollection(db.users) contexts = MADMaxCollection(db.contexts) contexts_with_twitter_username = contexts.search({"twitterUsernameId": {"$exists": True}}) readable_follow_list = [users_to_follow.get('twitterUsername', '').lower() for users_to_follow in contexts_with_twitter_username] twitter_username = twitter_username.lower() # If we have a tweet from a followed user if twitter_username in readable_follow_list: # Find the context maxcontext = contexts.search({"twitterUsername": twitter_username}) # Watch for the case when two or more context share twitterUsername for context in maxcontext: url_hash = context.get("urlHash") context_url = context.get("url") # Construct the payload with the activity information newactivity = { "object": { "objectType": "note", "content": content }, "contexts": [ context_url, ], "generator": twitter_generator_name } re = requests.post('%s/admin/contexts/%s/activities' % (max_server_url, url_hash), json.dumps(newactivity), auth=('admin', 'admin'), verify=False) if re.status_code == 201: # 200: Successful tweet from context logger.info("(201) Successfully posted tweet %s from @%s as context %s" % (str(tweetID), twitter_username, context_url)) return "(201) %s tweet from context %s" % (twitter_username, context_url) else: # 500: Error accessing the MAX API logger.info("(404) Discarding tweet %s from @%s : There's no MAX user with that twitter username." % (str(tweetID), twitter_username)) return "(404) No Such Max User" return "Should not see me..." # If we have a tweet from a tracked hashtag # Parse text and determine the second or nth hashtag possible_hastags = findHashtags(content) # Prepare query with normalized possible_hastags query = [dict(twitterHashtag=hashtag.lower()) for hashtag in possible_hastags] if debug_hashtag in possible_hastags: logger.info("%s Debug hashtag detected!" % content) return "%s Debug hashtag detected! %s" % content # Check if twitter_username is a registered for a valid MAX username # if not, discard it maxuser = users.search({"twitterUsername": twitter_username}) if maxuser: maxuser = maxuser[0] else: logger.info("(404) Discarding tweet %s from @%s : There's no MAX user with that twitter username." % (str(tweetID), twitter_username)) return "(404) %s: No such MAX user." % twitter_username # Check if hashtag is registered for a valid MAX context # if not, discard it successful_tweets = 0 maxcontext = contexts.search({"$or": query}) if maxcontext: for context in maxcontext: # Check if MAX username has permission to post to the MAX context # if not, discard it try: can_write = canWriteInContexts(maxuser, [context.url]) except: can_write = False logger.info("(401) Failure posting tweet %s: User %s can't write to %s" % (str(tweetID), maxuser.username, context['url'])) if can_write: # Construct the payload with the activity information newactivity = { "object": { "objectType": "note", "content": content }, "contexts": [ context.url, ], "generator": twitter_generator_name } # Use the restricted REST endpoint for create a new activity in the specified # MAX context in name of the specified MAX username re = requests.post('%s/admin/people/%s/activities' % (max_server_url, maxuser.username), json.dumps(newactivity), auth=('admin', 'admin'), verify=False) if re.status_code == 201: logger.info("(201) Successfully posted tweet %s from @%s as %s on context %s" % (str(tweetID), twitter_username, maxuser['username'], context.url)) successful_tweets += 1 #return "(201) Successful tweet from user %s in context %s" % (maxuser, context.url) else: logger.info("(500) Error accessing the MAX API at %s" % max_server_url) #return "(500) MAX API error" error_message = len(maxcontext) != successful_tweets and " We weren't able to send the tweet to all contexts. See above for more information." or "" logger.info("(INFO) Processed tweet %s to %d of %d possible contexts.%s" % (str(tweetID), successful_tweets, len(maxcontext), error_message)) if error_message: return "(401) Some posts not sent" else: return "(200) All posts sent" else: logger.info("(404) Discarding tweet %s from @%s: There are no MAX context with any of those hashtags" % (str(tweetID), twitter_username)) return "(404) %s: Not such MAX context %s" % (maxuser.username, maxcontext) return "Should not see mee"
def processTweet(twitter_username, content, tweetID='---'): """ Process inbound tweet """ logger.info("(INFO) Processing tweet %s from @%s with content: %s" % (str(tweetID), twitter_username, content)) conn = pymongo.Connection(mongodb_url) db = conn[mongodb_db_name] users = MADMaxCollection(db.users) contexts = MADMaxCollection(db.contexts) contexts_with_twitter_username = contexts.search( {"twitterUsernameId": { "$exists": True }}) readable_follow_list = [ users_to_follow.get('twitterUsername', '').lower() for users_to_follow in contexts_with_twitter_username ] twitter_username = twitter_username.lower() # If we have a tweet from a followed user if twitter_username in readable_follow_list: # Find the context maxcontext = contexts.search({"twitterUsername": twitter_username}) # Watch for the case when two or more context share twitterUsername for context in maxcontext: url_hash = context.get("urlHash") context_url = context.get("url") # Construct the payload with the activity information newactivity = { "object": { "objectType": "note", "content": content }, "contexts": [ context_url, ], "generator": twitter_generator_name } re = requests.post('%s/admin/contexts/%s/activities' % (max_server_url, url_hash), json.dumps(newactivity), auth=('admin', 'admin'), verify=False) if re.status_code == 201: # 200: Successful tweet from context logger.info( "(201) Successfully posted tweet %s from @%s as context %s" % (str(tweetID), twitter_username, context_url)) return "(201) %s tweet from context %s" % (twitter_username, context_url) else: # 500: Error accessing the MAX API logger.info( "(404) Discarding tweet %s from @%s : There's no MAX user with that twitter username." % (str(tweetID), twitter_username)) return "(404) No Such Max User" return "Should not see me..." # If we have a tweet from a tracked hashtag # Parse text and determine the second or nth hashtag possible_hastags = findHashtags(content) # Prepare query with normalized possible_hastags query = [ dict(twitterHashtag=hashtag.lower()) for hashtag in possible_hastags ] if debug_hashtag in possible_hastags: logger.info("%s Debug hashtag detected!" % content) return "%s Debug hashtag detected! %s" % content # Check if twitter_username is a registered for a valid MAX username # if not, discard it maxuser = users.search({"twitterUsername": twitter_username}) if maxuser: maxuser = maxuser[0] else: logger.info( "(404) Discarding tweet %s from @%s : There's no MAX user with that twitter username." % (str(tweetID), twitter_username)) return "(404) %s: No such MAX user." % twitter_username # Check if hashtag is registered for a valid MAX context # if not, discard it successful_tweets = 0 maxcontext = contexts.search({"$or": query}) if maxcontext: for context in maxcontext: # Check if MAX username has permission to post to the MAX context # if not, discard it try: can_write = canWriteInContexts(maxuser, [context.url]) except: can_write = False logger.info( "(401) Failure posting tweet %s: User %s can't write to %s" % (str(tweetID), maxuser.username, context['url'])) if can_write: # Construct the payload with the activity information newactivity = { "object": { "objectType": "note", "content": content }, "contexts": [ context.url, ], "generator": twitter_generator_name } # Use the restricted REST endpoint for create a new activity in the specified # MAX context in name of the specified MAX username re = requests.post('%s/admin/people/%s/activities' % (max_server_url, maxuser.username), json.dumps(newactivity), auth=('admin', 'admin'), verify=False) if re.status_code == 201: logger.info( "(201) Successfully posted tweet %s from @%s as %s on context %s" % (str(tweetID), twitter_username, maxuser['username'], context.url)) successful_tweets += 1 #return "(201) Successful tweet from user %s in context %s" % (maxuser, context.url) else: logger.info("(500) Error accessing the MAX API at %s" % max_server_url) #return "(500) MAX API error" error_message = len( maxcontext ) != successful_tweets and " We weren't able to send the tweet to all contexts. See above for more information." or "" logger.info( "(INFO) Processed tweet %s to %d of %d possible contexts.%s" % (str(tweetID), successful_tweets, len(maxcontext), error_message)) if error_message: return "(401) Some posts not sent" else: return "(200) All posts sent" else: logger.info( "(404) Discarding tweet %s from @%s: There are no MAX context with any of those hashtags" % (str(tweetID), twitter_username)) return "(404) %s: Not such MAX context %s" % (maxuser.username, maxcontext) return "Should not see mee"