skillpp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/COMPATIBILITY.md +58 -0
  2. package/LICENSE +21 -0
  3. package/README.md +307 -0
  4. package/README.zh-CN.md +307 -0
  5. package/SKILL.md +490 -0
  6. package/adapters/binance-ai.md +22 -0
  7. package/adapters/claude.md +21 -0
  8. package/adapters/gemini.md +26 -0
  9. package/adapters/gpt.md +28 -0
  10. package/adapters/kimi.md +26 -0
  11. package/adapters/mimo.md +22 -0
  12. package/adapters/openclaw.md +29 -0
  13. package/assets/skillpp-banner.png +0 -0
  14. package/package.json +59 -0
  15. package/pipelines.md +310 -0
  16. package/prompts/newbie-mode.md +48 -0
  17. package/prompts/router-prompt.md +32 -0
  18. package/prompts/universal-system-prompt.md +41 -0
  19. package/registry.md +209 -0
  20. package/rules.md +323 -0
  21. package/schemas/audit.schema.json +67 -0
  22. package/schemas/checkpoint.schema.json +86 -0
  23. package/schemas/handoff.schema.json +82 -0
  24. package/schemas/token.schema.json +36 -0
  25. package/scripts/compatibility-check.mjs +130 -0
  26. package/scripts/selftest.mjs +384 -0
  27. package/scripts/skillpp.mjs +448 -0
  28. package/scripts/validate-skillpp.mjs +140 -0
  29. package/skillpp.manifest.json +714 -0
  30. package/skills/audit-plus/SKILL.md +612 -0
  31. package/skills/binance/binance/CHANGELOG.md +112 -0
  32. package/skills/binance/binance/LICENSE.md +9 -0
  33. package/skills/binance/binance/SKILL.md +69 -0
  34. package/skills/binance/binance/references/algo.md +21 -0
  35. package/skills/binance/binance/references/alpha.md +9 -0
  36. package/skills/binance/binance/references/auth.md +32 -0
  37. package/skills/binance/binance/references/c2c.md +5 -0
  38. package/skills/binance/binance/references/convert.md +19 -0
  39. package/skills/binance/binance/references/copy-trading.md +6 -0
  40. package/skills/binance/binance/references/crypto-loan.md +27 -0
  41. package/skills/binance/binance/references/derivatives-options-streams.md +25 -0
  42. package/skills/binance/binance/references/derivatives-options.md +85 -0
  43. package/skills/binance/binance/references/derivatives-portfolio-margin-pro-streams.md +5 -0
  44. package/skills/binance/binance/references/derivatives-portfolio-margin-pro.md +34 -0
  45. package/skills/binance/binance/references/derivatives-portfolio-margin-streams.md +5 -0
  46. package/skills/binance/binance/references/derivatives-portfolio-margin.md +146 -0
  47. package/skills/binance/binance/references/dual-investment.md +15 -0
  48. package/skills/binance/binance/references/fiat.md +9 -0
  49. package/skills/binance/binance/references/futures-coin-streams.md +29 -0
  50. package/skills/binance/binance/references/futures-coin.md +109 -0
  51. package/skills/binance/binance/references/futures-usds-streams.md +35 -0
  52. package/skills/binance/binance/references/futures-usds.md +144 -0
  53. package/skills/binance/binance/references/gift-card.md +10 -0
  54. package/skills/binance/binance/references/margin-trading-streams.md +6 -0
  55. package/skills/binance/binance/references/margin-trading.md +101 -0
  56. package/skills/binance/binance/references/mining.md +17 -0
  57. package/skills/binance/binance/references/pay.md +5 -0
  58. package/skills/binance/binance/references/rebate.md +5 -0
  59. package/skills/binance/binance/references/simple-earn.md +56 -0
  60. package/skills/binance/binance/references/spot-streams.md +25 -0
  61. package/skills/binance/binance/references/spot.md +114 -0
  62. package/skills/binance/binance/references/staking.md +59 -0
  63. package/skills/binance/binance/references/sub-account.md +67 -0
  64. package/skills/binance/binance/references/vip-loan.md +27 -0
  65. package/skills/binance/binance/references/wallet.md +75 -0
  66. package/skills/binance/fiat/CHANGELOG.md +11 -0
  67. package/skills/binance/fiat/LICENSE.md +9 -0
  68. package/skills/binance/fiat/SKILL.md +169 -0
  69. package/skills/binance/fiat/references/authentication.md +126 -0
  70. package/skills/binance/fiat/references/sapi-endpoints.md +217 -0
  71. package/skills/binance/onchain-pay/.local.md.example +10 -0
  72. package/skills/binance/onchain-pay/CHANGELOG.md +20 -0
  73. package/skills/binance/onchain-pay/LICENSE.md +9 -0
  74. package/skills/binance/onchain-pay/SKILL.md +466 -0
  75. package/skills/binance/onchain-pay/references/authentication.md +92 -0
  76. package/skills/binance/onchain-pay/scripts/sign_and_call.sh +52 -0
  77. package/skills/binance/p2p/CHANGELOG.md +33 -0
  78. package/skills/binance/p2p/LICENSE.md +9 -0
  79. package/skills/binance/p2p/SKILL.md +1082 -0
  80. package/skills/binance/p2p/references/agent-sapi-api.md +795 -0
  81. package/skills/binance/p2p/references/authentication.md +100 -0
  82. package/skills/binance/payment/SKILL.md +824 -0
  83. package/skills/binance/payment/common.py +560 -0
  84. package/skills/binance/payment/payment_skill.py +86 -0
  85. package/skills/binance/payment/receive.py +109 -0
  86. package/skills/binance/payment/references/setup-guide.md +77 -0
  87. package/skills/binance/payment/requirements.txt +4 -0
  88. package/skills/binance/payment/send.py +952 -0
  89. package/skills/binance/payment/send_extension/__init__.py +43 -0
  90. package/skills/binance/payment/send_extension/base.py +48 -0
  91. package/skills/binance/payment/send_extension/c2c.py +193 -0
  92. package/skills/binance/payment/send_extension/pix.py +316 -0
  93. package/skills/binance/square-post/README.md +62 -0
  94. package/skills/binance/square-post/SKILL.md +171 -0
  95. package/skills/binance/square-post/scripts/lib.mjs +175 -0
  96. package/skills/binance/square-post/scripts/post-image.mjs +80 -0
  97. package/skills/binance/square-post/scripts/post-text.mjs +41 -0
  98. package/skills/binance/square-post/scripts/post-video.mjs +110 -0
  99. package/skills/binance/square-post/scripts/save-key.mjs +34 -0
  100. package/skills/binance-web3/binance-agentic-wallet/SKILL.md +150 -0
  101. package/skills/binance-web3/binance-agentic-wallet/references/authentication.md +136 -0
  102. package/skills/binance-web3/binance-agentic-wallet/references/limit-order.md +204 -0
  103. package/skills/binance-web3/binance-agentic-wallet/references/market-order.md +179 -0
  104. package/skills/binance-web3/binance-agentic-wallet/references/prediction.md +489 -0
  105. package/skills/binance-web3/binance-agentic-wallet/references/preflight.md +66 -0
  106. package/skills/binance-web3/binance-agentic-wallet/references/security.md +47 -0
  107. package/skills/binance-web3/binance-agentic-wallet/references/send.md +53 -0
  108. package/skills/binance-web3/binance-agentic-wallet/references/wallet-setting.md +86 -0
  109. package/skills/binance-web3/binance-agentic-wallet/references/wallet-view.md +312 -0
  110. package/skills/binance-web3/binance-agentic-wallet/references/x402-payment.md +259 -0
  111. package/skills/binance-web3/binance-tokenized-securities-info/SKILL.md +613 -0
  112. package/skills/binance-web3/crypto-market-rank/SKILL.md +91 -0
  113. package/skills/binance-web3/crypto-market-rank/references/cli.md +219 -0
  114. package/skills/binance-web3/crypto-market-rank/scripts/cli.mjs +149 -0
  115. package/skills/binance-web3/meme-rush/SKILL.md +72 -0
  116. package/skills/binance-web3/meme-rush/references/cli.md +158 -0
  117. package/skills/binance-web3/meme-rush/scripts/cli.mjs +101 -0
  118. package/skills/binance-web3/query-address-info/SKILL.md +61 -0
  119. package/skills/binance-web3/query-address-info/references/cli.md +56 -0
  120. package/skills/binance-web3/query-address-info/scripts/cli.mjs +132 -0
  121. package/skills/binance-web3/query-token-audit/SKILL.md +162 -0
  122. package/skills/binance-web3/query-token-info/SKILL.md +83 -0
  123. package/skills/binance-web3/query-token-info/references/cli.md +135 -0
  124. package/skills/binance-web3/query-token-info/scripts/cli.mjs +112 -0
  125. package/skills/binance-web3/trading-signal/SKILL.md +66 -0
  126. package/skills/binance-web3/trading-signal/references/cli.md +90 -0
  127. package/skills/binance-web3/trading-signal/scripts/cli.mjs +92 -0
  128. package/skills/four-meme/four-guard/API-Contract-TaxToken.md +277 -0
  129. package/skills/four-meme/four-guard/API-CreateToken.02-02-2026.md +285 -0
  130. package/skills/four-meme/four-guard/API-Documents.03-03-2026.md +789 -0
  131. package/skills/four-meme/four-guard/AgentIdentifier.abi +585 -0
  132. package/skills/four-meme/four-guard/README.md +21 -0
  133. package/skills/four-meme/four-guard/SKILL.md +31 -0
  134. package/skills/four-meme/four-guard/TaxToken.abi +969 -0
  135. package/skills/four-meme/four-guard/TokenIdentifierSample.js_ +81 -0
  136. package/skills/four-meme/four-guard/TokenIdentifierSample.sol +69 -0
  137. package/skills/four-meme/four-guard/TokenManager.lite.abi +836 -0
  138. package/skills/four-meme/four-guard/TokenManager2.lite.abi +2325 -0
  139. package/skills/four-meme/four-guard/TokenManagerHelper3.abi +999 -0
  140. package/skills/four-meme/four-guard/go.mod +36 -0
  141. package/skills/four-meme/four-guard/go.sum +127 -0
  142. package/skills/four-meme/four-guard/main.go +183 -0
  143. package/skills/four-meme/four-meme-ai/SKILL.md +31 -0
  144. package/skills/four-meme/four-meme-ai/references/agent-creator-and-wallets.md +87 -0
  145. package/skills/four-meme/four-meme-ai/references/api-create-token.md +55 -0
  146. package/skills/four-meme/four-meme-ai/references/contract-addresses.md +47 -0
  147. package/skills/four-meme/four-meme-ai/references/create-token-scripts.md +131 -0
  148. package/skills/four-meme/four-meme-ai/references/errors.md +29 -0
  149. package/skills/four-meme/four-meme-ai/references/event-listening.md +75 -0
  150. package/skills/four-meme/four-meme-ai/references/execute-trade.md +31 -0
  151. package/skills/four-meme/four-meme-ai/references/tax-token-query.md +38 -0
  152. package/skills/four-meme/four-meme-ai/references/token-query-api.md +44 -0
  153. package/skills/four-meme/four-meme-ai/references/token-tax-info.md +77 -0
  154. package/skills/four-meme/four-meme-ai/scripts/8004-balance.ts +52 -0
  155. package/skills/four-meme/four-meme-ai/scripts/8004-register.ts +108 -0
  156. package/skills/four-meme/four-meme-ai/scripts/create-token-api.ts +321 -0
  157. package/skills/four-meme/four-meme-ai/scripts/create-token-chain.ts +102 -0
  158. package/skills/four-meme/four-meme-ai/scripts/create-token-instant.ts +106 -0
  159. package/skills/four-meme/four-meme-ai/scripts/execute-buy.ts +198 -0
  160. package/skills/four-meme/four-meme-ai/scripts/execute-sell.ts +150 -0
  161. package/skills/four-meme/four-meme-ai/scripts/get-public-config.ts +25 -0
  162. package/skills/four-meme/four-meme-ai/scripts/get-recent-events.ts +76 -0
  163. package/skills/four-meme/four-meme-ai/scripts/get-tax-token-info.ts +69 -0
  164. package/skills/four-meme/four-meme-ai/scripts/get-token-info.ts +94 -0
  165. package/skills/four-meme/four-meme-ai/scripts/quote-buy.ts +85 -0
  166. package/skills/four-meme/four-meme-ai/scripts/quote-sell.ts +66 -0
  167. package/skills/four-meme/four-meme-ai/scripts/send-token.ts +98 -0
  168. package/skills/four-meme/four-meme-ai/scripts/token-get.ts +31 -0
  169. package/skills/four-meme/four-meme-ai/scripts/token-list.ts +134 -0
  170. package/skills/four-meme/four-meme-ai/scripts/token-rankings.ts +162 -0
  171. package/skills/four-meme/four-meme-ai/scripts/verify-events.ts +47 -0
  172. package/skills/four-meme/four-meme-integration/SKILL.md +374 -0
  173. package/skills/four-meme/four-meme-integration/references/agent-creator-and-wallets.md +87 -0
  174. package/skills/four-meme/four-meme-integration/references/api-create-token.md +55 -0
  175. package/skills/four-meme/four-meme-integration/references/contract-addresses.md +47 -0
  176. package/skills/four-meme/four-meme-integration/references/create-token-scripts.md +131 -0
  177. package/skills/four-meme/four-meme-integration/references/errors.md +29 -0
  178. package/skills/four-meme/four-meme-integration/references/event-listening.md +75 -0
  179. package/skills/four-meme/four-meme-integration/references/execute-trade.md +31 -0
  180. package/skills/four-meme/four-meme-integration/references/tax-token-query.md +38 -0
  181. package/skills/four-meme/four-meme-integration/references/token-query-api.md +44 -0
  182. package/skills/four-meme/four-meme-integration/references/token-tax-info.md +77 -0
  183. package/skills/four-meme/four-meme-integration/scripts/8004-balance.ts +52 -0
  184. package/skills/four-meme/four-meme-integration/scripts/8004-register.ts +108 -0
  185. package/skills/four-meme/four-meme-integration/scripts/create-token-api.ts +321 -0
  186. package/skills/four-meme/four-meme-integration/scripts/create-token-chain.ts +102 -0
  187. package/skills/four-meme/four-meme-integration/scripts/create-token-instant.ts +106 -0
  188. package/skills/four-meme/four-meme-integration/scripts/execute-buy.ts +198 -0
  189. package/skills/four-meme/four-meme-integration/scripts/execute-sell.ts +150 -0
  190. package/skills/four-meme/four-meme-integration/scripts/get-public-config.ts +25 -0
  191. package/skills/four-meme/four-meme-integration/scripts/get-recent-events.ts +76 -0
  192. package/skills/four-meme/four-meme-integration/scripts/get-tax-token-info.ts +69 -0
  193. package/skills/four-meme/four-meme-integration/scripts/get-token-info.ts +94 -0
  194. package/skills/four-meme/four-meme-integration/scripts/quote-buy.ts +85 -0
  195. package/skills/four-meme/four-meme-integration/scripts/quote-sell.ts +66 -0
  196. package/skills/four-meme/four-meme-integration/scripts/send-token.ts +98 -0
  197. package/skills/four-meme/four-meme-integration/scripts/token-get.ts +31 -0
  198. package/skills/four-meme/four-meme-integration/scripts/token-list.ts +134 -0
  199. package/skills/four-meme/four-meme-integration/scripts/token-rankings.ts +162 -0
  200. package/skills/four-meme/four-meme-integration/scripts/verify-events.ts +47 -0
  201. package/skills/skillpp/contract-profiler/SKILL.md +118 -0
  202. package/skills/skillpp/newbie-tutor/SKILL.md +85 -0
  203. package/skills/skillpp/opportunity-board/SKILL.md +87 -0
  204. package/skills/skillpp/risk-fusion/SKILL.md +146 -0
  205. package/skills/skillpp/scam-pattern-lab/SKILL.md +115 -0
  206. package/skills/skillpp/wallet-doctor/SKILL.md +119 -0
  207. package/skills/skillpp/watchtower/SKILL.md +72 -0
  208. package/tests/compatibility/v0.1.0.json +117 -0
