Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a6c3df5d17 | |||
| 452fe47969 | |||
| 8f71e4dbe4 | |||
| 5949de6611 |
22
CHANGELOG.md
22
CHANGELOG.md
@@ -7,7 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
_nothing changed yet_
|
||||
_No changes_
|
||||
|
||||
|
||||
## [v0.1.2] - 2025-11-08
|
||||
|
||||
### Changed
|
||||
|
||||
- Public elements are now `virtual` to allow mocking in unit tests
|
||||
|
||||
|
||||
## [v0.1.1] - 2025-10-24
|
||||
|
||||
### Changed
|
||||
|
||||
- Property names of the event should be more descriptive:
|
||||
- From `CallerNumber` to `ExternalNumber`
|
||||
- From `CalleeNumber` to `InternalNumber`
|
||||
|
||||
|
||||
## [v0.1.0] - 2025-08-28
|
||||
@@ -27,6 +43,8 @@ _Inital release_
|
||||
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/AM-WD/FritzCallMonitor/compare/v0.1.0...HEAD
|
||||
[Unreleased]: https://github.com/AM-WD/FritzCallMonitor/compare/v0.1.2...HEAD
|
||||
|
||||
[v0.1.2]: https://github.com/AM-WD/FritzCallMonitor/compare/v0.1.1...v0.1.2
|
||||
[v0.1.1]: https://github.com/AM-WD/FritzCallMonitor/compare/v0.1.0...v0.1.1
|
||||
[v0.1.0]: https://github.com/AM-WD/FritzCallMonitor/commits/v0.1.0
|
||||
|
||||
@@ -44,11 +44,11 @@ namespace FritzCallMonitor.Demo
|
||||
switch (e.Event)
|
||||
{
|
||||
case EventType.Ring:
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss K} | #{e.ConnectionId} | Incoming Call from {e.CallerNumber} to {e.CalleeNumber}");
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss K} | #{e.ConnectionId} | Incoming Call from {e.ExternalNumber} to {e.InternalNumber}");
|
||||
break;
|
||||
|
||||
case EventType.Connect:
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss K} | #{e.ConnectionId} | Call connected to {e.CallerNumber}");
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss K} | #{e.ConnectionId} | Call connected to {e.ExternalNumber}");
|
||||
break;
|
||||
|
||||
case EventType.Disconnect:
|
||||
@@ -56,7 +56,7 @@ namespace FritzCallMonitor.Demo
|
||||
break;
|
||||
|
||||
case EventType.Call:
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss K} | #{e.ConnectionId} | Outgoing Call from {e.CalleeNumber} to {e.CallerNumber}");
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss K} | #{e.ConnectionId} | Outgoing Call from {e.InternalNumber} to {e.ExternalNumber}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -51,12 +51,12 @@ namespace AMWD.Net.Api.Fritz.CallMonitor
|
||||
/// <remarks>
|
||||
/// The event provides details using the <see cref="CallMonitorEventArgs"/> parameter.
|
||||
/// </remarks>
|
||||
public event EventHandler<CallMonitorEventArgs>? OnEvent;
|
||||
public virtual event EventHandler<CallMonitorEventArgs>? OnEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a logger instance.
|
||||
/// </summary>
|
||||
public ILogger? Logger
|
||||
public virtual ILogger? Logger
|
||||
{
|
||||
get => _logger;
|
||||
set
|
||||
@@ -69,7 +69,7 @@ namespace AMWD.Net.Api.Fritz.CallMonitor
|
||||
/// <summary>
|
||||
/// Releases all resources used by the current instance of the <see cref="CallMonitorClient"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
@@ -31,12 +31,12 @@ namespace AMWD.Net.Api.Fritz.CallMonitor
|
||||
/// <summary>
|
||||
/// Gets the external number displayed in the FRITZ!Box.
|
||||
/// </summary>
|
||||
public string? CallerNumber { get; private set; }
|
||||
public string? ExternalNumber { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal number registered in the FRITZ!Box.
|
||||
/// </summary>
|
||||
public string? CalleeNumber { get; private set; }
|
||||
public string? InternalNumber { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration of the call (only on <see cref="EventType.Disconnect"/> event).
|
||||
@@ -71,13 +71,13 @@ namespace AMWD.Net.Api.Fritz.CallMonitor
|
||||
switch (eventType)
|
||||
{
|
||||
case EventType.Ring:
|
||||
args.CallerNumber = columns[3];
|
||||
args.CalleeNumber = columns[4];
|
||||
args.ExternalNumber = columns[3];
|
||||
args.InternalNumber = columns[4];
|
||||
break;
|
||||
|
||||
case EventType.Connect:
|
||||
args.LinePort = int.TryParse(columns[3], out int connectLinePort) ? connectLinePort : null;
|
||||
args.CallerNumber = columns[4];
|
||||
args.ExternalNumber = columns[4];
|
||||
break;
|
||||
|
||||
case EventType.Disconnect:
|
||||
@@ -87,8 +87,8 @@ namespace AMWD.Net.Api.Fritz.CallMonitor
|
||||
|
||||
case EventType.Call:
|
||||
args.LinePort = int.TryParse(columns[3], out int callLinePort) ? callLinePort : null;
|
||||
args.CalleeNumber = columns[4];
|
||||
args.CallerNumber = columns[5];
|
||||
args.InternalNumber = columns[4];
|
||||
args.ExternalNumber = columns[5];
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -19,11 +19,11 @@ using (var client = new CallMonitorClient(host, port))
|
||||
switch (e.Event)
|
||||
{
|
||||
case EventType.Ring:
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss} | #{e.ConnectionId} | Incoming Call from {e.CallerNumber} to {e.CalleeNumber}");
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss} | #{e.ConnectionId} | Incoming Call from {e.ExternalNumber} to {e.InternalNumber}");
|
||||
break;
|
||||
|
||||
case EventType.Connect:
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss} | #{e.ConnectionId} | Call connected to {e.CallerNumber}");
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss} | #{e.ConnectionId} | Call connected to {e.ExternalNumber}");
|
||||
break;
|
||||
|
||||
case EventType.Disconnect:
|
||||
@@ -31,7 +31,7 @@ using (var client = new CallMonitorClient(host, port))
|
||||
break;
|
||||
|
||||
case EventType.Call:
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss} | #{e.ConnectionId} | Outgoing Call from {e.CalleeNumber} to {e.CallerNumber}");
|
||||
Console.WriteLine($"{e.Timestamp:yyyy-MM-dd HH:mm:ss} | #{e.ConnectionId} | Outgoing Call from {e.InternalNumber} to {e.ExternalNumber}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace FritzCallMonitor.Tests
|
||||
|
||||
private const string HOST = "localhost";
|
||||
private const int PORT = 1012;
|
||||
private readonly DateTime NOW = new(2025, 8, 25, 20, 15, 30, DateTimeKind.Local);
|
||||
|
||||
private string _dateOffset;
|
||||
|
||||
@@ -34,7 +35,7 @@ namespace FritzCallMonitor.Tests
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var offset = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now);
|
||||
var offset = TimeZoneInfo.Local.GetUtcOffset(NOW);
|
||||
_dateOffset = offset < TimeSpan.Zero
|
||||
? "-" + offset.ToString("hh\\:mm")
|
||||
: "+" + offset.ToString("hh\\:mm");
|
||||
@@ -43,7 +44,7 @@ namespace FritzCallMonitor.Tests
|
||||
|
||||
_readAsyncResponses = new Queue<(int, byte[])>();
|
||||
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes("25.08.25 20:15:30;RING;2;012345678901;9876543;SIP0;\r\n")));
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes($"{NOW:dd.MM.yy HH:mm:ss};RING;2;012345678901;9876543;SIP0;\r\n")));
|
||||
_readAsyncResponses.Enqueue((Timeout.Infinite, Array.Empty<byte>()));
|
||||
}
|
||||
|
||||
@@ -166,8 +167,8 @@ namespace FritzCallMonitor.Tests
|
||||
Assert.AreEqual(EventType.Ring, eventArgs.Event);
|
||||
Assert.AreEqual(2, eventArgs.ConnectionId);
|
||||
Assert.IsNull(eventArgs.LinePort);
|
||||
Assert.AreEqual("012345678901", eventArgs.CallerNumber);
|
||||
Assert.AreEqual("9876543", eventArgs.CalleeNumber);
|
||||
Assert.AreEqual("012345678901", eventArgs.ExternalNumber);
|
||||
Assert.AreEqual("9876543", eventArgs.InternalNumber);
|
||||
Assert.IsNull(eventArgs.Duration);
|
||||
|
||||
_tcpClientMock.VerifyGet(m => m.IsConnected, Times.Exactly(2));
|
||||
@@ -184,7 +185,7 @@ namespace FritzCallMonitor.Tests
|
||||
{
|
||||
// Arrange
|
||||
_readAsyncResponses.Clear();
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes("25.08.25 20:15:30;RING;")));
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes($"{NOW:dd.MM.yy HH:mm:ss};RING;")));
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes("2;012345678901;9876543;SIP0;\n")));
|
||||
_readAsyncResponses.Enqueue((Timeout.Infinite, Array.Empty<byte>()));
|
||||
|
||||
@@ -209,8 +210,8 @@ namespace FritzCallMonitor.Tests
|
||||
Assert.AreEqual(EventType.Ring, eventArgs.Event);
|
||||
Assert.AreEqual(2, eventArgs.ConnectionId);
|
||||
Assert.IsNull(eventArgs.LinePort);
|
||||
Assert.AreEqual("012345678901", eventArgs.CallerNumber);
|
||||
Assert.AreEqual("9876543", eventArgs.CalleeNumber);
|
||||
Assert.AreEqual("012345678901", eventArgs.ExternalNumber);
|
||||
Assert.AreEqual("9876543", eventArgs.InternalNumber);
|
||||
Assert.IsNull(eventArgs.Duration);
|
||||
|
||||
_tcpClientMock.VerifyGet(m => m.IsConnected, Times.Exactly(3));
|
||||
@@ -227,7 +228,7 @@ namespace FritzCallMonitor.Tests
|
||||
{
|
||||
// Arrange
|
||||
_readAsyncResponses.Clear();
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes("25.08.25 20:15:30;RING;2;012345678901;9876543;SIP0;\n25.08.25 20:15:30")));
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes($"{NOW:dd.MM.yy HH:mm:ss};RING;2;012345678901;9876543;SIP0;\n{NOW:dd.MM.yy HH:mm:ss}")));
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes(";RING;2;012345678901;9876543;SIP0;\r\n")));
|
||||
_readAsyncResponses.Enqueue((Timeout.Infinite, Array.Empty<byte>()));
|
||||
|
||||
@@ -259,7 +260,7 @@ namespace FritzCallMonitor.Tests
|
||||
{
|
||||
// Arrange
|
||||
_readAsyncResponses.Clear();
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes("25.08.25 20:15:30;TEST;2;012345678901;9876543;SIP0;\n25.08.25 20:15:30")));
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes($"{NOW:dd.MM.yy HH:mm:ss};TEST;2;012345678901;9876543;SIP0;\n{NOW:dd.MM.yy HH:mm:ss}")));
|
||||
_readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes(";RING;2;012345678901;9876543;SIP0;\r\n")));
|
||||
_readAsyncResponses.Enqueue((Timeout.Infinite, Array.Empty<byte>()));
|
||||
|
||||
|
||||
@@ -8,10 +8,12 @@ namespace FritzCallMonitor.Tests
|
||||
{
|
||||
private string _dateOffset;
|
||||
|
||||
private readonly DateTime NOW = new(2025, 8, 25, 20, 15, 30, DateTimeKind.Local);
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var offset = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now);
|
||||
var offset = TimeZoneInfo.Local.GetUtcOffset(NOW);
|
||||
_dateOffset = offset < TimeSpan.Zero
|
||||
? "-" + offset.ToString("hh\\:mm")
|
||||
: "+" + offset.ToString("hh\\:mm");
|
||||
@@ -21,7 +23,7 @@ namespace FritzCallMonitor.Tests
|
||||
public void ShouldParseRingEvent()
|
||||
{
|
||||
// Arrange
|
||||
string line = "25.08.25 20:15:30;RING;2;012345678901;9876543;SIP0;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};RING;2;012345678901;9876543;SIP0;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
@@ -29,15 +31,15 @@ namespace FritzCallMonitor.Tests
|
||||
Assert.AreEqual(EventType.Ring, result.Event);
|
||||
Assert.AreEqual(2, result.ConnectionId);
|
||||
Assert.IsNull(result.LinePort);
|
||||
Assert.AreEqual("012345678901", result.CallerNumber);
|
||||
Assert.AreEqual("9876543", result.CalleeNumber);
|
||||
Assert.AreEqual("012345678901", result.ExternalNumber);
|
||||
Assert.AreEqual("9876543", result.InternalNumber);
|
||||
Assert.IsNull(result.Duration);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldParseConnectEvent()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;CONNECT;1;3;012345678901;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};CONNECT;1;3;012345678901;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
@@ -45,15 +47,15 @@ namespace FritzCallMonitor.Tests
|
||||
Assert.AreEqual(EventType.Connect, result.Event);
|
||||
Assert.AreEqual(1, result.ConnectionId);
|
||||
Assert.AreEqual(3, result.LinePort);
|
||||
Assert.AreEqual("012345678901", result.CallerNumber);
|
||||
Assert.IsNull(result.CalleeNumber);
|
||||
Assert.AreEqual("012345678901", result.ExternalNumber);
|
||||
Assert.IsNull(result.InternalNumber);
|
||||
Assert.IsNull(result.Duration);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldParseDisconnectEvent()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;DISCONNECT;2;42;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};DISCONNECT;2;42;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
@@ -61,15 +63,15 @@ namespace FritzCallMonitor.Tests
|
||||
Assert.AreEqual(EventType.Disconnect, result.Event);
|
||||
Assert.AreEqual(2, result.ConnectionId);
|
||||
Assert.IsNull(result.LinePort);
|
||||
Assert.IsNull(result.CallerNumber);
|
||||
Assert.IsNull(result.CalleeNumber);
|
||||
Assert.IsNull(result.ExternalNumber);
|
||||
Assert.IsNull(result.InternalNumber);
|
||||
Assert.AreEqual(TimeSpan.FromSeconds(42), result.Duration);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldParseCallEvent()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;CALL;4;7;9876543;012345678901;SIP0;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};CALL;4;7;9876543;012345678901;SIP0;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
@@ -77,8 +79,8 @@ namespace FritzCallMonitor.Tests
|
||||
Assert.AreEqual(EventType.Call, result.Event);
|
||||
Assert.AreEqual(4, result.ConnectionId);
|
||||
Assert.AreEqual(7, result.LinePort);
|
||||
Assert.AreEqual("012345678901", result.CallerNumber);
|
||||
Assert.AreEqual("9876543", result.CalleeNumber);
|
||||
Assert.AreEqual("012345678901", result.ExternalNumber);
|
||||
Assert.AreEqual("9876543", result.InternalNumber);
|
||||
Assert.IsNull(result.Duration);
|
||||
}
|
||||
|
||||
@@ -93,7 +95,7 @@ namespace FritzCallMonitor.Tests
|
||||
[TestMethod]
|
||||
public void ShouldReturnNullOnUnknownEventType()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;UNKNOWN;2;012345678901;9876543;SIP0;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};UNKNOWN;2;012345678901;9876543;SIP0;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
@@ -101,7 +103,7 @@ namespace FritzCallMonitor.Tests
|
||||
[TestMethod]
|
||||
public void ShouldReturnNullOnInvalidConnectionId()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;RING;abc;012345678901;9876543;SIP0;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};RING;abc;012345678901;9876543;SIP0;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
@@ -109,7 +111,7 @@ namespace FritzCallMonitor.Tests
|
||||
[TestMethod]
|
||||
public void ShouldHandleInvalidLinePortInConnect()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;CONNECT;1;abc;012345678901;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};CONNECT;1;abc;012345678901;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
Assert.IsNotNull(result);
|
||||
Assert.IsNull(result.LinePort);
|
||||
@@ -118,7 +120,7 @@ namespace FritzCallMonitor.Tests
|
||||
[TestMethod]
|
||||
public void ShouldHandleInvalidLinePortInCall()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;CALL;4;abc;9876543;012345678901;SIP0;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};CALL;4;abc;9876543;012345678901;SIP0;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
Assert.IsNotNull(result);
|
||||
Assert.IsNull(result.LinePort);
|
||||
@@ -127,7 +129,7 @@ namespace FritzCallMonitor.Tests
|
||||
[TestMethod]
|
||||
public void ShouldHandleInvalidDurationInDisconnect()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;DISCONNECT;2;abc;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};DISCONNECT;2;abc;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
Assert.IsNotNull(result);
|
||||
Assert.IsNull(result.Duration);
|
||||
@@ -136,7 +138,7 @@ namespace FritzCallMonitor.Tests
|
||||
[TestMethod]
|
||||
public void ShouldReturnNullOnTooFewColumns()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;RING;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};RING;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
@@ -144,11 +146,11 @@ namespace FritzCallMonitor.Tests
|
||||
[TestMethod]
|
||||
public void ShouldParseWithExtraColumns()
|
||||
{
|
||||
string line = "25.08.25 20:15:30;RING;2;012345678901;9876543;SIP0;EXTRA;COLUMN;";
|
||||
string line = $"{NOW:dd.MM.yy HH:mm:ss};RING;2;012345678901;9876543;SIP0;EXTRA;COLUMN;";
|
||||
var result = CallMonitorEventArgs.Parse(line);
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("012345678901", result.CallerNumber);
|
||||
Assert.AreEqual("9876543", result.CalleeNumber);
|
||||
Assert.AreEqual("012345678901", result.ExternalNumber);
|
||||
Assert.AreEqual("9876543", result.InternalNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user