How to Authorize user on winforms when connecting to telegram - wtelegramclient

If i run this code on console application:
static async Task Main(string[] _)
{
using var client = new WTelegram.Client();
var user = await client.LoginUserIfNeeded();
Console.WriteLine($"We are logged-in as {user.username ?? user.first_name + " " + user.last_name} (id {user.id})");
}
It will prompt interactively for App api_id and api_hash.
How can i Authorize user on winforms application?. So that i can input the api_id and api_hash through textbox

Edit: (Oct 2022) Latest version of the library has a simplified config system that makes it more easy to use in WinForms apps.
Please take a look at the example WinForms app provided in the repository that demonstrate how to proceed.
The original answer below is still valid but maybe more complex
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
First, you should read WTelegramClient FAQ #3:
3. How to use the library in a WinForms or WPF application
The library should work without a problem in a GUI application.
The difficulty might be in your Config callback when the user must enter the verification code or password, as you can't use Console.ReadLine here.
An easy solution is to call Interaction.InputBox("Enter verification code") instead.
This might require adding a reference (and using) to the Microsoft.VisualBasic assembly.
A more complex solution requires the use of a ManualResetEventSlim that you will wait for in Config callback,
and when the user has provided the verification_code through your GUI, you "set" the event to release your Config callback so it can return the code.
Here is an example solution for your Form class with a ManualResetEventSlim and textboxes:
using Microsoft.VisualBasic;
using TL;
private readonly ManualResetEventSlim _codeReady = new ManualResetEventSlim();
private WTelegram.Client _client;
private User _user;
string Config(string what)
{
switch (what)
{
case "api_id": return textBoxApiID.Text;
case "api_hash": return textBoxApiHash.Text;
case "phone_number": return textBoxPhone.Text;
case "verification_code":
_codeReady.Reset();
_codeReady.Wait();
return textBoxCode.Text;
case "password": return Interaction.InputBox("Enter 2FA password");
default: return null;
};
}
private void textBoxCode_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == '\r') // pressing Return in the textboxCode
{
_codeReady.Set();
e.Handled = true;
}
}
private async void buttonLogin_Click(object sender, EventArgs e)
{
buttonLogin.Enabled = false;
_client = new WTelegram.Client(Config);
_user = await _client.LoginUserIfNeeded();
MessageBox.Show("We are now connected as " + _user);
}
private async void buttonGetChats_Click(object sender, EventArgs e)
{
if (_user == null) { MessageBox.Show("You must complete the login first."); return; }
var chats = await _client.Messages_GetAllChats(null);
MessageBox.Show(string.Join("\n", chats.chats.Values.Where(c => c.IsActive)));
}

Related

Multiple calls to MessageDialog cause crash under Windows Phone 8.1

