由于最近听的捕蛇者说的这期播客节目1,去了解了一下经常听到但是从来没去了解过的 ,看了几篇网上的博文 23初步使用了几天。Beancount 的记账模式我非常的喜欢,但是美中不足的是基于文本很难能够进行随手记录。
发现了一款需要注册的 Beancount 应用,但和我想象中的不太一样,我希望的是能够通过 iCloud 读取 Beancount 文本并且进行快速的添加记录的应用。然后发现了一篇博文讲到通过 Telegram Bot 来进行记账的方式,看起来非常的靠谱。发现这种方式也不错,于是就开始了折腾起来。
首先,第一个问题是如何在 Telegram 里面方便的进行记录,如果是输入 beancount 语法的话就会非常的麻烦:
2021-03-13 * "Ahonn.me" "赞助 Ahonn 买一杯咖啡"
Expenses:Blog:Donate 20 CNY
Liabilities:CreditCard:CMB -20 CNY
原先是打算自己写一个 parser 来通过简化的语义命令来转换为 Beancount 的,偶然间发现了 Costflow Parser,简直就是我所需要的,节省了写 parser 的工作。虽然 Costflow 是可以 Self-Hosted 的,但是作为爱折腾的我来说,这种事情是我可以自己折腾的。
有了方便输入的文本格式转换之后,第二个问题就是怎么写 Telegram Bot,这里我通过 来实现:
import TelegramBot from 'node-telegram-bot-api'
const token = 'YOUR_TELEGRAM_BOT_TOKEN';
const bot = new TelegramBot(token, { polling: true });
Telegram Bot 的 Token 可以很容易的通过 这个机器人来得到4。然后这里又遇到了问题,Telegram Bot 是有两种实现的方式:一种是通过设置 polling 来进行轮训消息处理;另一种是通过 webhook 的方式让 Telegram Bot 有输入的时候进行触发。这里我选择了我更喜欢的 webhook 的方式,好处是说不定未来还可以通过其他的方式(IFTTT 之类的)来触发。
作为 Vercel 的无脑粉丝自然是希望能够将机器人部署在 Vercel 上了。搜索了许久终于找到 Build a serverless Telegram chatbot deployed using Vercel 这篇文章,无脑照做。于是就变成了下面这样:
import TelegramBot from 'node-telegram-bot-api';
import costflow from 'costflow';
const config = {
mode: 'beancount',
currency: 'CNY',
timezone: 'Asia/Hong_Kong',
account: {
信用卡: 'Liabilities:CreditCard:CMB',
捐赠: 'Expenses:Blog:Donate',
// ...
},
};
module.exports = async (req: NowRequest, res: NowResponse) => {
const bot = new TelegramBot(BOT_TOKEN);
const { message } = req.body;
if (message) {
const { chat: { id }, text, message_id } = message as TelegramBot.Message;
try {
const { output } = await costflow.parse(text, config);
bot.sendMessage(id, output, { reply_to_message_id: message_id });
} catch (e) {
bot.sendMessage(id, e.message, {
reply_to_message_id: message_id,
});
}
}
res.send('OK');
};
发布到 Vercel,设置对应的机器人的 webhook4,大功告成!按照 Cosflow 的语法在 Telegram 的机器人上进行输入:给 ahonn.me 捐赠 20 CNY 信用卡 > 捐赠
就可以得到对应的记录文本了。
更新 beancount 文件
在 Telegram 的机器人入口搞定了,那么现在要解决的就是存储的问题。如果 beancount 文件存放在本地,机器人很难能够进行更新。所以最后决定把 beancount 文件放在 GitHub 上,这样就可以通过 webhook 来添加记录,本地 git pull 更新之后就可以愉快的使用 进行可视化查看。
一切都非常的顺利,GitHub 对应的操作只需要生成一个 personal access tokens 配合 就可以了:
const response = await octokit.request(
'GET /repos/{owner}/{repo}/contents/{path}',
{
owner: OWNER,
repo: REPO,
path: 'txs/2021.bean',
},
);
const { content: encodeContent, encoding, sha, path } = response.data;
const content = Buffer.from(encodeContent, encoding).toString();
await octokit.request('PUT /repos/{owner}/{repo}/contents/{path}', {
path,
sha,
owner: OWNER,
repo: REPO,
message: text!,
content: Buffer.from(`${content}${output}\n\n`).toString('base64'),
});
在 Telegram 进行记录,同时 GitHub 上的 beancount 文件也有了一个新的提交,完工!
-
[特别篇 04] - 四位主播的无主题闲聊: https://pythonhunter.org/episodes/sp04
↩ -
Beancount —— 命令行复式簿记: https://wzyboy.im/post/1063.html
↩ -
Beancount复式记账(一):为什么: https://byvoid.com/zhs/blog/beancount-bookkeeping-1/
↩ -
How do I create bot:
↩ -
Telegram Bot setWebhook:
↩