mirror of
https://gitflic.ru/project/maks1ms/ocab.git
synced 2025-12-23 21:49:55 +03:00
wip
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"standard.filters": "^1.0.0"
|
||||
"required": {
|
||||
"standard.filters": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
{
|
||||
"id": "standard.command_helper",
|
||||
"name": "Command helper",
|
||||
"description": "Модуль для отображения команд при вводе '/'",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.database": "^1.0.0"
|
||||
"id": "standard.command_helper",
|
||||
"name": "Command helper",
|
||||
"description": "Модуль для отображения команд при вводе '/'",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"required": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.database": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,4 @@ from .config import (
|
||||
get_yandexgpt_in_words,
|
||||
get_yandexgpt_start_words,
|
||||
)
|
||||
from .main import module_late_init
|
||||
|
||||
@@ -1,22 +1,67 @@
|
||||
# flake8: noqa
|
||||
|
||||
import yaml
|
||||
from .config_manager import ConfigManager
|
||||
|
||||
from src.service import paths
|
||||
config = ConfigManager(
|
||||
config_path="/home/maxim/dev/alt-gnome-infrastructure/ocab/src/ocab_core/config.yaml"
|
||||
)
|
||||
|
||||
|
||||
def get_config(is_test: bool = False) -> dict:
|
||||
if is_test:
|
||||
path = f"{paths.modules_standard}/config/tests"
|
||||
else:
|
||||
path = paths.core
|
||||
path = f"{path}/config.yaml"
|
||||
def register_settings(settings_manager: ConfigManager):
|
||||
# TELEGRAM settings
|
||||
settings_manager.register_setting(
|
||||
["TELEGRAM", "TOKEN"], "", "string", is_private=True
|
||||
)
|
||||
settings_manager.register_setting(
|
||||
["TELEGRAM", "APPROVED_CHAT_ID"],
|
||||
"-123456789 | -012345678",
|
||||
"string",
|
||||
pretty_name="ID разрешенных чатов",
|
||||
description='Чаты, в которых будет работать бот. "|" - разделитель',
|
||||
)
|
||||
settings_manager.register_setting(
|
||||
["TELEGRAM", "ADMINCHATID"],
|
||||
-12345678,
|
||||
"number",
|
||||
pretty_name="ID чата администраторов",
|
||||
)
|
||||
settings_manager.register_setting(
|
||||
["TELEGRAM", "DEFAULT_CHAT_TAG"],
|
||||
"@alt_gnome_chat",
|
||||
"string",
|
||||
pretty_name="Основной чат",
|
||||
)
|
||||
settings_manager.register_setting(["TELEGRAM", "CHECK_BOT"], True, "checkbox")
|
||||
|
||||
with open(path, "r") as file:
|
||||
return yaml.full_load(file)
|
||||
# YANDEXGPT settings
|
||||
settings_manager.register_setting(
|
||||
["YANDEXGPT", "TOKEN"], "", "string", is_private=True
|
||||
)
|
||||
settings_manager.register_setting(
|
||||
["YANDEXGPT", "TOKEN_FOR_REQUEST"], 8000, "number"
|
||||
)
|
||||
settings_manager.register_setting(["YANDEXGPT", "TOKEN_FOR_ANSWER"], 2000, "number")
|
||||
settings_manager.register_setting(
|
||||
["YANDEXGPT", "CATALOGID"], "", "string", is_private=True
|
||||
)
|
||||
settings_manager.register_setting(
|
||||
["YANDEXGPT", "PROMPT"], "Ты чат-бот ...", "string"
|
||||
)
|
||||
settings_manager.register_setting(
|
||||
["YANDEXGPT", "STARTWORD"], "Бот| Бот, | бот | бот,", "string"
|
||||
)
|
||||
settings_manager.register_setting(
|
||||
["YANDEXGPT", "INWORD"], "помогите | не работает", "string"
|
||||
)
|
||||
|
||||
# ROLES settings
|
||||
settings_manager.register_setting(["ROLES", "ADMIN"], 2, "number")
|
||||
settings_manager.register_setting(["ROLES", "MODERATOR"], 1, "number")
|
||||
settings_manager.register_setting(["ROLES", "USER"], 0, "number")
|
||||
settings_manager.register_setting(["ROLES", "BOT"], 3, "number")
|
||||
|
||||
|
||||
config = get_config()
|
||||
register_settings(config)
|
||||
|
||||
|
||||
def get_telegram_token() -> str:
|
||||
@@ -76,4 +121,4 @@ def get_yandexgpt_token_for_answer() -> int:
|
||||
|
||||
|
||||
def get_access_rights() -> dict:
|
||||
return get_config()["ACCESS_RIGHTS"]
|
||||
return config["ACCESS_RIGHTS"]
|
||||
|
||||
259
src/ocab_modules/standard/config/config_manager.py
Normal file
259
src/ocab_modules/standard/config/config_manager.py
Normal file
@@ -0,0 +1,259 @@
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import yaml
|
||||
|
||||
# from ocab_core.modules_system.public_api import get_module, log
|
||||
|
||||
try:
|
||||
import dash_bootstrap_components as dbc
|
||||
from dash_extensions.enrich import Input, Output, dcc, html
|
||||
|
||||
DASH_AVAILABLE = True
|
||||
except ImportError:
|
||||
DASH_AVAILABLE = False
|
||||
|
||||
|
||||
class ConfigManager:
|
||||
def __init__(self, config_path: str):
|
||||
self._config_path = config_path
|
||||
self._config = self.load_config()
|
||||
self._registered_settings = dict()
|
||||
self._registered_settings_meta = dict()
|
||||
self._update_callbacks = []
|
||||
self._update_required = False
|
||||
|
||||
@property
|
||||
def config(self):
|
||||
return self._config
|
||||
|
||||
@config.setter
|
||||
def config(self, value):
|
||||
self._config = value
|
||||
|
||||
def load_config(self) -> Dict[str, Any]:
|
||||
with open(self._config_path, "r") as file:
|
||||
return yaml.safe_load(file)
|
||||
|
||||
def save_config(self):
|
||||
with open(self._config_path, "w") as file:
|
||||
yaml.dump(self._config, file)
|
||||
|
||||
def register_setting(
|
||||
self,
|
||||
key: Union[str, List[str]],
|
||||
default_value: Any,
|
||||
setting_type: str,
|
||||
is_private: bool = False,
|
||||
pretty_name: str = None,
|
||||
description: str = None,
|
||||
options: Optional[List[str]] = None,
|
||||
):
|
||||
if isinstance(key, str):
|
||||
key = [key]
|
||||
|
||||
current = self._registered_settings
|
||||
for k in key[:-1]:
|
||||
if k not in current:
|
||||
current[k] = {}
|
||||
current = current[k]
|
||||
|
||||
current[key[-1]] = self.get_nested_setting(self._config, key, default_value)
|
||||
|
||||
self.set_nested_setting(
|
||||
self._registered_settings_meta,
|
||||
key,
|
||||
{
|
||||
"type": setting_type,
|
||||
"is_private": is_private,
|
||||
"options": options,
|
||||
"pretty_name": pretty_name,
|
||||
"description": description,
|
||||
},
|
||||
)
|
||||
|
||||
def get_nested_setting(
|
||||
self, config: dict, keys: List[str], default: Any = None
|
||||
) -> Any:
|
||||
current = config
|
||||
for key in keys:
|
||||
if key in current:
|
||||
current = current[key]
|
||||
else:
|
||||
return default
|
||||
return current
|
||||
|
||||
def set_nested_setting(self, config: dict, keys: List[str], value: Any):
|
||||
current = config
|
||||
for key in keys[:-1]:
|
||||
if key not in current:
|
||||
current[key] = {}
|
||||
current = current[key]
|
||||
current[keys[-1]] = value
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key in self._registered_settings:
|
||||
return self._registered_settings[key]
|
||||
raise KeyError(key)
|
||||
|
||||
def update_setting(self, key: Union[str, List[str]], value: Any):
|
||||
if isinstance(key, str):
|
||||
key = [key]
|
||||
self.set_nested_setting(self._registered_settings, key, value)
|
||||
self.set_nested_setting(self._config, key, value)
|
||||
self.save_config()
|
||||
|
||||
def get_settings_layout(self):
|
||||
from dash_extensions.enrich import DashBlueprint
|
||||
|
||||
bp = DashBlueprint()
|
||||
|
||||
def create_layout():
|
||||
def create_nested_layout(settings: dict, key_list=None):
|
||||
if key_list is None:
|
||||
key_list = []
|
||||
|
||||
components = []
|
||||
|
||||
for key, value in settings.items():
|
||||
current_key_list = key_list.copy()
|
||||
current_key_list.append(key)
|
||||
|
||||
if isinstance(value, dict):
|
||||
nested = create_nested_layout(value, current_key_list)
|
||||
if len(nested) > 0:
|
||||
components.append(
|
||||
dbc.Card(
|
||||
[
|
||||
dbc.CardHeader(html.H3(key, className="mb-0")),
|
||||
dbc.CardBody(nested),
|
||||
],
|
||||
className="mb-3",
|
||||
)
|
||||
)
|
||||
else:
|
||||
meta = self.get_nested_setting(
|
||||
self._registered_settings_meta, current_key_list
|
||||
)
|
||||
|
||||
if not meta.get("is_private"):
|
||||
row = []
|
||||
label_text = meta.get("pretty_name", key)
|
||||
|
||||
if meta.get("type") != "checkbox":
|
||||
row.append(dbc.Label(label_text))
|
||||
|
||||
component_id = {
|
||||
"type": "setting",
|
||||
"key": "-".join(current_key_list),
|
||||
}
|
||||
|
||||
if meta.get("type") == "string":
|
||||
component = dbc.Input(
|
||||
id=component_id, type="text", value=value
|
||||
)
|
||||
elif meta.get("type") == "number":
|
||||
component = dbc.Input(
|
||||
id=component_id, type="number", value=value
|
||||
)
|
||||
elif meta.get("type") == "checkbox":
|
||||
component = dbc.Col(
|
||||
[
|
||||
dbc.Checkbox(
|
||||
id=component_id,
|
||||
value=value,
|
||||
label=dbc.Label(
|
||||
label_text,
|
||||
style={"margin-right": "10px"},
|
||||
check=True,
|
||||
),
|
||||
)
|
||||
]
|
||||
)
|
||||
elif meta.get("type") == "select":
|
||||
options = [
|
||||
{"label": opt, "value": opt}
|
||||
for opt in meta.get("options", [])
|
||||
]
|
||||
component = dcc.Dropdown(
|
||||
id=component_id, options=options, value=value
|
||||
)
|
||||
else:
|
||||
continue
|
||||
|
||||
row.append(component)
|
||||
|
||||
if meta.get("description"):
|
||||
row.append(dbc.FormText(meta.get("description")))
|
||||
|
||||
components.append(dbc.Row(row, className="mb-3"))
|
||||
|
||||
return components
|
||||
|
||||
settings_components = create_nested_layout(self._registered_settings)
|
||||
|
||||
layout = html.Div(
|
||||
[
|
||||
html.H1("Настройки"),
|
||||
dbc.Form(settings_components),
|
||||
dbc.Button(
|
||||
"Сохранить",
|
||||
id="save-settings",
|
||||
color="primary",
|
||||
className="mt-3",
|
||||
n_clicks=0,
|
||||
),
|
||||
html.Div(id="settings-update-trigger", style={"display": "none"}),
|
||||
html.Span(
|
||||
id="save-confirmation", style={"verticalAlign": "middle"}
|
||||
),
|
||||
dcc.Store(id="settings-store"),
|
||||
],
|
||||
style={
|
||||
"padding": "20px",
|
||||
},
|
||||
)
|
||||
|
||||
return layout
|
||||
|
||||
bp.layout = create_layout
|
||||
self.setup_callbacks(bp)
|
||||
|
||||
return bp
|
||||
|
||||
def setup_callbacks(self, app):
|
||||
# ws = WebSocket(app, url="/ws")
|
||||
|
||||
@app.callback(
|
||||
Output("save-confirmation", "children"),
|
||||
# Output("settings-store", "data"),
|
||||
Input("save-settings", "n_clicks"),
|
||||
# State({"type": "setting", "key": ALL}, "value"),
|
||||
# State({"type": "setting", "key": ALL}, "id"),
|
||||
running=[(Output("save-settings", "disabled"), True, False)],
|
||||
)
|
||||
def save_settings(n_clicks, values, ids):
|
||||
if n_clicks > 0:
|
||||
# updated_settings = {}
|
||||
# print(ids)
|
||||
# for value, id_dict in zip(values, ids):
|
||||
# key = id_dict["key"]
|
||||
# self.update_setting(key.split("-"), value)
|
||||
# updated_settings[key] = value
|
||||
|
||||
return "Настройки сохранены!" # , json.dumps(updated_settings)
|
||||
return "" # , None
|
||||
|
||||
# @app.callback(
|
||||
# Output({"type": "setting", "key": ALL}, "value"),
|
||||
# Input("settings-store", "data"),
|
||||
# )
|
||||
# def update_settings_from_store(data):
|
||||
# if data:
|
||||
# updated_settings = json.loads(data)
|
||||
# print(
|
||||
# [current_value for key, current_value in updated_settings.items()]
|
||||
# )
|
||||
# return [
|
||||
# current_value for key, current_value in updated_settings.items()
|
||||
# ]
|
||||
# raise dash.exceptions.PreventUpdate()
|
||||
@@ -5,5 +5,14 @@
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0.0",
|
||||
"privileged": true,
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"optional": {
|
||||
"standard.miniapp": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"pythonDependencies": {
|
||||
"optional": {
|
||||
"flet": "^0.23.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
src/ocab_modules/standard/config/main.py
Normal file
35
src/ocab_modules/standard/config/main.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from ocab_core.modules_system.public_api import get_module, log
|
||||
|
||||
from .config import config
|
||||
|
||||
|
||||
def register_settings_page():
|
||||
try:
|
||||
register_page = get_module("standard.miniapp", "register_page")
|
||||
#
|
||||
# def setup_callbacks_wrapper(config_manager):
|
||||
# def setup(app):
|
||||
# config_manager.setup_callbacks(app)
|
||||
#
|
||||
# return setup
|
||||
#
|
||||
# register_page(
|
||||
# name="Настройки",
|
||||
# path="/settings",
|
||||
# layout=config.get_settings_layout(),
|
||||
# setup_callbacks=setup_callbacks_wrapper(config),
|
||||
# )
|
||||
register_page(
|
||||
name="Настройки", path="/settings", blueprint=config.get_settings_layout()
|
||||
)
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
log(str(e))
|
||||
pass
|
||||
|
||||
|
||||
def module_late_init():
|
||||
register_settings_page()
|
||||
|
||||
pass
|
||||
@@ -6,7 +6,9 @@
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.config": "^1.0.0"
|
||||
"required": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.config": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
{
|
||||
"id": "standard.fsm_database_storage",
|
||||
"name": "FSM Database Storage",
|
||||
"description": "Очень полезный модуль",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"standard.database": "^1.0.0"
|
||||
"id": "standard.fsm_database_storage",
|
||||
"name": "FSM Database Storage",
|
||||
"description": "Очень полезный модуль",
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"required": {
|
||||
"standard.database": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.database": "^1.0.0",
|
||||
"standard.command_helper": "^1.0.0"
|
||||
"required": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.database": "^1.0.0",
|
||||
"standard.command_helper": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.database": "^1.0.0",
|
||||
"standard.command_helper": "^1.0.0"
|
||||
"required": {
|
||||
"standard.roles": "^1.0.0",
|
||||
"standard.database": "^1.0.0",
|
||||
"standard.command_helper": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
from .main import module_init
|
||||
from .lib import register_page
|
||||
from .main import module_init, module_late_init
|
||||
|
||||
@@ -5,5 +5,17 @@
|
||||
"author": "OCAB Team",
|
||||
"version": "1.0.0",
|
||||
"privileged": false,
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"required": {
|
||||
"standard.config": {
|
||||
"version": "^1.0.0",
|
||||
"uses": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"pythonDependencies": {
|
||||
"required": {
|
||||
"flet": "^0.23.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
172
src/ocab_modules/standard/miniapp/lib.py
Normal file
172
src/ocab_modules/standard/miniapp/lib.py
Normal file
@@ -0,0 +1,172 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
import dash
|
||||
import dash_bootstrap_components as dbc
|
||||
from dash_extensions.enrich import DashBlueprint, DashProxy, Input, Output, dcc, html
|
||||
from dash_extensions.pages import setup_page_components
|
||||
from flask import Flask
|
||||
|
||||
pages = OrderedDict()
|
||||
|
||||
|
||||
def register_page(name, path, blueprint):
|
||||
pages[path] = {
|
||||
"name": name,
|
||||
"blueprint": blueprint,
|
||||
}
|
||||
|
||||
|
||||
def register_home_page():
|
||||
page = DashBlueprint()
|
||||
page.layout = html.Div([html.H1("Главная")])
|
||||
register_page("Главная", path="/", blueprint=page)
|
||||
|
||||
|
||||
register_home_page()
|
||||
|
||||
|
||||
def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
|
||||
server = Flask(__name__)
|
||||
app = DashProxy(
|
||||
pages_folder="",
|
||||
use_pages=True,
|
||||
suppress_callback_exceptions=True,
|
||||
external_stylesheets=[
|
||||
dbc.themes.BOOTSTRAP,
|
||||
dbc.icons.BOOTSTRAP,
|
||||
],
|
||||
external_scripts=[
|
||||
"https://telegram.org/js/telegram-web-app.js"
|
||||
], # Add Telegram Mini Apps script to <head>
|
||||
server=server,
|
||||
requests_pathname_prefix=requests_pathname_prefix,
|
||||
meta_tags=[
|
||||
{"name": "viewport", "content": "width=device-width, initial-scale=1"},
|
||||
],
|
||||
)
|
||||
|
||||
app.enable_dev_tools(
|
||||
dev_tools_ui=True,
|
||||
dev_tools_serve_dev_bundles=True,
|
||||
)
|
||||
|
||||
# Register pages
|
||||
for path, page in pages.items():
|
||||
# dash.register_page(page["name"], path=path, layout=page["layout"])
|
||||
page["blueprint"].register(app, path, prefix="a")
|
||||
|
||||
# Create sidebar
|
||||
sidebar = dbc.Offcanvas(
|
||||
id="offcanvas",
|
||||
title="Меню",
|
||||
is_open=False,
|
||||
children=[
|
||||
dbc.Nav(
|
||||
[
|
||||
dbc.NavLink(
|
||||
page["name"],
|
||||
href=f"{requests_pathname_prefix}{path.lstrip('/')}",
|
||||
id={"type": "nav-link", "index": path},
|
||||
)
|
||||
for path, page in pages.items()
|
||||
],
|
||||
vertical=True,
|
||||
pills=True,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Create navbar
|
||||
navbar = dbc.Navbar(
|
||||
dbc.Container(
|
||||
[
|
||||
dbc.Button(
|
||||
html.I(className="bi bi-list"),
|
||||
id="open-offcanvas",
|
||||
color="light",
|
||||
className="me-2",
|
||||
),
|
||||
dbc.NavbarBrand("OCAB"),
|
||||
]
|
||||
),
|
||||
color="primary",
|
||||
dark=True,
|
||||
)
|
||||
|
||||
# Define app layout
|
||||
app.layout = html.Div(
|
||||
[
|
||||
dcc.Location(id="url", refresh=False),
|
||||
dcc.Store(id="user-data", storage_type="session"),
|
||||
dcc.Interval(
|
||||
id="init-telegram-interval",
|
||||
interval=100,
|
||||
n_intervals=0,
|
||||
max_intervals=1,
|
||||
),
|
||||
# WebSocket(url="/ws"),
|
||||
html.Div(id="telegram-login-info"),
|
||||
navbar,
|
||||
sidebar,
|
||||
dash.page_container,
|
||||
setup_page_components(),
|
||||
]
|
||||
)
|
||||
|
||||
# Clientside callback to initialize Telegram Mini Apps and get user data
|
||||
app.clientside_callback(
|
||||
"""
|
||||
function(n_intervals) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve("test");
|
||||
if (window.Telegram && window.Telegram.WebApp) {
|
||||
const webapp = window.Telegram.WebApp;
|
||||
webapp.ready();
|
||||
if (webapp.initDataUnsafe && webapp.initDataUnsafe.user) {
|
||||
resolve(webapp.initDataUnsafe.user);
|
||||
} else {
|
||||
reject("User not authorized");
|
||||
}
|
||||
} else {
|
||||
reject("Telegram Mini Apps not available");
|
||||
}
|
||||
});
|
||||
}
|
||||
""",
|
||||
Output("user-data", "data"),
|
||||
Input("init-telegram-interval", "n_intervals"),
|
||||
)
|
||||
|
||||
# Открытие на кнопку меню
|
||||
app.clientside_callback(
|
||||
"""
|
||||
function(n_clicks) {
|
||||
if (n_clicks == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
""",
|
||||
Output(
|
||||
"offcanvas",
|
||||
"is_open",
|
||||
),
|
||||
Input("open-offcanvas", "n_clicks"),
|
||||
)
|
||||
|
||||
# Закрываем offcanvas при клике на ссылку в меню
|
||||
app.clientside_callback(
|
||||
"""
|
||||
function(n_clicks) {
|
||||
if (n_clicks == null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
Output("offcanvas", "is_open", allow_duplicate=True),
|
||||
Input({"type": "nav-link", "index": dash.dependencies.ALL}, "n_clicks"),
|
||||
prevent_initial_call="initial_duplicate",
|
||||
)
|
||||
|
||||
return app
|
||||
@@ -1,2 +1,31 @@
|
||||
from aiogram import types
|
||||
from fastapi.middleware.wsgi import WSGIMiddleware
|
||||
|
||||
from ocab_core.modules_system.public_api import Storage, set_chat_menu_button
|
||||
|
||||
|
||||
def get_link():
|
||||
pass
|
||||
|
||||
|
||||
def module_init():
|
||||
pass
|
||||
|
||||
|
||||
def register_page():
|
||||
pass
|
||||
|
||||
|
||||
async def module_late_init():
|
||||
from .lib import create_dash_app
|
||||
|
||||
dash_app = create_dash_app(requests_pathname_prefix="/webapp/")
|
||||
|
||||
Storage.set("webapp", WSGIMiddleware(dash_app.server))
|
||||
|
||||
web_app_info = types.WebAppInfo(
|
||||
url="https://mackerel-pumped-foal.ngrok-free.app/webapp"
|
||||
)
|
||||
menu_button = types.MenuButtonWebApp(text="Меню", web_app=web_app_info)
|
||||
|
||||
await set_chat_menu_button(menu_button)
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
"version": "1.0.0",
|
||||
"privileged": true,
|
||||
"dependencies": {
|
||||
"standard.config": "^1.0.0",
|
||||
"standard.database": "^1.0.0"
|
||||
"required": {
|
||||
"standard.config": "^1.0.0",
|
||||
"standard.database": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user