这该死的群贴图接收问题,早上好好的,中午就不行了。

修正了后,中午早上好了,下午又不行了……

这叫什么事啊

posted @ 2008-05-07 20:51 蓝色的风之精灵 阅读(40) | 评论 (1)编辑

       不知道这个功能有多少人期待,反正我是很想要这个功能的,因为看着群里人说话贴表情,自己看不见是很郁闷的事情。狠狠心找来LumaQQ的Java代码,自己一点点翻译成C#的加了进去。呵呵,在这里感谢Luma前辈和阿不同学的辛勤劳动,没有他们这个功能也出不来。

        好了,不多说了,因为说也说不清……改动太多,代码也比较乱。提交给阿不同学了,只能辛苦他去整理了。

 

        这里就放一个编译好的DLL吧,感兴趣的同学自己反编译看看实现的代码好了,真正的源代码等阿不再次更新吧~

        嗯,说下怎么去下载自定义表情和图片:

        在接收到群消息时,调用CustomFaceManage的AddClusterIM方法

   1: client.CustomFaceManager.AddClusterIM(e.InPacket);

        好了,就这么简单。那么有人问,图片保存到哪里去了呢?

        这个是在App.config(编译后自动会更名成 程序名.exe.config)里可以配置的:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <appSettings>
   4:     <add key="CustomFaceDir" value="D:\LumaQQ.NET\Faces\CustomFaceRecv" />
   5:     <add key="ImageDir" value="D:\LumaQQ.NET\Faces\Image" />
   6:   </appSettings>
   7: </configuration>

        如果不配置,默认放在程序所在目录的CustomFaceDir和imageDir里面。

        好了,使用就这么简单~

        点击下载LumaQQ.NET.rar

posted @ 2008-05-06 17:30 蓝色的风之精灵 阅读(1386) | 评论 (5)编辑

        之前我写了一篇关于怎么在LumaQQ.NET里发送超长群消息的文章,经过自己的试验,是成功的。

        但是之后我在实际应用中,发现有一段消息怎么也发送不出去,我觉得很奇怪,难道是我的程序有问题?

        而且同样的消息,我用私聊也能发出去,这更让我以为是发送长消息的代码有问题……

        于是我尝试了过滤回车,换行,查找非法字符等等方法,仍然一无所获……

        然后我尝试截获收到的消息包,发现竟然只能收到第一段消息,而第二段根本收不到!这就难怪了,因为消息包不完整,自然不会显示出来。

        这是怎么回事?为什么第二段消息收不到?我明明发送出去了,也没有收到超时的提示?在绝望中我想到了一点,会不会是腾讯因为消息中有什么敏感词汇将消息过滤了?

        于是我将消息一点点的拆开发送,终于发现,这段消息里有两个汉字的组合是发不出去的,而且很神奇的是,不仅连在一起发不出去,在他们之间加上空格,换行,数字,字母依然是发不出去的,只有加上汉字才能发送成功。

 

        好吧,这篇文章主要要表示的不是谴责腾讯干涉言论自由,这个错不再腾讯,责任在谁大家心知肚明;而是提醒下大家,使用LumaQQ.NET发消息失败时,不要将目光局限于是不是代码的问题,而是有可能问题出在其他外界因素上。

