hctvwrapper
A Pycord-style Python wrapper for hackclub.tv. Build chat bots with decorators — no boilerplate.
pip install hctvwrapper
Quick Start
from hctvwrapper import Bot
bot = Bot(command_prefix="!")
@bot.event
async def on_ready(session):
print(f"Logged in as {session.viewer}")
@bot.event
async def on_message(message):
print(f"{message.author.username}: {message.content}")
@bot.command()
async def ping(ctx):
await ctx.reply("pong!")
bot.run("hctvb_your_token_here", channel="bot-playground")
Getting a Bot Token
- Go to hackclub.tv
- Create a bot account and get your API key (starts with
hctvb_) - Set it as an environment variable:
export BOT_TOKEN=hctvb_xxx
Guide
Events
Register event handlers with @bot.event. The function name determines which event it handles.
@bot.event
async def on_ready(session):
"""Fired when the bot connects and receives session info."""
print(f"Logged in as {session.viewer.username}")
print(f"Can moderate: {session.permissions.can_moderate}")
print(f"Max message length: {session.moderation.max_message_length}")
@bot.event
async def on_message(message):
"""Fired on every chat message."""
print(f"[{message.channel}] {message.author.username}: {message.content}")
# message.author.id, .pfp_url, .display_name, .is_bot,
# .is_platform_admin, .channel_role
# message.msg_id, .timestamp, .type
@bot.event
async def on_history(messages):
"""Fired once on connect with up to 100 recent messages."""
print(f"Got {len(messages)} historical messages")
@bot.event
async def on_system_message(message):
"""Fired on system notifications (bans, unbans, etc.)."""
print(f"System: {message.content}")
@bot.event
async def on_message_deleted(event):
"""Fired when a message is deleted by a moderator."""
print(f"Message {event.msg_id} deleted in {event.channel}")
@bot.event
async def on_chat_access(access, channel):
"""Fired when chat permissions change (timeouts, bans)."""
print(f"Can send in {channel}: {access.can_send}")
if access.restriction:
print(f" Restriction: {access.restriction.type}")
@bot.event
async def on_moderation_error(error, channel):
"""Fired when a moderation action or message is rejected."""
print(f"Error in {channel}: {error.code} — {error.message}")
# error.code is one of: FORBIDDEN, RATE_LIMIT, SLOW_MODE,
# TIMED_OUT, BANNED, MESSAGE_TOO_LONG, BLOCKED_TERM,
# INVALID_TARGET, INVALID_REQUEST, NOT_FOUND
Commands
Register commands with @bot.command(). The bot automatically parses messages starting with the prefix.
bot = Bot(command_prefix="!")
# Simple command — no arguments
@bot.command()
async def ping(ctx):
await ctx.reply("pong!")
# Named command with aliases
@bot.command(name="say", aliases=["echo", "repeat"])
async def say_cmd(ctx, *, text):
await ctx.send(text)
# Positional arguments (split by whitespace)
@bot.command()
async def greet(ctx, name, greeting="hello"):
await ctx.reply(f"{greeting}, {name}!")
Context
The ctx object passed to commands gives you everything you need:
@bot.command()
async def info(ctx):
ctx.message # the full Message object
ctx.author # shortcut to ctx.message.author (Author)
ctx.channel # channel name (str)
ctx.bot # reference to the Bot
await ctx.reply("text") # sends "@username text"
await ctx.send("text") # sends "text" without mention
await ctx.delete() # deletes the triggering message (needs mod perms)
Sending Messages
# Inside a command
await ctx.reply("mentioned reply")
await ctx.send("plain message")
# Anywhere (if you have a reference to the bot)
await bot.send("hello!", channel="bot-playground")
Multi-Channel
Connect to multiple channels at once:
bot.run("hctvb_xxx", channels=["channel1", "channel2", "bot-playground"])
Messages and commands work across all channels. Use ctx.channel or message.channel to know which channel a message came from.
@bot.event
async def on_message(message):
print(f"[{message.channel}] {message.author.username}: {message.content}")
@bot.command()
async def where(ctx):
await ctx.reply(f"you're in {ctx.channel}")
Moderation
Bots with moderation permissions can manage users:
# Timeout a user for 5 minutes (default)
await bot.timeout_user("channel", user_id="user123", duration=300, reason="spam")
# Ban a user
await bot.ban_user("channel", user_id="user123", reason="repeated violations")
# Remove a timeout
await bot.lift_timeout("channel", user_id="user123")
# Unban a user
await bot.unban_user("channel", user_id="user123")
# Delete a specific message
await bot.delete_message("channel", msg_id="msg-uuid")
Async Entry Point
If you manage your own event loop:
import asyncio
async def main():
bot = Bot(command_prefix="!")
@bot.event
async def on_ready(session):
print("Connected!")
await bot.start("hctvb_xxx", channel="bot-playground")
asyncio.run(main())
Models Reference
| Model | Fields |
|---|---|
Message |
content, author, channel, msg_id, timestamp, type, is_bot |
Author |
id, username, pfp_url, display_name, is_bot, is_platform_admin, channel_role |
Session |
viewer (Author), permissions, moderation |
Permissions |
can_moderate |
ModerationSettings |
has_blocked_terms, slow_mode_seconds, max_message_length |
SystemMessage |
type, channel, content, timestamp |
ChatAccess |
can_send, restriction |
Restriction |
type (timeout/ban), reason, expires_at |
ModerationError |
code, message, restriction |
ModerationEvent |
type, msg_id, channel |
Requirements
- Python 3.10+
websockets(only dependency)
License
MIT
Release files for hctvwrapper 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hctvwrapper-0.1.0.tar.gz | 11.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hctvwrapper-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.9 kB
Release files / hctvwrapper-0.1.0.tar.gz
| Download URL | hctvwrapper-0.1.0.tar.gz |
|---|---|
| Size | 11.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
24fbe72f024bf08e7e4877f6c076f6fc81709c59f76dafe48775b4303bdd7d64
|
|
BLAKE2b-256 checksum How to use checksums |
68ff9c5ecbbfd8663edfa96c3ab6aefa3dd4abbf160eb73af95318a8f23a01d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / hctvwrapper-0.1.0-py3-none-any.whl
| Download URL | hctvwrapper-0.1.0-py3-none-any.whl |
|---|---|
| Size | 11.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0aee6800ca45ba4c7e0f41fe9ffce7e2607dd37accda3bdd5d0552b60746fb21
|
|
BLAKE2b-256 checksum How to use checksums |
43d5e1508f04af61d7fbba1b69f533daf24a5de1667be86e84c772c913747df9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|