系列导航


本文系列源码:点我进入

本期目标

以获取群公告为例,教你利用功能接口实现调用

1.找接口


所谓接口,就是可以获取到你想要的信息的方法,接口获取渠道有很多。

  1. 抓包工具
  2. 浏览器F12
  3. 作者特意开放出来并有附带说明文档的 比如这个
  4. 口口相传、坊间流传



一般的HTTP接口常用两种方式:POST\GET
简单地说,
GET请求,只需要一个URL就可以获取到请求信息的,参数附带在URL里。
POST请求,GET+一个POST请求体,参数可以在URL里也可以在POST里,POST体有请求格式要求,比较常用的是JSON/XML,也可能POST方法但是不需要请求体

这里我们采用“坊间流传的群公告获取接口”

接口地址:https://web.qun.qq.com/cgi-bin/announce/get_t_list
方式:POST
请求体:无
URL参数:?bkn=QQ认证TOKEN&qid=群号&ft=23&s=-1&n=10&ni=1&i=1
cookie:uin=uin值; skey=密钥

2.添加HTTP工具类


此工具类我们只针对常用的POST/GET方法(网上很多,这里贴一个我自用的,支持HTTPS)


首先,增加JSON解析库
Site.Traceless.Demo.Code-右键-管理NUGET包-浏览-选中Newtonsoft.Json-安装

Site.Traceless.Demo.Code-新建文件夹-Tools 存放工具类

添加HttpHelper如下:

HttpHelper.cs

using Newtonsoft.Json;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace Site.Traceless.Demo.Code.Tools
{
    /// <summary>
    /// Http访问的操作类
    /// </summary>
    public static class HttpHelper
    {
        /// <summary>
        /// 调用GET API
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string GetAPI(string url)
        {
            System.Net.HttpWebRequest request = System.Net.WebRequest.Create(url) as System.Net.HttpWebRequest;
            request.Method = "GET";
            request.UserAgent = DefaultUserAgent;
            System.Net.HttpWebResponse result = request.GetResponse() as System.Net.HttpWebResponse;
            System.IO.StreamReader sr = new System.IO.StreamReader(result.GetResponseStream(), System.Text.Encoding.UTF8);
            string strResult = sr.ReadToEnd();
            sr.Close();
            //Console.WriteLine(strResult);
            return strResult.Replace(" ", "").Replace("\n", "");
        }

        /// <summary>
        /// 调用POST API
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string PostAPI(string url, string cookieStr)
        {
            System.Net.HttpWebRequest request = System.Net.WebRequest.Create(url) as System.Net.HttpWebRequest;
            request.Method = "POST";
            request.UserAgent = DefaultUserAgent;
            request.Headers.Add("cookie", cookieStr);
            System.Net.HttpWebResponse result = request.GetResponse() as System.Net.HttpWebResponse;
            System.IO.StreamReader sr = new System.IO.StreamReader(result.GetResponseStream(), System.Text.Encoding.UTF8);
            string strResult = sr.ReadToEnd();
            sr.Close();
            //Console.WriteLine(strResult);
            return strResult.Replace(" ", "").Replace("\n", "");
        }

        /// <summary>
        /// 调用GET API
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static T GetAPI<T>(string url) where T : class
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
            System.Net.HttpWebRequest request = System.Net.WebRequest.Create(url) as HttpWebRequest;
            request.Method = "GET";
            request.UserAgent = DefaultUserAgent;
            System.Net.HttpWebResponse result = request.GetResponse() as HttpWebResponse;
            System.IO.StreamReader sr = new System.IO.StreamReader(result.GetResponseStream(), System.Text.Encoding.UTF8);
            string strResult = sr.ReadToEnd();
            var res = JsonConvert.DeserializeObject<T>(strResult.Replace(" ", "").Replace("\n", ""));
            sr.Close();
            //Console.WriteLine(strResult);
            return res;
        }

        public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }

        /// <summary>
        /// 调用POST API
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static T PostAPI<T>(string url, string cookieStr) where T : class
        {
            System.Net.HttpWebRequest request = System.Net.WebRequest.Create(url) as System.Net.HttpWebRequest;
            request.Method = "POST";
            request.UserAgent = DefaultUserAgent;
            request.Headers.Add("cookie", cookieStr);
            System.Net.HttpWebResponse result = request.GetResponse() as System.Net.HttpWebResponse;
            System.IO.StreamReader sr = new System.IO.StreamReader(result.GetResponseStream(), System.Text.Encoding.UTF8);
            string strResult = sr.ReadToEnd();
            var res = JsonConvert.DeserializeObject<T>(strResult.Replace(" ", "").Replace("\n", ""));
            sr.Close();
            //Console.WriteLine(strResult);
            return res;
        }

        private static readonly string DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";

        public static string DownUrlPic(string url, string path, string name)
        {
            using (System.Net.WebClient wc = new System.Net.WebClient())
            {
                wc.Headers.Add("User-Agent", DefaultUserAgent);
                wc.DownloadFile(url, Path.Combine(path, name));
                return name;
            }
        }
    }
}

3.快速生成接口业务模型