posted @ 2008-05-03 10:34 蓝色的风之精灵 阅读(97) | 评论 (1)编辑

        QQ群有两个ID,内部ID和外部ID。外部ID就是我们平时常见的ID,搜索啊,查看啊,都是这个ID。

        而内部ID就不常见了,应该说用QQ的人是从来不知道还有这个ID的。但是,这个ID是这篇文章的重点。因为发群信息必须用到这个ID。

        用过LumaQQ.NET的人,尝试过用QQ上可见的群号码发群消息,相信都是超时失败的。为什么呢?因为发群消息应该用群内部ID
        那么怎么得到群内部ID呢?它和群外部ID有什么联系?

        很遗憾,两者没有任何联系……

        不过我们有办法将两者联系起来,那就是在登录时手动去保存对应关系表:

        先要定义一个字典:

   1: public static Dictionary<int, int> ClusterDict { get; private set; }

        然后在登录成功事件里添加获取分组信息的代码:

   1: void LoginManager_LoginSuccessed(object sender, LumaQQ.NET.Events.QQEventArgs<LumaQQ.NET.Packets.In.LoginReplyPacket, LoginPacket> e)
   2: {
   3:     WriteError("登录成功");
   4:     WriteError("开始读取好友信息");
   5:     e.QQClient.FriendManager.GetFriendList();
   6:     e.QQClient.FriendManager.GetUserInfo(e.QQClient.QQUser.QQ);
   7:     e.QQClient.FriendManager.DownloadGroupFriends(0);//下载分组信息,这里是为了得到群的内部号码。
   8:  
   9:     //改变状态为离开状态
  10:     e.QQClient.FriendManager.ChangeStatus(QQStatus.AWAY, false);
  11:     //读取天气预报
  12:     e.QQClient.PrivateManager.GetWeather();
  13: }

 

        当下载分组信息成功后,判断是不是群,是群的话,就去获取群信息。(其实这里的friend.QQ就是群内部ID了,只不过我们不知道它的外部ID是什么)

   1: void FriendManager_DownloadGroupFriendSuccessed(object sender, QQEventArgs<DownloadGroupFriendReplyPacket, DownloadGroupFriendPacket> e)
   2: {
   3:     foreach (DownloadFriendEntry friend in e.InPacket.Friends)
   4:     {
   5:         if (friend.Type == FriendType.IS_CLUSTER)
   6:         {
   7:             e.QQClient.ClusterManager.GetClusterInfo((int)friend.QQ);
   8:         }
   9:     }
  10: }

          最后,在获取群信息成功的事件里,就能得到外部ID了,我们保存下来

   1: void ClusterManager_GetClusterInfoSuccessed(object sender, QQEventArgs<ClusterCommandReplyPacket, ClusterGetInfoPacket> e)
   2: {
   3:     if (!ClusterDict.ContainsKey((int)e.InPacket.Info.ExternalId))
   4:         ClusterDict.Add((int)e.InPacket.Info.ExternalId, (int)e.InPacket.Info.ClusterId);
   5:     else
   6:         ClusterDict[(int)e.InPacket.Info.ExternalId] = (int)e.InPacket.Info.ClusterId;
   7:     WriteError("得到群{0}的外部ID:{1},内部ID:{2}", e.InPacket.Info.Name,e.InPacket.Info.ExternalId,e.InPacket.Info.ClusterId);
   8: }

 

        这样,以后我们发群消息时,根据群外部ID从字典里取到对应的内部ID就能发送信息了。

posted @ 2008-04-29 16:21 蓝色的风之精灵 阅读(1848) | 评论 (4)编辑
     摘要:         之前我们修改了LumaQQ.NET,让他能发送超长消息了。现在我们再来看看,怎么让其支持发送群超长消息。         可能有人说,这个简单,我照着发送普通消息的方法写一个发送群的不是一样吗?    ...  阅读全文
posted @ 2008-04-29 15:21 蓝色的风之精灵 阅读(1506) | 评论 (7)编辑
哈哈,前两天从公司包了两个波利抱枕回家,好可爱啊~~

posted @ 2008-04-26 15:49 蓝色的风之精灵 阅读(14) | 评论 (0)编辑

        之前的文章里说明了怎么让LumaQQ.NET发送长消息,这里我们看看怎么让LumaQQ.NET支持接收长消息。

        其实QQ的服务器在发送长消息时,是将其拆分成几个短的消息来发送的,这点在上一篇发送长消息时,大家已经能看出来了。那么怎么将几个短消息合并成一个长消息呢。

        这里主要是用到了NormalIM的TotalFragments和FragmentSequence两个属性,FragmentSequence表示这个是第几个分片,基于0的index,TotalFragments表示一共有几个分片。

        由于收到消息时,不一定是按照顺序接收到的(网络延迟等原因),因此我们定一个Dictionary来管理分片。

