微信到 Obsidian 2.0

微信到 Obsidian 2.0,以前出过一个1.0版本。最近发现了一个 Obsidian 的 Memos 插件,跟 flomo 界面非常像,或者说一样。不过数据来源用的是 Obsidian 。我使用了多端同步插件 remotely save插件,选择腾讯云COS同步。我想,如果能够在微信测试号中,信息发送过去,然后存储到COS中。在 Obsidian 中一刷新就能看到了。

1.0 版本 微信使用 Remotely Save S3 兼容 发送到 Obsidian 1.0版本。不太好用

因此我写下了2.0版本。实现了以下功能:

  • 支持图片和文字
  • 图片下载到存储本地,而非链接(微信发送的图片,会给咱们的服务器返回图片URL)
  • 对用户的判断,仅限特定用户存储笔记。(根据 OpenID 判断)
  • 检索文字中含有 “todo” ,则生成勾选框。如 - [ ] 13:11 somethingtodo
  • 正常生成 - 13:11 something
  • 内容能在 Memos 中正常显示
  • 支持收藏链接(部分支持),位置,语音(转文字存储)。(2.1 新增)
  • 返回可点击的链接,可以在微信内置浏览器中使用 Memos (2.1 新增,需要kkbt/obweb支持,参考页面,支持查看三天,修改一天)

BUG:

  • 不推荐批量传图片,推荐显示已保存后依次上传。
  • 不推荐一秒内上传多个文件,图片命名精确到1S。1S内多图片会覆盖。
  • 不要使用微信自带的表情符号,请使用输入法表情。
  • 如果微信输入框换行或分段,只会在这一条消息最开始有 - 13:11 。也就是说,第二行、第二段不会在 Memos 中显示。

说明: 不推荐批量传图片,程序对图片处理非常粗糙。。例如1M带宽服务器,连续传五个图片时。造成费时间会超过五秒,触发微信重传机制。这个机制带来的问题有很多。比如会多上传几份重复的文件,并且在微信测试号显示服务故障。(其实解决方案有很多,如使用任务队列或者是异步的方式。先返回success,另外开线程上传文件。留着以后有机会解决吧)。

开源地址: https://gitee.com/kkbt/wechat2obsidian

依赖

  • werobot
  • cos-python-sdk-v5

程序思路

程序思路运行一个测试号服务器,使用werobot。根据message的公共属性source,即微信用户的唯一openid,判断用户。是自己的ID,如果是文字消息,判断消息字符串是否包含todo,包含则在消息字符串增加勾选框markdown语法,否则使用无序列表。然后根据当日时间,判断当日日志是否存在。若存在,下载本日日志并添加处理好的字符串,然后上传。不存在则直接上传处理好的字符串。 如果是图片消息,从消息中获取图片url并下载。下载完成之后上传至COS中,图片命名为当前的时间精确到秒。图片名包装成md图片链接。处理好的字符串按照文字消息的处理方法保存。

注意:以下代码仅为参考,最新代码请带 https://gitee.com/kkbt/wechat2obsidian 获取。

主文件 wechat.py 和 get_add.py 需要放到同一级目录下。运行 python wechat.py

 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
# -*- coding=utf-8

# README
# 需要安装的依赖
# werobot cos-python-sdk-v5
# 可用 Alist 或其他可挂载 S3 的开源网盘 实现web编辑.md

from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
import werobot
import time
import get_add as cosga

# 相关信息

token = "token"  # 自定义
APP_ID = "wxxxxxxxxxxxxxxxxx"  # 微信公众号测试号APP_ID
APP_SECRET = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"  # 微信公众号测试号APP_SECRET


# werobot 配置

robot = werobot.WeRoBot(token=token)
robot.config["APP_ID"] = APP_ID
robot.config["APP_SECRET"] = APP_SECRET

rob_client = robot.client
rob_client.delete_menu()
# 
# rob_client.create_menu({
#     "button":[{
#          "type": "click",
#          "name": "今日",
#          "key": "info"
#     }]
# })

# message.source 为 OpenID 。获取->测试号二维码->用户列表(最多100个)->微信号

@robot.key_click("info")
def menu_click(message):
    if (message.source != "o6_bmjrPTlm6_2sgVt7hMZOPfL2M"):
        return '你不是恐咖兵糖'
    return cosga.get_daily_today()

# 关注回复


@robot.subscribe
def subscribe(message):
    return "这是恐咖兵糖的测试公众号"


# 文字消息回复 put_object append_object
@robot.text
def text_reply(message):
    if (message.source != "o6_bmjrPTlm6_2sgVt7hMZOPfL2M"):
        return '你不是恐咖兵糖'
    str = cosga.add_memos_in_daily(message.content) # ETag
    return "📩 已保存"


