summaryrefslogtreecommitdiff
path: root/protocols/FacebookRM/src
diff options
context:
space:
mode:
authorRobert Pösel <robyer@seznam.cz>2013-09-29 20:47:11 +0000
committerRobert Pösel <robyer@seznam.cz>2013-09-29 20:47:11 +0000
commit7538e4d50ab714f503df3c82c9ef94e90fb49ad9 (patch)
tree6c531d21f0c694a9ced942142784de5297364c1b /protocols/FacebookRM/src
parent2799ac954aa83e0f3a1c57dacadd3a41fa9ba6bb (diff)
Facebook: first experimental implementation of multi user chats (thanks to nobodyreal for patch)
git-svn-id: http://svn.miranda-ng.org/main/trunk@6273 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/FacebookRM/src')
-rw-r--r--protocols/FacebookRM/src/chat.cpp127
-rw-r--r--protocols/FacebookRM/src/client.h3
-rw-r--r--protocols/FacebookRM/src/entities.h12
-rw-r--r--protocols/FacebookRM/src/json.cpp127
-rw-r--r--protocols/FacebookRM/src/messages.cpp14
-rw-r--r--protocols/FacebookRM/src/proto.cpp3
-rw-r--r--protocols/FacebookRM/src/proto.h3
7 files changed, 259 insertions, 30 deletions
diff --git a/protocols/FacebookRM/src/chat.cpp b/protocols/FacebookRM/src/chat.cpp
index cbb52248ac..e0414ae9c7 100644
--- a/protocols/FacebookRM/src/chat.cpp
+++ b/protocols/FacebookRM/src/chat.cpp
@@ -21,6 +21,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
+#include <m_history.h>
+#include <m_userinfo.h>
void FacebookProto::UpdateChat(const char *chat_id, const char *id, const char *name, const char *message, DWORD timestamp)
{
@@ -45,9 +47,15 @@ void FacebookProto::UpdateChat(const char *chat_id, const char *id, const char *
gce.ptszUID = tid;
CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
+
+ std::map<std::string, facebook_chatroom>::iterator chatroom = facy.chat_rooms.find(chat_id);
+ chatroom->second.message_readers = "";
+
+ HANDLE hChatContact = ChatIDToHContact(chat_id);
+ CallService(MS_MSG_SETSTATUSTEXT, (WPARAM)hChatContact, (LPARAM)mir_a2u("Unseen"));
}
-int FacebookProto::OnChatOutgoing(WPARAM wParam,LPARAM lParam)
+int FacebookProto::OnGCEvent(WPARAM wParam,LPARAM lParam)
{
GCHOOK *hook = reinterpret_cast<GCHOOK*>(lParam);
@@ -69,11 +77,61 @@ int FacebookProto::OnChatOutgoing(WPARAM wParam,LPARAM lParam)
break;
}
- case GC_USER_LEAVE:
- case GC_SESSION_TERMINATE:
+ case GC_USER_PRIVMESS:
+ {
+ char* sn = mir_t2a(hook->ptszUID);
+ HANDLE hContact = ContactIDToHContact(sn);
+ mir_free(sn);
+ CallService(MS_MSG_SENDMESSAGET, (WPARAM)hContact, 0);
+
+ break;
+ }
+
+ /*
+ case GC_USER_LOGMENU:
+ {
+ switch(hook->dwData)
+ {
+ case 10:
+ DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHATROOM_INVITE), NULL, invite_to_chat_dialog,
+ LPARAM(new invite_chat_param(item->id, this)));
+ break;
+
+ case 20:
+ //chat_leave(id);
+ break;
+ }
+ break;
+ }
+ */
+
+ case GC_USER_NICKLISTMENU:
{
+ char *sn = mir_t2a(hook->ptszUID);
+ HANDLE hContact = ContactIDToHContact(sn);
+ mir_free(sn);
+
+ switch (hook->dwData)
+ {
+ case 10:
+ CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)hContact, 0);
+ break;
+
+ case 20:
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, (WPARAM)hContact, 0);
+ break;
+
+ case 110:
+ //chat_leave(id);
+ break;
+ }
+
break;
}
+
+ case GC_USER_LEAVE:
+ case GC_SESSION_TERMINATE:
+ break;
}
return 0;
@@ -102,6 +160,10 @@ void FacebookProto::AddChatContact(const char *chat_id, const char *id, const ch
else
gce.ptszStatus = _T("Normal");
+ std::map<std::string, facebook_chatroom>::iterator room = facy.chat_rooms.find(chat_id);
+ if(room != facy.chat_rooms.end())
+ room->second.participants.insert(std::make_pair(id, name));
+
CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
}
@@ -128,6 +190,10 @@ void FacebookProto::RemoveChatContact(const char *chat_id, const char *id)
gce.time = ::time(NULL);
gce.bIsMe = false;//!strcmp(id, facy.self_.user_id.c_str());
+ std::map<std::string, facebook_chatroom>::iterator room = facy.chat_rooms.find(chat_id);
+ if (room != facy.chat_rooms.end())
+ room->second.participants.erase(id);
+
CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
}
@@ -187,6 +253,10 @@ void FacebookProto::AddChat(const char *id, const char *name)
gcd.iType = GC_EVENT_CONTROL;
gce.time = ::time(NULL);
gce.pDest = &gcd;
+
+ facebook_chatroom chatroom;
+ chatroom.chat_name = name;
+ facy.chat_rooms.insert(std::make_pair(id, chatroom));
// Add self contact
AddChatContact(id, facy.self_.user_id.c_str(), facy.self_.real_name.c_str());
@@ -293,4 +363,53 @@ void FacebookProto::SetChatStatus(int status)
CallServiceSync(MS_GC_EVENT,SESSION_OFFLINE,reinterpret_cast<LPARAM>(&gce));
}
}
-*/ \ No newline at end of file
+*/
+
+int FacebookProto::OnGCMenuHook(WPARAM, LPARAM lParam)
+{
+ GCMENUITEMS *gcmi= (GCMENUITEMS*) lParam;
+
+ if (gcmi == NULL || _stricmp(gcmi->pszModule, m_szModuleName)) return 0;
+
+ if (gcmi->Type == MENU_ON_LOG)
+ {
+ static const struct gc_item Items[] =
+ {
+ { LPGENT("&Invite user..."), 10, MENU_ITEM, FALSE },
+ { LPGENT("&Leave chat session"), 20, MENU_ITEM, FALSE }
+ };
+ gcmi->nItems = SIZEOF(Items);
+ gcmi->Item = (gc_item*)Items;
+ }
+ else if (gcmi->Type == MENU_ON_NICKLIST)
+ {
+ char* email = mir_t2a(gcmi->pszUID);
+ if (!_stricmp(facy.self_.user_id.c_str(), email))
+ {
+ /*static const struct gc_item Items[] =
+ {
+ { LPGENT("User &details"), 10, MENU_ITEM, FALSE },
+ { LPGENT("User &history"), 20, MENU_ITEM, FALSE },
+ { _T(""), 100, MENU_SEPARATOR, FALSE },
+ { LPGENT("&Leave chat session"), 110, MENU_ITEM, FALSE }
+ };
+ gcmi->nItems = SIZEOF(Items);
+ gcmi->Item = (gc_item*)Items;*/
+ gcmi->nItems = 0;
+ gcmi->Item = NULL;
+ }
+ else
+ {
+ static const struct gc_item Items[] =
+ {
+ { LPGENT("User &details"), 10, MENU_ITEM, FALSE },
+ { LPGENT("User &history"), 20, MENU_ITEM, FALSE }
+ };
+ gcmi->nItems = SIZEOF(Items);
+ gcmi->Item = (gc_item*)Items;
+ }
+ mir_free(email);
+ }
+
+ return 0;
+}
diff --git a/protocols/FacebookRM/src/client.h b/protocols/FacebookRM/src/client.h
index a14a29b483..41a8b6a1d0 100644
--- a/protocols/FacebookRM/src/client.h
+++ b/protocols/FacebookRM/src/client.h
@@ -95,6 +95,7 @@ public:
std::map<std::string, std::string> cookies;
std::map<std::string, std::string> pages;
+ std::map<std::string, facebook_chatroom> chat_rooms;
std::string get_newsfeed_type();
std::string get_server_type();
@@ -102,7 +103,7 @@ public:
char* load_cookies();
void store_headers(http::response* resp, NETLIBHTTPHEADER* headers, int headers_count);
- void clear_cookies();
+ void clear_cookies();
////////////////////////////////////////////////////////////
diff --git a/protocols/FacebookRM/src/entities.h b/protocols/FacebookRM/src/entities.h
index 20008b2f21..4ad4612985 100644
--- a/protocols/FacebookRM/src/entities.h
+++ b/protocols/FacebookRM/src/entities.h
@@ -59,6 +59,18 @@ struct facebook_user
}
};
+struct facebook_chatroom
+{
+ HANDLE handle;
+
+ std::string chat_name;
+ std::map<std::string, std::string> participants;
+
+ std::string message_readers;
+
+ DWORD last_active;
+};
+
struct facebook_message
{
std::string user_id;
diff --git a/protocols/FacebookRM/src/json.cpp b/protocols/FacebookRM/src/json.cpp
index be1e6232ab..529dd8abd1 100644
--- a/protocols/FacebookRM/src/json.cpp
+++ b/protocols/FacebookRM/src/json.cpp
@@ -418,14 +418,65 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
if (reader == NULL || time == NULL)
continue;
- // TODO: add check for chat contacts
- HANDLE hContact = proto->ContactIDToHContact(json_as_pstring(reader));
- if (hContact) {
- TCHAR ttime[64], tstr[100];
- _tcsftime(ttime, SIZEOF(ttime), _T("%X"), utils::conversion::fbtime_to_timeinfo(json_as_float(time)));
- mir_sntprintf(tstr, SIZEOF(tstr), TranslateT("Message read: %s"), ttime);
-
- CallService(MS_MSG_SETSTATUSTEXT, (WPARAM)hContact, (LPARAM)tstr);
+ JSONNODE *threadid = json_get(it, "tid");
+ if (threadid != NULL) { // multi user chat
+ const char *tid = json_as_string(threadid);
+ const char *reader_id = json_as_string(reader);
+
+ std::map<std::string, facebook_chatroom>::iterator chatroom = proto->facy.chat_rooms.find(tid);
+ if (chatroom != proto->facy.chat_rooms.end()) {
+ std::map<std::string, std::string>::const_iterator participant = chatroom->second.participants.find(reader_id);
+ if (participant == chatroom->second.participants.end()) {
+ std::string search = utils::url::encode(tid) + "?";
+ http::response resp = proto->facy.flap(REQUEST_USER_INFO, NULL, &search);
+
+ if (resp.code == HTTP_CODE_FOUND && resp.headers.find("Location") != resp.headers.end()) {
+ search = utils::text::source_get_value(&resp.headers["Location"], 2, FACEBOOK_SERVER_MOBILE"/", "_rdr", true);
+ resp = proto->facy.flap(REQUEST_USER_INFO, NULL, &search);
+ }
+
+ proto->facy.validate_response(&resp);
+
+ if (resp.code == HTTP_CODE_OK) {
+ std::string about = utils::text::source_get_value(&resp.data, 2, "<div class=\"timeline", "<div id=\"footer");
+
+ std::string name = utils::text::source_get_value(&about, 3, "class=\"profileName", ">", "</");
+ std::string surname;
+
+ std::string::size_type pos;
+ if ((pos = name.find(" ")) != std::string::npos) {
+ surname = name.substr(pos + 1, name.length() - pos - 1);
+ name = name.substr(0, pos);
+ }
+
+ proto->AddChatContact(tid, reader_id, name.c_str());
+ }
+ }
+
+ participant = chatroom->second.participants.find(reader_id);
+ if (participant != chatroom->second.participants.end()) {
+ HANDLE hChatContact = proto->ChatIDToHContact(tid);
+ const char *participant_name = participant->second.c_str();
+
+ if (!chatroom->second.message_readers.empty())
+ chatroom->second.message_readers += ", ";
+ chatroom->second.message_readers += participant_name;
+
+ TCHAR ttime[64], tstr[100];
+ _tcsftime(ttime, SIZEOF(ttime), _T("%X"), utils::conversion::fbtime_to_timeinfo(json_as_float(time)));
+ mir_sntprintf(tstr, SIZEOF(tstr), TranslateT("Message read: %s by %s"), ttime, ptrT(mir_utf8decodeT(chatroom->second.message_readers.c_str())));
+ CallService(MS_MSG_SETSTATUSTEXT, (WPARAM)hChatContact, (LPARAM)tstr);
+ }
+ }
+ } else { // classic contact
+ HANDLE hContact = proto->ContactIDToHContact(json_as_pstring(reader));
+ if (hContact) {
+ TCHAR ttime[64], tstr[100];
+ _tcsftime(ttime, SIZEOF(ttime), _T("%X"), utils::conversion::fbtime_to_timeinfo(json_as_float(time)));
+ mir_sntprintf(tstr, SIZEOF(tstr), TranslateT("Message read: %s"), ttime);
+
+ CallService(MS_MSG_SETSTATUSTEXT, (WPARAM)hContact, (LPARAM)tstr);
+ }
}
} else if (t == "deliver") {
// inbox message (multiuser or direct)
@@ -461,14 +512,58 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
if (message_text.empty())
continue;
- facebook_message* message = new facebook_message();
- message->message_text = message_text;
- message->sender_name = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(json_as_pstring(sender_name)));
- message->time = utils::time::fix_timestamp(json_as_float(timestamp));
- message->user_id = id; // TODO: Check if we have contact with this ID in friendlist and otherwise do something different?
- message->message_id = message_id;
-
- messages->push_back(message);
+ // Multi-user
+ JSONNODE *gthreadinfo = json_get(msg, "group_thread_info");
+ if (gthreadinfo != NULL) {
+ JSONNODE *thread_name = json_get(gthreadinfo, "name");
+ const char* thread_id = json_as_string(tid);
+
+ // RM TODO: better use check if chatroom exists/is in db/is online... no?
+ /// e.g. HANDLE hChatContact = proto->ChatIDToHContact(thread_id); ?
+ if (proto->GetChatUsers(thread_id) == NULL) {
+ // Set thread id (TID) for later.
+ proto->AddChat(thread_id, json_as_string(thread_name));
+ db_set_s(proto->ChatIDToHContact(thread_id), proto->m_szModuleName, FACEBOOK_KEY_TID, thread_id);
+ }
+
+ // This is a recent 5 person listing of participants.
+ JSONNODE *participants_ids = json_get(gthreadinfo, "participant_ids");
+ JSONNODE *participants_names = json_get(gthreadinfo, "participant_names");
+ for (unsigned int n = 0; n < json_size(participants_ids); n++) {
+ JSONNODE *idItr = json_at(participants_ids, n);
+ const char* pId = json_as_string(idItr);
+
+ JSONNODE *nameItr = json_at(participants_names, n);
+ const char* pName = json_as_string(nameItr);
+
+ if (!proto->IsChatContact(thread_id, pId)) {
+ proto->AddChatContact(thread_id, pId, pName);
+ }
+ }
+
+ std::string senderName = json_as_string(sender_name);
+ std::string::size_type pos;
+ /*if ((pos = senderName.find(" ")) != std::string::npos) {
+ senderName = senderName.substr(0, pos);
+ }*/
+
+ // Last fall-back for adding this sender (Incase was not in the participants) - Is this even needed?
+ if (!proto->IsChatContact(thread_id, id.c_str())) {
+ proto->AddChatContact(thread_id, id.c_str(), senderName.c_str());
+ }
+
+ // Update chat with message.
+ proto->UpdateChat(thread_id, id.c_str(), senderName.c_str(), message_text.c_str(), utils::time::fix_timestamp(json_as_float(timestamp)));
+ } else {
+ facebook_message* message = new facebook_message();
+ message->message_text = message_text;
+ message->sender_name = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(json_as_string(sender_name)));
+ message->time = utils::time::fix_timestamp(json_as_float(timestamp));
+ message->user_id = id; // TODO: Check if we have contact with this ID in friendlist and otherwise do something different?
+ message->message_id = message_id;
+
+ messages->push_back(message);
+ }
}
} else if (t == "thread_msg") {
// multiuser message
diff --git a/protocols/FacebookRM/src/messages.cpp b/protocols/FacebookRM/src/messages.cpp
index 298413cec4..5a7dc086ae 100644
--- a/protocols/FacebookRM/src/messages.cpp
+++ b/protocols/FacebookRM/src/messages.cpp
@@ -97,8 +97,10 @@ void FacebookProto::SendChatMsgWorker(void *p)
Log(" Got thread info: %s = %s", data->chat_id.c_str(), tid.c_str());
}
- if (!tid.empty())
- facy.send_message(tid, data->msg, &err_message, MESSAGE_TID);
+ if (!tid.empty()) {
+ if (facy.send_message(tid, data->msg, &err_message, MESSAGE_TID))
+ UpdateChat(data->chat_id.c_str(), facy.self_.user_id.c_str(), facy.self_.real_name.c_str(), data->msg.c_str());
+ }
}
delete data;
@@ -110,7 +112,7 @@ int FacebookProto::SendMsg(HANDLE hContact, int flags, const char *msg)
if (flags & PREF_UNICODE)
msg = mir_utf8encode(msg);
- facy.msgid_ = (facy.msgid_ % 1024)+1;
+ facy.msgid_ = (facy.msgid_ % 1024) + 1;
ForkThread(&FacebookProto::SendMsgWorker, new send_direct(hContact, msg, (HANDLE)facy.msgid_));
return facy.msgid_;
}
@@ -138,15 +140,13 @@ void FacebookProto::SendTypingWorker(void *p)
facy.is_typing_ = (typing->status == PROTOTYPE_SELFTYPING_ON);
SleepEx(2000, true);
- if (!facy.is_typing_ == (typing->status == PROTOTYPE_SELFTYPING_ON))
- {
+ if (!facy.is_typing_ == (typing->status == PROTOTYPE_SELFTYPING_ON)) {
delete typing;
return;
}
DBVARIANT dbv;
- if (!getString(typing->hContact, FACEBOOK_KEY_ID, &dbv))
- {
+ if (!getString(typing->hContact, FACEBOOK_KEY_ID, &dbv)) {
std::string data = "&source=mercury-chat";
data += (typing->status == PROTOTYPE_SELFTYPING_ON ? "&typ=1" : "&typ=0"); // PROTOTYPE_SELFTYPING_OFF
data += "&to=" + std::string(dbv.pszVal);
diff --git a/protocols/FacebookRM/src/proto.cpp b/protocols/FacebookRM/src/proto.cpp
index b07a209a2d..4c122c8f99 100644
--- a/protocols/FacebookRM/src/proto.cpp
+++ b/protocols/FacebookRM/src/proto.cpp
@@ -48,9 +48,10 @@ FacebookProto::FacebookProto(const char* proto_name,const TCHAR* username) :
HookProtoEvent(ME_CLIST_PREBUILDSTATUSMENU, &FacebookProto::OnBuildStatusMenu);
HookProtoEvent(ME_OPT_INITIALISE, &FacebookProto::OnOptionsInit);
- HookProtoEvent(ME_GC_EVENT, &FacebookProto::OnChatOutgoing);
HookProtoEvent(ME_IDLE_CHANGED, &FacebookProto::OnIdleChanged);
HookProtoEvent(ME_TTB_MODULELOADED, &FacebookProto::OnToolbarInit);
+ HookProtoEvent(ME_GC_EVENT, &FacebookProto::OnGCEvent);
+ HookProtoEvent(ME_GC_BUILDMENU, &FacebookProto::OnGCMenuHook);
InitHotkeys();
InitPopups();
diff --git a/protocols/FacebookRM/src/proto.h b/protocols/FacebookRM/src/proto.h
index 65aa316ee5..d3cc040fb2 100644
--- a/protocols/FacebookRM/src/proto.h
+++ b/protocols/FacebookRM/src/proto.h
@@ -146,7 +146,8 @@ public:
int __cdecl OnPreShutdown(WPARAM,LPARAM);
int __cdecl OnPrebuildContactMenu(WPARAM,LPARAM);
int __cdecl OnIdleChanged(WPARAM,LPARAM);
- int __cdecl OnChatOutgoing(WPARAM,LPARAM);
+ int __cdecl OnGCEvent(WPARAM,LPARAM);
+ int __cdecl OnGCMenuHook(WPARAM,LPARAM);
// Loops
bool NegotiateConnection();