import 'dart:convert'; import 'package:bytedesk_kefu/bytedesk_kefu.dart'; import 'package:bytedesk_kefu/model/message.dart'; import 'package:bytedesk_kefu/model/thread.dart'; import 'package:bytedesk_kefu/model/user.dart'; // TODO: 条件引入web,格式类似:import 'src/configure_imp.dart' if (dart.library.html) 'src/configure_web.dart' as conf; // ignore: uri_does_not_exist import 'package:bytedesk_kefu/mqtt/lib/mqtt_browser_client.dart'; import 'package:bytedesk_kefu/mqtt/lib/mqtt_client.dart'; import 'package:bytedesk_kefu/mqtt/lib/mqtt_server_client.dart'; import 'package:bytedesk_kefu/util/bytedesk_constants.dart'; import 'package:bytedesk_kefu/model/messageProvider.dart'; import 'package:bytedesk_kefu/util/bytedesk_events.dart'; import 'package:bytedesk_kefu/util/bytedesk_extraparam.dart'; import 'package:bytedesk_kefu/util/bytedesk_utils.dart'; import 'package:bytedesk_kefu/util/bytedesk_uuid.dart'; import 'package:sp_util/sp_util.dart'; // FIXME: proto文件重新生成兼容null-safty报错 // ignore: import_of_legacy_library_into_null_safe import 'package:bytedesk_kefu/protobuf/message.pb.dart' as protomsg; // ignore: import_of_legacy_library_into_null_safe import 'package:bytedesk_kefu/protobuf/thread.pb.dart' as protothread; // ignore: import_of_legacy_library_into_null_safe import 'package:bytedesk_kefu/protobuf/user.pb.dart' as protouser; import 'package:flutter/services.dart'; // import 'package:fluttertoast/fluttertoast.dart'; // import 'package:vibration/vibration.dart'; class BytedeskMqtt { // var mqttClient; String? clientId; int keepAlivePeriod = 20; // final key = Key.fromUtf8('16BytesLengthKey'); // final iv = IV.fromUtf8('A-16-Byte-String'); MessageProvider messageProvider = new MessageProvider(); List midList = []; // bool _isConnected = false; String? currentUid; String? client; // 单例模式 static final BytedeskMqtt _singleton = BytedeskMqtt._internal(); factory BytedeskMqtt() { return _singleton; } BytedeskMqtt._internal(); void connect() async { // eventbus发送广播,连接中... bytedeskEventBus .fire(ConnectionEventBus(BytedeskConstants.USER_STATUS_CONNECTING)); // currentUid = SpUtil.getString(BytedeskConstants.uid); client = BytedeskUtils.getClient(); clientId = "$currentUid/$client"; //注意:必须要先判断web,否则在web运行会报错: // Unsupported operation: Platform._operatingSystem if (BytedeskUtils.isWeb) { if (BytedeskConstants.isDebug) { mqttClient = MqttBrowserClient.withPort( 'ws://127.0.0.1/websocket', clientId!, 3885); } else { mqttClient = MqttBrowserClient.withPort( BytedeskConstants.webSocketWssUrl, clientId!, 443); } } else { if (BytedeskConstants.isWebSocketWss) { mqttClient = MqttServerClient(BytedeskConstants.webSocketWssUrl, clientId!); mqttClient.useWebSocket = true; mqttClient.port = 443; /// You can also supply your own websocket protocol list or disable this feature using the websocketProtocols /// setter, read the API docs for further details here, the vast majority of brokers will support the client default /// list so in most cases you can ignore this. Mosquito needs the single default setting. // mqttClient.websocketProtocols = MqttClientConstants.protocolsSingleDefault; } else { mqttClient = MqttServerClient(BytedeskConstants.mqttHost, clientId!); mqttClient.port = BytedeskConstants.mqttPort; mqttClient.secure = BytedeskConstants.isSecure; } } // 启用3.1.1版本协议,否则clientId限制最大长度为23 mqttClient.setProtocolV311(); /// Set logging on if needed, defaults to off // mqttClient.logging(on: BytedeskConstants.isDebug); mqttClient.logging(on: false); // BytedeskConstants.isDebug /// If you intend to use a keep alive value in your connect message that is not the default(60s) /// you must set it here mqttClient.keepAlivePeriod = keepAlivePeriod; mqttClient.autoReconnect = true; // FIXME: // mqttClient.onAutoReconnect = _onAutoReconnect; // FIXME: mqttClient.onDisconnected = _onDisconnected; mqttClient.onConnected = _onConnected; mqttClient.onSubscribed = _onSubscribed; mqttClient.onUnsubscribed = _onUnSubscribed; mqttClient.onSubscribeFail = _onSubscribeFailed; /// Set a ping received callback if needed, called whenever a ping response(pong) is received from the broker. mqttClient.pongCallback = _onPong; /// Create a connection message to use or use the default one. The default one sets the /// client identifier, any supplied username/password, the default keepalive interval(60s) /// and clean session, an example of a specific one below. final MqttConnectMessage connMessage = MqttConnectMessage() .withClientIdentifier(clientId!) .authenticateAs('username', 'password'); // TODO: 服务器暂时不需要auth,随便填写 // .keepAliveFor(keepAlivePeriod); // Must agree with the keep alive set above or not set // 取消客户端设置,直接在服务器端统一内容格式推送 // .withWillTopic('protobuf/lastWill/mqtt') // If you set this you must set a will message // .withWillMessage('My Will message') // .startClean() // Non persistent session for testing // .withWillQos(MqttQos.atLeastOnce); BytedeskUtils.printLog('mqttClient connecting....'); mqttClient.connectionMessage = connMessage; /// Connect the client, any errors here are communicated by raising of the appropriate exception. Note /// in some circumstances the broker will just disconnect us, see the spec about this, we however eill /// never send malformed messages. try { await mqttClient.connect(); } on Exception catch (e) { BytedeskUtils.printLog('mqttClient exception - $e'); mqttClient.disconnect(); } /// Check we are connected if (mqttClient.connectionStatus.state == MqttConnectionState.connected) { BytedeskUtils.printLog('mqttClient connected'); } else { /// Use status here rather than state if you also want the broker return code. BytedeskUtils.printLog( 'ERROR mqttClient connection failed - disconnecting, status is ${mqttClient.connectionStatus}'); mqttClient.disconnect(); // exit(-1); } /// The client has a change notifier object(see the Observable class) which we then listen to to get /// notifications of published updates to each subscribed topic. // mqttClient.updates!.listen((List> c) { // final MqttPublishMessage recMess = c[0].payload; // final String pt = // MqttPublishPayload.bytesToStringAsString(recMess.payload.message); // // BytedeskUtils.printLog('Change notification:: topic is <${c[0].topic}>, payload is <-- $pt -->'); // }); /// 收到的消息 // if (mqttClient != null && mqttClient.published != null) { // mqttClient.published!.listen((MqttPublishMessage messageBinary) { // BytedeskUtils.printLog('Published notification:: topic is ${messageBinary.variableHeader.topicName}, with Qos ${messageBinary.header.qos}'); // protomsg.Message messageProto = protomsg.Message.fromBuffer(messageBinary.payload.message); // FIXME: 自己发送的消息显示两条?此处根据mid去个重 var mid = messageProto.mid; if (midList.contains(mid)) { return; } midList.add(mid); // var uid = messageProto.user.uid; var username = messageProto.user.username; var nickname = messageProto.user.nickname; var avatar = messageProto.user.avatar; // var content = ''; var type = messageProto.type; var timestamp = messageProto.timestamp; var client = messageProto.client; // BytedeskUtils.printLog('bytedesk_mqtt.dart receive type:' + type + ' client:' + client + ' mid:' + mid); // 非会话消息,如:会议通知等, 另行处理 if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_NOTICE) { // TODO: 待处理 return; } // User user = new User( uid: uid, username: username, nickname: nickname, avatar: avatar); // Thread thread = new Thread( tid: messageProto.thread.tid, type: messageProto.thread.type, // uid: '', nickname: messageProto.thread.nickname, avatar: messageProto.thread.avatar, content: messageProto.thread.content, timestamp: messageProto.thread.timestamp, unreadCount: messageProto.thread.unreadCount, topic: messageProto.thread.topic, client: client); Message message = new Message( mid: mid, content: content, imageUrl: content, nickname: nickname, avatar: avatar, type: type, timestamp: timestamp, status: 'stored', isSend: uid == currentUid ? 1 : 0, currentUid: currentUid, thread: thread, user: user, client: client); // 是否发送消息回执 // var autoReply = false; var sendReceipt = false; // var webRTCVideoInvite = false; // var webRTCAudioInvite = false; switch (type) { case BytedeskConstants.MESSAGE_TYPE_TEXT: { // // autoReply = true; sendReceipt = true; // TODO: 判断是否加密,暂时不需要 message.content = messageProto.text.content; BytedeskUtils.printLog('receive text:' + message.content!); break; } case BytedeskConstants.MESSAGE_TYPE_IMAGE: { // // autoReply = true; sendReceipt = true; message.imageUrl = messageProto.image.imageUrl; break; } case BytedeskConstants.MESSAGE_TYPE_VOICE: { // // autoReply = true; sendReceipt = true; message.voiceUrl = messageProto.voice.voiceUrl; break; } case BytedeskConstants.MESSAGE_TYPE_FILE: { // // autoReply = true; sendReceipt = true; message.fileUrl = messageProto.file.fileUrl; break; } case BytedeskConstants.MESSAGE_TYPE_VIDEO: case BytedeskConstants.MESSAGE_TYPE_SHORT_VIDEO: { // // autoReply = true; sendReceipt = true; message.videoUrl = messageProto.video.videoOrShortUrl; break; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_THREAD: { message.content = messageProto.text.content; break; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_THREAD_REENTRY: case BytedeskConstants.MESSAGE_TYPE_COMMODITY: case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_CONNECT: case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_DISCONNECT: { message.content = messageProto.text.content; break; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_QUEUE: { message.content = messageProto.text.content; break; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_QUEUE_ACCEPT: { // 替换 'joinQueueThread' message.content = '接入队列会话'; // TODO: 国际化 break; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_AGENT_CLOSE: case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_VISITOR_CLOSE: case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_AUTO_CLOSE: { message.content = messageProto.text.content; break; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_TICKET: { // TODO: 工单消息 message.content = messageProto.text.content; break; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_FEEDBACK: { // TODO: 意见反馈 message.content = messageProto.text.content; break; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_CHANNEL: { // TODO: 渠道消息 message.content = messageProto.text.content; break; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_PREVIEW: { // 对方正在输入 String uid = messageProto.user.uid; String previewContent = messageProto.preview.content; if (uid != currentUid) { bytedeskEventBus .fire(ReceiveMessagePreviewEventBus(previewContent)); } return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_RECEIPT: { // 消息回执 var midR = messageProto.receipt.mid; var statusR = messageProto.receipt.status; messageProvider.update(midR, statusR); // 通知界面更新聊天记录状态 bytedeskEventBus.fire(ReceiveMessageReceiptEventBus(midR, statusR)); return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_RECALL: { // 消息撤回 String mid = messageProto.recall.mid; bytedeskEventBus.fire(DeleteMessageEventBus(mid)); return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_TRANSFER: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_TRANSFER_ACCEPT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_TRANSFER_REJECT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_INVITE: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_INVITE_ACCEPT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_INVITE_REJECT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_INVITE_RATE: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_RATE_RESULT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_INVITE_VIDEO: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_INVITE_AUDIO: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_CANCEL: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_OFFER_VIDEO: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_OFFER_AUDIO: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_ANSWER: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_CANDIDATE: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_ACCEPT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_REJECT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_READY: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_BUSY: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_CLOSE: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_CREATE: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_UPDATE: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_ANNOUNCEMENT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_INVITE: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_INVITE_ACCEPT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_INVITE_REJECT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_APPLY: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_APPLY_APPROVE: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_APPLY_DENY: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_KICK: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_MUTE: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_TRANSFER: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_TRANSFER_ACCEPT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_TRANSFER_REJECT: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_WITHDRAW: { // return; } case BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_DISMISS: { // return; } case BytedeskConstants.MESSAGE_TYPE_LOCATION: { // return; } case BytedeskConstants.MESSAGE_TYPE_LINK: { // return; } case BytedeskConstants.MESSAGE_TYPE_EVENT: { // return; } case BytedeskConstants.MESSAGE_TYPE_CUSTOM: { // return; } case BytedeskConstants.MESSAGE_TYPE_RED_PACKET: { // return; } default: BytedeskUtils.printLog('other message type:' + type); } // 客服端使用 // if (autoReply) { // // // } // if (sendReceipt && message.isSend == 0) { // 发送送达回执 sendReceiptReceivedMessage(mid, thread); } // // final encrypter = Encrypter(AES(key, mode: AESMode.cbc)); // final decrypted = encrypter.decrypt64(messageProto.text.content, iv: iv); // BytedeskUtils.printLog('content: ' + decrypted); // // 忽略连接信息 if (message.content == 'visitorConnect' || message.content == 'visitorDisconnect') { return; } // 插入本地数据库 // if (messageProvider != null) { messageProvider.insert(message); // } // 通知界面显示聊天记录 bytedeskEventBus.fire(ReceiveMessageEventBus(message)); // 接收消息播放提示音,放到SDK外实现,迁移到demo中 if (BytedeskKefu.getPlayAudioOnReceiveMessage()! && message.isSend == 0) { // BytedeskUtils.printLog('play audio'); SystemSound.play(SystemSoundType.click); } // 振动,放到SDK外实现,迁移到demo中 if (BytedeskKefu.getVibrateOnReceiveMessage()! && message.isSend == 0) { // BytedeskUtils.printLog('should vibrate'); vibrate(); } }); // // } else { // BytedeskUtils.printLog('mqttClient.published is null'); // } } // FIXME: ld: library not found for -lvibration void vibrate() async { // if (await Vibration.hasVibrator()) { // Vibration.vibrate(); // } } void subscribe(String topic) { // BytedeskUtils.printLog('Subscribing to the hello topic'); mqttClient.subscribe(topic, MqttQos.exactlyOnce); } void unsubscribe(String topic) { mqttClient.unsubscribe(topic); } void sendTextMessage(String content, Thread currentThread) { publish(content, BytedeskConstants.MESSAGE_TYPE_TEXT, currentThread, null); } void sendImageMessage(String content, Thread currentThread) { publish(content, BytedeskConstants.MESSAGE_TYPE_IMAGE, currentThread, null); } void sendFileMessage(String content, Thread currentThread) { publish(content, BytedeskConstants.MESSAGE_TYPE_FILE, currentThread, null); } void sendVoiceMessage(String content, Thread currentThread) { publish(content, BytedeskConstants.MESSAGE_TYPE_VOICE, currentThread, null); } void sendVideoMessage(String content, Thread currentThread) { publish(content, BytedeskConstants.MESSAGE_TYPE_VIDEO, currentThread, null); } // 商品消息 void sendCommodityMessage(String content, Thread currentThread) { publish( content, BytedeskConstants.MESSAGE_TYPE_COMMODITY, currentThread, null); } // 消息预知 void sendPreviewMessage(String previewContent, Thread currentThread) { publish(previewContent, BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_PREVIEW, currentThread, null); } // 消息撤回 void sendRecallMessage(String mid, Thread currentThread) { ExtraParam extraParam = new ExtraParam(); extraParam.recallMid = mid; publish(mid, BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_RECALL, currentThread, extraParam); } // 送达回执 void sendReceiptReceivedMessage(String mid, Thread currentThread) { sendReceiptMessage( mid, BytedeskConstants.MESSAGE_STATUS_RECEIVED, currentThread); } // 已读回执 void sendReceiptReadMessage(String mid, Thread currentThread) { sendReceiptMessage( mid, BytedeskConstants.MESSAGE_STATUS_READ, currentThread); } void sendReceiptMessage(String mid, String status, Thread currentThread) { ExtraParam extraParam = new ExtraParam(); extraParam.receiptMid = mid; extraParam.receiptStatus = status; publish('content', BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_RECEIPT, currentThread, extraParam); } void publish(String content, String type, Thread currentThread, ExtraParam? extraParam) { // if (currentThread == null) { // BytedeskUtils.printLog('连接客服失败,请退出页面重新进入。注意: 请在App启动的时候,调用init接口'); // Fluttertoast.showToast(msg: '连接客服失败,请退出页面重新进入'); // return; // } // https://pub.dev/packages/encrypt#aes // final encrypter = Encrypter(AES(key, mode: AESMode.cbc)); // final encrypted = encrypter.encrypt(content, iv: iv); // BytedeskUtils.printLog(encrypted.base64); // final decrypted = encrypter.decrypt(encrypted, iv: iv); // BytedeskUtils.printLog(decrypted); // thread protothread.Thread thread = new protothread.Thread(); thread.tid = currentThread.tid!; thread.type = currentThread.type!; thread.topic = currentThread.topic!; thread.nickname = currentThread.nickname!; thread.avatar = currentThread.avatar!; thread.client = currentThread.client!; thread.timestamp = BytedeskUtils.formatedDateNow(); // 没有必要填写,服务器端会填充 thread.unreadCount = 0; var extra = {'top': false, 'undisturb': false}; thread.extra = jsonEncode(extra); // user protouser.User user = new protouser.User(); user.uid = SpUtil.getString(BytedeskConstants.uid)!; user.username = SpUtil.getString(BytedeskConstants.username)!; user.nickname = SpUtil.getString(BytedeskConstants.nickname)!; user.avatar = SpUtil.getString(BytedeskConstants.avatar)!; // msg protomsg.Message messageProto = new protomsg.Message(); messageProto.mid = BytedeskUuid.uuid(); messageProto.type = type; messageProto.timestamp = BytedeskUtils.formatedDateNow(); messageProto.client = BytedeskUtils.getClient(); //BytedeskConstants.client; messageProto.version = '1'; messageProto.encrypted = false; // 用来在发送之前显示到界面 Message message = new Message(); message.mid = messageProto.mid; message.type = messageProto.type; message.timestamp = messageProto.timestamp; message.client = messageProto.client; message.nickname = user.nickname; message.avatar = user.avatar; message.topic = thread.topic; message.status = BytedeskConstants.MESSAGE_STATUS_SENDING; message.isSend = 1; message.currentUid = currentUid; message.answersJson = ''; message.thread = currentThread; message.user = User(uid: user.uid, avatar: user.avatar, nickname: user.nickname); // 判断是否应该插入本地并显示 bool shouldInsertLocal = false; // 发送protobuf if (type == BytedeskConstants.MESSAGE_TYPE_TEXT) { protomsg.Text text = new protomsg.Text(); text.content = content; messageProto.text = text; // thread.content = content; // shouldInsertLocal = true; message.content = content; } else if (type == BytedeskConstants.MESSAGE_TYPE_IMAGE) { protomsg.Image image = new protomsg.Image(); image.imageUrl = content; messageProto.image = image; // thread.content = '[图片]'; // shouldInsertLocal = true; message.imageUrl = content; } else if (type == BytedeskConstants.MESSAGE_TYPE_VOICE) { protomsg.Voice voice = new protomsg.Voice(); voice.voiceUrl = content; // voice.length // voice.format messageProto.voice = voice; // thread.content = '[语音]'; // shouldInsertLocal = true; message.voiceUrl = content; } else if (type == BytedeskConstants.MESSAGE_TYPE_FILE) { protomsg.File file = new protomsg.File(); file.fileUrl = content; messageProto.file = file; // thread.content = '[文件]'; // shouldInsertLocal = true; message.fileUrl = content; } else if (type == BytedeskConstants.MESSAGE_TYPE_VIDEO || type == BytedeskConstants.MESSAGE_TYPE_SHORT_VIDEO) { protomsg.Video video = new protomsg.Video(); video.videoOrShortUrl = content; messageProto.video = video; // thread.content = '[视频]'; // shouldInsertLocal = true; message.videoUrl = content; } else if (type == BytedeskConstants.MESSAGE_TYPE_COMMODITY) { protomsg.Text text = new protomsg.Text(); text.content = content; messageProto.text = text; // thread.content = '[商品]'; // shouldInsertLocal = true; message.content = content; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_PREVIEW) { protomsg.Preview preview = new protomsg.Preview(); preview.content = content; messageProto.preview = preview; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_RECEIPT) { // 发送消息回执 protomsg.Receipt receipt = new protomsg.Receipt(); receipt.mid = extraParam!.receiptMid!; receipt.status = extraParam.receiptStatus!; // messageProto.receipt = receipt; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_RECALL) { // protomsg.Recall recall = new protomsg.Recall(); recall.mid = extraParam!.recallMid!; // messageProto.recall = recall; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_TRANSFER) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_TRANSFER_ACCEPT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_TRANSFER_REJECT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_INVITE) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_INVITE_ACCEPT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_INVITE_REJECT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_INVITE_RATE) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_RATE_RESULT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_INVITE_VIDEO) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_INVITE_AUDIO) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_CANCEL) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_OFFER_VIDEO) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_OFFER_AUDIO) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_ANSWER) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_CANDIDATE) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_ACCEPT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_REJECT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_READY) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_BUSY) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_WEBRTC_CLOSE) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_CREATE) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_UPDATE) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_ANNOUNCEMENT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_INVITE) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_INVITE_ACCEPT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_INVITE_REJECT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_APPLY) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_APPLY_APPROVE) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_APPLY_DENY) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_KICK) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_MUTE) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_TRANSFER) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_TRANSFER_ACCEPT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_TRANSFER_REJECT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_WITHDRAW) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_GROUP_DISMISS) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_LOCATION) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_LINK) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_EVENT) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_CUSTOM) { // return; } else if (type == BytedeskConstants.MESSAGE_TYPE_RED_PACKET) { // return; } else { // 其他类型消息 BytedeskUtils.printLog('other type:' + type); } // messageProto.user = user; messageProto.thread = thread; // 先通知界面本地显示聊天记录,然后再发送 if (shouldInsertLocal) { // 插入本地数据库 // if (messageProvider != null) { messageProvider.insert(message); // } bytedeskEventBus.fire(ReceiveMessageEventBus(message)); midList.add(message.mid!); } // final MqttClientPayloadBuilder builder = MqttClientPayloadBuilder(); // 注意:此函数为自行添加,原先库中没有 builder.addProtobuf(messageProto.writeToBuffer()); mqttClient.publishMessage( currentThread.topic, MqttQos.exactlyOnce, builder.payload); // 播放发送消息提示音,放到SDK外实现,迁移到demo中 if (BytedeskKefu.getPlayAudioOnSendMessage()!) { // } } // 断开长连接 void disconnect() { if (mqttClient != null) { mqttClient.disconnect(); } } /// The subscribed callback void _onSubscribed(String? topic) { // BytedeskUtils.printLog('Subscription confirmed for topic $topic'); } /// The unsubscribed callback void _onUnSubscribed(String? topic) { // BytedeskUtils.printLog('UnSubscription confirmed for topic $topic'); } /// The subscribed callback void _onSubscribeFailed(String? topic) { // BytedeskUtils.printLog('Subscribe Failed confirmed for topic $topic'); } /// The unsolicited disconnect callback void _onDisconnected() { // _isConnected = false; // BytedeskUtils.printLog('OnDisconnected client callback - Client disconnection'); // eventbus发广播,通知长连接断开 bytedeskEventBus .fire(ConnectionEventBus(BytedeskConstants.USER_STATUS_DISCONNECTED)); // if (mqttClient.connectionStatus.returnCode == MqttConnectReturnCode.solicited) { // BytedeskUtils.printLog('OnDisconnected callback is solicited, this is correct'); // } // 延时10s执行重连 Future.delayed(Duration(seconds: 10), () { // BytedeskUtils.printLog('start reconnecting'); // reconnect(); }); } /// The unsolicited disconnect callback // void _onAutoReconnect() { // // BytedeskUtils.printLog('EXAMPLE::onAutoReconnect client callback - Client auto reconnection sequence will start'); // // connect(); // } /// The successful connect callback void _onConnected() { // _isConnected = true; // BytedeskUtils.printLog('OnConnected client callback - Client connection was sucessful'); // TODO: eventbus发广播,通知长连接建立 bytedeskEventBus .fire(ConnectionEventBus(BytedeskConstants.USER_STATUS_CONNECTED)); } /// Pong callback void _onPong() { // BytedeskUtils.printLog('Ping response client callback invoked'); } bool isConnected() { // return _isConnected; return mqttClient.connectionStatus.state == MqttConnectionState.connected; } }