Skip to content

Instantly share code, notes, and snippets.

@C5H8NNaO4
Last active August 10, 2023 23:30
Show Gist options
  • Save C5H8NNaO4/f799297688f640d15c00e7fee8b4fa26 to your computer and use it in GitHub Desktop.
Save C5H8NNaO4/f799297688f640d15c00e7fee8b4fa26 to your computer and use it in GitHub Desktop.
WhatsApp Web - Displays Desktop Notifications when a user gets online [console]
(function (window) {
var DOUBLE_CLICK_DELAY = 550;
var OnlineNotifier = (function () {
var LOG_LEVEL = 01;
var POLL_TIMEOUT = 1E3;
var MIN_TIMEOUT = 60E3;
var MIN_TIMEOUT_TYPING = 5E3
var DEBUG_CHECK_ALL = false;
var DEBUG_CHECK_ONLINE = false;
var DEBUG_CHECK_TYPING = false;
var DEBUG_CHECK_COUNT = 27 * 3
var DEBUG_TIMEOUT = 1E3
var DEBUG_CHANCE = .33
var NOTIFY_SUBSCRIBE = true;
var debug = DEBUG_CHECK_ALL || DEBUG_CHECK_ONLINE || DEBUG_CHECK_TYPING
if (debug) {
POLL_TIMEOUT = DEBUG_TIMEOUT
}
var chats = Store.Chat.models.slice ();
var msg = {
online: "Online",
offline:"Offline",
typing: "Typing...",
unsubscribing: "...unsubscribing %s.",
subscribing: "... subscribing %s.",
check: "checking state: %s [%s].",
handle: "... check sucessfull. calling handler.",
look: "looking for people online... found %d.",
timeout: "scheduling next check in %dms.",
reset:"reseted",
started:"started",
state: "Last seen: %0",
subscribed:"enabled notifications",
unsubscribed:"disabled notifications",
ppurl: "https://dyn.web.whatsapp.com/pp?t=s&u=%user%&i=%tag%"
}
var debug_last = "offline";
var debug_time = 0
var debug_state = 0;
var checks = {
online: function (contact,name) {
if (DEBUG_CHECK_ONLINE) {
return debug_state === OnlineNotifier.observe.online;
}
return contact.presence._values.isOnline
},
typing: function (contact, name) {
if (DEBUG_CHECK_TYPING) {
return debug_state === OnlineNotifier.observe.typing;
}
return contact._values.typing || DEBUG_CHECK_TYPING
},
offline: function (contact, name) {
return !checks.online.call(this,contact, name) && !checks.typing.call (this,contact, name);
}
}
var util = {
extract: function (keys, src, trg) {
if (!Array.isArray (keys)) keys = Object.keys (keys);
trg = trg || {}
return keys.reduce (function (trg,key) {
trg [key] = src [key];
return trg;
}, trg);
}
}
var handler = {
online: function (contact, name) {
log (10, "Handler", "online");
var state = this.getState (name);
if ( state.changed || +new Date - MIN_TIMEOUT > contact._lastnotification) {
log (10, "Handler", "changed", state.changed)
notify (contact, "online")
}
},
typing: function (contact,name) {
var state = this.getState (name);
if ( state.changed || +new Date - MIN_TIMEOUT_TYPING > contact._lastnotification) {
notify (contact, "typing")
}
},
offline: function (contact,name) {
log (10, "Handler","offline")
var state = this.getState (name)
if (state.state === "online" || state.changed)
notify (contact, "offline");
}
}
var OnlineNotifier = function (startMonitor) {
this.watch = OnlineNotifier.observe.online + OnlineNotifier.observe.typing;
this.subscribed = [];
this.unsubscribed = chats.slice ();
this._timeout = 0;
this._lastnotification = +new Date - MIN_TIMEOUT
this._states = {};
this.load ()
this.monitor (startMonitor)
notify (0,"started");
}
OnlineNotifier._notifications = {}
OnlineNotifier.storageKey = "mon"
OnlineNotifier.LOG_LEVEL = LOG_LEVEL;
OnlineNotifier.prototype.timeout = POLL_TIMEOUT;
OnlineNotifier.observe = {
offline:0,
online: 1,
typing: 2,
}
OnlineNotifier.contacts = {
byName: function () {
return chats.reduce (function (a,b) {
var values = b._values;
var name = values.name;
a [name] = b;
return a
}, {});
},
byId: function () {
return chats.reduce (function (a,b) {
var values = b._values;
var name = values.id;
a [name] = b;
return a
}, {});
},
filter: {
name: filter.bind (0,"name"),
id: filter.bind (0,"id")
},
gen: {
ppurl: function (contact, cb) {
var id = contact._values.id;
log (9, "Generating Profile Picture Url")
Store.Wap.profilePicFind( "ProfilePicThumb", id).then (function (data) {
var tag = data.tag;
var url = msg.ppurl.replace ("%user%", id).replace ("%tag%",tag)
cb (url, tag)
})
}
}
}
function notify (contact, status) {
if (!contact && status) {
return new Notification ("OnlineNotifier", {tag:"system",body:msg[status]});
}
if (!contact._lastnotification) contact._lastnotification = 0;
//if (+new Date - MIN_TIMEOUT > contact._lastnotification) {
var n = OnlineNotifier.contacts.filter.name (contact);
m = msg [status]
for (var i = 2; i < arguments.length; i++) {
m = m.replace ("%"+(i-2), arguments [i])
}
log (9,"Preparing Notification for ", n)
OnlineNotifier.contacts.gen.ppurl (contact, function (url, tag) {
//tag = status + "-" + tag;
contact._lastnotification = +new Date
log (10,"Displaying notification",tag);
OnlineNotifier._notifications [n] = new Notification (n, {
body: m,
icon: url,
tag: tag
});
})
//}
}
function log (lvl) {
if (OnlineNotifier.LOG_LEVEL >= lvl) {
console.log.apply (console, [].slice.call (arguments, 1));
}
}
function filter (attr, contacts) {
if (!Array.isArray (contacts)) {contacts = [contacts]}
return contacts.map (function (a) {
return a._values [attr];
})
}
OnlineNotifier.prototype.getOnline = function () {
if (DEBUG_CHECK_ONLINE || (this.watched & OnlineNotifier.observe.offline) == OnlineNotifier.observe.offline) {
return this.subscribed;
}
return this.subscribed.filter (checks.online);
}
OnlineNotifier.prototype.monitor = function mon(subscribeTo) {
this.save ();
var that = this;
if (subscribeTo) {
this.subscribe (subscribeTo)
}
var online = this.getOnline ();
var state = "";
var oldstate = "";
var newstate;
log (4, msg ["look"], online.length);
for (var key in OnlineNotifier.observe) {
var val = OnlineNotifier.observe [key];
var now = +new Date;
if ((this.watch & val) === val)
for (var i=0; i<online.length; i++) {
var contact = online [i];
var name = OnlineNotifier.contacts.filter.name (contact) [0]
log (6, msg ["check"],key,name);
if (checks [key].call(this,contact,name) || DEBUG_CHECK_ALL) {
log (7, msg ["handle"], key, handler [key])
state = OnlineNotifier.observe[key];
oldstate = this.getState (name).state;
newstate = {state: state, t:now, changed: state !== oldstate,id : contact._values.id};
newstate[state] = now
util.extract (newstate, newstate, this.getState (name))
handler [key] && handler[key].call (this,contact, name);
log (10, "new state", state, oldstate, this.getState(name));
}
}
}
log (9,"check debug", debug_state, debug_time)
if ((debug_time % DEBUG_CHECK_COUNT) === 0) {
debug_state = (debug_state + 1) % 3
}
debug_time++;
if (this._timeout == -1) {
this._timeout = 0;
this.save ()
} else {
this._timeout = setTimeout (function () {
mon.call (that);
}, this.timeout)
this.save()
log (7, msg ["timeout"], this.timeout, this._timeout)
}
}
OnlineNotifier.prototype.showStates = function () {
for (var key in this._states) {
var state = this.getState (key);
notify (this.getChatById (state.id), "state", state[1])
}
}
OnlineNotifier.prototype.subscribe = function (contacts,reverse) {
var byName;
var srcTrg = [this.unsubscribed, this.subscribed];
var srcInd = 0 + ~~reverse;
var msgPre = reverse ? "un":""
var wasRunning = this.isRunning ();
if (wasRunning) {
log (4, "pausing");
this.cancel ();
}
if (!Array.isArray (contacts)) {
return this.subscribe (typeof contacts === "string"?[contacts]:[], reverse)
} else if (typeof contacts [0] == "string") {
byName = OnlineNotifier.contacts.byName ();
byId = OnlineNotifier.contacts.byId ();
contacts = contacts.map (function (a) {
if (byId.hasOwnProperty (a)) {
return byId [a]
} else if(byName.hasOwnProperty (a)){
return byName [a];
}
});
}
for (var i = 0; i < contacts.length; i++) {
var contact = contacts [i]
var ind = srcTrg [srcInd].indexOf (contact);
srcTrg[(srcInd + 1) % 2].push (srcTrg [srcInd].splice (ind, 1)[0])
log (5,msg [msgPre + "subscribing"], contact._values.name)
if (NOTIFY_SUBSCRIBE) {
notify (contact, msgPre + "subscribed")
}
}
if (wasRunning) {
log (4, "resuming")
this.monitor ();
}
log (3, msg[msgPre+"subscribed"], OnlineNotifier.contacts.filter.name (contacts))
}
OnlineNotifier.prototype.unsubscribe = function (contacts) {
//var s="subscribed";
//var u="un"+s;
//swap (this, s, u);
this.subscribe (contacts,true);
}
OnlineNotifier.prototype.isRunning = function () {
return this._timeout > 0
}
OnlineNotifier.prototype.cancel = function () {
clearTimeout (this._timeout);
this._timeout = -1
}
OnlineNotifier.prototype.getOptions = function () {
var opt = util.extract (["timeout","_timeout"], this);
opt._subscribed = this.subscribed.map (function (a) {
return a._values.name;
});
return opt;
}
OnlineNotifier.prototype.subscribeAll = function () {
return this.subscribe (this.unsubscribed);
}
OnlineNotifier.prototype.getState = function (name) {
if (!this._states [name])
this._states [name] = {state: -1, t:+ new Date, changed: true}
return this._states [name]
}
OnlineNotifier.prototype.save = function () {
var opt = this.getOptions ();
localStorage [OnlineNotifier.storageKey] = JSON.stringify (opt);
}
OnlineNotifier.prototype.load = function () {
var options;
try {
options = JSON.parse (localStorage [OnlineNotifier.storageKey]);
} catch (e) {
options = {}
}
util.extract (options,options,this);
this.subscribe (options._subscribed);
}
OnlineNotifier.e = util.extract;
OnlineNotifier.prototype.onBookmarkTriggered = function (open,dblclick) {
delete window.monTriggerTimeout
var active = Store.Chat.active ();
var chat;
if (open) {
return;
}
if (dblclick) {
notify (0, "dbc")
return ;
}
if (typeof active == "undefined") {
this.reset ();
} else {
chat = this.getChatById (active);
if (!~active.indexOf (this.subscribed)) {
this.unsubscribe (active);
} else {
this.subscribe (active);
}
}
setTimeout (this.monitor.bind(this),0);
}
OnlineNotifier.prototype.getChatById = function (id) {
return OnlineNotifier.contacts.byId ()[id]
}
OnlineNotifier.prototype.reset = function () {
notify (0, "reset")
delete localStorage [OnlineNotifier.storageKey];
}
return OnlineNotifier
})()
if (!window.mon ) {
window.mon = new OnlineNotifier ();
//window.mon.onBookmarkTriggered (true,false);
} else if (window.monTriggerTimeout) {
clearTimeout (window.monTriggerTimeout);
window.mon.onBookmarkTriggered (false,true);
} else if (!window.monTriggerTimeout) {
window.monTriggerTimeout = window.setTimeout (window.mon.onBookmarkTriggered.bind (window.mon,false,false), DOUBLE_CLICK_DELAY);
}
})(window);
@dedsec1911
Copy link

