GyverLibs / FastBot2

Очень быстрая и лёгкая библиотека Telegram бота для Arduino, ESP8266, ESP32
MIT License
34 stars 2 forks source link

Кастомизация Menu #8

Closed Laxilef closed 3 weeks ago

Laxilef commented 2 months ago

Приветствую. Спасибо за библиотеку!

Есть проблема с кастомизацией. Для себя написал CustomMenu, который наследует Menu для использования параметров кнопок:

gson::string superButton;
superButton[tg_api::text] = "Super button";
superButton[tg_api::request_contact] = true;
menu.addButton(superButton);

Однако без изменения исходного Menu нормально это не работает, т.к. toJson приватный и не виртуальный. Возможно ли добавить интерфейсы? Т.е. создать MenuInterface и в остальном коде использовать их, например

// сейчас
Message::setMenu(Menu& menu)

// сделать
Message::setMenu(MenuInterface& menu)

Ну а стоковый Menu будет наследовать MenuInterface. Это позволит создавать любые кастомные Menu согласно интерфейсу. В принципе это актуально и для других классов, это добавит гибкости.

Для примера:

#pragma once
#include <Arduino.h>
#include <GSON.h>
#include <StringUtils.h>

#include "core/types/Message_class.h"
#include "core/types/Menu.h"
#include "core/api.h"
#include "core/packet.h"

namespace fb {
  class CustomMenu : public Menu {
    friend class Message;

  public:
    CustomMenu() {
      // т.к. if (menu.text.length()) _menu = &menu;
      text = "123";
    }

    // добавить кнопку
    CustomMenu& addButton(su::Text text) {
      if (!arrayCreated) {
        buttons.beginArr();
        arrayCreated = true;
      }

      buttons.addStringEsc(text);
      return *this;
    }

    CustomMenu& addButton(gson::string str) {
      if (!arrayCreated) {
        buttons.beginArr();
        arrayCreated = true;
      }

      buttons.beginObj();
      buttons.addTextRaw(str);
      buttons.endObj();

      return *this;
    }

    // перенести строку
    CustomMenu& newRow() {
      if (arrayCreated) {
        buttons.endArr();
      }

      buttons.beginArr();
      arrayCreated = true;

      return *this;
    }

  private:
    bool arrayCreated = false;
    gson::string buttons;

    void _toJson(Packet& p) {
      if (arrayCreated) {
        buttons.endArr();
        arrayCreated = false;
      }

      p.beginArr(tg_api::keyboard);
      p.addTextRaw(buttons);
      p.endArr();

      if (persistent) p[tg_api::is_persistent] = true;
      if (resize) p[tg_api::resize_keyboard] = true;
      if (oneTime) p[tg_api::one_time_keyboard] = true;
      if (selective) p[tg_api::selective] = true;
      if (placeholder.length()) p.addStringEsc(tg_api::input_field_placeholder, placeholder);
    }
  };
}
Laxilef commented 2 months ago

Либо можно добавить virtual к методам addButton, newRow и перенести _toJson в public и добавить ему virtual.

Но тогда условие в Message.h

    void setMenu(Menu& menu) {
        if (menu.text.length()) _menu = &menu;
    }

целесообразно заменить на что-то вроде

    void setMenu(Menu& menu) {
        if (!menu.empty()) _menu = &menu;
    }

А метод empty добавить в Menu и повесить на него virtual. Для InlineMenu аналогично.

GyverLibs commented 2 months ago

Хорошее дело, спасибо. А для чего ещё кроме меню может пригодиться?

Laxilef commented 2 months ago

А для чего ещё кроме меню может пригодиться?

Без серьезного рефакторинга скорее всего только для методов FastBot2Client.h, которые создают пакет, например:

    // отправить сообщение
    fb::Result sendMessage(const fb::Message& m, bool wait = true) {
        // ... существующий метод
    }

    // отправить сообщение
    fb::Result sendMessage(const fb::Message& m, gson::string& opts, bool wait = true) {
        if (!m.text.length() || !m.chatID) return fb::Result();
        fb::Packet p(tg_cmd::sendMessage, _token);
        if (opts.length()) {
            p.addTextRaw(opts);
        }
        m.makePacket(p);
        return sendPacket(p, wait);
    }

Может быть полезно для параметров, для которых не созданы свойства в классе, но все же иногда используются. В меню все немного иначе, т.к. нам нужно там хранить массив кнопок, поэтому и подход другой.

Могу попробовать подготовить PR.

upd: взглянул поподробнее, есть вариативность. Можно запихнуть функционал кастомизации запроса как в Message и наследников, так и в FastBot2Client. Как лучше сделать - хз, зависит от желаемой архитектуры. Однако если разраб добавит какой-то параметр, который уже существует в запросе, то запрос будет невалидным, т.к. архитектурно GSON не позволяет менять отдельные параметры. Т.е. если функционал добавлять, то нужно в документации это указать.

GyverLibs commented 2 months ago

Можно ещё в условный message добавить публичный gson string, и писать в него нужные параметры из скетча. А при сборке он добавится к пакету. Ну и в остальных классах сделать так же