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,再继续登另外一批。这就是我所说的批量。^.^

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

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

 

你可能感兴趣的:(Web)