Extensible chat app with command bots

0
Sign in

Implement an in-memory chat app with one shared channel that people and bots post to. Users send text. Some messages are slash commands (/name arg ...), and bots can respond to those. Bots can also react to ordinary messages.

The main goal is extensibility. Adding a new bot type must not require changes to ChatApp.

API

class ChatApp:
    def __init__(self, bots: list[Bot] | None = None) -> None: ...
    def add_bot(self, bot: Bot) -> None: ...
    def send_message(self, user: str, text: str) -> list[str]: ...
    def get_log(self) -> list[str]: ...
  • Log entries are rendered as "sender: text". Bot replies use the bot's name as the sender.
  • send_message first appends the user's message to the log. Then bots may append replies, in the order the bots were registered. It returns the lines this call added.
  • get_log returns the whole channel history in order.
  • Bot replies never trigger other bots.
  • A command that no bot owns is logged with no reply. Command names are case-insensitive.
  • If two bots register the same command, add_bot raises ValueError.

Bots to support

AwayBot

  • /away [reason] marks the sender away and replies "<user> is away: <reason>". The default reason is away.
  • /back replies "welcome back, <user>", or "<user> was not away" if the sender was not away.
  • When a plain (non-command) message mentions @name and that user is away, it replies "<name> is away: <reason>". It posts this once per away user, in the order they were mentioned, and never about the sender.

MeetBot

  • /meet @a @b ... replies "meeting #<n> started: <sender>, a, b". Numbers start at 1. The sender comes first, duplicates are removed, and the rest keep the order they were mentioned.
  • With no mentions, it replies "usage: /meet @user [@user ...]" and does not use up a meeting number.

TacoBot

  • /taco @user [n] gives n tacos (default 1, allowed range 1-5) and replies "<giver> gave <n> taco(s) to <user> (<user> now has <total>)".
  • An invalid amount or a missing target gets "usage: /taco @user [1-5]". Giving tacos to yourself gets "you can't give tacos to yourself".
  • /tacos replies "tacos: u1=n1, u2=n2", sorted by count descending and then by name. If nobody has tacos yet, it replies "no tacos yet".

Example

app = ChatApp([AwayBot(), MeetBot(), TacoBot()])
app.send_message("dana", "/away dentist")
# ["dana: /away dentist", "AwayBot: dana is away: dentist"]
app.send_message("eli", "@dana can you review?")
# ["eli: @dana can you review?", "AwayBot: dana is away: dentist"]
app.send_message("eli", "/taco @fay 2")
# ["eli: /taco @fay 2", "TacoBot: eli gave 2 taco(s) to fay (fay now has 2)"]
app.send_message("fay", "/meet @eli @eli")
# ["fay: /meet @eli @eli", "MeetBot: meeting #1 started: fay, eli"]

Be ready to explain how a fourth bot would be added, and how the design would change for multiple channels.

Reconstructed from the reported question; details may differ from what interviewers ask.

OpenAICodingMediumLast asked Oct 2026

Sign in to see the worked answer

The full answer, chapter by chapter, with what interviewers expect at each level. Free with an account: one full answer per company.