How to work with messages from WTelegramClient updates? (get chat/user infos) - telegram-api

I'm new to the WTelegramClient C# Library and was used to TLSharp (not working anymore)
I'm trying to understand how I get User info after update is received,
I have the example code that listen to updates and write them in console
but I can't understand how I can respond to the user that sent the message (new update)
I think I need the user id/access_hash to send message to the sender but I can't understand how
Here is how I get the new messages but it can get only username or name/id
private static void DisplayMessage(MessageBase messageBase, bool edit = false)
{
if (edit) Console.Write("(Edit): ");
switch (messageBase)
{
case Message m: Console.WriteLine($"{Peer(m.from_id) ?? m.post_author} in {Peer(m.peer_id)}> {m.message}"); break;
case MessageService ms: Console.WriteLine($"{Peer(ms.from_id)} in {Peer(ms.peer_id)} [{ms.action.GetType().Name[13..]}]"); break;
}
}
Here i can get the name or username of sender(if have) and the message itself
MessageService ('user' not channel or group) for example get me only firstname and lastname
How to get all info of sender or chat itself (i want to try mark as read the message)
I'm used to TLSharp and the new library WTelegramClient is different.
Thanks!!!

Below is a quick example on how to modify DisplayMessage to react to a message sent in private from a user, get the details about this user, verify who it is and which text was sent to us, and then send him a message back.
Notes:
For this example to work, you will need the latest version of Program_ListenUpdates.cs with static variables
DisplayMessage is now async Task, in order to use await
You can pass user to send a message because class User is implicitly converted to InputPeerUser (with the user id/access_hash).
You can do similarly for messages coming from chats, using PeerChat/PeerChannel classes and the _chats dictionary to get chat details
private static async Task DisplayMessage(MessageBase messageBase, bool edit = false)
{
if (edit) Console.Write("(Edit): ");
switch (messageBase)
{
case Message m:
Console.WriteLine($"{Peer(m.from_id) ?? m.post_author} in {Peer(m.peer_id)}> {m.message}");
if (m.flags.HasFlag(Message.Flags.out_))
break; // ignore our own outgoing messages
if (m.Peer is PeerUser pu) // got a message in a direct chat with a user
{
if (_users.TryGetValue(pu.user_id, out var user)) // get user details
{
if (user.username == "Wiz0u" && m.message == "hello")
{
await Client.SendMessageAsync(user, $"hi {user.first_name}, I'm {My.first_name}");
}
}
}
break;
case MessageService ms:
Console.WriteLine($"{Peer(ms.from_id)} in {Peer(ms.peer_id)} [{ms.action.GetType().Name[13..]}]");
break;
}
}

Related

AWS Amplify Auth Errors

I'm using the Android Amplify library. I am having trouble finding out what kind of error would be passed back from the Amplify.Auth.signIn() function. I'm not finding the documentation for this anywhere. Right now I am just kind of guessing as to what it will return. What I want is to tell the user how to recover from the error. Does the username not exist, was the password incorrect, was it of bad format, etc. Reading the source code I am given the impression that AmplifyException.recoveryMessage is what I want but that would still be problematic as it doesn't allow me to customize the message.
/**
* Sign in the user to the back-end service and set the currentUser for this application
* #param username User's username
* #param password User's password
*/
override fun initiateSignin(username : String, password : String) {
//Sign in the user to the AWS back-end
Amplify.Auth.signIn(
username,
password,
{result ->
if (result.isSignInComplete) {
Timber.tag(TAG).i("Sign in successful.")
//Load the user if the sign in was successful
loadUser()
} else {
Timber.tag(TAG).i("Sign in unsuccessful.")
//TODO: I think this will happen if the password is incorrect?
}
},
{error ->
Timber.tag(UserLogin.TAG).e(error.toString())
authenticationRecoveryMessage.value = error.recoverySuggestion
}
)
}
Authentication recovery message is LiveData that I want to update a snackbar which will tell the user what they need to do for a successful login. I feel there must be some way to get the error from this that I just haven't figured out yet. The ideal way to handle messages to the user is with XML strings for translation possibilities so I would really like to use my own strings in the snackbar but I need to know the things that can go wrong with sign-up and what is being communicated to me through the error -> {} callback.
I couldn't find them in the documentation myself, so i decided to log the possibles cases.
try {
const signInResult = await Auth.signIn({
username: emailOrPhoneNumber,
password
});
const userId = signInResult.attributes.sub;
const token = (await Auth.currentSession()).getAccessToken().getJwtToken();
console.log(userId, 'token: ', token);
resolve(new AuthSession(userId, token, false));
} catch (e) {
switch (e.message) {
case 'Username should be either an email or a phone number.':
reject(`${AuthError.usernameInvalid}: ${e.message}`);
break;
case 'Password did not conform with policy: Password not long enough':
reject(`${AuthError.passwordTooShort}: ${e.message}`);
break;
case 'User is not confirmed.':
reject(`${AuthError.userIsNotConfirmed}: ${e.message}`);
break;
case 'Incorrect username or password.':
reject(`${AuthError.incorrectUsernameOrPassword}: ${e.message}`);
break;
case 'User does not exist.':
reject(`${AuthError.userDoesNotExist}: ${e.message}`);
break;
default:
reject(`${AuthError.unknownError}: ${e.message}`);
}
}
SignIn uses Cognito's InitiateAuth under the hood, so error codes can be found here:
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html#API_InitiateAuth_Errors
They are available in the code field of the error.

