Skip to content
This repository has been archived by the owner on Dec 5, 2021. It is now read-only.

Receiving messages without polling. #679

Closed
wants to merge 31 commits into from
Closed

Receiving messages without polling. #679

wants to merge 31 commits into from

Conversation

pp4x
Copy link

@pp4x pp4x commented Jan 8, 2018

On this branch, it is introduced a feature that allows the API to receive updates from the server without polling by subscribing an event (simply called Updates) and leaving the application on a receive loop rather than simply waiting to poll again.

pp4x added 11 commits December 26, 2017 15:27
…sion object

to be passed on to Telegram Client and modify, in example, IP address and port.
…trieved by application in case the sign in process is bypassed.

Example code:

                if (client.IsUserAuthorized())
                    user = client.Session.TLUser;
                else
                {
			/* sign in or sign up */
		}
* Must remove debug message "Msg code:" when feature will get complete.
Changed to highlight the changes to the fork.
* main event loop added to TelegramClient as a single function call.
README.md Outdated
[![Build status](https://ci.appveyor.com/api/projects/status/95rl618ch5c4h2fa?svg=true)](https://ci.appveyor.com/project/sochix/tlsharp)
[![NuGet version](https://badge.fury.io/nu/TLSharp.svg)](https://badge.fury.io/nu/TLSharp)
<a href="https://github.com/sochix/telegram-tools"><img src=https://img.shields.io/badge/Telegram%20Tools-1.0.0-blue.svg /></a>

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you removing this?

pp4x added 3 commits January 8, 2018 16:51
…rupted.

* There's also a new event that allows a client app to know whether it's safe to do requests without interfering with the event loop.
@ruben0909
Copy link

+1
When will be available this feature?

Thank you

@@ -128,7 +128,7 @@ public class TelegramClient : IDisposable
}
finally
{
IdleLoop(this);
IdleLoop?.Invoke(this);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ppanhoto78 can you propose this commit as a separate pull request please?

@knocte
Copy link
Collaborator

knocte commented Jan 29, 2018

@ruben0909 well, it seems @ppanhoto78 has mixed many changes in this PR which are unrelated to the task at hand (such as changing the README file).

If you want to speed up this process, you could import his changes in a fork of yours, and create a new PR.

@Marjani
Copy link

Marjani commented Mar 2, 2018

I run this code whit @ppanhoto78 sample but don't receive updates!
https://github.com/Marjani/TLSharp`

class Program
{
    const int APIId = 0000000;
    const string APIHash = "be1????????????????????";
    const string phone = "+98921????????";
    static void Main(string[] args)
    {
        new Program().MainAsync(args).Wait();

    }

    private async Task MainAsync(string[] args)
    {
        TelegramClient client = null;
        try
        {
            // -- if necessary, IP can be changed so the client can connect to the test network.
            Session session = null;
            //    new Session(new FileSessionStore(), "session")
            //{
            //    ServerAddress = "149.154.175.10",
            //    Port = 443
            //};
            //Console.WriteLine($"{session.ServerAddress}:{session.Port} {phone}");
            client = new TelegramClient(APIId, APIHash, session);
            // subscribe an event to receive live messages
            client.Updates += Client_Updates;
            await client.ConnectAsync();
            Console.WriteLine($"Authorised: {client.IsUserAuthorized()}");
            TLUser user = null;
            // -- If the user has already authenticated, this step will prevent account from being blocked as it
            // -- reuses the data from last authorisation.
            if (client.IsUserAuthorized())
                user = client.Session.TLUser;
            else
            {
                var registered = await client.IsPhoneRegisteredAsync(phone);
                var hash = await client.SendCodeRequestAsync(phone);
                Console.Write("Code: ");
                var code = Console.ReadLine();
                if (!registered)
                {
                    Console.WriteLine($"Sign up {phone}");
                    user = await client.SignUpAsync(phone, hash, code, "First", "Last");
                }
                Console.WriteLine($"Sign in {phone}");
                user = await client.MakeAuthAsync(phone, hash, code);
            }

            var contacts = await client.GetContactsAsync();
            Console.WriteLine("Contacts:");
            foreach (var contact in contacts.Users.OfType<TLUser>())
            {
                var contactUser = contact as TLUser;
                Console.WriteLine($"\t{contact.Id} {contact.Phone} {contact.FirstName} {contact.LastName}");
            }


            var dialogs = await client.GetUserDialogsSliceAsync();
            Console.WriteLine("Channels: ");
            foreach (var channelObj in dialogs.Chats.OfType<TLChannel>())
            {
                var channel = channelObj as TLChannel;
                Console.WriteLine($"\tChat: {channel.Title}");
            }

            Console.WriteLine("Groups:");
            TLChat chat = null;
            foreach (var chatObj in dialogs.Chats.OfType<TLChat>())
            {
                chat = chatObj as TLChat;
                Console.WriteLine($"Chat name: {chat.Title}");
                var request = new TLRequestGetFullChat() { ChatId = chat.Id };
                var fullChat = await client.SendRequestAsync<TeleSharp.TL.Messages.TLChatFull>(request);

                var participants = (fullChat.FullChat as TeleSharp.TL.TLChatFull).Participants as TLChatParticipants;
                foreach (var p in participants.Participants)
                {
                    if (p is TLChatParticipant)
                    {
                        var participant = p as TLChatParticipant;
                        Console.WriteLine($"\t{participant.UserId}");
                    }
                    else if (p is TLChatParticipantAdmin)
                    {
                        var participant = p as TLChatParticipantAdmin;
                        Console.WriteLine($"\t{participant.UserId}**");
                    }
                    else if (p is TLChatParticipantCreator)
                    {
                        var participant = p as TLChatParticipantCreator;
                        Console.WriteLine($"\t{participant.UserId}**");
                    }
                }

                var peer = new TLInputPeerChat() { ChatId = chat.Id };
                var m = await client.GetHistoryAsync(peer, 0, 0, 0);
                Console.WriteLine(m);
                if (m is TLMessages)
                {
                    var messages = m as TLMessages;


                    foreach (var message in messages.Messages)
                    {
                        if (message is TLMessage)
                        {
                            var m1 = message as TLMessage;
                            Console.WriteLine($"\t\t{m1.Id} {m1.Message}");
                        }
                        else if (message is TLMessageService)
                        {
                            var m1 = message as TLMessageService;
                            Console.WriteLine($"\t\t{m1.Id} {m1.Action}");
                        }
                    }
                }
                else if (m is TLMessagesSlice)
                {
                    bool done = false;
                    int total = 0;
                    while (!done)
                    {
                        var messages = m as TLMessagesSlice;

                        foreach (var m1 in messages.Messages)
                        {
                            if (m1 is TLMessage)
                            {
                                var message = m1 as TLMessage;
                                Console.WriteLine($"\t\t{message.Id} {message.Message}");
                                ++total;
                            }
                            else if (m1 is TLMessageService)
                            {
                                var message = m1 as TLMessageService;
                                Console.WriteLine($"\t\t{message.Id} {message.Action}");
                                ++total;
                                done = message.Action is TLMessageActionChatCreate;
                            }
                        }
                        m = await client.GetHistoryAsync(peer, total, 0, 0);
                    }
                }
            }

            // -- Wait in a loop to handle incoming updates. No need to poll.
            for (;;)
            {
                await client.WaitEventAsync(180000);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }

    private void Client_Updates(TelegramClient client, TLAbsUpdates updates)
    {
        Console.WriteLine($"Got update: {updates}");
        if (updates is TLUpdateShort)
        {
            var updateShort = updates as TLUpdateShort;
            Console.WriteLine($"Short: {updateShort.Update}");
            if (updateShort.Update is TLUpdateUserStatus)
            {
                var status = updateShort.Update as TLUpdateUserStatus;
                Console.WriteLine($"User {status.UserId} is {status.Status}");
                if (status.Status is TLUserStatusOnline)
                {
                    try
                    {
                        var peer = new TLInputPeerUser() { UserId = status.UserId };
                        client.SendMessageAsync(peer, "Você está online.").Wait();
                    }
                    catch { }
                }
            }
        }
        else if (updates is TLUpdateShortMessage)
        {
            var message = updates as TLUpdateShortMessage;
            Console.WriteLine($"Message: {message.Message}");
            MarkMessageRead(client, new TLInputPeerUser() { UserId = message.UserId }, message.Id);
        }
        else if (updates is TLUpdateShortChatMessage)
        {
            var message = updates as TLUpdateShortChatMessage;
            Console.WriteLine($"Chat Message: {message.Message}");
            MarkMessageRead(client, new TLInputPeerChat() { ChatId = message.ChatId }, message.Id);
        }
        else if (updates is TLUpdates)
        {
            var allUpdates = updates as TLUpdates;
            foreach (var update in allUpdates.Updates)
            {
                Console.WriteLine($"\t{update}");
                if (update is TLUpdateNewChannelMessage)
                {
                    var metaMessage = update as TLUpdateNewChannelMessage;
                    var message = metaMessage.Message as TLMessage;
                    Console.WriteLine($"Channel message: {message.Message}");
                    var channel = allUpdates.Chats[0] as TLChannel;
                    MarkMessageRead(client,
                                    new TLInputPeerChannel() { ChannelId = channel.Id, AccessHash = channel.AccessHash.Value },
                                    message.Id);
                }
            }

            foreach (var user in allUpdates.Users)
            {
                Console.WriteLine($"{user}");
            }

            foreach (var chat in allUpdates.Chats)
            {
                Console.WriteLine($"{chat}");
            }
        }
    }

    private void MarkMessageRead(TelegramClient client, TLAbsInputPeer peer, int id)
    {
        // An exception happens here but it's not fatal.
        try
        {
            var request = new TLRequestReadHistory();
            request.MaxId = id;
            request.Peer = peer;
            client.SendRequestAsync<bool>(request).Wait();
        }
        catch { }

    }
}

`

…() is called before the loop starts, the client will never loop for events.
@knocte
Copy link
Collaborator

knocte commented Mar 9, 2018

@ppanhoto78 man, if you keep pushing to your branch, we always get notified... Any chance you can separate this contribution properly?

@knocte knocte closed this Mar 9, 2018
@knocte knocte reopened this Mar 9, 2018
@pp4x
Copy link
Author

pp4x commented Mar 9, 2018

I'm really sorry for that. This was my first pull request ever and I went through a lot of trouble after that first release.

Some serious problems came from the command/event interaction: if a command is issued and an event comes in between and another command is issue as an action for that processed event. That corrupted the internal state horribly and took me quite a while to figure out how those interactions broke. Of course, I had to add logging in order to figure that out.

The ScheduledTasks event was my best shot to keep the cohesion of request/reply nature of the API with events that could intrude in between. Every action in response to an event must be scheduled for a time the API is idle for sure.

IdleTasks appeared because Ping messages are needed from time to time or the server will drop the client. The blocking receive() are a product of my inability to control async receive() for very short periods.

There's one more thing: large channels and groups do not report messages as events, those must be polled anyway.

I'm open to suggestions on how I could break all my lessons learned in small patches that could be easily integrated. Thank you for your patience.

…Slice is received, the continuation can be asked for.
@imclint21
Copy link

imclint21 commented Jan 5, 2020

Hi,

Does this PR will be merged? it seems to be a nice contribution! and maybe it fixes #891

PS: I advise you to use GitHub's actions!

@merqlove
Copy link

merqlove commented Jan 6, 2020

Hello guys, anyone tried that pr? If it works i can fix and repull it, i need events too in my app

@knocte
Copy link
Collaborator

knocte commented Jan 6, 2020

@merqlove please try it and tell us if it works for you

@imclint21
Copy link

I agree with @merqlove if we can have updates subscriptions it could be great!

@imclint21
Copy link

imclint21 commented Jan 6, 2020

This lib works fine for updates: https://github.com/OpenTl/OpenTl.ClientApi/wiki/Quick-Start

image

@merqlove
Copy link

merqlove commented Jan 7, 2020

@knocte i’ll try, after local merge i see that this pr is outdated. As i see we don’t need updates for json and testing deps., as it presence in this pr?

@knocte
Copy link
Collaborator

knocte commented Jan 7, 2020

Not sure what you mean.

@merqlove
Copy link

merqlove commented Jan 7, 2020

This pr have updates for dependencies in projects, if you look inside the files. I think such updates must be extracted

@knocte
Copy link
Collaborator

knocte commented Jan 7, 2020

I don't think those updates are needed for just the purpose of receiving updates without polling. Clearly what happened here is that the original contributor created the initial PR out of his master branch and then went ahead to evolve his own fork on top of the updates-with-no-polling feature.

@merqlove
Copy link

merqlove commented Jan 9, 2020

I did review of the pr & current proj. Sorry i’ve selected python analog which have all needed features inside & works well out of the box, because current project have no support of netstandard 2.x., for me is too much of time to port it first.
Ps i didn’t tried to test the pr because i have another problems (like reauth every second run, net45 and so on). As @knocte said that pr need cleanup, l did some & will pr what i have. Anyone will able to fork and work on
Good luck

@knocte
Copy link
Collaborator

knocte commented Jan 9, 2020

Everybody claims "anyone can do it", but in the end no one does it. Thanks for nothing.

@knocte knocte closed this Jan 9, 2020
merqlove added a commit to merqlove/TLSharp that referenced this pull request Jan 9, 2020
@imclint21
Copy link

Like I said, this library works fine: #679 (comment)

markwest51 pushed a commit to markwest51/TLSharp that referenced this pull request Apr 24, 2020
pr updates

amended pr review changes

resolved merge conflicts

updates from last night before rebase

update on message test now passing

removed nlog references and usage

resolve conflicts from HEAD

Reapply Events PR sochix#679

update on message test now passing

removed nlog references and usage
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants