feat: email subscribe

feat: content copy
feat: localnetroom perf
feat: user table add flag field
feat: language perf
feat: token api resopnse perf
feat: icon perf
feat: doc update
This commit is contained in:
https://blog.iamtsm.cn
2023-11-04 12:49:32 +08:00
parent 653a987bdd
commit b4f79d4da8
48 changed files with 1126 additions and 356 deletions

View File

@@ -0,0 +1 @@
# 待完善补充...

View File

@@ -40,6 +40,10 @@ chatgpt聊天框的对话上下文的简单处理。
开启设置后,你创建的房间,同一个局域网用户进入网页后将看到你创建的房间。
### 系统通知 (开发中) 【此设置刷新网页后有效,长久保存】
开启设置后,当收到消息或者提示时,会使用浏览器自带的桌面消息提示,进行及时的音效,横幅提示
### 文件分片传输大小 【此开关刷新网页后失效,需重新设置】
由于网站的文件传输是分片传输但是由于webrtc的数据传输通道有限制所以项目提供了一个合理范围的可选项用于自定义控制每次webrtc的数据通道发送数据时的分片大小。默认是 16KB最大可调整到64KB不同浏览器实现可能不同1664是我认为比较合适的可选范围

View File

@@ -22,50 +22,150 @@
* [配置简要说明](ENV_SETTING.md)
* [主要功能](FEATURE_LIST.md)
* [发送文字](FEATURE_LIST.md)
* [公共聊天](FEATURE_LIST.md)
* [群聊](FEATURE_LIST.md)
* [私聊](FEATURE_LIST.md)
* [文字复制](FEATURE_LIST.md)
* [富文本](FEATURE_LIST.md)
* [图片](FEATURE_LIST.md)
* [表情](FEATURE_LIST.md)
* [发送文件](FEATURE_LIST.md)
* [群发](FEATURE_LIST.md)
* [私发](FEATURE_LIST.md)
* [排队发](FEATURE_LIST.md)
* [同时发](FEATURE_LIST.md)
* [多文件拖拽](FEATURE_LIST.md)
* [indexedDb存储](FEATURE_LIST.md)
* [在线文件预览](FEATURE_LIST.md)
* [文件暂存](FEATURE_LIST.md)
* [自定义文件分片大小](FEATURE_LIST.md)
* [自定义文件预览大小](FEATURE_LIST.md)
* [屏幕共享](FEATURE_LIST.md)
* [多人共享](FEATURE_LIST.md)
* [屏幕开关](FEATURE_LIST.md)
* [音频开关](FEATURE_LIST.md)
* [视频通话](FEATURE_LIST.md)
* [多人视频](FEATURE_LIST.md)
* [视频开关](FEATURE_LIST.md)
* [摄像头切换](FEATURE_LIST.md)
* [音频开关](FEATURE_LIST.md)
* [开始直播](FEATURE_LIST.md)
* [视频直播](FEATURE_LIST.md)
* [屏幕直播](FEATURE_LIST.md)
* [视频开关](FEATURE_LIST.md)
* [摄像头切换](FEATURE_LIST.md)
* [音频开关](FEATURE_LIST.md)
* [开播端](FEATURE_LIST.md)
* [观众端](FEATURE_LIST.md)
* [语音连麦](FEATURE_LIST.md)
* [音频开关](FEATURE_LIST.md)
* [取件号码](FEATURE_LIST.md)
* [文件下载](FEATURE_LIST.md)
* [远程画笔](FEATURE_LIST.md)
* [多人画笔同步](FEATURE_LIST.md)
* [密码房间](FEATURE_LIST.md)
* [自定义密码](FEATURE_LIST.md)
* [屏幕录制](FEATURE_LIST.md)
* [录制文件下载](FEATURE_LIST.md)
* [chatGPT](FEATURE_LIST.md)
* [次要功能](FEATURE_LIST.md)
* [多语言版本](FEATURE_LIST.md)
* [微信扫码登录](FEATURE_LIST.md)
* [分享房间](FEATURE_LIST.md)
* [网站通知](FEATURE_LIST.md)
* [日志记录](FEATURE_LIST.md)
* [自定义日志数量限制](FEATURE_LIST.md)
* [中继设置](FEATURE_LIST.md)
* [自定义昵称](FEATURE_LIST.md)
* [在线人数统计](FEATURE_LIST.md)
* [消息红点开关](FEATURE_LIST.md)
* [固定房间号](FEATURE_LIST.md)
* [局域网房间扫描](FEATURE_LIST.md)
* [自定义socket地址](FEATURE_LIST.md)
* [后台管理统计](FEATURE_LIST.md)
* [后台开关设置](FEATURE_LIST.md)
* [敏感词过滤](FEATURE_LIST.md)
* [心跳检测](FEATURE_LIST.md)
* [webrtc连接展示](FEATURE_LIST.md)
* [tl-rtc-file-开发手册](README_DEV.md)
* [设计简介](dev/INTRO.md)
* [客户端](dev/client/CLIENT.md)
* [文字聊天](dev/client/CHAT.md)
* [文件传输](dev/client/FILE.md)
* [多人音视频](dev/client/VIDEO.md)
* [多人屏幕共享](dev/client/SCREEN.md)
* [单人直播](dev/client/LIVE.md)
* [多人语音](dev/client/AUDIO.md)
* [文件暂存/取件码](dev/client/FILE_CODE.md)
* [多人画笔](dev/client/DRAW.md)
* [屏幕录制](dev/client/RECODE.md)
* [客户端](dev/CLIENT.md)
* [服务端](dev/SVR.md)
* [文字聊天](dev/svr/CHAT.md)
* [文件传输](dev/svr/FILE.md)
* [多人音视频](dev/svr/VIDEO.md)
* [多人屏幕共享](dev/svr/SCREEN.md)
* [单人直播](dev/svr/LIVE.md)
* [多人语音](dev/svr/AUDIO.md)
* [文件暂存/取件码](dev/svr/FILE_CODE.md)
* [多人画笔](dev/svr/DRAW.md)
* [屏幕录制](dev/svr/RECODE.md)
* [tl-rtc-file-设置选项说明](SETTING.md)
* [tl-rtc-file-常见问题列表](FAQ.md)

View File

@@ -1 +1,2 @@
# 项目设计简介
# 项目简介

View File

@@ -1 +0,0 @@
# 多人语音

View File

@@ -1 +0,0 @@
# 文字聊天

View File

@@ -1 +0,0 @@
# 多人画笔

View File

@@ -1 +0,0 @@
# 文件传输

View File

@@ -1 +0,0 @@
# 文件暂存/取件码

View File

@@ -1 +0,0 @@
# 单人直播

View File

@@ -1 +0,0 @@
# 屏幕录制

View File

@@ -1 +0,0 @@
# 多人屏幕共享

View File

@@ -1 +0,0 @@
# 多人音视频

View File

@@ -1 +0,0 @@
# 多人语音

View File

@@ -1 +0,0 @@
# 文字聊天

View File

@@ -1 +0,0 @@
# 多人画笔

View File

@@ -1 +0,0 @@
# 文件传输

View File

@@ -1 +0,0 @@
# 文件暂存/取件码

View File

@@ -1 +0,0 @@
# 单人直播

View File

@@ -1 +0,0 @@
# 屏幕录制

View File

@@ -1 +0,0 @@
# 多人屏幕共享

View File

@@ -1 +0,0 @@
# 多人音视频

View File

