如何写一个 things3 client
Things3[1] 是一款苹果生态内的任务管理软件,是一家德国公司做的,非常好用。我前后尝试了众多任务管理软件,最终选定 things3,以后有机会会写文章介绍我是如何用 things3 来管理我的日常任务。
本文主要介绍欧神写的 tli[2] 工具来学习如何写一个定制的通过邮件和 things3 沟通的工具。很多软件都有类似的邮件功能,例如给绑定的 kindle 邮件地址发送电子书文件,就可以在 kindle 设备上看到。学会写工具的套路后,今后就能自己写类似的工具了。
使用场景
正常情况下,我们可以在 mac/ipad/iphone 上可以通过软件界面添加 TODO 事项,而且 things3 本身也有全局快捷录入的功能,非常方便。但是 things3 只能在苹果生态内使用,当我们临时切换到 windows 或者 linux 上工作,就不好操作了。这时如果产生了新的 TODO,通过 tli,打开 terminal 工具就能将 TODO 加到 inbox 里。
命令行操作:
同步到 things3:

初始化配置
因为要通过邮件来和 things3 沟通,因此需要配置发送邮件的邮箱、SMTP 服务器、用户名、密码、things3 给我们的专属邮件地址。
由于通过 tli 发送 TODO 是一次性的任务,因此这些配置项需要保存在某个文件中,之后用到的时候直接读取就好了。
具体的配置项包括:
type tliConf struct {SMTPHost string `yaml:"smtp_host"`SMTPPort string `yaml:"smtp_port"`Avatar string `yaml:"avatar"`EmailAddr string `yaml:"email_addr"`Username string `yaml:"username"`Password string `yaml:"password"`ThingsAddr string `yaml:"things_addr"`
}我用的 gmail 作为发送邮件,配置的 SMTP 参数是:smtp.gmail.com:587。EmailAddr 就是 gmail 地址,Password 需要设置一个专用的。
使用 user.Current() 方法可以拿到当前用户的信息,包括 home directory,用户名等等。tli 将配置文件保存到 home 目录下。
使用 bufio 包,在 terminal 里读取用户的输入:
s := bufio.NewScanner(os.Stdin)
log.Printf("SMTP Host Address: ")
if !s.Scan() {log.Println("init was canceled.")return
}
info := s.Text()
tli.SMTPHost = info将配置序列化成 yaml 后,写入 ~/.tli_config。
func (c *tliConf) save() {checkhome()data, err := yaml.Marshal(c)if err != nil {log.Fatalf("cannot save your data, err: %v", err)}f, err := os.OpenFile(homedir+"/"+pathConf,os.O_CREATE|os.O_RDWR, 0600)if err != nil {return}defer f.Close()all := []byte("---\n")all = append(all, data...)if _, err := f.Write(all); err != nil {return}
}读取 title 和 body
用户执行 todo 命令时,可输入 title 和 body。且 body 支持多行输入,输入空行或者按 ctrl+C 时取消输入。
TODO title 通过命令行直接传入,body 则通过一个 for 循环等待用户输入:
func (a *tliTODO) waitBody() bool {s := bufio.NewScanner(os.Stdin)fmt.Println("(Enter an empty line to complete; Ctrl+C/Ctrl+D to cancel)")sigCh := make(chan os.Signal, 1)signal.Notify(sigCh, os.Interrupt)line := make(chan string, 1)go func() {for {fmt.Print("> ")if !s.Scan() {sigCh <- os.Interruptreturn}l := s.Text()if len(l) == 0 {line <- ""return}line <- l}}()for {select {case <-sigCh:return falsecase l := <-line:if len(l) == 0 {return true}a.body = append(a.body, l)}}
}并且监听了取消息信号,异步启动一个协程去监听输入,再在 for select 中监听 sigCh,若用户手动取消了,则返回 false。若用户输入了空行,则返回 true,代表输入完成,之后就可以发送邮件。
发送邮件
因为 things3 有 2000 字的限制,所以需要做一个分割,防止被截断。
func (a *tliTODO) Range(f func(string, string)) {whole := strings.Join(a.body, "\n")if len(whole) < maxlen {f(a.title, whole)return}count := 1for i := 0; i < len(whole); i += maxlen {f(a.title+fmt.Sprintf(" (%d)", count), whole[i:min(i+maxlen, len(whole))])count++}
}调了 smtp.SendMail 方法发送邮件。
注意,需要对中文字符做一个编码,否则 things3 里会出现乱码。
TODO 历史
每次执行 todo 命令时,都会保存到历史中,同样是用 yaml 序列化。之后,执行 log 命令时,可将其读出来,展示历史。
每条记录前加一个”---“用于分离,读的时候,就可以读出多条记录:
rs := []record{}
for {var r recorderr = d.Decode(&r)if err != nil {if err == io.EOF {break}log.Fatalf("corrupted ~/.tli_history file, err: %v", err)}rs = append(rs, r)
}cobra 命令行
这是一个比较常用的库了,用于写命令行工具。
定义 init, log, todo 三个 command,再定义一个 root command,说明用法。
总结
总体来说这个项目比较简单,不到 500 行,但也能学到不少写工具软件的技巧,之后写类似的工具时可以参考。
使用 cobra 创建不同的命令。
将配置文件保存到用户 home 目录下。
如何从控制台接收用户的输入文本。
使用 smtp.SendMail 发送邮件。
参考资料
[1]
Things3: https://culturedcode.com/things/
[2]tli: https://github.com/changkun/tli
相关文章:
如何写一个 things3 client
Things3[1] 是一款苹果生态内的任务管理软件,是一家德国公司做的,非常好用。我前后尝试了众多任务管理软件,最终选定 things3,以后有机会会写文章介绍我是如何用 things3 来管理我的日常任务。本文主要介绍欧神写的 tli[2] 工具来…...
人工智能原理复习 | 命题逻辑和谓词演算
文章目录 一、前言二、命题逻辑三、谓词逻辑CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 一、前言 数理逻辑思想的起源:莱布尼茨之梦。古典数理逻辑主要包括两部分:命题逻辑和谓词逻辑,命题逻辑又是谓词逻辑的一种简单情形。 逻辑研究的基本内容: 语法。语言部分:基…...
前端基础面试题:如何判断对象是否具有某属性?遍历数组的方法有哪些?
一、如何判断对象具有某属性? 如:let obj{name:zhangsan,age:21} 有以下方法 ( property 为属性名的变量,实际上是key,键名): 1. property in obj 效果如图: in 运算符 2. Reflect.has(obj, property)…...
Docker入门和安装教程
一、Docker入门简介 Docker 是一个基于GO语言开发的开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。 容器是完全使用沙箱机制,相互之间不会…...
有了java基础,迅速学完Python并做了一份笔记-全套Python,建议收藏
面向过程Python简介Python和Java的解释方式对比Java:源代码 -> 编译成class -> Jvm解释运行Python:源代码 -> Python解释器解释运行我经常和身边的Java开发者开玩笑说:“Java真变态,别的语言都是要么直接编译要么直接解释…...
LeetCode——51. N 皇后
一、题目 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上,并且使皇后彼此之间不能相互攻击。 给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案…...
jQuery基本操作
学习目标: 会使用基本选择器获取元素 会使用层次选择器获取元素 会使用属性选择器获取元素 会使用过滤选择器获取元素 学习内容: 1.回顾jQuery语法结构 语法 $(selector).action; 工厂函数$():将DOM对象转化为jQuery对象。 选择器 sele…...
基于蜣螂算法优化Kmeans图像分割-附代码
基于蜣螂优化Kmeans图像分割算法 - 附代码 文章目录基于蜣螂优化Kmeans图像分割算法 - 附代码1.Kmeans原理2.基于蜣螂算法的Kmeans聚类3.算法实验结果4.Matlab代码摘要:基于蜣螂优化Kmeans图像分割算法。1.Kmeans原理 K-Means算法是一种无监督分类算法,…...
第二章 Kafka设计原理详解
第二章 Kafka设计原理详解 1、Kafka核心总控制器Controller 在 Kafka 集群中会有一个或者多个 broker,其中有一个 broker 会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态。 当某个分区的 leader…...
《NFL橄榄球》:费城老鹰·橄榄1号位
费城老鹰(英语:Philadelphia Eagles)是美国橄榄球联盟在宾夕法尼亚州费城的一支球队。1933年在国家橄榄球联盟扩编时与匹兹堡钢人和辛辛那提红人一起加入;1943年赛季因二次大战的缘故,和匹兹堡钢人作短暂的合并。 在20…...
【人工智能AI】四、NoSQL进阶《NoSQL 企业级基础入门与进阶实战》
帮我写一篇介绍NoSQL的技术文章,文章的标题是《四、NoSQL进阶》,不少于3000字。帮我细化到三级目录,使用markdown格式。这篇文章的目录是: 四、NoSQL 进阶 4.1 NoSQL 高可用 4.2 NoSQL 数据安全 4.3 NoSQL 性能优化 4.4 总结 四、…...
K8S 部署 Jenkins
本文使用 bitnami 镜像部署 Jenkins 官方文档:https://github.com/bitnami/charts/tree/main/bitnami/jenkins 添加 bitnami 仓库 helm repo add bitnami https://charts.bitnami.com/bitnami自定义 values.yaml storageClass:集群的存储类ÿ…...
【人工智能AI】五、NoSQL 应用实践《NoSQL 企业级基础入门与进阶实战》
帮我写一篇介绍NoSQL的技术文章,文章的标题是《五、NoSQL 应用实践》,不少于3000字。目录需要细化到三级目录,使用markdown格式。这篇文章的大的目录是: 五、NoSQL 应用实践 5.1 NoSQL 实时数据分析 5.2 NoSQL 分布式系统 5.3 NoS…...
Java爬虫系列 - 爬虫补充内容+ElasticSearch展示数据
一,定时任务Cron表达式Component public class TaskTest {Scheduled(cron "0/5 * * * * *") // 从0秒开始,每个五秒 执行一次 { 秒 分 时 天 月 周 }public void test(){System.out.println("定时任务执行了");} }二,网…...
Typora常用快捷键
Typora常用快捷键大全 ctrl1到6:1~6级标题,标题用ctrlH是没用的 ctrlshiftk:随时随地插入代码块,极为方便。 ctrlt:创建表格,也可直接输入|列1|列2|列3|并回车来创建表 ctrlshiftq:能实现添加…...
开学季好用电容笔有哪些?好用实惠的电容笔推荐
随着科学技术的快速发展,ipad的影响力越来越大,而且ipad的用户也越来越多,如果要提高ipad的功能,让ipad更加有趣,那么就需要一款非常适合自己,并且非常实用的电容笔。那么,究竟该选择哪个品牌的…...
C++_复习Recording
文章目录C复习课填空题编程题C复习课 试卷说明: 题型: 填空 编程题目 填空题 构造函数无返回类型,与类名同名; 复制构造函数用于创建对象,形实参结合,返回和接收对象。 补充: 只有在声明语句中使用一个变…...
【java】Spring Cloud --Spring Cloud 的核心组件
文章目录前言一、Eureka(注册中心)二、Zuul(服务网关)三、 Ribbon(负载均衡)四、Hystrix(熔断保护器)五、 Feign(REST转换器)六、 Config(分布式配…...
【C++】RBTree——红黑树
文章目录一、红黑树的概念二、红黑树的性质三、红黑树节点的定义四、红黑树的插入五、代码实现一、红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上…...
【5G RRC】5G系统消息SIB2介绍
博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
