Level 1 Refactor the legacy chat service
The starter file is a working but messy chat service: one send_message function, string slicing everywhere, and all state in module-level globals. It hosts three bots:
- AwayBot:
/away <text>sets the sender's away status (it prints nothing). Whenever a message mentions a user who is away, it postsAwayBot: <user> is away: <text>. - MeetBot:
/meet <user>posts a linkhttps://meet.example.com/room-<k>(k counts meetings from 1) and marks both people as away within a meeting with <other>. - TacoBot:
/givetaco <tacos> @<user>counts the 🌮 characters, adds them to the user's running total, and announces the gift and the new total.
Read the starter for the exact output strings and the order they appear in.
Refactor it into an extensible object-oriented design. Your module must provide:
BotService: an abstract base class (abc.ABC) with abstract methodsshould_activate(name, msg) -> boolandexecute(name, msg) -> list[str]. Instantiating it directly must raiseTypeError.AwayBot(),MeetBot(),TacoBot(): each aBotServicesubclass that keeps its own state. You choose how MeetBot updates AwayBot's statuses for now.ChatRoom()withregister_bot(bot),send_message(name, msg)and amessageslist. It appends"<name>: <msg>", then asks each bot in registration order whether it activates and appends whatever it returns. Adding a new bot must not require editingChatRoom.default_room(): a newChatRoomwith an AwayBot, a MeetBot and a TacoBot registered, in that order.
Requirements:
- For well-formed input,
default_room()must produce exactly the lines the legacy code produces. The tests replay hundreds of random conversations through both. - No global state. Two rooms are completely independent, and that includes the meeting counter.
- Each bot works on its own. For example,
TacoBot().execute("A", "/givetaco 🌮🌮 @b")returns the announcement.
room = default_room()
room.send_message("Bob", "/meet Alice")
room.send_message("Emily", "ping @Alice")
room.messages[-1] # 'AwayBot: Alice is away: in a meeting with Bob'