Golang实现发送微信公众号模板消息(每日一句和天气预报)

Golang实现发送微信模板消息(每日一句和天气预报)

前言

本文是基于https://www.cnblogs.com/connect/p/python-wechat-iciba.html 这篇博客写成的。该博客实现了用python将金山词霸的每日一句推送到微信公众测试号,我想既然python能实现,那么用Golang也可以。

后来又加了每天早晨定时给自己和女朋友发天气预报提醒,开始着手做,gogogo!

运行环境

  1. 阿里云Linux服务器
  2. Go开发环境

完整的项目代码:https://github.com/qq737310694/WechatTemplate

一、获取接口数据

1、每日一句接口

调用地址:http://open.iciba.com/dsapi/
请求方式:GET
请求参数:

参数 必选 类型 说明
date string 格式为:2013-05-06;如果date为空,则默认取当天
type string 可选值为last和next;以date日期为准的,last返回前一天的,next返回后一天的

返回类型:JSON
JSON字段解释:

属性名 属性值类型 说明
sid string 每日一句ID
tts string 音频地址
content string 英文内容
note string 中文内容
love string 每日一句喜欢个数
translation string 词霸小编
picture string 图片地址
picture2 string 大图片地址
caption string 标题
dateline string 时间
s_pv string 浏览数
sp_pv string 语音评测浏览数
tags array 相关标签
fenxiang_img string 合成图片,建议分享微博用的

返回示例:

{
    "sid": "3369",
    "tts": "http://news.iciba.com/admin/tts/2019-04-23-day.mp3",
    "content": "There is no such thing as a great talent without great will.",
    "note": "没有伟大的意志力,便没有雄才大略。",
    "love": "197",
    "translation": "小编的话:正如爱迪生所说一般,强者容易坚强。只有坚强的意志力才能给我们克服各种困难的勇气和决心。",
    "picture": "http://cdn.iciba.com/news/word/20190423.jpg",
    "picture2": "http://cdn.iciba.com/news/word/big_20190423b.jpg",
    "caption": "词霸每日一句",
    "dateline": "2019-04-23",
    "s_pv": "0",
    "sp_pv": "0",
    "tags": [
        {
            "id": null,
            "name": null
        }
    ],
    "fenxiang_img": "http://cdn.iciba.com/web/news/longweibo/imag/2019-04-23.jpg"
}

请求示例:

type sentence struct {
	Content     string `json:"content"`
	Note        string `json:"note"`
	Translation string `json:"translation"`
}

func getsen() (sentence, string) {
	resp, err := http.Get("http://open.iciba.com/dsapi/?date")
	sent := sentence{}
	if err != nil {
		fmt.Println("获取每日一句失败", err)
		return sent, ""
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取内容失败", err)
		return sent, ""
	}

	err = json.Unmarshal(body, &sent)
	if err != nil {
		fmt.Println("每日一句解析json失败")
		return sent, ""
	}
	fenxiangurl := gjson.Get(string(body), "fenxiang_img").String()
	fmt.Println(sent)
	return sent, fenxiangurl
}

这里使用了golang自带的http包发起了一次get请求,然后将返回的json数据解析出来,另外使用了gjon这个包,该包可以直接从json字符串中解析出需要的字段,十分方便。(其实这段代码我偷了个懒,如果内部有错误应该将错误return出去,大家不要学我啊 ~~o(>_<)o ~~)

2、获取天气预报接口

调用地址:https://www.tianqiapi.com/api
请求方式:GET
请求参数:

参数 必选 类型 说明
version string v1(版本标识)
cityid 以下参数3选1 string 101120201(城市编号,不要带CN, 以下参数3选1)
city 3选1 string 青岛(城市名称,不要带市和区)
ip 3选1 string 27.193.XX.XXX(IP地址)
callback string jsonp方式

返回类型:JSON
返回示例:

