Skip to main content

自动推送微信消息和课表

· 20 min read
Chengzihan

前言

在上一篇博客中,我介绍了写好的爬取课程表的代码,但是这个代码只能在本地运行,但是现在我希望可以使用微信来进行课程提醒。但是配置服务器无疑是比较麻烦的,经过分析我发现定时任务和代码执行都可以使用 GitHub Action 执行。所以我就想到了使用 GitHub Actions 及 GitHub API 来实现这个功能。本篇文章将介绍如何使用我的代码实现课程与每日天气自动推送

涉及的工具和技术

  • Python
  • GitHub Actions
  • 微信公众平台开发
  • 腾讯云函数 SCF

GitHub Actions 介绍

1

GitHub Action 是 GitHub 提供的一项功能,可以在 GitHub 上运行自定义的脚本,可以用来自动化构建、测试、打包、发布或部署任何项目。GitHub Action 是 GitHub 为开发者提供的一项功能,可以在 GitHub 上运行自定义的脚本,可以用来自动化构建、测试、打包、发布或部署任何项目。

微信公众测试号

注册

我们要使用微信公众号来帮助我们传递消息。但是一般个人能注册的订阅号是不能满足我们的要求的,每天只能推送一条图文消息。但是微信为我们提供了测试号,测试号可以满足我们的需求,毕竟课表推送我们仅仅个人使用,不需要太多的人数。所以一个测试号就够了。

首先打开微信公众平台测试号注册。扫码登录后,点击立即开通,然后填写信息,就可以注册完成了。

扫码关注

在界面上找到二维码,让需要使用的用户扫码关注。

2

获取高德天气 API

不需要这个的可以跳过这一步。

注册

打开高德开放平台,点击右上角注册,然后填写信息,就可以注册完成了。

创建应用

注册成功后,打开控制台,在左侧选择应用管理,点击我的应用,然后在右上角选择创建新任务。应用名称和应用类型可以随意填写,然后点击创建

1

添加 Key

2

接着,填写信息,选择 Web 服务。然后点击提交。名称可以随便取。

2

记住这个生成的 Key ,之后会用到。

3

在 GitHub 上 Fork 本项目

打开本项目 仓库地址,点击右上角 Fork 并顺便点击 Star 纳入你的收藏,就可以将本项目全部代码复制到你的仓库中了。

2

然后,克隆源代码到你的本地。

在线编辑项目源代码

但是,实际上我们并不需要做过多修改,只需要一点点的参数修改就可以完成项目的构建了。那么,克隆在本地也就不再必要了,这里将为希望省事的人或者不会使用 Git 的人提供一个方法,直接在 GitHub 上修改代码。

GitHub 提供了一种能力叫做 GitHub Codespaces,可以在浏览器中直接修改代码,不需要下载任何东西。我们只需要把你刚刚克隆的仓库 <你的昵称>/wx_weather_class_push 中浏览器上方导航栏的链接中 github.com 改为 github.dev ,就可以启动在线 VScode ,便于你在浏览器中修改代码了。

https://github.com/<你的GitHub昵称>/wx_weather_class_push

修改后:

https://github.dev/<你的GitHub昵称>/wx_weather_class_push

启动过程,请稍等片刻:

2

启动成功,出现编辑器界面:

3

tip

右下角可能提示让你安装 Python 依赖,安装即可。

字太小了,我们可以把字体放大一点。点击左下角的设置图标。将文字大小设置为 20.可以按照自己的喜好设置。

1

修改配置

本项目提供了两种功能,可以每天早上道早安每天晚上道晚安同时在每节课开始前可以推送课程信息。这两个内容互相独立,可以只执行一个,也可以两者都执行。

修改早安推送代码

早安推送代码对应文件 morning.py。在左侧文件栏中找到 morning.py,点击打开。

2

在这个功能中,你只需要修改 user_id_list 即用户列表。

user_id_list = [
{
'user_id': user_id_1, # 用户 ID,不需要在这里修改
"name": 'Orange', # 用户昵称,可以修改,随便写
"date": "2021-04-02", # 纪念日,可以修改
"birthday": "05-28", # 生日,可以修改
"city": "110108" # 城市代码,可以修改,在高德 API 中查询
}
]