@@ -1,5 +1,5 @@
{
"version": "10.5.0",
"version": "10.5.1",
"socket": {
"port": "请到 tlrtcfile.env 中进行配置",
"host": "请到 tlrtcfile.env 中进行配置"

View File

@@ -5,6 +5,7 @@ const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
const wxapi = require("./../../bussiness/wxapi/wxapi")
const { CookieKey } = require("./../../bussiness/cache/key")
const scanCache = require("./../../bussiness/cache/scan/scanCache")
const utils = require("../../utils/utils");
const uuid = require("uuid")
// 扫码状态
@@ -39,13 +40,14 @@ async function loginWechat(req, res) {
const { session_key, openid, unionid } = openIdInfo;
const userId = await user.addWxUser({
const { userId, flag } = await user.addWxUser({
openid: openid,
avatar: userInfo.avatarUrl,
uname: userInfo.nickName,
pwd: '',
solt: '',
role: 'user',
flag : 0,
}, tables, dbClient);
//设置登录信息缓存
@@ -55,6 +57,7 @@ async function loginWechat(req, res) {
userId : userId,
avatar : userInfo.avatarUrl,
nickName : userInfo.nickName,
flag : flag,
loginTime : Date.now(),
});
@@ -226,10 +229,15 @@ async function getTokenState(req, res){
const loginInfo = scanCache.getLoginInfo(token);
const avatar = loginInfo.avatar || "/image/44826979.png";
const username = loginInfo.nickName || "";
const username = loginInfo.nickName || " -- ";
const flag = loginInfo.flag || 0;
const subscribeNotify = utils.checkBit(flag, req.ctx.tables.UserOther.Flag.IS_SUBSCRIBE_WEBSITE_NOTIFY)
if(Object.keys(loginInfo).length > 0){
res.json({ code: 200, login: true, token, avatar, username });
res.json({
code: 200, login: true, loginInfo,
token, avatar, username, subscribeNotify
});
}else{
res.json({ code: 200, login: false });
}
@@ -276,7 +284,10 @@ async function getLoginInfo(req, res){
const loginInfo = scanCache.getLoginInfo(token);
res.json({ code: 200, userId : loginInfo.userId });
res.json({ code: 200, userInfo :{
userId : loginInfo.userId,
flag : loginInfo.flag,
}});
}

View File

@@ -39,14 +39,14 @@ async function addWxUser(params, tables, dbClient) {
role: params.role,
});
return data && data.dataValues ? data.dataValues.id : 0;
return data && data.dataValues ? dataValues : { id : 0 };
}
if(users && users.length === 1){
return users[0].dataValues.id;
return users[0].dataValues;
}
return 0;
return { id : 0 };
}catch(e){
console.error(e);
return {};
@@ -54,11 +54,46 @@ async function addWxUser(params, tables, dbClient) {
}
/**
* 更新用户标识
* @param {*} params
* @param {*} tables
* @param {*} dbClient
*/
async function updateUserFlag(params, tables, dbClient){
if(!tables || !dbClient){
return {};
}
if(!params){
params = {};
}
if(!params.id){
return {};
}
let data = await tables.User.update({
flag: params.flag,
}, {
where: {
id: params.id
}
});
utils.tlConsole("更新用户标识 : ", params, data)
return data;
}
module.exports = dbOpen ? {
addWxUser
addWxUser, updateUserFlag
} : {
addWxUser : function(){
return {}
},
updateUserFlag : function(){
return {}
}
}

View File

@@ -20,6 +20,7 @@ const rtcHeartbeat = require("./rtcHeartbeat/heartbeat");
const rtcAddCodeFile = require("./rtcCodeFile/addCodeFile");
const rtcGetCodeFile = require("./rtcCodeFile/getCodeFile");
const rtcLocalNetRoom = require("./rtcLocalNetRoom/localNetRoom");
const rtcSubscribe = require("./rtcSubscribe/subscribe");
const rtcServerEvent = require("./rtcConstant").rtcServerEvent
const rtcToken = require("./rtcToken/token")
@@ -32,11 +33,6 @@ module.exports = (io, socket, tables, dbClient) => {
// 在线人数统计
rtcCount.count(io, socket, tables, dbClient, {})
// 局域网房间发现列表
rtcLocalNetRoom.localNetRoom(io, socket, tables, dbClient, {
toCurrentSocket : true
})
// 断开连接
socket.on(rtcServerEvent.disconnect, (data)=>{
rtcDisConnect.disconnect(io, socket, tables, dbClient, data)
@@ -141,4 +137,14 @@ module.exports = (io, socket, tables, dbClient) => {
socket.on(rtcServerEvent.changeNickName, (data) => {
rtcChangeNickName.changeNickName(io, socket, tables, dbClient, data)
});
// 订阅网站通知
socket.on(rtcServerEvent.subscribeNofity, (data) => {
rtcSubscribe.subscribeNofity(io, socket, tables, dbClient, data)
});
// 局域网房间发现列表
socket.on(rtcServerEvent.localNetRoom, (data)=>{
rtcLocalNetRoom.localNetRoomForConnect(io, socket, tables, dbClient, data)
});
}

View File

@@ -48,6 +48,10 @@ const rtcServerEvent = {
heartbeat : "heartbeat",
//修改昵称
changeNickName : "changeNickName",
//订阅网站通知
subscribeNofity : "subscribeNofity",
//局域网房间发现列表
localNetRoom : "localNetRoom",
}
/**
@@ -84,7 +88,7 @@ let rtcServerMessageEvent = {
//开始远程画笔
startRemoteDraw : "startRemoteDraw",
//结束远程画笔
stopRemoteDraw : "stopRemoteDraw",
stopRemoteDraw : "stopRemoteDraw"
}
/**
@@ -134,6 +138,8 @@ let rtcClientEvent = {
changeNickName : "changeNickName",
//局域网房间发现列表
localNetRoom : "localNetRoom",
//订阅网站通知
subscribeNofity : "subscribeNofity"
}
/**

View File

@@ -1,7 +1,6 @@
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const utils = require("../../../src/utils/utils");
const rtcLocalNetRoom = require("../rtcLocalNetRoom/localNetRoom");
/**
* 在线人数统计广播
@@ -18,11 +17,6 @@ async function count(io, socket, tables, dbClient, data){
io.sockets.emit(rtcClientEvent.count, {
mc : allManCount
})
rtcLocalNetRoom.localNetRoom(io, socket, tables, dbClient, {
toAll : true
});
}catch(e){
utils.tlConsole(e)
}

View File

@@ -26,7 +26,7 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
let {
room = '', type = 'file', nickName = '', password = '',
langMode = 'zh', ua = '', network = '', liveShareRole = '',
localNetRoom = false
localNetRoom = false, ips = []
} = data;
if (room && room.length > 15) {
@@ -66,7 +66,7 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
io.sockets.connected[socket.id].langMode = langMode;
io.sockets.connected[socket.id].ua = ua;
io.sockets.connected[socket.id].network = network;
io.sockets.connected[socket.id].ip = ip;
io.sockets.connected[socket.id].ips = ips;
const joinTime = utils.formateDateTime(new Date(), "yyyy-MM-dd hh:mm:ss")
io.sockets.connected[socket.id].joinTime = joinTime;
io.sockets.connected[socket.id].userAgent = userAgent;
@@ -120,26 +120,25 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
});
//设置为房主
io.sockets.connected[socket.id].owner = true;
io.sockets.adapter.rooms[room].owner = socket.id;
//设置房主ip为房间号ip, address
io.sockets.adapter.rooms[room].ip = ip;
io.sockets.adapter.rooms[room].address = address;
//房间是否可以被局域网发现
io.sockets.adapter.rooms[room].localNetRoom = localNetRoom;
if(localNetRoom){
rtcLocalNetRoom.localNetRoom(io, socket, tables, dbClient, {
toAll : true
});
}
//设置房主上报的ip为房间号ip
io.sockets.adapter.rooms[room].ips = ips;
//设置房间类型
io.sockets.adapter.rooms[room].type = type;
//房间是否可以被局域网发现
io.sockets.adapter.rooms[room].localNetRoom = localNetRoom;
//密码房间设置密码
if(type === 'password'){
io.sockets.adapter.rooms[room].password = password
}
//房间创建时间
io.sockets.adapter.rooms[room].createTime = utils.formateDateTime(new Date(), "yyyy-MM-dd hh:mm:ss");
}else {
//加入时,房间类型不匹配,提示并退出
const createdRoomType = io.sockets.adapter.rooms[room].type;
@@ -244,9 +243,11 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
});
}
//人数变更通知
rtcCount.count(io, socket, tables, dbClient, data);
io.sockets.adapter.rooms[room].createTime = utils.formateDateTime(new Date(), "yyyy-MM-dd hh:mm:ss")
//局域网房间变动通知
rtcLocalNetRoom.localNetRoomForJoin(io, socket, tables, dbClient, { room });
if(password && password.length > 0){
bussinessNotify.sendCreateJoinPasswordRoomNotify({

View File

@@ -1,7 +1,8 @@
const daoRoom = require("./../../dao/room/room")
const rtcCount = require("./../rtcCount/count");
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const rtcClientEvent = rtcConstant.rtcClientEvent;
const rtcLocalNetRoom = require("../rtcLocalNetRoom/localNetRoom");
/**
* 断开连接的操作
@@ -18,7 +19,7 @@ async function disconnect(io, socket, tables, dbClient, data){
await daoRoom.exitRoomBySid({ socket_id: socket.id }, tables, dbClient);
rtcCount.count(io, socket, tables, dbClient, data)
rtcCount.count(io, socket, tables, dbClient, data);
}
module.exports = {

View File

@@ -3,8 +3,8 @@ const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
const rtcCount = require("./../rtcCount/count");
const utils = require("./../../utils/utils");
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const rtcClientEvent = rtcConstant.rtcClientEvent;
const rtcLocalNetRoom = require("../rtcLocalNetRoom/localNetRoom");
/**
* 退出房间
@@ -48,6 +48,9 @@ async function exit(io, socket, tables, dbClient, data){
rtcCount.count(io, socket, tables, dbClient, data);
//局域网房间变动通知
rtcLocalNetRoom.localNetRoomForExit(io, socket, tables, dbClient, { room });
} catch (e) {
socket.emit(rtcClientEvent.tips, {
room: data.room,

View File

@@ -2,134 +2,46 @@ const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const bussinessNotify = require("../../bussiness/notify/notifyHandler")
const utils = require("../../../src/utils/utils");
const os = require('os');
/**
* 局域网房间发现列表
*
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* 服务器网络列表
*/
let serverNetworkList = null;
(() => {
if (!serverNetworkList) {
serverNetworkList = [];
let ifaces = os.networkInterfaces();
for (let iface in ifaces) {
for (let i = 0, len = ifaces[iface].length; i < len; ++i) {
const item = ifaces[iface][i];
if (item.family === 'IPv4') {
serverNetworkList.push(ifaces[iface][i])
}
}
}
}
})()
console.log(serverNetworkList)
/**
* 通过ip获取子网掩码
* @param {*} ip
* @returns
*/
async function localNetRoom(io, socket, tables, dbClient, data){
try{
const { toCurrentSocket = false, toAll = false } = data;
let { ip, address } = utils.getSocketClientInfo(socket);
let roomList = [];
let rooms = io.sockets.adapter.rooms;
for(let roomId in rooms){
if(roomId.length > 15){
continue;
function getNetMaskByIp(ip) {
if (!serverNetworkList) {
return "255.255.255.255";
}
//最多返回10个房间
if(roomList.length > 10){
break;
}
// 是否开启局域网房间
const localNetRoom = rooms[roomId].localNetRoom || false;
if(!localNetRoom){
continue;
}
// 房间ip
const roomIp = rooms[roomId].ip || "";
// 房间address/ip
const roomAddress = rooms[roomId].address || "";
if(roomIp){
// 本地请求
if(
(roomIp.indexOf("127.0.0.1") > -1 && ip.indexOf("127.0.0.1") > -1)
||
(roomIp.indexOf("localhost") > -1 && ip.indexOf("localhost") > -1)
){
roomList = addFilterRoomListData(roomList, {
room : roomId,
type : 'file',
ips : [roomIp],
count : rooms[roomId].length
})
}else{
// 在同一个网段
if(utils.isSameSubnet(roomIp, ip, "255.255.255.255")){
roomList = addFilterRoomListData(roomList, {
room : roomId,
type : 'file',
ips : [roomIp],
count : rooms[roomId].length
})
}
}
}
if(roomAddress){
// 本地请求
if(
(roomAddress.indexOf("127.0.0.1") > -1 && address.indexOf("127.0.0.1") > -1)
||
(roomAddress.indexOf("localhost") > -1 && address.indexOf("localhost") > -1)
){
roomList = addFilterRoomListData(roomList, {
room : roomId,
type : 'file',
ips : [roomAddress],
count : rooms[roomId].length
})
}else{
// 在同一个网段
if(utils.isSameSubnet(roomAddress, address, "255.255.255.255")){
roomList = addFilterRoomListData(roomList, {
room : roomId,
type : 'file',
ips : [roomAddress],
count : rooms[roomId].length
})
}
}
}
}
roomList.map(item=>{
item.room = item.room.toString().substring(0,1) + "***";
})
// 通知当前用户
if(toCurrentSocket){
socket.emit(rtcClientEvent.localNetRoom, {
list : roomList
})
}
// 通知全部用户
if(toAll){
io.sockets.emit(rtcClientEvent.localNetRoom, {
list : roomList
});
}
}catch(e){
utils.tlConsole(e)
bussinessNotify.sendSystemErrorMsg({
title: "socket-localNetRoom",
data: JSON.stringify(data),
room: "",
from : socket.id,
msg : JSON.stringify({
message: e.message,
fileName: e.fileName,
lineNumber: e.lineNumber,
stack: e.stack,
name: e.name
}, null, '\t')
const filterList = serverNetworkList.filter(item => {
return item.address === ip;
})
if (!filterList || filterList.length === 0) {
return "255.255.255.255";
}
return filterList[0].netmask;
}
@@ -156,6 +68,245 @@ function addFilterRoomListData(list, obj){
}
module.exports = {
localNetRoom
/**
* 局域网房间发现列表
* 连接类型广播
*
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
function localNetRoom(io, socket, tables, dbClient, data) {
const { ips : currentIps = [] } = data;
let filterRoomList = [];
//所有正常的房间列表ip统计
let rooms = io.sockets.adapter.rooms;
let roomIpsList = [];
for (let roomId in rooms) {
let roomIps = rooms[roomId].ips;
if (
roomId.length > 15 || // 非法房间号
!rooms[roomId] || // 房间不存在
!roomIps || // 房间没有上报ip
roomIps.length === 0 || // 房间没有上报ip
rooms[roomId].length === 0 || // 房间没有人
!rooms[roomId].localNetRoom // 房间没有开启局域网
) {
continue;
}
//最多处理返回10个局域网房间
if (roomIpsList.length > 10) {
break;
}
roomIpsList.push(...roomIps.map(item => {
item.room = roomId;
item.count = rooms[roomId] ? Object.keys(rooms[roomId].sockets).length : 0,
item.owner = rooms[roomId].owner;
item.roomType = rooms[roomId].type;
return item;
}))
}
//根据当前socket连接的ip信息筛选出和ip在同一个局域网的房间列表
currentIps.forEach(currentIpInfo => {
const { ipType: currentIpType, address: currentAddress } = currentIpInfo;
roomIpsList.forEach(otherIpInfo => {
const { ipType: otherIpType, address: otherAddress, room, count, owner, roomType } = otherIpInfo;
if (otherIpType === "srflx" && currentIpType === 'srflx') { //公网ip
if (utils.isSameSubnet(currentAddress, otherAddress, getNetMaskByIp(currentAddress))) {
filterRoomList = addFilterRoomListData(filterRoomList, {
room, roomType, count, owner, ips: [otherAddress]
})
}
} else if (otherIpType === 'host' && currentIpType === 'host') { //内网ip
if (utils.isSameSubnet(currentAddress, otherAddress, getNetMaskByIp(currentAddress))) {
filterRoomList = addFilterRoomListData(filterRoomList, {
room, roomType, count, owner, ips: [otherAddress]
})
}
}
})
});
// filterRoomList.map(item => {
// item.room = item.room.toString().substring(0, 1) + "***";
// })
return filterRoomList;
}
/**
* 局域网房间发现列表
* socket用户连接之后广播
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
function localNetRoomForConnect(io, socket, tables, dbClient, data) {
try {
let filterRoomList = localNetRoom(io, socket, tables, dbClient, data);
socket.emit(rtcClientEvent.localNetRoom, {
list: filterRoomList,
mode : 'connect'
});
} catch (e) {
utils.tlConsole(e)
bussinessNotify.sendSystemErrorMsg({
title: "socket-localNetRoomForConnect",
data: JSON.stringify(data),
room: "",
from: socket.id,
msg: JSON.stringify({
message: e.message,
fileName: e.fileName,
lineNumber: e.lineNumber,
stack: e.stack,
name: e.name
}, null, '\t')
})
}
}
/**
* 局域网房间发现列表
* socket用户加入房间之后广播
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
function localNetRoomForJoin(io, socket, tables, dbClient, data) {
try {
const { room } = data;
const socketRoomInfo = io.sockets.adapter.rooms[room];
const ips = socketRoomInfo ? socketRoomInfo.ips : [];
const owner = socketRoomInfo ? socketRoomInfo.owner : socket.id;
const count = socketRoomInfo ? Object.keys(socketRoomInfo.sockets).length : 0;
const roomType = socketRoomInfo ? socketRoomInfo.type : "file";
io.sockets.emit(rtcClientEvent.localNetRoom, {
list : [{ room, roomType, count, owner, ips : ips.map(item => item.address) }],
mode : 'join'
});
} catch (e) {
utils.tlConsole(e)
bussinessNotify.sendSystemErrorMsg({
title: "socket-localNetRoomForJoin",
data: JSON.stringify(data),
room: "",
from: socket.id,
msg: JSON.stringify({
message: e.message,
fileName: e.fileName,
lineNumber: e.lineNumber,
stack: e.stack,
name: e.name
}, null, '\t')
})
}
}
/**
* 局域网房间发现列表
* socket用户退出房间之后广播
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
function localNetRoomForExit(io, socket, tables, dbClient, data) {
try {
const { room } = data;
const socketRoomInfo = io.sockets.adapter.rooms[room];
const ips = socketRoomInfo ? socketRoomInfo.ips : [];
const owner = socketRoomInfo ? socketRoomInfo.owner : socket.id;
const count = socketRoomInfo ? Object.keys(socketRoomInfo.sockets).length : 0;
const roomType = socketRoomInfo ? socketRoomInfo.type : "file";
io.sockets.emit(rtcClientEvent.localNetRoom, {
list : [{ room, roomType, count, owner, ips: ips.map(item => item.address) }],
mode : 'exit'
});
} catch (e) {
utils.tlConsole(e)
bussinessNotify.sendSystemErrorMsg({
title: "socket-localNetRoomForExit",
data: JSON.stringify(data),
room: "",
from: socket.id,
msg: JSON.stringify({
message: e.message,
fileName: e.fileName,
lineNumber: e.lineNumber,
stack: e.stack,
name: e.name
}, null, '\t')
})
}
}
/**
* 局域网房间发现列表
* socket用户断开连接之后广播
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
function localNetRoomForDisconnect(io, socket, tables, dbClient, data) {
try {
io.sockets.emit(rtcClientEvent.localNetRoom, {
list : [{ socketId : socket.id }],
mode : 'disconnect'
});
} catch (e) {
utils.tlConsole(e)
bussinessNotify.sendSystemErrorMsg({
title: "socket-localNetRoomForDisconnect",
data: JSON.stringify(data),
room: "",
from: socket.id,
msg: JSON.stringify({
message: e.message,
fileName: e.fileName,
lineNumber: e.lineNumber,
stack: e.stack,
name: e.name
}, null, '\t')
})
}
}
module.exports = {
localNetRoomForConnect, localNetRoomForJoin,
localNetRoomForExit, localNetRoomForDisconnect
}

View File

@@ -0,0 +1,57 @@
const utils = require("./../../utils/utils");
const daoDog = require("./../../dao/dog/dog")
const daoUser = require("./../../dao/user/user")
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const daoRelation = require("../../dao/relation/relation");
/**
* 订阅网站通知
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
async function subscribeNofity(io, socket, tables, dbClient, data){
let { roomId = '', email = '' } = data;
let { handshake, userAgent, ip } = utils.getSocketClientInfo(socket);
socket.emit(rtcClientEvent.subscribeNofity, data);
//控制操作事件入库
let recoderId = await daoDog.addDogData({
name: "订阅网站通知",
roomId: roomId,
socketId: socket.id,
device: userAgent,
flag: 0,
content: JSON.stringify(data),
handshake: JSON.stringify(handshake),
ip: ip
}, tables, dbClient);
//更新用户订阅标识
const { userId, flag = 0 } = socket.userInfo;
if(userId){
await daoUser.updateUserFlag({
id : userId,
flag : utils.setBit(flag, tables.UserOther.Flag.IS_SUBSCRIBE_WEBSITE_NOTIFY)
}, tables, dbClient);
}
//添加用户-操作关联记录
if(socket.userId){
daoRelation.addUserDogRelation({
dogId : recoderId,
userId : socket.userId,
}, tables, dbClient);
}
}
module.exports = {
subscribeNofity
}

View File

@@ -29,7 +29,7 @@ async function token(io, socket, tables, dbClient, data){
"content-type": "application/json",
},
qs: {
token, key : "iamtsm-socket"
token, key : cfg.login.token.key
},
}, (err, res, body) => {
if(err){
@@ -42,9 +42,9 @@ async function token(io, socket, tables, dbClient, data){
return;
}
socket.userId = body.userId;
socket.userInfo = body.userInfo;
utils.tlConsole("同步token信息成功 : ", token, body.userId)
utils.tlConsole("同步token信息成功 : ", token, body.userInfo)
});
}

View File

@@ -1,6 +1,11 @@
// user
module.exports = (sequelize, DataTypes) => {
return {
UserOther : {
Flag : {
IS_SUBSCRIBE_WEBSITE_NOTIFY : 0x1, //是否已订阅网站通知
}
},
User: sequelize.define('user', {
id: {
type: DataTypes.INTEGER,
@@ -36,6 +41,11 @@ module.exports = (sequelize, DataTypes) => {
role: {
type: DataTypes.STRING(15),
comment: '用户身份'
},
flag: {
type: DataTypes.INTEGER,
comment: '标志位',
defaultValue: 0,
}
}, {
timestamps: true,

View File

@@ -49,7 +49,7 @@ function getLocalIP() {
function isSameSubnet(ip1, ip2, subnetMask) {
// 将IPv4或IPv6地址和子网掩码转换为数字形式
function ipToNumber(ip) {
if (ip.includes(':')) { // IPv6
if (ip.indexOf(':') > -1) { // IPv6
const parts = ip.split(':');
return parts.map(part => parseInt(part, 16)).join('');
} else { // IPv4
@@ -374,6 +374,15 @@ function checkBit(flag, bit){
return (flag & bit) === bit;
}
/**
* 根据flag和bit设置对应的值
* @param {*} flag
* @param {*} bit
*/
function setBit(flag, bit){
return flag | bit;
}
module.exports = {
@@ -392,5 +401,6 @@ module.exports = {
unescapeStr,
escapeStr,
isSameSubnet,
checkBit
checkBit,
setBit
}

View File

@@ -54,6 +54,24 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe635;</span>
<div class="name">通知</div>
<div class="code-name">&amp;#xe635;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe702;</span>
<div class="name">群蜂操作记录</div>
<div class="code-name">&amp;#xe702;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6af;</span>
<div class="name">订阅</div>
<div class="code-name">&amp;#xe6af;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe604;</span>
<div class="name">置顶</div>
@@ -738,9 +756,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1698150656017') format('woff2'),
url('iconfont.woff?t=1698150656017') format('woff'),
url('iconfont.ttf?t=1698150656017') format('truetype');
src: url('iconfont.woff2?t=1698825191464') format('woff2'),
url('iconfont.woff?t=1698825191464') format('woff'),
url('iconfont.ttf?t=1698825191464') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -766,6 +784,33 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-rtc-file-tongzhi"></span>
<div class="name">
通知
</div>
<div class="code-name">.icon-rtc-file-tongzhi
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-rtc-file-qunfengcaozuojilu"></span>
<div class="name">
群蜂操作记录
</div>
<div class="code-name">.icon-rtc-file-qunfengcaozuojilu
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-rtc-file-dingyue"></span>
<div class="name">
订阅
</div>
<div class="code-name">.icon-rtc-file-dingyue
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-rtc-file-zhiding"></span>
<div class="name">
@@ -1792,6 +1837,30 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-rtc-file-tongzhi"></use>
</svg>
<div class="name">通知</div>
<div class="code-name">#icon-rtc-file-tongzhi</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-rtc-file-qunfengcaozuojilu"></use>
</svg>
<div class="name">群蜂操作记录</div>
<div class="code-name">#icon-rtc-file-qunfengcaozuojilu</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-rtc-file-dingyue"></use>
</svg>
<div class="name">订阅</div>
<div class="code-name">#icon-rtc-file-dingyue</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-rtc-file-zhiding"></use>

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4147343 */
src: url('iconfont.woff2?t=1698150656017') format('woff2'),
url('iconfont.woff?t=1698150656017') format('woff'),
url('iconfont.ttf?t=1698150656017') format('truetype');
src: url('iconfont.woff2?t=1698825191464') format('woff2'),
url('iconfont.woff?t=1698825191464') format('woff'),
url('iconfont.ttf?t=1698825191464') format('truetype');
}
.iconfont {
@@ -13,6 +13,18 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-rtc-file-tongzhi:before {
content: "\e635";
}
.icon-rtc-file-qunfengcaozuojilu:before {
content: "\e702";
}
.icon-rtc-file-dingyue:before {
content: "\e6af";
}
.icon-rtc-file-zhiding:before {
content: "\e604";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,27 @@
"css_prefix_text": "icon-rtc-file-",
"description": "",
"glyphs": [
{
"icon_id": "10906513",
"name": "通知",
"font_class": "tongzhi",
"unicode": "e635",
"unicode_decimal": 58933
},
{
"icon_id": "423509",
"name": "群蜂操作记录",
"font_class": "qunfengcaozuojilu",
"unicode": "e702",
"unicode_decimal": 59138
},
{
"icon_id": "28108444",
"name": "订阅",
"font_class": "dingyue",
"unicode": "e6af",
"unicode_decimal": 59055
},
{
"icon_id": "8224818",
"name": "置顶",

View File

@@ -213,7 +213,7 @@ body {
border-radius: 15px;
color: white;
background: #7187e685;
transition: font-size 0.3s;
transition: all .4s;
}
.tl-rtc-file-tool-list .swiper-slide {
@@ -1054,18 +1054,11 @@ body {
margin-bottom: 10px;
}
.tl-rtc-file-login-user{
margin-top: 20px;
margin-bottom: 20px;
width: 80%;
margin-left: 10%;
}
.tl-rtc-file-login-user-info{
margin-top: 20px;
margin-bottom: 20px;
width: 80%;
margin-left: 10%;
.tl-rtc-file-login-user, .tl-rtc-file-login-user-info{
margin: 10px;
text-align: center;
max-height: 250px;
overflow: hidden;
}
.tl-rtc-file-login-user-info-avatar{
@@ -1075,8 +1068,10 @@ body {
}
.tl-rtc-file-login-user-info-avatar img{
width: 100%;
width: 50%;
border-radius: 50%;
box-shadow: #c2b6b63b 0 3px 10px 0px;
cursor: pointer;
}
#tl-rtc-file-user-help{
@@ -1096,16 +1091,36 @@ body {
-webkit-box-orient: vertical;
}
.tl-rtc-file-logout{
width: 60%;
margin-left: 10%;
bottom: 20px;
.tl-rtc-file-login-user-btn{
width: 80%;
position: absolute;
margin-left: 8%;
}
.tl-rtc-file-logout button{
width: 100%;
margin-bottom: 10px;
border-radius: 8px;
background: #79b0e8;
}
.tl-rtc-file-user-oper{
display: inline-flex;
align-items: center;
margin-top: 20px;
margin-bottom: 20px;
}
.tl-rtc-file-user-oper svg{
font-size: 32px;
margin: 5px;
cursor: pointer;
border-radius: 50%;
padding: 5px;
transition: all .3s;
}
.tl-rtc-file-user-oper svg:hover{
background: #f1f1f1;
}
.isMediaing{

View File

@@ -71,11 +71,6 @@
<use xlink:href="#icon-rtc-file-gongzhonghao"></use>
</svg>
</a>
<a :title="lang.relay_on" id="useTurn" v-show="switchData.openUseTurnIcon && useTurn" @click="useTurnMsg">
<svg class="icon" aria-hidden="true" style="width: 20px;height: 20px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-yunfuwuqi"></use>
</svg>
</a>
<a :title="lang.other_language" id="language">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;margin-right: 10px;">
<use xlink:href="#icon-rtc-file-duoyuyan"></use>
@@ -1082,7 +1077,7 @@
<div class="layui-col-sm2" style="width: 100%;">
<div class="layui-card">
<div class="layui-card-header">
{{lang.log}} <b v-show="isCloseLogs" style="color: red;">{{lang.off}}</b> - {{lang.total}}:{{filterLogs.length}}
{{lang.log}} <b v-show="!useLogOutput" style="color: red;">{{lang.off}}</b> - {{lang.total}}:{{filterLogs.length}}
<i @click="clickLogs" class="layui-icon layui-icon-close"
style="cursor: pointer; right: 10px;position: absolute;"></i>
</div>

View File

@@ -48,6 +48,9 @@ window.tlrtcfile = {
containSymbol: function (str) {
return new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>《》/?~@#¥……&*()——|{}【】‘;:”“'。,、?]").test(str)
},
isEmail : function(str){
return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
},
genNickNameRandom: function () {
// 获取指定范围内的随机数
function randomAccess(min, max) {
@@ -435,15 +438,13 @@ window.tlrtcfile = {
return await getRTCStats(peerConnection);
},
copyTxt: function (id, content) {
copyTxt: function (id, content, callback) {
let that = this;
document.querySelector("#" + id).setAttribute("data-clipboard-text", content);
let clipboard = new ClipboardJS('#' + id);
clipboard.on('success', function (e) {
e.clearSelection();
if (window.layer) {
layer.msg("复制内容成功!")
}
callback && callback();
});
},
getQrCode: function (id, content) {
@@ -483,23 +484,23 @@ window.tlrtcfile = {
callback("keyup", event)
})
},
getRoomTypeZh: function (type){
getRoomType: function (type){
if(type === 'file'){
return "文件房间"
return "file_room_type"
}else if(type === 'live'){
return "直播房间"
return "live_room_type"
}else if(type === 'video'){
return "音视频房间"
return "video_room_type"
}else if(type === 'screen'){
return "屏幕共享房间"
return "screen_room_type"
}else if(type === 'password'){
return "密码房间"
return "password_room_type"
}else if(type === 'audio'){
return "语音连麦房间"
return "audio_room_type"
}else if(type === 'system'){
return "系统房间"
return "system_room_type"
}else{
return "未知类型房间"
return "unknown_room_type"
}
},
scrollToBottom: function (dom, duration, timeout) {
@@ -551,8 +552,8 @@ window.tlrtcfile = {
animateMargin()
}, timeout);
},
genNickName: function () {
let { adjectives, nouns } = this.nameDatabase()
genNickName: function (language) {
let { adjectives, nouns } = language === 'zh' ? this.nameDatabase() : this.nameDatabaseEn()
let adjectiveIndex = Math.floor(Math.random() * adjectives.length);
let nounIndex = Math.floor(Math.random() * nouns.length);
let adjective = adjectives[adjectiveIndex];
@@ -618,6 +619,62 @@ window.tlrtcfile = {
]
return { adjectives, nouns }
},
nameDatabaseEn: function () {
const adjectives = [
"Humorous", "Funny", "Crazy", "Strange", "Quirky", "Boring", "Mysterious", "Magical", "Witty", "Playful",
"Clever", "Beautiful", "Cute", "Charming", "Cool", "Adorable", "Stylish", "Majestic", "Fierce", "Radiant",
"Clever", "Mischievous", "Tiny", "Delicate", "Tender", "Soft", "Friendly", "Humble", "Reserved", "Proud",
"Narcissistic", "Romantic", "Innocent", "Passionate", "Persistent", "Cold", "Spoiled", "Naive", "Passionate", "Mature",
"Melancholic", "Neurotic", "Lonely", "Nostalgic", "Fresh", "Elegant", "Elegant", "Aloof", "Sassy", "Rebellious",
"Irritable", "Violent", "Alluring", "Crafty", "Confident", "Insecure", "Pessimistic", "Optimistic", "Brave", "Timid", "Joyful",
"Painful", "Kind", "Evil", "Profound", "Sacred", "Plump", "Slender", "Obese", "Slender", "Handsome", "Ugly",
"Fragrant", "Stinky", "Passionate", "Indifferent", "Vibrant", "Clean", "Dirty", "Carefree", "Moody",
"Ordinary", "Extraordinary", "Shy", "Warm-hearted", "Witty", "Agile", "Dull", "Intelligent", "Ignorant", "Sincere", "Hypocritical",
"Frank", "Cautious", "Bold", "Humble", "Arrogant", "Serious", "Relaxed", "Anxious", "Hardworking", "Lazy", "Punctual",
"Late", "Strong", "Weak", "Intelligent", "Stupid", "Smart", "Clever", "Mischievous", "Naughty", "Lively", "Silent",
"Healthy", "Unhealthy", "Tall", "Short", "Long", "Short", "Fat", "Thin", "Happy", "Unhappy", "Sweet", "Bitter",
"Astute", "Foolish", "Intelligent", "High IQ", "Resourceful", "Clumsy", "Calm", "Impulsive", "Steady", "Frivolous",
"Gentle", "Rough", "Learner", "Hates Learning", "Eater", "Picky Eater", "Patient", "Impatient", "Friendly", "Aloof", "Tolerant",
"Stubborn", "Cautious", "Kind", "Malicious", "Calm", "Hysterical", "Opportunistic", "Pessimistic", "Optimistic", "Open-minded", "Narrow-minded",
"Loyal", "Untrustworthy", "Charming", "Boring", "Thoughtful", "Boring", "Strategic", "Shortsighted", "Understanding", "Selfish", "Frank",
"Curious", "Insensitive", "Social", "Reclusive", "Talkative", "Quiet", "Thoughtful", "Quick-witted", "Emotionally Rich", "Kind-hearted", "Confident",
"Innocent", "Pursuing Perfection", "Energetic", "Adventurous", "Creative", "Calm", "Goal-oriented", "Gentle", "Helpful",
"Smart and Quick-witted", "Loyal", "Quick-witted", "Generous", "Graceful and Multifaceted", "Fashionable", "Easy-going", "Elegant and Graceful",
"Resilient", "Independent", "Outgoing", "Introverted", "Passionate and Focused", "Energetic", "Humerous", "Thoughtful and Kind", "Sacrificing",
"Decisive", "Curious", "Warm-hearted", "Enthusiastic", "Lonely and Sad", "Romantic and Passionate", "Smiling", "Unrestrained", "Silly", "Carefree",
"Lazy", "Boring", "Low-key", "Sensitive", "Cold", "Focused", "Scornful", "Passionate", "Loyal", "Mysterious", "Proud", "Free", "Artistic",
"Fashionable", "Generous", "Talented", "Elegant", "Sunny", "Witty", "Naive and Romantic", "Cheerful", "Reserved and Calm", "Diligent", "Lazy",
"Responsible", "Desiring Freedom", "Emotional", "Rational", "Insecure", "Seeking Security", "Emotional", "Optimistic", "Pessimistic", "Realistic", "Idealistic",
"Approachable", "Arrogant", "Valuing Family", "Valuing Friendship", "Valuing Love", "Compassionate", "Sense of Justice", "Compassionate", "Childlike", "Confident", "Timid",
"Talkative", "Reserved", "Outgoing", "Introverted", "Curious", "Not Understanding Others", "Sociable", "Loves Being Alone", "Self-aware", "Loves Music", "Loves Reading",
"Loves Traveling", "Loves Food", "Loves Sports", "Loves Photography", "Loves Collecting", "Loves Shopping", "Hates Going Out", "Picky", "Self-reflective", "Doesn't Judge Others",
"Judges Others", "Tasteful", "Unhygienic", "Hygienic", "Loves Cleanliness", "Loves Chaos", "Loves Organization", "Loves Casualness", "Loves Tidiness"
];
const nouns = [
'Puppy', 'Kitten', 'Fawn', 'Bear Cub', 'Bunny', 'Lamb', 'Piglet', 'Foal', 'Little Lion', 'Little Tiger',
'Little Monkey', 'Little Fish', 'Little Turtle', 'Little Bird', 'Little Ant', 'Little Bee', 'Little Butterfly', 'Little Dragonfly', 'Little Crab', 'Little Octopus',
'Little Dolphin', 'Little Shark', 'Little Whale', 'Little Crocodile', 'Little Duck', 'Snowman', 'Little Ball', 'Little Basketball', 'Little Soccer', 'Little Volleyball',
'Little Baseball', 'Little Skateboard', 'Little Ice Cream', 'Little Umbrella', 'Little Glove', 'Little Movie', 'Little Blue Sky', 'Little Princess', 'Little Prince', 'Little Toy',
'Little Candy', 'Little Chocolate', 'Little Ice Cream', 'Little Cake', 'Little Pizza', 'Little Burger', 'Little Fried Chicken', 'Little Roast Duck', 'Little Fish Ball', 'Little Hot Pot',
'Little Skewer', 'Little Pancake', 'Little Deep-Fried Dough Stick', 'Little Rice Porridge', 'Little Yogurt', 'Little Tofu', 'Little Dumpling', 'Little Bun', 'Little Wonton',
'Little Noodles', 'Little Beef Noodles', 'Little Glutinous Rice Chicken', 'Little Steamed Dumpling', 'Little Fried Noodles', 'Little Steamed Bun', 'Little Roast Meat', 'Little Skewer', 'Little Peanut', 'Little Sun',
'Little Moon', 'Little Star', 'Little Rainbow', 'Little Windmill', 'Little Balloon', 'Little Piano', 'Little Guitar', 'Little Speaker', 'Little Microphone', 'Little Actor',
'Little Painter', 'Little Engineer', 'Little Doctor', 'Little Policeman', 'Little Firefighter', 'Little Driver', 'Little Farmer', 'Little Diver', 'Little Pilot', 'Little Basketball',
'Little Swimming Champion', 'Little Running Champion', 'Little Martial Arts Expert', 'Little Ballet Dancer', 'Little Sand Painter', 'Little Calligrapher', 'Little Jigsaw Expert', 'Little Toy Collector',
'Little Movie Producer', 'Little Space Traveler', 'Superhero', 'Invincible Demon King', 'Ultimate Overlord', 'Supreme Emperor', 'Giant from Heaven', 'Peerless Genius', 'Mythical Gate',
'Terrifying Monster', 'Wizard', 'Mysterious Swordsman', 'Immortal Legend', 'Cosmic Tyrant', 'Volcano of Hell', 'Endless Darkness', 'Shining Star', 'Radiant Light', 'Golden Knight',
'Doomsday Annihilator', 'Invincible', 'Crushing Everything', 'Peerless Master', 'Extraordinary', 'King of All', 'Dark Knight', 'War God', 'Center of Attention', 'Shocking the World',
'Domineering', 'Never Wavering', 'Empire Ruler', 'Unyielding', 'Ruthless King', 'Beyond Limits', 'Infinite Power', 'Shining Brightly', 'Endless Pursuit', 'Dance of the Blade',
'Solo World', 'Consuming Everything', 'Eternal Realm', 'Doomsday War God', 'Vast Wealth', 'Mythical Legend', 'Unrivaled', 'Master of a Thousand Swords', 'Bloodthirsty Demon', 'King of the Deep Sea',
'Fantasy City', 'Chosen One', 'Immortal Swordsman', 'Eternal Legend', 'Cosmic Overlord', 'Fire Volcano', 'Boundless Darkness', 'Shining Star', 'Infinite Quest', 'Dance of the Blade',
'Swallower of All Things', 'Eternal War God', 'Massive Fortune', 'Mythical Legend', 'Only One', 'Myriad Swords Return to the Origin', 'Bloodthirsty Madman', 'King of the Deep Sea', 'Fantasy City', 'Chosen One',
'Immortal Swordsman', 'Eternal Legend', 'Cosmic Overlord', 'Fire Volcano', 'Boundless Darkness', 'Shining Star', 'Infinite Quest', 'Dance of the Blade', 'Swallower of All Things', 'Eternal War God',
'Massive Fortune', 'Mythical Legend', 'Only One', 'Myriad Swords Return to the Origin', 'Bloodthirsty Madman', 'King of the Deep Sea', 'Fantasy City', 'Chosen One'
];
return { adjectives, nouns }
},
previewCodeFile: function (options) {
let { file, max, callback } = options;

View File

@@ -18,15 +18,18 @@ let useIndexedDb = (window.localStorage.getItem("tl-rtc-file-receive-file-use-in
let useFixedRoom = window.localStorage.getItem("tl-rtc-file-use-fixed-room") || "";
// 是否开启局域网房间被发现
let useLocalNetworkRoomShare = (window.localStorage.getItem("tl-rtc-file-use-local-network-room-share") || "true") === 'true';
// 是否关闭消息红点
// 是否开启消息红点
let useMessageDot = (window.localStorage.getItem("tl-rtc-file-use-message-dot") || "true") === 'true';
// 是否开启浏览器系统消息通知
let useWebMsgNotify = (window.localStorage.getItem("tl-rtc-file-use-web-msg-notify") || "true") === 'true';
axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
let { data : {
code, login,
token : userToken,
avatar,
username
username,
subscribeNotify
} } = await axios.get("/api/login/state");
let resData = initData.data;
@@ -88,9 +91,10 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
isCameraEnabled : true, //音视频场景下自己的摄像头是否开启
isAudioEnabled : true, //音视频场景下自己的麦克风是否开启
isLoudspeakerEnabled : true, //音视频场景下自己的扬声器是否开启
isCloseLogs : false, //是否关闭日志, 默认开启日志
useLogOutput : true, //是否输出日志, 默认输出日志
chatCommFilterGood : false, // 是否展示公共聊天频道的精选消息
chatCommFilterTop : false, // 是否展示公共聊天频道的置顶消息
useWebMsgNotify: useWebMsgNotify, //是否开启网页消息通知
useMessageDot : useMessageDot, //是否关闭消息提示红点, 默认开启
useFixedRoom: useFixedRoom, //是否使用自定义房间号,持久化,后续自动进入房间
useIndexedDb : useIndexedDb, // 是否需要将文件保存到indexedDb
@@ -126,7 +130,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
nickName: "", //本人名称
socketId: 0, //本人的id
roomId: "10086", //房间号
roomType : "file", //房间类型 video, live, audio ,file
roomType : "file", //房间类型 video, live, audio ,file, password
liveShareMode : "video", //直播类型 video, screen
liveShareRole : "owner", //直播身份 owner: 主播, viwer:观众
codeId: "", //取件码
@@ -135,8 +139,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
remoteMap: {}, //远程连接map
switchData: {}, //配置开关数据
chatRoomSingleSocketId : "", //私聊对方的socketId
avatar : avatar, //用户登录头像
username : username, //用户登录昵称
avatar : avatar || "/image/44826979.png", //用户登录头像
username : username || " -- ", //用户登录昵称
subscribeNotify : subscribeNotify || false, //是否已经订阅过网站
chunkSize: 16 * 1024, // 一块16kb 最大应该可以设置到64kb
currentSendAllSize: 0, // 统计发送文件总大小 (流量统计)
@@ -167,7 +172,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
popUpList: [], //消息数据
localNetRoomList : [], //局域网房间列表
preMouseMove : {}, //上一次鼠标移动的事件
ips: [], // 记录ip列表检测是否支持p2p
ips: [], // 记录ip列表
popUpMsgDom : [], // 消息弹窗dom
videoDeviceList : [], //摄像头设备列表
audioDeviceList : [], //麦克风设备列表
@@ -249,7 +254,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
}
},
roomTypeName: function(){
return window.tlrtcfile.getRoomTypeZh(this.roomType)
return this.lang[window.tlrtcfile.getRoomType(this.roomType)]
},
canSaveToIndexedDb: function(){
return localforage.supports(localforage.INDEXEDDB)
@@ -330,6 +335,38 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
}, 500);
}
});
document.querySelector("#tl-rtc-file-user-log").addEventListener('click', async () => {
layer.msg(that.lang.load_history_oper_log_succ)
});
document.querySelector("#tl-rtc-file-user-subscribe").addEventListener('click', async () => {
if(that.subscribeNotify){
layer.msg(that.lang.alreay_subscribe)
return
}
layer.prompt({
formType: 0,
title: that.lang.email_subscribe_website_notify,
btn : [that.lang.confirm, that.lang.cancel],
value: "",
maxlength : 100,
}, function (value, index, elem) {
if(!tlrtcfile.isEmail(value)){
layer.msg(that.lang.email_format_error)
return
}
that.socket.emit("subscribeNofity", {
room: that.roomId,
email : value
})
layer.close(index);
return false
});
});
},
content: `
<div class="tl-rtc-file-login-user">
@@ -338,14 +375,26 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
<img src="${that.avatar}" alt="头像">
</div>
<div class="tl-rtc-file-login-user-info-name">
微信昵称: ${that.username}
${that.lang.wchat_nickname}: ${that.username}
</div>
<div class="tl-rtc-file-login-user-info-name">
个人昵称: ${that.nickName}
${that.lang.website_nickname}: ${that.nickName}
</div>
<div style="text-align: center;">
<div class="tl-rtc-file-user-oper">
<svg class="icon" aria-hidden="true" id="tl-rtc-file-user-log">
<use xlink:href="#icon-rtc-file-qunfengcaozuojilu"></use>
</svg>
<svg class="icon" aria-hidden="true" id="tl-rtc-file-user-subscribe">
<use xlink:href="#icon-rtc-file-dingyue"></use>
</svg>
</div>
</div>
</div>
<div class="tl-rtc-file-login-user-btn">
<div class="tl-rtc-file-logout">
<button id="tl-rtc-file-logout-btn" type="button" class="layui-btn">退出登录</button>
<button id="tl-rtc-file-logout-btn" type="button" class="layui-btn">${that.lang.logout}</button>
</div>
</div>
</div>
`
@@ -450,6 +499,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
layer.prompt({
formType: 0,
title: that.lang.changeNickName,
btn : [that.lang.confirm, that.lang.cancel],
value: "",
maxlength : 10,
}, function (value, index, elem) {
@@ -828,6 +878,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
layer.prompt({
formType: 0,
title: this.lang.please_enter_code,
btn : [that.lang.confirm, that.lang.cancel],
value: this.codeId,
}, function (value, index, elem) {
if(value.length < 30 || tlrtcfile.containSymbol(value) || tlrtcfile.containChinese(value)){
@@ -893,6 +944,21 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
that.sendChatingRoomSingle();
})
let remoteRtc = that.getRemoteInfo(that.chatRoomSingleSocketId);
let receiveChatRoomSingleList = [];
if(remoteRtc && remoteRtc.receiveChatRoomSingleList){
receiveChatRoomSingleList = remoteRtc.receiveChatRoomSingleList;
}
receiveChatRoomSingleList.forEach((item, index) => {
const id = "tl-rtc-file-content-copy" + index;
if(!document.getElementById(id)) return;
document.getElementById(id).addEventListener('click', function () {
tlrtcfile.copyTxt(id, item.content, function () {
layer.msg(that.lang.copy_success)
});
that.addUserLogs(that.lang.copy_success);
});
})
},
cancel: function (index, layero) {
this.chatRoomSingleSocketId = "";
@@ -910,6 +976,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
<small>${this.lang.user}: <b>{{info.nickName}}</b></small> -
<small>id: <b>{{info.socketId}}</b></small> -
<small>${this.lang.time}: <b>{{info.timeAgo}}</b></small>
<svg class="icon" aria-hidden="true" style="margin-left: 5px;cursor: pointer;" id="tl-rtc-file-content-copy{{index}}">
<use xlink:href="#icon-rtc-file-fuzhi"></use>
</svg>
</div>
<div style="margin-top: 5px;word-break: break-all;width: 90%;">
<b style="font-weight: bold; font-size: large;">{{- info.content }}</b>
@@ -1053,8 +1122,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
2 : 1800, // 队列只有两个弹窗排队时, 弹窗悬停时间1800ms
5 : 1600,
8 : 1300,
10 : 900,
20 : 700
10 : 500,
20 : 200
};
//轮训是否有弹窗排队中
if(!data){
@@ -1245,7 +1314,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
// 设置昵称
setNickName: function(){
if(window.tlrtcfile.genNickName){
this.nickName = window.tlrtcfile.genNickName();
this.nickName = window.tlrtcfile.genNickName(this.langMode);
}
},
// 打开公告
@@ -1496,15 +1565,18 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
let that = this;
layer.prompt({
formType: 0,
title: this.lang.please_enter_password
title: this.lang.please_enter_password_room_num,
btn : [that.lang.confirm, that.lang.cancel],
}, function (value, index, elem) {
that.roomId = value;
layer.close(index);
that.isPasswordRoom = !that.isPasswordRoom;
that.roomType = "password";
layer.prompt({
formType: 1,
title: that.lang.please_enter_password
title: that.lang.please_enter_password,
btn : [that.lang.confirm, that.lang.cancel],
}, function (value, index, elem) {
that.createPasswordRoom(value);
layer.close(index);
@@ -1560,6 +1632,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
}else{
layer.prompt({
formType: 0,
btn : [that.lang.confirm, that.lang.cancel],
title: that.lang.please_enter_video_call_room_num
}, function (value, index, elem) {
that.roomId = value;
@@ -1624,6 +1697,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
}else{
layer.prompt({
formType: 0,
btn : [that.lang.confirm, that.lang.cancel],
title: this.lang.please_enter_screen_sharing_room_num,
}, function (value, index, elem) {
that.roomId = value;
@@ -1848,6 +1922,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
layer.prompt({
formType: 0,
title: this.lang.please_enter_audio_sharing_room_num,
btn : [that.lang.confirm, that.lang.cancel],
}, function (value, index, elem) {
that.roomId = value;
that.createMediaRoom("audio");
@@ -2040,6 +2115,17 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
document.getElementById("tl-sendChatingComm").addEventListener('click',function(){
that.sendChatingComm();
})
that.receiveChatCommList.forEach((item, index) => {
const id = "tl-rtc-file-content-copy" + index;
if(!document.getElementById(id)) return;
document.getElementById(id).addEventListener('click', function () {
tlrtcfile.copyTxt(id, item.msg, function () {
layer.msg(that.lang.copy_success)
});
that.addUserLogs(that.lang.copy_success);
});
})
},
content: `
<div class="layui-col-sm12" style="padding: 15px;">
@@ -2059,6 +2145,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
<small>${this.lang.room}: <b>{{info.room}}</b></small> -
<small>${this.lang.user}: <b>{{info.socketId}}</b></small> -
<small>${this.lang.time}: <b>{{info.timeAgo}}</b></small>
<svg class="icon" aria-hidden="true" style="margin-left: 5px;cursor: pointer;" id="tl-rtc-file-content-copy{{index}}">
<use xlink:href="#icon-rtc-file-fuzhi"></use>
</svg>
{{# if(info.admin) { }}
<svg class="icon" aria-hidden="true" style="width: 24px;height: 24px;color:#1e9fff;">
<use xlink:href="#icon-rtc-file-zhuip"></use>
@@ -2260,6 +2349,17 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
document.getElementById("tl-sendChatingRoom").addEventListener("click",function(){
that.sendChatingRoom();
})
that.receiveChatRoomList.forEach((item, index) => {
const id = "tl-rtc-file-content-copy" + index;
if(!document.getElementById(id)) return;
document.getElementById(id).addEventListener('click', function () {
tlrtcfile.copyTxt(id, item.content, function () {
layer.msg(that.lang.copy_success)
});
that.addUserLogs(that.lang.copy_success);
});
})
},
content: `
<div class="layui-col-sm12" style="padding: 15px;">
@@ -2283,6 +2383,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
{{# } }}
<small>id: <b>{{info.socketId}}</b></small> -
<small>${this.lang.time}: <b>{{info.timeAgo}}</b></small>
<svg class="icon" aria-hidden="true" style="margin-left: 5px;cursor: pointer;" id="tl-rtc-file-content-copy{{index}}">
<use xlink:href="#icon-rtc-file-fuzhi"></use>
</svg>
</div>
<div style="margin-top: 5px;word-break: break-all;width: 90%;">
<b style="font-weight: bold; font-size: large;"> {{- info.content}} </b>
@@ -2454,7 +2557,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
langMode : this.langMode,
ua: this.isMobile ? 'mobile' : 'pc',
network : this.network,
localNetRoom : this.useLocalNetworkRoomShare
localNetRoom : this.useLocalNetworkRoomShare,
ips : this.ips,
});
this.isJoined = true;
this.addPopup({
@@ -2486,7 +2590,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
ua: this.isMobile ? 'mobile' : 'pc',
network : this.network,
liveShareRole : this.liveShareRole,
localNetRoom : this.useLocalNetworkRoomShare
localNetRoom : this.useLocalNetworkRoomShare,
ips : this.ips,
});
this.isJoined = true;
this.roomType = type;
@@ -2524,7 +2629,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
langMode : this.langMode,
ua: this.isMobile ? 'mobile' : 'pc',
network : this.network,
localNetRoom : this.useLocalNetworkRoomShare
localNetRoom : this.useLocalNetworkRoomShare,
ips : this.ips,
});
this.isJoined = true;
this.addPopup({
@@ -3365,14 +3471,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
sdp: event.candidate.candidate
};
this.socket.emit('candidate', message);
let ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/;
let match = ipRegex.exec(event.candidate.candidate);
let ipAddress = match && Array.isArray(match) && match.length > 0 ? match[1] : "unknow";
if (ipAddress !== 'unknow') {
this.ips.push(ipAddress);
}
this.addSysLogs("IP: " + ipAddress);
}
},
// offer
@@ -3418,7 +3516,22 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
let that = this;
this.socket.on("localNetRoom", data => {
const { mode, list } = data;
if(mode === 'connect'){
that.localNetRoomList = data.list || [];
}else if(mode === 'join'){
let newList = that.localNetRoomList.filter(item => { return item.owner !== list[0].owner });
newList.push(list[0]);
that.localNetRoomList = newList;
}else if(mode === 'exit'){
let newList = that.localNetRoomList.filter(item => { return item.owner !== list[0].owner });
if(list[0].count === 0){ //退出后房间没人了,清理
that.localNetRoomList = newList;
}else{ // 退出后房间还有人,更新
newList.push(list[0]);
that.localNetRoomList = newList;
}
}
if(that.localNetRoomList.length === 0 && that.showLocalNetRoom){
that.clickLocalNetRooms(true);
}
@@ -4032,6 +4145,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
layer.prompt({
formType: 1,
title: that.lang.please_enter,
btn : [that.lang.confirm, that.lang.cancel],
}, function (value, index, elem) {
that.socket.emit('manageConfirm', {
room: that.roomId,
@@ -4041,6 +4155,11 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
});
});
//关闭音视频
this.socket.on('subscribeNofity', function (data) {
layer.msg(that.lang.subscribe_website_notify_succ)
});
this.socket.on('manage', function (data) {
if (data.socketId !== that.socketId) {
layer.msg(that.lang.illegal_event)
@@ -4168,8 +4287,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
})
})
document.getElementById("closeLogs").addEventListener('click', function(){
that.closeLogs();
document.getElementById("logOutput").addEventListener('click', function(){
that.logOutput();
})
document.getElementById("coffee").addEventListener('click', function(){
@@ -4199,6 +4318,10 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
document.getElementById("fileTransferSettingHelp").addEventListener('click', function(){
that.settingHelp();
})
document.getElementById("webMsgNotify").addEventListener('click', function(){
that.webMsgNotify();
})
},
content: `
<div class="setting-main layui-carousel" id="tl-rtc-file-setting-info">
@@ -4340,13 +4463,13 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
</li>
<li class="layui-col-xs4">
<a title="${this.lang.log}">
<svg class="icon" aria-hidden="true" id="closeLogs">
<svg class="icon" aria-hidden="true" id="logOutput">
<use xlink:href="#icon-rtc-file-rizhiguanli"></use>
</svg>
<svg id="closeLogsOpen" class="icon settingOpenIcon" aria-hidden="true" style="${this.isCloseLogs ? 'display:none;' : ''}">
<svg id="logOutputOpen" class="icon settingOpenIcon" aria-hidden="true" style="${this.useLogOutput ? '' : 'display:none;'}">
<use xlink:href="#icon-rtc-file-zhengque"></use>
</svg>
<svg id="closeLogsClose" class="icon settingCloseIcon" aria-hidden="true" style="${!this.isCloseLogs ? 'display:none;' : ''}">
<svg id="logOutputClose" class="icon settingCloseIcon" aria-hidden="true" style="${!this.useLogOutput ? '' : 'display:none;'}">
<use xlink:href="#icon-rtc-file-cuowu"></use>
</svg>
<cite>${this.lang.log}</cite>
@@ -4394,6 +4517,20 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
<cite>${this.lang.local_network_room_share}</cite>
</a>
</li>
<li class="layui-col-xs4">
<a title="${this.lang.web_msg_notify}">
<svg class="icon" aria-hidden="true" id="webMsgNotify">
<use xlink:href="#icon-rtc-file-tongzhi"></use>
</svg>
<svg id="webMsgNotifyOpen" class="icon settingOpenIcon" aria-hidden="true" style="${this.useWebMsgNotify ? '' : 'display:none;'}">
<use xlink:href="#icon-rtc-file-zhengque"></use>
</svg>
<svg id="webMsgNotifyClose" class="icon settingCloseIcon" aria-hidden="true" style="${!this.useWebMsgNotify ? '' : 'display:none;'}">
<use xlink:href="#icon-rtc-file-cuowu"></use>
</svg>
<cite>${this.lang.web_msg_notify}</cite>
</a>
</li>
</ul>
</div>
@@ -4466,24 +4603,23 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
layer.msg(this.lang.auto_join_fixed_room + ": " + this.useFixedRoom)
},
// 是否关闭日志输出
closeLogs: function(){
this.isCloseLogs = !this.isCloseLogs;
logOutput: function(){
this.useLogOutput = !this.useLogOutput;
if (this.isCloseLogs) {
layer.msg(`${this.lang.logs_switch}${this.lang.on}`)
$("#closeLogsOpen").css("display", "inline");
$("#closeLogsClose").css("display", "none");
if (this.useLogOutput) {
$("#logOutputOpen").css("display", "inline");
$("#logOutputClose").css("display", "none");
} else {
layer.msg(`${this.lang.logs_switch}${this.lang.off}`)
$("#closeLogsOpen").css("display", "none");
$("#closeLogsClose").css("display", "inline");
$("#logOutputOpen").css("display", "none");
$("#logOutputClose").css("display", "inline");
}
$("#closeLogs").removeClass("layui-anim-rotate")
layer.msg(`${this.lang.logs_switch}${this.useLogOutput ? this.lang.on : this.lang.off}`)
$("#logOutput").removeClass("layui-anim-rotate")
setTimeout(() => {
$("#closeLogs").addClass("layui-anim-rotate")
$("#logOutput").addClass("layui-anim-rotate")
}, 50)
},
// ai对话上下文开关
@@ -4507,43 +4643,64 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
$("#aiContext").addClass("layui-anim-rotate")
}, 50)
},
// 是否关闭消息红点提示
// 是否开启消息红点提示
messageDot : function(){
this.useMessageDot = !this.useMessageDot;
if (this.useMessageDot) {
window.localStorage.setItem("tl-rtc-file-use-message-dot", false)
layer.msg(`${this.lang.messgae_dot_switch}${this.lang.off}`)
$("#messageDotOpen").css("display", "none");
$("#messageDotClose").css("display", "inline");
} else {
window.localStorage.setItem("tl-rtc-file-use-message-dot", true)
layer.msg(`${this.lang.messgae_dot_switch}${this.lang.on}`)
$("#messageDotOpen").css("display", "inline");
$("#messageDotClose").css("display", "none");
} else {
window.localStorage.setItem("tl-rtc-file-use-message-dot", false)
$("#messageDotOpen").css("display", "none");
$("#messageDotClose").css("display", "inline");
}
this.useMessageDot = !this.useMessageDot;
layer.msg(`${this.lang.messgae_dot_switch}${this.useMessageDot ? this.lang.on : this.lang.off}`)
$("#messageDot").removeClass("layui-anim-rotate")
setTimeout(() => {
$("#messageDot").addClass("layui-anim-rotate")
}, 50)
},
// 是否开启局域网房间分享
localNetworkRoomShare : function(){
if(this.useLocalNetworkRoomShare){
window.localStorage.setItem("tl-rtc-file-use-local-network-room-share", false)
layer.msg(`${this.lang.local_network_room_share}${this.lang.off}`)
$("#localNetworkRoomShareOpen").css("display", "none");
$("#localNetworkRoomShareClose").css("display", "inline");
// 开启系统消息提示弹窗
webMsgNotify: function(){
this.useWebMsgNotify = !this.useWebMsgNotify;
if (this.useWebMsgNotify) {
window.localStorage.setItem("tl-rtc-file-use-web-message-notify", true)
$("#webMsgNotifyOpen").css("display", "inline");
$("#webMsgNotifyClose").css("display", "none");
} else {
window.localStorage.setItem("tl-rtc-file-use-local-network-room-share", true)
layer.msg(`${this.lang.local_network_room_share}${this.lang.on}`)
$("#localNetworkRoomShareOpen").css("display", "inline");
$("#localNetworkRoomShareClose").css("display", "none");
window.localStorage.setItem("tl-rtc-file-use-web-message-notify", false)
$("#webMsgNotifyOpen").css("display", "none");
$("#webMsgNotifyClose").css("display", "inline");
}
layer.msg(`${this.lang.web_msg_notify}${this.useWebMsgNotify ? this.lang.on : this.lang.off}`)
$("#webMsgNotify").removeClass("layui-anim-rotate")
setTimeout(() => {
$("#webMsgNotify").addClass("layui-anim-rotate")
}, 50)
},
// 是否开启局域网房间分享
localNetworkRoomShare : function(){
this.useLocalNetworkRoomShare = !this.useLocalNetworkRoomShare;
if(this.useLocalNetworkRoomShare){
window.localStorage.setItem("tl-rtc-file-use-local-network-room-share", true)
$("#localNetworkRoomShareOpen").css("display", "inline");
$("#localNetworkRoomShareClose").css("display", "none");
}else{
window.localStorage.setItem("tl-rtc-file-use-local-network-room-share", false)
$("#localNetworkRoomShareOpen").css("display", "none");
$("#localNetworkRoomShareClose").css("display", "inline");
}
layer.msg(`${this.lang.local_network_room_share}${this.useLocalNetworkRoomShare ? this.lang.on : this.lang.off}`)
$("#localNetworkRoomShare").removeClass("layui-anim-rotate")
setTimeout(() => {
$("#localNetworkRoomShare").addClass("layui-anim-rotate")
@@ -4567,6 +4724,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
formType: 0,
value: '',
title: that.lang.fixed_room,
btn : [that.lang.confirm, that.lang.cancel],
}, function (value, index, elem) {
if (!that.switchData.allowChinese && window.tlrtcfile.containChinese(value)) {
layer.msg(that.lang.room_num_no_zh)
@@ -4642,6 +4800,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
formType: 0,
value: 'wss://',
title: that.lang.input_custom_ws_url,
btn : [that.lang.confirm, that.lang.cancel],
}, function (value, index, elem) {
if(!/^wss?:\/\/[^\s/$.?#].[^\s]*$/.test(value)){
layer.msg(that.lang.ws_url_error)
@@ -4685,11 +4844,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
window.location.reload()
}, 300);
},
// 中继信息提示
useTurnMsg: function () {
layer.msg(this.lang.relay_on)
this.addUserLogs(this.lang.relay_on)
},
// 当前网络状态
networkMsg: function () {
layer.msg(this.lang.current_network + (this.network !== 'wifi' ? this.lang.mobile_data : this.network))
@@ -4712,7 +4866,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
},
// 记录日志
addLogs: function (msg, type) {
if(this.isCloseLogs){
if(!this.useLogOutput){
return
}
if (this.logs.length > this.maxLogCount) {
@@ -4792,13 +4946,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
tlrtcfile.getQrCode("tl-rtc-file-room-share-image", content)
}
document.querySelector("#shareUrl").setAttribute("data-clipboard-text", content);
let clipboard = new ClipboardJS('#shareUrl');
clipboard.on('success', function (e) {
e.clearSelection();
setTimeout(() => {
tlrtcfile.copyTxt("shareUrl", content, function () {
layer.msg(that.lang.copy_room_link)
}, 500);
});
that.addUserLogs(that.lang.copy_room_link);
},
@@ -5274,6 +5423,37 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
}, 50)
}
},
// 局域网房间功能开启进行的的ip上报
localNetRoomIpReport: async function () {
let that = this;
await new Promise((resolve, reject) => {
const pc = new RTCPeerConnection(that.config);
pc.createDataChannel('report');
pc.createOffer().then(offer => pc.setLocalDescription(offer)).catch(reject);
pc.addEventListener("icegatheringstatechange", (ev) => {
if(pc.iceGatheringState === 'complete'){
resolve(true)
}
});
pc.onicecandidate = (e) => {
if (e.candidate) {
if(e.candidate.type !== 'relay'){
let alreadyHas = that.ips.filter(item=>{
return item.address === e.candidate.address;
}).length > 0;
if(!alreadyHas){
that.ips.push({
ipType : e.candidate.type,
address : e.candidate.address,
protocol : e.candidate.protocol,
})
}
}
}
};
});
this.socket.emit("localNetRoom", { ips : this.ips })
},
// 自动监听窗口变化更新css
reCaculateWindowSize: function () {
this.clientWidth = document.body.clientWidth;
@@ -5614,6 +5794,10 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then(async (initData) => {
this.loadVConsoleJs();
this.addSysLogs(this.lang.debug_init_done);
this.addSysLogs(this.lang.local_network_room_report_init);
this.localNetRoomIpReport();
this.addSysLogs(this.lang.local_network_room_report_init_done);
this.addSysLogs(this.lang.current_relay_status + (this.useTurn ? this.lang.on : this.lang.off))
}
});

View File

@@ -413,8 +413,51 @@ const local_lang = {
"webrtc_ice_state" : "webrtc state",
"ip" : "IP",
"online_count" : "Online count",
"load_history_oper_log_succ" : "Load histroy oper log list successfully",
"subscribe_website_notify_succ" : "Subscribe website notify successfully",
"wchat_nickname" : "Wxchat nickname",
"website_nickname" : "Website nickname",
"logout" : "Logout",
"email_subscribe" : "Email subscribe",
"email_format_error" : "Email format error",
"email_subscribe_website_notify" : "Email subscribe website notify",
"alreay_subscribe" : "Alreay subscribe website notify",
"copy_success" : "Copy success",
"web_msg_notify" : "Web msg notify",
"please_enter_password_room_num" : "Please enter the password room number",
"file_room_type" : "File room type",
"live_room_type" : "Live room type",
"video_room_type" : "Video room type",
"screen_room_type" : "Screen room type",
"password_room_type" : "Password room type",
"audio_room_type" : "Audio room type",
"system_room_type" : "System room type",
"unknown_room_type" : "Unknown room type",
"local_network_room_report_init" : "Local network room report init",
"local_network_room_report_init_done" : "Local network room report init done",
},
"zh": {
"local_network_room_report_init" : "局域网房间上报初始化",
"local_network_room_report_init_done" : "局域网房间上报初始化完成",
"file_room_type" : "文件房间",
"live_room_type" : "直播房间",
"video_room_type" : "音视频房间",
"screen_room_type" : "屏幕共享房间",
"password_room_type" : "密码房间",
"audio_room_type" : "语音连麦房间",
"system_room_type" : "系统房间",
"unknown_room_type" : "未知类型房间",
"web_msg_notify" : "系统通知",
"copy_success" : "复制成功",
"alreay_subscribe" : "您已订阅网站通知",
"email_subscribe_website_notify" : "邮件订阅网站通知",
"email_format_error" : "邮箱格式错误",
"email_subscribe" : "邮件订阅",
"logout" : "退出登录",
"website_nickname" : "网站昵称",
"wchat_nickname" : "微信昵称",
"subscribe_website_notify_succ" : "订阅网站通知成功",
"load_history_oper_log_succ" : "即将支持加载历史记录",
"question_answer" : "问答/建议反馈列表",
"ip" : "IP",
"online_count" : "在线人数",
@@ -680,6 +723,7 @@ const local_lang = {
"please_enter_code": "请输入取件码",
"please_enter_content": "请输入文本内容",
"please_enter_live_room_num": "请输入直播房间号",
"please_enter_password_room_num" : "请输入密码房间号",
"please_enter_password": "请输入密码房间密码",
"please_enter_right_code": "请输入正确的取件码",
"please_enter_room_num": "请先填写房间号",