0%

telegram | mini app 与 后端以及 webhook 交互方式

这里说一下怎么和后端的 webhook 进行交互。这里的 webhook 指的是通过调用 telegramsetWebhook 来设置的后台 api webhook 地址,具体可以看 telegram | 数据传递

这里的 mini app 指的是,bot 中打开 open app 的按钮。

那么,通过 open app 如何和 bot 绑定的后端进行交互?

有两个方法。

  • 调用 bot 的 sendMessage 方法
    • https://api.telegram.org/bot<YOUR_BOT_TOKEN>/sendMessage,但是,这样就会把 token 暴漏出来,没有任何安全性
  • 直接和后端进行 webhook 发送请求

之前有一种和后台 webhook 的交互方式,就是直接和 bot 聊天,telegram | 前后端 3种 简单的交互案例,而在 mini app 中是无法做到这样的。无法调用 tg.sendData 直接发给 telegram 服务器,然后 telegram 服务器转发给后台。

对于,mini app,要时刻记住一点,打开后,它就是一个普通的网页,只不过,如果这个网页是在 telegram 中打开的,可以通过 telegram 的 js 获取相关的数据。

这里举一个例子。

  • 前端:地址是 https://tg.thlm.bond
  • 后端:地址是 https://tgapi.thlm.bond

前端

telegram 中打开,可以在 telegram 中获取用户信息发到后端中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Telegram Bot Interface</title>
<!-- 引入 Telegram WebApp JS SDK -->
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: var(--tg-theme-bg-color, #ffffff);
color: var(--tg-theme-text-color, #000000);
}
.container {
text-align: center;
}
.btn {
background-color: var(--tg-theme-button-color, #0088cc);
color: var(--tg-theme-button-text-color, #ffffff);
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
.btn:hover {
opacity: 0.9;
}
#status {
margin-top: 20px;
padding: 10px;
border-radius: 5px;
}
.success {
background-color: #dff0d8;
color: #3c763d;
}
.error {
background-color: #f2dede;
color: #a94442;
}
</style>
</head>
<body>
<div class="container">
<h1>Telegram Bot Interface</h1>
<button id="sendInfoBtn" class="btn">点击:发送用户信息</button>
<div id="status"></div>
</div>

<script>
// 确保 Telegram WebApp 已准备就绪
window.Telegram.WebApp.ready();

// 获取 Telegram WebApp 实例
const tg = window.Telegram.WebApp;

// 设置主题
document.documentElement.style.setProperty('--tg-theme-bg-color', tg.backgroundColor);
document.documentElement.style.setProperty('--tg-theme-text-color', tg.textColor);
document.documentElement.style.setProperty('--tg-theme-button-color', tg.buttonColor);
document.documentElement.style.setProperty('--tg-theme-button-text-color', tg.buttonTextColor);

document.getElementById('sendInfoBtn').addEventListener('click', async function() {
const statusDiv = document.getElementById('status');

try {
// 检查是否在 Telegram WebApp 环境中
if (!tg.initDataUnsafe || !tg.initDataUnsafe.user) {
throw new Error('无法获取 Telegram 用户信息');
}

const user = tg.initDataUnsafe.user;

const response = await fetch('https://tgapi.thlm.bond/info', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 添加 Telegram 初始化数据作为验证
'Telegram-Data': tg.initData
},
body: JSON.stringify({
userId: user.id,
username: user.username,
firstName: user.first_name,
lastName: user.last_name,
languageCode: user.language_code
})
});

const data = await response.json();

if (response.ok) {
statusDiv.className = 'success';
statusDiv.textContent = '信息发送成功!';

// 显示 Telegram 弹窗提示
tg.showPopup({
title: '成功',
message: '用户信息已成功发送',
buttons: [{text: '确定'}]
});
} else {
throw new Error(data.message || '请求失败');
}
} catch (error) {
console.error('Error:', error);
statusDiv.className = 'error';
statusDiv.textContent = '发送失败:' + error.message;

// 显示错误提示
tg.showPopup({
title: '错误',
message: error.message,
buttons: [{text: '确定'}]
});
}
});

// 设置 Telegram WebApp 返回按钮
tg.BackButton.show();
tg.BackButton.onClick(() => {
tg.close();
});

// 展开 WebApp 到全屏
tg.expand();
</script>
</body>
</html>

后端

后端通过 nginxhttps://tgapi.thlm.bond 的请求都转移到 http://127.0.0.1:8334 上。

这个后端不仅可以接受来自前端的请求,还可以接收用户和 bot 聊天的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import logging
from datetime import datetime

import requests
from flask import Flask, request, jsonify
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# 配置
BOT_TOKEN = 'YOUR_BOT_TOKEN' # 替换为你的 bot token
WEBHOOK_URL = 'https://tgapi.thlm.bond/webhook'


def setup_webhook():
"""设置 Telegram webhook"""
try:
# 首先删除现有的 webhook
delete_url = f'https://api.telegram.org/bot{BOT_TOKEN}/deleteWebhook'
delete_response = requests.get(delete_url)
logger.info(f"Delete webhook response: {delete_response.json()}")

# 设置新的 webhook
set_url = f'https://api.telegram.org/bot{BOT_TOKEN}/setWebhook'
set_response = requests.get(
set_url,
params={
'url': WEBHOOK_URL,
'allowed_updates': ['message', 'callback_query'] # 指定需要接收的更新类型
}
)

if set_response.status_code == 200:
result = set_response.json()
if result.get('ok'):
logger.info("Webhook set successfully!")
logger.info(f"Webhook info: {result}")
else:
logger.error(f"Failed to set webhook: {result}")
else:
logger.error(f"Failed to set webhook. Status code: {set_response.status_code}")

# 获取当前 webhook 信息
info_url = f'https://api.telegram.org/bot{BOT_TOKEN}/getWebhookInfo'
info_response = requests.get(info_url)
logger.info(f"Current webhook info: {info_response.json()}")

except Exception as e:
logger.error(f"Error setting up webhook: {str(e)}")
raise e


# 用户信息存储
users_info = {}


@app.route('/info', methods=['POST'])
def handle_user_info():
"""处理从前端发送来的用户信息"""
try:
user_info = request.json

# 记录用户信息
user_id = user_info.get('userId')
if user_id:
users_info[user_id] = {
'info': user_info,
'timestamp': datetime.now().isoformat()
}

logger.info(f"Received user info: {user_info}")

return jsonify({
'status': 'success',
'message': 'User information received successfully'
}), 200

except Exception as e:
logger.error(f"Error processing user info: {str(e)}")
return jsonify({
'status': 'error',
'message': str(e)
}), 500


@app.route('/webhook', methods=['POST'])
def webhook():
"""处理来自 Telegram 的 webhook 请求"""
try:
update = request.json
logger.info(f"Received webhook update: {update}")

# 获取消息信息
if 'message' in update:
chat_id = update['message']['chat']['id']
user_id = update['message']['from']['id']

# 检查是否有该用户的stored信息
user_stored_info = users_info.get(str(user_id))

if user_stored_info:
# 如果有存储的用户信息,发送回复
response_text = "已收到您的信息!"
send_telegram_message(chat_id, response_text)

return jsonify({'status': 'ok'}), 200

except Exception as e:
logger.error(f"Error processing webhook: {str(e)}")
return jsonify({
'status': 'error',
'message': str(e)
}), 500


def send_telegram_message(chat_id, text):
"""发送 Telegram 消息的辅助函数"""
try:
url = f'https://api.telegram.org/bot{BOT_TOKEN}/sendMessage'
data = {
'chat_id': chat_id,
'text': text
}
response = requests.post(url, json=data)
return response.json()
except Exception as e:
logger.error(f"Error sending telegram message: {str(e)}")
return None


@app.route('/')
def index():
"""健康检查端点"""
return jsonify({
'status': 'ok',
'timestamp': datetime.now().isoformat()
})


def init_app():
"""初始化应用"""
try:
# 设置 webhook
setup_webhook()
logger.info("Application initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize application: {str(e)}")
# 在生产环境中,你可能想要在这里优雅地处理错误
raise e


if __name__ == '__main__':
# 初始化应用
init_app()

# 启动应用
app.run(host='127.0.0.1', port=8443)
请我喝杯咖啡吧~