@@ -0,0 +1,43 @@
1
+ """
2
+ Payment Extension Registry.
3
+
4
+ Extensions are checked in order — first match wins.
5
+ PIX is checked before C2C because C2C is the catch-all fallback.
6
+ """
7
+ from .base import PaymentExtension
8
+ from .c2c import C2cExtension
9
+ from .pix import PixExtension
10
+
11
+ # Ordered list: specific detectors first, fallback last
12
+ EXTENSIONS = [PixExtension(), C2cExtension()]
13
+
14
+
15
+ def detect_extension(raw_qr: str) -> PaymentExtension:
16
+ """Find the matching extension for a given QR code string."""
17
+ for ext in EXTENSIONS:
18
+ if ext.detect(raw_qr):
19
+ return ext
20
+ # Should never reach here since C2C is catch-all, but just in case
21
+ return C2cExtension()
22
+
23
+
24
+ def get_extension_by_type(payment_type: str) -> PaymentExtension:
25
+ """Look up extension by payment_type string (e.g. 'C2C', 'PIX')."""
26
+ for ext in EXTENSIONS:
27
+ if ext.payment_type == payment_type:
28
+ return ext
29
+ return C2cExtension()
30
+
31
+
32
+ def get_all_endpoints() -> dict:
33
+ """Merge endpoints from all extensions into one dict.
34
+
35
+ Keys are prefixed with payment_type to avoid collisions.
36
+ e.g. 'c2c_parse_qr', 'pix_parse_qr'
37
+ """
38
+ merged = {}
39
+ for ext in EXTENSIONS:
40
+ prefix = ext.payment_type.lower()
41
+ for key, path in ext.endpoints.items():
42
+ merged[f"{prefix}_{key}"] = path
43
+ return merged
@@ -0,0 +1,48 @@
1
+ """
2
+ PaymentExtension base class.
3
+
4
+ Each payment type (C2C, PIX, ...) implements this interface.
5
+ The main payment_skill.py dispatches to the matched extension.
6
+ """
7
+ from typing import Dict, Any, Optional
8
+
9
+
10
+ class PaymentExtension:
11
+ """Base class for payment type extensions."""
12
+
13
+ payment_type: str = '' # e.g. 'C2C', 'PIX'
14
+ endpoints: Dict[str, str] = {} # endpoint_key -> path
15
+
16
+ def detect(self, raw_qr: str) -> bool:
17
+ """Return True if this extension handles the given QR data."""
18
+ return False
19
+
20
+ def purchase(self, api: Any, raw_qr: str, state_helpers: Dict[str, Any]):
21
+ """
22
+ Step 1: Parse QR code and save order state.
23
+
24
+ Args:
25
+ api: PaymentAPI instance (for making HTTP requests)
26
+ raw_qr: Raw QR code string
27
+ state_helpers: Dict with helper functions:
28
+ - set_order_status(status, **fields)
29
+ - update_state(updates)
30
+ - OrderStatus enum
31
+ """
32
+ raise NotImplementedError
33
+
34
+ def build_confirm_params(self, state: Dict[str, Any], amount: str, currency: str) -> Dict[str, Any]:
35
+ """Build request params for confirmPayment API."""
36
+ raise NotImplementedError
37
+
38
+ def get_confirm_endpoint(self) -> str:
39
+ """Return the endpoint key for confirmPayment."""
40
+ raise NotImplementedError
41
+
42
+ def get_poll_endpoint(self) -> str:
43
+ """Return the endpoint key for queryPaymentStatus."""
44
+ raise NotImplementedError
45
+
46
+ def build_poll_params(self, state: Dict[str, Any]) -> Dict[str, Any]:
47
+ """Build request params for queryPaymentStatus API."""
48
+ return {'payOrderId': state.get('pay_order_id', '')}
@@ -0,0 +1,193 @@
1
+ """
2
+ C2C Payment Extension.
3
+
4
+ Handles Binance C2C QR Code payments (URL-based QR codes).
5
+ """
6
+ import json
7
+ from typing import Dict, Any
8
+
9
+ from .base import PaymentExtension
10
+
11
+ # Payment type constant
12
+ PAYMENT_TYPE_C2C = 'C2C'
13
+
14
+
15
+ # ============================================================
16
+ # Data Models
17
+ # ============================================================
18
+ class C2cParseQrResponse:
19
+ """Response from C2C parseQr API"""
20
+ def __init__(self, data: Dict[str, Any]):
21
+ self.checkout_id = data.get('checkoutId', '')
22
+ self.checkout_type = data.get('checkoutType', '')
23
+ self.biz_type = data.get('bizType', '')
24
+ self.nickname = data.get('nickname', '')
25
+ self.avatar_url = data.get('avatarUrl', '')
26
+ self.currency = data.get('currency', '')
27
+ self.currency_fixed = data.get('currencyFixed', False)
28
+ self.amount = data.get('amount')
29
+ self.has_preset_amount = data.get('hasPresetAmount', False)
30
+ self.description = data.get('description', '')
31
+ self.single_transaction_limit = data.get('singleTransactionLimit')
32
+ self.daily_limit = data.get('dailyLimit')
33
+
34
+
35
+ class C2cConfirmPaymentResponse:
36
+ """Response from C2C confirmPayment API"""
37
+ def __init__(self, data: Dict[str, Any]):
38
+ self.pay_order_id = data.get('payOrderId', '')
39
+ self.status = data.get('status', '')
40
+ self.usd_amount = data.get('usdAmount')
41
+ self.daily_used_before = data.get('dailyUsedBefore')
42
+ self.daily_used_after = data.get('dailyUsedAfter')
43
+
44
+
45
+ # ============================================================
46
+ # C2C Extension
47
+ # ============================================================
48
+ class C2cExtension(PaymentExtension):
49
+ """C2C QR Code payment extension."""
50
+
51
+ payment_type = PAYMENT_TYPE_C2C
52
+
53
+ # OpenAPI endpoints
54
+ endpoints = {
55
+ 'parse_qr': '/binancepay/openapi/user/c2c/parseQr',
56
+ 'confirm_payment': '/binancepay/openapi/user/c2c/confirmPayment',
57
+ 'query_payment_status': '/binancepay/openapi/user/c2c/queryPaymentStatus',
58
+ }
59
+
60
+ def detect(self, raw_qr: str) -> bool:
61
+ """C2C is the default/fallback — matches anything that isn't PIX."""
62
+ # C2C detection is intentionally broad; PIX is checked first in registry.
63
+ return True
64
+
65
+ def purchase(self, api, raw_qr: str, state_helpers: Dict[str, Any]):
66
+ """C2C purchase flow - Step 1: Parse C2C QR code"""
67
+ set_order_status = state_helpers['set_order_status']
68
+ update_state = state_helpers['update_state']
69
+ OrderStatus = state_helpers['OrderStatus']
70
+
71
+ print("🔍 [Step 1] Parsing QR code...")
72
+ parse_result = api.make_parsed_request(
73
+ self.endpoints['parse_qr'],
74
+ {'rawQr': raw_qr},
75
+ C2cParseQrResponse,
76
+ use_body=True
77
+ )
78
+
79
+ if not parse_result['success']:
80
+ error_status = parse_result.get('status', 'ERROR')
81
+ error_msg = parse_result.get('message', 'Parse QR failed')
82
+ error_hint = parse_result.get('hint', '')
83
+
84
+ print(f"❌ {error_msg}")
85
+ if error_hint:
86
+ print(f"💡 {error_hint}")
87
+
88
+ set_order_status(OrderStatus.FAILED, error_message=error_msg, error_code=parse_result.get('code'))
89
+ print(json.dumps({
90
+ 'status': error_status,
91
+ 'code': parse_result.get('code'),
92
+ 'message': error_msg,
93
+ 'hint': error_hint
94
+ }))
95
+ return
96
+
97
+ order_info = parse_result['order_info']
98
+
99
+ # Save order info to state
100
+ set_order_status(OrderStatus.QR_PARSED,
101
+ checkout_id=order_info.checkout_id,
102
+ biz_type=order_info.biz_type,
103
+ nickname=order_info.nickname,
104
+ avatar_url=order_info.avatar_url,
105
+ currency=order_info.currency or 'USDT',
106
+ currency_fixed=order_info.currency_fixed,
107
+ has_preset_amount=order_info.has_preset_amount,
108
+ preset_amount=str(order_info.amount) if order_info.amount else None,
109
+ description=order_info.description,
110
+ single_transaction_limit=str(order_info.single_transaction_limit) if order_info.single_transaction_limit else None,
111
+ daily_limit=str(order_info.daily_limit) if order_info.daily_limit else None
112
+ )
113
+
114
+ print(f"✅ QR Parsed Successfully")
115
+ print(f" 📝 Checkout ID: {order_info.checkout_id}")
116
+ print(f" 🏪 Payee: {order_info.nickname}")
117
+ print(f" 💱 Currency: {order_info.currency or 'Not specified'}")
118
+ if order_info.single_transaction_limit:
119
+ print(f" 📊 Single Limit: {order_info.single_transaction_limit} USD")
120
+ if order_info.daily_limit:
121
+ print(f" 📊 Daily Limit: {order_info.daily_limit} USD")
122
+ print()
123
+
124
+ # Output result based on preset amount
125
+ if order_info.has_preset_amount and order_info.amount:
126
+ currency = order_info.currency or 'USDT'
127
+ print("════════════════════════════════════════════════════")
128
+ print(f"💰 Preset Amount: {order_info.amount} {currency}")
129
+ print("════════════════════════════════════════════════════")
130
+ print()
131
+ print("💡 Reply 'y' to confirm payment, 'n' to cancel")
132
+ update_state({
133
+ 'suggested_amount': float(order_info.amount),
134
+ 'needs_amount_input': False,
135
+ 'order_status': OrderStatus.AMOUNT_SET.value
136
+ })
137
+ print(json.dumps({
138
+ 'status': 'AWAITING_CONFIRMATION',
139
+ 'checkout_id': order_info.checkout_id,
140
+ 'biz_type': order_info.biz_type,
141
+ 'payment_type': PAYMENT_TYPE_C2C,
142
+ 'payee': order_info.nickname,
143
+ 'amount': str(order_info.amount),
144
+ 'currency': currency,
145
+ 'has_preset_amount': True,
146
+ 'single_transaction_limit': str(order_info.single_transaction_limit) if order_info.single_transaction_limit else None,
147
+ 'daily_limit': str(order_info.daily_limit) if order_info.daily_limit else None
148
+ }))
149
+ else:
150
+ currency = order_info.currency or 'USDT'
151
+ print("════════════════════════════════════════════════════")
152
+ print("📝 No preset amount")
153
+ print("════════════════════════════════════════════════════")
154
+ print()
155
+ print(f"💡 Please enter the amount (e.g., '100' or '100 USDT')")
156
+ update_state({
157
+ 'needs_amount_input': True,
158
+ 'order_status': OrderStatus.AWAITING_AMOUNT.value
159
+ })
160
+ print(json.dumps({
161
+ 'status': 'AWAITING_AMOUNT',
162
+ 'checkout_id': order_info.checkout_id,
163
+ 'biz_type': order_info.biz_type,
164
+ 'payment_type': PAYMENT_TYPE_C2C,
165
+ 'payee': order_info.nickname,
166
+ 'currency': currency,
167
+ 'has_preset_amount': False,
168
+ 'single_transaction_limit': str(order_info.single_transaction_limit) if order_info.single_transaction_limit else None,
169
+ 'daily_limit': str(order_info.daily_limit) if order_info.daily_limit else None
170
+ }))
171
+
172
+ def build_confirm_params(self, state: Dict[str, Any], amount: str, currency: str) -> Dict[str, Any]:
173
+ """Build C2C confirmPayment params (includes bizType)."""
174
+ return {
175
+ 'checkoutId': state.get('checkout_id', ''),
176
+ 'bizType': state.get('biz_type', 'C2C_QR_CODE'),
177
+ 'currency': currency,
178
+ 'amount': float(amount),
179
+ }
180
+
181
+ def get_confirm_endpoint(self) -> str:
182
+ return self.endpoints['confirm_payment']
183
+
184
+ def get_poll_endpoint(self) -> str:
185
+ return self.endpoints['query_payment_status']
186
+
187
+ def build_poll_params(self, state: Dict[str, Any]) -> Dict[str, Any]:
188
+ """C2C poll includes bizType."""
189
+ params = {'payOrderId': state.get('pay_order_id', '')}
190
+ biz_type = state.get('biz_type')
191
+ if biz_type:
192
+ params['bizType'] = biz_type
193
+ return params
@@ -0,0 +1,316 @@
1
+ """
2
+ PIX Payment Extension.
3
+
4
+ Handles Brazilian PIX EMV QR code payments (BR Code / Copia e Cola).
5
+ """
6
+ import json
7
+ from typing import Dict, Any
8
+
9
+ from .base import PaymentExtension
10
+
11
+ # Payment type constant
12
+ PAYMENT_TYPE_PIX = 'PIX'
13
+
14
+
15
+ # ============================================================
16
+ # Data Models
17
+ # ============================================================
18
+ class PixParseQrResponse:
19
+ """Response from Pix parseQr API"""
20
+ def __init__(self, data: Dict[str, Any]):
21
+ self.checkout_id = data.get('checkoutId', '')
22
+ self.status = data.get('status', '')
23
+ # Receiver info
24
+ self.receiver_name = data.get('receiverName', '')
25
+ self.receiver_psp = data.get('receiverPsp', '')
26
+ self.receiver_cnpj = data.get('receiverCnpj', '')
27
+ self.receiver_cpf = data.get('receiverCpf', '')
28
+ self.receiver_identifier = data.get('receiverIdentifier', '')
29
+ # Bill info
30
+ self.debtor_name = data.get('debtorName', '')
31
+ self.bill_due_date = data.get('billDueDate')
32
+ self.bill_amount = data.get('billAmount')
33
+ self.allow_amount_edit = data.get('allowAmountEdit', True)
34
+ # Limits (from Pix backend)
35
+ self.max_limit = data.get('maxLimit')
36
+ self.min_limit = data.get('minLimit')
37
+ self.limit_type = data.get('limitType', '')
38
+ self.limit_period_type = data.get('limitPeriodType', '')
39
+ # Limits (from Skills config)
40
+ self.single_transaction_limit = data.get('singleTransactionLimit')
41
+ self.daily_limit = data.get('dailyLimit')
42
+ # Additional info
43
+ self.additional_infos = data.get('additionalInfos', [])
44
+ self.allow_note_add = data.get('allowNoteAdd', False)
45
+
46
+ @property
47
+ def has_preset_amount(self) -> bool:
48
+ """Check if this QR has a preset amount (non-editable bill)"""
49
+ return (self.bill_amount is not None
50
+ and float(self.bill_amount) > 0
51
+ and not self.allow_amount_edit)
52
+
53
+ @property
54
+ def display_name(self) -> str:
55
+ """Get display name for the receiver"""
56
+ return self.receiver_name or self.debtor_name or 'Unknown'
57
+
58
+ @property
59
+ def display_document(self) -> str:
60
+ """Get masked document for display"""
61
+ if self.receiver_cnpj:
62
+ return f"CNPJ: {self.receiver_cnpj}"
63
+ if self.receiver_cpf:
64
+ return f"CPF: {self.receiver_cpf}"
65
+ return ''
66
+
67
+
68
+ class PixConfirmPaymentResponse:
69
+ """Response from Pix confirmPayment API"""
70
+ def __init__(self, data: Dict[str, Any]):
71
+ self.pay_order_id = data.get('payOrderId', '')
72
+ self.status = data.get('status', '')
73
+ self.usd_amount = data.get('usdAmount')
74
+ self.daily_used_before = data.get('dailyUsedBefore')
75
+ self.daily_used_after = data.get('dailyUsedAfter')
76
+
77
+
78
+ # ============================================================
79
+ # PIX EMV QR Code Parser (local preview)
80
+ # ============================================================
81
+ def parse_pix_emv_qr(qr_string: str) -> Dict[str, Any]:
82
+ """
83
+ Parse PIX EMV QR code (TLV format) for local preview display.
84
+
85
+ This extracts merchant info from the QR data before calling the API.
86
+ The API response is authoritative; this is just for quick preview.
87
+
88
+ EMVCo TLV format: Tag(2) + Length(2) + Value(Length)
89
+
90
+ Key tags:
91
+ - 53: Transaction Currency (986=BRL)
92
+ - 54: Transaction Amount
93
+ - 58: Country Code
94
+ - 59: Merchant Name
95
+ - 60: Merchant City
96
+ - 26: Merchant Account Info (contains sub-TLV with br.gov.bcb.pix)
97
+ """
98
+ result = {
99
+ 'currency': 'BRL', # default for PIX
100
+ 'country': 'BR',
101
+ }
102
+ try:
103
+ i = 0
104
+ while i + 4 <= len(qr_string):
105
+ tag = qr_string[i:i + 2]
106
+ length = int(qr_string[i + 2:i + 4])
107
+ if i + 4 + length > len(qr_string):
108
+ break
109
+ value = qr_string[i + 4:i + 4 + length]
110
+ i += 4 + length
111
+
112
+ if tag == '59':
113
+ result['merchant_name'] = value
114
+ elif tag == '60':
115
+ result['merchant_city'] = value
116
+ elif tag == '53':
117
+ result['currency_code'] = value
118
+ if value == '986':
119
+ result['currency'] = 'BRL'
120
+ elif tag == '54':
121
+ try:
122
+ result['amount'] = float(value)
123
+ except ValueError:
124
+ result['amount_raw'] = value
125
+ elif tag == '58':
126
+ result['country'] = value
127
+ except Exception:
128
+ pass
129
+ return result
130
+
131
+
132
+ # ============================================================
133
+ # PIX Extension
134
+ # ============================================================
135
+ class PixExtension(PaymentExtension):
136
+ """PIX EMV QR Code payment extension."""
137
+
138
+ payment_type = PAYMENT_TYPE_PIX
139
+
140
+ endpoints = {
141
+ 'parse_qr': '/binancepay/openapi/user/pix/parseQr',
142
+ 'confirm_payment': '/binancepay/openapi/user/pix/confirmPayment',
143
+ 'query_payment_status': '/binancepay/openapi/user/pix/queryPaymentStatus',
144
+ }
145
+
146
+ def detect(self, raw_qr: str) -> bool:
147
+ """PIX EMV QR codes contain 'br.gov.bcb.pix' as GUI identifier."""
148
+ if not raw_qr:
149
+ return False
150
+ return 'br.gov.bcb.pix' in raw_qr.lower()
151
+
152
+ def purchase(self, api, raw_qr: str, state_helpers: Dict[str, Any]):
153
+ """PIX purchase flow - Step 1: Parse PIX QR code"""
154
+ set_order_status = state_helpers['set_order_status']
155
+ update_state = state_helpers['update_state']
156
+ OrderStatus = state_helpers['OrderStatus']
157
+
158
+ # Show local preview from EMV data (before API call)
159
+ preview = parse_pix_emv_qr(raw_qr)
160
+ if preview.get('merchant_name') or preview.get('amount'):
161
+ print("📋 QR Preview (local decode):")
162
+ if preview.get('merchant_name'):
163
+ print(f" 🏪 Merchant: {preview.get('merchant_name', '')}", end='')
164
+ if preview.get('merchant_city'):
165
+ print(f" ({preview['merchant_city']})", end='')
166
+ print()
167
+ if preview.get('amount'):
168
+ print(f" 💰 Amount: {preview['amount']} {preview.get('currency', 'BRL')}")
169
+ print()
170
+
171
+ # Call PIX parseQr API
172
+ print("🔍 [Step 1] Parsing PIX QR code...")
173
+ parse_result = api.make_parsed_request(
174
+ self.endpoints['parse_qr'],
175
+ {'rawQr': raw_qr},
176
+ PixParseQrResponse,
177
+ use_body=True
178
+ )
179
+
180
+ if not parse_result['success']:
181
+ error_status = parse_result.get('status', 'ERROR')
182
+ error_msg = parse_result.get('message', 'Parse QR failed')
183
+ error_hint = parse_result.get('hint', '')
184
+
185
+ print(f"❌ {error_msg}")
186
+ if error_hint:
187
+ print(f"💡 {error_hint}")
188
+
189
+ set_order_status(OrderStatus.FAILED, error_message=error_msg, error_code=parse_result.get('code'))
190
+ print(json.dumps({
191
+ 'status': error_status,
192
+ 'code': parse_result.get('code'),
193
+ 'message': error_msg,
194
+ 'hint': error_hint
195
+ }))
196
+ return
197
+
198
+ order_info = parse_result['order_info']
199
+
200
+ # Determine currency (PIX is typically BRL)
201
+ currency = 'BRL'
202
+
203
+ # Determine amount
204
+ amount = order_info.bill_amount
205
+ pix_has_amount = amount is not None and float(amount) > 0
206
+ pix_amount_locked = pix_has_amount # True = amount from QR, cannot be changed
207
+
208
+ # Save order info to state
209
+ set_order_status(OrderStatus.QR_PARSED,
210
+ checkout_id=order_info.checkout_id,
211
+ biz_type='PIX',
212
+ nickname=order_info.display_name,
213
+ receiver_psp=order_info.receiver_psp,
214
+ receiver_document=order_info.display_document,
215
+ currency=currency,
216
+ currency_fixed=True, # PIX is always BRL
217
+ has_preset_amount=pix_has_amount,
218
+ preset_amount=str(amount) if amount else None,
219
+ allow_amount_edit=not pix_amount_locked,
220
+ pix_amount_locked=pix_amount_locked,
221
+ single_transaction_limit=str(order_info.single_transaction_limit) if order_info.single_transaction_limit else None,
222
+ daily_limit=str(order_info.daily_limit) if order_info.daily_limit else None,
223
+ pix_max_limit=str(order_info.max_limit) if order_info.max_limit else None,
224
+ pix_min_limit=str(order_info.min_limit) if order_info.min_limit else None,
225
+ additional_infos=order_info.additional_infos,
226
+ )
227
+
228
+ print(f"✅ PIX QR Parsed Successfully")
229
+ print(f" 📝 Checkout ID: {order_info.checkout_id}")
230
+ print(f" 🏪 Receiver: {order_info.display_name}")
231
+ if order_info.receiver_psp:
232
+ print(f" 🏦 Bank: {order_info.receiver_psp}")
233
+ if order_info.display_document:
234
+ print(f" 📄 {order_info.display_document}")
235
+ print(f" 💱 Currency: {currency}")
236
+ if order_info.single_transaction_limit:
237
+ print(f" 📊 Single Limit: {order_info.single_transaction_limit} USD")
238
+ if order_info.daily_limit:
239
+ print(f" 📊 Daily Limit: {order_info.daily_limit} USD")
240
+ if order_info.additional_infos:
241
+ print(f" 📎 Additional Info:")
242
+ for info in order_info.additional_infos:
243
+ print(f" {info.get('key', '')}: {info.get('value', '')}")
244
+ print()
245
+
246
+ # Output result based on whether QR has amount
247
+ if pix_has_amount:
248
+ print("════════════════════════════════════════════════════")
249
+ print(f"💰 Amount: {amount} {currency} (from QR, cannot be modified)")
250
+ print("════════════════════════════════════════════════════")
251
+ print()
252
+ print("💡 Reply 'y' to confirm payment, 'n' to cancel")
253
+ update_state({
254
+ 'suggested_amount': float(amount),
255
+ 'needs_amount_input': False,
256
+ 'order_status': OrderStatus.AMOUNT_SET.value
257
+ })
258
+ print(json.dumps({
259
+ 'status': 'AWAITING_CONFIRMATION',
260
+ 'checkout_id': order_info.checkout_id,
261
+ 'biz_type': 'PIX',
262
+ 'payment_type': PAYMENT_TYPE_PIX,
263
+ 'payee': order_info.display_name,
264
+ 'receiver_psp': order_info.receiver_psp,
265
+ 'amount': str(amount),
266
+ 'currency': currency,
267
+ 'has_preset_amount': True,
268
+ 'pix_amount_locked': True,
269
+ 'single_transaction_limit': str(order_info.single_transaction_limit) if order_info.single_transaction_limit else None,
270
+ 'daily_limit': str(order_info.daily_limit) if order_info.daily_limit else None
271
+ }))
272
+ else:
273
+ print("════════════════════════════════════════════════════")
274
+ print("📝 No preset amount")
275
+ print("════════════════════════════════════════════════════")
276
+ print()
277
+ min_hint = f" (min: {order_info.min_limit})" if order_info.min_limit else ""
278
+ max_hint = f" (max: {order_info.max_limit})" if order_info.max_limit else ""
279
+ print(f"💡 Please enter the amount in {currency}{min_hint}{max_hint}")
280
+ update_state({
281
+ 'needs_amount_input': True,
282
+ 'order_status': OrderStatus.AWAITING_AMOUNT.value
283
+ })
284
+ print(json.dumps({
285
+ 'status': 'AWAITING_AMOUNT',
286
+ 'checkout_id': order_info.checkout_id,
287
+ 'biz_type': 'PIX',
288
+ 'payment_type': PAYMENT_TYPE_PIX,
289
+ 'payee': order_info.display_name,
290
+ 'receiver_psp': order_info.receiver_psp,
291
+ 'currency': currency,
292
+ 'has_preset_amount': False,
293
+ 'pix_amount_locked': False,
294
+ 'pix_min_limit': str(order_info.min_limit) if order_info.min_limit else None,
295
+ 'pix_max_limit': str(order_info.max_limit) if order_info.max_limit else None,
296
+ 'single_transaction_limit': str(order_info.single_transaction_limit) if order_info.single_transaction_limit else None,
297
+ 'daily_limit': str(order_info.daily_limit) if order_info.daily_limit else None
298
+ }))
299
+
300
+ def build_confirm_params(self, state: Dict[str, Any], amount: str, currency: str) -> Dict[str, Any]:
301
+ """Build PIX confirmPayment params (no bizType needed)."""
302
+ return {
303
+ 'checkoutId': state.get('checkout_id', ''),
304
+ 'currency': currency,
305
+ 'amount': float(amount),
306
+ }
307
+
308
+ def get_confirm_endpoint(self) -> str:
309
+ return self.endpoints['confirm_payment']
310
+
311
+ def get_poll_endpoint(self) -> str:
312
+ return self.endpoints['query_payment_status']
313
+
314
+ def build_poll_params(self, state: Dict[str, Any]) -> Dict[str, Any]:
315
+ """PIX poll does NOT include bizType."""
316
+ return {'payOrderId': state.get('pay_order_id', '')}
@@ -0,0 +1,62 @@
1
+ # Square Post Skill
2
+
3
+ Publish text, image, article, and video posts to Binance Square through the
4
+ local Node.js scripts in `scripts/`.
5
+
6
+ ## Dependencies
7
+
8
+ ### Runtime
9
+
10
+ - Node.js 18 or newer. The scripts use native ES modules and the built-in
11
+ `fetch` API.
12
+ - Bash-capable shell for running the scripts through the agent tool.
13
+
14
+ ### System Tools
15
+
16
+ - `ffmpeg` is required for video posts. `scripts/post-video.mjs` extracts the
17
+ first frame from the source video and uploads it as the post cover.
18
+ - `ffprobe` is required when the user does not provide a video duration. The
19
+ agent uses it to determine the duration before calling `post-video.mjs`.
20
+
21
+ ### External Services
22
+
23
+ - Binance Square OpenAPI access through `BINANCE_SQUARE_OPENAPI_KEY` or the
24
+ local saved key file.
25
+ - Network access to the Square OpenAPI endpoints and to presigned upload URLs
26
+ returned by the API.
27
+
28
+ No npm package install is required for the current scripts; they only use
29
+ Node.js built-in modules.
30
+
31
+ ## Authentication
32
+
33
+ Scripts read the OpenAPI key in this order:
34
+
35
+ 1. `BINANCE_SQUARE_OPENAPI_KEY`
36
+ 2. The saved key file at `~/.config/binance-square/openapi-key`
37
+
38
+ Do not pass API keys as CLI arguments. `--key` is rejected because command-line
39
+ arguments can appear in process listings and shell history.
40
+
41
+ To save a key for future runs, explicitly run:
42
+
43
+ ```bash
44
+ BINANCE_SQUARE_OPENAPI_KEY=<apiKey> node scripts/save-key.mjs
45
+ ```
46
+
47
+ The saved key file is written with `0600` permissions. To remove it, delete
48
+ `~/.config/binance-square/openapi-key`.
49
+
50
+ ## Directory Structure
51
+
52
+ ```
53
+ square-post/
54
+ ├── SKILL.md # Skill instructions and publishing workflow
55
+ ├── README.md # Directory overview
56
+ ├── scripts/
57
+ │ ├── lib.mjs # Shared API, upload, polling, and publish helpers
58
+ │ ├── save-key.mjs # Saves the OpenAPI key to a local private config file
59
+ │ ├── post-text.mjs # Text and article publishing script
60
+ │ ├── post-image.mjs # Image post and article-with-cover publishing script
61
+ │ └── post-video.mjs # Video publishing script with generated cover
62
+ ```