# 图片消息
@robot.image
def image_reply(message):
    if (message.source != "o6_bmjrPTlm6_2sgVt7hMZOPfL2M"):
        return '你不是恐咖兵糖'
    now_key = cosga.save_asset(message.img)
    str = cosga.add_memos_in_daily("![]("+now_key+")") # ETag
    return  "📩 已保存"


@robot.error_page
def make_error_page(url):
    return "404"


@robot.handler
def error_message(message):
    return "不支持的消息类型"


robot.config["HOST"] = "0.0.0.0"
robot.config["PORT"] = "8008"
robot.run()

get_add.py

 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
# -*- coding=utf-8

# 需要安装的依赖
# werobot cos-python-sdk-v5

from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
import time
import requests

# 相关信息
secret_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"  # 腾讯云 secret_i
secret_key = "xxxxxxxxxxxxxxxxxxxxxxxxx"  # 腾讯云 secret_key
region = "ap-nanjing"  # 腾讯云 COS
cos_token = None  # 腾讯云 COS token
scheme = "https"  # 腾讯云 COS 访问模式
bucket = "test-0123456789"  # 腾讯云 Bucket
config = CosConfig(
    Region=region,
    SecretId=secret_id,
    SecretKey=secret_key,
    Token=cos_token,
    Scheme=scheme,
)

client = CosS3Client(config)
   

# name = time.strftime("%Y%m%d%H%M%S", time.localtime())
def add_memos_in_daily(text):
    # 生成ObjectKey => 日志/2022-08-08.md
    daily_note_dir_path = "日志/"
    today_daily_file_key = daily_note_dir_path + time.strftime("%Y-%m-%d", time.localtime()) + ".md"
    todo = "todo"
    if todo in text:
        message = time.strftime("\n- [ ] %H:%M ", time.localtime()) + text
    else:
        message = time.strftime("\n- %H:%M ", time.localtime()) + text
    response3 = client.object_exists(
        Bucket=bucket,
        Key=today_daily_file_key
     )
    # 判断本日日志是否存在
    if(response3==True):
         # 存在 读取文件
        response2 = client.get_object(
            Bucket=bucket,
            Key=today_daily_file_key
        )
        str1=response2['Body'].get_raw_stream().read().decode('utf-8')
        btyes = bytes(str1+ message, encoding="utf8")
    else:
        btyes = bytes(message, encoding="utf8")


    #btyes = bytes(str1+"- 20:01 somgthing", encoding="utf8")	
    # name = time.strftime("%Y%m%d%H%M%S", time.localtime())
    response_sum = client.put_object(
            Bucket=bucket,
            Body=btyes,
            Key=today_daily_file_key
        )
    return response_sum["ETag"]

def save_asset(file):
    # 生成ObjectKey => 日志/附件/202208/20220808131104.jpg 
    file = requests.get(file).content # wechat file is url 为了防止过期,下载下来。
    # file = bytes("![]("+file+")", encoding = "utf8") # 直接使用腾讯微信链接显示图片
    daily_asset_dir_path = "日志/附件/" + time.strftime("%Y%m",time.localtime()) + "/" 
    today_daily_asset_file_key = daily_asset_dir_path + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".jpg"
    now_key = today_daily_asset_file_key
    response_file = client.put_object(
        Bucket=bucket,
        Body=file,
        Key=now_key
    )
    print(response_file["ETag"])
    return now_key

# 已弃用 原因: 超过一定长度后微信显示服务错误
def get_daily_today():
    # 生成ObjectKey => 日志/2022-08-08.md
    daily_note_dir_path = "日志/"
    today_daily_file_key = daily_note_dir_path + time.strftime("%Y-%m-%d", time.localtime()) + ".md"
    response3 = client.object_exists(
        Bucket=bucket,
        Key=today_daily_file_key
    )
    if(response3==True):
    # 存在 读取文件
        response2 = client.get_object(
            Bucket=bucket,
            Key=today_daily_file_key
        )
        str1=response2['Body'].get_raw_stream().read().decode('utf-8')
        return str1
    else:
        return '今日无日志'

微信测试号建议直接放在桌面

https://cdn.ftls.xyz/images/2022/07/Screenshot_2022-08-08-18-39-24-000_com.jpg
微信测试号建议直接放在桌面
https://cdn.ftls.xyz/images/2022/07/Screenshot_2022-08-08-18-33-33-695_com.jpg
微信测试号
https://cdn.ftls.xyz/images/2022/06/20220808183650.png
Memos 效果
https://cdn.ftls.xyz/images/2022/06/20220808183750.jpg
源码效果

以上效果图为早期版本效果图,新的版本已经返回已保存,和可点击的链接文字。即可在微信内置浏览器使用 Memeos

希望以后会有优秀的类似转发服务提供商,而且按量付费不太贵那种。能用很简单,好用很困难。Knuth 大佬(发明 KMP 算法的那位),说二分

Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky…

这软件也是如此,思路很简单,细节是魔鬼