博客从 hexo 切换成 NotionNext

博客从 hexo 切换成 NotionNext

很久没有发布博客文章,原因是坐下来开始写的动作太复杂,使用 markdown 写作对图片的管理很麻烦

  • 需要一个专门的图片管理工具,虽然开源的 PicGo 已经很好用了
  • 需要将笔记内容转换成 markdown. markdown 的所见即所得其实并没有那么所见即所得.
  • 需要去发布,虽然只需要一行命令.

写博客最关键的是开始写.而使用 markdown 无时无刻不在想文档是怎么管理的,图片是怎么管理的,最后花在折腾各种工具上的时间远远大于写博客的时间.

当看到 NotionNext 的时候,就想起了当年使用 windows Live Writer(现在叫 https://openlivewriter.com/ ,已经被微软开源) 写博客的日子,不需要任何的多余动作:打开,写.

自从所有的笔记转移到 notion 后,笔记里面躺了很多的内容,但是一点都不想去整理到博客上面.我的博客也基本算是个公开的笔记,使用 Hexo 搭建的,有一段时间想着将 notion 里面的内容转到 Hexo 里面. 折腾了两天 notion2md .

bookmark

由于转出图片的问题,还给 notion2md 提了一个 merge.但是依然心有阻力.

在网上找相关的库,期望能够将 Hexo 的内容转换到 NotionNext . md2noton 基本可以满足需要.

于是开始照着例子写了一个 hexo 导入到 NotionNext datebase 的脚本. 写完后想着其他人可能也有这种需求,于是支持了指定配置文件、支持 hexo 格式的路径.

nobelium 相比 NotionNext 只相差一个 category 的列,所以也顺带支持了 nobelium .

link_preview

使用非常简单

  1. 安装 pip install hexo2notionnext
  2. 创建一个配置文件config.yaml 并填上配置
  3. hexo2notionnext -c config.yaml 开始导入

如何用 golang 生成比特币钱包

前一段时间看了区块链相关的内容,学习了一下bitcoin地址生成。内容来自网络。

第一步,随机选取一个32字节的数,大小介于1~0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141之间,作为私钥

PS: 助记词是将这个随机数按每 11位分组映射到 2048 个单词位得到 12 16 或 24 个单词。

18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725

第二步,使用椭圆曲线加密算法(ECDSA-SECP256k1)计算私钥所对应的非压缩公钥(共65字节,1字节0x04,32字节为x坐标,32字节为y坐标)。

0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6

第三步,计算公钥的SHA-256哈希值

600FFE422B4E00731A59557A5CCA46CC183944191006324A447BDB2D98D4B408

第四步,计算上一步哈希值的RIPEMD-160哈希值

010966776006953D5567439E5E39F86A0D273BEE

第五步,在上一步结果之间加入地址版本号(如比特币主网版本号”0x00”)

00010966776006953D5567439E5E39F86A0D273BEE

第六步,计算上一步结果的SHA-256哈希值

445C7A8007A93D8733188288BB320A8FE2DEBD2AE1B47F0F50BC10BAE845C094

第七步,再次计算上一步结果的SHA-256哈希值

D61967F63C7DD183914A4AE452C9F6AD5D462CE3D277798075B107615C1A8A30

第八步,取上一步结果的前4个字节(8位十六进制数)D61967F6,把这4个字节加在第五步结果的后面,作为校验(这就是比特币地址的16进制形态)

00010966776006953D5567439E5E39F86A0D273BEED61967F6

第九步,用base58表示法变换一下地址(这就是最常见的比特币地址形态)

16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package main

import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"golang.org/x/crypto/ripemd160"
"log"
)

const VERSION = byte(0x00)
const CHECKSUM_LENGTH = 4

type BitcoinKeys struct {
PrivateKey *ecdsa.PrivateKey
PublicKey []byte
}

func GetBitcoinKeys() *BitcoinKeys {
b := &BitcoinKeys{nil, nil}
b.newKeyPair()
return b
}

func (b *BitcoinKeys) newKeyPair() {
curve := elliptic.P256()
var err error
b.PrivateKey, err = ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
log.Panic(err)
}
b.PublicKey = append(b.PrivateKey.PublicKey.X.Bytes(), b.PrivateKey.PublicKey.Y.Bytes()...)
}

//获取地址
func (b *BitcoinKeys) GetAddress() []byte {
//1.ripemd160(sha256(publickey))
ripPubKey := GeneratePublicKeyHash(b.PublicKey)
//2.最前面添加一个字节的版本信息获得 versionPublickeyHash
versionPublickeyHash := append([]byte{VERSION}, ripPubKey[:]...)
//3.sha256(sha256(versionPublickeyHash)) 取最后四个字节的值
tailHash := CheckSumHash(versionPublickeyHash)
//4.拼接最终hash versionPublickeyHash + checksumHash
finalHash := append(versionPublickeyHash, tailHash...)
//进行base58加密
address := Base58Encode(finalHash)
return address
}

func GeneratePublicKeyHash(publicKey []byte) []byte {
sha256PubKey := sha256.Sum256(publicKey)
r := ripemd160.New()
r.Write(sha256PubKey[:])
ripPubKey := r.Sum(nil)
return ripPubKey
}

//通过地址获得公钥
func GetPublicKeyHashFromAddress(address string) []byte {
addressBytes := []byte(address)
fullHash := Base58Decode(addressBytes)
publicKeyHash := fullHash[1 : len(fullHash)-CHECKSUM_LENGTH]
return publicKeyHash
}

func CheckSumHash(versionPublickeyHash []byte) []byte {
versionPublickeyHashSha1 := sha256.Sum256(versionPublickeyHash)
versionPublickeyHashSha2 := sha256.Sum256(versionPublickeyHashSha1[:])
tailHash := versionPublickeyHashSha2[:CHECKSUM_LENGTH]
return tailHash
}

//检测比特币地址是否有效
func IsVaildBitcoinAddress(address string) bool {
adddressByte := []byte(address)
fullHash := Base58Decode(adddressByte)
if len(fullHash) != 25 {
return false
}
prefixHash := fullHash[:len(fullHash)-CHECKSUM_LENGTH]
tailHash := fullHash[len(fullHash)-CHECKSUM_LENGTH:]
tailHash2 := CheckSumHash(prefixHash)
if bytes.Compare(tailHash, tailHash2[:]) == 0 {
return true
} else {
return false
}
}

func main() {
keys := GetBitcoinKeys()
bitcoinAddress := keys.GetAddress()
fmt.Println("比特币地址:", string(bitcoinAddress))
fmt.Printf("比特币地址是否有效:%v\n:", IsVaildBitcoinAddress(string(bitcoinAddress)))
}


base58

Base58 采用数字、大写字母、小写字母,去除歧义字符 0(零)、O(大写字母 O)、I(大写字母i)、l(小写字母L),总计58个字符作为编码的字母表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package main

import (
"bytes"
"math/big"
)

var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")

func Base58Encode(input []byte) []byte {
var result []byte

x := big.NewInt(0).SetBytes(input)

base := big.NewInt(int64(len(b58Alphabet)))
zero := big.NewInt(0)
mod := &big.Int{}

for x.Cmp(zero) != 0 {
x.DivMod(x, base, mod)
result = append(result, b58Alphabet[mod.Int64()])
}

ReverseBytes(result)

for _, b := range input {
if b == 0x00 {
result = append([]byte{b58Alphabet[0]}, result...)
} else {
break
}
}
return result

}

func Base58Decode(input []byte) []byte {
result := big.NewInt(0)
zeroBytes := 0
for _, b := range input {
if b != b58Alphabet[0] {
break
}
zeroBytes++
}
payload := input[zeroBytes:]
for _, b := range payload {
charIndex := bytes.IndexByte(b58Alphabet, b)
result.Mul(result, big.NewInt(int64(len(b58Alphabet))))
result.Add(result, big.NewInt(int64(charIndex)))
}

decoded := result.Bytes()
decoded = append(bytes.Repeat([]byte{byte(0x00)}, zeroBytes), decoded...)

return decoded
}

func ReverseBytes(data []byte) {
for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
data[i], data[j] = data[j], data[i]
}
}


2022年了,我还在怀念RSS

很久以前,我习惯用 Google Reader 来订阅各种有趣的独立博客来阅读。后来 Google Reader 关闭了,Google Reader 作为当时最好的 Rss 阅读器,可以快速的获取我感兴趣的内容。当 Google 关闭它后,我尝试了很多替代者,体验都 Google Reader 好。鲜果、抓虾、豆瓣 9 点 后来也关闭了。然后 开始用 inoreader 。

现在大家看视频用抖音,看新闻用头条这种用算法推荐的 app,可能 Rss 是什么已经不知道了。而微信公众打造的所谓的内容池,也只能在微信里面用关注的方式来订阅。

RSS仍然存在,但我怀念它,因为它不像以前那样随处可见了。

很久以前,每个网站都是封闭的。除了去那里,没有办法知道一个网站是否被更新。于是大家想出一种更容易聚合内容的方法,这就是 Rss ,大家都喜欢它并且用它来获取信息。

可能大家都不了解 Rss ,百科的解释是:

RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用。RSS搭建了信息迅速传播的一个技术平台,使得每个人都成为潜在的信息提供者。发布一个RSS文件后,这个RSS Feed中包含的信息就能直接被其他站点调用,而且由于这些数据都是标准的XML格式,所以也能在其他的终端和服务中使用,是一种描述和同步网站内容的格式。

它曾经出现在每一个网络浏览器中,而且所有的主要网站,比如你看到的新闻网站,博客,都提供feeds。然后,你在你的电脑上安装一个feed阅读器,或者直接实用 Google Reader 这种网络的 Rss 阅读器。

这本身并没有什么特别的,但有了RSS,你也可以订阅任何提供feed的新闻网站或博客。这意味着你有一个一站式的服务。你有一个地方可以在你喜欢的网站更新时提醒你。

最流行的博客工具,如WordPress,仍然默认支持了Rss。由于很多的独立博客都是基于它来搭建的,所以很有可能你最喜欢的网站仍然提供RSS。

但是大的网络公司已经对 RSS 不再支持,他们在构建自己的护城河,比如微信公众号,你不能在那里深度的阅读任何的东西,只能蜻蜓点水的刷一刷。

可能是 10 年以前,我很喜欢用遨游浏览器,就是因为它可以很方便的订阅 Rss ,而现在很多浏览器取消的对 Rss 一键订阅的功能,好吧,这事情是 Chrome 干的。我认为是 Google 当时利用自己在搜索和浏览器的垄断地位杀死了 Rss 。

互联网之所以称之为互联网,是它的开放和便捷带来的。有人曾经说过,中国的互联网公司通用的互联互通的接口叫做爬虫。仔细想一想还真是那么回事。

Rss 真的是个非常妙的东西,用一个标准来提醒大家订阅更新了,我不需要去盯着它。也不需要为了获取特定的信息去打开一个特定的网站或者 App。 作为内容提供者,只需要提供好的内容就好。而现在大家都在制造标题党,都在发广告。

我在知乎上偶尔还会看到这样的问题: 202x 年什么 Rss 订阅工具比较好用?

很无奈,国内没有,不是因为做不出来,我看到了好几款订阅工具的出来,也都尝试过。最后都放弃了。不是他们不够好。而是国内内容生产已经都封闭起来了。你找不到好的内容。

也许还有有一些人和我一样,还坚持着使用着 Rss,但是很多年轻人不知道它出现过,只记得 App 里的那个关注按钮。

很可惜,大家只喜欢在社交平台上刷刷刷和手机屏幕里面飘过的老铁 666 。

很可惜,RSS 死了,我还怀念它。

使用 notion2md 从 notion 导出 markdown

为了用 notion 写公众号,我给 notion2md 提了个 merge 。

前段时间使用 notion 作为笔记,当需要将一篇笔记导出来贴到其他地方,比如公众号的时候,会有一些问题。主要碰到的是自带的一些图片导出的问题。

阅读更多

lua package 面向对象开发

lua 作为一门简单的语言,在变量是只有 table 和非 table 的区别。

基本

1
2
3
4
5
6
7
local _M = {} 
function _M:foo()
...
end

return _M

在 lua 中 _M:foo() 就是 _M.foo(self) 。在使用时和个人习惯有关。这里我希望能够严格的区分库和对象.当函数为静态方法时,比如一些utils函数,使用点号(.)。当函数需要面向对象时,使用冒号(:)。

阅读更多

计算最大公约数

这是计算最大公约数的函数.辗转相除法

1
2
3
4
5
6
function gcd(a, b)
if b == 0 then
return a
end
return gcd(b, a % b)
end

它的计算其实是使用欧拉定理.

https://zh.wikipedia.org/wiki/欧拉定理_(数论)

证明

对于任何可以整除a和b的整数,那么它也一定能整除a-b

1.

假设 a b 都有公约数 n 且 a>b, 假设 $a=x_1n$, $b=x_2n$

那么$ a-b =(x_1-x_2)n$

  1. a=kb+t 那么 t=a-kb

    $\frac{t}{d} = \frac{a}{d} -\frac{kb}{d}$

    因为 a、b都能够被d整除

    所以 $\frac{a}{d}-\frac{kb}{d}$ 为整数,

    即 $\frac{t}{d}$ 为整数,所以 d 也是 t 的公约数.

在均衡负载中,对设置的权重求最大公约数,需要用到 gcd 函数.

当传入的值是非数字时, 比如传入 字符串 “0”, b==0 判断失效

执行 gcd(”0”,nil) ,再次执行 gcd(nil,nil) 导致出现死循环.

https://mp.weixin.qq.com/s?__biz=Mzg3Njc0NTgwMg==&mid=2247487272&idx=1&sn=038a30ce61706c97e3397eee982b1486&amp

go-singlefilght

singleflight

1
2
3
golang.org/x/sync/singleflight


singleflight 是 go 提供的一个扩展并发原语,主要是用来合并请求来降低服务压力。

code

1
2
https://cs.opensource.google/go/x/sync/+/036812b2:singleflight/singleflight.go

原理

实现了一个 Group 的 struct

1
2
3
4
5
type Group struct {
mu sync.Mutex // protects m
m map[string]*call // lazily initialized
}

阅读更多

docker build alpine dns error

使用 alpine 构建 Ddockerfile ,导致 docker 打包失败,错误如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fetch https://mirrors.aliyun.com/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://mirrors.aliyun.com/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
v3.13.4-69-g5bcff43ec5 [https://mirrors.aliyun.com/alpine/v3.13/main]
v3.13.4-66-g32aee0eba0 [https://mirrors.aliyun.com/alpine/v3.13/community]
OK: 13892 distinct packages available
fetch https://mirrors.aliyun.com/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
WARNING: Ignoring https://mirrors.aliyun.com/alpine/v3.13/main: DNS lookup error
fetch https://mirrors.aliyun.com/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
WARNING: Ignoring https://mirrors.aliyun.com/alpine/v3.13/community: DNS lookup error
ERROR: unable to select packages:
bash (no such package):
required by: world[bash]
curl (no such package):
required by: world[curl]
ERROR: Service 'microservice-users-rpc' failed to build : The command '/bin/sh -c apk update && apk add --no-cache curl bash' returned a non-zero code: 2
make: *** [docker-compose-up] Error 1

阅读更多