从 wasm-bindgen 到 C-ABI 写微信小程序WASM:一次真实的 WASM 落地复盘

在写完 ESP8266 IoT 架构之后,我就在想一件事情:既然代码开源,IoT 网关岂不是谁都能连? 于是我试图在微信小程序端做文章。但开源的小程序不能硬编码密钥,也不能仅靠接口鉴权。 我想到了很早之前预研的 WebAssembly(WASM)。配合后端防护,可以大幅提升攻击者的成本。 一、背景与目标 实现的功能非常简单: 传入 小程序 APPID + 设备 ID + 时间戳,使用 Ed25519 签名,返回 Token。 以前在微信小程序调通过 WxWebAssembly(简称 wxwasm),但当时主要处理 Int 类型,这次需要返回字符串。 当我拿出以前的预研代码进行修改时,我才意识到: “能用 Rust 写 WASM” 和 “能在微信小程序里稳定运行”,完全是两回事。 二、第一阶段:理想很丰满 —— Rust + wasm-bindgen 最初的环境是: Rust 1.95 wasm-bindgen wasm-pack Cargo.toml [dependencies] wasm-bindgen = "0.2" ed25519-dalek = { version = "2.1", features = ["rand_core"] } hex = "0.4" lib.rs [wasm_bindgen] pub fn generate_token(appid: &str, device_id: &str, timestamp: &str) -> Result<String, JsValue> { // Ed25519 签名逻辑 } 编译很顺利(就是有点慢 😭),本地 test.html 测试一切正常。 ...

2026-05-24 · 3 min · Eagle

一步一步将ESP8266 WIFI 配网搞定并丝滑体验

准备入手ESP网关项目了,手头有一块esp8266的开发小板子,就先从esp8266开始入手。白天搞定了ESP8266 的UDP配网编码和小程序端的控制编码。就一个功能模块给ESP8266配置WIFI网络。这是必须的入门操作,因为WIFI 的SSID和密码不能真的写入到代码中。 网络配置有很多种方式,我选择了SoftAP配网,虽然配置有点繁琐,但这是一种很可靠的配网方案,兼容性极高,不依赖手机硬件的特殊协议。以前做过HTTP配网,这是2025年时做的,到现在已经忘的差不多了。现在捡起来再回忆一下。 准确来说,这不应该算是一个项目,但因为它具有通用性和实用性。我决定把它记录下来,可以方便地应用到我的其它项目中。 Soft配网原理 初始化模式:ESP8266 启动后进入 WIFI_AP_STA 模式。它会开启一个无密码(或已知密码)的热点(AP),并运行一个轻量级的 Web 服务器(HTTP Server)。 通道建立:手机通过小程序或系统设置连接到该热点。此时手机与 ESP8266 处于同一个局域网内。 数据交互:小程序通过 HTTP Post 请求将目标 WiFi 的 SSID 和 Password 发送给 ESP8266 的固定接口(如 /config)。 校验与切换:ESP8266 收到参数后,尝试作为客户端(STA)连接路由器。如果连接成功,则关闭 AP 热点,保存参数到 Flash;如果失败,则返回并重置到AP状态。 ESP8266端核心代码(Arduino IDE)和小程序端代码 在Arduino IDE打开项目后,需要安装ESP8266 WebServer库。这个库提供了Web服务,可以接收其它HTTP客户端的连接。 它的核心是实现了一个非常简单的Web服务,它监听/config接口并能够接收WIFI信息,以及接收之后可以本地存储,实现动态配置的功能。 下面是Arduino端的代码片段: #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> // 定义AP热点的名称 const char* ap_ssid = "ESP8266_Config_Device"; ESP8266WebServer server(80); void handleConfig() { // 处理SSID和密码逻辑 } void setup() { // 设置为AP+STA模式 WiFi.mode(WIFI_AP_STA); WiFi.softAP(ap_ssid); // 注册接口 server.on("/config", HTTP_POST, handleConfig); server.begin(); } void loop() { server.handleClient(); if (WiFi.status() == WL_CONNECTED) { // 如果连接成功,可以根据需要在这里处理业务逻辑 } } 完整代码可以从https://91demo.top/tools/下载。小程序端则充当了HTTP客户端,它用来作为输入WIFI信息的终端。这个界面非常简单,一个WiFi信息提交表单,包含了SSID信息和密码信息,以及一个提交按钮。 ...

2026-04-29 · 3 min · Eagle

对于微信小程序的一些回忆和思考

有些事,不做笔记真的意识不到已经过去了这么久。 最近在整理博客内容时,我翻到了小程序的最早版本记录。那是 2018 年,最初的它简陋得甚至有点滑稽:整个页面只有一个按钮,点一下,生成一个随机数。 一、豆子工具小程序 这个小程序项目名称“Wander”,它的小程序名称换了很多,只有到微信开始要求备案和实名后,才开始真正的确定下来。 这几年里,我断断续续的维护着这个项目,是由于自己没事的时候喜欢做一些东西,当忙起来时又会忘记了它,当有新的想法时,会改变它,然后更换微信小程序名称。印象深刻的有几次:1,随机数,纯粹是为了小程序上线,为了学习小程序,做了最简单的示例,然后上线了。2,图片集,制作了一个图片列表,然后展示图片,图片存储在云服务上,那是还是免费,并且提供两个环境,为了学习云存储,云函数。3,找厕所,这是我最费心最耗时最吃力的一次改进,为了学习地图map,最后没有完成放弃了。4,自用工具,音频格式转换,图片格式转换,MD5哈希等小工具,从这之后应该算是入门了吧,毕竟以前是自学,现在因为有了工作经历,感觉自己进步了,思维和技巧不在停留在简单的示例教程上。5,工具+项目,以前自学的一些项目以及现在新做的模块都包含进了这个小程序,工具还是自己在用,项目呢,可以用来分享给有需要的人。 在这几年里,我在这款小程序上倾注了太多的时间和精力,看起来是总在变化,没有一个明确的目标,但是现在回忆总结一下,还有在一个圆内,只不过半径大了一些而已。它像是我个人开发者生涯的一个“活化石”,记录了我每一个阶段遇到的问题和想出的方案。 总结了最近几年开发过和上线了的一些内容。现在的“豆子工具【已备案】”已经从当年的随机数生成器,进化成了一个覆盖网络、多媒体、开发调试的全能工具箱: 算法:随机数生成,MD5,SHA256,RSA,AES,Base64,URL编码等。 网络:获取公网IP地址和归属地,获取 WIFI 公网地址、云厂商安全组管理、查服务端口、域名证书检测,局域网TCP、UDP、WebSocket调试,ESP网络配置等。 多媒体:音频格式转换(m4a 转 mp3)、图片格式转换(png 转 webp)、二维码识别与生成,图片拼接等。 系统功能:邮件通知、微信服务通知、微信聊天分享,打开文档,导出文档等。 小程序一直在迭代中,这中间的很多功能因为自身原因或者微信审核政策的原因,遗憾地消失在了版本更迭中。 二、豆子碎片小程序 这个小程序项目名称“Visit”,它的微信小程序名称也是变更了多次,只有在备案时才确定了下来。不过它的内容现在已经融入了豆子工具v11.0.0版本中。只是为了维护方便。 这个Visit小程序在Wander小程序一年后注册,刚开始做的功能就是浏览 Go Web 框架 Beego 的教程。当时,正在自学 Beego,而 Beego 的网站时常打不开,就想做一个小程序,可以浏览 Beego 内容。刚做出来的一段时间,因为新鲜度小火了一把。 后来又切换到唐诗主题,当时自己费劲的采集了小学的古诗词作为内容源。印象最深的是添加拼音,当时没有AI,从网上搜索和自己琢磨。最后还是放弃了,一是因为素材的原因,没有解析,没有音频,界面美观一般(回忆来看)。到备案的时候,卡住了,个人主体不允许出现诗词这些关键字。要改名字了,我觉得内容不符合,就换内容了,对于我来说,名字需要和内容匹配,心里才会舒服。当时为了选内容而发愁,因为不想放弃这个小程序。想了几天后,觉得把自己的开发经验做成笔记吧。因为碎片化,所以叫豆子碎片。添加豆子前缀,是因为当时备案时,需要区分小程序名称。觉得豆子比较简单上口。那段时间学了towxml,将内容使用Markdown来编写。 这之后,虽然也改过游戏关卡,作品集,模块集市。但都是通过Markdown来编写和展示内容。 三、思考现在与未来 今年已经是2026年,AI相当发达,我以前收录的代码片段和想法思考,AI已经分分钟替代,并且做的更好。现在的小程序在备案和实名后,自己的小程序更是无人问津。到了今天,已经没有精力再去想尝鲜了,为了一个新出的功能或者想法去搞好几天去研究了。总结了小程序最近几年的变更,虽然内容一直在变,但是自己在技术上并没有新的突破,依旧是列表详情布局,依旧是本地和后端联动。以前围绕着微信小程序为中心,当抬高视野时,发现局限性很大,小程序很多内容是不能做的,很多功能是做不了的。当前的豆子工具是功能小程序,已经可用的功能如音频格式转换,图片格式转换继续保留。豆子碎片当前的模块集市依旧保留,它是一个项目列表,可以查看详情并可获取到源码和实用客户端工具。所以可以将豆子碎片和豆子工具的内容合并到一起,减少小程序的维护,可以有更多的精力去做一些其他事情。2026年已经计划在博客中写文章了,已经不再使用小程序记笔记,因为小程序的缺陷很明显,特别对于长篇文章。那么未来就可以这样,首页网站可以做Web实验性项目,博客中写项目实现和想法,豆子工具中做小程序最合适的工具和上架模块源码,公众号主要用来引流和发布一些比较好的博客文章,或者发布一些工具更新,或者一些好的开源项目。以后的内容可以深入一些,实用一些,比如以前的模块,现在可以做一个产品。然后继续新的方向,桌面客户端工具和嵌入式开发尝试,继续深入Rust和Go。

2026-04-24 · 1 min · Eagle

小程序激励广告防刷演进史:从前端校验到“挑战模式”

在开发“模块集市”时,用户通过观看激励广告获取源码下载地址。虽然对接广告并不难,但如何防刷成了我最头疼的问题。由于小程序激励广告缺乏服务器回调(Server-to-Server),奖励的发放完全依赖前端触发,这给了一些“羊毛党”可乘之机。 在接口防护的过程中,我经历了四个阶段: 第一阶段:身份鉴权(解决匿名刷取) 我的获取奖励接口是getAdReward。调用它就可以获取奖励,后台进行发放。最初,由于经验不足,我没有对它做任何防护。这样任何人任何客户端都可以进行访问。了解小程序微信账号体系后,我添加了鉴权,只有携带Token的用户可以访问。这样可以保证只有能够调用wx.login的用户才可以访问。 这解决了一部分问题,但是又有了新的问题,它防不住“真实用户”,只要用户登录后拦截并解析出接口地址和 Token,就可以跳过广告手动调用了getAdReward接口,没有观看广告就获取了奖励。 第二阶段:共享密钥加密(解决接口暴露) 为了防止直接调用接口,我引入了对称加密。前端与后端约定一个硬编码的共享密钥,在小程序端和服务端同时使用这个共享密钥加密随机数和时间戳进行校验,匹配则发放奖励。这样可以防止fidder等工具直接拦截调用接口。 它正常运行了一段时间,我发现又有了新的情况。用户可能通过反编译或者其它方式获取到了共享密钥。这在一段时间内对我造成困扰,除了定期更新共享密钥,我没有好的办法,但更新共享密钥需要升级发布小程序。直到当我了解到小程序提供加密网络通道时,我发现这是一个好的解决方案。 第三阶段:微信加密网络通道(解决密钥安全) 首先,我们来了解一下加密网络通道,它是微信为小程序提供的一套加密KEY机制,它可以同时在微信小程序端和服务器端获取相同的密钥。由于密钥由微信官方通道生成且动态更新,反编译源码也拿不到密钥。除非攻击者能拦截微信的网络通道,否则无法伪造解密过程。同时,配合时间戳校验,有效防御了初级的重放攻击。这对于我来说,是非常好的利好消息,我不需要在源码中硬编码共享密钥了。 我们可以参考获取短信验证码,短信验证码之所以安全,是因为应用和短信验证码是两个不相同的通道。同样的这个加密KEY,在小程序获取和服务端获取都是通过微信的网络通道,和自己的应用通道也不是一个。对于我的小程序稍加改造就可以了。在获取奖励的接口中,首先使用wx.getUserEncryptKey获取微信的加密KEY,然后使用自定义的AES加密方法,将要传递的时间戳和Nonce等参数加密后,将加密数据提交给服务器就可以了。当数据到达服务器,服务器同样调用微信的接口获取微信的加密KEY,然后进行AES解密。这样就可以防止获取共享密钥的弊端。 好了,我们来看看具体如何使用?为了避免小程序与开发者后台通信时数据被截取和篡改,微信侧维护了一个用户维度的可靠key,用于小程序和后台通信时进行加密和签名。开发者可以分别通过小程序前端和微信后台提供的接口,获取用户的加密 key。 1,小程序端获取: const somedata = 'xxxxx' const userCryptoManager = wx.getUserCryptoManager() userCryptoManager.getLatestUserKey({ success({encryptKey, iv, version, expireTime}) { const encryptedData = someAESEncryptMethod(encryptKey, iv, somedata) wx.request({ data: encryptedData, success(res) { const decryptedData = someAESDEcryptMethod(encryptKey, iv, res.data) console.log(decryptedData) } }) } }) 其中,someAESEncryptMethod 和 someAESDEcryptMethod 分别为加解密函数,由开发者自行引入加解密库来实现,基础库暂时不提供加解密能力。 2,服务端获取: 在开发者服务端,可以调用getUserEncryptKey后台接口获取用户最近三次的key。在获取key的同时,接口会携带version信息,开发者可以比较version版本来选择使用对应的key对数据进行加解密。 curl -X POST "https://api.weixin.qq.com/wxa/business/getuserencryptkey?access_token=ACCESS_TOKEN&openid=OPENID&signature=SIGNATURE&sig_method=hmac_sha256" 其中,openid是用户的openid,signature用sessionkey对空字符串签名得到的结果。即 signature = hmac_sha256(session_key, “"),sig_method为签名方法,固定为hmac_sha256。 这个是核心,获取激励广告奖励基于它再添加一些参数进行加解密。 第四阶段:后端“挑战模式”(解决重放与逻辑伪造) 我在参数中添加的Nonce,它是一个噪音参数,它是小程序端本地生成的,还有时间戳也是本地生成的,增加它们是为了增加解密难度。在实际运行中,我发现硬核玩家会通过修改本地时间戳和 Nonce(随机数)来绕过检测,或者使用相同的参数进行重放攻击,或者不够15s(广告正常播放最短时间)调用多次接口。 为了解决这个问题,我将逻辑升级为“挑战模式”:首先,小程序端在广告开始前进行一次预请求,前端必须先调用 getNonce 接口获取Nonce然后使用这个Nonce进行加密,getNonce是一个新增接口,用来生成Nonce,并记录生成时间和关联openid,服务端存入缓存(如 Redis),完成服务端打标。当前端再次调用getAdReward接口时,如果差值小于广告常规时长(如 15s),则判定为非正常观看,直接拒绝。 ...

2026-04-07 · 1 min · Eagle

我是如何设计和实现模块源码集市的?

当我开发完51单片机温度采集并转换Modbus RTU后,我一度想分享出去,但是我又不想草率的去分享。我一直在思考,如何能让这些代码发挥更大的价值,而不仅仅是躺在个人仓库里。作为一名摸爬滚打多年的开发者,我手头积累了大量来自实践的代码、模块和解决方案。它们散落在硬盘的各个角落,像一颗颗未经打磨的珍珠。 于是,我萌生了做一个“模块源码集市”小程序的想法——一个可以直接浏览、查阅、并一键下载优质源码的平台。 经过最近一段时间的集中开发,这个小程序终于和大家见面了。今天,我想和大家分享其背后的设计与思考,希望能为有相似想法的朋友提供一些参考。这个小程序的核心功能简洁而实用: 首页全景浏览:所有上架的代码模块都在首页清晰展示,你可以快速了解其名称、简介和分类,对全站资源一目了然。 沉浸式详情阅读:点击任一模块,即可进入详情页。这里我使用了Markdown渲染引擎,来展示模块的详细介绍、使用说明、技术要点等。Markdown的优雅排版能让技术文档的阅读体验大幅提升。 一键获取源码:在详情页,点击获取模块源码链接按钮,即可轻松获取模块的完整源码压缩包。 为了让这个简单的流程稳定、安全、可扩展,我在几个关键环节做了一些设计和取舍: 动态资源与安全通道源码文件并非直接打包在小程序内,而是存储在后台的MinIO对象存储中。当用户点击下载时,服务端会动态生成一个具有时效性的访问链接返回给小程序。这样做的好处是资源可以随时更新。同时,为了维持后端服务器持续运行,我在列表及详情中接入了少量广告。为了保护下载接口不被恶意刷取,我集成了激励式视频广告。用户观看一则简短的广告,即可解锁下载权限。这不仅是简单的广告接入,更重要的是,我为此构建了一套防刷机制,我使用了微信的加密网络通道,对关键数据进行加密传输和验证,极大地增加了自动化盗刷的成本和难度,保护了服务器资源。 灵活的沟通与通知我深知,一个“活”的产品需要与用户保持沟通。因此,小程序内设置了系统消息中心,我可以在这里向所有用户下发重要的系统公告、模块上新以及更新日志。更重要的是,我为每个模块的上新功能,接入了微信服务通知。当有新的、优秀的代码模块入库时,订阅了的用户会在早上8点后收到一条微信消息提醒,能让你随时跟上“仓库”的更新节奏。 持续生长的内容生态目前上架的或正在上架的模块,覆盖了我擅长的多个领域:小程序、Go语言后端工具、Rust系统编程实践、Web前端片段、嵌入式软硬件代码,包含音视频、网络、加密等技术要点,形式包括客户端、命令行工具、固件、小程序、网页等。这只是一个开始。我的计划是将其作为一个长期项目,持续将我实践中验证过的、有价值的代码沉淀、整理并开源出来。未来,我还会在项目详情页中,增加关联的公众号技术文章跳转,或是有功能关联的其他小程序跳转,形成一个立体化的技术知识网络。 坦率地说,这个小程序的开发过程远超我最初的预期。从架构设计到具体实现,尤其是几个关键的技术卡点上,耗费了我大量的精力。 与广告盗刷的攻防:我的服务器每天都有人在扫描,所以在实现激励广告防刷机制时,我花了大量时间加强防刷机制,其实这个在微信小程序社区中也可以看到很多类似的问题。我这里研究了一种解决方案。就是使用加密网络通道。在对接的过程中,我与各种加密错误、签名错误(如常见的40079错误)作斗争。例如,处理会话密钥时,发现不需要进行Base64编码,而是直接以空字符串的字节形式处理;在URL拼接的细节上,POST与GET方法的误用也会导致失败;而加解密环节更是陷阱重重——加密密钥需要从Base64解码,IV却是一个直接的16位字符串,加密数据则需要从十六进制格式解码……这些细枝末节,任何一个出错都会导致整个流程崩掉,往往需要清空Token重新登录来排查。这个过程让我对网络数据安全有了更深刻的认识。这些错误和经验本想专开一篇文章介绍,但是细想一下,可能一段话就能总结,但是其中的辛酸和感悟无法表达在纸面上。索性后期整理出来开源这个模块,减少其他开发者的一些坎坷。 存储方案的抉择:在对象存储的选择上,也有一段小插曲。我最熟悉的是Mino,但是在我了解到MinIO版本已归档,便尝试了rustfs等其他方案进行探索。但经过综合对比在可靠性、API友好度以及与现有技术栈的契合度后,最终还是选择了功能完备、文档清晰的MinIO作为存储后端,事实证明这个选择是稳定高效的。 所幸,在这个艰难的过程中,AI编程助手给予了我巨大的帮助(但是也让我吃了很多苦头,因为它上面的资料都比较旧)。许多代码片段的生成、调试过程中错误信息的解读、API文档的快速查询,都因AI的介入而效率倍增。如果没有它,单靠我自己,恐怕几个月也难见成果,绝无可能在十几天内推动项目成型上线。 做这个小程序,并非为了追逐热点,这个小程序刚刚上线,还需要时间去细细打磨。开发其初心很简单:整理自己的知识资产,并以一种对开发者最友好、最便捷的方式分享出去。我希望它不仅仅是一个下载站点,更可以成为一个高质量的、经过实战检验的“代码片段集市”和“工具箱”。路漫漫其修远兮。在代码世界的探索中,每个人都是学生,也是老师。我在这里抛砖引玉,期待这些代码模块能真正帮助到在具体问题上寻找解决方案的你。也欢迎你常来看看,这些项目模块,会和我一样,在技术的道路上不断生长。 扫码体验:

2026-03-26 · 1 min · Eagle

Modbus转MQTT网关固件研发与共创计划

一、 项目愿景:让设备上云,像接线一样简单 新的一年,新的计划,我们致力于为工程师、创客及小团队,打造一套开箱即用、稳定可靠的工业物联网数据采集方案。你无需深究复杂的协议栈,只需聚焦业务,即可让传统设备轻松上云。 本项目的核心价值在于: 协议翻译官:打造一个高性能的“中间件”,将底层的Modbus协议(支持RS485/TTL)无缝转换为上层的MQTT协议,打通设备与云端的“最后一公里”。 软硬解耦:不绑定特定硬件。无论是WiFi环境(ESP32)还是4G环境(Cat.1模组),只要具备基本通信能力,软件就能赋予其“智能”,提供极大的硬件选型自由度。 解决技术断层:解决有业务需求但没有研发实力的小团队(如硬件工程师、创客、小厂),本项目提供了一种低成本解决方案。它只需通用开发板,就能实现数据采集和上云,花小钱办大事。 二、 技术架构:三位一体的全栈实现 为确保方案的极致稳定与高度可扩展,我们采用“固件 + 配置工具 + 演示端”的三位一体架构,确保方案的完整性与专业性。 核心固件(“翻译官” - 逻辑版) 固件架构:采用分层架构设计,硬件抽象层隔离具体芯片与通信接口(如RS485/TTL),实现核心逻辑与硬件的解耦;协议栈层实现完整的Modbus协议栈(主/从站,RTU/TCP),并进行高效、健壮的JSON封装;服务层内置MQTT客户端,负责可靠的数据上传、断线重连与本地缓存;配置管理层,通过Rust编写的PC工具下发设备配置,实现软件定义功能。 技术栈:采用C语言开发,优先实现裸机(No OS)逻辑版本,确保在资源受限的MCU上也能稳定运行,降低用户使用门槛。 功能:负责实时采集Modbus寄存器数据(如电压、电流),并根据配置的倍率进行数据转换,最后通过MQTT协议将数据打包上传。 PC配置工具(“指挥官” - Rust版) 技术栈:使用Rust语言开发,利用其内存安全特性,确保配置过程绝对可靠,工具长期运行不崩溃。 功能:提供图形化界面,用户可轻松配置通信模式、寄存器地址、倍率等参数。支持通过串口(USB转TTL) 进行固件刷新与参数下发,解决现场实施难题,无需复杂的网络配置。 数据演示端(“仪表盘”) 技术栈:微信小程序。 功能:扫码即可查看实时数据曲线与数值面板,直观验证数据上云效果,方便现场调试与演示。 三、 当前状态:诚邀你,成为“共创者” 项目正处于核心研发阶段,当前全力攻克裸机版本,实现通信方式支持WiFi和4G的Modbus RTU(串口)协议的稳定性与性能。它适合简单应用,保证运行稳定。我们相信,最好的产品源于真实场景的千锤百炼。 未来会研发通信方式支持LoRa、以太网的Modbus TCP,以及FreeRTOS多任务版,用于解决需要同时处理多路数据采集,复杂业务逻辑的高端场景。 因此,我们发起此次共创计划: 你的支持,将直接转化为测试硬件(ESP32、各类Modbus传感器),用于极限环境下的兼容性验证。 你的设备,可以寄来成为我们的“适配样本”,共同完善设备库。 你的反馈,将直接塑造产品的下一个版本。 四、未来承诺 当固件通过大量真实场景验证,达到工业级稳定标准后,我们将正式申请软著,并启动授权售卖。所有共创阶段的支持者,都将依据历史贡献,获得丰厚的升级折扣与永久优先支持权。 我入驻了爱发电,更多详情内容请查看:https://afdian.com/a/modujson

2026-02-24 · 1 min · Eagle

基于 Go + 小程序构建“口袋指令中心”,实现远程发布控制

我日常涉及 Hugo 博客发布、客户端打包、Nginx 运维等多种重复性脚本。每次都要 SSH 连服务器并执行命令,操作链过长,也不方便,特别是在身边没有电脑的情况。 曾经想过使用HTML 远程调用,又担心安全,使用了微信公众号发消息,也特适合这个场景,但是因为微信更新,私信入口现在非常难进入。所以就想能否通过自己的小程序实现远程控制和鉴权。构建一个通用的执行引擎,通过小程序远程触发,且具备零前端修改(免于小程序上线审核)的扩展能力。 系统架构设计 为了实现“明天增加脚本,小程序不发版”的目标,采用了“配置驱动” 模式。即“配置在云端,指令在指尖”。通过将业务逻辑(脚本路径与名称)完全从前端小程序中解耦,实现一套代码支持无限扩展的运维能力。 核心流程 后端 (Go):维护一个脚本配置列表(数据库或配置文件)。 前端 (小程序):启动时请求后端接口,拉取可用脚本列表。 触发:使用时选择脚本名称,点击执行。 鉴权:后端校验小程序 OpenID,仅允许本人指令生效。 技术实现 系统分为三层,确保安全性与扩展性的统一: 配置层 (Go Config):在服务器端定义脚本的 ID、名称和实际路径。 鉴权层 (WeChat Auth):利用微信小程序 OpenID 建立强一致性的身份白名单。 展示层 (Mini Program UI):动态拉取后端配置,仅负责“展示列表”与“触发指令”。 技术实现方案 A. 后端:动态脚本引擎 (Go) 后端不再硬编码脚本路径,而是定义一个结构体: // 脚本任务定义 type ScriptTask struct { ID string `json:"id"` // 前端传递的任务标识 Name string `json:"name"` // 小程序界面显示的文字 Command string `json:"-"` // 实际执行的脚本路径 (对前端保密) } // 示例配置(可存放在 JSON 文件或数据库中) var tasks = []ScriptTask{ {ID: "hugo-post", Name: "发布 Hugo 文章", Command: "/scripts/deploy_hugo.sh"}, {ID: "build-client", Name: "构建客户端", Command: "/scripts/build_mole_go.sh"}, {ID: "nginx-restart", Name: "重启 Nginx 服务", Command: "systemctl restart nginx"}, } 对外接口定义: ...

2026-02-04 · 3 min · Eagle

实战笔记:实现一个语音验证码远程呼叫版本

在实现了主动使用VOIP客户端拨打8000号码播报语音验证码的功能后,我发现了一个最大的缺点,就是这需要用户主动去操作。这对于想使用API集成的第三方应用来说根本无法实现。 在思考之后,我决定实现一个可以调用API就呼叫VOIP客户端的版本,当呼叫用户并且用户接通后,自动播报语音验证码的功能。当实现这个功能后,它的好处是显而易见的。比如,可以集成到嵌入式,集成到第三方网站,直接调用API就可以呼叫出去。 那么该如何实现它呢? 一、 系统原理 传统的拨号方案(Dialplan)是静态的,而 ARI 允许我们动态控制。整个“API 触发呼叫并播报”的流程如下: 触发阶段:第三方系统通过 API 向 Go 服务发送呼叫请求(包含目标 ID 和验证码)。 呼叫发起(Originate):Go 服务调用 Asterisk ARI 的 /channels 接口。此时 Asterisk 会尝试向 PJSIP 终端(或通过中继向手机)发起呼叫。 接通监听(Stasis Start):一旦用户接起电话,该通道会被移交给一个名为 Stasis 的应用。此时 Go 服务会收到一个“通道已接通”的 WebSocket 事件。 语音合成与播放:Go 服务识别到接通后,调用播报指令(可以播放预录音文件,或对接 TTS 引擎生成的语音流)。 挂断处理:播报完毕后,服务发送挂断指令,释放资源。 二、系统架构 [第三方API] --> [Go 后端服务] --(REST API)--> [Asterisk ARI] | | (WebSocket) (PJSIP/IMS) | | [接通状态回调] <--- [用户终端接听] 三、 核心代码实现 (Golang) 这里使用了 GitHub 上的 go-ari 库。 1. 初始化 ARI 客户端 import ( "github.com/v5" "github.com/v5/client/native" ) // 连接到 Asterisk ARI cl, err := native.Connect(&native.Options{ Application: "voice-verify", // 必须与 asterisk.conf 配置一致 Username: "admin", Password: "password", URL: "http://localhost:8088/ari", }) 2. 实现呼叫并播报语音验证码 这是核心逻辑:接收参数 -> 发起呼叫 -> 监听接通 -> 播放语音。 ...

2026-01-13 · 3 min · Eagle

Markdown 进化论:从小程序文章列表到游戏关卡到时间轴作品集再到项目模块集市

Markdown越来越玩的纯熟,在小程序架构稳定之后,剩下的就是Markdown内容的编辑,我已经不满足于基础的标题和段落,我还会根据适当的场景使用。我必须把常用的Markdown介绍一下,感觉其设计真是奇妙,比如在一行中用三个以上的星号、减号、底线来建立一个分割线,行内不能有其它东西。也可以在星号或减号中间插入空格。这样就是一条分割线。当你完全掌握之后,就会发现我们还可以进行扩展,比如我后面的跳转公众号文章,跳转小程序。 我们先来快速浏览一下除了标题和段落之外不常用但重要的Markdown语法。不感兴趣可以跳过。 *斜体文本* _斜体文本_ **粗体文本** __粗体文本__ ***粗斜体文本*** ___粗斜体文本___ # 分割线 *** * * * ***** - - - --------- ~~删除线~~ <u>下划线文本</u> [^脚注,要注明的文本] # 无序列表 * 第一项 + 第二项 - 第三项 # 有序列表 1. 第一项 2. 第二项 3. 第三项 # 列表嵌套需要在子列表中的选项签名添加两个或四个空格即可。 1. 第一项: - 第一项嵌套的第一个元素 - 第一项嵌套的第二个元素 2. 第二项: - 第二项嵌套的第一个元素 - 第二项嵌套的第二个元素 # 区块 > 最外层 > > 第一层嵌套 > > > 第二层嵌套 # 列表中使用区块,如果列表中要使用区块,请在>前添加四个空格即可。 * 第一项 > 菜鸟教程 > 学的不仅是技术更是梦想 * 第二项 # 链接 [链接名称](链接地址) <链接地址> # 图片显示 ![alt 属性文本](图片地址) ![alt 属性文本](图片地址 "可选标题") ![alt 属性文本](图片地址居中#pic_center) ![alt 属性文本](图片地址宽和高 =400x200) # 只设置宽,会按比例进行缩放 ![alt 属性文本](图片地址 =400x) # 使用 html <img>标签。 <img src="https://xxx.com/x.png" width="50%"> # 制作表格使用|来分割不同的单元格,可以使用-来分割表头和其它行。 | 表头 | 表头 | | ---- | ---- | | 单元格 | 单元格 | | 单元格 | 单元格 | # 设置表格的对齐方式使用如下符号: -: 设置内容和标题栏居右对齐。 :- 设置内容和标题栏居左对齐。 :-: 设置内容和标题栏居中对齐。 # Markdown 使用了很多特殊符号来表示特定的意义,如果需要显示特定的符号则需要使用转义字符,Markdown 使用反斜杠转义特殊字符: **文本加粗** \*\* 正常显示星号 \*\* 使用 KaTex 或 MathJax 来渲染数学表达式。 Mardown语法扩展 当然我还扩展了一些内容,比如链接,我在小程序中支持跳转到公众号文章,以及跳转到小程序。具体的实现就是通过前缀。 ...

2025-09-03 · 1 min · Eagle

万物皆可 Markdown:小程序动态内容渲染通用架构设计

在学习了微信小程序之后,我使用wxml页面可以做出唐诗的内容了。结合wxss,我可以定义好看的布局。在小程序需要备案时,因为唐诗需要资质,而我个人无法满足这些条件,所以我需要开发新的项目。恰好最近在为我的技术笔记发愁。我希望有一个可以展示我笔记内容的工具。 我的最初想法非常简单: 1,可以更新文章内容而不需要升级小程序。 2,需要在小程序端进行展示,并且需要美观。 3,能够根据标题或者关键字进行搜索文章内容。 经过一番研究后,我决定小程序使用原生开发,界面 UI 使用 WeUI,内容渲染使用Markdown。 小程序页面架构是列表+详情。列表作为首页,首页页面非常简单,一个标题和描述,用来解释小程序干什么?一个搜索框用来搜索内容,三个快捷按钮用于快速查找内容。 这个也是为了小程序审核,避免成为资讯类主题。 在完成之后,我发现这套架构可以通用,用来渲染动态内容。像列表页面以及WEUI使用,熟悉前端的非常容易理解实现。下面是一些核心介绍,主要介绍Markdown的渲染实现。 在小程序中,无法直接展示 Markdown 数据,我们需要将 Markdown 转换为 WXML,网上的大神很多,有多款 Markdown 转 WXML 库,我使用的是 TOWXML 库。我使用Markdown主要是因为更新内容不用升级小程序,这是动态渲染的最大优点。当然它还有其它一些优势,比如容易编辑,支持数学公式,布局美观等。 下面是Markdown具体实现,不感兴趣可以跳过。可以看技术下面的架构思考。 1,按照 towxml 库的说明文档,我们将编译后的 towxml 复制到我们的项目中,然后在 app.js 文件中引入这个组件。 App({ towxml: require('./towxml/index'), 2,按照首页的步骤,我们添加文章详情页面。 "pages/article/index" 在 index.wxml 文件中,我们添加页面布局,这个很简单,显示渲染后的内容即可。 <!--pages/article/index.wxml--> <view class="page"> <!--使用towxml--> <towxml nodes="{{article}}" /> </view> 3,注意,我们还需要在 index.json 中添加组件引入。 { "usingComponents": { "towxml": "../../towxml/towxml" } } 4,在 js 文件中,我们需要调用 Markdown 数据。 onLoad(options) { const _ts = this; wx.showLoading({ title: '加载中', }) httpGet('/artd', { uuid: options.guid, }).then((res) => { const result = res.data; if (result.code == 1) { let content = result.data; let obj = app.towxml(content, 'markdown', { theme: 'light', events: { tap: (e) => { console.log('tap', e); } } }); _ts.setData({ article: obj, }); wx.hideLoading({ success: (res) => { }, }) } else { wx.hideLoading() } }).catch((err) => { console.log(err); wx.hideLoading() wx.showToast({ title: '网络异常请重试', }) }) }, 我们在 onLoad 中加载函数,这样页面启动就会自动网络请求,然后渲染 Markdown 数据。在请求数据时,我们注意到 guid,这个参数是上一个页面即首页,点击文章列表项带过来的数据。 你也可以使用其它参数,它能唯一代表从服务器拉取哪篇文章的Markdown内容。 ...

2025-06-03 · 2 min · Eagle