private static Dictionary<int, Dictionary<int, byte[]>> NormalLongTextDict = new Dictionary<int, Dictionary<int, byte[]>>();
 
        然后在接收到消息的事件里,进行判断和组装
void MessageManager_ReceiveNormalIM(object sender, LumaQQ.NET.Events.QQEventArgs<LumaQQ.NET.Packets.In.ReceiveIMPacket, OutPacket> e)
{
    if (e.InPacket.NormalIM.TotalFragments > 1)//如果是长消息的分片
    {
        Dictionary<int, byte[]> TextFragment;
        if (!NormalLongTextDict.ContainsKey(e.InPacket.NormalIM.MessageId))//如果字典里没有缓存这个消息id
        {
            TextFragment = new Dictionary<int, byte[]>();
            TextFragment.Add(e.InPacket.NormalIM.FragmentSequence, e.InPacket.NormalIM.MessageBytes);//将分片加入以分片id为标示的分片缓存
            NormalLongTextDict.Add(e.InPacket.NormalIM.MessageId, TextFragment);//将分片缓存加入以消息id为标示的消息缓存
        }
        else
        {
            TextFragment = NormalLongTextDict[e.InPacket.NormalIM.MessageId];//取出分片缓存
            TextFragment.Add(e.InPacket.NormalIM.FragmentSequence, e.InPacket.NormalIM.MessageBytes);加入新的分片
            NormalLongTextDict[e.InPacket.NormalIM.MessageId] = TextFragment;放回消息缓存
        }
 
        if (TextFragment.Count == e.InPacket.NormalIM.TotalFragments)//如果所有分片都已经接收完毕
        {
            List<byte> LongTextBytes = new List<byte>();
            string LongText = "";
            for (int i = 0; i < TextFragment.Count; i++)
            {
                LongTextBytes.AddRange(TextFragment[i]);合并byte数组
            }
            LongText = Encoding.GetEncoding(QQGlobal.QQ_CHARSET_DEFAULT).GetString(LongTextBytes.ToArray());转为文字字符串
              NormalLongTextDict.Remove(e.InPacket.NormalIM.MessageId);//删除缓存
              e.QQClient.MessageManager.SendIM(e.InPacket.NormalHeader.Sender, string.Format("我收到你的消息:{0}", LongText);
        }
    }
    else
    {
        WriteError("收到好友:{0}/{1} 发来的信息:{2}", e.InPacket.NormalHeader.Sender, Nick, AnalyCustomFace(e.InPacket.NormalIM.MessageBytes));
        e.QQClient.MessageManager.SendIM(e.InPacket.NormalHeader.Sender, string.Format("我收到你的消息:{0}", e.InPacket.NormalIM.Message));
 
    }
 
 
}
posted @ 2008-04-22 17:21 蓝色的风之精灵 阅读(1454) | 评论 (8)编辑

        默认LumaQQ.NET不支持发送长消息,只能文字量比较小的文字信息。其实在内部设计上,已经支持了分片发送的功能,只是没有去拆分实现。我们这就来自己实现它。

1.首先在QQGlobal里添加消息最长长度定义:

   1: /// <summary>
   2: /// 消息最大长度,QQ是最大699字节   LumaQQ里说最长是700,但是我试下来只能是699,700就会出错,不知道为什么
   3: /// </summary>
   4: public const int QQ_MAX_SEND_IM = 699;

 

2.修改MessageManager里其中2个SendIM的方法

   1: /// <summary>
   2: /// 发送普通信息
   3: ///  <remark>abu 2008-03-11 </remark>
   4: /// </summary>
   5: /// <param name="receiver">The receiver.</param>
   6: /// <param name="message">The message.</param>
   7: /// <param name="fontSytle">The font sytle.</param>
   8: public void SendIM(int receiver, string message, FontStyle fontSytle)
   9: {
  10:     int MaxByte = QQGlobal.QQ_MAX_SEND_IM;//取最长长度
  11:  
  12:  
  13:     if (Encoding.GetEncoding(QQGlobal.QQ_CHARSET_DEFAULT).GetBytes(message).Length > MaxByte)//判断是不是要分段发送
  14:     {
  15:         List<byte> messageBytes = new List<byte>();
  16:         messageBytes.AddRange(Utils.Util.GetBytes(message));
  17:         messageBytes.Add(0x20);//补一个空格,不补似乎也会出问题
  18:         int messageSize = messageBytes.Count;
  19:  
  20:         int totalFragments = ((messageSize % MaxByte) > 0) ? (messageSize / MaxByte + 1) : (messageSize / MaxByte);//计算分片数
  21:         for (int fragementSequence = 0; fragementSequence < totalFragments; fragementSequence++)
  22:         {
  23:             int index = fragementSequence * MaxByte;
  24:             int BytesSize = ((messageSize - index) > MaxByte) ? MaxByte : (messageSize - index);//不能每次都申请最大长度的byte数组,不然字体会出问题
  25:             byte[] messageFragementBytes = new byte[BytesSize];
  26:  
  27:  
  28:             messageBytes.CopyTo(index, messageFragementBytes, 0, BytesSize);
  29:             SendIM(receiver, messageFragementBytes, totalFragments, fragementSequence, fontSytle);
  30:  
  31:  
  32:         }
  33:     }
  34:     else
  35:     {
  36:         SendIM(receiver, Utils.Util.GetBytes(message), 1, 0, fontSytle);
  37:     }
  38: }
  39: /// <summary>
  40: /// 发送普通信息
  41: ///  <remark>abu 2008-03-11 </remark>
  42: /// </summary>
  43: /// <param name="receiver">The receiver.</param>
  44: /// <param name="message">The message.</param>
  45: /// <param name="totalFragments">The total fragments.总分块数</param>
  46: /// <param name="fragementSequence">The fragement sequence.当前当块序号</param>
  47: /// <param name="fontSytle">The font sytle.</param>
  48: public void SendIM(int receiver, byte[] message, int totalFragments,
  49:     int fragementSequence, FontStyle fontSytle)
  50: {
  51:     SendIMPacket packet = new SendIMPacket(QQUser);
  52:     packet.Receiver = receiver;
  53:     packet.Message = message;//Utils.Util.GetBytes(message);
  54:     packet.TotalFragments = totalFragments;
  55:     packet.FragmentSequence = fragementSequence;
  56:     packet.FontStyle = fontSytle;
  57:     QQClient.PacketManager.SendPacket(packet, QQPort.Main.Name);
  58: }
posted @ 2008-04-22 13:17 蓝色的风之精灵 阅读(1610) | 评论 (15)编辑
     摘要: QQ的系统表情,自定表情和截图都是直接混合在文字信息中直接发送过来的,如果在接收到时不加转换,直接显示,会造成乱码。因此我们要把这些信息转化下再显示。 关于这些表情,自定义表情和截图的说明如下:来源LumaQQ的代码注解 * <pre>* 普通消息的本体,其在NormalIMHeader之后* * 普通消息中可能内嵌一些图片信息,除了普通的文本之外,图片的信息格式为:* 一. 缺省...  阅读全文
posted @ 2008-04-21 11:18 蓝色的风之精灵 阅读(2222) | 评论 (15)编辑
最近在跟踪LumaQQ.NET的进展,发现一个问题,就是QQ的自带表情,也是直接用文本消息的方式传输过来的。
不过由于表情都是0x14开头的,这个编码不属于正常的文本编码,甚至直接放在XML都是非法字符,因此需要过滤,将其转成可以识别的文本。
具体的转法我就不写了,我是用ArrayList转的。相信这里有人有更好的写法。
这里就放一下对应的QQ表情包把,我把QQ表情的文件名改成和0x14后面一个字节一样的数字了,方便直接调用。
QQ默认表情包
posted @ 2008-04-09 13:11 蓝色的风之精灵 阅读(135) | 评论 (3)编辑