"""greeter.py: welcomes each person who joins a presence room, to them alone, in Python: "Welcome, Ada! Bo and Cy are here." Someone who was here before (since the bot started) gets "Welcome back, Ada!". Python 3.10+. pip install -r requirements.txt, then python greeter.py (reads .env). Its key (dashboard, New API key): rights subscribe and client_events, kind presence, channels: prefix presence-chat-. Names come from the member data the app's backend signed into each join token ({ name, user_id }), never from anything a member sends. The greeting goes to one connection (send_to). """ import asyncio from pikoman_bot import Bot, env ROOMS = [r.strip() for r in env("ROOMS", "presence-chat-lobby").split(",") if r.strip()] bot = Bot("Greeter", access=3) queue = asyncio.Queue() # greetings wait their turn: eight a second, under the limit of 10 seen = {} # room -> people seen there since the bot started (in memory: a restart forgets them) def who(connection, data): """Who a member is, from its signed member data: its user id, or its connection without one.""" data = data if isinstance(data, dict) else {} return f"user:{data['user_id']}" if "user_id" in data else f"connection:{connection}" def name_of(data): name = data.get("name") if isinstance(data, dict) else None return name.strip()[:40] if isinstance(name, str) and name.strip() else "there" def listed(names): """"Bo", "Bo and Cy", "Bo, Cy and Di", "Bo, Cy, Di and 4 others".""" if len(names) <= 1: return "".join(names) if len(names) <= 3: return f"{', '.join(names[:-1])} and {names[-1]}" return f"{', '.join(names[:3])} and {len(names) - 3} others" def greet(room): before = seen.setdefault(room.name, set()) @room.on_join async def joined(connection, data): if room.is_bot(connection): return # other bots are not greeted person = who(connection, data) others = [(c, d) for c, d in room.members.items() if c != connection and not room.is_bot(c)] if any(who(c, d) == person for c, d in others): return # the same person in a second tab: greeted once names = list({who(c, d): name_of(d) for c, d in others}.values()) text = f"Welcome back, {name_of(data)}!" if person in before else f"Welcome, {name_of(data)}!" if not names: text += " You're the first one here." else: text += f" {listed(names)} {'is' if len(names) == 1 else 'are'} here." before.add(person) await queue.put((room, connection, text)) async def send_greetings(): while True: room, connection, text = await queue.get() # To that connection alone: nobody else in the room sees it. await room.send_to(connection, "welcome", {"text": text}) await asyncio.sleep(0.125) for name in ROOMS: greet(bot.join(name)) print(f"Greeting in {', '.join(ROOMS)}") bot.start(send_greetings())