csharp-sdk-upm/RTM/Source/Public/AVIMConversation.cs

1546 lines
56 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LeanCloud.Realtime.Internal;
using LeanCloud;
using LeanCloud.Storage.Internal;
using System.Collections;
using System.IO;
namespace LeanCloud.Realtime
{
/// <summary>
/// 对话
/// </summary>
public class AVIMConversation : IEnumerable<KeyValuePair<string, object>>, IAVObject
{
private DateTime? updatedAt;
private DateTime? createdAt;
private DateTime? lastMessageAt;
internal DateTime? expiredAt;
private string name;
private AVObject convState;
internal readonly Object mutex = new Object();
//private readonly IDictionary<string, object> estimatedData = new Dictionary<string, object>();
internal AVIMClient _currentClient;
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
{
lock (mutex)
{
return ((IEnumerable<KeyValuePair<string, object>>)convState).GetEnumerator();
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
lock (mutex)
{
return ((IEnumerable<KeyValuePair<string, object>>)convState).GetEnumerator();
}
}
virtual public object this[string key]
{
get
{
return convState[key];
}
set
{
convState[key] = value;
}
}
public ICollection<string> Keys
{
get
{
lock (mutex)
{
return convState.Keys;
}
}
}
public T Get<T>(string key)
{
return this.convState.Get<T>(key);
}
public bool ContainsKey(string key)
{
return this.convState.ContainsKey(key);
}
internal IDictionary<string, object> EncodeAttributes()
{
var currentOperations = convState.StartSave();
var jsonToSave = AVObject.ToJSONObjectForSaving(currentOperations);
return jsonToSave;
}
internal void MergeFromPushServer(IDictionary<string, object> json)
{
if (json.Keys.Contains("cdate"))
{
createdAt = DateTime.Parse(json["cdate"].ToString());
updatedAt = DateTime.Parse(json["cdate"].ToString());
json.Remove("cdate");
}
if (json.Keys.Contains("lm"))
{
var ts = long.Parse(json["lm"].ToString());
updatedAt = ts.ToDateTime();
lastMessageAt = ts.ToDateTime();
json.Remove("lm");
}
if (json.Keys.Contains("c"))
{
Creator = json["c"].ToString();
json.Remove("c");
}
if (json.Keys.Contains("m"))
{
MemberIds = json["m"] as IList<string>;
json.Remove("m");
}
if (json.Keys.Contains("mu"))
{
MuteMemberIds = json["mu"] as IList<string>;
json.Remove("mu");
}
if (json.Keys.Contains("tr"))
{
IsTransient = bool.Parse(json["tr"].ToString());
json.Remove("tr");
}
if (json.Keys.Contains("sys"))
{
IsSystem = bool.Parse(json["sys"].ToString());
json.Remove("sys");
}
if (json.Keys.Contains("cid"))
{
ConversationId = json["cid"].ToString();
json.Remove("cid");
}
if (json.Keys.Contains("name"))
{
Name = json["name"].ToString();
json.Remove("name");
}
}
/// <summary>
/// 当前的AVIMClient一个对话理论上只存在一个AVIMClient。
/// </summary>
public AVIMClient CurrentClient
{
get
{
if (_currentClient == null) throw new NullReferenceException("当前对话没有关联有效的 AVIMClient。");
return _currentClient;
}
//set
//{
// _currentClient = value;
//}
}
/// <summary>
/// 对话的唯一ID
/// </summary>
public string ConversationId { get; internal set; }
/// <summary>
/// 对话在全局的唯一的名字
/// </summary>
public string Name
{
get
{
if (convState.ContainsKey("name"))
{
name = this.convState.Get<string>("name");
}
return name;
}
set
{
if (value == null)
this["name"] = "";
else
{
this["name"] = value;
}
}
}
/// <summary>
/// 对话中存在的 Client 的 ClientId 列表
/// </summary>
public IEnumerable<string> MemberIds { get; internal set; }
/// <summary>
/// 对该对话静音的成员列表
/// <remarks>
/// 对该对话设置了静音的成员,将不会收到离线消息的推送。
/// </remarks>
/// </summary>
public IEnumerable<string> MuteMemberIds { get; internal set; }
/// <summary>
/// 对话的创建者
/// </summary>
public string Creator { get; private set; }
/// <summary>
/// 是否为聊天室
/// </summary>
public bool IsTransient { get; internal set; }
/// <summary>
/// 是否系统对话
/// </summary>
public bool IsSystem { get; internal set; }
/// <summary>
/// 是否是唯一对话
/// </summary>
public bool IsUnique { get; internal set; }
/// <summary>
/// 对话是否为虚拟对话
/// </summary>
public bool IsTemporary { get; internal set; }
/// <summary>
/// 对话创建的时间
/// </summary>
public DateTime? CreatedAt
{
get
{
DateTime? nullable;
lock (this.mutex)
{
nullable = this.createdAt;
}
return nullable;
}
private set
{
lock (this.mutex)
{
this.createdAt = value;
}
}
}
/// <summary>
/// 对话更新的时间
/// </summary>
public DateTime? UpdatedAt
{
get
{
DateTime? nullable;
lock (this.mutex)
{
nullable = this.updatedAt;
}
return nullable;
}
private set
{
lock (this.mutex)
{
this.updatedAt = value;
}
}
}
/// <summary>
/// 对话中最后一条消息的时间,可以用此判断对话的最后活跃时间
/// </summary>
public DateTime? LastMessageAt
{
get
{
DateTime? nullable;
lock (this.mutex)
{
nullable = this.lastMessageAt;
}
return nullable;
}
private set
{
lock (this.mutex)
{
this.lastMessageAt = value;
}
}
}
/// <summary>
/// 已知 id在本地构建一个 AVIMConversation 对象
/// </summary>
public AVIMConversation(string id)
: this(id, null)
{
}
/// <summary>
/// 已知 id 在本地构建一个对话
/// </summary>
/// <param name="id">对话 id</param>
/// <param name="client">AVIMClient 实例,必须是登陆成功的</param>
public AVIMConversation(string id, AVIMClient client) : this(client)
{
this.ConversationId = id;
}
internal AVIMConversation(AVIMClient client)
{
this._currentClient = client;
this.CurrentClient.OnMessageReceived += CurrentClient_OnMessageReceived;
}
/// <summary>
/// AVIMConversation Build 驱动器
/// </summary>
/// <param name="source"></param>
/// <param name="name"></param>
/// <param name="creator"></param>
/// <param name="members"></param>
/// <param name="muteMembers"></param>
/// <param name="isTransient"></param>
/// <param name="isSystem"></param>
/// <param name="attributes"></param>
/// <param name="state"></param>
/// <param name="isUnique"></param>
/// <param name="isTemporary"></param>
internal AVIMConversation(AVIMConversation source = null,
string name = null,
string creator = null,
IEnumerable<string> members = null,
IEnumerable<string> muteMembers = null,
bool isTransient = false,
bool isSystem = false,
IEnumerable<KeyValuePair<string, object>> attributes = null,
AVObject state = null,
bool isUnique = true,
bool isTemporary = false,
int ttl = 86400,
AVIMClient client = null) :
this(client)
{
convState = source != null ? source.convState : new AVObject("_Conversation");
this.Name = source?.Name;
this.MemberIds = source?.MemberIds;
this.Creator = source?.Creator;
this.MuteMemberIds = source?.MuteMemberIds;
if (!string.IsNullOrEmpty(name))
{
this.Name = name;
}
if (!string.IsNullOrEmpty(creator))
{
this.Creator = creator;
}
if (members != null)
{
this.MemberIds = members.ToList();
}
if (muteMembers != null)
{
this.MuteMemberIds = muteMembers.ToList();
}
this.IsTransient = isTransient;
this.IsSystem = isSystem;
this.IsUnique = isUnique;
this.IsTemporary = isTemporary;
this.expiredAt = DateTime.Now + new TimeSpan(0, 0, ttl);
if (state != null)
{
convState = state;
this.ConversationId = state.ObjectId;
this.CreatedAt = state.CreatedAt;
this.UpdatedAt = state.UpdatedAt;
this.MergeMagicFields(state.ToDictionary(x => x.Key, x => x.Value));
}
if (attributes != null)
{
this.MergeMagicFields(attributes.ToDictionary(x => x.Key, x => x.Value));
}
}
/// <summary>
/// 从本地构建一个对话
/// </summary>
/// <param name="convId">对话的 objectId</param>
/// <param name="client"></param>
/// <returns></returns>
public static AVIMConversation CreateWithoutData(string convId, AVIMClient client)
{
return new AVIMConversation(client)
{
ConversationId = convId,
};
}
/// <summary>
///
/// </summary>
/// <param name="magicFields"></param>
/// <param name="client"></param>
/// <returns></returns>
public static AVIMConversation CreateWithData(IEnumerable<KeyValuePair<string, object>> magicFields, AVIMClient client)
{
if (magicFields is AVObject)
{
return new AVIMConversation(state: (AVObject)magicFields, client: client);
}
return new AVIMConversation(attributes: magicFields, client: client);
}
#region save to cloud
/// <summary>
/// 将修改保存到云端
/// </summary>
/// <returns></returns>
public Task SaveAsync()
{
var cmd = new ConversationCommand()
.Generate(this);
var convCmd = cmd.Option("update")
.PeerId(this.CurrentClient.ClientId);
return this.CurrentClient.RunCommandAsync(convCmd);
}
#endregion
#region send message
/// <summary>
/// 向该对话发送消息。
/// </summary>
/// <param name="avMessage">消息体</param>
/// <param name="receipt">是否需要送达回执</param>
/// <param name="transient">是否是暂态消息,暂态消息不返回送达回执(ack),不保留离线消息,不触发离线推送</param>
/// <param name="priority">消息等级默认是1可选值还有 2 3</param>
/// <param name="will">标记该消息是否为下线通知消息</param>
/// <param name="pushData">如果消息的接收者已经下线了,这个字段的内容就会被离线推送到接收者
/// <remarks>例如,一张图片消息的离线消息内容可以类似于:[您收到一条图片消息,点击查看] 这样的推送内容,参照微信的做法</remarks>
/// </param>
/// <returns></returns>
public Task<IAVIMMessage> SendMessageAsync(IAVIMMessage avMessage,
bool receipt = true,
bool transient = false,
int priority = 1,
bool will = false,
IDictionary<string, object> pushData = null)
{
return this.SendMessageAsync(avMessage, new AVIMSendOptions()
{
Receipt = receipt,
Transient = transient,
Priority = priority,
Will = will,
PushData = pushData
});
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="avMessage">消息体</param>
/// <param name="options">消息的发送选项,包含了一些特殊的标记<see cref="AVIMSendOptions"/></param>
/// <returns></returns>
public Task<IAVIMMessage> SendMessageAsync(IAVIMMessage avMessage, AVIMSendOptions options)
{
if (this.CurrentClient == null) throw new Exception("当前对话未指定有效 AVIMClient无法发送消息。");
return this.CurrentClient.SendMessageAsync(this, avMessage, options);
}
#endregion
#region recall message
#endregion
#region message listener and event notify
/// <summary>
/// Registers the listener.
/// </summary>
/// <param name="listener">Listener.</param>
public void RegisterListener(IAVIMListener listener)
{
this.CurrentClient.RegisterListener(listener, this.ConversationIdHook);
}
internal bool ConversationIdHook(AVIMNotice notice)
{
if (!notice.RawData.ContainsKey("cid")) return false;
return notice.RawData["cid"].ToString() == this.ConversationId;
}
#endregion
#region mute && unmute
/// <summary>
/// 当前用户针对对话做静音操作
/// </summary>
/// <returns></returns>
public Task MuteAsync()
{
return this.CurrentClient.MuteConversationAsync(this);
}
/// <summary>
/// 当前用户取消对话的静音,恢复该对话的离线消息推送
/// </summary>
/// <returns></returns>
public Task UnmuteAsync()
{
return this.CurrentClient.UnmuteConversationAsync(this);
}
#endregion
#region 成员操作相关接口
/// <summary>
/// Joins the async.
/// </summary>
/// <returns>The async.</returns>
public Task JoinAsync()
{
return AddMembersAsync(CurrentClient.ClientId);
}
/// <summary>
/// Adds the members async.
/// </summary>
/// <returns>The members async.</returns>
/// <param name="clientId">Client identifier.</param>
/// <param name="clientIds">Client identifiers.</param>
public Task AddMembersAsync(string clientId = null, IEnumerable<string> clientIds = null)
{
return this.CurrentClient.InviteAsync(this, clientId, clientIds);
}
/// <summary>
/// Removes the members async.
/// </summary>
/// <returns>The members async.</returns>
/// <param name="clientId">Client identifier.</param>
/// <param name="clientIds">Client identifiers.</param>
public Task RemoveMembersAsync(string clientId = null, IEnumerable<string> clientIds = null)
{
return this.CurrentClient.KickAsync(this, clientId, clientIds);
}
/// <summary>
/// Quits the async.
/// </summary>
/// <returns>The async.</returns>
public Task QuitAsync()
{
return RemoveMembersAsync(CurrentClient.ClientId);
}
#endregion
#region load message history
/// <summary>
/// 获取当前对话的历史消息
/// <remarks>不支持聊天室(暂态对话)</remarks>
/// </summary>
/// <param name="beforeMessageId">从 beforeMessageId 开始向前查询(和 beforeTimeStampPoint 共同使用,为防止某毫秒时刻有重复消息)</param>
/// <param name="afterMessageId"> 截止到某个 afterMessageId (不包含)</param>
/// <param name="beforeTimeStampPoint">从 beforeTimeStampPoint 开始向前查询</param>
/// <param name="afterTimeStampPoint">拉取截止到 afterTimeStampPoint 时间戳(不包含)</param>
/// <param name="direction">查询方向,默认是 1如果是 1 表示从新消息往旧消息方向, 0 则相反,其他值无效</param>
/// <param name="limit">获取的消息数量</param>
/// <returns></returns>
public Task<IEnumerable<T>> QueryMessageAsync<T>(
string beforeMessageId = null,
string afterMessageId = null,
DateTime? beforeTimeStampPoint = null,
DateTime? afterTimeStampPoint = null,
int direction = 1,
int limit = 20)
where T : IAVIMMessage
{
return this.CurrentClient.QueryMessageAsync<T>(this, beforeMessageId, afterMessageId, beforeTimeStampPoint, afterTimeStampPoint, direction, limit)
.OnSuccess(t =>
{
//OnMessageLoad(t.Result);
return t.Result;
});
}
/// <summary>
/// Gets the message query.
/// </summary>
/// <returns>The message query.</returns>
public AVIMMessageQuery GetMessageQuery()
{
return new AVIMMessageQuery(this);
}
/// <summary>
/// Gets the message pager.
/// </summary>
/// <returns>The message pager.</returns>
public AVIMMessagePager GetMessagePager()
{
return new AVIMMessagePager(this);
}
#endregion
#region 字典与对象之间的转换
internal virtual void MergeMagicFields(IDictionary<String, Object> data)
{
lock (this.mutex)
{
if (data.ContainsKey("objectId"))
{
this.ConversationId = (data["objectId"] as String);
data.Remove("objectId");
}
if (data.ContainsKey("createdAt"))
{
this.CreatedAt = AVDecoder.ParseDate(data["createdAt"] as string);
data.Remove("createdAt");
}
if (data.ContainsKey("updatedAt"))
{
this.updatedAt = AVDecoder.ParseDate(data["updatedAt"] as string);
data.Remove("updatedAt");
}
if (data.ContainsKey("name"))
{
this.Name = (data["name"] as String);
data.Remove("name");
}
if (data.ContainsKey("lm"))
{
this.LastMessageAt = AVDecoder.Instance.Decode(data["lm"]) as DateTime?;
data.Remove("lm");
}
if (data.ContainsKey("m"))
{
this.MemberIds = AVDecoder.Instance.DecodeList<string>(data["m"]);
data.Remove("m");
}
if (data.ContainsKey("mu"))
{
this.MuteMemberIds = AVDecoder.Instance.DecodeList<string>(data["mu"]);
data.Remove("mu");
}
if (data.ContainsKey("c"))
{
this.Creator = data["c"].ToString();
data.Remove("c");
}
if (data.ContainsKey("unique"))
{
if (data["unique"] is bool)
{
this.IsUnique = (bool)data["unique"];
}
data.Remove("unique");
}
foreach (var kv in data)
{
this[kv.Key] = kv.Value;
}
}
}
#endregion
#region SyncStateAsync & unread & mark as read
/// <summary>
/// sync state from server.suhc unread state .etc;
/// </summary>
/// <returns></returns>
public Task<AggregatedState> SyncStateAsync()
{
lock (mutex)
{
var rtn = new AggregatedState();
rtn.Unread = GetUnreadStateFromLocal();
return Task.FromResult(rtn);
}
}
private UnreadState _unread;
private UnreadState _lastUnreadWhenOpenSession;
public UnreadState Unread
{
get
{
_lastUnreadWhenOpenSession = GetUnreadStateFromLocal();
// v.2 协议,只给出上次离线之后的未读消息,本次在线的收到的消息均视为已读
if (this.CurrentClient.LinkedRealtime.CurrentConfiguration.OfflineMessageStrategy == AVRealtime.OfflineMessageStrategy.UnreadNotice)
{
_unread = _lastUnreadWhenOpenSession;
}
else if (this.CurrentClient.LinkedRealtime.CurrentConfiguration.OfflineMessageStrategy == AVRealtime.OfflineMessageStrategy.UnreadAck)
{
if (_lastUnreadWhenOpenSession == null) _unread = new UnreadState().MergeReceived(this.Received);
else _unread = _lastUnreadWhenOpenSession.MergeReceived(this.Received);
}
return _unread;
}
internal set
{
_unread = value;
}
}
private readonly object receivedMutex = new object();
public ReceivedState Received
{
get; set;
}
public ReadState Read
{
get; set;
}
UnreadState GetUnreadStateFromLocal()
{
lock (mutex)
{
var notice = ConversationUnreadListener.Get(this.ConversationId);
if (notice != null)
{
var unreadState = new UnreadState()
{
LastMessage = notice.LastUnreadMessage,
SyncdAt = ConversationUnreadListener.NotifTime,
Count = notice.UnreadCount
};
return unreadState;
}
return null;
}
}
internal void OnMessageLoad(IEnumerable<IAVIMMessage> messages)
{
var lastestInCollection = messages.OrderByDescending(m => m.ServerTimestamp).FirstOrDefault();
if (lastestInCollection != null)
{
if (CurrentClient.CurrentConfiguration.AutoRead)
{
this.ReadAsync(lastestInCollection);
}
}
}
/// <summary>
/// mark this conversation as read
/// </summary>
/// <returns></returns>
public Task ReadAsync(IAVIMMessage message = null, DateTime? readAt = null)
{
// 标记已读必须至少是从上一次离线产生的最后一条消息开始,否则无法计算 Count
if (_lastUnreadWhenOpenSession != null)
{
if (_lastUnreadWhenOpenSession.LastMessage != null)
{
message = _lastUnreadWhenOpenSession.LastMessage;
}
}
return this.CurrentClient.ReadAsync(this, message, readAt).OnSuccess(t =>
{
Received = null;
_lastUnreadWhenOpenSession = null;
Read = new ReadState()
{
ReadAt = readAt != null ? readAt.Value.ToUnixTimeStamp() : 0,
LastMessage = message,
SyncdAt = DateTime.Now.ToUnixTimeStamp()
};
});
}
/// <summary>
/// aggregated state for the conversation
/// </summary>
public class AggregatedState
{
/// <summary>
/// Unread state
/// </summary>
public UnreadState Unread { get; internal set; }
}
/// <summary>
/// UnreadState recoder for the conversation
/// </summary>
public class UnreadState
{
/// <summary>
/// unread count
/// </summary>
public int Count { get; internal set; }
/// <summary>
/// last unread message
/// </summary>
public IAVIMMessage LastMessage { get; internal set; }
/// <summary>
/// last sync timestamp
/// </summary>
public long SyncdAt { get; internal set; }
internal UnreadState MergeReceived(ReceivedState receivedState)
{
if (receivedState == null) return this;
var count = Count + receivedState.Count;
var lastMessage = this.LastMessage;
if (receivedState.LastMessage != null)
{
lastMessage = receivedState.LastMessage;
}
var syncdAt = this.SyncdAt > receivedState.SyncdAt ? this.SyncdAt : receivedState.SyncdAt;
return new UnreadState()
{
Count = count,
LastMessage = lastMessage,
SyncdAt = syncdAt
};
}
}
public class ReceivedState
{
public int Count { get; internal set; }
/// <summary>
/// last received message
/// </summary>
public IAVIMMessage LastMessage { get; internal set; }
/// <summary>
/// last sync timestamp
/// </summary>
public long SyncdAt { get; internal set; }
}
public class ReadState
{
public long ReadAt { get; set; }
public IAVIMMessage LastMessage { get; internal set; }
public long SyncdAt { get; internal set; }
}
#endregion
#region on client message received to update unread
private void CurrentClient_OnMessageReceived(object sender, AVIMMessageEventArgs e)
{
if (this.CurrentClient.CurrentConfiguration.AutoRead)
{
this.ReadAsync(e.Message);
return;
}
lock (receivedMutex)
{
if (this.Received == null) this.Received = new ReceivedState();
this.Received.Count++;
this.Received.LastMessage = e.Message;
this.Received.SyncdAt = DateTime.Now.ToUnixTimeStamp();
}
}
#endregion
}
/// <summary>
/// AVIMConversation extensions.
/// </summary>
public static class AVIMConversationExtensions
{
/// <summary>
/// Send message async.
/// </summary>
/// <returns>The async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="message">Message.</param>
/// <param name="options">Options.</param>
public static Task SendAsync(this AVIMConversation conversation, IAVIMMessage message, AVIMSendOptions options)
{
return conversation.SendMessageAsync(message, options);
}
/// <summary>
/// Send message async.
/// </summary>
/// <returns>The async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="message">Message.</param>
/// <param name="options">Options.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public static Task<T> SendAsync<T>(this AVIMConversation conversation, T message, AVIMSendOptions options)
where T : IAVIMMessage
{
return conversation.SendMessageAsync(message, options).OnSuccess(t =>
{
return (T)t.Result;
});
}
/// <summary>
/// Send message async.
/// </summary>
/// <returns>The async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="message">Message.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public static Task<T> SendAsync<T>(this AVIMConversation conversation, T message)
where T : IAVIMMessage
{
return conversation.SendMessageAsync(message).OnSuccess(t =>
{
return (T)t.Result;
});
}
/// <summary>
/// Send text message.
/// </summary>
/// <returns>The text async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="text">Text.</param>
public static Task<AVIMTextMessage> SendTextAsync(this AVIMConversation conversation, string text)
{
return conversation.SendAsync<AVIMTextMessage>(new AVIMTextMessage(text));
}
#region Image messages
/// <summary>
/// Send image message async.
/// </summary>
/// <returns>The image async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="url">URL.</param>
/// <param name="name">Name.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="customAttributes">Custom attributes.</param>
public static Task<AVIMImageMessage> SendImageAsync(this AVIMConversation conversation, string url, string name = null, string textTitle = null, IDictionary<string, object> customAttributes = null)
{
return conversation.SendFileMessageAsync<AVIMImageMessage>(url, name, textTitle, customAttributes);
}
/// <summary>
/// Send image message async.
/// </summary>
/// <returns>The image async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="fileName">File name.</param>
/// <param name="data">Data.</param>
/// <param name="mimeType">MIME type.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="metaData">Meta data.</param>
/// <param name="customAttributes">Custom attributes.</param>
public static Task<AVIMImageMessage> SendImageAsync(this AVIMConversation conversation, string fileName, Stream data, string mimeType = null, string textTitle = null, IDictionary<string, object> metaData = null, IDictionary<string, object> customAttributes = null)
{
return conversation.SendFileMessageAsync<AVIMImageMessage>(fileName, data, mimeType, textTitle, metaData, customAttributes);
}
/// <summary>
/// Send image message async.
/// </summary>
/// <returns>The image async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="fileName">File name.</param>
/// <param name="data">Data.</param>
/// <param name="mimeType">MIME type.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="metaData">Meta data.</param>
/// <param name="customAttributes">Custom attributes.</param>
public static Task<AVIMImageMessage> SendImageAsync(this AVIMConversation conversation, string fileName, byte[] data, string mimeType = null, string textTitle = null, IDictionary<string, object> metaData = null, IDictionary<string, object> customAttributes = null)
{
return conversation.SendFileMessageAsync<AVIMImageMessage>(fileName, data, mimeType, textTitle, metaData, customAttributes);
}
#endregion
#region audio message
/// <summary>
/// Send audio message async.
/// </summary>
/// <returns>The audio async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="url">URL.</param>
/// <param name="name">Name.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="customAttributes">Custom attributes.</param>
public static Task<AVIMAudioMessage> SendAudioAsync(this AVIMConversation conversation, string url, string name = null, string textTitle = null, IDictionary<string, object> customAttributes = null)
{
return conversation.SendFileMessageAsync<AVIMAudioMessage>(name, url, textTitle, customAttributes);
}
/// <summary>
/// Send audio message async.
/// </summary>
/// <returns>The audio async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="fileName">File name.</param>
/// <param name="data">Data.</param>
/// <param name="mimeType">MIME type.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="metaData">Meta data.</param>
/// <param name="customAttributes">Custom attributes.</param>
public static Task<AVIMAudioMessage> SendAudioAsync(this AVIMConversation conversation, string fileName, Stream data, string mimeType = null, string textTitle = null, IDictionary<string, object> metaData = null, IDictionary<string, object> customAttributes = null)
{
return conversation.SendFileMessageAsync<AVIMAudioMessage>(fileName, data, mimeType, textTitle, metaData, customAttributes);
}
/// <summary>
/// Send audio message async.
/// </summary>
/// <returns>The audio async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="fileName">File name.</param>
/// <param name="data">Data.</param>
/// <param name="mimeType">MIME type.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="metaData">Meta data.</param>
/// <param name="customAttributes">Custom attributes.</param>
public static Task<AVIMAudioMessage> SendAudioAsync(this AVIMConversation conversation, string fileName, byte[] data, string mimeType = null, string textTitle = null, IDictionary<string, object> metaData = null, IDictionary<string, object> customAttributes = null)
{
return conversation.SendFileMessageAsync<AVIMAudioMessage>(fileName, data, mimeType, textTitle, metaData, customAttributes);
}
#endregion
#region video message
/// <summary>
/// Send video message async.
/// </summary>
/// <returns>The video async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="url">URL.</param>
/// <param name="name">Name.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="customAttributes">Custom attributes.</param>
public static Task<AVIMVideoMessage> SendVideoAsync(this AVIMConversation conversation, string url, string name = null, string textTitle = null, IDictionary<string, object> customAttributes = null)
{
return conversation.SendFileMessageAsync<AVIMVideoMessage>(name, url, textTitle, customAttributes);
}
/// <summary>
/// Send video message async.
/// </summary>
/// <returns>The video async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="fileName">File name.</param>
/// <param name="data">Data.</param>
/// <param name="mimeType">MIME type.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="metaData">Meta data.</param>
/// <param name="customAttributes">Custom attributes.</param>
public static Task<AVIMVideoMessage> SendVideoAsync(this AVIMConversation conversation, string fileName, Stream data, string mimeType = null, string textTitle = null, IDictionary<string, object> metaData = null, IDictionary<string, object> customAttributes = null)
{
return conversation.SendFileMessageAsync<AVIMVideoMessage>(fileName, data, mimeType, textTitle, metaData, customAttributes);
}
/// <summary>
/// Send video message async.
/// </summary>
/// <returns>The video async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="fileName">File name.</param>
/// <param name="data">Data.</param>
/// <param name="mimeType">MIME type.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="metaData">Meta data.</param>
/// <param name="customAttributes">Custom attributes.</param>
public static Task<AVIMVideoMessage> SendVideoAsync(this AVIMConversation conversation, string fileName, byte[] data, string mimeType = null, string textTitle = null, IDictionary<string, object> metaData = null, IDictionary<string, object> customAttributes = null)
{
return conversation.SendFileMessageAsync<AVIMVideoMessage>(fileName, data, mimeType, textTitle, metaData, customAttributes);
}
#endregion
/// <summary>
/// Send file message async.
/// </summary>
/// <returns>The file message async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="url">URL.</param>
/// <param name="name">Name.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="customAttributes">Custom attributes.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public static Task<T> SendFileMessageAsync<T>(this AVIMConversation conversation, string url, string name = null, string textTitle = null, IDictionary<string, object> customAttributes = null)
where T : AVIMFileMessage, new()
{
var fileMessage = AVIMFileMessage.FromUrl<T>(name, url, textTitle, customAttributes);
return conversation.SendAsync<T>(fileMessage);
}
/// <summary>
/// Send file message async.
/// </summary>
/// <returns>The file message async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="fileName">File name.</param>
/// <param name="data">Data.</param>
/// <param name="mimeType">MIME type.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="metaData">Meta data.</param>
/// <param name="customAttributes">Custom attributes.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public static Task<T> SendFileMessageAsync<T>(this AVIMConversation conversation, string fileName, Stream data, string mimeType = null, string textTitle = null, IDictionary<string, object> metaData = null, IDictionary<string, object> customAttributes = null)
where T : AVIMFileMessage, new()
{
var fileMessage = AVIMFileMessage.FromStream<T>(fileName, data, mimeType, textTitle, metaData, customAttributes);
return fileMessage.File.SaveAsync().OnSuccess(fileUploaded =>
{
return conversation.SendAsync<T>(fileMessage);
}).Unwrap();
}
/// <summary>
/// Send file message async.
/// </summary>
/// <returns>The file message async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="fileName">File name.</param>
/// <param name="data">Data.</param>
/// <param name="mimeType">MIME type.</param>
/// <param name="textTitle">Text title.</param>
/// <param name="metaData">Meta data.</param>
/// <param name="customAttributes">Custom attributes.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public static Task<T> SendFileMessageAsync<T>(this AVIMConversation conversation, string fileName, byte[] data, string mimeType = null, string textTitle = null, IDictionary<string, object> metaData = null, IDictionary<string, object> customAttributes = null)
where T : AVIMFileMessage, new()
{
var fileMessage = AVIMFileMessage.FromStream<T>(fileName, new MemoryStream(data), mimeType, textTitle, metaData, customAttributes);
return conversation.SendFileMessageAsync<T>(fileName, new MemoryStream(data), mimeType, textTitle, metaData, customAttributes);
}
/// <summary>
/// Send location async.
/// </summary>
/// <returns>The location async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="point">Point.</param>
public static Task SendLocationAsync(this AVIMConversation conversation, AVGeoPoint point)
{
var locationMessage = new AVIMLocationMessage(point);
return conversation.SendAsync<AVIMLocationMessage>(locationMessage);
}
/// <summary>
/// Query the message async.
/// </summary>
/// <returns>The message async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="beforeMessageId">Before message identifier.</param>
/// <param name="afterMessageId">After message identifier.</param>
/// <param name="beforeTimeStamp">Before time stamp.</param>
/// <param name="afterTimeStamp">After time stamp.</param>
/// <param name="direction">Direction.</param>
/// <param name="limit">Limit.</param>
public static Task<IEnumerable<IAVIMMessage>> QueryMessageAsync(
this AVIMConversation conversation,
string beforeMessageId = null,
string afterMessageId = null,
long? beforeTimeStamp = null,
long? afterTimeStamp = null,
int direction = 1,
int limit = 20)
{
return conversation.QueryMessageAsync<IAVIMMessage>(beforeMessageId,
afterMessageId,
beforeTimeStamp,
afterTimeStamp,
direction,
limit);
}
/// <summary>
/// Query message with speciafic subclassing type.
/// </summary>
/// <returns>The message async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="beforeMessageId">Before message identifier.</param>
/// <param name="afterMessageId">After message identifier.</param>
/// <param name="beforeTimeStamp">Before time stamp.</param>
/// <param name="afterTimeStamp">After time stamp.</param>
/// <param name="direction">Direction.</param>
/// <param name="limit">Limit.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public static Task<IEnumerable<T>> QueryMessageAsync<T>(
this AVIMConversation conversation,
string beforeMessageId = null,
string afterMessageId = null,
long? beforeTimeStamp = null,
long? afterTimeStamp = null,
int direction = 1,
int limit = 20)
where T : IAVIMMessage
{
return conversation.QueryMessageAsync<T>(beforeMessageId,
afterMessageId,
beforeTimeStampPoint: beforeTimeStamp.HasValue ? beforeTimeStamp.Value.ToDateTime() : DateTime.MinValue,
afterTimeStampPoint: afterTimeStamp.HasValue ? afterTimeStamp.Value.ToDateTime() : DateTime.MinValue,
direction: direction,
limit: limit);
}
/// <summary>
/// Query message record before the given message async.
/// </summary>
/// <returns>The message before async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="message">Message.</param>
/// <param name="limit">Limit.</param>
public static Task<IEnumerable<IAVIMMessage>> QueryMessageBeforeAsync(
this AVIMConversation conversation,
IAVIMMessage message,
int limit = 20)
{
return conversation.QueryMessageAsync(beforeMessageId: message.Id, beforeTimeStamp: message.ServerTimestamp, limit: limit);
}
/// <summary>
/// Query message record after the given message async.
/// </summary>
/// <returns>The message after async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="message">Message.</param>
/// <param name="limit">Limit.</param>
public static Task<IEnumerable<IAVIMMessage>> QueryMessageAfterAsync(
this AVIMConversation conversation,
IAVIMMessage message,
int limit = 20)
{
return conversation.QueryMessageAsync(afterMessageId: message.Id, afterTimeStamp: message.ServerTimestamp, limit: limit);
}
/// <summary>
/// Query messages after conversation created.
/// </summary>
/// <returns>The message after async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="limit">Limit.</param>
public static Task<IEnumerable<IAVIMMessage>> QueryMessageFromOldToNewAsync(
this AVIMConversation conversation,
int limit = 20)
{
return conversation.QueryMessageAsync(afterTimeStamp: conversation.CreatedAt.Value.ToUnixTimeStamp(), limit: limit);
}
/// <summary>
/// Query messages in interval async.
/// </summary>
/// <returns>The message interval async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="start">Start.</param>
/// <param name="end">End.</param>
/// <param name="limit">Limit.</param>
public static Task<IEnumerable<IAVIMMessage>> QueryMessageInIntervalAsync(
this AVIMConversation conversation,
IAVIMMessage start,
IAVIMMessage end,
int limit = 20)
{
return conversation.QueryMessageAsync(
beforeMessageId: end.Id,
beforeTimeStamp: end.ServerTimestamp,
afterMessageId: start.Id,
afterTimeStamp: start.ServerTimestamp,
limit: limit);
}
/// <summary>
/// Recall message async.
/// </summary>
/// <returns>The async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="message">Message.</param>
public static Task<AVIMRecalledMessage> RecallAsync(this AVIMConversation conversation, IAVIMMessage message)
{
return conversation.CurrentClient.RecallAsync(message);
}
/// <summary>
/// Modifiy message async.
/// </summary>
/// <returns>The async.</returns>
/// <param name="conversation">Conversation.</param>
/// <param name="oldMessage">要修改的消息对象</param>
/// <param name="newMessage">新的消息对象</param>
public static Task<IAVIMMessage> UpdateAsync(this AVIMConversation conversation, IAVIMMessage oldMessage, IAVIMMessage newMessage)
{
return conversation.CurrentClient.UpdateAsync(oldMessage, newMessage);
}
}
/// <summary>
/// AVIMConversatio builder.
/// </summary>
public interface IAVIMConversatioBuilder
{
/// <summary>
/// Build this instance.
/// </summary>
/// <returns>The build.</returns>
AVIMConversation Build();
}
/// <summary>
/// AVIMConversation builder.
/// </summary>
public class AVIMConversationBuilder : IAVIMConversatioBuilder
{
/// <summary>
/// Gets or sets the client.
/// </summary>
/// <value>The client.</value>
public AVIMClient Client { get; internal set; }
private bool isUnique;
private Dictionary<string, object> properties;
private string name;
private bool isTransient;
private bool isSystem;
private List<string> members;
internal static AVIMConversationBuilder CreateDefaultBuilder()
{
return new AVIMConversationBuilder();
}
/// <summary>
/// Build this instance.
/// </summary>
/// <returns>The build.</returns>
public AVIMConversation Build()
{
var result = new AVIMConversation(
members: members,
name: name,
isUnique: isUnique,
isSystem: isSystem,
isTransient: isTransient,
client: Client);
if (properties != null)
{
foreach (var key in properties.Keys)
{
result[key] = properties[key];
}
}
return result;
}
/// <summary>
/// Sets the unique.
/// </summary>
/// <returns>The unique.</returns>
/// <param name="toggle">If set to <c>true</c> toggle.</param>
public AVIMConversationBuilder SetUnique(bool toggle = true)
{
this.isUnique = toggle;
return this;
}
/// <summary>
/// Sets the system.
/// </summary>
/// <returns>The system.</returns>
/// <param name="toggle">If set to <c>true</c> toggle.</param>
public AVIMConversationBuilder SetSystem(bool toggle = true)
{
this.isSystem = toggle;
return this;
}
/// <summary>
/// Sets the transient.
/// </summary>
/// <returns>The transient.</returns>
/// <param name="toggle">If set to <c>true</c> toggle.</param>
public AVIMConversationBuilder SetTransient(bool toggle = true)
{
this.isTransient = toggle;
return this;
}
/// <summary>
/// Sets the name.
/// </summary>
/// <returns>The name.</returns>
/// <param name="name">Name.</param>
public AVIMConversationBuilder SetName(string name)
{
this.name = name;
return this;
}
/// <summary>
/// Sets the members.
/// </summary>
/// <returns>The members.</returns>
/// <param name="memberClientIds">Member client identifiers.</param>
public AVIMConversationBuilder SetMembers(IEnumerable<string> memberClientIds)
{
this.members = memberClientIds.ToList();
return this;
}
/// <summary>
/// Adds the member.
/// </summary>
/// <returns>The member.</returns>
/// <param name="memberClientId">Member client identifier.</param>
public AVIMConversationBuilder AddMember(string memberClientId)
{
return AddMembers(new string[] { memberClientId });
}
/// <summary>
/// Adds the members.
/// </summary>
/// <returns>The members.</returns>
/// <param name="memberClientIds">Member client identifiers.</param>
public AVIMConversationBuilder AddMembers(IEnumerable<string> memberClientIds)
{
if (this.members == null) this.members = new List<string>();
this.members.AddRange(memberClientIds);
return this;
}
/// <summary>
/// Sets the property.
/// </summary>
/// <returns>The property.</returns>
/// <param name="key">Key.</param>
/// <param name="value">Value.</param>
public AVIMConversationBuilder SetProperty(string key, object value)
{
if (this.properties == null) this.properties = new Dictionary<string, object>();
this.properties[key] = value;
return this;
}
}
/// <summary>
/// AVIMM essage emitter builder.
/// </summary>
public class AVIMMessageEmitterBuilder
{
private AVIMConversation _conversation;
/// <summary>
/// Sets the conversation.
/// </summary>
/// <returns>The conversation.</returns>
/// <param name="conversation">Conversation.</param>
public AVIMMessageEmitterBuilder SetConversation(AVIMConversation conversation)
{
_conversation = conversation;
return this;
}
/// <summary>
/// Gets the conversation.
/// </summary>
/// <value>The conversation.</value>
public AVIMConversation Conversation
{
get
{
return _conversation;
}
}
private AVIMSendOptions _sendOptions;
/// <summary>
/// Gets the send options.
/// </summary>
/// <value>The send options.</value>
public AVIMSendOptions SendOptions
{
get
{
return _sendOptions;
}
}
/// <summary>
/// Sets the send options.
/// </summary>
/// <returns>The send options.</returns>
/// <param name="sendOptions">Send options.</param>
public AVIMMessageEmitterBuilder SetSendOptions(AVIMSendOptions sendOptions)
{
_sendOptions = sendOptions;
return this;
}
private IAVIMMessage _message;
/// <summary>
/// Gets the message.
/// </summary>
/// <value>The message.</value>
public IAVIMMessage Message
{
get
{
return _message;
}
}
/// <summary>
/// Sets the message.
/// </summary>
/// <returns>The message.</returns>
/// <param name="message">Message.</param>
public AVIMMessageEmitterBuilder SetMessage(IAVIMMessage message)
{
_message = message;
return this;
}
/// <summary>
/// Send async.
/// </summary>
/// <returns>The async.</returns>
public Task SendAsync()
{
return this.Conversation.SendAsync(this.Message, this.SendOptions);
}
}
}