Sitecore EXM 3.2(ECM) Assign goal to the triggered message

I need to do a simple newsletter form. This form should work like this:
User inputs an email and presses to the submit button
User recieves message on email with confirm link
After user clicks on the link his email is added to Recipient list
This form should be work with help EXM
I've created Triggered message in the EXM with link for subscription.
And I wrote this code for the Submit button for trigger the Newsletter Goal
[HttpPost]
public ActionResult NewsletterSubscribe(NewsletterViewBag model)
{
var goal = Context.Database.GetItem(newsletterGoal);
if (goal == null)
{
continue;
}
var registerGoal = new Sitecore.Analytics.Data.Items.PageEventItem(goal);
var eventData = Tracker.Current.CurrentPage.Register(registerGoal);
eventData.Data = goal[DateTime.Now.ToString(CultureInfo.InvariantCulture)];
Tracker.Submit();
}
How I can assign my triggered message to the newsletterGoal?
Also I try manually send message this way:
MessageItem message = Sitecore.Modules.EmailCampaign.Factory.GetMessage(new ID(messageId));
Sitecore.Modules.EmailCampaign.AsyncSendingManager manager = new AsyncSendingManager(message);
var contactId = ClientApi.GetAnonymousIdFromEmail(email);
var recipientId = (RecipientId) new XdbContactId(contactId);
manager.SendStandardMessage(recipientId);
And I see error in the log: The recipient 'xdb:857bbea1-1f18-4621-a798-178399cd0b54' does not exist. But Triggered Message haven't any recipient list
Goals are not assigned directly to messages. You can, however, assign engagement plans and campaigns. Each message has its own engagement plan to handle tracking the contacts actions with the message. If you create a campaign that triggers a goal, you can assign that to the message and it will be associated with the contact when they receive the message. You can also leverage the message engagement plan to trigger events as the contact proceeds through those states.
Also, you're missing some details while recording the contact data.
Look at the newsletter signup control that is included in the EXM module. The important part in there is this:
protected virtual RecipientId RecipientId
{
get
{
RecipientId recipientId = null;
var contactId = ContactId;
if (contactId != (ID)null)
{
recipientId = new XdbContactId(contactId);
}
return recipientId;
}
}
protected virtual ID ContactId
{
get
{
if (!Email.Visible || string.IsNullOrEmpty(Email.Text))
{
return new ID(Tracker.Current.Contact.ContactId);
}
var anonymousId = ClientApi.GetAnonymousIdFromEmail(Email.Text);
return anonymousId.HasValue ? new ID(anonymousId.Value) : new ID(Tracker.Current.Contact.ContactId);
}
}
protected virtual void UpdateEmailInXdb()
{
_recipientRepository.UpdateRecipientEmail(RecipientId, Email.Text);
}
It will write the email address directly to Mongo, rather than waiting for the session to end. Include this and the related RecipientId and ContactId properties in your signup code.
Once they are signed up you can register the goal programmatically or send them to a Thank You page where the goal can be registered (Advanced - Tracking), or send the message and let that register the goal. Or create an engagement plan with states for each step of the process (this is the best way).
You'll also want to add the recipient to a list that the newsletter message can use later. Actually, it looks to me like the example Subscription Form does everything you need.

Akka Ask & Futures

