import 'dart:collection'; import 'dart:convert'; import 'dart:io'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:extended_image/extended_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_debouncer/flutter_debouncer.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:yumi/ui_kit/components/sc_tts.dart'; import 'package:yumi/app/constants/sc_global_config.dart'; import 'package:yumi/shared/tools/sc_message_utils.dart'; import 'package:yumi/shared/tools/sc_path_utils.dart'; import 'package:yumi/shared/tools/sc_room_utils.dart'; import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; import 'package:yumi/services/audio/rtc_manager.dart'; import 'package:provider/provider.dart'; import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart'; import 'package:tencent_cloud_chat_sdk/enum/V2TimConversationListener.dart'; import 'package:tencent_cloud_chat_sdk/enum/V2TimGroupListener.dart'; import 'package:tencent_cloud_chat_sdk/enum/V2TimSDKListener.dart'; import 'package:tencent_cloud_chat_sdk/enum/conversation_type.dart'; import 'package:tencent_cloud_chat_sdk/enum/group_type.dart'; import 'package:tencent_cloud_chat_sdk/enum/log_level_enum.dart'; import 'package:tencent_cloud_chat_sdk/enum/message_status.dart'; import 'package:tencent_cloud_chat_sdk/manager/v2_tim_group_manager.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation_result.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_image_elem.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_receipt.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_msg_create_info_result.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_status.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'; import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart'; import 'package:yumi/app_localizations.dart'; import 'package:yumi/ui_kit/components/dialog/dialog_base.dart'; import 'package:yumi/app/constants/sc_room_msg_type.dart'; import 'package:yumi/shared/tools/sc_lk_event_bus.dart'; import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart'; import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_config_repository_imp.dart'; import 'package:yumi/shared/data_sources/models/message/big_broadcast_group_message.dart'; import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart'; import 'package:yumi/shared/business_logic/models/res/sc_broad_cast_luck_gift_push.dart'; import 'package:yumi/shared/business_logic/models/res/broad_cast_mic_change_push.dart'; import 'package:yumi/shared/business_logic/models/res/gift_res.dart'; import 'package:yumi/shared/business_logic/models/res/sc_public_message_page_res.dart'; import 'package:yumi/shared/business_logic/models/res/sc_room_theme_list_res.dart'; import 'package:yumi/ui_kit/widgets/room/invite/invite_room_dialog.dart'; import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart'; import 'package:yumi/shared/business_logic/models/res/login_res.dart'; import 'package:yumi/services/gift/gift_system_manager.dart'; import '../../shared/data_sources/models/enum/sc_gift_type.dart'; import '../../shared/data_sources/models/enum/sc_room_roles_type.dart'; typedef RoomNewMsgListener = Function(Msg msg); typedef OnNewMessageListener = Function(V2TimMessage? message, {String? msgId}); typedef OnNewANMessageListener = Function(Records? message); typedef OnRevokeMessageListener = Function(String msgId); typedef OnNewGroupMessageListener = Function(String groupID, V2TimMessage message); typedef OnMessageRecvC2CReadListener = Function(List messageIDList); typedef RtmProvider = RealTimeMessagingManager; class RealTimeMessagingManager extends ChangeNotifier { BuildContext? context; void _giftFxLog(String message) { debugPrint('[GiftFX][RTM] $message'); } ///消息列表 List roomAllMsgList = []; List roomChatMsgList = []; List roomGiftMsgList = []; RoomNewMsgListener? msgAllListener; RoomNewMsgListener? msgChatListener; RoomNewMsgListener? msgGiftListener; RoomNewMsgListener? msgFloatingGiftListener; RoomNewMsgListener? msgUserJoinListener; /// 当前会话 V2TimConversation? currentConversation; /// 会话列表缓存 Map conversationMap = {}; ///消息已读监听 OnMessageRecvC2CReadListener? onMessageRecvC2CReadListener; ///消息被撤回监听 OnRevokeMessageListener? onRevokeMessageListener; ///新消息监听 单聊 OnNewMessageListener? onNewMessageCurrentConversationListener; OnNewANMessageListener? onNewActivityMessageCurrentConversationListener; OnNewANMessageListener? onNewNotifcationMessageCurrentConversationListener; ///新消息监听 群聊 Map onNewMessageListenerGroupMap = {}; int allUnReadCount = 0; int messageUnReadCount = 0; int systemUnReadCount = 0; int customerUnReadCount = 0; int activityUnReadCount = 0; int notifcationUnReadCount = 0; SCBroadCastLuckGiftPush? currentPlayingLuckGift; final Queue _luckGiftPushQueue = Queue(); Debouncer debouncer = Debouncer(); List conversationList = []; ///客服 SocialChatUserProfile? customerInfo; int _systemUnreadCount() { int count = 0; for (final conversationId in SCGlobalConfig.systemConversationIds) { count += conversationMap[conversationId]?.unreadCount ?? 0; } return count; } V2TimConversation getPreferredSystemConversation() { final systemConversations = conversationMap.values .where( (element) => SCGlobalConfig.isSystemConversationId(element.conversationID), ) .toList() ..sort((e1, e2) { final time1 = e1.lastMessage?.timestamp ?? 0; final time2 = e2.lastMessage?.timestamp ?? 0; return time2.compareTo(time1); }); if (systemConversations.isNotEmpty) { return systemConversations.first; } return V2TimConversation( type: ConversationType.V2TIM_C2C, userID: SCGlobalConfig.primarySystemUserId, conversationID: SCGlobalConfig.primarySystemConversationId, ); } void getConversationList() { List list = conversationMap.values.toList(); list.removeWhere((element) { if (element.conversationID == "c2c_${customerInfo?.id}") { return true; } if (element.conversationID == "c2c_atyou-newsletter") { ///删除这个会话,后台乱发的联系人。。防止无效未读数 clearC2CHistoryMessage(element.conversationID, false); return true; } if (element.lastMessage == null) { return true; } return false; }); list.sort((e1, e2) { int time1 = e1.lastMessage?.timestamp ?? 0; int time2 = e2.lastMessage?.timestamp ?? 0; return time2.compareTo(time1); }); conversationList = list; systemUnReadCount = _systemUnreadCount(); customerUnReadCount = conversationMap["c2c_${customerInfo?.id}"]?.unreadCount ?? 0; notifyListeners(); } init(BuildContext context) async { this.context = context; V2TimSDKListener sdkListener = V2TimSDKListener( onConnectFailed: (int code, String error) { // 连接失败的回调函数 // code 错误码 // error 错误信息 }, onConnectSuccess: () { // SDK 已经成功连接到腾讯云服务器 }, onConnecting: () { // SDK 正在连接到腾讯云服务器 }, onKickedOffline: () { // 当前用户被踢下线,此时可以 UI 提示用户,并再次调用 V2TIMManager 的 login() 函数重新登录。 }, onSelfInfoUpdated: (V2TimUserFullInfo info) { // 登录用户的资料发生了更新 // info登录用户的资料 }, onUserSigExpired: () { // 在线时票据过期:此时您需要生成新的 userSig 并再次调用 V2TIMManager 的 login() 函数重新登录。 }, onUserStatusChanged: (List userStatusList) { //用户状态变更通知 //userStatusList 用户状态变化的用户列表 //收到通知的情况:订阅过的用户发生了状态变更(包括在线状态和自定义状态),会触发该回调 //在 IM 控制台打开了好友状态通知开关,即使未主动订阅,当好友状态发生变更时,也会触发该回调 //同一个账号多设备登录,当其中一台设备修改了自定义状态,所有设备都会收到该回调 }, ); V2TimValueCallback initSDKRes = await TencentImSDKPlugin.v2TIMManager .initSDK( sdkAppID: int.parse(SCGlobalConfig.tencentImAppid), // SDKAppID loglevel: LogLevelEnum.V2TIM_LOG_ALL, // 日志登记等级 listener: sdkListener, // 事件监听器 ); if (initSDKRes.code == 0) {} try { customerInfo = await SCConfigRepositoryImp().customerService(); } catch (e) {} /// 登录 await loginTencetRtm(context); /// 初始化会话列表 // _onRefreshConversationSub = FTIM.getContactManager().addRefreshConversationListener(_onRefreshConversation); TencentImSDKPlugin.v2TIMManager .getConversationManager() .addConversationListener( listener: V2TimConversationListener( onNewConversation: (conversationList) { // _onRefreshConversation(conversationList); initConversation(); }, onTotalUnreadMessageCountChanged: (int totalUnreadCount) { messageUnReadCount = totalUnreadCount; systemUnReadCount = _systemUnreadCount(); customerUnReadCount = conversationMap["c2c_${customerInfo?.id}"]?.unreadCount ?? 0; allUnReadCount = messageUnReadCount + notifcationUnReadCount + activityUnReadCount; notifyListeners(); }, ), ); TencentImSDKPlugin.v2TIMManager.addGroupListener( listener: V2TimGroupListener( onMemberEnter: (String groupID, List memberList) {}, onMemberLeave: (String groupID, V2TimGroupMemberInfo member) { Provider.of( context, listen: false, ).removOnlineUser(groupID, member.userID!); }, onMemberKicked: ( String groupID, V2TimGroupMemberInfo opUser, List memberList, ) { ///踢出房间 if (memberList.isNotEmpty) { if (memberList.first.userID == AccountStorage().getCurrentUser()?.userProfile?.id) { Provider.of( context!, listen: false, ).engine?.setClientRole(role: ClientRoleType.clientRoleAudience); ///退出房间 Provider.of( context!, listen: false, ).exitCurrentVoiceRoomSession(false).whenComplete(() { SCRoomUtils.closeAllDialogs(); SmartDialog.show( tag: "showConfirmDialog", alignment: Alignment.center, debounce: true, animationType: SmartAnimationType.fade, builder: (_) { return MsgDialog( title: SCAppLocalizations.of(context)!.tips, msg: SCAppLocalizations.of(context)!.kickRoomTips, btnText: SCAppLocalizations.of(context)!.confirm, onEnsure: () {}, ); }, ); }); } } }, ), ); /// 新消息监听 // FTIM.getMessageManager().addNewMessagesListener(_onNewMessage); TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener( listener: V2TimAdvancedMsgListener( onRecvC2CReadReceipt: (List receiptList) { //会话已读回调 }, onRecvMessageModified: (V2TimMessage message) { // msg 为被修改之后的消息对象 }, onRecvMessageReadReceipts: (List receiptList) { //群聊/单聊已读回调 List messageIDList = []; for (var element in receiptList) { messageIDList.add(element.msgID!); } onMessageRecvC2CReadListener?.call(messageIDList); }, onRecvMessageRevoked: (String messageId) { // 在本地维护的消息中处理被对方撤回的消息 TencentImSDKPlugin.v2TIMManager .getMessageManager() .deleteMessages(msgIDs: [messageId]) .then((result) { onRevokeMessageListener?.call(messageId); for (var ms in conversationList) { if (ms.lastMessage?.msgID == messageId) { ms.lastMessage?.status = MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED; break; } } notifyListeners(); }); }, onRecvNewMessage: (V2TimMessage message) async { _onNewMessage(message); }, onSendMessageProgress: (V2TimMessage message, int progress) { //文件上传进度回调 }, ), ); getAllUnReadCount(); joinBigBroadcastGroup(); } /// 初始化会话 /// 打开聊天界面的时候调用 int sTime = 0; Future startConversation(V2TimConversation conversation) async { assert(conversation != null); int eTime = DateTime.now().millisecondsSinceEpoch; if (eTime - sTime > 5000) { sTime = eTime; currentConversation = conversation; await TencentImSDKPlugin.v2TIMManager .getMessageManager() .markC2CMessageAsRead(userID: conversation.userID!); notifyListeners(); return true; } else { return false; } } ///初始话会话 Future initConversation() async { V2TimValueCallback convList = await TencentImSDKPlugin.v2TIMManager .getConversationManager() .getConversationList(nextSeq: '0', count: 100); List? conversationList = convList.data?.conversationList; print('conversationList:${conversationList?.length}'); conversationMap.clear(); if (conversationList != null) { for (V2TimConversation? conversation in conversationList) { conversationMap[conversation!.conversationID] = conversation; } } getConversationList(); } // /// 所有消息未读数 // int getAllUnReadCount() { // allUnReadCount = 0; // for (var value in conversationList) { // allUnReadCount += value.unreadCount ?? 0; // } // // for (var value in systemConversationList) { // // i += value.unreadMessageNum; // // } // notifyListeners(); // return allUnReadCount; // } /// 所有消息未读数 void getAllUnReadCount() async { V2TimValueCallback res = await TencentImSDKPlugin.v2TIMManager .getConversationManager() .getTotalUnreadMessageCount(); if (res.code == 0) { print('初始未读总数: ${res.data}'); messageUnReadCount = res.data ?? 0; allUnReadCount = messageUnReadCount + activityUnReadCount + notifcationUnReadCount; notifyListeners(); // 这里可以先用初始值更新UI } } ///登录IM Future loginTencetRtm(BuildContext context) async { SocialChatLoginRes? userModel = AccountStorage().getCurrentUser(); bool logined = false; while (!logined && userModel != null) { await Future.delayed(Duration(milliseconds: 550)); try { if (userModel.userSig != null) { V2TimCallback res = await TencentImSDKPlugin.v2TIMManager.login( userID: userModel.userProfile?.id ?? "", userSig: userModel.userSig ?? "", ); print( 'tim voLogin:${res.code},${userModel.userProfile?.id},${userModel.userSig}', ); if (res.code == 0) { isLogout = false; // 登录成功逻辑 logined = true; print('tim 登录成功'); await initConversation(); } else { // 登录失败逻辑 //print('timm 需要重新登录2'); print('tim 登录失败${res.code}'); SCTts.show('tim login fail'); } } else { //print('timm 需要重新登录sign'); SCTts.show('tim login fail'); } } catch (e) { //print('timm 登录异常:${e.toString()}'); SCTts.show('timm login fail:${e.toString()}'); } userModel = AccountStorage().getCurrentUser(); } } _onNewMessage(V2TimMessage message) { if (message.groupID != null) { ///全服通知 if (message.groupID == SCGlobalConfig.bigBroadcastGroup) { _newBroadCastMsgRecv(message.groupID!, message); } ///群消息 for (var element in onNewMessageListenerGroupMap.values) { element?.call(message.groupID!, message); } } else { ///单聊消息 _onNew1v1Message(message); } } _onNew1v1Message(V2TimMessage? message, {String? msgId}) async { for (var element in conversationList) { if (message?.userID == element?.userID) { element.lastMessage = message; if (onNewMessageCurrentConversationListener == null) { element.unreadCount = element.unreadCount! + 1; } } } if (message?.userID == customerInfo?.id) { if (onNewMessageCurrentConversationListener == null) { conversationMap["c2c_${customerInfo?.id}"]?.unreadCount = (conversationMap["c2c_${customerInfo?.id}"]?.unreadCount ?? 0) + 1; } } systemUnReadCount = _systemUnreadCount(); notifyListeners(); onNewMessageCurrentConversationListener?.call(message, msgId: msgId); } void _onRefreshConversation(List conversations) { for (V2TimConversation conversation in conversations) { conversationMap[conversation.conversationID] = conversation; } getConversationList(); } ///创建房间im群聊 Future> createRoomGroup( String groupID, String groupName, ) { return V2TIMGroupManager().createGroup( groupID: groupID, groupType: GroupType.AVChatRoom, groupName: groupName, ); } ///加入房间的im群聊 Future joinRoomGroup(String groupID, String message) async { _luckGiftPushQueue.clear(); currentPlayingLuckGift = null; var joinResult = await TencentImSDKPlugin.v2TIMManager.joinGroup( groupID: groupID, message: message, ); if (joinResult.code == 0) { onNewMessageListenerGroupMap[groupID] = _newGroupMsg; } return joinResult; } ///发送文本消息 Future dispatchMessage( Msg msg, { bool showEmoticons = true, bool addLocal = true, }) async { if (addLocal) { addMsg(msg); } // 发送消息到腾讯云IM if (msg.groupId != null) { await _sendTencentMessage(msg); } notifyListeners(); } // 发送腾讯云消息 Future _sendTencentMessage(Msg msg) async { try { switch (msg.type) { case SCRoomMsgType.text: case SCRoomMsgType.shangMai: case SCRoomMsgType.emoticons: case SCRoomMsgType.xiaMai: case SCRoomMsgType.killXiaMai: case SCRoomMsgType.roomRoleChange: case SCRoomMsgType.roomSettingUpdate: case SCRoomMsgType.roomBGUpdate: case SCRoomMsgType.qcfj: case SCRoomMsgType.fengMai: case SCRoomMsgType.jieFeng: case SCRoomMsgType.joinRoom: case SCRoomMsgType.gift: case SCRoomMsgType.bsm: case SCRoomMsgType.roomDice: case SCRoomMsgType.roomRPS: case SCRoomMsgType.roomLuckNumber: case SCRoomMsgType.image: case SCRoomMsgType.roomGameClose: case SCRoomMsgType.roomGameCreate: case SCRoomMsgType.luckGiftAnimOther: _sendRoomMessage(msg); break; default: break; } } catch (e) { print('发送消息失败: $e'); // 处理发送失败的情况 } } ///发送单聊文本消息 Future sendC2CTextMsg( String msg, V2TimConversation toConversation, ) async { // 创建文本消息 V2TimValueCallback createTextMessageRes = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .createTextMessage( text: msg, // 文本信息 ); if (createTextMessageRes.code == 0) { // 文本信息创建成功 String id = createTextMessageRes.data!.id!; // 发送文本消息 // 在sendMessage时,若只填写receiver则发个人用户单聊消息 // 若只填写groupID则发群组消息 // 若填写了receiver与groupID则发群内的个人用户,消息在群聊中显示,只有指定receiver能看见 String receiveId = toConversation.userID!; V2TimValueCallback sendMessageRes = await TencentImSDKPlugin .v2TIMManager .getMessageManager() .sendMessage( id: id, // 创建的messageid receiver: toConversation.userID!, // 接收人id needReadReceipt: true, groupID: '', // 是否需要已读回执 ); if (sendMessageRes.code == 0) { // 发送成功 _onNew1v1Message(sendMessageRes.data); } else { SCTts.show( 'create fail,code:${sendMessageRes.code},${sendMessageRes.desc}', ); } } } ///发送单聊自定义消息 Future sendC2CCustomMsg( String msg, V2TimConversation toConversation, String extension, ) async { // 创建文本消息 V2TimValueCallback createCustomMessageRes = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .createCustomMessage( data: msg, // 文本信息 extension: extension, ); if (createCustomMessageRes.code == 0) { // 文本信息创建成功 String id = createCustomMessageRes.data!.id!; // 发送文本消息 // 在sendMessage时,若只填写receiver则发个人用户单聊消息 // 若只填写groupID则发群组消息 // 若填写了receiver与groupID则发群内的个人用户,消息在群聊中显示,只有指定receiver能看见 String receiveId = toConversation.userID!; V2TimValueCallback sendMessageRes = await TencentImSDKPlugin .v2TIMManager .getMessageManager() .sendMessage( id: id, // 创建的messageid receiver: toConversation.userID!, // 接收人id needReadReceipt: true, isSupportMessageExtension: true, groupID: '', // 是否需要已读回执 ); if (sendMessageRes.code == 0) { // 发送成功 _onNew1v1Message(sendMessageRes.data); } else { SCTts.show( 'create fail,code:${sendMessageRes.code},${sendMessageRes.desc}', ); } } } ///发送单聊图片消息 Future sendImageMsg({ List? selectedList, File? file, required V2TimConversation conversation, }) async { if (file != null) { File newFile = await SCMessageUtils.createImageElem(file); V2TimValueCallback createImageMessageRes = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .createImageMessage(imagePath: newFile.path); if (createImageMessageRes.code == 0) { String id = createImageMessageRes.data!.id!; V2TimValueCallback sendMessageRes = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .sendMessage( id: id, receiver: conversation.userID!, needReadReceipt: true, groupID: '', ); if (sendMessageRes.code == 0) { // 发送成功 _onNew1v1Message(sendMessageRes.data); } else { SCTts.show( 'create fail,code:${sendMessageRes.code},${sendMessageRes.desc}', ); } } } else { if (selectedList != null) { for (File entity in selectedList) { String id = ""; V2TimMessage? message; //判断是视频或者图片消息 if (SCPathUtils.getFileType(entity.path) == "image") { V2TimValueCallback createImageMessageRes = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .createImageMessage(imagePath: entity.path); if (createImageMessageRes.code == 0) { message = createImageMessageRes.data?.messageInfo; id = createImageMessageRes.data!.id!; // 创建图片 File newFile = await SCMessageUtils.createImageElem(entity); //发送 V2TimImageElem elem = V2TimImageElem(path: newFile.path); message?.imageElem = elem; } } else if (SCPathUtils.getFileType(entity.path) == "video_pic") { if (entity.lengthSync() > 50000000) { SCTts.show( SCAppLocalizations.of(context!)!.theVideoSizeCannotExceed, ); return; } // 复制一份视频 String md5Str1 = keyToMd5(entity.path); File newFile = File( "${FileCacheManager.videoCachePath}/$md5Str1.mp4", ); if (!newFile.existsSync()) { await entity.copy(newFile.path); } // 创建缩略图 String? thumbImagePath = await SCMessageUtils.generateFileThumbnail( newFile.path, 128, ); V2TimValueCallback createVideoMessageRes = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .createVideoMessage( videoFilePath: entity.path, type: "mp4", duration: 0, snapshotPath: thumbImagePath ?? "", ); if (createVideoMessageRes.code == 0) { message = createVideoMessageRes.data?.messageInfo; id = createVideoMessageRes.data!.id!; } } // 消息设置 message?.isSelf = true; // message.conversation = conversation; message?.status = MessageStatus.V2TIM_MSG_STATUS_SENDING; print('组装完成,准备发送:${message?.toJson()}'); // String id = await FTIM.getMessageManager().sendMessage(message); // message.msgId = id; V2TimValueCallback sendMessageRes = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .sendMessage( id: id, receiver: conversation.userID!, needReadReceipt: true, groupID: '', ); if (sendMessageRes.code == 0) { // 发送成功 _onNew1v1Message(sendMessageRes.data); } else { SCTts.show( 'create fail,code:${sendMessageRes.code},${sendMessageRes.desc}', ); } } } } notifyListeners(); } // 发送消息(房间) Future _sendRoomMessage(Msg msg) async { try { if (msg.type == SCRoomMsgType.roomSettingUpdate) { debugPrint( "[Room Cover Sync] send roomSettingUpdate groupId=${msg.groupId ?? ""} roomId=${msg.msg ?? ""}", ); } var user = msg.user?.copyWith(); var toUser = msg.toUser?.copyWith(); user?.cleanWearHonor(); user?.cleanWearBadge(); user?.cleanPhotos(); toUser?.cleanWearHonor(); toUser?.cleanWearBadge(); toUser?.cleanUseProps(); toUser?.cleanPhotos(); msg.needUpDataUserInfo = Provider.of( context!, listen: false, ).needUpDataUserInfo; msg.user = user; msg.toUser = toUser; final textMsg = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .createCustomMessage(data: jsonEncode(msg.toJson())); if (textMsg.code != 0) return; final sendResult = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .sendMessage( id: textMsg.data!.id!, groupID: msg.groupId!, receiver: '', ); if (sendResult.code == 0) {} } catch (e) { throw Exception("create fail: $e"); } } /// 添加消息 addMsg(Msg msg) { roomAllMsgList.insert(0, msg); if (roomAllMsgList.length > 250) { print('大于200条消息'); roomAllMsgList.removeAt(roomAllMsgList.length - 1); } msgAllListener?.call(msg); if (msg.type == SCRoomMsgType.text) { roomChatMsgList.insert(0, msg); if (roomChatMsgList.length > 250) { print('大于200条消息'); roomChatMsgList.removeAt(roomChatMsgList.length - 1); } msgChatListener?.call(msg); } else if (msg.type == SCRoomMsgType.image) { roomChatMsgList.insert(0, msg); if (roomChatMsgList.length > 250) { print('大于200条消息'); roomChatMsgList.removeAt(roomChatMsgList.length - 1); } msgChatListener?.call(msg); } else if (msg.type == SCRoomMsgType.gift) { roomGiftMsgList.insert(0, msg); if (roomGiftMsgList.length > 250) { print('大于200条消息'); roomGiftMsgList.removeAt(roomGiftMsgList.length - 1); } msgGiftListener?.call(msg); msgFloatingGiftListener?.call(msg); } } bool isLogout = false; logout() async { V2TimCallback logoutRes = await TencentImSDKPlugin.v2TIMManager.logout(); TencentImSDKPlugin.v2TIMManager .getMessageManager() .removeAdvancedMsgListener(); if (logoutRes.code == 0) { isLogout = true; } } ///全服广播消息 _newBroadCastMsgRecv(String groupID, V2TimMessage message) { try { String? customData = message.customElem?.data; if (customData != null && customData.isNotEmpty) { final data = json.decode(customData); var type = data["type"]; if (type == "SYS_ACTIVITY") { if (onNewActivityMessageCurrentConversationListener != null) { var recode = Records.fromJson(data["data"]); onNewActivityMessageCurrentConversationListener?.call(recode); } else { activityUnReadCount = activityUnReadCount + 1; allUnReadCount = messageUnReadCount + notifcationUnReadCount + activityUnReadCount; notifyListeners(); } } else if (type == "SYS_ANNOUNCEMENT") { if (onNewNotifcationMessageCurrentConversationListener != null) { var recode = Records.fromJson(data["data"]); onNewNotifcationMessageCurrentConversationListener?.call(recode); } else { notifcationUnReadCount = notifcationUnReadCount + 1; allUnReadCount = messageUnReadCount + notifcationUnReadCount + activityUnReadCount; notifyListeners(); } } else if (type == "GAME_BAISHUN_WIN") { if (SCGlobalConfig.isReview) { ///审核状态不播放动画 return; } var fdata = data["data"]; var winCoins = fdata["currencyDiff"]; if (winCoins > 14999) { ///达到5000才飘屏 SCFloatingMessage msg = SCFloatingMessage( type: 2, userId: fdata["account"], userAvatarUrl: fdata["userAvatar"], userName: fdata["userNickname"], giftUrl: fdata["gameUrl"], roomId: fdata["roomId"], coins: fdata["currencyDiff"], ); OverlayManager().addMessage(msg); } } else if (type == "GAME_LUCKY_GIFT") { } else if (type == "ROCKET_ENERGY_LAUNCH") { ///火箭触发飘屏 var fdata = data["data"]; SCFloatingMessage msg = SCFloatingMessage( type: 3, roomId: fdata["roomId"], rocketLevel: fdata["fromLevel"], userAvatarUrl: fdata["userAvatar"], userName: fdata["nickname"], userId: fdata["actualAccount"], priority: 1000, ); OverlayManager().addMessage(msg); } else if (type == SCRoomMsgType.roomRedPacket) { ///红包触发飘屏 var fData = data["data"]; SCFloatingMessage msg = SCFloatingMessage( type: 4, roomId: fData["roomId"], userAvatarUrl: fData["userAvatar"], userName: fData["userNickname"], userId: fData["actualAccount"], toUserId: fData["packetId"], priority: 1000, ); if (msg.roomId == Provider.of( context!, listen: false, ).currenRoom?.roomProfile?.roomProfile?.id) { Provider.of( context!, listen: false, ).loadRoomRedPacketList(1); } OverlayManager().addMessage(msg); } else if (type == SCRoomMsgType.inviteRoom) { ///邀请进入房间 var fdata = data["data"]; SCFloatingMessage msg = SCFloatingMessage.fromJson(fdata); if (msg.toUserId == AccountStorage().getCurrentUser()?.userProfile?.id && msg.roomId != Provider.of( context!, listen: false, ).currenRoom?.roomProfile?.roomProfile?.id) { SmartDialog.dismiss(tag: "showInviteRoom"); SmartDialog.show( tag: "showInviteRoom", alignment: Alignment.center, animationType: SmartAnimationType.fade, builder: (_) { return InviteRoomDialog(msg); }, ); } } } } catch (e) {} } _newGroupMsg(String groupID, V2TimMessage message) { if (groupID != Provider.of( context!, listen: false, ).currenRoom?.roomProfile?.roomProfile?.roomAccount) { return; } try { String? customData = message.customElem?.data; if (customData != null && customData.isNotEmpty) { // 直接处理字符串格式的自定义数据 final data = json.decode(customData); debugPrint(">>>>>>>>>>>>>>>>>>>消息类型${data["type"]}"); if (data["type"] == SCRoomMsgType.roomRedPacket) { ///房间红包 var fData = data["data"]; SCFloatingMessage msg = SCFloatingMessage( type: 4, roomId: fData["roomId"], userAvatarUrl: fData["userAvatar"], userName: fData["userNickname"], userId: fData["actualAccount"], toUserId: fData["packetId"], priority: 1000, ); Provider.of( context!, listen: false, ).loadRoomRedPacketList(1); OverlayManager().addMessage(msg); return; } Msg msg = Msg.fromJson(data); if (msg.type == SCRoomMsgType.sendGift || msg.type == SCRoomMsgType.gameBurstCrystalSprint || msg.type == SCRoomMsgType.gameBurstCrystalBox) { ///这个消息暂时不监听 return; } if (msg.type == SCRoomMsgType.bsm) { if (msg.toUser?.id == AccountStorage().getCurrentUser()?.userProfile?.id) { SmartDialog.show( tag: "showConfirmDialog", alignment: Alignment.center, debounce: true, animationType: SmartAnimationType.fade, builder: (_) { return MsgDialog( title: SCAppLocalizations.of(context!)!.tips, msg: SCAppLocalizations.of( context!, )!.invitesYouToTheMicrophone(msg.msg ?? ""), btnText: SCAppLocalizations.of(context!)!.confirm, onEnsure: () { ///上麦 num index = Provider.of( context!, listen: false, ).findWheat(); if (index > -1) { Provider.of( context!, listen: false, ).shangMai( index, eventType: "INVITE", inviterId: msg.role, ); } }, ); }, ); } return; } if (msg.type == SCRoomMsgType.killXiaMai) { ///踢下麦 if (msg.msg == AccountStorage().getCurrentUser()?.userProfile?.id) { Provider.of( context!, listen: false, ).engine?.setClientRole(role: ClientRoleType.clientRoleAudience); } // Provider.of(context!, listen: false).getMicList(); return; } if (msg.type == SCRoomMsgType.roomSettingUpdate) { debugPrint( "[Room Cover Sync] recv roomSettingUpdate groupId=$groupID roomId=${msg.msg ?? ""}", ); Provider.of( context!, listen: false, ).loadRoomInfo(msg.msg ?? ""); return; } if (msg.type == SCRoomMsgType.roomBGUpdate) { SCRoomThemeListRes res; if ((msg.msg ?? "").isNotEmpty) { res = SCRoomThemeListRes.fromJson(jsonDecode(msg.msg!)); } else { res = SCRoomThemeListRes(); } Provider.of( context!, listen: false, ).updateRoomBG(res); return; } if (msg.type == SCRoomMsgType.emoticons) { Provider.of( context!, listen: false, ).starPlayEmoji(msg); return; } if (msg.type == SCRoomMsgType.micChange) { Provider.of( context!, listen: false, ).micChange(BroadCastMicChangePush.fromJson(data).data?.mics); } else if (msg.type == SCRoomMsgType.refreshOnlineUser) { Provider.of( context!, listen: false, ).fetchOnlineUsersList(); } else if (msg.type == SCRoomMsgType.gameLuckyGift) { var broadCastRes = SCBroadCastLuckGiftPush.fromJson(data); msg.gift = SocialChatGiftRes(giftPhoto: broadCastRes.data?.giftCover); msg.awardAmount = broadCastRes.data?.awardAmount; msg.user = SocialChatUserProfile( id: broadCastRes.data?.sendUserId, userNickname: broadCastRes.data?.nickname, ); msg.toUser = SocialChatUserProfile( id: broadCastRes.data?.acceptUserId, userNickname: broadCastRes.data?.acceptNickname, ); addMsg(msg); if ((broadCastRes.data?.multiple ?? 0) > 0) { Msg msg2 = Msg( groupId: '', msg: '${broadCastRes.data?.multiple}', type: SCRoomMsgType.gameLuckyGift_5, ); ///5倍率以上聊天页面需要发个消息 msg2.awardAmount = broadCastRes.data?.awardAmount; msg2.user = SocialChatUserProfile( id: broadCastRes.data?.sendUserId, userNickname: broadCastRes.data?.nickname, ); addMsg(msg2); if ((broadCastRes.data?.multiple ?? 0) > 2) { ///3倍率 SCFloatingMessage msg = SCFloatingMessage( type: 0, userId: broadCastRes.data?.sendUserId, roomId: broadCastRes.data?.roomId, toUserId: broadCastRes.data?.acceptUserId, userAvatarUrl: broadCastRes.data?.userAvatar, userName: broadCastRes.data?.nickname, toUserName: broadCastRes.data?.acceptNickname, giftUrl: broadCastRes.data?.giftCover, number: broadCastRes.data?.giftQuantity, coins: broadCastRes.data?.awardAmount, multiple: broadCastRes.data?.multiple, ); OverlayManager().addMessage(msg); addluckGiftPushQueue(broadCastRes); } } if (broadCastRes.data?.sendUserId == AccountStorage().getCurrentUser()?.userProfile?.id) { Provider.of( context!, listen: false, ).updateLuckyRewardAmount(msg.awardAmount ?? 0); } } else { if (msg.type == SCRoomMsgType.joinRoom) { if (msg.user != null) { Provider.of( context!, listen: false, ).addOnlineUser(msg.groupId ?? "", msg.user!); } if (msgUserJoinListener != null) { msgUserJoinListener!(msg); } ///坐骑 if (msg.user?.getMountains() != null) { if (SCGlobalConfig.isEntryVehicleAnimation) { SCGiftVapSvgaManager().play( msg.user?.getMountains()?.sourceUrl ?? "", priority: 100, type: 1, ); } } } else if (msg.type == SCRoomMsgType.gift) { final gift = msg.gift; final special = gift?.special ?? ""; final giftSourceUrl = gift?.giftSourceUrl ?? ""; final hasSource = giftSourceUrl.isNotEmpty; final hasAnimation = scGiftHasAnimationSpecial(special); final hasGlobalGift = special.contains(SCGiftType.GLOBAL_GIFT.name); final hasFullScreenEffect = scGiftHasFullScreenEffect(special); _giftFxLog( 'recv gift msg ' 'fromUserId=${msg.user?.id} ' 'fromUserName=${msg.user?.userNickname} ' 'toUserId=${msg.toUser?.id} ' 'toUserName=${msg.toUser?.userNickname} ' 'giftId=${gift?.id} ' 'giftName=${gift?.giftName} ' 'giftSourceUrl=$giftSourceUrl ' 'special=$special ' 'hasSource=$hasSource ' 'hasAnimation=$hasAnimation ' 'hasGlobalGift=$hasGlobalGift ' 'hasFullScreenEffect=$hasFullScreenEffect ' 'effectsEnabled=${SCGlobalConfig.isGiftSpecialEffects}', ); if (msg.gift!.giftSourceUrl != null && msg.gift!.special != null) { if (scGiftHasFullScreenEffect(msg.gift!.special)) { if (SCGlobalConfig.isGiftSpecialEffects) { _giftFxLog( 'trigger player play path=${msg.gift!.giftSourceUrl} ' 'giftId=${msg.gift?.id} giftName=${msg.gift?.giftName}', ); SCGiftVapSvgaManager().play(msg.gift!.giftSourceUrl!); } else { _giftFxLog( 'skip player play because isGiftSpecialEffects=false ' 'giftId=${msg.gift?.id}', ); } } else { _giftFxLog( 'skip player play because special does not include ' '${SCGiftType.ANIMSCION.name}/$kSCGiftAnimationSpecialAlias/${SCGiftType.GLOBAL_GIFT.name} ' 'giftId=${msg.gift?.id} special=${msg.gift?.special}', ); } } else { _giftFxLog( 'skip player play because giftSourceUrl or special is null ' 'giftId=${msg.gift?.id} ' 'giftSourceUrl=${msg.gift?.giftSourceUrl} ' 'special=${msg.gift?.special}', ); } if (Provider.of( context!, listen: false, ).currenRoom?.roomProfile?.roomSetting?.showHeartbeat ?? false) { debouncer.debounce( duration: Duration(milliseconds: 350), onDebounce: () { Provider.of( context!, listen: false, ).retrieveMicrophoneList(); }, ); } num coins = msg.number! * msg.gift!.giftCandy!; if (coins > 9999) { OverlayManager().addMessage( SCFloatingMessage( type: 1, userAvatarUrl: msg.user?.userAvatar ?? "", userName: msg.user?.userNickname ?? "", toUserName: msg.toUser?.userNickname ?? "", toUserAvatarUrl: msg.toUser?.userAvatar ?? "", giftUrl: msg.gift!.giftPhoto, number: msg.number, coins: coins, roomId: msg.msg, ), ); } } else if (msg.type == SCRoomMsgType.luckGiftAnimOther) { if (Provider.of( context!, listen: false, ).hideLGiftAnimal) { return; } eventBus.fire( GiveRoomLuckWithOtherEvent( msg.gift?.giftPhoto ?? "", (jsonDecode(msg.msg ?? "") as List) .map((e) => e as String) .toList(), ), ); return; } else if (msg.type == SCRoomMsgType.roomRoleChange) { ///房间身份变动 Provider.of( context!, listen: false, ).retrieveMicrophoneList(); if (msg.toUser?.id == AccountStorage().getCurrentUser()?.userProfile?.id) { Provider.of( context!, listen: false, ).currenRoom?.entrants?.setRoles(msg.msg); if (msg.msg == SCRoomRolesType.TOURIST.name && !(Provider.of( context!, listen: false, ).currenRoom?.roomProfile?.roomSetting?.touristMike ?? false)) { ///如果变成了游客,房间又是禁止游客上麦,需要下麦 num index = Provider.of( context!, listen: false, ).userOnMaiInIndex( AccountStorage().getCurrentUser()?.userProfile?.id ?? "", ); if (index > -1) { Provider.of( context!, listen: false, ).xiaMai(index); } } } } else if (msg.type == SCRoomMsgType.roomDice) { if ((msg.number ?? -1) > -1) { Provider.of( context!, listen: false, ).starPlayEmoji(msg); } } else if (msg.type == SCRoomMsgType.roomRPS) { if ((msg.number ?? -1) > -1) { Provider.of( context!, listen: false, ).starPlayEmoji(msg); } } addMsg(msg); } } } catch (e) { throw Exception("message parser fail: $e"); } } ///加入全服广播群 joinBigBroadcastGroup() async { bool joined = false; while (!isLogout && !joined) { await Future.delayed(Duration(milliseconds: 550)); try { var joinResult = await TencentImSDKPlugin.v2TIMManager.joinGroup( groupID: SCGlobalConfig.bigBroadcastGroup, message: "", ); if (joinResult.code == 0) { joined = true; } } catch (e) { //print('timm 登录异常:${e.toString()}'); SCTts.show('broadcastGroup join fail:${e.toString()}'); } } } ///发送全服消息 sendBigBroadcastGroup(BigBroadcastGroupMessage msg) async { try { final textMsg = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .createCustomMessage(data: jsonEncode(msg.toJson())); if (textMsg.code != 0) return; final sendResult = await TencentImSDKPlugin.v2TIMManager .getMessageManager() .sendMessage( id: textMsg.data!.id!, groupID: SCGlobalConfig.bigBroadcastGroup, receiver: '', ); if (sendResult.code == 0) {} } catch (e) { throw Exception("create fail: $e"); } } Future quitGroup(String groupID) async { await TencentImSDKPlugin.v2TIMManager.quitGroup(groupID: groupID); } ///清屏 void clearMessage() { roomAllMsgList.clear(); roomChatMsgList.clear(); roomGiftMsgList.clear(); msgChatListener?.call(Msg(groupId: "-1000", msg: "", type: "")); msgAllListener?.call(Msg(groupId: "-1000", msg: "", type: "")); msgGiftListener?.call(Msg(groupId: "-1000", msg: "", type: "")); notifyListeners(); } cleanRoomData() { roomAllMsgList.clear(); roomGiftMsgList.clear(); roomChatMsgList.clear(); _luckGiftPushQueue.clear(); currentPlayingLuckGift = null; onNewMessageListenerGroupMap.forEach((k, v) { v = null; }); onNewMessageListenerGroupMap.clear(); } void addluckGiftPushQueue(SCBroadCastLuckGiftPush broadCastRes) { if (SCGlobalConfig.isLuckGiftSpecialEffects) { _luckGiftPushQueue.add(broadCastRes); playLuckGiftBackCoins(); } } void cleanLuckGiftBackCoins() { _luckGiftPushQueue.clear(); } bool showLuckGiftBigHead = true; void playLuckGiftBackCoins() { if (currentPlayingLuckGift != null || _luckGiftPushQueue.isEmpty) { return; } currentPlayingLuckGift = _luckGiftPushQueue.removeFirst(); notifyListeners(); Future.delayed(Duration(milliseconds: 2300), () { showLuckGiftBigHead = false; }); Future.delayed(Duration(milliseconds: 3000), () { currentPlayingLuckGift = null; showLuckGiftBigHead = true; notifyListeners(); playLuckGiftBackCoins(); }); } void updateNotificationCount(int count) { notifcationUnReadCount = 0; allUnReadCount = messageUnReadCount + notifcationUnReadCount + activityUnReadCount; notifyListeners(); } void updateActivityCount(int count) { activityUnReadCount = 0; allUnReadCount = messageUnReadCount + notifcationUnReadCount + activityUnReadCount; notifyListeners(); } void updateSystemCount(int count) { for (final conversationId in SCGlobalConfig.systemConversationIds) { conversationMap[conversationId]?.unreadCount = 0; } systemUnReadCount = 0; notifyListeners(); } void updateCustomerCount(int count) { conversationMap["c2c_${customerInfo?.id}"]?.unreadCount = 0; customerUnReadCount = 0; notifyListeners(); } void clearC2CHistoryMessage(String conversationID, bool needShowToast) async { // 清空单聊本地及云端的消息(不删除会话) V2TimCallback clearC2CHistoryMessageRes = await TencentImSDKPlugin .v2TIMManager .getConversationManager() .deleteConversation(conversationID: conversationID); // 需要清空记录的用户id if (clearC2CHistoryMessageRes.code == 0) { // 清除成功 if (needShowToast) { SCTts.show(SCAppLocalizations.of(context!)!.operationSuccessful); } initConversation(); } else { // 清除失败,可以查看 clearC2CHistoryMessageRes.desc 获取错误描述 } } }