Qmsg消息推送助手

发现一个简易的使用qq推送消息的工具,原本打算用来做网盘的提醒助手,最后放弃了。

不过感觉这个玩具挺好玩的,做个记录。

Qmsg首页

qq登录即注册qmsg的账号添加对应账号机器人,获取key之后就可以用它做消息的提醒。

文档写的很简单 就做一个get/post请求即可

用go来封装 http请求

模拟http请求在线测试工具

复习相关知识 (可以直接跳过)

Go 的 HTTP 标准库-基本工作原理

当然简单的操作,可以直接用curl to go将curl命令行转换为对应go代码

go http包 有简易的 get post,若是发送较长的数据 推荐使用postform发送表单 或者转为json数据post指定json格式发送

以下是通过post发送表单和解析json数据的示例代码

注意 接收的resp.Body不要忘记关闭

实现

package main

import (
	"encoding/json"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const key = "***" // 申请的key
const url1 = "https://qmsg.zendee.cn/send/" // 私聊消息推送接口:
// const ur2 ="https://qmsg.zendee.cn/group/" // 群消息推送接口:

type infodetail struct {
	Lmyidc string
}

type QmsgResult struct {
	Reason  string
	Success bool
	Code    int
	// Info    interface{}	//嵌套类型json暂不处理直接给此类型 等价为map[string] interface{}
	Info infodetail
}

func main() {
	log.SetFlags(log.Ldate | log.Lshortfile)
	myurl := url1 + key
	client := &http.Client{Timeout: time.Second * 15}

	params := url.Values{}
	params.Add("msg", "有没有这么一种可能@face=172@")
	// params.Add("msg", "解析json测试@face=177@")
	params.Add("key2", `value:2`)
	body := strings.NewReader(params.Encode())

	resp, err := client.Post(myurl, "application/x-www-form-urlencoded", body)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	DataPrint(resp)
}

func DataPrint(resp *http.Response) {
	if resp.StatusCode != http.StatusOK {
		log.Printf("http response failed: %s\n", resp.Status)
	}
	var result QmsgResult
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { //底层unmarshal
		log.Println(err)
	}
	log.Println(result.Reason, result.Code, result.Info.Lmyidc, result.Success)
}

测试无误后只需要将这一小段嵌入到业务代码即可,

很多人用法是写成js插件做到前端页面例如博客的评论区回复提醒等 基于Leancloud或javascript推送Valine评论

其他备忘

get post用法

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

const url = "https://baidu.com"

func main() {
	// 方式一,直接通过 Get 函数
	resp, err := http.Get(url)
	ErrPrint(err)
	defer resp.Body.Close()

	// 拿到数据
	bytes, err := ioutil.ReadAll(resp.Body)
	ErrPrint(err)

	// 这里要格式化再输出,因为 ReadAll 返回的是字节切片
	fmt.Println("------------- 方法一 ---------------")
	fmt.Printf("%s", bytes)

	// 方式二,通过 client 结构体的 Get 方法
	// client := new(http.Client) 等价下式
	client := &http.Client{}

	resp, err = client.Get(url)
	ErrPrint(err)
	defer resp.Body.Close()

	res, err := ioutil.ReadAll(resp.Body)
	ErrPrint(err)
	fmt.Println("\n\n\n------------- 方法二 ---------------")
	fmt.Printf("%s", res)
}

func ErrPrint(err error) {
	if err != nil {
		log.Fatalln(err)
		os.Exit(1)
	}
}

post 普通字串 json用法

/* post client */
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

// json包解析变量只能解析首字母大写的
type Mytest struct {
	Type   int
	Desc   string
	Name   string
	UserId string
}

const url = "http://localhost:8080/hello"

func main() {
	// 方式一,直接通过 Post 函数
	fmt.Println("------------- 方法一 ---------------")
	resp, err := http.Post(url, "application/x-www-form-urlencoded",
		strings.NewReader("name=Bro Qiang"))
	ErrPrint(err)
	defer resp.Body.Close()

	DataPrint(resp.Body)

	// 方式二,通过 client 结构体中的 Post 方法
	fmt.Println("------------- 方法二 ---------------")
	client := &http.Client{}
	resp, err = client.Post(url, "application/x-www-form-urlencoded",
		strings.NewReader("name=New Bro Qiang"))
	ErrPrint(err)
	defer resp.Body.Close()

	DataPrint(resp.Body)

	// 方式一发送json
	fmt.Println("------------- 方法一json ---------------")
	info := Mytest{
		Type:   1,
		UserId: "lsj",
		Name:   "lk233",
		Desc:   "test_UserInfo",
	}

	bytes, _ := json.Marshal(info)
	resp, err = http.Post(url, "application/json", strings.NewReader(string(bytes)))
	if err != nil {
		log.Println("query info failed", err.Error())
	}
	defer resp.Body.Close()

	DataPrint(resp.Body)

}

func DataPrint(body io.ReadCloser) {
	bytes, err := ioutil.ReadAll(body)
	ErrPrint(err)
	fmt.Printf("%s", bytes)
}

func ErrPrint(err error) {
	if err != nil {
		log.Fatalln(err)
		os.Exit(1)
	}
}
/* 
get 的server与 post server类似
添加 下面的类似模块
if req.Method == "GET" {
err := req.ParseForm()
if err != nil {
    http.Error(w, http.StatusText(http.StatusInternalServerError),
        http.StatusInternalServerError)
    return
}
for k, v := range req.Form {
}
*/
/* post_server */
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

type Mytest struct {
	Type   int
	Desc   string
	UserId string
	Name   string
}

func main() {
	http.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
		if req.Method == "POST" {
			// if req.Context()
			// fmt.Fprintf(w,"Hello %s\n",req.FormValue("name")) 此函数可以直接获取对应键值 底层实际也是调用 ParseForm
			err := req.ParseForm()
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError),
					http.StatusInternalServerError)
				return
			}
			// fmt.Println(req.Header["Content-Type"][0])
			if req.Header["Content-Type"][0] == "application/x-www-form-urlencoded" {
				formData := req.Form
				fmt.Fprintf(w, "Hello %s\n", formData.Get("name"))
			} else { //post数据是json需要其他处理
				body, _ := ioutil.ReadAll(req.Body) // read req之后 req的其他解析全为空
				myjson := Mytest{}
				if json.Unmarshal(body, &myjson) != nil {
					fmt.Fprintf(w, "error json\n")
				} else {
					fmt.Fprintf(w, "json Hello %s\n", myjson.Name)
				}
			}
			return
		}
		http.NotFound(w, req)
	})

	log.Fatalf("%v", http.ListenAndServe("localhost:8080", nil))
}

