微信定时推送LeetCode每日一题,再也不怕没人喊你刷题了

前段时间发过一篇关于微信机器人开发的文章,讲述了如何快速开发一个微信机器人,本篇文章就来实现一个最近开发的一个功能案例,在这个案例中会遇到了各种问题,可以帮助大家减少自己去踩坑的时间。通过此案例也可以帮助你去扩想一些更符合你的使用场景。

如果你还不了解微信机器人的开发,建议先阅读下面的文章:

只需要6行代码,就可以开发一个微信机器人

通过微信定时推送功能,每日向用户发送LeetCode的每日题目。这项服务不仅方便了编程爱好者和求职者随时练习算法题,而且解决了缺乏动力或组织者来提醒刷题的问题,鼓励读者养成每日刷题的学习习惯。

功能描述

  • 每日通过微信消息的方式,把leetcode当日题目推送到指定联系人或群聊中。
  • 给机器人发送消息,随机推送一条leetcode的题目

推送的消息中需要包含题目的一些基本信息。比如:题目名称、难度等级、通过率、解题地址等。

为了可以更直观的了解详情,也需要将题目内容进行推送。由于内容一般都是比较大的,所以采用图片的方式推送是一个不错的选择。效果如下:

在这里插入图片描述

如果你对今天的题目比较感兴趣,就可以通过点击解题地址去打卡今日任务,即使电脑不在身边,也可通过题目内容图片了解到今日题目内容

实现

在这里插入图片描述

  • 接收消息:接收到指定的消息后,推送内容,例如:每日一题、随机出题
  • 定时推送:每日定时将题目题送到指定联系人或群聊
  • 获取数据:获取leetcode题目内容
  • 整合数据:生成需要推送的文本和图片消息
  • 发送消息:将内容推送给用户

1. 获取数据

打开leetcode官网首页,可以看到在题目列表中第一项就是当题的题目。
在这里插入图片描述

打开控制台,可以看到有很多相同的URL请求,使用的是graphQL查询。一个一个的点看查看返回内容,找到今日题目的请求。
在这里插入图片描述
在这里插入图片描述

复制请求参数,构建请求。

// 获取今日题目
async function fetchTodayLeetCode() {
    const url = "https://leetcode.cn/graphql/";
    return new Promise((resolve) => {
    axios
        .post(url, {
          query: "\n    query questionOfToday {\n  todayRecord {\n    date\n    userStatus\n    question {\n      questionId\n      frontendQuestionId: questionFrontendId\n      difficulty\n      title\n      titleCn: translatedTitle\n      titleSlug\n      paidOnly: isPaidOnly\n      freqBar\n      isFavor\n      acRate\n      status\n      solutionNum\n      hasVideoSolution\n      topicTags {\n        name\n        nameTranslated: translatedName\n        id\n      }\n      extra {\n        topCompanyTags {\n          imgUrl\n          slug\n          numSubscribed\n        }\n      }\n    }\n    lastSubmission {\n      id\n    }\n  }\n}\n    ",
          variables: {},
          operationName: "questionOfToday",
        })
        .then((res) => {
            try {
            const data = res.data.data.todayRecord[0];
                resolve(data);
            } catch (error) {
                resolve("");
            }
        })
        .catch(() => {
            resolve("");
        });
    });
}

拿到返回的数据后,就可以提取想要发送的数据信息。

// 获取今天LeetCode题目
async function getTodayLeetCode() {
    //  获取题目数据
    const data = await fetchTodayLeetCode();
    if (!data) return "";
    const question = data.question;
    const { title, titleCn, titleSlug, difficulty, acRate } = question;
    let difficultyCn = "未知";
    switch (difficulty) {
        case "Hard":
            difficultyCn = "困难";
            break;
        case "MEDIUM":
            difficultyCn = "中等";
            break;
        case "EASY":
            ifficultyCn = "简单";
            break;
        default:
            break;
    }const url = `https://leetcode.cn/problems/${titleSlug}/description`;
    const text = `----每日一题----\n题目:${titleCn}\n难度:${difficultyCn}\n通过率:${Number(
            acRate * 100
        ).toFixed(2)}%\n解题地址:${url}`;
    return text;
}

