Актуальный Foxity API для автоматического обновления статистики ботов и серверов.
Используйте https://foxity.foxbot.su/api.
https://foxity.foxbot.su/api/bots/:discord_bot_id/stats/ingest/| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| guilds_count | integer | Да | Количество серверов, где находится бот. |
| users_count | integer | Да | Суммарное количество участников на серверах бота. |
| online_members_count | integer | Нет | Онлайн-участники, если библиотека может получить presence данные. |
| shards_count | integer | Нет | Количество шардов. По умолчанию: 1. |
// foxity-stats.js
const API_URL = 'https://foxity.foxbot.su/api';
async function sendFoxityStats({
botId,
statsToken,
guildsCount,
usersCount,
onlineMembersCount = 0,
shardsCount = 1
}) {
const response = await fetch(`${API_URL}/bots/${botId}/stats/ingest/`, {
method: 'POST',
headers: {
'Authorization': `BotStats ${statsToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
guilds_count: guildsCount,
users_count: usersCount,
online_members_count: onlineMembersCount,
shards_count: shardsCount
})
});
if (!response.ok) {
throw new Error(`Foxity API error ${response.status}: ${await response.text()}`);
}
return response.json();
}
module.exports = { sendFoxityStats };// discord.js v14 + Node.js 18+
const API_URL = 'https://foxity.foxbot.su/api';
const STATS_TOKEN = process.env.FOXITY_STATS_TOKEN;
async function postStats(client) {
const guildCounts = client.shard
? await client.shard.fetchClientValues('guilds.cache.size')
: [client.guilds.cache.size];
const memberCounts = client.shard
? await client.shard.broadcastEval((c) =>
c.guilds.cache.reduce((total, guild) => total + (guild.memberCount ?? 0), 0)
)
: [client.guilds.cache.reduce((total, guild) => total + (guild.memberCount ?? 0), 0)];
const payload = {
guilds_count: guildCounts.reduce((total, value) => total + Number(value || 0), 0),
users_count: memberCounts.reduce((total, value) => total + Number(value || 0), 0),
shards_count: client.shard?.count ?? 1
};
const response = await fetch(`${API_URL}/bots/${client.user.id}/stats/ingest/`, {
method: 'POST',
headers: {
'Authorization': `BotStats ${STATS_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`Foxity API error ${response.status}: ${await response.text()}`);
}
}
client.once('ready', () => {
void postStats(client);
setInterval(() => void postStats(client), 30 * 60 * 1000);
});# discord.py
import aiohttp
API_URL = "https://foxity.foxbot.su/api"
STATS_TOKEN = "YOUR_STATS_TOKEN"
async def post_stats(bot):
payload = {
"guilds_count": len(bot.guilds),
"users_count": sum(guild.member_count or 0 for guild in bot.guilds),
"shards_count": bot.shard_count or 1,
}
headers = {
"Authorization": f"BotStats {STATS_TOKEN}",
"Content-Type": "application/json",
}
async with aiohttp.ClientSession() as session:
async with session.post(
f"{API_URL}/bots/{bot.user.id}/stats/ingest/",
json=payload,
headers=headers,
) as response:
if response.status >= 400:
raise RuntimeError(await response.text())# disnake
import aiohttp
API_URL = "https://foxity.foxbot.su/api"
STATS_TOKEN = "YOUR_STATS_TOKEN"
async def post_stats(bot):
payload = {
"guilds_count": len(bot.guilds),
"users_count": sum(guild.member_count or 0 for guild in bot.guilds),
"shards_count": bot.shard_count or 1,
}
headers = {
"Authorization": f"BotStats {STATS_TOKEN}",
"X-Bot-Stats-Token": STATS_TOKEN,
"Content-Type": "application/json",
}
async with aiohttp.ClientSession() as session:
async with session.post(
f"{API_URL}/bots/{bot.user.id}/stats/ingest/",
json=payload,
headers=headers,
) as response:
if response.status >= 400:
raise RuntimeError(await response.text())curl -X POST https://foxity.foxbot.su/api/bots/YOUR_BOT_ID/stats/ingest/ \
-H "Authorization: BotStats YOUR_STATS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"guilds_count": 1500,
"users_count": 120000,
"online_members_count": 42000,
"shards_count": 2
}'UP-события можно встроить в вашего собственного дискорд бота или backend через обычный HTTP webhook. Это удобно для выдачи валюты, ролей, кейсов, опыта и любых других наград после апа сервера или бота.
В настройках карточки проекта укажите Webhook URL для UP и, при желании, Webhook secret. После каждого UP Foxity отправит POST на ваш URL.
Что приходит в запросе
X-Foxity-Event: listing_votedContent-Type: application/jsonX-Foxity-Webhook-Secret только если вы задали secret в панели{
"event": "listing_voted",
"source": "site",
"project": {
"type": "server",
"id": 17,
"discord_id": "123456789012345678",
"name": "Foxity Lounge",
"owner_name": "Лис Дизайнер",
"owner_discord_id": "805400045159579668",
"votes_count": 91,
"score": 91,
"active_boost_multiplier": 3
},
"voter": {
"user_id": 42,
"discord_id": "998877665544332211",
"name": "Maxspeaker",
"avatar_url": "https://cdn.discordapp.com/avatars/..."
},
"vote": {
"id": 8841,
"awarded": 3,
"boost_multiplier": 3,
"created_at": "2026-05-08T15:04:05+00:00"
}
}import express from "express";
const app = express();
app.use(express.json());
app.post("/foxity/up-webhook", async (req, res) => {
const secret = req.header("X-Foxity-Webhook-Secret");
if (secret !== process.env.FOXITY_UP_SECRET) {
return res.sendStatus(403);
}
if (req.header("X-Foxity-Event") !== "listing_voted") {
return res.sendStatus(204);
}
const payload = req.body;
const discordId = payload?.voter?.discord_id;
const reward = Number(payload?.vote?.awarded || 1) * 100;
if (!discordId) {
return res.sendStatus(400);
}
await economy.addCoins(discordId, reward);
await rewardBot.sendMessage(discordId, `Вы получили ${reward} монет за UP проекта!`);
return res.sendStatus(204);
});
app.listen(3000);from fastapi import FastAPI, Header, HTTPException, Request, Response
import os
app = FastAPI()
FOXITY_UP_SECRET = os.getenv("FOXITY_UP_SECRET")
@app.post("/foxity/up-webhook")
async def foxity_up_webhook(
request: Request,
x_foxity_event: str | None = Header(default=None),
x_foxity_webhook_secret: str | None = Header(default=None),
):
if FOXITY_UP_SECRET and x_foxity_webhook_secret != FOXITY_UP_SECRET:
raise HTTPException(status_code=403)
if x_foxity_event != "listing_voted":
return Response(status_code=204)
payload = await request.json()
discord_id = payload.get("voter", {}).get("discord_id")
reward = int(payload.get("vote", {}).get("awarded", 1)) * 100
if not discord_id:
raise HTTPException(status_code=400, detail="Missing voter.discord_id")
await economy.add_coins(discord_id, reward)
await reward_bot.send_message(discord_id, f"Вы получили {reward} монет за UP проекта!")
return Response(status_code=204)Логика дальше уже ваша: можно начислять игровую валюту, открывать временные роли, выдавать бонусы за бустовый UP-множитель или писать в свою экономику историю наград по voter.discord_id.