flyink13 / Web API Messages helper

// ==UserScript==
// @name         Web API Messages helper
// @namespace    http://vk.com/
// @version      3.1
// @description  Показывает информацию о сообщении при нажатии на него
// @author       Flyink13
// @match        https://*.vk.com/*
// @copyright 2018, flyink13 (https://openuserjs.org/users/flyink13)
// @downloadURL https://openuserjs.org/install/flyink13/Web_API_Messages_helper.user.js
// @updateURL https://openuserjs.org/meta/flyink13/Web_API_Messages_helper.meta.js
// @license MIT
// ==/UserScript==
/* global clean, MessageBox, vkApi, loadScript, ace, geByClass1, cur */

/* changelog:
   3.1: vkApi, chat members
   3.0: Dialogs, link etc...
   2.0: Add api version and rename
   1.8: REPLACE KEY FROM CTRL TO ALT
   1.7: fix ctrl key
   1.6: add focus + callback
   1.5: add ace.js
   1.4: add groups
*/

function initImHelper() {
  if (/^\/(away|notifier)\.php/.test(window.location.pathname) || typeof vk !== 'object') return;
  var aceCorePath = '/js/ace/ace.js';
  var API = (method, data) => vkApi.api(method, data);
  var group_regexp = /vk\.com\/gim(\d+)/;
  var api_objects = [
      {
          selector: '.im-mess-stack--lnk, .mem_link',
          callback: function onFind(el) {
              const id = el.href.replace(/^.+\//, '')
              el.dataset.api_object_id = id;
              el.api_data = { id };
              insertGlobalApiData(el.api_data);
          },
          execute: function execute(Args) {
              var object = API.utils.resolveScreenName({
                  screen_name: Args.id
              });

              if (object.type == 'user') {
                  object.user = API.users.get({
                      user_ids: Args.id,
                      v: Args.v
                  })[0];
              }
              if (object.type == 'group') {
                  object.group = API.groups.getById({
                      group_ids: Args.id,
                      v: Args.v
                  })[0];
              }
              if (object.type == 'app') {
                  object.app = API.apps.get({
                      app_ids: Args.id,
                      v: Args.v
                  })[0];
              }

              return object;
          }
      },
      {
          selector: '#im_dialogs ._im_dialog',
          callback: function onFind(el) {
              const id = el.dataset.peer;
              el.dataset.api_object_id = id;
              el.api_data = { id };
              insertGlobalApiData(el.api_data);
          },
          execute: function execute(Args) {
              return API.messages.getConversationsById({
                  peer_ids: Args.id,
                  group_id: Args.group_id,
                  v: Args.v
              }).items[0];
          }
      },
      {
          selector: '._im_chat_members',
          callback: function onFind(el) {
              const id = cur.peer;
              el.dataset.api_object_id = id;
              el.api_data = { id };
              insertGlobalApiData(el.api_data);
          },
          execute: function execute(Args) {
              return API.messages.getConversationMembers({
                  peer_id: Args.id,
                  group_id: Args.group_id,
                  count: 200,
                  v: Args.v
              }).items.map(item => item.member_id);
          }
      },
      {
          selector: '._im_peer_history ._im_mess',
          callback: function onFind(el) {
              const id = el.dataset.msgid;
              el.dataset.api_object_id = id;
              el.api_data = { id };
              insertGlobalApiData(el.api_data);
          },
          execute: function execute(Args) {
              var message = API.messages.getById({
                  message_ids: Args.id,
                  group_id: Args.group_id,
                  v: Args.v
              }).items[0];

              message._conversation_keyboard = API.messages.getConversationsById({
                  peer_ids: message.from_id,
                  v: Args.v
              }).items[0].current_keyboard;

              return message;
          }
      }
  ];

  function insertGlobalApiData(data) {
    var cur_href = window.location.href;

    data.v = '5.150';
    if (group_regexp.test(cur_href)) {
      data.group_id = cur_href.match(group_regexp)[1];
    }

    return data;
  }

  function getExecuteCode(fn) {
    return fn.toString()
        .replace(/.+?\{([^]+)\}/, "$1")
        .replace(/\.map\(item => item.([\w\d_]+)\)/i, "@.$1")
  }

  function wrapAceEditor(aceWrapElem, callback) {
    loadScript(aceCorePath, {
      onLoad: function onAceLoad() {
        var editor = ace.edit(aceWrapElem);
        editor.$blockScrolling = Infinity;
        editor.setShowPrintMargin(false);
        editor.getSession().setMode('ace/mode/json');
        callback(editor);
      }
    });
  }

  function loadObject(data) {
    return API('execute', data).catch((error) => {
      return {
        description: 'Произошла ошибка',
        error,
        data
      };
    }).then(function (res) {
      var box = new MessageBox({
        title: 'Object Info: ' + data.id + ' <input class="api_version dark" value="' + data.v + '" style="float: right; margin: 13px;"/>',
        hideButtons: 1,
        width: 700
      });
      var api_version = geByClass1('api_version', box.titleWrap);
      api_version.onkeydown = function (e) {
        if (e.keyCode == 13) {
          data.v = e.target.value;
          loadObject(data).then(function () {
            box.hide();
          });
        }
      }

      res = res.response || res;
      box.bodyNode.innerHTML = clean(JSON.stringify(res, null, 3));
      box.bodyNode.className += ' api_object_helper_pre';
      wrapAceEditor(box.bodyNode, function (editor) {
        box.show();
        api_version.focus();
        api_version.selectionStart = 0;
        api_version.selectionEnd = api_version.value.length;
      });
    }).catch(console.error);
  }

  function addClickListener() {
    var keydown;
    var el_storage = [];

    function validateEvent(event) {
      return event.altKey;
    }

    function onClick(event, el) {
      if (!validateEvent(event)) return;
      if (!el.dataset.api_object_id) return;
      loadObject(el.api_data);
      event.preventDefault();
      event.stopPropagation();
      event.cancelBubble = true;
      event.returnValue = false;
      return false;
    }

    document.body.addEventListener('keydown', function (event) {
      if (!validateEvent(event)) return;
      keydown = true;
      document.body.classList.add('api_object_helper');
      api_objects.forEach((api_object) => {
        document.querySelectorAll(api_object.selector).forEach((el) => {
          api_object.callback(el);
          if (!el.dataset.api_object_id) return;
          el.api_data.code = getExecuteCode(api_object.execute);
          el.onclick = (event) => onClick(event, el);
          el_storage.push(el);
        });
      });
    });

    document.body.addEventListener('keyup', function (event) {
      if (!keydown) return;
      document.body.classList.remove('api_object_helper');
      el_storage.forEach((el) => {
        delete el.api_data;
        delete el.onclick;
      });
      el_storage = [];
    });
  }

  addClickListener();
}

var style = `
.api_object_helper_pre {
    overflow: scroll;
    width: 100%;
    height: 500px;
    white-space: pre;
    padding: 20px;
    box-sizing: border-box;
    background: #fff;
}
.api_object_helper [data-api_object_id]:before {
    content: attr(data-api_object_id);
    position: absolute;
    padding: 3px;
    background: #F44336;
    z-index: 2;
    color: #fff;
    font-size: 9px;
    border-radius: 2px;
    line-height: 1em;
}

.api_object_helper [data-api_object_id] {
    box-shadow: inset 0 0 0 1px #f00;
}`;

window.addEventListener("load", function (e) {
  document.head.appendChild(document.createElement("style")).innerHTML = style;
});

(function injectScript() {
  var script = document.createElement('script');
  var code = '(' + initImHelper + ')();';
  script.appendChild(document.createTextNode(code));
  (document.body || document.head || document.documentElement).appendChild(script);
})();