{
    "cityid": "101120201",
    "update_time": "2019-04-24 18:00:00",
    "city": "青岛",
    "cityEn": "qingdao",
    "country": "中国",
    "countryEn": "China",
    "data": [
        {
            "day": "24日(今天)",
            "date": "2019-04-24",
            "week": "星期三",
            "wea": "阵雨",
            "wea_img": "lei",
            "air": 45,
            "humidity": 96,
            "air_level": "优",
            "air_tips": "空气很好,可以外出活动,呼吸新鲜空气,拥抱大自然!",
            "alarm": {
                "alarm_type": "大雾",
                "alarm_level": "橙色",
                "alarm_content": "青岛市气象台2019年04月24日02时15分继续发布大雾橙色预警信号:目前我市沿海地区和近海海域已出现能见度小于200米局部小于100米的大雾,预计今天凌晨到白天,我市沿海地区和近海海域仍将有能见度小于200米的大雾天气,局部小于100米,请注意防范。(预警信息来源:国家预警信息发布中心)"
            },
            "tem1": "16℃",
            "tem2": "10℃",
            "tem": "13℃",
            "win": [
                "南风"
            ],
            "win_speed": "3-4级",
            "hours": [
                {
                    "day": "24日20时",
                    "wea": "晴",
                    "tem": "13℃",
                    "win": "南风",
                    "win_speed": "<3级"
                },
                {
                    "day": "24日23时",
                    "wea": "多云",
                    "tem": "15℃",
                    "win": "南风",
                    "win_speed": "<3级"
                },
                {
                    "day": "25日02时",
                    "wea": "多云",
                    "tem": "15℃",
                    "win": "南风",
                    "win_speed": "<3级"
                },
                {
                    "day": "25日05时",
                    "wea": "多云",
                    "tem": "15℃",
                    "win": "南风",
                    "win_speed": "<3级"
                }
            ],
            "index": [
                {
                    "title": "紫外线指数",
                    "level": "最弱",
                    "desc": "辐射弱,涂擦SPF8-12防晒护肤品。"
                },
                {
                    "title": "减肥指数",
                    "level": null,
                    "desc": "天气较舒适,减肥正当时。"
                },
                {
                    "title": "健臻·血糖指数",
                    "level": "较易波动",
                    "desc": "血糖较易波动,注意监测。"
                },
                {
                    "title": "穿衣指数",
                    "level": "较冷",
                    "desc": "建议着厚外套加毛衣等服装。"
                },
                {
                    "title": "洗车指数",
                    "level": "不宜",
                    "desc": "有雨,雨水和泥水会弄脏爱车。"
                },
                {
                    "title": "空气污染扩散指数",
                    "level": "中",
                    "desc": "易感人群应适当减少室外活动。"
                }
            ]
        }
    ]
}

该接口会返回往后一周的天气预报,因为返回的参数过多,我只截取了当天的数据。如果需要其它数据可以自己请求一下。

请求示例代码:

func getweather(city string) (string, string, string, string) {
	url := fmt.Sprintf("https://www.tianqiapi.com/api?version=%s&city=%s", WeatherVersion, city)
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("获取天气失败", err)
		return "", "", "", ""
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取内容失败", err)
		return "", "", "", ""
	}

	data := gjson.Get(string(body), "data").Array()
	thisday := data[0].String()
	day := gjson.Get(thisday, "day").Str 	//日期
	wea := gjson.Get(thisday, "wea").Str	//天气
	tem := gjson.Get(thisday, "tem").Str	//平均气温
	air_tips := gjson.Get(thisday, "air_tips").Str		//提示
	return day, wea, tem, air_tips
}

get请求获得天气数据,gjson包将当天的天气信息解析出来后返回。同样地,偷懒错误没return出去 ?。

二、微信公众平台接口测试帐号

通过上一步我们已经成功的获取到了数据,接下来申请一个微信公众平台测试帐号,其实正式帐号的操作也是一样的,但方便起见,我们直接用测试号。

1、每日一句模板

  1. 扫码登录公众平台测试号
    申请测试号的地址 https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
  2. 手机上确认登录
  3. 找到 新增测试模板 ,添加模板消息在这里插入图片描述
    填写模板标题 《每日一句》,填写如下模板内容

{ {content.DATA}}
{ {note.DATA}}
{ {translation.DATA}}

注意:后面的.DATA必须保留,前面是你定义的字段。在这里插入图片描述
提交保存之后,记住该模板ID,一会儿会用到在这里插入图片描述

  1. 找到测试号信息,记住 appidappsecret,一会儿会用到在这里插入图片描述
  2. 找到测试号二维码。手机扫描此二维码,关注之后,你的昵称会出现在右侧列表里,记住该微信号,一会儿会用到(注:此微信号非你真实的微信号,而是你的微信在关注了该测试号后分配的在该号下的唯一ID)在这里插入图片描述

2、天气预报模板

和每日一句的添加方法一样,区别在于第3步中新增测试模板使用的模板不同:

{ {city.DATA}}
{ {day.DATA}}
{ {wea.DATA}}
{ {tem1.DATA}}
{ {air_tips.DATA}}
在这里插入图片描述

三、发送微信模板消息的程序