postform用法

/* postform server */
package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/form", MyForm)

	log.Fatalf("%v",
		http.ListenAndServe("localhost:8080", nil))
}

func MyForm(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError),
			http.StatusInternalServerError)
		return
	}

	formData := r.Form
	log.Printf("收到的数据: %v", formData)

	fmt.Fprintf(w, "提交成功")
}
/* client */
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

const url = "http://localhost:8080/form"

func main() {
	data := map[string][]string{"name": {"Bro|", "Qiang"}, "gender": {"male"}}

	// 方法一:PostForm 函数
	resp, err := http.PostForm(url, data)
	ErrPrint(err)
	defer resp.Body.Close()

	DataPrint(resp.Body)

	// 方法二:client 结构体的 PostForm 方法
	client := &http.Client{}
	resp, err = client.PostForm(url, data)
	ErrPrint(err)
	defer resp.Body.Close()

	DataPrint(resp.Body)
}

func DataPrint(body io.ReadCloser) {
	// 拿到数据
	bytes, err := ioutil.ReadAll(body)
	ErrPrint(err)

	// 这里要格式化再输出,因为 ReadAll 返回的是字节切片
	fmt.Printf("%s\n", bytes)
}

func ErrPrint(err error) {
	if err != nil {
		log.Fatalln(err)
		os.Exit(1)
	}
}