def restart(session, topic_id, other_user_id): """ Restart a stopped conversation. """ raise UnauthorizedException() # Disable for now. if session == None or topic_id == None or other_user_id == None: raise InvalidParametersException() sessionHandler.validate(session) user = sessionHandler.get_user(session) try: topic = Topic.objects.get(id=topic_id) except Topic.DoesNotExist: raise NoSuchTopicException() try: other_user = User.objects.get(id=other_user_id) except User.DoesNotExist: raise NoSuchUserException() conversation = conversationHandler.get(user.id, other_user.id, topic.id) conversationHandler.send_to_both_parties( conversation, settings.SMS_TEXT_CONVERSATION_RESTARTING) conversation.stopped = False conversation.save()
def update(session, photo=None, name=None, email=None, website=None, location=None, bio=None): """ Update the user profile for the currently logged-in user. """ raise UnauthorizedException() # Disable for now. # Check that the session is still valid. try: if session == None: raise InvalidParametersException() sessionHandler.validate(session) user = sessionHandler.get_user(session) # Log into the 3taps Identity API. success,response = identity_api.login(username=user.username, pass_hash=user.identity_api_hash) if not success: print "No 3taps identity!" return {} # No 3taps identity -> can't update the profile. session = response # Update the user's profile. changes = {} if photo != None: changes['photo'] = photo if name != None: changes['name'] = name if email != None: changes['email'] = email if website != None: changes['website'] = website if location != None: changes['location'] = location if bio != None: changes['bio'] = bio success,response = identity_api.update(session, changes) if not success: print "Internal server error with Identity API!" print response return {} updated_profile = response['profile'] # Finally, log out again and return the updated profile back to the # caller. identity_api.logout(session) return updated_profile except: traceback.print_exc() raise
def update(session, topic_id, name=None, active=None, hide_username=None): """ Update the details of the given topic. """ raise UnauthorizedException() # Disable for now. if session == None: raise InvalidParametersException() sessionHandler.validate(session) user = sessionHandler.get_user(session) try: topic = Topic.objects.get(id=topic_id) except Topic.DoesNotExist: raise NoSuchTopicException() if topic.user != user: raise UnauthorizedException() # Update the topic name, if we've been asked to do so. if name not in ["", None]: _check_topic_name(name) try: existing_topic = Topic.objects.get(user=user, name=name) except Topic.DoesNotExist: existing_topic = None if existing_topic != None and topic.id != existing_topic.id: raise DuplicateTopicNameException() topic.name = name # Update the 'active' flag, if we've been asked to do so. if active != None: topic.active = (active in [1, "1", "true", "True", True]) # Update the 'hide_username' flag, if we've been asked to do so. if hide_username != None: topic.hide_username = (hide_username in [1, "1", "true", "True", True]) # Finally, save the updated topic, and return a copy of it back to the # caller. topic.save() return topic.to_dict()
def get(session): """ Retrieve the user dictionary for the currently logged-in user. """ raise UnauthorizedException() # Disable for now. logger.debug("in core.api.users.get(" + "session=%s)" % repr(session)) if session == None: raise InvalidParametersException() sessionHandler.validate(session) return sessionHandler.get_user(session).to_dict()
def send(session, topic_id, sender_name=None, message=None): """ Send a message. """ raise UnauthorizedException() # Disable for now. logger.debug("core.api.messages.send(" + 'session=%s, topic_id=%s, sender_name=%s, message=%s)' % (repr(session), repr(topic_id), repr(sender_name), repr(message))) try: if session == None or topic_id == None or message == None: raise InvalidParametersException() sessionHandler.validate(session) sender = sessionHandler.get_user(session) try: topic = Topic.objects.get(id=topic_id) except Topic.DoesNotExist: raise NoSuchTopicException() if not topic.active: raise InactiveTopicException() recipient = topic.user phone_number = recipient.phone_number rateLimiter.ensure_phone_number_below_rate_limit(phone_number) if messageHandler.handle_special_message(sender, recipient, topic, message): msg = None else: msg = messageHandler.send_message(sender, recipient, topic, sender_name, message) rateLimiter.sms_message_sent_to(phone_number) if msg != None: return msg.to_dict() else: return None except RateLimitReachedException: raise except: traceback.print_exc() raise
def list(session): """ Return a list of the topics owned by the currently logged-in user. """ raise UnauthorizedException() # Disable for now. if session == None: raise InvalidParametersException() sessionHandler.validate(session) user = sessionHandler.get_user(session) topic_dicts = [] for topic in Topic.objects.filter(user=user).order_by("id"): topic_dicts.append(topic.to_dict()) return topic_dicts
def verify_user_owns_number(session, phone_number, verification_code): """ Verify that the currently logged-in user owns the given phone number. """ raise UnauthorizedException() # Disable for now. logger.debug("in core.api.users.verify_user_owns_number(" + "session=%s, phone_number=%s, verification_code=%s)" % (repr(session), repr(phone_number), repr(verification_code))) if session == None or phone_number == None or verification_code == None: raise InvalidParametersException() sessionHandler.validate(session) user = sessionHandler.get_user(session) phone_number = utils.format_phone_number(phone_number) try: user_with_phone = User.objects.get(phone_number=phone_number) except User.DoesNotExist: user_with_phone = None if user_with_phone == None: raise NoSuchPhoneNumberException() if verification_code != user_with_phone.verification_code: raise UnauthorizedException() if user == user_with_phone: # The user already has this phone number. Simply verify it. user.verified = True user.save() return # If we get here, the phone number was originally owned by someone else. # Transfer the phone number over to the currently logged-in user. user.phone_number = user_with_phone.phone_number user.verification_code = user_with_phone.verification_code user.verified = True user.save() user_with_phone.phone_number = None user_with_phone.verification_code = None user_with_phone.verified = False user_with_phone.save()
def delete(session): """ Delete the currently logged-in user. Note that we don't currently delete the user from the 3taps Identity API. This may change in the future. """ raise UnauthorizedException() # Disable for now. logger.debug("in core.api.users.delete(" + "session=%s)" % repr(session)) if session == None: raise InvalidParametersException() sessionHandler.validate(session) user = sessionHandler.get_user(session) sessionHandler.delete(session) user.delete()
def create(session, topic_name=None): """ Create a new topic for the currently logged-in user. """ raise UnauthorizedException() # Disable for now. if session == None: raise InvalidParametersException() sessionHandler.validate(session) user = sessionHandler.get_user(session) if topic_name == "": topic_name = None if topic_name == None: # Create a new random name for this topic. while True: topic_name = utils.random_letters_and_digits(min_length=4, max_length=6) if not Topic.objects.filter(user=user,name=topic_name).exists(): break _check_topic_name(topic_name) try: existing_topic = Topic.objects.get(user=user, name=topic_name) except Topic.DoesNotExist: existing_topic = None if existing_topic != None: raise DuplicateTopicNameException() topic = Topic() topic.user = user topic.name = topic_name topic.active = True topic.num_views = 0 topic.hide_username = False topic.hidden_url = hiddenURLs.generate(user) # URL for user, not topic. topic.save() return topic.to_dict()
def get(session): """ Return the user profile for the currently logged-in user. """ raise UnauthorizedException() # Disable for now. # Check that the session is still valid. if session == None: raise InvalidParametersException() sessionHandler.validate(session) user = sessionHandler.get_user(session) # Log in to the 3taps identity API. success,response = identity_api.login(username=user.username, pass_hash=user.identity_api_hash) if not success: print "No 3taps identity!" return {} # No 3taps identity -> return an empty profile. session = response # Get the user's details, and extract the profile. success,response = identity_api.get_user(session) if not success: print "Should never happen!" print response return {} # Should never happen. profile = response['profile'] # Finally, log out from the identity API again, and return the profile back # to the caller. identity_api.logout(session) return profile
def merge(old_session, new_session): """ Merge two users. """ raise UnauthorizedException() # Disable for now. logger.debug("in core.api.users.merge(" + "old_session=%s, new_session=%s)" % (repr(old_session), repr(new_session))) # Check that the session parameters are correct. We require two valid # session IDs, the old one for an ad hoc user, and the new one for a non-ad # hoc user. if old_session == None or new_session == None: raise InvalidParametersException() sessionHandler.validate(old_session) sessionHandler.validate(new_session) old_user = sessionHandler.get_user(old_session) new_user = sessionHandler.get_user(new_session) if not old_user.ad_hoc or new_user.ad_hoc: raise UnauthorizedException() # Build a list of the old user's conversations. old_conversations = [] for conversation in Conversation.objects.filter(user_1=old_user): old_conversations.append(conversation) for conversation in Conversation.objects.filter(user_2=old_user): if conversation.user_1 != old_user: old_conversations.append(conversation) # Build a list of the old user's active conversations. Note that we only # store the ID of the other user and the topic ID for the conversation, as # the Conversation object itself may get deleted as part of the merge # process. We'll need this to send an SMS to the new user telling them # that they've taken the conversation mobile. active_conversations = [] # List of (other_user_id, topic_id) tuples. for conversation in Conversation.objects.filter(user_1=old_user): if not conversation.stopped: active_conversations.append((conversation.user_2.id, conversation.topic.id)) for conversation in Conversation.objects.filter(user_2=old_user): if not conversation.stopped and conversation.user_1 != old_user: active_conversations.append((conversation.user_1.id, conversation.topic.id)) # Build a list of the new user's conversations. new_conversations = [] for conversation in Conversation.objects.filter(user_1=new_user): new_conversations.append(conversation) for conversation in Conversation.objects.filter(user_2=new_user): if conversation.user_1 == new_user: new_conversations.append(conversation) # Change each of the old conversations to belong to the new user. If the # updated conversation is a duplicate of an existing conversation for the # new user, we have to delete the old conversation and update the # associated messages. for old_conversation in old_conversations: if old_conversation.user_1 == old_user: other_old_user = old_conversation.user_2 else: other_old_user = old_conversation.user_1 is_duplicate = False for new_conversation in new_conversations: if new_conversation.user_1 == new_user: other_new_user = new_conversation.user_2 else: other_new_user = new_conversation.user_1 if other_old_user == other_new_user: is_duplicate = True break if is_duplicate: # Update the messages for old_conversation to be part of the # new_conversation instead, and delete the old conversation. Message.objects.filter(conversation=old_conversation).update( conversation=new_conversation) old_conversation.delete() else: # Change the old_conversation to belong to the new_user instead of # the old_user. if old_conversation.user_1 == old_user: old_conversation.user_1 = new_user else: old_conversation.user_2 = new_user old_conversation.save() # Update the old user's messages to refer to the new user. Message.objects.filter(sender=old_user).update(sender=new_user) # Update any topics owned by the old user to now be owned by the new user. # Note that we deliberately avoid overwriting the default and any existing # named topics for the new user. topics = [] for topic in Topic.objects.filter(user=old_user): topics.append(topic) for topic in topics: if topic.default: continue # Don't copy across default topic. if Topic.objects.filter(user=new_user, name=topic.name): continue # Don't overwrite existing named topic. topic.user = new_user topic.save() # Update any SMS channels which sent messages to the old user to now send # messages to the new user. channels = [] for channel in TwilioSMSChannel.objects.filter(sender=old_user): channels.append(channel) for channel in channels: if TwilioSMSChannel.objects.filter(conversation=channel.conversation, recipient=channel.recipient, phone_number=channel.phone_number, sender=new_user).exists(): continue # Don't overwrite existing SMS channel. channel.sender = new_user channel.save() # Update any SMS channels which sent messages from the old user to now send # messages from the new user. channels = [] for channel in TwilioSMSChannel.objects.filter(recipient=old_user): channels.append(channel) for channel in channels: if TwilioSMSChannel.objects.filter(conversation=channel.conversation, sender=channel.sender, phone_number=channel.phone_number, recipient=new_user).exists(): continue # Don't overwrite existing SMS channel. channel.recipient = new_user channel.save() # Finally, if the new user has a verified phone number, send a message to # the new user for each active conversation, telling the user that they # have "taken the conversation mobile." if new_user.verified: for other_user,topic in active_conversations: try: conversation = Conversation.objects.get(user_1=new_user, user_2__id=other_user, topic=topic) except Conversation.DoesNotExist: conversation = Conversation.objects.get(user_1__id=other_user, user_2=new_user, topic=topic) sending_phone_number = twilio_gateway.calc_sending_phone_number( conversation, new_user) msg = settings.SMS_TEXT_CONVERSATION_TAKEN_MOBILE twilio_gateway.send_sms(sending_phone_number, new_user.phone_number, msg)
def update(session, username=None, password=None, phone_number=None): """ Update the details of the currently logged-in user. """ raise UnauthorizedException() # Disable for now. logger.debug("in core.api.users.update(" + "session=%s, username=%s, password=%s, phone_number=%s)" % (repr(session), repr(username), repr(password), repr(phone_number))) if session == None: raise InvalidParametersException() sessionHandler.validate(session) user = sessionHandler.get_user(session) # Remember if the user had a username or password. if user.username not in ["", None]: had_username = True else: had_username = False if user.password_salt not in ["", None]: had_password = True else: had_password = False # If we're setting a username and password for this user, and we didn't # have one previously, create a 3taps Identity for this user. Note that # this may fail, if the username is already in use. if not had_username and username != None: if password == None: raise InvalidParametersException() _check_username(username) _check_password(password) # Try creating this user within the 3taps Identity API. success,response = identity_api.create(username, password) if not success: if response.startswith("403 error"): raise DuplicateUsernameException() else: raise InvalidParametersException() # Check that we don't have a local user with that username. try: existing_user = User.objects.get(username__iexact=username) except User.DoesNotExist: existing_user = None if existing_user != None: raise DuplicateUsernameException() # Finally, save the updated user details into our database. salt = response['server_salt'] hash = hashlib.md5(password + salt).hexdigest() user.uses_identity_api = True user.username = username user.identity_api_salt = salt user.identity_api_hash = hash user.save() # If we're changing the username for this user, ask the 3taps Identity API # to change the username. Note that this may fail, if the new username is # already in use. if had_username and username != None and username != user.username: success,response = identity_api.login(user.username, pass_hash=user.identity_api_hash) if not success: raise UnauthorizedException() session = response success,response = identity_api.update(session, {'username' : username}) if not success: if response.startswith("403 error"): raise DuplicateUsernameException() else: raise InvalidParametersException() identity_api.logout(session) # Check that we don't have a local user with that username. try: existing_user = User.objects.get(username__iexact=username) except User.DoesNotExist: existing_user = None if existing_user != None: raise DuplicateUsernameException() # Finally, save the updated user details into our database. user.username = username user.save() # If we're changing the password for this user, ask the 3taps Identity API # to change the password. if password != None: if user.username in ["", None]: # We can't change the password if we don't have a username. raise InvalidParametersException() if user.uses_identity_api: success,response = \ identity_api.login(user.username, pass_hash=user.identity_api_hash) else: success,response = \ identity_api.login(user.username, password="******") if not success: raise UnauthorizedException() session = response success,response = identity_api.update(session, {'username' : username}) if not success: if response.startswith("403 error"): raise DuplicateUsernameException() else: raise InvalidParametersException() identity_api.logout(session) salt = response['server_salt'] hash = hashlib.md5(password + salt).hexdigest() user.uses_identity_api = True user.identity_api_salt = salt user.identity_api_hash = hash user.save() # If we've been asked to update the user's phone number, do so. # NOTE: someone was using this to hack our system, so I've disabled it. if False: # phone_number != None: if phone_number == "": user.phone_number = None # Remove current phone number. else: phone_number = utils.format_phone_number(phone_number) try: existing_user = User.objects.get(phone_number=phone_number) except User.DoesNotExist: existing_user = None if existing_user != None and user.id != existing_user.id: raise DuplicatePhoneNumberException() user.phone_number = phone_number # If this was an ad hoc user who we're now making permanent, change their # "ad hoc" status, and create a new default topic for the user. if user.ad_hoc and (username != None or password != None or phone_number != None): user.ad_hoc = False _create_default_topic(user) # If we have been given a username and password for this user, record them # as signing up. if not had_username and not had_password: if username not in ["", None] and password not in ["", None]: eventRecorder.record_event(eventRecorder.EVENT_TYPE_NEW_USER_SIGNUP) # Finally, save the updated user and return a copy of it back to the # caller. user.updated_at = datetime.datetime.utcnow() user.save() return user.to_dict()