* LCIMMessage.cs:

* LCIMFileMessage.cs:
* LCIMTextMessage.cs:
* LCIMAudioMessage.cs:
* LCIMImageMessage.cs:
* LCIMTypedMessage.cs:
* LCIMBinaryMessage.cs:
* LCIMLocationMessage.cs:
* LCIMRecalledMessage.cs:
* LCIMMessageController.cs:

* LCIMVideoMessage.cs: chore: 重构并支持自定义类型消息
oneRain 2020-04-27 17:44:14 +08:00
parent 5379ee1285
commit fb830691c4
11 changed files with 342 additions and 133 deletions

View File

@ -32,7 +32,7 @@ namespace LeanCloud.Realtime.Internal.Controller {
} else if (message is LCIMBinaryMessage binaryMessage) {
direct.BinaryMsg = ByteString.CopyFrom(binaryMessage.Data);
} else {
throw new ArgumentException("Message MUST BE LCIMTypedMessage or LCIMBinaryMessage.");
throw new ArgumentException("Message MUST be LCIMTypedMessage or LCIMBinaryMessage.");
}
// 暂态消息
if (options.Transient) {

View File

@ -2,14 +2,15 @@
using LeanCloud.Storage;
namespace LeanCloud.Realtime {
/// <summary>
/// 音频消息
/// </summary>
public class LCIMAudioMessage : LCIMFileMessage {
/// <summary>
/// 时长
/// </summary>
public double Duration {
get {
if (double.TryParse(File.MetaData["duration"] as string, out double duration)) {
return duration;
}
return 0;
}
get; private set;
}
internal LCIMAudioMessage() {
@ -21,14 +22,22 @@ namespace LeanCloud.Realtime {
internal override Dictionary<string, object> Encode() {
Dictionary<string, object> data = base.Encode();
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
if (File.MetaData.TryGetValue("duration", out object duration)) {
metaData["duration"] = duration;
Dictionary<string, object> fileData = data[MessageFileKey] as Dictionary<string, object>;
Dictionary<string, object> metaData = fileData[MessageDataMetaDataKey] as Dictionary<string, object>;
if (File.MetaData.TryGetValue(MessageDataMetaDurationKey, out object duration)) {
metaData[MessageDataMetaDurationKey] = duration;
}
return data;
}
internal override int MessageType => AudioMessageType;
internal override void Decode(Dictionary<string, object> msgData) {
base.Decode(msgData);
if (File.MetaData.TryGetValue(MessageDataMetaDurationKey, out object duration) &&
double.TryParse(duration as string, out double d)) {
Duration = d;
}
}
public override int MessageType => AudioMessageType;
}
}

View File

@ -1,5 +1,11 @@
namespace LeanCloud.Realtime {
/// <summary>
/// 二进制消息
/// </summary>
public class LCIMBinaryMessage : LCIMMessage {
/// <summary>
/// 消息数据
/// </summary>
public byte[] Data {
get; internal set;
}

View File

@ -3,29 +3,41 @@ using System.Collections.Generic;
using LeanCloud.Storage;
namespace LeanCloud.Realtime {
/// <summary>
/// 文件消息
/// </summary>
public class LCIMFileMessage : LCIMTextMessage {
/// <summary>
/// 文件
/// </summary>
public LCFile File {
get; set;
}
/// <summary>
/// 文件大小
/// </summary>
public int Size {
get {
if (int.TryParse(File.MetaData["size"] as string, out int size)) {
if (int.TryParse(File.MetaData[MessageDataMetaSizeKey] as string, out int size)) {
return size;
}
return 0;
}
}
/// <summary>
/// 文件类型
/// </summary>
public string Format {
get {
if (File.MetaData.TryGetValue("format", out object format)) {
return format as string;
}
return "unknown/unknown";
return File.MimeType;
}
}
/// <summary>
/// 文件链接
/// </summary>
public string Url {
get {
return File.Url;
@ -40,41 +52,46 @@ namespace LeanCloud.Realtime {
File = file;
}
internal override int MessageType => FileMessageType;
public override int MessageType => FileMessageType;
internal override Dictionary<string, object> Encode() {
if (File == null) {
throw new Exception("File MUST NOT be null before sent.");
}
Dictionary<string, object> fileData = new Dictionary<string, object> {
{ "objId", File.ObjectId },
{ "url", File.Url },
{ "metaData", new Dictionary<string, object> {
{ "name", File.Name },
{ "format", File.MimeType }
{ MessageDataObjectIdKey, File.ObjectId },
{ MessageDataUrlKey, File.Url },
{ MessageDataMetaDataKey, new Dictionary<string, object> {
{ MessageDataMetaNameKey, File.Name },
{ MessageDataMetaFormatKey, File.MimeType }
} }
};
if (File.MetaData.TryGetValue("size", out object size)) {
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
metaData["size"] = size;
if (File.MetaData.TryGetValue(MessageDataMetaSizeKey, out object size)) {
Dictionary<string, object> metaData = fileData[MessageDataMetaDataKey] as Dictionary<string, object>;
metaData[MessageDataMetaSizeKey] = size;
}
Dictionary<string, object> data = base.Encode();
data["_lcfile"] = fileData;
data[MessageFileKey] = fileData;
return data;
}
protected override void DecodeMessageData(Dictionary<string, object> msgData) {
base.DecodeMessageData(msgData);
Dictionary<string, object> fileData = msgData["_lcfile"] as Dictionary<string, object>;
string objectId = fileData["objId"] as string;
File = LCObject.CreateWithoutData(LCFile.CLASS_NAME, objectId) as LCFile;
if (fileData.TryGetValue("url", out object url)) {
File.Url = url as string;
}
if (fileData.TryGetValue("metaData", out object metaData)) {
File.MetaData = metaData as Dictionary<string, object>;
if (File.MetaData.TryGetValue("name", out object name)) {
File.Name = name as string;
internal override void Decode(Dictionary<string, object> msgData) {
base.Decode(msgData);
if (msgData.TryGetValue(MessageFileKey, out object fileDataObject)) {
Dictionary<string, object> fileData = fileDataObject as Dictionary<string, object>;
if (fileData.TryGetValue(MessageDataObjectIdKey, out object objectIdObject)) {
string objectId = objectIdObject as string;
File = LCObject.CreateWithoutData(LCFile.CLASS_NAME, objectId) as LCFile;
if (fileData.TryGetValue(MessageDataUrlKey, out object url)) {
File.Url = url as string;
}
if (fileData.TryGetValue(MessageDataMetaDataKey, out object metaData)) {
File.MetaData = metaData as Dictionary<string, object>;
if (File.MetaData.TryGetValue(MessageDataMetaNameKey, out object name)) {
File.Name = name as string;
}
}
}
}
}

View File

@ -2,23 +2,22 @@
using LeanCloud.Storage;
namespace LeanCloud.Realtime {
/// <summary>
/// 图像消息
/// </summary>
public class LCIMImageMessage : LCIMFileMessage {
/// <summary>
/// 图像宽度
/// </summary>
public int Width {
get {
if (int.TryParse(File.MetaData["width"] as string, out int width)) {
return width;
}
return 0;
}
get; private set;
}
/// <summary>
/// 图像高度
/// </summary>
public int Height {
get {
if (int.TryParse(File.MetaData["height"] as string, out int height)) {
return height;
}
return 0;
}
get; private set;
}
internal LCIMImageMessage() : base() {
@ -30,17 +29,29 @@ namespace LeanCloud.Realtime {
internal override Dictionary<string, object> Encode() {
Dictionary<string, object> data = base.Encode();
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
if (File.MetaData.TryGetValue("width", out object width)) {
metaData["width"] = width;
Dictionary<string, object> fileData = data[MessageFileKey] as Dictionary<string, object>;
Dictionary<string, object> metaData = fileData[MessageDataMetaDataKey] as Dictionary<string, object>;
if (File.MetaData.TryGetValue(MessageDataMetaWidthKey, out object width)) {
metaData[MessageDataMetaWidthKey] = width;
}
if (File.MetaData.TryGetValue("height", out object height)) {
metaData["height"] = height;
if (File.MetaData.TryGetValue(MessageDataMetaHeightKey, out object height)) {
metaData[MessageDataMetaHeightKey] = height;
}
return data;
}
internal override int MessageType => ImageMessageType;
internal override void Decode(Dictionary<string, object> msgData) {
base.Decode(msgData);
if (File.MetaData.TryGetValue(MessageDataMetaWidthKey, out object width) &&
int.TryParse(width as string, out int w)) {
Width = w;
}
if (File.MetaData.TryGetValue(MessageDataMetaHeightKey, out object height) &&
int.TryParse(height as string, out int h)) {
Height = h;
}
}
public override int MessageType => ImageMessageType;
}
}

View File

@ -2,7 +2,13 @@
using LeanCloud.Storage;
namespace LeanCloud.Realtime {
/// <summary>
/// 位置消息
/// </summary>
public class LCIMLocationMessage : LCIMTextMessage {
/// <summary>
/// 位置
/// </summary>
public LCGeoPoint Location {
get; set;
}
@ -17,19 +23,30 @@ namespace LeanCloud.Realtime {
internal override Dictionary<string, object> Encode() {
Dictionary<string, object> data = base.Encode();
Dictionary<string, object> locationData = new Dictionary<string, object> {
{ "longitude", Location.Longitude },
{ "latitude", Location.Latitude }
{ MessageDataLongitudeKey, Location.Longitude },
{ MessageDataLatitudeKey, Location.Latitude }
};
data["_lcloc"] = locationData;
data[MessageLocationKey] = locationData;
return data;
}
protected override void DecodeMessageData(Dictionary<string, object> msgData) {
base.DecodeMessageData(msgData);
Dictionary<string, object> locationData = msgData["_lcloc"] as Dictionary<string, object>;
Location = new LCGeoPoint((double)locationData["latitude"], (double)locationData["longitude"]);
internal override void Decode(Dictionary<string, object> msgData) {
base.Decode(msgData);
if (msgData.TryGetValue(MessageLocationKey, out object val)) {
Dictionary<string, object> locationData = val as Dictionary<string, object>;
double latitude = 0, longitude = 0;
if (locationData.TryGetValue(MessageDataLatitudeKey, out object lat) &&
double.TryParse(lat as string, out double la)) {
latitude = la;
}
if (locationData.TryGetValue(MessageDataLongitudeKey, out object lon) &&
double.TryParse(lon as string, out double lo)) {
longitude = lo;
}
Location = new LCGeoPoint(latitude, longitude);
}
}
internal override int MessageType => LocationMessageType;
public override int MessageType => LocationMessageType;
}
}

View File

@ -2,31 +2,41 @@
using System.Collections.Generic;
namespace LeanCloud.Realtime {
/// <summary>
/// 消息基类
/// </summary>
public abstract class LCIMMessage {
internal const int TextMessageType = -1;
internal const int ImageMessageType = -2;
internal const int AudioMessageType = -3;
internal const int VideoMessageType = -4;
internal const int LocationMessageType = -5;
internal const int FileMessageType = -6;
internal const int RecalledMessageType = -127;
/// <summary>
/// 消息所在对话 Id
/// </summary>
public string ConversationId {
get; set;
}
/// <summary>
/// 消息 Id
/// </summary>
public string Id {
get; set;
}
/// <summary>
/// 发送者 Id
/// </summary>
public string FromClientId {
get; set;
}
/// <summary>
/// 发送时间戳
/// </summary>
public long SentTimestamp {
get; internal set;
}
/// <summary>
/// 发送时间
/// </summary>
public DateTime SentAt {
get {
return DateTimeOffset.FromUnixTimeMilliseconds(SentTimestamp)
@ -34,10 +44,16 @@ namespace LeanCloud.Realtime {
}
}
/// <summary>
/// 送达时间戳
/// </summary>
public long DeliveredTimestamp {
get; internal set;
}
/// <summary>
/// 送达时间
/// </summary>
public DateTime DeliveredAt {
get {
return DateTimeOffset.FromUnixTimeMilliseconds(DeliveredTimestamp)
@ -45,10 +61,16 @@ namespace LeanCloud.Realtime {
}
}
/// <summary>
/// 已读时间戳
/// </summary>
public long ReadTimestamp {
get; internal set;
}
/// <summary>
/// 已读时间
/// </summary>
public DateTime ReadAt {
get {
return DateTimeOffset.FromUnixTimeMilliseconds(ReadTimestamp)
@ -56,10 +78,16 @@ namespace LeanCloud.Realtime {
}
}
/// <summary>
/// 修改时间戳
/// </summary>
public long PatchedTimestamp {
get; internal set;
}
/// <summary>
/// 修改时间
/// </summary>
public DateTime PatchedAt {
get {
return DateTimeOffset.FromUnixTimeMilliseconds(PatchedTimestamp)
@ -67,18 +95,30 @@ namespace LeanCloud.Realtime {
}
}
/// <summary>
/// 提醒成员 Id 列表
/// </summary>
public List<string> MentionIdList {
get; set;
}
/// <summary>
/// 是否提醒所有人
/// </summary>
public bool MentionAll {
get; set;
}
/// <summary>
/// 是否提醒当前用户
/// </summary>
public bool Mentioned {
get; internal set;
}
/// <summary>
/// 是否是暂态消息
/// </summary>
public bool IsTransient {
get; internal set;
}

View File

@ -7,6 +7,6 @@ namespace LeanCloud.Realtime {
public LCIMRecalledMessage() {
}
internal override int MessageType => RecalledMessageType;
public override int MessageType => RecalledMessageType;
}
}

View File

@ -1,7 +1,13 @@
using System.Collections.Generic;
namespace LeanCloud.Realtime {
/// <summary>
/// 文本消息
/// </summary>
public class LCIMTextMessage : LCIMTypedMessage {
/// <summary>
/// 文本
/// </summary>
public string Text {
get; set;
}
@ -16,16 +22,16 @@ namespace LeanCloud.Realtime {
internal override Dictionary<string, object> Encode() {
Dictionary<string, object> data = base.Encode();
if (!string.IsNullOrEmpty(Text)) {
data["_lctext"] = Text;
data[MessageTextKey] = Text;
}
return data;
}
internal override int MessageType => TextMessageType;
public override int MessageType => TextMessageType;
protected override void DecodeMessageData(Dictionary<string, object> msgData) {
base.DecodeMessageData(msgData);
if (msgData.TryGetValue("_lctext", out object value)) {
internal override void Decode(Dictionary<string, object> msgData) {
base.Decode(msgData);
if (msgData.TryGetValue(MessageTextKey, out object value)) {
Text = value as string;
}
}

View File

@ -1,16 +1,85 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using LeanCloud.Storage.Internal.Codec;
using LeanCloud.Storage.Internal;
namespace LeanCloud.Realtime {
public abstract class LCIMTypedMessage : LCIMMessage {
/// <summary>
/// 已知类型消息
/// </summary>
public class LCIMTypedMessage : LCIMMessage {
/// <summary>
/// 文本消息
/// </summary>
public const int TextMessageType = -1;
/// <summary>
/// 图像消息
/// </summary>
public const int ImageMessageType = -2;
/// <summary>
/// 音频消息
/// </summary>
public const int AudioMessageType = -3;
/// <summary>
/// 视频消息
/// </summary>
public const int VideoMessageType = -4;
/// <summary>
/// 位置消息
/// </summary>
public const int LocationMessageType = -5;
/// <summary>
/// 文件消息
/// </summary>
public const int FileMessageType = -6;
/// <summary>
/// 撤回消息
/// </summary>
public const int RecalledMessageType = -127;
/// <summary>
/// 保留字段
/// </summary>
protected const string MessageTypeKey = "_lctype";
protected const string MessageAttributesKey = "_lcattrs";
protected const string MessageTextKey = "_lctext";
protected const string MessageLocationKey = "_lcloc";
protected const string MessageFileKey = "_lcfile";
protected const string MessageDataLongitudeKey = "longitude";
protected const string MessageDataLatitudeKey = "latitude";
protected const string MessageDataObjectIdKey = "objId";
protected const string MessageDataUrlKey = "url";
protected const string MessageDataMetaDataKey = "metaData";
protected const string MessageDataMetaNameKey = "name";
protected const string MessageDataMetaFormatKey = "format";
protected const string MessageDataMetaSizeKey = "size";
protected const string MessageDataMetaWidthKey = "width";
protected const string MessageDataMetaHeightKey = "height";
protected const string MessageDataMetaDurationKey = "duration";
private Dictionary<string, object> customProperties;
internal virtual int MessageType {
/// <summary>
/// 完整的消息数据
/// </summary>
protected Dictionary<string, object> data = new Dictionary<string, object>();
/// <summary>
/// 消息类型
/// </summary>
public virtual int MessageType {
get; private set;
}
/// <summary>
/// 消息属性访问
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object this[string key] {
get {
if (customProperties == null) {
@ -30,18 +99,19 @@ namespace LeanCloud.Realtime {
}
internal virtual Dictionary<string, object> Encode() {
Dictionary<string, object> msgData = new Dictionary<string, object> {
{ "_lctype", MessageType }
};
Dictionary<string, object> msgData = data != null ?
new Dictionary<string, object>(data) : new Dictionary<string, object>();
msgData[MessageTypeKey] = MessageType;
if (customProperties != null && customProperties.Count > 0) {
msgData["_lcattrs"] = LCEncoder.Encode(customProperties);
msgData[MessageAttributesKey] = LCEncoder.Encode(customProperties);
}
return msgData;
}
protected virtual void DecodeMessageData(Dictionary<string, object> msgData) {
MessageType = (int)msgData["_lctype"];
if (msgData.TryGetValue("_lcattrs", out object attrObj)) {
internal virtual void Decode(Dictionary<string, object> msgData) {
data = msgData;
MessageType = (int)msgData[MessageTypeKey];
if (msgData.TryGetValue(MessageAttributesKey, out object attrObj)) {
customProperties = LCDecoder.Decode(attrObj) as Dictionary<string, object>;
}
}
@ -50,36 +120,38 @@ namespace LeanCloud.Realtime {
Dictionary<string, object> msgData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json,
new LCJsonConverter());
LCIMTypedMessage message = null;
int msgType = (int)msgData["_lctype"];
switch (msgType) {
case TextMessageType:
message = new LCIMTextMessage();
break;
case ImageMessageType:
message = new LCIMImageMessage();
break;
case AudioMessageType:
message = new LCIMAudioMessage();
break;
case VideoMessageType:
message = new LCIMVideoMessage();
break;
case LocationMessageType:
message = new LCIMLocationMessage();
break;
case FileMessageType:
message = new LCIMFileMessage();
break;
case RecalledMessageType:
message = new LCIMRecalledMessage();
break;
default:
// TODO 用户自定义类型消息
break;
int msgType = (int)msgData[MessageTypeKey];
if (customMessageDict.TryGetValue(msgType, out Func<LCIMTypedMessage> msgConstructor)) {
// 已注册的类型消息
message = msgConstructor.Invoke();
} else {
// 未注册的类型消息
message = new LCIMTypedMessage();
}
message.DecodeMessageData(msgData);
message.Decode(msgData);
return message;
}
// 内置已知类型消息
static readonly Dictionary<int, Func<LCIMTypedMessage>> customMessageDict = new Dictionary<int, Func<LCIMTypedMessage>> {
{ TextMessageType, () => new LCIMTextMessage() },
{ ImageMessageType, () => new LCIMImageMessage() },
{ AudioMessageType, () => new LCIMAudioMessage() },
{ VideoMessageType, () => new LCIMVideoMessage() },
{ LocationMessageType, () => new LCIMLocationMessage() },
{ FileMessageType, () => new LCIMFileMessage() },
{ RecalledMessageType, () => new LCIMRecalledMessage() }
};
/// <summary>
/// 注册自定义类型消息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="msgType"></param>
/// <param name="msgConstructor"></param>
public static void Register<T>(int msgType, Func<T> msgConstructor)
where T : LCIMTypedMessage {
customMessageDict[msgType] = msgConstructor;
}
}
}

View File

@ -2,14 +2,29 @@
using LeanCloud.Storage;
namespace LeanCloud.Realtime {
/// <summary>
/// 视频消息
/// </summary>
public class LCIMVideoMessage : LCIMFileMessage {
/// <summary>
/// 宽度
/// </summary>
public int Width {
get; private set;
}
/// <summary>
/// 高度
/// </summary>
public int Height {
get; private set;
}
/// <summary>
/// 时长
/// </summary>
public double Duration {
get {
if (double.TryParse(File.MetaData["duration"] as string, out double duration)) {
return duration;
}
return 0;
}
get; private set;
}
internal LCIMVideoMessage() {
@ -21,20 +36,36 @@ namespace LeanCloud.Realtime {
internal override Dictionary<string, object> Encode() {
Dictionary<string, object> data = base.Encode();
Dictionary<string, object> fileData = data["_lcfile"] as Dictionary<string, object>;
Dictionary<string, object> metaData = fileData["metaData"] as Dictionary<string, object>;
if (File.MetaData.TryGetValue("width", out object width)) {
metaData["width"] = width;
Dictionary<string, object> fileData = data[MessageFileKey] as Dictionary<string, object>;
Dictionary<string, object> metaData = fileData[MessageDataMetaDataKey] as Dictionary<string, object>;
if (File.MetaData.TryGetValue(MessageDataMetaWidthKey, out object width)) {
metaData[MessageDataMetaWidthKey] = width;
}
if (File.MetaData.TryGetValue("height", out object height)) {
metaData["height"] = height;
if (File.MetaData.TryGetValue(MessageDataMetaHeightKey, out object height)) {
metaData[MessageDataMetaHeightKey] = height;
}
if (File.MetaData.TryGetValue("duration", out object duration)) {
metaData["duration"] = duration;
if (File.MetaData.TryGetValue(MessageDataMetaDurationKey, out object duration)) {
metaData[MessageDataMetaDurationKey] = duration;
}
return data;
}
internal override int MessageType => VideoMessageType;
internal override void Decode(Dictionary<string, object> msgData) {
base.Decode(msgData);
if (File.MetaData.TryGetValue(MessageDataMetaWidthKey, out object width) &&
int.TryParse(width as string, out int w)) {
Width = w;
}
if (File.MetaData.TryGetValue(MessageDataMetaHeightKey, out object height) &&
int.TryParse(height as string, out int h)) {
Height = h;
}
if (File.MetaData.TryGetValue(MessageDataMetaDurationKey, out object duration) &&
double.TryParse(duration as string, out double d)) {
Duration = d;
}
}
public override int MessageType => VideoMessageType;
}
}