很久很久没有提笔写东西了,也意味着很久很久没有瞎折腾 Copy 大法了。我是谁?我是谁并不重要,江湖肯定没有 Copy 攻城狮的传说,不过,也许这是一篇真情露出的踩坑文。以前,听说过“If I have seen further,it is by standing on the shoulders of giants.”,而此刻我正站在Ryan Dahl[1] 和 乂乂又又[2]的肩膀上,体验万物皆可 Serverless 的 Serverless Deno ,从零到一开(kao)发(bei)然并卵的铝盆友彩虹屁 bot(目前仅仅是定时发送邮件)。
看小标题是不是猜到什么恶心的东西了?是的,正是在下!本大狮,历经九九八十一分钟(实际折腾了一宿,主要卡在 Serverless 部分了),翻阅了多处 API 文档,几经波折之后,具有辣眼睛的新鲜代码出来了:
/*
* Copyer huqi
* https://github.com/hu-qi
*/
import * as log from "https://deno.land/std@0.79.0/log/mod.ts";
import { SmtpClient } from "https://deno.land/x/smtp/mod.ts";
import {
differenceInDays,
format,
} from "https://deno.land/x/date_fns@v2.15.0/index.js";
import { zhCN } from "https://deno.land/x/date_fns@v2.15.0/locale/index.js";
import "https://deno.land/x/dotenv/load.ts";
// 很随意的入参,来自.env
const {
SEND_EMAIL,
PASSWORD,
RECV_EMAIL,
NAME_GIRL,
CITY,
CUTDOWNDATE,
CUTDOWNTHINGS,
} = Deno.env.toObject();
// 很随意的API,来自掘金
const URL = {
weather: `http://wthrcdn.etouch.cn/weather_mini?city=${CITY}`,
soup: "https://www.iowen.cn/jitang/api/",
pi: "https://chp.shadiao.app/api.php",
};
// 先配置下邮箱服务,管他行不行
const client = new SmtpClient();
const connectConfig: any = {
hostname: "smtp.163.com",
port: 25,
username: SEND_EMAIL,
password: PASSWORD,
};
// 姑且认为返回的都是结构数据
async function _html(url: string): Promise<string> {
return await (await fetch(url)).text();
}
// 目标城市的天气
async function getWeather(url: string) {
let data = await _html(url);
if (data.indexOf("OK") > -1) {
let _data = JSON.parse(data).data;
const { ganmao, wendu, forecast } = _data;
const weather = forecast[0].type;
return `天气:${weather} 当前温度:${wendu}
${ganmao}`;
} else {
return "亲爱的,今天天气真奇妙!";
}
}
// 倒计时
function getTime() {
const today = format(new Date(), "PPPP", { locale: zhCN });
const days = differenceInDays(new Date(CUTDOWNDATE), new Date());
return `今天是 ${today} ${CUTDOWNTHINGS}倒计时:${days}天`;
}
// 心灵鸡汤
async function getSoup(url: string) {
let data = await _html(url);
if (data.indexOf("数据获取成功") > -1) {
let _data = JSON.parse(data).data;
const { content } = _data.content;
return content;
} else {
return `高考在昨天,${CUTDOWNTHINGS}在明天,今天没有什么事儿!`;
}
}
// 彩虹🌈屁?
async function getPi(url: string) {
let data = await _html(url);
return data.length > 3 ? data : "你上辈子一定是碳酸饮料吧,为什么我一看到你就开心的冒泡";
}
// 早安
async function morning() {
return `
<p>${getTime()}</p>
<p>${await getSoup(URL.soup)} </p>
<p>${await getWeather(URL.weather)} </p>
<p>${await getPi(URL.pi)}</p>
`;
}
// 晚安
async function ngiht() {
return `
<p>${await getSoup(URL.soup)} </p>
<p>${await getPi(URL.pi)} </p>
<p>晚安,${NAME_GIRL}同学,今天你也是最棒的,继续加油鸭!</p>
`;
}
// 日期插件有点屌
function getTimeX() {
// 返回 “上午” 或者 “下午”
return format(new Date(), "aaaa", { locale: zhCN });
}
// 入口函数
async function main_handler() {
// 邮件正文
const content = getTimeX() === "上午" ? await morning() : await ngiht();
// 邮件标题
const greeting = getTimeX() === "上午"
? `早安, ${NAME_GIRL}`
: `晚安,${NAME_GIRL} `;
// "及时关注可能会发生的错误"
try {
await client.connect(connectConfig);
await client.send({
from: SEND_EMAIL,
to: RECV_EMAIL,
subject: greeting,
content: content,
});
await client.close();
log.info("send email success");
} catch (error) {
// "现在开始执行B计划",
// "与其关心程序的异常,不如多关注下身边的女孩子吧"
log.error(error);
log.info("Error: send email fail");
}
log.info(content);
return content;
}
// 立即执行(宫刑?)
main_handler();
不得不感叹 Deno 的生态真牛掰,想用什么插件就有什么插件,刚好满足了上边这么多需求。像这个日期库,十分丰富,无论是日期格式化、国际化还是日期常用的函数等等,考虑得很周到,像这么好用的插件,Copy 攻城狮就别学了,我是学不会的,这辈子都不可能学会的。
第一个夜晚叫初夜,第一场雪叫初雪,新闻上说这几天全国很多地方迎来了初雪,我在广州也感受到了阵阵寒意,昨晚感觉像露宿街头,冬风呼呼地吹,似乎在嘲笑我弱不经吹的技术,啪啪啪地扇了我一整宿……还好,经过腾讯云工程师的指点,我如梦初醒,终于走出了“千里冰封,万里雪飘”,迎来了部署成功的喜悦。
先说说部署 Deno 云函数大概的流程:
Deno.env.toObject()
捕获到;当然测试事件中的传参在官方模板提供的代码中也能捕获到,这样就做到了简单的可配置,改下环境变量或者输出的事件参数,我就能给其他“铝盆友”发送暖心的邮件了,甚至还可以一次配置 10 个“铝盆友”,同时发送邮件,“爱拼才会赢”!为了填这些“坑”,我差点跟鹅厂的工程师怼上了,还好不是大佬的 bug,不然我也不讲武德,在大佬的倾情讲解和耐心解答下,我也只能耗子尾汁,悻悻离去!我还年轻的时候,江湖就有“没图说个”的传说,现在老了,幸好有云平台的工单系统,还能和各个大厂的工程师进行“攻城狮和工程师的交流”。
“怼”腾讯工程师
为了避坑,我去掉了最后那行立即执行的函数,加入了官方模板中的如下代码,看样纸是捕获触发函数参数的:
// do initialize
const scf_host: string | undefined = Deno.env.get("SCF_RUNTIME_API");
const scf_port: string | undefined = Deno.env.get("SCF_RUNTIME_API_PORT");
const func_name: string | undefined = Deno.env.get("_HANDLER");
const ready_url = `http://${scf_host}:${scf_port}/runtime/init/ready`;
const event_url = `http://${scf_host}:${scf_port}/runtime/invocation/next`;
const response_url =
`http://${scf_host}:${scf_port}/runtime/invocation/response`;
const error_url = `http://${scf_host}:${scf_port}/runtime/invocation/error`;
// post ready -- finish initialization
console.log(`post ${ready_url}`);
postData(ready_url, { msg: "deno ready" }).then((data) => {
console.log(`Initialize finish`);
});
async function processEvent(evt='') {
if (evt.length === 0) {
postData(error_url, {msg: "error handling event"}).then(data => {
console.log(`Error response: ${data}`);
});
} else {
postData(response_url, {msg:`finish process event`}).then(data => {
console.log(`invoke response: ${data}`);
});
}
}
// Example POST method implementation:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return response.text(); // parses JSON response into native JavaScript objects
}
while (true) {
// get event
// 立即执行改判si缓
const responseEmail = await main_handler();
const response = await fetch(event_url);
response.text().then(function(text) {
console.log(`get event: ${text}`);
processEvent(text);
});
}
值得提一下官方模板提供的文件,请看截图,罪大恶极的就是这个deno
文件,50 多 M 大小导致无法友好地修改在线代码:
Deno 云函数模板
此次部署能得以成功,层这里处理得当时第一步,我的理解是大文件如 NodeJS 的 node_modules 之类的文件有必要放到层里,理论上 Deno 的依赖包也是同理,好在 Deno 依赖比较轻量。
其次,根据官方文档“层中的文件将会添加到 /opt 目录中,此目录在函数执行期间可访问”,我们将启动文件稍作修改:
此外,就是我们的“铝盆友”配置啦,入参随心所欲了,看您想怎么用就怎么定义,完事了代码里接一下就 OK:
配置铝盆友
不过,最终遇到时区的问题,只能暂时放一放了:
时区问题
希望各位大佬能解答一下!
最后附上Copy的代码,欢迎指教: hu-qi/deno-serverless
🏆 技术专题第七期 |万物皆可 Serverless[3]
[1]
Ryan Dahl: https://github.com/ry
[2]
乂乂又又: https://juejin.cn/user/2418581313189326
[3]
🏆 技术专题第七期 |万物皆可 Serverless: https://juejin.im/post/6894087576504926215/
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有