cirandas.net

ref: master

app/controllers/public/chat_controller.rb


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
class ChatController < PublicController

  before_filter :login_required
  before_filter :check_environment_feature
  before_filter :can_send_message, :only => :register_message

  def start_session
    login = user.jid
    password = current_user.crypted_password
    session[:chat] ||= {:rooms => []}
    begin
      jid, sid, rid = RubyBOSH.initialize_session(login, password, "http://#{environment.default_hostname}/http-bind",
                                                  :wait => 30, :hold => 1, :window => 5)
      session_data = { :jid => jid, :sid => sid, :rid => rid }
      render :text => session_data.to_json, :layout => false, :content_type => 'application/javascript'
    rescue
      render :action => 'start_session_error', :layout => false, :status => 500
    end
  end

  def toggle
    session[:chat][:status] = session[:chat][:status] == 'opened' ? 'closed' : 'opened'
    render :nothing => true
  end

  def tab
    session[:chat][:tab_id] = params[:tab_id]
    render :nothing => true
  end

  def join
    session[:chat][:rooms] << params[:room_id]
    session[:chat][:rooms].uniq!
    render :nothing => true
  end

  def leave
    session[:chat][:rooms].delete(params[:room_id])
    render :nothing => true
  end

  def my_session
    render :text => session[:chat].to_json, :layout => false
  end

  def avatar
    profile = environment.profiles.find_by(identifier: params[:id])
    filename, mimetype = profile_icon(profile, :minor, true)
    if filename =~ /^(https?:)?\/\//
      redirect_to filename
    else
      data = File.read(File.join(Rails.root, 'public', filename))
      render :text => data, :layout => false, :content_type => mimetype
      expires_in 24.hours
    end
  end

  def avatars
    profiles = environment.profiles.where(:identifier => params[:profiles])
    avatar_map = profiles.inject({}) do |result, profile|
      result[profile.identifier] = profile_icon(profile, :minor)
      result
    end

    render_json avatar_map
  end

  def update_presence_status
    if request.xhr?
      current_user.update({:chat_status_at => DateTime.now}.merge(params[:status] || {}))
    end
    render :nothing => true
  end

  def save_message
    if request.post?
      to = environment.profiles.where(:identifier => params[:to]).first
      body = params[:body]

      begin
        ChatMessage.create!(:to => to, :from => user, :body => body)
        return render_json({:status => 0})
      rescue Exception => exception
        return render_json({:status => 3, :message => exception.to_s, :backtrace => exception.backtrace})
      end
    end
  end

  def recent_messages
    other = environment.profiles.find_by(identifier: params[:identifier])
    if other.kind_of?(Organization)
      messages = ChatMessage.where('to_id=:other', :other => other.id)
    else
      messages = ChatMessage.where('(to_id=:other and from_id=:me) or (to_id=:me and from_id=:other)', {:me => user.id, :other => other.id})
    end

    messages = messages.order('created_at DESC').includes(:to, :from).offset(params[:offset]).limit(20)
    messages_json = messages.map do |message|
      {
        :body => message.body,
        :to => {:id => message.to.identifier, :name => message.to.name},
        :from => {:id => message.from.identifier, :name => message.from.name},
        :created_at => message.created_at
      }
    end
    render :json => messages_json.reverse
  end

  def recent_conversations
    profiles = Profile.find_by_sql("select profiles.* from profiles inner join (select distinct r.id as id, MAX(r.created_at) as created_at from (select from_id, to_id, created_at, (case when from_id=#{user.id} then to_id else from_id end) as id from chat_messages where from_id=#{user.id} or to_id=#{user.id}) as r group by id order by created_at desc, id) as t on profiles.id=t.id order by t.created_at desc")
    jids = profiles.map(&:jid).reverse
    render :json => jids.to_json
  end

  #TODO Ideally this is done through roster table on ejabberd.
  def rosters
    rooms = user.memberships.map {|m| {:jid => m.jid, :name => m.name}}
    friends = user.friends.map {|f| {:jid => f.jid, :name => f.name}}
    rosters = {:rooms => rooms, :friends => friends}
    render :text => rosters.to_json
  end

  def availabilities
    current_user.update_column(:chat_status_at, DateTime.now)
    availabilities = user.friends.map do |friend|
      status = friend.user.chat_status
      status = 'offline' if status.blank? || !friend.user.chat_alive?
      {:jid => friend.jid, :status => status}
    end
    render :text => availabilities.to_json
  end

  protected

  def check_environment_feature
    unless environment.enabled?('xmpp_chat')
      render_not_found
      return
    end
  end

  def can_send_message
    return render_json({:status => 1, :message => 'Missing parameters!'}) if params[:from].nil? || params[:to].nil? || params[:message].nil?
    return render_json({:status => 2, :message => 'You can not send message as another user!'}) if params[:from] != user.jid
    # TODO Maybe register the jid in a table someday to avoid this below
    return render_json({:status => 3, :messsage => 'You can not send messages to strangers!'}) if user.friends.where(:identifier => params[:to].split('@').first).blank?
  end

  def render_json(result)
    render :text => result.to_json
  end
end