去年 11 月,我和几个同事参加了一个会议,我们希望让每个人都了解最新情况,并有组织地跟踪我们的计划。
我们建立了一个群聊系统,让其中一个成员发送短信,其他人都能收到,如果有人回复,我们都能看到回复。
这很方便,今天,我将向您展示如何构建一个类似的 web 应用。该应用将由一个简单的控制面板和一个后端组成,在控制面板上,你可以管理谁是一个组的一部分,后端将处理传入和传出的文本消息,并将它们路由到适当的组成员。
你还可以从网站上的一个页面实时发送和接收消息,当你可能没有带手机,但想给小组发消息时,反之亦然。
在继续之前,您需要设置好这些。
我们将使用 Flybase ( http://flybase.io/ )来处理 app 的数据存储和实时方面,Twilio ( www.twilio.com/ )来处理实际的短信工作,Node.js 用于系统本身。
我们将为一个单独的组构建这个特定的应用,但是将它扩展到多个组并不困难。
最后,我们将把这个应用作为一个免费的应用托管在 Heroku 上( https://heroku.com/ ,一个方便的托管平台,可以让你的项目快速启动和运行,尤其是在与 Flybase 和 Twilio 结合使用时)。
Node.js 将是我们应用的后端部分;这是我们为 Twilio 构建监听器的地方,无论我们何时发送或接收文本消息,我们都可以与之对话。
Flybase 是一个实时应用平台,将成为我们应用的首选数据存储。它将用于管理谁是一个组的成员,并存储传入和传出的消息以及它们来自谁。如果你还没有,现在就注册( https://app.flybase.io/signup )一个免费的 Flybase 帐户,然后在你的仪表盘内创建一个新的应用。你将在你的群聊系统中使用此应用。
Twilio 是我们一直以来都很方便的电话 API,它让我们可以构建像群聊应用甚至呼叫中心这样的服务。还没有 Twilio 帐户吗?免费报名( www.twilio.com/try-twilio )。
我们首先需要设置 Node.js 应用。
除了 Twilio 和 Flybase 模块,我们将使用 express 框架( http://expressjs.com/ )来设置我们的 Node web 服务器,以接收来自 Twilio 的 POST 请求,因此我们需要安装 Express 包。我们还将使用 body-parser 模块,所以我们也将安装它。
让我们创建我们的package.json文件:
javascript
{
"name": "group-chat",
"version": "0.0.1",
"description": "SMS Group Chat powered by Flybase, Twilio and Node.js",
"main": "app.js",
"repository": "https://github.com/flybaseio/group-chat",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"twilio",
"flybase",
"sms"
],
"author": "Roger Stringer",
"license": "MIT",
"dependencies": {
"body-parser": "~1.16.0",
"ejs": "~2.5.5",
"express": "~4.14.0",
"flybase": "^1.8.2",
"less-middleware": "~2.2.0",
"method-override": "~2.3.7",
"moment": "~2.17.1",
"node-buzz": "~1.1.0",
"twilio": "~2.11.1"
}
}保存该文件,并从终端运行以下命令:
javascript
npm install这将创建一个包含我们想要使用的所有模块的node_modules文件夹。
让我们设置我们的文件夹结构,并创建一个名为views的文件夹。这是我们将保持前端的地方。
现在,创建一个名为public的文件夹。这将托管我们的静态文件。在这个文件夹中,创建一个css文件夹和一个js文件夹。我们稍后将回到这些。
我们要创建的第一个文件是config.js;这将保存我们的配置信息:
javascript
module.exports = {
// Twilio API keys
twilio: {
sid: "ACCOUNTSID",
token: "AUTHTOKEN",
from_number: "YOUR-NUMBER"
},
flybase: {
api_key: "YOUR-API-KEY",
app_name: "YOUR-FLYBASE-APP"
},
un: 'admin',
pw: 'password'
};该文件用于我们的配置。我们可以在任何时候通过引用文件和调用键来访问这里的任何东西。例如,为了获得我们的 Flybase API 密钥,我们将调用
javascript
var config = require('./config');
console.log( config.flybase.api_key );将ACCOUNTSID、AUTHTOKEN和YOUR-NUMBER替换为您将使用的 Twilio 凭证和 Twilio 帐户中的电话号码。
然后,用您的 Flybase API 密钥替换YOUR-API-KEY和YOUR-FLYBASE-APP。
在我们的app.js文件的开头,我们需要 require express 并将其初始化为一个名为 app 的变量。我们还将使用 bodyParser 中间件( https://github.com/expressjs/body-parser )来方便地使用我们将在 POST 请求中获得的数据。
创建一个名为app.js的新文件,并需要 twilio、express 和 flybase 包:
javascript
var express = require('express');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
var path = require('path');
var config = require('./config');
var app = express();
app.set('views', path.join(process.cwd(), 'views'));
app.set('view engine', 'ejs');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded( extended: true }));
app.use(express.static(__dirname + '/public')); // set the static files location /public/img will be /img for users
var port = process.env.PORT || 8080; // set our port
var twilio = require('twilio');
var client = twilio(config.twilio.sid, config.twilio.token );
var flybase = require('flybase');
var messagesRef = flybase.init(config.flybase.app_name, "messages", config.flybase.api_key);
var groupRef = flybase.init(config.flybase.app_name, "groups", config.flybase.api_key);Flybase 使用集合来组织应用中的数据,因此一个应用可以有几个集合。如果您熟悉关系数据库,这相当于一个表。
我们将为我们的项目使用两个集合:一个包含messages,另一个包含groups。考虑到这一点,我们为我们的 Flybase 应用创建了两个不同的引用,一个用于消息,一个用于我们的组。这是我们应用的开始。接下来,我们将构建 web 界面来管理组成员,并允许发送和接收消息。
之后,我们将构建我们的 Twilio 界面,您将拥有一个有趣的应用来玩。
我们需要添加一些东西来发送和接收文本。我们的第一步是为 Twilio 添加一个监听器。
Twilio 使用 webhooks ( https://en.wikipedia.org/wiki/Webhook )来让您的服务器知道何时有消息或电话进入我们的应用。我们需要设置一个端点,我们可以告诉 Twilio 将它用于消息传递 webhook。
我们将为/message添加一条路由,它用一些 TwiML ( www.twilio.com/docs/api/twiml )进行响应。TwiML 是一组基本指令,当你收到来电或短信时,你可以用它来告诉 Twilio 该做什么。我们的代码将如下所示:
javascript
// listen for incoming sms messages
app.post('/message', function (request, response) {
groupRef.where( {"memberNumber":request.param('From')} ).limit(1).on( "value", function ( data ){
if( data.count() ){
data.forEach( function( snapshot ){
var member = snapshot.value();
messagesRef.push({
sid: request.param('MessageSid'),
type:'text',
tstamp: new Date().toLocaleString(),
fromName:member.memberName,
fromNumber:request.param('From'),
message:request.param('Body'),
media:"",
fromCity:request.param('FromCity'),
fromState:request.param('FromState'),
fromCountry:request.param('FromCountry'),
groupNumber:request.param('To')
});
});
}
});
var numMedia = parseInt( request.param('NumMedia') );
if (numMedia > 0) {
for (i = 0; i < numMedia; i++) {
var mediaUrl = request.param('MediaUrl' + i);
groupRef.where( {"memberNumber":request.param('From')} ).limit(1).on( "value", function ( data ){
if( data.count() ){
data.forEach( function( snapshot ){
var member = snapshot.value();
messagesRef.push({
sid: request.param('MessageSid'),
type:'text',
tstamp: new Date().toLocaleString(),
fromName:member.memberName,
fromNumber:request.param('From'),
message:"",
media:mediaUrl,
fromCity:request.param('FromCity'),
fromState:request.param('FromState'),
fromCountry:request.param('FromCountry'),
groupNumber:request.param('To')
});
});
}
});
}
}
var resp = new twilio.TwimlResponse();
resp.message('Message received.');
response.writeHead(200, {
'Content-Type':'text/xml'
});
response.end(resp.toString());
});这将监听任何传入的短信,并将它们存储在你的 Flybase 应用中,特别是在messages集合中。
作为存储消息的一部分,我们执行一个查找来找到与发送消息的电话号码相同的groups成员。然后,我们使用这个查找来验证该成员是否是组的一部分,并获取该成员的名称。
如果找不到成员,则不会发送任何消息。
一旦收到消息,我们使用 Twilio Node 库初始化一个新的 TwimlResponse 。然后我们使用消息关键字( www.twilio.com/docs/api/twiml/sms/message )来设置我们想要用什么来响应消息。在这种情况下,我们只说“消息已收到。”
然后,我们将响应的内容类型设置为text/xml,并发送我们构建的 TwimlResponse 的字符串表示。
作为我们的app.js代码的一部分,我们还想添加一些异步监听器来监听我们的 Flybase 应用的变化:
javascript
// when a new message is added to the Flybase app, send it via Twilio...
messagesRef.on("added", function (data ){
var snapshot = data.value();
sendMessage(
snapshot.groupNumber,
snapshot.fromName,
snapshot.fromNumber,
snapshot.message,
snapshot.media || ""
);
});
groupRef.on("added", function ( data ){
var snapshot = data.value();
var msg = snapshot.memberName + ' has joined the group';
messagesRef.push({
sid: "",
type:'',
tstamp: new Date().toLocaleString(),
fromName:"Admin",
fromNumber:"",
message:msg,
media:"",
fromCity:"",
fromState:"",
fromCountry:"",
groupNumber:snapshot.groupNumber
});
});
groupRef.on("removed", function ( data ){
var snapshot = data.value();
var msg = snapshot.memberName + ' has left the group';
// send broadcast that a group member has been removed
messagesRef.push({
sid: "",
type:'',
tstamp: new Date().toLocaleString(),
fromName:"Admin",
fromNumber:"",
message:msg,
media:"",
fromCity:"",
fromState:"",
fromCountry:"",
groupNumber:snapshot.groupNumber
});
});
// broadcast a message to the group
function sendMessage( group_number, from_name, from_number, message, media ){
var msg = from_name + ": " + message;
groupRef.where( {"memberNumber":{"$not":from_number}} ).on( "value", function ( data ){
if( data.count() ){
data.forEach( function( snapshot ){
var member = snapshot.value();
var msgObj = {
to:member.memberNumber,
from:group_number,
body:msg
};
if( media !== "" ){
msgObj.mediaUrl = media;
}
client.sendMessage( msgObj, function( err, data ) {});
});
}
});
}我们设置了三个异步侦听器,一个用于 messages 集合,它侦听任何被“添加”到其中的消息,当它收到新消息的通知时,调用我们的 sendMessage 函数将消息发送给组中的其他成员。
另外两个异步监听器用于我们的组集合:第一个监听器监听任何加入到组中的新成员,然后发送成员已经加入组的通知。
最后一个监听器将监听从组中“移除”的任何成员,并发送成员已经离开组的通知。
最后,我们的 sendMessage 函数用于向其他组成员发送消息;它将执行查询以返回该组的所有成员,不包括发送消息的人,并将消息发送给每个成员。
消息将以成员姓名后跟消息的格式显示:
John: How about pizza after work?最后,让我们设置我们的服务器监听端口8080,并告诉它当我们从浏览器中查看它时该做什么:
javascript
// frontend routes =========================
// Create basic middleware used to authenticate all admin requests
var auth = express.basicAuth(config.un, config.pw);
// route to handle all frontend requests with a password to protect unauthorized access....
app.get('*', auth, function(req, res) {
res.render('index', {
api_key:config.flybase.api_key,
app_name:config.flybase.app_name,
group_number:config.twilio.from_number
});
});
var server = app.listen(port, function() {
console.log('Listening on port %d', server.address().port);
});这是我们群聊应用的后端部分。它会监听收到的短信,将其存储在我们的 Flybase 应用中,然后发送给小组的其他成员。
现在,我们需要建立我们的控制面板,管理员可以管理组成员,也可以发送和接收消息。
我们将构建一个简单的 web 界面来管理我们的组成员。
我们为小组成员存储的数据将由以下三部分数据组成:
-
-群组电话号码(我们存储在
"Getting Started"部分的 twilio_number 变量中的 Twilio 号码) -
-成员姓名
-
-会员电话号码
我们还将显示一个基本的聊天框,让我们的管理员发送消息,并查看正在发送的消息。
首先,让我们创建视图。在/views文件夹中,创建名为index.ejs的文件:
`
HTML
<!doctype html>
<html>
<head>
<link href='//fonts.googleapis.com/css?family=Lato:400,300italic,400italic&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="//angular-ui.github.com/ng-grid/css/ng-grid.css" />
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="https://cdn.flybase.io/flybase.js?20150217"></script>
<script src="https://cdn.flybase.io/libs/phone.js"></script>
<script src="/js/group.js"></script>
<title>Group Chat, powered by Flybase and Twilio</title>
</head>
<body>
<div class='container'>
<div class="row">
<div class="col-md-6">
<h3>Group Members</h3>
<div id="group_wrapper"></div>
<hr />
<h2>Add new member</h2>
<div class="well">
<form id="group_form" method="post" accept-charset="utf-8" class="form-inline">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-pencil"></i></div>
<input type="text" class="form-control" id="name" name="name" placeholder="name">
</div>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-mobile"></i></div>
<input type="tel" class="form-control" id="phone" name="phone" placeholder="+11112223333"/>
</div>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
<div class="col-md-4 col-md-offset-1">
<div id="chatBox" class='chat'>
<header>Chat Log</header>
<ul id='messagesDiv' class='chat-messages'></ul>
<footer>
<form id="msg_form" method="post" accept-charset="utf-8" class="form-inline">
<input type="text" id="messageInput" placeholder="Type a message..." />
</form>
</footer>
</div>
</div>
</div>
<script>
$(function(){
// initialize our Flybase object
var myGroupManager = new groupManager( "<%= api_key %>", "<%= app_name %>", "<?%= group_number %>");
myGroupManager.start();
});
</script>
</body>
</html>这将显示我们的控制面板,它将分为两个窗格,左侧用于查看群组成员,右侧用于查看聊天日志。
在页面底部,我们正在初始化我们的groupManager类。我们将很快创建该文件。
接下来,让我们创建我们的样式表。在public/css文件夹中,创建名为style.css的文件:
css
body{font-size:12pt;font-family:helvetica}
.chatWindow{float:left;margin:20px;border:1px solid #000;width:300px;background:#e5e5e5;border-radius:5px}
.chatName{margin-bottom:10px;background:#666;color:#fff;padding:4px}
.messages{padding:4px}
.message_outbound{color:blue;text-align:right}
.tstamp{font-size:9px;padding:2px;margin-bottom:10px;border-bottom:1px dotted #666;color:#666}
.error{color:red;text-align:center}
.messageForm textarea{float:left;width:220px;margin:5px}
#phone{width:140px;}
#chatBox{background-color: #f8f8f8;background: rgb(229, 228, 228);margin:10px;}
.hide {display: none; }
.chat {font-family: "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;border-radius: 3px;-webkit-box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.2);box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.2);background-color: #dfe3ea;border: 1px solid #CCC;overflow: auto;padding: 0px;font-size: 18px;line-height: 22px;color: #666; }
.chat header {background-color: #EEE;background: -webkit-gradient(linear, left top, left bottom, from(#EEEEEE), to(#DDDDDD));background: -webkit-linear-gradient(top, #EEEEEE, #DDDDDD);background: linear-gradient(top, #EEEEEE, #DDDDDD);-webkit-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.9), 0px 1px 2px rgba(0, 0, 0, 0.1);box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.9), 0px 1px 2px rgba(0, 0, 0, 0.1);border-radius: 3px 3px 0px 0px;border-bottom: 1px solid #CCC;line-height: 24px;font-size: 12px;text-align: center;color: #999; }
.chat input {-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;-webkit-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.2);box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.2);border-radius: 3px;padding: 0px 10px;height: 30px;font-size: 18px;width: 100%;font-weight: normal;outline: none; }
.chat .chat-toolbar {background-color: #FFF;padding: 10px;position: relative;border-bottom: 1px solid #CCC; }
.chat .chat-toolbar label {text-transform: uppercase;line-height: 32px;font-size: 14px;color: #999;position: absolute;top: 10px;left: 20px;z-index: 1; }
.chat .chat-toolbar input {-webkit-box-shadow: none;box-shadow: none;border: 1px solid #FFF;padding-left: 100px;color: #999; }
.chat .chat-toolbar input:active, .chat .chat-toolbar input:focus {color: #1d9dff;border: 1px solid #FFF; }
.chat ul {list-style: none;margin: 0px;padding: 20px;height: 200px;overflow: auto; }
.chat ul li {margin-bottom: 10px;line-height: 24px; }
.chat ul li:last-child {margin: 0px; }
.chat ul .chat-username {margin-right: 10px; }
.chat footer {display: block;padding: 10px; }
.chat footer input {border: 1px solid #ced3db;height: 40px; width:75%;}现在,让我们来看看我们系统的大脑。在public/js文件夹中,我们将创建一个名为group.js的文件:
javascript
var groupManager = function(api_key, app_name, group_number) {
// store the group number
this.group_number = group_number;
// reference to our messages collection...
this.messagesRef = new Flybase(api_key, app_name, "messages");
// reference to our group collection...
this.groupRef = new Flybase(api_key, app_name, "groups");
this.group_members = [];
};这是我们 groupManager 课程的第一部分。到目前为止,我们已经告诉它启动两个 Flybase 引用,一个名为messagesRef,一个名为groupRef。我们还将我们的组号存储为一个名为group_number的变量。
现在,让我们开始行动:
javascript
groupManager.prototype.start = function(){
var _this = this;
// list group members if any
this.groupRef.on("value", function( data ){
if( data.count() ){
data.forEach( function( snapshot ){
var member = snapshot.value();
_this.group_members[member._id] = member;
});
}
_this.displayGroup();
});
// listen for new members being added
this.groupRef.on("added", function( snapshot ){
var member = snapshot.value();
_this.group_members[member._id] = member;
_this.displayGroup();
});
// save new group member to our app
$("#group_form").submit( function(e){
e.preventDefault();
var member = {
'groupNumber': _this.group_number,
'memberName': $("#name").val(),
'memberNumber': clean_phone( $("#phone").val() )
};
_this.groupRef.push( member );
$("#name").val('');
$("#phone").val('');
return false;
});
// listen for members being removed
$('div').on('click','a.delete', function(e){
var _id = e.target.id;
_this.groupRef.remove(_id);
return false;
});
this.groupRef.on("removed", function( snapshot ){
var member = snapshot.value();
_this.group_members[member._id] = undefined;
_this.displayGroup();
});
// list any existing chat message
this.messagesRef.on('value', function (data) {
if( data.count() ){
data.forEach( function(message){
_this.displayChatMessage(message.value() );
});
}
});
// listen for incoming chat messages
this.messagesRef.on('added', function (data) {
var message = data.value();
_this.displayChatMessage( message );
});
// listen for outgoing chat messages
$('#msg_form').submit( function(e){
e.preventDefault();
var message = {
"tstamp": new Date().toLocaleString(),
"fromName": "Admin",
"fromNumber": "",
"message": $('#messageInput').val(),
"fromCity": "",
"fromState": "",
"fromCountry": "",
"groupNumber": _this.group_number
}
_this.messagesRef.push( message );
$('#messageInput').val('');
return false;
});
};我们的函数设置了异步监听器,以及通过按下delete按钮删除表单提交和成员的监听器。
如果添加了一个组成员,那么该成员将被添加到组集合中,并且会向该组的其他成员发送一个通知。群组成员列表也将显示新成员。
如果一个人被删除,他们的名字将从列表中消失,一条消息将被发送给其余的组成员。
我们的 groupManager 类的另一面是我们程序的实际聊天面。当管理员键入一条消息时,它将被发送给其他组成员。同时,当另一个群组成员发送消息时,管理员会在聊天框中看到该消息。
我们还剩下两个功能:一个显示一个组的所有成员,另一个显示聊天消息。
对于我们的组,我们将信息存储在名为 group_members 的类范围变量中。这使我们能够在收到相关通知时快速添加、更新或删除成员:
javascript
// Display group members
groupManager.prototype.displayGroup = function(){
$('#group_wrapper').html('');
for (var i in this.group_members ) {
var member = this.group_members[i];
if( member !== undefined ){
var html = '';
html = '<span>'+member.memberName+' ( ' + member.memberNumber + ' )</span> <a href="#delete" class="delete" id="' + member._id+'">[remove]</a>';
$('<div/>').prepend( html ).appendTo($('#group_wrapper'));
}
}
};我们的最后一个功能显示收到的每条聊天消息:
javascript
// Display chat messages
groupManager.prototype.displayChatMessage = function( message ){
var _this = this;
var msg = message.message;
if( message.media !== "" ){
msg += '<br /><img src="' + message.media + '" />';
}
$('<li/>')
.attr("id",message._id)
.html(msg)
.prepend(
$("<strong class='example-chat-username' />").text(message.fromName+': ')
).appendTo( $('#messagesDiv') );
$('#messagesDiv')[0].scrollTop = $('#messagesDiv')[0].scrollHeight;
};最后一件事是启动我们的应用:
javascript
node app.js我们已经告诉我们的应用在端口 8080 上运行,所以如果你在网络浏览器上键入http://localhost:8080/,你应该会看到你的群聊。
Heroku 非常适合让服务器配置变得简单而轻松。我们可以更快地构建,并担心对我们重要的事情,而不是试图配置我们自己的服务器。这与我们在 Flybase 的理念完美契合,让我们可以快速地构建东西。让我们看看如何在几秒钟内将我们的群聊应用部署到 Heroku。
请前往 http://heroku.com 创建您的免费账户。仪表板非常简单和用户友好。
接下来,您需要安装 Heroku Toolbelt。Heroku Toolbelt 将让我们访问 Heroku 命令行实用程序。Heroku Toolbelt 程序有不同的操作系统,您可以从以下链接下载:
-
Windows (
https://devcenter.heroku.com/toolbelt-downloads/windows) -
Debian/Ubuntu (
https://devcenter.heroku.com/toolbelt-downloads/debian
安装工具带后,我们将可以使用 heroku 命令。
现在,您需要执行以下操作:
-
在您创建群组聊天应用的文件夹中,创建一个新的 git 存储库
-
登录 Heroku
-
在 Heroku 中创建应用
-
将您的群组聊天储存库推送到 Heroku
-
告诉 Heroku 创建一个 dyno(一个 worker,用来响应 web 请求)
-
heroku open在新的自定义网址打开网页浏览器
就这样。您的应用现在正在 Heroku 上运行。
现在,我们想回到 Twilio 帐户,打开我们用来发送消息的电话号码。
当你在 Heroku 上创建你的应用时,你可以给它一个唯一的 URL。例如,让我们说
T2https://my-group-chat.herokuapp.com/
我们通过短信接收信息的网址现在将变为 https://my-group-chat.herokuapp.com/message 。
现在向您的 Twilio 号码发送短信,您应该会收到回复。如果你不知道,看看 Twilio 应用监视器( www.twilio.com/user/account/developer-tools/app-monitor )来帮助确定哪里出了问题。
我们用 Flybase ( http://flybase.io )和 Twilio ( http://twilio.com )搭建了一个实时群聊 app。
这个群聊应用甚至可以处理传入的媒体(图片、Word 文档、视频等。)并将其重新发送给组中的其他人。
你可以在 GitHub ( https://github.com/flybaseio/group-chat )找到我们的群聊应用。
这个应用可以供一群人进行对话。这在参加活动时会很方便。
你可以用它来通知与会者即将到来的会谈。例如,一个会议可以将他们的与会者添加到一个组中,然后在谈话开始、午餐时间或紧急情况时发送广播。