Community生产力与协作github.com

2016judea/sms-native-product

构建一个以短信而非应用或网站为界面的产品——“无应用,无网站”,“他们只需发短信到一个号码”,“通过短信原生与我们的代理聊天”,“能通过短信搞定吗”,“一个短信机器人”,“一个Telegram机器人”,“把它变成一个群聊”,“最小化摩擦”。同样适用于当事情已经出错时——“我的推广活动被拒了”,“我们的号码被屏蔽了”,“短信收不到了”,“为什么我需要注册”。覆盖了决定此类产品能否发送的门槛(美国A2P 10DLC注册、SHAFT内容规则、永久拒绝代码)、通过代码实现群组MMS、每个渠道的限制、能够承受渠道丢失的架构,以及守卫模型重写的用户内容。

sms-native-product 是什么?

这个技能聚焦于构建以短信、Telegram等消息渠道为唯一用户界面的产品,其核心洞察是“你不拥有这个渠道”——运营商掌握着内容审批权和发送资格。因此,技能首先强调在架构设计前必须通过美国A2P 10DLC注册这道“大门”,并详细列举了30953、30885等关键拒绝代码及其永久性后果,指导如何诚实描述业务类别(尤其注意SHAFT类别)以避免注册风险。技能随后提供了一套完整的、可离线测试的架构模式:将决策核心(machine.js)与渠道适配器(twilio.js、telegram.js等)严格分离,并利用模拟器(sim/)驱动时间跳跃来发现逻辑缺陷,这对处理定时、提醒和群组交互等易错场景至关重要。此外,技能深入覆盖了渠道能力边界(如群组MMS的限制、Telegram bot无法创建群组、iMessage成本高昂),并给出了模型重写用户内容时的稳健工程实践——包括限定不能编造或删除的内容、将回归测试设为可选以避免每次构建都调用API产生费用。整体上,这是一份从运营商合规、架构弹性到锁屏级文案和模型守卫的工程蓝图,适用于任何希望将消息作为产品原生于面的开发者和AI Agent。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/2016judea/brick-mortar-skills/tree/HEAD/skills/sms-native-product

Installed? Explore more 生产力与协作 skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

以短信为界面的产品

其吸引力是实实在在的:人们不喜欢安装应用或访问网站,但每个人都能发短信。陷阱在于:你并不拥有这个渠道。 美国运营商拥有它,并且他们有内容政策和审批流程,可能会永久拒绝你。这道大门决定了产品被允许成为什么,所以它必须优先处理。


第零步——大门。在设计之前做这件事,而不是之后。

自2025年2月起,美国运营商直接屏蔽未注册的A2P流量,因此品牌和活动必须在发送第一条消息之前获得批准。每次都要重新阅读最新的错误文档;运营商政策会变。

代码触发原因可重新提交?
30953露骨/暗示性语言,或链接的网站托管“色情、陪同、约会或成人娱乐内容”否
30885“高风险”——欺骗性营销,或虚假注册流量否
30898一个EIN下品牌过多,或看似重复的注册是,需说明原因

规则:在第一次回复中大声说出产品的内容类别,并说明它是否在该拒绝列表中。 如果该类别(例如约会)在拒绝规则中是一个字面关键字,那么在五分钟内发现这一点将改变架构、落地页文案和备用计划。SHAFT是简写:性、仇恨、酒精、枪支、烟草——外加大麻,约会也紧随其后。

规则:将副项目注册为个体工商户品牌,而不是挂靠在现有公司的EIN下。 风险模式会跟随税号(30898),因此副项目的拒绝绝不能影响主业务的发送能力。个体工商户的范围仅限于没有税号的个人;吞吐量较低,而对于测试版来说,低也足够了。

规则:绝不要为了获得活动批准而构建一个诱饵落地页。 隐藏实际流量的页面正是30885模式,它会将一个号码的风险转化为你注册过的所有号码的风险。坦诚说明几率,让决策者做决定。

根据其实际功能描述服务,并且仅当描述属实(例如,当消息内容确实是日记和小团体问责时)。如果没有真实描述能通过大门,那么答案是不同的渠道,而不是一个更好的委婉说法。

审核更青睐你可以指出的事实:仅限入站选择加入、对话量小、定期消息中没有链接、优先处理STOP、在入口处拒绝露骨内容。

第零步半——阅读需求文档,关注数字和对象,而非意图

将创始人的需求文档原文保留为README.md,并在CLAUDE.md中声明它的优先级高于所有工程文档。

规则:在编写状态机之前,列出需求文档中的每个持续时间以及它作用的名词。如果有两个持续时间,那么就有两个功能。 一个需求文档要求朋友回复到达作者有一周延迟,而作者自己更新有两周锁定——很容易坍塌为一个七天的关于“条目”的持有期,而这个条目恰好是两个延迟都没有针对的那个对象——之后每一个下游错误都源于此合并。

规则:明确每个延迟的对象——“对谁持有”。 对朋友持有和对作者持有是截然不同的产品。

规则:当需求文档的字面设计无法在渠道上强制执行时,说出来并提供备选方案——不要静默地选择可强制执行的那个。(群聊的创建者本身是该群的成员,所以没有代码能扣留群里的回复不给他们看。)这是一个前提决策,前提决策属于产品所有者。

一个看似合理的误读感觉不像误读。 保留被废弃的设计段落,将其划线保留,而不是删除;它向后续会话展示了如何误读了需求文档。

第一步——渠道能做什么和不能做什么

  • 通过代码发送群组短信是可行的。 Twilio Conversions仅在**+1长号码上支持群组MMS——不支持免费号码、短号码——上限为10个参与者(包括你自己的号码)**。不要凭记忆断言其他情况。
  • 群组MMS短信参与者携带一个地址而没有ProxyAddress,所以一个号码可以服务于所有对话——但参与者/代理唯一性规则(50425)对于没有代理的参与者没有记录行为。绝不要设计多个同时存在的对话共享一个号码和同一个参与者集合。 每人一个持久对话具有稳定的参与者集合。
  • 网页无法读取iPhone通讯录。 联系人选择器API仅适用于Chrome on Android。“同步联系人”从一开始就不可行;用一条用户转发的链接替代——接收者的入站短信是运营商想要的同意记录。
  • Telegram bot无法创建群组或添加任何人进群,并且开启隐私模式时,它只能听到发给它的命令。两者都有帮助:创建者将bot添加到他们已经有的聊天中,并且一般的闲聊不会被误认为是输入。创建bot需要在@BotFather中人工操作(无API);请索要令牌。
  • WhatsApp Cloud API没有群组消息功能。
  • iMessage API(Sendblue、LoopMessage)支持蓝色气泡和真正的群聊,但成本约为每条线路每月100美元,外加设置费用。

第二步——能够承受渠道丢失的架构

因为渠道可能被拒绝,传输层必须是一个文件:

machine.js   纯函数:(context, event) -> {replies, effects}   无I/O,无时钟
engine.js    上下文组装 -> machine -> effects          完整路径
effects.js   createEffects({db, channel})              执行决策
twilio.js  / telegram.js                               可互换
api/*.js     engine.js之上的薄层                       约10行
sim/         内存数据库 + 控制台渠道
  • 决策核心将now作为参数,不接触网络。 这类产品中的每个bug都是排序错误(重复触发的提醒、发送到空群组的消息、永远无法送达的持有项),并且没有任何一个可以在实时网络和七天计时器上进行测试。
  • 适配器是参数,决不在效果层中导入——模拟器和备用渠道需要同样的重构。
  • HTTP处理器内没有逻辑。 写在api/telegram.js中的渠道特定用户引导是无法测试的;将其提取出来。

第三步——驱动,而非仅仅测试

node sim/drive.js         脚本驱动:完整循环,跳过一周,守卫检查
node sim/drive.js --repl  以任意数字输入;/as <n>  /jump <days>  /state
  • 模拟器必须运行生产路径,而不是一个平行副本。
  • 预计首次驱动会出现一些单元测试无法触及的真实缺陷:一个在任何人进入之前就自行打招呼的群组、一个用于一周后日期的普通星期几、一个没有说明具体内容的提醒、一个成员全部离开的群组。
  • 当测试变红时,先检查测试本身(一个不允许Sun, Aug 30中逗号的日期正则;一个在另一个合法消息上触发的doesNotMatch)。
  • 绝不要根据存储的时间戳做决策或打印。 存储从真实时钟写入created_at,而不是引擎注入的now,所以在跳转时钟下它会撒谎(并打印过去的日期)。仅根据machien写入的列做决策,并将相关日期挂在一个存储的锚点上。

第四步——锁屏文案

一个文件容纳所有对外字符串。

  • 没有表情符号,没有感叹号,没有营销用语。
  • 对于超过一两天的日期,绝不要使用普通星期几。 Sun Aug 30。
  • 对每个关键词的标点和大小写保持宽容——Wait. 和 Yes! 必须被匹配到。
  • 包含关键词的文本仍然只是文本:“we should wait and see”不是WAIT。匹配整个规范化的消息,而不是子字符串。
  • STOP / HELP / START在所有应用状态和任何模型调用之前被解析,因此退出操作不会因其他地方的bug或API故障而损坏。
  • 如果一个模型守卫内容,失败时关闭:如果检查无法运行,不发送任何内容并告知用户。

第五步——如果模型重写用户内容,在信任之前先运行它

一个从用户条目中删除第三方名字的守卫可以完美阅读,但仍然可能:

  1. 捏造事实——被要求将“Jane Doe”替换为“自然化名”,它写成了“来自应用的那个女孩”,尽管没有任何内容提到应用;接收者将捏造视为事实。
  2. 过度纠正并删除真实细节——“来自跑步俱乐部的Jane”变成了“她”。

规则:指令必须说明模型不得编造什么和不得删除什么。 可行形式:将替换建立在源内容基础上,替换标记而非其周围的短语,并优先使用中性占位符而非重复句子已有的细节。

规则:将每个实时模型的缺陷固定为可选测试,在没有API密钥时跳过,这样提示词编辑不会在每次测试运行时静默地回归它们并增加成本。每次守卫调用预算2到5秒。

成本(数量级;实时确认)

一个+1长号码每月一两美元;每条短信每段几分钱(入站和出站);彩信几分钱。个体工商户注册是一次性几美元加上每月少量活动费用。Telegram是免费的。

询问 vs. 决定

  • 询问: 是否提交一份清楚知道可能被永久拒绝的诚实注册,或者从Telegram开始(金钱和不可逆的结果);任何改变产品本质的事情。
  • 决定: 架构、文案风格、状态机、测试覆盖率、使用哪个适配器、日期格式、守卫行为。

在报告任何内容被屏蔽之前,枚举每一个候选凭据(磁盘上一个耗尽的API密钥并不意味着没有另一个可用的)。

另请参阅: build-remote-mcp-server(另一种“原生表面”模式)。

Individual skills in this repo

This repo contains 7 individual skills — each has its own dedicated page.

2016judea/ascii-charts

Build charts as plain Unicode-block text — bar, range, bullet, gap-bracket, star-histogram, heatmap, line, Venn, pie, donut and scatter — with a dependency-free Python script, for anywhere only monospace text survives (newsletter code blocks, Markdown, terminals, plain-text email, chat). Use when a chart has to embed as literal text instead of an image or a third-party chart service, or when you want a hand-made terminal look with no branding footer.

2016judea/buffett-checklist

Apply Warren Buffett's investment framework as a discipline lens when weighing a buy, hold, or sell decision — circle of competence, economic moat, owner earnings & intrinsic value, margin of safety, management/capital-allocation quality, and market psychology (Mr. Market, fear vs greed, temperament). Use when asked to "run this through Buffett", "what would Buffett say", "does this pass the moat test", "is this cheap enough", or for a second, more skeptical read on a name being evaluated or already held. This is a discipline overlay, not a replacement for a strategy — it applies the Buffett lens to how someone actually invests, rather than issuing generic advice.

2016judea/image-riff

Turn one or more reference images into original art in that reference's own visual language, then cut it to whatever surface it's for. Use when you paste or point at art and say "use this as the base image", "generate art from this", "riff on this", "some creative ideation with these images", "make me a channel banner / avatar / cover / plate", "wilder version of this", or want a spread of options off one piece of reference. This is the TASK skill; the engine is the `make-art` tool skill.

2016judea/leonardo

Draw the way Leonardo da Vinci drew — a study sheet, a notebook folio, a red-chalk or silverpoint study, a machine cutaway, a storm of vortices — in his real materials and page-logic, generated with the make-art engine and anchored on fifteen of his actual (public-domain) sheets. Use when you say "Leonardo", "da Vinci", "like his notebooks", "a codex page of X", "draw this as one of his study sheets", "sanguine", "silverpoint", or wants a design, diagram, or illustration in that hand. Also a reading guide to his notebooks (Richter's 1883 public-domain translation) for "what did Leonardo say about shadow / trees / water". This is a TASK skill; the engine is `make-art`.

2016judea/mcp-stack

Which free MCP server or framework to reach for, and its traps — finance/gov data (SEC EDGAR, Yahoo, SAM.gov, USAspending), web research (Exa, Firecrawl, Crawl4AI, Jina reader), design/art/brand (Canva, Inkscape, Penpot, Figma, Orshot, Hugging Face image gen, vectorizing, charts), data/spatial (DuckDB spatial, live ArcGIS layers, OpenStreetMap/Overpass, Census, MapLibre), and ops (PostHog, iMessage, context7 docs). Use when making a social post, OG card, poster, logo/vector, chart, map, or brand asset; when querying parquet/geojson/duckdb or a city's open-data layer; when an MCP "needs authentication" or fails; or when asked "what tools do we have", "is there an MCP for X", "should we add X".

2016judea/records-request

Get a specific record out of a US government office by public-records request — "request the assessor's model", "file a data practices request", "FOIA this", "open records request", "can we just ask the county for it", "who do I write to at <county>", "chase that request", "did they ever answer". Covers checking the website first, finding the right office and statute, the carve-out paragraph that makes a request answerable instead of refused, web-form traps (hidden character caps, WAFs that reject apostrophes), chasing (there is usually no deadline), whether what comes back may be republished, and parsing multi-year tables without silent errors.

2016judea/vertical-market-agent

Build a chat agent that knows one industry in one metro better than any general model because it carries a precomputed digest of real public records (or a mined review corpus) and can pull live records mid-conversation — finding the moat, building a byte-stable substrate, enumerating what the records can't, the data traps that produce plausible wrong numbers, deterministic math in tools, measured prompt cost and cache TTL, shipping as an ad destination, an off-sector ending, and a precomputed spatial "what is this next to" layer. Use when building a vertical AI agent grounded in records rather than general knowledge.

相关技能