def send_message(message, giphy=None): if message[VarNames.TIME_DIFF] < 0: raise ValidationError("Back to the future?") files = UploadedFile.objects.filter(id__in=message.get( VarNames.FILES), user_id=self.user_id) symbol = get_max_key(files) channel = message[VarNames.ROOM_ID] js_id = message[VarNames.JS_MESSAGE_ID] message_db = Message(sender_id=self.user_id, content=message[VarNames.CONTENT], symbol=symbol, giphy=giphy, room_id=channel) message_db.time -= message[VarNames.TIME_DIFF] res_files = [] do_db(message_db.save) if files: images = up_files_to_img(files, message_db.id) res_files = MessagesCreator.prepare_img_video( images, message_db.id) prepared_message = self.create_send_message( message_db, Actions.PRINT_MESSAGE, res_files, js_id) self.publish(prepared_message, channel) self.notify_offline(channel, message_db.id)
def edit_glyphy(message, giphy): do_db(Message.objects.filter(id=message.id).update, content=message.content, symbol=message.symbol, giphy=giphy, edited_times=message.edited_times) message.giphy = giphy self.publish( self.create_send_message(message, Actions.EDIT_MESSAGE, None, js_id), message.room_id)
def create_new_room(self, message): room_name = message[VarNames.ROOM_NAME] if not room_name or len(room_name) > 16: raise ValidationError('Incorrect room name "{}"'.format(room_name)) room = Room(name=room_name) do_db(room.save) RoomUsers(room_id=room.id, user_id=self.user_id).save() subscribe_message = self.subscribe_room_channel_message( room.id, room_name) self.publish(subscribe_message, self.channel, True)
def auth(self, username, password): """ Logs in into system. """ try: if '@' in username: user = do_db(UserProfile.objects.get, email=username) else: user = do_db(UserProfile.objects.get, username=username) if not user.check_password(password): raise ValidationError("Invalid password") except User.DoesNotExist: raise ValidationError("User {} doesn't exist".format(username)) return self.__generate_session__(user.id)
def save_ip(self): """ This code is not used anymore """ if not do_db( UserJoinedInfo.objects.filter( Q(ip__ip=self.ip) & Q(user_id=self.user_id)).exists): res = get_or_create_ip_wrapper(self.ip, self.logger, self.fetch_and_save_ip_http) if res is not None: UserJoinedInfo.objects.create(ip=res, user_id=self.user_id)
def open(self): session_key = self.get_cookie(settings.SESSION_COOKIE_NAME) if sessionStore.exists(session_key): self.ip = self.get_client_ip() session = SessionStore(session_key) self.user_id = int(session["_auth_user_id"]) self.generate_self_id() log_params = {'id': self.id, 'ip': self.ip} self._logger = logging.LoggerAdapter(parent_logger, log_params) cookies = [ "{}={}".format(k, self.request.cookies[k].value) for k in self.request.cookies ] self.logger.debug( "!! Incoming connection, session %s, thread hash %s, cookies: %s", session_key, self.id, ";".join(cookies)) self.async_redis.connect() user_db = do_db(User.objects.get, id=self.user_id) self.sender_name = user_db.username self.sex = user_db.sex_str user_rooms = get_users_in_current_user_rooms(self.user_id) # get all missed messages self.channels = [] # py2 doesn't support clear() self.channels.append(self.channel) self.channels.append(self.id) rooms_online = {} was_online = False for room_id in user_rooms: self.channels.append(room_id) rooms_online[room_id] = self.get_is_online(room_id) was_online = was_online or rooms_online[room_id][0] self.listen(self.channels) off_messages, history = self.get_offline_messages( user_rooms, was_online, self.get_argument('history', False)) for room_id in user_rooms: user_rooms[room_id][ VarNames.LOAD_MESSAGES_HISTORY] = history.get(room_id) user_rooms[room_id][ VarNames.LOAD_MESSAGES_OFFLINE] = off_messages.get(room_id) self.ws_write(self.set_room(user_rooms)) for room_id in user_rooms: self.async_redis_publisher.sadd(room_id, self.id) self.add_online_user(room_id, rooms_online[room_id][0], rooms_online[room_id][1]) self.logger.info("!! User %s subscribes for %s", self.sender_name, self.channels) self.connected = True # self.save_ip() else: self.logger.warning('!! Session key %s has been rejected', str(session_key)) self.close(403, "Session key %s has been rejected" % session_key)
def on_close(self): if self.async_redis.subscribed: self.logger.info("Close event, unsubscribing from %s", self.channels) self.async_redis.unsubscribe(self.channels) else: self.logger.info("Close event, not subscribed, channels: %s", self.channels) self.async_redis_publisher.srem(RedisPrefix.ONLINE_VAR, self.id) is_online, online = self.get_online_and_status_from_redis() if self.connected: if not is_online: message = self.room_online_logout(online) self.publish(message, settings.ALL_ROOM_ID) res = do_db(execute_query, settings.UPDATE_LAST_READ_MESSAGE, [ self.user_id, ]) self.logger.info("Updated %s last read message", res) self.disconnect()
def delete_channel(self, message): room_id = message[VarNames.ROOM_ID] if room_id not in self.channels or room_id == ALL_ROOM_ID: raise ValidationError('You are not allowed to exit this room') room = do_db(Room.objects.get, id=room_id) if room.disabled: raise ValidationError('Room is already deleted') if room.name is None: # if private then disable room.disabled = True else: # if public -> leave the room, delete the link RoomUsers.objects.filter(room_id=room.id, user_id=self.user_id).delete() online = self.get_online_from_redis(room_id) online.remove(self.user_id) self.publish(self.room_online(online, Actions.LOGOUT, room_id), room_id) room.save() message = self.unsubscribe_direct_message(room_id) self.publish(message, room_id, True)
def edit_message(self, data): js_id = data[VarNames.JS_MESSAGE_ID] message = do_db(Message.objects.get, id=data[VarNames.MESSAGE_ID]) validate_edit_message(self.user_id, message) message.content = data[VarNames.CONTENT] MessageHistory(message=message, content=message.content, giphy=message.giphy).save() message.edited_times += 1 giphy_match = self.isGiphy(data[VarNames.CONTENT]) if message.content is None: Message.objects.filter(id=data[VarNames.MESSAGE_ID]).update( deleted=True, edited_times=message.edited_times, content=None) self.publish( self.create_send_message(message, Actions.DELETE_MESSAGE, None, js_id), message.room_id) elif giphy_match is not None: self.edit_message_giphy(giphy_match, message, js_id) else: self.edit_message_edit(data, message, js_id)
def delete_channel(self, message): room_id = message[VarNames.ROOM_ID] js_id = message[VarNames.JS_MESSAGE_ID] if room_id not in self.channels or room_id == ALL_ROOM_ID: raise ValidationError('You are not allowed to exit this room') room = do_db(Room.objects.get, id=room_id) if room.disabled: raise ValidationError('Room is already deleted') if room.name is None: # if private then disable room.disabled = True room.save() else: # if public -> leave the room, delete the link RoomUsers.objects.filter(room_id=room.id, user_id=self.user_id).delete() ru = list( RoomUsers.objects.filter(room_id=room.id).values_list('user_id', flat=True)) message = self.unsubscribe_direct_message(room_id, js_id, self.id, ru, room.name) self.publish(message, room_id, True)
def process_get_messages(self, data): """ :type data: dict """ header_id = data.get(VarNames.GET_MESSAGES_HEADER_ID, None) count = int(data.get(VarNames.GET_MESSAGES_COUNT, 10)) room_id = data[VarNames.CHANNEL] self.logger.info('!! Fetching %d messages starting from %s', count, header_id) if header_id is None: messages = Message.objects.filter( room_id=room_id).order_by('-pk')[:count] else: messages = Message.objects.filter( Q(id__lt=header_id), Q(room_id=room_id)).order_by('-pk')[:count] imv = do_db(get_message_images_videos, messages) response = self.get_messages(messages, room_id, imv, MessagesCreator.prepare_img_video) self.ws_write(response)
def on_close(self): if self.async_redis.subscribed: self.logger.info("Close event, unsubscribing from %s", self.channels) self.async_redis.unsubscribe(self.channels) else: self.logger.info("Close event, not subscribed, channels: %s", self.channels) log_data = {} for channel in self.channels: if not isinstance(channel, Number): continue self.sync_redis.srem(channel, self.id) if self.connected: self.publish_logout(channel, log_data) if self.connected: res = do_db(execute_query, settings.UPDATE_LAST_READ_MESSAGE, [ self.user_id, ]) self.logger.info("Updated %s last read message", res) self.disconnect(json.dumps(log_data))
def invite_user(self, message): room_id = message[VarNames.ROOM_ID] if room_id not in self.channels: raise ValidationError( "Access denied, only allowed for channels {}".format( self.channels)) room = do_db(Room.objects.get, id=room_id) if room.is_private: raise ValidationError( "You can't add users to direct room, create a new room instead" ) users = message.get(VarNames.ROOM_USERS) users_in_room = list( RoomUsers.objects.filter(room_id=room_id).values_list('user_id', flat=True)) intersect = set(users_in_room) & set(users) if bool(intersect): raise ValidationError("Users %s are already in the room", intersect) users_in_room.extend(users) max_id = Message.objects.filter(room_id=room_id).aggregate( Max('id'))['id__max'] if not max_id: max_id = Message.objects.all().aggregate(Max('id'))['id__max'] ru = [ RoomUsers(user_id=user_id, room_id=room_id, last_read_message_id=max_id, volume=1, notifications=False) for user_id in users ] RoomUsers.objects.bulk_create(ru) add_invitee = { VarNames.EVENT: Actions.ADD_INVITE, VarNames.ROOM_ID: room_id, VarNames.ROOM_USERS: users_in_room, VarNames.ROOM_NAME: room.name, VarNames.INVITEE_USER_ID: users, VarNames.INVITER_USER_ID: self.user_id, VarNames.HANDLER_NAME: HandlerNames.CHANNELS, VarNames.TIME: get_milliseconds(), VarNames.VOLUME: 1, VarNames.NOTIFICATIONS: False, } add_invitee_dumped = encode_message(add_invitee, True) for user in users: self.raw_publish(add_invitee_dumped, RedisPrefix.generate_user(user)) invite = { VarNames.EVENT: Actions.INVITE_USER, VarNames.ROOM_ID: room_id, VarNames.INVITEE_USER_ID: users, VarNames.INVITER_USER_ID: self.user_id, VarNames.HANDLER_NAME: HandlerNames.CHANNELS, VarNames.ROOM_USERS: users_in_room, VarNames.TIME: get_milliseconds(), VarNames.CB_BY_SENDER: self.id, VarNames.JS_MESSAGE_ID: message[VarNames.JS_MESSAGE_ID] } self.publish(invite, room_id, True)
def create_new_room(self, message): room_name = message.get(VarNames.ROOM_NAME) users = message.get(VarNames.ROOM_USERS) users.append(self.user_id) users = list(set(users)) if room_name and len(room_name) > 16: raise ValidationError('Incorrect room name "{}"'.format(room_name)) create_user_rooms = True if not room_name and len(users) == 2: user_rooms = evaluate( Room.users.through.objects.filter( user_id=self.user_id, room__name__isnull=True).values('room_id')) user_id = users[0] if users[1] == self.user_id else users[1] try: room = RoomUsers.objects.filter(user_id=user_id, room__in=user_rooms).values( 'room__id', 'room__disabled').get() room_id = room['room__id'] if room['room__disabled']: Room.objects.filter(id=room_id).update(disabled=False) else: raise ValidationError('This room already exist') create_user_rooms = False except RoomUsers.DoesNotExist: pass elif not room_name: raise ValidationError( 'At least one user should be selected, or room should be public' ) if create_user_rooms: room = Room(name=room_name) do_db(room.save) room_id = room.id max_id = Message.objects.all().aggregate(Max('id'))['id__max'] ru = [ RoomUsers(user_id=user_id, room_id=room_id, last_read_message_id=max_id, volume=message[VarNames.VOLUME], notifications=message[VarNames.NOTIFICATIONS]) for user_id in users ] RoomUsers.objects.bulk_create(ru) m = { VarNames.EVENT: Actions.CREATE_ROOM_CHANNEL, VarNames.ROOM_ID: room_id, VarNames.ROOM_USERS: users, VarNames.CB_BY_SENDER: self.id, VarNames.INVITER_USER_ID: self.user_id, VarNames.HANDLER_NAME: HandlerNames.CHANNELS, VarNames.VOLUME: message[VarNames.VOLUME], VarNames.NOTIFICATIONS: message[VarNames.NOTIFICATIONS], VarNames.ROOM_NAME: room_name, VarNames.TIME: get_milliseconds(), VarNames.JS_MESSAGE_ID: message[VarNames.JS_MESSAGE_ID], } jsoned_mess = encode_message(m, True) for user in users: self.raw_publish(jsoned_mess, RedisPrefix.generate_user(user))
def open(self): session_key = self.get_argument('sessionId', None) try: if session_key is None: raise Error401() session = SessionStore(session_key) try: self.user_id = int(session["_auth_user_id"]) except: raise Error401() self.ip = self.get_client_ip() user_db = do_db(UserProfile.objects.get, id=self.user_id) self.generate_self_id() self._logger = logging.LoggerAdapter(parent_logger, { 'id': self.id, 'ip': self.ip }) cookies = [ "{}={}".format(k, self.request.cookies[k].value) for k in self.request.cookies ] self.logger.debug( "!! Incoming connection, session %s, thread hash %s, cookies: %s", session_key, self.id, ";".join(cookies)) self.async_redis.connect() self.async_redis_publisher.sadd(RedisPrefix.ONLINE_VAR, self.id) # since we add user to online first, latest trigger will always show correct online was_online, online = self.get_online_and_status_from_redis() user_rooms_query = Room.objects.filter(users__id=self.user_id, disabled=False) \ .values('id', 'name', 'roomusers__notifications', 'roomusers__volume') room_users = [{ VarNames.ROOM_ID: room['id'], VarNames.ROOM_NAME: room['name'], VarNames.NOTIFICATIONS: room['roomusers__notifications'], VarNames.VOLUME: room['roomusers__volume'], VarNames.ROOM_USERS: [] } for room in user_rooms_query] user_rooms_dict = { room[VarNames.ROOM_ID]: room for room in room_users } room_ids = [room_id[VarNames.ROOM_ID] for room_id in room_users] rooms_users = RoomUsers.objects.filter( room_id__in=room_ids).values('user_id', 'room_id') for ru in rooms_users: user_rooms_dict[ru['room_id']][VarNames.ROOM_USERS].append( ru['user_id']) # get all missed messages self.channels = room_ids # py2 doesn't support clear() self.channels.append(self.channel) self.channels.append(self.id) self.listen(self.channels) off_messages, history = self.get_offline_messages( room_users, was_online, self.get_argument('history', False)) for room in room_users: room_id = room[VarNames.ROOM_ID] h = history.get(room_id) o = off_messages.get(room_id) if h: room[VarNames.LOAD_MESSAGES_HISTORY] = h if o: room[VarNames.LOAD_MESSAGES_OFFLINE] = o if settings.SHOW_COUNTRY_CODE: fetched_users = User.objects.annotate( user_c=Count('id')).values( 'id', 'username', 'sex', 'userjoinedinfo__ip__country_code', 'userjoinedinfo__ip__country', 'userjoinedinfo__ip__region', 'userjoinedinfo__ip__city') user_dict = [ RedisPrefix.set_js_user_structure_flag( user['id'], user['username'], user['sex'], user['userjoinedinfo__ip__country_code'], user['userjoinedinfo__ip__country'], user['userjoinedinfo__ip__region'], user['userjoinedinfo__ip__city']) for user in fetched_users ] else: fetched_users = User.objects.values('id', 'username', 'sex') user_dict = [ RedisPrefix.set_js_user_structure(user['id'], user['username'], user['sex']) for user in fetched_users ] if self.user_id not in online: online.append(self.user_id) self.ws_write(self.set_room(room_users, user_dict, online, user_db)) if not was_online: # if a new tab has been opened online_user_names_mes = self.room_online_login( online, user_db.username, user_db.sex_str) self.logger.info( '!! First tab, sending refresh online for all') self.publish(online_user_names_mes, settings.ALL_ROOM_ID) self.logger.info("!! User %s subscribes for %s", self.user_id, self.channels) self.connected = True except Error401: self.logger.warning('!! Session key %s has been rejected' % session_key) self.close(403, "Session key %s has been rejected" % session_key)