NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name bumble-enhanced // @namespace https://openuserjs.org/users/93Akkord // @match https://*.bumble.com/* // @run-at document-start // @version 3.0.7 // @author 93Akkord // @description show votes, show online status, and change location on bumble // @grant GM.getValue // @grant GM.setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant unsafeWindow // @grant GM_notification // @require https://code.jquery.com/jquery-3.2.1.min.js // @require https://openuserjs.org/src/libs/93Akkord/loglevel.js // @require https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js // @require https://openuserjs.org/src/libs/93Akkord/akkd-common.js // @require https://greasyfork.org/scripts/446257-waitforkeyelements-utility-function/code/waitForKeyElements%20utility%20function.js?version=1059316 // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js // @require https://openuserjs.org/src/libs/sizzle/GM_config.min.js // require https://unpkg.com/esserializer/dist/bundle.js // @require https://cdnjs.cloudflare.com/ajax/libs/luxon/3.2.1/luxon.min.js // @copyright 2022+, Michael Barros (https://openuserjs.org/users/93Akkord) // @license CC-BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode // @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADf0lEQVRYhb2XT2hcVRTGf+e+l8xklDakElJj/nRKM6Fd1oWKLlS02kU2VXBRFF20tEqKroI7odBuVATtpgqiLgTdaKlaVIwK1UW7EWlNmkZj2tgRMoQxncnMm3uPC+0kb+bNexNIeuAu3jnf+b5z/78rrDE9P9KPmqOI7AcGgR42xgrAHMoXqDslD04t3ArIqvjuF1T1HSC9QaKtrCxGj8r9v71fL+B/8fc2WThkIjwnD1z+QPSn0WFn5TKb3/OGCiibDi9nnMqx2y4OoHQ56475zrJPJBm/GSZO9/kiDLWFNmnkzt1gUvE4V0GXL4FbSaRUxw5fVTNtqOPtOY1s2dtWrVq8iP3lWcAlQe8w7RCavqfbFgeQLXsxfU+1hTWqENv8bszwy22L14mHX0H97nhuhcQR8AYOg791jUfRah5dmQ+3aj6c6G/Fu+dQYqF+XFAyWczdB0M++8ebuPnTkXgzeARvaHz1u/8g7sYnaPn3lhqxU2B2TIA01Fgrt8Rjyw096MBkJ2KnQCrfj2pkZdsext9zqjmgFq0sNPsBSfcTNau1X4/gCpOROX60eidedqLB6dDSLLhKJBGALhfBpJBMNlSIt3MCu3QeXLW5gCgir3cM6RoM92LqVWz+s5biofy+A/gjx+vf0jWE1zuGvfFpcwEaMQQa4VTniMJGmTobyRmVL+XJiDVgOkndeyY8ClpDK3+1VYCktocWr5bnqFwYi5yC6F1gqwQzJxtYfSQ9UG8oaFBEgyIg4VjDzglmTqK2GrkLWp4DdvE7bOFHvJ6HmmK1hY8Jpl8L+Tpzx/G2H2jmKfyAXZxsJYP5b26iW3DlBGitOUtpxkddPC4guHKiJb+qxp+ErjRL7dqH+APPh/x+/zN4dz2yuiVNCkn1NuUH1z/ClVqfggCy/G0udm2Ll6Hrvq8iBeJMK39T/vkJ1JZicYmXkdoS1atvrEscoHr19URxAKNAUgvyn2OXLrYtbpcuEOTPJPIqIP98k7uJkPxX5KUxmSxrnhItTHGlWbDJv2QCN32FeZRcIrq2gi1eSoStxxSZM6BnN5R1fSWclcLX2UGPjilu89tA0LJv2GV6Hpv9U4yMJ6dssBl5MfPo9PX6ilo6N3oIo2+hmzwShjJOx7sfn34XGpb04rmdA56YlxB5EmQEJeEV0qYJFdApVL+0NX172/6Za7dC/wImUfGIbr9yngAAAABJRU5ErkJggg== // @updateURL https://openuserjs.org/meta/93Akkord/bumble-enhanced.meta.js // @downloadURL https://openuserjs.org/install/93Akkord/bumble-enhanced.user.js // ==/UserScript== // #region Workers // prettier-ignore {var W=Object.create;var y=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty;var G=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var B=(e,t,s,u)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of F(t))!C.call(e,a)&&a!==s&&y(e,a,{get:()=>t[a],enumerable:!(u=k(t,a))||u.enumerable});return e};var j=(e,t,s)=>(s=e!=null?W(L(e)):{},B(t||!e||!e.__esModule?y(s,"default",{value:e,enumerable:!0}):s,e));var M=G((p,b)=>{(function(e,t){typeof p=="object"&&typeof b<"u"?t(p):typeof define=="function"&&define.amd?define(["exports"],t):(e=typeof globalThis<"u"?globalThis:e||self,t(e.fastUniqueNumbers={}))})(p,function(e){"use strict";var t=function(o){return function(d){var m=o(d);return d.add(m),m}},s=function(o){return function(d,m){return o.set(d,m),m}},u=Number.MAX_SAFE_INTEGER===void 0?9007199254740991:Number.MAX_SAFE_INTEGER,a=536870912,h=a*2,g=function(o,d){return function(m){var v=d.get(m),c=v===void 0?m.size:v<h?v+1:0;if(!m.has(c))return o(m,c);if(m.size<a){for(;m.has(c);)c=Math.floor(Math.random()*h);return o(m,c)}if(m.size>u)throw new Error("Congratulations, you created a collection of unique numbers which uses all available integers!");for(;m.has(c);)c=Math.floor(Math.random()*u);return o(m,c)}},w=new WeakMap,E=s(w),n=g(E,w),r=t(n);e.addUniqueNumber=r,e.generateUniqueNumber=n})});var I=e=>e.method!==void 0&&e.method==="call";var N=e=>e.error===null&&typeof e.id=="number";var l=j(M()),_=e=>{let t=new Map([[0,()=>{}]]),s=new Map([[0,()=>{}]]),u=new Map,a=new Worker(e);return a.addEventListener("message",({data:n})=>{if(I(n)){let{params:{timerId:r,timerType:i}}=n;if(i==="interval"){let o=t.get(r);if(typeof o=="number"){let d=u.get(o);if(d===void 0||d.timerId!==r||d.timerType!==i)throw new Error("The timer is in an undefined state.")}else if(typeof o<"u")o();else throw new Error("The timer is in an undefined state.")}else if(i==="timeout"){let o=s.get(r);if(typeof o=="number"){let d=u.get(o);if(d===void 0||d.timerId!==r||d.timerType!==i)throw new Error("The timer is in an undefined state.")}else if(typeof o<"u")o(),s.delete(r);else throw new Error("The timer is in an undefined state.")}}else if(N(n)){let{id:r}=n,i=u.get(r);if(i===void 0)throw new Error("The timer is in an undefined state.");let{timerId:o,timerType:d}=i;u.delete(r),d==="interval"?t.delete(o):s.delete(o)}else{let{error:{message:r}}=n;throw new Error(r)}}),{clearInterval:n=>{let r=(0,l.generateUniqueNumber)(u);u.set(r,{timerId:n,timerType:"interval"}),t.set(n,r),a.postMessage({id:r,method:"clear",params:{timerId:n,timerType:"interval"}})},clearTimeout:n=>{let r=(0,l.generateUniqueNumber)(u);u.set(r,{timerId:n,timerType:"timeout"}),s.set(n,r),a.postMessage({id:r,method:"clear",params:{timerId:n,timerType:"timeout"}})},setInterval:(n,r)=>{let i=(0,l.generateUniqueNumber)(t);return t.set(i,()=>{n(),typeof t.get(i)=="function"&&a.postMessage({id:null,method:"set",params:{delay:r,now:performance.now(),timerId:i,timerType:"interval"}})}),a.postMessage({id:null,method:"set",params:{delay:r,now:performance.now(),timerId:i,timerType:"interval"}}),i},setTimeout:(n,r)=>{let i=(0,l.generateUniqueNumber)(s);return s.set(i,n),a.postMessage({id:null,method:"set",params:{delay:r,now:performance.now(),timerId:i,timerType:"timeout"}}),i}}};var R=(e,t)=>{let s=null;return()=>{if(s!==null)return s;let u=new Blob([t],{type:"application/javascript; charset=utf-8"}),a=URL.createObjectURL(u);return s=e(a),setTimeout(()=>URL.revokeObjectURL(a)),s}};var x=`(()=>{"use strict";const e=new Map,t=new Map,r=(e,t)=>{let r,o;const i=performance.now();r=i,o=e-Math.max(0,i-t);return{expected:r+o,remainingDelay:o}},o=(e,t,r,i)=>{const s=performance.now();s>r?postMessage({id:null,method:"call",params:{timerId:t,timerType:i}}):e.set(t,setTimeout(o,r-s,e,t,r,i))};addEventListener("message",(i=>{let{data:s}=i;try{if("clear"===s.method){const{id:r,params:{timerId:o,timerType:i}}=s;if("interval"===i)(t=>{const r=e.get(t);if(void 0===r)throw new Error('There is no interval scheduled with the given id "'.concat(t,'".'));clearTimeout(r),e.delete(t)})(o),postMessage({error:null,id:r});else{if("timeout"!==i)throw new Error('The given type "'.concat(i,'" is not supported'));(e=>{const r=t.get(e);if(void 0===r)throw new Error('There is no timeout scheduled with the given id "'.concat(e,'".'));clearTimeout(r),t.delete(e)})(o),postMessage({error:null,id:r})}}else{if("set"!==s.method)throw new Error('The given method "'.concat(s.method,'" is not supported'));{const{params:{delay:i,now:n,timerId:a,timerType:d}}=s;if("interval"===d)((t,i,s)=>{const{expected:n,remainingDelay:a}=r(t,s);e.set(i,setTimeout(o,a,e,i,n,"interval"))})(i,a,n);else{if("timeout"!==d)throw new Error('The given type "'.concat(d,'" is not supported'));((e,i,s)=>{const{expected:n,remainingDelay:a}=r(e,s);t.set(i,setTimeout(o,a,t,i,n,"timeout"))})(i,a,n)}}}}catch(e){postMessage({error:{message:e.message},id:s.id,result:null})}}))})();`;var f=R(_,x),O=e=>f().clearInterval(e),U=e=>f().clearTimeout(e),A=(e,t)=>f().setInterval(e,t),q=(e,t)=>f().setTimeout(e,t);function T(){return globalThis.GM_info&&GM_info.script.grant.includes("unsafeWindow")?unsafeWindow:globalThis}T().clearIntervalEx=O,T().clearTimeoutEx=U,T().setIntervalEx=A,T().setTimeoutEx=q;} setTimeoutEx = getWindow().setTimeoutEx; clearTimeoutEx = getWindow().clearTimeoutEx; setIntervalEx = getWindow().setIntervalEx; clearIntervalEx = getWindow().clearIntervalEx; window.setTimeoutEx = getWindow().setTimeoutEx; window.clearTimeoutEx = getWindow().clearTimeoutEx; window.setIntervalEx = getWindow().setIntervalEx; window.clearIntervalEx = getWindow().clearIntervalEx; // #endregion Workers // #region luxon luxon.Duration.prototype.__toHuman__ = luxon.Duration.prototype.toHuman; luxon.Duration.prototype.toHuman = function (opts = { stripZeroUnits: 'all' }) { let duration = this.normalize(); let durationUnits = []; let precision = typeof opts.precision == 'object' ? luxon.Duration.fromObject(opts.precision) : luxon.Duration.fromMillis(0); let remainingDuration = duration; //list of all available units const allUnits = ['years', 'months', 'days', 'hours', 'minutes', 'seconds', 'milliseconds']; let smallestUnitIndex; let biggestUnitIndex; // check if user has specified the smallest unit that should be displayed if (opts.smallestUnit) { smallestUnitIndex = allUnits.indexOf(opts.smallestUnit); } // check if user has specified a biggest unit if (opts.biggestUnit) { biggestUnitIndex = allUnits.indexOf(opts.biggestUnit); } // use seconds and years as default for smallest and biggest unit if (!smallestUnitIndex || !(smallestUnitIndex >= 0 && smallestUnitIndex < allUnits.length)) smallestUnitIndex = allUnits.indexOf('seconds'); if (!biggestUnitIndex || !(biggestUnitIndex <= smallestUnitIndex && biggestUnitIndex < allUnits.length)) biggestUnitIndex = allUnits.indexOf('years'); for (let unit of allUnits.slice(biggestUnitIndex, smallestUnitIndex + 1)) { const durationInUnit = remainingDuration.as(unit); if (durationInUnit >= 1) { durationUnits.push(unit); let tmp = {}; tmp[unit] = Math.floor(remainingDuration.as(unit)); remainingDuration = remainingDuration.minus(luxon.Duration.fromObject(tmp)).normalize(); // check if remaining duration is smaller than precision if (remainingDuration < precision) { // ok, we're allowed to remove the remaining parts and to round the current unit break; } } // check if we have already the maximum count of units allowed if (opts.maxUnits && durationUnits.length >= opts.maxUnits) { break; } } // after gathering of units that shall be displayed has finished, remove the remaining duration to avoid non-integers duration = duration.minus(remainingDuration).normalize(); duration = duration.shiftTo(...durationUnits); if (opts.stripZeroUnits == 'all') { durationUnits = durationUnits.filter((unit) => duration.values[unit] > 0); } else if (opts.stripZeroUnits == 'end') { let mayStrip = true; durationUnits = durationUnits.reverse().filter((unit /*, index*/) => { if (!mayStrip) return true; if (duration.values[unit] == 0) { return false; } else { mayStrip = false; } return true; }); } // if `durationUnits` is empty (i.e. duration is zero), then just shift to the smallest unit if (!durationUnits.length) { durationUnits.push(allUnits[smallestUnitIndex]); } return duration.shiftTo(...durationUnits).__toHuman__(opts); }; // #endregion luxon // #region ESSerializer // prettier-ignore function initESSerializer(_window=window){!function(r,e){for(var _ in e)r[_]=e[_];e.__esModule&&Object.defineProperty(r,"__esModule",{value:!0})}(this,(()=>{"use strict";var __webpack_modules__={607:(module,exports,__webpack_require__)=>{Object.defineProperty(exports,"__esModule",{value:!0});var serializer_1=__webpack_require__(810),deserializer_1=__webpack_require__(496),Module;"undefined"!=typeof process&&process.release&&"node"===process.release.name&&(Module=eval("require")("module"));var _ESSerializer=function(){function r(){}return r.throwErrorIfInNonNodeEnvironment=function(){if(!Module)throw new Error("Cannot intercept require in non-Node environment.")},r.interceptRequire=function(){this.isRequireIntercepted||(this.isRequireIntercepted=!0,this.throwErrorIfInNonNodeEnvironment(),r.originRequire=Module.prototype.require,Module.prototype.require=function(){var e=r.originRequire.apply(this,arguments),_=e.name;return r.requiredClasses[_]||(r.requiredClasses[_]=e),e})},r.stopInterceptRequire=function(){this.throwErrorIfInNonNodeEnvironment(),Module.prototype.require=r.originRequire,this.isRequireIntercepted=!1},r.isInterceptingRequire=function(){return this.isRequireIntercepted},r.getRequiredClasses=function(){return this.requiredClasses},r.clearRequiredClasses=function(){this.requiredClasses={}},r.registerClass=function(r){this.registeredClasses.push(r)},r.registerClasses=function(r){this.registeredClasses=this.registeredClasses.concat(r)},r.clearRegisteredClasses=function(){this.registeredClasses=[]},r.serialize=function(r,e){return void 0===e&&(e={}),JSON.stringify(serializer_1.getSerializeValueWithClassName(r,e))},r.deserialize=function(r,e,_){return void 0===e&&(e=[]),void 0===_&&(_={}),deserializer_1.deserializeFromParsedObj(JSON.parse(r),Object.values(this.requiredClasses).concat(this.registeredClasses).concat(e),_)},r.originRequire=null,r.isRequireIntercepted=!1,r.requiredClasses={},r.registeredClasses=[],r}();_window.ESSerializer=_ESSerializer},917:function(r,e){var _=this&&this.__spreadArrays||function(){for(var r=0,e=0,_=arguments.length;e<_;e++)r+=arguments[e].length;var I=Array(r),t=0;for(e=0;e<_;e++)for(var n=arguments[e],L=0,A=n.length;L<A;L++,t++)I[t]=n[L];return I};Object.defineProperty(e,"__esModule",{value:!0}),e.TO_STRING_FIELD=e.TIMESTAMP_FIELD=e.OPTIONS_FIELD=e.CLASS_NAME_FIELD=e.BOOLEAN_FIELD=e.ARRAY_FIELD=e.BUILTIN_TYPE_UNDEFINED=e.BUILTIN_TYPE_NOT_FINITE=e.BUILTIN_TYPE_BIG_INT=e.BUILTIN_CLASS_AGGREGATE_ERROR=e.BUILTIN_CLASS_URI_ERROR=e.BUILTIN_CLASS_TYPE_ERROR=e.BUILTIN_CLASS_SYNTAX_ERROR=e.BUILTIN_CLASS_REFERENCE_ERROR=e.BUILTIN_CLASS_RANGE_ERROR=e.BUILTIN_CLASS_EVAL_ERROR=e.BUILTIN_CLASS_ERROR=e.BUILTIN_CLASS_STRING=e.BUILTIN_CLASS_SET=e.BUILTIN_CLASS_REGEXP=e.BUILTIN_CLASS_INTL_RELATIVETIMEFORMAT=e.BUILTIN_CLASS_INTL_PLURALRULES=e.BUILTIN_CLASS_INTL_NUMBERFORMAT=e.BUILTIN_CLASS_INTL_LOCALE=e.BUILTIN_CLASS_INTL_LISTFORMAT=e.BUILTIN_CLASS_INTL_DATETIMEFORMAT=e.BUILTIN_CLASS_INTL_COLLATOR=e.BUILTIN_CLASS_DATE=e.BUILTIN_CLASS_DATAVIEW=e.BUILTIN_CLASS_BOOLEAN=e.BUILTIN_CLASS_SHAREDARRAYBUFFER=e.BUILTIN_CLASS_ARRAYBUFFER=e.BUILTIN_CLASS_BIGUINT64ARRAY=e.BUILTIN_CLASS_BIGINT64ARRAY=e.BUILTIN_CLASS_FLOAT64ARRAY=e.BUILTIN_CLASS_FLOAT32ARRAY=e.BUILTIN_CLASS_UINT32ARRAY=e.BUILTIN_CLASS_INT32ARRAY=e.BUILTIN_CLASS_UINT16ARRAY=e.BUILTIN_CLASS_INT16ARRAY=e.BUILTIN_CLASS_UINT8CLAMPEDARRAY=e.BUILTIN_CLASS_UINT8ARRAY=e.BUILTIN_CLASS_INT8ARRAY=e.CLASSNAMES_WHOSE_ENUMERABLE_PROPERTIES_SHOULD_BE_IGNORED=e.ALL_BUILTIN_INTLS=e.ALL_BUILTIN_ERRORS=e.ALL_BUILTIN_ARRAYS=e.ESSERIALIZER_NULL=void 0,e.ESSERIALIZER_NULL="__ESSERIALIZER_NULL__",e.ARRAY_FIELD="ess_arr",e.BOOLEAN_FIELD="ess_bool",e.CLASS_NAME_FIELD="ess_cn",e.OPTIONS_FIELD="ess_opt",e.TIMESTAMP_FIELD="ess_ts",e.TO_STRING_FIELD="ess_str";var I="Int8Array";e.BUILTIN_CLASS_INT8ARRAY=I;var t="Uint8Array";e.BUILTIN_CLASS_UINT8ARRAY=t;var n="Uint8ClampedArray";e.BUILTIN_CLASS_UINT8CLAMPEDARRAY=n;var L="Int16Array";e.BUILTIN_CLASS_INT16ARRAY=L;var A="Uint16Array";e.BUILTIN_CLASS_UINT16ARRAY=A;var a="Int32Array";e.BUILTIN_CLASS_INT32ARRAY=a;var S="Uint32Array";e.BUILTIN_CLASS_UINT32ARRAY=S;var R="Float32Array";e.BUILTIN_CLASS_FLOAT32ARRAY=R;var i="Float64Array";e.BUILTIN_CLASS_FLOAT64ARRAY=i;var o="BigInt64Array";e.BUILTIN_CLASS_BIGINT64ARRAY=o;var T="BigUint64Array";e.BUILTIN_CLASS_BIGUINT64ARRAY=T,e.BUILTIN_CLASS_ARRAYBUFFER="ArrayBuffer",e.BUILTIN_CLASS_SHAREDARRAYBUFFER="SharedArrayBuffer",e.BUILTIN_CLASS_BOOLEAN="Boolean",e.BUILTIN_CLASS_DATAVIEW="DataView",e.BUILTIN_CLASS_DATE="Date";var s="Collator";e.BUILTIN_CLASS_INTL_COLLATOR=s;var E="DateTimeFormat";e.BUILTIN_CLASS_INTL_DATETIMEFORMAT=E;var N="ListFormat";e.BUILTIN_CLASS_INTL_LISTFORMAT=N,e.BUILTIN_CLASS_INTL_LOCALE="Locale";var u="NumberFormat";e.BUILTIN_CLASS_INTL_NUMBERFORMAT=u;var c="PluralRules";e.BUILTIN_CLASS_INTL_PLURALRULES=c;var U="RelativeTimeFormat";e.BUILTIN_CLASS_INTL_RELATIVETIMEFORMAT=U,e.BUILTIN_CLASS_REGEXP="RegExp",e.BUILTIN_CLASS_SET="Set";var l="String";e.BUILTIN_CLASS_STRING=l;var B="Error";e.BUILTIN_CLASS_ERROR=B;var C="EvalError";e.BUILTIN_CLASS_EVAL_ERROR=C;var f="RangeError";e.BUILTIN_CLASS_RANGE_ERROR=f;var O="ReferenceError";e.BUILTIN_CLASS_REFERENCE_ERROR=O;var F="SyntaxError";e.BUILTIN_CLASS_SYNTAX_ERROR=F;var p="TypeError";e.BUILTIN_CLASS_TYPE_ERROR=p;var d="URIError";e.BUILTIN_CLASS_URI_ERROR=d;var v="AggregateError";e.BUILTIN_CLASS_AGGREGATE_ERROR=v,e.BUILTIN_TYPE_BIG_INT="BI",e.BUILTIN_TYPE_NOT_FINITE="NF",e.BUILTIN_TYPE_UNDEFINED="UD";var D=[I,t,n,L,A,a,S,R,i,o,T];e.ALL_BUILTIN_ARRAYS=D;var y=[B,C,f,O,F,p,d,v];e.ALL_BUILTIN_ERRORS=y;var g=[s,E,N,u,c,U];e.ALL_BUILTIN_INTLS=g;var Y=_([l],D);e.CLASSNAMES_WHOSE_ENUMERABLE_PROPERTIES_SHOULD_BE_IGNORED=Y},496:function(r,e,_){var I=this&&this.__spreadArrays||function(){for(var r=0,e=0,_=arguments.length;e<_;e++)r+=arguments[e].length;var I=Array(r),t=0;for(e=0;e<_;e++)for(var n=arguments[e],L=0,A=n.length;L<A;L++,t++)I[t]=n[L];return I};Object.defineProperty(e,"__esModule",{value:!0}),e.getParentClassName=e.getClassMappingFromClassArray=e.deserializeFromParsedObjWithClassMapping=e.deserializeFromParsedObj=void 0;var t=_(821),n=_(917),L=/^\s*class\s+/;function A(r,e,_){if(void 0===_&&(_={}),t.notObject(r))return r;if(Array.isArray(r))return a(r,e);var N=r[n.CLASS_NAME_FIELD],u=function(r,e,_){switch(N){case n.BUILTIN_CLASS_INT8ARRAY:return S(e[n.ARRAY_FIELD],Int8Array);case n.BUILTIN_CLASS_UINT8ARRAY:return S(e[n.ARRAY_FIELD],Uint8Array);case n.BUILTIN_CLASS_UINT8CLAMPEDARRAY:return S(e[n.ARRAY_FIELD],Uint8ClampedArray);case n.BUILTIN_CLASS_INT16ARRAY:return S(e[n.ARRAY_FIELD],Int16Array);case n.BUILTIN_CLASS_UINT16ARRAY:return S(e[n.ARRAY_FIELD],Uint16Array);case n.BUILTIN_CLASS_INT32ARRAY:return S(e[n.ARRAY_FIELD],Int32Array);case n.BUILTIN_CLASS_UINT32ARRAY:return S(e[n.ARRAY_FIELD],Uint32Array);case n.BUILTIN_CLASS_FLOAT32ARRAY:return S(e[n.ARRAY_FIELD],Float32Array);case n.BUILTIN_CLASS_FLOAT64ARRAY:return S(e[n.ARRAY_FIELD],Float64Array);case n.BUILTIN_CLASS_BIGINT64ARRAY:return R(e[n.ARRAY_FIELD],BigInt64Array);case n.BUILTIN_CLASS_BIGUINT64ARRAY:return R(e[n.ARRAY_FIELD],BigUint64Array);case n.BUILTIN_TYPE_BIG_INT:return i(e[n.TO_STRING_FIELD]);case n.BUILTIN_TYPE_UNDEFINED:return;case n.BUILTIN_TYPE_NOT_FINITE:return t.getValueFromToStringResult(e[n.TO_STRING_FIELD]);case n.BUILTIN_CLASS_ARRAYBUFFER:return I=e[n.ARRAY_FIELD],new Uint8Array(I).buffer;case n.BUILTIN_CLASS_SHAREDARRAYBUFFER:return function(r){var e=new SharedArrayBuffer(r.length),_=new Uint8Array(e);return r.forEach(function(r,e){_[e]=r}),e}(e[n.ARRAY_FIELD]);case n.BUILTIN_CLASS_BOOLEAN:return new Boolean(e[n.BOOLEAN_FIELD]);case n.BUILTIN_CLASS_DATAVIEW:return function(r){return new DataView(new Uint8Array(r).buffer)}(e[n.ARRAY_FIELD]);case n.BUILTIN_CLASS_DATE:return function(r){return"number"==typeof r[n.TIMESTAMP_FIELD]?new Date(r[n.TIMESTAMP_FIELD]):null}(e);case n.BUILTIN_CLASS_INTL_COLLATOR:return o(e,Intl.Collator);case n.BUILTIN_CLASS_INTL_DATETIMEFORMAT:return o(e,Intl.DateTimeFormat);case n.BUILTIN_CLASS_INTL_LISTFORMAT:return o(e,Intl.ListFormat);case n.BUILTIN_CLASS_INTL_LOCALE:return new Intl.Locale(e[n.TO_STRING_FIELD]);case n.BUILTIN_CLASS_INTL_NUMBERFORMAT:return o(e,Intl.NumberFormat);case n.BUILTIN_CLASS_INTL_PLURALRULES:return o(e,Intl.PluralRules);case n.BUILTIN_CLASS_INTL_RELATIVETIMEFORMAT:return o(e,Intl.RelativeTimeFormat);case n.BUILTIN_CLASS_REGEXP:return function(r){var e=r[n.TO_STRING_FIELD],_=e.lastIndexOf("/");return new RegExp(e.substring(1,_),e.substring(_+1))}(e);case n.BUILTIN_CLASS_SET:return function(r,e){return new Set(a(r[n.ARRAY_FIELD],e))}(e,_);case n.BUILTIN_CLASS_STRING:return new String(e[n.TO_STRING_FIELD]);case n.BUILTIN_CLASS_ERROR:return T(e,Error);case n.BUILTIN_CLASS_EVAL_ERROR:return T(e,EvalError);case n.BUILTIN_CLASS_RANGE_ERROR:return T(e,RangeError);case n.BUILTIN_CLASS_REFERENCE_ERROR:return T(e,ReferenceError);case n.BUILTIN_CLASS_SYNTAX_ERROR:return T(e,SyntaxError);case n.BUILTIN_CLASS_TYPE_ERROR:return T(e,TypeError);case n.BUILTIN_CLASS_URI_ERROR:return T(e,URIError);case n.BUILTIN_CLASS_AGGREGATE_ERROR:return T(e,AggregateError);default:return n.ESSERIALIZER_NULL}var I}(0,r,e);if(u!==n.ESSERIALIZER_NULL)return u;if(N&&!e[N])throw new Error("Class "+N+" not found");var c=[];return _.fieldsForConstructorParameters&&(c=_.fieldsForConstructorParameters.map(function(e){return e in r?r[e]:{}})),function(r,e,_,I){for(var t in e){var L=e[t];if(!I.ignoreProperties||!I.ignoreProperties.includes(t))if(I.rawProperties&&I.rawProperties.includes(t))r[t]=JSON.stringify(L);else{var a=Object.getOwnPropertyDescriptor(r,t);S=L,R=a,t===n.CLASS_NAME_FIELD||R&&("function"==typeof R.set||!1===R.writable&&"object"!=typeof S)||(a&&!1===a.writable&&"object"==typeof L?E(r[t],L,_):r[t]=A(L,_))}}var S,R;return r}(function(r,e){var _={};if(!r)return _;var t=r.length-e.length;if(t>0&&(e=e.concat(Array.from(Array(t),function(){return{}}))),L.test(r.toString()))try{_=new(r.bind.apply(r,I([void 0],e)))}catch(e){s(r.name),_={},Object.setPrototypeOf(_,r?r.prototype:Object.prototype)}else{_=Object.create(r.prototype.constructor.prototype);try{r.prototype.constructor.call(_,e)}catch(e){s(r.name)}}return _}(e[N],c),r,e,_)}function a(r,e){return r.map(function(r){return A(r,e)})}function S(r,e){return new e(r)}function R(r,e){return new e(r.map(function(r){return i(r[n.TO_STRING_FIELD])}))}function i(r){return BigInt(r)}function o(r,e){var _=r[n.OPTIONS_FIELD],I=_.locale;return delete _.locale,new e(I,_)}function T(r,e){var _;return delete(_=r.message?new e(r.message):new e).stack,r.name&&(_.name=r.name),r.stack&&(_.stack=r.stack),e===AggregateError&&(_.errors=A(r.errors,{})),_}function s(r){console.warn("Incorrect parameter type passed to constructor: "+r)}function E(r,e,_){for(var I in e){var t=Object.getOwnPropertyDescriptor(r,I);t&&!0!==t.writable&&"function"!=typeof t.set||(r[I]=A(e[I],_))}}function N(r){void 0===r&&(r=[]);var e={};return r.forEach(function(r){if(t.isClass(r)){var _=r.name,I=e[_];I&&I!==r&&console.warn("WARNING: Found class definition with the same name: "+_),e[_]=r}}),e}e.deserializeFromParsedObj=function(r,e,_){return A(r,N(e),_)},e.deserializeFromParsedObjWithClassMapping=A,e.getClassMappingFromClassArray=N,e.getParentClassName=function(r){return r.prototype.__proto__.constructor.name}},821:(r,e)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.isClass=e.notObject=e.getValueFromToStringResult=void 0,e.notObject=function(r){return null===r||"object"!=typeof r},e.getValueFromToStringResult=function(r){switch(r){case"Infinity":return 1/0;case"-Infinity":return-1/0;case"NaN":return NaN;default:return null}},e.isClass=function(r){if(function(r){return[Date].indexOf(r)>=0}(r))return!0;try{Reflect.construct(String,[],r)}catch(r){return!1}return!0}},810:(r,e,_)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.getSerializeValueWithClassName=void 0;var I=_(917),t=_(821);function n(r,e){void 0===e&&(e={});var _=function(r){var e,_,n;return void 0===r?((e={})[I.CLASS_NAME_FIELD]=I.BUILTIN_TYPE_UNDEFINED,e):"number"!=typeof r||isFinite(r)?"bigint"==typeof r?((n={})[I.CLASS_NAME_FIELD]=I.BUILTIN_TYPE_BIG_INT,n[I.TO_STRING_FIELD]=r.toString(),n):t.notObject(r)?r:I.ESSERIALIZER_NULL:((_={})[I.CLASS_NAME_FIELD]=I.BUILTIN_TYPE_NOT_FINITE,_[I.TO_STRING_FIELD]=r.toString(),_)}(r);if(_!==I.ESSERIALIZER_NULL)return _;if(Array.isArray(r))return L(r);var A={};if(!function(r){var e=r.__proto__.constructor.name;return I.CLASSNAMES_WHOSE_ENUMERABLE_PROPERTIES_SHOULD_BE_IGNORED.includes(e)}(r)){if(e.ignoreProperties&&e.ignoreProperties.forEach(function(e){delete r[e]}),e.interceptProperties)for(var a in e.interceptProperties)r[a]=e.interceptProperties[a].call(r,r[a]);for(var S in r)A[S]=n(r[S])}return function(r,e){var _=r.__proto__.constructor.name;return"Object"!==_&&(e[I.CLASS_NAME_FIELD]=_,_===I.BUILTIN_CLASS_ARRAYBUFFER||_===I.BUILTIN_CLASS_SHAREDARRAYBUFFER?e[I.ARRAY_FIELD]=L(Array.from(new Uint8Array(r))):_===I.BUILTIN_CLASS_BOOLEAN?e[I.BOOLEAN_FIELD]=r.valueOf():_===I.BUILTIN_CLASS_DATAVIEW?e[I.ARRAY_FIELD]=L(Array.from(new Uint8Array(r.buffer))):_===I.BUILTIN_CLASS_DATE?e[I.TIMESTAMP_FIELD]=r.getTime():_===I.BUILTIN_CLASS_INTL_LOCALE||_===I.BUILTIN_CLASS_REGEXP?e[I.TO_STRING_FIELD]=r.toString():_===I.BUILTIN_CLASS_SET?e[I.ARRAY_FIELD]=L(Array.from(r)):_===I.BUILTIN_CLASS_STRING?e[I.TO_STRING_FIELD]=r.toString():I.ALL_BUILTIN_ARRAYS.includes(_)?e[I.ARRAY_FIELD]=L(Array.from(r)):I.ALL_BUILTIN_ERRORS.includes(_)?function(r,e,_){"Error"!==r.name&&(e.name=r.name),r.message&&(e.message=r.message),r.stack&&(e.stack=r.stack),_===I.BUILTIN_CLASS_AGGREGATE_ERROR&&(e.errors=n(r.errors))}(r,e,_):I.ALL_BUILTIN_INTLS.includes(_)&&(e[I.OPTIONS_FIELD]=r.resolvedOptions())),e}(r,A)}function L(r){return r.map(function(r){return n(r)})}e.getSerializeValueWithClassName=n}},__webpack_module_cache__={};function __webpack_require__(r){if(__webpack_module_cache__[r])return __webpack_module_cache__[r].exports;var e=__webpack_module_cache__[r]={exports:{}};return __webpack_modules__[r].call(e.exports,e,e.exports,__webpack_require__),e.exports}return __webpack_require__(607)})())} // #endregion ESSerializer // #region Bumble Classes class BumbleUser { constructor(userProps) { this.updateProps(userProps); } updateProps(userProps) { this.user_id = userProps.user_id; this.access_level = userProps.access_level; this.name = userProps.name; this.age = userProps.age; this.gender = userProps.gender; this.is_deleted = userProps.is_deleted; this.is_extended_match = userProps.is_extended_match; this.online_status = userProps.online_status; this.is_match = userProps.is_match; this.match_mode = userProps.match_mode; this.is_crush = userProps.is_crush; this.their_vote_mode = userProps.their_vote_mode; this.unread_messages_count = userProps.unread_messages_count; this.is_inapp_promo_partner = userProps.is_inapp_promo_partner; this.is_locked = userProps.is_locked; this.type = userProps.type; this.connection_status_indicator = userProps.connection_status_indicator; this.profile_photo_preview_url = this.profile_photo_preview_url || userProps?.profile_photo?.preview_url || userProps.profile_photo_preview_url; this.profile_photo_large_url = this.profile_photo_large_url || userProps?.profile_photo?.large_url || userProps.profile_photo_large_url; this.last_seen = this.online_status == 1 ? new Date().getTime() : this.last_seen || userProps.last_seen; this.last_seen_dt = this.last_seen != null && this.last_seen != undefined ? luxon.DateTime.fromMillis(this.last_seen).toFormat('yyyy-MM-dd hh:mm:ss a') : null; this.time_since_last_seen = this.last_seen != null && this.last_seen != undefined ? new Date().getTime() - this.last_seen : null; this.time_since_last_seen_h = this.time_since_last_seen != null && this.time_since_last_seen != undefined ? luxon.Duration.fromMillis(this.time_since_last_seen).toHuman({ unitDisplay: 'short' }) : null; this.played_track_user_online = firstBumbleCreations ? false : userProps.played_track_user_online != null && userProps.played_track_user_online != undefined ? userProps.played_track_user_online : false; return this; } updateLastSeen() { this.last_seen_dt = this.last_seen != null && this.last_seen != undefined ? luxon.DateTime.fromMillis(this.last_seen).toFormat('yyyy-MM-dd hh:mm:ss a') : null; this.time_since_last_seen = this.last_seen != null && this.last_seen != undefined ? new Date().getTime() - this.last_seen : null; this.time_since_last_seen_h = this.time_since_last_seen != null && this.time_since_last_seen != undefined ? luxon.Duration.fromMillis(this.time_since_last_seen).toHuman({ unitDisplay: 'short' }) : null; return this; } } // #endregion Bumble Classes initESSerializer(getWindow()); const DEBUG_MESSAGES = false; let encs = []; let user; let numEncountersCalls = 0; let queue = []; let convos = []; let quota = 0; let lastestMessageId; const userIds = []; let trackUserOnline = false; let firstBumbleCreations = true; let updateing = false; let updateingEncounters = false; /** @type {BumbleUser[]} */ let bumbleUsersCurrent = []; /** @type {BumbleUser[]} */ let bumbleUsersNew = []; /** @type {(userId: string) => void} */ let openChat; let debugObj = { projection: [], }; const logger = getLogger('bumble-enhanced', { logLevel: log.levels.DEBUG }); logger.info(`Loading ${GM_info.script.name}...`); /* jshint esversion: 8 */ (async function () { setupConfig(); deserializeUsers(); exposeGlobalVariables([ // libs { name: 'jQuery', value: jQuery }, { name: '$', value: $ }, // functions/variables { name: 'pp', value: pp }, { name: 'pformat', value: pformat }, { name: 'getObjProps', value: getObjProps }, { name: 'getUserDefinedGlobalProps', value: getUserDefinedGlobalProps }, { name: 'getLocalStorageSize', value: getLocalStorageSize }, { name: 'unsafeWindow', value: unsafeWindow }, { name: 'getWindow', value: getWindow }, { name: 'getTopWindow', value: getTopWindow }, { name: 'getStyle', value: getStyle }, { name: 'BumbleUser', value: BumbleUser }, { name: 'deserializeUsers', value: deserializeUsers }, { name: 'findKeyPathsFuzzy', value: findKeyPathsFuzzy }, { name: 'luxon', value: luxon }, { name: 'openChat', value: openChat }, { name: 'debugObj', value: debugObj }, { name: 'serverGetUser', value: serverGetUser }, { name: '_showAllContactsScroll', value: _showAllContactsScroll }, { name: 'wait', value: wait }, ]); $(document).arrive('.contact', function () { if (openChat == null || openChat == undefined) { openChat = findKeyPathsFuzzy(document.querySelectorAll('.contact')[0], 'openChat')[0].value; exposeGlobalVariables([ // functions/variables { name: 'openChat', value: openChat }, ]); } }); function setupPageContentArriveHandler() { $(document).unbindArrive('.page__content', setupPageContentArriveHandler); setupUserMonitorDropdown(); $(document).arrive('.page__content', setupPageContentArriveHandler); } $(document).arrive('.page__content', setupPageContentArriveHandler); if (!unsafeWindow.XMLHttpRequest.prototype.getResponseText) { unsafeWindow.XMLHttpRequest.prototype.getResponseText = Object.getOwnPropertyDescriptor(unsafeWindow.XMLHttpRequest.prototype, 'responseText').get; } if (!unsafeWindow.XMLHttpRequest.prototype.sendEx) { unsafeWindow.XMLHttpRequest.prototype.sendEx = Object.getOwnPropertyDescriptor(unsafeWindow.XMLHttpRequest.prototype, 'send').value; } Object.defineProperty(unsafeWindow.XMLHttpRequest.prototype, 'send', { /* eslint-disable-next-line no-undef */ value: exportFunction(function () { let args = Array.from(arguments); let body = args[0]; // try { // body = JSON.parse(body); // debugObj.projection = Array.from(new Set([...debugObj.projection, ...findKeyPathsFuzzy(body, 'projection')[0].value])).sort((a, b) => a - b); // } catch (error) {} // debugger; unsafeWindow.XMLHttpRequest.prototype.sendEx.call(this, ...args); }, unsafeWindow), enumerable: true, configurable: true, writable: true, }); Object.defineProperty(unsafeWindow.XMLHttpRequest.prototype, 'responseText', { /* eslint-disable-next-line no-undef */ get: exportFunction(function () { let responseText = unsafeWindow.XMLHttpRequest.prototype.getResponseText.call(this); try { let body = JSON.parse(responseText); debugObj.projection = Array.from(new Set([...debugObj.projection, ...findKeyPathsFuzzy(body, 'projection')[0].value])).sort((a, b) => a - b); } catch (error) {} // debugger; if (this.responseURL.includes('bumble.com/mwebapi.phtml?')) { const resp = JSON.parse(responseText); lastestMessageId = resp.message_id; } try { if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_APP_STARTUP')) { const resp = JSON.parse(responseText); user = (resp.body.find((o) => o.user) || {}).user; } if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_ENCOUNTERS_VOTE')) { const body = JSON.stringify({ $gpb: 'badoo.bma.BadooMessage', body: [ { message_type: 81, server_get_encounters: { number: 0, context: 1, user_field_filter: { projection: [200], request_albums: [], game_mode: 0, request_music_services: {}, }, }, }, ], message_id: 1, message_type: 81, version: 1, is_background: false, }); try { window .fetch('/mwebapi.phtml?SERVER_GET_ENCOUNTERS', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Message-type': '81', 'x-use-session-cookie': '1', 'X-Pingback': calculateBumbleChecksum(body), }, body, }) .then((resp) => resp.json().then((data) => { quota = (data.body[0].client_encounters.quota || {}).yes_votes_quota || 0; (document.querySelector('#voteQuota') || {}).innerHTML = quota; }) ); } catch (err) { logger.error(err); } } if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_GET_ENCOUNTERS')) { numEncountersCalls++; const resp = JSON.parse(responseText); // encs.push(...resp.body[0].client_encounters.results); quota = (resp.body[0].client_encounters.quota || {}).yes_votes_quota || 0; responseText = JSON.stringify(resp); updateEncountersLists(); } if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_GET_USER_LIST')) { } if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_OPEN_CHAT')) { const resp = JSON.parse(responseText); updateMessagesWithDatetime(resp, user); } } catch (err) { logger.error(err); } return responseText; }, unsafeWindow), enumerable: true, configurable: true, }); window.setIntervalEx(() => { const hdr = document.querySelector('.encounters-story-profile__header'); if (!hdr) { return; } if (hdr.parentElement.querySelector('.showBumbleVotes')) { return; } let name = (document.querySelector('.encounters-story-profile__name') || {}).innerText; let age = +(document.querySelector('.encounters-story-profile__age') || { innerText: '' }).innerText.replace(',', '').trim(); let enc = encs.find((enc) => enc.user.name === name && enc.user.age === age); if (!enc) { return; } userIds.push({ name, id: enc.user.user_id }); const div = document.createElement('div'); div.classList.add('showBumbleVotes'); let vote = enc.user.their_vote; let voteText = vote === 1 ? 'Not voted!' : vote === 2 ? 'Swiped right!' : vote === 3 ? 'Swiped left!' : 'Unknown!'; div.innerHTML = `${voteText}<br /> (<span id="voteQuota">${quota}</span> yes votes remaining)`; hdr.after(div); let color; switch (enc.user.online_status) { case 1: // online color = 'green'; break; case 2: // ?? color = 'yellow'; break; case 3: // offline color = 'grey'; break; default: // offline color = 'grey'; break; } document.querySelectorAll('span[data-qa-icon-name=profile-badge-about], span[data-qa-icon-name=profile-badge-location]').forEach((iconElem) => { iconElem.firstChild.setAttribute('color', color); }); }, 1000); setupUserMsgCarouselArrive(); setupSidebarArrive(); setIntervalEx(async () => { await updateOnlineStatuses(); }, 5000); setIntervalEx(() => { const filters = document.querySelector('.encounters-filter__content'); if (!filters) return; if (filters.querySelector('.locationSpoofer')) return; const div = document.createElement('div'); div.classList.add('encounters-filter__entry'); div.classList.add('locationSpoofer'); div.innerHTML = ` <div class="encounters-filter__content"> <section class="settings-fieldset"> <header class="settings-fieldset__header"> <div class="settings-fieldset__title"> <h2 class="p-1 text-color-gray-dark"><span>Change Location</span></h2> </div> </header> <div class="form__control form__control--vertical"> <div class="form__field"> <div class="text-field text-field--full-rounded" data-qa-role="dialog-add-job-title-field"> <input type="text" id="spoofLatitude" placeholder="Latitude (Decimal Format)" class="text-field__input" maxlength="40" size="5" dir="auto" value="" /> </div> </div> </div> <div class="form__control form__control--vertical"> <div class="form__field"> <div class="text-field text-field--full-rounded" data-qa-role="dialog-add-job-title-field"> <input type="text" id="spoofLongitude" placeholder="Longitude (Decimal Format)" class="text-field__input" maxlength="40" size="5" dir="auto" value="" /> </div> </div> </div> </section> <div class="encounters-filter__actions"> <div class="encounters-filter__action"> <div class="button button--narrow button--size-m color-primary button--filled" role="button" id="applyLocationSpoofer"> <span class="button__content"> <span class="button__text"><span class="action text-break-words"><span id="applySpoofLocation">Apply Location</span></span></span> </span> </div> </div> </div> </div> `; filters.prepend(div); document.querySelector('#applyLocationSpoofer').addEventListener('click', async () => { const body = JSON.stringify({ $gpb: 'badoo.bma.BadooMessage', body: [ { message_type: 4, server_update_location: { location: [ { latitude: +document.querySelector('#spoofLatitude').value, longitude: +document.querySelector('#spoofLongitude').value, }, ], }, }, ], message_id: 1, message_type: 4, version: 1, is_background: false, }); try { const resp = await window.fetch('/mwebapi.phtml?SERVER_UPDATE_LOCATION', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Message-type': '4', 'x-use-session-cookie': '1', 'X-Pingback': calculateBumbleChecksum(body), }, body, }); document.querySelector('#applySpoofLocation').innerText = 'Location Set!'; filters.querySelector('.color-primary[data-qa-role=button]').click(); } catch (e) { window.alert(`Error setting location: ${e}`); } }); }, 1000); setupBiggerProfilePictures(); waitForKeyElements('.profile', actionFunction, false); initNewSection(); // setupModifyRequestHeaders(); // setupLocationIntercept(); addConversationOption(); addCustomCss(); logger.info(`${GM_info.script.name} loaded`); })(); // #region Helper Functions function isObject(value) { return !!(value && typeof value === 'object' && !Array.isArray(value)); } function findNestedObject(object = {}, keyToMatch = '') { if (isObject(object)) { const entries = Object.entries(object); for (let i = 0; i < entries.length; i += 1) { const [objectKey, objectValue] = entries[i]; if (objectKey === keyToMatch) { return object; } if (isObject(objectValue)) { const child = findNestedObject(objectValue, keyToMatch); if (child !== null) { return child; } } else if (Array.isArray(objectValue)) { for (let a = 0; a < objectValue.length; a++) { const item = objectValue[a]; if (isObject(item)) { const child = findNestedObject(item, keyToMatch); if (child !== null) { return child; } } } } } } return null; } let gestureOccured = false; $(document).on('click pointerup mouseup', function (e) { gestureOccured = true; }); async function playBeep() { let beep = new Audio('data:audio/mpeg;base64,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//PkRPscWgjcADEslLjcEbgCS9KQsiilgMzc5KVUt066PIOE3y82o9CWehRWhjnIhMCoamFTyK8WFTAWNCllWAqNERMVYDRcU4kKVEbAJI2bQpskQfAkRDyEiUFTRUlzrIopIpaVMrWQoD6FKeETSwqbimh6ERMEyopSBJGGVhEbFKSZKkiIhSoCQmXCp4VDJ8KkqRNLUK0NSaISWBMmm6VS/jG0NIkQhBkBjSpwTK5cfUcleERohQohkSkT2ZWtHyRNFSXL+gKOhYVIrQokTRUESVIEjbSzRCUExCzWdg1VTEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV'); try { if (gestureOccured) { await beep.play(); } } catch (error) {} } function findKeyPathsFuzzy(obj, searchTerm) { const keyPaths = []; const visited = new Set(); function searchKeys(obj, currentPath) { if (typeof obj !== 'object' || obj === null || visited.has(obj)) { return; } visited.add(obj); const keys = Object.keys(obj); for (const key of keys) { const newPath = currentPath ? `${currentPath}.${key}` : key; if (fuzzyMatch(key, searchTerm)) { keyPaths.push({ path: newPath, value: obj[key], }); } searchKeys(obj[key], newPath); } } searchKeys(obj, ''); return keyPaths; } function fuzzyMatch(str, searchTerm) { // Perform fuzzy matching logic here // You can use any fuzzy matching algorithm/library you prefer // For example, a simple case-insensitive substring match return str.toLowerCase().includes(searchTerm.toLowerCase()); } // #endregion Helper Functions // #region Bumble Functions let allKnownProjections = [10, 11, 71, 91, 93, 100, 200, 210, 220, 230, 231, 250, 280, 291, 300, 305, 310, 330, 340, 370, 380, 490, 493, 530, 540, 560, 570, 580, 582, 583, 584, 585, 590, 591, 592, 640, 662, 700, 732, 762, 763, 860, 880, 890, 911, 912, 930, 1140, 1150, 1160, 1161, 1262, 1423]; async function serverGetUser(userId) { let body = JSON.stringify({ // $gpb: 'badoo.bma.BadooMessage', body: [ { message_type: 403, server_get_user: { user_id: userId, user_field_filter: { game_mode: 0, projection: allKnownProjections, request_music_services: { top_artists_limit: 10, supported_services: [29] }, request_albums: [ { person_id: userId, album_type: 2, offset: 1 }, { person_id: userId, album_type: 12, external_provider: 12 }, ], }, client_source: 10, }, }, ], message_id: lastestMessageId++, message_type: 403, version: 1, is_background: false, }); let res = await fetch('https://bumble.com/mwebapi.phtml?SERVER_GET_USER', { headers: { accept: '*/*', // 'accept-language': 'en-US,en;q=0.9', 'content-type': 'application/json', // 'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"', // 'sec-ch-ua-mobile': '?0', // 'sec-ch-ua-platform': '"Windows"', // 'sec-fetch-dest': 'empty', // 'sec-fetch-mode': 'cors', // 'sec-fetch-site': 'same-origin', // 'x-message-type': '403', 'x-pingback': calculateBumbleChecksum(body), 'x-use-session-cookie': '1', }, // referrer: 'https://bumble.com/app/connections', // referrerPolicy: 'origin-when-cross-origin', body: body, method: 'POST', // mode: 'cors', // credentials: 'include', }); res = await res.json(); return res; } function calculateBumbleChecksum(inputString) { inputString += 'whitetelevisionbulbelectionroofhorseflying'; const hc = '0123456789abcdef'; function rh(n) { let j, s = ''; for (j = 0; j <= 3; j++) s += hc.charAt((n >> (j * 8 + 4)) & 0x0f) + hc.charAt((n >> (j * 8)) & 0x0f); return s; } function ad(x, y) { let l = (x & 0xffff) + (y & 0xffff); let m = (x >> 16) + (y >> 16) + (l >> 16); return (m << 16) | (l & 0xffff); } function rl(n, c) { return (n << c) | (n >>> (32 - c)); } function cm(q, a, b, x, s, t) { return ad(rl(ad(ad(a, q), ad(x, t)), s), b); } function ff(a, b, c, d, x, s, t) { return cm((b & c) | (~b & d), a, b, x, s, t); } function gg(a, b, c, d, x, s, t) { return cm((b & d) | (c & ~d), a, b, x, s, t); } function hh(a, b, c, d, x, s, t) { return cm(b ^ c ^ d, a, b, x, s, t); } function ii(a, b, c, d, x, s, t) { return cm(c ^ (b | ~d), a, b, x, s, t); } function sb(x) { let i; let nblk = ((x.length + 8) >> 6) + 1; let blks = new Array(nblk * 16); for (i = 0; i < nblk * 16; i++) blks[i] = 0; for (i = 0; i < x.length; i++) blks[i >> 2] |= x.charCodeAt(i) << ((i % 4) * 8); blks[i >> 2] |= 0x80 << ((i % 4) * 8); blks[nblk * 16 - 2] = x.length * 8; return blks; } let i, x = sb(inputString), a = 1732584193, b = -271733879, c = -1732584194, d = 271733878, olda, oldb, oldc, oldd; for (i = 0; i < x.length; i += 16) { olda = a; oldb = b; oldc = c; oldd = d; a = ff(a, b, c, d, x[i + 0], 7, -680876936); d = ff(d, a, b, c, x[i + 1], 12, -389564586); c = ff(c, d, a, b, x[i + 2], 17, 606105819); b = ff(b, c, d, a, x[i + 3], 22, -1044525330); a = ff(a, b, c, d, x[i + 4], 7, -176418897); d = ff(d, a, b, c, x[i + 5], 12, 1200080426); c = ff(c, d, a, b, x[i + 6], 17, -1473231341); b = ff(b, c, d, a, x[i + 7], 22, -45705983); a = ff(a, b, c, d, x[i + 8], 7, 1770035416); d = ff(d, a, b, c, x[i + 9], 12, -1958414417); c = ff(c, d, a, b, x[i + 10], 17, -42063); b = ff(b, c, d, a, x[i + 11], 22, -1990404162); a = ff(a, b, c, d, x[i + 12], 7, 1804603682); d = ff(d, a, b, c, x[i + 13], 12, -40341101); c = ff(c, d, a, b, x[i + 14], 17, -1502002290); b = ff(b, c, d, a, x[i + 15], 22, 1236535329); a = gg(a, b, c, d, x[i + 1], 5, -165796510); d = gg(d, a, b, c, x[i + 6], 9, -1069501632); c = gg(c, d, a, b, x[i + 11], 14, 643717713); b = gg(b, c, d, a, x[i + 0], 20, -373897302); a = gg(a, b, c, d, x[i + 5], 5, -701558691); d = gg(d, a, b, c, x[i + 10], 9, 38016083); c = gg(c, d, a, b, x[i + 15], 14, -660478335); b = gg(b, c, d, a, x[i + 4], 20, -405537848); a = gg(a, b, c, d, x[i + 9], 5, 568446438); d = gg(d, a, b, c, x[i + 14], 9, -1019803690); c = gg(c, d, a, b, x[i + 3], 14, -187363961); b = gg(b, c, d, a, x[i + 8], 20, 1163531501); a = gg(a, b, c, d, x[i + 13], 5, -1444681467); d = gg(d, a, b, c, x[i + 2], 9, -51403784); c = gg(c, d, a, b, x[i + 7], 14, 1735328473); b = gg(b, c, d, a, x[i + 12], 20, -1926607734); a = hh(a, b, c, d, x[i + 5], 4, -378558); d = hh(d, a, b, c, x[i + 8], 11, -2022574463); c = hh(c, d, a, b, x[i + 11], 16, 1839030562); b = hh(b, c, d, a, x[i + 14], 23, -35309556); a = hh(a, b, c, d, x[i + 1], 4, -1530992060); d = hh(d, a, b, c, x[i + 4], 11, 1272893353); c = hh(c, d, a, b, x[i + 7], 16, -155497632); b = hh(b, c, d, a, x[i + 10], 23, -1094730640); a = hh(a, b, c, d, x[i + 13], 4, 681279174); d = hh(d, a, b, c, x[i + 0], 11, -358537222); c = hh(c, d, a, b, x[i + 3], 16, -722521979); b = hh(b, c, d, a, x[i + 6], 23, 76029189); a = hh(a, b, c, d, x[i + 9], 4, -640364487); d = hh(d, a, b, c, x[i + 12], 11, -421815835); c = hh(c, d, a, b, x[i + 15], 16, 530742520); b = hh(b, c, d, a, x[i + 2], 23, -995338651); a = ii(a, b, c, d, x[i + 0], 6, -198630844); d = ii(d, a, b, c, x[i + 7], 10, 1126891415); c = ii(c, d, a, b, x[i + 14], 15, -1416354905); b = ii(b, c, d, a, x[i + 5], 21, -57434055); a = ii(a, b, c, d, x[i + 12], 6, 1700485571); d = ii(d, a, b, c, x[i + 3], 10, -1894986606); c = ii(c, d, a, b, x[i + 10], 15, -1051523); b = ii(b, c, d, a, x[i + 1], 21, -2054922799); a = ii(a, b, c, d, x[i + 8], 6, 1873313359); d = ii(d, a, b, c, x[i + 15], 10, -30611744); c = ii(c, d, a, b, x[i + 6], 15, -1560198380); b = ii(b, c, d, a, x[i + 13], 21, 1309151649); a = ii(a, b, c, d, x[i + 4], 6, -145523070); d = ii(d, a, b, c, x[i + 11], 10, -1120210379); c = ii(c, d, a, b, x[i + 2], 15, 718787259); b = ii(b, c, d, a, x[i + 9], 21, -343485551); a = ad(a, olda); b = ad(b, oldb); c = ad(c, oldc); d = ad(d, oldd); } return rh(a) + rh(b) + rh(c) + rh(d); } /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {string} className * @param {number} onlineStatus * @param {SVGSVGElement} onlineStatusElem * @return {*} */ function makeOnlineStatusSVG(className, onlineStatus, onlineStatusElem) { /** @type {SVGSVGElement} */ let circle; if (!onlineStatusElem) { onlineStatusElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); onlineStatusElem.setAttribute('height', 80); onlineStatusElem.setAttribute('width', 80); onlineStatusElem.classList.add(className); onlineStatusElem.style.position = 'absolute'; onlineStatusElem.style.zIndex = '9'; onlineStatusElem.style.opacity = '.9'; circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); circle.setAttribute('cx', 40); circle.setAttribute('cy', 40); circle.setAttribute('r', 7); circle.setAttribute('stroke', 'grey'); circle.setAttribute('stroke-width', 1); onlineStatusElem.appendChild(circle); } else { circle = onlineStatusElem.querySelector('circle'); } /** @type {string} */ let fill; switch (onlineStatus) { case 1: // online fill = 'green'; break; case 2: // ?? fill = 'yellow'; break; case 3: // offline fill = 'grey'; break; default: // offline fill = 'grey'; break; } circle.setAttribute('fill', fill); // onlineStatusElem.appendChild(circle); return onlineStatusElem; } function updateBackToBanner(encountersAvailable) { try { let elem = document.querySelectorAll('.sidebar-profile__name > div')[0]; // document.querySelectorAll('.sidebar-action-banner')[0].querySelectorAll('.font-weight-medium')[0].querySelectorAll('span')[0]; let textContent = elem.textContent; textContent = textContent.replace(/\s+?\[\d+?\]/, ''); textContent = `${textContent} [${encountersAvailable}]`; elem.textContent = textContent; } catch (error) {} } function updateMessagesWithDatetime(resp, user) { if (DEBUG_MESSAGES) { console.clear(); } /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {string} str * @returns {string} */ function convert(str) { str = str.replace(/&#(?:x([\da-f]+)|(\d+));/gi, function (_, hex, dec) { return String.fromCharCode(dec || +('0x' + hex)); }); str = str .replace(/ /gi, ' ') .replace(/&/gi, '&') .replace(/"/gi, `"`) .replace(/</gi, '<') .replace(/>/gi, '>'); return str; } /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {string} userId * @param {[]} messages * @param {string} messageType * @param {HTMLElement} elem */ function findMessage(userId, messages, messageType, elem) { for (let i = 0; i < messages.length; i++) { const message = messages[i]; let gifSource = getGifSource(elem); let audioSource = getAudioSource(elem); if (messageType == 'sent' && message.from_person_id == userId) { if (gifSource.length > 0) { let match = /embed\/(.+)/.exec(message.mssg); if (match && gifSource[0].src.includes(match[1])) { return message; } } else if (audioSource.length > 0) { if (message?.multimedia?.audio?.url == audioSource[0].src) { return message; } } else if (convert(message.mssg) == elem.textContent) { return message; } } else if (messageType == 'received' && message.to_person_id == userId) { if (gifSource.length > 0) { let match = /embed\/(.+)/.exec(message.mssg); if (match && gifSource[0].src.includes(match[1])) { return message; } } else if (audioSource.length > 0) { if (message?.multimedia?.audio?.url == audioSource[0].src) { return message; } } else if (convert(message.mssg) == elem.textContent) { return message; } } } } /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {HTMLElement} elem * @returns {HTMLSourceElement[]} */ function getGifSource(elem) { return Array.from(elem.querySelectorAll('.message-gif > video > source')); } /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {HTMLElement} elem * @returns {HTMLAudioElement[]} */ function getAudioSource(elem) { return Array.from(elem.querySelectorAll('audio')); } let userId = user.user_id; /** @type {[]} */ let messages = resp.body[0].client_open_chat.chat_messages; let index = 0; let messagesSelector = '.messages-list__conversation > .message > div > div > div > .message-bubble__text, .messages-list__conversation > .message > div > div > div > .message-gif__media, .messages-list__conversation > .message > div > div > .message-audio'; async function arriveWorker(ev) { /** @type {HTMLElement} */ let elem = ev; while (!(elem.classList.contains('message--out') || elem.classList.contains('message--in'))) { elem = elem.parentElement; } // let elem = ev.parentElement.parentElement.parentElement.parentElement; try { let sent = elem.className.includes('out'); let received = elem.className.includes('in'); let messageType = sent ? 'sent' : 'received'; let foundMessage = findMessage(userId, messages, messageType, elem); if (DEBUG_MESSAGES) { logger.debug(`${sent ? '<-' : '->'} :: ${foundMessage ? foundMessage.mssg : null}`); logger.debug(elem); logger.debug(''); } if (foundMessage) { try { let date = new Date(0); date.setUTCSeconds(foundMessage.date_created); elem.setAttribute('title', moment(date).format('ddd MMM Do yyyy h:mm A')); } catch (error2) { logger.error('[akkd.error2]', error2); } } } catch (error1) { logger.error('[akkd.error1]', error1); } index++; } // .messages-list__conversation > .message $(document).unbindArrive(messagesSelector); $(document).arrive(messagesSelector, arriveWorker); } /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {string} selector * @param {string} clazz * @param {[]} matches */ async function updateOnlineStatus(selector, clazz, matches) { try { /** @type {HTMLElement[]} */ let elems = (document.querySelector(selector) || { children: [] }).children; elems.forEach(async (elem) => { let onlineStatusElem = elem.querySelector(`.${clazz}`); let onlineStatusElemExists = onlineStatusElem != null; let uidElem = [elem, ...Array.from(elem.querySelectorAll('li, div'))].find((e) => e.hasAttribute('data-qa-uid')); let match = matches.find((match) => match.user_id === (uidElem ? uidElem.getAttribute('data-qa-uid') : null)); if (!match) { return; } elem.setAttribute('data-qa-online-status', match.online_status); let div = makeOnlineStatusSVG(clazz, match.online_status, onlineStatusElem); if (!onlineStatusElemExists) { uidElem.append(div); } let user = bumbleUsersCurrent.find((user) => user.user_id === (uidElem ? uidElem.getAttribute('data-qa-uid') : null)); if (user.time_since_last_seen_h != null && user.time_since_last_seen_h != undefined) { 7; div.parentElement.setAttribute( 'title', `Last seen: ${user.time_since_last_seen_h} ${user.last_seen_dt}` ); } try { await playOnlineSound(user); } catch (error) {} }); } catch (err) { logger.error(err); } } /** * * * @author Michael Barros <michaelcbarros@gmail.com> */ async function updateOnlineStatusChat() { return; try { let elem = $('.page__content > .messages-header > .messages-header__inner > .messages-header__content .header-2')[0]; let userId = findKeyPathsFuzzy($('.page__content')[0], 'userId')[0].value; let name = findKeyPathsFuzzy($('.page__content')[0], 'chatUser')[0].value.name; let user = bumbleUsersNew.find((user) => user.user_id === userId); let color; switch (user.online_status) { case 1: // online color = 'green'; break; case 2: // ?? color = 'yellow'; break; case 3: // offline color = 'grey'; break; default: // offline color = 'grey'; break; } elem.style.color = color; } catch (err) {} } /** @type {{ name: string; userId: string; timePlayed: number; }[]} */ let playedNames = []; // #region Notify User /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {BumbleUser} user */ async function playOnlineSound(user) { if (GM_config.get('play_users_to_monitor_sound', true)) { /** @type {{ userId: string, name: string }[]} */ let usersToMonitor = JSON.parse(GM_config.get('users_to_monitor')); for (let i = 0; i < usersToMonitor.length; i++) { const userId = usersToMonitor[i].userId; if (!findPlayedName(user.user_id) && user.online_status == 1 && user.user_id == userId) { let imgUrl = (await testUrl(user.profile_photo_preview_url)) ? user.profile_photo_preview_url : user.profile_photo_large_url; notifyUser(user.name, imgUrl, user.user_id); localStorage.setItem('users', ESSerializer.serialize(bumbleUsersCurrent)); playedNames.push({ name: userId, userId: user.user_id, timePlayed: new Date().getTime(), }); } else if (findPlayedName(user.user_id) && user.online_status !== 1 && user.user_id == userId) { playedNames = playedNames.filter((x) => x.userId !== user.user_id); } } } } async function testUrl(url) { try { const response = await fetch(url, { method: 'HEAD' }); return response.ok; } catch (error) { logger.error(`Error testing ${url}: ${error.message}`); return false; } } function findPlayedName(userId) { return playedNames.find((x) => x.userId == userId); } /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {string} userName * @param {string} imgUrl * @param {string} userId */ function notifyUser(userName, imgUrl, userId) { /** @type {NotificationOptions} */ let notificationOptions = { body: `${userName} is online`, icon: imgUrl, }; /** @type {Notification} */ let notification; if (!('Notification' in window)) { window.focus(); alert('This browser does not support desktop notification'); return; } else if (Notification.permission === 'granted') { notification = new Notification('Bumble', notificationOptions); } else if (Notification.permission !== 'denied') { Notification.requestPermission(function (permission) { if (permission === 'granted') { notification = new Notification('Bumble', notificationOptions); } }); } notification.onclick = async function () { window.focus(); for (let i = 0; i < 5; i++) { setTimeoutEx(() => { openChat(userId); }, 100 * i); } }; try { playBeep(); } catch (error) {} } /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {{ userId: string, name: string }[]} usersToMonitor * @param {string} userId * @returns {{ userId: string, name: string } | undefined} */ function findUserToMonitor(usersToMonitor, userId) { return usersToMonitor.find((user) => user.userId == userId); } function setupUserMonitorDropdown() { let selector = '.page__content'; // Options for the observer (which mutations to observe) let config = { characterData: true, attributes: true, childList: true, subtree: true }; // Callback function to execute when mutations are observed let callback = (mutationList, observer) => { if ($('.page__content > .messages-header > .messages-header__inner > .messages-header__content .header-2').length > 0) { let optionsContainer = $('.page__content > .messages-header > .messages-header__inner > .messages-header__menu .options'); if (optionsContainer.length > 0) { optionsContainer = optionsContainer[0]; if ($('#akkd-notify-option').length > 0) { $('#akkd-notify-option')[0].remove(); } let userId = findKeyPathsFuzzy($('.page__content')[0], 'userId')[0].value; let name = findKeyPathsFuzzy($('.page__content')[0], 'chatUser')[0].value.name; let usersToMonitor = JSON.parse(GM_config.get('users_to_monitor')); let user = findUserToMonitor(usersToMonitor, userId); let optionText = user ? 'Remove from monitor' : 'Add to monitor'; let elem = document.createElement('div'); elem.innerHTML = ` <div class="option " data-qa-role="option" data-qa-value="FOLDER_ONLINE", id="akkd-notify-option"> <div class="option__text"> <div class="p-2 text-ellipsis text-break-words"> <span>${optionText}</span> </div> </div> </div>`; elem = elem.children[0]; elem.addEventListener('click', async (ev) => { let usersToMonitor = JSON.parse(GM_config.get('users_to_monitor')); if (user) { usersToMonitor = usersToMonitor.filter((u) => u.userId != userId); playedNames = playedNames.filter((x) => x.userId !== userId); } else { usersToMonitor.push({ userId, name }); } GM_config.set('users_to_monitor', JSON.stringify(usersToMonitor, null, 4)); GM_config.set('valid_users_to_monitor', GM_config.get('users_to_monitor')); GM_config.save(); }); getWindow().akkd_observer2.disconnect(); optionsContainer.append(elem); getWindow().akkd_observer2.observe($(selector)[0], config); updateOnlineStatusChat(); } } }; try { getWindow().akkd_observer2.disconnect(); } catch (error) {} // Create an observer instance linked to the callback function getWindow().akkd_observer2 = new MutationObserver(callback); getWindow().akkd_observer2.observe($(selector)[0], config); } // #endregion Notify User async function updateOnlineStatuses() { await updateUserLists(); await updateEncountersLists(); // Carousel await updateOnlineStatus('.scrollable-carousel__scroll', 'show-bumble-carousel-online', queue); // Messages await updateOnlineStatus('.scroll__inner', 'show-bumble-msgs-online', convos); // Chat updateOnlineStatusChat(); } function setupUserMsgCarouselArrive() { $(document).arrive('.contact, .scrollable-carousel-item', async function (ev) { await updateOnlineStatuses(); }); } function deserializeUsers() { let bumbleUsersCurrentSerialized = localStorage.getItem('users'); bumbleUsersCurrent = bumbleUsersCurrentSerialized ? ESSerializer.deserialize(bumbleUsersCurrentSerialized, [BumbleUser]) : []; return bumbleUsersCurrent; } /** * * * @author Michael Barros <michaelcbarros@gmail.com> * @param {[]} users * @returns {*} */ async function createBumbleUsers(users) { deserializeUsers(); bumbleUsersNew = []; for (let i = 0; i < bumbleUsersCurrent.length; i++) { let user = bumbleUsersCurrent[i]; user.updateLastSeen(); // if (currentUser.length > 0) { // } // await playOnlineSound(user); // bumbleUsersNew.push(user); } for (let i = 0; i < users.length; i++) { let user = new BumbleUser(users[i]); let currentUser = bumbleUsersCurrent.filter((x) => x.user_id == user.user_id); if (currentUser.length > 0) { user = currentUser[0].updateProps(user); } await playOnlineSound(user); bumbleUsersNew.push(user); } for (let i = 0; i < bumbleUsersNew.length; i++) { const userNew = bumbleUsersNew[i]; let currentUserIndex = bumbleUsersCurrent.findIndex((x) => x.user_id == userNew.user_id); if (currentUserIndex > -1) { bumbleUsersCurrent[currentUserIndex] = userNew; } else { bumbleUsersCurrent.push(userNew); } } localStorage.setItem('users', ESSerializer.serialize(bumbleUsersCurrent)); getWindow().bumbleUsersCurrent = bumbleUsersCurrent; getWindow().bumbleUsersNew = bumbleUsersNew; firstBumbleCreations = false; } async function updateUserLists() { if (!updateing) { updateing = true; let body = JSON.stringify({ $gpb: 'badoo.bma.BadooMessage', body: [ { message_type: 245, server_get_user_list: { user_field_filter: { projection: [200, 210, 340, 230, 640, 580, 300, 860, 280, 590, 591, 250, 700, 762, 592, 880, 582, 930, 585, 583, 305, 330, 763, 1423, 584, 1262, 911, 912], }, preferred_count: 1000, folder_id: 0, }, }, ], message_id: lastestMessageId++, message_type: 245, version: 1, is_background: false, }); let res = await fetch('https://bumble.com/mwebapi.phtml?SERVER_GET_USER_LIST', { headers: { accept: '*/*', 'accept-language': 'en-US,en;q=0.9', 'content-type': 'application/json', 'sec-ch-ua': '".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'x-message-type': '245', 'x-pingback': calculateBumbleChecksum(body), 'x-use-session-cookie': '1', }, referrer: 'https://bumble.com/app/connections', referrerPolicy: 'origin-when-cross-origin', body: body, method: 'POST', mode: 'cors', credentials: 'include', }); let resp = await res.json(); try { let sections = resp.body[0].client_user_list.section; for (let i = 0; i < sections.length; i++) { let section = sections[i]; let sectionName = section.name; if (section.users) { await createBumbleUsers(section.users); if (sectionName == 'Conversations') { convos = []; if (section.users) { convos.push(...section.users); } } else if (sectionName == 'Match Queue') { queue = []; if (section.users) { queue.push(...section.users); } } } } } catch (error) {} setTimeoutEx(() => { updateing = false; }, 5000); } } async function updateEncountersLists(force = false) { if (!updateingEncounters || force) { updateingEncounters = true; let body = JSON.stringify({ $gpb: 'badoo.bma.BadooMessage', body: [ { message_type: 81, server_get_encounters: { number: 10, context: 1, user_field_filter: { projection: [10, 11, 71, 91, 93, 100, 200, 210, 220, 230, 231, 250, 280, 291, 300, 305, 310, 330, 340, 370, 380, 490, 493, 530, 540, 560, 570, 580, 582, 583, 584, 585, 590, 591, 592, 640, 662, 700, 732, 762, 763, 860, 880, 890, 911, 912, 930, 1140, 1150, 1160, 1161, 1262, 1423], request_albums: [ { album_type: 7, }, { album_type: 12, external_provider: 12, count: 8, }, ], game_mode: 0, request_music_services: { top_artists_limit: 8, supported_services: [29], preview_image_size: { width: 120, height: 120, }, }, }, }, }, ], message_id: lastestMessageId++, message_type: 81, version: 1, is_background: false, }); let res = await fetch('https://bumble.com/mwebapi.phtml?SERVER_GET_ENCOUNTERS', { headers: { accept: '*/*', 'accept-language': 'en-US,en;q=0.9', 'content-type': 'application/json', 'sec-ch-ua': '".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'x-message-type': '81', 'x-pingback': calculateBumbleChecksum(body), 'x-use-session-cookie': '1', }, referrer: 'https://bumble.com/app', referrerPolicy: 'origin-when-cross-origin', body: body, method: 'POST', mode: 'cors', credentials: 'include', }); let resp = await res.json(); try { numEncountersCalls++; if ('results' in resp.body[0].client_encounters) { encs = []; encs.push(...resp.body[0].client_encounters.results); quota = (resp.body[0].client_encounters.quota || {}).yes_votes_quota || 0; updateBackToBanner(encs.length); } else if ('user_substitutes' in resp.body[0].client_encounters) { updateBackToBanner(0); } // responseText = JSON.stringify(resp); } catch (error) {} setTimeoutEx(() => { updateingEncounters = false; }, 2000); } } function setupSidebarArrive() { $(document).arrive('.sidebar-action-banner', async function (ev) { await updateEncountersLists(true); }); } function setupBiggerProfilePictures() { GM_addStyle(`.gallery-preview__media-image { max-width: 110% !important; max-height: 110% !important; } .gallery__preview { max-height: 100% !important; } .dialog-layout .gallery__close { height: 64px !important; } `); } function actionFunction(jNode) { let x = document.getElementsByClassName('profile__photo'); for (let i = 0; i < x.length; i++) { let src = x[i].src; let slug = src.split('&size')[0]; let elemHtml = `<li class="profile__badge"> <div class="pill"> <div class="pill__title"> <div class="p-3 text-ellipsis font-weight-medium"> <a href="${slug}" style="text-decoration:underline;color:#454650;" target="_blank">Pic ${i}</a> </div> </div> </div> </li> `; $('.profile__badges').append(elemHtml); } } function setupModifyRequestHeaders() { // hook and generate fake 'responseText' xhook.before(function (request) { let projection = findNestedObject(request.headers, 'projection'); if (projection) { logger.debug(projection); // xhook.updateRequestHeaders(request, { // 'Cache-Control': 'no-store', // no-store', // , no-cache // // pragma: 'no-cache', // // Cache-Control: no-cache, no-transform // }); } // if (request.url.includes('board-response')) { // xhook.updateRequestHeaders(request, { // 'Cache-Control': 'no-store', // no-store', // , no-cache // // pragma: 'no-cache', // // Cache-Control: no-cache, no-transform // }); // request.url = `${request.url}&t=${new Date().valueOf()}`; // } }); } function initNewSection() { function setupNewSection() { try { let akkdSections = Array.from(document.querySelectorAll('.akkd-section')); if (akkdSections.length > 0) { akkdSections.forEach((elem) => { elem.remove(); }); } let storySection = document.querySelectorAll('.encounters-story-profile')[0]; let aboutSectionHeader = document.querySelectorAll('.encounters-story-section__heading')[0].cloneNode(true); let aboutSection = document.querySelectorAll('.encounters-story-about__badges')[0].cloneNode(true); let locationSectionHeader = document.querySelectorAll('.encounters-story-section.encounters-story-section--location')[0].firstChild.cloneNode(true); let locationSection = document.querySelectorAll('.location-widget.location-widget--align-center')[0].cloneNode(true); let childElems = [ { elem: aboutSectionHeader, isTitle: true }, { elem: aboutSection, isTitle: false }, { elem: locationSectionHeader, isTitle: true }, { elem: locationSection, isTitle: false }, ]; childElems.forEach((elem) => { elem.elem.classList.add('akkd-section'); storySection.appendChild(elem.elem); elem.elem.style.paddingTop = '10px'; if (!elem.isTitle) { elem.elem.style.paddingBottom = '10px'; } }); Array.from(aboutSection.querySelectorAll('.encounters-story-about__badge')).forEach((elem) => { elem.style.margin = 'unset'; }); Array.from(locationSection.querySelectorAll('.header-2.text-color-black')).forEach((elem) => { elem.style.fontSize = '16px'; }); } catch (error) {} } function setupMutationObserver02(encountersUserFrame) { if (getWindow().akkd_observer) { try { getWindow().akkd_observer.disconnect(); } catch (error) {} } // Options for the observer (which mutations to observe) let config = { attributes: true, childList: true, subtree: true }; // Callback function to execute when mutations are observed let callback = (mutationList, observer) => { for (let m = 0; m < mutationList.length; m++) { let mutation = mutationList[m]; for (let i = 0; i < mutation.addedNodes.length; i++) { let addedNode = mutation.addedNodes[i]; try { let classList = Array.from(addedNode.classList); if (classList.includes('encounters-album__stories-container') || classList.includes('encounters-album anim-enter-done') || classList.includes('encounters-album__progress')) { setupNewSection(); } } catch (error) {} } } }; // Create an observer instance linked to the callback function getWindow().akkd_observer = new MutationObserver(callback); // Start observing the target node for configured mutations // let encountersUserFrame = document.querySelectorAll('.encounters-user__frame')[0]; getWindow().akkd_observer.observe(encountersUserFrame, config); } if (document.querySelectorAll('body').length == 0) { setTimeoutEx(() => { initNewSection(); }, 100); } else { // Options for the observer (which mutations to observe) let config = { attributes: true, childList: true, subtree: true }; // Callback function to execute when mutations are observed let callback = (mutationList, observer) => { for (let m = 0; m < mutationList.length; m++) { let mutation = mutationList[m]; for (let i = 0; i < mutation.addedNodes.length; i++) { let addedNode = mutation.addedNodes[i]; try { if (Array.from(addedNode.classList).includes('encounters-user__frame')) { setupNewSection(); setupMutationObserver02(addedNode); } } catch (error) {} } } }; // Create an observer instance linked to the callback function let observer = new MutationObserver(callback); // Start observing the target node for configured mutations observer.observe(document.querySelectorAll('body')[0], config); setupNewSection(); } } function setupLocationIntercept() { const watchPosition = unsafeWindow.navigator.geolocation.watchPosition; const handlers = {}; unsafeWindow.navigator.geolocation.watchPosition = function (cb1, cb2, options) { // We need to return a handler synchronously, but decide whether we'll use the real watchPosition or not // asynchronously. So we create our own handler, and we'll associate it with the real one later. const handler = Math.floor(Math.random() * 10000); handlers[handler] = watchPosition.apply(navigator.geolocation, [ (position) => { let latitude = 26.181744177358595; let longitude = -80.16515493392944; let newPosition = { coords: { accuracy: position.coords.accuracy, altitude: position.coords.altitude, altitudeAccuracy: position.coords.altitudeAccuracy, heading: position.coords.heading, latitude: latitude, longitude: longitude, speed: position.coords.speed, }, timestamp: position.timestamp, }; cb1(newPosition); }, (error) => { cb2(error); }, options, ]); return handler; }; const clearWatch = unsafeWindow.navigator.geolocation.clearWatch; unsafeWindow.navigator.geolocation.clearWatch = function (handler) { if (handler in handlers) { clearWatch.apply(navigator.geolocation, [handlers[handler]]); delete handlers[handler]; } }; } let saveContainerLeft; let saveContainerTop; function createLastSeenTablePopup() { function generateFloatingTable(arr) { // Create the container element const container = document.createElement('div'); container.style.position = 'fixed'; container.style.top = saveContainerTop ? `${saveContainerTop}px` : '50%'; container.style.left = saveContainerLeft ? `${saveContainerLeft}px` : '50%'; container.style.transform = 'translate(-50%, -50%)'; container.style.zIndex = '9999'; container.style.overflow = 'none'; container.style.maxHeight = '80vh'; container.style.backgroundColor = '#333333'; container.style.border = '1px solid black'; container.style.minWidth = `460.3px !important`; container.id = 'akkd-users-table'; // Create the title bar const titleBar = document.createElement('div'); titleBar.style.cursor = 'move'; titleBar.style.padding = '8px'; titleBar.style.userSelect = 'none'; titleBar.style.backgroundColor = '#ccc'; titleBar.style.textAlign = 'center'; // Center text alignment titleBar.style.borderBottom = '1px solid black'; titleBar.textContent = 'Matches'; const container2 = document.createElement('div'); container2.style.overflow = 'auto'; container2.style.maxHeight = '70vh'; container2.style.border = '1px solid black'; // Create the table element const table = document.createElement('table'); table.style.borderCollapse = 'collapse'; // table.style.border = '1px solid black'; // Create the table header row const headerRow = document.createElement('tr'); const keys = Object.keys(arr[0]); keys.forEach((key) => { if (['name', 'last_seen'].includes(key)) { const th = document.createElement('th'); th.textContent = key; th.style.border = '1px solid black'; th.style.padding = '8px'; headerRow.appendChild(th); } }); table.appendChild(headerRow); createRows(arr, table); // Add table to the container container.appendChild(titleBar); container2.appendChild(table); container.appendChild(container2); let intervalId = setIntervalEx(() => { let users = getLatestUsers(); // Remove all rows from the table while (table.rows.length > 1) { table.deleteRow(1); } createRows(users, table); }, 5000); // Create close button const closeButton = document.createElement('button'); closeButton.textContent = 'Close'; closeButton.style.margin = '16px'; closeButton.addEventListener('click', () => { document.body.removeChild(container); try { clearIntervalEx(intervalId); } catch (error) {} }); container.appendChild(closeButton); let isOpacityOn = true; // Create close button const opacityButton = document.createElement('button'); opacityButton.textContent = 'Turn Opacity On/Off'; opacityButton.style.margin = '16px'; opacityButton.style.marginRight = 'auto'; opacityButton.style.right = '16px'; opacityButton.style.position = 'absolute'; opacityButton.addEventListener('click', () => { isOpacityOn = !isOpacityOn; }); container.appendChild(opacityButton); // Add event listeners for dragging the container let isDragging = false; let startX = 0; let startY = 0; titleBar.addEventListener('mousedown', (e) => { isDragging = true; startX = e.clientX - container.offsetLeft; startY = e.clientY - container.offsetTop; }); document.addEventListener('mousemove', (e) => { if (isDragging) { const newLeft = e.clientX - startX; const newTop = e.clientY - startY; const containerWidth = container.offsetWidth; const containerHeight = container.offsetHeight; const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; const maxLeft = windowWidth - containerWidth / 2; const maxTop = windowHeight - containerHeight / 2; const maxRight = maxLeft * 1; const maxBottom = maxTop * 1; const boundedLeft = Math.max(containerWidth / 2, Math.min(newLeft, maxLeft)); const boundedTop = Math.max(containerHeight / 2, Math.min(newTop, maxTop)); saveContainerTop = boundedTop; saveContainerLeft = boundedLeft; container.style.left = `${boundedLeft}px`; container.style.top = `${boundedTop}px`; } }); document.addEventListener('mouseup', () => { isDragging = false; }); container.addEventListener('mouseenter', () => { container.style.opacity = 1; }); container.addEventListener('mouseleave', () => { if (isDragging) { return; } if (!isOpacityOn) return; container.style.opacity = 0.25; }); // Add a mousemove event listener to the document document.addEventListener('mousemove', handleMouseMove); // Mousemove event handler function handleMouseMove(event) { // Check if the element contains the mouse coordinates if (container.contains(event.target)) { // Mouse is over the element container.style.opacity = 1; } else { // Mouse is not over the element if (isDragging) { return; } if (!isOpacityOn) return; container.style.opacity = 0.25; } } // Add container to the body of the document document.body.appendChild(container); } function createRows(arr, table) { const keys = Object.keys(arr[0]); // Create table rows for each object arr.forEach((obj) => { const row = document.createElement('tr'); keys.forEach((key) => { if (['name', 'last_seen'].includes(key)) { const td = document.createElement('td'); if (key == 'name') { const a = document.createElement('a'); // a.href = `javascript:openChat(${obj['user_id']});`; a.href = '#'; a.addEventListener('click', () => { openChat(obj['user_id']); }); a.textContent = obj[key]; a.style.color = getColorForOnlineStatus(obj); td.appendChild(a); } else { td.textContent = obj[key]; } td.style.border = '1px solid black'; td.style.padding = '8px'; row.appendChild(td); } }); table.appendChild(row); }); } function sortObjectsWithNullLast(arr, key, order = 'asc') { const sortedArr = arr.slice(); // Create a shallow copy of the array sortedArr.sort((a, b) => { const valueA = a[key]; const valueB = b[key]; if (valueA === null && valueB !== null) { return 1; // `null` values are considered greater, so `a` comes after `b` } else if (valueA !== null && valueB === null) { return -1; // `null` values are considered greater, so `a` comes before `b` } else { // Both values are either `null` or non-`null`, use regular comparison if (order === 'asc') { return valueA < valueB ? -1 : valueA > valueB ? 1 : 0; } else if (order === 'desc') { return valueA < valueB ? 1 : valueA > valueB ? -1 : 0; } else { throw new Error('Invalid sort order. Please provide "asc" or "desc".'); } } }); return sortedArr; } function logPropertiesInEvenColumns(arr) { const keys = Object.keys(arr[0]); // Calculate the maximum length of each property value const maxLengths = {}; keys.forEach((key) => { maxLengths[key] = Math.max(...arr.map((obj) => String(obj[key]).length)); }); // Log the properties in even columns arr.forEach((obj) => { let output = ''; keys.forEach((key, index) => { const value = String(obj[key]); const padding = ' '.repeat(maxLengths[key] - value.length); output += `${key}: ${value}${padding} ${index % 2 !== 0 ? '\t' : ''}`; }); logger.debug(output); }); } function getLatestUsers() { let users = sortObjectsWithNullLast(bumbleUsersCurrent.slice(), 'time_since_last_seen', 'asc').map((x, index) => { return { name: x.name, last_seen: x.time_since_last_seen_h, user_id: x.user_id, online_status: x.online_status, time_since_last_seen: x.time_since_last_seen, }; }); return users; } function getColorForOnlineStatus(user) { if (user['time_since_last_seen'] != 0) return 'grey'; switch (user['online_status']) { case 1: // online return 'green'; case 2: // ?? return 'yellow'; case 3: // offline return 'grey'; default: // offline return 'grey'; } } generateFloatingTable(getLatestUsers()); } // #region Conversation Option function addConversationOption() { let isShowOnline = false; let showOnlineContactsIntervalId; $(document).arrive('.contact-tabs__section-header-dropdown', async function (ev) { let optionsContainer = document.querySelectorAll('.contact-tabs__section-header-dropdown > .dropdown')[0].querySelectorAll('.options')[0]; let elem = document.createElement('div'); elem.innerHTML = `<div class="option " data-qa-role="option" data-qa-value="FOLDER_ONLINE"><div class="option__text"><div class="p-2 text-ellipsis text-break-words">Online</div></div></div>`; elem = elem.children[0]; elem.addEventListener('click', async (ev) => { isShowOnline = !isShowOnline; try { clearIntervalEx(showOnlineContactsIntervalId); } catch (error) {} if (isShowOnline) { $('.contact-tabs__section.contact-tabs__section--conversations .contact-tabs__section-title-text span')[0].style.color = 'green'; await _showOnlyOnlineContactsScroll(); showOnlineContactsIntervalId = setIntervalEx(async () => { _showOnlyOnlineContacts(); }, 1000); } else { $(document.querySelectorAll('.contact-tabs__section-content .scroll__inner')[0]).unbindArrive('.contact'); _showAllContacts(); $('.contact-tabs__section.contact-tabs__section--conversations .contact-tabs__section-title-text span')[0].style.color = ''; } setTimeoutEx(() => { document.querySelectorAll('.contact-tabs__section-header-dropdown > .dropdown')[0].classList.remove('is-active'); }, 1); document.querySelectorAll('.contact-tabs__section-header-dropdown > .dropdown')[0].classList.remove('is-active'); }); optionsContainer.append(elem); }); } async function _showOnlyOnlineContactsScroll() { /** @type {HTMLElement} */ let contactsContainer = document.querySelectorAll('.contact-tabs__section-content .scroll__inner')[0]; let previousScrollTop; let tries = 0; let maxTries = 250; async function arriveWorker(ev) { // index++; if (!ev.classList.contains('is-loading')) { _showOnlyOnlineContacts(); } } // .messages-list__conversation > .message $(contactsContainer).unbindArrive('.contact'); // showOnlyOnlineContacts(); do { previousScrollTop = contactsContainer.scrollTop; contactsContainer.scrollTo({ top: contactsContainer.scrollHeight, behavior: 'auto' }); await wait(1); _showOnlyOnlineContacts(); if (previousScrollTop != contactsContainer.scrollTop) { tries = 0; } else { tries++; } } while (previousScrollTop != contactsContainer.scrollTop || tries < maxTries); contactsContainer.scrollTo({ top: 0, behavior: 'auto' }); // $(contactsContainer).unbindArrive('.contact'); $(contactsContainer).arrive('.contact', arriveWorker); } async function _showAllContactsScroll() { /** @type {HTMLElement} */ let contactsContainer = document.querySelectorAll('.contact-tabs__section-content .scroll__inner')[0]; let previousScrollTop; let tries = 0; let maxTries = 250; async function arriveWorker(ev) { // index++; if (!ev.classList.contains('is-loading')) { _showOnlyOnlineContacts(); } } // .messages-list__conversation > .message // $(contactsContainer).unbindArrive('.contact'); // showOnlyOnlineContacts(); do { previousScrollTop = contactsContainer.scrollTop; contactsContainer.scrollTo({ top: contactsContainer.scrollHeight, behavior: 'auto' }); await wait(1); // _showOnlyOnlineContacts(); if (previousScrollTop != contactsContainer.scrollTop) { tries = 0; } else { tries++; } } while (previousScrollTop != contactsContainer.scrollTop || tries < maxTries); contactsContainer.scrollTo({ top: 0, behavior: 'auto' }); // $(contactsContainer).unbindArrive('.contact'); // $(contactsContainer).arrive('.contact', arriveWorker); } function _showAllContacts() { /** @type {HTMLElement[]} */ let contactElems = Array.from(document.querySelectorAll('.contact-tabs__section-content .scroll__inner .contact')); for (let i = 0; i < contactElems.length; i++) { const contactElem = contactElems[i]; contactElem.style.display = 'flex'; } } function _showOnlyOnlineContacts() { /** @type {HTMLElement[]} */ let contactElems = Array.from(document.querySelectorAll('.contact-tabs__section-content .scroll__inner .contact')); for (let i = 0; i < contactElems.length; i++) { const contactElem = contactElems[i]; let onlineStatus = parseInt(contactElem.getAttribute('data-qa-online-status')); if (onlineStatus != 1) { contactElem.style.display = 'none'; } else { contactElem.style.display = 'flex'; } } } // #endregion Conversation Option function addCustomCss() { let cssStyles = [ { css: /*css*/ `.contact.is-selected { pointer-events: auto !important; cursor: auto !important; } .scrollable-carousel-item.is-selected { pointer-events: auto !important; cursor: auto !important; } #akkd-users-table { min-width: 458.663px !important; transition: opacity 0.1s linear 0s; } div#akkd-users-table button { background-color: rgba(30, 30, 30, .55); padding: 8px; border-radius: 5px; transition: background-color 0.1s linear 0s; } div#akkd-users-table button:hover { background-color: rgba(30, 30, 30, .85); } div#akkd-users-table button:active { background-color: rgba(30, 30, 30, .35); } `, node: document.documentElement, }, ]; addStyles(cssStyles); } function setupConfig() { // demo: http://sizzlemctwizzle.github.io/GM_config/ GM_config.init({ id: `main-${location.host.replace(/\./g, '_')}`, title: 'Bumble Enhanced Config', fields: { play_users_to_monitor_sound: { label: 'Play Users To Monitor Sound', type: 'checkbox', default: true, }, users_to_monitor: { label: 'Users To Monitor', type: 'textarea', title: 'Enter JSON array string', default: '["Example1", "Exmaple2"]', save: false, // This field's value will NOT be saved }, valid_users_to_monitor: { type: 'hidden', default: '', }, }, events: { init: function () { // Set the value of the dummy field to the saved value GM_config.set('users_to_monitor', GM_config.get('valid_users_to_monitor')); }, open: function () { // Use a listener to update the hidden field when the dummy field passes validation GM_config.fields['users_to_monitor'].node.addEventListener( 'change', function () { // get the current value of the visible field var users_to_monitor = GM_config.get('users_to_monitor', true); try { JSON.parse(users_to_monitor); // Only save valid CSS GM_config.set('valid_users_to_monitor', users_to_monitor); } catch (error) {} }, false ); }, save: function (forgotten) { if (GM_config.isOpen) { // If the values don't match then valid_users_to_monitor wasn't valid if (forgotten.users_to_monitor == null || forgotten.users_to_monitor == undefined) { GM_config.set('users_to_monitor', JSON.stringify(JSON.parse(GM_config.get('valid_users_to_monitor')), null, 4)); } else if (forgotten.users_to_monitor !== GM_config.get('valid_users_to_monitor')) { GM_config.set('valid_users_to_monitor', '[]'); GM_config.set('users_to_monitor', '[]'); } else { GM_config.set('users_to_monitor', JSON.stringify(JSON.parse(forgotten.users_to_monitor), null, 4)); } } }, close: function () { logger.debug('users_to_monitor: ', JSON.parse(GM_config.get('users_to_monitor'))); logger.debug('play_users_to_monitor_sound:', GM_config.get('play_users_to_monitor_sound')); logger.debug(''); }, reset: function () {}, }, css: /*css*/ ` #main-bumble_com_field_users_to_monitor { width: calc(100% - 150px) !important; height: calc(100% - 150px) !important; resize: none; }`, }); let menuId = GM_registerMenuCommand(`Config`, () => { GM_config.open(); }); let menuId2 = GM_registerMenuCommand(`Last Seen Table`, () => { createLastSeenTablePopup(); }); } // #endregion Bumble Functions