VK: Check Online

Checks the last online on page user and in dialog

当前为 2020-05-19 提交的版本,查看 最新版本

  1. /* globals vk, cur, getDateText, GM, GM_xmlhttpRequest */
  2. /* eslint padded-blocks: ["error", { "blocks": "always" }]
  3. object-curly-spacing: ["error", "always", { "objectsInObjects": false }]
  4. curly: ["error", "multi"] */
  5.  
  6. // ==UserScript==
  7. // @name VK: Check Online
  8. // @name:ru ВК: Проверка онлайна
  9. // @description Checks the last online on page user and in dialog
  10. // @description:ru Проверяет последний онлайн пользователя на странице и в диалогe
  11. // @namespace vk-check-online.user.js
  12. // @license MIT
  13. // @author askornot
  14. // @version 1.0.5
  15. // @match https://vk.com/*
  16. // @connect vk.com
  17. // @compatible chrome Violentmonkey 2.12.7
  18. // @compatible firefox Violentmonkey 2.12.7
  19. // @homepageURL https://greasyfork.org/en/scripts/403717-vk-check-online
  20. // @supportURL https://greasyfork.org/en/scripts/403717-vk-check-online/feedback
  21. // @grant GM_xmlhttpRequest
  22. // @grant GM.xmlHttpRequest
  23. // @run-at document-end
  24. // @noframes
  25. // ==/UserScript==
  26.  
  27. 'use strict';
  28.  
  29. const USERS = new Map();
  30.  
  31. const request = (id, data = {}) => new Promise((resolve, reject) => {
  32.  
  33. data.method = 'GET';
  34. data.url = `/foaf.php?id=${id}`;
  35. data.responseType = 'document';
  36. /* eslint-disable unicorn/prefer-add-event-listener, no-unused-expressions, no-constant-condition, operator-linebreak, new-cap */
  37. data.onload = resolve;
  38. data.onerror = reject;
  39.  
  40. typeof !GM && typeof !GM.xmlHttpRequest
  41. ? GM.xmlHttpRequest(data)
  42. : GM_xmlhttpRequest(data);
  43. /* eslint-enable */
  44.  
  45. });
  46.  
  47. const floor = time => Math.floor(time);
  48.  
  49. const now = () => (floor(Date.now()));
  50.  
  51. const render = uts => {
  52.  
  53. const online = document.querySelector('.mail_box_label_info') ||
  54. document.querySelector('.profile_online_lv') ||
  55. document.querySelector('._im_page_peer_online');
  56.  
  57. const { lang } = vk;
  58.  
  59. const text = lang === 3 ? 'last seen' : 'заходил(а)';
  60.  
  61. online.textContent = `${text} ${getDateText(uts, null)}`;
  62.  
  63. };
  64.  
  65. const getTs = rdf => {
  66.  
  67. // eslint-disable-next-line unicorn/prefer-query-selector
  68. const [element] = rdf.getElementsByTagName('ya:lastloggedin');
  69.  
  70. if (!element) return 0;
  71.  
  72. const date = element.getAttribute('dc:date');
  73.  
  74. const uts = floor(Date.parse(date) / 1000); // A getDateText requires unixtime
  75.  
  76. return uts;
  77.  
  78. };
  79.  
  80. const getUser = user => USERS.has(user) ? USERS.get(user) : {};
  81.  
  82. const start = async () => {
  83.  
  84. const { options = {}, peer } = cur; // VK object
  85.  
  86. // eslint-disable-next-line no-prototype-builtins
  87. const userId = (peer ? peer : (options.hasOwnProperty('user_id') ? options.user_id : 0));
  88.  
  89. if (userId === 0 || Math.sign(userId) === -1) return;
  90.  
  91. let { expires = 0, uts = 0 } = getUser(userId);
  92.  
  93. if (expires < now()) { // eslint-disable-line curly
  94.  
  95. try {
  96.  
  97. const { response } = await request(userId);
  98. uts = getTs(response);
  99.  
  100. USERS.set(userId, {
  101. uts,
  102. expires: now() + 60000 // Every one minute
  103. });
  104.  
  105. } catch (error) {
  106.  
  107. console.error(error.stack);
  108.  
  109. }
  110.  
  111. }
  112.  
  113. if (uts === 0) return;
  114.  
  115. render(uts);
  116.  
  117. };
  118.  
  119. start().catch(console.error);
  120.  
  121. const filterMutsByClassName = muts => {
  122.  
  123. return muts.filter((array, i, self) => self.findIndex(_array => (array.target.className === _array.target.className)) === i);
  124.  
  125. };
  126.  
  127. new MutationObserver(muts => {
  128.  
  129. muts = filterMutsByClassName(muts);
  130.  
  131. for (const { target, addedNodes } of muts) {
  132.  
  133. const { tagName } = target;
  134.  
  135. if (tagName === 'DIV' || tagName === 'SPAN') {
  136.  
  137. const { classList } = target;
  138.  
  139. if ( // eslint-disable-line curly
  140. (classList.contains('wide_column') && addedNodes.length >= 2) ||
  141. classList.contains('box_body') ||
  142. classList.contains('_im_page_peer_online') ||
  143. classList.contains('im-page-chat-contain')
  144. ) {
  145.  
  146. setTimeout(start, 1000);
  147.  
  148. }
  149.  
  150. }
  151.  
  152. }
  153.  
  154. }).observe(document.body || document, {
  155. childList: true,
  156. subtree: true
  157. });