这里 可以查询你的城市的天气接口代码。

2

同时,你可以添加多个人,只需要在 user_id_list 中再添加一个字典即可。但是请注意,你需要添加一个新的 user_id 环境变量。

user_id_list = [
{
'user_id': user_id_1, # 用户 ID,不需要在这里修改
"name": 'Orange', # 用户昵称,可以修改,随便写
"date": "2021-04-02", # 纪念日,可以修改
"birthday": "05-28", # 生日,可以修改
"city": "110108" # 城市代码,可以修改,在高德 API 中查询
},
{
'user_id': user_id_2, # 用户 ID,不需要在这里修改
"name": 'Orange', # 用户昵称,可以修改,随便写
"date": "2021-04-02", # 纪念日,可以修改
"birthday": "05-28", # 生日,可以修改
"city": "110108" # 城市代码,可以修改,在高德 API 中查询
}
]

修改晚安推送代码

晚安代码对应 eve.py 在基础使用中,不需要修改。

修改课表信息代码

课表代码对应文件 classPush.py。在左侧文件栏中找到 classPush.py,点击打开。

修改用户列表

首先,同上,你可以修改 user_id_list 即用户列表。

2

修改学期信息

修改学年号和学期开始时间。除了下面高亮的两行( semesterfirstDay )外,其他不要修改。

id = os.environ["STUDENT_ID"]  # 学号
pwd = os.environ["PASSWORD"] # 密码
# 微信公众号 ID
app_id = os.environ["APP_ID"]
# 微信公众号 app_secret
app_secret = os.environ["APP_SECRET"]
semester = '2022-2023-1' # 学期
firstDay = '2022-08-29' # 学期开始日期
# ...省略

修改教务系统链接

tip

本套代码适用于强智教务系统,如果你的教务系统不是强智,请自行寻找 API 进行适配。

然后,你可以修改两个链接,将前面的前缀地址改为你学校的教务系统地址。即把 <http://newjwxt.bjfu.edu.cn/> 改为你学校的教务系统地址。后面的链接不要改动。

# ...省略其他代码
def Crawl():
loginLink = "http://newjwxt.bjfu.edu.cn/app.do?method=authUser&xh=" + id + "&pwd=" + pwd
rep = requests.get(loginLink)
res = json.loads(rep.text)
# 使用账号密码换取网站 token
token = res["token"]
tableUrl = "http://newjwxt.bjfu.edu.cn/app.do?method=getKbcxAzc&xh=" + id + "&xnxqid=" + semester + "&zc=" + week
header = {
"token": token # 传入 token ,鉴权
}
res = requests.get(url=tableUrl, headers=header)
schedule = json.loads(res.text) # 读取课表 json
# ...省略其他代码

修改每节课开始时间

这一步就稍微有点复杂了,不过也很好理解,我们需要判断当前时间在课表的哪个时间段,来判断现在应该要上哪一节课。

先获取现在的时间,由于我想要提前进行提醒,所以我把时间推迟20分钟,这样就可以提前20分钟提醒了。(就是说要去查询20分钟后的课程)

# 获取现在时间
now = datetime.datetime.now()
# 获取现在时间的小时和分钟
hour = now.hour
minute = now.minute + 20 # 查询20分钟后的课程
second = now.second
# 分钟加20后,可能需要进行进位,如果分钟大于60,小时加1,分钟减60
if minute >= 60:
hour += 1
minute -= 60

接着,我们要把处理好的时间转换成字符串,方便后面的比较。

# 如果小时小于10,前面加0
if hour<10:
nowTime = '0' + str(hour) + ':' + str(minute) + ':' + str(second)
else :
nowTime = str(hour) + ':' + str(minute) + ':' + str(second)
# 如果进位后时间为 24:00:00 ,则改为 00:00:00
if hour==24:
nowTime = '00' + ':' + str(minute) + ':' + str(second)

然后,我们要判断现在时间在课表的哪个时间段,来判断现在应该要上哪一节课。我们把课表的几个关键点创建为变量。例如,我们学校的时间表如下:

# 8:00-9:35 第1-2节
# 9:50-12:15 第3-5节
# 13:30-15:05 第6-7节
# 15:20-16:55 第8-9节
# 18:30-20:55 第10-12节

