Metadata-Version: 2.4
Name: kroxy
Version: 1.0.1
Summary: A powerful Python toolkit for building automation, integrations, and bots.
Author-email: kroxy <kroxy@example.com>
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://pypi.org/project/kroxy
Keywords: bot,automation,antinuke,giveaway,music,toolkit,utility
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Internet
Classifier: Development Status :: 5 - Production/Stable
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp>=3.8.0
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# kroxy

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

[![PyPI version](https://img.shields.io/pypi/v/kroxy)](https://pypi.org/project/kroxy/)
[![Python](https://img.shields.io/pypi/pyversions/kroxy)](https://pypi.org/project/kroxy/)
[![License](https://img.shields.io/badge/license-Proprietary-red)](https://pypi.org/project/kroxy/)

---

## Installation

```bash
pip install kroxy
```

```bash
pip install --upgrade kroxy
```

---

## Import Style

Everything in `kroxy` is available at the top level — no need to navigate into submodules.

```python
# Import the whole package and use anything directly
import kroxy

antinuke = kroxy.AntiNuke(whitelist=[OWNER_ID])
manager  = kroxy.GiveawayManager()
utils    = kroxy.Utils()
api      = kroxy.DiscordAPI(token="Bot TOKEN")
```

```python
# Or import exactly what you need
from kroxy import AntiNuke, Checkers, Utils
from kroxy import GiveawayManager, Giveaway
from kroxy import MusicPlayerManager, Track
from kroxy import SlashCommand, PrefixCommand, Option
from kroxy import DiscordAPI
```

```python
# Submodule access also works if preferred
from kroxy import discord

discord.AntiNuke(...)
discord.Utils.build_embed(...)
```

---

## Package Structure

```
kroxy/
├── discord/
│   ├── api.py        ← Async Discord REST API client
│   ├── commands.py   ← Slash and prefix command builders
│   ├── utils.py      ← Embeds, mentions, timestamps, permissions, formatting
│   ├── antinuke.py   ← Real-time nuke detection and auto-punishment
│   ├── checkers.py   ← Permission, role, and hierarchy validation
│   ├── giveaway.py   ← Weighted giveaway engine with scheduling
│   └── music.py      ← Multi-guild music queue and player manager
└── website/          ← Coming soon
```

---

## Module Reference

### `DiscordAPI` — Discord REST API Client

Async HTTP client for the Discord REST API v10.

**Constructor:** `DiscordAPI(token: str, bot: bool = True)`

```python
from kroxy import DiscordAPI

api = DiscordAPI(token="Bot YOUR_TOKEN")

guild   = await api.get_guild(GUILD_ID)
channel = await api.get_channel(CHANNEL_ID)
user    = await api.get_user(USER_ID)

await api.send_message(channel_id=CHANNEL_ID, content="Hello!")
await api.send_message(channel_id=CHANNEL_ID, embed=embed_dict)

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")

await api.add_role(GUILD_ID, USER_ID, ROLE_ID)
await api.remove_role(GUILD_ID, USER_ID, ROLE_ID)

await api.create_channel(GUILD_ID, name="general", channel_type=0)
await api.delete_channel(CHANNEL_ID)

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 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 |

```python
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 handlers:
await antinuke.on_member_ban(user_id=executor_id, guild=guild)
await antinuke.on_member_kick(user_id=executor_id, guild=guild)
await antinuke.on_channel_delete(user_id=executor_id, guild=guild)
await antinuke.on_channel_create(user_id=executor_id, guild=guild)
await antinuke.on_role_delete(user_id=executor_id, guild=guild)
await antinuke.on_role_create(user_id=executor_id, guild=guild)
await antinuke.on_webhook_create(user_id=executor_id, guild=guild)
await antinuke.on_webhook_delete(user_id=executor_id, guild=guild)
await antinuke.on_bot_add(user_id=executor_id, guild=guild)
await antinuke.on_mass_mention(user_id=executor_id, guild=guild)

# Whitelist management
antinuke.add_whitelist(MOD_ID)
antinuke.remove_whitelist(MOD_ID)
antinuke.reset_user(USER_ID)

# View current config
print(antinuke.get_stats())
```

---

### `Checkers` — Permission & Role Validation

Raises `CheckFailed` on failure, returns `True` on success.

```python
from kroxy import Checkers, CheckFailed

try:
    Checkers.require_admin(member.permissions)
    Checkers.require_ban_members(member.permissions)
    Checkers.require_manage_guild(member.permissions)
    Checkers.require_manage_roles(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.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` — General Utilities

Embed builder, mentions, timestamps, permissions, and text formatters.

```python
from kroxy import Utils

# Embed builder
embed = Utils.build_embed(
    title="Title",
    description="Description here",
    color=0x5865F2,
    fields=[
        {"name": "Field 1", "value": "Value 1", "inline": True},
        {"name": "Field 2", "value": "Value 2", "inline": True},
    ],
    footer="kroxy",
    thumbnail="https://example.com/thumb.png",
    image="https://example.com/image.png",
    author_name="Bot Name",
    author_icon="https://example.com/icon.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(datetime.now(), style="R")   # Relative: "5 minutes ago"
Utils.discord_timestamp(datetime.now(), style="F")   # Full: "21 June 2026 12:00"
Utils.time_until(3725)             # "1 hour, 2 minutes, 5 seconds"
Utils.snowflake_to_timestamp(snowflake_id)

# Permissions
Utils.has_permission(perms_bitfield, "administrator")   # True/False
Utils.has_permission(perms_bitfield, "ban_members")
Utils.permissions_list(perms_bitfield)   # ["administrator", "ban_members", ...]

# Text formatting
Utils.truncate(text, max_length=2048)
Utils.code_block("print('hi')", language="python")
Utils.bold("text")           # **text**
Utils.italic("text")         # *text*
Utils.underline("text")      # __text__
Utils.strikethrough("text")  # ~~text~~
Utils.spoiler("text")        # ||text||
Utils.inline_code("text")    # `text`
```

---

### `GiveawayManager` / `Giveaway` — Giveaway Engine

Weighted giveaway system with bonus roles, reroll, and auto-scheduling.

```python
from kroxy import GiveawayManager

manager = GiveawayManager()

async def on_end(giveaway, winners):
    print(f"Winners of '{giveaway.prize}': {winners}")

manager.on_end = on_end

# Create
giveaway = await manager.create(
    prize="Discord Nitro",
    host_id=HOST_USER_ID,
    channel_id=CHANNEL_ID,
    guild_id=GUILD_ID,
    duration=86400,          # seconds (24 hours)
    winner_count=3,
    required_role_id=MEMBER_ROLE_ID,
    bonus_roles={
        BOOSTER_ROLE_ID: 2,  # Boosters: 3 entries
        VIP_ROLE_ID: 4,      # VIPs: 5 entries
    },
)

# Entries
giveaway.add_entry(user_id=555, role_ids=[BOOSTER_ROLE_ID])
giveaway.remove_entry(user_id=555)

# Info
giveaway.is_active         # True/False
giveaway.time_remaining    # seconds left
giveaway.total_entries     # weighted total
giveaway.participant_count # unique users
giveaway.giveaway_id       # "A1B2C3D4"

# Control
await manager.end_now(giveaway.giveaway_id)    # Force end
await manager.cancel(giveaway.giveaway_id)     # Cancel (no winner)
new_winners = giveaway.reroll()                # Reroll

# Lookup
manager.get(giveaway_id)
manager.get_by_message(message_id)
manager.all_active()
manager.all_ended()
```

---

### `MusicPlayerManager` / `MusicPlayer` / `Track` — Music

Multi-guild music queue and player state manager.

```python
from kroxy import MusicPlayerManager, Track, LoopMode

manager = MusicPlayerManager()

# Get or create a guild player
player = manager.get_or_create(
    guild_id=GUILD_ID,
    channel_id=VOICE_CHANNEL_ID,
    text_channel_id=TEXT_CHANNEL_ID,
)

# Create a track
track = Track(
    title="Song Name",
    url="https://youtube.com/watch?v=...",
    stream_url="https://audio.stream/url.mp3",
    duration=240,             # seconds
    requester_id=USER_ID,
    thumbnail="https://img.youtube.com/vi/.../0.jpg",
    source="youtube",
)

# Queue
player.queue.add(track)
player.queue.add_next(track)       # Play next
player.queue.shuffle()
player.queue.remove(index=0)
player.queue.move(from_index=2, to_index=0)
player.queue.clear()
len(player.queue)                  # queue size
player.queue.total_duration        # seconds
player.queue.is_empty              # True/False

# Playback
await player.play_next()           # Play next track (respects loop)
await player.skip()                # Skip current track
player.toggle_pause()              # Returns new paused state (True/False)
player.set_volume(0.75)            # 0.0 – 2.0
player.set_loop("track")           # "none" | "track" | "queue"
player.stop()                      # Stop + clear queue

# State
player.current                     # Current Track or None
player.paused                      # True/False
player.volume                      # 0.0 – 2.0
player.position                    # Current position in seconds
player.loop_mode                   # LoopMode.NONE / TRACK / QUEUE
player.get_state()                 # Full state dict

# Events
async def now_playing(track, player):
    print(f"Now playing: {track.title} [{track.duration_str}]")

async def queue_empty(guild_id):
    print("Queue finished.")

player.on_track_start = now_playing
player.on_track_end   = None
player.on_queue_empty = queue_empty

# Cleanup
manager.remove(guild_id=GUILD_ID)
```

---

### `SlashCommand` / `PrefixCommand` — Command Builders

```python
from kroxy import SlashCommand, PrefixCommand, Option, CommandRegistry

# Slash command
@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",  # Administrator
)
async def warn(interaction, user, reason="No reason"):
    ...

# Prefix command with cooldown + aliases
@PrefixCommand.decorator(
    name="ban",
    aliases=["b", "yeet"],
    description="Ban a member",
    cooldown=5,
    permissions=["ban_members"],
)
async def ban(ctx, member, *, reason="No reason"):
    ...

# Slash command export (for registering with Discord API)
warn.to_dict()

# Command registry
registry = CommandRegistry()
registry.add_slash(warn)
registry.add_prefix(ban)

registry.get_slash("warn")
registry.get_prefix("ban")
registry.get_prefix("b")    # aliases work too
registry.all_slash()
registry.all_prefix()
```

---

## Quick Start Example

```python
import kroxy

async def main():
    api      = kroxy.DiscordAPI(token="Bot YOUR_TOKEN")
    antinuke = kroxy.AntiNuke(whitelist=[OWNER_ID])
    manager  = kroxy.GiveawayManager()
    player   = kroxy.MusicPlayerManager().get_or_create(GUILD_ID)

    embed = kroxy.Utils.build_embed(
        title="Bot Ready",
        description="kroxy is running.",
        color=0x5865F2,
        timestamp=True,
    )
    await api.send_message(LOG_CHANNEL_ID, embed=embed)

    giveaway = await manager.create(
        prize="Nitro",
        host_id=HOST_ID,
        channel_id=CHANNEL_ID,
        guild_id=GUILD_ID,
        duration=3600,
        winner_count=1,
    )

    await api.close()
```

---

## Requirements

- Python **3.9+**
- `aiohttp >= 3.8.0`

---

## License

**Proprietary** — Copyright © 2026 kroxy. All rights reserved.

Unauthorized copying, redistribution, or modification of this software is strictly prohibited.
Contact **@kroxy** for permissions.
