refactoring: voipService

This commit is contained in:
Anton Suhorukov 2022-02-16 17:07:58 +03:00
parent 420f34c3ee
commit e42e0aa4e5
13 changed files with 1007 additions and 1028 deletions

View File

@ -28,23 +28,22 @@ namespace ASC.VoipService.Dao
{
public class AbstractDao
{
private readonly string dbid = "default";
private Lazy<VoipDbContext> LazyVoipDbContext { get; }
protected VoipDbContext VoipDbContext { get => LazyVoipDbContext.Value; }
protected AbstractDao(DbContextManager<VoipDbContext> dbOptions, TenantManager tenantManager)
{
LazyVoipDbContext = new Lazy<VoipDbContext>(() => dbOptions.Get(dbid));
TenantID = tenantManager.GetCurrentTenant().TenantId;
}
private readonly string _dbid = "default";
private Lazy<VoipDbContext> _lazyVoipDbContext;
protected VoipDbContext VoipDbContext { get => _lazyVoipDbContext.Value; }
protected int TenantID
{
get;
private set;
}
protected AbstractDao(DbContextManager<VoipDbContext> dbOptions, TenantManager tenantManager)
{
_lazyVoipDbContext = new Lazy<VoipDbContext>(() => dbOptions.Get(_dbid));
TenantID = tenantManager.GetCurrentTenant().TenantId;
}
protected string GetTenantColumnName(string table)
{
const string tenant = "tenant_id";

View File

@ -23,78 +23,77 @@
*
*/
namespace ASC.VoipService.Dao
namespace ASC.VoipService.Dao;
[Singletone]
public class VoipDaoCache
{
[Singletone]
public class VoipDaoCache
internal readonly ICache _cache;
private readonly ICacheNotify<CachedVoipItem> _notify;
public VoipDaoCache(ICacheNotify<CachedVoipItem> notify, ICache cache)
{
internal ICache Cache { get; }
private ICacheNotify<CachedVoipItem> Notify { get; }
public VoipDaoCache(ICacheNotify<CachedVoipItem> notify, ICache cache)
{
Cache = cache;
Notify = notify;
Notify.Subscribe((c) => Cache.Remove(CachedVoipDao.GetCacheKey(c.Tenant)), Common.Caching.CacheNotifyAction.Any);
}
public void ResetCache(int tenant)
{
Notify.Publish(new CachedVoipItem { Tenant = tenant }, Common.Caching.CacheNotifyAction.Any);
}
_cache = cache;
_notify = notify;
_notify.Subscribe((c) => _cache.Remove(CachedVoipDao.GetCacheKey(c.Tenant)), Common.Caching.CacheNotifyAction.Any);
}
[Scope]
public class CachedVoipDao : VoipDao
public void ResetCache(int tenant)
{
private readonly ICache cache;
private static readonly TimeSpan timeout = TimeSpan.FromDays(1);
_notify.Publish(new CachedVoipItem { Tenant = tenant }, Common.Caching.CacheNotifyAction.Any);
}
}
private VoipDaoCache VoipDaoCache { get; }
[Scope]
public class CachedVoipDao : VoipDao
{
private static readonly TimeSpan timeout = TimeSpan.FromDays(1);
private readonly ICache _cache;
private readonly VoipDaoCache _voipDaoCache;
public CachedVoipDao(
TenantManager tenantManager,
DbContextManager<VoipDbContext> dbOptions,
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext,
BaseCommonLinkUtility baseCommonLinkUtility,
ConsumerFactory consumerFactory,
VoipDaoCache voipDaoCache)
: base(tenantManager, dbOptions, authContext, tenantUtil, securityContext, baseCommonLinkUtility, consumerFactory)
{
cache = voipDaoCache.Cache;
VoipDaoCache = voipDaoCache;
}
public CachedVoipDao(
TenantManager tenantManager,
DbContextManager<VoipDbContext> dbOptions,
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext,
BaseCommonLinkUtility baseCommonLinkUtility,
ConsumerFactory consumerFactory,
VoipDaoCache voipDaoCache)
: base(tenantManager, dbOptions, authContext, tenantUtil, securityContext, baseCommonLinkUtility, consumerFactory)
{
_cache = voipDaoCache._cache;
_voipDaoCache = voipDaoCache;
}
public override VoipPhone SaveOrUpdateNumber(VoipPhone phone)
public override VoipPhone SaveOrUpdateNumber(VoipPhone phone)
{
var result = base.SaveOrUpdateNumber(phone);
_voipDaoCache.ResetCache(TenantID);
return result;
}
public override void DeleteNumber(string phoneId = "")
{
base.DeleteNumber(phoneId);
_voipDaoCache.ResetCache(TenantID);
}
public override IEnumerable<VoipPhone> GetNumbers(params string[] ids)
{
var numbers = _cache.Get<List<VoipPhone>>(GetCacheKey(TenantID));
if (numbers == null)
{
var result = base.SaveOrUpdateNumber(phone);
VoipDaoCache.ResetCache(TenantID);
return result;
numbers = new List<VoipPhone>(base.GetAllNumbers());
_cache.Insert(GetCacheKey(TenantID), numbers, DateTime.UtcNow.Add(timeout));
}
public override void DeleteNumber(string phoneId = "")
{
base.DeleteNumber(phoneId);
VoipDaoCache.ResetCache(TenantID);
}
return ids.Length > 0 ? numbers.Where(r => ids.Contains(r.Id) || ids.Contains(r.Number)).ToList() : numbers;
}
public override IEnumerable<VoipPhone> GetNumbers(params string[] ids)
{
var numbers = cache.Get<List<VoipPhone>>(GetCacheKey(TenantID));
if (numbers == null)
{
numbers = new List<VoipPhone>(base.GetAllNumbers());
cache.Insert(GetCacheKey(TenantID), numbers, DateTime.UtcNow.Add(timeout));
}
return ids.Length > 0 ? numbers.Where(r => ids.Contains(r.Id) || ids.Contains(r.Number)).ToList() : numbers;
}
public static string GetCacheKey(int tenant)
{
return "voip" + tenant.ToString(CultureInfo.InvariantCulture);
}
public static string GetCacheKey(int tenant)
{
return "voip" + tenant.ToString(CultureInfo.InvariantCulture);
}
}

View File

@ -23,92 +23,91 @@
*
*/
namespace ASC.VoipService.Dao
namespace ASC.VoipService.Dao;
public class VoipCallFilter
{
public class VoipCallFilter
public string Type { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public Guid? Agent { get; set; }
public int? Client { get; set; }
public int? ContactID { get; set; }
public string Id { get; set; }
public string ParentId { get; set; }
public string SortBy { get; set; }
public bool SortOrder { get; set; }
public string SearchText { get; set; }
public long Offset { get; set; }
public long Max { get; set; }
public int? TypeStatus
{
public string Type { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public Guid? Agent { get; set; }
public int? Client { get; set; }
public int? ContactID { get; set; }
public string Id { get; set; }
public string ParentId { get; set; }
public string SortBy { get; set; }
public bool SortOrder { get; set; }
public string SearchText { get; set; }
public long Offset { get; set; }
public long Max { get; set; }
public int? TypeStatus
get
{
get
{
if (string.IsNullOrWhiteSpace(Type)) return null;
if (TypeStatuses.TryGetValue(Type, out var status)) return status;
if (string.IsNullOrWhiteSpace(Type)) return null;
if (TypeStatuses.TryGetValue(Type, out var status)) return status;
return null;
}
return null;
}
}
public string SortByColumn
public string SortByColumn
{
get
{
get
{
if (string.IsNullOrWhiteSpace(SortBy)) return null;
return SortColumns.ContainsKey(SortBy) ? SortColumns[SortBy] : null;
}
if (string.IsNullOrWhiteSpace(SortBy)) return null;
return SortColumns.ContainsKey(SortBy) ? SortColumns[SortBy] : null;
}
}
private static Dictionary<string, int> TypeStatuses
private static Dictionary<string, int> TypeStatuses
{
get
{
get
{
return new Dictionary<string, int>
return new Dictionary<string, int>
{
{
{
"answered", (int)VoipCallStatus.Answered
},
{
"missed", (int)VoipCallStatus.Missed
},
{
"outgoing", (int)VoipCallStatus.Outcoming
}
};
}
}
private static Dictionary<string, string> SortColumns
{
get
{
return new Dictionary<string, string>
"answered", (int)VoipCallStatus.Answered
},
{
{
"date", "dial_date"
},
{
"duration", "dial_duration"
},
{
"price", "price"
},
};
}
"missed", (int)VoipCallStatus.Missed
},
{
"outgoing", (int)VoipCallStatus.Outcoming
}
};
}
}
private static Dictionary<string, string> SortColumns
{
get
{
return new Dictionary<string, string>
{
{
"date", "dial_date"
},
{
"duration", "dial_duration"
},
{
"price", "price"
},
};
}
}
}

View File

@ -23,342 +23,338 @@
*
*/
namespace ASC.VoipService.Dao
namespace ASC.VoipService.Dao;
[Scope(typeof(CachedVoipDao))]
public class VoipDao : AbstractDao
{
[Scope(typeof(CachedVoipDao))]
public class VoipDao : AbstractDao
private readonly AuthContext _authContext;
private readonly TenantUtil _tenantUtil;
private readonly SecurityContext _securityContext;
private readonly BaseCommonLinkUtility _baseCommonLinkUtility;
private readonly ConsumerFactory _consumerFactory;
public VoipDao(
TenantManager tenantManager,
DbContextManager<VoipDbContext> dbOptions,
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext,
BaseCommonLinkUtility baseCommonLinkUtility,
ConsumerFactory consumerFactory)
: base(dbOptions, tenantManager)
{
public VoipDao(
TenantManager tenantManager,
DbContextManager<VoipDbContext> dbOptions,
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext,
BaseCommonLinkUtility baseCommonLinkUtility,
ConsumerFactory consumerFactory)
: base(dbOptions, tenantManager)
_authContext = authContext;
_tenantUtil = tenantUtil;
_securityContext = securityContext;
_baseCommonLinkUtility = baseCommonLinkUtility;
_consumerFactory = consumerFactory;
}
public virtual VoipPhone SaveOrUpdateNumber(VoipPhone phone)
{
if (!string.IsNullOrEmpty(phone.Number))
{
AuthContext = authContext;
TenantUtil = tenantUtil;
SecurityContext = securityContext;
BaseCommonLinkUtility = baseCommonLinkUtility;
ConsumerFactory = consumerFactory;
phone.Number = phone.Number.TrimStart('+');
}
public virtual VoipPhone SaveOrUpdateNumber(VoipPhone phone)
var voipNumber = new VoipNumber
{
if (!string.IsNullOrEmpty(phone.Number))
{
phone.Number = phone.Number.TrimStart('+');
}
Id = phone.Id,
Number = phone.Number,
Alias = phone.Alias,
Settings = phone.Settings.ToString(),
TenantId = TenantID
};
var voipNumber = new VoipNumber
{
Id = phone.Id,
Number = phone.Number,
Alias = phone.Alias,
Settings = phone.Settings.ToString(),
TenantId = TenantID
};
VoipDbContext.VoipNumbers.Add(voipNumber);
VoipDbContext.SaveChanges();
VoipDbContext.VoipNumbers.Add(voipNumber);
VoipDbContext.SaveChanges();
return phone;
}
return phone;
public virtual void DeleteNumber(string phoneId = "")
{
var number = VoipDbContext.VoipNumbers.Where(r => r.Id == phoneId && r.TenantId == TenantID).FirstOrDefault();
VoipDbContext.VoipNumbers.Remove(number);
VoipDbContext.SaveChanges();
}
public virtual IEnumerable<VoipPhone> GetAllNumbers()
{
return VoipDbContext.VoipNumbers
.Where(r => r.TenantId == TenantID)
.ToList()
.ConvertAll(ToPhone);
}
public virtual IEnumerable<VoipPhone> GetNumbers(params string[] ids)
{
var numbers = VoipDbContext.VoipNumbers.Where(r => r.TenantId == TenantID);
if (ids.Length > 0)
{
numbers = numbers.Where(r => ids.Any(a => a == r.Number || a == r.Id));
}
public virtual void DeleteNumber(string phoneId = "")
return numbers.ToList().ConvertAll(ToPhone);
}
public VoipPhone GetNumber(string id)
{
return GetNumbers(id.TrimStart('+')).FirstOrDefault();
}
public virtual VoipPhone GetCurrentNumber()
{
return GetNumbers().FirstOrDefault(r => r.Caller != null);
}
public VoipCall SaveOrUpdateCall(VoipCall call)
{
var voipCall = new DbVoipCall
{
var number = VoipDbContext.VoipNumbers.Where(r => r.Id == phoneId && r.TenantId == TenantID).FirstOrDefault();
VoipDbContext.VoipNumbers.Remove(number);
VoipDbContext.SaveChanges();
TenantId = TenantID,
Id = call.Id,
NumberFrom = call.From,
NumberTo = call.To,
ContactId = call.ContactId
};
if (!string.IsNullOrEmpty(call.ParentID))
{
voipCall.ParentCallId = call.ParentID;
}
public virtual IEnumerable<VoipPhone> GetAllNumbers()
if (call.Status.HasValue)
{
return VoipDbContext.VoipNumbers
.Where(r => r.TenantId == TenantID)
.ToList()
.ConvertAll(ToPhone);
voipCall.Status = (int)call.Status.Value;
}
public virtual IEnumerable<VoipPhone> GetNumbers(params string[] ids)
if (!call.AnsweredBy.Equals(Guid.Empty))
{
var numbers = VoipDbContext.VoipNumbers.Where(r => r.TenantId == TenantID);
if (ids.Length > 0)
{
numbers = numbers.Where(r => ids.Any(a => a == r.Number || a == r.Id));
}
return numbers.ToList().ConvertAll(ToPhone);
voipCall.AnsweredBy = call.AnsweredBy;
}
public VoipPhone GetNumber(string id)
if (call.DialDate == DateTime.MinValue)
{
return GetNumbers(id.TrimStart('+')).FirstOrDefault();
call.DialDate = DateTime.UtcNow;
}
public virtual VoipPhone GetCurrentNumber()
voipCall.DialDate = _tenantUtil.DateTimeToUtc(call.DialDate);
if (call.DialDuration > 0)
{
return GetNumbers().FirstOrDefault(r => r.Caller != null);
voipCall.DialDuration = call.DialDuration;
}
public VoipCall SaveOrUpdateCall(VoipCall call)
if (call.Price > decimal.Zero)
{
var voipCall = new DbVoipCall
{
TenantId = TenantID,
Id = call.Id,
NumberFrom = call.From,
NumberTo = call.To,
ContactId = call.ContactId
};
voipCall.Price = call.Price;
}
if (!string.IsNullOrEmpty(call.ParentID))
if (call.VoipRecord != null)
{
if (!string.IsNullOrEmpty(call.VoipRecord.Id))
{
voipCall.ParentCallId = call.ParentID;
voipCall.RecordSid = call.VoipRecord.Id;
}
if (call.Status.HasValue)
if (!string.IsNullOrEmpty(call.VoipRecord.Uri))
{
voipCall.Status = (int)call.Status.Value;
voipCall.RecordUrl = call.VoipRecord.Uri;
}
if (!call.AnsweredBy.Equals(Guid.Empty))
if (call.VoipRecord.Duration != 0)
{
voipCall.AnsweredBy = call.AnsweredBy;
voipCall.RecordDuration = call.VoipRecord.Duration;
}
if (call.DialDate == DateTime.MinValue)
if (call.VoipRecord.Price != default)
{
call.DialDate = DateTime.UtcNow;
voipCall.RecordPrice = call.VoipRecord.Price;
}
}
voipCall.DialDate = TenantUtil.DateTimeToUtc(call.DialDate);
VoipDbContext.VoipCalls.Add(voipCall);
VoipDbContext.SaveChanges();
if (call.DialDuration > 0)
{
voipCall.DialDuration = call.DialDuration;
}
return call;
}
if (call.Price > decimal.Zero)
{
voipCall.Price = call.Price;
}
public IEnumerable<VoipCall> GetCalls(VoipCallFilter filter)
{
var query = GetCallsQuery(filter);
if (call.VoipRecord != null)
{
if (!string.IsNullOrEmpty(call.VoipRecord.Id))
{
voipCall.RecordSid = call.VoipRecord.Id;
}
if (filter.SortByColumn != null)
{
query.OrderBy(filter.SortByColumn, filter.SortOrder);
}
if (!string.IsNullOrEmpty(call.VoipRecord.Uri))
{
voipCall.RecordUrl = call.VoipRecord.Uri;
}
query = query.Skip((int)filter.Offset);
query = query.Take((int)filter.Max * 3);
if (call.VoipRecord.Duration != 0)
{
voipCall.RecordDuration = call.VoipRecord.Duration;
}
if (call.VoipRecord.Price != default)
{
voipCall.RecordPrice = call.VoipRecord.Price;
}
}
VoipDbContext.VoipCalls.Add(voipCall);
VoipDbContext.SaveChanges();
var calls = query.ToList().ConvertAll(ToCall);
calls = calls.GroupJoin(calls, call => call.Id, h => h.ParentID, (call, h) =>
{
call.ChildCalls.AddRange(h);
return call;
}
}).Where(r => string.IsNullOrEmpty(r.ParentID)).ToList();
public IEnumerable<VoipCall> GetCalls(VoipCallFilter filter)
return calls;
}
public VoipCall GetCall(string id)
{
return GetCalls(new VoipCallFilter { Id = id }).FirstOrDefault();
}
public int GetCallsCount(VoipCallFilter filter)
{
return GetCallsQuery(filter).Where(r => r.DbVoipCall.ParentCallId == "").Count();
}
public IEnumerable<VoipCall> GetMissedCalls(Guid agent, long count = 0, DateTime? from = null)
{
var query = GetCallsQuery(new VoipCallFilter { Agent = agent, SortBy = "date", SortOrder = true, Type = "missed" });
if (from.HasValue)
{
var query = GetCallsQuery(filter);
if (filter.SortByColumn != null)
{
query.OrderBy(filter.SortByColumn, filter.SortOrder);
}
query = query.Skip((int)filter.Offset);
query = query.Take((int)filter.Max * 3);
var calls = query.ToList().ConvertAll(ToCall);
calls = calls.GroupJoin(calls, call => call.Id, h => h.ParentID, (call, h) =>
{
call.ChildCalls.AddRange(h);
return call;
}).Where(r => string.IsNullOrEmpty(r.ParentID)).ToList();
return calls;
query = query.Where(r => r.DbVoipCall.DialDate >= _tenantUtil.DateTimeFromUtc(from.Value));
}
public VoipCall GetCall(string id)
if (count != 0)
{
return GetCalls(new VoipCallFilter { Id = id }).FirstOrDefault();
query = query.Take((int)count);
}
public int GetCallsCount(VoipCallFilter filter)
var a = query.Select(ca => new
{
return GetCallsQuery(filter).Where(r => r.DbVoipCall.ParentCallId == "").Count();
}
dbVoipCall = ca,
tmpDate = VoipDbContext.VoipCalls
.Where(tmp => tmp.TenantId == ca.DbVoipCall.TenantId)
.Where(tmp => tmp.NumberFrom == ca.DbVoipCall.NumberFrom || tmp.NumberTo == ca.DbVoipCall.NumberFrom)
.Where(tmp => tmp.Status <= (int)VoipCallStatus.Missed)
.Max(tmp => tmp.DialDate)
}).Where(r => r.dbVoipCall.DbVoipCall.DialDate >= r.tmpDate || r.tmpDate == default);
public IEnumerable<VoipCall> GetMissedCalls(Guid agent, long count = 0, DateTime? from = null)
return a.ToList().ConvertAll(r => ToCall(r.dbVoipCall));
}
private IQueryable<CallContact> GetCallsQuery(VoipCallFilter filter)
{
var q = VoipDbContext.VoipCalls
.Where(r => r.TenantId == TenantID);
if (!string.IsNullOrEmpty(filter.Id))
{
var query = GetCallsQuery(new VoipCallFilter { Agent = agent, SortBy = "date", SortOrder = true, Type = "missed" });
if (from.HasValue)
{
query = query.Where(r => r.DbVoipCall.DialDate >= TenantUtil.DateTimeFromUtc(from.Value));
}
if (count != 0)
{
query = query.Take((int)count);
}
var a = query.Select(ca => new
{
dbVoipCall = ca,
tmpDate = VoipDbContext.VoipCalls
.Where(tmp => tmp.TenantId == ca.DbVoipCall.TenantId)
.Where(tmp => tmp.NumberFrom == ca.DbVoipCall.NumberFrom || tmp.NumberTo == ca.DbVoipCall.NumberFrom)
.Where(tmp => tmp.Status <= (int)VoipCallStatus.Missed)
.Max(tmp => tmp.DialDate)
}).Where(r => r.dbVoipCall.DbVoipCall.DialDate >= r.tmpDate || r.tmpDate == default);
return a.ToList().ConvertAll(r => ToCall(r.dbVoipCall));
q = q.Where(r => r.Id == filter.Id || r.ParentCallId == filter.Id);
}
private IQueryable<CallContact> GetCallsQuery(VoipCallFilter filter)
if (filter.ContactID.HasValue)
{
var q = VoipDbContext.VoipCalls
.Where(r => r.TenantId == TenantID);
if (!string.IsNullOrEmpty(filter.Id))
{
q = q.Where(r => r.Id == filter.Id || r.ParentCallId == filter.Id);
}
if (filter.ContactID.HasValue)
{
q = q.Where(r => r.ContactId == filter.ContactID.Value);
}
if (!string.IsNullOrWhiteSpace(filter.SearchText))
{
q = q.Where(r => r.Id.StartsWith(filter.SearchText));
}
if (filter.TypeStatus.HasValue)
{
q = q.Where(r => r.Status == filter.TypeStatus.Value);
}
if (filter.FromDate.HasValue)
{
q = q.Where(r => r.DialDate >= filter.FromDate.Value);
}
if (filter.ToDate.HasValue)
{
q = q.Where(r => r.DialDate <= filter.ToDate.Value);
}
if (filter.Agent.HasValue)
{
q = q.Where(r => r.AnsweredBy == filter.Agent.Value);
}
return q
.GroupBy(r => r.Id, r => r)
.Join(
VoipDbContext.CrmContact.DefaultIfEmpty(),
r => r.FirstOrDefault().ContactId,
c => c.Id,
(call, contact) => new CallContact { DbVoipCall = call.FirstOrDefault(), CrmContact = contact })
;
q = q.Where(r => r.ContactId == filter.ContactID.Value);
}
class CallContact
if (!string.IsNullOrWhiteSpace(filter.SearchText))
{
public DbVoipCall DbVoipCall { get; set; }
public CrmContact CrmContact { get; set; }
q = q.Where(r => r.Id.StartsWith(filter.SearchText));
}
#region Converters
private VoipPhone ToPhone(VoipNumber r)
if (filter.TypeStatus.HasValue)
{
return GetProvider().GetPhone(r);
q = q.Where(r => r.Status == filter.TypeStatus.Value);
}
private VoipCall ToCall(CallContact dbVoipCall)
if (filter.FromDate.HasValue)
{
var call = new VoipCall
q = q.Where(r => r.DialDate >= filter.FromDate.Value);
}
if (filter.ToDate.HasValue)
{
q = q.Where(r => r.DialDate <= filter.ToDate.Value);
}
if (filter.Agent.HasValue)
{
q = q.Where(r => r.AnsweredBy == filter.Agent.Value);
}
return q
.GroupBy(r => r.Id, r => r)
.Join(
VoipDbContext.CrmContact.DefaultIfEmpty(),
r => r.FirstOrDefault().ContactId,
c => c.Id,
(call, contact) => new CallContact { DbVoipCall = call.FirstOrDefault(), CrmContact = contact })
;
}
class CallContact
{
public DbVoipCall DbVoipCall { get; set; }
public CrmContact CrmContact { get; set; }
}
private VoipPhone ToPhone(VoipNumber r)
{
return GetProvider().GetPhone(r);
}
private VoipCall ToCall(CallContact dbVoipCall)
{
var call = new VoipCall
{
Id = dbVoipCall.DbVoipCall.Id,
ParentID = dbVoipCall.DbVoipCall.ParentCallId,
From = dbVoipCall.DbVoipCall.NumberFrom,
To = dbVoipCall.DbVoipCall.NumberTo,
AnsweredBy = dbVoipCall.DbVoipCall.AnsweredBy,
DialDate = _tenantUtil.DateTimeFromUtc(dbVoipCall.DbVoipCall.DialDate),
DialDuration = dbVoipCall.DbVoipCall.DialDuration,
Price = dbVoipCall.DbVoipCall.Price,
Status = (VoipCallStatus)dbVoipCall.DbVoipCall.Status,
VoipRecord = new VoipRecord
{
Id = dbVoipCall.DbVoipCall.Id,
ParentID = dbVoipCall.DbVoipCall.ParentCallId,
From = dbVoipCall.DbVoipCall.NumberFrom,
To = dbVoipCall.DbVoipCall.NumberTo,
AnsweredBy = dbVoipCall.DbVoipCall.AnsweredBy,
DialDate = TenantUtil.DateTimeFromUtc(dbVoipCall.DbVoipCall.DialDate),
DialDuration = dbVoipCall.DbVoipCall.DialDuration,
Price = dbVoipCall.DbVoipCall.Price,
Status = (VoipCallStatus)dbVoipCall.DbVoipCall.Status,
VoipRecord = new VoipRecord
{
Id = dbVoipCall.DbVoipCall.RecordSid,
Uri = dbVoipCall.DbVoipCall.RecordUrl,
Duration = dbVoipCall.DbVoipCall.RecordDuration,
Price = dbVoipCall.DbVoipCall.RecordPrice
},
ContactId = dbVoipCall.CrmContact.Id,
ContactIsCompany = dbVoipCall.CrmContact.IsCompany,
};
Id = dbVoipCall.DbVoipCall.RecordSid,
Uri = dbVoipCall.DbVoipCall.RecordUrl,
Duration = dbVoipCall.DbVoipCall.RecordDuration,
Price = dbVoipCall.DbVoipCall.RecordPrice
},
ContactId = dbVoipCall.CrmContact.Id,
ContactIsCompany = dbVoipCall.CrmContact.IsCompany,
};
if (call.ContactId != 0)
{
call.ContactTitle = call.ContactIsCompany
? dbVoipCall.CrmContact.CompanyName
: dbVoipCall.CrmContact.FirstName == null || dbVoipCall.CrmContact.LastName == null ? null : $"{dbVoipCall.CrmContact.FirstName} {dbVoipCall.CrmContact.LastName}";
}
return call;
}
public Consumer Consumer
if (call.ContactId != 0)
{
get { return ConsumerFactory.GetByKey("twilio"); }
call.ContactTitle = call.ContactIsCompany
? dbVoipCall.CrmContact.CompanyName
: dbVoipCall.CrmContact.FirstName == null || dbVoipCall.CrmContact.LastName == null ? null : $"{dbVoipCall.CrmContact.FirstName} {dbVoipCall.CrmContact.LastName}";
}
public TwilioProvider GetProvider()
return call;
}
public Consumer Consumer
{
get { return _consumerFactory.GetByKey("twilio"); }
}
public TwilioProvider GetProvider()
{
return new TwilioProvider(Consumer["twilioAccountSid"], Consumer["twilioAuthToken"], _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility);
}
public bool ConfigSettingsExist
{
get
{
return new TwilioProvider(Consumer["twilioAccountSid"], Consumer["twilioAuthToken"], AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility);
return !string.IsNullOrEmpty(Consumer["twilioAccountSid"]) &&
!string.IsNullOrEmpty(Consumer["twilioAuthToken"]);
}
public bool ConfigSettingsExist
{
get
{
return !string.IsNullOrEmpty(Consumer["twilioAccountSid"]) &&
!string.IsNullOrEmpty(Consumer["twilioAuthToken"]);
}
}
private AuthContext AuthContext { get; }
private TenantUtil TenantUtil { get; }
private SecurityContext SecurityContext { get; }
private BaseCommonLinkUtility BaseCommonLinkUtility { get; }
private ConsumerFactory ConsumerFactory { get; }
#endregion
}
}

View File

@ -23,32 +23,31 @@
*
*/
namespace ASC.VoipService
namespace ASC.VoipService;
public interface IVoipProvider
{
public interface IVoipProvider
{
IEnumerable<VoipPhone> GetExistingPhoneNumbers();
IEnumerable<VoipPhone> GetExistingPhoneNumbers();
IEnumerable<VoipPhone> GetAvailablePhoneNumbers(PhoneNumberType phoneNumberType, string isoCountryCode);
IEnumerable<VoipPhone> GetAvailablePhoneNumbers(PhoneNumberType phoneNumberType, string isoCountryCode);
VoipPhone BuyNumber(string phoneNumber);
VoipPhone BuyNumber(string phoneNumber);
VoipPhone DeleteNumber(VoipPhone phone);
VoipPhone DeleteNumber(VoipPhone phone);
VoipPhone GetPhone(VoipNumber r);
VoipPhone GetPhone(VoipNumber r);
VoipPhone GetPhone(string id);
VoipPhone GetPhone(string id);
VoipCall GetCall(string callId);
VoipCall GetCall(string callId);
string GetToken(Agent agent, int seconds = 60 * 60 * 24);
string GetToken(Agent agent, int seconds = 60 * 60 * 24);
void UpdateSettings(VoipPhone phone);
void UpdateSettings(VoipPhone phone);
VoipRecord GetRecord(string callId, string recordId);
VoipRecord GetRecord(string callId, string recordId);
void CreateQueue(VoipPhone newPhone);
void CreateQueue(VoipPhone newPhone);
void DisablePhone(VoipPhone phone);
}
void DisablePhone(VoipPhone phone);
}