我们只需要记录开始时间。

dt1 = datetime.datetime.strptime('08:00:00', '%H:%M:%S')
dt2 = datetime.datetime.strptime('09:50:00', '%H:%M:%S')
dt3 = datetime.datetime.strptime('13:30:00', '%H:%M:%S')
dt4 = datetime.datetime.strptime('15:20:00', '%H:%M:%S')
dt5 = datetime.datetime.strptime('18:30:00', '%H:%M:%S')

但是,Github采用UTC时间,北京时间比UTC时间早8小时。所以我们必须把上述时间减去8小时。

dt1 = datetime.datetime.strptime('00:00:00', '%H:%M:%S')
dt2 = datetime.datetime.strptime('01:50:00', '%H:%M:%S')
dt3 = datetime.datetime.strptime('05:30:00', '%H:%M:%S')
dt4 = datetime.datetime.strptime('07:20:00', '%H:%M:%S')
dt5 = datetime.datetime.strptime('10:30:00', '%H:%M:%S')

将现在的时间(准确来说是二十分钟后的时间)存为变量。

dtNow = datetime.datetime.strptime(nowTime, '%H:%M:%S')

然后计算整个时间段的秒数。例如 8:00-9:35 的秒数为 95 * 60 = 5700,依此类推。用现在的时间减去课程开始的时间,如果结果大于 0 且小于这个时间段的秒数,那么就是在这个时间段。

if 0 <= (dtNow - dt1).seconds < 5700:
return 1
elif 0 <= (dtNow - dt2).seconds < 8700:
return 3
elif 0 <= (dtNow - dt3).seconds < 5700:
return 6
elif 0 <= (dtNow - dt4).seconds < 5700:
return 8
elif 0 <= (dtNow - dt5).seconds < 8700:
return 10
else:
return -1

添加 Secrets

在上面的代码中,有许多涉及隐私的东西。比如学号、密码、用户ID等。我们不希望这些东西在源代码被公开,所以我们需要把它们添加到 GitHub Secrets 中。

下面举一个例子:

在上面我们需要把学号存储为密匙,在代码中,它的变量名为 STUDENT_ID 。 打开你 Fork 后你的仓库。点击 Settings,然后点击 Secrets,点击 New repository secret,在 Name 中输入 STUDENT_ID,在 Value 中输入你的学号,点击 Add secret

2

3

4

5

然后点击添加。

按照上述步骤,你还需要添加下面内容:

APP_ID # 测试公众号的ID
APP_SECRET # 测试公众号的密匙
KEY # 高德应用的密匙
PASSWORD # 教务系统密码
STUDENT_ID # 学号
TEMPLATE_ID # 早安模板ID
TEMPLATE_ID_CLASS # 课表模板ID
TEMPLATE_ID_EVE # 晚安模板ID
TEMPLATE_ID_NOCLASS # 无课模板ID
USER_ID_1 # 用户1 ID
USER_ID_2 # 用户2 ID ,还有多个用户可以继续添加

获取 APP_ID 和 APP_SECRET

在微信公众平台测试账号页面,前面的就是 APP_ID,后面的就是 APP_SECRET

2

获取高德应用的密匙 KEY

就在上面的步骤中。

获取模板 ID

在微信公众平台测试账号页面创建模板。

2

目前,你按需可以创建四个模板。如下添加:

3

在这里我把全部模板提供给你,你也可以修改。

# 上课模板
20分钟后即将上课: 课程名称: {{kcmc.DATA}} 上课节数: {{sksj.DATA}} 上课地点: {{jsmc.DATA}} 课程教师: {{jsxm.DATA}} {{words.DATA}}
# 无课模板
一会没有课嗷,休息一会或者自习吧!
# 早安模板
早上好! 今天 {{cityname.DATA}} ,天气 {{weather.DATA}} ,温度 {{temperature.DATA}},风向 {{winddirection.DATA}},今天是我们第 {{love_days.DATA}} 天,距离生日还有 {{birthday_left.DATA}} 天! {{words.DATA}}
# 晚安模板
晚安! 月亮坠入不见底的河,星星垂眸惊动了舸。晚安~~ {{words.DATA}}

