Skip to main content

A powerful Python toolkit for building automation, integrations, and bots.

Project description

kroxy

A powerful Python toolkit for building automation, integrations, and bots.

PyPI version Python License


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

kroxy-1.0.4.tar.gz (59.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kroxy-1.0.4-py3-none-any.whl (63.3 kB view details)

Uploaded Python 3

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

Hashes for kroxy-1.0.4.tar.gz
Algorithm Hash digest
SHA256 19ee7218f0b110de863897b14874e42f578fb929088099605b48c42cc451815d
MD5 6ba74a51b1270f42beac42eae261ee4a
BLAKE2b-256 7a62b78ddcea36233a620928f22fa3d755131cf30448764b93048565b3b7c459

See more details on using hashes here.

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

Hashes for kroxy-1.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 1a72e28afd074a07d74a37ace54c0b423f0be2d795b55f7251ee02f32bbfed7f
MD5 6a1983ddaf9c1dfafadcbf065402e0d1
BLAKE2b-256 8fe381678c7b66b779189f52e8ed49e5ab07e97f3226d70918cdeaae86b29b31

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page