""" Provides a command to fetch random or specific xkcd comics. Usage: !xkcd -> random comic !xkcd -> comic # (e.g. !xkcd 538) """ import aiohttp import tempfile import random import os import simplematrixbotlib as botlib XKCD_LATEST_URL = "https://xkcd.com/info.0.json" XKCD_COMIC_URL = "https://xkcd.com/{}/info.0.json" async def handle_command(room, message, bot, prefix, config): match = botlib.MessageMatch(room, message, bot, prefix) if not (match.prefix() and match.command("xkcd")): return args = match.args() try: async with aiohttp.ClientSession() as session: # Get latest comic number async with session.get(XKCD_LATEST_URL, timeout=10) as resp: resp.raise_for_status() latest_data = await resp.json() latest_num = latest_data["num"] # Determine target comic number if args and args[0].isdigit(): requested_num = int(args[0]) if requested_num < 1 or requested_num > latest_num: await bot.api.send_text_message( room.room_id, f"❌ Comic #{requested_num} doesn't exist. Valid range: 1 – {latest_num}." ) return comic_num = requested_num else: comic_num = random.randint(1, latest_num) # Fetch the comic data comic_url = XKCD_COMIC_URL.format(comic_num) async with session.get(comic_url, timeout=10) as resp: resp.raise_for_status() comic_data = await resp.json() image_url = comic_data["img"] title = comic_data.get("safe_title", comic_data.get("title", "xkcd")) alt = comic_data.get("alt", "") # Download the image async with session.get(image_url, timeout=10) as img_resp: img_resp.raise_for_status() image_data = await img_resp.read() with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp: tmp.write(image_data) img_path = tmp.name # Send image await bot.api.send_image_message(room_id=room.room_id, image_filepath=img_path) # Send comic info as text (optional but helpful) info = f"**#{comic_num} – {title}**" if alt: info += f"\n*{alt}*" await bot.api.send_markdown_message(room.room_id, info) os.remove(img_path) except aiohttp.ClientError as e: await bot.api.send_text_message(room.room_id, f"❌ Network error fetching xkcd: {e}") except Exception as e: await bot.api.send_text_message(room.room_id, f"❌ Error: {str(e)}") __version__ = "1.1.0" __author__ = "Funguy Bot" __description__ = "Fetch random or specific xkcd comics" __help__ = """
!xkcd – xkcd comics
  • !xkcd – random comic
  • !xkcd <number> – specific comic (e.g. !xkcd 538)
"""