Skip to main content

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

  1. Go to hackclub.tv
  2. Create a bot account and get your API key (starts with hctvb_)
  3. 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)

Source distribution for hctvwrapper 0.1.0
File Size Uploaded
hctvwrapper-0.1.0.tar.gz 11.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for hctvwrapper 0.1.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page