Pidgin 中 MSN 不显示群成员名字的问题

最新的补丁下载:[pidgin_msn_group_member_nick.diff]

最近用 MSN 群比较多一点,发现在看不到群中发言人的名字,只是显示出这个群本身的名字,非常不方便。那就解决之。

首先要弄明白的是 MSN 群的机制是什么,看起来应该是群所对应的那个账号是一个机器人,成员向它发送的消息自动转发给所有成员,本质上还是即时消息(IM)而非聊天(CHAT)。第一步是要找到群转发给我们的消息中哪个是发言人的名字。Debug Window 里显示出来的是:

  1. (19:05:37) msn: C: SB 001: MSG 52 A 170
  2. (19:05:38) msn: S: SB 002: MSG groupXXXXX@msnzone.cn M群-XXXXX 139

似乎没有把具体的信息给打出来,研究了一番以后发现是在 cmdproc.c:78:show_debug_cmd() 中打出来的。说明命令是 MSG。基本上可以确定调用如下:

  1. // 以下行号以 2008-10-03 3e5468581850f86ee2b706cd57dfe1096ddcc0ac 版本为准
  2. servconn.c:read_cb():456
  3.   cmdproc.c:msn_cmdproc_process_cmd_text():336 // 调用 show_debug_cmd() 打出相应信息
  4.   cmdproc.c:msn_cmdproc_process_cmd_text():343
  5.     // 调用相应的回调函数,在这里就是 switchboard:msn_switchboard_init():1328 所定义的 msg_cmd()
  6.     cmdproc.c:msn_cmdproc_process_cmd():321
  7.       switchboard.c:msg_cmd():758 // 设置 payload 大小并将 payload 回调函数设置为 msg_cmd_post()
  8.  
  9. // 然后处理 payload
  10. servconn.c:read_cb():451
  11.   cmdproc.c:msn_cmdproc_process_payload():231 // 调用命令的处理回调函数
  12.     switchboard.c:msg_cmd_post():737 // 解析并处理消息
  13.       cmdproc.c:msn_cmdproc_process_msg:256 // 根据 content type 找到回调函数

由于还不太清楚具体的消息是什么,所以还并不知道具体的 content type 会是什么,所以必须得把具体的 payload 内容给打出来。幸好在 switchboard:msg_cmd_post() 有这么几行:

  1. #ifdef MSN_DEBUG_SB
  2.         msn_message_show_readable(msg, "SB RECV", FALSE);
  3. #endif

所以应该把 MSN_DEBUG_SB 选项加上。为简单起见直接在项目根目录下的 config.h 加上 #define MSN_DEBUG_SB,并重新编译。
这下有东西打出来,我们关心的消息如下:

  1. (19:05:38) msn: Message SB RECV:
  2. {MIME-Version: 1.0
  3.  
  4. Content-Type: text/plain; charset=UTF-8
  5.  
  6. P4-Context: 测试名字
  7.  
  8. X-MMS-IM-Format: FN=SimSun; EF=; CO=0; CS=86; PF=0
  9.  
  10.  
  11.  
  12. ok
  13.  
  14. }

看来 content type 是 text/plain,那么相对应的回调函数就是在 switchboard.c:msn_switchboard_init():1343 中定义的 switchboard.c:plain_msg()。并且看起来 P4-Content 就是我们所需要的名字。

plain_msg() 的处理也比较简单,主要是拿到消息的内容或者属性,最重要的一个函数调用就是 930:serv_got_im(),将收到消息的具体内容传给界面。其中 passport 就是群的地址,body_final 就是具体的消息内容。现在的问题就是怎么把成员名字显示出来,有两种方法:

  • 直接作为消息的一部分
  • 真正发言人的名字

在 Pidgin 的架构下,第一个方法在消息的每一行之前都会有群名,看起来象这样:“群名字: 测试名字:消息内容”,群名显示在 TAB 上,所以前面的群名就是多余的。第二种方法就跟其他的即时通讯或者聊天就一样的,只有发言人的名字在消息内容之前。

