2017-09-11 22:54:15 +08:00
|
|
|
const config = require("./config.js");
|
|
|
|
const Logger = require("./logger.js");
|
|
|
|
const IDMapping = require("./id_mapping.js");
|
2017-09-15 01:06:23 +08:00
|
|
|
const UserMapping = require("./userMapping.js");
|
2017-08-25 00:22:30 +08:00
|
|
|
module.exports = class MessageMapping {
|
2017-09-11 22:54:15 +08:00
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.mappedObject = {};
|
|
|
|
this.mappedMessage = {};
|
2018-07-31 23:12:32 +08:00
|
|
|
this.meetingEvents = ["MeetingCreatedEvtMsg","MeetingDestroyedEvtMsg", "ScreenshareRtmpBroadcastStartedEvtMsg", "ScreenshareRtmpBroadcastStoppedEvtMsg", "SetCurrentPresentationEvtMsg", "RecordingStatusChangedEvtMsg"];
|
|
|
|
this.userEvents = ["UserJoinedMeetingEvtMsg","UserLeftMeetingEvtMsg","UserJoinedVoiceConfToClientEvtMsg","UserLeftVoiceConfToClientEvtMsg","PresenterAssignedEvtMsg", "PresenterUnassignedEvtMsg", "UserBroadcastCamStartedEvtMsg", "UserBroadcastCamStoppedEvtMsg", "UserEmojiChangedEvtMsg"];
|
2017-09-13 02:44:01 +08:00
|
|
|
this.chatEvents = ["SendPublicMessageEvtMsg","SendPrivateMessageEvtMsg"];
|
2017-09-11 22:54:15 +08:00
|
|
|
this.rapEvents = ["archive_started","archive_ended","sanity_started","sanity_ended","post_archive_started","post_archive_ended","process_started","process_ended","post_process_started","post_process_ended","publish_started","publish_ended","post_publish_started","post_publish_ended"];
|
2018-10-25 10:49:56 +08:00
|
|
|
|
|
|
|
this.compMeetingEvents = ["meeting_created_message","meeting_destroyed_event"];
|
|
|
|
this.compUserEvents = ["user_joined_message","user_left_message","user_listening_only","user_joined_voice_message","user_left_voice_message","user_shared_webcam_message","user_unshared_webcam_message","user_status_changed_message"];
|
2017-09-11 22:54:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Map internal message based on it's type
|
|
|
|
mapMessage(messageObj) {
|
2017-09-13 02:44:01 +08:00
|
|
|
if (this.mappedEvent(messageObj,this.meetingEvents)) {
|
2017-09-11 22:54:15 +08:00
|
|
|
this.meetingTemplate(messageObj);
|
2017-09-13 02:44:01 +08:00
|
|
|
} else if (this.mappedEvent(messageObj,this.userEvents)) {
|
2017-09-11 22:54:15 +08:00
|
|
|
this.userTemplate(messageObj);
|
2017-09-13 02:44:01 +08:00
|
|
|
} else if (this.mappedEvent(messageObj,this.chatEvents)) {
|
2017-09-11 22:54:15 +08:00
|
|
|
this.chatTemplate(messageObj);
|
2017-09-13 02:44:01 +08:00
|
|
|
} else if (this.mappedEvent(messageObj,this.rapEvents)) {
|
2017-09-11 22:54:15 +08:00
|
|
|
this.rapTemplate(messageObj);
|
2018-10-25 10:49:56 +08:00
|
|
|
} else if (this.mappedEvent(messageObj,this.compMeetingEvents)) {
|
|
|
|
this.compMeetingTemplate(messageObj);
|
|
|
|
} else if (this.mappedEvent(messageObj,this.compUserEvents)) {
|
|
|
|
this.compUserTemplate(messageObj);
|
2017-09-11 22:54:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-13 02:44:01 +08:00
|
|
|
mappedEvent(messageObj,events) {
|
|
|
|
return events.some( event => {
|
|
|
|
if ((messageObj.header != null ? messageObj.header.name : undefined) === event) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if ((messageObj.envelope != null ? messageObj.envelope.name : undefined) === event) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-09-11 22:54:15 +08:00
|
|
|
// Map internal to external message for meeting information
|
|
|
|
meetingTemplate(messageObj) {
|
2017-09-13 02:44:01 +08:00
|
|
|
const props = messageObj.core.body.props;
|
2018-07-31 23:12:32 +08:00
|
|
|
const meetingId = messageObj.core.body.meetingId || messageObj.core.header.meetingId;
|
2017-09-11 22:54:15 +08:00
|
|
|
this.mappedObject.data = {
|
|
|
|
"type": "event",
|
2017-09-13 02:44:01 +08:00
|
|
|
"id": this.mapInternalMessage(messageObj),
|
2017-09-11 22:54:15 +08:00
|
|
|
"attributes":{
|
|
|
|
"meeting":{
|
2018-07-31 23:12:32 +08:00
|
|
|
"internal-meeting-id": meetingId,
|
|
|
|
"external-meeting-id": IDMapping.getExternalMeetingID(meetingId)
|
2017-09-11 22:54:15 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"event":{
|
2017-09-13 02:44:01 +08:00
|
|
|
"ts": Date.now()
|
2017-09-11 22:54:15 +08:00
|
|
|
}
|
|
|
|
};
|
2017-09-13 02:44:01 +08:00
|
|
|
if (messageObj.envelope.name === "MeetingCreatedEvtMsg") {
|
2017-09-11 22:54:15 +08:00
|
|
|
this.mappedObject.data.attributes = {
|
|
|
|
"meeting":{
|
2017-09-13 02:44:01 +08:00
|
|
|
"internal-meeting-id": props.meetingProp.intId,
|
2017-11-02 03:06:22 +08:00
|
|
|
"external-meeting-id": props.meetingProp.extId,
|
|
|
|
"name": props.meetingProp.name,
|
|
|
|
"is-breakout": props.meetingProp.isBreakout,
|
|
|
|
"duration": props.durationProps.duration,
|
|
|
|
"create-time": props.durationProps.createdTime,
|
|
|
|
"create-date": props.durationProps.createdDate,
|
|
|
|
"moderator-pass": props.password.moderatorPass,
|
|
|
|
"viewer-pass": props.password.viewerPass,
|
|
|
|
"record": props.recordProp.record,
|
|
|
|
"voice-conf": props.voiceProp.voiceConf,
|
|
|
|
"dial-number": props.voiceProp.dialNumber,
|
|
|
|
"max-users": props.usersProp.maxUsers,
|
|
|
|
"metadata": props.metadataProp.metadata
|
|
|
|
}
|
2017-09-11 22:54:15 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
this.mappedMessage = JSON.stringify(this.mappedObject);
|
|
|
|
Logger.info("[MessageMapping] Mapped message:", this.mappedMessage);
|
|
|
|
}
|
|
|
|
|
2018-10-25 10:49:56 +08:00
|
|
|
compMeetingTemplate(messageObj) {
|
|
|
|
const props = messageObj.payload;
|
|
|
|
const meetingId = props.meeting_id;
|
|
|
|
this.mappedObject.data = {
|
|
|
|
"type": "event",
|
|
|
|
"id": this.mapInternalMessage(messageObj),
|
|
|
|
"attributes":{
|
|
|
|
"meeting":{
|
|
|
|
"internal-meeting-id": meetingId,
|
|
|
|
"external-meeting-id": IDMapping.getExternalMeetingID(meetingId)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"event":{
|
|
|
|
"ts": Date.now()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if (messageObj.header.name === "meeting_created_message") {
|
|
|
|
this.mappedObject.data.attributes = {
|
|
|
|
"meeting":{
|
|
|
|
"internal-meeting-id": meetingId,
|
|
|
|
"external-meeting-id": props.external_meeting_id,
|
|
|
|
"name": props.name,
|
|
|
|
"is-breakout": props.is_breakout,
|
|
|
|
"duration": props.duration,
|
|
|
|
"create-time": props.create_time,
|
|
|
|
"create-date": props.create_date,
|
|
|
|
"moderator-pass": props.moderator_pass,
|
|
|
|
"viewer-pass": props.viewer_pass,
|
|
|
|
"record": props.recorded,
|
|
|
|
"voice-conf": props.voice_conf,
|
|
|
|
"dial-number": props.dial_number,
|
|
|
|
"max-users": props.max_users,
|
|
|
|
"metadata": props.metadata
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
this.mappedMessage = JSON.stringify(this.mappedObject);
|
|
|
|
Logger.info("[MessageMapping] Mapped message:", this.mappedMessage);
|
|
|
|
}
|
|
|
|
|
2017-09-11 22:54:15 +08:00
|
|
|
// Map internal to external message for user information
|
|
|
|
userTemplate(messageObj) {
|
2017-09-13 02:44:01 +08:00
|
|
|
const msgBody = messageObj.core.body;
|
2017-11-02 03:06:22 +08:00
|
|
|
const msgHeader = messageObj.core.header;
|
2018-07-31 23:12:32 +08:00
|
|
|
const extId = UserMapping.getExternalUserID(msgHeader.userId) || msgBody.extId || "";
|
2017-09-11 22:54:15 +08:00
|
|
|
this.mappedObject.data = {
|
|
|
|
"type": "event",
|
2017-09-13 02:44:01 +08:00
|
|
|
"id": this.mapInternalMessage(messageObj),
|
2017-09-11 22:54:15 +08:00
|
|
|
"attributes":{
|
|
|
|
"meeting":{
|
2017-09-13 02:44:01 +08:00
|
|
|
"internal-meeting-id": messageObj.envelope.routing.meetingId,
|
|
|
|
"external-meeting-id": IDMapping.getExternalMeetingID(messageObj.envelope.routing.meetingId)
|
2017-09-11 22:54:15 +08:00
|
|
|
},
|
|
|
|
"user":{
|
2017-11-02 03:06:22 +08:00
|
|
|
"internal-user-id": msgHeader.userId,
|
2018-07-31 23:12:32 +08:00
|
|
|
"external-user-id": extId,
|
2017-09-13 02:44:01 +08:00
|
|
|
"sharing-mic": msgBody.muted,
|
|
|
|
"name": msgBody.name,
|
|
|
|
"role": msgBody.role,
|
|
|
|
"presenter": msgBody.presenter,
|
|
|
|
"stream": msgBody.stream,
|
|
|
|
"listening-only": msgBody.listenOnly
|
2017-09-11 22:54:15 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"event":{
|
2017-09-13 02:44:01 +08:00
|
|
|
"ts": Date.now()
|
2017-09-11 22:54:15 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
this.mappedMessage = JSON.stringify(this.mappedObject);
|
|
|
|
Logger.info("[MessageMapping] Mapped message:", this.mappedMessage);
|
|
|
|
}
|
|
|
|
|
2018-10-25 10:49:56 +08:00
|
|
|
// Map internal to external message for user information
|
|
|
|
compUserTemplate(messageObj) {
|
|
|
|
const msgBody = messageObj.payload;
|
|
|
|
const msgHeader = messageObj.header;
|
|
|
|
|
|
|
|
let user;
|
|
|
|
if (msgHeader.name === "user_joined_message") {
|
|
|
|
user = {
|
|
|
|
"internal-user-id": msgBody.user.userid,
|
|
|
|
"external-user-id": msgBody.user.extern_userid,
|
|
|
|
"sharing-mic": msgBody.user.voiceUser.joined,
|
|
|
|
"name": msgBody.user.name,
|
|
|
|
"role": msgBody.user.role,
|
|
|
|
"presenter": msgBody.user.presenter,
|
|
|
|
"stream": msgBody.user.webcam_stream,
|
|
|
|
"listening-only": msgBody.user.listenOnly
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
user = UserMapping.getUser(msgBody.userid) || { "internal-user-id": msgBody.userid || msgBody.user.userid };
|
|
|
|
if (msgHeader.name === "user_status_changed_message") {
|
|
|
|
if (msgBody.status === "presenter") {
|
|
|
|
user["presenter"] = msgBody.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (msgHeader.name === "user_listening_only") {
|
|
|
|
user["listening-only"] = msgBody.listen_only;
|
|
|
|
}
|
|
|
|
else if (msgHeader.name === "user_joined_voice_message" || msgHeader.name === "user_left_voice_message") {
|
|
|
|
user["sharing-mic"] = msgBody.user.voiceUser.joined;
|
|
|
|
}
|
|
|
|
else if (msgHeader.name === "user_shared_webcam_message") {
|
|
|
|
user["stream"].push(msgBody.stream);
|
|
|
|
}
|
|
|
|
else if (msgHeader.name === "user_unshared_webcam_message") {
|
|
|
|
let streams = user["stream"];
|
|
|
|
let index = streams.indexOf(msgBody.stream);
|
|
|
|
if (index != -1) {
|
|
|
|
streams.splice(index,1);
|
|
|
|
}
|
|
|
|
user["stream"] = streams;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.mappedObject.data = {
|
|
|
|
"type": "event",
|
|
|
|
"id": this.mapInternalMessage(messageObj),
|
|
|
|
"attributes":{
|
|
|
|
"meeting":{
|
|
|
|
"internal-meeting-id": msgBody.meeting_id,
|
|
|
|
"external-meeting-id": IDMapping.getExternalMeetingID(msgBody.meeting_id)
|
|
|
|
},
|
|
|
|
"user": user
|
|
|
|
},
|
|
|
|
"event":{
|
|
|
|
"ts": Date.now()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
this.mappedMessage = JSON.stringify(this.mappedObject);
|
|
|
|
Logger.info("[MessageMapping] Mapped message:", this.mappedMessage);
|
|
|
|
}
|
|
|
|
|
2017-09-11 22:54:15 +08:00
|
|
|
// Map internal to external message for chat information
|
|
|
|
chatTemplate(messageObj) {
|
2017-09-13 02:44:01 +08:00
|
|
|
const message = messageObj.core.body.message;
|
2017-09-11 22:54:15 +08:00
|
|
|
this.mappedObject.data = {
|
|
|
|
"type": "event",
|
2017-09-13 02:44:01 +08:00
|
|
|
"id": this.mapInternalMessage(messageObj),
|
2017-09-11 22:54:15 +08:00
|
|
|
"attributes":{
|
|
|
|
"meeting":{
|
2017-09-13 02:44:01 +08:00
|
|
|
"internal-meeting-id": messageObj.envelope.routing.meetingId,
|
|
|
|
"external-meeting-id": IDMapping.getExternalMeetingID(messageObj.envelope.routing.meetingId)
|
2017-09-11 22:54:15 +08:00
|
|
|
},
|
|
|
|
"chat-message":{
|
2017-09-13 02:44:01 +08:00
|
|
|
"message": message.message,
|
2017-09-11 22:54:15 +08:00
|
|
|
"sender":{
|
2017-09-13 02:44:01 +08:00
|
|
|
"internal-user-id": message.fromUserId,
|
|
|
|
"external-user-id": message.fromUsername,
|
|
|
|
"timezone-offset": message.fromTimezoneOffset,
|
|
|
|
"time": message.fromTime
|
2017-09-11 22:54:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"event":{
|
2017-09-13 02:44:01 +08:00
|
|
|
"ts": Date.now()
|
2017-09-11 22:54:15 +08:00
|
|
|
}
|
|
|
|
};
|
2017-09-13 02:44:01 +08:00
|
|
|
if (messageObj.envelope.name.indexOf("Private") !== -1) {
|
2017-09-11 22:54:15 +08:00
|
|
|
this.mappedObject.data.attributes["chat-message"].receiver = {
|
2017-09-13 02:44:01 +08:00
|
|
|
"internal-user-id": message.toUserId,
|
|
|
|
"external-user-id": message.toUsername
|
2017-09-11 22:54:15 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
this.mappedMessage = JSON.stringify(this.mappedObject);
|
|
|
|
Logger.info("[MessageMapping] Mapped message:", this.mappedMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
rapTemplate(messageObj) {
|
2018-08-25 04:02:17 +08:00
|
|
|
const data = messageObj.payload;
|
2017-09-11 22:54:15 +08:00
|
|
|
this.mappedObject.data = {
|
|
|
|
"type": "event",
|
2018-10-27 01:28:57 +08:00
|
|
|
"id": this.mapInternalMessage(messageObj),
|
2017-11-02 03:06:22 +08:00
|
|
|
"attributes": {
|
|
|
|
"meeting": {
|
|
|
|
"internal-meeting-id": data.meeting_id,
|
2018-11-04 11:27:01 +08:00
|
|
|
"external-meeting-id": data.external_meeting_id || IDMapping.getExternalMeetingID(data.meeting_id)
|
2017-11-02 03:06:22 +08:00
|
|
|
},
|
2018-10-27 01:28:57 +08:00
|
|
|
"record-id": data.record_id,
|
2018-08-25 04:02:17 +08:00
|
|
|
"success": data.success,
|
|
|
|
"step-time": data.step_time
|
2017-09-11 22:54:15 +08:00
|
|
|
},
|
2017-11-02 03:06:22 +08:00
|
|
|
"event": {
|
2017-09-11 22:54:15 +08:00
|
|
|
"ts": messageObj.header.current_time
|
|
|
|
}
|
|
|
|
};
|
2018-08-25 04:02:17 +08:00
|
|
|
|
2018-10-27 01:28:57 +08:00
|
|
|
if (data.workflow) {
|
|
|
|
this.mappedObject.data.attributes.workflow = data.workflow;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.mappedObject.data.id === "rap-publish-ended") {
|
|
|
|
this.mappedObject.data.attributes.recording = {
|
2018-08-25 04:02:17 +08:00
|
|
|
"name": data.metadata.meetingName,
|
2018-10-27 01:28:57 +08:00
|
|
|
"is-breakout": data.metadata.isBreakout,
|
|
|
|
"start-time": data.startTime,
|
|
|
|
"end-time": data.endTime,
|
2018-08-25 04:02:17 +08:00
|
|
|
"size": data.playback.size,
|
2018-10-27 01:28:57 +08:00
|
|
|
"raw-size": data.rawSize,
|
2018-08-25 04:02:17 +08:00
|
|
|
"metadata": data.metadata,
|
|
|
|
"playback": data.playback,
|
|
|
|
"download": data.download
|
|
|
|
}
|
|
|
|
}
|
2017-09-11 22:54:15 +08:00
|
|
|
this.mappedMessage = JSON.stringify(this.mappedObject);
|
|
|
|
Logger.info("[MessageMapping] Mapped message:", this.mappedMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mapInternalMessage(message) {
|
2018-10-25 10:49:56 +08:00
|
|
|
let name;
|
2017-09-13 02:44:01 +08:00
|
|
|
if (message.envelope) {
|
2018-10-25 10:49:56 +08:00
|
|
|
name = message.envelope.name
|
2017-09-13 02:44:01 +08:00
|
|
|
}
|
|
|
|
else if (message.header) {
|
2018-10-25 10:49:56 +08:00
|
|
|
name = message.header.name
|
2017-09-13 02:44:01 +08:00
|
|
|
}
|
2018-10-25 10:49:56 +08:00
|
|
|
const mappedMsg = (() => { switch (name) {
|
2017-09-13 02:44:01 +08:00
|
|
|
case "MeetingCreatedEvtMsg": return "meeting-created";
|
2017-09-15 01:06:23 +08:00
|
|
|
case "MeetingDestroyedEvtMsg": return "meeting-ended";
|
2018-07-31 23:12:32 +08:00
|
|
|
case "RecordingStatusChangedEvtMsg": return "meeting-recording-changed";
|
|
|
|
case "ScreenshareRtmpBroadcastStartedEvtMsg": return "meeting-screenshare-started";
|
|
|
|
case "ScreenshareRtmpBroadcastStoppedEvtMsg": return "meeting-screenshare-stopped";
|
|
|
|
case "SetCurrentPresentationEvtMsg": return "meeting-presentation-changed";
|
2017-09-13 02:44:01 +08:00
|
|
|
case "UserJoinedMeetingEvtMsg": return "user-joined";
|
|
|
|
case "UserLeftMeetingEvtMsg": return "user-left";
|
|
|
|
case "UserJoinedVoiceConfToClientEvtMsg": return "user-audio-voice-enabled";
|
|
|
|
case "UserLeftVoiceConfToClientEvtMsg": return "user-audio-voice-disabled";
|
|
|
|
case "UserBroadcastCamStartedEvtMsg": return "user-cam-broadcast-start";
|
2017-11-02 03:06:22 +08:00
|
|
|
case "UserBroadcastCamStoppedEvtMsg": return "user-cam-broadcast-end";
|
|
|
|
case "PresenterAssignedEvtMsg": return "user-presenter-assigned";
|
2018-07-31 23:12:32 +08:00
|
|
|
case "PresenterUnassignedEvtMsg": return "user-presenter-unassigned";
|
|
|
|
case "UserEmojiChangedEvtMsg": return "user-emoji-changed";
|
2017-09-13 02:44:01 +08:00
|
|
|
case "SendPublicMessageEvtMsg": return "chat-public-message-sent";
|
|
|
|
case "SendPrivateMessageEvtMsg": return "chat-private-message-sent";
|
2017-09-11 22:54:15 +08:00
|
|
|
case "archive_started": return "rap-archive-started";
|
|
|
|
case "archive_ended": return "rap-archive-ended";
|
|
|
|
case "sanity_started": return "rap-sanity-started";
|
|
|
|
case "sanity_ended": return "rap-sanity-ended";
|
|
|
|
case "post_archive_started": return "rap-post-archive-started";
|
|
|
|
case "post_archive_ended": return "rap-post-archive-ended";
|
|
|
|
case "process_started": return "rap-process-started";
|
|
|
|
case "process_ended": return "rap-process-ended";
|
|
|
|
case "post_process_started": return "rap-post-process-started";
|
|
|
|
case "post_process_ended": return "rap-post-process-ended";
|
|
|
|
case "publish_started": return "rap-publish-started";
|
|
|
|
case "publish_ended": return "rap-publish-ended";
|
|
|
|
case "post_publish_started": return "rap-post-publish-started";
|
|
|
|
case "post_publish_ended": return "rap-post-publish-ended";
|
2018-10-25 10:49:56 +08:00
|
|
|
case "meeting_created_message": return "meeting-created";
|
|
|
|
case "meeting_destroyed_event": return "meeting-ended";
|
|
|
|
case "user_joined_message": return "user-joined";
|
|
|
|
case "user_left_message": return "user-left";
|
2018-10-27 07:32:32 +08:00
|
|
|
case "user_listening_only": return (message.payload.listen_only ? "user-audio-listen-only-enabled" : "user-audio-listen-only-disabled");
|
2018-10-25 10:49:56 +08:00
|
|
|
case "user_joined_voice_message": return "user-audio-voice-enabled";
|
|
|
|
case "user_left_voice_message": return "user-audio-voice-disabled";
|
|
|
|
case "user_shared_webcam_message": return "user-cam-broadcast-start";
|
|
|
|
case "video_stream_unpublished": return "user-cam-broadcast-end";
|
|
|
|
case "user_status_changed_message":
|
|
|
|
if (message.payload.status === "presenter") {
|
|
|
|
return (message.payload.value === "true" ? "user-presenter-assigned" : "user-presenter-unassigned" );
|
|
|
|
}
|
2017-09-11 22:54:15 +08:00
|
|
|
} })();
|
|
|
|
return mappedMsg;
|
|
|
|
}
|
2017-08-25 00:22:30 +08:00
|
|
|
};
|