在首页的请求只会返回题目的基本信息,没有内容信息,此时需要点击进入题目详情页面,按照同样的方法,找到查询题目详情的接口来获取详情内容。
在这里插入图片描述
在这里插入图片描述

这里需要注意修改查询参数titleSlug的值,改为题目中返回的titleSlug字段。

// 查询题目详情
async function fetchLeetCodeQuestionDetail(titleSlug) {
    const url = "https://leetcode.cn/graphql/";
    return new Promise((resolve) => {
    axios
        .post(url, {
            query: "\n    query questionTranslations($titleSlug: String!) {\n  question(titleSlug: $titleSlug) {\n    translatedTitle\n    translatedContent\n  }\n}\n    ",
            variables: {
            titleSlug: titleSlug,
         },
          operationName: "questionTranslations",
         })
         .then((res) => {
              try {
              const data = res.data.data.question;
                  resolve(data);
              } catch (error) {
                  resolve("");
              }
         })
        .catch(() => {
            resolve("");
        });
    });
}

2. 内容截图

题目详情接口中返回的详情内容格式为html字符串的格式,很容易就联想到将其渲染后再进行截图。

如果是在前段环境下,这很容易做到,可以直接创建一个元素,设置innerHtml为题目内容,变成真正的dom元素。然后使用html2canvas转成图片即可。

但是在node环境下是没有window对象的,相关的API也不可用。这里介绍一个强大的node.js库puppeteer来实现这个过程。

Puppeteer 是一个 Node.js 库,提供了一套高级 API 通过 DevTools 协议控制 Chrome 或 Chromium 浏览器,用于自动化测试、网页抓取、生成页面截图和 PDF 等,它的功能还远不止这些。

async function htmlText2Image(content, savePath) {
    // 启动一个新的浏览器实例
    const browser = await puppeteer.launch();
    // 创建一个新的页面
    const page = await browser.newPage();
    // 设置要渲染的HTML内容;
    const htmlContent = `
      <!DOCTYPE html>
      <html>
      <head>
          <meta charset="UTF-8">
          <title>leetcode题目</title>
          <style>
              pre {
              white-space: pre-wrap;
              }
</style>
      </head>
      <body>
          ${content}
      </body>
      </html>
      `;
    // 将HTML内容设置为页面的内容
    await page.setContent(htmlContent);
    // 将页面渲染为图片并保存到文件
    await page.screenshot({
        path: savePath,
        fullPage: true,
    });
    // 关闭浏览器
    await browser.close();
}

对图片中的内容可以灵活的自定义,比如添加额外信息,添加一些样式等,就跟我们开发一个页面一样简单。

// ...
const detail = await fetchLeetCodeQuestionDetail(titleSlug);
// 截图
const { translatedTitle, translatedContent } = detail;
await htmlText2Image(
    `<h1>${translatedTitle}&nbsp;&nbsp;<span style="font-size:18px;font-weight:400">难度:${difficultyCn}&nbsp;&nbsp;通过率:${Number(
        acRate * 100
    ).toFixed(2)}%</span></h1>${translatedContent}
    <div style="text-align:center;">
        <img src="https://resource.dengzhanyong.com/images/9ce7efc7-d880-470d-8864-9c29e242c4f5.png" style="height:120px;"/>
    </div>`,
    "这里是图片存储的路径"
);// ...

封装推送的图片消息

const { FileBox } = require("file-box");const imagePath = '这里是图片的路径';
const imageFileBox = FileBox.fromFile(imagePath);

踩坑记录

到现在为止,上面一切在本地运行测试都没问题,但当我部署到服务器(centos)上时,一系列的问题就出现了。

1. 服务器上没有chrome浏览器
使用sudo yum install chromium 去安装,会报找不到chromium这个包,可能是源不对,并且这种方式安装的chromium会有很多问题,不推荐。

推荐直接下载安装包,然后手动安装:

wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
mkdir ./google_chrome
mv google-chrome-stable_current_x86_64.rpm ./google_chrome/  
cd  ./google_chrome    # 进入目录
yum -y install google-chrome-stable_current_x86_64.rpm

2. glibc版本过低
centos7默认的glibc函数库的版本为2.17,chromium要求glibc-2.25版本,因此需要升级glibc版本。可能不同版本的chromium要求glibc不同,根据实际错误提示进行升级。

#查询已安装的glibc版本
strings /lib64/libc.so.6 | grep GLIBC

在这里插入图片描述

3. chromium拥有很多依赖库,必须把所有的必须依赖库全部安装上才可以

sudo yum install libatk1.0-0 libatk-bridge2.0-0 libcups2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango1.0-0 libasound2 

4. 需要禁用沙箱模式

在这里插入图片描述

在启动浏览器是,需要配置--no-sandbox 参数,其的作用是禁用沙箱模式,这可以解决在一些环境中由于权限问题无法正常启动浏览器的问题,并提高浏览器的启动速度。

// 启动一个新的浏览器实例
const browser = await puppeteer.launch({
    args: ["--no-sandbox"],
});

5. 截图中无法显示中文
在这里插入图片描述

使用Puppeteer进行截图时,会出现部分中文显示方块字乱码的问题。这并不是Puppeteer的问题,实际上是Linux字体库对中文支持不好的原因。
只需要给服务器的Linux系统安装支持的中文字体库即可。

sudo yum install wqy-microhei-fonts.noarch -y
sudo yum install wqy-unibit-fonts.noarch -y
sudo yum install wqy-zenhei-fonts.noarch -y

消息推送

最后只需要创建定时器,或接收到指定消息后执行上面的流程即可。

1. 创建推送列表配置信息

const = LEETCODE_PUSH_LIST: [
    {
        name: "前端筱园交流群",
        id: "@@6ab29397570b5672d1b53945432b5ce1326dc43682bf3de4dff4c3579afa4325",
        date: "0 30 9 * * *",
    }
]

2. 封装定时器

const schedule = require("node-schedule");
function setSchedule(date, callback) {
    schedule.scheduleJob(date, callback);
}

3. 机器人登录后启动定时器

bot.on("login", onLogin);async function onLogin(user) {
    console.log(`${user} 登录成功`);
    setTimeout(() => {
        leetCodePush(this);
    }, 5000);
}/**
 * 每日一题推荐
 * @param {*} that
 */
async function leetCodePush(that) {
    console.log("开启每日一题推荐");
    const list = config.LEETCODE_PUSH_LIST;
    if (list.length) {
        for (const item of list) {
        const { title, name, date, count } = item;
        setSchedule(date, async () => {
            const message = await getTodayLeetCode();
            // 找到目标群聊
            const room = await findRoom(that, name);
            if (room && message) {
                // 如果是多条消息,则逐条推送
                if (isArray(message)) {
                for (const item of message) {
                    await room.say(item);
                }
            } else {
                room.say(message);
            }
         }
        });
    }
    }
}

收到指定消息回复中间的流程都一样,只是需要判断下收到的消息是否匹配,如果是个人发送就回复给个人,如果是群聊中被@,就推送到群聊中。
在这里插入图片描述
在这里插入图片描述

通过本案例不仅是希望你了解学习到更多的关于**微信机器人chromiumpuppeteer**等相关知识。更是为了让你开拓自己的想象空间,开发更多的适合自己的使用场景,给你的生活带来便利。

写在最后

最后,如果你觉得这个功能对你很有帮助,但是自己又不想去开发,回复“交流群”加入前端筱园交流群,就可以免费体验啦。
可以在群内添加前端筱园机器人为好友,独自享受更多功能服务。

欢迎访问我的个人网站:www.dengzhanyong.com

