[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
///本文属于原创,如果有您需要的内容想要转载,请保留此段签名注释,否则作者将保留追究法律责任的权利。
///******************************************************
原文地址:https://www.cnblogs.com/uu102/p/2699911.html