emile 10 months ago
parent d3df878d0e
commit 80c0ddb33d

Binary file not shown.

@ -439,7 +439,6 @@
.onlineDot {
width: 15px;
height: 15px;
background-color: #1ABC9C;
/* Fallback color */
border-radius: 50%;
position: relative;

@ -38,11 +38,14 @@ class OsitcomVisitor(WebsocketConsumer):
session_id = session_id,
ip_address = text_data_json.get('client_ip'),
country = text_data_json.get('client_country'),
browser_name = text_data_json.get('browser_name'),
os_name = text_data_json.get('os_name'),
)
self.current_log = VisitorLog.objects.create(
visitor = self.visitor,
referrer = text_data_json.get('referrer'),
title = text_data_json.get('title'),
url = text_data_json.get('url'),
visit_date = datetime.now()
)
@ -86,7 +89,6 @@ class Osichat(WebsocketConsumer):
)
if event_type == 'get_visitors':
print('eeewew')
event = {
'type': 'get_visitors_handler',
}
@ -154,7 +156,7 @@ class Osichat(WebsocketConsumer):
if self.client_type == 'mobile_admin':
chat_room_data = model_to_dict(chat_room)
self.send(text_data=json.dumps({
'event_type': 'new_update',
'event_type': 'new_chat_update',
'chat_room_data': chat_room_data,
}, cls=DjangoJSONEncoder))
else:
@ -168,13 +170,18 @@ class Osichat(WebsocketConsumer):
def get_visitors_handler(self, event):
visitors = Visitor.objects.all().order_by('-id')
today = timezone.now().date()
visitors = Visitor.objects.filter(visitorlog__visit_date__date=today).annotate(latest_visit=Max('visitorlog__visit_date')).order_by('-latest_visit')
context = {
'visitors': visitors,
}
if self.client_type == 'mobile_admin':
visitors_data = []
for visitor in visitors:
visitors_data.append({
'is_online': visitor.is_online,
'duration': visitor.total_duration,
})
visitor_dict = model_to_dict(visitor)
visitor_logs = VisitorLog.objects.filter(visitor=visitor)
@ -193,7 +200,29 @@ class Osichat(WebsocketConsumer):
self.send(text_data=json.dumps({
'event_type': 'get_visitors',
'html': html,
}))
}))
def new_visitor_update_handler(self, event):
visitor = get_object_or_404(Visitor, id=event['visitor_id'])
context = {
'visitor': visitor,
}
if self.client_type == 'mobile_admin':
visitor_data = model_to_dict(visitor)
self.send(text_data=json.dumps({
'event_type': 'new_visitor_update',
'visitor_data': visitor_data,
'action': event['action'],
}, cls=DjangoJSONEncoder))
else:
html = render_to_string("chat_templates/partials/new-visitor.html", context=context)
self.send(text_data=json.dumps({
'event_type': 'new_visitor_update',
'visitor_id': visitor.id,
'action': event['action'],
'html': html,
}))
@ -209,7 +238,6 @@ class OsitcomChatRoom(WebsocketConsumer):
self.visitor = Visitor.objects.filter(session_id=self.session_id).last()
if not self.visitor:
self.close()
return
if self.scope['url_route']['kwargs'].get('chat_id'): #Case where admin is accessing a specific conversation between the conversations of this visior
self.chat_room = get_object_or_404(ChatRoom, id=self.scope['url_route']['kwargs'].get('chat_id'))

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-08-09 08:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('osichat', '0021_chatroomreview_details'),
]
operations = [
migrations.AddField(
model_name='visitorlog',
name='title',
field=models.CharField(blank=True, max_length=500, null=True),
),
]

@ -0,0 +1,23 @@
# Generated by Django 4.2.5 on 2024-08-09 09:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('osichat', '0022_visitorlog_title'),
]
operations = [
migrations.AddField(
model_name='visitor',
name='browser_name',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='visitor',
name='os_name',
field=models.CharField(blank=True, max_length=100, null=True),
),
]