Press f12 on WhatsApp Web page, hit the console section copy the whole code paste it there and press Enter !
If done as stated then it will show a popup on bottom right corner.

@AdamSmith1020
Copy link

It doesn't work. The store object was missing so I added the following code :

if (window.Store === undefined) {
            webpackJsonp([], {"bcihgfbdeb": (x, y, z) => window.Store = z('"bcihgfbdeb"')}, "bcihgfbdeb");
            webpackJsonp([], {"iaeeehaci": (x, y, z) => window.Store.Wap = z('"iaeeehaci"')}, "iaeeehaci");
        }

However, now it gives the welcome notification nut none after that!!

It seems to be an interesting code, only if you could get it work please :)

May be if you add an overlaying UI that shows people who are online would be very useful. Thanks.

@MHSZ
Copy link

MHSZ commented Jan 1, 2019

Could anyone help on how to do it, after i press f12 and go to console and enter the code it says " error reference "

@C5H8NNaO4
Copy link
Author

C5H8NNaO4 commented Apr 25, 2019

This was supposed to be used as a bookmarklet.
You select the chat you want to be notified for and run the bookmarklet to enable notifications; run again to turn them off.

I don't know if it's still working in 2019

Copy link

ghost commented Sep 10, 2020

hello, does anyone know if this is working as of 10.9.2020? been trying to make something similar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment