zoukankan      html  css  js  c++  java
  • [c#] WebQQ群发限制的突破[续]

      持久的毅力是事业成功的基础

    上一篇《WebQQ群发限制的突破》讲到了基本的消息发送,但是后面没讲完,结果很多朋友就在那里犯迷糊了,说是突破WebQQ群发的限制,却说了一大堆与突破没关系的废话。所以,今天趁着有时间,还是好好补充一下上篇文章没讲完的地方。

     针对发送好友消息没有实际收到的解决办法

           可能由于网络原因,或者Tx的关键词屏蔽功能,使得有些字词或句子无法发送出去,这就要测试一下web3.qq.com本身到底屏蔽了哪些字词,好对症下药。如果我们确实必须要发送这样一些被屏蔽掉的字词该怎么做呢?对了,通过将文字转换成图片。就可以发送了。但是,在WebQQ里要想发送图片,对于刚入门WebQQ的新手来说谈何容易,可能你对WebQQ的登录,或是发送消息,已经可以做到熟能生巧,但是发送图片这一块很多人可能会卡住。

         在WebQQ里发送图片给好友,其实分为两部分:上传图片到TX指定服务器,上传之后得到一个图片的guid和一些其他参数,作为发送的参数再发送。

        上传图片,我们需要改变一下http请求的正文类型(CntentType)为multipart/form-data,并且加上一个分隔符号,这个丰富好可以自己定,但是一定要能将前后两段文本完整的区分开来。对于请求头参数构造,我们单独作为一个方法,将构造好的结果返回到一个字典里,供请求方法调用。

    View Code
     /// <summary>
          /// 本文自博客园原创,转载请加上此链接
          /// http://www.cnblogs.com/uu102
          /// </summary>
          /// <param name="fileName"></param>
          /// <returns></returns>
            public string Upload_Offline_Pic(string fileName)
            {   
                string timeStamp = Util.GetTimestamp(DateTime.Now).ToString();
                String time = DateTime.Now.Ticks.ToString("x");
                string url = string.Format("http://weboffline.ftn.qq.com/ftn_access/upload_offline_pic?time={0}", timeStamp);
                string skeys = QQGlobal.ACCountManager[this.Uin].CookieString.Substring(QQGlobal.ACCountManager[this.Uin].CookieString.IndexOf("skey=") + 5, 10);
                Dictionary<string, string> dic = new Dictionary<string, string>();
                dic.Add("callback", "parent.EQQ.Model.ChatMsg.callbackSendPic");
                dic.Add("locallangid", "2052");
                dic.Add("clientversion", "1409");
                dic.Add("uin", this.Uin);
                dic.Add("skey", skeys);
                dic.Add("appid", "15000101");
                dic.Add("peeruin", "593023668");
                dic.Add("fileid", this.fileId.ToString());
                dic.Add("vfwebqq", QQGlobal.ACCountManager[this.Uin].Vfwebqq);
                dic.Add("senderviplevel", "0");
                dic.Add("reciverviplevel", "0");
                this.fileId++;
                string imagePath = new Regex(@"""filepath"":""(?'filepath'[^\""]+)""").Match(SubmitData(url, fileName, dic)).Groups["filepath"].Value.Replace("\\", "");
               string shortfilename = fileName.Substring(fileName.LastIndexOf("\\") + 1, fileName.Length - fileName.LastIndexOf("\\") - 1);
               this.OffLine_Pics[this.OffLine_Pics.Count-1].ServerPath = imagePath;
              
                return imagePath;
            }

    在这个请求头的参数构造里,有几个必须附带讲明一下,时间戳。

    View Code
      /// <summary>
          /// 本文自博客园原创,转载请加上此链接
          /// http://www.cnblogs.com/uu102
          /// </summary>
          /// <param name="fileName"></param>
          /// <returns></returns>
    public static long GetTimestamp(DateTime dateTime)//获取时间戳
            {
                DateTime startDate = new DateTime(1970, 1, 1);
                DateTime endDate = dateTime.ToUniversalTime();
                TimeSpan span = endDate - startDate;
                return (long)(span.TotalMilliseconds + 0.5);
            }

          this.Uin是当前登录的QQ号码,skeys是从当前所在的cookie里提取出来的,仔细找找,在cookies里确实存在这样的键。file_id是自己定义的一个数字,从1开始累加1,QQGlobal.ACCountManager[this.Uin].Vfwebqq是前面登录就已经获取到了的参数。具体获取登陆之前的参数过程,请大家自己查阅网上登录WebQQ的教程吧。这里这些东西就不做多余的说明了。

        注意观察,第一段代码里面有一个突然冒出来的函数SubmitData(url, fileName, dic),这一个函数,其实就是我们接下来要讲到的核心请求了,通过以上构造的参数,我们可以做以下请求了。

    View Code
     /// <summary>
            /// 模拟表单提交上传图片
            /// 本文自博客园原创,转载请加上此链接
            /// http://www.cnblogs.com/uu102
            /// </summary>
            /// <param name="url">地址</param>
            /// <param name="fileName">图pain的文件名(例如:a.jpg)</param>
            /// <param name="dic">dictionay<T,T>结构"/></param>
            /// <returns></returns>
            private string SubmitData(string url, string fileName, Dictionary<string, string> dic/* ,string[] keys, string[] values*/)
            {
                string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
                HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
                httpWebRequest.CookieContainer = QQGlobal.ACCountManager[this.Uin].CookieContainer;
                httpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary;
                httpWebRequest.Method = "POST";
                StringBuilder sb = new StringBuilder();
    
                if (dic.Count != 0)
                {
                    foreach (KeyValuePair<string, string> kvp in dic)
                    {
                        sb.Append("--");
                        sb.Append(boundary);
                        sb.Append("\r\n");
                        sb.Append("Content-Disposition: form-data; name=\"" + kvp.Key + "\"\r\n\r\n");
                        sb.Append(kvp.Value);
                        sb.Append("\r\n");
                    }
                }
                string shortfilename = fileName.Substring(fileName.LastIndexOf("\\") + 1, fileName.Length - fileName.LastIndexOf("\\") - 1);
                this.OffLine_Pics.Add(new OffLine_Up_Pic()); 
                sb.Append("--");
                sb.Append(boundary);
                sb.Append("\r\n");
                sb.Append("Content-Disposition: form-data; name=\"file\"; filename=\"");
                sb.Append(shortfilename);
                sb.Append("\"");
                sb.Append("\r\n");
                sb.Append("Content-Type: application/image/jpeg");
                sb.Append("\r\n");
                sb.Append("\r\n");
    
                string postHeader = sb.ToString();
                byte[] postHeaderBytes = Encoding.UTF8.GetBytes(postHeader);
                byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
    
                FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
    
                this.OffLine_Pics[this.OffLine_Pics.Count-1].Length = fileStream.Length;//
                this.OffLine_Pics[this.OffLine_Pics.Count-1].ShortFileName = shortfilename;
                long length = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length;
                httpWebRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
                httpWebRequest.AllowWriteStreamBuffering = false;
                httpWebRequest.ServicePoint.Expect100Continue = false;
                httpWebRequest.ContentLength = length;
    
                Stream requestStream = httpWebRequest.GetRequestStream();
    
                requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);
    
                byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];
                long filebytes = fileStream.Length;
                int bytesRead = 0;
                while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                    requestStream.Write(buffer, 0, bytesRead);
                requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
    
                WebResponse webResponse2 = httpWebRequest.GetResponse();
                Stream stream = webResponse2.GetResponseStream();
                StreamReader streamReader = new StreamReader(stream);
                string html = streamReader.ReadToEnd();
                requestStream.Close();
                fileStream.Close();
    
                return html;
    
            }

    这一段代码其实很好看懂,无非就是一个HttpWebRequest请求,只不过请求的类型不一样,而且数据是分段上传的。配合前文所列出的发送消息的代码,就可以发送图片了。
    另外再补充说明一下,在请求之后返回的html文本里,有三个参数都是非常重要的,到时候自己一看便知道了。

    针对图片不能重复发送的解决办法

    图片如果重复发送,在现实WebQQ聊天过程中很少会发生重复多次发送同一张图片的,谁还有事没事只发同一张图片啊,但是群发里面就经常碰到的这样的情况,重复发送一张图片很多次,所以TX就给你来个限制。

    对于重复发送同一张图片,很好的解决办法就是控制图片的发送次数,或者发送频率快慢。最大程度的模拟真实发送过程,但是这样是治标不治本,很难保证不会被TX限制。

    在图片里用GDI+给图片添加干扰素。所谓干扰素,无非就是给图片随机生成一些颜色或点,让图片看起来不是发的同一张图片。

    这个方法很简单,代码就不加了。

     

    针对一台机器挂QQ数量有限制的解决办法

    一台电脑最多挂QQ的数量很有限,超过这个限额tx会要求强制性下线或是验证。因此,唯一的办法,就是批量更改IP。所谓批量,就是说等完10个QQ或者20,然后马上改变IP,再继续登另外一批。这就是我所说的批量。^.^

    好了,这些就是说掌握的突破方法,大家有什么别的好方法,不妨共享一下。

    教程每天都更新,请继续保持关注!



    ///******************************************************
    ///本文作者:凌晨的搜索者
    ///博客地址:http://www.cnblogs.com/uu102
    ///本文属于原创,如果有您需要的内容想要转载,请保留此段签名注释,否则作者将保留追究法律责任的权利。
    ///******************************************************
  • 相关阅读:
    linux进程cpu使用率过高分析与排查
    重启服务器后keepalived,vip找不到
    linux五种IO模型
    redis为什么单线程这么快?
    nginx为什么比apache快?
    redis高可用,主从,哨兵,集群
    git合并远端commit
    查看cpu和内存
    CommitLog文件和ConsumeQueue在文件系统中的映射
    hadoop集群启动
  • 原文地址:https://www.cnblogs.com/uu102/p/2699911.html
Copyright © 2011-2022 走看看