如何使用 Let's Encrypt 证书创建 Python HTTPS Web 服务器?

How to create a Python HTTPS Webserver using Let's Encrypt certificate?

我已经完成了我的 Python 电报机器人通过聊天发送 HTML5 游戏,感谢这个社区的帮助! 不幸的是,为了让机器人获取分数,我需要在机器人中实际设置一个 HTTP 服务器才能这样做。通过我的研究,我似乎无法弄清楚如何在 python 中使用 ssl 创建一个服务器,而不是自签名(因为当用户点击玩游戏时它会给出一个空白页面).

我买了一个域,它已经设置了我的 VPS IP 地址,尽管我有 Apache 的 ssl 证书...

有人可以帮我设置吗?由于发送和不安全的 HTTP 连接或自签名连接将导致应用程序内出现空白页面...

非常感谢!

Edit1:机器人代码:

import configparser, threading, requests, json, re, time, sys
from uuid import uuid4

from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import InlineQueryResultGame, ParseMode, InputTextMessageContent
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, InlineQueryHandler, CommandHandler, CallbackContext
from http.server import HTTPServer, BaseHTTPRequestHandler


def error_callback(update, context):
    logger.warning('Update "%s" caused error "%s"', update, context.error)

class Global:
    def __init__(self):
        return

class GameHTTPRequestHandler(BaseHTTPRequestHandler):
    def __init__(self, *args):
        BaseHTTPRequestHandler.__init__(self, *args)

    def do_GET(self):
        if "#" in self.path:
            self.path = self.path.split("#")[0]
        if "?" in self.path:
            (route, params) = self.path.split("?")
        else:
            route = self.path
            params = ""
        route = route[1:]
        params = params.split("&")
        if route in Global.games:
            self.send_response(200)
            self.end_headers()
            self.wfile.write(open(route+'.html', 'rb').read())
        elif route == "setScore":
            params = {}
            for item in self.path.split("?")[1].split("&"):
                if "=" in item:
                    pair = item.split("=")
                    params[pair[0]] = pair[1]
            print(params)
            if "imid" in params:
                Global.bot.set_game_score(params["uid"], params["score"], inline_message_id=params["imid"]) 
            else:
                Global.bot.set_game_score(params["uid"], params["score"], message_id=params["mid"], chat_id=params["cid"])
            self.send_response(200)
            self.end_headers()
            self.wfile.write(b'Set score')
        else:
            self.send_response(404)
            self.end_headers()
            self.wfile.write(b'Invalid game!')

def start(update, context):
    Global.bot.send_game(update.message.chat_id, Global.featured)

def error(update, context):
    print(update, error)

def button(update, context):
    print(update)
    query = update.callback_query
    game = query.game_short_name
    uid = str(query.from_user.id)
    if query.message:
        mid = str(query.message.message_id)
        cid = str(query.message.chat.id)
        url = "http://" + Global.host + ":"+Global.port + "/" + game + "?uid="+uid+"&mid="+mid+"&cid="+cid
    else:
        imid = update.callback_query.inline_message_id
        url = "http://" + Global.host + ":"+Global.port + "/" + game + "?uid="+uid+"&imid="+imid
    print(url)
    Global.bot.answer_callback_query(query.id, text=game, url=url)

def inlinequery(update, context):
    query = update.inline_query.query
    results = []
    for game in Global.games:
        if query.lower() in game.lower():
            results.append(InlineQueryResultGame(id=str(uuid4()),game_short_name=game))
    Global.update.inline_query.answer(results)

def main():
    config = configparser.ConfigParser()
    config.read('config.ini')
    token = config['DEFAULT']['API_KEY']
    Global.games = config['DEFAULT']['GAMES'].split(',')
    Global.host = config['DEFAULT']['HOST']
    Global.port = config['DEFAULT']['PORT']
    Global.featured = config['DEFAULT']['FEATURED']
    updater = Updater(token=token, use_context=True)

    dp = updater.dispatcher

    dp.add_handler(CommandHandler('start', start))
    dp.add_handler(InlineQueryHandler(inlinequery))
    dp.add_handler(CallbackQueryHandler(button))
    dp.add_error_handler(error)
    Global.bot = updater.bot

    print("Polling telegram")
    updater.start_polling()

    print("Starting http server")   
    http = HTTPServer((Global.host, int(Global.port)), GameHTTPRequestHandler)
    http.serve_forever()


if __name__ == '__main__':
    main()

HTML5游戏内代码,与分数相关:

function gameOver() {
            isGameOver = true;
            clearInterval(gameInterval);

            const urlParams = new URLSearchParams(window.location.search);
            const uid = urlParams.get('uid');
            const mid = urlParams.get('mid');
            const cid = urlParams.get('cid');
            const imid = urlParams.get('imid');
            if (imid) {
                const request = new Request(`/setScore?uid=${uid}&imid=${imid}&score=${score}`);
                fetch(request).then(response => console.log("set score"));
            }
            else {
                const request = new Request(`/setScore?uid=${uid}&mid=${mid}&cid=${cid}&score=${score}`);
                fetch(request).then(response => console.log("set score"));
            }
        }

Mark Powers

的原创机器人

在对评论的问题进行了一些澄清之后,让我尝试发表一些评论。这可能远非一个现成的解决方案,更多的是关于寻找什么的提示。

最终目标是从 HTML5 + JS 网页触发 setGameScore 请求,同时对该网页进行适当的 SSL 加密。该页面可以独立于机器人托管,在这种情况下,JS 代码应该自己发出请求(下面的方法 1),或者通过基于 python 的网络服务器在与机器人相同的脚本中托管(方法2 + 3),在这种情况下,目标是

  1. 让 JS 代码向该网络服务器发出请求,以便网络服务器执行请求并且
  2. 确保 SSL 加密,以便网站可以集成到 Telegams 游戏设置中。

方法一:让网站直接做

当前您的 JS 代码执行类似

的操作
const request = new Request(`/setScore?uid=${uid}&imid=${imid}&score=${score}`);
fetch(request).then(response => console.log("set score"));

我对 JS 的了解有限,但从 this 等来源来看,似乎

fetch("https://api.telegram.org/botTOKEN/setGameScore?...").then(response => console.log("set score"));

应该已经成功了

方法 2:使用 BaseHTTPRequestHandler

找出 SSL

这似乎是一个更笼统的话题。例如。我发现了这些看起来很相似的问题:

方法 3:使用反向代理来处理 SSL 内容

即让 BaseHTTPRequestHandler 监听“本地主机”并让 Apache/Nginx 将流量转发到正确的端口。这方面的灵感基本上来自于here。在这种情况下,使用 letsencrypt 加密 apache/nginx 应该相当简单。