欢迎加入前端筱园交流群:
描述文字
关注我的公众号【前端筱园】,不错过每一篇推送

描述文字

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/789631.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS Next应用开发之系统概述

一、鸿蒙系统概述 鸿蒙系统可以分为华为鸿蒙系统&#xff08;HUAWEI HarmonyOS&#xff09;和开源鸿蒙系统&#xff08;OpenHarmony&#xff09;&#xff0c;华为鸿蒙系统是基于OpenHarmony基础之上开发的商业版操作系统。他们二者的关系可以用下图来表示&#xff1a; 1.1、…

Python 轻松生成多种条形码、二维码 (Code 128、EAN-13、QR code等)

条形码和二维码是现代信息交换和数据存储的重要工具&#xff0c;它们将信息以图形的形式编码&#xff0c;便于机器识别和数据处理&#xff0c;被广泛应用于物流、零售、医疗、教育等各领域。 本文将介绍如何使用Python快速生成各种常见的条形码如Code 128、EAN-13&#xff0c;…

CentOS7 安装 git 命令

通过yum源install下载的git版本比较低&#xff0c;不推荐此方式安装。 官网下载最新版git源码&#xff1a;Git 1. 解压安装包 tar -xzvf git-2.45.2.tar.gz 2. 安装相关依赖 yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-ExtUtils…

如何用Vue3和Plotly.js创建交互式表格?

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 Plotly.js 动态生成 HTML 表格 应用场景介绍 在数据分析和可视化领域&#xff0c;经常需要以表格的形式展示数据。Plotly.js 是一款功能强大的 JavaScript 库&#xff0c;不仅可以创建交互式图表&#xff0c;…

【源码+文档+调试讲解】文物管理系统

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

前端与嵌入式开发通信之QWebChannel(Qt)

前端与嵌入式开发通信之QWebChannel 最近开发中需要用到和c开发的操作台进行通信的的需求&#xff0c;就找到了这个技术&#xff0c;记录一下 首先需要安装导入 qwebchannel npm i qwebchannel import { QWebChannel } from "qwebchannel"; 初始化qwebchannel并封…

嵌入式音频处理技术的现在发展及未来的方向

嵌入式音频处理技术&#xff1a;从音频流媒体到声音识别 嵌入式音频处理技术的迅猛发展正在改变我们的生活方式&#xff0c;从音频流媒体到声音识别&#xff0c;这个领域为人们的生活和工作带来了巨大的影响。本文将探讨嵌入式音频处理技术的最新趋势和应用&#xff0c;以及提…

安防管理平台LntonCVS视频汇聚融合云平台智慧火电厂安全生产管理应用方案

中国的电力产业作为国民经济发展的重要能源支柱&#xff0c;被视为国民经济的基础产业之一。目前&#xff0c;我国主要依赖火力发电&#xff0c;主要燃料包括煤炭、石油和天然气等&#xff0c;通过燃烧转化为动能&#xff0c;再转变为电能输送至全国各地。火力发电量占全国发电…

告别混乱,可道云企业网盘个人标签,让文件管理更轻松

在信息爆炸的时代&#xff0c;你是不是常常觉得自己的大脑就像一台过载的处理器&#xff0c;各种文件、资料、想法在脑海中“打架”&#xff0c;让你焦头烂额&#xff1f; 别担心&#xff0c;可道云企业网盘个人标签功能来拯救你的“大脑内存”了&#xff01; 我们需要告别无…

tensorflow之欠拟合与过拟合,正则化缓解

过拟合泛化性弱 欠拟合解决方法&#xff1a; 增加输入特征项 增加网络参数 减少正则化参数 过拟合的解决方法&#xff1a; 数据清洗 增大训练集 采用正则化 增大正则化参数 正则化缓解过拟合 正则化在损失函数中引入模型复杂度指标&#xff0c;利用给w增加权重&#xff0c;…

如何分析软件测试中发现的Bug!

