Release beta 1.0.3 Mora wallet updates
This commit is contained in:
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "jwebot-kernel"
|
||||
version = "1.0.2b1+1016"
|
||||
version = "1.0.3b1+1016"
|
||||
description = "Stable routing and extension authority for JWeBot"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"jwebot-config==1.0.2b1+1016",
|
||||
"jwebot-sdk==1.0.2b1+1016",
|
||||
"jwebot-official-extensions==1.0.2b1+1016",
|
||||
"jwebot-config==1.0.3b1+1016",
|
||||
"jwebot-sdk==1.0.3b1+1016",
|
||||
"jwebot-official-extensions==1.0.3b1+1016",
|
||||
"tomli>=2; python_version < '3.11'",
|
||||
]
|
||||
|
||||
|
||||
@@ -107,4 +107,4 @@ __all__ = [
|
||||
"verify_installed_release_signature",
|
||||
]
|
||||
|
||||
__version__ = "1.0.2b1+1016"
|
||||
__version__ = "1.0.3b1+1016"
|
||||
|
||||
@@ -71,6 +71,8 @@ from jwebot_official_extensions.steam import (
|
||||
SteamUnavailable,
|
||||
)
|
||||
from jwebot_official_extensions.player_games import (
|
||||
append_coin_event,
|
||||
append_game_coin_event,
|
||||
append_game_record,
|
||||
hand_value,
|
||||
next_game_index,
|
||||
@@ -98,6 +100,7 @@ EXTENSION_SOCKET_ROOT = Path("/run/jwebot-extension-host")
|
||||
MAX_EVENT_TEXT = 5_000
|
||||
MAX_REPLY_CHARS = 900
|
||||
MAX_CONTEXT_TURNS = 6
|
||||
OCR_EVENT_SOURCES = frozenset({"ocr", "rapidocr-local"})
|
||||
MAX_OUTBOX_ATTEMPTS = 3
|
||||
DIRECT_CHAT_GENERATION_WORKERS = 2
|
||||
DIRECT_API_SOCKET_TIMEOUT_SECONDS = 18.0
|
||||
@@ -354,6 +357,11 @@ def event_question(event: dict[str, Any]) -> str:
|
||||
return bounded_text(match.group(1) if match else text, limit=MAX_EVENT_TEXT)
|
||||
|
||||
|
||||
def is_ocr_event(event: dict[str, Any]) -> bool:
|
||||
source = str(event.get("source", "")).casefold()
|
||||
return source in OCR_EVENT_SOURCES or source.startswith("rapidocr-")
|
||||
|
||||
|
||||
def classify_question(question: str) -> tuple[str, str]:
|
||||
decision = route_question(question)
|
||||
return decision.route.value, decision.reason
|
||||
@@ -1293,6 +1301,43 @@ class Ledger:
|
||||
raise EdgeError("ledger write did not persist")
|
||||
return result, cursor.rowcount == 1
|
||||
|
||||
def recent_semantic_duplicate(
|
||||
self,
|
||||
*,
|
||||
group_fingerprint: str,
|
||||
sender: str,
|
||||
question: str,
|
||||
route: str,
|
||||
route_reason: str,
|
||||
within_seconds: float = 8.0,
|
||||
) -> dict[str, Any] | None:
|
||||
"""Find OCR re-ingestion of the same visible message under a new event id."""
|
||||
group = bounded_text(group_fingerprint, limit=256)
|
||||
actor = bounded_text(sender, limit=256) or "unknown"
|
||||
content = bounded_text(question, limit=2_000)
|
||||
if not group or not actor or not content:
|
||||
return None
|
||||
cutoff = (
|
||||
datetime.now(timezone.utc) - timedelta(seconds=within_seconds)
|
||||
).isoformat(timespec="milliseconds")
|
||||
with self.lock:
|
||||
row = self.connection.execute(
|
||||
"""
|
||||
SELECT *
|
||||
FROM events
|
||||
WHERE group_fingerprint = ?
|
||||
AND sender = ?
|
||||
AND question = ?
|
||||
AND route = ?
|
||||
AND route_reason = ?
|
||||
AND created_at >= ?
|
||||
ORDER BY created_at DESC, event_id DESC
|
||||
LIMIT 1
|
||||
""",
|
||||
(group, actor, content, route, route_reason, cutoff),
|
||||
).fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
def set_event(
|
||||
self,
|
||||
event_id: str,
|
||||
@@ -3140,7 +3185,7 @@ class Ledger:
|
||||
|
||||
def _player_identity(self, group: str, sender: str) -> dict[str, str]:
|
||||
if not group or not sender or sender == "unknown":
|
||||
raise LocalCommandError("无法确认本人身份,不能操作金币账户")
|
||||
raise LocalCommandError("无法确认本人身份,不能操作摩拉账户")
|
||||
person = self.resolve_person(group, sender)
|
||||
person["display_name"] = person_display_label(person["display_name"])
|
||||
return person
|
||||
@@ -3178,13 +3223,15 @@ class Ledger:
|
||||
(group, person_key),
|
||||
).fetchone()
|
||||
if stored is None:
|
||||
return normalize_player_account({})
|
||||
account = normalize_player_account({})
|
||||
append_coin_event(account, int(account["balance"]), "首次初始化")
|
||||
return account
|
||||
try:
|
||||
account = json.loads(str(stored["state_json"]))
|
||||
except json.JSONDecodeError as error:
|
||||
raise LocalCommandError("金币账户状态无效") from error
|
||||
raise LocalCommandError("摩拉账户状态无效") from error
|
||||
if not isinstance(account, dict):
|
||||
raise LocalCommandError("金币账户状态无效")
|
||||
raise LocalCommandError("摩拉账户状态无效")
|
||||
return normalize_player_account(account)
|
||||
|
||||
def _save_player_account(
|
||||
@@ -3199,7 +3246,7 @@ class Ledger:
|
||||
type(account["balance"]) is not int
|
||||
or not 0 <= account["balance"] <= 1_000_000_000
|
||||
):
|
||||
raise LocalCommandError("金币结算超出允许范围")
|
||||
raise LocalCommandError("摩拉结算超出允许范围")
|
||||
self.connection.execute(
|
||||
"""
|
||||
INSERT INTO player_accounts VALUES (?, ?, ?, ?)
|
||||
@@ -3555,6 +3602,13 @@ class Ledger:
|
||||
+ " ".join(map(str, drawn_numbers))
|
||||
),
|
||||
)
|
||||
append_game_coin_event(
|
||||
account,
|
||||
kind="lottery",
|
||||
outcome=f"命中{matches}个",
|
||||
stake=stake,
|
||||
payout=payout,
|
||||
)
|
||||
self._save_player_account(
|
||||
group, key, account, timestamp
|
||||
)
|
||||
@@ -3661,6 +3715,13 @@ class Ledger:
|
||||
payout=stake,
|
||||
summary="主持人取消,下注已全额退回",
|
||||
)
|
||||
append_game_coin_event(
|
||||
account,
|
||||
kind="lottery",
|
||||
outcome="本轮取消",
|
||||
stake=stake,
|
||||
payout=stake,
|
||||
)
|
||||
self._save_player_account(
|
||||
group, key, account, timestamp
|
||||
)
|
||||
@@ -3827,6 +3888,13 @@ class Ledger:
|
||||
f"玩家{player_value}点,Taffy{dealer_value}点"
|
||||
),
|
||||
)
|
||||
append_game_coin_event(
|
||||
account,
|
||||
kind="blackjack",
|
||||
outcome=outcome,
|
||||
stake=bet,
|
||||
payout=payout,
|
||||
)
|
||||
self._save_player_account(group, key, account, timestamp)
|
||||
self.connection.execute(
|
||||
"""
|
||||
@@ -4830,9 +4898,9 @@ class Ledger:
|
||||
try:
|
||||
account = json.loads(str(row["state_json"]))
|
||||
except json.JSONDecodeError as error:
|
||||
raise LocalCommandError("金币账户状态无效") from error
|
||||
raise LocalCommandError("摩拉账户状态无效") from error
|
||||
if not isinstance(account, dict):
|
||||
raise LocalCommandError("金币账户状态无效")
|
||||
raise LocalCommandError("摩拉账户状态无效")
|
||||
normalize_player_account(account)
|
||||
loan = account.get("loan")
|
||||
loan_principal = (
|
||||
@@ -4939,9 +5007,9 @@ class Ledger:
|
||||
try:
|
||||
account = json.loads(str(row["state_json"]))
|
||||
except json.JSONDecodeError as error:
|
||||
raise LocalCommandError("金币账户状态无效") from error
|
||||
raise LocalCommandError("摩拉账户状态无效") from error
|
||||
if not isinstance(account, dict):
|
||||
raise LocalCommandError("金币账户状态无效")
|
||||
raise LocalCommandError("摩拉账户状态无效")
|
||||
normalize_player_account(account)
|
||||
stake = 0
|
||||
payout = 0
|
||||
@@ -5052,6 +5120,16 @@ class Ledger:
|
||||
)
|
||||
source_account["balance"] -= amount
|
||||
target_account["balance"] += amount
|
||||
append_coin_event(
|
||||
source_account,
|
||||
-amount,
|
||||
f"转账给 {target_display_name}",
|
||||
)
|
||||
append_coin_event(
|
||||
target_account,
|
||||
amount,
|
||||
f"转账 来自 {source['display_name']}",
|
||||
)
|
||||
timestamp = utc_now()
|
||||
self._save_player_account(
|
||||
group, source_key, source_account, timestamp
|
||||
@@ -6502,6 +6580,34 @@ class EdgeApplication:
|
||||
reason=reason,
|
||||
execution_path=execution_path,
|
||||
)
|
||||
existing_row = self.ledger.row(event_id) if event_id else None
|
||||
semantic_duplicate = None
|
||||
if existing_row is None and is_ocr_event(event):
|
||||
semantic_duplicate = self.ledger.recent_semantic_duplicate(
|
||||
group_fingerprint=group_id,
|
||||
sender=str(event.get("sender", "")),
|
||||
question=question,
|
||||
route=route,
|
||||
route_reason=reason,
|
||||
)
|
||||
if semantic_duplicate is not None:
|
||||
self.telemetry.elapsed(
|
||||
"edge.event.accepted",
|
||||
started,
|
||||
status="semantic_duplicate",
|
||||
event_id=event_id,
|
||||
group_id=group_id,
|
||||
route=route,
|
||||
execution_path=execution_path,
|
||||
duplicate=True,
|
||||
)
|
||||
return {
|
||||
"status": "ignored",
|
||||
"route": route,
|
||||
"execution_path": execution_path,
|
||||
"duplicate": True,
|
||||
"duplicate_event_id": str(semantic_duplicate["event_id"]),
|
||||
}
|
||||
row, created = self.ledger.record_event(
|
||||
event,
|
||||
question=question,
|
||||
|
||||
Reference in New Issue
Block a user