Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 3 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Gmail Channel (Plugin)

Connects Moltbot to Gmail via the `gog` CLI.
Connects OpenClaw to Gmail via the `gog` CLI.

## Installation

This is a plugin. To install from source:

```bash
moltbot plugins install ./extensions/gmail
openclaw plugins install ./extensions/gmail
```

## Features
Expand All @@ -17,28 +17,6 @@ moltbot plugins install ./extensions/gmail
- **Rich Text**: Markdown support for outbound emails.
- **Threading**: Native Gmail thread support with quoted reply context.
- **Archiving**: Automatically archives threads upon reply.
- **Email Body Sanitisation**: Automatically cleans incoming email bodies for LLM consumption.

## Email Body Sanitisation

Incoming emails are automatically sanitised to produce clean, readable text — no configuration needed.

### What It Does

- **HTML-to-text conversion**: Strips tags, removes `<style>` and `<script>` blocks, filters out tracking pixels, and decodes HTML entities.
- **Footer junk removal**: Strips common noise like unsubscribe links, "Sent from my iPhone", and confidentiality notices.
- **Whitespace cleanup**: Collapses excessive blank lines and trims leading/trailing whitespace.
- **Signature stripping**: Removes content below `-- ` signature separators by default.

### Configurable Signature Stripping

Signature stripping is enabled by default. If you need to preserve content after `--` separators (e.g. for emails where dashes appear in the body), you can disable it programmatically:

```ts
extractTextBody(html, plain, { stripSignature: false })
```

No plugin configuration is required — sanitisation runs automatically on every inbound message.

## Reply Behavior

Expand All @@ -49,7 +27,7 @@ No plugin configuration is required — sanitisation runs automatically on every

## Configuration

Add to `moltbot.json`:
Add to `openclaw.json`:

```json5
{
Expand Down
4 changes: 2 additions & 2 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { gmailPlugin } from "./src/channel.js";

import { setGmailRuntime } from "./src/runtime.js";

const plugin = {
...gmailPlugin,
register: (api: ClawdbotPluginApi) => {
register: (api: OpenClawPluginApi) => {
setGmailRuntime(api.runtime);
api.registerChannel(gmailPlugin);
}
Expand Down
2 changes: 1 addition & 1 deletion moltbot.plugin.json → openclaw.plugin.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"id": "moltbot-gmail",
"id": "openclaw-gmail",
"channels": [
"gmail"
],
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
{
"name": "@mcinteerj/moltbot-gmail",
"name": "@mcinteerj/openclaw-gmail",
"version": "1.1.4",
"description": "Gmail channel plugin for Moltbot - uses gog CLI for secure Gmail access",
"description": "Gmail channel plugin for OpenClaw - uses gog CLI for secure Gmail access",
"type": "module",
"main": "index.ts",
"author": "Jake McInteer <mcinteerj@gmail.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/moltbot/moltbot",
"url": "https://github.com/openclaw/openclaw",
"directory": "extensions/gmail"
},
"keywords": [
"moltbot",
"openclaw",
"gmail",
"email",
"channel",
"plugin"
],
"moltbot": {
"openclaw": {
"extensions": [
"./index.ts"
],
Expand All @@ -38,7 +38,7 @@
"files": [
"index.ts",
"src/**/*.ts",
"moltbot.plugin.json",
"openclaw.plugin.json",
"README.md"
],
"dependencies": {
Expand All @@ -50,6 +50,6 @@
"zod": "^4.3.5"
},
"peerDependencies": {
"moltbot": ">=2026.1.0"
"openclaw": ">=2026.1.0"
}
}
2 changes: 1 addition & 1 deletion src/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
type ChannelConfig,
type ResolvedChannelAccount,
DEFAULT_ACCOUNT_ID,
} from "moltbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import type { GmailConfig } from "./config.js";

export interface ResolvedGmailAccount extends ResolvedChannelAccount {
Expand Down
8 changes: 4 additions & 4 deletions src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import {
setAccountEnabledInConfigSection,
deleteAccountFromConfigSection,
type InboundMessage,
type ClawdbotConfig,
type OpenClawConfig,
type ChannelGatewayContext,
type MsgContext,
} from "moltbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import { GmailConfigSchema } from "./config.js";
import {
resolveGmailAccount,
Expand Down Expand Up @@ -53,7 +53,7 @@ const dispatchSemaphore = new Semaphore(5);
function buildGmailMsgContext(
msg: InboundMessage,
account: ResolvedGmailAccount,
cfg: ClawdbotConfig,
cfg: OpenClawConfig,
): MsgContext {
const runtime = getGmailRuntime();
const to = `gmail:${account.email}`;
Expand Down Expand Up @@ -273,7 +273,7 @@ export const gmailPlugin: ChannelPlugin<ResolvedGmailAccount> = {
},
},
agentPrompt: {
messageToolHints: ({ cfg, accountId }: { cfg: ClawdbotConfig; accountId: string }) => {
messageToolHints: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId: string }) => {
const account = resolveGmailAccount(cfg, accountId);
return [
"### Gmail Messaging",
Expand Down
4 changes: 2 additions & 2 deletions src/history-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";

// Store historyIds in ~/.moltbot/state/gmail/history-{account}.json
const STORE_DIR = path.join(os.homedir(), ".moltbot", "state", "gmail");
// Store historyIds in ~/.openclaw/state/gmail/history-{account}.json
const STORE_DIR = path.join(os.homedir(), ".openclaw", "state", "gmail");

interface HistoryState {
historyId: string;
Expand Down
2 changes: 1 addition & 1 deletion src/inbound.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type InboundMessage } from "moltbot/plugin-sdk";
import { type InboundMessage } from "openclaw/plugin-sdk";
import { extractTextBody } from "./strip-quotes.js";
import { extractAttachments } from "./attachments.js";

Expand Down
4 changes: 2 additions & 2 deletions src/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import lockfile from "proper-lockfile";
import type { ChannelLogSink, InboundMessage } from "moltbot/plugin-sdk";
import type { ChannelLogSink, InboundMessage } from "openclaw/plugin-sdk";
import type { ResolvedGmailAccount } from "./accounts.js";
import { loadHistoryId, saveHistoryId } from "./history-store.js";
import { parseInboundGmail, parseSearchGmail, type GogPayload, type GogSearchMessage } from "./inbound.js";
Expand Down Expand Up @@ -152,7 +152,7 @@ async function markAsRead(id: string, threadId: string | undefined, accountEmail
*/
async function pruneGmailSessions(account: ResolvedGmailAccount, log: ChannelLogSink) {
const ttlMs = account.sessionTtlDays * 24 * 60 * 60 * 1000;
const stateDir = path.join(os.homedir(), ".clawdbot", "agents", "main", "sessions");
const stateDir = path.join(os.homedir(), ".openclaw", "agents", "main", "sessions");
const storePath = path.join(stateDir, "sessions.json");

// Base directory for agent workspace (where attachments are stored)
Expand Down
10 changes: 5 additions & 5 deletions src/onboarding.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ClawdbotConfig, ChannelOnboardingAdapter } from "moltbot/plugin-sdk";
import { promptAccountId } from "moltbot/plugin-sdk";
import type { OpenClawConfig, ChannelOnboardingAdapter } from "openclaw/plugin-sdk";
import { promptAccountId } from "openclaw/plugin-sdk";
import { exec } from "node:child_process";
import { promisify } from "node:util";
import { listGmailAccountIds, resolveDefaultGmailAccountId } from "./accounts.js";
Expand Down Expand Up @@ -71,7 +71,7 @@ async function fetchGmailName(email: string): Promise<string | undefined> {

export const gmailOnboardingAdapter: ChannelOnboardingAdapter = {
channel,
getStatus: async ({ cfg }: { cfg: ClawdbotConfig }) => {
getStatus: async ({ cfg }: { cfg: OpenClawConfig }) => {
const ids = listGmailAccountIds(cfg);
const configured = ids.length > 0;
return {
Expand All @@ -88,7 +88,7 @@ export const gmailOnboardingAdapter: ChannelOnboardingAdapter = {
accountOverrides,
shouldPromptAccountIds,
}: {
cfg: ClawdbotConfig;
cfg: OpenClawConfig;
prompter: {
text: (opts: { message: string; validate?: (val?: string) => string | undefined; initialValue?: string }) => Promise<string>;
confirm: (opts: { message: string; initialValue?: boolean }) => Promise<boolean>;
Expand Down Expand Up @@ -204,7 +204,7 @@ export const gmailOnboardingAdapter: ChannelOnboardingAdapter = {

return { cfg: next, accountId: email };
},
disable: (cfg: ClawdbotConfig) => ({
disable: (cfg: OpenClawConfig) => ({
...cfg,
channels: {
...cfg.channels,
Expand Down
2 changes: 1 addition & 1 deletion src/moltbot.plugin.json → src/openclaw.plugin.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"id": "moltbot-gmail",
"id": "openclaw-gmail",
"channels": [
"gmail"
],
Expand Down
2 changes: 1 addition & 1 deletion src/outbound.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { spawn } from "node:child_process";
import { marked } from "marked";
import sanitizeHtml from "sanitize-html";
import { type OutboundContext, type ClawdbotConfig } from "moltbot/plugin-sdk";
import { type OutboundContext, type OpenClawConfig } from "openclaw/plugin-sdk";
import { resolveGmailAccount } from "./accounts.js";
import { isGmailThreadId } from "./normalize.js";
import { fetchQuotedContext } from "./quoting.js";
Expand Down
2 changes: 1 addition & 1 deletion src/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { PluginRuntime } from "moltbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";

let runtime: PluginRuntime | null = null;

Expand Down
2 changes: 1 addition & 1 deletion src/threading.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ChannelThreadingAdapter } from "moltbot/plugin-sdk";
import { type ChannelThreadingAdapter } from "openclaw/plugin-sdk";

export const gmailThreading: ChannelThreadingAdapter = {
buildToolContext: ({ context, hasRepliedRef }) => ({
Expand Down