def get_room_or_error(room_id, user): """ Tries to fetch a room for the user, checking permissions along the way. """ # Check if the user is logged in # if not user.is_authenticated: # raise ClientError("USER_HAS_TO_LOGIN", "You must login.") # Find the room they requested (by ID) try: room = PrivateChatRoom.objects.get(pk=room_id) except PrivateChatRoom.DoesNotExist: raise ClientError("ROOM_INVALID", "Invalid room.") # Check permissions # Is this user allowed in the room? (must be user1 or user2) if user != room.user1 and user != room.user2: raise ClientError("ROOM_ACCESS_DENIED", "You do not have permission to join this room.") # Are the users in this room friends? friend_list = FriendList.objects.get(user=user).friends.all() if not room.user1 in friend_list: if not room.user2 in friend_list: raise ClientError("ROOM_ACCESS_DENIED", "You must be friends to chat.") return room
async def receive_json(self, content): """ Called when we get a text frame. Channels will JSON-decode the payload for us and pass it as the first argument. """ # Messages will have a "command" key we can switch on print("ChatConsumer: receive_json") command = content.get("command", None) try: if command == "join": # Make them join the room await self.join_room(content["room"]) elif command == "leave": # Leave the room await self.leave_room(content["room"]) elif command == "send": if len(content["message"].lstrip()) == 0: raise ClientError(422, "You can't send an empty message.") await self.send_room(content["room"], content["message"]) elif command == "get_room_chat_messages": await self.display_progress_bar(True) room = await get_room_or_error(content['room_id'], self.scope["user"]) payload = await get_room_chat_messages(room, content['page_number']) if payload != None: payload = json.loads(payload) await self.send_messages_payload( payload['messages'], payload['new_page_number']) else: raise ClientError( 204, "Something went wrong retrieving the chatroom messages." ) await self.display_progress_bar(False) elif command == "get_user_info": room = await get_room_or_error(content['room_id'], self.scope["user"]) payload = get_user_info(room, self.scope["user"]) if payload != None: payload = json.loads(payload) await self.send_user_info_payload(payload['user_info']) else: raise ClientError( 204, "Something went wrong retrieving the other users account details." ) except ClientError as e: await self.display_progress_bar(False) # Catch any errors and send it back errorData = {} errorData['error'] = e.code if e.message: errorData['message'] = e.message await self.send_json(errorData)
async def send_room(self, room_id, message): """ Called by receive_json when someone sends a message to a room. """ print("ChatConsumer: send_room") # Check they are in this room if self.room_id != None: if str(room_id) != str(self.room_id): raise ClientError("ROOM_ACCESS_DENIED", "Room access denied") else: raise ClientError("ROOM_ACCESS_DENIED", "Room access denied") # Get the room and send to the group about it room = await get_room_or_error(room_id, self.scope["user"]) # get list of connected_users connected_users = room.connected_users.all() # Execute these functions asychronously await asyncio.gather(*[ append_unread_msg_if_not_connected(room, room.user1, connected_users, message), append_unread_msg_if_not_connected(room, room.user2, connected_users, message), create_room_chat_message(room, self.scope["user"], message) ]) await self.channel_layer.group_send( room.group_name, { "type": "chat.message", "profile_image": self.scope["user"].profile_image.url, "username": self.scope["user"].username, "user_id": self.scope["user"].id, "message": message, } )
async def send_room(self, room_id, message): """ Called by receive_json when someone sends a message to a room. """ # Check they are in this room print("PublicChatConsumer: send_room") if self.room_id != None: if str(room_id) != str(self.room_id): raise ClientError("ROOM_ACCESS_DENIED", "Room access denied") if not is_authenticated(self.scope["user"]): raise ClientError("AUTH_ERROR", "You must be authenticated to chat.") else: raise ClientError("ROOM_ACCESS_DENIED", "Room access denied") # Get the room and send to the group about it room = await get_room_or_error(room_id) await create_public_room_chat_message(room, self.scope["user"], message) await self.channel_layer.group_send( room.group_name, { "type": "chat.message", "profile_image": self.scope["user"].profile_image.url, "username": self.scope["user"].username, "user_id": self.scope["user"].id, "message": message, })
def get_treatment_or_error(treatment_id, user): if not user.is_authenticated: raise ClientError("USER_HAS_TO_LOGIN") try: treatment = Treatment.objects.get(id=treatment_id) except Treatment.DoesNotExist: raise ClientError("TREATMENT_INVALID") has_perm = user.is_superuser or user.has_perm('monitor.offer_treatment') if not has_perm: raise ClientError("TREATMENT_ACCESS_DENIED") return treatment
def _treatment_follow(message, treatment): if treatment.is_closed or treatment.user is None: raise ClientError("TREATMENT_ACCESS_DENIED") if not has_permission_to_user(message.user, treatment.user.id): raise ClientError("TREATMENT_ACCESS_DENIED") if treatment.id not in message.channel_session['treatments']: treatment.websocket_group.add(message.reply_channel) message.channel_session['treatments'].append(treatment.id) message.reply_channel.send({"text": json.dumps({"action": "following", "volunteer": treatment.user.first_name})}) _send_treatment_messages(message.reply_channel, treatment)
def new_message(room_id, user, message): """ Add new message to db. """ # Check if the user is logged in if not user.is_authenticated: raise ClientError("USER_HAS_TO_LOGIN") try: #add message to log msg_room = Room.objects.filter(pk=room_id).first() msg = Message(dialog=msg_room, user=user, message=message) msg.save() except: raise ClientError("NEW MESSAGE DB ERROR") return msg
def get_room_or_error(room_id, user): """ Tries to fetch a room for the user, checking permissions along the way. """ # Check if the user is logged in if not user.is_authenticated: raise ClientError("USER_HAS_TO_LOGIN") # Find the room they requested (by ID) try: room = Room.objects.get(pk=room_id) except Room.DoesNotExist: raise ClientError("ROOM_INVALID") if room.user1 != user and room.user2 != user: raise ClientError("ROOM_NOT_FOR_YOU") return room
def get_room_or_error(room_id: int, user: User): """ Tries to fetch a room for the user, checking permissions along the way. """ # Check if the user is logged in if not user.is_authenticated: raise ClientError("USER_HAS_TO_LOGIN") # Find the room they requested (by ID) try: room = Room.objects.get(pk=room_id) except Room.DoesNotExist: raise ClientError("ROOM_INVALID") # Check permissions if room.staff_only and not user.is_staff: raise ClientError("ROOM_ACCESS_DENIED") return room
async def send_room(self, room_id, message): """ Called by receive_json when someone sends a message to a room. """ # Check they are in this room print("ChatConsumer: send_room") if room_id not in self.rooms: raise ClientError("ROOM_ACCESS_DENIED", "Room access denied") # Get the room and send to the group about it room = await get_room_or_error(room_id, self.scope["user"]) # get list of connected_users connected_users = room.connected_users.all() await self.append_unread_msg_if_not_connected(room, room.user1, connected_users, message) await self.append_unread_msg_if_not_connected(room, room.user2, connected_users, message) await self.create_room_chat_message(room, self.scope["user"], message) await self.channel_layer.group_send( room.group_name, { "type": "chat.message", "room_id": room_id, "profile_image": self.scope["user"].profile_image.url, "username": self.scope["user"].username, "user_id": self.scope["user"].id, "message": message, })
def get_chat_notifications(user, page_number): """ Get Chat Notifications with Pagination (next page of results). This is for appending to the bottom of the notifications list. Chat Notifications are: 1. UnreadChatRoomMessages """ if user.is_authenticated: chatmessage_ct = ContentType.objects.get_for_model( UnreadChatRoomMessages) notifications = Notification.objects.filter( target=user, content_type=chatmessage_ct).order_by('-timestamp') p = Paginator(notifications, DEFAULT_NOTIFICATION_PAGE_SIZE) # sleep 1s for testing # sleep(1) print("PAGES: " + str(p.num_pages)) payload = {} if len(notifications) > 0: if int(page_number) <= p.num_pages: s = LazyNotificationEncoder() serialized_notifications = s.serialize( p.page(page_number).object_list) payload['notifications'] = serialized_notifications new_page_number = int(page_number) + 1 payload['new_page_number'] = new_page_number return json.dumps(payload) else: return None else: raise ClientError("AUTH_ERROR", "User must be authenticated to get notifications.") return None
def decline_friend_request(user, notification_id): """ Decline a friend request """ payload = {} if user.is_authenticated: try: notification = Notification.objects.get(pk=notification_id) friend_request = notification.content_object # confirm this is the correct user if friend_request.receiver == user: # accept the request and get the updated notification updated_notification = friend_request.decline() # return the notification associated with this FriendRequest s = LazyNotificationEncoder() payload['notification'] = s.serialize([updated_notification ])[0] return json.dumps(payload) except Notification.DoesNotExist: raise ClientError( "AUTH_ERROR", "An error occurred with that notification. Try refreshing the browser." ) return None
def refresh_general_notifications(user, oldest_timestamp, newest_timestamp): """ Retrieve the general notifications newer than the oldest one on the screen and younger than the newest one the screen. The result will be: Notifications currently visible will be updated """ payload = {} if user.is_authenticated: oldest_ts = oldest_timestamp[0:oldest_timestamp.find( "+")] # remove timezone because who cares oldest_ts = datetime.strptime(oldest_ts, '%Y-%m-%d %H:%M:%S.%f') newest_ts = newest_timestamp[0:newest_timestamp.find( "+")] # remove timezone because who cares newest_ts = datetime.strptime(newest_ts, '%Y-%m-%d %H:%M:%S.%f') friend_request_ct = ContentType.objects.get_for_model(FriendRequest) friend_list_ct = ContentType.objects.get_for_model(FriendList) notifications = Notification.objects.filter( target=user, content_type__in=[friend_request_ct, friend_list_ct], timestamp__gte=oldest_ts, timestamp__lte=newest_ts).order_by('-timestamp') s = LazyNotificationEncoder() payload['notifications'] = s.serialize(notifications) else: raise ClientError("AUTH_ERROR", "User must be authenticated to get notifications.") return json.dumps(payload)
def _treatment_begin(message, treatment): lock_id = "consumers.treatment-begin" acquire_lock = lambda: cache.add(lock_id, "true", LOCK_EXPIRE) release_lock = lambda: cache.delete(lock_id) if acquire_lock(): try: if treatment is not None and (treatment.is_closed or (treatment.user is not None and treatment.user != message.user)): raise ClientError("TREATMENT_ACCESS_DENIED") try: current_treatment = Treatment.objects.get(user=message.user,is_closed=False) except: current_treatment = None if current_treatment: treatment = current_treatment else: treatment.user = message.user treatment.treatment_at = datetime.now() treatment.save() treatment.websocket_group.add(message.reply_channel) ChannelGroup("treatment-waiting").discard(message.reply_channel) message.channel_session['treatments'] = list(set(message.channel_session['treatments']).union([treatment.id])) message.reply_channel.send({"text": json.dumps({"action": "treating", "treatment": treatment.id})}) _send_treatment_messages(message.reply_channel, treatment) finally: release_lock()
def get_general_notifications(user, page_number): """ Get General Notifications with Pagination (next page of results). This is for appending to the bottom of the notifications list. General Notifications are: 1. FriendRequest 2. FriendList """ if user.is_authenticated: friend_request_ct = ContentType.objects.get_for_model(FriendRequest) friend_list_ct = ContentType.objects.get_for_model(FriendList) notifications = Notification.objects.filter(target=user, content_type__in=[ friend_request_ct, friend_list_ct ]).order_by('-timestamp') p = Paginator(notifications, DEFAULT_NOTIFICATION_PAGE_SIZE) payload = {} if len(notifications) > 0: if int(page_number) <= p.num_pages: s = LazyNotificationEncoder() serialized_notifications = s.serialize( p.page(page_number).object_list) payload['notifications'] = serialized_notifications new_page_number = int(page_number) + 1 payload['new_page_number'] = new_page_number else: return None else: raise ClientError("AUTH_ERROR", "User must be authenticated to get notifications.") return json.dumps(payload)
def _treatment_wake(message, treatment): if not (treatment.is_closed and treatment.user is None): raise ClientError("TREATMENT_ACCESS_DENIED") treatment.is_closed = False treatment.closed_at = None treatment.save() message.reply_channel.send({"text": json.dumps({"action": "waked"})})
def get_room_or_error(room_id): """ Tries to fetch a room for the user """ try: room = PublicChatRoom.objects.get(pk=room_id) except PublicChatRoom.DoesNotExist: raise ClientError("ROOM_INVALID", "Invalid room.") return room
def _treatment_send(message, treatment): if message.user != treatment.user: raise ClientError("TREATMENT_ACCESS_DENIED") new_message = Message() new_message.treatment = treatment new_message.created_at = datetime.now() new_message.text = str(message["message"]) new_message.msg_type = 'S' new_message.save()
def _treatment_view(message, treatment): if not (treatment.is_closed and treatment.user is None): raise ClientError("TREATMENT_ACCESS_DENIED") if treatment.id not in message.channel_session['treatments']: treatment.websocket_group.add(message.reply_channel) message.channel_session['treatments'].append(treatment.id) message.reply_channel.send({"text": json.dumps({"action": "viewing"})}) _send_treatment_messages(message.reply_channel, treatment)
async def receive_json(self, content): """ Called when we get a text frame. Channles will JSON-decode the payload fro us and pass it as the first argument. """ command = content.get("command", None) # message = content.get("message", None) # can use this to see what message is print("PublicChatConsumer: receive_json: " + str(command)) # print("PublicChatConsumer: receive_json: " + str(message)) # in order to get the send from command you can either use # content.get("command", None) or you can use content['message'] try: if command == "send": if len(content['message'].lstrip()) == 0: # raise Exception("nothing here") raise ClientError(422, "you can't send an empty message") # MDN web docs 422 Unproccessable entity # await self.send_json({ # "message": content['message'] # }) # await self.send_message(content['room_id'], content['message']) # now using send_room instead of send_message. await self.send_room(content['room_id'], content['message']) elif command == "join": # mitch uses room inside of the addeventlistener (home.html) await self.join_room(content['room_id']) elif command == "leave": # same as above await self.leave_room(content['room_id']) elif command == "get_room_chat_messages": await self.display_progress_bar(True) room = await get_room_or_error(content['room_id']) payload = await get_room_chat_messages(room, content['page_number']) if payload != None: payload = json.loads(payload) await self.send_messages_payload(payload['messages'], payload['new_page_number']) else: raise ClientError(204, "Something went wrong retrieving chatroom messages.") await self.display_progress_bar(False) except ClientError as e: await self.display_progress_bar(False) await self.handle_client_error(e)
def get_history_json(room, start=0, offset=20): """ Get history """ try: history = Message.objects.filter( dialog=room.pk).order_by('-id')[start:start + offset][::-1] history = serializers.serialize('json', history) except: raise ClientError("CANT GET HISTORY") return history
def get_room_or_error(room_id, user): """ Tries to fetch a room for the user, checking permissions along the way. """ try: room = PrivateChatRoom.objects.get(pk=room_id) except PrivateChatRoom.DoesNotExist: raise ClientError("ROOM_INVALID", "Invalid room.") # Is this user allowed in the room? (must be user1 or user2) if user != room.user1 and user != room.user2: raise ClientError("ROOM_ACCESS_DENIED", "You do not have permission to join this room.") # Are the users in this room friends? # friend_list = FriendList.objects.get(user=user).friends.all() # if not room.user1 in friend_list: # if not room.user2 in friend_list: # raise ClientError("ROOM_ACCESS_DENIED", "You must be friends to chat.") return room
async def send_room(self, room_id, message): """ Called by receive_json when someone sends a message to a room. """ print("ChatConsumer: send_room") if self.room_id != None: if str(room_id) != str(self.room_id): raise ClientError("ROOM_ACCESS_DENIED", "Room access denied") else: raise ClientError("ROOM_ACCESS_DENIED", "Room access denied") room = await get_room_or_error(room_id, self.scope['user']) # get list of connected_users connected_users = room.connected_users.all() """ Finally using an async to fire these three all at the same time. """ await asyncio.gather(*[ append_unread_msg_if_not_connected(room, room.user1, connected_users, message), append_unread_msg_if_not_connected(room, room.user2, connected_users, message), create_room_chat_message(room, self.scope['user'], message) ]) # await append_unread_msg_if_not_connected(room, room.user1, connected_users, message) # await append_unread_msg_if_not_connected(room, room.user2, connected_users, message) # await create_room_chat_message(room, self.scope['user'], message) await self.channel_layer.group_send( room.group_name, { "type": "chat.message", # executes chat_message "profile_image": self.scope['user'].profile_image.url, "username": self.scope['user'].username, "user_id": self.scope['user'].id, "message": message, })
def get_unread_chat_notification_count(user): payload = {} if user.is_authenticated: chatmessage_ct = ContentType.objects.get_for_model(UnreadChatRoomMessages) notifications = Notification.objects.filter(target=user, content_type__in=[chatmessage_ct]) unread_count = 0 if notifications: unread_count = len(notifications.all()) payload['count'] = unread_count return json.dumps(payload) else: ClientError("AUTH_ERROR", "User must be authenticated to get notifications.") return None
async def send_room(self, room_id: int, message: str): """Called by receive_json when someone sends a message to a room.""" # Check they are in this room if room_id not in self.rooms: raise ClientError("ROOM_ACCESS_DENIED") # Get the room and send to the group about it room: Room = await get_room_or_error(room_id, self.scope["user"]) user: User = self.scope['user'] await self.channel_layer.group_send( room.group_name, { "type": "chat.message", "room_id": room_id, "username": user.username, "message": message, }) await cache_or_update_room_presence(room_id, user)
def get_room_or_error(id=None, event=None): """ Tries to get the requested a room """ if id is None and event is None: raise ValueError("id and event cannot both be None!") try: if id is not None: room = Room.objects.get(id=id) else: room = Room.objects.get(event=event) except Room.DoesNotExist: raise ClientError("Room not found.") return room
def get_new_general_notifications(user, newest_timestamp): """ Retrieve any notifications newer than the newest_timestamp on the screen. """ payload = {} if user.is_authenticated: timestamp = newest_timestamp[0:newest_timestamp.find("+")] timestamp = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S.%f") friend_request_ct = ContentType.objects.get_for_model(FriendRequest) friend_list_ct = ContentType.objects.get_for_model(FriendList) notifications = Notification.objects.filter(target=user, content_type__in=[friend_request_ct, friend_list_ct], timestamp__gt=timestamp, read=False).order_by("-timestamp") s = LazyNotificationEncoder() payload['notifications'] = s.serialize(notifications) else: raise ClientError("AUTH_ERROR", "User must be authenticated to get notifications") return json.dumps(payload)
def get_new_chat_notifications(user, newest_timestamp): """ Retrieve any notifications newer than the newest_timestamp on the screen. """ payload = {} if user.is_authenticated: timestamp = newest_timestamp[0:newest_timestamp.find("+")] timestamp = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S.%f") chatmessage_ct = ContentType.objects.get_for_model(UnreadChatRoomMessages) # content_type=chatmessage_ct or use what we used below. this is how we make a list but we don't really need a list just for reference. notifications = Notification.objects.filter(target=user, content_type__in=[chatmessage_ct], timestamp__gt=timestamp).order_by("-timestamp") s = LazyNotificationEncoder() payload['notifications'] = s.serialize(notifications) return json.dumps(payload) else: ClientError("AUTH_ERROR", "User must be authenticated to get notifications.") return None
def _treatment_end(message, treatment): if treatment.is_closed or treatment.user != message.user: raise ClientError("TREATMENT_ACCESS_DENIED") treatment.is_closed = True treatment.closed_at = datetime.now() treatment.save() message = Message() message.treatment = treatment message.created_at = datetime.now() message.text = config.PLEASE_TREATMENT_CLODED_MESSAGE % web.utils.get_now_as_str() message.msg_type = 'S' message.save() treatment.websocket_group.send({"text": json.dumps({"action": "closed"})}) message.channel_session['treatments'] = list(set(message.channel_session['treatments']).difference([treatment.id]))
def get_unread_general_notifications_count(user): payload = {} if user.is_authenticated: friend_request_ct = ContentType.objects.get_for_model(FriendRequest) friend_list_ct = ContentType.objects.get_for_model(FriendList) notifications = Notification.objects.filter(target=user, content_type__in=[friend_request_ct, friend_list_ct], read=False) unread_count = 0 if notifications: for notification in notifications.all(): if not notification.read: unread_count = unread_count + 1 payload['count'] = unread_count return json.dumps(payload) else: raise ClientError("AUTH_ERROR", "User must be authenticated to get notifications.") return None