/chat.js Secret
Created
September 18, 2021 21:32
Chat.js EthGlobal March
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function renderChat() { | |
manageNotLoggedInState(); | |
window.badWordFilter = new Filter(); | |
window.template = '<div class="message" style="padding-top:5px; word-break:break-word"><span class="c-msg-id" data-msg-id="" style="display:none"></span><img class="c-avatar" src="" width="18" height="18"><b class="c-name"></b><span class="c-message"></span><span class="c-time" style="display:none"></span><span class="c-type" data-chat-type="" style="display:none"><span class="c-user-id" data-user-id="" style="display:none"></span></div>'; | |
window.messages = new List('chatbox', { | |
valueNames: [ | |
'c-name', | |
'c-message', | |
'c-time', | |
{name: "c-avatar", attr: "src"}, | |
{name: "c-type", attr: "data-chat-type"}, | |
{name: "c-user-id", attr: "data-user-id"}, | |
{name: "c-msg-id", attr: "data-msg-id"}, | |
], | |
item: window.template, | |
indexAsync: true, | |
}, []); | |
window.messages.sort("c-time", {order: "asc"}); | |
setInterval(function() { | |
hideJoinAvatars(); | |
}, 100) | |
$('.auth-visible').hide(); | |
$("#openProfile").click(function() { | |
openApp("user"); | |
}) | |
$("#profile-avatar, #profile-switch").click(function() { | |
window.USER.gender = ["male", "female"].filter((g) => g != window.USER.gender)[0]; | |
updateProfileImage(); | |
}) | |
$("#profile-generate").click(function() { | |
updateProfileImage(true); | |
}) | |
$("#sendHeart").click(function() { | |
window.chat.sendHearts(); | |
}) | |
$("#profile-save").click(function() { | |
window.USER.name = $("#profile-name").val(); | |
window.USER.country = $("#profile-country").val(); | |
$(".flag").text(countryToEmoji(window.USER.country||"US")) | |
window.chat.updatePresence(true); | |
updateProfileInfo(); | |
}); | |
$("#profile-verify").click(function() { | |
let fname = ($("#profile-fname").val() || "").trim(); | |
let lname = ($("#profile-lname").val() || "").trim(); | |
let email = ($("#profile-email").val() || "").trim(); | |
if (fname.length > 1 && lname.length > 1 && email.length > 2 && email.indexOf("@") !== -1) { | |
$.ajax({ | |
url: "https://hack.ethglobal.co/ethernet", | |
type: "POST", | |
dataType: "json", | |
data: {first_name: fname, last_name: lname, email: email, summit: window.EG_EVENT.authSummit || "ETHGlobalTV"}, | |
success: function(data){ | |
if (data.success === true) { | |
$("#auth-msg").html("<b>Please check your email (and spam folder) for verification link!</b>").css("color", "red"); | |
} | |
}, | |
error: function(data) { | |
alert("backend error, please retry in a couple of mins or email hello@ethglobal.co") | |
} | |
}); | |
} else { | |
alert("invalid name or email") | |
} | |
}); | |
db.collection(`chats/${window.ROOM}/banlist`).onSnapshot(function(querySnapshot) { | |
querySnapshot.docChanges().forEach(function(change) { | |
let data = change.doc.data(); | |
if (change.type == "added" || change.type == "modified") { | |
window.state.banlist[data.id] = data; | |
} else if (change.type == "removed") { | |
delete window.state.banlist[data.id]; | |
} | |
}); | |
}) | |
db.collection(`chats/${window.ROOM}/messages`).orderBy("time", "desc").limit(300).onSnapshot(function(querySnapshot) { | |
querySnapshot.docChanges().forEach(function(change) { | |
let data = change.doc.data(); | |
if (change.type == "added") { | |
try { | |
if ((window.state.banlist[data.user_id]||{}).banned !== true) { | |
renderMessage(data, change.doc.id); | |
} | |
} catch(err) { | |
console.log("message renderError", err, data); | |
} | |
} else if (change.type == "removed") { | |
window.messages.remove("c-msg-id", change.doc.id); | |
} else if (change.type == "modified") { | |
} | |
}); | |
$('#messages').scrollTop($('#messages')[0].scrollHeight); | |
}) | |
db.collection("users").orderBy("last_active", "desc").onSnapshot(function(querySnapshot) { | |
querySnapshot.docChanges().forEach(function(change) { | |
let data = change.doc.data(); | |
let prev = {...window.activeUsers[data["id"]]}; | |
if (!data.id) {return} | |
window.activeUsers[data["id"]] = data; | |
updateMessages(data["id"]); | |
if (data.hearts > prev.hearts) { | |
queueHearts() | |
} | |
}); | |
renderPresence(); | |
}) | |
db.collection(`chats/${window.ROOM}/sessions`).orderBy("last_active", "desc").limit(250).onSnapshot(function(querySnapshot) { | |
querySnapshot.docChanges().forEach(function(change) { | |
let data = change.doc.data(); | |
if (!data.session) {return} | |
window.anonymousActiveUsers[data["session"]] = data; | |
}); | |
}) | |
db.collection(`chats`).doc(window.ROOM).onSnapshot(function(doc) { | |
let item = doc.data(); | |
syncStage(item); | |
}); | |
db.collection(`chats`).doc(window.ROOM).get().then(function(doc) { | |
let item = doc.data(); | |
setBackground((Object.keys(window.themes).includes(item.theme))? item.theme: "defi"); | |
if (item.action == "updateCaption") { | |
let isValid = Object.keys(window.state.agenda).includes(item.talkId) | |
if (isValid) { | |
updateCaption(item.talkId); | |
} else { | |
$("#subtitles").html(""); | |
} | |
} | |
if (item.countdownTs > (new Date()).getTime()) { | |
clearInterval(window.state.countdownId); | |
window.state.countdownId = null; | |
window.state.countdownTs = item.countdownTs; | |
if (window.state.countdownTs > (new Date()).getTime()) { | |
window.state.countdownId = setInterval(displayCountdown, 1000); | |
$("#subtitles").slideDown("slow"); | |
} else { | |
$("#subtitles").html(""); | |
} | |
} | |
}); | |
} | |
firebase.initializeApp({ | |
apiKey: "AIzaSyDgX5-mlLFv50I8TZ4uKkd1WMjk7ajnlHs", | |
authDomain: "webchat-288900.firebaseapp.com", | |
databaseURL: "https://webchat-288900.firebaseio.com", | |
projectId: "webchat-288900", | |
storageBucket: "webchat-288900.appspot.com", | |
messagingSenderId: "604854920267", | |
appId: "1:604854920267:web:72213ed0501c8d842899ce", | |
measurementId: "G-QEQJD16M6B" | |
}); | |
window.USER = {}; | |
window.ROOM = window.EG_EVENT.room || "ethglobal"; | |
window.activeUsers = {}; | |
window.anonymousActiveUsers = {}; | |
window.params = new URLSearchParams(window.location.search); | |
window.db = firebase.firestore(); | |
function queueHearts() { | |
let rate = 3; | |
let period = 2; | |
let current = (new Date()).getTime()/1000; | |
let timeDelta = current - window.state.lastCheck; | |
window.state.lastCheck = current; | |
window.state.heartAllowance += timeDelta * (rate/period); | |
if (window.state.heartAllowance > rate) { | |
window.state.heartAllowance = rate; | |
} | |
if (window.state.heartAllowance < 1.0){ | |
// console.log("discard") | |
} else { | |
let animate = function(t) { | |
return setTimeout(function() { | |
$("#hearts").hide(); | |
window.state.heartActiveId = null; | |
}, t) | |
} | |
$("#hearts").show(); | |
if (window.state.heartActiveId == null) { | |
window.state.heartActiveId = animate(1500); | |
} | |
window.state.heartAllowance -= 1; | |
} | |
} | |
function enableModeration() { | |
$(".message > .c-avatar").off("click") | |
$(".message > .c-avatar").on("click", function() { | |
let parent = $(this).parent(); | |
let docId = $(parent).find('.c-msg-id').attr('data-msg-id'); | |
let userId = $(parent).find('.c-user-id').attr('data-user-id'); | |
let message = $(parent).find('.c-message').text(); | |
let username = $(parent).find('.c-name').text(); | |
openApp("mod") | |
$('#mod-usr').text(username); | |
$('#mod-msg').text(message); | |
$('#mod-block').attr('data-doc', userId); | |
$('#mod-del').attr('data-doc', docId); | |
}); | |
$('#mod-del').off("click"); | |
$('#mod-del').on("click", function() { | |
let msgId = $(this).attr("data-doc"); | |
window.chat.deleteMessage(msgId); | |
}); | |
$('#mod-block').off("click"); | |
$('#mod-block').on("click", function() { | |
let userId = $(this).attr("data-doc"); | |
window.chat.banUser(userId, true); | |
}); | |
} | |
function renderPresence() { | |
let html = ""; | |
let activeCount = 0; | |
let anonActiveCount = 0; | |
let sortedUsers = Object.values(window.activeUsers); | |
sortedUsers.sort(function(a, b) { | |
let aName = (a.name||"").toLowerCase().trim(); | |
let bName = (b.name||"").toLowerCase().trim(); | |
return (aName > bName) ? 1 : -1 | |
}) | |
let anonSortedUsers = Object.values(window.anonymousActiveUsers); | |
for(let u in anonSortedUsers) { | |
let user = anonSortedUsers[u]; | |
let activeWindow = (new Date()).getTime()/1000 - 180; | |
if (user.last_active !== null && (user.last_active.seconds < activeWindow ) ) { | |
continue; | |
} | |
anonActiveCount += 1; | |
} | |
for(let u in sortedUsers) { | |
let user = sortedUsers[u]; | |
let activeWindow = (new Date()).getTime()/1000 - 180; | |
if (user.last_active !== null && (user.last_active.seconds < activeWindow ) ) { | |
continue; | |
} | |
activeCount += 1; | |
html += ` | |
<div class="message" style="padding-top:5px; display:flex;"> | |
<div class="presence-name"> | |
<img src="${generateAvatar(user)}" width="18" height="18"> | |
<b>${DOMPurify.sanitize(user.name)}</b><br> | |
</div> | |
<div class="presence-country"> | |
${countryToEmoji(user.country)} | |
</div> | |
</div> | |
` | |
} | |
$('.online-count').text(activeCount) | |
$('.total-count').text(activeCount+anonActiveCount); | |
$("#viewers-list").html(html); | |
hideJoinAvatars(); | |
} | |
function hideJoinAvatars() { | |
// $('*[data-chat-Type="join"]').parent().find('.c-avatar').hide(); | |
} | |
window.renderMessage = function(data, docId) { | |
let addObj = { | |
"c-time": (data.time) ? data.time.seconds + data.time.nanoseconds/10**9 : (new Date()).getTime()/1000, | |
"c-type": data.type, | |
"c-user-id": data.user_id, | |
"c-msg-id": docId, | |
"c-name": DOMPurify.sanitize(data.name.replace(/<[^>]*>/g, '')), | |
"c-avatar": generateAvatar(data), | |
"c-message": "<br>" + linkifyHtml(DOMPurify.sanitize(window.badWordFilter.clean(data.message)).replace(/(<([^>]+)>)/ig, ""), {target: "_blank"}), | |
}; | |
if (data.type == "join") { | |
addObj["c-name"] = ""; | |
addObj["c-message"] = DOMPurify.sanitize(data.name).replace(/(<([^>]+)>)/ig, "") + " joined the chat"; | |
} | |
addObj["c-message"] = addObj["c-message"].slice(0, 500); | |
addObj["c-name"] = addObj["c-name"].slice(0, 50); | |
window.messages.add(addObj); | |
window.messages.sort("c-time", {order: "asc"}); | |
} | |
function manageNotLoggedInState() { | |
let storage = window.localStorage; | |
let sessionId = storage.getItem("EGTVSessionID"); | |
if (sessionId !== null) { | |
if (window.LOGGED_IN !== true) { | |
updateAnonPresence(storage.getItem("EGTVSessionNew") != "yes"); | |
setInterval(updateAnonPresence, 30*1000); | |
} | |
} else { | |
storage.setItem("EGTVSessionID", guid()); | |
storage.setItem("EGTVSessionNew", "yes"); | |
setTimeout(function() {updateAnonPresence(true);}, 500); | |
setInterval(updateAnonPresence, 30*1000); | |
} | |
} | |
function updateAnonPresence(firstRun) { | |
let payload = {}; | |
let sessionId = window.localStorage.getItem("EGTVSessionID"); | |
if (sessionId == null || window.LOGGED_IN == true) {return;} | |
payload.session = sessionId; | |
payload.pulse = firebase.firestore.FieldValue.increment(1); | |
payload.last_active = firebase.firestore.FieldValue.serverTimestamp(); | |
if (firstRun) { | |
payload.first_active = firebase.firestore.FieldValue.serverTimestamp(); | |
} | |
window.db.doc(`chats/${window.ROOM}/sessions/${sessionId}`).set(payload, { merge: true }); | |
} | |
window.chat = { | |
sendMessage: function(message, type) { | |
if (window.LOGGED_IN !== true) {return;} | |
if (message.length > 500) {alert("message too long");} | |
window.db.collection(`chats/${window.ROOM}/messages`).add({ | |
name: (window.USER.name||"").slice(0, 50), | |
message: (message||"").slice(0, 500), | |
type: type||"message", | |
user_id: window.USER.id, | |
guid: window.USER.guid, | |
gender: window.USER.gender, | |
time: firebase.firestore.FieldValue.serverTimestamp(), | |
}) | |
}, | |
updatePresence: function(full) { | |
if (window.LOGGED_IN !== true) {return;} | |
let payload = (full === true) ? {...window.USER} : {}; | |
payload.name = window.USER.name; | |
payload.last_active = firebase.firestore.FieldValue.serverTimestamp(); | |
window.db.doc(`users/${window.USER.id}`).set(payload, { merge: true }); | |
}, | |
sendHearts: function() { | |
if (window.LOGGED_IN !== true) {return;} | |
let payload = {}; | |
payload.hearts = firebase.firestore.FieldValue.increment(1); | |
window.db.doc(`users/${window.USER.id}`).set(payload, { merge: true }); | |
}, | |
broadcast: function(body) { | |
if (window.LOGGED_IN !== true) {return;} | |
window.db.doc(`chats/${window.ROOM}`).set(body, { merge: true }); | |
}, | |
deleteMessage: function(docId) { | |
if (window.LOGGED_IN !== true) {return;} | |
window.db.doc(`chats/${window.ROOM}/messages/${docId}`).delete(); | |
}, | |
banUser: function(userId, banned) { | |
if (window.LOGGED_IN !== true) {return;} | |
window.db.doc(`chats/${window.ROOM}/banlist/${userId}`).set({id: userId, banned: banned}, { merge: true }); | |
}, | |
} | |
function getUserMeta() { | |
$.get("http://ip-api.com/json/?fields=status,countryCode,country,timezone,city", function(resp) { | |
if (resp.status == "success") { | |
if (!window.USER.country) { | |
window.USER.country = resp.countryCode; | |
// updateProfileInfo(); | |
} | |
} | |
}) | |
} | |
function guid() { | |
return 'xxxxxxxxxxzxxxyxxxxxxxxx'.replace(/[xy]/g, function(c) { | |
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); | |
return v.toString(16); | |
}); | |
} | |
function countryToEmoji(cc) { | |
if (!cc) {return "";} | |
let chars = [...(cc.toUpperCase()||"").slice(0,2)].map(c => c.charCodeAt() + 127397); | |
return String.fromCodePoint(...chars); | |
} | |
function updateProfileImage(generate) { | |
if (generate) { | |
window.USER.guid = guid(); | |
} | |
$('#profile-avatar').attr('src', generateAvatar(window.USER)); | |
updateProfileInfo(); | |
} | |
function generateAvatar(user) { | |
return `https://avatars.dicebear.com/api/${user.gender}/${user.guid}.svg?mood[]=happy`; | |
} | |
function updateMessages(userId) { | |
let items = window.messages.get('c-user-id', userId); | |
for (let i=0; i<items.length; i++) { | |
let user = window.activeUsers[userId]; | |
let existing = items[i].values(); | |
let payload = {"c-avatar": generateAvatar(user)}; | |
if (existing["c-type"] !== "join") { | |
payload["c-name"] = user.name; | |
} | |
items[i].values(payload); | |
} | |
} | |
function updateProfileInfo() { | |
if (window.USER.name != null) { | |
$("#profile-name").val(window.USER.name); | |
} | |
if (window.USER.email != null) { | |
$("#profile-email").val(window.USER.email); | |
} | |
if (window.USER.country != null) { | |
$("#profile-country").val(window.USER.country||"US"); | |
$(".flag").text(countryToEmoji(window.USER.country||"US")) | |
} | |
if (window.USER.name != null) { | |
let info = `<div style="border-bottom:1px solid #ccc; overflow: hidden; white-space: nowrap; cursor:pointer"> | |
<img src="${generateAvatar(window.USER)}" width="32" height="32"> | |
<b>${window.USER.name}</b> | |
</div>` | |
$("#profile").html(info) | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment