Blog (220)
Komentarze (4k)
Recenzje (1)
@djfoxerPowiadomienia z dobreprogramy.pl w C# — z dziennika dewelopera

Powiadomienia z dobreprogramy.pl w C# — z dziennika dewelopera

20.03.2016 11:56, aktualizacja: 22.03.2016 00:05

Prace ku stworzeniu uniwersalnej aplikacji Windows 10 (+ Mobile) obsługującej powiadomienia z portalu dobreprogramy.pl posuwają się na przódu. We wcześniejszym poście przedstawiłem kod (plus projekt w VS), który służy do logowania się na swoje konto z poziomu C#. Został nam zatem ostatni etap w przygotowaniu serca naszej aplikacji - zarządzanie powiadomieniami. Zatem do dzieła!

Pobieramy powiadomienia z portalu w formacie JSON

Analiza sposobu działania powiadomień na portalu została przedstawiona w poście: Analizujemy kod portalu dobreprogramy.pl — czyli jak działa system pow.... Dziś przejdziemy już jednak do kodowania.

Zacznijmy zatem od pobrania JSONa z listą powiadomień dla zalogowanego użytkownika. Zakładamy oczywiście, że posiadamy już ciasteczko (w kodzie jest to zmienna cookie), które identyfikuje zalogowanego użytkownika. Opis w jaki sposób jest to zrobione znajduje się w ostatnim wpisie (Logujemy się do dobreprogramy.pl z poziomu kodu C# ).


            request = WebRequest.Create(Const.NotifyUrlWithTimeStamp);
            request.Headers["Cookie"] = cookie;

            response = await request.GetResponseAsync();

            string pageSource;
            using (StreamReader sr = new StreamReader(response.GetResponseStream()))
            {
                pageSource = sr.ReadToEnd();
            }

Podobnie jak przy logowaniu, tworzymy zapytanie do serwera poprzez użycie metody z klasy abstrakcyjnej WebRequest. Naszym adresem docelowym jest:

http://www.dobreprogramy.pl/Providers/NotifyHelper.ashx?ping=ping&_=znacznik_czasu

Oczywiście zmienna znacznik_czasu będzie generowana przez nasz kod przy każdym zapytaniu. Jest to nic innego jak aktualna data JavaScript jako int (ilość milisekund od 1 stycznia 1970 roku).

Ważnym elementem jest tutaj uzupełnienie nagłówka o ciasteczko, jakie pozyskaliśmy na etapie logowania. W kolejnych krokach pobieramy zapytanie z serwera, czyli string posiadający odpowiedź w JSONie:

585186

Praca z JSONem - Json.NET na ratunek!

Zostaje nam zatem przerobienie JSONa na coś bardziej zjadliwego. Celem jest stworzenie obiektów nowej klasy, które będą reprezentować powiadomienia z portalu. Chcąc ułatwić pracę z JSONem, nie trzeba tworzyć klas pośrednich lub ręcznie parsować stringa. Posłużymy się tutaj deserializatorem z frameworku Json.NET. W tym celu do projektu dodajemy przez NuGeta pakiet Newtonsoft.Json. Nasz kod uzupełniamy o linijkę:


            var respList = (JObject)JsonConvert.DeserializeObject(pageSource);

Pozwoli to nam na operowanie na danych w znacznie wygodniejszy sposób:

585191

Jak wygląda powiadomienie?

W celu przechowywania powiadomień w aplikacji (bazka SQLite, a może coś bardziej trywialnego, jak plik XML ładowany, tylko częściowo, na wejście do aplikacji - to jeszcze kwestia otwarta). W tym celu stworzyłem klasę, która będzie przetrzymywała dane:



    public class Notification
    {
        public string Id { get; set; }

        public string PublicationId { get; set; }

        public string Avatar { get; set; }

        public string CustomText { get; set; }

        public string Title { get; set; }

        public string TargetUrl { get; set; }

        public DateTime AddedDate { get; set; }

        public NotificationType TypeValue { get; set; }

        public NotificationStatus Status { get; set; }

        public string UserName { get; set; }
    }

Pola są odwzorowaniem danych z JSONa, dodatkowo uzupełnione o bardziej strawne formaty odnośnie statusu powiadomienia (enum NotificationStatus), typu powiadomienia (enum NotificationType), a także daty przekonwertowanej na format DateTime.

Statusy powiadomień mamy dwa (w tym jeden własny, gdyby coś nowego się pojawiło: Unknown). Typów notyfikacji jest znacznie więcej. Całość przedstawia się następująco:



        public enum NotificationType
        {
            Unknown = -1,
            Comment = 0,
            CommentBlog = 1,
            ProgramUpdate = 2,
            Contest = 3,
            FriendsAccept = 4,
            FriendsInvite = 5,
            BlogAnnotation = 6,
            PrivateMsg = 7,
            Mention = 8,
            License = 9,
            Badges = 10,
        }

        public enum NotificationStatus
        {
            Unknown = -1,
            New = 0,
            Old = 1
        }

Typy powiadomień w JSONie są w formie tekstu, więc parsujemy je wg następującego schematu:



        public static NotificationType ParseToNotificationType(string typeString)
        {
            if (string.IsNullOrWhiteSpace(typeString))
            {
                return NotificationType.Unknown;
            }

            typeString = typeString.ToLower();
            switch (typeString)
            {
                case "comment":
                    return NotificationType.Comment;
                case "comment_blog":
                    return NotificationType.CommentBlog;
                case "program_update":
                    return NotificationType.ProgramUpdate;
                case "contest":
                    return NotificationType.Contest;
                case "friends_accept":
                    return NotificationType.FriendsAccept;
                case "friends_invite":
                    return NotificationType.FriendsInvite;
                case "blog_annotation":
                    return NotificationType.BlogAnnotation;
                case "private_msg":
                    return NotificationType.PrivateMsg;
                case "mention":
                    return NotificationType.Mention;
                case "license":
                    return NotificationType.License;
                case "badges":
                    return NotificationType.Badges;
                default:
                    return NotificationType.Unknown;
            }
        }

JSON => Notification

Samo parsowanie z JSONa na nasz obiekt Notification jest trywialnie proste dzięki Json.NET:



            if (respList.HasValues)
            {
                var c = respList.First.First;

                for (int i = 0; i < c.Count(); i++)
                {
                    var ele = (JProperty)c.ElementAt(i);
                    Notification n = JsonConvert.DeserializeObject<Notification>(ele.Value.ToString());

                    n.AddedDate = new DateTime(1970, 1, 1).AddMilliseconds((long)(((JValue)ele.Value["Data"]).Value));
                    n.TypeValue = Enum.ParseToNotificationType(((JValue)ele.Value["Type"]).Value.ToString());
                    n.PublicationId = ele.Name.Split(':')[0];
                    n.Id = ele.Name.Split(':')[1];
                    notList.Add(n);
                }
            }

Głównym rdzeniem jest tutaj:

Notification n = JsonConvert.DeserializeObject<Notification>(ele.Value.ToString());

Nie chciałem się już bawić w jakieś convertery, więc ręcznie zamieniłem pola, które nie mogą być automatycznie zmapowane: AddedDate (rzutowanie daty z JS na DateTime), TypeValue (rzutowanie na enuma) oraz Id i PublicationId (trzeba rozdzielić oryginalne pole Name).

W taki oto sposób otrzymujemy piękną listę powiadomień w C#:

585207

Na takiej liście można już spokojnie pracować

Powiadomienie - odczytywanie/usuwanie

Zostało jeszcze dodanie metody, które oznaczy powiadomienie jako odczytane i usunięte. Wystarczy tutaj dodać mały kawałek kodu:



            var request = WebRequest.Create(Const.NotifyUrlRaw);
            request.Headers["Cookie"] = cookie;
            request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
            request.Method = "POST";

            byte[] form = Encoding.UTF8.GetBytes(string.Format("{1}%5B%5D={0}", id, method));
            using (Stream os = await request.GetRequestStreamAsync())
            {
                os.Write(form, 0, form.Length);
            }
            var resesponse = await request.GetResponseAsync();

Wysyłamy zapytanie pod adres: (pamiętając o ciasteczku)

http://www.dobreprogramy.pl/Providers/NotifyHelper.ashx

. Tym razem jednak dodajemy prostego forma, który zawiera id powiadomienia i typ akcji (method = markAsRead/deleteNotify) do wykonania na powiadomieniu (odczytanie/usunięcie). Oczywiście request uzupełniony jest o typ zawartości i metodę wysyłania zapytania.

W ten sposób stworzyliśmy pełnoprawny mechanizm do zarządzania powiadomieniami z portalu dobreprogramy.pl z poziomu kodu w C#.

Kolejne kroki?

Główny mechanizm do logowania i zarządzania powiadomieniami już mamy. Myślę, że w następnym tygodniu uda mi się złożyć coś, co nie będzie wyglądać dobrze :P , ale będzie działać w tle i pokazywać powiadomienia w Windows 10. Zobaczymy, czy uda się ten plan osiągnąć przed Wielkanocą i/lub maratonem w Dębnie ;)

Zapraszam do kolejnych odcinków z serii :)

Aktualne źródła można znaleźć na GitHub pod adresem: https://github.com/djfoxer/dp.notification
585220
Wybrane dla Ciebie
Komentarze (6)