VK: Check Online

Checks the last online on page user and in dialog

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

// ==UserScript==
// @name            VK: Check Online
// @name:ru         ВК: Проверка онлайна
// @description     Checks the last online on page user and in dialog
// @description:ru  Проверяет последний онлайн пользователя на странице и в диалогe
// @namespace       vk-check-online.user.js
// @license         MIT
// @author          askornot
// @version         1.1.1
// @match           https://vk.com/*
// @connect         vk.com
// @compatible      chrome     Violentmonkey 2.12.7
// @compatible      firefox    Violentmonkey 2.12.7
// @homepageURL     https://gf.qytechs.cn/en/scripts/403717-vk-check-online
// @supportURL      https://gf.qytechs.cn/en/scripts/403717-vk-check-online/feedback
// @run-at          document-end
// @noframes
// ==/UserScript==

'use strict';

const W = unsafeWindow || window;
const MINUTE = 60 * 1000;
const UNIXTIME = 1000;

class Cache {
  constructor() {
    this.default = { uts: 0, expires: 0 };
    this.data = new Map();
  }

  get(key) {
    const exist = this.data.get(key);
    if (exist) return exist;
    return this.default;
  }

  set(key, value) {
    const expires = Date.now() + MINUTE;
    this.data.set(key, { uts: value, expires });
  }
}

const request = (url, callback) => {
  const xhr = new XMLHttpRequest();
  xhr.onreadystatechange = () => {
    if (xhr.readyState === 4) {
      const container =
        document.implementation.createHTMLDocument().documentElement;
      if (xhr.status === 0 || xhr.status === 200) {
        container.innerHTML = xhr.responseText;
      }
      callback(container);
    }
  };
  xhr.open('GET', url, true);
  xhr.send();
};

const render = (uts) => {
  const online =
    document.querySelector('.mail_box_label_info') ||
    document.querySelector('.profile_online_lv') ||
    document.querySelector('._im_page_peer_online');
  const { lang } = window.vk;
  const text = lang === 3 ? 'last seen' : 'заходил(а)';
  online.textContent = `${text} ${window.getDateText(uts, null)}`;
};

const extractTimestamp = (body) => {
  const [element] = body.getElementsByTagName('ya:lastloggedin');
  if (!element) return 0;
  const date = element.getAttribute('dc:date');
  const uts = Math.floor(Date.parse(date) / UNIXTIME);
  return uts;
};

const extract = () => {
  const { options, peer } = window.cur;
  const id = peer || (options && options.user_id);
  return id || 0;
};

const start = () => {
  const id = extract();
  if (id === 0 || Math.sign(id) === -1) return;
  const { expires, uts } = cache.get(id);
  if (expires > Date.now()) {
    render(uts);
    return;
  }
  request(`/foaf.php?id=${id}`, (body) => {
    const uts = extractTimestamp(body);
    if (uts === 0) return;
    render(uts);
    cache.set(id, uts);
  });
};

const observable = (target) => {
  const handlers = [];
  const observe = (handler) => {
    handlers.push(handler);
  };
  const proxy = new Proxy(target, {
    get(...args) {
      const output = Reflect.get(...args);
      if (output) {
        handlers.forEach((fn) => fn(output));
      }
      return output;
    },
  });
  return [proxy, observe];
};

const throttle = (timeout, fn) => {
  let timer;
  let wait = false;
  let wrapped = null;

  const throttled = () => {
    timer = undefined;
    if (wait) wrapped();
  };
  wrapped = (...args) => {
    if (!timer) {
      timer = setTimeout(throttled, timeout);
      wait = false;
      return fn(...args);
    } else {
      wait = true;
    }
  };
  return wrapped;
};

const throttledStart = throttle(3000, start);

const types = {
  object: throttledStart,
};

const [instance, observe] = observable(W.nav);

W.nav = instance;

observe((data) => {
  const type = typeof data;
  const fn = types[type];
  if (fn) fn();
});

const cache = new Cache();
throttledStart();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址