利用微信服务号实现早安自动化

7 min read

最近抖音一直刷到利用微信服务号实现早安自动化,感觉挺有意思的,所以自己也整了一个。参考项目 daily_morning,该项目托管于 Github Actions ,确实方便一些不懂编程的朋友很快入门,不过身为有点基础且有服务器的博主来说,必须整活。

原项目中描述为:给别人家的女朋友发早安,所以仅仅只给一个人别人的家的女朋友发显然是不够的,博主简单用 PHP 重写了一下,新功能如下:

  • 薅光免费资源 支持100人群发/10个模板(不可能所有的宝都叫一个名字吧),舔一个人叫舔,舔一百个人叫…
  • 实时推送详情 实时通知推送成功/失败数量和具体名字

具体效果如图所示(推送提醒 仅主人才会收到)

image-20220823192356048

食用方法

申请测试号

前往 微信公众平台 申请 接口测试帐号 ,因为只有 服务号模板消息 推送功能, 订阅号没有( 服务号 需要 企业 才能申请

https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

获取appID和appsecret

登录过后我们得到一个测试号,我们需要保存的信息 appIDappsecret

image-20220823193325724

获取微信号

假设我们们要推送给张三,让张三扫描左侧的 测试号二维码 关注,右侧会列出 微信号 ,我们把需要推送的用户的 微信号 保存下来

image-20220823195922330

获取模板ID

我们在 模板消息接口 中点击 新建测试模板模板标题 即为推送卡片的标题,这里博主填写为 早安呀~模板内容 填入如下内容

今日天气: {{weather.DATA}}
当前温度: {{temp.DATA}}
今日温度: {{tempRange.DATA}}
今天是我们的第 {{loveDay.DATA}} 天
距离你的生日还有 {{birthDay.DATA}} 天
{{rainbow.DATA}}	

image-20220823193351672

我们再次重复上面的步骤,模板标题 填写 推送提醒模板内容 填入如下内容

共推送 {{count.DATA}} 人
成功: {{successNum.DATA}} | 失败: {{errorNum.DATA}}
成功用户: {{success.DATA}}
失败用户: {{error.DATA}}	

这样我们就得到了两个 模板ID ,保存下来。

image-20220823193339767

填写配置

目前的得到的信息清单如下,直接对照着填入下面的 php 代码中即可

appID,appsecret,微信号,两个模板ID

另外,开源地址为 GoodMorning,欢迎 starfork

<?php
header('Access-Control-Allow-Origin:*');

$config = array(
    'APP_ID' => 'appID',
    'APP_SECRET' => 'appsecret',
    'TEMPLATE_ID' => '默认模板ID',
    // 一次舔多人,最多可以舔100个人,模板最多只能10个
    'user' => array(
        array(
            // name可看做是备注
            'name' => '001',
            'id' => '被推送人的微信号',
            // 这里是开始恋爱的日期,格式必须是 2017-01-01
            'date' => '2017-01-01',
            // 城市只定位到市级即可
            'city' => '潍坊',
            // 这里是对方的生日日期,格式必须是 05-20
            'birthday' => '05-20',
            // 每个编号可对应一个模板,为空则使用默认模板ID
            'TEMPLATE_ID' => '早安呀的模板ID',
        ),
        // 这里是多用户写法
        // array(
        //     'name' => '002',
        //     'id' => '被推送人的微信号',
        //     'date' => '2017-01-01',
        //     'city' => '济南',
        //     'birthday' => '05-20',
        //     'TEMPLATE_ID' => '',
        // ),
    ),
    // 用来推送是否成功
    'MASTER_ID' => '主人的微信号',
    'MASTER_TEMPLATE_ID' => '推送提醒的模板ID',
);

$getToken = json_decode(GET("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=". $config['APP_ID'] ."&secret=". $config['APP_SECRET']), true)['access_token'];

$url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' . $getToken;

$userList = array('success' => array(), 'error' => array());

foreach ($config['user'] as $k => $v){
    $weather = getWeather($v['city'])['weather'];
    $temp = getWeather($v['city'])['temp'] . '℃';
    $tempRange = getWeather($v['city'])['low'] . '℃~' . getWeather($v['city'])['high'] . '℃';
    $loveDay = getLoveDay($v['date']);
    $birthdayDay = getBirthday($v['birthday']);
    $rainbow = getRainbow();

    $data = array(
        'touser' => $v['id'],
        'template_id' => $templateID = $v['TEMPLATE_ID'] ? $v['TEMPLATE_ID'] : $config['TEMPLATE_ID'],
        'data' => array(
            'weather' => array(
                'value' => $weather,
                'color' => getRandomColor(),
            ),
            'temp' => array(
                'value' => $temp,
                'color' => getRandomColor(),
            ),
            'tempRange' => array(
                'value' => $tempRange,
                'color' => getRandomColor(),
            ),
            'loveDay' => array(
                'value' => $loveDay,
                'color' => getRandomColor(),
            ),
            'birthDay' => array(
                'value' => $birthdayDay,
                'color' => getRandomColor(),
            ),
            'rainbow' => array(
                'value' => $rainbow,
                'color' => getRandomColor(),
            ),
        ),
    );
    $json = json_decode(POST($url, $data), true);
    if($json['errcode'] == 0){
        array_push($userList['success'], $v['name']);
    }else{
        array_push($userList['error'], $v['name']);
    }
}

$success = implode(',', $userList['success']);
$error = implode(',', $userList['error']);
$successNum = count($userList['success']);
$errorNum = count($userList['error']);

$data = array(
    'touser' => $config['MASTER_ID'],
    'template_id' => $config['MASTER_TEMPLATE_ID'],
    'data' => array(
        'count' => array(
            'value' => $successNum + $errorNum,
        ),
        'success' => array(
            'value' => $success,
        ),
        'error' => array(
            'value' => $error,
        ),
        'successNum' => array(
            'value' => $successNum,
        ),
        'errorNum' => array(
            'value' => $errorNum,
        ),
    ),
);

$json = json_decode(POST($url, $data), true);

if($json['errcode'] == 0){
    $reJson = array(
        'code' => 200,
        'msg' => '执行完成'
    );
}else{
    $reJson = array(
        'code' => 201,
        'msg' => '执行失败'
    );
}

echo json_encode($reJson, JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

function getRainbow()
{
    $url = 'https://tenapi.cn/chp/';
    $Json = json_decode(GET($url), true);
    return $Json['data']['text'];
}

function getWeather($city)
{
    $url = 'http://autodev.openspeech.cn/csp/api/v2.1/weather?openId=aiuicus&clientType=android&sign=android&city='. $city;
    $Json = json_decode(GET($url), true);
    return $Json['data']['list'][0];
}

function getLoveDay($time)
{
    $time = time() - strtotime($time);
    $day = floor($time / (24 * 3600));
    return $day;
}

function getBirthday($birthday)
{
    $today = time();
    $next = strtotime(date('Y') . '-' . $birthday);
    if ($next < $today) {
        $next = strtotime(date('Y') + 1 . '-' . $birthday);
    }
    return ceil(($next - $today) / (24 * 3600));
}

function getRandomColor()
{
    $str = '0123456789abcdef';
    $color = '#';
    for ($i = 0; $i < 6; $i++) {
        $color .= $str[mt_rand(0, 15)];
    }
    return $color;
}

function GET($url)
{
    $ch=curl_init((string)$url);
    curl_setopt($ch,CURLOPT_HEADER,false);
    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
    curl_setopt($ch,CURLOPT_TIMEOUT,5000);
    $result = curl_exec($ch);
    return $result;
}

function POST($url, $data)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    $output = curl_exec($ch);
    curl_close($ch);
    return $output;
}

定时推送

我们可以把上面的 php 文件上传到网站中,使用宝塔计划任务进行触发,或者说写个 shell 脚本,python 脚本之类的搭配 crontab 定时触发

image-20220823195132459