假如你是一名软件测试工程师&#xff0c;每天面对的就是那些“刁钻”的Bug&#xff0c;它们像是隐藏在黑暗中的敌人&#xff0c;时不时跳出来给你一个“惊喜”。那么&#xff0c;如何才能有效地分析和处理这些Bug&#xff0c;让你的测试工作变得高效且有趣呢&#xff1f;今天我…

AWS-WAF-Log S3存放,通过Athena查看

1.创建好waf-cdn 并且设置好规则和log存储方式为s3 2. Amazon Athena 服务 使用 &#xff08;注意s3桶位置相同得区域&#xff09; https://docs.aws.amazon.com/zh_cn/athena/latest/ug/waf-logs.html#waf-example-count-matched-ip-addresses 官方文档参考,建一个分区查询表…

内容协商源码解析与自定义 MessageConverter

目录 内容协商 1、引入xml依赖 2、postman分别测试返回json和xml 3、开启浏览器参数方式内容协商功能 4、内容协商原理 5、自定义 MessageConverter 综上 内容协商 根据客户端接收能力不同&#xff0c;返回不同媒体类型的数据。 若客户端无法解析服务端返回的内容&#…

SAP SD销售订单的ATP检查简介

前面的文章中我们解释了PP模块中的ATP检查,也解释了MM模块中的ATP的检查,本文将说明一下SD模块中的ATP检查。 SAP 销售ATP(可用性检查)详解 ATP(Available-to-Promise)检查是SAP中的一项关键功能,用于确保在创建销售订单时能够满足客户需求。本文将详细介绍SAP销售ATP检…

【人工智能】-- 搜索技术(状态空间法)

个人主页&#xff1a;欢迎来到 Papicatch的博客 课设专栏 &#xff1a;学生成绩管理系统 专业知识专栏&#xff1a; 专业知识 文章目录 &#x1f349;引言 &#x1f348;介绍 &#x1f349;状态空间法 &#x1f348;状态空间的构成 &#x1f34d;状态 &#x1f34d;算符…

一文带你快速了解项目ASPICE评估的那些事-MUNIK

01、摘要 随着汽车电动化、智能化和互联化不断演进&#xff0c;汽车的电子电气架构得到持续升级&#xff0c;而汽车硬件方面逐渐趋向标准化。与此同时&#xff0c;汽车软件呈现出不断多样化和日益复杂的趋势。在这个大背景下&#xff0c;传统的软件开发流程已经无法满足这一需…

第4章 课程发布:模块需求分析,课程预览(模板引擎 静态页面),课程审核,课程发布(分布式事务,页面静态化:熔断降级),课程搜索(es索引)

1 模块需求分析 1.1 模块介绍 课程信息编辑完毕即可发布课程&#xff0c;发布课程相当于一个确认操作&#xff0c;课程发布后学习者在网站可以搜索到课程&#xff0c;然后查看课程的详细信息&#xff0c;进一步选课、支付、在线学习。 下边是课程编辑与发布的整体流程&#…

C++ 编译体系入门指北

前言 之从入坑C之后&#xff0c;项目中的编译构建就经常跟CMake打交道&#xff0c;但对它缺乏系统的了解&#xff0c;遇到问题又陷入盲人摸象。对C的编译体系是如何发展的&#xff0c;为什么要用CMake&#xff0c;它的运作原理是如何的比较感兴趣&#xff0c;所以就想系统学习…

2008年上半年软件设计师【上午题】真题及答案

文章目录 2008年上半年软件设计师上午题--真题2008年上半年软件设计师上午题--答案 2008年上半年软件设计师上午题–真题 2008年上半年软件设计师上午题–答案

CSS【详解】边框 border,边框-圆角 border-radius,边框-填充 border-image,轮廓 outline

边框 border border 是以下三种边框样式的简写&#xff1a; border-width 边框宽度 —— 数值 px&#xff08;像素&#xff09;,thin&#xff08;细&#xff09;,medium&#xff08;中等&#xff09;,thick&#xff08;粗&#xff09;border-style 边框线型 —— none【默认值…