· 4 years ago · Mar 17, 2021, 11:50 AM
1 def search_message
2 begin
3 message = params[:message]
4 if message.nil? || message == ""
5 raise InputError.new("message cannot be empty.")
6 end
7 comments = Queue.new
8 ActiveRecord::Base.transaction do
9 qiscus_token = @current_user.qiscus_token
10 application = @current_user.application
11
12 qiscus_room_ids = params[:qiscus_room_id]
13 if qiscus_room_ids
14 unless qiscus_room_ids.is_a? Array
15 qiscus_room_ids = [qiscus_room_ids]
16 end
17 qiscus_room_ids = qiscus_room_ids.reject(&:empty?).map(&:to_i).uniq.select { |room| room > 0 }
18 else
19 qiscus_room_ids = []
20 end
21 room_ids = @current_user.chat_rooms.pluck(:qiscus_room_id)
22 if qiscus_room_ids.empty?
23 qiscus_room_ids = room_ids
24 end
25 limit = params[:limit].to_i
26 if limit.blank? or limit <= 0
27 limit = 100
28 end
29
30 expiry_time = 5.minutes.to_i
31 n = ENV['SEARCH_MESSAGE_THREAD_POOL_SIZE'].to_i > 0 ? ENV['SEARCH_MESSAGE_THREAD_POOL_SIZE'].to_i : 5
32 timeout = ENV['SEARCH_MESSAGE_TIMEOUT'].to_i > 0 ? ENV['SEARCH_MESSAGE_TIMEOUT'].to_i : 20
33 # pool = Thread.pool(n)
34 pool = Concurrent::FixedThreadPool.new(n)
35 qiscus_sdk = QiscusSdk.new(application.app_id, application.qiscus_sdk_secret)
36 begin
37 Timeout::timeout(timeout) do
38 qiscus_room_ids.each do |id|
39 pool.post {
40 if room_ids.include? id
41 redis_key = "search_message"+id.to_s
42 redis_key_reached_last = "reached_last"+id.to_s
43 room_comment = $redis.get(redis_key)
44 if room_comment
45 room_comment = Marshal.load(room_comment).sort_by { |h| h["unix_nano_timestamp"] }
46 if $redis.get(redis_key_reached_last)
47 remaining = 0
48 else
49 remaining = limit - room_comment.count
50 last_comment_id = room_comment[0]["id"] rescue nil
51 end
52 else
53 room_comment = []
54 last_comment_id = nil
55 remaining = limit
56 end
57 if remaining > 0
58 load = true
59 end
60 while remaining > 0
61 if remaining < 100
62 room_limit = remaining
63 else
64 room_limit = 100
65 end
66 begin
67 load_comment = qiscus_sdk.load_comments(qiscus_token, id, last_comment_id, nil, nil, room_limit)
68 rescue
69 load_comment = []
70 end
71 if load_comment.empty?
72 $redis.set(redis_key_reached_last, "true")
73 $redis.expire(redis_key_reached_last, expiry_time)
74 break
75 else
76 room_comment = room_comment + load_comment
77 last_comment_id = load_comment.sort_by { |h| h["unix_nano_timestamp"] }[0]["id"]
78 end
79 remaining = remaining - 100
80 end
81 if load
82 $redis.set(redis_key, Marshal.dump(room_comment))
83 $redis.expire(redis_key, expiry_time)
84 end
85 comments << room_comment.sort_by { |h| -h["unix_nano_timestamp"] }.first(limit)
86 end
87 }
88 end
89 pool.shutdown
90 pool.wait_for_termination
91 end
92 rescue Timeout::Error
93 pool.kill
94 end
95 end
96 comments = (Array.new(comments.size) { comments.pop }).flatten.select { |comment| comment["type"] != "system_event" and comment["type"] != "file_attachment" and comment["message"].downcase.include? message.downcase }
97
98 if comments.empty?
99 response_message = "Messages '#{message}' not found"
100 else
101 response_message = "Messages '#{message}' found"
102 emails = comments.map{|x| x["email"]}
103 contact_user = User.where(qiscus_email: emails).select(:id, :qiscus_email)
104 contact_id = contact_user.pluck(:id)
105 contacts = @current_user.contacts.where(contact_id: contact_id, is_active: true).select(:contact_id, :contact_name).map(&:attributes)
106 contact_user = contact_user.map(&:attributes)
107 contacts.each do |c|
108 user = contact_user.select {|u| u["id"] == c["contact_id"] }[0]
109 c.merge!("qiscus_email" => user["qiscus_email"])
110 end
111
112 comments.each do |c|
113 contact_name = nil
114 is_contact = false
115 contact = contacts.select {|u| u["qiscus_email"] == c["email"] }[0]
116 if contact
117 contact_name = contact["contact_name"]
118 is_contact = true
119 end
120 c.merge!("is_contact" => is_contact)
121 c.merge!("contact_name" => contact_name)
122 end
123 end
124
125 render json: {
126 message: response_message,
127 data: comments.sort_by { |h| -h["unix_nano_timestamp"] }
128 }
129
130 rescue ActiveRecord::RecordInvalid => e
131 msg = ""
132 e.record.errors.map do |k, v|
133 key = k.to_s.humanize
134 msg = msg + "#{key} #{v}, "
135 end
136
137 msg = msg.chomp(", ") + "."
138 render json: {
139 error: {
140 message: msg
141 }
142 }, status: 422 and return
143
144 rescue => e
145 render json: {
146 error: {
147 message: e.message,
148 class: e.class.name
149 }
150 }, status: 422 and return
151 end
152 end
153
154 # =begin
155 # @apiVersion 1.0.0
156 # @api {get} /api/v1/chat/conversations/search_room Get conversation Chat By Room Name
157 # @apiName SearcRoomName
158 # @apiGroup Chat
159 #
160 # @apiParam {String} access_token User access token
161 # @apiParam {String} room_name room name
162 # @apiParam {Number} page page(optional)
163 # @apiParam {Number} limit limit(optional)
164
165 # =end
166
167 def search_room
168 begin
169 room_name = params[:room_name]
170 if !params[:room_name].present?
171 raise InputError.new("room_name must be present.")
172 end
173
174 page = params[:page].to_i
175 if !params[:page].present?
176 page = 1
177 end
178
179 if params[:page].present? && page.to_i <= 0
180 raise InputError.new("page invalid.")
181 end
182
183 per_page = params[:limit].to_i
184 if !params[:limit].present?
185 per_page = 10
186 end
187
188 if params[:limit].present? && per_page.to_i <= 0
189 raise InputError.new("limit invalid.")
190 end
191
192 # get current time, to measure calling time each process
193 start_time_pluck_room_id = Time.now
194 end_time_pluck_room_id = Time.now
195
196 contact_ids = Contact.where(:user_id => @current_user.id
197 ).where("lower(contact_name) LIKE lower(?)", "%#{room_name}%").pluck(:contact_id)
198 user_ids = User.where("lower(fullname) LIKE lower(?)", "%#{room_name}%").pluck(:id)
199 target_ids = contact_ids + user_ids
200 target_ids = target_ids.uniq
201
202 chat_user_ids = ChatUser.where(:user_id => @current_user.id).pluck(:chat_room_id)
203 chat_rooms = ChatRoom.where("target_user_id = (?) or user_id = (?) or chat_rooms.id IN (?)", @current_user.id, @current_user.id, chat_user_ids)
204
205 chat_rooms = chat_rooms.where(
206 "is_group_chat = true and lower(group_chat_name) LIKE lower(?)
207 or is_group_chat = false and target_user_id IN (?)
208 or is_group_chat = false and user_id IN (?)", "%#{room_name}%", target_ids, target_ids)
209 qiscus_room_ids = chat_rooms.pluck(:qiscus_room_id)
210
211 chat_room_sdk_info = []
212
213 # if not empty qiscus room id, then call sdk to avoid error
214 start_time_sdk = Time.now
215 if qiscus_room_ids.empty? == false
216 qiscus_sdk = QiscusSdk.new(@current_user.application.app_id, @current_user.application.qiscus_sdk_secret)
217 sdk_status, chat_room_sdk_info = qiscus_sdk.get_rooms_info_mobile(
218 @current_user.qiscus_email, qiscus_room_ids, @current_user.qiscus_token)
219
220 if sdk_status != 200
221 raise InputError.new(chat_room_sdk_info['error']['detailed_messages'].to_a.join(", ").capitalize)
222 end
223 end
224 end_time_sdk = Time.now
225
226 start_chat_room_load = Time.now
227 chat_rooms_total, chat_rooms = SearchRoomHelper.load_for(@current_user, chat_rooms, chat_room_sdk_info, page, per_page)
228
229 end_chat_room_load = Time.now
230
231 pluck_room_id_time = (end_time_pluck_room_id - start_time_pluck_room_id) * 1000
232 sdk_call_time = (end_time_sdk - start_time_sdk) * 1000
233 chat_room_load = (end_chat_room_load - start_chat_room_load) * 1000
234
235 # roundup total_page
236 total_page = (chat_rooms_total/per_page.to_f).ceil
237
238 render json: {
239 meta: {
240 total: chat_rooms_total,
241 per_page: per_page,
242 total_page: ((chat_rooms_total / per_page) <= 0) ? 1 : (total_page),
243 current_page: (params[:page].to_i <= 0) ? 1 : params[:page].to_i,
244 debug: {
245 # pluck_room_id: "#{pluck_room_id_time} ms",
246 sdk_call: "#{sdk_call_time} ms",
247 chat_room_load: "#{chat_room_load} ms",
248 total_time: "#{pluck_room_id_time + sdk_call_time + chat_room_load} ms"
249 }
250 },
251 data: chat_rooms
252 } and return
253 rescue => e
254 render json: {
255 error: {
256 code: 422,
257 message: e.message,
258 class: e.class.name
259 }
260 }, status: 422 and return
261 end
262 end
263