@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "jwebot-official-extensions"
|
||||
version = "0.6.9a0+1015"
|
||||
version = "0.6.10a0+1015"
|
||||
description = "Reviewed local command adapters and broker implementation for JWeBot"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"jwebot-sdk==0.6.9a0+1015",
|
||||
"jwebot-sdk==0.6.10a0+1015",
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
|
||||
@@ -90,4 +90,4 @@ __all__ = [
|
||||
"render_feature_list",
|
||||
]
|
||||
|
||||
__version__ = "0.6.9a0+1015"
|
||||
__version__ = "0.6.10a0+1015"
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
"""Generated reviewed adapter for coin-loan."""
|
||||
|
||||
from jwebot_sdk import Event, ExtensionResult, ReplyPlan, SingleGroupContext
|
||||
|
||||
|
||||
PROFILE = 'coin-loan'
|
||||
AUTHOR = 'jwebot'
|
||||
DESCRIPTION = '贷款'
|
||||
HELP_ENTRIES = ('/贷款;/还款',)
|
||||
COMMANDS = ('/贷款', '/借款', '/loan', '/borrow', '/还款', '/repay')
|
||||
|
||||
|
||||
def _handler(context: SingleGroupContext, command: str):
|
||||
def handle(event: Event, arguments: str) -> ExtensionResult:
|
||||
response = context.services.call(
|
||||
PROFILE,
|
||||
operation="execute",
|
||||
payload={
|
||||
"command": command,
|
||||
"arguments": arguments,
|
||||
"event": event.to_mapping(),
|
||||
},
|
||||
)
|
||||
texts = response.get("texts")
|
||||
if isinstance(texts, list) and texts and all(
|
||||
isinstance(item, str) and item.strip() for item in texts
|
||||
):
|
||||
return ExtensionResult(
|
||||
reply=ReplyPlan.multi_text(texts),
|
||||
metadata={"local_extension": PROFILE, "author": AUTHOR},
|
||||
)
|
||||
text = response.get("text")
|
||||
if not isinstance(text, str) or not text.strip():
|
||||
raise ValueError("local service returned invalid text")
|
||||
return ExtensionResult(
|
||||
reply=ReplyPlan.text(text),
|
||||
metadata={"local_extension": PROFILE, "author": AUTHOR},
|
||||
)
|
||||
|
||||
return handle
|
||||
|
||||
|
||||
def plugin(context: SingleGroupContext) -> None:
|
||||
for command in COMMANDS:
|
||||
context.commands.register(command, _handler(context, command))
|
||||
@@ -44,6 +44,12 @@ LOCAL_COMMAND_ALIASES = {
|
||||
"wallet": "wallet",
|
||||
"转账": "transfer",
|
||||
"transfer": "transfer",
|
||||
"贷款": "loan",
|
||||
"借款": "loan",
|
||||
"loan": "loan",
|
||||
"borrow": "loan",
|
||||
"还款": "repay",
|
||||
"repay": "repay",
|
||||
"抽奖": "prize_draw",
|
||||
"draw": "prize_draw",
|
||||
"彩票": "lottery",
|
||||
@@ -165,6 +171,13 @@ LOCAL_EXTENSIONS = (
|
||||
help_entries=("/转账 对方 金额;/transfer",),
|
||||
command_kinds=("transfer",),
|
||||
),
|
||||
LocalExtensionDescriptor(
|
||||
extension_id="coin-loan",
|
||||
author=JWEBOT_AUTHOR,
|
||||
description="贷款",
|
||||
help_entries=("/贷款;/还款",),
|
||||
command_kinds=("loan", "repay"),
|
||||
),
|
||||
LocalExtensionDescriptor(
|
||||
extension_id="cs-major-autochess",
|
||||
author=JWEBOT_AUTHOR,
|
||||
|
||||
@@ -155,6 +155,8 @@ class LocalCommandEngine:
|
||||
if kind in {
|
||||
"wallet",
|
||||
"transfer",
|
||||
"loan",
|
||||
"repay",
|
||||
"prize_draw",
|
||||
"lottery",
|
||||
"blackjack",
|
||||
|
||||
+104
-3
@@ -18,6 +18,8 @@ MIN_BLACKJACK_BET = 1
|
||||
MAX_BLACKJACK_BET = 500
|
||||
MIN_TRANSFER_AMOUNT = 1
|
||||
MAX_TRANSFER_AMOUNT = 100_000
|
||||
MIN_LOAN_AMOUNT = 1
|
||||
MAX_LOAN_AMOUNT = 500
|
||||
GAME_TIMEOUT_SECONDS = 600
|
||||
GAME_HISTORY_LIMIT = 50
|
||||
GAME_HISTORY_PAGE_SIZE = 5
|
||||
@@ -53,6 +55,18 @@ TRANSFER_MARKED = re.compile(
|
||||
def normalize_player_account(account: dict[str, Any]) -> dict[str, Any]:
|
||||
account.setdefault("balance", INITIAL_COINS)
|
||||
account.setdefault("game", None)
|
||||
loan = account.get("loan")
|
||||
try:
|
||||
loan_principal = int(loan.get("principal", 0) or 0) if isinstance(loan, dict) else 0
|
||||
except (TypeError, ValueError):
|
||||
loan_principal = 0
|
||||
if loan_principal <= 0:
|
||||
account["loan"] = None
|
||||
else:
|
||||
account["loan"] = {
|
||||
"principal": loan_principal,
|
||||
"borrowed_at": float(loan.get("borrowed_at", time.time())),
|
||||
}
|
||||
account.setdefault("wins", 0)
|
||||
account.setdefault("losses", 0)
|
||||
account.setdefault("ties", 0)
|
||||
@@ -137,6 +151,8 @@ class PlayerGames:
|
||||
"每人首次使用获 1000 虚拟金币;/金币 查看余额;/游戏记录 查看本人记录。\n"
|
||||
"转账:/转账 对方 金额,或 /transfer name amount;"
|
||||
"对方需先用 /金币 初始化账户。\n"
|
||||
"贷款:/贷款 [1~500] 借入金币;/还款 [金额|全部] 归还,"
|
||||
"同一时间最多一笔未还贷款。\n"
|
||||
"抽奖:/抽奖 [2~100],5%返5倍、20%返2倍,其余无返还。\n"
|
||||
"多人彩票:/彩票 开始;/彩票 报名 3 4 7 [1~500];"
|
||||
"/彩票 当前状态;/彩票 开奖。三位号码顺序无关,省略金额默认10;"
|
||||
@@ -161,6 +177,8 @@ class PlayerGames:
|
||||
return self.group_lottery(row, arguments)
|
||||
if kind == "transfer":
|
||||
return self.transfer(row, arguments)
|
||||
if kind in {"loan", "repay"}:
|
||||
return self.loan_or_repay(row, kind, arguments)
|
||||
if kind == "wallet" and arguments:
|
||||
return "用法:/金币(/wallet),查看本人的虚拟金币;/游戏帮助 查看规则"
|
||||
if kind == "prize_draw":
|
||||
@@ -233,9 +251,13 @@ class PlayerGames:
|
||||
expired = "上一局已超时,按认输结束;"
|
||||
if kind == "wallet":
|
||||
state = "进行中" if account.get("game") else "无进行中的牌局"
|
||||
return (f"{expired}金币 {account['balance']};21点 {state};"
|
||||
f"已结算胜 {account['wins']} / 负 {account['losses']} / 平 {account['ties']}。"
|
||||
"首次赠送1000仅发一次;/游戏帮助 查看规则")
|
||||
loan = account.get("loan")
|
||||
loan_text = f"未还贷款 {loan['principal']};" if loan else "无贷款;"
|
||||
return (
|
||||
f"{expired}金币 {account['balance']};{loan_text}21点 {state};"
|
||||
f"已结算胜 {account['wins']} / 负 {account['losses']} / 平 {account['ties']}。"
|
||||
"首次赠送1000仅发一次;/游戏帮助 查看规则"
|
||||
)
|
||||
if kind == "blackjack" and action in {"hit", "stand", "status", "end"}:
|
||||
active = account.get("game")
|
||||
index = int(active["index"]) if active else None
|
||||
@@ -366,6 +388,85 @@ class PlayerGames:
|
||||
render,
|
||||
)
|
||||
|
||||
def loan_or_repay(self, row: dict[str, Any], kind: str, arguments: str) -> str:
|
||||
value = re.sub(r"\s+", "", arguments.casefold())
|
||||
if kind == "loan":
|
||||
if not re.fullmatch(r"\d{1,3}", value):
|
||||
return "用法:/贷款 [1~500];同一时间最多一笔未还贷款"
|
||||
amount = int(value)
|
||||
if not MIN_LOAN_AMOUNT <= amount <= MAX_LOAN_AMOUNT:
|
||||
return "贷款金额必须为 1~500 的整数金币"
|
||||
else:
|
||||
amount = 0
|
||||
if value not in {"", "全部", "all"}:
|
||||
if not re.fullmatch(r"\d{1,6}", value):
|
||||
return "用法:/还款 [金额|全部]"
|
||||
amount = int(value)
|
||||
if amount <= 0:
|
||||
return "还款金额必须为正整数金币"
|
||||
|
||||
group = str(row["group_fingerprint"])
|
||||
sender = str(row["sender"])
|
||||
person = self.ledger.resolve_person(group, sender)
|
||||
player_label = self.display_label(person["display_name"])
|
||||
|
||||
def execute(account: dict[str, Any]) -> str:
|
||||
normalize_player_account(account)
|
||||
loan = account.get("loan")
|
||||
if kind == "loan":
|
||||
if loan:
|
||||
return (
|
||||
f"{player_label}:已有未还贷款 {loan['principal']} 金币;"
|
||||
"请先 /还款 [金额|全部]"
|
||||
)
|
||||
account["balance"] += amount
|
||||
account["loan"] = {
|
||||
"principal": amount,
|
||||
"borrowed_at": time.time(),
|
||||
}
|
||||
return (
|
||||
f"{player_label}:贷款 {amount} 金币已到账;"
|
||||
f"当前金币 {account['balance']};未还贷款 {amount}"
|
||||
)
|
||||
if not loan:
|
||||
return f"{player_label}:当前没有未还贷款"
|
||||
debt = int(loan["principal"])
|
||||
if amount <= 0:
|
||||
pay = min(int(account["balance"]), debt)
|
||||
else:
|
||||
pay = min(amount, debt)
|
||||
if pay <= 0:
|
||||
return f"{player_label}:金币不足,无法还款;未还贷款 {debt}"
|
||||
if pay > int(account["balance"]):
|
||||
return (
|
||||
f"{player_label}:金币不足,余额 {account['balance']},"
|
||||
f"本次还款需要 {pay}"
|
||||
)
|
||||
account["balance"] -= pay
|
||||
debt -= pay
|
||||
if debt:
|
||||
account["loan"] = {
|
||||
"principal": debt,
|
||||
"borrowed_at": float(loan.get("borrowed_at", time.time())),
|
||||
}
|
||||
return (
|
||||
f"{player_label}:已还款 {pay} 金币;"
|
||||
f"剩余贷款 {debt};当前金币 {account['balance']}"
|
||||
)
|
||||
account["loan"] = None
|
||||
return (
|
||||
f"{player_label}:已还清贷款 {pay} 金币;"
|
||||
f"当前金币 {account['balance']}"
|
||||
)
|
||||
|
||||
return self.ledger.apply_player_action(
|
||||
group,
|
||||
sender,
|
||||
str(row["event_id"]),
|
||||
f"{kind}:{arguments}",
|
||||
execute,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def parse_lottery_entry(
|
||||
arguments: str,
|
||||
|
||||
Reference in New Issue
Block a user