I'm an akka noob so apologies!
I'm playing around with a system that uses Spray and Akka.
I'm using the following code snippet to send a message to another actor.
It uses ask which, from what I understand will return a future which is resolved in "mapTo" and "map". I then return the result to the users using Sprays "complete" directive.
val response = (worker ? Create(json))
.mapTo[Ok]
.map(result => s"I got a response: ${result}")
.recover { case _ => "error" }
complete(response)
My question is, since this is a future, do I need to be worried about sending the correct response to the client? In some code samples I see examples where the actorRef to reply to is sent as part of the request...
// set reply to actor
val replyTo = sender() // important to not close over sender()
// create actor that collects replies from workers
val aggregator = context.actorOf(Props(
classOf[StatsAggregator], words.size, replyTo))
Then in the receiving actor...
replyTo ! SendResult
Should I be passing the "replyTo" actor as part of the request or is this all taken care of in the mapTo?
Thanks in advance!
The complete directive will send back a response to http/https client of your service. You don't need to do more than that. Please note that your code swallows errors by making recover on a future. Spray will treat it as a success and will return status code 200.
The last and most importantly, your worker has to reply with Ok message back like this.
class Worker extends Actor {
def receive: Receive = {
case Create(json) =>
//do some staff with json
sender() ! Ok // This Ok message will be passed in the future in spray route
}
}
The replyTo idiom is needed only when worker uses Future internally to process the work load. As it in the following example
class Worker extends Actor {
def recieve: Recieve = {
case Create(json) =>
val future = Future{
//do some staff with json
}
val replyTo = sender()
future.onComplete {
case scala.util.Success(result) =>
replyTo ! Ok
case scala.util.Failure(ex) =>
replyTo ! akka.actor.Status.Failure(ex)
}
}
}
The replyTo is needed to fix actual sender of the message since onComplete may be executed within a different actor context that can point to a different sender resulting in message being sent to a wrong actor.

Akka actor response caching

i'm using Akka on one of my projects and i need to get the state of an actor, the way i'm doing it is as follows.
a REST request comes in
#GET
#Produces(Array(MediaType.APPLICATION_JSON))
def get() = {
try {
Await.result((getScanningActor ? WorkInfo), 5.second).asInstanceOf[ScanRequest]
}
catch{
case ex: TimeoutException => {
RequestTimedOut()
}
}
}
on the actor i respond with the current work state
case WorkInfo => sender ! currentWork
for some reason the first time i call this function i get the correct value, on the following requests i get the same value i received on the first call
I'm also using DCEVM if that makes any difference.

Send a push notification from a windows phone device to webservice

I have a question about the push notification service of the Windows Phone 7 device:
I can now send a push notification, using a web application to a phone, changing the data of the tile. But the problem is: when I start the app, I need to display the URI in the debugger output, and then copy-paste it inside the web application, which in turn will contact the MPNS, which is fine for an update, once to a single phone. But I want to create a webservice that can make multiple calls automatically, retrieve the URI of the application ( which changes after closing-and-opening the app, I think ) and send a push notification to it. But I haven't found an MSDN - topic that deals with this. They just use commends, saying : "to be replaced later with the URI needed." So my question is: how do I use the phone to send such a message to the webservice, respond to it, and connect to the phone again, handling such request?
and also: do I need an authenticated webservice, or is there a debug version?
This is what I have thus far :
/// <summary>
/// Setup a connection with a webservice, in order to update a shell, either a toast- or a tile shell.
/// </summary>
/// <param name="shellType">The type of shell you wish to update</param>
public void SetupShellChannel ( ShellBindType shellType )
{
//holds the push notification that is created. Since we can only have one notification channel open at any one time,
//we will need to check for existance. That is why, the channelName shouldn't be changed
HttpNotificationChannel _httpChannel = HttpNotificationChannel.Find( _channelName );
//if the _httpChannel was not found ( read: does not exist )
if ( _httpChannel == null )
{
_httpChannel = new HttpNotificationChannel( _channelName );
_httpChannel.Open( );
//because there is more than one shelltype we can open, we will use a switch to call the method we seek
BindToShell( _httpChannel, shellType );
}
//Only one push notification service is allowed per application, so we cannot send a tile notification, as well as
//a toast message notification. When we attempt this, we get a InvalidOperationException
else
{
//in this case, the _httpChannel did already exist, but the problem is, we cannot just add the eventHandlers,
//because there is the danger that it didn't exist, and we would get a null pointer exception.
//_httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>( httpChannel_ChannelUriUpdated );
//_httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>( httpChannel_ErrorOccurred );
//For testing purposes, we now display the URI to the user, and as output. Normally, we would pass this URI back to the webserver
System.Diagnostics.Debug.WriteLine( _httpChannel.ChannelUri.ToString( ) );
}
//if ( _httpChannel.ChannelUri )
//When the URI is updated, we want this to be sent to the server as well, so we know that the adress has changed,
//and don't just send data somewhere into the void. Also, when encountering an error, we want to show the user when
//an error has occured.
_httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>( HttpChannel_ChannelUriUpdated );
_httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>( HttpChannel_ErrorOccurred );
}
//here, also we would return the URI to the server, but for debugging purposes, we display them to the user.
void HttpChannel_ChannelUriUpdated( object sender, NotificationChannelUriEventArgs e )
{
Deployment.Current.Dispatcher.BeginInvoke( ( ) =>
{
System.Diagnostics.Debug.WriteLine( e.ChannelUri.ToString( ) );
MessageBox.Show( String.Format( "the URI is {0}", e.ChannelUri.ToString( ) ) );
} );
}
private void BindToShell( HttpNotificationChannel channel, ShellBindType shellType )
{
switch ( shellType )
{
case ShellBindType.BindToShellTile:
channel.BindToShellTile( );
break;
case ShellBindType.BindToShellToast:
channel.BindToShellToast( );
break;
}
}
void HttpChannel_ErrorOccurred( object sender, NotificationChannelErrorEventArgs e )
{
//getting an error would be caugth here, and then displayed to the user.
Deployment.Current.Dispatcher.BeginInvoke( ( ) =>
{
MessageBox.Show( String.Format( "A push notification {0} error occured. {1}{(2)}{3}",
e.ErrorType, e.Message, e.ErrorCode, e.ErrorAdditionalData ) );
} );
}
Ok I understand your question. What I have done is once I get teh URI from MPNS, I call a web method on a service with this as a parameter -
Subscribe(int subscriberId, Uri channelUri);
So you need to make sure you generate a subscriberId in your app to identify a user and store it in Isolated Storage. This can be a GUID.
The onus is now upon hte server to save the Subscriber to Uri mapping in persistant storage.
Also you need to provide UnSubscribe method for user to opt out of the push notification. This is one of the certification requirement for Push notifications.
Now about your second question - Yes, you would need to secure your services - you dont want to be handling with unknown requests.
What i do personally, divide it into 2 services - Publishing service and subscription service. The Publishing service will send out hte notifications while subscription will have the subscribe/unsubscribe methods.
I guess you are trying to ask that you can send push notification from Windows Phone itself or not instead using any other server side ASP/PHP like explained in Sample Applications in MSDN. Yes. You can send notifications from phone/device itself. You have to just change Send function of Sample app as given in MSDN. Reply if you have any queries.
static async Task<string> SendPushNotification(string textToSend)
{
//You can maintain a DB to query different channel URIs of devices
string subscriptionUri = "<Uri To Which You Want Send Notification>";
HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);
sendNotificationRequest.Method = "POST";
string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>" + textToSend + "</wp:Text1>" +
"<wp:Param>/NotificationDetails.xaml?Message=" + textToSend + "</wp:Param>" +
"</wp:Toast> " +
"</wp:Notification>";
byte[] notificationMessage = Encoding.UTF8.GetBytes(toastMessage);
sendNotificationRequest.ContentLength = notificationMessage.Length;
sendNotificationRequest.ContentType = "text/xml";
sendNotificationRequest.Headers["X-WindowsPhone-Target"] = "toast";
sendNotificationRequest.Headers["X-NotificationClass"] = "2";
using (var requestStream = await Task.Factory.FromAsync<Stream>(sendNotificationRequest.BeginGetRequestStream, sendNotificationRequest.EndGetRequestStream, null))
{
requestStream.Write(notificationMessage, 0, notificationMessage.Length);
}
string notificationStatus;
using (var response = (HttpWebResponse)(await Task<WebResponse>.Factory.FromAsync(sendNotificationRequest.BeginGetResponse, sendNotificationRequest.EndGetResponse, null)))
{
//StreamReader reader = new StreamReader(response.GetResponseStream());
//result = reader.ReadToEnd();
notificationStatus = response.Headers["X-NotificationStatus"];
MessageBox.Show(notificationStatus);
}
return notificationStatus;
}