View File

@ -27,8 +27,7 @@ namespace ASC.VoipService.Twilio
{
public class TwilioPhone : VoipPhone
{
private readonly TwilioRestClient twilio;
private readonly TwilioRestClient _twilio;
public TwilioPhone(
TwilioRestClient twilio,
AuthContext authContext,
@ -37,7 +36,7 @@ namespace ASC.VoipService.Twilio
BaseCommonLinkUtility baseCommonLinkUtility) :
base(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
{
this.twilio = twilio;
_twilio = twilio;
Settings = new TwilioVoipSettings(authContext, tenantUtil, securityContext, baseCommonLinkUtility);
}
@ -52,7 +51,7 @@ namespace ASC.VoipService.Twilio
SendDigits = number.Length > 1 ? number[1] + "#" : string.Empty,
Record = Settings.Caller.Record,
Url = new System.Uri(Settings.Connect(contactId: contactId))
}, twilio);
}, _twilio);
return new VoipCall { Id = call.Sid, From = call.From, To = call.To };
}
@ -64,7 +63,7 @@ namespace ASC.VoipService.Twilio
public override VoipCall RedirectCall(string callId, string to)
{
var call = CallResource.Update(callId, url: new System.Uri(Settings.Redirect(to)), method: HttpMethod.Post, client: twilio);
var call = CallResource.Update(callId, url: new System.Uri(Settings.Redirect(to)), method: HttpMethod.Post, client: _twilio);
return new VoipCall { Id = call.Sid, To = to };
}
@ -79,24 +78,24 @@ namespace ASC.VoipService.Twilio
public Queue CreateQueue(string name, int size, string waitUrl, int waitTime)
{
var queues = QueueResource.Read(new ReadQueueOptions(), twilio);
var queues = QueueResource.Read(new ReadQueueOptions(), _twilio);
var queue = queues.FirstOrDefault(r => r.FriendlyName == name);
if (queue == null)
{
queue = QueueResource.Create(name, client: twilio);
queue = QueueResource.Create(name, client: _twilio);
}
return new Queue(queue.Sid, name, size, waitUrl, waitTime);
}
public string GetQueue(string name)
{
var queues = QueueResource.Read(new ReadQueueOptions(), twilio);
var queues = QueueResource.Read(new ReadQueueOptions(), _twilio);
return queues.First(r => r.FriendlyName == name).Sid;
}
public IEnumerable<string> QueueCalls(string id)
{
var calls = MemberResource.Read(id, client: twilio);
var calls = MemberResource.Read(id, client: _twilio);
return calls.Select(r => r.CallSid);
}
@ -106,7 +105,7 @@ namespace ASC.VoipService.Twilio
if (calls.Contains(callId))
{
MemberResource.Update(queueId, callId, new System.Uri(Settings.Dequeue(reject)), HttpMethod.Post,
client: twilio);
client: _twilio);
}
}

