lint: wrap long lines (E501), fix F841/F821/E731 — make check fully green

All 32 findings resolved: regex literals kept single-line with noqa
(pattern IS the reviewed artifact), HTML rows string-concatenated for
line length, walrus tuple in BIP-340 verify split into named generator,
unused pytest import dropped. No behavior changes; 44/44 tests pass.
Full-gate discipline restored: ruff exit 0.
This commit is contained in:
2026-10-19 22:03:53 +00:00
parent abbaf7e4e2
commit a282aedb94
7 changed files with 147 additions and 47 deletions
+20 -4
View File
@@ -15,9 +15,22 @@ from cayceops.models import MailClassification
_RULES: list[tuple[MailClassification, re.Pattern[str], float]] = [
# (class, pattern, confidence when matched)
(MailClassification.REJECTION, re.compile(r"\b(declin|reject|not able to|unable to|unfortunately|we regret|won.t be able)\w*", re.I), 0.9),
(MailClassification.APPROVAL, re.compile(r"\b(approv|accept|welcome to the|you.re (in|approved)|live|activated|glad to)\w*", re.I), 0.85),
(MailClassification.QUESTION, re.compile(r"\b(could you|can you|please (provide|clarify|confirm)|what (is|are)|how (do|does|can)|need more (info|details))\b", re.I), 0.7),
(
MailClassification.REJECTION,
# noqa: E501 (kept on one line: the pattern IS the reviewed artifact)
re.compile(r"\b(declin|reject|not able to|unable to|unfortunately|we regret|won.t be able)\w*", re.I), # noqa: E501
0.9,
),
(
MailClassification.APPROVAL,
re.compile(r"\b(approv|accept|welcome to the|you.re (in|approved)|live|activated|glad to)\w*", re.I), # noqa: E501
0.85,
),
(
MailClassification.QUESTION,
re.compile(r"\b(could you|can you|please (provide|clarify|confirm)|what (is|are)|how (do|does|can)|need more (info|details))\b", re.I), # noqa: E501
0.7,
),
]
_LLM_PROMPT = """Classify this merchant email into exactly one of:
@@ -41,7 +54,10 @@ def _llm_classify(text: str, cfg: Config) -> tuple[MailClassification, float] |
try:
provider = get_provider(cfg)
raw = provider.complete(_LLM_PROMPT.format(body=text[:4000]), system="You classify merchant emails. Reply with JSON only.")
raw = provider.complete(
_LLM_PROMPT.format(body=text[:4000]),
system="You classify merchant emails. Reply with JSON only.",
)
data = json.loads(re.search(r"\{.*\}", raw, re.S).group(0))
kind = MailClassification(data["classification"].lower())
confidence = float(data["confidence"])
+29 -15
View File
@@ -29,9 +29,11 @@ DAEMONS = ("scanner", "executor", "maild", "publisher", "monitor")
_STYLE = """<style>
body{font-family:ui-monospace,Menlo,monospace;background:#0a0908;color:#f2ece0;margin:2rem}
h1{color:#c9a84c;font-weight:600} h2{color:#c9a84c;margin-top:2rem;font-size:1.1rem}
table{border-collapse:collapse;margin:.5rem 0} td,th{border:1px solid #333;padding:.35rem .8rem;text-align:left}
table{border-collapse:collapse;margin:.5rem 0}
td,th{border:1px solid #333;padding:.35rem .8rem;text-align:left}
.ok{color:#6fce6f}.error{color:#ef6b6b}.muted{color:#888;font-size:.85rem}
.card{display:inline-block;background:#141210;border:1px solid #2a2a2a;border-radius:8px;padding:.8rem 1.4rem;margin:.3rem}
.card{display:inline-block;background:#141210;border:1px solid #2a2a2a;
border-radius:8px;padding:.8rem 1.4rem;margin:.3rem}
</style>"""
@@ -49,33 +51,45 @@ def _daemon_rows(sess) -> str:
.first()
)
if last is None:
rows.append(f"<tr><td>{name}</td><td class=muted>never run</td><td>—</td></tr>")
rows.append(
"<tr><td>" + name + "</td><td class=muted>never run</td><td>—</td></tr>"
)
else:
cls = "ok" if last.status == "ok" else "error"
when = _fmt_ts(last.finished_at or last.started_at)
rows.append(
f"<tr><td>{html.escape(name)}</td>"
f"<td class={cls}>{html.escape(last.status)}</td>"
f"<td>{_fmt_ts(last.finished_at or last.started_at)}</td></tr>"
"<tr><td>" + html.escape(name) + "</td>"
+ '<td class="' + cls + '">' + html.escape(last.status) + "</td>"
+ "<td>" + when + "</td></tr>"
)
return "<table><tr><th>daemon</th><th>last status</th><th>last run</th></tr>" + "".join(rows) + "</table>"
head = "<tr><th>daemon</th><th>last status</th><th>last run</th></tr>"
return "<table>" + head + "".join(rows) + "</table>"
def _queue_cards(sess) -> str:
counts = dict(sess.query(Proposal.status, func.count(Proposal.id)).group_by(Proposal.status).all())
cards = [
f'<div class=card>{html.escape(status.value)}: <strong>{counts.get(status, 0)}</strong></div>'
for status in ProposalStatus
]
counts = dict(
sess.query(Proposal.status, func.count(Proposal.id)).group_by(Proposal.status).all()
)
cards = []
for status in ProposalStatus:
value = counts.get(status, 0)
cards.append(
"<div class=card>" + html.escape(status.value)
+ ": <strong>" + str(value) + "</strong></div>"
)
return "".join(cards)
def _recent_audit(sess, limit: int = 20) -> str:
rows = []
for entry in sess.query(AuditEntry).order_by(AuditEntry.ts.desc()).limit(limit).all():
query = sess.query(AuditEntry).order_by(AuditEntry.ts.desc()).limit(limit)
for entry in query.all():
cls = "ok" if entry.result == "ok" else "error"
rows.append(
f"<tr><td>{_fmt_ts(entry.ts)}</td><td>{html.escape(entry.actor)}</td>"
f"<td>{html.escape(entry.action)}</td><td class={cls}>{html.escape(entry.result)}</td></tr>"
"<tr><td>" + _fmt_ts(entry.ts) + "</td>"
+ "<td>" + html.escape(entry.actor) + "</td>"
+ "<td>" + html.escape(entry.action) + "</td>"
+ '<td class="' + cls + '">' + html.escape(entry.result) + "</td></tr>"
)
body = "".join(rows) or "<tr><td colspan=4 class=muted>no activity yet</td></tr>"
return (
+12 -2
View File
@@ -15,9 +15,19 @@ WIDTH, HEIGHT = 1080, 1350
_BRANDS = {
# bitcoinhotels — warm gold on near-black
"bh": {"bg": (20, 18, 16), "accent": (201, 168, 76), "text": (242, 236, 224), "muted": (150, 140, 120)},
"bh": {
"bg": (20, 18, 16),
"accent": (201, 168, 76),
"text": (242, 236, 224),
"muted": (150, 140, 120),
},
# hardcap — bitcoin orange on cool black
"hc": {"bg": (13, 13, 16), "accent": (247, 147, 26), "text": (250, 250, 250), "muted": (120, 120, 130)},
"hc": {
"bg": (13, 13, 16),
"accent": (247, 147, 26),
"text": (250, 250, 250),
"muted": (120, 120, 130),
},
}
+13 -3
View File
@@ -119,7 +119,14 @@ def _schnorr_verify(sig32: bytes, msg32: bytes, xonly_pub32: bytes) -> bool:
if pub_point is None:
return False
e = int.from_bytes(tag_hash("BIP0340/challenge", sig32[:32] + xonly_pub32 + msg32), "big") % N
r_point = point_add(point_mul((Gx := 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, Gy := 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8), s), point_mul(pub_point, (N - e) % N))
generator = (
0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, # Gx
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, # Gy
)
r_point = point_add(
point_mul(generator, s),
point_mul(pub_point, (N - e) % N),
)
if r_point is None or r_point[1] % 2 != 0:
return False
return r_point[0] == r
@@ -134,7 +141,8 @@ def verify_event(event: dict[str, Any]) -> bool:
)
if hashlib.sha256(serialized.encode("utf-8")).hexdigest() != event["id"]:
return False
compressed = PublicKey(bytes.fromhex("02" + event["pubkey"])).format() # 33-byte, prefix 02
# 33-byte compressed point (02-prefixed x-only)
compressed = PublicKey(bytes.fromhex("02" + event["pubkey"])).format()
return _schnorr_verify(
bytes.fromhex(event["sig"]), bytes.fromhex(event["id"]), compressed[1:]
)
@@ -152,7 +160,9 @@ def relay_publish(relay_url: str, event: dict[str, Any], timeout: float = 10.0)
return str(reply[1])
def publish(relays: list[str], event: dict[str, Any], timeout: float = 10.0) -> list[dict[str, str]]:
def publish(
relays: list[str], event: dict[str, Any], timeout: float = 10.0
) -> list[dict[str, str]]:
"""Publish to every relay; report per-relay outcome (never raises unless all fail)."""
results = []
for relay in relays:
+31 -7
View File
@@ -37,10 +37,22 @@ def _caption(theme: str, link: str) -> str:
"elsalvador-budget": "A real weekly budget on a Bitcoin standard.",
}
bodies = {
"lightning-abroad": "No foreign transaction fees, no dynamic currency conversion, no declines. Field-tested guide.",
"seed-strategy": "Steel plate, memorized + decoy, or split custody — the full breakdown, brutally honest.",
"atm-fees": "The prepared traveler pays 4-6%. The difference is knowing the operator and the no-KYC cap.",
"elsalvador-budget": "Surf hostels, comidas, coworking — the corners where you still need dollars, and the 90-day protocol.",
"lightning-abroad": (
"No foreign transaction fees, no dynamic currency conversion, "
"no declines. Field-tested guide."
),
"seed-strategy": (
"Steel plate, memorized + decoy, or split custody — "
"the full breakdown, brutally honest."
),
"atm-fees": (
"The prepared traveler pays 4-6%. The difference is knowing "
"the operator and the no-KYC cap."
),
"elsalvador-budget": (
"Surf hostels, comidas, coworking — the corners where you still "
"need dollars, and the 90-day protocol."
),
}
return _CAPTION_TEMPLATE.format(
hook=hooks.get(theme, theme.replace("-", " ").title()),
@@ -83,7 +95,12 @@ def _plan(cfg: Config, sess, days: int = 4) -> dict:
)
sess.add(item)
sess.flush()
audit(sess, actor="daemon:publisher", action="plan_draft", detail={"item": item.id, "theme": theme, "channel": channel})
audit(
sess,
actor="daemon:publisher",
action="plan_draft",
detail={"item": item.id, "theme": theme, "channel": channel},
)
created += 1
return {"created": created}
@@ -103,7 +120,12 @@ def _approve(item_id: int, actor: str, sess) -> int:
)
return 1
item.status = ContentStatus.APPROVED
audit(sess, actor=actor, action="approve_content", detail={"item": item.id, "channel": item.channel})
audit(
sess,
actor=actor,
action="approve_content",
detail={"item": item.id, "channel": item.channel},
)
return 0
@@ -132,7 +154,9 @@ def _render_image(item: ContentItem, cfg: Config) -> str | None:
def _publish_nostr(item: ContentItem, cfg: Config, sess, sender=None) -> str:
if not cfg.nostr_private_key or not cfg.nostr_relays:
raise RuntimeError("nostr channel requires CAYCE_OPS_NOSTR_PRIVATE_KEY and CAYCE_OPS_NOSTR_RELAYS")
raise RuntimeError(
"nostr channel requires CAYCE_OPS_NOSTR_PRIVATE_KEY and CAYCE_OPS_NOSTR_RELAYS"
)
secret = nostr.normalize_key(cfg.nostr_private_key)
event = nostr.build_event(secret, item.caption, tags=[["t", item.theme]])
if not nostr.verify_event(event): # pragma: no cover — build/verify symmetry