Edited at

kobitonote.py - Kobitoで編集したアイテムをEvernoteに同期保存

More than 5 years have passed since last update.


これは何?

Kobitoで編集したアイテムをEvernoteにも保存するpythonスクリプト


何が嬉しいの?

Evernoteではmarkdownでのテキスト作成ができないけれどKobitoとなら!

KobitoでレンダリングされたHTMLをそのままEvernoteに保存します。

Kobitoで更新したアイテムについては、該当するEvernoteノートを更新します。

Kobito側のタグもEvernoteに反映します。

アイテムをQiitaで公開すると、公開URLがEvernoteにも反映されます。


メモ

Kobitoで最近更新されたアイテムをEvernoteに保存します。

保存先ノートブックはKobitoです。無ければ作られます。

「最近」判定は、スクリプトを実行したディレクトリに作られるファイルlast_kobitoに保存されるタイムスタンプで行います。

Evernoteとのやり取りはAppleScriptです。

更新監視は行なっていませんので、各自cron等で定期実行してください。


注意事項

保証なしです。

Kobito/Evernote上のコンテンツを失うようなコードではないはずですが、どうか自己責任で。

Kobitoの仕様が変わったら突然動かなくなるかもしれません。


ソース

git clone git://github.com/naoyat/qiita-toolkit.git


kobitonote.py

#!/usr/bin/env python

# -*- coding: utf-8 -*-
##
## kobitonote - KobitoのアイテムをEvernoteにも保存する
##
## (c)2012 by @naoya_t
##
import os
import re
import sqlite3
import subprocess
import sys
import time

## 保存先ノートブック
NOTEBOOK_FOR_KOBITO = 'Kobito'

## Kobito.db の所在PATH
DB_PATH = os.environ['HOME'] + '/Library/Kobito/Kobito.db'

## Kobito.db 内部時刻のオフセット
TIME_OFFSET = 978307200.0 # time.mktime(time.strptime("Mon Jan 1 09:00:00 2001"))

## 最後に処理したタイムスタンプ
LAST_KOBITO = './last_kobito'

## 秒 → ISO-8601フォーマット
def iso_8601_jst(t):
return time.strftime('%Y-%m-%dT%H:%M:%S+09:00', time.localtime(t))

## AppleScript生成
def make_script(title, body, created_at, updated_at, tags, url=None):
def osa_escape(string):
return string.replace('¥¥', '¥¥¥¥').replace('"', '¥¥"')
def my_datetime(t):
return time.strftime('my datetime(%Y,%m,%d,%H,%M,%S)', time.localtime(t))
def make_tag_list(tags):
return '{' + ','.join(['"'+osa_escape(tag)+'"' for tag in tags]) + '}'

return """
on datetime(year, mon, day, hour, min, sec)
set d to current date
set d's year to year
set d's month to mon
set d's day to day
set d's hours to hour
set d's minutes to min
set d's seconds to sec
return d
end datetime

tell application "Evernote"
set notebookStr to "%s"
set titleStr to "
%s"
set htmlStr to "
%s"
set createdAt to
%s
set updatedAt to
%s
set tagNameList to
%s
set urlStr to
%s

if not exists notebook notebookStr then
make new notebook with properties {name:notebookStr}
end if

set tagList to {}
repeat with aTagName in tagNameList
if exists tag aTagName then
set aTag to tag aTagName
else
set aTag to make new tag with properties {name:aTagName}
end if
set end of tagList to aTag
end repeat

set destNote to null
repeat with aNote in (find notes "notebook:" & notebookStr & " intitle:¥¥"" & titleStr & "¥¥"")
if aNote's creation date = createdAt then
set destNote to aNote
end if
end repeat

if destNote = null then
set destNote to (create note with html htmlStr title titleStr notebook notebookStr created createdAt tags tagList)
else
set destNote's title to titleStr
set destNote's HTML content to htmlStr
set destNote's tags to tagList
end if
set destNote's modification date to updatedAt
if not urlStr is equal to "" then
set destNote's source URL to urlStr
end if
end tell
""" % (NOTEBOOK_FOR_KOBITO,
osa_escape(title),
osa_escape(body),
my_datetime(created_at),
my_datetime(updated_at),
make_tag_list(tags),
'"'+url+'"' if url else '""')

