bumble-enhanced

show votes, show online status, and change location on bumble

  1. // ==UserScript==
  2. // @name bumble-enhanced
  3. // @namespace https://openuserjs.org/users/93Akkord
  4. // @match https://*.bumble.com/*
  5. // @run-at document-start
  6. // @version 3.0.7
  7. // @author 93Akkord
  8. // @description show votes, show online status, and change location on bumble
  9. // @grant GM.getValue
  10. // @grant GM.setValue
  11. // @grant GM_addStyle
  12. // @grant GM_registerMenuCommand
  13. // @grant unsafeWindow
  14. // @grant GM_notification
  15. // @require https://code.jquery.com/jquery-3.2.1.min.js
  16. // @require https://openuserjs.org/src/libs/93Akkord/loglevel.js
  17. // @require https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js
  18. // @require https://openuserjs.org/src/libs/93Akkord/akkd-common.js
  19. // @require https://greasyfork.org/scripts/446257-waitforkeyelements-utility-function/code/waitForKeyElements%20utility%20function.js?version=1059316
  20. // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js
  21. // @require https://openuserjs.org/src/libs/sizzle/GM_config.min.js
  22. // require https://unpkg.com/esserializer/dist/bundle.js
  23. // @require https://cdnjs.cloudflare.com/ajax/libs/luxon/3.2.1/luxon.min.js
  24. // @copyright 2022+, Michael Barros (https://openuserjs.org/users/93Akkord)
  25. // @license CC-BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
  26. // @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
  27. // @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==
  28. // ==/UserScript==
  29.  
  30. // #region Workers
  31.  
  32. // prettier-ignore
  33. {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;}
  34.  
  35. setTimeoutEx = getWindow().setTimeoutEx;
  36. clearTimeoutEx = getWindow().clearTimeoutEx;
  37. setIntervalEx = getWindow().setIntervalEx;
  38. clearIntervalEx = getWindow().clearIntervalEx;
  39.  
  40. window.setTimeoutEx = getWindow().setTimeoutEx;
  41. window.clearTimeoutEx = getWindow().clearTimeoutEx;
  42. window.setIntervalEx = getWindow().setIntervalEx;
  43. window.clearIntervalEx = getWindow().clearIntervalEx;
  44.  
  45. // #endregion Workers
  46.  
  47. // #region luxon
  48.  
  49. luxon.Duration.prototype.__toHuman__ = luxon.Duration.prototype.toHuman;
  50. luxon.Duration.prototype.toHuman = function (opts = { stripZeroUnits: 'all' }) {
  51. let duration = this.normalize();
  52. let durationUnits = [];
  53. let precision = typeof opts.precision == 'object' ? luxon.Duration.fromObject(opts.precision) : luxon.Duration.fromMillis(0);
  54. let remainingDuration = duration;
  55. //list of all available units
  56. const allUnits = ['years', 'months', 'days', 'hours', 'minutes', 'seconds', 'milliseconds'];
  57. let smallestUnitIndex;
  58. let biggestUnitIndex;
  59. // check if user has specified the smallest unit that should be displayed
  60. if (opts.smallestUnit) {
  61. smallestUnitIndex = allUnits.indexOf(opts.smallestUnit);
  62. }
  63. // check if user has specified a biggest unit
  64. if (opts.biggestUnit) {
  65. biggestUnitIndex = allUnits.indexOf(opts.biggestUnit);
  66. }
  67. // use seconds and years as default for smallest and biggest unit
  68. if (!smallestUnitIndex || !(smallestUnitIndex >= 0 && smallestUnitIndex < allUnits.length)) smallestUnitIndex = allUnits.indexOf('seconds');
  69. if (!biggestUnitIndex || !(biggestUnitIndex <= smallestUnitIndex && biggestUnitIndex < allUnits.length)) biggestUnitIndex = allUnits.indexOf('years');
  70. for (let unit of allUnits.slice(biggestUnitIndex, smallestUnitIndex + 1)) {
  71. const durationInUnit = remainingDuration.as(unit);
  72. if (durationInUnit >= 1) {
  73. durationUnits.push(unit);
  74. let tmp = {};
  75. tmp[unit] = Math.floor(remainingDuration.as(unit));
  76. remainingDuration = remainingDuration.minus(luxon.Duration.fromObject(tmp)).normalize();
  77. // check if remaining duration is smaller than precision
  78. if (remainingDuration < precision) {
  79. // ok, we're allowed to remove the remaining parts and to round the current unit
  80. break;
  81. }
  82. }
  83. // check if we have already the maximum count of units allowed
  84. if (opts.maxUnits && durationUnits.length >= opts.maxUnits) {
  85. break;
  86. }
  87. }
  88. // after gathering of units that shall be displayed has finished, remove the remaining duration to avoid non-integers
  89. duration = duration.minus(remainingDuration).normalize();
  90. duration = duration.shiftTo(...durationUnits);
  91. if (opts.stripZeroUnits == 'all') {
  92. durationUnits = durationUnits.filter((unit) => duration.values[unit] > 0);
  93. } else if (opts.stripZeroUnits == 'end') {
  94. let mayStrip = true;
  95. durationUnits = durationUnits.reverse().filter((unit /*, index*/) => {
  96. if (!mayStrip) return true;
  97. if (duration.values[unit] == 0) {
  98. return false;
  99. } else {
  100. mayStrip = false;
  101. }
  102. return true;
  103. });
  104. }
  105. // if `durationUnits` is empty (i.e. duration is zero), then just shift to the smallest unit
  106. if (!durationUnits.length) {
  107. durationUnits.push(allUnits[smallestUnitIndex]);
  108. }
  109. return duration.shiftTo(...durationUnits).__toHuman__(opts);
  110. };
  111.  
  112. // #endregion luxon
  113.  
  114. // #region ESSerializer
  115.  
  116. // prettier-ignore
  117. 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)})())}
  118.  
  119. // #endregion ESSerializer
  120.  
  121. // #region Bumble Classes
  122.  
  123. class BumbleUser {
  124. constructor(userProps) {
  125. this.updateProps(userProps);
  126. }
  127.  
  128. updateProps(userProps) {
  129. this.user_id = userProps.user_id;
  130. this.access_level = userProps.access_level;
  131. this.name = userProps.name;
  132. this.age = userProps.age;
  133. this.gender = userProps.gender;
  134. this.is_deleted = userProps.is_deleted;
  135. this.is_extended_match = userProps.is_extended_match;
  136. this.online_status = userProps.online_status;
  137. this.is_match = userProps.is_match;
  138. this.match_mode = userProps.match_mode;
  139. this.is_crush = userProps.is_crush;
  140. this.their_vote_mode = userProps.their_vote_mode;
  141. this.unread_messages_count = userProps.unread_messages_count;
  142. this.is_inapp_promo_partner = userProps.is_inapp_promo_partner;
  143. this.is_locked = userProps.is_locked;
  144. this.type = userProps.type;
  145. this.connection_status_indicator = userProps.connection_status_indicator;
  146. this.profile_photo_preview_url = this.profile_photo_preview_url || userProps?.profile_photo?.preview_url || userProps.profile_photo_preview_url;
  147. this.profile_photo_large_url = this.profile_photo_large_url || userProps?.profile_photo?.large_url || userProps.profile_photo_large_url;
  148. this.last_seen = this.online_status == 1 ? new Date().getTime() : this.last_seen || userProps.last_seen;
  149. 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;
  150. this.time_since_last_seen = this.last_seen != null && this.last_seen != undefined ? new Date().getTime() - this.last_seen : null;
  151. 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;
  152. 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;
  153.  
  154. return this;
  155. }
  156.  
  157. updateLastSeen() {
  158. 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;
  159. this.time_since_last_seen = this.last_seen != null && this.last_seen != undefined ? new Date().getTime() - this.last_seen : null;
  160. 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;
  161.  
  162. return this;
  163. }
  164. }
  165.  
  166. // #endregion Bumble Classes
  167.  
  168. initESSerializer(getWindow());
  169.  
  170. const DEBUG_MESSAGES = false;
  171.  
  172. let encs = [];
  173. let user;
  174. let numEncountersCalls = 0;
  175. let queue = [];
  176. let convos = [];
  177. let quota = 0;
  178. let lastestMessageId;
  179.  
  180. const userIds = [];
  181.  
  182. let trackUserOnline = false;
  183. let firstBumbleCreations = true;
  184. let updateing = false;
  185. let updateingEncounters = false;
  186.  
  187. /** @type {BumbleUser[]} */
  188. let bumbleUsersCurrent = [];
  189.  
  190. /** @type {BumbleUser[]} */
  191. let bumbleUsersNew = [];
  192.  
  193. /** @type {(userId: string) => void} */
  194. let openChat;
  195.  
  196. let debugObj = {
  197. projection: [],
  198. };
  199.  
  200. const logger = getLogger('bumble-enhanced', { logLevel: log.levels.DEBUG });
  201.  
  202. logger.info(`Loading ${GM_info.script.name}...`);
  203.  
  204. /* jshint esversion: 8 */
  205. (async function () {
  206. setupConfig();
  207.  
  208. deserializeUsers();
  209.  
  210. exposeGlobalVariables([
  211. // libs
  212. { name: 'jQuery', value: jQuery },
  213. { name: '$', value: $ },
  214.  
  215. // functions/variables
  216. { name: 'pp', value: pp },
  217. { name: 'pformat', value: pformat },
  218. { name: 'getObjProps', value: getObjProps },
  219. { name: 'getUserDefinedGlobalProps', value: getUserDefinedGlobalProps },
  220. { name: 'getLocalStorageSize', value: getLocalStorageSize },
  221. { name: 'unsafeWindow', value: unsafeWindow },
  222. { name: 'getWindow', value: getWindow },
  223. { name: 'getTopWindow', value: getTopWindow },
  224. { name: 'getStyle', value: getStyle },
  225. { name: 'BumbleUser', value: BumbleUser },
  226. { name: 'deserializeUsers', value: deserializeUsers },
  227. { name: 'findKeyPathsFuzzy', value: findKeyPathsFuzzy },
  228. { name: 'luxon', value: luxon },
  229. { name: 'openChat', value: openChat },
  230. { name: 'debugObj', value: debugObj },
  231. { name: 'serverGetUser', value: serverGetUser },
  232.  
  233. { name: '_showAllContactsScroll', value: _showAllContactsScroll },
  234. { name: 'wait', value: wait },
  235. ]);
  236.  
  237. $(document).arrive('.contact', function () {
  238. if (openChat == null || openChat == undefined) {
  239. openChat = findKeyPathsFuzzy(document.querySelectorAll('.contact')[0], 'openChat')[0].value;
  240.  
  241. exposeGlobalVariables([
  242. // functions/variables
  243. { name: 'openChat', value: openChat },
  244. ]);
  245. }
  246. });
  247.  
  248. function setupPageContentArriveHandler() {
  249. $(document).unbindArrive('.page__content', setupPageContentArriveHandler);
  250.  
  251. setupUserMonitorDropdown();
  252.  
  253. $(document).arrive('.page__content', setupPageContentArriveHandler);
  254. }
  255.  
  256. $(document).arrive('.page__content', setupPageContentArriveHandler);
  257.  
  258. if (!unsafeWindow.XMLHttpRequest.prototype.getResponseText) {
  259. unsafeWindow.XMLHttpRequest.prototype.getResponseText = Object.getOwnPropertyDescriptor(unsafeWindow.XMLHttpRequest.prototype, 'responseText').get;
  260. }
  261.  
  262. if (!unsafeWindow.XMLHttpRequest.prototype.sendEx) {
  263. unsafeWindow.XMLHttpRequest.prototype.sendEx = Object.getOwnPropertyDescriptor(unsafeWindow.XMLHttpRequest.prototype, 'send').value;
  264. }
  265.  
  266. Object.defineProperty(unsafeWindow.XMLHttpRequest.prototype, 'send', {
  267. /* eslint-disable-next-line no-undef */
  268. value: exportFunction(function () {
  269. let args = Array.from(arguments);
  270. let body = args[0];
  271.  
  272. // try {
  273. // body = JSON.parse(body);
  274.  
  275. // debugObj.projection = Array.from(new Set([...debugObj.projection, ...findKeyPathsFuzzy(body, 'projection')[0].value])).sort((a, b) => a - b);
  276. // } catch (error) {}
  277.  
  278. // debugger;
  279.  
  280. unsafeWindow.XMLHttpRequest.prototype.sendEx.call(this, ...args);
  281. }, unsafeWindow),
  282. enumerable: true,
  283. configurable: true,
  284. writable: true,
  285. });
  286.  
  287. Object.defineProperty(unsafeWindow.XMLHttpRequest.prototype, 'responseText', {
  288. /* eslint-disable-next-line no-undef */
  289. get: exportFunction(function () {
  290. let responseText = unsafeWindow.XMLHttpRequest.prototype.getResponseText.call(this);
  291.  
  292. try {
  293. let body = JSON.parse(responseText);
  294.  
  295. debugObj.projection = Array.from(new Set([...debugObj.projection, ...findKeyPathsFuzzy(body, 'projection')[0].value])).sort((a, b) => a - b);
  296. } catch (error) {}
  297.  
  298. // debugger;
  299.  
  300. if (this.responseURL.includes('bumble.com/mwebapi.phtml?')) {
  301. const resp = JSON.parse(responseText);
  302.  
  303. lastestMessageId = resp.message_id;
  304. }
  305.  
  306. try {
  307. if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_APP_STARTUP')) {
  308. const resp = JSON.parse(responseText);
  309.  
  310. user = (resp.body.find((o) => o.user) || {}).user;
  311. }
  312.  
  313. if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_ENCOUNTERS_VOTE')) {
  314. const body = JSON.stringify({
  315. $gpb: 'badoo.bma.BadooMessage',
  316. body: [
  317. {
  318. message_type: 81,
  319. server_get_encounters: {
  320. number: 0,
  321. context: 1,
  322. user_field_filter: {
  323. projection: [200],
  324. request_albums: [],
  325. game_mode: 0,
  326. request_music_services: {},
  327. },
  328. },
  329. },
  330. ],
  331. message_id: 1,
  332. message_type: 81,
  333. version: 1,
  334. is_background: false,
  335. });
  336.  
  337. try {
  338. window
  339. .fetch('/mwebapi.phtml?SERVER_GET_ENCOUNTERS', {
  340. method: 'POST',
  341. headers: {
  342. 'Content-Type': 'application/json',
  343. 'X-Message-type': '81',
  344. 'x-use-session-cookie': '1',
  345. 'X-Pingback': calculateBumbleChecksum(body),
  346. },
  347. body,
  348. })
  349. .then((resp) =>
  350. resp.json().then((data) => {
  351. quota = (data.body[0].client_encounters.quota || {}).yes_votes_quota || 0;
  352. (document.querySelector('#voteQuota') || {}).innerHTML = quota;
  353. })
  354. );
  355. } catch (err) {
  356. logger.error(err);
  357. }
  358. }
  359. if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_GET_ENCOUNTERS')) {
  360. numEncountersCalls++;
  361.  
  362. const resp = JSON.parse(responseText);
  363.  
  364. // encs.push(...resp.body[0].client_encounters.results);
  365.  
  366. quota = (resp.body[0].client_encounters.quota || {}).yes_votes_quota || 0;
  367.  
  368. responseText = JSON.stringify(resp);
  369.  
  370. updateEncountersLists();
  371. }
  372.  
  373. if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_GET_USER_LIST')) {
  374. }
  375.  
  376. if (this.responseURL.endsWith('bumble.com/mwebapi.phtml?SERVER_OPEN_CHAT')) {
  377. const resp = JSON.parse(responseText);
  378.  
  379. updateMessagesWithDatetime(resp, user);
  380. }
  381. } catch (err) {
  382. logger.error(err);
  383. }
  384.  
  385. return responseText;
  386. }, unsafeWindow),
  387. enumerable: true,
  388. configurable: true,
  389. });
  390.  
  391. window.setIntervalEx(() => {
  392. const hdr = document.querySelector('.encounters-story-profile__header');
  393.  
  394. if (!hdr) {
  395. return;
  396. }
  397.  
  398. if (hdr.parentElement.querySelector('.showBumbleVotes')) {
  399. return;
  400. }
  401.  
  402. let name = (document.querySelector('.encounters-story-profile__name') || {}).innerText;
  403. let age = +(document.querySelector('.encounters-story-profile__age') || { innerText: '' }).innerText.replace(',', '').trim();
  404. let enc = encs.find((enc) => enc.user.name === name && enc.user.age === age);
  405.  
  406. if (!enc) {
  407. return;
  408. }
  409.  
  410. userIds.push({ name, id: enc.user.user_id });
  411.  
  412. const div = document.createElement('div');
  413.  
  414. div.classList.add('showBumbleVotes');
  415.  
  416. let vote = enc.user.their_vote;
  417. let voteText = vote === 1 ? 'Not voted!' : vote === 2 ? 'Swiped right!' : vote === 3 ? 'Swiped left!' : 'Unknown!';
  418.  
  419. div.innerHTML = `${voteText}<br />
  420. (<span id="voteQuota">${quota}</span> yes votes remaining)`;
  421.  
  422. hdr.after(div);
  423.  
  424. let color;
  425.  
  426. switch (enc.user.online_status) {
  427. case 1:
  428. // online
  429. color = 'green';
  430.  
  431. break;
  432.  
  433. case 2:
  434. // ??
  435. color = 'yellow';
  436.  
  437. break;
  438.  
  439. case 3:
  440. // offline
  441. color = 'grey';
  442.  
  443. break;
  444.  
  445. default:
  446. // offline
  447. color = 'grey';
  448.  
  449. break;
  450. }
  451.  
  452. document.querySelectorAll('span[data-qa-icon-name=profile-badge-about], span[data-qa-icon-name=profile-badge-location]').forEach((iconElem) => {
  453. iconElem.firstChild.setAttribute('color', color);
  454. });
  455. }, 1000);
  456.  
  457. setupUserMsgCarouselArrive();
  458. setupSidebarArrive();
  459.  
  460. setIntervalEx(async () => {
  461. await updateOnlineStatuses();
  462. }, 5000);
  463.  
  464. setIntervalEx(() => {
  465. const filters = document.querySelector('.encounters-filter__content');
  466. if (!filters) return;
  467. if (filters.querySelector('.locationSpoofer')) return;
  468. const div = document.createElement('div');
  469. div.classList.add('encounters-filter__entry');
  470. div.classList.add('locationSpoofer');
  471. div.innerHTML = `
  472. <div class="encounters-filter__content">
  473. <section class="settings-fieldset">
  474. <header class="settings-fieldset__header">
  475. <div class="settings-fieldset__title">
  476. <h2 class="p-1 text-color-gray-dark"><span>Change Location</span></h2>
  477. </div>
  478. </header>
  479. <div class="form__control form__control--vertical">
  480. <div class="form__field">
  481. <div class="text-field text-field--full-rounded" data-qa-role="dialog-add-job-title-field">
  482. <input type="text" id="spoofLatitude" placeholder="Latitude (Decimal Format)" class="text-field__input" maxlength="40" size="5" dir="auto" value="" />
  483. </div>
  484. </div>
  485. </div>
  486. <div class="form__control form__control--vertical">
  487. <div class="form__field">
  488. <div class="text-field text-field--full-rounded" data-qa-role="dialog-add-job-title-field">
  489. <input type="text" id="spoofLongitude" placeholder="Longitude (Decimal Format)" class="text-field__input" maxlength="40" size="5" dir="auto" value="" />
  490. </div>
  491. </div>
  492. </div>
  493. </section>
  494. <div class="encounters-filter__actions">
  495. <div class="encounters-filter__action">
  496. <div class="button button--narrow button--size-m color-primary button--filled" role="button" id="applyLocationSpoofer">
  497. <span class="button__content">
  498. <span class="button__text"><span class="action text-break-words"><span id="applySpoofLocation">Apply Location</span></span></span>
  499. </span>
  500. </div>
  501. </div>
  502. </div>
  503. </div>
  504. `;
  505. filters.prepend(div);
  506. document.querySelector('#applyLocationSpoofer').addEventListener('click', async () => {
  507. const body = JSON.stringify({
  508. $gpb: 'badoo.bma.BadooMessage',
  509. body: [
  510. {
  511. message_type: 4,
  512. server_update_location: {
  513. location: [
  514. {
  515. latitude: +document.querySelector('#spoofLatitude').value,
  516. longitude: +document.querySelector('#spoofLongitude').value,
  517. },
  518. ],
  519. },
  520. },
  521. ],
  522. message_id: 1,
  523. message_type: 4,
  524. version: 1,
  525. is_background: false,
  526. });
  527. try {
  528. const resp = await window.fetch('/mwebapi.phtml?SERVER_UPDATE_LOCATION', {
  529. method: 'POST',
  530. headers: {
  531. 'Content-Type': 'application/json',
  532. 'X-Message-type': '4',
  533. 'x-use-session-cookie': '1',
  534. 'X-Pingback': calculateBumbleChecksum(body),
  535. },
  536. body,
  537. });
  538. document.querySelector('#applySpoofLocation').innerText = 'Location Set!';
  539. filters.querySelector('.color-primary[data-qa-role=button]').click();
  540. } catch (e) {
  541. window.alert(`Error setting location: ${e}`);
  542. }
  543. });
  544. }, 1000);
  545.  
  546. setupBiggerProfilePictures();
  547.  
  548. waitForKeyElements('.profile', actionFunction, false);
  549.  
  550. initNewSection();
  551.  
  552. // setupModifyRequestHeaders();
  553. // setupLocationIntercept();
  554.  
  555. addConversationOption();
  556.  
  557. addCustomCss();
  558.  
  559. logger.info(`${GM_info.script.name} loaded`);
  560. })();
  561.  
  562. // #region Helper Functions
  563.  
  564. function isObject(value) {
  565. return !!(value && typeof value === 'object' && !Array.isArray(value));
  566. }
  567.  
  568. function findNestedObject(object = {}, keyToMatch = '') {
  569. if (isObject(object)) {
  570. const entries = Object.entries(object);
  571.  
  572. for (let i = 0; i < entries.length; i += 1) {
  573. const [objectKey, objectValue] = entries[i];
  574.  
  575. if (objectKey === keyToMatch) {
  576. return object;
  577. }
  578.  
  579. if (isObject(objectValue)) {
  580. const child = findNestedObject(objectValue, keyToMatch);
  581.  
  582. if (child !== null) {
  583. return child;
  584. }
  585. } else if (Array.isArray(objectValue)) {
  586. for (let a = 0; a < objectValue.length; a++) {
  587. const item = objectValue[a];
  588.  
  589. if (isObject(item)) {
  590. const child = findNestedObject(item, keyToMatch);
  591.  
  592. if (child !== null) {
  593. return child;
  594. }
  595. }
  596. }
  597. }
  598. }
  599. }
  600.  
  601. return null;
  602. }
  603.  
  604. let gestureOccured = false;
  605.  
  606. $(document).on('click pointerup mouseup', function (e) {
  607. gestureOccured = true;
  608. });
  609.  
  610. async function playBeep() {
  611. let beep = new Audio('data:audio/mpeg;base64,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//PkRPscWgjcADEslLjcEbgCS9KQsiilgMzc5KVUt066PIOE3y82o9CWehRWhjnIhMCoamFTyK8WFTAWNCllWAqNERMVYDRcU4kKVEbAJI2bQpskQfAkRDyEiUFTRUlzrIopIpaVMrWQoD6FKeETSwqbimh6ERMEyopSBJGGVhEbFKSZKkiIhSoCQmXCp4VDJ8KkqRNLUK0NSaISWBMmm6VS/jG0NIkQhBkBjSpwTK5cfUcleERohQohkSkT2ZWtHyRNFSXL+gKOhYVIrQokTRUESVIEjbSzRCUExCzWdg1VTEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV');
  612.  
  613. try {
  614. if (gestureOccured) {
  615. await beep.play();
  616. }
  617. } catch (error) {}
  618. }
  619.  
  620. function findKeyPathsFuzzy(obj, searchTerm) {
  621. const keyPaths = [];
  622. const visited = new Set();
  623.  
  624. function searchKeys(obj, currentPath) {
  625. if (typeof obj !== 'object' || obj === null || visited.has(obj)) {
  626. return;
  627. }
  628.  
  629. visited.add(obj);
  630.  
  631. const keys = Object.keys(obj);
  632.  
  633. for (const key of keys) {
  634. const newPath = currentPath ? `${currentPath}.${key}` : key;
  635.  
  636. if (fuzzyMatch(key, searchTerm)) {
  637. keyPaths.push({
  638. path: newPath,
  639. value: obj[key],
  640. });
  641. }
  642.  
  643. searchKeys(obj[key], newPath);
  644. }
  645. }
  646.  
  647. searchKeys(obj, '');
  648.  
  649. return keyPaths;
  650. }
  651.  
  652. function fuzzyMatch(str, searchTerm) {
  653. // Perform fuzzy matching logic here
  654. // You can use any fuzzy matching algorithm/library you prefer
  655.  
  656. // For example, a simple case-insensitive substring match
  657. return str.toLowerCase().includes(searchTerm.toLowerCase());
  658. }
  659.  
  660. // #endregion Helper Functions
  661.  
  662. // #region Bumble Functions
  663.  
  664. 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];
  665.  
  666. async function serverGetUser(userId) {
  667. let body = JSON.stringify({
  668. //
  669. $gpb: 'badoo.bma.BadooMessage',
  670. body: [
  671. {
  672. message_type: 403,
  673. server_get_user: {
  674. user_id: userId,
  675. user_field_filter: {
  676. game_mode: 0,
  677. projection: allKnownProjections,
  678. request_music_services: { top_artists_limit: 10, supported_services: [29] },
  679. request_albums: [
  680. { person_id: userId, album_type: 2, offset: 1 },
  681. { person_id: userId, album_type: 12, external_provider: 12 },
  682. ],
  683. },
  684. client_source: 10,
  685. },
  686. },
  687. ],
  688. message_id: lastestMessageId++,
  689. message_type: 403,
  690. version: 1,
  691. is_background: false,
  692. });
  693.  
  694. let res = await fetch('https://bumble.com/mwebapi.phtml?SERVER_GET_USER', {
  695. headers: {
  696. accept: '*/*',
  697. // 'accept-language': 'en-US,en;q=0.9',
  698. 'content-type': 'application/json',
  699. // 'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
  700. // 'sec-ch-ua-mobile': '?0',
  701. // 'sec-ch-ua-platform': '"Windows"',
  702. // 'sec-fetch-dest': 'empty',
  703. // 'sec-fetch-mode': 'cors',
  704. // 'sec-fetch-site': 'same-origin',
  705. // 'x-message-type': '403',
  706. 'x-pingback': calculateBumbleChecksum(body),
  707. 'x-use-session-cookie': '1',
  708. },
  709. // referrer: 'https://bumble.com/app/connections',
  710. // referrerPolicy: 'origin-when-cross-origin',
  711. body: body,
  712. method: 'POST',
  713. // mode: 'cors',
  714. // credentials: 'include',
  715. });
  716.  
  717. res = await res.json();
  718.  
  719. return res;
  720. }
  721.  
  722. function calculateBumbleChecksum(inputString) {
  723. inputString += 'whitetelevisionbulbelectionroofhorseflying';
  724. const hc = '0123456789abcdef';
  725.  
  726. function rh(n) {
  727. let j,
  728. s = '';
  729. for (j = 0; j <= 3; j++) s += hc.charAt((n >> (j * 8 + 4)) & 0x0f) + hc.charAt((n >> (j * 8)) & 0x0f);
  730. return s;
  731. }
  732.  
  733. function ad(x, y) {
  734. let l = (x & 0xffff) + (y & 0xffff);
  735. let m = (x >> 16) + (y >> 16) + (l >> 16);
  736. return (m << 16) | (l & 0xffff);
  737. }
  738.  
  739. function rl(n, c) {
  740. return (n << c) | (n >>> (32 - c));
  741. }
  742.  
  743. function cm(q, a, b, x, s, t) {
  744. return ad(rl(ad(ad(a, q), ad(x, t)), s), b);
  745. }
  746.  
  747. function ff(a, b, c, d, x, s, t) {
  748. return cm((b & c) | (~b & d), a, b, x, s, t);
  749. }
  750.  
  751. function gg(a, b, c, d, x, s, t) {
  752. return cm((b & d) | (c & ~d), a, b, x, s, t);
  753. }
  754.  
  755. function hh(a, b, c, d, x, s, t) {
  756. return cm(b ^ c ^ d, a, b, x, s, t);
  757. }
  758.  
  759. function ii(a, b, c, d, x, s, t) {
  760. return cm(c ^ (b | ~d), a, b, x, s, t);
  761. }
  762.  
  763. function sb(x) {
  764. let i;
  765. let nblk = ((x.length + 8) >> 6) + 1;
  766. let blks = new Array(nblk * 16);
  767. for (i = 0; i < nblk * 16; i++) blks[i] = 0;
  768. for (i = 0; i < x.length; i++) blks[i >> 2] |= x.charCodeAt(i) << ((i % 4) * 8);
  769. blks[i >> 2] |= 0x80 << ((i % 4) * 8);
  770. blks[nblk * 16 - 2] = x.length * 8;
  771. return blks;
  772. }
  773.  
  774. let i,
  775. x = sb(inputString),
  776. a = 1732584193,
  777. b = -271733879,
  778. c = -1732584194,
  779. d = 271733878,
  780. olda,
  781. oldb,
  782. oldc,
  783. oldd;
  784.  
  785. for (i = 0; i < x.length; i += 16) {
  786. olda = a;
  787. oldb = b;
  788. oldc = c;
  789. oldd = d;
  790. a = ff(a, b, c, d, x[i + 0], 7, -680876936);
  791. d = ff(d, a, b, c, x[i + 1], 12, -389564586);
  792. c = ff(c, d, a, b, x[i + 2], 17, 606105819);
  793. b = ff(b, c, d, a, x[i + 3], 22, -1044525330);
  794. a = ff(a, b, c, d, x[i + 4], 7, -176418897);
  795. d = ff(d, a, b, c, x[i + 5], 12, 1200080426);
  796. c = ff(c, d, a, b, x[i + 6], 17, -1473231341);
  797. b = ff(b, c, d, a, x[i + 7], 22, -45705983);
  798. a = ff(a, b, c, d, x[i + 8], 7, 1770035416);
  799. d = ff(d, a, b, c, x[i + 9], 12, -1958414417);
  800. c = ff(c, d, a, b, x[i + 10], 17, -42063);
  801. b = ff(b, c, d, a, x[i + 11], 22, -1990404162);
  802. a = ff(a, b, c, d, x[i + 12], 7, 1804603682);
  803. d = ff(d, a, b, c, x[i + 13], 12, -40341101);
  804. c = ff(c, d, a, b, x[i + 14], 17, -1502002290);
  805. b = ff(b, c, d, a, x[i + 15], 22, 1236535329);
  806. a = gg(a, b, c, d, x[i + 1], 5, -165796510);
  807. d = gg(d, a, b, c, x[i + 6], 9, -1069501632);
  808. c = gg(c, d, a, b, x[i + 11], 14, 643717713);
  809. b = gg(b, c, d, a, x[i + 0], 20, -373897302);
  810. a = gg(a, b, c, d, x[i + 5], 5, -701558691);
  811. d = gg(d, a, b, c, x[i + 10], 9, 38016083);
  812. c = gg(c, d, a, b, x[i + 15], 14, -660478335);
  813. b = gg(b, c, d, a, x[i + 4], 20, -405537848);
  814. a = gg(a, b, c, d, x[i + 9], 5, 568446438);
  815. d = gg(d, a, b, c, x[i + 14], 9, -1019803690);
  816. c = gg(c, d, a, b, x[i + 3], 14, -187363961);
  817. b = gg(b, c, d, a, x[i + 8], 20, 1163531501);
  818. a = gg(a, b, c, d, x[i + 13], 5, -1444681467);
  819. d = gg(d, a, b, c, x[i + 2], 9, -51403784);
  820. c = gg(c, d, a, b, x[i + 7], 14, 1735328473);
  821. b = gg(b, c, d, a, x[i + 12], 20, -1926607734);
  822. a = hh(a, b, c, d, x[i + 5], 4, -378558);
  823. d = hh(d, a, b, c, x[i + 8], 11, -2022574463);
  824. c = hh(c, d, a, b, x[i + 11], 16, 1839030562);
  825. b = hh(b, c, d, a, x[i + 14], 23, -35309556);
  826. a = hh(a, b, c, d, x[i + 1], 4, -1530992060);
  827. d = hh(d, a, b, c, x[i + 4], 11, 1272893353);
  828. c = hh(c, d, a, b, x[i + 7], 16, -155497632);
  829. b = hh(b, c, d, a, x[i + 10], 23, -1094730640);
  830. a = hh(a, b, c, d, x[i + 13], 4, 681279174);
  831. d = hh(d, a, b, c, x[i + 0], 11, -358537222);
  832. c = hh(c, d, a, b, x[i + 3], 16, -722521979);
  833. b = hh(b, c, d, a, x[i + 6], 23, 76029189);
  834. a = hh(a, b, c, d, x[i + 9], 4, -640364487);
  835. d = hh(d, a, b, c, x[i + 12], 11, -421815835);
  836. c = hh(c, d, a, b, x[i + 15], 16, 530742520);
  837. b = hh(b, c, d, a, x[i + 2], 23, -995338651);
  838. a = ii(a, b, c, d, x[i + 0], 6, -198630844);
  839. d = ii(d, a, b, c, x[i + 7], 10, 1126891415);
  840. c = ii(c, d, a, b, x[i + 14], 15, -1416354905);
  841. b = ii(b, c, d, a, x[i + 5], 21, -57434055);
  842. a = ii(a, b, c, d, x[i + 12], 6, 1700485571);
  843. d = ii(d, a, b, c, x[i + 3], 10, -1894986606);
  844. c = ii(c, d, a, b, x[i + 10], 15, -1051523);
  845. b = ii(b, c, d, a, x[i + 1], 21, -2054922799);
  846. a = ii(a, b, c, d, x[i + 8], 6, 1873313359);
  847. d = ii(d, a, b, c, x[i + 15], 10, -30611744);
  848. c = ii(c, d, a, b, x[i + 6], 15, -1560198380);
  849. b = ii(b, c, d, a, x[i + 13], 21, 1309151649);
  850. a = ii(a, b, c, d, x[i + 4], 6, -145523070);
  851. d = ii(d, a, b, c, x[i + 11], 10, -1120210379);
  852. c = ii(c, d, a, b, x[i + 2], 15, 718787259);
  853. b = ii(b, c, d, a, x[i + 9], 21, -343485551);
  854. a = ad(a, olda);
  855. b = ad(b, oldb);
  856. c = ad(c, oldc);
  857. d = ad(d, oldd);
  858. }
  859.  
  860. return rh(a) + rh(b) + rh(c) + rh(d);
  861. }
  862.  
  863. /**
  864. *
  865. *
  866. * @author Michael Barros <michaelcbarros@gmail.com>
  867. * @param {string} className
  868. * @param {number} onlineStatus
  869. * @param {SVGSVGElement} onlineStatusElem
  870. * @return {*}
  871. */
  872. function makeOnlineStatusSVG(className, onlineStatus, onlineStatusElem) {
  873. /** @type {SVGSVGElement} */
  874. let circle;
  875.  
  876. if (!onlineStatusElem) {
  877. onlineStatusElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  878.  
  879. onlineStatusElem.setAttribute('height', 80);
  880. onlineStatusElem.setAttribute('width', 80);
  881.  
  882. onlineStatusElem.classList.add(className);
  883. onlineStatusElem.style.position = 'absolute';
  884. onlineStatusElem.style.zIndex = '9';
  885. onlineStatusElem.style.opacity = '.9';
  886.  
  887. circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
  888.  
  889. circle.setAttribute('cx', 40);
  890. circle.setAttribute('cy', 40);
  891. circle.setAttribute('r', 7);
  892. circle.setAttribute('stroke', 'grey');
  893. circle.setAttribute('stroke-width', 1);
  894.  
  895. onlineStatusElem.appendChild(circle);
  896. } else {
  897. circle = onlineStatusElem.querySelector('circle');
  898. }
  899.  
  900. /** @type {string} */
  901. let fill;
  902.  
  903. switch (onlineStatus) {
  904. case 1:
  905. // online
  906. fill = 'green';
  907.  
  908. break;
  909.  
  910. case 2:
  911. // ??
  912. fill = 'yellow';
  913.  
  914. break;
  915.  
  916. case 3:
  917. // offline
  918. fill = 'grey';
  919.  
  920. break;
  921.  
  922. default:
  923. // offline
  924. fill = 'grey';
  925.  
  926. break;
  927. }
  928.  
  929. circle.setAttribute('fill', fill);
  930.  
  931. // onlineStatusElem.appendChild(circle);
  932.  
  933. return onlineStatusElem;
  934. }
  935.  
  936. function updateBackToBanner(encountersAvailable) {
  937. try {
  938. let elem = document.querySelectorAll('.sidebar-profile__name > div')[0]; // document.querySelectorAll('.sidebar-action-banner')[0].querySelectorAll('.font-weight-medium')[0].querySelectorAll('span')[0];
  939.  
  940. let textContent = elem.textContent;
  941.  
  942. textContent = textContent.replace(/\s+?\[\d+?\]/, '');
  943.  
  944. textContent = `${textContent} [${encountersAvailable}]`;
  945.  
  946. elem.textContent = textContent;
  947. } catch (error) {}
  948. }
  949.  
  950. function updateMessagesWithDatetime(resp, user) {
  951. if (DEBUG_MESSAGES) {
  952. console.clear();
  953. }
  954.  
  955. /**
  956. *
  957. *
  958. * @author Michael Barros <michaelcbarros@gmail.com>
  959. * @param {string} str
  960. * @returns {string}
  961. */
  962. function convert(str) {
  963. str = str.replace(/&#(?:x([\da-f]+)|(\d+));/gi, function (_, hex, dec) {
  964. return String.fromCharCode(dec || +('0x' + hex));
  965. });
  966.  
  967. str = str
  968. .replace(/&nbsp;/gi, ' ')
  969. .replace(/&amp;/gi, '&')
  970. .replace(/&quot;/gi, `"`)
  971. .replace(/&lt;/gi, '<')
  972. .replace(/&gt;/gi, '>');
  973.  
  974. return str;
  975. }
  976.  
  977. /**
  978. *
  979. *
  980. * @author Michael Barros <michaelcbarros@gmail.com>
  981. * @param {string} userId
  982. * @param {[]} messages
  983. * @param {string} messageType
  984. * @param {HTMLElement} elem
  985. */
  986. function findMessage(userId, messages, messageType, elem) {
  987. for (let i = 0; i < messages.length; i++) {
  988. const message = messages[i];
  989. let gifSource = getGifSource(elem);
  990. let audioSource = getAudioSource(elem);
  991.  
  992. if (messageType == 'sent' && message.from_person_id == userId) {
  993. if (gifSource.length > 0) {
  994. let match = /embed\/(.+)/.exec(message.mssg);
  995.  
  996. if (match && gifSource[0].src.includes(match[1])) {
  997. return message;
  998. }
  999. } else if (audioSource.length > 0) {
  1000. if (message?.multimedia?.audio?.url == audioSource[0].src) {
  1001. return message;
  1002. }
  1003. } else if (convert(message.mssg) == elem.textContent) {
  1004. return message;
  1005. }
  1006. } else if (messageType == 'received' && message.to_person_id == userId) {
  1007. if (gifSource.length > 0) {
  1008. let match = /embed\/(.+)/.exec(message.mssg);
  1009.  
  1010. if (match && gifSource[0].src.includes(match[1])) {
  1011. return message;
  1012. }
  1013. } else if (audioSource.length > 0) {
  1014. if (message?.multimedia?.audio?.url == audioSource[0].src) {
  1015. return message;
  1016. }
  1017. } else if (convert(message.mssg) == elem.textContent) {
  1018. return message;
  1019. }
  1020. }
  1021. }
  1022. }
  1023.  
  1024. /**
  1025. *
  1026. *
  1027. * @author Michael Barros <michaelcbarros@gmail.com>
  1028. * @param {HTMLElement} elem
  1029. * @returns {HTMLSourceElement[]}
  1030. */
  1031. function getGifSource(elem) {
  1032. return Array.from(elem.querySelectorAll('.message-gif > video > source'));
  1033. }
  1034.  
  1035. /**
  1036. *
  1037. *
  1038. * @author Michael Barros <michaelcbarros@gmail.com>
  1039. * @param {HTMLElement} elem
  1040. * @returns {HTMLAudioElement[]}
  1041. */
  1042. function getAudioSource(elem) {
  1043. return Array.from(elem.querySelectorAll('audio'));
  1044. }
  1045.  
  1046. let userId = user.user_id;
  1047.  
  1048. /** @type {[]} */
  1049. let messages = resp.body[0].client_open_chat.chat_messages;
  1050. let index = 0;
  1051. 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';
  1052.  
  1053. async function arriveWorker(ev) {
  1054. /** @type {HTMLElement} */
  1055. let elem = ev;
  1056.  
  1057. while (!(elem.classList.contains('message--out') || elem.classList.contains('message--in'))) {
  1058. elem = elem.parentElement;
  1059. }
  1060.  
  1061. // let elem = ev.parentElement.parentElement.parentElement.parentElement;
  1062.  
  1063. try {
  1064. let sent = elem.className.includes('out');
  1065. let received = elem.className.includes('in');
  1066. let messageType = sent ? 'sent' : 'received';
  1067.  
  1068. let foundMessage = findMessage(userId, messages, messageType, elem);
  1069.  
  1070. if (DEBUG_MESSAGES) {
  1071. logger.debug(`${sent ? '<-' : '->'} :: ${foundMessage ? foundMessage.mssg : null}`);
  1072. logger.debug(elem);
  1073. logger.debug('');
  1074. }
  1075.  
  1076. if (foundMessage) {
  1077. try {
  1078. let date = new Date(0);
  1079.  
  1080. date.setUTCSeconds(foundMessage.date_created);
  1081.  
  1082. elem.setAttribute('title', moment(date).format('ddd MMM Do yyyy h:mm A'));
  1083. } catch (error2) {
  1084. logger.error('[akkd.error2]', error2);
  1085. }
  1086. }
  1087. } catch (error1) {
  1088. logger.error('[akkd.error1]', error1);
  1089. }
  1090.  
  1091. index++;
  1092. }
  1093.  
  1094. // .messages-list__conversation > .message
  1095. $(document).unbindArrive(messagesSelector);
  1096. $(document).arrive(messagesSelector, arriveWorker);
  1097. }
  1098.  
  1099. /**
  1100. *
  1101. *
  1102. * @author Michael Barros <michaelcbarros@gmail.com>
  1103. * @param {string} selector
  1104. * @param {string} clazz
  1105. * @param {[]} matches
  1106. */
  1107. async function updateOnlineStatus(selector, clazz, matches) {
  1108. try {
  1109. /** @type {HTMLElement[]} */
  1110. let elems = (document.querySelector(selector) || { children: [] }).children;
  1111.  
  1112. elems.forEach(async (elem) => {
  1113. let onlineStatusElem = elem.querySelector(`.${clazz}`);
  1114. let onlineStatusElemExists = onlineStatusElem != null;
  1115.  
  1116. let uidElem = [elem, ...Array.from(elem.querySelectorAll('li, div'))].find((e) => e.hasAttribute('data-qa-uid'));
  1117.  
  1118. let match = matches.find((match) => match.user_id === (uidElem ? uidElem.getAttribute('data-qa-uid') : null));
  1119.  
  1120. if (!match) {
  1121. return;
  1122. }
  1123.  
  1124. elem.setAttribute('data-qa-online-status', match.online_status);
  1125.  
  1126. let div = makeOnlineStatusSVG(clazz, match.online_status, onlineStatusElem);
  1127.  
  1128. if (!onlineStatusElemExists) {
  1129. uidElem.append(div);
  1130. }
  1131.  
  1132. let user = bumbleUsersCurrent.find((user) => user.user_id === (uidElem ? uidElem.getAttribute('data-qa-uid') : null));
  1133.  
  1134. if (user.time_since_last_seen_h != null && user.time_since_last_seen_h != undefined) {
  1135. 7;
  1136. div.parentElement.setAttribute(
  1137. 'title',
  1138. `Last seen: ${user.time_since_last_seen_h}
  1139. ${user.last_seen_dt}`
  1140. );
  1141. }
  1142.  
  1143. try {
  1144. await playOnlineSound(user);
  1145. } catch (error) {}
  1146. });
  1147. } catch (err) {
  1148. logger.error(err);
  1149. }
  1150. }
  1151.  
  1152. /**
  1153. *
  1154. *
  1155. * @author Michael Barros <michaelcbarros@gmail.com>
  1156. */
  1157. async function updateOnlineStatusChat() {
  1158. return;
  1159.  
  1160. try {
  1161. let elem = $('.page__content > .messages-header > .messages-header__inner > .messages-header__content .header-2')[0];
  1162. let userId = findKeyPathsFuzzy($('.page__content')[0], 'userId')[0].value;
  1163. let name = findKeyPathsFuzzy($('.page__content')[0], 'chatUser')[0].value.name;
  1164.  
  1165. let user = bumbleUsersNew.find((user) => user.user_id === userId);
  1166.  
  1167. let color;
  1168.  
  1169. switch (user.online_status) {
  1170. case 1:
  1171. // online
  1172. color = 'green';
  1173.  
  1174. break;
  1175.  
  1176. case 2:
  1177. // ??
  1178. color = 'yellow';
  1179.  
  1180. break;
  1181.  
  1182. case 3:
  1183. // offline
  1184. color = 'grey';
  1185.  
  1186. break;
  1187.  
  1188. default:
  1189. // offline
  1190. color = 'grey';
  1191.  
  1192. break;
  1193. }
  1194.  
  1195. elem.style.color = color;
  1196. } catch (err) {}
  1197. }
  1198.  
  1199. /** @type {{ name: string; userId: string; timePlayed: number; }[]} */
  1200. let playedNames = [];
  1201.  
  1202. // #region Notify User
  1203.  
  1204. /**
  1205. *
  1206. *
  1207. * @author Michael Barros <michaelcbarros@gmail.com>
  1208. * @param {BumbleUser} user
  1209. */
  1210. async function playOnlineSound(user) {
  1211. if (GM_config.get('play_users_to_monitor_sound', true)) {
  1212. /** @type {{ userId: string, name: string }[]} */
  1213. let usersToMonitor = JSON.parse(GM_config.get('users_to_monitor'));
  1214.  
  1215. for (let i = 0; i < usersToMonitor.length; i++) {
  1216. const userId = usersToMonitor[i].userId;
  1217.  
  1218. if (!findPlayedName(user.user_id) && user.online_status == 1 && user.user_id == userId) {
  1219. let imgUrl = (await testUrl(user.profile_photo_preview_url)) ? user.profile_photo_preview_url : user.profile_photo_large_url;
  1220.  
  1221. notifyUser(user.name, imgUrl, user.user_id);
  1222.  
  1223. localStorage.setItem('users', ESSerializer.serialize(bumbleUsersCurrent));
  1224.  
  1225. playedNames.push({
  1226. name: userId,
  1227. userId: user.user_id,
  1228. timePlayed: new Date().getTime(),
  1229. });
  1230. } else if (findPlayedName(user.user_id) && user.online_status !== 1 && user.user_id == userId) {
  1231. playedNames = playedNames.filter((x) => x.userId !== user.user_id);
  1232. }
  1233. }
  1234. }
  1235. }
  1236.  
  1237. async function testUrl(url) {
  1238. try {
  1239. const response = await fetch(url, { method: 'HEAD' });
  1240.  
  1241. return response.ok;
  1242. } catch (error) {
  1243. logger.error(`Error testing ${url}: ${error.message}`);
  1244.  
  1245. return false;
  1246. }
  1247. }
  1248.  
  1249. function findPlayedName(userId) {
  1250. return playedNames.find((x) => x.userId == userId);
  1251. }
  1252.  
  1253. /**
  1254. *
  1255. *
  1256. * @author Michael Barros <michaelcbarros@gmail.com>
  1257. * @param {string} userName
  1258. * @param {string} imgUrl
  1259. * @param {string} userId
  1260. */
  1261. function notifyUser(userName, imgUrl, userId) {
  1262. /** @type {NotificationOptions} */
  1263. let notificationOptions = {
  1264. body: `${userName} is online`,
  1265. icon: imgUrl,
  1266. };
  1267.  
  1268. /** @type {Notification} */
  1269. let notification;
  1270.  
  1271. if (!('Notification' in window)) {
  1272. window.focus();
  1273.  
  1274. alert('This browser does not support desktop notification');
  1275.  
  1276. return;
  1277. } else if (Notification.permission === 'granted') {
  1278. notification = new Notification('Bumble', notificationOptions);
  1279. } else if (Notification.permission !== 'denied') {
  1280. Notification.requestPermission(function (permission) {
  1281. if (permission === 'granted') {
  1282. notification = new Notification('Bumble', notificationOptions);
  1283. }
  1284. });
  1285. }
  1286.  
  1287. notification.onclick = async function () {
  1288. window.focus();
  1289.  
  1290. for (let i = 0; i < 5; i++) {
  1291. setTimeoutEx(() => {
  1292. openChat(userId);
  1293. }, 100 * i);
  1294. }
  1295. };
  1296.  
  1297. try {
  1298. playBeep();
  1299. } catch (error) {}
  1300. }
  1301.  
  1302. /**
  1303. *
  1304. *
  1305. * @author Michael Barros <michaelcbarros@gmail.com>
  1306. * @param {{ userId: string, name: string }[]} usersToMonitor
  1307. * @param {string} userId
  1308. * @returns {{ userId: string, name: string } | undefined}
  1309. */
  1310. function findUserToMonitor(usersToMonitor, userId) {
  1311. return usersToMonitor.find((user) => user.userId == userId);
  1312. }
  1313.  
  1314. function setupUserMonitorDropdown() {
  1315. let selector = '.page__content';
  1316.  
  1317. // Options for the observer (which mutations to observe)
  1318. let config = { characterData: true, attributes: true, childList: true, subtree: true };
  1319.  
  1320. // Callback function to execute when mutations are observed
  1321. let callback = (mutationList, observer) => {
  1322. if ($('.page__content > .messages-header > .messages-header__inner > .messages-header__content .header-2').length > 0) {
  1323. let optionsContainer = $('.page__content > .messages-header > .messages-header__inner > .messages-header__menu .options');
  1324.  
  1325. if (optionsContainer.length > 0) {
  1326. optionsContainer = optionsContainer[0];
  1327.  
  1328. if ($('#akkd-notify-option').length > 0) {
  1329. $('#akkd-notify-option')[0].remove();
  1330. }
  1331.  
  1332. let userId = findKeyPathsFuzzy($('.page__content')[0], 'userId')[0].value;
  1333. let name = findKeyPathsFuzzy($('.page__content')[0], 'chatUser')[0].value.name;
  1334. let usersToMonitor = JSON.parse(GM_config.get('users_to_monitor'));
  1335. let user = findUserToMonitor(usersToMonitor, userId);
  1336.  
  1337. let optionText = user ? 'Remove from monitor' : 'Add to monitor';
  1338.  
  1339. let elem = document.createElement('div');
  1340.  
  1341. elem.innerHTML = `
  1342. <div class="option " data-qa-role="option" data-qa-value="FOLDER_ONLINE", id="akkd-notify-option">
  1343. <div class="option__text">
  1344. <div class="p-2 text-ellipsis text-break-words">
  1345. <span>${optionText}</span>
  1346. </div>
  1347. </div>
  1348. </div>`;
  1349.  
  1350. elem = elem.children[0];
  1351.  
  1352. elem.addEventListener('click', async (ev) => {
  1353. let usersToMonitor = JSON.parse(GM_config.get('users_to_monitor'));
  1354.  
  1355. if (user) {
  1356. usersToMonitor = usersToMonitor.filter((u) => u.userId != userId);
  1357. playedNames = playedNames.filter((x) => x.userId !== userId);
  1358. } else {
  1359. usersToMonitor.push({ userId, name });
  1360. }
  1361.  
  1362. GM_config.set('users_to_monitor', JSON.stringify(usersToMonitor, null, 4));
  1363. GM_config.set('valid_users_to_monitor', GM_config.get('users_to_monitor'));
  1364. GM_config.save();
  1365. });
  1366.  
  1367. getWindow().akkd_observer2.disconnect();
  1368.  
  1369. optionsContainer.append(elem);
  1370.  
  1371. getWindow().akkd_observer2.observe($(selector)[0], config);
  1372.  
  1373. updateOnlineStatusChat();
  1374. }
  1375. }
  1376. };
  1377.  
  1378. try {
  1379. getWindow().akkd_observer2.disconnect();
  1380. } catch (error) {}
  1381.  
  1382. // Create an observer instance linked to the callback function
  1383. getWindow().akkd_observer2 = new MutationObserver(callback);
  1384.  
  1385. getWindow().akkd_observer2.observe($(selector)[0], config);
  1386. }
  1387.  
  1388. // #endregion Notify User
  1389.  
  1390. async function updateOnlineStatuses() {
  1391. await updateUserLists();
  1392.  
  1393. await updateEncountersLists();
  1394.  
  1395. // Carousel
  1396. await updateOnlineStatus('.scrollable-carousel__scroll', 'show-bumble-carousel-online', queue);
  1397.  
  1398. // Messages
  1399. await updateOnlineStatus('.scroll__inner', 'show-bumble-msgs-online', convos);
  1400.  
  1401. // Chat
  1402. updateOnlineStatusChat();
  1403. }
  1404.  
  1405. function setupUserMsgCarouselArrive() {
  1406. $(document).arrive('.contact, .scrollable-carousel-item', async function (ev) {
  1407. await updateOnlineStatuses();
  1408. });
  1409. }
  1410.  
  1411. function deserializeUsers() {
  1412. let bumbleUsersCurrentSerialized = localStorage.getItem('users');
  1413.  
  1414. bumbleUsersCurrent = bumbleUsersCurrentSerialized ? ESSerializer.deserialize(bumbleUsersCurrentSerialized, [BumbleUser]) : [];
  1415.  
  1416. return bumbleUsersCurrent;
  1417. }
  1418.  
  1419. /**
  1420. *
  1421. *
  1422. * @author Michael Barros <michaelcbarros@gmail.com>
  1423. * @param {[]} users
  1424. * @returns {*}
  1425. */
  1426. async function createBumbleUsers(users) {
  1427. deserializeUsers();
  1428.  
  1429. bumbleUsersNew = [];
  1430.  
  1431. for (let i = 0; i < bumbleUsersCurrent.length; i++) {
  1432. let user = bumbleUsersCurrent[i];
  1433.  
  1434. user.updateLastSeen();
  1435.  
  1436. // if (currentUser.length > 0) {
  1437.  
  1438. // }
  1439.  
  1440. // await playOnlineSound(user);
  1441.  
  1442. // bumbleUsersNew.push(user);
  1443. }
  1444.  
  1445. for (let i = 0; i < users.length; i++) {
  1446. let user = new BumbleUser(users[i]);
  1447. let currentUser = bumbleUsersCurrent.filter((x) => x.user_id == user.user_id);
  1448.  
  1449. if (currentUser.length > 0) {
  1450. user = currentUser[0].updateProps(user);
  1451. }
  1452.  
  1453. await playOnlineSound(user);
  1454.  
  1455. bumbleUsersNew.push(user);
  1456. }
  1457.  
  1458. for (let i = 0; i < bumbleUsersNew.length; i++) {
  1459. const userNew = bumbleUsersNew[i];
  1460.  
  1461. let currentUserIndex = bumbleUsersCurrent.findIndex((x) => x.user_id == userNew.user_id);
  1462.  
  1463. if (currentUserIndex > -1) {
  1464. bumbleUsersCurrent[currentUserIndex] = userNew;
  1465. } else {
  1466. bumbleUsersCurrent.push(userNew);
  1467. }
  1468. }
  1469.  
  1470. localStorage.setItem('users', ESSerializer.serialize(bumbleUsersCurrent));
  1471.  
  1472. getWindow().bumbleUsersCurrent = bumbleUsersCurrent;
  1473. getWindow().bumbleUsersNew = bumbleUsersNew;
  1474.  
  1475. firstBumbleCreations = false;
  1476. }
  1477.  
  1478. async function updateUserLists() {
  1479. if (!updateing) {
  1480. updateing = true;
  1481.  
  1482. let body = JSON.stringify({
  1483. $gpb: 'badoo.bma.BadooMessage',
  1484. body: [
  1485. {
  1486. message_type: 245,
  1487. server_get_user_list: {
  1488. user_field_filter: {
  1489. 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],
  1490. },
  1491. preferred_count: 1000,
  1492. folder_id: 0,
  1493. },
  1494. },
  1495. ],
  1496. message_id: lastestMessageId++,
  1497. message_type: 245,
  1498. version: 1,
  1499. is_background: false,
  1500. });
  1501.  
  1502. let res = await fetch('https://bumble.com/mwebapi.phtml?SERVER_GET_USER_LIST', {
  1503. headers: {
  1504. accept: '*/*',
  1505. 'accept-language': 'en-US,en;q=0.9',
  1506. 'content-type': 'application/json',
  1507. 'sec-ch-ua': '".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"',
  1508. 'sec-ch-ua-mobile': '?0',
  1509. 'sec-ch-ua-platform': '"Windows"',
  1510. 'sec-fetch-dest': 'empty',
  1511. 'sec-fetch-mode': 'cors',
  1512. 'sec-fetch-site': 'same-origin',
  1513. 'x-message-type': '245',
  1514. 'x-pingback': calculateBumbleChecksum(body),
  1515. 'x-use-session-cookie': '1',
  1516. },
  1517. referrer: 'https://bumble.com/app/connections',
  1518. referrerPolicy: 'origin-when-cross-origin',
  1519. body: body,
  1520. method: 'POST',
  1521. mode: 'cors',
  1522. credentials: 'include',
  1523. });
  1524.  
  1525. let resp = await res.json();
  1526.  
  1527. try {
  1528. let sections = resp.body[0].client_user_list.section;
  1529.  
  1530. for (let i = 0; i < sections.length; i++) {
  1531. let section = sections[i];
  1532. let sectionName = section.name;
  1533.  
  1534. if (section.users) {
  1535. await createBumbleUsers(section.users);
  1536.  
  1537. if (sectionName == 'Conversations') {
  1538. convos = [];
  1539.  
  1540. if (section.users) {
  1541. convos.push(...section.users);
  1542. }
  1543. } else if (sectionName == 'Match Queue') {
  1544. queue = [];
  1545.  
  1546. if (section.users) {
  1547. queue.push(...section.users);
  1548. }
  1549. }
  1550. }
  1551. }
  1552. } catch (error) {}
  1553.  
  1554. setTimeoutEx(() => {
  1555. updateing = false;
  1556. }, 5000);
  1557. }
  1558. }
  1559.  
  1560. async function updateEncountersLists(force = false) {
  1561. if (!updateingEncounters || force) {
  1562. updateingEncounters = true;
  1563.  
  1564. let body = JSON.stringify({
  1565. $gpb: 'badoo.bma.BadooMessage',
  1566. body: [
  1567. {
  1568. message_type: 81,
  1569. server_get_encounters: {
  1570. number: 10,
  1571. context: 1,
  1572. user_field_filter: {
  1573. 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],
  1574. request_albums: [
  1575. {
  1576. album_type: 7,
  1577. },
  1578. {
  1579. album_type: 12,
  1580. external_provider: 12,
  1581. count: 8,
  1582. },
  1583. ],
  1584. game_mode: 0,
  1585. request_music_services: {
  1586. top_artists_limit: 8,
  1587. supported_services: [29],
  1588. preview_image_size: {
  1589. width: 120,
  1590. height: 120,
  1591. },
  1592. },
  1593. },
  1594. },
  1595. },
  1596. ],
  1597. message_id: lastestMessageId++,
  1598. message_type: 81,
  1599. version: 1,
  1600. is_background: false,
  1601. });
  1602.  
  1603. let res = await fetch('https://bumble.com/mwebapi.phtml?SERVER_GET_ENCOUNTERS', {
  1604. headers: {
  1605. accept: '*/*',
  1606. 'accept-language': 'en-US,en;q=0.9',
  1607. 'content-type': 'application/json',
  1608. 'sec-ch-ua': '".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"',
  1609. 'sec-ch-ua-mobile': '?0',
  1610. 'sec-ch-ua-platform': '"Windows"',
  1611. 'sec-fetch-dest': 'empty',
  1612. 'sec-fetch-mode': 'cors',
  1613. 'sec-fetch-site': 'same-origin',
  1614. 'x-message-type': '81',
  1615. 'x-pingback': calculateBumbleChecksum(body),
  1616. 'x-use-session-cookie': '1',
  1617. },
  1618. referrer: 'https://bumble.com/app',
  1619. referrerPolicy: 'origin-when-cross-origin',
  1620. body: body,
  1621. method: 'POST',
  1622. mode: 'cors',
  1623. credentials: 'include',
  1624. });
  1625.  
  1626. let resp = await res.json();
  1627.  
  1628. try {
  1629. numEncountersCalls++;
  1630.  
  1631. if ('results' in resp.body[0].client_encounters) {
  1632. encs = [];
  1633.  
  1634. encs.push(...resp.body[0].client_encounters.results);
  1635.  
  1636. quota = (resp.body[0].client_encounters.quota || {}).yes_votes_quota || 0;
  1637.  
  1638. updateBackToBanner(encs.length);
  1639. } else if ('user_substitutes' in resp.body[0].client_encounters) {
  1640. updateBackToBanner(0);
  1641. }
  1642.  
  1643. // responseText = JSON.stringify(resp);
  1644. } catch (error) {}
  1645.  
  1646. setTimeoutEx(() => {
  1647. updateingEncounters = false;
  1648. }, 2000);
  1649. }
  1650. }
  1651.  
  1652. function setupSidebarArrive() {
  1653. $(document).arrive('.sidebar-action-banner', async function (ev) {
  1654. await updateEncountersLists(true);
  1655. });
  1656. }
  1657.  
  1658. function setupBiggerProfilePictures() {
  1659. GM_addStyle(`.gallery-preview__media-image {
  1660. max-width: 110% !important;
  1661. max-height: 110% !important;
  1662. }
  1663.  
  1664. .gallery__preview {
  1665. max-height: 100% !important;
  1666. }
  1667.  
  1668. .dialog-layout .gallery__close {
  1669. height: 64px !important;
  1670. }
  1671. `);
  1672. }
  1673.  
  1674. function actionFunction(jNode) {
  1675. let x = document.getElementsByClassName('profile__photo');
  1676.  
  1677. for (let i = 0; i < x.length; i++) {
  1678. let src = x[i].src;
  1679. let slug = src.split('&size')[0];
  1680.  
  1681. let elemHtml = `<li class="profile__badge">
  1682. <div class="pill">
  1683. <div class="pill__title">
  1684. <div class="p-3 text-ellipsis font-weight-medium">
  1685. <a href="${slug}" style="text-decoration:underline;color:#454650;" target="_blank">Pic ${i}</a>
  1686. </div>
  1687. </div>
  1688. </div>
  1689. </li>
  1690. `;
  1691.  
  1692. $('.profile__badges').append(elemHtml);
  1693. }
  1694. }
  1695.  
  1696. function setupModifyRequestHeaders() {
  1697. // hook and generate fake 'responseText'
  1698. xhook.before(function (request) {
  1699. let projection = findNestedObject(request.headers, 'projection');
  1700.  
  1701. if (projection) {
  1702. logger.debug(projection);
  1703.  
  1704. // xhook.updateRequestHeaders(request, {
  1705. // 'Cache-Control': 'no-store', // no-store', // , no-cache
  1706. // // pragma: 'no-cache',
  1707. // // Cache-Control: no-cache, no-transform
  1708. // });
  1709. }
  1710.  
  1711. // if (request.url.includes('board-response')) {
  1712. // xhook.updateRequestHeaders(request, {
  1713. // 'Cache-Control': 'no-store', // no-store', // , no-cache
  1714. // // pragma: 'no-cache',
  1715. // // Cache-Control: no-cache, no-transform
  1716. // });
  1717.  
  1718. // request.url = `${request.url}&t=${new Date().valueOf()}`;
  1719. // }
  1720. });
  1721. }
  1722.  
  1723. function initNewSection() {
  1724. function setupNewSection() {
  1725. try {
  1726. let akkdSections = Array.from(document.querySelectorAll('.akkd-section'));
  1727.  
  1728. if (akkdSections.length > 0) {
  1729. akkdSections.forEach((elem) => {
  1730. elem.remove();
  1731. });
  1732. }
  1733.  
  1734. let storySection = document.querySelectorAll('.encounters-story-profile')[0];
  1735. let aboutSectionHeader = document.querySelectorAll('.encounters-story-section__heading')[0].cloneNode(true);
  1736. let aboutSection = document.querySelectorAll('.encounters-story-about__badges')[0].cloneNode(true);
  1737.  
  1738. let locationSectionHeader = document.querySelectorAll('.encounters-story-section.encounters-story-section--location')[0].firstChild.cloneNode(true);
  1739. let locationSection = document.querySelectorAll('.location-widget.location-widget--align-center')[0].cloneNode(true);
  1740.  
  1741. let childElems = [
  1742. { elem: aboutSectionHeader, isTitle: true },
  1743. { elem: aboutSection, isTitle: false },
  1744. { elem: locationSectionHeader, isTitle: true },
  1745. { elem: locationSection, isTitle: false },
  1746. ];
  1747.  
  1748. childElems.forEach((elem) => {
  1749. elem.elem.classList.add('akkd-section');
  1750.  
  1751. storySection.appendChild(elem.elem);
  1752.  
  1753. elem.elem.style.paddingTop = '10px';
  1754.  
  1755. if (!elem.isTitle) {
  1756. elem.elem.style.paddingBottom = '10px';
  1757. }
  1758. });
  1759.  
  1760. Array.from(aboutSection.querySelectorAll('.encounters-story-about__badge')).forEach((elem) => {
  1761. elem.style.margin = 'unset';
  1762. });
  1763.  
  1764. Array.from(locationSection.querySelectorAll('.header-2.text-color-black')).forEach((elem) => {
  1765. elem.style.fontSize = '16px';
  1766. });
  1767. } catch (error) {}
  1768. }
  1769.  
  1770. function setupMutationObserver02(encountersUserFrame) {
  1771. if (getWindow().akkd_observer) {
  1772. try {
  1773. getWindow().akkd_observer.disconnect();
  1774. } catch (error) {}
  1775. }
  1776.  
  1777. // Options for the observer (which mutations to observe)
  1778. let config = { attributes: true, childList: true, subtree: true };
  1779.  
  1780. // Callback function to execute when mutations are observed
  1781. let callback = (mutationList, observer) => {
  1782. for (let m = 0; m < mutationList.length; m++) {
  1783. let mutation = mutationList[m];
  1784.  
  1785. for (let i = 0; i < mutation.addedNodes.length; i++) {
  1786. let addedNode = mutation.addedNodes[i];
  1787.  
  1788. try {
  1789. let classList = Array.from(addedNode.classList);
  1790.  
  1791. if (classList.includes('encounters-album__stories-container') || classList.includes('encounters-album anim-enter-done') || classList.includes('encounters-album__progress')) {
  1792. setupNewSection();
  1793. }
  1794. } catch (error) {}
  1795. }
  1796. }
  1797. };
  1798.  
  1799. // Create an observer instance linked to the callback function
  1800. getWindow().akkd_observer = new MutationObserver(callback);
  1801.  
  1802. // Start observing the target node for configured mutations
  1803. // let encountersUserFrame = document.querySelectorAll('.encounters-user__frame')[0];
  1804.  
  1805. getWindow().akkd_observer.observe(encountersUserFrame, config);
  1806. }
  1807.  
  1808. if (document.querySelectorAll('body').length == 0) {
  1809. setTimeoutEx(() => {
  1810. initNewSection();
  1811. }, 100);
  1812. } else {
  1813. // Options for the observer (which mutations to observe)
  1814. let config = { attributes: true, childList: true, subtree: true };
  1815.  
  1816. // Callback function to execute when mutations are observed
  1817. let callback = (mutationList, observer) => {
  1818. for (let m = 0; m < mutationList.length; m++) {
  1819. let mutation = mutationList[m];
  1820.  
  1821. for (let i = 0; i < mutation.addedNodes.length; i++) {
  1822. let addedNode = mutation.addedNodes[i];
  1823.  
  1824. try {
  1825. if (Array.from(addedNode.classList).includes('encounters-user__frame')) {
  1826. setupNewSection();
  1827. setupMutationObserver02(addedNode);
  1828. }
  1829. } catch (error) {}
  1830. }
  1831. }
  1832. };
  1833.  
  1834. // Create an observer instance linked to the callback function
  1835. let observer = new MutationObserver(callback);
  1836.  
  1837. // Start observing the target node for configured mutations
  1838. observer.observe(document.querySelectorAll('body')[0], config);
  1839.  
  1840. setupNewSection();
  1841. }
  1842. }
  1843.  
  1844. function setupLocationIntercept() {
  1845. const watchPosition = unsafeWindow.navigator.geolocation.watchPosition;
  1846. const handlers = {};
  1847.  
  1848. unsafeWindow.navigator.geolocation.watchPosition = function (cb1, cb2, options) {
  1849. // We need to return a handler synchronously, but decide whether we'll use the real watchPosition or not
  1850. // asynchronously. So we create our own handler, and we'll associate it with the real one later.
  1851. const handler = Math.floor(Math.random() * 10000);
  1852.  
  1853. handlers[handler] = watchPosition.apply(navigator.geolocation, [
  1854. (position) => {
  1855. let latitude = 26.181744177358595;
  1856. let longitude = -80.16515493392944;
  1857.  
  1858. let newPosition = {
  1859. coords: {
  1860. accuracy: position.coords.accuracy,
  1861. altitude: position.coords.altitude,
  1862. altitudeAccuracy: position.coords.altitudeAccuracy,
  1863. heading: position.coords.heading,
  1864. latitude: latitude,
  1865. longitude: longitude,
  1866. speed: position.coords.speed,
  1867. },
  1868. timestamp: position.timestamp,
  1869. };
  1870.  
  1871. cb1(newPosition);
  1872. },
  1873. (error) => {
  1874. cb2(error);
  1875. },
  1876.  
  1877. options,
  1878. ]);
  1879.  
  1880. return handler;
  1881. };
  1882.  
  1883. const clearWatch = unsafeWindow.navigator.geolocation.clearWatch;
  1884.  
  1885. unsafeWindow.navigator.geolocation.clearWatch = function (handler) {
  1886. if (handler in handlers) {
  1887. clearWatch.apply(navigator.geolocation, [handlers[handler]]);
  1888.  
  1889. delete handlers[handler];
  1890. }
  1891. };
  1892. }
  1893.  
  1894. let saveContainerLeft;
  1895. let saveContainerTop;
  1896.  
  1897. function createLastSeenTablePopup() {
  1898. function generateFloatingTable(arr) {
  1899. // Create the container element
  1900. const container = document.createElement('div');
  1901. container.style.position = 'fixed';
  1902. container.style.top = saveContainerTop ? `${saveContainerTop}px` : '50%';
  1903. container.style.left = saveContainerLeft ? `${saveContainerLeft}px` : '50%';
  1904. container.style.transform = 'translate(-50%, -50%)';
  1905. container.style.zIndex = '9999';
  1906. container.style.overflow = 'none';
  1907. container.style.maxHeight = '80vh';
  1908. container.style.backgroundColor = '#333333';
  1909. container.style.border = '1px solid black';
  1910. container.style.minWidth = `460.3px !important`;
  1911. container.id = 'akkd-users-table';
  1912.  
  1913. // Create the title bar
  1914. const titleBar = document.createElement('div');
  1915. titleBar.style.cursor = 'move';
  1916. titleBar.style.padding = '8px';
  1917. titleBar.style.userSelect = 'none';
  1918. titleBar.style.backgroundColor = '#ccc';
  1919. titleBar.style.textAlign = 'center'; // Center text alignment
  1920. titleBar.style.borderBottom = '1px solid black';
  1921. titleBar.textContent = 'Matches';
  1922.  
  1923. const container2 = document.createElement('div');
  1924. container2.style.overflow = 'auto';
  1925. container2.style.maxHeight = '70vh';
  1926. container2.style.border = '1px solid black';
  1927.  
  1928. // Create the table element
  1929. const table = document.createElement('table');
  1930. table.style.borderCollapse = 'collapse';
  1931. // table.style.border = '1px solid black';
  1932.  
  1933. // Create the table header row
  1934. const headerRow = document.createElement('tr');
  1935. const keys = Object.keys(arr[0]);
  1936. keys.forEach((key) => {
  1937. if (['name', 'last_seen'].includes(key)) {
  1938. const th = document.createElement('th');
  1939. th.textContent = key;
  1940. th.style.border = '1px solid black';
  1941. th.style.padding = '8px';
  1942. headerRow.appendChild(th);
  1943. }
  1944. });
  1945. table.appendChild(headerRow);
  1946.  
  1947. createRows(arr, table);
  1948.  
  1949. // Add table to the container
  1950. container.appendChild(titleBar);
  1951. container2.appendChild(table);
  1952. container.appendChild(container2);
  1953.  
  1954. let intervalId = setIntervalEx(() => {
  1955. let users = getLatestUsers();
  1956.  
  1957. // Remove all rows from the table
  1958. while (table.rows.length > 1) {
  1959. table.deleteRow(1);
  1960. }
  1961.  
  1962. createRows(users, table);
  1963. }, 5000);
  1964.  
  1965. // Create close button
  1966. const closeButton = document.createElement('button');
  1967. closeButton.textContent = 'Close';
  1968. closeButton.style.margin = '16px';
  1969. closeButton.addEventListener('click', () => {
  1970. document.body.removeChild(container);
  1971.  
  1972. try {
  1973. clearIntervalEx(intervalId);
  1974. } catch (error) {}
  1975. });
  1976. container.appendChild(closeButton);
  1977.  
  1978. let isOpacityOn = true;
  1979.  
  1980. // Create close button
  1981. const opacityButton = document.createElement('button');
  1982. opacityButton.textContent = 'Turn Opacity On/Off';
  1983. opacityButton.style.margin = '16px';
  1984. opacityButton.style.marginRight = 'auto';
  1985. opacityButton.style.right = '16px';
  1986. opacityButton.style.position = 'absolute';
  1987. opacityButton.addEventListener('click', () => {
  1988. isOpacityOn = !isOpacityOn;
  1989. });
  1990. container.appendChild(opacityButton);
  1991.  
  1992. // Add event listeners for dragging the container
  1993. let isDragging = false;
  1994. let startX = 0;
  1995. let startY = 0;
  1996.  
  1997. titleBar.addEventListener('mousedown', (e) => {
  1998. isDragging = true;
  1999. startX = e.clientX - container.offsetLeft;
  2000. startY = e.clientY - container.offsetTop;
  2001. });
  2002.  
  2003. document.addEventListener('mousemove', (e) => {
  2004. if (isDragging) {
  2005. const newLeft = e.clientX - startX;
  2006. const newTop = e.clientY - startY;
  2007.  
  2008. const containerWidth = container.offsetWidth;
  2009. const containerHeight = container.offsetHeight;
  2010. const windowWidth = window.innerWidth;
  2011. const windowHeight = window.innerHeight;
  2012.  
  2013. const maxLeft = windowWidth - containerWidth / 2;
  2014. const maxTop = windowHeight - containerHeight / 2;
  2015.  
  2016. const maxRight = maxLeft * 1;
  2017. const maxBottom = maxTop * 1;
  2018.  
  2019. const boundedLeft = Math.max(containerWidth / 2, Math.min(newLeft, maxLeft));
  2020. const boundedTop = Math.max(containerHeight / 2, Math.min(newTop, maxTop));
  2021.  
  2022. saveContainerTop = boundedTop;
  2023. saveContainerLeft = boundedLeft;
  2024.  
  2025. container.style.left = `${boundedLeft}px`;
  2026. container.style.top = `${boundedTop}px`;
  2027. }
  2028. });
  2029.  
  2030. document.addEventListener('mouseup', () => {
  2031. isDragging = false;
  2032. });
  2033.  
  2034. container.addEventListener('mouseenter', () => {
  2035. container.style.opacity = 1;
  2036. });
  2037. container.addEventListener('mouseleave', () => {
  2038. if (isDragging) {
  2039. return;
  2040. }
  2041.  
  2042. if (!isOpacityOn) return;
  2043.  
  2044. container.style.opacity = 0.25;
  2045. });
  2046.  
  2047. // Add a mousemove event listener to the document
  2048. document.addEventListener('mousemove', handleMouseMove);
  2049.  
  2050. // Mousemove event handler
  2051. function handleMouseMove(event) {
  2052. // Check if the element contains the mouse coordinates
  2053. if (container.contains(event.target)) {
  2054. // Mouse is over the element
  2055. container.style.opacity = 1;
  2056. } else {
  2057. // Mouse is not over the element
  2058. if (isDragging) {
  2059. return;
  2060. }
  2061.  
  2062. if (!isOpacityOn) return;
  2063.  
  2064. container.style.opacity = 0.25;
  2065. }
  2066. }
  2067.  
  2068. // Add container to the body of the document
  2069. document.body.appendChild(container);
  2070. }
  2071.  
  2072. function createRows(arr, table) {
  2073. const keys = Object.keys(arr[0]);
  2074.  
  2075. // Create table rows for each object
  2076. arr.forEach((obj) => {
  2077. const row = document.createElement('tr');
  2078. keys.forEach((key) => {
  2079. if (['name', 'last_seen'].includes(key)) {
  2080. const td = document.createElement('td');
  2081.  
  2082. if (key == 'name') {
  2083. const a = document.createElement('a');
  2084.  
  2085. // a.href = `javascript:openChat(${obj['user_id']});`;
  2086. a.href = '#';
  2087. a.addEventListener('click', () => {
  2088. openChat(obj['user_id']);
  2089. });
  2090. a.textContent = obj[key];
  2091. a.style.color = getColorForOnlineStatus(obj);
  2092. td.appendChild(a);
  2093. } else {
  2094. td.textContent = obj[key];
  2095. }
  2096.  
  2097. td.style.border = '1px solid black';
  2098. td.style.padding = '8px';
  2099. row.appendChild(td);
  2100. }
  2101. });
  2102. table.appendChild(row);
  2103. });
  2104. }
  2105.  
  2106. function sortObjectsWithNullLast(arr, key, order = 'asc') {
  2107. const sortedArr = arr.slice(); // Create a shallow copy of the array
  2108.  
  2109. sortedArr.sort((a, b) => {
  2110. const valueA = a[key];
  2111. const valueB = b[key];
  2112.  
  2113. if (valueA === null && valueB !== null) {
  2114. return 1; // `null` values are considered greater, so `a` comes after `b`
  2115. } else if (valueA !== null && valueB === null) {
  2116. return -1; // `null` values are considered greater, so `a` comes before `b`
  2117. } else {
  2118. // Both values are either `null` or non-`null`, use regular comparison
  2119. if (order === 'asc') {
  2120. return valueA < valueB ? -1 : valueA > valueB ? 1 : 0;
  2121. } else if (order === 'desc') {
  2122. return valueA < valueB ? 1 : valueA > valueB ? -1 : 0;
  2123. } else {
  2124. throw new Error('Invalid sort order. Please provide "asc" or "desc".');
  2125. }
  2126. }
  2127. });
  2128.  
  2129. return sortedArr;
  2130. }
  2131.  
  2132. function logPropertiesInEvenColumns(arr) {
  2133. const keys = Object.keys(arr[0]);
  2134.  
  2135. // Calculate the maximum length of each property value
  2136. const maxLengths = {};
  2137.  
  2138. keys.forEach((key) => {
  2139. maxLengths[key] = Math.max(...arr.map((obj) => String(obj[key]).length));
  2140. });
  2141.  
  2142. // Log the properties in even columns
  2143. arr.forEach((obj) => {
  2144. let output = '';
  2145. keys.forEach((key, index) => {
  2146. const value = String(obj[key]);
  2147. const padding = ' '.repeat(maxLengths[key] - value.length);
  2148. output += `${key}: ${value}${padding} ${index % 2 !== 0 ? '\t' : ''}`;
  2149. });
  2150. logger.debug(output);
  2151. });
  2152. }
  2153.  
  2154. function getLatestUsers() {
  2155. let users = sortObjectsWithNullLast(bumbleUsersCurrent.slice(), 'time_since_last_seen', 'asc').map((x, index) => {
  2156. return {
  2157. name: x.name,
  2158. last_seen: x.time_since_last_seen_h,
  2159. user_id: x.user_id,
  2160. online_status: x.online_status,
  2161. time_since_last_seen: x.time_since_last_seen,
  2162. };
  2163. });
  2164.  
  2165. return users;
  2166. }
  2167.  
  2168. function getColorForOnlineStatus(user) {
  2169. if (user['time_since_last_seen'] != 0) return 'grey';
  2170.  
  2171. switch (user['online_status']) {
  2172. case 1:
  2173. // online
  2174. return 'green';
  2175.  
  2176. case 2:
  2177. // ??
  2178. return 'yellow';
  2179.  
  2180. case 3:
  2181. // offline
  2182. return 'grey';
  2183.  
  2184. default:
  2185. // offline
  2186. return 'grey';
  2187. }
  2188. }
  2189.  
  2190. generateFloatingTable(getLatestUsers());
  2191. }
  2192.  
  2193. // #region Conversation Option
  2194.  
  2195. function addConversationOption() {
  2196. let isShowOnline = false;
  2197. let showOnlineContactsIntervalId;
  2198.  
  2199. $(document).arrive('.contact-tabs__section-header-dropdown', async function (ev) {
  2200. let optionsContainer = document.querySelectorAll('.contact-tabs__section-header-dropdown > .dropdown')[0].querySelectorAll('.options')[0];
  2201. let elem = document.createElement('div');
  2202.  
  2203. 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>`;
  2204.  
  2205. elem = elem.children[0];
  2206.  
  2207. elem.addEventListener('click', async (ev) => {
  2208. isShowOnline = !isShowOnline;
  2209.  
  2210. try {
  2211. clearIntervalEx(showOnlineContactsIntervalId);
  2212. } catch (error) {}
  2213.  
  2214. if (isShowOnline) {
  2215. $('.contact-tabs__section.contact-tabs__section--conversations .contact-tabs__section-title-text span')[0].style.color = 'green';
  2216.  
  2217. await _showOnlyOnlineContactsScroll();
  2218.  
  2219. showOnlineContactsIntervalId = setIntervalEx(async () => {
  2220. _showOnlyOnlineContacts();
  2221. }, 1000);
  2222. } else {
  2223. $(document.querySelectorAll('.contact-tabs__section-content .scroll__inner')[0]).unbindArrive('.contact');
  2224.  
  2225. _showAllContacts();
  2226.  
  2227. $('.contact-tabs__section.contact-tabs__section--conversations .contact-tabs__section-title-text span')[0].style.color = '';
  2228. }
  2229.  
  2230. setTimeoutEx(() => {
  2231. document.querySelectorAll('.contact-tabs__section-header-dropdown > .dropdown')[0].classList.remove('is-active');
  2232. }, 1);
  2233.  
  2234. document.querySelectorAll('.contact-tabs__section-header-dropdown > .dropdown')[0].classList.remove('is-active');
  2235. });
  2236.  
  2237. optionsContainer.append(elem);
  2238. });
  2239. }
  2240.  
  2241. async function _showOnlyOnlineContactsScroll() {
  2242. /** @type {HTMLElement} */
  2243. let contactsContainer = document.querySelectorAll('.contact-tabs__section-content .scroll__inner')[0];
  2244.  
  2245. let previousScrollTop;
  2246. let tries = 0;
  2247. let maxTries = 250;
  2248.  
  2249. async function arriveWorker(ev) {
  2250. // index++;
  2251.  
  2252. if (!ev.classList.contains('is-loading')) {
  2253. _showOnlyOnlineContacts();
  2254. }
  2255. }
  2256.  
  2257. // .messages-list__conversation > .message
  2258. $(contactsContainer).unbindArrive('.contact');
  2259.  
  2260. // showOnlyOnlineContacts();
  2261.  
  2262. do {
  2263. previousScrollTop = contactsContainer.scrollTop;
  2264.  
  2265. contactsContainer.scrollTo({ top: contactsContainer.scrollHeight, behavior: 'auto' });
  2266.  
  2267. await wait(1);
  2268.  
  2269. _showOnlyOnlineContacts();
  2270.  
  2271. if (previousScrollTop != contactsContainer.scrollTop) {
  2272. tries = 0;
  2273. } else {
  2274. tries++;
  2275. }
  2276. } while (previousScrollTop != contactsContainer.scrollTop || tries < maxTries);
  2277.  
  2278. contactsContainer.scrollTo({ top: 0, behavior: 'auto' });
  2279.  
  2280. // $(contactsContainer).unbindArrive('.contact');
  2281. $(contactsContainer).arrive('.contact', arriveWorker);
  2282. }
  2283.  
  2284. async function _showAllContactsScroll() {
  2285. /** @type {HTMLElement} */
  2286. let contactsContainer = document.querySelectorAll('.contact-tabs__section-content .scroll__inner')[0];
  2287.  
  2288. let previousScrollTop;
  2289. let tries = 0;
  2290. let maxTries = 250;
  2291.  
  2292. async function arriveWorker(ev) {
  2293. // index++;
  2294.  
  2295. if (!ev.classList.contains('is-loading')) {
  2296. _showOnlyOnlineContacts();
  2297. }
  2298. }
  2299.  
  2300. // .messages-list__conversation > .message
  2301. // $(contactsContainer).unbindArrive('.contact');
  2302.  
  2303. // showOnlyOnlineContacts();
  2304.  
  2305. do {
  2306. previousScrollTop = contactsContainer.scrollTop;
  2307.  
  2308. contactsContainer.scrollTo({ top: contactsContainer.scrollHeight, behavior: 'auto' });
  2309.  
  2310. await wait(1);
  2311.  
  2312. // _showOnlyOnlineContacts();
  2313.  
  2314. if (previousScrollTop != contactsContainer.scrollTop) {
  2315. tries = 0;
  2316. } else {
  2317. tries++;
  2318. }
  2319. } while (previousScrollTop != contactsContainer.scrollTop || tries < maxTries);
  2320.  
  2321. contactsContainer.scrollTo({ top: 0, behavior: 'auto' });
  2322.  
  2323. // $(contactsContainer).unbindArrive('.contact');
  2324. // $(contactsContainer).arrive('.contact', arriveWorker);
  2325. }
  2326.  
  2327. function _showAllContacts() {
  2328. /** @type {HTMLElement[]} */
  2329. let contactElems = Array.from(document.querySelectorAll('.contact-tabs__section-content .scroll__inner .contact'));
  2330.  
  2331. for (let i = 0; i < contactElems.length; i++) {
  2332. const contactElem = contactElems[i];
  2333.  
  2334. contactElem.style.display = 'flex';
  2335. }
  2336. }
  2337.  
  2338. function _showOnlyOnlineContacts() {
  2339. /** @type {HTMLElement[]} */
  2340. let contactElems = Array.from(document.querySelectorAll('.contact-tabs__section-content .scroll__inner .contact'));
  2341.  
  2342. for (let i = 0; i < contactElems.length; i++) {
  2343. const contactElem = contactElems[i];
  2344.  
  2345. let onlineStatus = parseInt(contactElem.getAttribute('data-qa-online-status'));
  2346.  
  2347. if (onlineStatus != 1) {
  2348. contactElem.style.display = 'none';
  2349. } else {
  2350. contactElem.style.display = 'flex';
  2351. }
  2352. }
  2353. }
  2354.  
  2355. // #endregion Conversation Option
  2356.  
  2357. function addCustomCss() {
  2358. let cssStyles = [
  2359. {
  2360. css: /*css*/ `.contact.is-selected {
  2361. pointer-events: auto !important;
  2362. cursor: auto !important;
  2363. }
  2364.  
  2365. .scrollable-carousel-item.is-selected {
  2366. pointer-events: auto !important;
  2367. cursor: auto !important;
  2368. }
  2369.  
  2370. #akkd-users-table {
  2371. min-width: 458.663px !important;
  2372. transition: opacity 0.1s linear 0s;
  2373. }
  2374.  
  2375. div#akkd-users-table button {
  2376. background-color: rgba(30, 30, 30, .55);
  2377. padding: 8px;
  2378. border-radius: 5px;
  2379. transition: background-color 0.1s linear 0s;
  2380. }
  2381.  
  2382. div#akkd-users-table button:hover {
  2383. background-color: rgba(30, 30, 30, .85);
  2384. }
  2385.  
  2386. div#akkd-users-table button:active {
  2387. background-color: rgba(30, 30, 30, .35);
  2388. }
  2389. `,
  2390. node: document.documentElement,
  2391. },
  2392. ];
  2393.  
  2394. addStyles(cssStyles);
  2395. }
  2396.  
  2397. function setupConfig() {
  2398. // demo: http://sizzlemctwizzle.github.io/GM_config/
  2399. GM_config.init({
  2400. id: `main-${location.host.replace(/\./g, '_')}`,
  2401. title: 'Bumble Enhanced Config',
  2402.  
  2403. fields: {
  2404. play_users_to_monitor_sound: {
  2405. label: 'Play Users To Monitor Sound',
  2406. type: 'checkbox',
  2407. default: true,
  2408. },
  2409.  
  2410. users_to_monitor: {
  2411. label: 'Users To Monitor',
  2412. type: 'textarea',
  2413. title: 'Enter JSON array string',
  2414. default: '["Example1", "Exmaple2"]',
  2415. save: false, // This field's value will NOT be saved
  2416. },
  2417.  
  2418. valid_users_to_monitor: {
  2419. type: 'hidden',
  2420. default: '',
  2421. },
  2422. },
  2423.  
  2424. events: {
  2425. init: function () {
  2426. // Set the value of the dummy field to the saved value
  2427. GM_config.set('users_to_monitor', GM_config.get('valid_users_to_monitor'));
  2428. },
  2429. open: function () {
  2430. // Use a listener to update the hidden field when the dummy field passes validation
  2431. GM_config.fields['users_to_monitor'].node.addEventListener(
  2432. 'change',
  2433. function () {
  2434. // get the current value of the visible field
  2435. var users_to_monitor = GM_config.get('users_to_monitor', true);
  2436.  
  2437. try {
  2438. JSON.parse(users_to_monitor);
  2439.  
  2440. // Only save valid CSS
  2441. GM_config.set('valid_users_to_monitor', users_to_monitor);
  2442. } catch (error) {}
  2443. },
  2444. false
  2445. );
  2446. },
  2447. save: function (forgotten) {
  2448. if (GM_config.isOpen) {
  2449. // If the values don't match then valid_users_to_monitor wasn't valid
  2450. if (forgotten.users_to_monitor == null || forgotten.users_to_monitor == undefined) {
  2451. GM_config.set('users_to_monitor', JSON.stringify(JSON.parse(GM_config.get('valid_users_to_monitor')), null, 4));
  2452. } else if (forgotten.users_to_monitor !== GM_config.get('valid_users_to_monitor')) {
  2453. GM_config.set('valid_users_to_monitor', '[]');
  2454. GM_config.set('users_to_monitor', '[]');
  2455. } else {
  2456. GM_config.set('users_to_monitor', JSON.stringify(JSON.parse(forgotten.users_to_monitor), null, 4));
  2457. }
  2458. }
  2459. },
  2460. close: function () {
  2461. logger.debug('users_to_monitor: ', JSON.parse(GM_config.get('users_to_monitor')));
  2462. logger.debug('play_users_to_monitor_sound:', GM_config.get('play_users_to_monitor_sound'));
  2463. logger.debug('');
  2464. },
  2465. reset: function () {},
  2466. },
  2467.  
  2468. css: /*css*/ `
  2469. #main-bumble_com_field_users_to_monitor {
  2470. width: calc(100% - 150px) !important;
  2471. height: calc(100% - 150px) !important;
  2472. resize: none;
  2473. }`,
  2474. });
  2475.  
  2476. let menuId = GM_registerMenuCommand(`Config`, () => {
  2477. GM_config.open();
  2478. });
  2479.  
  2480. let menuId2 = GM_registerMenuCommand(`Last Seen Table`, () => {
  2481. createLastSeenTablePopup();
  2482. });
  2483. }
  2484.  
  2485. // #endregion Bumble Functions