Skip to content

peetzweg/operator

Repository files navigation

Matrix GitHub Issue Bot

A Matrix chat bot that creates GitHub issues from thread conversations using LLM summarization.

How It Works

  1. Have a conversation in a Matrix thread
  2. Mention the bot with @bot !issue [owner/repo] [title]
  3. Bot fetches all thread messages
  4. Bot uses an LLM to summarize the thread into an issue body
  5. Bot creates a GitHub issue
  6. Bot reacts with a checkmark and replies with the issue link

Command Syntax

@bot !issue                           # Use room default repo, auto-generate title
@bot !issue Fix login bug             # Use room default repo, specified title
@bot !issue myorg/myrepo              # Use specified repo, auto-generate title
@bot !issue myorg/myrepo Fix login    # Use specified repo, specified title

Project Structure

/
├── packages/
│   ├── core/                 # Interfaces, types, LLM summarizer
│   ├── matrix-adapter/       # Matrix messenger implementation
│   └── github-adapter/       # GitHub issue tracker implementation
├── apps/
│   └── matrix-github-bot/    # Main bot application
├── scripts/                  # Utility scripts (login, reset)
├── Dockerfile
└── docker-compose.yml

Packages

Reusable packages for building chat-to-issue bots:

Package Description Docs
@operator/core Core interfaces (Messenger, IssueTracker, ThreadSummarizer), types, OpenRouter summarizer, and logging utilities README
@operator/matrix-adapter Matrix chat platform adapter with E2EE support README
@operator/github-adapter GitHub issue tracker adapter README

Applications

Ready-to-deploy bots built with the packages above:

App Description Platforms Docs
matrix-github-bot Creates GitHub issues from Matrix thread conversations Matrix → GitHub README

Want to build your own?

See the Architecture section for diagrams and examples of how to create new adapters (e.g., Slack→Jira, Discord→Linear).

Quick Start

# Install dependencies
pnpm install

# Build all packages
pnpm build

# Copy and configure environment
cp .env.example .env
# Edit .env with your tokens

# Run the bot
pnpm dev

See apps/matrix-github-bot/README.md for detailed setup instructions.

Development

# Install dependencies
pnpm install

# Build all packages
pnpm build

# Run in development mode
pnpm dev

# Type check
pnpm typecheck

# Lint
pnpm lint

# Run tests
pnpm test

# Clean build artifacts
pnpm clean

Logging

The bot uses Pino for structured logging. Configure the log level via the LOG_LEVEL environment variable:

LOG_LEVEL=info  # Default

Available log levels (from most to least verbose):

  • trace - Very detailed debugging
  • debug - Debug messages (command parsing, message flow)
  • info - General operational messages (startup, shutdown, issue creation)
  • warn - Warnings (failed to fetch events, config issues)
  • error - Errors (API failures, handler exceptions)
  • fatal - Fatal errors causing shutdown

Development mode (NODE_ENV !== 'production'): Logs are pretty-printed with colors via pino-pretty.

Production mode: Logs are output as JSON for parsing by log aggregators.

Example - Enable debug logging:

LOG_LEVEL=debug pnpm dev

Scripts

Bot Management

Script Description
pnpm dev Start the bot in development mode
pnpm build Build all packages
pnpm typecheck Run TypeScript type checking
pnpm lint Run ESLint
pnpm test Run tests
pnpm clean Clean build artifacts

Matrix Account Management

Script Description
pnpm matrix-login Interactive login to get a new access token
pnpm reset-bot Soft reset - delete local crypto/storage, keep current token
pnpm reset-bot:hard Hard reset - delete ALL devices (invalidates token)

When to use reset scripts:

  • E2EE key conflicts ("One time key already exists"): Run pnpm reset-bot first. If that doesn't work, run pnpm reset-bot:hard and then pnpm matrix-login to get a new token.
  • Starting fresh: Run pnpm reset-bot:hard followed by pnpm matrix-login.
  • Switching accounts: Update credentials in .env, then run pnpm reset-bot.

Docker

Script Description
pnpm docker:build Build the Docker image
pnpm docker:up Start the bot container
pnpm docker:down Stop the bot container

Docker Deployment

# Build the image
docker-compose build

# Start the bot
docker-compose up -d

# View logs
docker-compose logs -f

# Stop the bot
docker-compose down

Architecture

The project uses a provider-agnostic design with interfaces that allow swapping implementations. This makes it easy to build similar bots for different platforms (e.g., Slack→Jira, Discord→Linear).

System Overview

