Discord bots
This site's data is public (see Public API), so you can bring it into your own Discord server. Two ways, from easy to more work:
/top Power and /player 24SE that anyone in your server can use.Both are small Python scripts. You need Python 3 on a computer that stays on while the bot should run (your PC, a Raspberry Pi or a cheap server).
Please: ask the API at most once a minute, and show "Data: OP Ninja Statistics (opninja.nyvix.net)" where people see it. Never share your webhook address or bot token - anyone who has them can post as your bot.
pip install requestsopninja_poster.py, paste your webhook URL into WEBHOOK, and run it with python opninja_poster.py
import time
import requests
WEBHOOK = "paste your webhook URL here"
FEED = "https://ingest.nyvix.net"
CREDIT = "-# Data: OP Ninja Statistics (opninja.nyvix.net)"
def post(text):
requests.post(WEBHOOK, json={"content": text + "\n" + CREDIT}, timeout=20)
last_top, last_moon = {}, None
while True:
try:
boards = requests.get(FEED + "/board.json", timeout=20).json()["boards"]
for name, board in boards.items():
top = board["rows"][0]
if last_top.get(name) and last_top[name] != top["name"]:
post(f"👑 **{top['name']}** took #1 on {name} from {last_top[name]} ({top['value']})")
last_top[name] = top["name"]
moon = requests.get(FEED + "/status.json", timeout=20).json().get("moon")
kind = moon["kind"] if moon else None
if kind and kind != last_moon:
post(f"🌕 **{moon['name']}** is up: {moon['effects']}")
last_moon = kind
except Exception as e:
print("could not check the feed:", e)
time.sleep(60) # once a minute is plenty: the boards change every few minutes
It stays quiet on the first check (nothing to compare with yet), then posts every change it sees.
pip install discord.py aiohttpopninja_bot.py, paste your token into TOKEN, and run it with python opninja_bot.py
import time
import aiohttp
import discord
from discord import app_commands
TOKEN = "paste your bot token here"
FEED = "https://ingest.nyvix.net"
SITE = "https://opninja.nyvix.net"
client = discord.Client(intents=discord.Intents.default())
tree = app_commands.CommandTree(client)
cache = {}
async def get(path):
# the API, remembered for a minute (a busy server doesn't ask more often)
hit = cache.get(path)
if hit and time.time() - hit[0] < 60:
return hit[1]
async with aiohttp.ClientSession() as session:
async with session.get(FEED + path, timeout=aiohttp.ClientTimeout(total=20)) as r:
data = await r.json(content_type=None)
cache[path] = (time.time(), data)
return data
def credit(embed):
embed.set_footer(text="Data: OP Ninja Statistics - opninja.nyvix.net")
return embed
@tree.command(description="The top 10 of a leaderboard")
@app_commands.choices(board=[app_commands.Choice(name="Power", value="Power"),
app_commands.Choice(name="Honor", value="Honor")])
async def top(interaction: discord.Interaction, board: app_commands.Choice[str]):
rows = (await get("/board.json"))["boards"][board.value]["rows"][:10]
lines = [f"**#{r['rank']}** {r['name']} - {r['value']} ({r['gains'].get('24h') or 'n/a'} in 24 h)" for r in rows]
embed = discord.Embed(title=f"{board.value} top 10", url=SITE, description="\n".join(lines))
await interaction.response.send_message(embed=credit(embed))
@tree.command(description="Look up a player")
async def player(interaction: discord.Interaction, username: str):
players = (await get("/players.json"))["players"]
p = next((x for x in players if x["name"].lower() == username.lower()), None)
if not p:
await interaction.response.send_message(f"No player called {username} on the leaderboards yet.")
return
def line(board):
v = p[board]
return f"#{v['rank']} - {v['value']}" if v["rank"] else f"not on the board (last {v['value'] or '-'})"
embed = discord.Embed(title=p["name"], url=f"{SITE}/player/{p['id']}")
embed.add_field(name="Power", value=line("Power"))
embed.add_field(name="Honor", value=line("Honor"))
embed.set_thumbnail(url=f"{FEED}/avatar/{p['uid']}.png?s=150")
await interaction.response.send_message(embed=credit(embed))
@tree.command(description="Is there a moon right now?")
async def moon(interaction: discord.Interaction):
m = (await get("/status.json")).get("moon")
text = f"🌕 **{m['name']}** is up: {m['effects']}" if m else "No moon right now."
await interaction.response.send_message(text)
@client.event
async def on_ready():
await tree.sync() # new commands can take a few minutes to show up in Discord
print("Logged in as", client.user)
client.run(TOKEN)
/rank <username>: their rank on both boards and how far they are from the rank above (gap in /board.json)./recap.json (biggest gain, biggest climb, most time at #1) every morning.<@&ROLE_ID> to the message so people can opt in.Everything the API offers is listed on Public API. Prefer JavaScript? discord.js works the same way: fetch the same addresses and reply with the numbers.
Sources
Parts of this wiki use information from the OP Ninja Simulator Roblox Wiki on Fandom, by its contributors, licensed under CC BY-SA. Text adapted from it is shared under the same license; each page lists its sources. A big thank-you to Deadshot123def456 for keeping the Fandom wiki up to date.