I develop an Universal App that uses MVVM-Light. I call WebServices from the ViewModels, and I throw the exceptions encountered by the calls at the WebServices to the ViewModels: TimeOut, Wrong URL, Server Exception, ...
I have created a class "ExceptionsMsgHelper.cs" which centralizes the messages displayed for each of these exceptions through MessageDialog.
My HomePage is based on a Pivot that containing several datas: some WebServices are called asynchronously. I so meet a crash if I show an Exception in a MessageDialog through the class "ExceptionsMsgHelper.cs", whereas a previous Exception is also showed in another MessageDialog.
Here is a part of my original class:
public class ExceptionsMsgHelper
{
public async static void MsgboxWebserviceErrors(WebServiceErrorsException wseE, string errors)
{
Windows.UI.Popups.MessageDialog msgbox =
new Windows.UI.Popups.MessageDialog("The Websercice '" + wseE.WebService + "' has returned errors : \n" + errors,
"Unexpected data");
await msgbox.ShowAsync();
}
}
=> If I call twice the "msgbox.ShowAsync()", I get the "System.UnauthorizedAccessException" Exception: with message "Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))"
I've so looked for solutions in order to fix it:
use a "Dispatcter", like it is recommended here (WinRT - MessageDialog.ShowAsync will throw UnauthorizedAccessException in my custom class)
The code is:
public class ExceptionsMsgHelper
{
public async static void MsgboxWebserviceErrors(WebServiceErrorsException wseE, string errors)
{
Windows.UI.Popups.MessageDialog msgbox =
new Windows.UI.Popups.MessageDialog("The Websercice '" + wseE.WebService + "' has returned errors : \n" + errors,
"Unexpected data");
CoreDispatcher dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await msgbox.ShowAsync();
});
}
}
=> But I always meet the same exception.
use a "IAsyncOperation" command to close the previous MessageDialog, like recommended here (MessageDialog ShowAsync throws accessdenied exception on second dialog)
With this code:
public class ExceptionsMsgHelper
{
private static IAsyncOperation<IUICommand> messageDialogCommand = null;
public async static Task<bool> ShowDialog(MessageDialog dlg)
{
// Close the previous one out
if (messageDialogCommand != null)
{
messageDialogCommand.Cancel();
messageDialogCommand = null;
}
messageDialogCommand = dlg.ShowAsync();
await messageDialogCommand;
return true;
}
public async static void MsgboxWebserviceErrors(WebServiceErrorsException wseE, string errors)
{
Windows.UI.Popups.MessageDialog msgbox =
new Windows.UI.Popups.MessageDialog("The Websercice '" + wseE.WebService + "' has returned errors : \n" + errors,
"Unexpected data");
CoreDispatcher dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await ShowDialog(msgbox);
});
}
}
=> But in this case too, I always get the same exception.
use an extension to queue up messagedialogs, like describing here (Multiple MessageDialog app crash)
The code is now:
public class ExceptionsMsgHelper
{
public async static void MsgboxWebserviceErrors(WebServiceErrorsException wseE, string errors)
{
Windows.UI.Popups.MessageDialog msgbox =
new Windows.UI.Popups.MessageDialog("The Websercice '" + wseE.WebService + "' has returned errors : \n" + errors,
"Unexpected data");
await MessageDialogExtensions.ShowAsyncQueue(msgbox);
}
}
public static class MessageDialogExtensions
{
private static TaskCompletionSource<MessageDialog> _currentDialogShowRequest;
public static async Task<IUICommand> ShowAsyncQueue(this MessageDialog dialog)
{
if (!Window.Current.Dispatcher.HasThreadAccess)
{
throw new InvalidOperationException("This method can only be invoked from UI thread.");
}
while (_currentDialogShowRequest != null)
{
await _currentDialogShowRequest.Task;
}
var request = _currentDialogShowRequest = new TaskCompletionSource<MessageDialog>();
var result = await dialog.ShowAsync();
_currentDialogShowRequest = null;
request.SetResult(dialog);
return result;
}
private static IAsyncOperation<IUICommand> messageDialogCommand = null;
public async static Task<bool> ShowDialog(this MessageDialog dlg)
{
// Close the previous one out
if (messageDialogCommand != null)
{
messageDialogCommand.Cancel();
messageDialogCommand = null;
}
messageDialogCommand = dlg.ShowAsync();
await messageDialogCommand;
return true;
}
#endregion
}
=> And this works for me.
But like says it's author, it's probably not the best solution:
Close existing dialog when you need to open a new one. This is the simplest option and possibly the best, although you risk cancelling a dialog that might be somehow important depending on what your dialogs are about.
Queue up dialogs so the old ones don't get dismissed, but the new ones show up after the old ones were dismissed. This one will make sure all dialogs are closed by the user, but that could be a problem if your app can somehow start showing hundreds of dialogs.
Only open a new one if there isn't one already displayed. Now this risks that a newer message is not shown, which sounds more problematic than the first option.
=> I would like to understand why I can't apply one the 2 first solutions that seems to be more adapted
Ofcourse you can't show 2 or more message dialog at the same time (windows phone limits). Moreover MesssageDialog on Windows Phone 8.1 has probably bug and can't be closed.
If closing previous dialog will be solution for you, try to use ContentDialog instead MessageDialog. Check my answer in this topic: Closing MessageDialog programatically in WP 8.1 RT
I think it solve your problem.

Update text block from downloaded text file = RPC_E_WRONG_THREAD WP8.1

I am learning Windows Phone 8.1 development, I have probably done something utterly incorrectly programming wise
The need: I want to download a text file from the web using HttpClient() and display it in the TextBlock1
From variety of tutorials I have found the following:
public async void DownloadDataAsync()
{
string data = "some link to Textfile.txt";
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(data);
HttpContent content = response.Content;
string result = await content.ReadAsStringAsync();
UpdateTextBlock1(result);
}
Then the other functions.
public void UpdateTextBlock1(string result)
{
TextBlock1.Text = result;
}
private void BtnDownloadData_Click(object sender, RoutedEventArgs e)
{
Task t = new Task(DownloadDataAsync);
t.Start();
}
The code starts well enough - on button pressed, I receive RPC_E_WRONG_THREAD.
Is it that I'm trying to call the method when all threads haven't finished? How can I code that efficently so the TextBlock1 is updated with txt data?
Thanks for understanding, baby steps here in programming, and I couldn't find a relevant answer over google. (Maybe I don't yet know how to ask?)
You need to update the textblock on the UI thread like so:
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
TextBlock1.Text = result;
});
There are many posts on this subject.

Adding "rate my app" to Web App Template

