构建一个简单的网页聊天工具
您好。在这篇文章中,您将学习如何使用 Python 和 WebSocket 构建一个简单的在线聊天系统。因此,您需要在计算机上安装Python 。
该项目的源代码可以在这个GitHub 仓库中找到,您也可以在Papo Reto网站上在线查看。
如果您没有该flask软件包,可以使用以下命令进行安装:pip install Flask。您还需要flask_socketio和eventlet,因此您可以运行pip install flask_socketio和pip install eventlet来安装它们。
现在所有文件都已安装完毕,项目的文件和目录结构如下所示:
templates
- index.html
static
css
- style.css
- main.py
我们项目的核心是这个main.py文件,它将控制我们的应用程序。它看起来会像这样:
#! -*- enconding: utf-8 -*-
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static/')
socketio = SocketIO(app)
@app.route('/')
def index():
return render_template('index.html')
@socketio.on('client_message')
def receive_message (client_msg):
emit('server_message', client_msg, broadcast=True)
if __name__ == '__main__':
socketio.run(app)
前两行代码导入了我们将在本项目中使用的包和模块。
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
Flask是我们的 Web 框架,是一个允许使用全双工低延迟通信协议(如 WebSocket)的flask_socketio软件包。flask
然后我们实例化 Flask 应用,并定义模板和静态文件的位置。我们还会实例化 Socket.IO 对象,并将 Flask 应用作为参数传递给它。
app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static/')
socketio = SocketIO(app)
现在我们定义默认路由,它将返回index.html页面:
@app.route('/')
def index():
return render_template('index.html')
当用户发送消息时,服务器会收到 client_message 事件,并将昵称和消息作为参数传递给服务器。然后,服务器会将此消息广播给所有已连接的用户:
@socketio.on('client_message')
def receive_message (client_msg):
emit('server_message', client_msg, broadcast=True)
最后,我们将使用以下代码启动应用程序:
if __name__ == '__main__':
socketio.run(app)
好了,我们的main.py文件现在已经完成了,接下来我们将进入该index.html文件。在这个例子中,它将包含三个主要部分:文件开头message-box、文件结尾input-box和文件末尾nickname-box。该文件的内容如下:
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<title>Papo Reto</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>
</head>
<body>
<script type="text/javascript" charset="utf-8">
const socket = io();
socket.on('server_message', (data) => {
let e = document.createElement('p');
let sp = document.createElement('span');
sp.innerHTML = data.nickname;
e.appendChild(sp);
e.innerHTML = e.innerHTML+'>> '+data.message;
if(document.getElementById('message-box').children.length>20){
document.getElementById('message-box').removeChild(document.getElementById('message-box').children[0]);
}
document.getElementById('message-box').appendChild(e);
document.getElementById('message-box').scroll(0, document.getElementById('message-box').scrollHeight);
});
function htmlEntities(str) {
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
}
function sendMessage(){
socket.emit('client_message', {'nickname': htmlEntities(document.getElementById('nickname-input').value), 'message': htmlEntities(document.getElementById('message-input').value)});
document.getElementById('message-input').value = '';
}
function isPressingEnter(e){
let k;
if(window.event){
k = e.keyCode;
if(k===13){
sendMessage();
}
}else if(e.which){
k = e.which;
if(k===13){
sendMessage();
}
}
}
</script>
<h1 id="title">Papo Reto</h1>
<section id="chat-box">
<section id="message-box">
</section>
<section id="input-box">
<input type="text" autofocus onkeypress="return isPressingEnter(event)" required placeholder="Digite sua mensagem aqui" id="message-input">
<button type="button" id="send-button" onclick="sendMessage()" >>></button>
</section>
</section>
<section id="nickname-box">
<label id="nickname-label" for="nickname-input">Nickname: </label>
<input type="text" id="nickname-input" autocomplete="off" value="Guest">
</section>
</body>
</html>
我们使用SocketIO客户端 API 来连接和处理 WebSocket 连接。因此,在这种情况下,我们使用 CDN 来实现这一点:
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>
现在,我们需要使用 JavaScript 定义一个 socket 对象,该对象将连接到服务器:
const socket = io();
由于 WebSocket 连接在同一路由上,因此我们不需要传递任何参数。
当用户点击发送按钮时,会调用该sendMessage函数。但如果用户在带有指定input-boxid 的输入框中输入内容,则会调用另一个isPressingEnter函数,该函数会验证用户是否按下了回车键。如果按下了回车键,还会调用sendMessage():
function isPressingEnter(e){
let k;
if(window.event){
k = e.keyCode;
if(k===13){
sendMessage();
}
}else if(e.which){
k = e.which;
if(k===13){
sendMessage();
}
}
}
然后,该sendMessage函数会client_message向服务器发出一个事件,并将昵称和消息作为数据传递:
function sendMessage(){
socket.emit('client_message', {'nickname': htmlEntities(document.getElementById('nickname-input').value), 'message': htmlEntities(document.getElementById('message-input').value)});
document.getElementById('message-input').value = '';
}
我们还使用该htmlEntities函数从输入框中移除 HTML 标签:
function htmlEntities(str) {
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
}
当服务器收到消息时,它会通过发出server_message事件将消息广播给所有人。因此,我们需要将此消息添加到收到事件的message-box通知中:server_message
socket.on('server_message', (data) => {
let e = document.createElement('p');
let sp = document.createElement('span');
sp.innerHTML = data.nickname;
e.appendChild(sp);
e.innerHTML = e.innerHTML+'>> '+data.message;
if(document.getElementById('message-box').children.length>20){
document.getElementById('message-box').removeChild(document.getElementById('message-box').children[0]);
}
document.getElementById('message-box').appendChild(e);
document.getElementById('message-box').scroll(0, document.getElementById('message-box').scrollHeight);
});
现在运行服务器python main.py,你会发现我们的在线聊天功能已经可以正常工作了。
但由于它看起来很糟糕,我们将添加一些 CSS 样式。Flask 模板使用jinja作为其模板引擎,因此将 CSS 文件路径添加到页面的方式略有不同index。我们可以通过在 标签内添加以下代码来实现head:
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='./css/stylxe.css') }}">
您可以随意操作,但在这里,这是我们 CSS 文件的内容:
*{
margin: 0;
padding: 0;
}
body{
background: #202020;
color: #eee;
font-family: 'monospace';
line-height: 1.4em;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
padding: 15px;
}
#title{
font-size: calc(20px + 2vw + 1vh);
padding: 40px;
}
#chat-box{
border: 2px solid #ccc;
border-radius: 10px;
box-shadow: 3px 4px 4px #111;
}
@media only screen and (max-width: 600px){
#chat-box{
width: 95vw;
}
}
@media only screen and (min-width: 600px){
#chat-box{
width: 80vw;
}
}
#message-box{
width: 100%;
height: 60vh;
background: #444;
overflow-y: scroll;
padding: 5px;
font-size: calc(10px + 1vh + 2vw);
}
#message-box p{
width: 100%;
line-height: 1.5em;
overflow-wrap: break-word;
padding: 5px 2px;
margin: 2px 0;
background: #4f4f4f;
}
#message-box span{
color: yellow;
font-weight: 900;
}
#input-box{
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 90% 10%;
}
#message-input{
height: 90%;
font-size: calc(10px + 1vh + 2vw);
border-radius: 0 0 0 6px;
}
#send-button{
background: purple;
color: #f0f0a0;
height: 100%;
font-size: calc(10px + 1vh);
font-weight: 900;
}
#nickname-box{
padding: 15px;
}
#nickname-input {
border-radius: 5px;
background: purple;
color: white;
padding: 2px;
font-size: calc(10px + 1vh + 1vw);
}
#nickname-label{
font-size: calc(10px + 1vh + 1vw);
}
谢谢大家的关注!这是我们用 Python 构建一个非常简单的在线聊天工具的方法之一。而且,实现起来也很容易,但如果你们需要,我还可以演示如何将其部署到 Heroku 上,这样你们就可以让它在线运行了。
文章来源:https://dev.to/raymag/building-a-simple-webchat-2504