//发送每日一句,将json字符串拼接好后调用templatepost函数发送模板
func everydaysen() {
	req, fxurl := getsen()
	if req.Content == "" {
		return
	}
	access_token := getaccesstoken()
	if access_token == "" {
		return
	}

	flist := getflist(access_token)  //获取公众号关注人列表
	if flist == nil {
		return
	}

	reqdata := "{\"content\":{\"value\":\"" + req.Content + "\", \"color\":\"#0000CD\"}, \"note\":{\"value\":\"" + req.Note + "\"}, \"translation\":{\"value\":\"" + req.Translation + "\"}}"
	for _, v := range flist {
		templatepost(access_token, reqdata, fxurl, SentTemplateID, v.Str)
	}
}

//发送天气预报
func weather() {
	access_token := getaccesstoken()
	if access_token == "" {
		return
	}

	flist := getflist(access_token)
	if flist == nil {
		return
	}

	var city string
	for _, v := range flist {
		switch v.Str {
		case "oeZ6P5kyGsLKn3sIGRVfpb8oT4mg":
			city = "青岛"
			go sendweather(access_token, city, v.Str)
		case "oeZ6P5jvFNh2y_h_2UcaoTXBaC2o":
			city = "西安"
			go sendweather(access_token, city, v.Str)
		default:
		}
	}
	fmt.Println("weather is ok")
}

//获取微信accesstoken
func getaccesstoken() string {
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%v&secret=%v", APPID, APPSECRET)
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("获取微信token失败", err)
		return ""
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("微信token读取失败", err)
		return ""
	}

	token := token{}
	err = json.Unmarshal(body, &token)
	if err != nil {
		fmt.Println("微信token解析json失败", err)
		return ""
	}

	return token.AccessToken
}

//获取关注人列表
func getflist(access_token string) []gjson.Result {
	url := "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + access_token + "&next_openid="
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("获取关注列表失败", err)
		return nil
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取内容失败", err)
		return nil
	}
	flist := gjson.Get(string(body), "data.openid").Array()
	return flist
}

//发送模板消息代码
func templatepost(access_token string, reqdata string, fxurl string, templateid string, openid string) {
	url := "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token

	reqbody := "{\"touser\":\"" + openid + "\", \"template_id\":\"" + templateid + "\", \"url\":\"" + fxurl + "\", \"data\": " + reqdata + "}"

	resp, err := http.Post(url,
		"application/x-www-form-urlencoded",
		strings.NewReader(string(reqbody)))
	if err != nil {
		fmt.Println(err)
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(string(body))
}

//拼接json字符串,调用templatepost函数发送天气模板
func sendweather(access_token, city, openid string) {
	day, wea, tem, air_tips := getweather(city)
	if day == "" || wea == "" || tem == ""|| air_tips == "" {
		return
	}
	reqdata := "{\"city\":{\"value\":\"城市:" + city + "\", \"color\":\"#0000CD\"}, \"day\":{\"value\":\"" + day + "\"}, \"wea\":{\"value\":\"天气:" + wea + "\"}, \"tem1\":{\"value\":\"平均温度:" + tem + "\"}, \"air_tips\":{\"value\":\"tips:" + air_tips + "\"}}"
	//fmt.Println(reqdata)
	templatepost(access_token, reqdata, "", WeatTemplateID, openid)
}

在发送模板消息之前要先获取微信 accesstoken关注人列表 ,再遍历所有关注人,给每位关注人发模板消息,或者给某位特定的人发送。

四、设置定时发送

func main() {
	spec := "0 0 12 * * *" // 每天12:00
	spec1 := "0 0 7 * * *" // 每天早晨7:00
	c := cron.New()
	c.AddFunc(spec, everydaysen)
	c.AddFunc(spec1, weather)
	c.Start()
	fmt.Println("开启定时任务")
	select {}
	//weather()
	//everydaysen()
}

使用了 github.com/robfig/cron 包的定时任务,使每天早晨7点和中午12点发送天气预报和每日一句。

spec := “0 0 12 * * *”
这里对应的分别是 “ 秒 分 时 日 月 周 ” 和linux里的crontab定时任务差不多。

在阿里云Linux服务器上后台启动该程序既可。

程序运行结果截图:
在这里插入图片描述
完美解决 ,好吧,并不完美,其实代码还是有很大的优化空间,由于时间紧凑,就不优化了,知道就行,是吧O(∩_∩)O~

完整的项目代码:https://github.com/qq737310694/WechatTemplate

猜你喜欢

转载自blog.csdn.net/u012140251/article/details/89529540