There's a project called Web App Template (aka WAT - http://wat.codeplex.com/) that allows you to wrap a webapp as a Windows 8 / Windows Phone 8 application. I've done that to an app, now I'm trying to add the "rate my app" feature to it. I don't see where/if I can inject code for this component to be added.
I'm following a guide here: http://developer.nokia.com/community/wiki/Implement_%22Rate_My_App%22_in_under_60_seconds
I'm stuck at Step 5 - where do I add the Event Handler? There is no MainPage.xaml.cs and I don't see any similar files.
I imagine that WAT is calling another library to load a web browser. Is there some way I can inject an Event Handler and method into this library?
I suggest not to prompt the user with 'rate my app' thing in the first opening of the app as user should be given some time to see what the app looks like and how it functions. Therefore, keeping the number of app launches and asking to rate the app after some 5th - 10th launch of app will make more sense. Besides you should check if you already prompted the user to rate your app, if so never prompt again. (Otherwise you will piss them off with 'rate my app' thing)
In order to achieve this, you should at first keep the app launch count in app settings class.
The interface for storing any kind of setting:
public interface ISettingService
{
void Save();
void Save(string key, object value);
bool AddOrUpdateValue(string Key, object value);
bool IsExist(string key);
T Load<T>(string key);
T GetValueOrDefault<T>(string Key, T defaultValue);
}
The rating service class that consumes the above interface to store such count and settings:
public class RatingService
{
private const string IsAppRatedKeyName = "isApprated";
private const string TabViewCountKeyName = "tabViewCount";
private const bool IsAppratedDefault = false;
private const int TabViewCountDefault = 0;
private const int ShowRatingInEveryN = 7;
private readonly ISettingService _settingService;
[Dependency]
public RatingService(ISettingService settingService)
{
_settingService = settingService;
}
public void RateApp()
{
if (_settingService.AddOrUpdateValue(IsAppRatedKeyName, true))
_settingService.Save();
}
public bool IsNeedShowMessage()
{
return (_settingService.GetValueOrDefault(TabViewCountKeyName, TabViewCountDefault)%ShowRatingInEveryN) == 0;
}
public void IncreaseTabViewCount()
{
int tabCount = _settingService.GetValueOrDefault(TabViewCountKeyName, TabViewCountDefault);
if (_settingService.AddOrUpdateValue(TabViewCountKeyName, (tabCount + 1)))
_settingService.Save();
}
public bool IsAppRated()
{
return _settingService.GetValueOrDefault(IsAppRatedKeyName, IsAppratedDefault);
}
}
This is how you will run such functionality and prompt the user to rate the app (if previously not rated) anywhere in your project (mainpage or some other page where user launches some functionality):
private void RunRating()
{
if (!RatingService.IsAppRated() && RatingService.IsNeedShowMessage())
{
MessageBoxResult result = MessageBox.Show("Review the app?", "Would you like to review this awesome app?",
MessageBoxButton.OKCancel);
//show message.
if (result == MessageBoxResult.OK)
{
RatingService.RateApp();
new MarketplaceReviewTask().Show();
}
}
}

mvvmcross and authentication

Is there a way to authenticate a user through Facebook in mvvmcross framework? I'm currently attempting use Mobile Azure Service to authenticate to Facebook, but I don't have any success. Without using mvvmcross, I can authenticate just fine.
Thank You!
Mark
In the MVVM sense what I've found is that no, you can not. Properties on the facebook login page are not bindable, nor should they be and is best treated as a modal view out of your control
What I would do is make it a view concern and use Xamarin.Auth to authenticate.
As an example let's say that you had a LoginView and LoginViewModel.
The LoginView provides your standard login Email/Password but with an option (button) to authenticate via facebook
From that view hook up to the touchupinside event of the facebooklogin button
this.btnFacebookLogin.TouchUpInside += (object sender, EventArgs e) =>
{
DoFacebookLogin ();
}
Then in your DoFacebookLogin method 'present' the viewcontroller for facebook as described here https://github.com/xamarin/Xamarin.Auth/blob/master/GettingStarted.md
For example :
private void DoFacebookLogin ()
{
var auth = new OAuth2Authenticator (
clientId: "yournumericclientidhere",
scope: "",
authorizeUrl: new Uri ("https://m.facebook.com/dialog/oauth/"),
redirectUrl: new Uri ("http://www.facebook.com/connect/login_success.html"));
auth.AllowCancel = true;
auth.Completed += (sender, eventArgs) => {
DismissViewController (false, null);
if (eventArgs.IsAuthenticated) {
string user = eventArgs.Account.Serialize ();
var messenger = Mvx.Resolve<IMvxMessenger> ();
messenger.Publish (new FacebookLoggedIn (user));
} else {
// Cancelled here
}
};
var vc = auth.GetUI ();
this.PresentViewController (vc, true, null);
}
Cancelled does not need to be handled since the modal viewcontroller will take you back to your LoginView
On success that viewcontroller is dismissed then I would use mvx's interpretation of an eventaggregator (plugins.messenger) to send a message to the viewmodel that the facebook modal view is closed, with that message you can pass the account details - accesstoken etc back to the viewmodel to do as you wish.
View (as above) :
string user = eventArgs.Account.Serialize();
var messenger = Mvx.Resolve<IMvxMessenger> ();
messenger.Publish (new FacebookLoggedIn (user));
Message Class in your PCL :
public class FacebookLoggedIn : MvxMessage
{
public FacebookLoggedIn(object sender) : base(sender) {}
}
ViewModel also in your PCL :
public LoginViewModel()
{
var messenger = Mvx.Resolve<IMvxMessenger> ();
user = messenger.SubscribeOnMainThread<FacebookLoggedIn> (OnFacebookLoggedIn);
}
private void OnFacebookLoggedIn (FacebookLoggedIn MvxMessage)
{
... do something with the accesstoken? call your IUserService etc
ShowViewModel<MainViewModel> ();
}
Since you're dismissing the facebook viewcontroller you'll find yourself back on the loginview momentarily before automatically navigating to the MainView
In your view project you need to ensure the plugin is loaded otherwise you'll receive an error during construction of the viewmodel, so in setup.cs
protected override void InitializeLastChance ()
{
Cirrious.MvvmCross.Plugins.Messenger.PluginLoader.Instance.EnsureLoaded();
base.InitializeLastChance ();
}
Additionally you can also store the account credentials locally, this is described on the Xamarin.Auth link under AccountStore.Create().Save. Note that if you receive a platform not supported exception then add PLATFORM_IOS as a preprocessor directive to your project.
I realise the question is a couple of months old but since it rates high on google thought I'd provide an answer since there isn't any

