Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

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

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:

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:

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#:

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

 

windows programowanie urządzenia mobilne

Komentarze

0 nowych
kkowalewski   8 #1 20.03.2016 11:21

O_o kosmos

drobok   13 #2 20.03.2016 12:20

O switch słyszał ? :P A tak apropo tej metody parsującej, skoro używasz json.net ta funkcja jest całkowicie zbędna - wystarczy w swojej klasie nazwać zmienne tak samo jak są one nazywane podczas przesyłania przez json :P

http://stackoverflow.com/questions/11260631/convert-json-into-class-object-in-c-...

djfoxer   17 #3 20.03.2016 12:39

@drobok: Raz: switch - z premedytacją go nie używam. Tyle. Taki mały "fetysz" :) Jest on dla mnie mniej praktyczny i jeśli trzeba zmienić jakiś warunek na bardziej złożony wówczas, trzeba robić refactoring ze switcha na ifa (przykład z życia wzięty). Także nie wiem po co ten komentarz o swtichu (wiem, wiem tablice hashujące itp. ale tutaj, różnicy nie będzie) ;)

Dwa. Json.NET. Jakbyś przeczytał i zrozumiał kod :P , to byś zauważył, że tak właśnie jest robione :) Deserializacja leci po nazwach, ale w przytoczonych kilku przypadkach nie jest to możliwe (int=>enum, JS DT int=> DT). Czytamy przed komentowaniem :)

Autor edytował komentarz.
mktos   9 #4 21.03.2016 19:24

@djfoxer: Jeżeli nie chcesz switcha, to jednak mimo wszystko wybrałbym Dictionary zamiast takiej drabinki if/elseif.

I można też pomyśleć o HttpClient zamiast "czystego" WebRequest.

djfoxer   17 #5 21.03.2016 20:13

@mktos: Pewnie finalnie będę musiał coś z tymi ifami zrobić :) Jeszcze zastanawiam się , czy tylko będą one do mapowania, czy jeszcze użyję do tworzenia szablonów różnych powiadomień.

WebRequest użyłem, gdyż ww wersji pierwszej logowałem się przy użyciu nie LoginProvider.ashx, a wysyłając czystego forma z nagłówkami, sesją itp. Wówczas szybciej i prościej wyglądało użycie WebRequesta, teraz faktycznie można pomyśleć o zmianie. Zobaczę jak w praktyce będzie to działało. Dzięki za cenne uwagi :)

djfoxer   17 #6 22.03.2016 00:06

@mktos @drobok A niech będzie już ten "znienawidzony" switch :P

Autor edytował komentarz.