Added Main Project
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
using CLMGRLib;
|
||||
|
||||
namespace TeamsNetphoneLink.Netphone
|
||||
{
|
||||
public class ClientSdkEventArgs : EventArgs
|
||||
{
|
||||
public CLMgrMessage Msg;
|
||||
public int Param;
|
||||
|
||||
public ClientSdkEventArgs(CLMgrMessage msg, int param)
|
||||
{
|
||||
Msg = msg;
|
||||
Param = param;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void LineManagerMessageHandler(ClientSdkEventArgs e);
|
||||
|
||||
public class ClientSdkEventSink
|
||||
{
|
||||
private ClientLineMgrClass ConnectedLineManager;
|
||||
private IClientLineMgrEventsPub_PubOnLineMgrNotificationEventHandler EventHandler;
|
||||
private LineManagerMessageHandler LineManagerMessageDelegateOfForm;
|
||||
|
||||
public ClientSdkEventSink()
|
||||
{
|
||||
EventHandler = new IClientLineMgrEventsPub_PubOnLineMgrNotificationEventHandler(clmgr_EventSink);
|
||||
}
|
||||
|
||||
public void Connect(ClientLineMgrClass lineManager, LineManagerMessageHandler lineManagerMessageDelegateOfForm)
|
||||
{
|
||||
ConnectedLineManager = lineManager;
|
||||
LineManagerMessageDelegateOfForm = lineManagerMessageDelegateOfForm;
|
||||
|
||||
//add eventhandler for the PubOnlineMgrNotification Events
|
||||
ConnectedLineManager.PubOnLineMgrNotification += EventHandler;
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
//remove eventhandler for the PubOnlineMgrNotification Events
|
||||
ConnectedLineManager.PubOnLineMgrNotification -= EventHandler;
|
||||
ConnectedLineManager = null;
|
||||
LineManagerMessageDelegateOfForm = null;
|
||||
}
|
||||
|
||||
private void clmgr_EventSink(int msg, int param)
|
||||
{
|
||||
//this method receives the COM events from the client line manger
|
||||
if ((LineManagerMessageDelegateOfForm != null))
|
||||
{
|
||||
LineManagerMessageDelegateOfForm(new ClientSdkEventArgs((CLMgrMessage)msg, param));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum CLMgrMessage
|
||||
{
|
||||
CLMgrLineStateChangedMessage = 0, //state of at least one line has changed
|
||||
CLMgrLineSelectionChangedMessage = 1, //line in focus has changed
|
||||
CLMgrLineDetailsChangedMessage = 2, //details of at least one line have changed
|
||||
CLMgrCallDetailsMessage = 4, //details of last call are available, post mortem for logging purpose
|
||||
CLMgrServerDownMessage = 5, //server goes down, keep line manager, wait for ServerUp message
|
||||
CLMgrServerUpMessage = 6, //server is up again, keep interfaces to line manger
|
||||
CLMgrWaveDeviceChanged = 7, //speaker / micro has been switched on / off
|
||||
CLMgrGroupCallNotificationMessage = 8, //notification about group call
|
||||
CLMgrNumberOfLinesChangedMessage = 10, //the number of lines has changed
|
||||
CLMgrClientShutDownRequest = 11, //Client Line Manager requests client to shutdown and release all interfaces
|
||||
CLMgrLineStateChangedMessageEx = 28, //state of certain line has changed, lParam: LOWORD: line index of line that changed its state (starting with 0) HIWORD: new state of this line
|
||||
CLMgrSIPRegistrationStateChanged = 30, //registration state of SIP account has changed
|
||||
//lParam: LOBYTE: Account index
|
||||
// HIBYTE: new state
|
||||
CLMgrWaveFilePlayed = 31, //wave file playback finished
|
||||
//lParam: line index;
|
||||
//if -1, the message is related to a LineMgr function PlaySoundFile or PlayToRtp
|
||||
//if >=0 the message is related to a line function PlaySoundFile of line with this index
|
||||
PubCLMgrFirstDataReceived = 32 //first RTP data received on line, might be silence
|
||||
//lParam: line index;
|
||||
}
|
||||
|
||||
public enum LineState
|
||||
{
|
||||
Inactive = 0, //line is inactive
|
||||
HookOffInternal = 1, //off hook, internal dialtone
|
||||
HookOffExternal = 2, //off hook, external dialtone
|
||||
Ringing = 3, //incoming call, ringing
|
||||
Dialing = 4, //outgoing call, we are dialing, no sound
|
||||
Alerting = 5, //outgoing call, alerting = ringing on destination
|
||||
Knocking = 6, //outgoing call, knocking = second call ringing on destination
|
||||
Busy = 7, //outgoing call, destination is busy
|
||||
Active = 8, //incoming / outgoing call, logical and physical connection is established
|
||||
OnHold = 9, //incoming / outgoing call, logical connection is established, destination gets music on hold
|
||||
ConferenceActive = 10, //incoming / outgoing conference, logical and physical connection is established
|
||||
ConferenceOnHold = 11, //incoming / outgoing conference, logical connection is established, not physcically connected
|
||||
Terminated = 12, //incoming / outgoing connection / call has been disconnected
|
||||
Transferring = 13, //special LSOnHold, call is awaiting to be transferred, peer gets special music on hold
|
||||
Disabled = 14 //special LSInactive: wrap up time
|
||||
}
|
||||
|
||||
public enum DisconnectReason
|
||||
{
|
||||
Normal = 0,
|
||||
Busy = 1,
|
||||
Rejected = 2,
|
||||
Cancelled = 3,
|
||||
Transferred = 4,
|
||||
JoinedConference = 5,
|
||||
NoAnswer = 6,
|
||||
TooLate = 7,
|
||||
DirectCallImpossible = 8,
|
||||
WrongNumber = 9,
|
||||
Unreachable = 10,
|
||||
CallDiverted = 11,
|
||||
CallRoutingFailed = 12,
|
||||
PermissionDenied = 13,
|
||||
NetworkCongestion = 14,
|
||||
NoChannelAvailable = 15,
|
||||
NumberChanged = 16,
|
||||
IncompatibleDestination = 17
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
using CLMGRLib;
|
||||
using System.Diagnostics;
|
||||
using TeamsLocalLibary.EventArgs;
|
||||
|
||||
namespace TeamsNetphoneLink.Netphone
|
||||
{
|
||||
public delegate void LineStateChangedEventHandler(LineState newLineState);
|
||||
public delegate void LoggedInStateEventHandler(bool loggedInState);
|
||||
|
||||
public class NetPhoneEvents : IDisposable
|
||||
{
|
||||
private ClientLine SelectedLine;
|
||||
private ClientLineMgrClass pCLMgr;
|
||||
private ClientSdkEventSink MyEventSink;
|
||||
private LineState LastLineState;
|
||||
private bool lastLoggedInState;
|
||||
|
||||
private CancellationTokenSource aliveCheckTokenSource;
|
||||
private CancellationToken aliveCheckToken;
|
||||
|
||||
private Dictionary<LineState, List<LineStateChangedEventHandler>> lineStateEvents = new Dictionary<LineState, List<LineStateChangedEventHandler>>();
|
||||
private List<LoggedInStateEventHandler> loggedInStateEvents = new List<LoggedInStateEventHandler>();
|
||||
|
||||
|
||||
private Timer loggedInStateTimer;
|
||||
private Timer aliveTimer;
|
||||
|
||||
public event EventHandler<TokenReceivedEventArgs>? TokenReceived;
|
||||
|
||||
public NetPhoneEvents()
|
||||
{
|
||||
loggedInStateTimer = new Timer(CheckLoggedInState, null, Timeout.Infinite, 1000);
|
||||
aliveTimer = new Timer(AliveCheck, null, Timeout.Infinite, 1000);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
loggedInStateTimer?.Change(Timeout.Infinite, 0);
|
||||
loggedInStateTimer?.Dispose();
|
||||
RemoveAllEventHandlers();
|
||||
}
|
||||
|
||||
public async Task<bool> Initialize(bool waitForNetphone = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (Process.GetProcessesByName("CLMgr").Length == 0 && !waitForNetphone)
|
||||
return false;
|
||||
|
||||
while (Process.GetProcessesByName("CLMgr").Length == 0)
|
||||
await Task.Delay(1000, cancellationToken);
|
||||
|
||||
|
||||
Console.WriteLine("new interface");
|
||||
pCLMgr = new ClientLineMgrClass();
|
||||
MyEventSink = new ClientSdkEventSink();
|
||||
MyEventSink.Connect(pCLMgr, new LineManagerMessageHandler(OnLineManagerMessage));
|
||||
|
||||
aliveTimer.Change(0, 1000);
|
||||
loggedInStateTimer.Change(0, 1500);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AliveCheck(object state)
|
||||
{
|
||||
if (Process.GetProcessesByName("CLMgr").Length == 0)
|
||||
{
|
||||
_ = Initialize(true).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckLoggedInState(object state)
|
||||
{
|
||||
bool currentLoggedInState = false;
|
||||
|
||||
try
|
||||
{
|
||||
currentLoggedInState = pCLMgr.DispIsLoggedIn != 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
currentLoggedInState = false;
|
||||
}
|
||||
|
||||
if (currentLoggedInState != lastLoggedInState)
|
||||
{
|
||||
lastLoggedInState = currentLoggedInState;
|
||||
OnLoggedInStateChanged(currentLoggedInState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnLoggedInStateChanged(bool isLoggedIn)
|
||||
{
|
||||
foreach (var handler in loggedInStateEvents)
|
||||
{
|
||||
handler?.Invoke(isLoggedIn);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLineStateEventHandler(LineState lineState, LineStateChangedEventHandler handler)
|
||||
{
|
||||
lock (lineStateEvents)
|
||||
{
|
||||
if (!lineStateEvents.ContainsKey(lineState))
|
||||
{
|
||||
lineStateEvents[lineState] = new List<LineStateChangedEventHandler>();
|
||||
}
|
||||
lineStateEvents[lineState].Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLoggedInStateEventHandler(LoggedInStateEventHandler handler)
|
||||
{
|
||||
lock (loggedInStateEvents)
|
||||
{
|
||||
loggedInStateEvents.Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveLineStateEventHandler(LineState lineState, LineStateChangedEventHandler handler)
|
||||
{
|
||||
lock (lineStateEvents)
|
||||
{
|
||||
if (lineStateEvents.ContainsKey(lineState))
|
||||
{
|
||||
lineStateEvents[lineState].Remove(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveLoggedInStateEventHandler(LoggedInStateEventHandler handler)
|
||||
{
|
||||
lock (loggedInStateEvents)
|
||||
{
|
||||
loggedInStateEvents.Remove(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAllEventHandlers()
|
||||
{
|
||||
lock (lineStateEvents)
|
||||
{
|
||||
lineStateEvents.Clear();
|
||||
}
|
||||
lock (loggedInStateEvents)
|
||||
{
|
||||
loggedInStateEvents.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLineManagerMessage(ClientSdkEventArgs e)
|
||||
{
|
||||
SelectedLine = (ClientLine)pCLMgr.DispSelectedLine;
|
||||
|
||||
if (e.Msg == CLMgrMessage.CLMgrClientShutDownRequest)
|
||||
{
|
||||
//aliveCheckTokenSource.Cancel();
|
||||
//MyEventSink.Disconnect();
|
||||
//OnConnectionStateChanged(false,true);
|
||||
}
|
||||
|
||||
if (e.Msg == CLMgrMessage.CLMgrLineStateChangedMessageEx)
|
||||
{
|
||||
int line = e.Param & 0xff;
|
||||
int high = e.Param >> 8;
|
||||
|
||||
LineState NewLineState = (LineState)high;
|
||||
|
||||
if (LastLineState != NewLineState)
|
||||
{
|
||||
LastLineState = NewLineState;
|
||||
|
||||
lock (lineStateEvents)
|
||||
{
|
||||
if (lineStateEvents.ContainsKey(NewLineState))
|
||||
{
|
||||
foreach (var handler in lineStateEvents[NewLineState])
|
||||
{
|
||||
handler?.Invoke(NewLineState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRichPresenceStatus(int away, int dnd, DateTime expires)
|
||||
{
|
||||
var clientConfig = (ClientConfig)this.pCLMgr.ClientConfig;
|
||||
clientConfig.SetRichPresenceStatus(away, dnd, expires);
|
||||
}
|
||||
|
||||
public void SetAppointmentText(string appointmentText, DateTime expires)
|
||||
{
|
||||
var clientConfig = (ClientConfig)this.pCLMgr.ClientConfig;
|
||||
clientConfig.SetAppointmentText(appointmentText, expires);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
using Azure.Core;
|
||||
using Azure.Identity;
|
||||
using Azure.Identity.Broker;
|
||||
using Microsoft.Graph;
|
||||
using Microsoft.Graph.Drives.Item.Items.Item.Workbook.Functions.Cosh;
|
||||
using Microsoft.Graph.Me.Presence.SetUserPreferredPresence;
|
||||
using Microsoft.Identity.Client;
|
||||
using Microsoft.Identity.Client.Broker;
|
||||
using Microsoft.Identity.Client.Extensions.Msal;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using TeamsLocalLibary;
|
||||
using static System.Formats.Asn1.AsnWriter;
|
||||
using Microsoft.Identity.Client.NativeInterop;
|
||||
using TeamsNetphoneLink.WPF;
|
||||
using TeamsNetphoneLink.Communication;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace TeamsNetphoneLink.Teams
|
||||
{
|
||||
public delegate void PresenceStausEventHandler(Microsoft.Graph.Models.Presence presence);
|
||||
|
||||
public class TeamsGraph : TeamsGraphEventHandlers
|
||||
{
|
||||
private GraphServiceClient graphClient;
|
||||
public bool Authenticated { get; private set; }
|
||||
|
||||
private Timer presenceStatusTimer;
|
||||
private Microsoft.Graph.Models.Presence lastPresence;
|
||||
public void CheckPresenceStatusTimer()
|
||||
{
|
||||
presenceStatusTimer = new Timer(CheckPresenceStatus, null, Timeout.Infinite, 2000);
|
||||
presenceStatusTimer.Change(0, 1000);
|
||||
}
|
||||
|
||||
public void CheckPresenceStatus(object state)
|
||||
{
|
||||
if (graphClient is not null && Authenticated)
|
||||
{
|
||||
try
|
||||
{
|
||||
var presence = graphClient.Me.Presence.GetAsync().GetAwaiter().GetResult();
|
||||
|
||||
// If lastPresence is null, initialize it with the current presence
|
||||
if (lastPresence == null)
|
||||
{
|
||||
lastPresence = presence;
|
||||
}
|
||||
|
||||
// Check if Availability has changed
|
||||
if (lastPresence.Availability != presence.Availability)
|
||||
{
|
||||
OnAvailabilityChanged(presence);
|
||||
}
|
||||
|
||||
// Check if Activity has changed
|
||||
if (lastPresence.Activity != presence.Activity)
|
||||
{
|
||||
OnActivityChanged(presence);
|
||||
}
|
||||
|
||||
// Update lastPresence to the current presence
|
||||
lastPresence = presence;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error checking presence state: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> IsCachedAccounts()
|
||||
{
|
||||
IPublicClientApplication app = PublicClientApplicationBuilder.Create(Settings.Default.AppID)
|
||||
.WithDefaultRedirectUri()
|
||||
.WithAuthority(String.Format("https://login.microsoftonline.com/{0}", Settings.Default.TenantID))
|
||||
.Build();
|
||||
|
||||
// Register MSAL cache
|
||||
|
||||
var storage = new StorageCreationPropertiesBuilder("teamsnetphonelink.msal.cache", MsalCacheHelper.UserRootDirectory).Build();
|
||||
|
||||
var cacheHelper = await MsalCacheHelper.CreateAsync(storage);
|
||||
|
||||
cacheHelper.RegisterCache(app.UserTokenCache);
|
||||
|
||||
IEnumerable<IAccount> accounts = await app.GetAccountsAsync();
|
||||
|
||||
return accounts.Any();
|
||||
}
|
||||
|
||||
// Authentifizierungsmethode mit Azure Identity
|
||||
public async Task<bool> AuthenticateAsync(bool clearCache = false, bool silent = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if caching is enabled in settings
|
||||
bool useCache = Settings.Default.SaveEntraCredentials;
|
||||
|
||||
IPublicClientApplication app = PublicClientApplicationBuilder.Create(Settings.Default.AppID)
|
||||
.WithDefaultRedirectUri()
|
||||
.WithAuthority(String.Format("https://login.microsoftonline.com/{0}", Settings.Default.TenantID))
|
||||
.Build();
|
||||
|
||||
// Register MSAL cache only if caching is enabled
|
||||
if (useCache)
|
||||
{
|
||||
var storage = new StorageCreationPropertiesBuilder("teamsnetphonelink.msal.cache", MsalCacheHelper.UserRootDirectory).Build();
|
||||
|
||||
var cacheHelper = await MsalCacheHelper.CreateAsync(storage);
|
||||
|
||||
cacheHelper.RegisterCache(app.UserTokenCache);
|
||||
}
|
||||
|
||||
IEnumerable<IAccount> accounts = await app.GetAccountsAsync();
|
||||
|
||||
//Alle Accounts aus dem Cache entfernen wenn clearCache gesetzt ist
|
||||
if (clearCache)
|
||||
{
|
||||
foreach(var account in accounts)
|
||||
await app.RemoveAsync(account);
|
||||
}
|
||||
|
||||
// Try to use the previously signed-in account from the cache
|
||||
var existingAccount = accounts.FirstOrDefault();
|
||||
|
||||
AuthenticationResult authentication;
|
||||
|
||||
if (existingAccount is not null && useCache && !clearCache)
|
||||
{
|
||||
Console.WriteLine("Attempting to acquire token silently using cached account.");
|
||||
try
|
||||
{
|
||||
authentication = await app.AcquireTokenSilent(new[] { "Presence.ReadWrite", "offline_access" }, existingAccount)
|
||||
.ExecuteAsync();
|
||||
|
||||
await InitializeGraphClient(authentication.AccessToken);
|
||||
|
||||
return Authenticated;
|
||||
}
|
||||
catch (MsalUiRequiredException)
|
||||
{
|
||||
Console.WriteLine("Silent token acquisition failed. Falling back to interactive authentication.");
|
||||
}
|
||||
}
|
||||
|
||||
// If no cached account or silent authentication fails, prompt the user for authentication
|
||||
Console.WriteLine("Prompting user for authentication.");
|
||||
authentication = await app.AcquireTokenInteractive(new[] { "Presence.ReadWrite", "offline_access" })
|
||||
.ExecuteAsync();
|
||||
|
||||
await InitializeGraphClient(authentication.AccessToken);
|
||||
|
||||
return Authenticated;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Fehler bei der Authentifizierung", ex.Message, ex.StackTrace);
|
||||
Console.WriteLine($"Error during authentication: {ex.Message}");
|
||||
return Authenticated;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to initialize GraphServiceClient
|
||||
private async Task InitializeGraphClient(string accessToken)
|
||||
{
|
||||
graphClient = new GraphServiceClient(new HttpClient(new AuthHandler(accessToken, new HttpClientHandler())));
|
||||
Authenticated = await TestAuthentication();
|
||||
}
|
||||
|
||||
// Testung der Anmeldung und Zugriffsrechte
|
||||
public async Task<bool> TestAuthentication()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Test-Anfrage, um Authentifizierung zu validieren
|
||||
await graphClient.Me.Presence.GetAsync();
|
||||
return true; // Anmeldung und Zugriffsrechte korrekt
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Authentifizierung fehlerhaft", ex.Message, ex.StackTrace);
|
||||
return false; // Anmeldung und Zugriffsrechte felerhaft
|
||||
}
|
||||
}
|
||||
|
||||
// Methode zum Setzen des Präsenzstatus
|
||||
public async Task<bool> SetPresenceAsync(PresenceState presenceState)
|
||||
{
|
||||
if (graphClient is null || !Authenticated)
|
||||
{
|
||||
Console.WriteLine("Authentifizierung nicht abgeschlossen. Präsenz kann nicht gesetzt werden.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Zuordnen des Präsenzstatus zu Verfügbarkeit und Aktivität
|
||||
var presenceMap = new Dictionary<PresenceState, (string Availability, string Activity)>
|
||||
{
|
||||
{ PresenceState.Available, ("Available", "Available") },
|
||||
{ PresenceState.Busy, ("Busy", "Busy") },
|
||||
{ PresenceState.DoNotDisturb, ("DoNotDisturb", "DoNotDisturb") },
|
||||
{ PresenceState.BeRightBack, ("BeRightBack", "BeRightBack") },
|
||||
{ PresenceState.Away, ("Away", "Away") },
|
||||
{ PresenceState.Offline, ("Offline", "OffWork") }
|
||||
};
|
||||
|
||||
if (!presenceMap.ContainsKey(presenceState))
|
||||
{
|
||||
Console.WriteLine("Ungültiger Präsenzstatus.");
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Ungültiger Präsenzstatus", presenceState.ToString());
|
||||
return false; // Ungültiger Status
|
||||
}
|
||||
|
||||
var (availability, activity) = presenceMap[presenceState];
|
||||
|
||||
var requestBody = new SetUserPreferredPresencePostRequestBody
|
||||
{
|
||||
Availability = availability,
|
||||
Activity = activity,
|
||||
ExpirationDuration = TimeSpan.FromHours(2) // Ablaufzeit der Präsenz
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await graphClient.Me.Presence.SetUserPreferredPresence.PostAsync(requestBody);
|
||||
return true; // Präsenz erfolgreich gesetzt
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if(ex.GetType() != typeof(Microsoft.Graph.Models.ODataErrors.ODataError))
|
||||
{
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Fehler beim Setzen der Präsenz", ex.Message, ex.StackTrace);
|
||||
}
|
||||
return false; // Fehler beim Setzen der Präsenz
|
||||
}
|
||||
}
|
||||
|
||||
// Enum für die verschiedenen Präsenzstatus
|
||||
public enum PresenceState
|
||||
{
|
||||
Available,
|
||||
Busy,
|
||||
DoNotDisturb,
|
||||
BeRightBack,
|
||||
Away,
|
||||
Offline
|
||||
}
|
||||
|
||||
private static Regex isGuid =
|
||||
new Regex(@"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$", RegexOptions.Compiled);
|
||||
|
||||
public static bool IsGuid(string candidate)
|
||||
{
|
||||
bool isValid = false;
|
||||
|
||||
if (candidate != null)
|
||||
{
|
||||
|
||||
if (isGuid.IsMatch(candidate))
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom HttpMessageHandler to inject the access token
|
||||
public class AuthHandler : DelegatingHandler
|
||||
{
|
||||
private readonly string _accessToken;
|
||||
|
||||
public AuthHandler(string accessToken, HttpMessageHandler innerHandler)
|
||||
: base(innerHandler)
|
||||
{
|
||||
_accessToken = accessToken;
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Add the access token to the request headers
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TeamsNetphoneLink.Communication
|
||||
{
|
||||
public class TeamsGraphEventHandlers
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
private readonly List<EventHandler<PresenceChangedEventArgs>> _activityChangedHandlers = new List<EventHandler<PresenceChangedEventArgs>>();
|
||||
private readonly List<EventHandler<PresenceChangedEventArgs>> _availabilityChangedHandlers = new List<EventHandler<PresenceChangedEventArgs>>();
|
||||
|
||||
public event EventHandler<PresenceChangedEventArgs> ActivityChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_activityChangedHandlers.Add(value);
|
||||
}
|
||||
}
|
||||
remove
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_activityChangedHandlers.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<PresenceChangedEventArgs> AvailabilityChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_availabilityChangedHandlers.Add(value);
|
||||
}
|
||||
}
|
||||
remove
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_availabilityChangedHandlers.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnActivityChanged(Microsoft.Graph.Models.Presence presence)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var args = new PresenceChangedEventArgs(presence);
|
||||
foreach (var handler in _activityChangedHandlers)
|
||||
{
|
||||
handler?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnAvailabilityChanged(Microsoft.Graph.Models.Presence presence)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var args = new PresenceChangedEventArgs(presence);
|
||||
foreach (var handler in _availabilityChangedHandlers)
|
||||
{
|
||||
handler?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAllEventHandlers()
|
||||
{
|
||||
foreach (var handler in _activityChangedHandlers)
|
||||
{
|
||||
_availabilityChangedHandlers.Remove(handler);
|
||||
}
|
||||
foreach (var handler in _availabilityChangedHandlers)
|
||||
{
|
||||
_availabilityChangedHandlers.Remove(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PresenceChangedEventArgs : EventArgs
|
||||
{
|
||||
public Microsoft.Graph.Models.Presence Presence { get; }
|
||||
|
||||
public PresenceChangedEventArgs(Microsoft.Graph.Models.Presence presence)
|
||||
{
|
||||
Presence = presence;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Net.WebSockets;
|
||||
using System.Reflection;
|
||||
using TeamsLocalLibary;
|
||||
using TeamsLocalLibary.EventArgs;
|
||||
|
||||
namespace TeamsNetphoneLink.Teams
|
||||
{
|
||||
public class TeamsLocalAPI
|
||||
{
|
||||
public event EventHandler<ConnectionStateChangedEventArgs>? ConnectionState;
|
||||
public event EventHandler<TokenReceivedEventArgs>? TokenReceived;
|
||||
|
||||
private Client teamsClient;
|
||||
private string token = Settings.Default.Token != string.Empty ? Settings.Default.Token : null;
|
||||
private Dictionary<string, List<PropertyChangedEventHandler>> propertyChangedHandlers = new Dictionary<string, List<PropertyChangedEventHandler>>();
|
||||
private CancellationTokenSource cts = new();
|
||||
|
||||
// Destructor
|
||||
~TeamsLocalAPI()
|
||||
{
|
||||
RemoveAllEventHandlers();
|
||||
}
|
||||
|
||||
public async Task<bool> Initialize()
|
||||
{
|
||||
// Initialize the TeamsClient with token and no auto-connect
|
||||
teamsClient = new Client(autoConnect: false, token: token) ?? throw new Exception("Could not create client");
|
||||
|
||||
// Event-handler for token reception
|
||||
teamsClient.TokenReceived += (_, args) =>
|
||||
{
|
||||
Settings.Default.Token = args.Token;
|
||||
Settings.Default.Save();
|
||||
|
||||
TokenReceived?.Invoke(_, args);
|
||||
};
|
||||
|
||||
WebSocketState lastConnectionState = WebSocketState.None;
|
||||
teamsClient.ConnectionState += (sender, args) =>
|
||||
{
|
||||
if (args.WebSocketState != lastConnectionState)
|
||||
{
|
||||
lastConnectionState = args.WebSocketState;
|
||||
// Raise the ConnectionState event in TeamsLocalAPI
|
||||
ConnectionState?.Invoke(sender, args);
|
||||
}
|
||||
};
|
||||
|
||||
teamsClient.PropertyChanged += HandlePropertyChanged;
|
||||
|
||||
teamsClient.ErrorReceived += (_, args) => Console.WriteLine("Event: ErrorReceived: {0}", args.ErrorMessage);
|
||||
|
||||
return await teamsClient.Connect(true, cts.Token);
|
||||
}
|
||||
|
||||
public void AddTokenRecievedHandler(EventHandler<TokenReceivedEventArgs> handler)
|
||||
{
|
||||
TokenReceived += handler;
|
||||
}
|
||||
|
||||
public void RemoveTokenRecievedHandler(EventHandler<TokenReceivedEventArgs> handler)
|
||||
{
|
||||
TokenReceived -= handler;
|
||||
}
|
||||
|
||||
public void AddConnectionStateHandler(EventHandler<ConnectionStateChangedEventArgs> handler)
|
||||
{
|
||||
ConnectionState += handler;
|
||||
}
|
||||
|
||||
public void RemoveConnectionStateHandler(EventHandler<ConnectionStateChangedEventArgs> handler)
|
||||
{
|
||||
ConnectionState -= handler;
|
||||
}
|
||||
|
||||
public async void SendDummyCommand()
|
||||
{
|
||||
var dummy = teamsClient.IsMuted = true;
|
||||
}
|
||||
|
||||
public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName is not null && propertyChangedHandlers.ContainsKey(e.PropertyName))
|
||||
{
|
||||
foreach (var handler in propertyChangedHandlers[e.PropertyName])
|
||||
{
|
||||
handler?.Invoke(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventHandler(string propertyName, PropertyChangedEventHandler handler)
|
||||
{
|
||||
if (!propertyChangedHandlers.ContainsKey(propertyName))
|
||||
{
|
||||
propertyChangedHandlers[propertyName] = new List<PropertyChangedEventHandler>();
|
||||
}
|
||||
|
||||
propertyChangedHandlers[propertyName].Add(handler);
|
||||
}
|
||||
|
||||
public void RemoveEventHandler(string propertyName, PropertyChangedEventHandler handler)
|
||||
{
|
||||
if (propertyChangedHandlers.ContainsKey(propertyName))
|
||||
{
|
||||
propertyChangedHandlers[propertyName].Remove(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAllEventHandlers()
|
||||
{
|
||||
propertyChangedHandlers.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user