majordomo,意思是大管家,衍生自之前的 worktools,主要是汇集一些提升工作效率,提升工作幸福感的工具。奈何 worktools 太零散,每次更换电脑都需要重新配置下 alias,费时费力。于是大致构思为 web 工具合集,从 SB(Script Boy)进化到PM(Platform Man)。
未来打算把 majordomo 系列做起来,鉴于不是所有的工具都适合拿来做 web 工具,因此在此做增量式开发。
majordomo 第一弹,是一个系统剪切板应用。背景来源于工作中时常需要查找需求 icafe(一种类似于需求说明书 wiki 的内部工具),每次都要翻好久记录,不够方便。
初期想着搞个本地知识库出来,有需要记录的内容,直接在里面记录就行了。搜了一圈GitHub,发现这玩意儿退化下不就是个本地的 XX 笔记吗?一旦忘记记录或者懒得记录,那不就又回来了吗?至少对我来说,可能坚持一段时间就忘了这回事了。
后来不知道怎么地就想到了,监听系统剪切板,所有内容统统记录下来,扔到 ElasticSearch 中,后期有啥想查的,直接搜剪切板历史,倒也是个不错的点子。
在开发 clipboard 之前,和damnever@github上发现了一个叫🌻 的项目,感觉人家的项目目录结构挺好,就用邮件请教了下。特此摘录一下和大伙分享一波。
然后基于此次交流,我也设计了自己预想中的web项目的结构(针对我自己比较合适,也可能别人不喜欢,这个众口难调,找到适合自己的就行), 并做成了一个 shell 脚本 。有兴趣的可以拿去改改。使用方式就是
bash gowebscaffold.sh generate demo
扯远了,继续回到 clipboard 项目上。宏观来看,我需要如下内容:
- web 查询页面
- 剪切板内容推送到 elasticsearch
从下到上来看的话,还是需要不少代码的,向各种 dao,service,library 等。关于设计一个项目,我目前还有一些疑惑。到底是要从上到下设计,还是从下到上设计。
角度 | 优点 | 缺点 |
---|---|---|
从上到下 | 需求清晰,模块可以划分的比较明确 | 细节把控可能不不到位,遗漏部分功能开发 |
从下到上 | 细致 | 需求把控不到位,可能写了一堆上层根本用不到的代码或者模块 |
还是得在实践中去权衡下。
clipboard 一期仅做了剪切板监听以及 elasticsearch 推送,实现效果如下图所示。
奈何前端技术太菜,要不然可以美化下页面,对我来说有功能也就行了。
最后来说下编写 clipboard 过程中的一些问题。
工具层
Elasticsearch 官方文档 这个版本很简洁,适合像我这种没用过 es的新人。
我个人觉得比较重要的知识点如下:
Index 类似于关系型数据库中的 DB
Type 类似于组、类别概念,将document 分组,分类使用。
Unique-Document-ID 具体的文档内容,JSON 格式
SQL 转 Elasticsearch 工具
http://www.ischoolbar.com/EsParser/ 这个工具还不赖,对新人蛮友好的。
Elasticsearch 的 header 工具
一个浏览器插件,可以方便的查看 Elasticsearch 元数据信息,还有简单的查询页供使用。
组件地址:https://pan.baidu.com/s/17WfSHkINvt1EHMpLJ_ZqyA 提取码:674o
代码层
关于 interface 的使用, 以 clipboard.go 为例。像这种,肯定是需要做成跨平台的,因此很适合以基本方法做抽象层。
package clipboard
type clipboard interface {
Get() (string, error)
Set(content string) error
}
macOS 上的实现见 clipboard_darwin.go
package clipboard
import (
"fmt"
"github.com/guoruibiao/commands"
)
type Darwin struct {
}
/**
* macOS 剪切板工具
*
* return: *Darwin
*/
func NewDarwin() *Darwin {
return &Darwin{}
}
/**
* 获取 macOS 系统 剪切板内容
*
* return: string
* return: error
*/
func (d *Darwin) Get() (content string, err error) {
// check pbpaste or pbcopy valid or not.
if status, output := commands.New().GetStatusOutput(`pbpaste`); !status {
err = fmt.Errorf("[Darwin] get content from clipboard with %s", err.Error())
}else{
content = output
}
return
}
/**
* 将内容写入系统剪切板
*
* param: string content
* return: error
*/
func (d *Darwin) Set(content string) (err error) {
// commands 设计的遗漏,err 被吞掉了 -_-||
commands.New().Run(`echo`, ` ` + content + ` | pbcopy`)
return
}
适当时机使用回调,可以减少大量冗余流程。在 clipboard 中,有一个监听系统剪切板的功能,由于没法注册系统事件的回调,因此做了轮训处理,见 scripts/task.go
package scripts
import (
"github.com/guoruibiao/majordomo/library/clipboard"
"github.com/guoruibiao/majordomo/models/data/elasticsearch"
"time"
)
const (
CLIPBOARD_LISTEN_INTERVAL = time.Second * 2
)
var (
clipboardStr = ""
darwinClipboard = clipboard.NewDarwin()
)
/**
* 监听系统剪切板变化,并发送到 elasticsearch
*/
func StartClipboardListening() {
go clipboardListener(elasticsearch.AddLog)
}
func clipboardListener(callback func(content string) error) {
ticker := time.NewTicker(CLIPBOARD_LISTEN_INTERVAL)
for {
select {
case <- ticker.C:
if newClipboardStr, _ := darwinClipboard.Get(); clipboardStr != newClipboardStr {
// 回调
callback(newClipboardStr)
clipboardStr = newClipboardStr
}
}
}
}
回调函数见models/data/elasticsearch/elasticsearch.go
func AddLog(content string) (err error) {
logid:= time.Now().Unix()
url := fmt.Sprintf("%s/%s/%s/%d", ELASTICSEARCH_URL, ELASTICSEARCH_INDEX, ELASTICSEARCH_TYPE, logid)
headers := map[string]string{
"User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
"Content-Type" : "application/json",
}
payload := map[string]interface{}{
"content" : content,
"timestamp" : logid,
}
response, err := gorequests.NewRequest("POST", url).Headers(headers).Body(payload).DoRequest()
if err != nil {
return
}
html, err := response.Content()
if err != nil {
return
}
fmt.Println(html)
return
}
拓展部分
在和baifenbai@github 聊天的时候,提到过困扰我的这个查询问题。
这个 repo 用的也是 es 模式,和我想到一块去了。毕竟即使把全天的系统剪切板数据扔到 es 中,一个人也用不了多少。
多交流,说不一定哪天就迸发出奇妙的创意了。