How to Handle Talk Events versus Square (OpenChat) Events in LINEJS

LINEJS exposes two independent event streams—Talk (personal chats) and Square (OpenChat)—through a unified Client.listen() API that accepts boolean flags to enable each stream separately.

The LINEJS library abstracts the complexity of LINE's messaging protocols by providing distinct handlers for personal chat operations and OpenChat square events. Understanding how to differentiate and process these streams is essential for building responsive bots that handle both private conversations and community group interactions.

Understanding the Two Event Streams

LINEJS processes messages from two distinct backend services, each with its own data structures and connection handling.

Talk Events (Operations)

Talk events represent actions in personal chats, group chats, and official account conversations. Internally, these are modeled as Operation objects from the TalkService.

  • Underlying type: LINETypes.Operation
  • Source service: TalkService polled through the legacy push connection
  • Emitted events: "event" (raw operation), "message" (decrypted TalkMessage)

In packages/linejs/client/client.ts (lines 61-75), the client decrypts end-to-end encrypted messages when operation types are "SEND_MESSAGE" or "RECEIVE_MESSAGE", emitting a TalkMessage instance.

Square Events (OpenChat)

Square events handle OpenChat communities (formerly known as Square), including message notifications, member joins, and chat room updates.

  • Underlying type: LINETypes.SquareEvent
  • Source service: SquareService polled through the legacy push connection
  • Emitted events: "square:event" (raw event), "square:message" (extracted SquareMessage)

According to packages/linejs/client/client.ts (lines 84-95), when a square event type equals "NOTIFICATION_MESSAGE", the client extracts the inner message and emits it as a SquareMessage via the "square:message" event.

Listening to Events with Client.listen()

The Client class provides a unified entry point for both streams through the listen() method, defined in packages/linejs/client/client.ts (lines 56-80).

The method accepts a configuration object to selectively enable streams:

client.listen({
  talk: true,      // Enable personal/group chat events
  square: true,    // Enable OpenChat events
  signal: abortSignal // Optional AbortSignal for cleanup
});

Internally, listen() creates a Polling instance via this.base.createPolling() and initiates asynchronous loops for each enabled stream. The polling mechanism is implemented in packages/linejs/base/polling/mod.ts.

Event Handling Architecture

Talk Event Processing

When talk: true is specified, the client iterates over polling.listenTalkEvents(), which returns a ReadableStream<Operation>.

As shown in packages/linejs/base/polling/mod.ts (lines 80-84), the method initializes the legacy pusher and returns the operation stream:

listenTalkEvents(): ReadableStream<Operation> {
    this.client.push.opStream.renew();
    this.initLegyPusher();
    return this.client.push.opStream.stream;
}

The client then processes each operation, emitting raw events as "event" and decrypted messages as "message" when applicable.

Square Event Processing

Similarly, listenSquareEvents() in packages/linejs/base/polling/mod.ts manages the OpenChat stream:

listenSquareEvents(): ReadableStream<SquareEvent> {
    this.client.push.sqStream.renew();
    this.initLegyPusher();
    return this.client.push.sqStream.stream;
}

The client handles these events by emitting "square:event" for all events and "square:message" specifically for NOTIFICATION_MESSAGE types.

Abort Signal Handling

To gracefully stop listening, pass an AbortSignal in the options. The client automatically closes both push streams when the signal aborts, as implemented in packages/linejs/client/client.ts (lines 51-55):

signal && signal.addEventListener("abort", () => {
    this.base.push.opStream.close();
    this.base.push.sqStream.close();
});

Practical Implementation Examples

The following example demonstrates handling both event types simultaneously:

import { Client } from "@evex/linejs/client/mod.ts";

// Assume `base` is a logged-in BaseClient instance
const client = new Client(base);

// Enable both streams
client.listen({ talk: true, square: true });

// Handle Talk messages (personal/group chats)
client.on("message", (msg) => {
  console.log("[Talk] From:", msg.raw.senderMid, "Text:", msg.raw.text);
});

// Handle raw Talk operations (system events, read receipts, etc.)
client.on("event", (op) => {
  console.log("[Talk Event] Type:", op.type);
});

// Handle Square (OpenChat) messages
client.on("square:message", (msg) => {
  console.log("[Square] Chat:", msg.raw.chatMid, "Text:", msg.raw.text);
});

// Handle raw Square events (member joins, chat updates, etc.)
client.on("square:event", (ev) => {
  console.log("[Square Event] Type:", ev.type);
});

// Graceful shutdown example
const controller = new AbortController();
client.listen({ talk: true, square: false, signal: controller.signal });

// Later, to stop listening:
controller.abort(); // Closes opStream and sqStream

Summary

  • Talk events use Operation objects from TalkService, exposed via the "event" and "message" emitters in packages/linejs/client/client.ts.
  • Square events use SquareEvent objects from SquareService, exposed via "square:event" and "square:message" emitters.
  • Both streams are managed by the Polling class in packages/linejs/base/polling/mod.ts, which interfaces with the legacy push connection.
  • Enable streams independently using Client.listen({ talk: boolean, square: boolean }) and handle cleanup with an optional AbortSignal.

Frequently Asked Questions

What is the difference between Talk and Square events in LINEJS?

Talk events represent actions in personal chats, group conversations, and official account messages, modeled as Operation types from the TalkService. Square events represent OpenChat community interactions, modeled as SquareEvent types from the SquareService. The two streams use separate underlying connections but are exposed through the same Client.listen() API.

How do I enable only Square (OpenChat) events in LINEJS?

Pass talk: false and square: true to the listen() method:

client.listen({ talk: false, square: true });
client.on("square:message", (msg) => {
  console.log("OpenChat message:", msg.raw.text);
});

This configuration prevents the client from initializing the Talk operation stream and only processes Square events.

Can I listen to both Talk and Square events simultaneously?

Yes, the Client class supports concurrent processing of both streams. Set both options to true in Client.listen() and attach listeners for both "message"/"event" (Talk) and "square:message"/"square:event" (Square). The underlying Polling class manages both streams through separate ReadableStream instances.

Where are the event streams implemented in the LINEJS source code?

The high-level event handling is implemented in packages/linejs/client/client.ts, which emits events and manages the listen() lifecycle. The underlying stream polling logic resides in packages/linejs/base/polling/mod.ts, which interfaces with the legacy push connection (LegyPusher) and provides listenTalkEvents() and listenSquareEvents() methods. The service-level APIs are defined in packages/linejs/base/service/talk/mod.ts (TalkService) and packages/linejs/base/service/square/mod.ts (SquareService).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →