npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

meeting-detection

v0.1.0

Published

macOS meeting detection engine for Electron/Node.js

Readme

Meeting Detection Engine

A macOS meeting detection engine for Electron and Node.js applications. Built with Rust and exposed to JavaScript via napi-rs.

Features

  • ✅ Two-tier decision tree algorithm for accurate detection
  • ✅ Event-based API (onMeetingStart, onMeetingEnd)
  • ✅ Simple JavaScript API

Supported Platforms & Services

| Service | Native App | Browser | Detection Method | | ------------------- | ---------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- | | Zoom | ✅ | ✅ | Tier 1: Network connections (UDP port 8801)Tier 2: URL patterns (zoom.us/j/, zoom.us/s/) | | Google Meet | ❌ | ✅ | Tier 2: URL patterns with meeting code validation (meet.google.com/xxx-yyyy-zzz) | | Microsoft Teams | ✅ | ✅ | Tier 1: Network connections (STUN/TURN ports)Tier 2: URL patterns (teams.live.com/v2/, teams.microsoft.com/_#/meet/) | | Webex | ✅ | ✅ | Tier 1: Network connections (video ports)Tier 2: URL patterns (*.webex.com/webapp/, *.webex.com/meet/) |

Supported Operating Systems:

  • macOS ✅ (Intel and Apple Silicon)

Installation

npm install meeting-detection

The package will automatically build native binaries for your platform during installation.

Usage

Basic Example

const {
  isMeetingActive,
  onMeetingStart,
  onMeetingEnd,
  init,
  getLastDetectionDetails,
} = require("meeting-detection");

// Initialize the engine (starts background polling every 2 seconds)
init();

// Check current status
const active = isMeetingActive();
console.log("Meeting active:", active);

// Get detailed detection information
const details = getLastDetectionDetails();
if (details) {
  console.log("App:", details.appName);
  console.log("Reason:", details.reason);
  if (details.meetingUrl) {
    console.log("URL:", details.meetingUrl);
  }
}

// Listen for meeting start
onMeetingStart((_, details) => {
  console.log("🎥 Meeting started!");
  console.log("App:", details.appName);
  // Do something when meeting starts
});

// Listen for meeting end
onMeetingEnd((_, details) => {
  console.log("✅ Meeting ended!");
  // Do something when meeting ends
});

Electron Example

const { app, BrowserWindow } = require("electron");
const {
  isMeetingActive,
  onMeetingStart,
  onMeetingEnd,
  init,
} = require("meeting-detection");

app.whenReady().then(() => {
  init();

  // Update UI when meeting status changes
  onMeetingStart((_, details) => {
    console.log("User is in a meeting:", details.appName);
    // Update your app UI
  });

  onMeetingEnd((_, details) => {
    console.log("User is no longer in a meeting");
    // Update your app UI
  });

  // Poll current status
  setInterval(() => {
    const active = isMeetingActive();
    updateStatusIndicator(active);
  }, 5000);
});

API Reference

init()

Initialize the meeting detection engine. This starts the background polling (every 2 seconds).

Note: Call this once when your app starts.

isMeetingActive(): boolean

Returns true if a meeting is currently detected, false otherwise.

Example:

const active = isMeetingActive();
if (active) {
  console.log("User is in a meeting");
}

onMeetingStart(callback: (error: null, details: JsDetectionDetails) => void): void

Register a callback that will be called when a meeting is detected to start. The callback follows Node.js error-first convention: first parameter is always null (no error), second parameter contains the detection details.

Example:

onMeetingStart((error, details) => {
  // error is always null, details contains the detection information
  console.log("Meeting started!");
  console.log("App:", details.appName);
  console.log("Reason:", details.reason);
  // Your logic here
});

// Or ignore the error parameter:
onMeetingStart((_, details) => {
  console.log("Meeting started!");
  console.log("App:", details.appName);
});

onMeetingEnd(callback: (error: null, details: JsDetectionDetails) => void): void

Register a callback that will be called when a meeting is detected to end. The callback follows Node.js error-first convention: first parameter is always null (no error), second parameter contains the detection details.

Example:

onMeetingEnd((error, details) => {
  // error is always null, details contains the detection information
  console.log("Meeting ended!");
  // Your logic here
});

// Or ignore the error parameter:
onMeetingEnd((_, details) => {
  console.log("Meeting ended!");
});

getLastDetectionDetails(): JsDetectionDetails | null

Get detailed information about the last detection cycle. Useful for debugging and understanding why the engine thinks a meeting is active or not.

Returns:

  • null if no detection has been performed yet
  • JsDetectionDetails object (see Type Reference below)

Example:

const details = getLastDetectionDetails();
if (details) {
  console.log("Active:", details.active);
  console.log("App:", details.appName);
  console.log("Reason:", details.reason);
}

Type Reference

JsDetectionDetails

The detection details object returned by callbacks and getLastDetectionDetails(). This type is exported and can be imported in TypeScript projects.

interface JsDetectionDetails {
  active: boolean; // Whether a meeting is currently active
  score: number; // Legacy scoring (for backward compatibility)
  appName?: string; // Name of the meeting app (e.g., "Zoom", "Safari", "Chrome")
  reason: string; // Detection reason (e.g., "NativeAppWithNetwork(Zoom)", "BrowserWithMeetingUrl(Safari)")
  meetingUrl?: string; // Meeting URL if detected in browser
  signals: SignalsBreakdown; // Breakdown of detection signals
}

interface SignalsBreakdown {
  meetingApp: SignalDetails;
  meetingWindow: SignalDetails;
  microphone: SignalDetails;
  camera: SignalDetails;
}

interface SignalDetails {
  active: boolean;
  weight: number;
}

Properties:

  • active: boolean - Whether a meeting is currently active
  • appName: string | undefined - Name of the meeting app (e.g., "Zoom", "Safari", "Chrome", "Microsoft Edge")
  • reason: string - Detection reason:
    • "NativeAppWithNetwork(Zoom)" - Native app detected with active network connections
    • "BrowserWithMeetingUrl(Safari)" - Browser tab with meeting URL detected
    • "None" - No meeting detected
  • meetingUrl: string | undefined - Full meeting URL if detected in browser (e.g., "https://meet.google.com/abc-def-ghi")
  • score: number - Legacy scoring system (kept for backward compatibility, not used in decision logic)
  • signals: SignalsBreakdown - Breakdown of individual detection signals (for debugging)

Example:

import { JsDetectionDetails } from "meeting-detection";

onMeetingStart((_, details: JsDetectionDetails) => {
  if (details.active) {
    console.log(`Meeting active in ${details.appName}`);
    if (details.meetingUrl) {
      console.log(`URL: ${details.meetingUrl}`);
    }
  }
});

How It Works

The engine uses a two-tier decision tree to accurately detect active meetings:

Tier 1: Native Meeting Apps (Zoom, Teams desktop, Webex desktop)

  • Detects if a native meeting app process is running
  • Checks for active network connections to meeting domains
  • Decision: If network activity detected → MEETING ACTIVE

Tier 2: Browser-Based Meetings (Google Meet, Teams web, Webex web)

  • Detects if a browser has a tab with a meeting URL
  • Validates meeting URLs (e.g., Google Meet codes must match xxx-yyyy-zzz format)
  • Decision: If valid meeting URL found → MEETING ACTIVE

Detection Signals

  1. Process Detection: Checks if known meeting applications are running (Zoom, Teams, Webex)
  2. Network Connection Detection: Monitors active connections to meeting service domains and ports
    • Zoom: UDP port 8801, connections to zoom.us
    • Teams: STUN/TURN ports, connections to teams.microsoft.com
    • Webex: Video ports, connections to webex.com
  3. Browser Tab Detection: Uses AppleScript to retrieve URLs from Chrome, Safari, and Edge tabs
  4. URL Pattern Matching: Validates meeting URLs against known patterns
    • Google Meet: meet.google.com/xxx-yyyy-zzz (with code validation)
    • Teams: teams.live.com/v2/, teams.microsoft.com/_#/meet/
    • Webex: *.webex.com/webapp/, *.webex.com/meet/

Permissions

macOS

The app may need the following permissions:

  • Accessibility: For browser tab URL detection (System Preferences → Security & Privacy → Accessibility)
    • Required for AppleScript to access browser tabs
    • Grant permission to Terminal/Node.js when prompted

Note: The engine does not require microphone or camera permissions. It detects meetings through network activity and browser tabs.

Limitations

  • macOS only: Currently supports macOS (Intel and Apple Silicon) only
  • Browser detection: Requires Accessibility permission for AppleScript to access browser tabs
  • Network detection: Uses lsof command which may require appropriate system permissions
  • Polling interval: Detection runs every 2 seconds (not real-time)

Roadmap

  • [ ] Windows support
  • [ ] Linux support
  • [ ] More meeting apps support (Jitsi, BlueJeans, etc.)
  • [ ] Configurable polling interval
  • [ ] Performance optimizations
  • [ ] Real-time detection (event-driven instead of polling)

License

MIT

Contributing

Contributions welcome! This is v1, so there's plenty of room for improvement.

Support

For issues and questions, please open an issue on GitHub.