From fc8d72fed84b6f857d9bb92e555fae0188593fd5 Mon Sep 17 00:00:00 2001 From: Hunter Horsman Date: Wed, 31 Jan 2024 20:10:17 -0500 Subject: [PATCH] feat: add activity text component --- .env.defaults | 5 +--- src/discordUtility.ts | 10 +++++--- src/handleMqttMessage.ts | 2 +- src/loadConfig.ts | 41 ++++++++++++++++----------------- src/models/BotConfig.ts | 2 ++ src/processCommand.ts | 6 +++-- src/publishDiscoveryMessages.ts | 14 +++++++++++ 7 files changed, 49 insertions(+), 31 deletions(-) diff --git a/.env.defaults b/.env.defaults index fcbbce6..79fed3e 100644 --- a/.env.defaults +++ b/.env.defaults @@ -5,10 +5,7 @@ MQTT_PORT=1883 MQTT_USERNAME=homeassistant MQTT_PASSWORD= TOPIC_DISCOVERY=homeassistant -TOPIC_CONNECTED=discordbot/connected -TOPIC_ONLINE=discordbot/online -TOPIC_COMMAND=discordbot/command -TOPIC_VOICE=discordbot/voice +TOPIC_BOT=discordbot GUILD_ID= YOUR_ID= BOT_ID= diff --git a/src/discordUtility.ts b/src/discordUtility.ts index 3b06b96..b5f1b93 100644 --- a/src/discordUtility.ts +++ b/src/discordUtility.ts @@ -11,6 +11,7 @@ import { PermissionFlagsBits, VoiceChannel, } from "discord.js"; +import type { MqttClient } from "mqtt"; import type { BotConfig } from "./models/BotConfig"; /** @@ -92,8 +93,10 @@ export async function moveToChannelByName( */ export function setBotActivity( - discordClient: Client, botActivity: string, + discordClient: Client, + mqttClient: MqttClient, + config: BotConfig, ): void { console.info(`Setting bot activity: ${botActivity}`); if (!discordClient.user) { @@ -108,6 +111,7 @@ export function setBotActivity( ], status: "online", }); + mqttClient.publish(config.mqtt.topics.activity, botActivity); } /** * Prints the invite link generated by the discordClient with specific permissions and scopes. @@ -191,8 +195,8 @@ export async function getVoiceChannels( channels = channels.filter((c) => c ? self - .permissionsIn(c) - .has(PermissionFlagsBits.ViewChannel | PermissionFlagsBits.Connect) + .permissionsIn(c) + .has(PermissionFlagsBits.ViewChannel | PermissionFlagsBits.Connect) : false, ); return channels.filter( diff --git a/src/handleMqttMessage.ts b/src/handleMqttMessage.ts index 53df878..2be1d9f 100644 --- a/src/handleMqttMessage.ts +++ b/src/handleMqttMessage.ts @@ -26,7 +26,7 @@ export const createHandleMqttMessage = ( */ const handleMqttMessage = (topic: string, message: Buffer): void => { if (topic === config.mqtt.topics.command) - processCommand(message.toString(), discordClient, config); + processCommand(message.toString(), discordClient, mqttClient, config); if (topic === "homeassistant/status" && message.toString() === "online") { console.debug("Message: Home Assistant MQTT Online..."); publishDiscoveryMessages(mqttClient, discordClient, config); diff --git a/src/loadConfig.ts b/src/loadConfig.ts index 751eafa..42e5ab8 100644 --- a/src/loadConfig.ts +++ b/src/loadConfig.ts @@ -9,41 +9,40 @@ import { throwEnvironmentError } from "./throwError"; * @return {BotConfig} the loaded configuration object */ export function loadConfig(): BotConfig { + const botTopic = + process.env["TOPIC_BOT"] ?? throwEnvironmentError("TOPIC_BOT"); return { bot: { - token: process.env['BOT_TOKEN'] ?? throwEnvironmentError("BOT_TOKEN"), + token: process.env["BOT_TOKEN"] ?? throwEnvironmentError("BOT_TOKEN"), nickname: - process.env['BOT_NICKNAME'] ?? throwEnvironmentError("BOT_NICKNAME"), - id: process.env['BOT_ID'] ?? throwEnvironmentError("BOT_ID"), + process.env["BOT_NICKNAME"] ?? throwEnvironmentError("BOT_NICKNAME"), + id: process.env["BOT_ID"] ?? throwEnvironmentError("BOT_ID"), }, mqtt: { - url: process.env['MQTT_URL'] ?? throwEnvironmentError("MQTT_URL"), - port: process.env['MQTT_PORT'] ?? throwEnvironmentError("MQTT_PORT"), + url: process.env["MQTT_URL"] ?? throwEnvironmentError("MQTT_URL"), + port: process.env["MQTT_PORT"] ?? throwEnvironmentError("MQTT_PORT"), username: - process.env['MQTT_USERNAME'] ?? throwEnvironmentError("MQTT_USERNAME"), + process.env["MQTT_USERNAME"] ?? throwEnvironmentError("MQTT_USERNAME"), password: - process.env['MQTT_PASSWORD'] ?? throwEnvironmentError("MQTT_PASSWORD"), + process.env["MQTT_PASSWORD"] ?? throwEnvironmentError("MQTT_PASSWORD"), clientId: - process.env['MQTT_CLIENT_ID'] ?? throwEnvironmentError("MQTT_CLIENT_ID"), + process.env["MQTT_CLIENT_ID"] ?? + throwEnvironmentError("MQTT_CLIENT_ID"), topics: { - connected: - process.env['TOPIC_CONNECTED'] ?? - throwEnvironmentError("TOPIC_CONNECTED"), - discovery: - process.env['TOPIC_DISCOVERY'] ?? - throwEnvironmentError("TOPIC_DISCOVERY"), - online: - process.env['TOPIC_ONLINE'] ?? throwEnvironmentError("TOPIC_ONLINE"), - command: - process.env['TOPIC_COMMAND'] ?? throwEnvironmentError("TOPIC_COMMAND"), - voice: process.env['TOPIC_VOICE'] ?? throwEnvironmentError("TOPIC_VOICE"), + bot: botTopic, + activity: `${botTopic}/activity`, + connected: `${botTopic}/connected`, + discovery: `${botTopic}/discovery`, + online: `${botTopic}/online`, + command: `${botTopic}/command`, + voice: `${botTopic}/voice`, }, }, guild: { - id: process.env['GUILD_ID'] ?? throwEnvironmentError("GUILD_ID"), + id: process.env["GUILD_ID"] ?? throwEnvironmentError("GUILD_ID"), }, you: { - id: process.env['YOUR_ID'] ?? throwEnvironmentError("YOUR_ID"), + id: process.env["YOUR_ID"] ?? throwEnvironmentError("YOUR_ID"), }, }; } diff --git a/src/models/BotConfig.ts b/src/models/BotConfig.ts index b316448..20c2fad 100644 --- a/src/models/BotConfig.ts +++ b/src/models/BotConfig.ts @@ -11,11 +11,13 @@ export interface BotConfig { password: string; clientId: string; topics: { + bot: string; connected: string; discovery: string; online: string; command: string; voice: string; + activity: string; }; }; guild: { diff --git a/src/processCommand.ts b/src/processCommand.ts index 1ae3c20..607ee03 100644 --- a/src/processCommand.ts +++ b/src/processCommand.ts @@ -1,4 +1,5 @@ import { Client, GuildMember } from "discord.js"; +import type { MqttClient } from "mqtt"; import { setBotNickname } from "./discordUtility"; import { setBotActivity } from "./discordUtility"; import { moveToChannelByName } from "./discordUtility"; @@ -16,6 +17,7 @@ import { type BotConfig } from "./models/BotConfig"; export async function processCommand( message: string, discordClient: Client, + mqttClient: MqttClient, config: BotConfig, ): Promise { const you: GuildMember = await getSelf(discordClient, config); @@ -25,7 +27,7 @@ export async function processCommand( return; } // Get the first word as the command, leave the rest as arguments. - const command: string = args.shift()?.toLowerCase(); + const command: string = args.shift()?.toLowerCase() ?? "MISSING_COMMAND"; switch (command) { case "mute": // Confirm the user is connected to a voice channel. @@ -88,7 +90,7 @@ export async function processCommand( } case "bot_activity": { const botActivity: string = args.join(" ").trim(); - setBotActivity(discordClient, botActivity); + setBotActivity(botActivity, discordClient, mqttClient, config); break; } case "bot_nick": { diff --git a/src/publishDiscoveryMessages.ts b/src/publishDiscoveryMessages.ts index cc0401c..0acb0dc 100644 --- a/src/publishDiscoveryMessages.ts +++ b/src/publishDiscoveryMessages.ts @@ -142,6 +142,20 @@ export async function publishDiscoveryMessages( }, }); + // Publish a text component for the bot activity. + discoveryComponents.push({ + topic: `${config.mqtt.topics.discovery}/text/${deviceId}/activity/config`, + payload: { + name: "Bot Activity", + state_topic: config.mqtt.topics.activity, + command_topic: config.mqtt.topics.command, + command_template: "bot_activity {{ value }}", + unique_id: `${deviceId}_activity`, + device: device, + icon: "mdi:robot", + }, + }); + // Publish all the discovery components. for (const component of discoveryComponents) { mqttClient.publish(component.topic, JSON.stringify(component.payload), {