123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 |
- import { useWebRTCStore } from "@/stores/modules/webrtcStore";
- import * as MsgType from "@/common/constant/msgType";
- import * as Constant from "@/common/constant/Constant";
- import { setupNotifications, soundVoice } from "@/utils/notifications.js";
- import { useWebSocketStore } from "@/stores/modules/webSocketStore";
- import { useWalletStore } from "@/stores/modules/walletStore.js";
- const IM_PATH = import.meta.env.VITE_IM_PATH_FIlE;
- // 格式化扩展消息提取
- const formatMessageExt = (message, ext) => {
- const isExt = ext && typeof ext == 'object';
- if(isExt){
- return {
- ...message,
- content:ext.content,
- msgId: ext.msgId,
- // fromUuids: ext.fromUuid,
- cc : ext.cc, // @群成员 0:全部
- id : ext.id, // 真实消息id
- quote : ext.quote, //引用的消息id
- isTemp : ext.isTemp, //消息阅后即焚
- sessionId: ext.sessionId, //会话ID
- sessionUuid: ext.sessionUuid, //会话UUID
- }
- }
- return message
- }
- // 格式化头像地址
- const formatAvatarUrl = (url) => url && /^https?:\/\//.test(url) ? url: (IM_PATH + (url || ''))
- // 消息通知
- const notifications = (state)=>{
- // 检测是否开启免打扰
- if (!state.toUserInfo.slience) {
- setupNotifications(1)
- }
- }
- /**
- * 发送消息处理
- * @param {{}} message
- * @param {useWebSocketStore} wsStore
- */
- export const setMessageHook = (message, wsStore) => {
- const walletStore = useWalletStore();
- console.log('发送消息',message)
- // 尝试解析扩展消息JOSON
- let msg;
- try{
- msg = JSON.parse(message.content);
- if (msg && typeof msg !== 'object') {
- msg = ''
- }
- }catch(e){}
- // 处理好友系统消息
- wsStore.funApprovalFriend(message)
- // 过滤发送的系统消息
- if (MsgType.MSG_SYSTEM_GROUP.includes(message.messageType)) {
- return;
- }
- // 处理个人消息撤回
- if (message.messageType === MsgType.MESSAGE_REVOKE) {
- // console.log('消息撤回',message)
- // 更新消息
- if (msg && msg.isTemp) {
- wsStore.modifyMessageId({isTemp: true}, msg, message.to)
- } else {
- wsStore.deleteMessage(message, msg.id, message.to);
- }
- // 更新会话列表
- if(msg.isTemp){
- wsStore.updateSessionNewMessage({...message, ...msg, content:'[消息已销毁]'}, message.to);
- } else {
- wsStore.pushMessage({
- ...message,
- align: 'right',
- fromAvatar: message.avatar,
- toUuid: message.to,
- fromUuid: message.from,
- content: `${message.from == walletStore.account ? '你' : (message.fromUsername || '对方')}撤回了一条消息`,
- }, message.to);
- }
- }
-
- // 语音和视频挂断/拒接消息处理
- if (Constant.MSG_AUDIO_GROUP.includes(message.contentType)) {
- let sender = wsStore.toUserAudioInfo.sender || {
- uuid: walletStore.account,
- nickname: walletStore.username,
- avatar: walletStore.avatar,
- }
- wsStore.pushMessage({
- ...message,
- align: sender.uuid == message.from ? 'right' : 'left',
- fromAvatar: message.avatar,
- toUuid: message.to,
- fromUuid: message.from,
- content: message.contentType === Constant.REJECT_AUDIO_ONLINE || message.contentType === Constant.REJECT_VIDEO_ONLINE ? '[对方拒绝]' : '[通话结束]',
- sender,
- }, message.to);
- }
- // 文本消息
- if (message.contentType == MsgType.MSG_TYPE.TEXT) {
- msg.isTemp=false;
- wsStore.pushMessage(formatMessageExt({
- ...message,
- align: 'right',
- fromAvatar: message.avatar,
- toUuid:message.to,
- fromUuid:message.from,
- }, msg), message.to);
- }
- // 音频消息
- if (message.contentType === MsgType.MSG_TYPE.AUDIO) {
- msg.isTemp = false;
- const blob = new Blob([message.file], { type: message.fileSuffix });
- const url = URL.createObjectURL(blob);
- wsStore.pushMessage(formatMessageExt({
- ...message,
- toUuid: message.to,
- fromUuid: message.from,
- align: 'right',
- fromAvatar: message.avatar,
- localUrl: url,
- }, msg), message.to);
- }
- // 图片
- if (message.contentType === MsgType.MSG_TYPE.IMAGE) {
- const blob = new Blob([message.file], { type: message.fileSuffix });
- const url = URL.createObjectURL(blob);
- wsStore.pushMessage(formatMessageExt({
- ...message,
- toUuid: message.to,
- fromUuid: message.from,
- align: 'right',
- fromAvatar: message.avatar,
- localUrl: url,
- }, msg), message.to);
- }
- // 视频消息
- if (message.contentType === MsgType.MSG_TYPE.VIDEO) {
- const blob = new Blob([message.file], { type: message.fileSuffix });
- const url = URL.createObjectURL(blob);
- wsStore.pushMessage(formatMessageExt({
- ...message,
- toUuid: message.to,
- fromUuid: message.from,
- align: 'right',
- fromAvatar: message.avatar,
- localUrl: url,
- }, msg), message.to);
- }
- };
- /**
- * 接收到消息处理
- * @param {{}} payload
- * @param {useWebSocketStore} wsStore
- * @returns
- */
- export const handleMessageHook = async (payload, wsStore) => {
- // console.log('接收消息', payload);
- const walletStore = useWalletStore();
- const message = {...payload};
- // 过滤欢迎消息和心跳
- if (message.type === MsgType.MESSAGE_HEAT_BEAT || message.from == 'System'){
- return;
- }
- console.log('接收消息', message);
- // if (!message.contentType && !message.messageType && !message.type) return;
- // 尝试解析扩展消息JOSON
- let msg;
- try{
- msg = JSON.parse(message.content);
- if (msg && typeof msg !== 'object') {
- msg = ''
- }
- }catch(e){}
- // 处理消息回执, 更新消息id
- if (message.messageType === MsgType.MESSAGE_RECEIPT) {
- // 检测是否在群内或是好友
- wsStore.modifyMessageId(message, { id: msg.id, uniqueId: msg.uniqueId, msgId: msg.msgId }, message.from)
- return;
- }
-
- // 消息撤回/阅后即焚(个人和群)
- if (message.messageType === MsgType.MESSAGE_REVOKE || message.messageType === MsgType.MESSAGE_REVOKE_GROUP) {
- // 消息送达回执发送
- wsStore.systemReceiptMessage(message, msg)
- // 更新消息
- if (msg && msg.isTemp) {
- wsStore.modifyMessageId({isTemp: true}, msg,message.from)
- } else {
- wsStore.deleteMessage(message, msg.id);
- }
- // 更新会话列表
- if (msg.isTemp) {
- wsStore.updateSessionNewMessage({...message, ...msg, content: '[消息已销毁]'}, message.from);
- }
- else {
- wsStore.pushMessage({
- ...message,
- toUuid: wsStore.toUserInfo.type == 'user' ? message.to : message.from,
- fromUuid: wsStore.toUserInfo.type == 'user' ? message.from : message.to,
- // fromUuids: msg?.fromUuid,
- align: 'left',
- content: `${(msg.fromUsername || '对方')}撤回了一条消息`,
- fromAvatar: message.avatar,
- });
- }
- }
- // 接收好友系统消息
- if(MsgType.MSG_TYPE_FRIEND.includes(message.messageType)){
- // 消息送达回执发送
- wsStore.systemReceiptMessage(message, msg)
- // 好友系统功能消息
- wsStore.listenFriendSystem(message);
- return;
- }
-
- // 接收群系统消息
- if (MsgType.MSG_TYPE_GROUP.includes(message.messageType)) {
- // 消息送达回执发送
- wsStore.systemReceiptMessage(message, msg)
- //群系统功能消息
- wsStore.listenGroupSystemMessage(message, msg);
- // 群系统消息转换
- let newContent = msg?msg.content:message.content;
- let contentType = MsgType.MSG_TYPE.NOTICE;
- // 处理群通知
- if (message.messageType === MsgType.MESSAGE_NOTICE_GROUP) {
- newContent = `管理员修改了群${msg.notice?'公告':'名称'}`;
- // 更新群通知
- if(msg.name){
- message.nickname = msg.name;
- }
- if(msg.notice){
- message.notice = msg.notice;
- }
- }
- // 如果是@群成员
- if(message.messageType == MsgType.MESSAGE_CC_GROUP){
- contentType = MsgType.MSG_TYPE.TEXT
- }
- // 如果是修改群个人昵称直接返回
- if (message.messageType == MsgType.MESSAGE_NICKNAME_GROUP) return;
- wsStore.pushMessage({
- ...message,
- align: 'left',
- content: newContent,
- contentType,
- messageType: MsgType.MESSAGE_TYPE_GROUP,
- fromAvatar: message.avatar,
- toUuid: wsStore.toUserInfo.type == 'user' ? message.to : message.from,
- fromUuid: wsStore.toUserInfo.type == 'user' ? message.from : message.to,
- // fromUuids: msg?.fromUuid,
- });
- notifications(wsStore);
- return;
- }
- // 语音和视频挂断/拒接消息处理
- if (Constant.MSG_AUDIO_GROUP.includes(message.contentType)) {
- // 消息送达回执发送
- wsStore.systemReceiptMessage(message, msg)
- let sender = wsStore.toUserAudioInfo.sender || {
- uuid: walletStore.account,
- nickname: walletStore.username,
- avatar: walletStore.avatar,
- }
- // 语音消息
- wsStore.pushMessage({
- ...message,
- align: sender.uuid != message.from?'right':'left',
- toUuid: wsStore.toUserAudioInfo.type == 'user' ? message.to : message.from,
- fromUuid: wsStore.toUserInfo.type == 'user' ? message.from : msg.fromUuid,
- // fromUuids: msg?.fromUuid,
- content: message.contentType === Constant.REJECT_AUDIO_ONLINE || message.contentType === Constant.REJECT_VIDEO_ONLINE ? '[对方拒绝]' : '[通话结束]',
- sender,
- fromAvatar: message.avatar,
- });
- }
- // 文本消息
- if (message.contentType === MsgType.MSG_TYPE.TEXT) {
- // 消息送达回执发送
- wsStore.systemReceiptMessage(message, msg)
- wsStore.pushMessage(formatMessageExt({
- ...message,
- toUuid: wsStore.toUserInfo.type == 'user' ? message.to : message.from,
- fromUuid: wsStore.toUserInfo.type == 'user' ? message.from : msg.fromUuid,
- // fromUuids: msg?.fromUuid,
- align: 'left',
- fromAvatar: message.avatar,
- }, msg));
- notifications(wsStore);
- return
- }
- // 音频消息
- if (message.contentType === MsgType.MSG_TYPE.AUDIO) {
- // 消息送达回执发送
- wsStore.systemReceiptMessage(message, msg)
- const audioBlob = new Blob([message.file], {
- type: `audio/${message.fileSuffix}`,
- });
- // 生成可播放的 ObjectURL
- const audioUrl = URL.createObjectURL(audioBlob);
- wsStore.pushMessage(formatMessageExt({
- ...message,
- file: audioUrl,
- toUuid: wsStore.toUserInfo.type == 'user' ? message.to : message.from,
- fromUuid: wsStore.toUserInfo.type == 'user' ? message.from : msg.fromUuid,
- // fromUuids: msg?.fromUuid,
- align: 'left',
- fromAvatar: message.avatar,
- }, msg));
- notifications(wsStore);
- return
- }
- // 图片
- if (message.contentType === MsgType.MSG_TYPE.IMAGE) {
- // 消息送达回执发送
- wsStore.systemReceiptMessage(message, msg)
- const blob = new Blob([message.file], { type: message.fileSuffix });
- const url = URL.createObjectURL(blob);
- wsStore.pushMessage(formatMessageExt({
- ...message,
- file: url,
- toUuid: wsStore.toUserInfo.type == 'user' ? message.to : message.from,
- fromUuid: wsStore.toUserInfo.type == 'user' ? message.from : msg.fromUuid,
- // fromUuids: msg?.fromUuid,
- align: 'left',
- fromAvatar: message.avatar,
- }, msg));
- notifications(wsStore);
- return
- }
- // 视频
- if (message.contentType === MsgType.MSG_TYPE.VIDEO) {
- // 消息送达回执发送
- wsStore.systemReceiptMessage(message, msg)
- const blob = new Blob([message.file], { type: message.fileSuffix });
- const url = URL.createObjectURL(blob);
- wsStore.pushMessage(formatMessageExt({
- ...message,
- file: url,
- toUuid: wsStore.toUserInfo.type == 'user' ? message.to : message.from,
- fromUuid: wsStore.toUserInfo.type == 'user' ? message.from : msg.fromUuid,
- // fromUuids: msg?.fromUuid,
- align: 'left',
- fromAvatar: message.avatar,
- }, msg));
- notifications(wsStore);
- return
- }
- // 音频在线:被呼叫
- if (
- message.contentType === Constant.DIAL_AUDIO_ONLINE ||
- message.contentType === Constant.DIAL_VIDEO_ONLINE
- ) {
- console.log("收到播号")
- // 接收请求者信息,更新当前会话
- wsStore.toUserAudioInfo = {
- uuid: message.from,
- type: "user",
- // 接收到来自谁的拨号请求信息
- toUuid: message.from,
- toAvatar: message.avatar,
- toUsername: message.fromUsername,
- // 自己的信息
- fromUuid: message.to,
- fromAvatar: walletStore.avatar,
- fromUsername: wsStore.toUserInfo.nickname,
- // 发送者信息
- sender: {
- uuid: message.from,
- avatar: message.avatar,
- nickname: message.fromUsername,
- },
- };
- // wsStore.toUserInfo = {
- // ...wsStore.toUserInfo,
- // uuid: message.from,
- // type: "user",
- // unReadNum: 1,
- // sender: {
- // uuid: message.from,
- // avatar: message.avatar,
- // nickname: message.fromUsername,
- // },
- // };
- // 调起通话界面
- const rtcStore = useWebRTCStore();
- rtcStore.streamType = message.contentType == Constant.DIAL_AUDIO_ONLINE ? 'audio' :'video'
- rtcStore.isCaller = false
- rtcStore.imSate = {
- ...rtcStore.imSate,
- videoCallModal: true,
- callName: message.fromUsername,
- fromUserUuid: message.from,
- callAvatar: message.avatar
- };
- // 播放铃声
- soundVoice.play()
- return
- }
- // 音频在线:挂断
- if (
- message.contentType === Constant.CANCELL_AUDIO_ONLINE ||
- message.contentType === Constant.CANCELL_VIDEO_ONLINE
- ) {
- console.log("音频在线:挂断")
- const rtcStore = useWebRTCStore();
- // 关闭通话界面
- rtcStore.imSate.videoCallModal = false;
- // 停止铃声
- soundVoice.stop()
- // 清理通话p2p配置
- rtcStore.cleanup()
- return;
- }
- // 音频在线:拒接
- if (
- message.contentType === Constant.REJECT_AUDIO_ONLINE ||
- message.contentType === Constant.REJECT_VIDEO_ONLINE
- ) {
- console.log("音频在线:拒接")
- // 停止铃声
- soundVoice.stop()
- const rtcStore = useWebRTCStore();
- // 关闭通话界面
- rtcStore.imSate.videoCallModal = false;
- // 清理通话p2p配置
- rtcStore.cleanup()
- return;
- }
- // 音频在线:接听
- if (
- message.contentType === Constant.ACCEPT_VIDEO_ONLINE ||
- message.contentType === Constant.ACCEPT_AUDIO_ONLINE
- ) {
- console.log("音频在线:接听", message.contentType)
- // 停止铃声
- soundVoice.stop()
- const video = message.contentType == Constant.ACCEPT_VIDEO_ONLINE
- // 初始化通话p2p配置
- startAudioOnline(video, wsStore);
- return;
- }
- // 音频通话交换信息
- if (message.type === Constant.MESSAGE_TRANS_TYPE) {
-
- if (
- message.contentType >= Constant.DIAL_MEDIA_START &&
- message.contentType <= Constant.DIAL_MEDIA_END
- ) {
- console.log("音频通话")
- // 媒体通话处理
- // dealMediaCall(message);
- return;
- }
- // 停止铃声
- soundVoice.stop()
- const rtcStore = useWebRTCStore();
- const { type, sdp, iceCandidate } = JSON.parse(message.content);
- // 接收answer:设置对端sdp
- if (type === "answer") {
- console.log('关联远程RTC')
- // 关联远程RTC标识到本地RTC
- const answerSdp = new RTCSessionDescription({ type, sdp });
- await rtcStore.setRemoteDescription(answerSdp);
- }
- // 处理 ICE 候选(统一处理offer_ice和answer_ice)
- if (type.endsWith("_ice")) {
- console.log("添加ICE", type, iceCandidate)
- const candidate = new RTCIceCandidate(iceCandidate);
- await rtcStore
- .addIceCandidate(candidate)
- .catch((error) => console.error("添加ICE候选失败:", error));
- // return;
- }
- // 响应对端offer
- if (type === "offer") {
- // 检查媒体权限是否开启
- // let preview = null;
- console.log("被呼叫者收到offer")
- // 被呼叫收到offer, 需要创建RTC, 并开启音视频数据流
- // console.log("message.contentType=", message.contentType)
- let video = false;
- // 视频电话
- if (message.contentType === Constant.VIDEO_ONLINE) {
- // preview = document.getElementById("localVideoReceiver");
- video = true;
- // // 屏幕共享
- }
- // 创建RTC
- if (!rtcStore.peerConnection) {
- rtcStore.initConnection(false, video); // false表示是Answer方
- }
- // 音频电话
- if (message.contentType === Constant.AUDIO_ONLINE) {
- // preview = document.getElementById("audioPhone");
- // 音频电话
- }
- // 获取音视频流数据
- // navigator.mediaDevices
- // .getUserMedia({ audio: true, video: video })
- rtcStore.getUserMedia({ audio: true, video: video })
- .then(async (stream) => {
- console.log('关联数据流和远程RTC')
- // 关联数据流到本地RTC
- rtcStore.addLocalStream(stream);
- // 关联远程RTC标识到本地RTC
- const offer = new RTCSessionDescription({ type, sdp });
- return await rtcStore.setRemoteDescription(offer);
- })
- // 创建回应offer
- .then(() => rtcStore.createAnswer())
- .then((answer) => {
- console.log('发送answer给呼叫者', answer)
- // 发送给被呼叫者(拨打者)
- wsStore.sendMessage({
- content: JSON.stringify(answer),
- type: Constant.MESSAGE_TRANS_TYPE,
- messageType: message.contentType,
- fromUsername: wsStore.toUserAudioInfo.fromUsername,
- avatar: wsStore.toUserAudioInfo.fromAvatar,
- to: wsStore.toUserAudioInfo.toUuid,
- from: wsStore.toUserAudioInfo.fromUuid,
- });
- })
- .catch((error) => {
- console.error("回应呼叫失败:", error);
- });
- }
- }
- };
- // 消息回调
- const messageCallback = (contentType, state)=>{
- state.sendMessage({
- contentType, // 消息内容类型
- content: "callback", //
- type: Constant.MESSAGE_TRANS_TYPE,
- });
- }
- // 创建呼叫:开启语音电话
- const startAudioOnline = (video, state) => {
- const rtcStore = useWebRTCStore();
- const wsStore = useWebSocketStore();
- // 初始化webrtc连接
- rtcStore.initConnection(true, video);
- // 获取音视频数据流
- // navigator.mediaDevices
- // .getUserMedia({ audio: true, video: video })
- rtcStore.getUserMedia({ audio: true, video: video })
- .then((stream) => {
- console.log('关联数据流和创建offer')
- // 关联数据流到本地RTC
- rtcStore.addLocalStream(stream);
- // 创建RTC offer
- return rtcStore.createOffer();
- })
- .then((offer) => {
- console.log('caller', offer)
- // 发送offer给被呼叫者(接听者)
- state.sendMessage({
- contentType: video? Constant.VIDEO_ONLINE: Constant.AUDIO_ONLINE, // 消息内容类型
- content: JSON.stringify(offer),
- type: Constant.MESSAGE_TRANS_TYPE,
- fromUsername: wsStore.toUserAudioInfo.fromUsername,
- avatar: wsStore.toUserAudioInfo.fromAvatar,
- to: wsStore.toUserAudioInfo.toUuid,
- from: wsStore.toUserAudioInfo.fromUuid,
- });
- })
- .catch((error) => {
- console.error("发起呼叫失败:", error);
- });
- };
|