kroxy 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
kroxy-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kroxy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include kroxy *.py
kroxy-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.4
2
+ Name: kroxy
3
+ Version: 0.1.0
4
+ Summary: A professional Discord utility library by @kroxy
5
+ Author-email: kroxy <kroxy@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://pypi.org/project/kroxy
8
+ Project-URL: Repository, https://github.com/kroxy/kroxy
9
+ Keywords: discord,bot,antinuke,giveaway,music,utility
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Topic :: Communications :: Chat
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: aiohttp>=3.8.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: build; extra == "dev"
24
+ Requires-Dist: twine; extra == "dev"
25
+ Dynamic: license-file
26
+
27
+ # kroxy
28
+
29
+ A professional Discord utility library by **@kroxy**.
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install kroxy
35
+ ```
36
+
37
+ ## Modules
38
+
39
+ ### `kroxy.discord`
40
+
41
+ | Module | Description |
42
+ |---|---|
43
+ | `api` | Async Discord REST API client |
44
+ | `commands` | Slash & prefix command builders |
45
+ | `utils` | Embed builder, mentions, timestamps, permissions |
46
+ | `antinuke` | Anti-nuke protection with rate-limit tracking |
47
+ | `checkers` | Permission & role checks with hierarchy validation |
48
+ | `giveaway` | Full-featured weighted giveaway system |
49
+ | `music` | Queue-based music player state manager |
50
+
51
+ ### `kroxy.website` *(coming soon)*
52
+
53
+ ---
54
+
55
+ ## Quick Examples
56
+
57
+ ### Anti-Nuke
58
+
59
+ ```python
60
+ from kroxy.discord import AntiNuke
61
+
62
+ antinuke = AntiNuke(whitelist=[OWNER_ID])
63
+
64
+ async def punish(action, user_id, guild):
65
+ print(f"NUKE DETECTED: {action} by {user_id}")
66
+
67
+ antinuke.on_trigger = punish
68
+ antinuke.punishment = "ban"
69
+
70
+ # In your event handler:
71
+ await antinuke.on_member_ban(user_id=some_user_id, guild=guild)
72
+ ```
73
+
74
+ ### Giveaway
75
+
76
+ ```python
77
+ from kroxy.discord.giveaway import GiveawayManager
78
+
79
+ manager = GiveawayManager()
80
+
81
+ async def on_giveaway_end(giveaway, winners):
82
+ print(f"Winners of {giveaway.prize}: {winners}")
83
+
84
+ manager.on_end = on_giveaway_end
85
+
86
+ giveaway = await manager.create(
87
+ prize="Discord Nitro",
88
+ host_id=123456789,
89
+ channel_id=987654321,
90
+ guild_id=111111111,
91
+ duration=3600, # 1 hour
92
+ winner_count=2,
93
+ )
94
+ ```
95
+
96
+ ### Embed Builder
97
+
98
+ ```python
99
+ from kroxy.discord import Utils
100
+
101
+ embed = Utils.build_embed(
102
+ title="Hello from kroxy!",
103
+ description="This is a professional embed.",
104
+ color=0x5865F2,
105
+ fields=[{"name": "Field", "value": "Value", "inline": True}],
106
+ footer="kroxy library",
107
+ timestamp=True,
108
+ )
109
+ ```
110
+
111
+ ### Slash Command
112
+
113
+ ```python
114
+ from kroxy.discord.commands import SlashCommand, Option
115
+
116
+ @SlashCommand.decorator(
117
+ name="ping",
118
+ description="Check bot latency",
119
+ )
120
+ async def ping(interaction):
121
+ await interaction.response.send_message("Pong!")
122
+ ```
123
+
124
+ ### Music Player
125
+
126
+ ```python
127
+ from kroxy.discord.music import MusicPlayerManager, Track
128
+
129
+ manager = MusicPlayerManager()
130
+ player = manager.get_or_create(guild_id=111111111, channel_id=222222222)
131
+
132
+ track = Track(
133
+ title="My Song",
134
+ url="https://youtube.com/...",
135
+ stream_url="https://...",
136
+ duration=240,
137
+ requester_id=123456789,
138
+ )
139
+ player.queue.add(track)
140
+ await player.play_next()
141
+ ```
142
+
143
+ ---
144
+
145
+ ## License
146
+
147
+ MIT © kroxy
kroxy-0.1.0/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # kroxy
2
+
3
+ A professional Discord utility library by **@kroxy**.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install kroxy
9
+ ```
10
+
11
+ ## Modules
12
+
13
+ ### `kroxy.discord`
14
+
15
+ | Module | Description |
16
+ |---|---|
17
+ | `api` | Async Discord REST API client |
18
+ | `commands` | Slash & prefix command builders |
19
+ | `utils` | Embed builder, mentions, timestamps, permissions |
20
+ | `antinuke` | Anti-nuke protection with rate-limit tracking |
21
+ | `checkers` | Permission & role checks with hierarchy validation |
22
+ | `giveaway` | Full-featured weighted giveaway system |
23
+ | `music` | Queue-based music player state manager |
24
+
25
+ ### `kroxy.website` *(coming soon)*
26
+
27
+ ---
28
+
29
+ ## Quick Examples
30
+
31
+ ### Anti-Nuke
32
+
33
+ ```python
34
+ from kroxy.discord import AntiNuke
35
+
36
+ antinuke = AntiNuke(whitelist=[OWNER_ID])
37
+
38
+ async def punish(action, user_id, guild):
39
+ print(f"NUKE DETECTED: {action} by {user_id}")
40
+
41
+ antinuke.on_trigger = punish
42
+ antinuke.punishment = "ban"
43
+
44
+ # In your event handler:
45
+ await antinuke.on_member_ban(user_id=some_user_id, guild=guild)
46
+ ```
47
+
48
+ ### Giveaway
49
+
50
+ ```python
51
+ from kroxy.discord.giveaway import GiveawayManager
52
+
53
+ manager = GiveawayManager()
54
+
55
+ async def on_giveaway_end(giveaway, winners):
56
+ print(f"Winners of {giveaway.prize}: {winners}")
57
+
58
+ manager.on_end = on_giveaway_end
59
+
60
+ giveaway = await manager.create(
61
+ prize="Discord Nitro",
62
+ host_id=123456789,
63
+ channel_id=987654321,
64
+ guild_id=111111111,
65
+ duration=3600, # 1 hour
66
+ winner_count=2,
67
+ )
68
+ ```
69
+
70
+ ### Embed Builder
71
+
72
+ ```python
73
+ from kroxy.discord import Utils
74
+
75
+ embed = Utils.build_embed(
76
+ title="Hello from kroxy!",
77
+ description="This is a professional embed.",
78
+ color=0x5865F2,
79
+ fields=[{"name": "Field", "value": "Value", "inline": True}],
80
+ footer="kroxy library",
81
+ timestamp=True,
82
+ )
83
+ ```
84
+
85
+ ### Slash Command
86
+
87
+ ```python
88
+ from kroxy.discord.commands import SlashCommand, Option
89
+
90
+ @SlashCommand.decorator(
91
+ name="ping",
92
+ description="Check bot latency",
93
+ )
94
+ async def ping(interaction):
95
+ await interaction.response.send_message("Pong!")
96
+ ```
97
+
98
+ ### Music Player
99
+
100
+ ```python
101
+ from kroxy.discord.music import MusicPlayerManager, Track
102
+
103
+ manager = MusicPlayerManager()
104
+ player = manager.get_or_create(guild_id=111111111, channel_id=222222222)
105
+
106
+ track = Track(
107
+ title="My Song",
108
+ url="https://youtube.com/...",
109
+ stream_url="https://...",
110
+ duration=240,
111
+ requester_id=123456789,
112
+ )
113
+ player.queue.add(track)
114
+ await player.play_next()
115
+ ```
116
+
117
+ ---
118
+
119
+ ## License
120
+
121
+ MIT © kroxy
@@ -0,0 +1,13 @@
1
+ """
2
+ kroxy - A professional Python utility library.
3
+ Author: @kroxy
4
+ """
5
+
6
+ __version__ = "0.1.0"
7
+ __author__ = "kroxy"
8
+ __license__ = "MIT"
9
+
10
+ from kroxy import discord
11
+ from kroxy import website
12
+
13
+ __all__ = ["discord", "website"]
@@ -0,0 +1,22 @@
1
+ """
2
+ kroxy.discord - Professional Discord utilities and helpers.
3
+ """
4
+
5
+ from kroxy.discord.api import DiscordAPI
6
+ from kroxy.discord.commands import SlashCommand, PrefixCommand
7
+ from kroxy.discord.utils import Utils
8
+ from kroxy.discord.antinuke import AntiNuke
9
+ from kroxy.discord.checkers import Checkers
10
+ from kroxy.discord.giveaway import Giveaway
11
+ from kroxy.discord.music import MusicPlayer
12
+
13
+ __all__ = [
14
+ "DiscordAPI",
15
+ "SlashCommand",
16
+ "PrefixCommand",
17
+ "Utils",
18
+ "AntiNuke",
19
+ "Checkers",
20
+ "Giveaway",
21
+ "MusicPlayer",
22
+ ]
@@ -0,0 +1,146 @@
1
+ """
2
+ kroxy.discord.antinuke - Anti-nuke protection system for Discord servers.
3
+ """
4
+
5
+ import asyncio
6
+ import time
7
+ from collections import defaultdict
8
+ from typing import Dict, List, Optional, Callable, Any
9
+
10
+
11
+ class ActionLog:
12
+ """Tracks recent actions per user for rate-limit detection."""
13
+
14
+ def __init__(self, threshold: int, window: int):
15
+ self.threshold = threshold
16
+ self.window = window # seconds
17
+ self._log: Dict[int, List[float]] = defaultdict(list)
18
+
19
+ def record(self, user_id: int) -> int:
20
+ """Record an action and return the count within the window."""
21
+ now = time.time()
22
+ self._log[user_id] = [t for t in self._log[user_id] if now - t < self.window]
23
+ self._log[user_id].append(now)
24
+ return len(self._log[user_id])
25
+
26
+ def exceeded(self, user_id: int) -> bool:
27
+ """Return True if the user has exceeded the threshold."""
28
+ return self.record(user_id) >= self.threshold
29
+
30
+ def reset(self, user_id: int):
31
+ self._log.pop(user_id, None)
32
+
33
+
34
+ class AntiNuke:
35
+ """
36
+ Anti-nuke system that monitors destructive actions and auto-punishes abusers.
37
+
38
+ Usage:
39
+ antinuke = AntiNuke(bot, whitelist=[owner_id])
40
+ antinuke.on_trigger = my_punishment_callback
41
+ """
42
+
43
+ DEFAULT_LIMITS = {
44
+ "ban": (3, 10), # 3 bans in 10s
45
+ "kick": (3, 10),
46
+ "channel_delete": (3, 10),
47
+ "channel_create": (5, 10),
48
+ "role_delete": (3, 10),
49
+ "role_create": (5, 10),
50
+ "webhook_create": (3, 10),
51
+ "webhook_delete": (3, 10),
52
+ "invite_delete": (5, 10),
53
+ "mass_mention": (5, 5),
54
+ "bot_add": (2, 30),
55
+ }
56
+
57
+ def __init__(self, whitelist: List[int] = None, limits: Dict = None):
58
+ self.whitelist: List[int] = whitelist or []
59
+ limits = limits or {}
60
+ merged = {**self.DEFAULT_LIMITS, **limits}
61
+ self._trackers: Dict[str, ActionLog] = {
62
+ action: ActionLog(threshold, window)
63
+ for action, (threshold, window) in merged.items()
64
+ }
65
+ self._punished: set = set()
66
+ self.on_trigger: Optional[Callable] = None
67
+ self.punishment: str = "ban" # "ban" | "kick" | "strip_roles"
68
+
69
+ def is_whitelisted(self, user_id: int) -> bool:
70
+ return user_id in self.whitelist
71
+
72
+ def add_whitelist(self, user_id: int):
73
+ if user_id not in self.whitelist:
74
+ self.whitelist.append(user_id)
75
+
76
+ def remove_whitelist(self, user_id: int):
77
+ self.whitelist = [u for u in self.whitelist if u != user_id]
78
+
79
+ async def check(self, action: str, user_id: int, guild: Any = None) -> bool:
80
+ """
81
+ Check an action for a user. Returns True if limit exceeded.
82
+ Calls self.on_trigger(action, user_id, guild) if set.
83
+ """
84
+ if self.is_whitelisted(user_id):
85
+ return False
86
+ if user_id in self._punished:
87
+ return True
88
+
89
+ tracker = self._trackers.get(action)
90
+ if tracker is None:
91
+ return False
92
+
93
+ if tracker.exceeded(user_id):
94
+ self._punished.add(user_id)
95
+ if self.on_trigger:
96
+ try:
97
+ await self.on_trigger(action=action, user_id=user_id, guild=guild)
98
+ except Exception:
99
+ pass
100
+ return True
101
+ return False
102
+
103
+ def reset_user(self, user_id: int):
104
+ """Clear all action logs and punishment flag for a user."""
105
+ for tracker in self._trackers.values():
106
+ tracker.reset(user_id)
107
+ self._punished.discard(user_id)
108
+
109
+ def get_stats(self) -> Dict:
110
+ """Return current limits configuration."""
111
+ return {
112
+ action: {"threshold": t.threshold, "window": t.window}
113
+ for action, t in self._trackers.items()
114
+ }
115
+
116
+ # ── Convenience event handlers (wire these to your event system) ──────────
117
+
118
+ async def on_member_ban(self, user_id: int, guild: Any = None):
119
+ return await self.check("ban", user_id, guild)
120
+
121
+ async def on_member_kick(self, user_id: int, guild: Any = None):
122
+ return await self.check("kick", user_id, guild)
123
+
124
+ async def on_channel_delete(self, user_id: int, guild: Any = None):
125
+ return await self.check("channel_delete", user_id, guild)
126
+
127
+ async def on_channel_create(self, user_id: int, guild: Any = None):
128
+ return await self.check("channel_create", user_id, guild)
129
+
130
+ async def on_role_delete(self, user_id: int, guild: Any = None):
131
+ return await self.check("role_delete", user_id, guild)
132
+
133
+ async def on_role_create(self, user_id: int, guild: Any = None):
134
+ return await self.check("role_create", user_id, guild)
135
+
136
+ async def on_webhook_create(self, user_id: int, guild: Any = None):
137
+ return await self.check("webhook_create", user_id, guild)
138
+
139
+ async def on_webhook_delete(self, user_id: int, guild: Any = None):
140
+ return await self.check("webhook_delete", user_id, guild)
141
+
142
+ async def on_bot_add(self, user_id: int, guild: Any = None):
143
+ return await self.check("bot_add", user_id, guild)
144
+
145
+ async def on_mass_mention(self, user_id: int, guild: Any = None):
146
+ return await self.check("mass_mention", user_id, guild)
@@ -0,0 +1,128 @@
1
+ """
2
+ kroxy.discord.api - Discord REST API wrapper helpers.
3
+ """
4
+
5
+ import aiohttp
6
+ import asyncio
7
+ from typing import Optional, Any, Dict
8
+
9
+ DISCORD_API_BASE = "https://discord.com/api/v10"
10
+
11
+
12
+ class DiscordAPI:
13
+ """Async Discord REST API client."""
14
+
15
+ def __init__(self, token: str, bot: bool = True):
16
+ prefix = "Bot" if bot else "Bearer"
17
+ self.headers = {
18
+ "Authorization": f"{prefix} {token}",
19
+ "Content-Type": "application/json",
20
+ }
21
+ self._session: Optional[aiohttp.ClientSession] = None
22
+
23
+ async def _get_session(self) -> aiohttp.ClientSession:
24
+ if self._session is None or self._session.closed:
25
+ self._session = aiohttp.ClientSession(headers=self.headers)
26
+ return self._session
27
+
28
+ async def close(self):
29
+ """Close the underlying HTTP session."""
30
+ if self._session and not self._session.closed:
31
+ await self._session.close()
32
+
33
+ async def request(self, method: str, endpoint: str, **kwargs) -> Any:
34
+ """Make a raw API request."""
35
+ session = await self._get_session()
36
+ url = f"{DISCORD_API_BASE}{endpoint}"
37
+ async with session.request(method, url, **kwargs) as resp:
38
+ resp.raise_for_status()
39
+ return await resp.json()
40
+
41
+ async def get_guild(self, guild_id: int) -> Dict:
42
+ """Fetch a guild by ID."""
43
+ return await self.request("GET", f"/guilds/{guild_id}")
44
+
45
+ async def get_channel(self, channel_id: int) -> Dict:
46
+ """Fetch a channel by ID."""
47
+ return await self.request("GET", f"/channels/{channel_id}")
48
+
49
+ async def get_user(self, user_id: int) -> Dict:
50
+ """Fetch a user by ID."""
51
+ return await self.request("GET", f"/users/{user_id}")
52
+
53
+ async def send_message(self, channel_id: int, content: str = None,
54
+ embed: Dict = None, components: list = None) -> Dict:
55
+ """Send a message to a channel."""
56
+ payload: Dict[str, Any] = {}
57
+ if content:
58
+ payload["content"] = content
59
+ if embed:
60
+ payload["embeds"] = [embed]
61
+ if components:
62
+ payload["components"] = components
63
+ return await self.request("POST", f"/channels/{channel_id}/messages", json=payload)
64
+
65
+ async def delete_message(self, channel_id: int, message_id: int) -> None:
66
+ """Delete a message."""
67
+ await self.request("DELETE", f"/channels/{channel_id}/messages/{message_id}")
68
+
69
+ async def ban_member(self, guild_id: int, user_id: int,
70
+ reason: str = None, delete_message_days: int = 0) -> None:
71
+ """Ban a member from a guild."""
72
+ payload = {"delete_message_days": delete_message_days}
73
+ headers = {}
74
+ if reason:
75
+ headers["X-Audit-Log-Reason"] = reason
76
+ await self.request("PUT", f"/guilds/{guild_id}/bans/{user_id}",
77
+ json=payload, headers=headers)
78
+
79
+ async def unban_member(self, guild_id: int, user_id: int) -> None:
80
+ """Unban a member."""
81
+ await self.request("DELETE", f"/guilds/{guild_id}/bans/{user_id}")
82
+
83
+ async def kick_member(self, guild_id: int, user_id: int, reason: str = None) -> None:
84
+ """Kick a member from a guild."""
85
+ headers = {}
86
+ if reason:
87
+ headers["X-Audit-Log-Reason"] = reason
88
+ await self.request("DELETE", f"/guilds/{guild_id}/members/{user_id}",
89
+ headers=headers)
90
+
91
+ async def add_role(self, guild_id: int, user_id: int, role_id: int) -> None:
92
+ """Add a role to a member."""
93
+ await self.request("PUT", f"/guilds/{guild_id}/members/{user_id}/roles/{role_id}")
94
+
95
+ async def remove_role(self, guild_id: int, user_id: int, role_id: int) -> None:
96
+ """Remove a role from a member."""
97
+ await self.request("DELETE", f"/guilds/{guild_id}/members/{user_id}/roles/{role_id}")
98
+
99
+ async def create_channel(self, guild_id: int, name: str,
100
+ channel_type: int = 0, **kwargs) -> Dict:
101
+ """Create a guild channel."""
102
+ payload = {"name": name, "type": channel_type, **kwargs}
103
+ return await self.request("POST", f"/guilds/{guild_id}/channels", json=payload)
104
+
105
+ async def delete_channel(self, channel_id: int) -> Dict:
106
+ """Delete a channel."""
107
+ return await self.request("DELETE", f"/channels/{channel_id}")
108
+
109
+ async def get_audit_logs(self, guild_id: int, limit: int = 50) -> Dict:
110
+ """Fetch guild audit logs."""
111
+ return await self.request("GET", f"/guilds/{guild_id}/audit-logs",
112
+ params={"limit": limit})
113
+
114
+ async def get_invites(self, guild_id: int) -> list:
115
+ """Get all guild invites."""
116
+ return await self.request("GET", f"/guilds/{guild_id}/invites")
117
+
118
+ async def delete_invite(self, invite_code: str) -> None:
119
+ """Delete an invite by code."""
120
+ await self.request("DELETE", f"/invites/{invite_code}")
121
+
122
+ async def get_webhooks(self, guild_id: int) -> list:
123
+ """Get all webhooks in a guild."""
124
+ return await self.request("GET", f"/guilds/{guild_id}/webhooks")
125
+
126
+ async def delete_webhook(self, webhook_id: int) -> None:
127
+ """Delete a webhook."""
128
+ await self.request("DELETE", f"/webhooks/{webhook_id}")