Refactor Telegram bot and web application for improved functionality
- Disabled the default menu button in the Telegram bot, allowing users to access the app via a direct link. - Updated the initData validation process to ensure URL-decoded values are used in the data-check string. - Enhanced error handling in the web application to provide more informative access denial messages. - Removed unnecessary debug information from the access denied section in the web app. - Cleaned up the web application code by removing unused functions and improving CSS styles for hidden elements.
This commit is contained in:
@@ -7,7 +7,7 @@ import time
|
||||
from urllib.parse import unquote
|
||||
|
||||
# Telegram algorithm: https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app
|
||||
# Data-check string must use the same key=value pairs as received (sorted by key); we preserve raw values.
|
||||
# Data-check string: sorted key=value with URL-decoded values, then HMAC-SHA256(WebAppData, token) as secret.
|
||||
|
||||
|
||||
def validate_init_data(
|
||||
@@ -44,7 +44,9 @@ def validate_init_data_with_reason(
|
||||
if not hash_val:
|
||||
return (None, "no_hash")
|
||||
data_pairs = sorted(params.items())
|
||||
data_string = "\n".join(f"{k}={v}" for k, v in data_pairs)
|
||||
# Data-check string: key=value with URL-decoded values (per Telegram example)
|
||||
data_string = "\n".join(f"{k}={unquote(v)}" for k, v in data_pairs)
|
||||
# HMAC-SHA256(key=WebAppData, message=bot_token) per reference implementations
|
||||
secret_key = hmac.new(
|
||||
b"WebAppData",
|
||||
msg=bot_token.encode(),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
from urllib.parse import quote
|
||||
from urllib.parse import quote, unquote
|
||||
|
||||
|
||||
from api.telegram_auth import validate_init_data
|
||||
@@ -21,7 +21,7 @@ def _make_init_data(
|
||||
if auth_date is not None:
|
||||
params["auth_date"] = str(auth_date)
|
||||
pairs = sorted(params.items())
|
||||
data_string = "\n".join(f"{k}={v}" for k, v in pairs)
|
||||
data_string = "\n".join(f"{k}={unquote(v)}" for k, v in pairs)
|
||||
secret_key = hmac.new(
|
||||
b"WebAppData",
|
||||
msg=bot_token.encode(),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""Command handlers: /start, /help; /start registers user and shows Calendar button."""
|
||||
"""Command handlers: /start, /help; /start registers user."""
|
||||
|
||||
import asyncio
|
||||
|
||||
import config
|
||||
from telegram import Update, WebAppInfo, InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram import Update
|
||||
from telegram.ext import CommandHandler, ContextTypes
|
||||
|
||||
from db.session import get_session
|
||||
@@ -42,27 +42,14 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
await asyncio.get_running_loop().run_in_executor(None, do_get_or_create)
|
||||
|
||||
text = "Привет! Я бот календаря дежурств. Используй /help для списка команд."
|
||||
if config.MINI_APP_BASE_URL:
|
||||
keyboard = InlineKeyboardMarkup(
|
||||
[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
"📅 Календарь",
|
||||
web_app=WebAppInfo(url=config.MINI_APP_BASE_URL + "/app/"),
|
||||
)
|
||||
],
|
||||
]
|
||||
)
|
||||
await update.message.reply_text(text, reply_markup=keyboard)
|
||||
else:
|
||||
await update.message.reply_text(text)
|
||||
await update.message.reply_text(text)
|
||||
|
||||
|
||||
async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
if update.message:
|
||||
await update.message.reply_text(
|
||||
"Доступные команды:\n"
|
||||
"/start — Начать и открыть календарь\n"
|
||||
"/start — Начать\n"
|
||||
"/help — Показать эту справку"
|
||||
)
|
||||
|
||||
|
||||
3
main.py
3
main.py
@@ -61,7 +61,8 @@ def _run_uvicorn(web_app, port: int) -> None:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
_set_default_menu_button_webapp()
|
||||
# Menu button (Календарь) and inline Calendar button are disabled; users open the app by link if needed.
|
||||
# _set_default_menu_button_webapp()
|
||||
app = ApplicationBuilder().token(config.BOT_TOKEN).build()
|
||||
register_handlers(app)
|
||||
|
||||
|
||||
@@ -77,14 +77,6 @@
|
||||
return "";
|
||||
}
|
||||
|
||||
function getInitDataDebug() {
|
||||
var sdk = !!(window.Telegram && window.Telegram.WebApp && window.Telegram.WebApp.initData);
|
||||
var hash = window.location.hash ? window.location.hash.slice(1) : "";
|
||||
var hashHasData = !!(hash && (getTgWebAppDataFromHash(hash) || new URLSearchParams(hash).get("tgWebAppData")));
|
||||
var queryHasData = !!(window.location.search && new URLSearchParams(window.location.search).get("tgWebAppData"));
|
||||
return "SDK: " + (sdk ? "да" : "нет") + ", hash: " + (hashHasData ? hash.length + " симв." : (hash ? "есть, без tgWebAppData" : "нет")) + ", query: " + (queryHasData ? "да" : "нет");
|
||||
}
|
||||
|
||||
function isLocalhost() {
|
||||
var h = window.location.hostname;
|
||||
return h === "localhost" || h === "127.0.0.1" || h === "";
|
||||
@@ -101,8 +93,8 @@
|
||||
} catch (e) { return false; }
|
||||
}
|
||||
|
||||
function showAccessDenied() {
|
||||
var debugStr = getInitDataDebug();
|
||||
/** @param {string} [serverDetail] - message from API 403 detail (unused; kept for callers) */
|
||||
function showAccessDenied(serverDetail) {
|
||||
if (headerEl) headerEl.hidden = true;
|
||||
if (weekdaysEl) weekdaysEl.hidden = true;
|
||||
calendarEl.hidden = true;
|
||||
@@ -110,14 +102,6 @@
|
||||
loadingEl.classList.add("hidden");
|
||||
errorEl.hidden = true;
|
||||
accessDeniedEl.hidden = false;
|
||||
var debugEl = document.getElementById("accessDeniedDebug");
|
||||
if (debugEl) debugEl.textContent = debugStr;
|
||||
var hintEl = document.getElementById("accessDeniedHint");
|
||||
if (hintEl) {
|
||||
hintEl.textContent = hasTelegramHashButNoInitData()
|
||||
? "Откройте календарь через кнопку меню бота (⋮ или «Календарь»), а не через «Открыть в браузере» или прямую ссылку."
|
||||
: "Откройте календарь из Telegram.";
|
||||
}
|
||||
}
|
||||
|
||||
function hideAccessDenied() {
|
||||
@@ -138,7 +122,18 @@
|
||||
var timeoutId = setTimeout(function () { controller.abort(); }, FETCH_TIMEOUT_MS);
|
||||
try {
|
||||
var res = await fetch(url, { headers: headers, signal: controller.signal });
|
||||
if (res.status === 403) throw new Error("ACCESS_DENIED");
|
||||
if (res.status === 403) {
|
||||
var detail = "Доступ запрещён";
|
||||
try {
|
||||
var body = await res.json();
|
||||
if (body && body.detail !== undefined) {
|
||||
detail = typeof body.detail === "string" ? body.detail : (body.detail.msg || JSON.stringify(body.detail));
|
||||
}
|
||||
} catch (parseErr) { /* ignore */ }
|
||||
var err = new Error("ACCESS_DENIED");
|
||||
err.serverDetail = detail;
|
||||
throw err;
|
||||
}
|
||||
if (!res.ok) throw new Error("Ошибка загрузки");
|
||||
return res.json();
|
||||
} catch (e) {
|
||||
@@ -281,7 +276,7 @@
|
||||
renderDutyList(duties);
|
||||
} catch (e) {
|
||||
if (e.message === "ACCESS_DENIED") {
|
||||
showAccessDenied();
|
||||
showAccessDenied(e.serverDetail);
|
||||
setNavEnabled(true);
|
||||
if (window.Telegram && window.Telegram.WebApp && !window._initDataRetried) {
|
||||
window._initDataRetried = true;
|
||||
@@ -298,10 +293,12 @@
|
||||
}
|
||||
|
||||
document.getElementById("prevMonth").addEventListener("click", function () {
|
||||
if (!accessDeniedEl.hidden) return;
|
||||
current.setMonth(current.getMonth() - 1);
|
||||
loadMonth();
|
||||
});
|
||||
document.getElementById("nextMonth").addEventListener("click", function () {
|
||||
if (!accessDeniedEl.hidden) return;
|
||||
current.setMonth(current.getMonth() + 1);
|
||||
loadMonth();
|
||||
});
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
<div class="error" id="error" hidden></div>
|
||||
<div class="access-denied" id="accessDenied" hidden>
|
||||
<p>Доступ запрещён.</p>
|
||||
<p class="muted" id="accessDeniedHint">Откройте календарь из Telegram.</p>
|
||||
<p class="debug" id="accessDeniedDebug"></p>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||
|
||||
@@ -36,6 +36,11 @@ body {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.header[hidden],
|
||||
.weekdays[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.nav {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
@@ -166,12 +171,6 @@ body {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.access-denied .debug {
|
||||
font-size: 0.75rem;
|
||||
margin-top: 12px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.access-denied[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user