VK: Check Online

Checks the last online on page user and in dialog

目前为 2022-02-07 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name VK: Check Online
  3. // @name:ru ВК: Проверка онлайна
  4. // @description Checks the last online on page user and in dialog
  5. // @description:ru Проверяет последний онлайн пользователя на странице и в диалогe
  6. // @namespace vk-check-online.user.js
  7. // @license MIT
  8. // @author askornot
  9. // @version 1.0.6
  10. // @match https://vk.com/*
  11. // @connect vk.com
  12. // @compatible chrome Violentmonkey 2.12.7
  13. // @compatible firefox Violentmonkey 2.12.7
  14. // @homepageURL https://greasyfork.org/en/scripts/403717-vk-check-online
  15. // @supportURL https://greasyfork.org/en/scripts/403717-vk-check-online/feedback
  16. // @run-at document-end
  17. // @noframes
  18. // ==/UserScript==
  19.  
  20. 'use strict';
  21.  
  22. const W = unsafeWindow || window;
  23. const MINUTE = 60 * 1000;
  24. const UNIXTIME = 1000;
  25.  
  26. class Cache {
  27. constructor() {
  28. this.default = { uts: 0, expires: 0 };
  29. this.data = new Map();
  30. }
  31.  
  32. get(key) {
  33. const exist = this.data.get(key);
  34. if (exist) return exist;
  35. return this.default;
  36. }
  37.  
  38. set(key, value) {
  39. const expires = Date.now() + MINUTE;
  40. this.data.set(key, { uts: value, expires });
  41. }
  42. }
  43.  
  44. const request = (url, callback) => {
  45. const xhr = new XMLHttpRequest();
  46. xhr.onreadystatechange = () => {
  47. if (xhr.readyState === 4) {
  48. const container =
  49. document.implementation.createHTMLDocument().documentElement;
  50. if (xhr.status === 0 || xhr.status === 200) {
  51. container.innerHTML = xhr.responseText;
  52. }
  53. callback(container);
  54. }
  55. };
  56. xhr.open('GET', url, true);
  57. xhr.send();
  58. };
  59.  
  60. const render = (uts) => {
  61. const online =
  62. document.querySelector('.mail_box_label_info') ||
  63. document.querySelector('.profile_online_lv') ||
  64. document.querySelector('._im_page_peer_online');
  65. const { lang } = window.vk;
  66. const text = lang === 3 ? 'last seen' : 'заходил(а)';
  67. online.textContent = `${text} ${window.getDateText(uts, null)}`;
  68. };
  69.  
  70. const extractTimestamp = (body) => {
  71. const [element] = body.getElementsByTagName('ya:lastloggedin');
  72. if (!element) return 0;
  73. const date = element.getAttribute('dc:date');
  74. const uts = Math.floor(Date.parse(date) / UNIXTIME);
  75. return uts;
  76. };
  77.  
  78. const extract = () => {
  79. const { options, peer } = window.cur;
  80. const id = peer || (options && options.user_id);
  81. return id || 0;
  82. };
  83.  
  84. const start = () => {
  85. const id = extract();
  86. if (id === 0 || Math.sign(id) === -1) return;
  87. const { expires } = cache.get(id);
  88. if (expires > Date.now()) {
  89. render(expires);
  90. return;
  91. }
  92. request(`/foaf.php?id=${id}`, (body) => {
  93. const uts = extractTimestamp(body);
  94. if (uts === 0) return;
  95. render(uts);
  96. cache.set(id, uts);
  97. });
  98. };
  99.  
  100. const observable = (target) => {
  101. const handlers = [];
  102. const observe = (handler) => {
  103. handlers.push(handler);
  104. };
  105. const proxy = new Proxy(target, {
  106. get(...args) {
  107. const output = Reflect.get(...args);
  108. if (output) {
  109. handlers.forEach((fn) => fn(output));
  110. }
  111. return output;
  112. },
  113. });
  114. return [proxy, observe];
  115. };
  116.  
  117. const throttle = (timeout, fn) => {
  118. let timer;
  119. let wait = false;
  120. let wrapped = null;
  121.  
  122. const throttled = () => {
  123. timer = undefined;
  124. if (wait) wrapped();
  125. };
  126. wrapped = (...args) => {
  127. if (!timer) {
  128. timer = setTimeout(throttled, timeout);
  129. wait = false;
  130. return fn(...args);
  131. } else {
  132. wait = true;
  133. }
  134. };
  135. return wrapped;
  136. };
  137.  
  138. const throttledStart = throttle(3000, start);
  139.  
  140. const types = {
  141. object: throttledStart,
  142. };
  143.  
  144. const [instance, observe] = observable(W.nav);
  145.  
  146. W.nav = instance;
  147.  
  148. observe((data) => {
  149. const type = typeof data;
  150. const fn = types[type];
  151. if (fn) fn();
  152. });
  153.  
  154. const cache = new Cache();
  155. throttledStart();