flowchart TB
    subgraph Chat["Chat Platform"]
        User([User])
        Thread[Thread/Conversation]
    end

    subgraph Bot["Bot Application"]
        Handler[Command Handler]

        subgraph Interfaces["Core Interfaces"]
            Messenger["«interface»\nMessenger"]
            Summarizer["«interface»\nThreadSummarizer"]
            Tracker["«interface»\nIssueTracker"]
        end
    end

    subgraph Implementations["Adapters (Swappable)"]
        subgraph MessengerImpls["Messenger Implementations"]
            Matrix[MatrixMessenger]
            Slack[SlackMessenger]
            Discord[DiscordMessenger]
        end

        subgraph SummarizerImpls["Summarizer Implementations"]
            OpenRouter[OpenRouterSummarizer]
            OpenAI[OpenAISummarizer]
            Local[LocalLLMSummarizer]
        end

        subgraph TrackerImpls["IssueTracker Implementations"]
            GitHub[GitHubIssueTracker]
            Jira[JiraIssueTracker]
            Linear[LinearIssueTracker]
        end
    end

    subgraph External["External Services"]
        ChatAPI[(Chat API)]
        LLM[(LLM API)]
        IssueAPI[(Issue API)]
    end

    User -->|"@bot !issue"| Thread
    Thread --> Handler
    Handler --> Messenger
    Handler --> Summarizer
    Handler --> Tracker

    Messenger -.->|implements| Matrix
    Messenger -.->|implements| Slack
    Messenger -.->|implements| Discord

    Summarizer -.->|implements| OpenRouter
    Summarizer -.->|implements| OpenAI
    Summarizer -.->|implements| Local

    Tracker -.->|implements| GitHub
    Tracker -.->|implements| Jira
    Tracker -.->|implements| Linear

    Matrix --> ChatAPI
    Slack --> ChatAPI
    Discord --> ChatAPI

    OpenRouter --> LLM
    OpenAI --> LLM
    Local --> LLM

    GitHub --> IssueAPI
    Jira --> IssueAPI
    Linear --> IssueAPI

    style Interfaces fill:#e1f5fe
    style Matrix fill:#90caf9
    style OpenRouter fill:#90caf9
    style GitHub fill:#90caf9
    style Slack fill:#f5f5f5,stroke-dasharray: 5 5
    style Discord fill:#f5f5f5,stroke-dasharray: 5 5
    style Jira fill:#f5f5f5,stroke-dasharray: 5 5
    style Linear fill:#f5f5f5,stroke-dasharray: 5 5
    style OpenAI fill:#f5f5f5,stroke-dasharray: 5 5
    style Local fill:#f5f5f5,stroke-dasharray: 5 5
Loading

Data Flow

sequenceDiagram
    participant U as User
    participant M as Messenger
    participant H as CommandHandler
    participant S as ThreadSummarizer
    participant I as IssueTracker

    U->>M: @bot !issue owner/repo
    M->>H: MentionEvent
    H->>M: fetchThreadMessages()
    M-->>H: Thread (messages[])
    H->>M: getUserProfile(senderId)
    M-->>H: UserProfile
    H->>S: summarize(thread)
    S-->>H: {title, body, labels}
    H->>H: buildIssueBody(summary, reporter)
    H->>I: createIssue({title, body, labels})
    I-->>H: {success, issue}
    H->>M: addReaction(✅)
    H->>M: sendReply(issue.url)
    M-->>U: "Issue created: url"
Loading

Core Interfaces

classDiagram
    class Messenger {
        <<interface>>
        +start() Promise~void~
        +stop() Promise~void~
        +fetchThreadMessages(roomId, threadId) Promise~Thread~
        +sendMessage(roomId, content, options?) Promise~string~
        +sendReply(roomId, replyToId, content) Promise~string~
        +addReaction(roomId, eventId, reaction) Promise~void~
        +onMention(handler) void
        +getUserProfile(userId) Promise~UserProfile~
        +getMessageLink(roomId, eventId) string
    }

    class IssueTracker {
        <<interface>>
        +createIssue(options) Promise~IssueCreateResult~
        +getIssue(owner, repo, number) Promise~Issue~
        +updateIssue(owner, repo, number, updates) Promise~IssueCreateResult~
        +addComment(owner, repo, number, body) Promise~void~
    }

    class ThreadSummarizer {
        <<interface>>
        +summarize(thread, options?) Promise~SummarizeResult~
    }

    class Thread {
        +id string
        +roomId string
        +messages Message[]
    }

    class Message {
        +id string
        +senderId string
        +senderName string
        +content string
        +timestamp Date
    }

    class SummarizeResult {
        +title string
        +body string
        +suggestedLabels string[]
    }

    Messenger ..> Thread : returns
    Thread *-- Message
    ThreadSummarizer ..> SummarizeResult : returns
Loading

Building a New Adapter

To add support for a new platform, implement the corresponding interface:

Example: Slack → Jira bot

  1. Create packages/slack-adapter/ implementing Messenger:

    export class SlackMessenger implements Messenger {
      async fetchThreadMessages(channelId, threadTs) { /* ... */ }
      async sendReply(channelId, threadTs, content) { /* ... */ }
      // ... other methods
    }
  2. Create packages/jira-adapter/ implementing IssueTracker:

    export class JiraIssueTracker implements IssueTracker {
      async createIssue(options) { /* ... */ }
      // ... other methods
    }
  3. Create apps/slack-jira-bot/ wiring them together:

    const messenger = new SlackMessenger(config.slack);
    const issueTracker = new JiraIssueTracker(config.jira);
    const summarizer = new OpenRouterSummarizer(config.llm);
    
    const handler = new IssueCommandHandler({
      messenger,
      issueTracker,
      summarizer,
      // ...
    });

The CommandHandler and ThreadSummarizer can be reused as-is.

Technology Stack

  • Runtime: Node.js 20+
  • Language: TypeScript
  • Package manager: pnpm workspaces
  • Matrix SDK: matrix-bot-sdk
  • Matrix E2EE: @matrix-org/matrix-sdk-crypto-nodejs
  • GitHub SDK: @octokit/rest
  • LLM: Vercel AI SDK + OpenRouter
  • Deployment: Docker

Features

  • Create GitHub issues from Matrix thread conversations
  • LLM-powered thread summarization
  • Support for encrypted Matrix rooms (E2EE)
  • Per-room configuration for default repos and labels
  • Docker deployment with persistent storage

License

MIT

About

A bot for the matrix to summarise threads and create tasks.

Resources

Stars

Watchers

Forks

Contributors