mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 18:47:51 +00:00
Previously, we returned the static value 'online' when the status was not present in the Redis cache. This PR changes it to fall back to the DB value and updates the cache in such cases. fixes: https://linear.app/chatwoot/issue/CW-3233/write-a-back-up-for-online-status-in-case-if-redis-keys-are-not
78 lines
2.9 KiB
Ruby
78 lines
2.9 KiB
Ruby
class OnlineStatusTracker
|
|
# NOTE: You can customise the environment variable to keep your agents/contacts as online for longer
|
|
PRESENCE_DURATION = ENV.fetch('PRESENCE_DURATION', 20).to_i.seconds
|
|
|
|
# presence : sorted set with timestamp as the score & object id as value
|
|
|
|
# obj_type: Contact | User
|
|
def self.update_presence(account_id, obj_type, obj_id)
|
|
::Redis::Alfred.zadd(presence_key(account_id, obj_type), Time.now.to_i, obj_id)
|
|
end
|
|
|
|
def self.get_presence(account_id, obj_type, obj_id)
|
|
connected_time = ::Redis::Alfred.zscore(presence_key(account_id, obj_type), obj_id)
|
|
connected_time && connected_time > (Time.zone.now - PRESENCE_DURATION).to_i
|
|
end
|
|
|
|
def self.presence_key(account_id, type)
|
|
case type
|
|
when 'Contact'
|
|
format(::Redis::Alfred::ONLINE_PRESENCE_CONTACTS, account_id: account_id)
|
|
else
|
|
format(::Redis::Alfred::ONLINE_PRESENCE_USERS, account_id: account_id)
|
|
end
|
|
end
|
|
|
|
# online status : online | busy | offline
|
|
# redis hash with obj_id key && status as value
|
|
|
|
def self.set_status(account_id, user_id, status)
|
|
::Redis::Alfred.hset(status_key(account_id), user_id, status)
|
|
end
|
|
|
|
def self.get_status(account_id, user_id)
|
|
::Redis::Alfred.hget(status_key(account_id), user_id)
|
|
end
|
|
|
|
def self.status_key(account_id)
|
|
format(::Redis::Alfred::ONLINE_STATUS, account_id: account_id)
|
|
end
|
|
|
|
def self.get_available_contact_ids(account_id)
|
|
range_start = (Time.zone.now - PRESENCE_DURATION).to_i
|
|
# exclusive minimum score is specified by prefixing (
|
|
# we are clearing old records because this could clogg up the sorted set
|
|
::Redis::Alfred.zremrangebyscore(presence_key(account_id, 'Contact'), '-inf', "(#{range_start}")
|
|
::Redis::Alfred.zrangebyscore(presence_key(account_id, 'Contact'), range_start, '+inf')
|
|
end
|
|
|
|
def self.get_available_contacts(account_id)
|
|
# returns {id1: 'online', id2: 'online'}
|
|
get_available_contact_ids(account_id).index_with { |_id| 'online' }
|
|
end
|
|
|
|
def self.get_available_users(account_id)
|
|
user_ids = get_available_user_ids(account_id)
|
|
|
|
return {} if user_ids.blank?
|
|
|
|
user_availabilities = ::Redis::Alfred.hmget(status_key(account_id), user_ids)
|
|
user_ids.map.with_index { |id, index| [id, (user_availabilities[index] || get_availability_from_db(account_id, id))] }.to_h
|
|
end
|
|
|
|
def self.get_availability_from_db(account_id, user_id)
|
|
availability = Account.find(account_id).account_users.find_by(user_id: user_id).availability
|
|
set_status(account_id, user_id, availability)
|
|
availability
|
|
end
|
|
|
|
def self.get_available_user_ids(account_id)
|
|
account = Account.find(account_id)
|
|
range_start = (Time.zone.now - PRESENCE_DURATION).to_i
|
|
user_ids = ::Redis::Alfred.zrangebyscore(presence_key(account_id, 'User'), range_start, '+inf')
|
|
# since we are dealing with redis items as string, casting to string
|
|
user_ids += account.account_users.where(auto_offline: false)&.map(&:user_id)&.map(&:to_s)
|
|
user_ids.uniq
|
|
end
|
|
end
|