@vizzor/cli 0.3.4 → 0.3.5
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.
- package/README.md +15 -0
- package/dist/index.js +291 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -205,6 +205,7 @@ vizzor config init # Initialize config
|
|
|
205
205
|
vizzor config set <key> <value> # Set config value
|
|
206
206
|
vizzor config show # Show config
|
|
207
207
|
vizzor bot start [options] # Start Discord/Telegram bots
|
|
208
|
+
vizzor bot validate # Check bot token configuration
|
|
208
209
|
```
|
|
209
210
|
|
|
210
211
|
### TUI Slash Commands
|
|
@@ -236,6 +237,20 @@ vizzor bot start [options] # Start Discord/Telegram bots
|
|
|
236
237
|
|
|
237
238
|
**Price Ticker:** Arrow keys to navigate, **Enter** to trigger full AI prediction for any token, **Tab** to toggle focus.
|
|
238
239
|
|
|
240
|
+
### Discord Bot
|
|
241
|
+
|
|
242
|
+
| Command | Description |
|
|
243
|
+
|---------|-------------|
|
|
244
|
+
| `/scan <address>` | Token security + risk scan |
|
|
245
|
+
| `/trends` | Trending tokens + market data |
|
|
246
|
+
| `/track <wallet>` | Wallet forensics |
|
|
247
|
+
| `/ico` | Upcoming launches and rounds |
|
|
248
|
+
| `/audit <contract>` | Contract audit |
|
|
249
|
+
| `/help` | Show all commands |
|
|
250
|
+
| *@mention* | AI chat guidance |
|
|
251
|
+
|
|
252
|
+
**Setup**: Enable the `MESSAGE_CONTENT` privileged intent in the [Discord Developer Portal](https://discord.com/developers/applications) for @mention responses.
|
|
253
|
+
|
|
239
254
|
### Telegram Bot
|
|
240
255
|
|
|
241
256
|
| Command | Description |
|
package/dist/index.js
CHANGED
|
@@ -2598,6 +2598,40 @@ var init_config = __esm({
|
|
|
2598
2598
|
}
|
|
2599
2599
|
});
|
|
2600
2600
|
|
|
2601
|
+
// src/discord/middleware/rate-limit.ts
|
|
2602
|
+
function checkRateLimit(userId) {
|
|
2603
|
+
const now = Date.now();
|
|
2604
|
+
const entry = userLimits.get(userId);
|
|
2605
|
+
if (!entry || now >= entry.resetAt) {
|
|
2606
|
+
userLimits.set(userId, { count: 1, resetAt: now + WINDOW_MS });
|
|
2607
|
+
return { allowed: true };
|
|
2608
|
+
}
|
|
2609
|
+
if (entry.count >= MAX_REQUESTS) {
|
|
2610
|
+
return { allowed: false };
|
|
2611
|
+
}
|
|
2612
|
+
entry.count++;
|
|
2613
|
+
return { allowed: true };
|
|
2614
|
+
}
|
|
2615
|
+
function startRateLimitCleanup(intervalMs = 3e5) {
|
|
2616
|
+
return setInterval(() => {
|
|
2617
|
+
const now = Date.now();
|
|
2618
|
+
for (const [userId, entry] of userLimits) {
|
|
2619
|
+
if (now >= entry.resetAt) {
|
|
2620
|
+
userLimits.delete(userId);
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
}, intervalMs);
|
|
2624
|
+
}
|
|
2625
|
+
var userLimits, MAX_REQUESTS, WINDOW_MS;
|
|
2626
|
+
var init_rate_limit = __esm({
|
|
2627
|
+
"src/discord/middleware/rate-limit.ts"() {
|
|
2628
|
+
"use strict";
|
|
2629
|
+
userLimits = /* @__PURE__ */ new Map();
|
|
2630
|
+
MAX_REQUESTS = 10;
|
|
2631
|
+
WINDOW_MS = 6e4;
|
|
2632
|
+
}
|
|
2633
|
+
});
|
|
2634
|
+
|
|
2601
2635
|
// src/discord/commands/index.ts
|
|
2602
2636
|
import { SlashCommandBuilder, EmbedBuilder } from "discord.js";
|
|
2603
2637
|
function registerSlashCommands() {
|
|
@@ -2607,30 +2641,54 @@ function registerSlashCommands() {
|
|
|
2607
2641
|
).addStringOption(
|
|
2608
2642
|
(opt) => opt.setName("chain").setDescription("Target chain").setRequired(false)
|
|
2609
2643
|
).toJSON(),
|
|
2610
|
-
new SlashCommandBuilder().setName("trends").setDescription("
|
|
2644
|
+
new SlashCommandBuilder().setName("trends").setDescription("Trending tokens + market data").toJSON(),
|
|
2611
2645
|
new SlashCommandBuilder().setName("track").setDescription("Analyze a wallet").addStringOption(
|
|
2612
2646
|
(opt) => opt.setName("wallet").setDescription("Wallet address").setRequired(true)
|
|
2613
2647
|
).addStringOption(
|
|
2614
2648
|
(opt) => opt.setName("chain").setDescription("Target chain").setRequired(false)
|
|
2615
2649
|
).toJSON(),
|
|
2616
|
-
new SlashCommandBuilder().setName("ico").setDescription("
|
|
2650
|
+
new SlashCommandBuilder().setName("ico").setDescription("Upcoming ICOs & fundraising rounds").toJSON(),
|
|
2617
2651
|
new SlashCommandBuilder().setName("audit").setDescription("Audit a smart contract").addStringOption(
|
|
2618
2652
|
(opt) => opt.setName("contract").setDescription("Contract address").setRequired(true)
|
|
2619
2653
|
).addStringOption(
|
|
2620
2654
|
(opt) => opt.setName("chain").setDescription("Target chain").setRequired(false)
|
|
2621
|
-
).toJSON()
|
|
2655
|
+
).toJSON(),
|
|
2656
|
+
new SlashCommandBuilder().setName("help").setDescription("Show all Vizzor commands").toJSON()
|
|
2622
2657
|
];
|
|
2623
2658
|
}
|
|
2624
2659
|
async function handleSlashCommand(interaction) {
|
|
2625
2660
|
const { commandName } = interaction;
|
|
2661
|
+
const { allowed } = checkRateLimit(interaction.user.id);
|
|
2662
|
+
if (!allowed) {
|
|
2663
|
+
await interaction.reply({
|
|
2664
|
+
content: "Rate limited. Please wait a moment before sending more commands.",
|
|
2665
|
+
ephemeral: true
|
|
2666
|
+
});
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2626
2669
|
try {
|
|
2627
2670
|
switch (commandName) {
|
|
2628
2671
|
case "scan":
|
|
2629
2672
|
await handleScanCommand(interaction);
|
|
2630
2673
|
break;
|
|
2674
|
+
case "trends":
|
|
2675
|
+
await handleTrendsCommand(interaction);
|
|
2676
|
+
break;
|
|
2677
|
+
case "track":
|
|
2678
|
+
await handleTrackCommand(interaction);
|
|
2679
|
+
break;
|
|
2680
|
+
case "ico":
|
|
2681
|
+
await handleIcoCommand(interaction);
|
|
2682
|
+
break;
|
|
2683
|
+
case "audit":
|
|
2684
|
+
await handleAuditCommand(interaction);
|
|
2685
|
+
break;
|
|
2686
|
+
case "help":
|
|
2687
|
+
await handleHelpCommand(interaction);
|
|
2688
|
+
break;
|
|
2631
2689
|
default:
|
|
2632
2690
|
await interaction.reply({
|
|
2633
|
-
content: `
|
|
2691
|
+
content: `Unknown command: \`/${commandName}\`. Use \`/help\` for available commands.`,
|
|
2634
2692
|
ephemeral: true
|
|
2635
2693
|
});
|
|
2636
2694
|
}
|
|
@@ -2678,6 +2736,133 @@ async function handleScanCommand(interaction) {
|
|
|
2678
2736
|
}
|
|
2679
2737
|
await interaction.editReply({ embeds: [embed] });
|
|
2680
2738
|
}
|
|
2739
|
+
async function handleTrendsCommand(interaction) {
|
|
2740
|
+
await interaction.deferReply();
|
|
2741
|
+
const trending = await fetchTrendingTokens();
|
|
2742
|
+
if (trending.length === 0) {
|
|
2743
|
+
await interaction.editReply("No trending data available right now.");
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
const embed = new EmbedBuilder().setTitle("Trending Tokens").setColor(5793266).setFooter({ text: "Live data from DexScreener & CoinGecko" }).setTimestamp();
|
|
2747
|
+
for (const t of trending.slice(0, 10)) {
|
|
2748
|
+
const changeSign = t.priceChange24h >= 0 ? "+" : "";
|
|
2749
|
+
const vol = t.volume24h >= 1e6 ? `$${(t.volume24h / 1e6).toFixed(1)}M` : `$${Math.round(t.volume24h).toLocaleString()}`;
|
|
2750
|
+
embed.addFields({
|
|
2751
|
+
name: `${t.symbol} (${t.chain})`,
|
|
2752
|
+
value: `Price: $${t.priceUsd}
|
|
2753
|
+
24h: ${changeSign}${t.priceChange24h.toFixed(1)}%
|
|
2754
|
+
Vol: ${vol}
|
|
2755
|
+
Source: ${t.source}`,
|
|
2756
|
+
inline: true
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2759
|
+
await interaction.editReply({ embeds: [embed] });
|
|
2760
|
+
}
|
|
2761
|
+
async function handleTrackCommand(interaction) {
|
|
2762
|
+
await interaction.deferReply();
|
|
2763
|
+
const wallet = interaction.options.getString("wallet", true);
|
|
2764
|
+
const chain = interaction.options.getString("chain") ?? "ethereum";
|
|
2765
|
+
const adapter = getAdapter(chain);
|
|
2766
|
+
await adapter.connect(void 0, getConfig().etherscanApiKey);
|
|
2767
|
+
const analysis = await analyzeWallet(wallet, adapter);
|
|
2768
|
+
await adapter.disconnect();
|
|
2769
|
+
const riskColor = analysis.riskLevel === "clean" ? 65280 : analysis.riskLevel === "suspicious" ? 16776960 : 16711680;
|
|
2770
|
+
const embed = new EmbedBuilder().setTitle(`Wallet Analysis`).setColor(riskColor).addFields(
|
|
2771
|
+
{ name: "Address", value: `\`${wallet}\``, inline: false },
|
|
2772
|
+
{ name: "Chain", value: chain, inline: true },
|
|
2773
|
+
{ name: "Balance", value: `${analysis.balance.toString()} wei`, inline: true },
|
|
2774
|
+
{ name: "Transactions", value: String(analysis.transactionCount), inline: true },
|
|
2775
|
+
{ name: "Risk Level", value: analysis.riskLevel.toUpperCase(), inline: true }
|
|
2776
|
+
).setFooter({ text: "Vizzor by 7ayLabs \u2014 Not financial advice" }).setTimestamp();
|
|
2777
|
+
if (analysis.patterns.length > 0) {
|
|
2778
|
+
const patternText = analysis.patterns.map((p) => `[${p.severity.toUpperCase()}] ${p.description}`).join("\n");
|
|
2779
|
+
embed.addFields({ name: "Patterns", value: patternText });
|
|
2780
|
+
} else {
|
|
2781
|
+
embed.addFields({ name: "Patterns", value: "No unusual patterns detected." });
|
|
2782
|
+
}
|
|
2783
|
+
await interaction.editReply({ embeds: [embed] });
|
|
2784
|
+
}
|
|
2785
|
+
async function handleIcoCommand(interaction) {
|
|
2786
|
+
await interaction.deferReply();
|
|
2787
|
+
const [icosResult, raisesResult] = await Promise.allSettled([
|
|
2788
|
+
fetchUpcomingICOs(),
|
|
2789
|
+
fetchRecentRaises(30)
|
|
2790
|
+
]);
|
|
2791
|
+
const icos = icosResult.status === "fulfilled" ? icosResult.value : [];
|
|
2792
|
+
const raises = raisesResult.status === "fulfilled" ? raisesResult.value : [];
|
|
2793
|
+
const items = raises.slice(0, 10).map((r) => ({
|
|
2794
|
+
name: r.name,
|
|
2795
|
+
round: r.round,
|
|
2796
|
+
amount: r.amount,
|
|
2797
|
+
chains: r.chains,
|
|
2798
|
+
leadInvestors: r.leadInvestors,
|
|
2799
|
+
date: new Date(r.date * 1e3).toISOString().split("T")[0] ?? ""
|
|
2800
|
+
}));
|
|
2801
|
+
const raiseNames = new Set(items.map((i) => i.name.toLowerCase()));
|
|
2802
|
+
for (const ico of icos.slice(0, 5)) {
|
|
2803
|
+
if (!raiseNames.has(ico.name.toLowerCase())) {
|
|
2804
|
+
items.push({
|
|
2805
|
+
name: ico.name,
|
|
2806
|
+
round: ico.status,
|
|
2807
|
+
amount: null,
|
|
2808
|
+
chains: [ico.chain ?? "multi-chain"],
|
|
2809
|
+
leadInvestors: [],
|
|
2810
|
+
date: ico.startDate ?? ""
|
|
2811
|
+
});
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
if (items.length === 0) {
|
|
2815
|
+
await interaction.editReply("No ICO or fundraising data available right now.");
|
|
2816
|
+
return;
|
|
2817
|
+
}
|
|
2818
|
+
const embed = new EmbedBuilder().setTitle("Upcoming ICOs & Fundraising Rounds").setColor(5793266).setFooter({ text: "Data from DeFiLlama & Pump.fun" }).setTimestamp();
|
|
2819
|
+
for (const item of items.slice(0, 10)) {
|
|
2820
|
+
const amount = item.amount ? `$${(item.amount / 1e6).toFixed(1)}M` : "Undisclosed";
|
|
2821
|
+
const chains = item.chains.join(", ") || "multi-chain";
|
|
2822
|
+
let value = `${item.round} (${amount})
|
|
2823
|
+
${chains} | ${item.date}`;
|
|
2824
|
+
if (item.leadInvestors.length > 0) {
|
|
2825
|
+
value += `
|
|
2826
|
+
Led by: ${item.leadInvestors.slice(0, 3).join(", ")}`;
|
|
2827
|
+
}
|
|
2828
|
+
embed.addFields({ name: item.name, value, inline: false });
|
|
2829
|
+
}
|
|
2830
|
+
await interaction.editReply({ embeds: [embed] });
|
|
2831
|
+
}
|
|
2832
|
+
async function handleAuditCommand(interaction) {
|
|
2833
|
+
await interaction.deferReply();
|
|
2834
|
+
const contract = interaction.options.getString("contract", true);
|
|
2835
|
+
const chain = interaction.options.getString("chain") ?? "ethereum";
|
|
2836
|
+
const adapter = getAdapter(chain);
|
|
2837
|
+
await adapter.connect(void 0, getConfig().etherscanApiKey);
|
|
2838
|
+
const result = await auditContract(contract, adapter);
|
|
2839
|
+
await adapter.disconnect();
|
|
2840
|
+
const riskColor = result.overallRisk === "low" ? 65280 : result.overallRisk === "medium" ? 16776960 : result.overallRisk === "high" ? 16746496 : 16711680;
|
|
2841
|
+
const embed = new EmbedBuilder().setTitle(`Contract Audit`).setColor(riskColor).addFields(
|
|
2842
|
+
{ name: "Address", value: `\`${contract}\``, inline: false },
|
|
2843
|
+
{ name: "Chain", value: chain, inline: true },
|
|
2844
|
+
{ name: "Risk Level", value: result.overallRisk.toUpperCase(), inline: true },
|
|
2845
|
+
{ name: "Has Code", value: result.hasCode ? "Yes" : "No", inline: true },
|
|
2846
|
+
{ name: "Code Size", value: `${result.codeSize} bytes`, inline: true }
|
|
2847
|
+
).setFooter({ text: "Vizzor by 7ayLabs \u2014 Not financial advice" }).setTimestamp();
|
|
2848
|
+
if (result.findings.length > 0) {
|
|
2849
|
+
const findingsText = result.findings.map((f) => `[${f.severity.toUpperCase()}] ${f.title}: ${f.description}`).join("\n");
|
|
2850
|
+
embed.addFields({ name: "Findings", value: findingsText.slice(0, 1024) });
|
|
2851
|
+
} else {
|
|
2852
|
+
embed.addFields({ name: "Findings", value: "No significant findings." });
|
|
2853
|
+
}
|
|
2854
|
+
await interaction.editReply({ embeds: [embed] });
|
|
2855
|
+
}
|
|
2856
|
+
async function handleHelpCommand(interaction) {
|
|
2857
|
+
await interaction.reply({
|
|
2858
|
+
embeds: [
|
|
2859
|
+
new EmbedBuilder().setTitle("Vizzor Commands").setColor(5793266).setDescription(
|
|
2860
|
+
"`/scan <address>` \u2014 Analyze token/project risk\n`/trends` \u2014 Trending tokens + market data\n`/track <wallet>` \u2014 Wallet forensics\n`/ico` \u2014 Upcoming ICOs & fundraising rounds\n`/audit <contract>` \u2014 Smart contract audit\n`/help` \u2014 Show this message\n\n_Mention the bot for AI chat guidance._"
|
|
2861
|
+
).setFooter({ text: "Vizzor by 7ayLabs" })
|
|
2862
|
+
],
|
|
2863
|
+
ephemeral: true
|
|
2864
|
+
});
|
|
2865
|
+
}
|
|
2681
2866
|
var init_commands = __esm({
|
|
2682
2867
|
"src/discord/commands/index.ts"() {
|
|
2683
2868
|
"use strict";
|
|
@@ -2685,6 +2870,12 @@ var init_commands = __esm({
|
|
|
2685
2870
|
init_loader();
|
|
2686
2871
|
init_project_analyzer();
|
|
2687
2872
|
init_risk_scorer();
|
|
2873
|
+
init_wallet_analyzer();
|
|
2874
|
+
init_contract_auditor();
|
|
2875
|
+
init_market();
|
|
2876
|
+
init_ico_tracker();
|
|
2877
|
+
init_defillama();
|
|
2878
|
+
init_rate_limit();
|
|
2688
2879
|
}
|
|
2689
2880
|
});
|
|
2690
2881
|
|
|
@@ -2695,14 +2886,19 @@ __export(bot_exports, {
|
|
|
2695
2886
|
});
|
|
2696
2887
|
import { Client, GatewayIntentBits, REST, Routes } from "discord.js";
|
|
2697
2888
|
async function startDiscordBot() {
|
|
2698
|
-
const config2 =
|
|
2889
|
+
const config2 = loadConfig();
|
|
2699
2890
|
const token = config2.discordToken;
|
|
2700
2891
|
if (!token) {
|
|
2701
2892
|
throw new Error("Discord token not configured. Run: vizzor config set discordToken <token>");
|
|
2702
2893
|
}
|
|
2703
2894
|
const client2 = new Client({
|
|
2704
|
-
intents: [
|
|
2895
|
+
intents: [
|
|
2896
|
+
GatewayIntentBits.Guilds,
|
|
2897
|
+
GatewayIntentBits.GuildMessages,
|
|
2898
|
+
GatewayIntentBits.MessageContent
|
|
2899
|
+
]
|
|
2705
2900
|
});
|
|
2901
|
+
startRateLimitCleanup();
|
|
2706
2902
|
client2.once("ready", async (readyClient) => {
|
|
2707
2903
|
console.log(`Discord bot logged in as ${readyClient.user.tag}`);
|
|
2708
2904
|
const rest = new REST({ version: "10" }).setToken(token);
|
|
@@ -2722,6 +2918,13 @@ async function startDiscordBot() {
|
|
|
2722
2918
|
if (!interaction.isChatInputCommand()) return;
|
|
2723
2919
|
await handleSlashCommand(interaction);
|
|
2724
2920
|
});
|
|
2921
|
+
client2.on("messageCreate", async (message) => {
|
|
2922
|
+
if (message.author.bot) return;
|
|
2923
|
+
if (!client2.user || !message.mentions.has(client2.user)) return;
|
|
2924
|
+
await message.reply(
|
|
2925
|
+
"Use slash commands for on-chain intelligence:\n`/scan` `/trends` `/track` `/ico` `/audit` `/help`\n\nFor full AI-powered predictions, run `vizzor` in your terminal."
|
|
2926
|
+
);
|
|
2927
|
+
});
|
|
2725
2928
|
await client2.login(token);
|
|
2726
2929
|
}
|
|
2727
2930
|
var init_bot = __esm({
|
|
@@ -2729,6 +2932,7 @@ var init_bot = __esm({
|
|
|
2729
2932
|
"use strict";
|
|
2730
2933
|
init_loader();
|
|
2731
2934
|
init_commands();
|
|
2935
|
+
init_rate_limit();
|
|
2732
2936
|
}
|
|
2733
2937
|
});
|
|
2734
2938
|
|
|
@@ -3035,36 +3239,36 @@ async function rateLimitMiddleware(ctx, next) {
|
|
|
3035
3239
|
return;
|
|
3036
3240
|
}
|
|
3037
3241
|
const now = Date.now();
|
|
3038
|
-
const entry =
|
|
3242
|
+
const entry = userLimits2.get(userId);
|
|
3039
3243
|
if (!entry || now >= entry.resetAt) {
|
|
3040
|
-
|
|
3244
|
+
userLimits2.set(userId, { count: 1, resetAt: now + WINDOW_MS2 });
|
|
3041
3245
|
await next();
|
|
3042
3246
|
return;
|
|
3043
3247
|
}
|
|
3044
|
-
if (entry.count >=
|
|
3248
|
+
if (entry.count >= MAX_REQUESTS2) {
|
|
3045
3249
|
await ctx.reply("Rate limited. Please wait a moment before sending more commands.");
|
|
3046
3250
|
return;
|
|
3047
3251
|
}
|
|
3048
3252
|
entry.count++;
|
|
3049
3253
|
await next();
|
|
3050
3254
|
}
|
|
3051
|
-
function
|
|
3255
|
+
function startRateLimitCleanup2(intervalMs = 3e5) {
|
|
3052
3256
|
return setInterval(() => {
|
|
3053
3257
|
const now = Date.now();
|
|
3054
|
-
for (const [userId, entry] of
|
|
3258
|
+
for (const [userId, entry] of userLimits2) {
|
|
3055
3259
|
if (now >= entry.resetAt) {
|
|
3056
|
-
|
|
3260
|
+
userLimits2.delete(userId);
|
|
3057
3261
|
}
|
|
3058
3262
|
}
|
|
3059
3263
|
}, intervalMs);
|
|
3060
3264
|
}
|
|
3061
|
-
var
|
|
3062
|
-
var
|
|
3265
|
+
var userLimits2, MAX_REQUESTS2, WINDOW_MS2;
|
|
3266
|
+
var init_rate_limit2 = __esm({
|
|
3063
3267
|
"src/telegram/middleware/rate-limit.ts"() {
|
|
3064
3268
|
"use strict";
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3269
|
+
userLimits2 = /* @__PURE__ */ new Map();
|
|
3270
|
+
MAX_REQUESTS2 = 10;
|
|
3271
|
+
WINDOW_MS2 = 6e4;
|
|
3068
3272
|
}
|
|
3069
3273
|
});
|
|
3070
3274
|
|
|
@@ -3120,7 +3324,7 @@ async function startTelegramBot() {
|
|
|
3120
3324
|
bot.catch((err) => {
|
|
3121
3325
|
logger.error({ err: err.message }, "Telegram bot error");
|
|
3122
3326
|
});
|
|
3123
|
-
const cleanupInterval =
|
|
3327
|
+
const cleanupInterval = startRateLimitCleanup2();
|
|
3124
3328
|
logger.info("Telegram bot starting...");
|
|
3125
3329
|
await bot.start({
|
|
3126
3330
|
onStart: (botInfo) => {
|
|
@@ -3135,7 +3339,7 @@ var init_bot2 = __esm({
|
|
|
3135
3339
|
"use strict";
|
|
3136
3340
|
init_loader();
|
|
3137
3341
|
init_commands2();
|
|
3138
|
-
|
|
3342
|
+
init_rate_limit2();
|
|
3139
3343
|
init_logger();
|
|
3140
3344
|
logger = createLogger("telegram-bot");
|
|
3141
3345
|
}
|
|
@@ -3144,7 +3348,8 @@ var init_bot2 = __esm({
|
|
|
3144
3348
|
// src/cli/commands/bot.ts
|
|
3145
3349
|
var bot_exports3 = {};
|
|
3146
3350
|
__export(bot_exports3, {
|
|
3147
|
-
handleBotStart: () => handleBotStart
|
|
3351
|
+
handleBotStart: () => handleBotStart,
|
|
3352
|
+
handleBotValidate: () => handleBotValidate
|
|
3148
3353
|
});
|
|
3149
3354
|
import chalk7 from "chalk";
|
|
3150
3355
|
async function handleBotStart(options) {
|
|
@@ -3179,6 +3384,68 @@ async function handleBotStart(options) {
|
|
|
3179
3384
|
process.on("SIGTERM", shutdown);
|
|
3180
3385
|
await Promise.all(promises);
|
|
3181
3386
|
}
|
|
3387
|
+
function handleBotValidate() {
|
|
3388
|
+
const config2 = getConfig();
|
|
3389
|
+
console.log(chalk7.bold("\nVizzor Bot Configuration Check\n"));
|
|
3390
|
+
const checks = [
|
|
3391
|
+
{
|
|
3392
|
+
label: "Anthropic API Key",
|
|
3393
|
+
value: config2.anthropicApiKey,
|
|
3394
|
+
required: true,
|
|
3395
|
+
key: "anthropicApiKey"
|
|
3396
|
+
},
|
|
3397
|
+
{
|
|
3398
|
+
label: "Etherscan API Key",
|
|
3399
|
+
value: config2.etherscanApiKey,
|
|
3400
|
+
required: true,
|
|
3401
|
+
key: "etherscanApiKey"
|
|
3402
|
+
},
|
|
3403
|
+
{ label: "Discord Token", value: config2.discordToken, required: false, key: "discordToken" },
|
|
3404
|
+
{
|
|
3405
|
+
label: "Discord Guild ID",
|
|
3406
|
+
value: config2.discordGuildId,
|
|
3407
|
+
required: false,
|
|
3408
|
+
key: "discordGuildId"
|
|
3409
|
+
},
|
|
3410
|
+
{ label: "Telegram Token", value: config2.telegramToken, required: false, key: "telegramToken" },
|
|
3411
|
+
{
|
|
3412
|
+
label: "CryptoPanic Key",
|
|
3413
|
+
value: config2.cryptopanicApiKey,
|
|
3414
|
+
required: false,
|
|
3415
|
+
key: "cryptopanicApiKey"
|
|
3416
|
+
}
|
|
3417
|
+
];
|
|
3418
|
+
let allRequired = true;
|
|
3419
|
+
for (const check of checks) {
|
|
3420
|
+
const set = hasKey(check.value);
|
|
3421
|
+
const status = set ? chalk7.green("OK") : check.required ? chalk7.red("MISSING") : chalk7.yellow("NOT SET");
|
|
3422
|
+
const masked = set ? maskKey(check.value) : chalk7.dim("(not set)");
|
|
3423
|
+
console.log(` ${status.padEnd(18)} ${check.label.padEnd(20)} ${masked}`);
|
|
3424
|
+
if (check.required && !set) allRequired = false;
|
|
3425
|
+
}
|
|
3426
|
+
console.log();
|
|
3427
|
+
if (hasKey(config2.discordToken)) {
|
|
3428
|
+
console.log(chalk7.green(" Discord bot: Ready to start"));
|
|
3429
|
+
} else {
|
|
3430
|
+
console.log(chalk7.yellow(" Discord bot: Set discordToken to enable"));
|
|
3431
|
+
console.log(chalk7.dim(" vizzor config set discordToken <token>"));
|
|
3432
|
+
}
|
|
3433
|
+
if (hasKey(config2.telegramToken)) {
|
|
3434
|
+
console.log(chalk7.green(" Telegram bot: Ready to start"));
|
|
3435
|
+
} else {
|
|
3436
|
+
console.log(chalk7.yellow(" Telegram bot: Set telegramToken to enable"));
|
|
3437
|
+
console.log(chalk7.dim(" vizzor config set telegramToken <token>"));
|
|
3438
|
+
}
|
|
3439
|
+
console.log();
|
|
3440
|
+
if (!allRequired) {
|
|
3441
|
+
console.log(chalk7.red("Required keys are missing. Run: vizzor config set <key> <value>"));
|
|
3442
|
+
} else if (hasKey(config2.discordToken) && hasKey(config2.telegramToken)) {
|
|
3443
|
+
console.log(chalk7.green("All bots ready. Run: vizzor bot start --all"));
|
|
3444
|
+
} else if (hasKey(config2.discordToken) || hasKey(config2.telegramToken)) {
|
|
3445
|
+
const which = hasKey(config2.discordToken) ? "--discord" : "--telegram";
|
|
3446
|
+
console.log(chalk7.green(`Bot ready. Run: vizzor bot start ${which}`));
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3182
3449
|
var init_bot3 = __esm({
|
|
3183
3450
|
"src/cli/commands/bot.ts"() {
|
|
3184
3451
|
"use strict";
|
|
@@ -9024,6 +9291,10 @@ botCmd.command("start").description("Start bot(s)").option("--discord", "Start D
|
|
|
9024
9291
|
const { handleBotStart: handleBotStart2 } = await Promise.resolve().then(() => (init_bot3(), bot_exports3));
|
|
9025
9292
|
await handleBotStart2(options);
|
|
9026
9293
|
});
|
|
9294
|
+
botCmd.command("validate").description("Check bot token configuration").action(async () => {
|
|
9295
|
+
const { handleBotValidate: handleBotValidate2 } = await Promise.resolve().then(() => (init_bot3(), bot_exports3));
|
|
9296
|
+
handleBotValidate2();
|
|
9297
|
+
});
|
|
9027
9298
|
var args = process.argv.slice(2);
|
|
9028
9299
|
if (args.length === 0) {
|
|
9029
9300
|
await loadConfig();
|