dm_mac.slack_handler module

class dm_mac.slack_handler.Message(text: str, user_id: str, user_name: str, user_handle: str, channel_id: str, channel_name: str)

Bases: object

Represent an incoming message.

property as_dict: Dict[str, Any]
class dm_mac.slack_handler.SlackHandler(quart_app: Quart)

Bases: object

Handle Slack integration.

HELP_RESPONSE: str = 'Hi, I\'m the Machine Access Control slack bot.\nMention my username followed by one of these commands:\n"status" - list all machines and their status\n"oops <machine name>" - set Oops state on this machine immediately\n"lock <machine name>" - set maintenance lockout on this machine\n"clear <machine name>" - clear oops and/or maintenance lockout on this machine\n\nMachine names and aliases are matched case-insensitively.\nYou can also use the "/oops-clear" slash command (in the control channel) to\nclear a machine, optionally with no machine name to pick one from a menu.\n\nI am Free and Open Source software:\nhttps://github.com/Decaturmakers/machine-access-control'
MODAL_ACTION_ID: str = 'machine_select'

action_id of the machine-selection static_select element.

MODAL_BLOCK_ID: str = 'machine_block'

block_id of the machine-selection input block in the modal.

MODAL_CALLBACK_ID: str = 'oops_clear_modal'

Block Kit callback_id for the /oops-clear selection modal; used both when opening the modal and when routing its submission.

static _both_relays_suffix(machine: Machine) str

Return ‘ (both relays)’ if machine has second_relay, else empty.

_build_clear_modal(machines: List[Machine]) Dict[str, Any]

Build the /oops-clear Block Kit selection modal.

The modal has a single required input: a static_select dropdown of the given machines (option text = display name, value = machine name) with no default selection.

async _clear_machine(mach: Machine) str | None

Clear oops and/or maintenance lock-out status on a machine.

Returns None if something was cleared (the resulting Slack channel posts from Machine.unoops() / Machine.unlock() cover the outcome), or a human-readable message string if the machine was already clear (so the caller can surface it to the requester).

static _invalid_machine_msg(name_or_alias: str) str

Return the standard ‘invalid machine name or alias’ message.

async admin_log(message: str) None

Log a string to the admin channel only.

This uses asyncio.create_task() to fire-and-forget the Slack postMessage call, so that we don’t block on communication with Slack. Otherwise, updates to the relay/LCD/LED would be delayed by at least the timeout trying to post to Slack.

async app_mention(body: Dict[str, Any], say: AsyncSay) None

Handle an at-mention of our app in Slack.

Body is a dict with string keys, which is documented at <https://api.slack.com/events/app_mention>. The important bits are in the event nested dict.

The parts of the event dict within body that are of interest to us are:

  • user - the user ID (string beginning with “U”) of the person who mentioned us.

  • text - the string text of the message that mentioned us.

  • channel - the channel ID (string beginning with “C”) of the channel that the message was in.

async clear(msg: Message, say: AsyncSay) None

Clear oops and lock status on a machine.

async handle_command(msg: Message, say: AsyncSay) None

Handle a command sent to the bot.

async lock(msg: Message, say: AsyncSay) None

Set lock status on a machine.

async log_lock(machine: Machine, source: str) None

Log when a machine is locked.

This uses asyncio.create_task() to fire-and-forget the Slack postMessage call, so that we don’t block on communication with Slack. Otherwise, updates to the relay/LCD/LED would be delayed by at least the timeout trying to post to Slack.

async log_oops(machine: Machine, source: str, user_name: str | None = 'unknown user') None

Log when a machine is oopsed.

This uses asyncio.create_task() to fire-and-forget the Slack postMessage call, so that we don’t block on communication with Slack. Otherwise, updates to the relay/LCD/LED would be delayed by at least the timeout trying to post to Slack.

async log_override_login(machine: Machine, user_name: str) None

Log an override login to the admin channel only.

This uses asyncio.create_task() to fire-and-forget the Slack postMessage call, so that we don’t block on communication with Slack.

async log_unlock(machine: Machine, source: str) None

Log when a machine is un-locked.

This uses asyncio.create_task() to fire-and-forget the Slack postMessage call, so that we don’t block on communication with Slack. Otherwise, updates to the relay/LCD/LED would be delayed by at least the timeout trying to post to Slack.

async log_unoops(machine: Machine, source: str) None

Log when a machine is un-oopsed.

This uses asyncio.create_task() to fire-and-forget the Slack postMessage call, so that we don’t block on communication with Slack. Otherwise, updates to the relay/LCD/LED would be delayed by at least the timeout trying to post to Slack.

async machine_status(say: AsyncSay) None

Respond with machine status.

async oops(msg: Message, say: AsyncSay) None

Set oops status on a machine.

async oops_clear_command(ack: AsyncAck, command: Dict[str, Any], client: AsyncWebClient) None

Handle the /oops-clear slash command.

Usable only from the control channel. With an argument (/oops-clear <machine name>) it clears that machine directly. With no argument it opens a Block Kit modal to pick a machine to clear. ack is always called promptly so Slack does not report the command as failed; error/edge cases respond with an ephemeral message, while a successful clear acks silently (the resulting channel posts cover the outcome).

async oops_clear_modal_submit(ack: AsyncAck, view: Dict[str, Any]) None

Handle submission of the /oops-clear selection modal.

Acknowledges promptly to close the modal, then clears the selected machine. The machine may have been cleared between opening and submitting the modal; that and any unexpected missing selection are handled gracefully (no error raised).