snackmoney-testing
Version:
A CLI tool for sending USDC payments on Twitter and Farcaster using x402.
417 lines (407 loc) • 15.2 kB
JavaScript
;
var fs = require('fs');
var dotenv = require('dotenv');
var commander = require('commander');
var path2 = require('path');
var url = require('url');
var axios = require('axios');
var accounts = require('viem/accounts');
var x402Axios = require('x402-axios');
var inquirer = require('inquirer');
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var fs__default = /*#__PURE__*/_interopDefault(fs);
var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
var path2__default = /*#__PURE__*/_interopDefault(path2);
var axios__default = /*#__PURE__*/_interopDefault(axios);
var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
// node_modules/kleur/index.mjs
var FORCE_COLOR;
var NODE_DISABLE_COLORS;
var NO_COLOR;
var TERM;
var isTTY = true;
if (typeof process !== "undefined") {
({ FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env || {});
isTTY = process.stdout && process.stdout.isTTY;
}
var $ = {
enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== "dumb" && (FORCE_COLOR != null && FORCE_COLOR !== "0" || isTTY),
// modifiers
reset: init(0, 0),
bold: init(1, 22),
dim: init(2, 22),
italic: init(3, 23),
underline: init(4, 24),
inverse: init(7, 27),
hidden: init(8, 28),
strikethrough: init(9, 29),
// colors
black: init(30, 39),
red: init(31, 39),
green: init(32, 39),
yellow: init(33, 39),
blue: init(34, 39),
magenta: init(35, 39),
cyan: init(36, 39),
white: init(37, 39),
gray: init(90, 39),
grey: init(90, 39),
// background colors
bgBlack: init(40, 49),
bgRed: init(41, 49),
bgGreen: init(42, 49),
bgYellow: init(43, 49),
bgBlue: init(44, 49),
bgMagenta: init(45, 49),
bgCyan: init(46, 49),
bgWhite: init(47, 49)
};
function run(arr, str) {
let i = 0, tmp, beg = "", end = "";
for (; i < arr.length; i++) {
tmp = arr[i];
beg += tmp.open;
end += tmp.close;
if (!!~str.indexOf(tmp.close)) {
str = str.replace(tmp.rgx, tmp.close + tmp.open);
}
}
return beg + str + end;
}
function chain(has, keys) {
let ctx = { has, keys };
ctx.reset = $.reset.bind(ctx);
ctx.bold = $.bold.bind(ctx);
ctx.dim = $.dim.bind(ctx);
ctx.italic = $.italic.bind(ctx);
ctx.underline = $.underline.bind(ctx);
ctx.inverse = $.inverse.bind(ctx);
ctx.hidden = $.hidden.bind(ctx);
ctx.strikethrough = $.strikethrough.bind(ctx);
ctx.black = $.black.bind(ctx);
ctx.red = $.red.bind(ctx);
ctx.green = $.green.bind(ctx);
ctx.yellow = $.yellow.bind(ctx);
ctx.blue = $.blue.bind(ctx);
ctx.magenta = $.magenta.bind(ctx);
ctx.cyan = $.cyan.bind(ctx);
ctx.white = $.white.bind(ctx);
ctx.gray = $.gray.bind(ctx);
ctx.grey = $.grey.bind(ctx);
ctx.bgBlack = $.bgBlack.bind(ctx);
ctx.bgRed = $.bgRed.bind(ctx);
ctx.bgGreen = $.bgGreen.bind(ctx);
ctx.bgYellow = $.bgYellow.bind(ctx);
ctx.bgBlue = $.bgBlue.bind(ctx);
ctx.bgMagenta = $.bgMagenta.bind(ctx);
ctx.bgCyan = $.bgCyan.bind(ctx);
ctx.bgWhite = $.bgWhite.bind(ctx);
return ctx;
}
function init(open, close) {
let blk = {
open: `\x1B[${open}m`,
close: `\x1B[${close}m`,
rgx: new RegExp(`\\x1b\\[${close}m`, "g")
};
return function(txt) {
if (this !== void 0 && this.has !== void 0) {
!!~this.has.indexOf(open) || (this.has.push(open), this.keys.push(blk));
return txt === void 0 ? this : $.enabled ? run(this.keys, txt + "") : txt + "";
}
return txt === void 0 ? chain([open], [blk]) : $.enabled ? run([blk], txt + "") : txt + "";
};
}
var kleur_default = $;
// package.json
var package_default = {
name: "snackmoney-testing",
version: "0.0.5",
description: "A CLI tool for sending USDC payments on Twitter and Farcaster using x402."};
// src/helper/logger.ts
var logPrefix = kleur_default.bold(kleur_default.blue("Snack Money CLI:"));
var Logger = class {
constructor() {
}
static log(...args) {
console.log(logPrefix, ...args);
}
static info(...args) {
console.info(logPrefix, ...args.map((item) => kleur_default.blue(item)));
}
static success(...args) {
console.info(logPrefix, ...args.map((item) => kleur_default.green(item)));
}
static warn(...args) {
console.warn(logPrefix, ...args.map((item) => kleur_default.yellow(item)));
}
static error(...args) {
console.error(logPrefix, ...args.map((item) => kleur_default.red(item)));
}
static grey(...args) {
console.log(logPrefix, ...args.map((item) => kleur_default.gray(item)));
}
static newLine(lines) {
if (!lines) lines = 1;
for (let i = 0; i < lines; i++) this.log();
}
};
// src/actions/pay.ts
async function payAction(cmd) {
const privateKey = process.env.PRIVATE_KEY;
const baseURL = "https://api.snack.money";
const endpointPath = "/payments/pay";
if (!privateKey || !endpointPath) {
Logger.error("Missing required environment variables");
process.exit(1);
}
const { identity, username, amount } = cmd;
const allowedIdentities = ["twitter", "farcaster"];
if (!allowedIdentities.includes(identity.toLowerCase())) {
Logger.error("receiver_identity must be either 'twitter' or 'farcaster'");
process.exit(1);
}
const parsedAmount = parseFloat(amount);
if (isNaN(parsedAmount)) {
Logger.error("Amount must be a valid number, e.g., 0.01");
process.exit(1);
}
const account = accounts.privateKeyToAccount(privateKey);
const api = x402Axios.withPaymentInterceptor(axios__default.default.create({ baseURL }), account);
try {
const response = await api.post(endpointPath, {
amount: parsedAmount,
currency: "USDC",
type: "social-network",
sender_username: "snackmoney-agent-x402",
receiver_username: username,
receiver_identity: identity
});
Logger.log("response", response.data);
const paymentResponse = x402Axios.decodeXPaymentResponse(
// eslint-disable-next-line prettier/prettier
response.headers["x-payment-response"]
);
Logger.log(paymentResponse);
} catch (error) {
Logger.error("error", error);
}
}
var __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
var __dirname$1 = path2__default.default.dirname(__filename$1);
var localEnvPath = path2__default.default.resolve(__dirname$1, "../.env");
async function envAction() {
const { privateKey } = await inquirer__default.default.prompt([
{
name: "privateKey",
type: "password",
message: "Enter your Ethereum PRIVATE_KEY:",
mask: "*",
validate: (input) => {
const isValid = /^0x[0-9a-fA-F]{64}$/.test(input);
if (!isValid) {
Logger.error("Must be a valid 0x-prefixed private key");
return "Invalid private key format.";
}
return true;
}
}
]);
const envExists = fs__default.default.existsSync(localEnvPath);
const envContent = envExists ? fs__default.default.readFileSync(localEnvPath, "utf-8") : "";
const lines = envContent.split("\n").filter(Boolean);
const updatedLines = lines.filter((line) => !line.startsWith("PRIVATE_KEY="));
updatedLines.push(`PRIVATE_KEY=${privateKey}`);
fs__default.default.writeFileSync(localEnvPath, updatedLines.join("\n"), "utf-8");
Logger.info(
// eslint-disable-next-line prettier/prettier
`Environment variable PRIVATE_KEY set successfully ${localEnvPath}`
);
}
async function batchPayAction(cmd) {
const privateKey = process.env.PRIVATE_KEY;
const baseURL = "https://api.snack.money";
const endpointPath = "/payments/batch-pay";
if (!privateKey || !endpointPath) {
Logger.error("Missing required environment variables");
process.exit(1);
}
const { identity, receivers } = cmd;
const allowedIdentities = ["twitter", "farcaster"];
if (!allowedIdentities.includes(identity.toLowerCase())) {
Logger.error("receiver_identity must be either 'twitter' or 'farcaster'");
process.exit(1);
}
let parsedReceivers;
try {
parsedReceivers = JSON.parse(receivers);
if (!Array.isArray(parsedReceivers)) {
throw new Error("Receivers must be an array");
}
} catch (e) {
Logger.error(
// eslint-disable-next-line prettier/prettier
`receivers must be a valid JSON array, e.g. '[{"username":"jrsarath","name":"Sarath Singh","amount":0.5}]'`
);
process.exit(1);
}
const account = accounts.privateKeyToAccount(privateKey);
const api = x402Axios.withPaymentInterceptor(axios__default.default.create({ baseURL }), account);
try {
const response = await api.post(endpointPath, {
currency: "USDC",
type: "social-network",
sender_username: "snackmoney-agent-x402",
receiver_identity: identity,
receivers: parsedReceivers
});
Logger.log("response", response.data);
const paymentResponse = x402Axios.decodeXPaymentResponse(
// eslint-disable-next-line prettier/prettier
response.headers["x-payment-response"]
);
Logger.log(paymentResponse);
} catch (error) {
Logger.error("error", error);
}
}
async function createRewardAction(cmd) {
const privateKey = process.env.PRIVATE_KEY;
const baseURL = "https://api.snack.money";
const endpointPath = "/rewards/create-distribution";
if (!privateKey || !endpointPath) {
Logger.error("Missing required environment variables");
process.exit(1);
}
const { platform, contentId, budget } = cmd;
const allowedPlatforms = ["twitter", "farcaster"];
if (!allowedPlatforms.includes(platform.toLowerCase())) {
Logger.error("platform must be either 'twitter' or 'farcaster'");
process.exit(1);
}
const parsedBudget = parseFloat(budget);
if (isNaN(parsedBudget)) {
Logger.error("budget must be a valid number, e.g., 0.01");
process.exit(1);
}
const account = accounts.privateKeyToAccount(privateKey);
const api = x402Axios.withPaymentInterceptor(axios__default.default.create({ baseURL }), account);
try {
const response = await api.post(endpointPath, {
budget: parsedBudget,
platform,
content_id: contentId
});
Logger.log("response", JSON.stringify(response.data, null, 2));
} catch (error) {
Logger.error("error", error);
}
}
async function confirmRewardAction(cmd) {
const privateKey = process.env.PRIVATE_KEY;
const baseURL = "https://api.snack.money";
const endpointPath = "/rewards/confirm-distribution";
if (!privateKey || !endpointPath) {
Logger.error("Missing required environment variables");
process.exit(1);
}
const { orderId } = cmd;
const account = accounts.privateKeyToAccount(privateKey);
const api = x402Axios.withPaymentInterceptor(axios__default.default.create({ baseURL }), account);
try {
const response = await api.post(`${endpointPath}/${orderId}`);
Logger.log("response", JSON.stringify(response.data, null, 2));
} catch (error) {
Logger.error("error", error);
}
}
// src/helper/math-diff.ts
function matchTextScore(text, pattern) {
let score = 0;
const textLength = text.length;
const patternLength = pattern.length;
let i = 0;
let j = 0;
while (i < textLength && j < patternLength) {
if (text[i] === pattern[j]) {
score++;
j++;
}
i++;
}
return score;
}
function findMostMatchText(list, pattern) {
let maxScore = 0;
let result = "";
for (const text of list) {
const score = matchTextScore(text, pattern);
if (score > maxScore) {
maxScore = score;
result = text;
}
}
return result !== "" ? result : null;
}
// src/index.ts
var __filename2 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
var __dirname2 = path2__default.default.dirname(__filename2);
var localEnvPath2 = path2__default.default.resolve(__dirname2, "../.env");
var fallbackEnvPath = path2__default.default.resolve(process.cwd(), ".env");
var envPathToUse = fs__default.default.existsSync(localEnvPath2) ? localEnvPath2 : fs__default.default.existsSync(fallbackEnvPath) ? fallbackEnvPath : null;
if (envPathToUse) {
dotenv__default.default.config({ path: envPathToUse });
Logger.info(`Loaded .env from ${envPathToUse}`);
} else {
Logger.warn("\u26A0\uFE0F No .env file found, some commands may not work properly.");
}
var commandList = [
"pay",
"batch-pay",
"confirm-reward-distribution",
"create-reward-distribution",
"env"
];
var snackmoney = new commander.Command();
snackmoney.name(`${package_default.name}`).usage("[command]").description(`${package_default.name} - ${package_default.description} - v${package_default.version}`).version(package_default.version, "-v, --version", "Output the current version").helpOption("-h, --help", "Display help for command").action(async (_, command) => {
let isArgs = false;
if (command) {
const args = command.args?.[0];
if (args && !commandList.includes(args)) {
isArgs = true;
const matchCommand = findMostMatchText(commandList, args);
if (matchCommand) {
Logger.error(
// eslint-disable-next-line prettier/prettier
`Unknown command '${args}', Did you mean '${kleur_default.underline(matchCommand)}'?`
);
} else {
Logger.error(`Unknown command '${args}'`);
}
}
}
if (!isArgs) {
Logger.info("snackmoney --help");
}
process.exit(0);
});
snackmoney.command("env").description("Set your PRIVATE_KEY to .env file securely").action(envAction);
snackmoney.command("pay").description("Send USDC to a single user").requiredOption(
"-i --identity <identity>",
// eslint-disable-next-line prettier/prettier
"Identity platform: twitter or farcaster"
).requiredOption("-u --username <username>", "Receiver username").requiredOption("-a --amount <amount>", "Amount in USDC").action(payAction);
snackmoney.command("batch-pay").description("Send USDC to multiple recipients").requiredOption(
"-i --identity <identity>",
// eslint-disable-next-line prettier/prettier
"Identity platform: twitter or farcaster"
).requiredOption("-r --receivers <json>", "Receivers JSON array string").action(batchPayAction);
snackmoney.command("create-reward-distribution").description("Create a reward distribution order").requiredOption("-b --budget <budget>", "USDC budget").requiredOption("-p --platform <platform>", "Platform: twitter or farcaster").requiredOption("-c --content-id <contentId>", "Platform content ID").action(createRewardAction);
snackmoney.command("confirm-reward-distribution").description("Confirm reward distribution order").requiredOption(
"-o --order-id <orderId>",
// eslint-disable-next-line prettier/prettier
"Order ID to confirm and distribute"
).action(confirmRewardAction);
snackmoney.parse();