hubber.py 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.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include elara *.py
@@ -0,0 +1,696 @@
1
+ Metadata-Version: 2.4
2
+ Name: hubber.py
3
+ Version: 0.1.0
4
+ Summary: A Discord-like bot framework for Hubber
5
+ Author-email: lane <rsalol@proton.me>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/6umb/elara-hubber
8
+ Project-URL: Repository, https://github.com/6umb/elara-hubber
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.8
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
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: aiohttp>=3.8.0
19
+ Requires-Dist: websockets>=10.0
20
+
21
+ # Elara
22
+
23
+ Elara is an enterprise-grade asynchronous Python wrapper for the Hubber.cc API. Built with performance and simplicity in mind, it provides a clean interface for building powerful bots with minimal code.
24
+
25
+ ## Features
26
+
27
+ - Fully asynchronous design using asyncio
28
+ - Discord.py-style command system with decorators
29
+ - Cog system for modular bot organization
30
+ - Rich embed builder with method chaining
31
+ - Interactive button components
32
+ - Advanced caching with LRU/LFU/FIFO eviction policies
33
+ - Token bucket rate limiting
34
+ - Automatic reconnection with exponential backoff
35
+ - Type hints throughout the codebase
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install python-socketio aiohttp
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ import asyncio
47
+ from elara import Client
48
+
49
+ client = Client("YOUR_BOT_TOKEN", prefix="!")
50
+
51
+ @client.on("ready")
52
+ async def on_ready(data):
53
+ print(f"Bot online as: {data['user']['username']}")
54
+
55
+ @client.command(name="ping")
56
+ async def ping(ctx):
57
+ await ctx.send("Pong!")
58
+
59
+ asyncio.run(client.run())
60
+ ```
61
+
62
+ ## Client
63
+
64
+ ### Initialization
65
+
66
+ ```python
67
+ Client(token: str, prefix: str = "!", enable_ratelimit: bool = True, enable_cache: bool = True)
68
+ ```
69
+
70
+ **Parameters:**
71
+ - `token`: Your bot token from Hubber.cc
72
+ - `prefix`: Command prefix (default: "!")
73
+ - `enable_ratelimit`: Enable automatic rate limiting (default: True)
74
+ - `enable_cache`: Enable caching system (default: True)
75
+
76
+ **Example:**
77
+ ```python
78
+ client = Client("YOUR_TOKEN", prefix=";", enable_ratelimit=True, enable_cache=True)
79
+ ```
80
+
81
+ ### Methods
82
+
83
+ #### client.run()
84
+ Start the bot and connect to Hubber.cc.
85
+
86
+ ```python
87
+ asyncio.run(client.run())
88
+ ```
89
+
90
+ #### client.on(event: str)
91
+ Register an event listener.
92
+
93
+ ```python
94
+ @client.on("message:new")
95
+ async def on_message(ctx):
96
+ print(f"{ctx.author.username}: {ctx.content}")
97
+ ```
98
+
99
+ #### client.command(name: str, description: str = None, aliases: List[str] = None)
100
+ Register a command.
101
+
102
+ ```python
103
+ @client.command(name="hello", description="Say hello", aliases=["hi", "hey"])
104
+ async def hello(ctx):
105
+ await ctx.send(f"Hello, {ctx.author.username}!")
106
+ ```
107
+
108
+ #### client.load_cog(path: str)
109
+ Load a cog from a file path.
110
+
111
+ ```python
112
+ await client.load_cog("cogs/moderation.py")
113
+ ```
114
+
115
+ #### client.unload_cog(cog_name: str)
116
+ Unload a loaded cog.
117
+
118
+ ```python
119
+ await client.unload_cog("ModerationCog")
120
+ ```
121
+
122
+ #### client.reload_cog(cog_name: str)
123
+ Reload a cog.
124
+
125
+ ```python
126
+ await client.reload_cog("ModerationCog")
127
+ ```
128
+
129
+ ## Events
130
+
131
+ ### Available Events
132
+
133
+ - `connect` - Connected to Hubber.cc
134
+ - `ready` - Bot is ready and authenticated
135
+ - `message:new` - New message received
136
+ - `message:edit` - Message was edited
137
+ - `message:delete` - Message was deleted
138
+ - `interaction:button` - Button was clicked
139
+ - `typing:start` - User started typing
140
+ - `server:member_join` - Member joined server
141
+ - `server:member_leave` - Member left server
142
+ - `presence:update` - User presence changed
143
+ - `session:expired` - Session token expired (user tokens only)
144
+
145
+ ### Event Examples
146
+
147
+ ```python
148
+ @client.on("connect")
149
+ async def on_connect():
150
+ print("Connected!")
151
+
152
+ @client.on("ready")
153
+ async def on_ready(data):
154
+ client.user = data["user"]
155
+ print(f"Logged in as {client.user['username']}")
156
+
157
+ @client.on("message:new")
158
+ async def on_message(ctx):
159
+ if "hello" in ctx.content.lower():
160
+ await ctx.send("Hi there!")
161
+
162
+ @client.on("interaction:button")
163
+ async def on_button(ctx):
164
+ if ctx.custom_id == "confirm":
165
+ await ctx.reply("Confirmed!", ephemeral=True)
166
+ ```
167
+
168
+ ## Context
169
+
170
+ The Context object is passed to message event handlers and commands.
171
+
172
+ ### Properties
173
+
174
+ - `ctx.message_id` - Message ID
175
+ - `ctx.channel_id` - Channel ID
176
+ - `ctx.server_id` - Server ID
177
+ - `ctx.user_id` - User ID
178
+ - `ctx.content` - Message content
179
+ - `ctx.author` - Author object
180
+ - `ctx.args` - Command arguments (commands only)
181
+ - `ctx.command` - Command object (commands only)
182
+
183
+ ### Methods
184
+
185
+ #### ctx.send(content: str = None, embed: Embed = None, embeds: List[Embed] = None, components: List[ActionRow] = None)
186
+ Send a message to the channel.
187
+
188
+ ```python
189
+ await ctx.send("Hello!")
190
+ await ctx.send(embed=my_embed)
191
+ await ctx.send("Choose:", components=[action_row])
192
+ ```
193
+
194
+ #### ctx.reply(content: str = None, embed: Embed = None, embeds: List[Embed] = None, components: List[ActionRow] = None)
195
+ Reply to the message.
196
+
197
+ ```python
198
+ await ctx.reply("Thanks for your message!")
199
+ ```
200
+
201
+ #### ctx.edit(content: str)
202
+ Edit the message.
203
+
204
+ ```python
205
+ await ctx.edit("Updated content")
206
+ ```
207
+
208
+ #### ctx.delete()
209
+ Delete the message.
210
+
211
+ ```python
212
+ await ctx.delete()
213
+ ```
214
+
215
+ #### ctx.react(emoji: str)
216
+ Add a reaction to the message.
217
+
218
+ ```python
219
+ await ctx.react("👍")
220
+ await ctx.react("✅")
221
+ ```
222
+
223
+ #### ctx.unreact(emoji: str)
224
+ Remove a reaction from the message.
225
+
226
+ ```python
227
+ await ctx.unreact("👍")
228
+ ```
229
+
230
+ #### ctx.typing()
231
+ Show typing indicator in the channel.
232
+
233
+ ```python
234
+ await ctx.typing()
235
+ ```
236
+
237
+ ## Author
238
+
239
+ The Author object contains information about a user.
240
+
241
+ ### Properties
242
+
243
+ - `author.id` - User ID
244
+ - `author.username` - Username
245
+ - `author.avatar` - Avatar path
246
+ - `author.avatar_url` - Full avatar URL
247
+ - `author.avatar_color` - Avatar color hex
248
+ - `author.display_badge` - Display badge
249
+ - `author.role_color` - Role color hex
250
+
251
+ ### Example
252
+
253
+ ```python
254
+ @client.command(name="userinfo")
255
+ async def userinfo(ctx):
256
+ await ctx.send(f"Username: {ctx.author.username}\nID: {ctx.author.id}")
257
+ ```
258
+
259
+ ## Interaction
260
+
261
+ The Interaction object is passed to button interaction handlers.
262
+
263
+ ### Properties
264
+
265
+ - `ctx.custom_id` - Button custom ID
266
+ - `ctx.channel_id` - Channel ID
267
+ - `ctx.message_id` - Message ID containing the button
268
+ - `ctx.interaction_id` - Interaction ID
269
+ - `ctx.author` - Author object
270
+
271
+ ### Methods
272
+
273
+ #### ctx.send(content: str, ephemeral: bool = False)
274
+ Send a response to the interaction.
275
+
276
+ ```python
277
+ await ctx.send("Button clicked!", ephemeral=True)
278
+ ```
279
+
280
+ #### ctx.reply(content: str, ephemeral: bool = False)
281
+ Reply to the interaction.
282
+
283
+ ```python
284
+ await ctx.reply("Processing...", ephemeral=False)
285
+ ```
286
+
287
+ ## Commands
288
+
289
+ ### Basic Command
290
+
291
+ ```python
292
+ @client.command(name="ping")
293
+ async def ping(ctx):
294
+ await ctx.send("Pong!")
295
+ ```
296
+
297
+ ### Command with Aliases
298
+
299
+ ```python
300
+ @client.command(name="info", aliases=["i", "information"])
301
+ async def info(ctx):
302
+ await ctx.send("Bot information here")
303
+ ```
304
+
305
+ ### Command with Arguments
306
+
307
+ ```python
308
+ @client.command(name="say")
309
+ async def say(ctx):
310
+ if ctx.args:
311
+ await ctx.send(ctx.args)
312
+ else:
313
+ await ctx.send("Please provide text to say")
314
+ ```
315
+
316
+ ### Command with Description
317
+
318
+ ```python
319
+ @client.command(name="help", description="Show help information")
320
+ async def help(ctx):
321
+ await ctx.send("Available commands: ping, help, info")
322
+ ```
323
+
324
+ ## Embeds
325
+
326
+ Create rich embedded messages with the Embed class.
327
+
328
+ ### Basic Embed
329
+
330
+ ```python
331
+ from elara import Embed
332
+
333
+ embed = Embed(
334
+ title="My Title",
335
+ description="My description",
336
+ color="#5865F2"
337
+ )
338
+ await ctx.send(embed=embed)
339
+ ```
340
+
341
+ ### Full Embed Example
342
+
343
+ ```python
344
+ embed = Embed(title="User Profile", color="#00FF00")
345
+ embed.set_author(name=ctx.author.username, icon_url=ctx.author.avatar_url)
346
+ embed.set_description("This is a user profile embed")
347
+ embed.add_field(name="Level", value="10", inline=True)
348
+ embed.add_field(name="XP", value="1500", inline=True)
349
+ embed.set_thumbnail("https://example.com/avatar.png")
350
+ embed.set_image("https://example.com/banner.png")
351
+ embed.set_footer(text="Profile System", icon_url="https://example.com/icon.png")
352
+ embed.set_timestamp()
353
+
354
+ await ctx.send(embed=embed)
355
+ ```
356
+
357
+ ### Embed Methods
358
+
359
+ #### Embed(title: str = None, description: str = None, color: str = None, url: str = None)
360
+ Create a new embed.
361
+
362
+ #### embed.set_author(name: str, icon_url: str = None, url: str = None)
363
+ Set the embed author.
364
+
365
+ #### embed.add_field(name: str, value: str, inline: bool = False)
366
+ Add a field to the embed. Maximum 25 fields.
367
+
368
+ #### embed.set_footer(text: str, icon_url: str = None)
369
+ Set the embed footer.
370
+
371
+ #### embed.set_thumbnail(url: str)
372
+ Set the embed thumbnail.
373
+
374
+ #### embed.set_image(url: str)
375
+ Set the embed image.
376
+
377
+ #### embed.set_timestamp(timestamp: datetime = None)
378
+ Set the embed timestamp. Uses current time if not provided.
379
+
380
+ #### embed.set_color(color: str)
381
+ Set the embed color.
382
+
383
+ ### Embed Limits
384
+
385
+ - Title: 256 characters
386
+ - Description: 4,096 characters
387
+ - Fields: 25 maximum
388
+ - Field Name: 256 characters
389
+ - Field Value: 1,024 characters
390
+ - Footer: 2,048 characters
391
+ - Total: 6,000 characters
392
+ - Embeds per message: 10 maximum
393
+
394
+ ## Buttons and Components
395
+
396
+ Create interactive buttons with ActionRow and Button classes.
397
+
398
+ ### Button Styles
399
+
400
+ ```python
401
+ from elara import ButtonStyle
402
+
403
+ ButtonStyle.PRIMARY # Blue button
404
+ ButtonStyle.SECONDARY # Gray button
405
+ ButtonStyle.SUCCESS # Green button
406
+ ButtonStyle.DANGER # Red button
407
+ ButtonStyle.LINK # Link button
408
+ ```
409
+
410
+ ### Basic Buttons
411
+
412
+ ```python
413
+ from elara import ActionRow, ButtonStyle
414
+
415
+ row = ActionRow()
416
+ row.add_button("Click Me", ButtonStyle.PRIMARY, custom_id="my_button")
417
+ row.add_button("Cancel", ButtonStyle.DANGER, custom_id="cancel")
418
+ row.add_button("Website", ButtonStyle.LINK, url="https://example.com")
419
+
420
+ await ctx.send("Choose an option:", components=[row])
421
+ ```
422
+
423
+ ### Handling Button Clicks
424
+
425
+ ```python
426
+ @client.on("interaction:button")
427
+ async def on_button(ctx):
428
+ if ctx.custom_id == "my_button":
429
+ await ctx.reply(f"{ctx.author.username} clicked the button!")
430
+ elif ctx.custom_id == "cancel":
431
+ await ctx.reply("Cancelled", ephemeral=True)
432
+ ```
433
+
434
+ ### Button Limits
435
+
436
+ - Maximum 5 action rows per message
437
+ - Maximum 5 buttons per action row
438
+ - Label: 80 characters maximum
439
+ - custom_id: 100 characters maximum
440
+ - Link buttons require url, not custom_id
441
+
442
+ ## Cogs
443
+
444
+ Organize your bot into modular components using cogs.
445
+
446
+ ### Creating a Cog
447
+
448
+ ```python
449
+ from elara import Cog, command, listener
450
+ from typing import TYPE_CHECKING
451
+
452
+ if TYPE_CHECKING:
453
+ from elara import Client
454
+
455
+ class MyCog(Cog):
456
+ def __init__(self, client: "Client"):
457
+ self.client = client
458
+ self.description = "My cog description"
459
+ super().__init__(client)
460
+
461
+ @command(name="test", description="Test command")
462
+ async def test_command(self, ctx):
463
+ await ctx.send("Test successful!")
464
+
465
+ @listener("message:new")
466
+ async def on_message(self, ctx):
467
+ if "test" in ctx.content:
468
+ await ctx.react("✅")
469
+
470
+ async def cog_load(self):
471
+ print("Cog loaded!")
472
+
473
+ async def cog_unload(self):
474
+ print("Cog unloaded!")
475
+
476
+ async def setup(client: "Client") -> None:
477
+ await client.add_cog(MyCog(client))
478
+ ```
479
+
480
+ ### Loading Cogs
481
+
482
+ ```python
483
+ @client.on("ready")
484
+ async def on_ready(data):
485
+ await client.load_cog("cogs/moderation.py")
486
+ await client.load_cog("cogs/fun.py")
487
+ ```
488
+
489
+ ### Cog Management
490
+
491
+ ```python
492
+ await client.load_cog("cogs/music.py")
493
+ await client.unload_cog("MusicCog")
494
+ await client.reload_cog("MusicCog")
495
+ ```
496
+
497
+ ### Cog Decorators
498
+
499
+ #### @command(name: str, description: str = None, aliases: List[str] = None)
500
+ Register a command in the cog.
501
+
502
+ #### @listener(event: str)
503
+ Register an event listener in the cog.
504
+
505
+ ## Cache
506
+
507
+ Advanced caching system with multiple eviction policies.
508
+
509
+ ### Basic Usage
510
+
511
+ ```python
512
+ await client.cache.set("key", "value", ttl=60.0)
513
+ value = await client.cache.get("key")
514
+ await client.cache.delete("key")
515
+ await client.cache.clear()
516
+ ```
517
+
518
+ ### Cache Methods
519
+
520
+ #### cache.get(key: str)
521
+ Get a value from cache.
522
+
523
+ ```python
524
+ value = await client.cache.get("user:123")
525
+ ```
526
+
527
+ #### cache.set(key: str, value: Any, ttl: float = None)
528
+ Set a value in cache with optional TTL.
529
+
530
+ ```python
531
+ await client.cache.set("user:123", user_data, ttl=300.0)
532
+ ```
533
+
534
+ #### cache.delete(key: str)
535
+ Delete a key from cache.
536
+
537
+ ```python
538
+ await client.cache.delete("user:123")
539
+ ```
540
+
541
+ #### cache.has(key: str)
542
+ Check if key exists in cache.
543
+
544
+ ```python
545
+ if await client.cache.has("user:123"):
546
+ print("User cached")
547
+ ```
548
+
549
+ #### cache.clear()
550
+ Clear all cache entries.
551
+
552
+ ```python
553
+ await client.cache.clear()
554
+ ```
555
+
556
+ #### cache.size()
557
+ Get current cache size.
558
+
559
+ ```python
560
+ size = client.cache.size()
561
+ ```
562
+
563
+ ## Rate Limiting
564
+
565
+ Automatic rate limiting is built-in and enabled by default.
566
+
567
+ ### Rate Limits
568
+
569
+ - Messages: 5 per 5 seconds
570
+ - Channel operations: 2 per 5 seconds
571
+
572
+ ### Disabling Rate Limiting
573
+
574
+ ```python
575
+ client = Client("TOKEN", enable_ratelimit=False)
576
+ ```
577
+
578
+ ## Message Formatting
579
+
580
+ ### User Mentions
581
+
582
+ ```python
583
+ await ctx.send(f"Hello <@{ctx.author.id}>!")
584
+ ```
585
+
586
+ ### Everyone Mention
587
+
588
+ ```python
589
+ await ctx.send("@everyone Important announcement!")
590
+ ```
591
+
592
+ ## Error Handling
593
+
594
+ ### Connection Errors
595
+
596
+ The client automatically reconnects with exponential backoff:
597
+ - 5s, 10s, 30s, 60s, 120s, 300s (max)
598
+
599
+ ### Example Error Handling
600
+
601
+ ```python
602
+ @client.command(name="divide")
603
+ async def divide(ctx):
604
+ try:
605
+ args = ctx.args.split()
606
+ result = int(args[0]) / int(args[1])
607
+ await ctx.send(f"Result: {result}")
608
+ except (ValueError, IndexError):
609
+ await ctx.send("Usage: !divide <num1> <num2>")
610
+ except ZeroDivisionError:
611
+ await ctx.send("Cannot divide by zero!")
612
+ ```
613
+
614
+ ## Best Practices
615
+
616
+ ### Use Type Hints
617
+
618
+ ```python
619
+ from typing import TYPE_CHECKING
620
+
621
+ if TYPE_CHECKING:
622
+ from elara import Client
623
+ ```
624
+
625
+ ### Organize with Cogs
626
+
627
+ Split your bot into logical modules using cogs for better maintainability.
628
+
629
+ ### Handle Errors Gracefully
630
+
631
+ Always wrap potentially failing operations in try-except blocks.
632
+
633
+ ### Use Ephemeral Responses
634
+
635
+ For sensitive information or temporary messages, use ephemeral responses:
636
+
637
+ ```python
638
+ await ctx.reply("This is private", ephemeral=True)
639
+ ```
640
+
641
+ ### Leverage Caching
642
+
643
+ Cache frequently accessed data to reduce API calls and improve performance.
644
+
645
+ ### Rate Limit Awareness
646
+
647
+ Keep rate limiting enabled in production to avoid API bans.
648
+
649
+ ## Complete Example
650
+
651
+ ```python
652
+ import asyncio
653
+ from elara import Client, Embed, ActionRow, ButtonStyle
654
+
655
+ client = Client("YOUR_TOKEN", prefix="!")
656
+
657
+ @client.on("ready")
658
+ async def on_ready(data):
659
+ print(f"Bot online as: {data['user']['username']}")
660
+
661
+ @client.command(name="profile", description="View user profile")
662
+ async def profile(ctx):
663
+ embed = Embed(
664
+ title=f"{ctx.author.username}'s Profile",
665
+ color="#5865F2"
666
+ )
667
+ embed.set_thumbnail(ctx.author.avatar_url)
668
+ embed.add_field(name="User ID", value=ctx.author.id, inline=True)
669
+ embed.add_field(name="Badge", value=ctx.author.display_badge or "None", inline=True)
670
+ embed.set_footer(text="Profile System")
671
+ embed.set_timestamp()
672
+
673
+ row = ActionRow()
674
+ row.add_button("Refresh", ButtonStyle.PRIMARY, custom_id="refresh_profile")
675
+
676
+ await ctx.send(embed=embed, components=[row])
677
+
678
+ @client.on("interaction:button")
679
+ async def on_button(ctx):
680
+ if ctx.custom_id == "refresh_profile":
681
+ await ctx.reply("Profile refreshed!", ephemeral=True)
682
+
683
+ @client.command(name="ping")
684
+ async def ping(ctx):
685
+ await ctx.send("Pong!")
686
+
687
+ asyncio.run(client.run())
688
+ ```
689
+
690
+ ## Support
691
+
692
+ For issues and questions, refer to the Hubber.cc API documentation at https://hubber.cc/docs
693
+
694
+ ## License
695
+
696
+ This library is provided as-is for use with the Hubber.cc platform.