From e4b1413ff3fb8c169e2725c3e7686f47eaf93ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20P=C3=B6sel?= Date: Sat, 27 Apr 2013 08:56:44 +0000 Subject: Facebook: Fixed getting offline messages. Loading some timestamp value of contacts (dword "LastActiveTS") for... I don't know. Version bump. git-svn-id: http://svn.miranda-ng.org/main/trunk@4548 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/FacebookRM/src/communication.cpp | 22 ++- protocols/FacebookRM/src/constants.h | 1 + protocols/FacebookRM/src/entities.h | 4 +- protocols/FacebookRM/src/json.cpp | 33 +++- protocols/FacebookRM/src/process.cpp | 232 ++++++++++++++++------------- protocols/FacebookRM/src/proto.h | 1 + protocols/FacebookRM/src/version.h | 2 +- 7 files changed, 178 insertions(+), 117 deletions(-) (limited to 'protocols') diff --git a/protocols/FacebookRM/src/communication.cpp b/protocols/FacebookRM/src/communication.cpp index fe8d53c121..48fc76e741 100644 --- a/protocols/FacebookRM/src/communication.cpp +++ b/protocols/FacebookRM/src/communication.cpp @@ -245,6 +245,7 @@ DWORD facebook_client::choose_security_level(int request_type) // case FACEBOOK_REQUEST_VISIBILITY: // case FACEBOOK_REQUEST_TABS: // case FACEBOOK_REQUEST_ASYNC: +// case FACEBOOK_REQUEST_UNREAD_MESSAGES: // case FACEBOOK_REQUEST_TYPING_SEND: default: return (DWORD)0; @@ -282,6 +283,7 @@ int facebook_client::choose_method(int request_type) // case FACEBOOK_REQUEST_LOAD_FRIENDS: // case FACEBOOK_REQUEST_LOAD_REQUESTS: // case FACEBOOK_REQUEST_SEARCH: +// case FACEBOOK_REQUEST_UNREAD_MESSAGES: default: return REQUEST_GET; } @@ -315,6 +317,7 @@ std::string facebook_client::choose_server(int request_type, std::string* data, case FACEBOOK_REQUEST_APPROVE_FRIEND: case FACEBOOK_REQUEST_LOAD_REQUESTS: case FACEBOOK_REQUEST_SEARCH: + case FACEBOOK_REQUEST_UNREAD_MESSAGES: return FACEBOOK_SERVER_MOBILE; // case FACEBOOK_REQUEST_LOGOUT: @@ -383,6 +386,15 @@ std::string facebook_client::choose_action(int request_type, std::string* data, return action; } + case FACEBOOK_REQUEST_UNREAD_MESSAGES: + { + std::string action = "/messages/?folder=unread"; + if (get_data != NULL) { + action += *get_data; + } + return action; + } + case FACEBOOK_REQUEST_DELETE_FRIEND: { std::string action = "/ajax/profile/removefriendconfirm.php?__a=1"; @@ -820,7 +832,7 @@ bool facebook_client::chat_state(bool online) std::string data = (online ? "visibility=1" : "visibility=0"); data += "&window_id=0"; data += "&fb_dtsg=" + this->dtsg_; - data += "&lsd=&phstamp=0&__user=" + self_.user_id; + data += "&phstamp=0&__user=" + self_.user_id; http::response resp = flap(FACEBOOK_REQUEST_VISIBILITY, &data); return handle_success("chat_state"); @@ -865,18 +877,16 @@ bool facebook_client::buddy_list() handle_entry("buddy_list"); // Prepare update data - std::string data = "user=" + this->self_.user_id + "&fetch_mobile=true&fb_dtsg=" + this->dtsg_ + "&lsd=&__user=" + this->self_.user_id; + std::string data = "user=" + this->self_.user_id + "&fetch_mobile=true&phstamp=0&fb_dtsg=" + this->dtsg_ + "&__user=" + this->self_.user_id; { ScopedLock s(buddies_lock_); + data += "&cached_user_info_ids="; int counter = 0; for (List::Item< facebook_user >* i = buddies.begin(); i != NULL; i = i->next, counter++) { - data += "&available_user_info_ids["; - data += utils::conversion::to_string(&counter, UTILS_CONV_UNSIGNED_NUMBER); - data += "]="; - data += i->data->user_id; + data += i->data->user_id + "%2C"; } } diff --git a/protocols/FacebookRM/src/constants.h b/protocols/FacebookRM/src/constants.h index ae39632fb3..f70ba28f40 100644 --- a/protocols/FacebookRM/src/constants.h +++ b/protocols/FacebookRM/src/constants.h @@ -90,6 +90,7 @@ along with this program. If not, see . #define FACEBOOK_REQUEST_LOAD_FRIENDS 121 // getting list of all friends #define FACEBOOK_REQUEST_FEEDS 122 // getting feeds #define FACEBOOK_REQUEST_NOTIFICATIONS 123 // getting notifications +#define FACEBOOK_REQUEST_UNREAD_MESSAGES 124 // getting unread messages #define FACEBOOK_REQUEST_LOAD_REQUESTS 125 // getting friend requests #define FACEBOOK_REQUEST_REQUEST_FRIEND 126 // requesting friends #define FACEBOOK_REQUEST_APPROVE_FRIEND 127 // approving friends diff --git a/protocols/FacebookRM/src/entities.h b/protocols/FacebookRM/src/entities.h index 0f9058c42d..a5e85d23a6 100644 --- a/protocols/FacebookRM/src/entities.h +++ b/protocols/FacebookRM/src/entities.h @@ -31,6 +31,7 @@ struct facebook_user unsigned int status_id; unsigned int gender; + unsigned int last_active; std::string image_url; @@ -41,7 +42,7 @@ struct facebook_user this->handle = NULL; this->user_id = this->real_name = this->image_url = ""; this->status_id = ID_STATUS_OFFLINE; - this->gender = 0; + this->gender = this->last_active = 0; this->deleted = false; } @@ -53,6 +54,7 @@ struct facebook_user this->status_id = fu->status_id; this->user_id = fu->user_id; this->gender = fu->gender; + this->last_active = fu->last_active; this->deleted = fu->deleted; } }; diff --git a/protocols/FacebookRM/src/json.cpp b/protocols/FacebookRM/src/json.cpp index 5ca3496c43..e3ade36a04 100644 --- a/protocols/FacebookRM/src/json.cpp +++ b/protocols/FacebookRM/src/json.cpp @@ -56,10 +56,27 @@ int facebook_json_parser::parse_buddy_list(void* data, List::List< facebook_user for (List::Item< facebook_user >* i = buddy_list->begin(); i != NULL; i = i->next) { i->data->status_id = ID_STATUS_OFFLINE; } + + // Load last active times + const Object& lastActive = objRoot["payload"]["buddy_list"]["last_active_times"]; + for (Object::const_iterator itLastActive(lastActive.Begin()); itLastActive != lastActive.End(); ++itLastActive) + { + const Object::Member& member = *itLastActive; + + current = buddy_list->find(member.name); + if (current == NULL) { + buddy_list->insert(std::make_pair(member.name, new facebook_user())); + current = buddy_list->find(member.name); + current->user_id = member.name; + } + + const Number& timestamp = member.element; + current->last_active = timestamp.Value(); + } - const Array& mobileFriends = objRoot["payload"]["buddy_list"]["mobile_friends"]; // Find mobile friends - for (Array::const_iterator buddy(mobileFriends.Begin()); buddy != mobileFriends.End(); ++buddy) { + const Array& mobileFriends = objRoot["payload"]["buddy_list"]["mobile_friends"]; + for (Array::const_iterator buddy(mobileFriends.Begin()); buddy != mobileFriends.End(); ++buddy) { const Number& member = *buddy; char was_id[32]; lltoa(member.Value(), was_id, 10); @@ -73,13 +90,13 @@ int facebook_json_parser::parse_buddy_list(void* data, List::List< facebook_user current = buddy_list->find(id); current->user_id = id; } - + current->status_id = ID_STATUS_ONTHEPHONE; } } - const Object& nowAvailableList = objRoot["payload"]["buddy_list"]["nowAvailableList"]; // Find now awailable contacts + const Object& nowAvailableList = objRoot["payload"]["buddy_list"]["nowAvailableList"]; for (Object::const_iterator itAvailable(nowAvailableList.Begin()); itAvailable != nowAvailableList.End(); ++itAvailable) { @@ -95,12 +112,12 @@ int facebook_json_parser::parse_buddy_list(void* data, List::List< facebook_user current = buddy_list->find(member.name); current->user_id = current->real_name = member.name; } - + current->status_id = (idle ? ID_STATUS_OFFLINE : ID_STATUS_ONLINE); } - const Object& userInfosList = objRoot["payload"]["buddy_list"]["userInfos"]; // Get aditional informations about contacts (if available) + const Object& userInfosList = objRoot["payload"]["buddy_list"]["userInfos"]; for (Object::const_iterator itUserInfo(userInfosList.Begin()); itUserInfo != userInfosList.End(); ++itUserInfo) { @@ -119,6 +136,7 @@ int facebook_json_parser::parse_buddy_list(void* data, List::List< facebook_user current->image_url = utils::text::slashu_to_utf8( utils::text::special_expressions_decode(imageUrl.Value())); } + } catch (Reader::ParseException& e) { @@ -160,7 +178,10 @@ int facebook_json_parser::parse_friends(void* data, std::map< std::string, faceb const String& realName = objMember["name"]; const String& imageUrl = objMember["thumbSrc"]; //const String& vanity = objMember["vanity"]; + //const String& uri = objMember["uri"]; const Number& gender = objMember["gender"]; + //const Boolean& isFriend = objMember["is_friend"]; + //const String& type = objMember["type"]; // "friend" = existing classic contact, "user" = contact with disabled/deleted account facebook_user *fbu = new facebook_user(); diff --git a/protocols/FacebookRM/src/process.cpp b/protocols/FacebookRM/src/process.cpp index e0608e3555..35aaa52bff 100644 --- a/protocols/FacebookRM/src/process.cpp +++ b/protocols/FacebookRM/src/process.cpp @@ -97,13 +97,20 @@ void FacebookProto::ProcessBuddyList(void* data) fbu->handle = AddToContactList(fbu, FACEBOOK_CONTACT_FRIEND); if (!fbu->real_name.empty()) { - db_set_utf(fbu->handle,m_szModuleName,FACEBOOK_KEY_NAME,fbu->real_name.c_str()); - db_set_utf(fbu->handle,m_szModuleName,FACEBOOK_KEY_NICK,fbu->real_name.c_str()); + db_set_utf(fbu->handle, m_szModuleName, FACEBOOK_KEY_NAME, fbu->real_name.c_str()); + db_set_utf(fbu->handle, m_szModuleName, FACEBOOK_KEY_NICK, fbu->real_name.c_str()); } } - if (db_get_w(fbu->handle,m_szModuleName,"Status", 0) != fbu->status_id) { - db_set_w(fbu->handle,m_szModuleName,"Status", fbu->status_id); + if (db_get_w(fbu->handle, m_szModuleName, "Status", 0) != fbu->status_id) { + db_set_w(fbu->handle, m_szModuleName, "Status", fbu->status_id); + } + + if (db_get_dw(fbu->handle, m_szModuleName, "LastActiveTS", 0) != fbu->last_active) { + if (fbu->last_active > 0) + db_set_dw(fbu->handle, m_szModuleName, "LastActiveTS", fbu->last_active); + else + db_unset(fbu->handle, m_szModuleName, "LastActiveTS"); } if (db_get_b(fbu->handle, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) != FACEBOOK_CONTACT_FRIEND) { @@ -278,145 +285,164 @@ void FacebookProto::ProcessUnreadMessages(void*) { facy.handle_entry("messages"); - std::string get_data = "sk=inbox&query=is%3Aunread"; + int count = 0; + std::string page; - std::string data = "&fb_dtsg=" + facy.dtsg_; - data += "&lsd=&phstamp=" + utils::time::mili_timestamp(); - data += "&__user=" + facy.self_.user_id; + while (count < 30) // allow max 30 unread threads to be parsed + { + std::string get_data = "&page=" + page; - // Get unread inbox threads - http::response resp = facy.flap(FACEBOOK_REQUEST_ASYNC, &data, &get_data); + http::response resp = facy.flap(FACEBOOK_REQUEST_UNREAD_MESSAGES, NULL, &get_data); - // sk=inbox, sk=other + // Process result data + facy.validate_response(&resp); - // Process result data - facy.validate_response(&resp); + if (resp.code == HTTP_CODE_OK) + { + std::string items = utils::text::source_get_value(&resp.data, 2, "
"); - if (resp.code != HTTP_CODE_OK) { - facy.handle_error("messages"); - return; + std::string::size_type pos = 0; + std::string::size_type pos2 = 0; + + while ((pos = items.find("id=\"threadlist_row_", pos)) != std::string::npos) { + std::string item = items.substr(pos, items.find("
", pos) - pos); + pos++; count++; + + std::string tid = utils::text::source_get_value2(&item, "?tid=", "&\""); + if (tid.empty()) + continue; + + std::string* data = new std::string(tid); + ForkThread(&FacebookProto::ProcessUnreadMessage, this, (void*)data); + } + + page = utils::text::source_get_value(&items, 3, "id=\"see_older_threads\"", "page=", "&"); + if (page.empty()) + break; // No more results + } } - std::string threadlist = utils::text::slashu_to_utf8(resp.data); - - std::string::size_type pos = 0; +} - while ((pos = threadlist.find("
  • ", pos); - std::string thread_content = threadlist.substr(pos, pos2 - pos); +void FacebookProto::ProcessUnreadMessage(void *tid_data) +{ + if (tid_data == NULL) + return; - pos = pos2; + std::string* tid = (std::string*)tid_data; - get_data = "sk=inbox&query=is%3Aunread&thread_query=is%3Aunread&action=read&tid="; - get_data += utils::text::source_get_value(&thread_content, 2, "id=\\\"", "\\\""); - utils::text::replace_all(&get_data, "+", "%2B"); - - resp = facy.flap(FACEBOOK_REQUEST_ASYNC, &data, &get_data); - // TODO: move this to new thread... + std::string data = "&fb_dtsg=" + facy.dtsg_; + data += "&lsd=&phstamp=" + utils::time::mili_timestamp(); + data += "&__user=" + facy.self_.user_id; - facy.validate_response(&resp); + std::string get_data = "sk=inbox&query=is%3Aunread&thread_query=is%3Aunread&action=read&tid=" + *tid; + http::response resp = facy.flap(FACEBOOK_REQUEST_ASYNC, &data, &get_data); + // TODO: move this to new thread... - if (resp.code != HTTP_CODE_OK) { - LOG(" !! !! Error when getting messages list"); - continue; - } + facy.validate_response(&resp); + + if (resp.code != HTTP_CODE_OK) { + LOG(" !! !! Error when getting messages list"); + delete tid; + return; + } - std::string messageslist = utils::text::slashu_to_utf8(resp.data); + std::string messageslist = utils::text::slashu_to_utf8(resp.data); - std::string user_id = utils::text::source_get_value(&messageslist, 2, "single_thread_id\":", ","); - if (user_id.empty()) { - LOG(" !! !! Thread id is empty - this is groupchat message."); // TODO: remove as this is not such 'error' - continue; - } - - facebook_user fbu; - fbu.user_id = user_id; - - HANDLE hContact = AddToContactList(&fbu, FACEBOOK_CONTACT_NONE); - // TODO: if contact is newly added, get his user info - // TODO: maybe create new "receiveMsg" function and use it for offline and channel messages? + std::string user_id = utils::text::source_get_value(&messageslist, 2, "single_thread_id\":", ","); + if (user_id.empty()) { + LOG(" !! !! Thread id is empty - this is groupchat message."); // TODO: remove as this is not such 'error' + delete tid; + return; + } - pos2 = 0; - while ((pos2 = messageslist.find("class=\\\"MessagingMessage ", pos2)) != std::string::npos) { - pos2 += 8; - std::string strclass = messageslist.substr(pos2, messageslist.find("\\\"", pos2) - pos2); + facebook_user fbu; + fbu.user_id = user_id; - if (strclass.find("MessagingMessageUnread") == std::string::npos) - continue; // ignoring old messages + HANDLE hContact = AddToContactList(&fbu, FACEBOOK_CONTACT_NONE); + // TODO: if contact is newly added, get his user info + // TODO: maybe create new "receiveMsg" function and use it for offline and channel messages? - //std::string::size_type pos3 = messageslist.find("/li>", pos2); // TODO: ne proti tomuhle li, protože i přílohy mají li... - std::string::size_type pos3 = messageslist.find("class=\\\"MessagingMessage ", pos2); - std::string messagesgroup = messageslist.substr(pos2, pos3 - pos2); + std::string::size_type pos2 = 0; + while ((pos2 = messageslist.find("class=\\\"MessagingMessage ", pos2)) != std::string::npos) { + pos2 += 8; + std::string strclass = messageslist.substr(pos2, messageslist.find("\\\"", pos2) - pos2); - DWORD timestamp = utils::conversion::to_timestamp( - utils::text::source_get_value(&messagesgroup, 2, "data-utime=\\\"", "\\\"")); + if (strclass.find("MessagingMessageUnread") == std::string::npos) + continue; // ignoring old messages - pos3 = 0; - while ((pos3 = messagesgroup.find("class=\\\"content noh", pos3)) != std::string::npos) - { + //std::string::size_type pos3 = messageslist.find("/li>", pos2); // TODO: ne proti tomuhle li, protože i přílohy mají li... + std::string::size_type pos3 = messageslist.find("class=\\\"MessagingMessage ", pos2); + std::string messagesgroup = messageslist.substr(pos2, pos3 - pos2); - std::string message_attachments = ""; - std::string::size_type pos4 = 0; - if ((pos4 = messagesgroup.find("class=\\\"attachments\\\"", pos4)) != std::string::npos) { - std::string attachments = messagesgroup.substr(pos4, messagesgroup.find("<\\/ul", pos4) - pos4); + DWORD timestamp = utils::conversion::to_timestamp( + utils::text::source_get_value(&messagesgroup, 2, "data-utime=\\\"", "\\\"")); - pos4 = 0; - while ((pos4 = attachments.find("", pos4) - pos4); - std::string link = utils::text::source_get_value(&attachment, 4, "", "<\\/a>"); - std::string name = utils::text::trim( - utils::text::special_expressions_decode( - utils::text::remove_html(attachment))); + pos4 = 0; + while ((pos4 = attachments.find("", pos4) - pos4); + std::string link = utils::text::source_get_value(&attachment, 4, " " + FACEBOOK_URL_HOMEPAGE; - message_attachments += link + "\r\n"; + // or first: std::string name = utils::text::source_get_value(&attachment, 4, "", "<\\/a>"); + std::string name = utils::text::trim( + utils::text::special_expressions_decode( + utils::text::remove_html(attachment))); - pos4++; + if (link.find("/ajax/messaging/attachments/photo/dialog.php?uri=") != std::string::npos) { + link = link.substr(49); + link = utils::url::decode(link); } + message_attachments += "< " + name + " > " + FACEBOOK_URL_HOMEPAGE; + message_attachments += link + "\r\n"; + + pos4++; } - std::string message_text = messagesgroup.substr(pos3, messagesgroup.find("<\\/div", pos3) + 6 - pos3); - LOG("Got unread message: \"%s\"", message_text.c_str()); - message_text = utils::text::source_get_value(&message_text, 2, "\\\">", "<\\/div"); - message_text = utils::text::trim( - utils::text::special_expressions_decode( - utils::text::remove_html(message_text))); + } - parseSmileys(message_text, hContact); + std::string message_text = messagesgroup.substr(pos3, messagesgroup.find("<\\/div", pos3) + 6 - pos3); + message_text = utils::text::source_get_value(&message_text, 2, "\\\">", "<\\/div"); + message_text = utils::text::trim( + utils::text::special_expressions_decode( + utils::text::remove_html(message_text))); - if (!message_attachments.empty()) { - if (!message_text.empty()) - message_text += "\r\n\r\n"; + LOG("Got unread message: \"%s\"", message_text.c_str()); - message_text += Translate("Attachments:"); - message_text += "\r\n" + message_attachments; - } + parseSmileys(message_text, hContact); - PROTORECVEVENT recv = {0}; - recv.flags = PREF_UTF; - recv.szMessage = const_cast(message_text.c_str()); - recv.timestamp = timestamp; - ProtoChainRecvMsg(hContact, &recv); + if (!message_attachments.empty()) { + if (!message_text.empty()) + message_text += "\r\n\r\n"; - pos3++; + message_text += Translate("Attachments:"); + message_text += "\r\n" + message_attachments; } + PROTORECVEVENT recv = {0}; + recv.flags = PREF_UTF; + recv.szMessage = const_cast(message_text.c_str()); + recv.timestamp = timestamp; + ProtoChainRecvMsg(hContact, &recv); + + pos3++; } - + } + delete tid; } void FacebookProto::ProcessMessages(void* data) diff --git a/protocols/FacebookRM/src/proto.h b/protocols/FacebookRM/src/proto.h index ae74b7f51c..0616d3c2b0 100644 --- a/protocols/FacebookRM/src/proto.h +++ b/protocols/FacebookRM/src/proto.h @@ -138,6 +138,7 @@ public: void __cdecl ProcessFriendList(void*); void __cdecl ProcessMessages(void*); void __cdecl ProcessUnreadMessages(void*); + void __cdecl ProcessUnreadMessage(void*); void __cdecl ProcessFeeds(void*); void __cdecl ProcessNotifications(void*); void __cdecl ProcessFriendRequests(void*); diff --git a/protocols/FacebookRM/src/version.h b/protocols/FacebookRM/src/version.h index 27839601b8..9c4795d83e 100644 --- a/protocols/FacebookRM/src/version.h +++ b/protocols/FacebookRM/src/version.h @@ -1,7 +1,7 @@ #define __MAJOR_VERSION 0 #define __MINOR_VERSION 0 #define __RELEASE_NUM 9 -#define __BUILD_NUM 5 +#define __BUILD_NUM 6 #define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM #define __FILEVERSION_DOTS __MAJOR_VERSION.__MINOR_VERSION.__RELEASE_NUM.__BUILD_NUM -- cgit v1.2.3