14

使用 Telegram Bot + Beancount 记账

 3 years ago
source link: https://www.ahonn.me/blog/use-telegram-bot-and-beancount-for-accounting
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

使用 Telegram Bot + Beancount 记账

Saturday, March 13, 2021BeancountTelegramBot

由于最近听的捕蛇者说的这期播客节目1,去了解了一下经常听到但是从来没去了解过的 Beancount,看了几篇网上的博文 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,这里我通过 node-telegram-bot-api 来实现:

import TelegramBot from 'node-telegram-bot-api'

const token = 'YOUR_TELEGRAM_BOT_TOKEN';
const bot = new TelegramBot(token, { polling: true });

Telegram Bot 的 Token 可以很容易的通过 @BotFather 这个机器人来得到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 更新之后就可以愉快的使用 fava 进行可视化查看。

一切都非常的顺利,GitHub 对应的操作只需要生成一个 personal access tokens 配合 octokit 就可以了:

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 文件也有了一个新的提交,完工!

interlace,1Telegram Beancount Bot


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK