Skip to content

Commit

Permalink
feat: 支持设置用户特定的交易标签,便于多人记账
Browse files Browse the repository at this point in the history
  • Loading branch information
kaaass committed Mar 1, 2022
1 parent 0235b68 commit 95d1b0d
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- 内建自由且强大的模板语法,适用于各种记账需求
- 允许通过插件扩展记账语法
- 支持定时任务
- 支持多个用户同时记账,设置不同的标签

## 安装

Expand Down
52 changes: 48 additions & 4 deletions beancount_bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
from beancount_bot.config import get_config, load_config
from beancount_bot.dispatcher import Dispatcher
from beancount_bot.i18n import _
from beancount_bot.session import get_session, SESS_AUTH, get_session_for, set_session
from beancount_bot.session import get_session, SESS_AUTH, get_session_for, set_session, SESS_TX_TAGS
from beancount_bot.session_config import SESSION_CONFIG
from beancount_bot.task import load_task, get_task
from beancount_bot.transaction import get_manager
from beancount_bot.util import logger
from beancount_bot.util import logger, stringify_tags

apihelper.ENABLE_MIDDLEWARE = True

Expand All @@ -24,6 +25,7 @@ def session_middleware(bot_instance, message):
:param message:
:return:
"""
bot_instance.session_user_id = message.from_user.id
bot_instance.session = get_session_for(message.from_user.id)


Expand Down Expand Up @@ -112,6 +114,8 @@ def help_handler(message):
_("/help - 使用帮助"),
_("/reload - 重新加载配置文件"),
_("/task - 查看、运行任务"),
_("/set - 设置用户特定配置"),
_("/get - 获取用户特定配置"),
]
help_text = \
_("记账 Bot\n\n可用指令列表:\n{command}\n\n交易语句语法帮助请选择对应模块,或使用 /help [模块名] 查看。").format(
Expand Down Expand Up @@ -185,6 +189,46 @@ def task_handler(message):
task.trigger(bot)


##################
# Session 特有配置 #
##################

@bot.message_handler(commands=['set', 'get'])
def session_config_handler(message):
"""
设置 session 配置
:param message:
:return:
"""
if not check_auth():
bot.reply_to(message, _("请先进行鉴权!"))
return

# 解析指令
is_set = message.text.startswith('/set')
cmd: str = message.text[4:]
conf = cmd.split(maxsplit=1)
# 显示指令帮助
if len(conf) < 1:
desc = '\n'.join(v.make_help(k, is_set=is_set) for k, v in SESSION_CONFIG.items())
bot.reply_to(message, _("使用方法:\n{desc}").format(desc=desc))
return
# 获得指令对象
conf = conf[0]
if conf not in SESSION_CONFIG:
bot.reply_to(message, _("配置不存在!命令使用方法请参考 {cmd}")
.format(cmd='/set' if is_set else '/get'))
return
obj_conf = SESSION_CONFIG[conf]
# 获得参数
cmd = cmd[len(conf) + 1:]
# 调用
if is_set:
obj_conf.set(cmd, bot, message)
else:
obj_conf.get(cmd, bot, message)


#######
# 交易 #
#######
Expand All @@ -204,7 +248,7 @@ def transaction_query_handler(message: Message):
manager = get_manager()
try:
# 准备交易上下文
tags = get_config('transaction.tags', [])
tags = get_config('transaction.tags', []) + get_session(message.from_user.id, SESS_TX_TAGS, [])
# 处理交易
tx_uuid, tx = manager.create_from_str(message.text, add_tags=tags)
# 创建消息按键
Expand All @@ -216,7 +260,7 @@ def transaction_query_handler(message: Message):
logger.info(f'{message.from_user.id}:无法添加交易', exc_info=e)
bot.reply_to(message, e.args[0])
except Exception as e:
logger.error(f'{message.from_user.id}:发生未知错误!添加交易失败。', exe_info=e)
logger.error(f'{message.from_user.id}:发生未知错误!添加交易失败。', exc_info=e)
bot.reply_to(message, _("发生未知错误!添加交易失败。"))


Expand Down
1 change: 1 addition & 0 deletions beancount_bot/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from beancount_bot.util import logger

SESS_AUTH = 'auth'
SESS_TX_TAGS = 'tx_tags'

_session_cache: Dict[str, dict] = {}

Expand Down
87 changes: 87 additions & 0 deletions beancount_bot/session_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from typing import Dict

from telebot import TeleBot

from beancount_bot.config import get_config
from beancount_bot.i18n import _
from beancount_bot.session import get_session, SESS_TX_TAGS, set_session
from beancount_bot.util import stringify_tags


class SessionSpecificConfig:
"""与特定 session 相关的配置"""

def get(self, cmd, bot: TeleBot, message):
"""获取当前配置内容的指令"""
pass

def set(self, cmd, bot: TeleBot, message):
"""设置当前配置的指令"""
pass

def help(self):
"""
获取帮助信息
:return: get 指令参数, get 帮助信息, set 指令参数, set 帮助信息
"""
return '[param]', 'help', '[param]', 'help'

def make_help(self, key, is_set=False):
"""
生成帮助信息
:param key: 参数
:param is_set: 是否是 set 指令
:return: 帮助信息
"""
prefix, param, info = None, None, None
if is_set:
prefix = '/set'
_, _, param, info = self.help()
else:
prefix = '/get'
param, info, _, _ = self.help()

if len(param) > 0:
param = f' {param}'
return f'{prefix} {key}{param} - {info}'


SESSION_CONFIG: Dict[str, SessionSpecificConfig] = {}


def register_session_config(key: str, conf: SessionSpecificConfig):
"""注册特定 session 的配置"""
SESSION_CONFIG[key] = conf


##########
# 具体配置 #
##########


class TagsConfig(SessionSpecificConfig):
"""交易标签"""

def help(self):
return '', _('获取添加于交易的标签'), \
_('[标签列表]'), _('设置当前用户的标签。多个标签使用空格隔开,为空则表示清空。')

def get(self, cmd, bot, message):
"""显示所有标签"""
global_tags = get_config('transaction.tags', [])
session_tags = get_session(bot.session_user_id, SESS_TX_TAGS, [])
bot.reply_to(message,
_("全局标签:{global_tags}\n"
"当前用户标签:{session_tags}\n"
"可以通过 /set tags [标签列表] 设置当前用户的标签。")
.format(global_tags=stringify_tags(global_tags, human_readable=True),
session_tags=stringify_tags(session_tags, human_readable=True)))

def set(self, cmd, bot, message):
"""设置标签"""
tags = list(set(cmd.split()))
set_session(bot.session_user_id, SESS_TX_TAGS, tags)
bot.reply_to(message, _("成功设置用户标签!"))


register_session_config('tags', TagsConfig())
11 changes: 11 additions & 0 deletions beancount_bot/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ def stringify_errors(errors: list) -> str:
.format(lineno=err.source["lineno"], message=err.message), errors))


def stringify_tags(tags, human_readable=False):
"""
格式化 Beancount 标签列表
"""
if human_readable:
if len(tags) == 0:
return _('无')
return ', '.join(f'#{t}' for t in tags)
return ' '.join(f'#{t}' for t in tags)


def indent(text: str, prefix: str = ' ') -> str:
"""
文本增加缩进
Expand Down

0 comments on commit 95d1b0d

Please sign in to comment.