由于是POST接口,我们不能使用浏览器直接输入地址,我们需要使用HTTP调试工具,常用的有Postman、各种在线HTTP工具(随手百度即可找到)

首先我们要试试接口!我这里使用Postman
由于这个接口要使用QQ认证TOKEN认证cookie,一般来说我们需要想办法计算这个TOKEN
但是!由于是QQ接口,酷Q已经提供了这个Token和cookie!我们可以在插件启动事件增加打印这两个值,或者私聊给主人(每次酷Q重启,都会变)


e.CQApi.SendPrivateMessage(你的QQ, e.CQApi.GetCsrfToken());//这个是token,假如结果是“831679459”
e.CQApi.SendPrivateMessage(你的QQ, e.CQApi.GetCookies("qun.qq.com"));//这个是cookie,假如结果是“uin=o3164170991; skey=MC8kbvaMTQ”

在Postman中,选择POST方法,填写对应的URL、到Headers中添加key为cookie,Value为uin=o3164170991; skey=MC8kbvaMTQ
postman
点击“Send”

接口返回,点我展开

{
    //对比一下实际信息,我们可以分析出来
    "ec": 0,
    "em": "",
    "ltsm": 1584515429,
    "srv_code": 0,
    "read_only": 0,
    "role": 3,
    "feeds": [
        {
            "u": 415206409,//发公告人的QQ
            "fid": "91b2c31e0000000093c8715e58b70300",//公告的唯一ID
            "pubt": 1584515219,//公告发送时间戳
            "msg": {
                "text": "这里是测试公告2",//公告内容
                "text_face": "这里是测试公告2",//公告摘要
                "title": "群公告"//公告标题
            },
            "type": 6,//公告类型,具体有哪些类型,谁知道呢
            "fn": 0,
            "cn": 0,
            "vn": 0,
            "settings": {//大概是一些设置
                "is_show_edit_card": 0,
                "remind_ts": 0,
                "tip_window_type": 0,
                "confirm_required": 0
            },
            "read_num": 0,//已读人数
            "is_read": 0,//本QQ是否已读
            "is_all_confirm": 0//是否全部已读
        },
        {
            "u": 415206409,
            "fid": "91b2c31e000000008dc8715e5f020100",
            "pubt": 1584515213,
            "msg": {
                "text": "这里是测试公告1",
                "text_face": "这里是测试公告1",
                "title": "群公告"
            },
            "type": 6,
            "fn": 0,
            "cn": 0,
            "vn": 0,
            "settings": {
                "is_show_edit_card": 0,
                "remind_ts": 0,
                "tip_window_type": 0,
                "confirm_required": 0
            },
            "read_num": 0,
            "is_read": 0,
            "is_all_confirm": 0
        }
    ],
    "group": {
        "group_id": 516141713,//群号
        "class_ext": 10048//鬼知道是什么不重要
    },
    "sta": 1,
    "gln": 0,
    "tst": 10,
    "ui": {
        "415206409": {
            "n": "return&nbsp;true;",
            "f": "http://thirdqq.qlogo.cn/g?b=oidb&amp;k=Z6ByEIFFnFl1PAzSpAictnw&amp;s=40"
        },
        "3164170991": {
            "n": "影妹",
            "f": "http://thirdqq.qlogo.cn/g?b=oidb&amp;k=0e0c3Ef2tzkku3638WiavjQ&amp;s=40"
        }
    },
    "server_time": 1584515429000,
    "svrt": 1584515429,
    "ad": 0
}



骚操作来了,复制整段JSON
Model中新建一个cs,比如GroupNoticeResp.cs
顶部工具栏 编辑-选择性粘贴-JSON粘贴为类
奇迹发生了!

自动生成了模型!

public class Rootobject
    {
        public int ec { get; set; }
        public string em { get; set; }
        public int ltsm { get; set; }
        public int srv_code { get; set; }
        public int read_only { get; set; }
        public int role { get; set; }
        public Feed[] feeds { get; set; }
        public Group group { get; set; }
        public int sta { get; set; }
        public int gln { get; set; }
        public int tst { get; set; }
        public Ui ui { get; set; }
        public long server_time { get; set; }
        public int svrt { get; set; }
        public int ad { get; set; }
    }

    public class Group
    {
        public int group_id { get; set; }
        public int class_ext { get; set; }
    }

    public class Feed
    {
        public int u { get; set; }
        public string fid { get; set; }
        public int pubt { get; set; }
        public Msg msg { get; set; }
        public int type { get; set; }
        public int fn { get; set; }
        public int cn { get; set; }
        public int vn { get; set; }
        public Settings settings { get; set; }
        public int read_num { get; set; }
        public int is_read { get; set; }
        public int is_all_confirm { get; set; }
    }

    public class Msg
    {
        public string text { get; set; }
        public string text_face { get; set; }
        public string title { get; set; }
    }

    public class Settings
    {
        public int is_show_edit_card { get; set; }
        public int remind_ts { get; set; }
        public int tip_window_type { get; set; }
        public int confirm_required { get; set; }
    }


由于这个接口ui部分的特殊性,我们稍微做一些修改,由于UI部分没有用,我们把class Ui和下面两个class _qq号 删掉(要使用的话,可以修改为Object,再进行额外解析)
把新建时自动生成的

class GroupNoticeResp{}

去掉

把Rootobject修改为GroupNoticeResp

4.封装统一方法

根据你的需要,封装一个获取群公告的方法

  • 新建一个Func文件夹,存放功能性方法
  • 新建QGroupExtend

QGroupExtend.cs

using Newtonsoft.Json;
using Site.Traceless.Demo.Code.Model;
using Site.Traceless.Demo.Code.Tools;

namespace Site.Traceless.Demo.Code.Func
{
    public static class QGroupExtend
    {
        public static string GROUP_NOTICE_STR = @"https://web.qun.qq.com/cgi-bin/announce/get_t_list";

        public static GroupNoticeResp getGroupNotice(string bkn, long gid, string cookieStr)
        {
            string url = $"{GROUP_NOTICE_STR}?bkn={bkn}&qid={gid}&ft=23&s=-1&n=10&ni=1&i=1";
            return JsonConvert.DeserializeObject<GroupNoticeResp>(JsonConvert.SerializeObject(HttpHelper.PostAPI<GroupNoticeResp>(url, cookieStr)));
        }
    }
}

5.方法调用

例如:我需要在私聊机器人 "公告 群号"的时候获得该群的公告列表
首先,在AppEnable做如下更改

iObject = new IniObject
                {
                    new IniSection("gcommands")
                    {
                        { "功能1","funcOne"},
                        { "功能2","funcTwo"}
                    },
                    new IniSection("pcommands")
                    {
                        { "功能1","funcOne"},
                        { "功能2","funcTwo"},
                        { "公告","getGNotice"},
                    }
                };

在GroupApp添加getGNotice方法

FriendApp.cs

using Native.Sdk.Cqp.EventArgs;
using Site.Traceless.Demo.Code.Func;
using Site.Traceless.Demo.Code.Model;
using System;
using System.Text;

namespace Site.Traceless.Demo.Code.Command
{
    public class FriendApp
    {
        public static void funcOne(CQPrivateMessageEventArgs e, AnalysisMsg msg)
        {
            e.CQApi.SendPrivateMessage(e.FromQQ, $"[这里是私聊方法1]", $"参数数 {msg.OrderCount}\n", $"触发指令(第一参数 what) {msg.What}\n", $"目标(第二参数 who) {msg.Who}\n", $"怎么做(第三参数 how) {msg.How}\n", $"原始信息 {msg.OriginStr}\n", e.ToString());
        }

        public static void funcTwo(CQPrivateMessageEventArgs e, AnalysisMsg msg)
        {
            e.CQApi.SendPrivateMessage(e.FromQQ, $"[这里是私聊方法2]", $"参数数 {msg.OrderCount}\n", $"触发指令(第一参数 what) {msg.What}\n", $"目标(第二参数 who) {msg.Who}\n", $"怎么做(第三参数 how) {msg.How}\n", $"原始信息 {msg.OriginStr}\n", e.ToString());
        }

        public static void getGNotice(CQPrivateMessageEventArgs e, AnalysisMsg msg)
        {
            //获取公告内容
            GroupNoticeResp resp = QGroupExtend.getGroupNotice(e.CQApi.GetCsrfToken() + "", Convert.ToInt32(msg.Who), e.CQApi.GetCookies("qun.qq.com"));
            if (resp == null || resp.feeds == null)
            {
                //公告为空
                e.CQApi.SendPrivateMessage(e.FromQQ, "抱歉,公告为空");
                return;
            }
            StringBuilder sb = new StringBuilder();
            int i = 0;
            sb.AppendLine($"总计公告({resp.feeds.Length}条");
            foreach (Feed feed in resp.feeds)
            {
                sb.AppendLine($"{i+1}.{feed.msg.title}");
                sb.AppendLine($"内容:");
                sb.AppendLine($"{feed.msg.text}");
                i++;
            }
            e.CQApi.SendPrivateMessage(e.FromQQ.Id, sb.ToString());
        }
    }
}


如果插件已经启动过
酷Q目录\data\app\你的appid 下,找到command.ini文件
修改为

[gcommands]
功能1=funcOne
功能2=funcTwo

[pcommands]
功能1=funcOne
功能2=funcTwo
公告=getGNotice

6.去爽一下

编译插件!打包为CPK!

只要私聊机器人 “公告 123456” 【当然,机器人要在那个群里】
即可收到机器人的返回如下:


总计公告1条
1.群公告测试标题
内容:
群公告测试内容

下集预告


1.使用JSONObject解析JSON字符串
2.读取setting.ini文件使用配置参数
3.Ini工具类的使用
4.SQlite-net库使用

本文作者:Author:     文章标题:【手摸手系列】教你开发QQ机器人 - HTTP接口使用
本文地址:https://traceless.site/index.php/archives/22/     
版权说明:若无注明,本文皆为“Traceless”原创,转载请保留文章出处。
Last modification:March 27th, 2020 at 04:03 pm
如果觉得我的文章对你有用,请随意赞赏