Windows Phone link from Tile error

I have a list of theaters and I created a secondary tile from my application to navigate directly to specific theater. I pass the id of the theater in query string :
I load the theaters from a WCF service in the file "MainViewModel.cs"
In my home page, I have a list of theaters and I can navigate to a details page.
But when I want to navigate from the tile, I have an error...
The Tile :
ShellTile.Create(new Uri("/TheaterDetails.xaml?selectedItem=" + theater.idTheater, UriKind.Relative), tile, false);
My TheaterDetails page :
public partial class TheaterDetails : PhoneApplicationPage
{
theater theater = new theater();
public TheaterDetails()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
if (DataContext == null)
{
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
theater = (from t in App.ViewModel.Theaters
where t.idTheater == index
select t).SingleOrDefault();
DataContext = theater;
....
....
....
The error :
https://dl.dropboxusercontent.com/u/9197067/error.png
Like if the data were not loaded...
Do you have an idea where the problem come from ?
The solution could be easy but I am a beginner... Maybe it's because I load the data asynchronously and the application doesn't wait until it's done...
Thanks
EDIT :
My LoadData() method :
public void LoadData()
{
client.GetTheatersCompleted += new EventHandler<ServiceReference1.GetTheatersCompletedEventArgs>(client_GetTheatersCompleted);
client.GetTheatersAsync();
// Other get methods...
this.IsDataLoaded = true;
}
private void client_GetTheatersCompleted(object sender, ServiceReference1.GetTheatersCompletedEventArgs e)
{
Theaters = e.Result;
}
You should check to see which variable is actually null. In this case it looks to be Theaters (otherwise the error would have thrown earlier).
Since Theaters is populated from a web call it is most likely being called asynchronously, in other words when you return from LoadData() the data is not yet there (it's still waiting for the web call to come back), and is waiting for the web service to return its values.
Possible solutions:
Make LoadData() an async function and then use await LoadData(). This might require a bit of rewriting / refactoring to fit into the async pattern (general introduction to async here, and specific to web calls on Windows Phone here)
A neat way of doing this that doesn't involve hacks (like looping until the data is there) is to raise a custom event when the data is actually populated and then do your Tile navigation processing in that event. There's a basic example here.
So the solution that I found, thanks to Servy in this post : Using async/await with void method
I managed to use async/await to load the data.
I replaced my LoadData() method by :
public static Task<ObservableCollection<theater>> WhenGetTheaters(ServiceClient client)
{
var tcs = new TaskCompletionSource<ObservableCollection<theater>>();
EventHandler<ServiceReference1.GetTheatersCompletedEventArgs> handler = null;
handler = (obj, args) =>
{
tcs.SetResult(args.Result);
client.GetTheatersCompleted -= handler;
};
client.GetTheatersCompleted += handler;
client.GetTheatersAsync();
return tcs.Task;
}
public async Task LoadData()
{
var theatersTask = WhenGetTheaters(client);
Theaters = await theatersTask;
IsDataLoaded = true;
}
And in my page :
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
await App.ViewModel.LoadData();
}