前言
本文是基于https://www.cnblogs.com/connect/p/python-wechat-iciba.html 这篇博客写成的。该博客实现了用python将金山词霸的每日一句推送到微信公众测试号,我想既然python能实现,那么用Golang也可以。
特别提醒:另外是实际项目开发中,先在正式微信公众号平台找到合适的模板,获取到模板内容设置,然后在测试号中根据获取到的模板设置添加测试模板,获取到测试模板的模板id进行开发(遇到过的坑,因为不知道需要先去正式平台上获取模板内容设置,导致项目上线时找不到可用的模板) 。
运行环境
1、阿里云Linux服务器
2、Go开发环境
完整的项目代码:https://github.com/qq737310694/WechatTemplate
获取接口数据
1、每日一句接口
参数 | 必选 | 参数 | 必选 |
---|---|---|---|
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
返回示例:
该接口会返回往后一周的天气预报,因为返回的参数过多,我只截取了当天的数据。如果需要其它数据可以自己请求一下。
请求示例代码:
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,一会儿会用到
4、找到测试号信息,记住 appid 和 appsecret,一会儿会用到
5、找到测试号二维码。手机扫描此二维码,关注之后,你的昵称会出现在右侧列表里,记住该微信号,一会儿会用到(注:此微信号非你真实的微信号,而是你的微信在关注了该测试号后分配的在该号下的唯一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服务器上后台启动该程序既可。