aaletov / dormyboba

Dormitory registration and mailing system
MIT License
0 stars 0 forks source link

[RES-1] Research ways to pass additional parameters to Vk bot on startup #2

Closed aaletov closed 6 months ago

aaletov commented 11 months ago

Overview

Telegram Bot API have deep linking mechanism which allows to pass additional parameters on bot startup without any user input. Read Vk Bot API docs and find out does it provide simillar functionality or consider ways to pass additional parameters using some proxy-endpoint + redirect

Probably we can pass parameters using Vk App authentication mechanism

aaletov commented 11 months ago

"Start" button Vk

https://dev.vk.com/ru/api/bots/development/keyboard#%D0%9A%D0%BD%D0%BE%D0%BF%D0%BA%D0%B0%20%C2%AB%D0%9D%D0%B0%D1%87%D0%B0%D1%82%D1%8C%C2%BB

aaletov commented 11 months ago

OAuth authorization process. Has parameter redirect_uri which may contain link to our proxy-page e.g. dormyboba.ru/vk_auth?token=<token> which will send data to auth service and redirect user back to the bot chat

https://dev.vk.com/ru/api/oauth-parameters

aaletov commented 11 months ago

Authorization Code Flow & users.get() may be used to authenticate user. Associating user id with our secret problem isn't solved yet. How to pass parameters to redirect_uri?

aaletov commented 11 months ago

HTTP request is fully encapsulated in TLS package, therefore we consider query parameters as safe. Decided to use readirect_uri OAuth2 parameter and pass token as a URI parameters to pass token

aaletov commented 6 months ago

Переоткрываем, чтобы разобрать возможные варианты реализации с опорой на исходники скрипта https://vk.com/js/api/openapi.js?169 используемого для подключения виджета. Методом технической интуиции выяснено, что по адресу https://vk.com/dist/public/api/openapi.js лежит распакованный исходник, не обработанный вебпаком

aaletov commented 6 months ago

На строке 454 поле window.VK инициализируется пустым объектом

if (!window.VK) window.VK = {}

При этом поля объекта window являются глобально разделяемыми переменными:

Image

Таким образом, дальнейшее обращение к переменной VK является обращением к полю window.VK, создаваемым на строке 454

aaletov commented 6 months ago

Метод VK.Observer.subscribe() (L1362-L1373):

    subscribe: function(eventName, handler) {
      var
          subscribers = this._subscribers();

      if (typeof handler != 'function') return false;

      if (!subscribers[eventName]) {
        subscribers[eventName] = [handler];
      } else {
        subscribers[eventName].push(handler);
      }
    },
aaletov commented 6 months ago

Ф-я VK.Widgets.AllowMessagesFromCommunity (L2183-L2200):

  VK.Widgets.AllowMessagesFromCommunity = function (objId, options, groupId) {
    groupId = parseInt(groupId, 10);
    if (!groupId || groupId < 0) throw Error('No group id passed');
    options = VK.Util.parseOptions(options);

    var params = {
      height: ({22: 22, 24: 24, 30: 30})[parseInt(options.height, 10) || 24],
      key: options.key ? options.key.substr(0, 256) : '',
      group_id: groupId
    }, rpc;

    return VK.Widgets._constructor('widget_allow_messages_from_community.php', objId, options, params, {}, {
      startHeight: params.height,
      height: params.height
    }, function(o, i, r) {
      rpc = r;
    });
  };
aaletov commented 6 months ago

Единственное взаимодействие VK.Widget и VK.Observer, происходит в конструкторе виджетов (L2764-L2768):

    funcs.publish = function() {
      var args = Array.prototype.slice.call(arguments);
      args.push(widgetId);
      VK.Observer.publish.apply(VK.Observer, args);
    };

Аргументы данной функции прокидываются методу VK.Observer.publish.apply

aaletov commented 6 months ago

Выяснено, что обычный статический хтмл-ник позволяет корректно обрабатывать ивенты, получен proof-of-concept

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Basic Django Project</title>
  <script src="https://vk.com/js/api/openapi.js?169" type="text/javascript"></script>
</head>
<body>

  <div id="vk_send_message"></div>
  <script type="text/javascript">
  <!-- TODO use template for group id -->
    const searchParams = new URLSearchParams(window.location.search);
    const token = searchParams.get("token");

    VK.Widgets.AllowMessagesFromCommunity("vk_send_message", {height: 30}, "223616799");
    VK.Observer.subscribe("widgets.allowMessagesFromCommunity.allowed", function f(userId) {
      console.log(userId);
      console.log("allowed");
      fetch("/invite/registerUser", {
        method: "POST",
        body: JSON.stringify({
          token: token,
          userId: userId,
        }),
        headers: {
          "Content-type": "application/json; charset=UTF-8"
        }
      });
    });
    VK.Observer.subscribe("widgets.allowMessagesFromCommunity.denied", function f(userId) {
      console.log(userId);
      console.log("denied");
      fetch("/invite/registerUser", {
        method: "POST",
        body: JSON.stringify({
          token: token,
          userId: userId,
        }),
        headers: {
          "Content-type": "application/json; charset=UTF-8"
        }
      });
    });
  </script>

</body>
</html>
aaletov commented 6 months ago

Рабочее объяснение причин ошибки - заголовки, которые неявно ставит django. Решено использовать fastapi ввиду большей простоты полной переработки модуля с виджетом по сравнению с устранением ошибки в существующем коде