## AppleScriptを実行
def run_osascript(script, *args):
p = subprocess.Popen(['arch', '-i386', 'osascript', '-e', script] +
[unicode(arg).encode('utf8') for arg in args],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
err = p.wait()
if err:
raise RuntimeError(err, p.stderr.read()[:-1].decode('utf8'))
return p.stdout.read()[:-1].decode('utf8')

## Kobitoアイテムクラス
class KobitoItem :
def __init__(self, row, items_tags={}):
z_pk, z_ent, z_opt, zprivate, zteam, zcreated_at, zposted_at, zupdated_at, ¥
zupdated_at_on_qiita, zbody, zkey, zlinked_file, zraw_body, ztitle, zurl, zuuid = row
self._pk = z_pk
self._ent = z_ent
self._opt = z_opt
self.private = zprivate
self.team = zteam
self.created_at = TIME_OFFSET + zcreated_at if zcreated_at else None
self.posted_at = TIME_OFFSET + zposted_at if zposted_at else None
self.updated_at = TIME_OFFSET + zupdated_at if zupdated_at else None
self.updated_at_on_qiita = TIME_OFFSET + zupdated_at_on_qiita if zupdated_at_on_qiita else None
self.body = zbody.encode('utf-8')
self.key = zkey.encode('utf-8')
self.linked_file = zlinked_file
self.raw_body = zraw_body.encode('utf-8')
self.title = ztitle.encode('utf-8')
self.url = zurl.encode('utf-8') if zurl else None
self.uuid = zuuid #
self.tags = items_tags[z_pk] if items_tags.has_key(z_pk) else []

def save_in_evernote(self):
title = self.title
body = self.body ¥
.replace("<!DOCTYPE HTML>¥n", "") ¥
.replace('rel="stylesheet" href="',
'rel="stylesheet" href="/Applications/Kobito.app/Contents/Resources/')
body = re.sub(r'[ ¥t]*<script[^¥n]*</script>¥n', r'', body)
body = re.sub(r' <body>¥n<h1>[^¥n]*</h1>', r'<body>', body)

script = make_script(title, body, self.created_at, self.updated_at, self.tags, self.url)
# print script

sys.stderr.write(iso_8601_jst(self.updated_at))
sys.stderr.write("「" + self.title + "」")
sys.stderr.write(" ".join(self.tags) + "¥n")
run_osascript(script)

## Kobito側のタグを取得
def get_kobito_tags(conn):
c = conn.cursor()
c.execute(u"select * from ZTAG")
tags = {}
for _pk, _ent, _opt, name in c:
tags[_pk] = name.encode('utf-8')
return tags

## Kobitoのアイテムに付加されたタグを取得
## {アイテムID1: [タグID1, タグID2, ...], アイテムID2: [...], ...}
## タグ取得済みなら、タグIDを文字列に置換
## {アイテムID1: [タグ1, タグ2, ...], アイテムID2: [...], ...}
def get_kobito_items_tags(conn, tags=None):
items_tags = {}
c = conn.cursor()
c.execute(u"select * from Z_1TAGS")
for _1items, _2tags in c:
if tags:
_2tags = tags[_2tags]
if items_tags.has_key(_1items):
items_tags[_1items].append(_2tags)
else:
items_tags[_1items] = [_2tags]
return items_tags

## Kobitoのアイテムを取得
def get_kobito_items(conn, last, items_tags=None):
c = conn.cursor()
c.execute(u"select * from ZITEM where ZUPDATED_AT > %s" % str(last))
return [KobitoItem(row, items_tags) for row in c]

## 最近(=最後にこのスクリプトを走らせた時以降)の更新アイテムをevernoteに保存
## Evernote の重複排除は created_at と title のみで照合
def save_recent_to_evernote():
def last_kobito_time():
if os.path.exists(LAST_KOBITO):
with open(LAST_KOBITO, 'r') as fp:
return float(fp.readline().rstrip())
else:
return 0
def save_current_kobito_time():
current_kobito_time = time.time() - TIME_OFFSET
with open(LAST_KOBITO, 'w') as fp:
fp.write(str(current_kobito_time) + '¥n')

conn = sqlite3.connect(DB_PATH)

tags = get_kobito_tags(conn)
items_tags = get_kobito_items_tags(conn, tags)
for item in get_kobito_items(conn, last_kobito_time(), items_tags):
item.save_in_evernote()

conn.close()
save_current_kobito_time()

if __name__ == '__main__':
save_recent_to_evernote()