Skip to content

Chat inventory for a user.

Lists a user's chats with type, topic, member count, and last activity, then summarizes the chat landscape (1:1 vs group vs meeting).

Use the signed-in user (delegated) by default, or --app-only with --user for application access.

Permissions

Requires delegated permission Chat.ReadBasic / Chat.Read, or application permission Chat.ReadBasic.All / Chat.Read.All.

Reference

View source

import argparse
import sys
from collections import Counter
from datetime import datetime, timezone

from office365.directory.users.user import User
from office365.graph_client import GraphClient
from tests.settings import client_id, client_secret, password, tenant, username


def _client(app_only: bool = False) -> GraphClient:
    if app_only:
        return GraphClient(tenant=tenant).with_client_secret(client_id, client_secret)
    return GraphClient(tenant=tenant).with_username_and_password(client_id, username, password)


def _resolve_user(client: GraphClient, user_ref: str) -> User:
    user = client.users[user_ref].get().execute_query()
    if user is None:
        sys.exit(f"User not found: {user_ref}")
    return user


def _preview_body(message) -> str:
    body = message.body if message is not None else None
    return str(body.content or "").replace("\n", " ")[:60] if body is not None else ""


def _fmt_time(dt) -> str:
    if isinstance(dt, datetime):
        return dt.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M")
    return str(dt or "?")


def main():
    parser = argparse.ArgumentParser(description="Chat inventory for a user")
    parser.add_argument("--app-only", action="store_true", help="app-only credentials (default: signed-in user)")
    parser.add_argument("--user", default=None, help="user id or UPN (requires --app-only)")
    parser.add_argument("--top", type=int, default=50, help="maximum number of chats (default 50)")
    parser.add_argument("--skip-members", action="store_true", help="skip per-chat member lookups (faster)")
    args = parser.parse_args()

    client = _client(args.app_only)
    if args.user:
        user = _resolve_user(client, args.user)
    else:
        user = client.me.get().execute_query()
    if user is None:
        sys.exit("Could not resolve the user.")

    chats = user.chats.top(args.top).get().execute_query()
    print(f"Chats for {user.display_name or user.id} ({len(chats)}):")

    summary: Counter = Counter()
    total_members = 0
    for chat in chats:
        chat_type = chat.chat_type or "?"
        summary[chat_type] += 1
        preview = _preview_body(chat.last_message_preview)
        member_count = ""
        if not args.skip_members:
            members = chat.members.get().execute_query()
            member_count = f" members={len(members)}"
            total_members += len(members)
        topic = chat.topic or "-"
        print(
            f"  {chat.id:50s} {chat_type:8s} {topic:20s} last={_fmt_time(chat.last_updated_datetime)}"
            f"  {member_count}  {preview}"
        )

    print("\nSummary:")
    for chat_type, count in summary.most_common():
        print(f"  {chat_type:8s}: {count}")
    if not args.skip_members:
        print(f"  total members across chats: {total_members}")


if __name__ == "__main__":
    main()

← Back to Chats