Pidgin 是根据账户以及联系人的名字(根据协议的不同而不同)来得到联系人的昵称:

  1. server.c:568:serv_got_im():632
  2.   conversation.c:1166:purple_conv_im_write():1178
  3.     gtkconv.c:5324:pidgin_conv_write_im:5343

主要的部分就是 pidgin_conv_write_im 了,根据账户和联系人拿到联系人应该显示的名字,这里主要是通过 purple_buddy_get_contact_alias() 拿到别名。然后显示在消息前面。所以可以对这个函数下手。由于这一系列的函数调用都是通过参数传递,所以这里真正的发言人名字无法传过来(否则就要修改无数的代码),无奈只好对 buddy 本身进行修改,给 PurpleBuddy 加上一个 char *special_alias 作为中转,这样可以在 MSN 那边将 special_alias 设置为发言人,然后在 purple_buddy_get_contact_alias() 中拿出来,就可以正确显示出来。

结果没错,已经在对话中正确显示了发言人名字,但后来发现,对话的 TAB 标题也变成最后一个发言人,而不是自己设置的群名。看来那个函数还不能随便改,只能另找他路。

既然拿别名的函数我们不能直接碰,那就在它之外做些手脚,只好在最后一个函数 pidgin_conv_write_im 获取别名之前先判断一下 special_alias,作为最终的别名。

这样的话,问题就解决完美了。不过其实也不算完美,需要给 PurplBuddy 结构加个域。从这里也可以看出 Pidgin 架构上的一些问题,参数传递比较多,牵一发而动全身。更好的方法应该是设计一个结构来传递,这样以后想要对传递的信息做改动,只需要对结构进行修改就可以了,无需修改多处。象现在这个问题,可以在结构里加个域作为特殊情况下的别名,也就不用再修改 PurplBuddy 了,整体的结构也显得干净漂亮。不过这就是面向对象的思维了。