创建好模板后,在侧边栏复制模板 ID。填入 TEMPLATE_IDTEMPLATE_ID_CLASSTEMPLATE_ID_EVETEMPLATE_ID_NOCLASS 中。

提交代码

代码编写完后,点击左侧分支按钮。

2

然后在上面随意输入信息。然后点击上面的对勾提交。

2

当仓库首页出现提交信息说明提交成功了。

2

腾讯云定时服务

背景

实际上 GitHub Pages 本身就能实现定时服务。让我们看看官方文档是怎么说的:

2

Note: The event can be delayed during periods of high loads of GitHub Actions workflow runs. High load times include the start of every hour. To decrease the chance of delay, schedule your workflow to run at a different time of the hour.schedule

注意:在 GitHub Actions 工作流高负载运行期间,事件可能会延迟。高负载时间包括每小时的开始时等。为了减少延迟的可能性,请安排工作流在每小时的不同时间运行。

实测使用 GitHub Pages 的定时服务,延迟在 10 分钟到 1 小时不等,极端条件下定时不会执行。我们果断抛弃这个方式。

而 GitHub Actions 支持 workflow_dispatch 触发器(请参阅 GitHub Docs 上的触发工作流的事件,因此如果您手动触发工作流,它将立刻执行。这意味着您可以使用第三方 cron 调度服务,如 腾讯云、IFTTT、Google Cloud Scheduler 等,向 GithubAPI 发出请求以触发工作流。

获取 GitHub Token

在你的 Github 主页,选择 setting
1
选择左下角的Developer settings
2
然后选择 Personal access tokens
3
填写相关信息,选择 无限期 , 勾选 repoworkflow 。点击创建。
4
然后生成完成,之后它会生成一串密码:
5
在你的 Github 主页,选择 setting
1
选择左下角的Developer settings
2
然后选择 Personal access tokens
3
填写相关信息,选择 无限期 , 勾选 repoworkflow 。点击创建。
4
然后生成完成,之后它会生成一串密码:
5

注意

请记住这一串密码,你将不再能看到它。

创建腾讯云函数

进入 腾讯云 并点击注册。可以使用微信登录。点击右上角进入控制台,搜索 云函数

2

你需要创建三个云函数,分别是:morningeveningclass。下面以 morning 为例。

点击新建云函数:

3

不选择模板,从头开始。

3

填写相关参数,选择环境 python

3

提高超时时间。

3

清空原有的代码,填入新的代码:

2

代码结构如下,你只需要修改 TOKEN (上面一步获取的) 这几参数以及 https://api.github.com/repos/<YOUR GITHUB NAME>/wx_weather_class_push/dispatches 中的 GitHub 用户名即可。

import requests

def run():
headers = {
'Accept': 'application/vnd.github+json',
'Authorization': 'token <TOKEN>',
# 请把 <TOKEN> 替换为你的 GitHub Token
}
data = '{"event_type": "morning"}'

response = requests.post(f'https://api.github.com/repos/<YOUR GITHUB NAME>/wx_weather_class_push/dispatches', headers=headers, data=data)

# 云函数入口
def main_handler(event, context):
return run()

点击启用日志。

2

然后点击完成。进入详细界面的代码界面,点击测试。

3

查看下面的日志:

3

如果出现 204,则表示成功。其他状态码均为失败。

然后到 GitHub 仓库查看 Actions,可以看到已经成功触发了工作流。

2

勾变为蓝色,表示成功。

3

这时手机收到了推送。

2

然后,在侧边中创建定时:

2

点击创建触发器。

3

其中填写 Cron 表达式。

0 0 8 * * * *   # 每天 8 点
0 0 18 * * * * # 每天 18 点
0 0 7 * * 1-5 * # 周一到周五 7 点
0 30 6 * * * * # 每天 6 点半

点击提交后,会在每天指定时间触发执行。

你还需要完成两个云函数,分别是 eveningclass。可以参考上述步骤。其中课表推送需要多个触发器,参考如下:

3

3

3

这需要根据你的课程表自行修改。

在源代码仓库的 run.py 中,由上面三个函数的源码。

支持

如果你觉得这个项目对你有帮助,欢迎给我一个 Star。

仓库地址:wx_weather_class_push