View File

@ -31,28 +31,27 @@ namespace ASC.VoipService.Twilio
{
public class TwilioProvider : IVoipProvider
{
private readonly string accountSid;
private readonly string authToken;
private readonly TwilioRestClient client;
private AuthContext AuthContext { get; }
private TenantUtil TenantUtil { get; }
private SecurityContext SecurityContext { get; }
private BaseCommonLinkUtility BaseCommonLinkUtility { get; }
private readonly string _accountSid;
private readonly string _authToken;
private readonly TwilioRestClient _client;
private readonly AuthContext _authContext;
private readonly TenantUtil _tenantUtil;
private readonly SecurityContext _securityContext;
private readonly BaseCommonLinkUtility _baseCommonLinkUtility;
public TwilioProvider(string accountSid, string authToken, AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
{
if (string.IsNullOrEmpty(accountSid)) throw new ArgumentNullException(nameof(accountSid));
if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException(nameof(authToken));
this.authToken = authToken;
AuthContext = authContext;
TenantUtil = tenantUtil;
SecurityContext = securityContext;
BaseCommonLinkUtility = baseCommonLinkUtility;
this.accountSid = accountSid;
_authToken = authToken;
_authContext = authContext;
_tenantUtil = tenantUtil;
_securityContext = securityContext;
_baseCommonLinkUtility = baseCommonLinkUtility;
_accountSid = accountSid;
client = new TwilioRestClient(accountSid, authToken);
_client = new TwilioRestClient(accountSid, authToken);
}
#region Call
@ -66,7 +65,7 @@ namespace ASC.VoipService.Twilio
{
try
{
var record = RecordingResource.Fetch(callId, recordSid, client: client);
var record = RecordingResource.Fetch(callId, recordSid, client: _client);
if (!record.Price.HasValue)
{
@ -108,31 +107,31 @@ namespace ASC.VoipService.Twilio
var newNumber = IncomingPhoneNumberResource.Create(
new CreateIncomingPhoneNumberOptions
{
PathAccountSid = accountSid,
PathAccountSid = _accountSid,
PhoneNumber = new PhoneNumber(phoneNumber)
}, client);
}, _client);
return new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { Id = newNumber.Sid, Number = phoneNumber.Substring(1) };
return new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility) { Id = newNumber.Sid, Number = phoneNumber.Substring(1) };
}
public VoipPhone DeleteNumber(VoipPhone phone)
{
IncomingPhoneNumberResource.Delete(phone.Id, client: client);
IncomingPhoneNumberResource.Delete(phone.Id, client: _client);
return phone;
}
public IEnumerable<VoipPhone> GetExistingPhoneNumbers()
{
var result = IncomingPhoneNumberResource.Read(client: client);
return result.Select(r => new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { Id = r.Sid, Number = r.PhoneNumber.ToString() });
var result = IncomingPhoneNumberResource.Read(client: _client);
return result.Select(r => new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility) { Id = r.Sid, Number = r.PhoneNumber.ToString() });
}
public IEnumerable<VoipPhone> GetAvailablePhoneNumbers(PhoneNumberType phoneNumberType, string isoCountryCode)
{
return phoneNumberType switch
{
PhoneNumberType.Local => LocalResource.Read(isoCountryCode, voiceEnabled: true, client: client).Select(r => new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { Number = r.PhoneNumber.ToString() }),
PhoneNumberType.TollFree => TollFreeResource.Read(isoCountryCode, voiceEnabled: true, client: client).Select(r => new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { Number = r.PhoneNumber.ToString() }),
PhoneNumberType.Local => LocalResource.Read(isoCountryCode, voiceEnabled: true, client: _client).Select(r => new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility) { Number = r.PhoneNumber.ToString() }),
PhoneNumberType.TollFree => TollFreeResource.Read(isoCountryCode, voiceEnabled: true, client: _client).Select(r => new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility) { Number = r.PhoneNumber.ToString() }),
_ => new List<VoipPhone>(),
};
@ -140,13 +139,13 @@ namespace ASC.VoipService.Twilio
public VoipPhone GetPhone(string phoneSid)
{
var phone = IncomingPhoneNumberResource.Fetch(phoneSid, client: client);
var phone = IncomingPhoneNumberResource.Fetch(phoneSid, client: _client);
var result = new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility)
var result = new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility)
{
Id = phone.Sid,
Number = phone.PhoneNumber.ToString(),
Settings = new TwilioVoipSettings(AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility)
Settings = new TwilioVoipSettings(_authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility)
};
if (phone.VoiceUrl == null)
@ -159,12 +158,12 @@ namespace ASC.VoipService.Twilio
public VoipPhone GetPhone(VoipNumber data)
{
return new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility)
return new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility)
{
Id = data.Id,
Number = data.Number,
Alias = data.Alias,
Settings = new TwilioVoipSettings(data.Settings, AuthContext)
Settings = new TwilioVoipSettings(data.Settings, _authContext)
};
}
@ -177,7 +176,7 @@ namespace ASC.VoipService.Twilio
{
try
{
var call = CallResource.Fetch(result.Id, client: client);
var call = CallResource.Fetch(result.Id, client: _client);
if (!call.Price.HasValue || string.IsNullOrEmpty(call.Duration))
{
count--;
@ -205,19 +204,19 @@ namespace ASC.VoipService.Twilio
{
new IncomingClientScope(agent.ClientID)
};
var capability = new ClientCapability(accountSid, authToken, scopes: scopes);
var capability = new ClientCapability(_accountSid, _authToken, scopes: scopes);
return capability.ToJwt();
}
public void UpdateSettings(VoipPhone phone)
{
IncomingPhoneNumberResource.Update(phone.Id, voiceUrl: new Uri(phone.Settings.Connect(false)), client: client);
IncomingPhoneNumberResource.Update(phone.Id, voiceUrl: new Uri(phone.Settings.Connect(false)), client: _client);
}
public void DisablePhone(VoipPhone phone)
{
IncomingPhoneNumberResource.Update(phone.Id, voiceUrl: new Uri("https://demo.twilio.com/welcome/voice/"), client: client);
IncomingPhoneNumberResource.Update(phone.Id, voiceUrl: new Uri("https://demo.twilio.com/welcome/voice/"), client: _client);
}
#endregion

View File

@ -23,189 +23,187 @@
*
*/
namespace ASC.VoipService.Twilio
namespace ASC.VoipService.Twilio;
public class TwilioResponseHelper
{
public class TwilioResponseHelper
private readonly VoipSettings _settings;
private readonly string _baseUrl;
private readonly AuthContext _authContext;
private readonly TenantUtil _tenantUtil;
private readonly SecurityContext _securityContext;
public TwilioResponseHelper(
VoipSettings settings,
string baseUrl,
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext)
{
private readonly VoipSettings settings;
private readonly string baseUrl;
_settings = settings;
_authContext = authContext;
_tenantUtil = tenantUtil;
_securityContext = securityContext;
_baseUrl = baseUrl.TrimEnd('/') + "/twilio/";
}
private AuthContext AuthContext { get; }
private TenantUtil TenantUtil { get; }
private SecurityContext SecurityContext { get; }
public VoiceResponse Inbound(Tuple<Agent, bool> agentTuple)
{
var agent = agentTuple?.Item1;
var anyOnline = agentTuple != null && agentTuple.Item2;
var response = new VoiceResponse();
public TwilioResponseHelper(
VoipSettings settings,
string baseUrl,
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext)
if (_settings.WorkingHours != null && _settings.WorkingHours.Enabled)
{
this.settings = settings;
AuthContext = authContext;
TenantUtil = tenantUtil;
SecurityContext = securityContext;
this.baseUrl = baseUrl.TrimEnd('/') + "/twilio/";
var now = _tenantUtil.DateTimeFromUtc(DateTime.UtcNow);
if (!(_settings.WorkingHours.From <= now.TimeOfDay && _settings.WorkingHours.To >= now.TimeOfDay))
{
return AddVoiceMail(response);
}
}
public VoiceResponse Inbound(Tuple<Agent, bool> agentTuple)
if (anyOnline)
{
var agent = agentTuple?.Item1;
var anyOnline = agentTuple != null && agentTuple.Item2;
var response = new VoiceResponse();
if (settings.WorkingHours != null && settings.WorkingHours.Enabled)
if (!string.IsNullOrEmpty(_settings.GreetingAudio))
{
var now = TenantUtil.DateTimeFromUtc(DateTime.UtcNow);
if (!(settings.WorkingHours.From <= now.TimeOfDay && settings.WorkingHours.To >= now.TimeOfDay))
{
return AddVoiceMail(response);
}
response.Play(Uri.EscapeDataString(_settings.GreetingAudio));
}
if (anyOnline)
{
if (!string.IsNullOrEmpty(settings.GreetingAudio))
{
response.Play(Uri.EscapeDataString(settings.GreetingAudio));
}
response.Enqueue(settings.Queue.Name, GetEcho("Enqueue", agent != null), "POST",
GetEcho("Wait", agent != null), "POST");
}
return AddVoiceMail(response);
response.Enqueue(_settings.Queue.Name, GetEcho("Enqueue", agent != null), "POST",
GetEcho("Wait", agent != null), "POST");
}
public VoiceResponse Outbound()
return AddVoiceMail(response);
}
public VoiceResponse Outbound()
{
return !_settings.Caller.AllowOutgoingCalls
? new VoiceResponse()
: AddToResponse(new VoiceResponse(), _settings.Caller);
}
public VoiceResponse Dial()
{
return new VoiceResponse();
}
public VoiceResponse Queue()
{
return new VoiceResponse();
}
public VoiceResponse Enqueue(string queueResult)
{
return queueResult == "leave" ? AddVoiceMail(new VoiceResponse()) : new VoiceResponse();
}
public VoiceResponse Dequeue()
{
return AddToResponse(new VoiceResponse(), _settings.Caller);
}
public VoiceResponse Leave()
{
return AddVoiceMail(new VoiceResponse());
}
public VoiceResponse Wait(string queueTime, string queueSize)
{
var response = new VoiceResponse();
var queue = _settings.Queue;
if (Convert.ToInt32(queueTime) > queue.WaitTime || Convert.ToInt32(queueSize) > queue.Size) return response.Leave();
if (!string.IsNullOrEmpty(queue.WaitUrl))
{
return !settings.Caller.AllowOutgoingCalls
? new VoiceResponse()
: AddToResponse(new VoiceResponse(), settings.Caller);
var gather = new Gather(method: "POST", action: GetEcho("gatherQueue"));
gather.Play(Uri.EscapeDataString(queue.WaitUrl));
response.Gather(gather);
}
public VoiceResponse Dial()
else
{
return new VoiceResponse();
response.Pause(queue.WaitTime);
}
public VoiceResponse Queue()
return response;
}
public VoiceResponse GatherQueue(string digits, List<Agent> availableOperators)
{
var response = new VoiceResponse();
if (digits == "#") return AddVoiceMail(response);
var oper = _settings.Operators.Find(r => r.PostFix == digits && availableOperators.Contains(r)) ??
_settings.Operators.FirstOrDefault(r => availableOperators.Contains(r));
return oper != null ? AddToResponse(response, oper) : response;
}
public VoiceResponse Redirect(string to)
{
if (to == "hold")
{
return new VoiceResponse();
return new VoiceResponse().Play(Uri.EscapeDataString(_settings.HoldAudio), 0);
}
public VoiceResponse Enqueue(string queueResult)
if (Guid.TryParse(to, out var newCallerId))
{
return queueResult == "leave" ? AddVoiceMail(new VoiceResponse()) : new VoiceResponse();
_securityContext.AuthenticateMeWithoutCookie(newCallerId);
}
public VoiceResponse Dequeue()
return new VoiceResponse().Enqueue(_settings.Queue.Name, GetEcho("enqueue"), "POST",
GetEcho("wait") + "&RedirectTo=" + to, "POST");
}
public VoiceResponse VoiceMail()
{
return new VoiceResponse();
}
private VoiceResponse AddToResponse(VoiceResponse response, Agent agent)
{
var dial = new Dial(method: "POST", action: GetEcho("dial"), timeout: agent.TimeOut, record: agent.Record ? "record-from-answer" : "do-not-record");
switch (agent.Answer)
{
return AddToResponse(new VoiceResponse(), settings.Caller);
case AnswerType.Number:
response.Dial(dial.Number(agent.PhoneNumber, method: "POST", url: GetEcho("client")));
break;
case AnswerType.Client:
response.Dial(dial.Client(agent.ClientID, "POST", GetEcho("client")));
break;
case AnswerType.Sip:
response.Dial(dial.Sip(agent.ClientID, method: "POST", url: GetEcho("client")));
break;
}
public VoiceResponse Leave()
return response;
}
private VoiceResponse AddVoiceMail(VoiceResponse response)
{
return string.IsNullOrEmpty(_settings.VoiceMail)
? response.Say("")
: response.Play(Uri.EscapeDataString(_settings.VoiceMail)).Record(method: "POST", action: GetEcho("voiceMail"), maxLength: 30);
}
public string GetEcho(string action, bool user = true)
{
var result = _baseUrl.TrimEnd('/');
if (!string.IsNullOrEmpty(action))
{
return AddVoiceMail(new VoiceResponse());
result += "/" + action.TrimStart('/');
}
public VoiceResponse Wait(string queueTime, string queueSize)
if (user)
{
var response = new VoiceResponse();
var queue = settings.Queue;
if (Convert.ToInt32(queueTime) > queue.WaitTime || Convert.ToInt32(queueSize) > queue.Size) return response.Leave();
if (!string.IsNullOrEmpty(queue.WaitUrl))
{
var gather = new Gather(method: "POST", action: GetEcho("gatherQueue"));
gather.Play(Uri.EscapeDataString(queue.WaitUrl));
response.Gather(gather);
}
else
{
response.Pause(queue.WaitTime);
}
return response;
result += "?CallerId=" + _authContext.CurrentAccount.ID;
}
public VoiceResponse GatherQueue(string digits, List<Agent> availableOperators)
{
var response = new VoiceResponse();
if (digits == "#") return AddVoiceMail(response);
var oper = settings.Operators.Find(r => r.PostFix == digits && availableOperators.Contains(r)) ??
settings.Operators.FirstOrDefault(r => availableOperators.Contains(r));
return oper != null ? AddToResponse(response, oper) : response;
}
public VoiceResponse Redirect(string to)
{
if (to == "hold")
{
return new VoiceResponse().Play(Uri.EscapeDataString(settings.HoldAudio), 0);
}
if (Guid.TryParse(to, out var newCallerId))
{
SecurityContext.AuthenticateMeWithoutCookie(newCallerId);
}
return new VoiceResponse().Enqueue(settings.Queue.Name, GetEcho("enqueue"), "POST",
GetEcho("wait") + "&RedirectTo=" + to, "POST");
}
public VoiceResponse VoiceMail()
{
return new VoiceResponse();
}
private VoiceResponse AddToResponse(VoiceResponse response, Agent agent)
{
var dial = new Dial(method: "POST", action: GetEcho("dial"), timeout: agent.TimeOut, record: agent.Record ? "record-from-answer" : "do-not-record");
switch (agent.Answer)
{
case AnswerType.Number:
response.Dial(dial.Number(agent.PhoneNumber, method: "POST", url: GetEcho("client")));
break;
case AnswerType.Client:
response.Dial(dial.Client(agent.ClientID, "POST", GetEcho("client")));
break;
case AnswerType.Sip:
response.Dial(dial.Sip(agent.ClientID, method: "POST", url: GetEcho("client")));
break;
}
return response;
}
private VoiceResponse AddVoiceMail(VoiceResponse response)
{
return string.IsNullOrEmpty(settings.VoiceMail)
? response.Say("")
: response.Play(Uri.EscapeDataString(settings.VoiceMail)).Record(method: "POST", action: GetEcho("voiceMail"), maxLength: 30);
}
public string GetEcho(string action, bool user = true)
{
var result = baseUrl.TrimEnd('/');
if (!string.IsNullOrEmpty(action))
{
result += "/" + action.TrimStart('/');
}
if (user)
{
result += "?CallerId=" + AuthContext.CurrentAccount.ID;
}
return result;
}
return result;
}
}

View File

@ -26,58 +26,57 @@
using Uri = System.Uri;
namespace ASC.VoipService.Twilio
namespace ASC.VoipService.Twilio;
public class TwilioVoipSettings : VoipSettings
{
public class TwilioVoipSettings : VoipSettings
public TwilioVoipSettings(
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext,
BaseCommonLinkUtility baseCommonLinkUtility) :
base(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
{ }
public TwilioVoipSettings(
Uri voiceUrl,
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext,
BaseCommonLinkUtility baseCommonLinkUtility) :
this(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
{
public TwilioVoipSettings(
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext,
BaseCommonLinkUtility baseCommonLinkUtility) :
base(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
{ }
if (string.IsNullOrEmpty(voiceUrl.Query)) return;
public TwilioVoipSettings(
Uri voiceUrl,
AuthContext authContext,
TenantUtil tenantUtil,
SecurityContext securityContext,
BaseCommonLinkUtility baseCommonLinkUtility) :
this(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
JsonSettings = Encoding.UTF8.GetString(Convert.FromBase64String(HttpUtility.UrlDecode(HttpUtility.ParseQueryString(voiceUrl.Query)["settings"])));
}
public TwilioVoipSettings(string settings, AuthContext authContext) : base(settings, authContext)
{
}
public override string Connect(bool user = true, string contactId = null)
{
var result = GetEcho("", user);
if (!string.IsNullOrEmpty(contactId))
{
if (string.IsNullOrEmpty(voiceUrl.Query)) return;
JsonSettings = Encoding.UTF8.GetString(Convert.FromBase64String(HttpUtility.UrlDecode(HttpUtility.ParseQueryString(voiceUrl.Query)["settings"])));
result += "&ContactId=" + contactId;
}
return result;
}
public TwilioVoipSettings(string settings, AuthContext authContext) : base(settings, authContext)
{
}
public override string Redirect(string to)
{
return GetEcho("redirect") + "&RedirectTo=" + to;
}
public override string Connect(bool user = true, string contactId = null)
{
var result = GetEcho("", user);
if (!string.IsNullOrEmpty(contactId))
{
result += "&ContactId=" + contactId;
}
return result;
}
public override string Dequeue(bool reject)
{
return GetEcho("dequeue") + "&Reject=" + reject;
}
public override string Redirect(string to)
{
return GetEcho("redirect") + "&RedirectTo=" + to;
}
public override string Dequeue(bool reject)
{
return GetEcho("dequeue") + "&Reject=" + reject;
}
private string GetEcho(string method, bool user = true)
{
return new TwilioResponseHelper(this, BaseCommonLinkUtility.GetFullAbsolutePath(""), AuthContext, TenantUtil, SecurityContext).GetEcho(method, user);
}
private string GetEcho(string method, bool user = true)
{
return new TwilioResponseHelper(this, BaseCommonLinkUtility.GetFullAbsolutePath(""), AuthContext, TenantUtil, SecurityContext).GetEcho(method, user);
}
}

View File

@ -23,54 +23,53 @@
*
*/
namespace ASC.VoipService
namespace ASC.VoipService;
public class VoipCall
{
public class VoipCall
public string Id { get; set; }
public string ParentID { get; set; }
public string From { get; set; }
public string To { get; set; }
public Guid AnsweredBy { get; set; }
public DateTime DialDate { get; set; }
public int DialDuration { get; set; }
public VoipCallStatus? Status { get; set; }
public decimal Price { get; set; }
public int ContactId { get; set; }
public bool ContactIsCompany { get; set; }
public string ContactTitle { get; set; }
public DateTime Date { get; set; }
public DateTime EndDialDate { get; set; }
public VoipRecord VoipRecord { get; set; }
public List<VoipCall> ChildCalls { get; set; }
public VoipCall()
{
public string Id { get; set; }
public string ParentID { get; set; }
public string From { get; set; }
public string To { get; set; }
public Guid AnsweredBy { get; set; }
public DateTime DialDate { get; set; }
public int DialDuration { get; set; }
public VoipCallStatus? Status { get; set; }
public decimal Price { get; set; }
public int ContactId { get; set; }
public bool ContactIsCompany { get; set; }
public string ContactTitle { get; set; }
public DateTime Date { get; set; }
public DateTime EndDialDate { get; set; }
public VoipRecord VoipRecord { get; set; }
public List<VoipCall> ChildCalls { get; set; }
public VoipCall()
{
ChildCalls = new List<VoipCall>();
VoipRecord = new VoipRecord();
}
ChildCalls = new List<VoipCall>();
VoipRecord = new VoipRecord();
}
}
public enum VoipCallStatus
{
Incoming,
Outcoming,
Answered,
Missed
}
public enum VoipCallStatus
{
Incoming,
Outcoming,
Answered,
Missed
}

View File

@ -23,142 +23,137 @@
*
*/
namespace ASC.VoipService
namespace ASC.VoipService;
public class Agent
{
public class Agent
public Guid Id { get; set; }
public AnswerType Answer { get; set; }
public string ClientID { get { return PhoneNumber + PostFix; } }
public bool Record { get; set; }
public int TimeOut { get; set; }
public AgentStatus Status { get; set; }
public bool AllowOutgoingCalls { get; set; }
public string PostFix { get; set; }
public string PhoneNumber { get; set; }
public string RedirectToNumber { get; set; }
public Agent()
{
public Guid Id { get; set; }
public AnswerType Answer { get; set; }
public string ClientID { get { return PhoneNumber + PostFix; } }
public bool Record { get; set; }
public int TimeOut { get; set; }
public AgentStatus Status { get; set; }
public bool AllowOutgoingCalls { get; set; }
public string PostFix { get; set; }
public string PhoneNumber { get; set; }
public string RedirectToNumber { get; set; }
public Agent()
{
Status = AgentStatus.Offline;
TimeOut = 30;
AllowOutgoingCalls = true;
Record = true;
}
public Agent(Guid id, AnswerType answer, VoipPhone phone, string postFix)
: this()
{
Id = id;
Answer = answer;
PhoneNumber = phone.Number;
AllowOutgoingCalls = phone.Settings.AllowOutgoingCalls;
Record = phone.Settings.Record;
PostFix = postFix;
}
Status = AgentStatus.Offline;
TimeOut = 30;
AllowOutgoingCalls = true;
Record = true;
}
public class Queue
public Agent(Guid id, AnswerType answer, VoipPhone phone, string postFix)
: this()
{
public string Id { get; set; }
public string Name { get; set; }
public int Size { get; set; }
public string WaitUrl { get; set; }
public int WaitTime { get; set; }
public Queue() { }
public Queue(string id, string name, int size, string waitUrl, int waitTime)
{
Id = id;
Name = name;
WaitUrl = waitUrl;
WaitTime = waitTime;
Size = size;
}
Id = id;
Answer = answer;
PhoneNumber = phone.Number;
AllowOutgoingCalls = phone.Settings.AllowOutgoingCalls;
Record = phone.Settings.Record;
PostFix = postFix;
}
public sealed class WorkingHours
{
public bool Enabled { get; set; }
public TimeSpan? From { get; set; }
public TimeSpan? To { get; set; }
public WorkingHours() { }
public WorkingHours(TimeSpan from, TimeSpan to)
{
From = from;
To = to;
}
private bool Equals(WorkingHours other)
{
return Enabled.Equals(other.Enabled) && From.Equals(other.From) && To.Equals(other.To);
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((WorkingHours)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(Enabled, From, To);
}
}
public class VoipUpload
{
public string Name { get; set; }
public string Path { get; set; }
public AudioType AudioType { get; set; }
public bool IsDefault { get; set; }
}
#region Enum
public enum AnswerType
{
Number,
Sip,
Client
}
public enum GreetingMessageVoice
{
Man,
Woman,
Alice
}
public enum AgentStatus
{
Online,
Paused,
Offline
}
public enum AudioType
{
Greeting,
HoldUp,
VoiceMail,
Queue
}
#endregion
}
public class Queue
{
public string Id { get; set; }
public string Name { get; set; }
public int Size { get; set; }
public string WaitUrl { get; set; }
public int WaitTime { get; set; }
public Queue() { }
public Queue(string id, string name, int size, string waitUrl, int waitTime)
{
Id = id;
Name = name;
WaitUrl = waitUrl;
WaitTime = waitTime;
Size = size;
}
}
public sealed class WorkingHours
{
public bool Enabled { get; set; }
public TimeSpan? From { get; set; }
public TimeSpan? To { get; set; }
public WorkingHours() { }
public WorkingHours(TimeSpan from, TimeSpan to)
{
From = from;
To = to;
}
private bool Equals(WorkingHours other)
{
return Enabled.Equals(other.Enabled) && From.Equals(other.From) && To.Equals(other.To);
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((WorkingHours)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(Enabled, From, To);
}
}
public class VoipUpload
{
public string Name { get; set; }
public string Path { get; set; }
public AudioType AudioType { get; set; }
public bool IsDefault { get; set; }
}
public enum AnswerType
{
Number,
Sip,
Client
}
public enum GreetingMessageVoice
{
Man,
Woman,
Alice
}
public enum AgentStatus
{
Online,
Paused,
Offline
}
public enum AudioType
{
Greeting,
HoldUp,
VoiceMail,
Queue
}

View File

@ -23,63 +23,62 @@
*
*/
namespace ASC.VoipService
namespace ASC.VoipService;
public class VoipPhone
{
public class VoipPhone
public string Id { get; set; }
public string Number { get; set; }
public string Alias { get; set; }
public VoipSettings Settings { get; set; }
public Agent Caller
{
public string Id { get; set; }
public string Number { get; set; }
public string Alias { get; set; }
public VoipSettings Settings { get; set; }
public Agent Caller
{
get { return Settings.Caller; }
}
public VoipPhone(AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
{
Settings = new VoipSettings(authContext, tenantUtil, securityContext, baseCommonLinkUtility);
}
public virtual VoipCall Call(string to, string contactId = null)
{
throw new NotImplementedException();
}
public virtual VoipCall LocalCall(string to)
{
throw new NotImplementedException();
}
public virtual VoipCall RedirectCall(string callId, string to)
{
throw new NotImplementedException();
}
public virtual VoipCall HoldUp(string callId)
{
throw new NotImplementedException();
}
public virtual void AnswerQueueCall(string callId)
{
throw new NotImplementedException();
}
public virtual void RejectQueueCall(string callId)
{
throw new NotImplementedException();
}
get { return Settings.Caller; }
}
public class VoipRecord
public VoipPhone(AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
{
public string Id { get; set; }
Settings = new VoipSettings(authContext, tenantUtil, securityContext, baseCommonLinkUtility);
}
public string Uri { get; set; }
public virtual VoipCall Call(string to, string contactId = null)
{
throw new NotImplementedException();
}
public int Duration { get; set; }
public virtual VoipCall LocalCall(string to)
{
throw new NotImplementedException();
}
public decimal Price { get; set; }
public virtual VoipCall RedirectCall(string callId, string to)
{
throw new NotImplementedException();
}
public virtual VoipCall HoldUp(string callId)
{
throw new NotImplementedException();
}
public virtual void AnswerQueueCall(string callId)
{
throw new NotImplementedException();
}
public virtual void RejectQueueCall(string callId)
{
throw new NotImplementedException();
}
}
public class VoipRecord
{
public string Id { get; set; }
public string Uri { get; set; }
public int Duration { get; set; }
public decimal Price { get; set; }
}

View File

@ -23,165 +23,164 @@
*
*/
namespace ASC.VoipService
namespace ASC.VoipService;
public class VoipSettings
{
public class VoipSettings
public string VoiceUrl { get; set; }
public string Name { get; set; }
public List<Agent> Operators { get; set; }
public Queue Queue { get; set; }
public Agent Caller { get { return Operators.FirstOrDefault(r => r.Id == AuthContext.CurrentAccount.ID); } }
public WorkingHours WorkingHours { get; set; }
public string VoiceMail { get; set; }
public string GreetingAudio { get; set; }
public string HoldAudio { get; set; }
public bool AllowOutgoingCalls { get; set; }
public bool Pause { get; set; }
public bool Record { get; set; }
internal string JsonSettings
{
public string VoiceUrl { get; set; }
public string Name { get; set; }
public List<Agent> Operators { get; set; }
public Queue Queue { get; set; }
public Agent Caller { get { return Operators.FirstOrDefault(r => r.Id == AuthContext.CurrentAccount.ID); } }
public WorkingHours WorkingHours { get; set; }
public string VoiceMail { get; set; }
public string GreetingAudio { get; set; }
public string HoldAudio { get; set; }
public bool AllowOutgoingCalls { get; set; }
public bool Pause { get; set; }
public bool Record { get; set; }
internal string JsonSettings
get
{
get
return JsonConvert.SerializeObject(
new
{
Operators,
GreetingAudio,
Name,
Queue,
WorkingHours,
VoiceMail,
HoldAudio,
AllowOutgoingCalls,
Pause,
Record
},
new JsonSerializerSettings { ContractResolver = CustomSerializeContractResolver.Instance });
}
set
{
try
{
return JsonConvert.SerializeObject(
new
{
Operators,
GreetingAudio,
Name,
Queue,
WorkingHours,
VoiceMail,
HoldAudio,
AllowOutgoingCalls,
Pause,
Record
},
new JsonSerializerSettings { ContractResolver = CustomSerializeContractResolver.Instance });
var settings = JsonConvert.DeserializeObject<VoipSettings>(value, new JsonSerializerSettings { ContractResolver = CustomSerializeContractResolver.Instance });
Operators = settings.Operators ?? new List<Agent>();
Name = settings.Name;
Queue = settings.Queue;
WorkingHours = settings.WorkingHours;
GreetingAudio = settings.GreetingAudio;
VoiceMail = settings.VoiceMail;
HoldAudio = settings.HoldAudio;
AllowOutgoingCalls = settings.AllowOutgoingCalls;
Pause = settings.Pause;
Record = settings.Record;
}
set
catch (Exception)
{
try
{
var settings = JsonConvert.DeserializeObject<VoipSettings>(value, new JsonSerializerSettings { ContractResolver = CustomSerializeContractResolver.Instance });
Operators = settings.Operators ?? new List<Agent>();
Name = settings.Name;
Queue = settings.Queue;
WorkingHours = settings.WorkingHours;
GreetingAudio = settings.GreetingAudio;
VoiceMail = settings.VoiceMail;
HoldAudio = settings.HoldAudio;
AllowOutgoingCalls = settings.AllowOutgoingCalls;
Pause = settings.Pause;
Record = settings.Record;
}
catch (Exception)
{
}
}
}
protected AuthContext AuthContext { get; }
protected TenantUtil TenantUtil { get; }
protected SecurityContext SecurityContext { get; }
protected BaseCommonLinkUtility BaseCommonLinkUtility { get; }
public VoipSettings(AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
{
Operators = new List<Agent>();
AuthContext = authContext;
TenantUtil = tenantUtil;
SecurityContext = securityContext;
BaseCommonLinkUtility = baseCommonLinkUtility;
}
public VoipSettings(string settings, AuthContext authContext)
{
JsonSettings = settings;
AuthContext = authContext;
}
public virtual string Connect(bool user = true, string contactId = null)
{
throw new NotImplementedException();
}
public virtual string Redirect(string to)
{
throw new NotImplementedException();
}
public virtual string Dequeue(bool reject)
{
throw new NotImplementedException();
}
public override string ToString()
{
return JsonSettings;
}
public VoipSettings GetSettings(string settings)
{
return new VoipSettings(AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { JsonSettings = settings };
}
}
class CustomSerializeContractResolver : CamelCasePropertyNamesContractResolver
protected AuthContext AuthContext { get; }
protected TenantUtil TenantUtil { get; }
protected SecurityContext SecurityContext { get; }
protected BaseCommonLinkUtility BaseCommonLinkUtility { get; }
public VoipSettings(AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
{
public static readonly CustomSerializeContractResolver Instance = new CustomSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.PropertyName == "voiceMail")
{
property.Converter = new VoiceMailConverter();
}
return property;
}
Operators = new List<Agent>();
AuthContext = authContext;
TenantUtil = tenantUtil;
SecurityContext = securityContext;
BaseCommonLinkUtility = baseCommonLinkUtility;
}
class VoiceMailConverter : JsonConverter
public VoipSettings(string settings, AuthContext authContext)
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
JsonSettings = settings;
AuthContext = authContext;
}
public virtual string Connect(bool user = true, string contactId = null)
{
throw new NotImplementedException();
}
public virtual string Redirect(string to)
{
throw new NotImplementedException();
}
public virtual string Dequeue(bool reject)
{
throw new NotImplementedException();
}
public override string ToString()
{
return JsonSettings;
}
public VoipSettings GetSettings(string settings)
{
return new VoipSettings(AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { JsonSettings = settings };
}
}
class CustomSerializeContractResolver : CamelCasePropertyNamesContractResolver
{
public static readonly CustomSerializeContractResolver Instance = new CustomSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.PropertyName == "voiceMail")
{
serializer.Serialize(writer, value);
property.Converter = new VoiceMailConverter();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
return property;
}
}
class VoiceMailConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.ValueType != null && reader.ValueType.Name == "String")
{
if (reader.ValueType != null && reader.ValueType.Name == "String")
{
return reader.Value;
}
var jObject = JObject.Load(reader);
var url = jObject.Value<string>("url");
return !string.IsNullOrEmpty(url) ? url : "";
return reader.Value;
}
public override bool CanConvert(Type objectType)
{
return true;
}
var jObject = JObject.Load(reader);
var url = jObject.Value<string>("url");
return !string.IsNullOrEmpty(url) ? url : "";
}
public override bool CanConvert(Type objectType)
{
return true;
}
}