A powerful Python toolkit for building automation, integrations, and bots.
Project description
kroxy
A powerful Python toolkit for building automation, integrations, and bots.
Installation
pip install kroxy
pip install --upgrade kroxy
Import Style
Everything in kroxy is importable directly from the top level.
# Everything from one place
import kroxy
# Discord
antinuke = kroxy.AntiNuke(whitelist=[OWNER_ID])
manager = kroxy.GiveawayManager()
api = kroxy.DiscordAPI(token="Bot TOKEN")
embed = kroxy.Utils.build_embed(title="Hello", color=0x5865F2)
# Website
async with kroxy.Fetcher() as f:
html = await f.get("https://example.com")
title = kroxy.Scraper.get_title(html)
slug = kroxy.WebUtils.slugify("Hello World")
# Or import only what you need
from kroxy import AntiNuke, Checkers, Utils
from kroxy import GiveawayManager, MusicPlayerManager
from kroxy import DiscordAPI, SlashCommand, PrefixCommand
from kroxy import Fetcher, Scraper, WebUtils
# Submodule access also works
from kroxy import discord, website
discord.AntiNuke(...)
website.Fetcher()
Package Structure
kroxy/
├── discord/
│ ├── api.py ← Async Discord REST API client (v10)
│ ├── commands.py ← Slash and prefix command builders + registry
│ ├── utils.py ← Embeds, mentions, timestamps, permissions, text formatting
│ ├── antinuke.py ← Real-time nuke detection with auto-punishment
│ ├── checkers.py ← Permission, role, and hierarchy validation
│ ├── giveaway.py ← Weighted giveaway engine with scheduling and reroll
│ └── music.py ← Multi-guild music queue and player state manager
└── website/
├── fetch.py ← Async HTTP GET/POST/download client
├── scraper.py ← HTML parsing: links, images, meta, tables, text
└── utils.py ← URL validation, parsing, slugify, file type detection
Module Reference
DiscordAPI — Discord REST API Client
Async HTTP wrapper for the Discord REST API v10.
Constructor: DiscordAPI(token: str, bot: bool = True)
from kroxy import DiscordAPI
api = DiscordAPI(token="Bot YOUR_TOKEN")
# Guild & Channel
guild = await api.get_guild(GUILD_ID)
channel = await api.get_channel(CHANNEL_ID)
user = await api.get_user(USER_ID)
# Messaging
await api.send_message(CHANNEL_ID, content="Hello!")
await api.send_message(CHANNEL_ID, embed=embed_dict)
await api.send_message(CHANNEL_ID, content="Hi", components=[...])
await api.delete_message(CHANNEL_ID, MESSAGE_ID)
# Moderation
await api.ban_member(GUILD_ID, USER_ID, reason="Spam", delete_message_days=1)
await api.unban_member(GUILD_ID, USER_ID)
await api.kick_member(GUILD_ID, USER_ID, reason="AFK")
# Roles
await api.add_role(GUILD_ID, USER_ID, ROLE_ID)
await api.remove_role(GUILD_ID, USER_ID, ROLE_ID)
# Channels
await api.create_channel(GUILD_ID, name="general", channel_type=0)
await api.delete_channel(CHANNEL_ID)
# Audit & Invites & Webhooks
await api.get_audit_logs(GUILD_ID, limit=50)
await api.get_invites(GUILD_ID)
await api.delete_invite("invite_code")
await api.get_webhooks(GUILD_ID)
await api.delete_webhook(WEBHOOK_ID)
await api.close()
AntiNuke — Anti-Nuke System
Detects mass destructive actions per user and triggers automatic punishment.
Constructor: AntiNuke(whitelist: list = [], limits: dict = {})
Default Limits:
| Action | Threshold | Window |
|---|---|---|
ban |
3 | 10s |
kick |
3 | 10s |
channel_delete |
3 | 10s |
channel_create |
5 | 10s |
role_delete |
3 | 10s |
role_create |
5 | 10s |
webhook_create |
3 | 10s |
webhook_delete |
3 | 10s |
bot_add |
2 | 30s |
mass_mention |
5 | 5s |
from kroxy import AntiNuke
antinuke = AntiNuke(
whitelist=[OWNER_ID],
limits={"ban": (2, 5)} # override: 2 bans in 5s triggers
)
async def on_nuke(action, user_id, guild):
print(f"[NUKE] {action} by {user_id}")
antinuke.on_trigger = on_nuke
antinuke.punishment = "ban" # "ban" | "kick" | "strip_roles"
# Wire to your event system:
await antinuke.on_member_ban(user_id=uid, guild=guild)
await antinuke.on_member_kick(user_id=uid, guild=guild)
await antinuke.on_channel_delete(user_id=uid, guild=guild)
await antinuke.on_channel_create(user_id=uid, guild=guild)
await antinuke.on_role_delete(user_id=uid, guild=guild)
await antinuke.on_role_create(user_id=uid, guild=guild)
await antinuke.on_webhook_create(user_id=uid, guild=guild)
await antinuke.on_webhook_delete(user_id=uid, guild=guild)
await antinuke.on_bot_add(user_id=uid, guild=guild)
await antinuke.on_mass_mention(user_id=uid, guild=guild)
# Whitelist management
antinuke.add_whitelist(MOD_ID)
antinuke.remove_whitelist(MOD_ID)
antinuke.reset_user(USER_ID)
antinuke.get_stats() # current thresholds dict
Checkers — Permission & Role Validation
All methods raise CheckFailed on failure, return True on success.
from kroxy import Checkers, CheckFailed
try:
Checkers.require_admin(member.permissions)
Checkers.require_manage_guild(member.permissions)
Checkers.require_manage_roles(member.permissions)
Checkers.require_ban_members(member.permissions)
Checkers.require_kick_members(member.permissions)
Checkers.require_manage_channels(member.permissions)
Checkers.require_manage_messages(member.permissions)
Checkers.require_manage_webhooks(member.permissions)
Checkers.require_role(member.role_ids, MOD_ROLE_ID, "Moderator")
Checkers.require_any_role(member.role_ids, [MOD_ROLE_ID, ADMIN_ROLE_ID])
Checkers.has_all_roles(member.role_ids, [ROLE_A, ROLE_B])
Checkers.check_hierarchy(
executor_top_role_pos=member.top_role.position,
target_top_role_pos=target.top_role.position,
bot_top_role_pos=bot.top_role.position,
)
Checkers.require_guild_owner(user_id, guild.owner_id)
Checkers.require_guild_only(ctx.guild_id)
Checkers.require_dm_only(ctx.guild_id)
Checkers.block_bots(ctx.author.bot)
Checkers.require_nsfw(channel.nsfw)
except CheckFailed as e:
await ctx.send(f"❌ {e.message}")
Utils — Discord Utilities
Embed builder, mentions, timestamps, permissions, and text formatters.
from kroxy import Utils
# Embed builder
embed = Utils.build_embed(
title="Report",
description="Weekly summary",
color=0x5865F2,
fields=[
{"name": "Members", "value": "1,200", "inline": True},
{"name": "Messages", "value": "45,000", "inline": True},
],
footer="kroxy",
thumbnail="https://example.com/icon.png",
image="https://example.com/banner.png",
author_name="Bot",
author_icon="https://example.com/bot.png",
timestamp=True,
)
# Mentions
Utils.mention_user(123456) # <@123456>
Utils.mention_role(654321) # <@&654321>
Utils.mention_channel(999999) # <#999999>
Utils.parse_mention("<@123456>") # 123456
# Timestamps
Utils.discord_timestamp(dt, style="R") # <t:...:R> relative
Utils.discord_timestamp(dt, style="F") # <t:...:F> full
Utils.time_until(3725) # "1 hour, 2 minutes, 5 seconds"
Utils.snowflake_to_timestamp(id) # datetime object
# Permissions
Utils.has_permission(bitfield, "administrator") # True/False
Utils.has_permission(bitfield, "ban_members")
Utils.permissions_list(bitfield) # list of names
# Text formatting
Utils.truncate(text, max_length=2048)
Utils.code_block("print('hi')", language="python")
Utils.inline_code("var")
Utils.bold("text") # **text**
Utils.italic("text") # *text*
Utils.underline("text") # __text__
Utils.strikethrough("text") # ~~text~~
Utils.spoiler("text") # ||text||
GiveawayManager — Giveaway Engine
Weighted giveaway system with bonus roles, auto-scheduling, and reroll.
from kroxy import GiveawayManager
manager = GiveawayManager()
async def on_end(giveaway, winners):
print(f"Winners of '{giveaway.prize}': {winners}")
manager.on_end = on_end
giveaway = await manager.create(
prize="Discord Nitro",
host_id=HOST_ID,
channel_id=CHANNEL_ID,
guild_id=GUILD_ID,
duration=86400, # seconds
winner_count=3,
required_role_id=MEMBER_ROLE_ID,
bonus_roles={
BOOSTER_ROLE_ID: 2, # +2 extra entries
VIP_ROLE_ID: 4, # +4 extra entries
},
)
giveaway.add_entry(user_id=555, role_ids=[BOOSTER_ROLE_ID])
giveaway.remove_entry(user_id=555)
giveaway.is_active # True/False
giveaway.time_remaining # seconds
giveaway.total_entries # weighted count
giveaway.participant_count # unique users
giveaway.giveaway_id # "A1B2C3D4"
giveaway.winners # list of user IDs after end
await manager.end_now(giveaway.giveaway_id)
await manager.cancel(giveaway.giveaway_id)
new_winners = giveaway.reroll()
manager.get(giveaway_id)
manager.get_by_message(message_id)
manager.all_active()
manager.all_ended()
MusicPlayerManager — Music Player
Multi-guild music queue and player state manager.
from kroxy import MusicPlayerManager, Track, LoopMode
manager = MusicPlayerManager()
player = manager.get_or_create(
guild_id=GUILD_ID,
channel_id=VOICE_CHANNEL_ID,
text_channel_id=TEXT_CHANNEL_ID,
)
track = Track(
title="Song Name",
url="https://youtube.com/watch?v=...",
stream_url="https://audio.stream/file.mp3",
duration=240,
requester_id=USER_ID,
thumbnail="https://img.youtube.com/vi/.../0.jpg",
source="youtube",
)
# Queue
player.queue.add(track)
player.queue.add_next(track)
player.queue.shuffle()
player.queue.remove(index=0)
player.queue.move(from_index=2, to_index=0)
player.queue.clear()
len(player.queue)
player.queue.total_duration
player.queue.is_empty
# Playback
await player.play_next()
await player.skip()
player.toggle_pause() # returns new paused state
player.set_volume(0.75) # 0.0 – 2.0
player.set_loop("track") # "none" | "track" | "queue"
player.stop()
# State
player.current # Track or None
player.paused # bool
player.volume # float
player.position # seconds elapsed
player.loop_mode # LoopMode enum
player.get_state() # full state dict
# Events
player.on_track_start = async_fn # called with (track, player)
player.on_track_end = async_fn # called with (track, player)
player.on_queue_empty = async_fn # called with (guild_id)
manager.remove(guild_id=GUILD_ID)
SlashCommand / PrefixCommand — Command Builders
from kroxy import SlashCommand, PrefixCommand, Option, CommandRegistry
@SlashCommand.decorator(
name="warn",
description="Warn a member",
options=[
Option("user", "Target member", option_type="user", required=True),
Option("reason", "Reason", required=False),
],
permissions="8",
)
async def warn(interaction, user, reason="No reason"):
...
@PrefixCommand.decorator(
name="ban",
aliases=["b", "yeet"],
cooldown=5,
permissions=["ban_members"],
)
async def ban(ctx, member, *, reason="No reason"):
...
warn.to_dict() # export for Discord API registration
registry = CommandRegistry()
registry.add_slash(warn)
registry.add_prefix(ban)
registry.get_slash("warn")
registry.get_prefix("b") # alias lookup works
registry.all_slash()
registry.all_prefix()
Fetcher — Async HTTP Client
from kroxy import Fetcher
# As context manager (auto-closes session)
async with Fetcher() as f:
html = await f.get("https://example.com")
data = await f.get_json("https://api.example.com/data")
response = await f.post("https://example.com/form", data={"key": "value"})
result = await f.post_json("https://api.example.com", json={"q": "hello"})
headers = await f.head("https://example.com")
code = await f.status("https://example.com") # 200
ok = await f.is_reachable("https://example.com") # True/False
path = await f.download("https://example.com/file.zip", save_path="./file.zip")
raw = await f.get_bytes("https://example.com/image.png")
final = await f.resolve_redirect("https://bit.ly/abc")
# With custom headers and timeout
async with Fetcher(headers={"Authorization": "Bearer TOKEN"}, timeout=30) as f:
data = await f.get_json("https://api.example.com/protected")
Scraper — HTML Parser
All methods are static — pass raw HTML strings, get structured data back.
from kroxy import Scraper, Fetcher
async with Fetcher() as f:
html = await f.get("https://example.com")
Scraper.get_title(html) # "Example Domain"
Scraper.get_description(html) # meta description
Scraper.get_meta(html) # {"og:title": ..., "description": ...}
Scraper.get_og(html) # {"og:title": ..., "og:image": ...}
Scraper.get_links(html, base_url="https://example.com")
# [{"href": "https://example.com/page", "text": "Click here"}, ...]
Scraper.get_links(html, base_url="https://example.com", internal_only=True)
Scraper.get_links(html, base_url="https://example.com", external_only=True)
Scraper.get_images(html, base_url="https://example.com")
# [{"src": "...", "alt": "...", "width": "...", "height": "..."}, ...]
Scraper.get_tables(html)
# [[[row1col1, row1col2], [row2col1, row2col2]], ...]
Scraper.get_text(html) # plain text, no tags
Scraper.find_by_tag(html, "h1") # ["Heading 1", "Heading 2"]
Scraper.find_by_id(html, "main") # inner HTML of id="main"
Scraper.find_by_class(html, "card") # list of inner HTML strings
Scraper.extract_emails(html) # ["hello@example.com", ...]
Scraper.extract_phone_numbers(html) # ["+1 800 555 0000", ...]
Scraper.word_count(html) # 342
WebUtils — URL Utilities
All methods are static.
from kroxy import WebUtils
# Validation
WebUtils.is_valid_url("https://example.com") # True
WebUtils.is_https("https://example.com") # True
WebUtils.is_http("http://example.com") # True
# Parsing
WebUtils.get_domain("https://www.example.com/path") # "www.example.com"
WebUtils.get_base_domain("https://www.example.com/path") # "example.com"
WebUtils.get_scheme("https://example.com") # "https"
WebUtils.get_path("https://example.com/foo/bar") # "/foo/bar"
WebUtils.get_query_params("https://example.com?q=hi&page=2")
# {"q": ["hi"], "page": ["2"]}
WebUtils.get_tld("https://www.example.co.uk") # "uk"
# Transformation
WebUtils.add_query_params("https://example.com", {"q": "hello", "page": "2"})
WebUtils.remove_query_params("https://example.com?q=hi") # "https://example.com"
WebUtils.join("https://example.com/foo", "../bar") # "https://example.com/bar"
WebUtils.to_https("http://example.com") # "https://example.com"
WebUtils.normalize("www.example.com/path/") # "https://example.com/path"
WebUtils.encode("hello world!") # "hello%20world%21"
# Slug
WebUtils.slugify("Hello World! 123") # "hello-world-123"
# File type detection
WebUtils.get_file_extension("https://example.com/image.png?v=1") # "png"
WebUtils.is_image_url("https://example.com/photo.jpg") # True
WebUtils.is_video_url("https://example.com/video.mp4") # True
WebUtils.is_audio_url("https://example.com/track.mp3") # True
# Misc
WebUtils.extract_urls("Visit https://example.com or https://test.com now")
# ["https://example.com", "https://test.com"]
WebUtils.is_same_domain("https://example.com/a", "https://example.com/b") # True
Combined Example
import kroxy
import asyncio
async def main():
# Fetch a page
async with kroxy.Fetcher() as f:
html = await f.get("https://example.com")
api_data = await f.get_json("https://api.example.com/stats")
# Scrape it
title = kroxy.Scraper.get_title(html)
links = kroxy.Scraper.get_links(html, base_url="https://example.com")
emails = kroxy.Scraper.extract_emails(html)
# Validate a URL
if kroxy.WebUtils.is_valid_url("https://example.com"):
slug = kroxy.WebUtils.slugify(title)
# Send results to Discord
api = kroxy.DiscordAPI(token="Bot TOKEN")
embed = kroxy.Utils.build_embed(
title=title,
description=f"Found {len(links)} links, {len(emails)} emails",
color=0x5865F2,
timestamp=True,
)
await api.send_message(CHANNEL_ID, embed=embed)
await api.close()
asyncio.run(main())
Requirements
- Python 3.9+
aiohttp >= 3.8.0
Versioning
See VERSIONS.md for the full changelog.
| Version | What changed |
|---|---|
| 1.0.2 | kroxy.website — Fetcher, Scraper, WebUtils |
| 1.0.1 | All classes importable from kroxy top level |
| 1.0.0 | Proprietary license, professional metadata |
| 0.1.0 | Initial release — discord module |
License
Proprietary — Copyright © 2026 kroxy. All rights reserved.
Unauthorized copying, redistribution, or modification is strictly prohibited. Contact @kroxy for permissions.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kroxy-1.0.4.tar.gz.
File metadata
- Download URL: kroxy-1.0.4.tar.gz
- Upload date:
- Size: 59.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19ee7218f0b110de863897b14874e42f578fb929088099605b48c42cc451815d
|
|
| MD5 |
6ba74a51b1270f42beac42eae261ee4a
|
|
| BLAKE2b-256 |
7a62b78ddcea36233a620928f22fa3d755131cf30448764b93048565b3b7c459
|
File details
Details for the file kroxy-1.0.4-py3-none-any.whl.
File metadata
- Download URL: kroxy-1.0.4-py3-none-any.whl
- Upload date:
- Size: 63.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a72e28afd074a07d74a37ace54c0b423f0be2d795b55f7251ee02f32bbfed7f
|
|
| MD5 |
6a1983ddaf9c1dfafadcbf065402e0d1
|
|
| BLAKE2b-256 |
8fe381678c7b66b779189f52e8ed49e5ab07e97f3226d70918cdeaae86b29b31
|