diff --git a/telegram-adium/telegram-adium.xcodeproj/project.pbxproj b/telegram-adium/telegram-adium.xcodeproj/project.pbxproj index 6719f1d..e61248b 100644 --- a/telegram-adium/telegram-adium.xcodeproj/project.pbxproj +++ b/telegram-adium/telegram-adium.xcodeproj/project.pbxproj @@ -112,7 +112,6 @@ C4B57BE51B109E6D006997F4 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; C4B57BE81B10D814006997F4 /* configure.ac */ = {isa = PBXFileReference; lastKnownFileType = text; name = configure.ac; path = ../configure.ac; sourceTree = ""; }; C4B57BE91B10D822006997F4 /* Makefile.in */ = {isa = PBXFileReference; lastKnownFileType = text; name = Makefile.in; path = ../Makefile.in; sourceTree = ""; }; - C4B57BEC1B13B2C4006997F4 /* auto-types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "auto-types.h"; path = "../auto/auto-types.h"; sourceTree = ""; }; C4B57BED1B1598BE006997F4 /* telegram-purple */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "telegram-purple"; path = ..; sourceTree = ""; }; C4B57BEF1B1598D4006997F4 /* libtgl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtgl.a; path = ../libs/libtgl.a; sourceTree = ""; }; C4D12DEE1BC534CF00C0F6E1 /* tgp-blist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "tgp-blist.h"; path = "../tgp-blist.h"; sourceTree = ""; }; @@ -268,7 +267,6 @@ C431EB7B1A76C737006521CB /* tgp-chat.c */, C431EB7C1A76C737006521CB /* tgp-chat.h */, C4E5280F1A8A907200C4B915 /* tgp-ft.c */, - C4B57BEC1B13B2C4006997F4 /* auto-types.h */, C4E528101A8A907200C4B915 /* tgp-ft.h */, C479A8001BB69C2100C153DF /* tgp-request.c */, C479A8011BB69C2100C153DF /* tgp-request.h */, diff --git a/telegram-purple.c b/telegram-purple.c index c50605a..dfd343e 100644 --- a/telegram-purple.c +++ b/telegram-purple.c @@ -34,8 +34,6 @@ static void update_secret_chat_typing (struct tgl_state *TLS, struct tgl_secret_ static void update_user_status_handler (struct tgl_state *TLS, struct tgl_user *U); static void update_user_handler (struct tgl_state *TLS, struct tgl_user *U, unsigned flags); static void update_secret_chat_handler (struct tgl_state *TLS, struct tgl_secret_chat *C, unsigned flags); -static void update_chat_handler (struct tgl_state *TLS, struct tgl_chat *C, unsigned flags); -static void update_channel_handler (struct tgl_state *TLS, struct tgl_channel *C, unsigned flags); static void update_on_failed_login (struct tgl_state *TLS); const char *config_dir = "telegram-purple"; @@ -154,37 +152,6 @@ static void update_user_handler (struct tgl_state *TLS, struct tgl_user *user, u } } -static void update_channel_handler (struct tgl_state *TLS, struct tgl_channel *C, unsigned flags) { - debug ("update_channel_handler() (%s)", print_flags_update (flags)); - - PurpleBuddy *buddy = tgp_blist_buddy_find (TLS, C->id); - - if (flags & TGL_UPDATE_CREATED) { - debug ("channel allocated '%s' (%s)", C->title, print_flags_channel (C->flags)); - if (buddy) { - tgp_blist_lookup_add (TLS, C->id, purple_buddy_get_name (buddy)); - if (C->title && strcmp (C->title, purple_buddy_get_name (buddy))) { - purple_blist_alias_buddy (buddy, C->title); - } - purple_prpl_got_user_status (tls_get_pa (TLS), purple_buddy_get_name (buddy), "available", NULL); - } else { - tgp_blist_lookup_add (TLS, C->id, C->title); - } - } else { - if (buddy) { - if (flags & TGL_UPDATE_FLAGS) { - debug ("channel updated flags '%s' to (%s)", C->title, print_flags_channel (C->flags)); - if (C->flags & TGLCHF_LEFT) { - purple_blist_remove_buddy (buddy); - } - } - if (flags & TGL_UPDATE_PHOTO) { - tgp_info_update_photo (buddy, tgl_peer_get (TLS, C->id)); - } - } - } -} - static void update_secret_chat_handler (struct tgl_state *TLS, struct tgl_secret_chat *U, unsigned flags) { debug ("update_secret_chat_handler() (%s)", print_flags_update (flags)); PurpleBuddy *buddy = tgp_blist_buddy_find (TLS, U->id); @@ -231,23 +198,6 @@ static void update_secret_chat_handler (struct tgl_state *TLS, struct tgl_secret } } -static void update_chat_handler (struct tgl_state *TLS, struct tgl_chat *chat, unsigned flags) { - debug ("update_chat_handler() (%s)", print_flags_update(flags)); - - if (flags & TGL_UPDATE_CREATED) { - debug ("new chat '%s' allocated (%s)", chat->title, print_flags_peer (chat->flags)); - tgp_blist_lookup_add (TLS, chat->id, chat->print_title); - } else { - PurpleChat *ch = tgp_blist_chat_find (TLS, chat->id); - if (flags & TGL_UPDATE_TITLE && ch) { - purple_blist_alias_chat (ch, chat->print_title); - } - if (flags & TGL_UPDATE_DELETED && ch) { - purple_blist_remove_chat (ch); - } - } -} - static void update_user_status_handler (struct tgl_state *TLS, struct tgl_user *U) { p2tgl_prpl_got_user_status (TLS, U->id, &U->status); } @@ -300,40 +250,16 @@ static void on_get_dialog_list_done (struct tgl_state *TLS, void *extra, int suc g_warn_if_reached (); continue; } + // our own contact shouldn't show up in our buddy list if (tgl_get_peer_id (UC->id) == tgl_get_peer_id (TLS->our_id)) { continue; } + if (tgl_get_peer_type (UC->id) == TGL_PEER_USER) { if (! (UC->user.flags & TGLUF_DELETED)) { tgp_blist_contact_add (TLS, &UC->user); } - } else if (tgl_get_peer_type (UC->id) == TGL_PEER_CHAT) { - if (purple_account_get_bool (tls_get_data (TLS)->pa, TGP_KEY_JOIN_GROUP_CHATS, TGP_DEFAULT_JOIN_GROUP_CHATS)) { - - PurpleChat *PC = tgp_blist_chat_find (TLS, UC->id); - if (! (UC->chat.flags & TGLCF_LEFT)) { - if (!PC) { - PC = p2tgl_chat_new (TLS, &UC->chat); - purple_blist_add_chat (PC, tgp_blist_group_init ("Telegram Chats"), NULL); - } - } else { - if (PC) { - purple_blist_remove_chat (PC); - } - } - } - } else if (tgl_get_peer_type (UC->id) == TGL_PEER_CHANNEL) { - // unlike the other peer types, deleted and left channels will not show up in the dialogue list - PurpleBuddy *buddy = tgp_blist_buddy_find (TLS, UC->id); - if (! buddy) { - info ("%s is in the dialogue list but not in the buddy list, add the channel", UC->print_name); - buddy = tgp_blist_buddy_new (TLS, UC); - purple_blist_add_buddy (buddy, NULL, tgp_blist_group_init (_("Telegram Channels")), NULL); - tgp_info_update_photo (buddy, UC); - } - purple_prpl_got_user_status (tls_get_pa (TLS), tgp_blist_lookup_purple_name (TLS, UC->id), "available", - NULL); } } @@ -403,14 +329,18 @@ static void start_secret_chat (PurpleBlistNode *node, gpointer data) { } static void create_chat_link_done (struct tgl_state *TLS, void *extra, int success, const char *url) { - tgl_peer_t *C = extra; + tgl_peer_t *P = extra; if (success) { - assert (tgl_get_peer_type (C->id) == TGL_PEER_CHAT); - tgp_chat_show (TLS, &C->chat); + assert (tgl_get_peer_type (P->id) == TGL_PEER_CHAT); + /* + tgp_chat_show (TLS, P); + serv_got_chat_in (tls_get_conn (TLS), tgl_get_peer_id (P->id), "WebPage", PURPLE_MESSAGE_SYSTEM, msg, time(NULL)); + */ char *msg = g_strdup_printf (_("Invite link: %s"), url); - serv_got_chat_in (tls_get_conn (TLS), tgl_get_peer_id (C->id), "WebPage", PURPLE_MESSAGE_SYSTEM, msg, time(NULL)); + + tgp_chat_got_in (TLS, P, P->id, msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); g_free (msg); } else { tgp_notify_on_error_gw (TLS, NULL, success); @@ -535,6 +465,7 @@ static void update_on_ready (struct tgl_state *TLS) { } tgl_do_get_dialog_list (TLS, 200, 0, on_get_dialog_list_done, NULL); + tgl_do_get_channels_dialog_list (TLS, 50, 0, NULL, NULL); tgl_do_update_contact_list (TLS, 0, 0); } @@ -729,16 +660,6 @@ static void tgprpl_chat_invite (PurpleConnection *gc, int id, const char *messag tgl_do_add_user_to_chat (gc_get_tls (gc), chat->id, user->id, 0, tgp_notify_on_error_gw, chat); } -static int tgprpl_send_chat (PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) { - debug ("tgprpl_send_chat()"); - int ret = tgp_msg_send (gc_get_tls (gc), message, TGL_MK_CHAT(id)); - if (ret != 0) { - p2tgl_got_chat_in (gc_get_tls (gc), TGL_MK_CHAT(id), gc_get_tls (gc)->our_id, message, PURPLE_MESSAGE_RECV, - time (NULL)); - } - return ret; -} - static gboolean tgprpl_can_receive_file (PurpleConnection *gc, const char *who) { return TRUE; } diff --git a/telegram-purple.h b/telegram-purple.h index 5240156..4424e90 100644 --- a/telegram-purple.h +++ b/telegram-purple.h @@ -87,6 +87,9 @@ #define TGP_DEFAULT_SEND_READ_NOTIFICATIONS TRUE #define TGP_KEY_SEND_READ_NOTIFICATIONS "send-read-notifications" +#define TGP_DEFAULT_CHANNEL_MEMBERS 500 +#define TGP_KEY_CHANNEL_MEMBERS "channel-member-count" + #define TGP_KEY_RESET_AUTH "reset-authorization" extern const char *pk_path; diff --git a/tgp-2prpl.c b/tgp-2prpl.c index d457a18..531dc32 100644 --- a/tgp-2prpl.c +++ b/tgp-2prpl.c @@ -63,10 +63,21 @@ int p2tgl_status_is_present (PurpleStatus *status) { return !(strcmp (name, "unavailable") == 0 || strcmp (name, "away") == 0); } -void p2tgl_got_chat_in (struct tgl_state *TLS, tgl_peer_id_t chat, tgl_peer_id_t who, const char *message, int flags, - time_t when) { - serv_got_chat_in (tls_get_conn (TLS), tgl_get_peer_id (chat), tgp_blist_lookup_purple_name (TLS, who), flags, - message, when); +void tgp_chat_got_in (struct tgl_state *TLS, tgl_peer_t *chat, tgl_peer_id_t from, const char *message, + int flags, time_t when) { + g_return_if_fail(chat); + if (tgp_chat_show (TLS, chat)) { + + // channel messages in non-megagroups are always sent by the channel itself + if (tgl_get_peer_type (chat->id) == TGL_PEER_CHANNEL && !(chat->channel.flags & TGLCHF_MEGAGROUP)) { + from = chat->id; + } + + serv_got_chat_in (tls_get_conn (TLS), tgl_get_peer_id (chat->id), tgp_blist_lookup_purple_name (TLS, from), + flags, message, when); + } else { + g_warn_if_reached(); + } } void p2tgl_got_im_combo (struct tgl_state *TLS, tgl_peer_id_t who, const char *msg, int flags, time_t when) { @@ -102,11 +113,11 @@ void p2tgl_got_im_combo (struct tgl_state *TLS, tgl_peer_id_t who, const char *m PurpleConversation *p2tgl_find_conversation_with_account (struct tgl_state *TLS, tgl_peer_id_t peer) { int type = PURPLE_CONV_TYPE_IM; - if (tgl_get_peer_type (peer) == TGL_PEER_CHAT) { + if (tgl_get_peer_type (peer) == TGL_PEER_CHAT || tgl_get_peer_type (peer) == TGL_PEER_CHANNEL) { type = PURPLE_CONV_TYPE_CHAT; } - PurpleConversation *conv = purple_find_conversation_with_account (type, tgp_blist_lookup_purple_name (TLS, peer), - tls_get_pa (TLS)); + PurpleConversation *conv = purple_find_conversation_with_account (type, + tgp_blist_lookup_purple_name (TLS, peer), tls_get_pa (TLS)); return conv; } diff --git a/tgp-2prpl.h b/tgp-2prpl.h index 8a8d239..af34881 100644 --- a/tgp-2prpl.h +++ b/tgp-2prpl.h @@ -34,7 +34,7 @@ struct tgl_state *gc_get_tls (PurpleConnection *gc); int p2tgl_status_is_present (PurpleStatus *status); -void p2tgl_got_chat_in (struct tgl_state *TLS, tgl_peer_id_t chat, tgl_peer_id_t who, const char *message, int flags, time_t when); +void tgp_chat_got_in (struct tgl_state *TLS, tgl_peer_t *chat, tgl_peer_id_t who, const char *message, int flags, time_t when); void p2tgl_got_im_combo (struct tgl_state *TLS, tgl_peer_id_t who, const char *msg, int flags, time_t when); void p2tgl_prpl_got_user_status (struct tgl_state *TLS, tgl_peer_id_t user, struct tgl_user_status *status); void p2tgl_conv_add_user (struct tgl_state *TLS, PurpleConversation *conv, int user, char *message, int flags, int new_arrival); diff --git a/tgp-chat.c b/tgp-chat.c index ee5a565..92dbc1f 100644 --- a/tgp-chat.c +++ b/tgp-chat.c @@ -20,25 +20,21 @@ #include "tgp-chat.h" -GHashTable *tgp_chat_info_new (struct tgl_state *TLS, struct tgl_chat *chat) { - gchar *title = g_strdup (chat->print_title); +GHashTable *tgp_chat_info_new (struct tgl_state *TLS, tgl_peer_t *P) { GHashTable *ht = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); - g_hash_table_insert (ht, "subject", title); - g_hash_table_insert (ht, "id", g_strdup_printf ("%d", tgl_get_peer_id (chat->id))); + g_hash_table_insert (ht, "subject", g_strdup (P->print_name)); + g_hash_table_insert (ht, "id", g_strdup_printf ("%d", tgl_get_peer_id (P->chat.id))); + + if (tgl_get_peer_type (P->id) == TGL_PEER_CHANNEL) { + g_hash_table_insert (ht, "last_server_id", g_strdup_printf ("%d", 0)); + } return ht; } -PurpleChat *p2tgl_chat_new (struct tgl_state *TLS, struct tgl_chat *chat) { - return purple_chat_new (tls_get_pa (TLS), chat->title, tgp_chat_info_new (TLS, chat)); -} - -void p2tgl_chat_update (struct tgl_state *TLS, PurpleChat *chat, tgl_peer_id_t id, int admin_id, const char *subject) { - GHashTable *ht = purple_chat_get_components (chat); - - g_hash_table_replace (ht, g_strdup ("id"), g_strdup_printf ("%d", tgl_get_peer_id (id))); - g_hash_table_replace (ht, g_strdup ("subject"), g_strdup (subject)); +PurpleChat *tgp_chat_new (struct tgl_state *TLS, tgl_peer_t *P) { + return purple_chat_new (tls_get_pa (TLS), P->chat.print_title, tgp_chat_info_new (TLS, P)); } int tgp_chat_has_id (PurpleChat *C) { @@ -52,15 +48,30 @@ tgl_peer_id_t tgp_chat_get_id (PurpleChat *C) { return TGL_MK_CHAT(atoi (id)); } -void tgp_chat_on_loaded_chat_full (struct tgl_state *TLS, struct tgl_chat *C) { - PurpleChat *PC = tgp_blist_chat_find (TLS, C->id); - if (!PC) { - PC = p2tgl_chat_new (TLS, C); - if (purple_account_get_bool (tls_get_pa (TLS), TGP_KEY_JOIN_GROUP_CHATS, TGP_DEFAULT_JOIN_GROUP_CHATS)) { - purple_blist_add_chat (PC, tgp_blist_group_init (_("Telegram Chats")), NULL); +static void tgp_chat_blist_store (struct tgl_state *TLS, tgl_peer_t *P, const char *group) { + g_return_if_fail(tgl_get_peer_type (P->id) == TGL_PEER_CHAT || tgl_get_peer_type (P->id) == TGL_PEER_CHANNEL); + + PurpleChat *PC = tgp_blist_chat_find (TLS, P->id); + if (! (P->flags & TGLCF_LEFT)) { + if (! PC) { + PC = tgp_chat_new (TLS, tgl_peer_get (TLS, P->id)); + if (purple_account_get_bool (tls_get_pa (TLS), TGP_KEY_JOIN_GROUP_CHATS, TGP_DEFAULT_JOIN_GROUP_CHATS)) { + purple_blist_add_chat (PC, tgp_blist_group_init (group), NULL); + tgp_info_update_photo (&PC->node, tgl_peer_get (TLS, P->id)); + } + } + } else { + if (PC) { + purple_blist_remove_chat (PC); } } - p2tgl_chat_update (TLS, PC, C->id, C->admin_id, C->print_title); + + if (PC) { + g_hash_table_replace (purple_chat_get_components (PC), g_strdup ("id"), + g_strdup_printf ("%d", tgl_get_peer_id (P->id))); + g_hash_table_replace (purple_chat_get_components (PC), g_strdup ("subject"), + g_strdup (tgl_get_peer_type (P->id) == TGL_PEER_CHANNEL ? P->channel.title : P->chat.title)); + } } static void tgp_chat_on_loaded_chat_full_joining (struct tgl_state *TLS, void *_, int success, struct tgl_chat *C) { @@ -70,58 +81,117 @@ static void tgp_chat_on_loaded_chat_full_joining (struct tgl_state *TLS, void *_ return; } - tgp_chat_on_loaded_chat_full (TLS, C); - tgp_chat_show (TLS, C); - + tgp_chat_blist_store (TLS, tgl_peer_get (TLS, C->id), _("Telegram Chats")); + tgp_chat_show (TLS, tgl_peer_get (TLS, C->id)); + // Check if the users attempts to join an empty chat - if (! C->user_list_size) { - p2tgl_got_chat_in (TLS, C->id, C->id, _("You have already left this chat."), PURPLE_MESSAGE_SYSTEM, time (NULL)); + if (C->flags & TGLCF_LEFT) { + tgp_chat_got_in (TLS, tgl_peer_get (TLS, C->id), C->id, _("You have already left this chat."), PURPLE_MESSAGE_SYSTEM, + time (NULL)); } } -static void tgp_chat_add_all_users (struct tgl_state *TLS, PurpleConversation *conv, struct tgl_chat *C) { +static void tgp_chat_on_loaded_channel_full_joining (struct tgl_state *TLS, int success, tgl_peer_t *P, void *extra) { + debug ("tgp_chat_on_loaded_channel_full_joining()"); + if (! success) { + tgp_notify_on_error_gw (TLS, NULL, success); + return; + } + + tgp_chat_blist_store (TLS, P, _("Telegram Channels")); + tgp_chat_show (TLS, P); +} + +static void tgp_chat_add_all_users (struct tgl_state *TLS, PurpleConversation *conv, tgl_peer_t *P) { + debug ("tgp_chat_add_all_users()"); + GList *users = NULL, *flags = NULL; - debug ("tgp_chat_add_all_users()"); - - int i = 0; - for (; i < C->user_list_size; i++) { - struct tgl_chat_user *uid = (C->user_list + i); - const char *name = tgp_blist_lookup_purple_name (TLS, TGL_MK_USER(uid->user_id)); - if (! name) { - g_warn_if_reached(); - continue; + + switch (tgl_get_peer_type (P->id)) { + case TGL_PEER_CHAT: { + struct tgl_chat *C = &P->chat; + + int i; + for (i = 0; i < C->user_list_size; i ++) { + struct tgl_chat_user *uid = (C->user_list + i); + const char *name = tgp_blist_lookup_purple_name (TLS, TGL_MK_USER(uid->user_id)); + if (name) { + users = g_list_append (users, g_strdup (name)); + flags = g_list_append (flags, GINT_TO_POINTER(C->admin_id == uid->user_id ? PURPLE_CBFLAGS_FOUNDER : PURPLE_CBFLAGS_NONE)); + } + } + break; } - users = g_list_append (users, g_strdup (name)); - flags = g_list_append (flags, GINT_TO_POINTER(C->admin_id == uid->user_id ? PURPLE_CBFLAGS_FOUNDER : PURPLE_CBFLAGS_NONE)); + + case TGL_PEER_CHANNEL: { + // fetch users + GList *MS = g_hash_table_lookup (tls_get_data (TLS)->channel_members, GINT_TO_POINTER(tgl_get_peer_id (P->id))); + + while (MS) { + struct tgp_channel_member *M = MS->data; + const char *name = tgp_blist_lookup_purple_name (TLS, M->id); + if (name) { + users = g_list_append (users, g_strdup (name)); + flags = g_list_append (flags, GINT_TO_POINTER(M->flags)); + } + MS = g_list_next (MS); + }; + break; + } + + default: + g_return_if_reached(); + break; } + purple_conv_chat_add_users (PURPLE_CONV_CHAT(conv), users, NULL, flags, FALSE); g_list_free_full (users, g_free); g_list_free (flags); } -PurpleConversation *tgp_chat_show (struct tgl_state *TLS, struct tgl_chat *C) { +PurpleConversation *tgp_chat_show (struct tgl_state *TLS, tgl_peer_t *P) { PurpleConvChat *chat = NULL; // check if chat is already shown - PurpleConversation *conv = purple_find_chat (tls_get_conn (TLS), tgl_get_peer_id (C->id)); + PurpleConversation *conv = purple_find_chat (tls_get_conn (TLS), tgl_get_peer_id (P->id)); if (conv) { chat = purple_conversation_get_chat_data (conv); if (chat && ! purple_conv_chat_has_left (chat)) { return conv; } } - + // join the chat now - conv = serv_got_joined_chat (tls_get_conn (TLS), tgl_get_peer_id (C->id), C->print_title); + const char *name = NULL; + if (tgl_get_peer_type (P->id) == TGL_PEER_CHAT) { + name = P->chat.print_title; + } else if (tgl_get_peer_type (P->id) == TGL_PEER_CHANNEL) { + name = P->channel.print_title; + } + g_return_val_if_fail(name, NULL); + + conv = serv_got_joined_chat (tls_get_conn (TLS), tgl_get_peer_id (P->id), name); g_return_val_if_fail(conv, NULL); - + purple_conv_chat_clear_users (purple_conversation_get_chat_data (conv)); - tgp_chat_add_all_users (TLS, conv, C); - + tgp_chat_add_all_users (TLS, conv, P); + return conv; } +int tgprpl_send_chat (PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) { + debug ("tgprpl_send_chat()"); + + tgl_peer_t *P = tgl_peer_get (gc_get_tls (gc), TGL_MK_CHAT(id)); + if (! P) { + P = tgl_peer_get (gc_get_tls (gc), TGL_MK_CHANNEL(id)); + } + g_return_val_if_fail(P != NULL, -1); + + return tgp_msg_send (gc_get_tls (gc), message, P->id); +} + GList *tgprpl_chat_join_info (PurpleConnection *gc) { struct proto_chat_entry *pce; pce = g_new0 (struct proto_chat_entry, 1); @@ -149,8 +219,7 @@ GHashTable *tgprpl_chat_info_defaults (PurpleConnection *gc, const char *chat_na if (chat_name) { tgl_peer_t *P = tgl_peer_get_by_name (gc_get_tls (gc), chat_name); if (P) { - debug ("found chat..."); - return tgp_chat_info_new (gc_get_tls (gc), &P->chat); + return tgp_chat_info_new (gc_get_tls (gc), P); } warning ("Chat not found, returning empty defaults..."); } @@ -176,13 +245,24 @@ void tgprpl_chat_join (PurpleConnection *gc, GHashTable *data) { // join existing chat by id when the user clicks on a chat in the buddy list void *value = g_hash_table_lookup (data, "id"); if (value && atoi (value)) { - tgl_peer_id_t cid = TGL_MK_CHAT(atoi (value)); - tgl_peer_t *P = tgl_peer_get (gc_get_tls (gc), cid); + tgl_peer_t *P = tgl_peer_get (gc_get_tls (gc), TGL_MK_CHAT(atoi (value))); + + if (! P) { + P = tgl_peer_get (gc_get_tls (gc), TGL_MK_CHANNEL(atoi (value))); + } + if (P) { - debug ("joining chat by id %d ...", tgl_get_peer_id (cid)); - tgl_do_get_chat_info (gc_get_tls (gc), cid, FALSE, tgp_chat_on_loaded_chat_full_joining, NULL); + debug ("type=%d", tgl_get_peer_type (P->id)); + if (tgl_get_peer_type (P->id) == TGL_PEER_CHAT) { + debug ("joining chat by id %d ...", tgl_get_peer_id (P->id)); + tgl_do_get_chat_info (gc_get_tls (gc), P->id, FALSE, tgp_chat_on_loaded_chat_full_joining, NULL); + } else { + g_return_if_fail(tgl_get_peer_type (P->id) == TGL_PEER_CHANNEL); + debug ("joining channel by id %d ...", tgl_get_peer_id (P->id)); + tgp_chat_load_channel_members (gc_get_tls (gc), P, tgp_chat_on_loaded_channel_full_joining, NULL); + } } else { - warning ("Cannot join chat %d, peer not found...", tgl_get_peer_id (cid)); + warning ("Cannot join chat %d, peer not found...", tgl_get_peer_id (P->id)); purple_serv_got_join_chat_failed (gc, data); } return; @@ -199,17 +279,25 @@ void tgprpl_chat_join (PurpleConnection *gc, GHashTable *data) { const char *subject = g_hash_table_lookup (data, "subject"); if (str_not_empty (subject)) { tgl_peer_t *P = tgl_peer_get_by_name (gc_get_tls (gc), subject); - - // handle joining chats by print_names as used by the Adium plugin - if (P && tgl_get_peer_type (P->id) == TGL_PEER_CHAT) { - debug ("joining chat by subject %s ...", subject); - - tgl_do_get_chat_info (gc_get_tls (gc), P->id, FALSE, tgp_chat_on_loaded_chat_full_joining, NULL); + if (! P) { + // user creates a new chat by providing its subject the chat join window + request_create_chat (gc_get_tls (gc), subject); return; } - // user creates a new chat by providing its subject the chat join window - request_create_chat (gc_get_tls (gc), subject); + // handle joining chats by print_names as used by the Adium plugin + if (tgl_get_peer_type (P->id) == TGL_PEER_CHAT) { + debug ("joining chat by subject %s ...", subject); + tgl_do_get_chat_info (gc_get_tls (gc), P->id, FALSE, tgp_chat_on_loaded_chat_full_joining, NULL); + return; + } else if (tgl_get_peer_type (P->id) == TGL_PEER_CHANNEL) { + debug ("joining channel by subject %s ...", subject); + tgp_chat_load_channel_members (gc_get_tls (gc), P, tgp_chat_on_loaded_channel_full_joining, NULL); + return; + } else { + warning ("Cannot join chat %s, wrong peer type", subject); + purple_serv_got_join_chat_failed (gc, data); + } } } @@ -283,3 +371,109 @@ void tgp_chat_join_all_pending (struct tgl_state *TLS) { tls_get_data (TLS)->pending_joins = NULL; } } + +static void tgp_channel_load_admins_done (struct tgl_state *TLS, void *extra, int success, int users_num, + struct tgl_user **users) { + debug ("tgp_channel_load_admins_done()"); + + struct tgp_channel_members_loading *D = extra; + + if (success) { + GHashTable *HT = g_hash_table_new (g_direct_hash, g_direct_equal); + + int i; + for (i = 0; i < users_num; i ++) { + g_hash_table_insert (HT, GINT_TO_POINTER(tgl_get_peer_id (users[i]->id)), GINT_TO_POINTER(1)); + } + + GList *MS = D->members; + do { + struct tgp_channel_member *M = MS->data; + if (g_hash_table_lookup (HT, GINT_TO_POINTER(tgl_get_peer_id (M->id)))) { + M->flags |= PURPLE_CBFLAGS_OP; + } + } while ((MS = g_list_next (MS))); + g_hash_table_insert (tls_get_data (TLS)->channel_members, GINT_TO_POINTER(tgl_get_peer_id (D->P->id)), MS); + + g_hash_table_destroy (HT); + } + + D->callback (TLS, success, D->P, D->extra); + free (D); +} + +static void tgp_channel_load_members_done (struct tgl_state *TLS, void *extra, int success, int users_num, + struct tgl_user **users) { + debug ("tgp_channel_load_members_done()"); + + struct tgp_channel_members_loading *D = extra; + + if (! success) { + D->callback (TLS, FALSE, D->P, NULL); + free (D); + return; + } + + int i; + for (i = 0; i < users_num; i ++) { + struct tgp_channel_member *M = talloc0 (sizeof(struct tgp_channel_member)); + M->id = users[i]->id; + D->members = g_list_append (D->members, M); + } + + if (D->P->flags & TGLCHF_ADMIN) { + tgl_do_channel_get_members (TLS, D->P->id, + purple_account_get_int (tls_get_pa (TLS), TGP_KEY_CHANNEL_MEMBERS, TGP_DEFAULT_CHANNEL_MEMBERS), + 0, 1, tgp_channel_load_admins_done, D); + } else { + g_hash_table_insert (tls_get_data (TLS)->channel_members, GINT_TO_POINTER(tgl_get_peer_id (D->P->id)), + D->members); + D->callback (TLS, success, D->P, D->extra); + free (D); + } +} + +void tgp_chat_load_channel_members (struct tgl_state *TLS, tgl_peer_t *P, + void (*callback) (struct tgl_state *TLS, int success, tgl_peer_t *P, void *extra), void *extra) { + + struct tgp_channel_members_loading *D = talloc0 (sizeof(struct tgp_channel_members_loading)); + D->P = P; + D->callback = callback; + D->remaining = 2; + D->extra = extra; + + tgl_do_channel_get_members (TLS, P->id, + purple_account_get_int (tls_get_pa (TLS), TGP_KEY_CHANNEL_MEMBERS, TGP_DEFAULT_CHANNEL_MEMBERS), + 0, 0, tgp_channel_load_members_done, D); +} + +static void update_chat (struct tgl_state *TLS, tgl_peer_t *C, unsigned flags, const char *group) { + if (flags & TGL_UPDATE_CREATED) { + tgp_blist_lookup_add (TLS, C->id, C->print_name); + tgp_chat_blist_store (TLS, tgl_peer_get (TLS, C->id), group); + + } else { + PurpleChat *PC = tgp_blist_chat_find (TLS, C->id); + if (PC) { + if (flags & TGL_UPDATE_TITLE) { + purple_blist_alias_chat (PC, C->print_name); + } + if (flags & TGL_UPDATE_DELETED) { // TODO: test if this is actually executed on deletion + purple_blist_remove_chat (PC); + } + } + } +} + +void update_channel_handler (struct tgl_state *TLS, struct tgl_channel *C, unsigned flags) { + debug ("update_channel_handler() (%s)", print_flags_update (flags)); + + update_chat (TLS, tgl_peer_get (TLS, C->id), flags, _("Telegram Channels")); +} + +void update_chat_handler (struct tgl_state *TLS, struct tgl_chat *C, unsigned flags) { + debug ("update_chat_handler() (%s)", print_flags_update (flags)); + + update_chat (TLS, tgl_peer_get (TLS, C->id), flags, _("Telegram Chats")); +} + diff --git a/tgp-chat.h b/tgp-chat.h index cb9f4d3..0e6986c 100644 --- a/tgp-chat.h +++ b/tgp-chat.h @@ -23,14 +23,26 @@ #include "telegram-purple.h" -PurpleChat *p2tgl_chat_new (struct tgl_state *TLS, struct tgl_chat *chat); +struct tgp_channel_member { + tgl_peer_id_t id; + int flags; +}; + +struct tgp_channel_members_loading { + tgl_peer_t *P; + GList *members; + void (*callback) (struct tgl_state *TLS, int success, tgl_peer_t *P, void *extra); + void *extra; + int remaining; +}; + +PurpleChat *tgp_chat_new (struct tgl_state *TLS, tgl_peer_t *P); tgl_peer_id_t tgp_chat_get_id (PurpleChat *C); int tgp_chat_has_id (PurpleChat *C); -void tgp_chat_on_loaded_chat_full (struct tgl_state *TLS, struct tgl_chat *C); -PurpleConversation *tgp_chat_show (struct tgl_state *TLS, struct tgl_chat *C); - +PurpleConversation *tgp_chat_show (struct tgl_state *TLS, tgl_peer_t *P); +int tgprpl_send_chat (PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags); char *tgprpl_get_chat_name (GHashTable *data); void tgprpl_chat_join (PurpleConnection *gc, GHashTable *data); GList *tgprpl_chat_join_info (PurpleConnection *gc); @@ -38,4 +50,10 @@ PurpleRoomlist *tgprpl_roomlist_get_list (PurpleConnection *gc); void tgprpl_roomlist_cancel (PurpleRoomlist *list); GHashTable *tgprpl_chat_info_defaults (PurpleConnection *gc, const char *chat_name); void tgp_chat_join_all_pending (struct tgl_state *TLS); + +void tgp_chat_load_channel_members (struct tgl_state *TLS, tgl_peer_t *P, + void (*callback) (struct tgl_state *TLS, int success, tgl_peer_t *P, void *extra), void *extra); +void update_channel_handler (struct tgl_state *TLS, struct tgl_channel *C, unsigned flags); +void update_chat_handler (struct tgl_state *TLS, struct tgl_chat *C, unsigned flags); + #endif diff --git a/tgp-msg.c b/tgp-msg.c index 970158d..9db1720 100644 --- a/tgp-msg.c +++ b/tgp-msg.c @@ -105,7 +105,7 @@ static char *format_service_msg (struct tgl_state *TLS, struct tgl_message *M) { // make sure that the chat is showing before deleting the user, otherwise the chat will be // initialised after removing the chat and the chat will still contain the deleted user - PurpleConversation *conv = tgp_chat_show (TLS, &chatPeer->chat); + PurpleConversation *conv = tgp_chat_show (TLS, chatPeer); if (conv) { char *alias = peer->print_name; const char *aliasLeft = tgp_blist_lookup_purple_name (TLS, TGL_MK_USER (M->action.user)); @@ -214,7 +214,8 @@ static gboolean tgp_msg_send_schedule_cb (gpointer data) { g_queue_pop_head (conn->out_messages); unsigned long long flags = 0; - if (tgl_get_peer_type (D->to) == TGL_PEER_CHANNEL) { + if (tgl_get_peer_type (D->to) == TGL_PEER_CHANNEL + && !(tgl_peer_get (conn->TLS, D->to)->channel.flags & TGLCHF_MEGAGROUP)) { flags |= TGLMF_POST_AS_CHANNEL; } // TODO: option for disable_msg_preview @@ -248,17 +249,12 @@ static int tgp_msg_send_split (struct tgl_state *TLS, const char *message, tgl_p start = end; } - // Prevent client from automatically printing the outgoing message. Channel messages are always printed by the - // channel user, not by ourselves to emulate behavior similar to the other Telegram clients. - if (tgl_get_peer_type (to) == TGL_PEER_CHANNEL) { - return 0; - } return 1; } void tgp_msg_special_out (struct tgl_state *TLS, const char *msg, tgl_peer_id_t to_id, int flags) { if (tgl_get_peer_type (to_id) == TGL_PEER_CHAT) { - p2tgl_got_chat_in (TLS, to_id, to_id, msg, flags, time(0)); + tgp_chat_got_in (TLS, tgl_peer_get (TLS, to_id), to_id, msg, flags, time(0)); } else { // Regular IM conversations will not display specialized message flags like PURPLE_MESSAGE_ERROR or // PURPLE_MESSAGE_SYSTEM correctly when using serv_got_in, therefore it is necessary to use the underlying @@ -419,20 +415,12 @@ static void tgp_msg_display (struct tgl_state *TLS, struct tgp_msg_loading *C) { } // TODO: return 0 for all messages, so this isn't necessary and rely on this code to display all outgoing messages - if (tgp_outgoing_msg (TLS, M) && tgl_get_peer_type (M->to_id) != TGL_PEER_CHANNEL) { + if (tgp_outgoing_msg (TLS, M) + && tgl_get_peer_type (M->to_id) != TGL_PEER_CHANNEL + && tgl_get_peer_type (M->to_id) != TGL_PEER_CHAT) { return; } -#ifdef __ADIUM_ - /* Adium ignores when this plugin returns 0 in prpl_send_im, even though that flag indicates that an outgoing - message shouldn't be printed to the conversation. This will still look messy, cause some posts will show up - with the current user's name, while external post will show up with the actual channel name, but there is no - way to prevent outgoing messages in Adium. */ - if (tgp_outgoing_msg (TLS, M)) { - return; - } -#endif - // filter empty or messages with invalid recipients if (! M->message || !tgl_get_peer_type (M->to_id)) { return; @@ -562,17 +550,11 @@ static void tgp_msg_display (struct tgl_state *TLS, struct tgp_msg_loading *C) { flags |= PURPLE_MESSAGE_RECV; } - if (tgl_get_peer_type (M->to_id) != TGL_PEER_ENCR_CHAT && ! (M->flags & TGLMF_UNREAD)) { + if (tgl_get_peer_type (M->to_id) != TGL_PEER_ENCR_CHAT + && tgl_get_peer_type (M->to_id) != TGL_PEER_CHANNEL + && ! (M->flags & TGLMF_UNREAD)) { flags |= PURPLE_MESSAGE_DELAYED; } - -#ifdef __ADIUM_ - /* (oh boy, here we go again...) Adium will print DELAYED messages with a custom layout, which causes the - already fucked-up layout (see ifdef __ADIUM_ above) to be even more confusing. */ - if (! (M->flags & TGLMF_UNREAD) && tgl_get_peer_type (M->to_id) == TGL_PEER_CHANNEL) { - flags ^= PURPLE_MESSAGE_DELAYED; - } -#endif // some service messages (like removing/adding users from chats) might print the message // text through other means and leave the text empty @@ -619,15 +601,26 @@ static void tgp_msg_display (struct tgl_state *TLS, struct tgp_msg_loading *C) { // display the message to the user switch (tgl_get_peer_type (M->to_id)) { - case TGL_PEER_CHAT: { + case TGL_PEER_CHANNEL: { tgl_peer_t *P = tgl_peer_get (TLS, M->to_id); g_return_if_fail(P != NULL); - if (tgp_chat_show (TLS, &P->chat)) { - p2tgl_got_chat_in (TLS, M->to_id, M->from_id, text, flags, M->date); + if (P->channel.flags & TGLCHF_MEGAGROUP) { + // sender is the group + tgp_chat_got_in (TLS, P, M->from_id, text, flags, M->date); + } else { + + // sender is the channel itself + tgp_chat_got_in (TLS, P, P->id, text, flags, M->date); } break; } + case TGL_PEER_CHAT: { + tgl_peer_t *P = tgl_peer_get (TLS, M->to_id); + g_return_if_fail(P != NULL); + tgp_chat_got_in (TLS, P, M->from_id, text, flags, M->date); + break; + } case TGL_PEER_ENCR_CHAT: { p2tgl_got_im_combo (TLS, M->to_id, text, flags, M->date); break; @@ -642,9 +635,6 @@ static void tgp_msg_display (struct tgl_state *TLS, struct tgp_msg_loading *C) { } break; } - case TGL_PEER_CHANNEL: - p2tgl_got_im_combo (TLS, M->to_id, text, flags, M->date); - break; } g_free (text); } @@ -701,19 +691,33 @@ static void tgp_msg_on_loaded_document (struct tgl_state *TLS, void *extra, int static void tgp_msg_on_loaded_chat_full (struct tgl_state *TLS, void *extra, int success, struct tgl_chat *chat) { debug ("tgp_msg_on_loaded_chat_full()"); - - tgp_chat_on_loaded_chat_full (TLS, chat); - struct tgp_msg_loading *C = extra; - + if (! success) { // foreign user's names won't be displayed in the user list g_warn_if_reached(); } - + + struct tgp_msg_loading *C = extra; -- C->pending; + tgp_msg_process_in_ready (TLS); } +static void tgp_msg_on_loaded_channel_members (struct tgl_state *TLS, int success, tgl_peer_t *P, void *extra) { + debug ("tgp_msg_on_loaded_channel_members()"); + + if (! success) { + // user names won't be available in the channel + g_warn_if_reached(); + } + + struct tgp_msg_loading *C = extra; + -- C->pending; + + tgp_msg_process_in_ready (TLS); +} + + /* static void tgp_msg_on_loaded_user_full (struct tgl_state *TLS, void *extra, int success, struct tgl_user *U) { debug ("tgp_msg_on_loaded_user_full()"); @@ -796,26 +800,39 @@ void tgp_msg_recv (struct tgl_state *TLS, struct tgl_message *M) { } */ - if (tgl_get_peer_type (M->to_id) == TGL_PEER_CHAT) { - - tgl_peer_t *P = tgl_peer_get (TLS, M->to_id); - g_warn_if_fail(P); - if (P && ! P->chat.user_list_size) { - // To display a chat the full name of every single user is needed, but the updates received from the server only - // contain the names of users mentioned in the events. In order to display a messages we always need to fetch the - // full chat info first. If the user list is empty, this means that we still haven't fetched the full chat information. + // To display a chat the full name of every single user is needed, but the updates received from the server only + // contain the names of users mentioned in the events. In order to display a messages we always need to fetch the + // full chat info first. If the user list is empty, this means that we still haven't fetched the full chat information. + // assure that there is only one chat info request for every + // chat to avoid causing FLOOD_WAIT_X errors that will lead to delays or dropped messages + gpointer to_ptr = GINT_TO_POINTER(tgl_get_peer_id (M->to_id)); + + if (! g_hash_table_lookup (conn->pending_chat_info, to_ptr)) { + + if (tgl_get_peer_type (M->to_id) == TGL_PEER_CHAT) { + tgl_peer_t *P = tgl_peer_get (TLS, M->to_id); + g_warn_if_fail(P); - // assure that there is only one chat info request for every - // chat to avoid causing FLOOD_WAIT_X errors that will lead to delays or dropped messages - gpointer to_ptr = GINT_TO_POINTER(tgl_get_peer_id (M->to_id)); - - if (! g_hash_table_lookup (conn->pending_chat_info, to_ptr)) { + if (P && ! P->chat.user_list_size) { ++ C->pending; tgl_do_get_chat_info (TLS, M->to_id, FALSE, tgp_msg_on_loaded_chat_full, C); g_hash_table_replace (conn->pending_chat_info, to_ptr, to_ptr); } } + + if (tgl_get_peer_type (M->to_id) == TGL_PEER_CHANNEL) { + tgl_peer_t *P = tgl_peer_get (TLS, M->to_id); + g_warn_if_fail(P); + + // FIXME: check if the types are actually valid + if (P && ((P->channel.flags & (TGLCHF_ADMIN | TGLCHF_CREATOR)) || (P->channel.flags & TGLCHF_MEGAGROUP))) { + ++ C->pending; + + tgp_chat_load_channel_members (TLS, P, tgp_msg_on_loaded_channel_members, C); + g_hash_table_replace (conn->pending_chat_info, to_ptr, to_ptr); + } + } } g_queue_push_tail (conn->new_messages, C); diff --git a/tgp-structs.c b/tgp-structs.c index e90b5ab..23bd001 100644 --- a/tgp-structs.c +++ b/tgp-structs.c @@ -104,6 +104,8 @@ connection_data *connection_data_init (struct tgl_state *TLS, PurpleConnection * conn->pending_chat_info = g_hash_table_new (g_direct_hash, g_direct_equal); conn->id_to_purple_name = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); conn->purple_name_to_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + conn->channel_members = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (void (*) (gpointer)) g_list_free); + return conn; } @@ -120,6 +122,8 @@ void *connection_data_free (connection_data *conn) { g_hash_table_destroy (conn->pending_chat_info); g_hash_table_destroy (conn->id_to_purple_name); g_hash_table_destroy (conn->purple_name_to_id); + g_hash_table_destroy (conn->channel_members); + tgprpl_xfer_free_all (conn); g_free (conn->TLS->base_path); tgl_free_all (conn->TLS); diff --git a/tgp-structs.h b/tgp-structs.h index 2468090..6d1b6d0 100644 --- a/tgp-structs.h +++ b/tgp-structs.h @@ -46,6 +46,7 @@ typedef struct { GHashTable *pending_chat_info; GHashTable *id_to_purple_name; GHashTable *purple_name_to_id; + GHashTable *channel_members; GList *pending_joins; int dialogues_ready; } connection_data;