【问题标题】:Quart & websocket: how to send data to selected users only (private message)Quart 和 websocket:如何仅向选定的用户发送数据(私人消息)
【发布时间】:2021-02-14 16:09:23
【问题描述】:

我知道如何广播,但我无法定位客户。 这是我的脚本:

import json    
import trio
from quart import render_template, websocket, render_template_string
from quart_trio import QuartTrio    
from quart_auth import current_user,login_required    
from quart_auth import AuthUser, login_user, logout_user, AuthManager    
import random

connections = set()

app = QuartTrio(__name__)
AuthManager(app)    
app.secret_key = "secret key"    

@app.route("/")
async def index():        
    clean_guy = await current_user.is_authenticated        
    if not clean_guy:        
        fake_ID = random.randrange(0, 9999) #quick dirty to test
        login_user(AuthUser(fake_ID)) 
        return await render_template_string("{{ current_user.__dict__ }}")
    return await render_template_string("{{ current_user.__dict__ }}")      

@app.websocket("/ws")
async def chat():
    try:
        connections.add(websocket._get_current_object())
        async with trio.open_nursery() as nursery:
            nursery.start_soon(heartbeat)
            while True:
                message = await websocket.receive()
                await broadcast(message)
    finally:
        connections.remove(websocket._get_current_object())


async def broadcast(message):
    for connection in connections:
        await connection.send(json.dumps({"type": "message", "value": message}))    

async def heartbeat():
    while True:
        await trio.sleep(1)
        await websocket.send(json.dumps({"type": "heartbeat"}))    

if __name__ == '__main__':    
    app.run(host='0.0.0.0', port=5000)

这是我的模板:

<div>      
  <div>
    <ul>    
    </ul>
  </div>
  <form>
    <input type="text">
    <button type="submit">Send</button>
  </form>
</div>

<script type="text/javascript">
  document.addEventListener("DOMContentLoaded", function() {
    const ws = new WebSocket(`ws://${window.location.host}/ws`);
    ws.onmessage = function(event) {
      const data = JSON.parse(event.data);
      if (data.type === "message") {
        const ulDOM = document.querySelectorAll("ul")[0];
        const liDOM = document.createElement("li");
        liDOM.innerText = data.value;
        ulDOM.appendChild(liDOM);
      }
    }
    document.querySelectorAll("form")[0].onsubmit = function(event) {
      event.preventDefault();
      const inputDOM = document.querySelectorAll("input")[0];
      ws.send(inputDOM.value);
      inputDOM.value = "";
      return false;
    };
  });
</script>

还有一个问题: 如果我在我的脚本中使用它:

return await render_template("{{ current_user.__dict__ }}")

即使我在模板中添加 {{ current_user.dict }},我也无法在我的 jinja 模板中显示它。

我还注意到:

  • 使用 mozilla:我得到了一些稳定的东西,例如 {'_auth_id': 9635, 'action': }
  • 使用 chrome:每次刷新都会改变,看起来像 {'_auth_id': 529, 'action': }

我需要显示作者,目的地,以及带有发送按钮的输入,如何修复模板?

是否也可以通过 curl 或 websocat 向目标用户发送消息?该怎么做?

【问题讨论】:

    标签: python websocket message private quart


    【解决方案1】:

    Quart-Auth 使用 cookie 在每个请求/websocket-request 上识别用户,因此如果请求通过身份验证,您始终可以从 current_user 获取用户的身份。然后根据您的需要,您需要将 websocket 连接映射到每个用户(这样您就可以定位消息),因此连接映射应该是一个连接字典,例如

    import random
    from collections import defaultdict
    
    from quart import request, websocket
    from quart_trio import QuartTrio      
    from quart_auth import (
        AuthUser, current_user, login_required, login_user, logout_user, AuthManager 
    )   
    
    connections = defaultdict(set)
    
    app = QuartTrio(__name__)
    AuthManager(app)    
    app.secret_key = "secret key"    
    
    @app.route("/login", methods=["POST"])
    async def login():
        # Figure out who the user is,
        user_id = random.randrange(0, 9999)
        login_user(AuthUser(fake_ID)) 
        return {}
    
    @app.websocket("/ws")
    @login_required
    async def chat():
        user_id = await current_user.auth_id
        try:
            connections[user_id].add(websocket._get_current_object())
            while True:
                data = await websocket.receive_json()
                await broadcast(data["message"])
        finally:
            connections[user_id].remove(websocket._get_current_object())
    
    @app.route('/broadcast', methods=['POST'])
    @login_required
    async def send_broadcast():   
        data = await request.get_json()
        await broadcast(data["message"], data.get("target_id"))
        return {}
    
    async def broadcast(message, target = None):
        if target is None:
            for user_connections in connections.values():
                for connection in user_connections:
                    await connection.send_json({"type": "message", "value": message})
        else:
            for connection in connections[target]:
                await connection.send_json({"type": "message", "value": message})
    
     
    if __name__ == '__main__':    
        app.run(host='0.0.0.0', port=5000)
    

    然后,您可以将 JSON 发送到 /broadcast,这只是一条消息 {"message": "something"} 或带有 id 的消息,专门针对某人 {"message": "something for user 2", "target_id": 2}。还要注意 @login_required 装饰器确保路由处理程序只为登录用户调用。

    【讨论】:

      猜你喜欢
      • 2017-06-04
      • 1970-01-01
      • 2020-07-28
      • 1970-01-01
      • 2020-09-10
      • 1970-01-01
      • 2016-02-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多