#!/usr/bin/env python3 # Bitlair HobbyBot import asyncio from typing import Optional from time import sleep from string import Template from discord import Intents from discord.ext import commands from discord_webhook import DiscordWebhook, DiscordEmbed from datetime import datetime import pytz import paho.mqtt.client as mqtt import paho.mqtt.subscribe as subscribe import os import sys mqtt_host = os.getenv('MQTT_HOST') if not mqtt_host: print('MQTT_HOST unset') sys.exit(1) token = os.getenv('DISCORD_TOKEN') if not token: print('DISCORD_TOKEN unset') sys.exit(1) webhook_url = os.getenv('DISCORD_WEBHOOK_URL') if not webhook_url: print('DISCORD_WEBHOOK_URL unset') sys.exit(1) timezone = pytz.timezone('Europe/Amsterdam') # Discord bot stuff intents = Intents.default() intents.message_content = True intents.members = True HobbyBot = commands.Bot(command_prefix='!', description='Bitlair Bot', intents=intents) def mqtt_get_one(topic): try: msg = subscribe.simple(topic, hostname=mqtt_host, keepalive=10) return msg.payload.decode() except err: print(err) return '' # Define bot commands @HobbyBot.event async def on_ready(): print(f'Logged in as {HobbyBot.user} (ID: {HobbyBot.user.id})') # !state @HobbyBot.command(description='Bitlair Space State') async def state(ctx): async with ctx.typing(): spaceState = mqtt_get_one("bitlair/state") if spaceState == "open": await ctx.send("Bitlair is OPEN! :sunglasses:") elif spaceState == "closed": await ctx.send("Bitlair is closed :pensive:") # !co2 @HobbyBot.command(description='co2 levels') async def co2(ctx): async with ctx.typing(): hoofdruimte = mqtt_get_one("bitlair/climate/hoofdruimte_ingang/co2_ppm") await ctx.send("Hoofdruimte: %s ppm\n" % hoofdruimte) # !temp @HobbyBot.command(description='Temperature') async def temp(ctx): async with ctx.typing(): hoofdruimte = mqtt_get_one("bitlair/climate/hoofdruimte_ingang/temperature_c") await ctx.send("Hoofdruimte: %s °C\n" % hoofdruimte ) # !humid @HobbyBot.command(description='Humidity') async def humid(ctx): async with ctx.typing(): hoofdruimte = mqtt_get_one("bitlair/climate/hoofdruimte_ingang/humidity_pct") await ctx.send("Hoofdruimte: %s pct\n" % hoofdruimte) # !np @HobbyBot.command(description='Now Playing') async def np(ctx): async with ctx.typing(): await ctx.send("Now playing: Darude - Sandstorm") # define mqtt client stuff # # subscribe to topics def on_connect(client, userdata, flags, rc): client.subscribe("bitlair/state/bitlair") client.subscribe("bitlair/state/djo") # post to mqtt discord channel when state changes def on_message(client, userdata, msg): try: topic = msg.topic msg = msg.payload.decode() state_template = Template('$topic is now $state') if topic == "bitlair/state/bitlair": msg = state_template.substitute(topic='Bitlair', state=msg.upper()) elif topic == "bitlair/state/djo": msg = state_template.substitute(topic='DJO', state=msg.upper()) else: return webhook = DiscordWebhook(url=webhook_url, rate_limit_retry=True, content=msg) webhook.execute() sleep(1) # Prevent triggering rate limits. except Exception as e: print(e) client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message client.connect(mqtt_host, 1883, 60) # Start mqtt loop and discord bot client.loop_start() HobbyBot.run(token) # Exit when bot crashes client.loop_stop(force=True)