@ -15,19 +15,65 @@ class Visitor(models.Model):
name = models.CharField(max_length=200, blank=True, null=True)
mobile_number = models.CharField(max_length=10, null=True, blank=True)
email = models.CharField(max_length=100, null=True, blank=True)
browser_name = models.CharField(max_length=100, null=True, blank=True)
os_name = models.CharField(max_length=100, null=True, blank=True)
@property
def flag_image_url(self):
flag_url = f"https://osina.ositcom.com/static/images/flags/{self.country.lower()}.svg"
return flag_url
@property
def is_online(self):
latest_log = self.visitorlog_set.order_by('-visit_date').first()
if latest_log and latest_log.left_date is None:
return True
return False
@property
def total_duration(self):
total_seconds = 0
for log in self.visitorlog_set.all():
end_time = log.left_date if log.left_date else datetime.now(timezone.utc)
total_seconds += (end_time - log.visit_date).total_seconds()
duration = timedelta(seconds=total_seconds)
hours, remainder = divmod(duration.total_seconds(), 3600)
minutes, seconds = divmod(remainder, 60)
if hours >= 1:
return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"
else:
return f"{int(minutes):02}:{int(seconds):02}"
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
channel_layer = get_channel_layer()
event = {
'type': 'new_visitor_update_handler',
'visitor_id': self.id,
'action': 'new_visitor'
}
async_to_sync(channel_layer.group_send)("osichat", event)
class VisitorLog(models.Model):
visitor = models.ForeignKey(Visitor, on_delete=models.CASCADE, null=True)
url = models.URLField()
title = models.CharField(null=True, blank=True, max_length=500)
referrer = models.URLField(null=True, blank=True)
visit_date = models.DateTimeField(null=True)
left_date = models.DateTimeField(null=True)
def save(self, *args, **kwargs):
if self.left_date:
action = 'end_log'
else:
action = 'new_log'
super().save(*args, **kwargs)
channel_layer = get_channel_layer()
event = {
'type': 'new_visitor_update_handler',
'visitor_id': self.visitor.id,
'action': action
}
async_to_sync(channel_layer.group_send)("osichat", event)
class ChatRoom(models.Model):
@ -51,10 +97,7 @@ class ChatRoom(models.Model):
return last_updated_time.strftime('%d-%m-%Y')
def unread_messages(self, user):
return ChatMessage.objects.filter(room=self).exclude(member=user).exclude(chatmessageseen__member=user).count()
def save(self, *args, **kwargs):
is_new = self.pk is None
super().save(*args, **kwargs)
channel_layer = get_channel_layer()
event = {
@ -98,7 +141,6 @@ class ChatMessage(models.Model):
content = models.TextField(null=True, blank=True)
date_sent = models.DateTimeField()
def save(self, *args, **kwargs):
is_new = self.pk is None
super().save(*args, **kwargs)
channel_layer = get_channel_layer()
event = {

@ -2,6 +2,7 @@ from django.urls import path
from .consumers import *
websocket_urlpatterns = [
path("ws/osichat/visitors/", OsitcomVisitor.as_asgi()),
path("ws/osichat/", Osichat.as_asgi()),
path("ws/osichat/<str:session_id>/", OsitcomChatRoom.as_asgi()),
path("ws/osichat-admin/<str:session_id>/<int:chat_id>/", OsitcomChatRoom.as_asgi()),

@ -0,0 +1,24 @@
{% load static %}
<div data-visitorid = {{visitor.id}} class="visitor w-full flex justify-between gap-2 items-center py-3 px-3 border-b border-gray-200 text-sm cursor-pointer bg-white">
<div class="w-full flex gap-2 items-center">
<div class="w-[40px] h-[40px] bg-osiblue rounded-full relative">
{% if not visitor.country == 'Unknown' %}
<img src="{{visitor.flag_image_url}}">
{% else %}
<img class="w-[30px] h-[25px] absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2" src="{% static 'images/ositcom_logos/ositcomwhite(o).png' %}">
{% endif %}
{% if visitor.is_online %}
<div class="online absolute w-[12px] h-[12px] border-2 border-white bg-green-600 rounded-full bottom-0 right-0"></div>
{% endif %}
</div>
<div class="flex flex-col">
<p class="text-secondosiblue font-poppinsBold">{{visitor.ip_address}}</p>
<p class="text-fifthosiblue text-xs">{{visitor.visitorlog_set.all.last.url}}</p>
</div>
</div>
<p class="duration text-gray-500 text-sm">{{visitor.total_duration}}</p>
</div>

@ -28,5 +28,4 @@
<audio id="notification-sound" src="{{domain}}/static/notifications/osichat-notification.mp3" preload="auto"></audio>
<audio id="out-notification-sound" src="{{domain}}/static/notifications/out-osichat-notification.wav"
preload="auto"></audio>
<audio id="out-notification-sound" src="{{domain}}/static/notifications/out-osichat-notification.wav" preload="auto"></audio>

@ -1,30 +1,47 @@
{% load static %}
<!-- VISITORS -->
<div class="h-full flex flex-col" id="visitors">
<div class="h-full flex flex-col">
<div class="w-full py-3 px-3 border-b border-gray-200 flex items-center gap-2">
<div class="w-[20px] h-[20px] bg-green-600 rounded-full onlineDot">
<div class="wave"></div>
</div>
<p class="text-secondosiblue">Active</p>
<p class="text-secondosiblue">Visitors today</p>
</div>
{% for visitor in visitors %}
<div class="w-full flex justify-between gap-2 items-center py-3 px-3 border-b border-gray-200 text-sm cursor-pointer bg-white">
<div class="w-full flex gap-2 items-center">
<div class="w-[40px] h-[40px] rounded-full">
<img src="{{visitor.flag_image_url}}">
</div>
<div id="visitors">
{% for visitor in visitors %}
<div data-visitorid = {{visitor.id}} class="visitor w-full flex justify-between gap-2 items-center py-3 px-3 border-b border-gray-200 text-sm cursor-pointer bg-white">
<div class="w-full flex gap-2 items-center">
<div class="w-[40px] h-[40px] bg-osiblue rounded-full relative">
{% if not visitor.country == 'Unknown' %}
<img src="{{visitor.flag_image_url}}">
{% else %}
<img class="w-[30px] h-[25px] absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2" src="{% static 'images/ositcom_logos/ositcomwhite(o).png' %}">
{% endif %}
{% if visitor.is_online %}
<div class="online absolute w-[12px] h-[12px] border-2 border-white bg-green-600 rounded-full bottom-0 right-0"></div>
{% endif %}
</div>
<div class="flex flex-col">
<p class="text-secondosiblue font-poppinsBold">{{visitor.ip_address}}</p>
<p class="text-fifthosiblue text-xs">{{visitor.visitorlog_set.all.last.url}}</p>
<div class="flex flex-col">
<p class="text-secondosiblue font-poppinsBold">{{visitor.ip_address}}</p>
<p class="text-fifthosiblue text-xs">
{% if not visitor.visitorlog_set.all.last.title %}
{{ visitor.visitorlog_set.all.last.url|slice:":26" }}{% if visitor.visitorlog_set.all.last.url|length > 26 %}…{% endif %}
{% else %}
{{ visitor.visitorlog_set.all.last.title|slice:":26" }}{% if visitor.visitorlog_set.all.last.title|length > 26 %}…{% endif %}
{% endif %}
</p>
</div>
</div>
</div>
<p class="text-gray-500 text-sm">00:00</p>
<p class="duration text-gray-500 text-sm">{{visitor.total_duration}}</p>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
<audio id="visitor-notification-sound" src="{{domain}}/static/notifications/new-visitor.mp3" preload="auto"></audio>

@ -1453,10 +1453,6 @@ video {
width: 100%;
}
.w-\[\] {
width: ;
}
.min-w-full {
min-width: 100%;
}
@ -2264,11 +2260,21 @@ video {
fill: #374a7a;
}
.object-contain {
-o-object-fit: contain;
object-fit: contain;
}
.object-cover {
-o-object-fit: cover;
object-fit: cover;
}
.object-center {
-o-object-position: center;
object-position: center;
}
.p-1 {
padding: 0.25rem;
}
@ -3228,7 +3234,6 @@ video {
.onlineDot {
width: 15px;
height: 15px;
background-color: #1ABC9C;
/* Fallback color */
border-radius: 50%;
position: relative;

@ -55,6 +55,7 @@ function initializeOsichat(){
case 'get_visitors':
leftDynamicDiv.innerHTML = data.html;
setupDurationUpdater();
break;
case 'new_chat_update':
@ -90,9 +91,31 @@ function initializeOsichat(){
}
}
setInterval(updateOnlineDurations, 1000);
appendInnerConversationScript(leftDiv);
break;
case 'new_visitor_update':
const visitorsContainer = document.getElementById('visitors');
const visitorDiv = visitorsContainer.querySelector(`.visitor[data-visitorid='${data.visitor_id}']`);
if (data.action === 'new_log') {
if (visitorDiv){
visitorDiv.remove();
}
const newVisitorDiv = document.createElement('div');
newVisitorDiv.innerHTML = data.html;
visitorsContainer.insertAdjacentElement('afterbegin', newVisitorDiv.firstElementChild);
const visitorNotificationSound = document.getElementById('visitor-notification-sound');
visitorNotificationSound.play();
} else if (data.action === 'end_log') {
const newVisitorDiv = document.createElement('div');
newVisitorDiv.innerHTML = data.html;
visitorDiv.replaceWith(newVisitorDiv.firstElementChild);
}
setupDurationUpdater();
break;
default:
console.log('Unknown event type:', data.event_type);
@ -149,5 +172,46 @@ function switchToVisitors() {
chatsTab.addEventListener('click', switchToChats);
visitorsTab.addEventListener('click', switchToVisitors);
let updateInterval;
function updateOnlineDurations() {
document.querySelectorAll('.visitor').forEach(visitorDiv => {
const onlineIndicator = visitorDiv.querySelector('.online');
if (onlineIndicator) {
const durationElem = visitorDiv.querySelector('.duration');
const currentText = durationElem.textContent;
const parts = currentText.split(':').map(Number);
let hours = 0, minutes = 0, seconds = 0;
if (parts.length === 3) {
[hours, minutes, seconds] = parts;
} else if (parts.length === 2) {
[minutes, seconds] = parts;
}
const totalSeconds = hours * 3600 + minutes * 60 + seconds;
const updatedTotalSeconds = totalSeconds + 1;
const updatedHours = Math.floor(updatedTotalSeconds / 3600);
const updatedMinutes = Math.floor((updatedTotalSeconds % 3600) / 60);
const updatedSeconds = updatedTotalSeconds % 60;
const formattedDuration =
(updatedHours > 0 ? `${String(updatedHours).padStart(2, '0')}:` : '') +
`${String(updatedMinutes).padStart(2, '0')}:${String(updatedSeconds).padStart(2, '0')}`;
durationElem.textContent = formattedDuration;
}
});
}
function setupDurationUpdater() {
// Clear any existing interval to avoid multiple intervals
if (updateInterval) {
clearInterval(updateInterval);
}
// Set up a new interval
updateInterval = setInterval(updateOnlineDurations, 1000);
}
initializeOsichat();

@ -0,0 +1,27 @@
function updateOnlineDurations() {
document.querySelectorAll('.visitor').forEach(visitorDiv => {
if (visitorDiv.querySelector('.online')) {
const durationElem = visitorDiv.querySelector('.duration');
const currentText = durationElem.textContent;
const [hours, minutes, seconds] = currentText.split(':').map(Number);
const totalSeconds = hours * 3600 + minutes * 60 + seconds;
// Increment the duration by 1 second
const updatedTotalSeconds = totalSeconds + 1;
const updatedHours = Math.floor(updatedTotalSeconds / 3600);
const updatedMinutes = Math.floor((updatedTotalSeconds % 3600) / 60);
const updatedSeconds = updatedTotalSeconds % 60;
// Format the updated duration
const formattedDuration =
(updatedHours > 0 ? `${String(updatedHours).padStart(2, '0') + ':' : ''}` +
`${String(updatedMinutes).padStart(2, '0')}:${String(updatedSeconds).padStart(2, '0')}`);
// Update the duration element
durationElem.textContent = formattedDuration;
}
});
}
// Call this function at intervals to update durations
setInterval(updateOnlineDurations, 1000); // Update every second

@ -3,6 +3,7 @@ const protocol = window.location.protocol === "https:" ? "https" : "http";
const osinaDomain = "osina.ositcom.com";
const ositcomDomain = "ositcom.com";
async function fetchClientData() {
let clientData = { client_ip: 'Unknown', client_country: 'Unknown' };
while (clientData.client_ip === 'Unknown') {
@ -39,23 +40,69 @@ async function fetchVisitorsSession() {
return session_id;
}
const getBrowserInfo = () => {
const userAgent = navigator.userAgent;
let browserName = "Unknown";
let osName = "Unknown";
if (userAgent.indexOf("Firefox") > -1) {
browserName = "Firefox";
} else if (userAgent.indexOf("SamsungBrowser") > -1) {
browserName = "Samsung Internet";
} else if (userAgent.indexOf("Opera") > -1 || userAgent.indexOf("OPR") > -1) {
browserName = "Opera";
} else if (userAgent.indexOf("Trident") > -1) {
browserName = "Internet Explorer";
} else if (userAgent.indexOf("Edge") > -1) {
browserName = "Edge";
} else if (userAgent.indexOf("Chrome") > -1) {
browserName = "Chrome";
} else if (userAgent.indexOf("Safari") > -1) {
browserName = "Safari";
}
if (userAgent.indexOf("Win") > -1) {
osName = "Windows";
} else if (userAgent.indexOf("Mac") > -1) {
osName = "MacOS";
} else if (userAgent.indexOf("X11") > -1) {
osName = "UNIX";
} else if (userAgent.indexOf("Linux") > -1) {
osName = "Linux";
} else if (userAgent.indexOf("Android") > -1) {
osName = "Android";
} else if (userAgent.indexOf("like Mac") > -1) {
osName = "iOS";
}
return { browserName, osName };
};
async function initializeVisitorsWebSocket() {
const referrer = document.referrer;
const clientData = await fetchClientData();
const session_id = await fetchVisitorsSession();
const browserInfo = getBrowserInfo();
const visitorsSocketUrl = `${wsScheme}://${osinaDomain}/ws/osichat/visitors/`;
const visitorsSocket = new WebSocket(visitorsSocketUrl);
visitorsSocket.onopen = () => {
console.log('WebSocket connection to visitors established');
console.log('alooo'+window.document.title)
const event_message = {
'event_type': 'visitor_ping',
'referrer': referrer,
'url': window.location.href,
'title': window.document.title,
'client_ip': clientData.client_ip,
'client_country': clientData.client_country,
'browser_name': browserInfo.browserName,
'os_name': browserInfo.osName,
'session_id': session_id
};
visitorsSocket.send(JSON.stringify(event_message));

Loading…
Cancel
Save