最后的补丁如下:

  1. #
  2. # old_revision [3e5468581850f86ee2b706cd57dfe1096ddcc0ac]
  3. #
  4. # patch "libpurple/blist.h"
  5. #  from [b1dac94a9ac59d6dc854ceed75d0a1e5438bd57c]
  6. #    to [f71a2a259ea5054ffcd1b7c8d56776ad1662de83]
  7. #
  8. # patch "libpurple/conversation.c"
  9. #  from [3bccc3746ca0b742de2278d36d977e4e1707df29]
  10. #    to [cad2fff328fc376a2fb6be76dddaaca3ac6ba94a]
  11. #
  12. # patch "libpurple/protocols/msn/switchboard.c"
  13. #  from [f7fce1b63dc21b75eff51c1d6aafe704c5c41439]
  14. #    to [a0ca553d0195fb987afc22cdbce8dc22be504aec]
  15. #
  16. ============================================================
  17. --- libpurple/blist.h   b1dac94a9ac59d6dc854ceed75d0a1e5438bd57c
  18. +++ libpurple/blist.h   f71a2a259ea5054ffcd1b7c8d56776ad1662de83
  19. @@ -112,6 +112,7 @@ struct _PurpleBuddy {
  20.         char *name;                             /**< The screenname of the buddy. */
  21.         char *alias;                            /**< The user-set alias of the buddy */
  22.         char *server_alias;                     /**< The server-specified alias of the buddy.  (i.e. MSN "Friendly Names") */
  23. +       char *special_alias;                    /**< Special Alias in certain situations. (i.e. MSN Group Chat names) */
  24.         void *proto_data;                       /**< This allows the prpl to associate whatever data it wants with a buddy */
  25.         PurpleBuddyIcon *icon;                    /**< The buddy icon. */
  26.         PurpleAccount *account;                                 /**< the account this buddy belongs to */
  27. ============================================================
  28. --- libpurple/conversation.c    3bccc3746ca0b742de2278d36d977e4e1707df29
  29. +++ libpurple/conversation.c    cad2fff328fc376a2fb6be76dddaaca3ac6ba94a
  30. @@ -912,6 +912,8 @@ purple_conversation_write(PurpleConversa
  31.  
  32.                                 if (purple_account_get_alias(account) != NULL)
  33.                                         alias = account->alias;
  34. +                               else if (b != NULL && b->special_alias != NULL)
  35. +                                       alias = b->special_alias;
  36.                                 else if (b != NULL && strcmp(b->name, purple_buddy_get_contact_alias(b)))
  37.                                         alias = purple_buddy_get_contact_alias(b);
  38.                                 else if (purple_connection_get_display_name(gc) != NULL)
  39. @@ -924,7 +926,12 @@ purple_conversation_write(PurpleConversa
  40.                                 b = purple_find_buddy(account, who);
  41.  
  42.                                 if (b != NULL)
  43. -                                       alias = purple_buddy_get_contact_alias(b);
  44. +                               {
  45. +                                       if (b->special_alias != NULL)
  46. +                                               alias = b->special_alias;
  47. +                                       else
  48. +                                               alias = purple_buddy_get_contact_alias(b);
  49. +                               }
  50.                         }
  51.                 }
  52.         }
  53. ============================================================
  54. --- libpurple/protocols/msn/switchboard.c       f7fce1b63dc21b75eff51c1d6aafe704c5c41439
  55. +++ libpurple/protocols/msn/switchboard.c       a0ca553d0195fb987afc22cdbce8dc22be504aec
  56. @@ -915,6 +915,19 @@ plain_msg(MsnCmdProc *cmdproc, MsnMessag
  57.         }
  58.  #endif
  59.  
  60. +       /* Get the group member nick */
  61. +       PurpleBuddy *buddy = purple_find_buddy(cmdproc->session->account, passport);
  62. +       if ((value = msn_message_get_attr(msg, "P4-Context")) != NULL)
  63. +       {
  64. +               g_free(buddy->special_alias);
  65. +               buddy->special_alias = g_strdup(value);
  66. +       }
  67. +       else
  68. +       {
  69. +               g_free(buddy->special_alias);
  70. +               buddy->special_alias = NULL;
  71. +       }
  72. +
  73.         if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)
  74.         {
  75.                 char *pre, *post;

也可以在 [Pidgin Trac] 上拿到补丁。不过开发者没接受,认为每次都修改 special_alias 不太干净利索。在现在的架构下我觉得没有什么好的办法了。

问题解决了

哈哈 刚才用pidgin,看老同学们聊天,却不知道是谁说话
google了一上,第三篇就是你的解决办法,看不懂啊

不过看到了这个
http://www.moonless.net/blog/2008/08/pidgin-mac-msnmsn.html

“一直以来用pidgin都有无法显示MSN群发言人昵称的问题,以前求人打补丁解决了问题,后来Pidgin再次升级,原来的补丁已经不管用了-_-b,没办法就这么一直放着……
直到有一天,在某个工作群里,有人说他用的是mac msn,也是看不到谁在发言人,极不方便,很是郁闷,由此我继续上网google,后来在一个mac论坛里看到有网友说不用插件,使用msn群的/showname即可显示昵称,汗……”

没错

这是 xiaoi 提供的功能,不过显示效果和 Pidgin 本身收发消息的效果还是有些区别的。

大的改动

Pidgin 的 MSN 代码前一段时间大改了一次,新的修改如下:

  1. #
  2. # old_revision [5592cb6a8b667422747bafd555fea0aed19931b6]
  3. #
  4. # patch "libpurple/blist.h"
  5. #  from [93ef934fff7f894ba39a2024fdac4a77c86c49c2]
  6. #    to [4623c13cbccf95a201b7cbb52998b420f40b6da1]
  7. #
  8. # patch "libpurple/conversation.c"
  9. #  from [73d3d0a936e4d7ecaf9e4213ca4798cf18c663d5]
  10. #    to [a60ed4170c97b2c2b0d07ed840cd1d486ddd893d]
  11. #
  12. # patch "libpurple/protocols/msn/msg.c"
  13. #  from [e635599d8868e7b8a31662d4605556c25fb19839]
  14. #    to [920f180b9a257de0b7480397c61beeb241918756]
  15. #
  16. ============================================================
  17. --- libpurple/blist.h   93ef934fff7f894ba39a2024fdac4a77c86c49c2
  18. +++ libpurple/blist.h   4623c13cbccf95a201b7cbb52998b420f40b6da1
  19. @@ -139,6 +139,7 @@ struct _PurpleBuddy {
  20.         char *name;                             /**< The name of the buddy. */
  21.         char *alias;                            /**< The user-set alias of the buddy */
  22.         char *server_alias;                     /**< The server-specified alias of the buddy.  (i.e. MSN "Friendly Names") */
  23. +       char *special_alias;                    /**< Special Alias in certain situations. (i.e. MSN Group Chat names) */
  24.         void *proto_data;                       /**< This allows the prpl to associate whatever data it wants with a buddy */
  25.         PurpleBuddyIcon *icon;                    /**< The buddy icon. */
  26.         PurpleAccount *account;                                 /**< the account this buddy belongs to */
  27. ============================================================
  28. --- libpurple/conversation.c    73d3d0a936e4d7ecaf9e4213ca4798cf18c663d5
  29. +++ libpurple/conversation.c    a60ed4170c97b2c2b0d07ed840cd1d486ddd893d
  30. @@ -912,6 +912,8 @@ purple_conversation_write(PurpleConversa
  31.  
  32.                                 if (purple_account_get_alias(account) != NULL)
  33.                                         alias = account->alias;
  34. +                               else if (b != NULL && b->special_alias != NULL)
  35. +                                       alias = b->special_alias;
  36.                                 else if (b != NULL && !purple_strequal(purple_buddy_get_name(b), purple_buddy_get_contact_alias(b)))
  37.                                         alias = purple_buddy_get_contact_alias(b);
  38.                                 else if (purple_connection_get_display_name(gc) != NULL)
  39. @@ -924,7 +926,12 @@ purple_conversation_write(PurpleConversa
  40.                                 b = purple_find_buddy(account, who);
  41.  
  42.                                 if (b != NULL)
  43. -                                       alias = purple_buddy_get_contact_alias(b);
  44. +                               {
  45. +                                       if (b->special_alias != NULL)
  46. +                                               alias = b->special_alias;
  47. +                                       else
  48. +                                               alias = purple_buddy_get_contact_alias(b);
  49. +                               }
  50.                         }
  51.                 }
  52.         }
  53. ============================================================
  54. --- libpurple/protocols/msn/msg.c       e635599d8868e7b8a31662d4605556c25fb19839
  55. +++ libpurple/protocols/msn/msg.c       920f180b9a257de0b7480397c61beeb241918756
  56. @@ -851,6 +853,19 @@ msn_plain_msg(MsnCmdProc *cmdproc, MsnMe
  57.         }
  58.  #endif
  59.  
  60. +       /* Get the group member nick */
  61. +       PurpleBuddy *buddy = purple_find_buddy(cmdproc->session->account, passport);
  62. +       if ((value = msn_message_get_attr(msg, "P4-Context")) != NULL)
  63. +       {
  64. +               g_free(buddy->special_alias);
  65. +               buddy->special_alias = g_strdup(value);
  66. +       }
  67. +       else
  68. +       {
  69. +               g_free(buddy->special_alias);
  70. +               buddy->special_alias = NULL;
  71. +       }
  72. +
  73.         if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)
  74.         {
  75.                 char *pre, *post;

WOW! Great Tutorial!!

WOW! Great Tutorial!!

我在网上找到了

我在网上找到了 libmsn.so,因该是根据你的这个方法弄的。但是问题是覆盖了以后,不能发离线消息了。
大侠能不能做一个pidgin2.5.6的patch。或者直接给出相应的lib。
谢谢。

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote>
  • You can use BBCode tags in the text.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>. Beside the tag style "<foo>" it is also possible to use "[foo]".
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
  _  _            __        __    _       _   _   ____   __  __
| || | _ _ \ \ / / (_) | | (_) | ___| \ \/ /
| || |_ | | | | \ \ /\ / / | | _ | | | | |___ \ \ /
|__ _| | |_| | \ V V / | | | |_| | | | ___) | / \
|_| \__,_| \_/\_/ _/ | \___/ |_| |____/ /_/\_\
|__/
Enter the code depicted in ASCII art style.