"""history.py: catch-up for your chat rooms, in Python. Pikoman keeps no messages; this bot does, the last 50 of each room in its own file (history.jsonl), and when someone comes back it sends them what they missed, to them alone. A member can also ask for the history ("history" event). Python 3.10+. pip install -r requirements.txt, then python history.py (reads .env). Its key (dashboard, New API key): rights subscribe and client_events, kind presence, channels: prefix presence-chat-. """ import json import time from pikoman_bot import Bot, env ROOMS = env("ROOMS", "presence-chat-lobby").split(",") FILE = env("HISTORY_FILE", "history.jsonl") KEEP = 50 # messages kept per room FIRST_VISIT = 20 # what someone who has never been here gets history = {} # room -> [{at, name, user_id, text}] last_seen = {} # (room, user) -> when that user was last in the room # Its own storage: one JSON line per message, read back at start. try: with open(FILE, encoding="utf-8") as file: for line in file: if line.strip(): message = json.loads(line) room_name = message.pop("room") history[room_name] = (history.get(room_name, []) + [message])[-KEEP:] except FileNotFoundError: pass # no file yet: nothing kept bot = Bot("History", access=3) def keep(room): user_of = {} # connection -> user id, while they are in # Every chat message, kept with who sent it: the name and user id your backend signed. @room.on("message") def message(sender, data, info): if room.is_bot(sender): return member = room.member(sender) kept = {"at": int(time.time() * 1000), "name": member.get("name", "Someone"), "user_id": member.get("user_id"), "text": str((data or {}).get("text", ""))[:500]} history[room.name] = (history.get(room.name, []) + [kept])[-KEEP:] with open(FILE, "a", encoding="utf-8") as file: file.write(json.dumps({"room": room.name, **kept}) + "\n") # Someone joined: what they missed, to that connection alone. @room.on_join async def joined(connection, data): if room.is_bot(connection) or not isinstance(data, dict) or "user_id" not in data: return user = str(data["user_id"]) user_of[connection] = user kept = history.get(room.name, []) since = last_seen.get((room.name, user)) missed = kept[-FIRST_VISIT:] if since is None else [m for m in kept if m["at"] > since] if missed: await room.send_to(connection, "missed", {"since": since, "messages": missed}) @room.on_leave def left(connection, data): user = user_of.pop(connection, None) if user: last_seen[(room.name, user)] = int(time.time() * 1000) # Asked for: all that is kept, to the one who asked. @room.on("history") async def asked(sender, data, info): await room.send_to(sender, "history", {"messages": history.get(room.name, [])}) for name in ROOMS: keep(bot.join(name)) print(f"Keeping history of {', '.join(ROOMS)} in {FILE}") bot.start()