diff --git a/apps/docs/src/content/docs/guides/python-wrapper.mdx b/apps/docs/src/content/docs/guides/python-wrapper.mdx
index 73d2cfa..3ead85f 100644
--- a/apps/docs/src/content/docs/guides/python-wrapper.mdx
+++ b/apps/docs/src/content/docs/guides/python-wrapper.mdx
@@ -1,6 +1,6 @@
---
-title: How to use HCTV's python wrapper!
-description: How to use HCTV's unofficial Python wrapper.
+title: Python wrapper
+description: How to use hctv's unofficial Python wrapper.
---
A Pycord-style Python wrapper for [hackclub.tv](https://hackclub.tv), made by [Christian](https://github.com/christianwell). Build chat bots with decorators and minimal boilerplate.
@@ -15,13 +15,14 @@ pip install hctvwrapper
## Quick Start
```python
+import os
from hctvwrapper import Bot
bot = Bot(command_prefix="!")
@bot.event
async def on_ready(session):
- print(f"Logged in as {session.viewer}")
+ print(f"Logged in as {session.viewer.username}")
@bot.event
async def on_message(message):
@@ -31,10 +32,10 @@ async def on_message(message):
async def ping(ctx):
await ctx.reply("pong!")
-bot.run("hctvb_your_token_here", channel="bot-playground")
+bot.run(os.environ['BOT_TOKEN'], channel="bot-playground")
```
-## Getting a Bot Token
+### Getting a Bot Token
1. Go to [hackclub.tv](https://hackclub.tv)
2. Create a bot account and get your API key (starts with `hctvb_`)
@@ -100,7 +101,7 @@ Register commands with `@bot.command()`. The bot automatically parses messages s
```python
bot = Bot(command_prefix="!")
-# Simple command — no arguments
+# Simple command, no arguments
@bot.command()
async def ping(ctx):
await ctx.reply("pong!")
@@ -114,8 +115,17 @@ async def say_cmd(ctx, *, text):
@bot.command()
async def greet(ctx, name, greeting="hello"):
await ctx.reply(f"{greeting}, {name}!")
+
+# Keyword-only (rest of message)
+# Use *, text to capture everything after the command as a single string:
+@bot.command()
+async def echo(ctx, *, text):
+ await ctx.reply(text)
+# !echo hello world foo → text = "hello world foo"
```
+> The bot automatically ignores its own messages to prevent loops.
+
### Context
The `ctx` object passed to commands gives you everything you need:
@@ -185,6 +195,29 @@ await bot.unban_user("channel", user_id="user123")
await bot.delete_message("channel", msg_id="msg-uuid")
```
+### Emojis
+
+Look up or search emojis from the Slack. Results come back via events.
+
+```python
+# Look up emoji URLs
+await bot.lookup_emojis(["yay", "aga"])
+
+# Search emojis
+await bot.search_emojis("yay")
+
+# Handle results
+@bot.event
+async def on_emoji_response(emojis):
+ # emojis = {"yay": "https://...", "aga": "https://..."}
+ print(emojis)
+
+@bot.event
+async def on_emoji_search(results):
+ # results = ["yay", "yay-bounce", "yay-spin", ...]
+ print(results)
+```
+
### Async Entry Point
If you manage your own event loop:
@@ -204,6 +237,77 @@ async def main():
asyncio.run(main())
```
+## Examples
+
+### Echo Bot
+
+```python
+from hctvwrapper import Bot
+import os
+
+bot = Bot(command_prefix="!")
+
+@bot.event
+async def on_ready(session):
+ print(f"✅ Logged in as {session.viewer}")
+
+@bot.command()
+async def ping(ctx):
+ await ctx.reply("pong! 🏓")
+
+@bot.command(name="echo", aliases=["say"])
+async def echo_cmd(ctx, *, text):
+ await ctx.send(text)
+
+bot.run(os.environ["BOT_TOKEN"], channel="bot-playground")
+```
+
+### AI Bot
+
+```python
+from hctvwrapper import Bot
+import aiohttp, os
+
+bot = Bot(command_prefix="/")
+
+@bot.command(name="ai")
+async def ai_cmd(ctx, *, prompt):
+ async with aiohttp.ClientSession() as http:
+ resp = await http.post(
+ "https://ai.hackclub.com/proxy/v1/chat/completions",
+ headers={"Authorization": f"Bearer {os.environ['AI_TOKEN']}"},
+ json={
+ "model": "google/gemini-3-flash-preview",
+ "messages": [{"role": "user", "content": prompt}],
+ },
+ )
+ data = await resp.json()
+ answer = data["choices"][0]["message"]["content"]
+ await ctx.reply(answer)
+
+bot.run(os.environ["BOT_TOKEN"], channel="bot-playground")
+```
+
+### Moderation Bot
+
+```python
+from hctvwrapper import Bot
+import os
+
+bot = Bot(command_prefix="!")
+
+@bot.command()
+async def timeout(ctx, user_id, seconds="300"):
+ await bot.timeout_user(ctx.channel, user_id, duration=int(seconds))
+ await ctx.send(f"⏰ Timed out for {seconds}s")
+
+@bot.event
+async def on_moderation_error(error, channel):
+ print(f"⚠️ {error.code}: {error.message}")
+
+bot.run(os.environ["BOT_TOKEN"], channel="my-channel")
+```
+
## Models Reference
| Model | Fields |
@@ -226,4 +330,4 @@ asyncio.run(main())
## License
-Copyright (c) 2026 Christian Well - [MIT License](https://github.com/christianwell/hctvwrapper/blob/main/LICENSE)
\ No newline at end of file
+Copyright (c) 2026 Christian Well - [MIT License](https://github.com/christianwell/hctvwrapper/blob/main/LICENSE)
diff --git a/apps/docs/src/content/docs/guides/start-stream.mdx b/apps/docs/src/content/docs/guides/start-stream.mdx
index f7aa0dc..9170766 100644
--- a/apps/docs/src/content/docs/guides/start-stream.mdx
+++ b/apps/docs/src/content/docs/guides/start-stream.mdx
@@ -29,7 +29,7 @@ _This guide demonstrates how to stream with hackclub.tv via OBS Studio. For othe

-- Go back to the website and click this
icon to regenerate your stream key. Copy the new key and paste it into the `Stream Key` box in OBS.
+- Go back to the website and click this
icon to regenerate your stream key. Copy the new key and paste it into the `Stream Key` box in OBS.

(_Your OBS settings should now look like this_)