#python #websocket #message #private #quart
#python #websocket #Сообщение #Частное #quart
Вопрос:
Я знаю, как транслировать, но я не могу настроить таргетинг на клиентов. Вот мой скрипт:
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__ }}")
я не могу отобразить его с помощью моего шаблона jinja, даже если добавлю {{ current_user .dict }} в моем шаблоне.
Я также заметил, что:
- с mozilla: я получаю что-то стабильное, например {‘_auth_id’: 9635, ‘action’:ПРОХОД: 2>}
- с Chrome: он меняется при каждом обновлении, выглядит как {‘_auth_id’: 529, ‘action’:НАПИШИТЕ: 3>}
Мне нужно отобразить автора, адресата и ввод с помощью кнопки отправки, как исправить шаблон?
Возможно ли также отправлять сообщения целевым пользователям с помощью post через curl или websocat? как это сделать?
Ответ №1:
Quart-Auth использует файлы cookie для идентификации пользователя при каждом запросе / websocket-запросе, поэтому вы всегда можете получить идентификатор пользователя от 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)
Затем вы можете отправить /broadcast
либо JSON, который представляет собой просто сообщение {"message": "something"}
, либо сообщение с идентификатором, предназначенное для кого-то конкретно {"message": "something for user 2", "target_id": 2}
. Также обратите @login_required
внимание, что декоратор гарантирует, что обработчик маршрута вызывается только для зарегистрированных пользователей.