From 16c2ffb6c0d670bac8e944670035cf73b4b99986 Mon Sep 17 00:00:00 2001 From: Andreas Hochsteger Date: Wed, 23 Aug 2023 09:45:22 +0200 Subject: [PATCH] fix: match processing + improved logging --- docs/config-schema-v2.md | 26 ++++--- docs/reference-docs.md | 2 +- src/lib/Context.spec.ts | 3 + src/lib/Context.ts | 8 ++- src/lib/config/AttachmentConfig.spec.ts | 2 +- src/lib/config/AttachmentMatchConfig.ts | 8 ++- src/lib/config/ThreadMatchConfig.ts | 6 +- src/lib/config/config-schema-v2.json | 9 +-- .../processors/AttachmentProcessor.spec.ts | 10 ++- src/lib/processors/AttachmentProcessor.ts | 57 ++++++++++++--- src/lib/processors/BaseProcessor.ts | 40 +++++++++++ src/lib/processors/GmailProcessor.ts | 10 +++ src/lib/processors/MessageProcessor.spec.ts | 4 +- src/lib/processors/MessageProcessor.ts | 51 +++++++++---- src/lib/processors/ThreadProcessor.spec.ts | 2 + src/lib/processors/ThreadProcessor.ts | 71 ++++++++++++++++--- 16 files changed, 251 insertions(+), 58 deletions(-) diff --git a/docs/config-schema-v2.md b/docs/config-schema-v2.md index c594cea3..7b3c9a4a 100644 --- a/docs/config-schema-v2.md +++ b/docs/config-schema-v2.md @@ -148,9 +148,11 @@ Represents a config to match a certain GMail attachment |**contentType**|`string`|A RegEx matching the content type of the attachment|No, default: `".*"`| |**includeAttachments**|`boolean`|Should regular attachments be included in attachment processing (default: true)|No, default: `true`| |**includeInlineImages**|`boolean`|Should inline images be included in attachment processing (default: true)|No, default: `true`| -|**largerThan**|`number`|Only include attachments larger than the given size in bytes|No, default: `-1`| +|**largerThan**|`number`|Only include attachments larger than the given size in bytes. +Set to `-1` to ignore it.|No, default: `-1`| |**name**|`string`|A RegEx matching the name of the attachment|No, default: `"(.*)"`| -|**smallerThan**|`number`|Only include attachments smaller than the given size in bytes|No| +|**smallerThan**|`number`|Only include attachments smaller than the given size in bytes. +Set to `-1` to ignore it.|No, default: `-1`| Additional properties are not allowed. @@ -177,7 +179,8 @@ Should inline images be included in attachment processing (default: true) ### AttachmentMatchConfig.largerThan -Only include attachments larger than the given size in bytes +Only include attachments larger than the given size in bytes. +Set to `-1` to ignore it. * **Type**: `number` * **Required**: No, default: `-1` @@ -191,10 +194,11 @@ A RegEx matching the name of the attachment ### AttachmentMatchConfig.smallerThan -Only include attachments smaller than the given size in bytes +Only include attachments smaller than the given size in bytes. +Set to `-1` to ignore it. * **Type**: `number` -* **Required**: No +* **Required**: No, default: `-1` @@ -760,8 +764,10 @@ Represents a config to match a certain GMail thread |---|---|---|---| |**firstMessageSubject**|`string`|The regex to match `firstMessageSubject`|No, default: `".*"`| |**labels**|`string`|The regex to match at least one label|No, default: `".*"`| -|**maxMessageCount**|`number`|The maximum number of messages a matching thread is allowed to have|No, default: `-1`| -|**minMessageCount**|`number`|The minimum number of messages a matching thread must have|No, default: `1`| +|**maxMessageCount**|`number`|The maximum number of messages a matching thread is allowed to have. +Set to `-1` to ignore it.|No, default: `-1`| +|**minMessageCount**|`number`|The minimum number of messages a matching thread must have. +Set to `-1` to ignore it.|No, default: `1`| |**query**|`string`|The GMail search query additional to the global query to find threads to be processed. See [Search operators you can use with Gmail](https://support.google.com/mail/answer/7190?hl=en) for more information.|No, default: | @@ -783,14 +789,16 @@ The regex to match at least one label ### ThreadMatchConfig.maxMessageCount -The maximum number of messages a matching thread is allowed to have +The maximum number of messages a matching thread is allowed to have. +Set to `-1` to ignore it. * **Type**: `number` * **Required**: No, default: `-1` ### ThreadMatchConfig.minMessageCount -The minimum number of messages a matching thread must have +The minimum number of messages a matching thread must have. +Set to `-1` to ignore it. * **Type**: `number` * **Required**: No, default: `1` diff --git a/docs/reference-docs.md b/docs/reference-docs.md index c0b6de95..97e44024 100644 --- a/docs/reference-docs.md +++ b/docs/reference-docs.md @@ -47,7 +47,7 @@ The following table lists all enum types and their values referenced in the conf | `MetaInfoType` | The type of meta information used for context substitution placerholders. | | | `PlaceholderType` | The type of a placeholder. | | | `ProcessingStage` | The stage of processing | | -| `ProcessingStatus` | The result status of processing a config or an action. | | +| `ProcessingStatus` | The result status of processing a config or an action. | | | `RunMode` | The runtime mode in which processing takes place. | | ## Substitution Placeholder diff --git a/src/lib/Context.spec.ts b/src/lib/Context.spec.ts index 9f274462..0110f3e5 100644 --- a/src/lib/Context.spec.ts +++ b/src/lib/Context.spec.ts @@ -10,6 +10,9 @@ it("should create a new processing result", () => { const expected: ProcessingResult = { status: ProcessingStatus.OK, performedActions: [], + processedAttachments: 0, + processedMessages: 0, + processedThreads: 0, } expect(actual).toMatchObject(expected) }) diff --git a/src/lib/Context.ts b/src/lib/Context.ts index 11f17aa1..e56745e1 100644 --- a/src/lib/Context.ts +++ b/src/lib/Context.ts @@ -150,7 +150,7 @@ export type AttachmentContext = MessageContext & { /** The result status of processing a config or an action. */ export enum ProcessingStatus { - /** An error has occured. */ + /** An error has occurred. */ ERROR = "error", /** The processing was successful. */ OK = "ok", @@ -165,6 +165,9 @@ export class ProcessingError extends Error { export type ProcessingResult = { status: ProcessingStatus performedActions: ActionConfig[] + processedThreads: number + processedMessages: number + processedAttachments: number failedAction?: ActionConfig error?: Error } @@ -172,5 +175,8 @@ export function newProcessingResult(): ProcessingResult { return { status: ProcessingStatus.OK, performedActions: [], + processedAttachments: 0, + processedMessages: 0, + processedThreads: 0, } } diff --git a/src/lib/config/AttachmentConfig.spec.ts b/src/lib/config/AttachmentConfig.spec.ts index c980e6cb..1347e51d 100644 --- a/src/lib/config/AttachmentConfig.spec.ts +++ b/src/lib/config/AttachmentConfig.spec.ts @@ -23,5 +23,5 @@ it("should ensure nested object defaults", () => { expect(attachmentConfig.match.includeAttachments).toBe(true) expect(attachmentConfig.match.includeInlineImages).toBe(true) expect(attachmentConfig.match.largerThan).toBe(-1) - expect(attachmentConfig.match.smallerThan).toBe(Number.MAX_SAFE_INTEGER) + expect(attachmentConfig.match.smallerThan).toBe(-1) }) diff --git a/src/lib/config/AttachmentMatchConfig.ts b/src/lib/config/AttachmentMatchConfig.ts index f23418ee..4c20c805 100644 --- a/src/lib/config/AttachmentMatchConfig.ts +++ b/src/lib/config/AttachmentMatchConfig.ts @@ -21,7 +21,8 @@ export class AttachmentMatchConfig { */ includeInlineImages? = true /** - * Only include attachments larger than the given size in bytes + * Only include attachments larger than the given size in bytes. + * Set to `-1` to ignore it. */ largerThan? = -1 /** @@ -29,9 +30,10 @@ export class AttachmentMatchConfig { */ name? = "(.*)" /** - * Only include attachments smaller than the given size in bytes + * Only include attachments smaller than the given size in bytes. + * Set to `-1` to ignore it. */ - smallerThan? = Number.MAX_SAFE_INTEGER + smallerThan? = -1 } export type RequiredAttachmentMatchConfig = RequiredDeep diff --git a/src/lib/config/ThreadMatchConfig.ts b/src/lib/config/ThreadMatchConfig.ts index b2661e79..4a67a5d9 100644 --- a/src/lib/config/ThreadMatchConfig.ts +++ b/src/lib/config/ThreadMatchConfig.ts @@ -18,13 +18,15 @@ export class ThreadMatchConfig { labels? = ".*" /** - * The maximum number of messages a matching thread is allowed to have + * The maximum number of messages a matching thread is allowed to have. + * Set to `-1` to ignore it. */ @Expose() maxMessageCount? = -1 /** - * The minimum number of messages a matching thread must have + * The minimum number of messages a matching thread must have. + * Set to `-1` to ignore it. */ @Expose() minMessageCount? = 1 diff --git a/src/lib/config/config-schema-v2.json b/src/lib/config/config-schema-v2.json index f0c291a9..53b9ee20 100644 --- a/src/lib/config/config-schema-v2.json +++ b/src/lib/config/config-schema-v2.json @@ -123,7 +123,7 @@ }, "largerThan": { "default": -1, - "description": "Only include attachments larger than the given size in bytes", + "description": "Only include attachments larger than the given size in bytes.\nSet to `-1` to ignore it.", "title": "largerThan", "type": "number" }, @@ -134,7 +134,8 @@ "type": "string" }, "smallerThan": { - "description": "Only include attachments smaller than the given size in bytes", + "default": -1, + "description": "Only include attachments smaller than the given size in bytes.\nSet to `-1` to ignore it.", "title": "smallerThan", "type": "number" } @@ -577,13 +578,13 @@ }, "maxMessageCount": { "default": -1, - "description": "The maximum number of messages a matching thread is allowed to have", + "description": "The maximum number of messages a matching thread is allowed to have.\nSet to `-1` to ignore it.", "title": "maxMessageCount", "type": "number" }, "minMessageCount": { "default": 1, - "description": "The minimum number of messages a matching thread must have", + "description": "The minimum number of messages a matching thread must have.\nSet to `-1` to ignore it.", "title": "minMessageCount", "type": "number" }, diff --git a/src/lib/processors/AttachmentProcessor.spec.ts b/src/lib/processors/AttachmentProcessor.spec.ts index 43e6ef76..abb18e7b 100644 --- a/src/lib/processors/AttachmentProcessor.spec.ts +++ b/src/lib/processors/AttachmentProcessor.spec.ts @@ -80,7 +80,7 @@ it("should build a match config with special globals", () => { expect(actual).toMatchObject(expected) }) -it("should match messages with matching parameters", () => { +it("should match attachments with matching parameters", () => { const matchExamples: { config: AttachmentMatchConfig; matched: string[] }[] = [ { @@ -123,7 +123,13 @@ it("should match messages with matching parameters", () => { }) const res = [] for (const att of mockedMessage.getAttachments()) { - if (AttachmentProcessor.matches(attachmentConfig.match, att)) { + if ( + AttachmentProcessor.matches( + mocks.messageContext, + attachmentConfig.match, + att, + ) + ) { res.push(att.getName()) } } diff --git a/src/lib/processors/AttachmentProcessor.ts b/src/lib/processors/AttachmentProcessor.ts index 8361a5b5..7a8fd91d 100644 --- a/src/lib/processors/AttachmentProcessor.ts +++ b/src/lib/processors/AttachmentProcessor.ts @@ -2,7 +2,6 @@ import { ProcessingStage } from "../config/ActionConfig" import { RequiredAttachmentConfig } from "../config/AttachmentConfig" import { AttachmentMatchConfig, - newAttachmentMatchConfig, RequiredAttachmentMatchConfig, } from "../config/AttachmentMatchConfig" import { @@ -42,14 +41,42 @@ export class AttachmentProcessor extends BaseProcessor { return attachmentContext } public static matches( + ctx: MessageContext, matchConfig: RequiredAttachmentMatchConfig, attachment: GoogleAppsScript.Gmail.GmailAttachment, ) { if (!attachment.getContentType().match(matchConfig.contentType)) - return false - if (!attachment.getName().match(matchConfig.name)) return false - if (attachment.getSize() <= matchConfig.largerThan) return false - if (attachment.getSize() >= matchConfig.smallerThan) return false + return this.noMatch( + ctx, + `contentType '${attachment.getContentType()}' does not match '${ + matchConfig.contentType + }'`, + ) + if (!attachment.getName().match(matchConfig.name)) + return this.noMatch( + ctx, + `name '${attachment.getName()}' does not match '${matchConfig.name}'`, + ) + if ( + matchConfig.largerThan != -1 && + attachment.getSize() <= matchConfig.largerThan + ) + return this.noMatch( + ctx, + `size ${attachment.getSize()} not larger than ${ + matchConfig.largerThan + }`, + ) + if ( + matchConfig.smallerThan != -1 && + attachment.getSize() >= matchConfig.smallerThan + ) + return this.noMatch( + ctx, + `size ${attachment.getSize()} not smaller than ${ + matchConfig.smallerThan + }`, + ) return true } @@ -58,7 +85,7 @@ export class AttachmentProcessor extends BaseProcessor { global: RequiredAttachmentMatchConfig, local: RequiredAttachmentMatchConfig, ): RequiredAttachmentMatchConfig { - return newAttachmentMatchConfig({ + const matchConfig: RequiredAttachmentMatchConfig = { contentType: PatternUtil.substitute( ctx, `${global.contentType}|${local.contentType}`.replace(".*|", ""), @@ -66,15 +93,24 @@ export class AttachmentProcessor extends BaseProcessor { includeAttachments: global.includeAttachments && local.includeAttachments, includeInlineImages: global.includeInlineImages && local.includeInlineImages, - largerThan: Math.max(global.largerThan, local.largerThan), + largerThan: this.effectiveMaxNumber( + global.largerThan, + local.largerThan, + -1, + ), name: PatternUtil.substitute( ctx, `${global.name}|${local.name}` .replace("(.*)|", "") .replace("|(.*)", ""), ), - smallerThan: Math.min(global.smallerThan, local.smallerThan), - }) + smallerThan: this.effectiveMinNumber( + global.smallerThan, + local.smallerThan, + -1, + ), + } + return matchConfig } public static getRegexMapFromAttachmentMatchConfig( @@ -178,7 +214,7 @@ export class AttachmentProcessor extends BaseProcessor { const attachments = ctx.message.object.getAttachments(opts) for (let index = 0; index < attachments.length; index++) { const attachment = attachments[index] - if (!this.matches(matchConfig, attachment)) { + if (!this.matches(ctx, matchConfig, attachment)) { ctx.log.debug( `Skipping non-matching attachment hash '${attachment.getHash()}' (name:'${attachment.getName()}', type:${attachment.getContentType()}, size:${attachment.getSize()}) started ...`, ) @@ -231,6 +267,7 @@ export class AttachmentProcessor extends BaseProcessor { ctx.attachment.config.actions, ctx.proc.config.global.attachment.actions, ) + result.processedAttachments += 1 ctx.log.info( `Processing of attachment hash '${attachment.getHash()}' finished.`, ) diff --git a/src/lib/processors/BaseProcessor.ts b/src/lib/processors/BaseProcessor.ts index 1e90b943..a747571c 100644 --- a/src/lib/processors/BaseProcessor.ts +++ b/src/lib/processors/BaseProcessor.ts @@ -127,6 +127,11 @@ export abstract class BaseProcessor { return true } + protected static noMatch(ctx: ProcessingContext, message: string): boolean { + ctx.log.debug(`-> Reason: ${message}`) + return false + } + protected static isSet( value: boolean | number | string | undefined, unsetValue?: unknown, @@ -137,6 +142,19 @@ export abstract class BaseProcessor { return this.isSet(value) ? value : defaultVal } + protected static effectiveCSV( + global: string | undefined, + local: string, + ): string { + return [ + ...new Set( + [...(global ?? "").split(","), ...(local ?? "").split(",")].map((v) => + v.trim(), + ), + ), + ].join(",") + } + protected static effectiveValue( global: T | undefined, local: T, @@ -149,6 +167,28 @@ export abstract class BaseProcessor { : unsetValue } + protected static effectiveMaxNumber( + global: number | undefined, + local: number, + unsetValue: number, + ): number { + global = global ?? unsetValue + if (global === unsetValue) return local + if (local === unsetValue) return global + return Math.max(global, local) + } + + protected static effectiveMinNumber( + global: number | undefined, + local: number, + unsetValue: number, + ): number { + global = global ?? unsetValue + if (global === unsetValue) return local + if (local === unsetValue) return global + return Math.min(global, local) + } + protected static effectiveNumber( global: number | undefined, local: number, diff --git a/src/lib/processors/GmailProcessor.ts b/src/lib/processors/GmailProcessor.ts index 0ea81f7f..6d36c341 100644 --- a/src/lib/processors/GmailProcessor.ts +++ b/src/lib/processors/GmailProcessor.ts @@ -108,6 +108,16 @@ export class GmailProcessor { newProcessingResult(), ) ctx.log.info("Processing of GmailProcessor config finished.") + ctx.log.info(`Processing summary:`) + ctx.log.info(` - Processed threads: ${result.processedThreads}`) + ctx.log.info(` - Processed messages: ${result.processedMessages}`) + ctx.log.info(` - Processed attachments: ${result.processedAttachments}`) + ctx.log.info(` - Executed actions: ${result.performedActions.length}`) + ctx.log.info( + ` - Failed action (if any): ${JSON.stringify(result.failedAction)}`, + ) + ctx.log.info(` - Error (if any): ${JSON.stringify(result.error)}`) + ctx.log.info(` - Overall status: ${result.status}`) return result } diff --git a/src/lib/processors/MessageProcessor.spec.ts b/src/lib/processors/MessageProcessor.spec.ts index c9ff1365..86dbd8ca 100644 --- a/src/lib/processors/MessageProcessor.spec.ts +++ b/src/lib/processors/MessageProcessor.spec.ts @@ -99,7 +99,9 @@ describe("match()", () => { }) const res = [] for (const m of mockedThread.getMessages()) { - if (MessageProcessor.matches(messageConfig.match, m)) { + if ( + MessageProcessor.matches(mocks.threadContext, messageConfig.match, m) + ) { res.push(m.getSubject()) } } diff --git a/src/lib/processors/MessageProcessor.ts b/src/lib/processors/MessageProcessor.ts index dbd307b3..81289c01 100644 --- a/src/lib/processors/MessageProcessor.ts +++ b/src/lib/processors/MessageProcessor.ts @@ -16,7 +16,6 @@ import { MessageFlag } from "../config/MessageFlag" import { MessageMatchConfig, RequiredMessageMatchConfig, - newMessageMatchConfig, } from "../config/MessageMatchConfig" import { MarkProcessedMethod } from "../config/SettingsConfig" import { AttachmentProcessor } from "../processors/AttachmentProcessor" @@ -45,30 +44,54 @@ export class MessageProcessor extends BaseProcessor { } public static matches( + ctx: ThreadContext, matchConfig: RequiredMessageMatchConfig, message: GoogleAppsScript.Gmail.GmailMessage, ) { - if (!message.getFrom().match(matchConfig.from)) return false - if (!message.getTo().match(matchConfig.to)) return false - if (!message.getSubject().match(matchConfig.subject)) return false + if (!message.getFrom().match(matchConfig.from)) + return this.noMatch( + ctx, + `from '${message.getFrom()}' does not match '${matchConfig.from}'`, + ) + if (!message.getTo().match(matchConfig.to)) + return this.noMatch( + ctx, + `to '${message.getTo()}' does not match '${matchConfig.to}'`, + ) + if (!message.getSubject().match(matchConfig.subject)) + return this.noMatch( + ctx, + `subject '${message.getSubject()}' does not match '${ + matchConfig.subject + }'`, + ) if (!this.matchTimestamp(matchConfig.newerThan, message.getDate(), true)) - return false + return this.noMatch( + ctx, + `date '${message.getDate()}' not newer than '${matchConfig.newerThan}'`, + ) if (!this.matchTimestamp(matchConfig.olderThan, message.getDate(), false)) - return false + return this.noMatch( + ctx, + `date '${message.getDate()}' not older than '${matchConfig.olderThan}'`, + ) for (let i = 0; i < matchConfig.is.length; i++) { const flag = matchConfig.is[i] switch (flag) { case MessageFlag.READ: - if (message.isUnread()) return false + if (message.isUnread()) + return this.noMatch(ctx, `message is not read`) break case MessageFlag.UNREAD: - if (!message.isUnread()) return false + if (!message.isUnread()) return this.noMatch(ctx, `message is read`) break case MessageFlag.STARRED: - if (!message.isStarred()) return false + if (!message.isStarred()) + return this.noMatch(ctx, `message is not starred`) break case MessageFlag.UNSTARRED: - if (message.isStarred()) return false + if (message.isStarred()) + return this.noMatch(ctx, `message is starred`) break } } @@ -80,7 +103,7 @@ export class MessageProcessor extends BaseProcessor { global: MessageMatchConfig, local: RequiredMessageMatchConfig, ): RequiredMessageMatchConfig { - return newMessageMatchConfig({ + const matchConfig: RequiredMessageMatchConfig = { from: PatternUtil.substitute( ctx, this.effectiveValue(global.from, local.from, ""), @@ -96,7 +119,8 @@ export class MessageProcessor extends BaseProcessor { ctx, this.effectiveValue(global.to, local.to, ""), ), - }) + } + return matchConfig } public static getRegexMapFromMessageMatchConfig( @@ -305,7 +329,7 @@ export class MessageProcessor extends BaseProcessor { ctx.proc.timer.checkMaxRuntimeReached() } const message = messages[index] - if (!this.matches(matchConfig, message)) { + if (!this.matches(ctx, matchConfig, message)) { ctx.log.debug( `Skipping non-matching message id ${message.getId()} (date:'${message .getDate() @@ -369,6 +393,7 @@ export class MessageProcessor extends BaseProcessor { ctx.message.config.actions, ctx.proc.config.global.message.actions, ) + result.processedMessages += 1 ctx.log.info(`Processing of message id ${message.getId()} finished.`) return result } diff --git a/src/lib/processors/ThreadProcessor.spec.ts b/src/lib/processors/ThreadProcessor.spec.ts index 3fde8292..3b72f055 100644 --- a/src/lib/processors/ThreadProcessor.spec.ts +++ b/src/lib/processors/ThreadProcessor.spec.ts @@ -38,6 +38,7 @@ it("should construct a GMail search query with globals (query, newerThan) and pr config, ) const matchConfig = ThreadProcessor.buildMatchConfig( + ctx, ctx.proc.config.global.thread.match, threadConfig.match, ) @@ -86,6 +87,7 @@ it("should process a matching thread config", () => { }), ]) expect(result.status).toEqual(ProcessingStatus.OK) + expect(result.processedThreads).toEqual(1) }) it("should process a non-matching thread config", () => { diff --git a/src/lib/processors/ThreadProcessor.ts b/src/lib/processors/ThreadProcessor.ts index 633abf02..61fb9539 100644 --- a/src/lib/processors/ThreadProcessor.ts +++ b/src/lib/processors/ThreadProcessor.ts @@ -16,7 +16,6 @@ import { RequiredThreadConfig } from "../config/ThreadConfig" import { RequiredThreadMatchConfig, ThreadMatchConfig, - newThreadMatchConfig, } from "../config/ThreadMatchConfig" import { PatternUtil } from "../utils/PatternUtil" import { BaseProcessor } from "./BaseProcessor" @@ -65,22 +64,39 @@ export class ThreadProcessor extends BaseProcessor { } public static buildMatchConfig( + ctx: ProcessingContext, global: ThreadMatchConfig, local: RequiredThreadMatchConfig, ): RequiredThreadMatchConfig { - return newThreadMatchConfig({ - query: `${global.query} ${local.query}`, - maxMessageCount: this.effectiveNumber( + const matchConfig: RequiredThreadMatchConfig = { + firstMessageSubject: PatternUtil.substitute( + ctx, + this.effectiveValue( + global.firstMessageSubject, + local.firstMessageSubject, + "", + ), + ), + labels: PatternUtil.substitute( + ctx, + this.effectiveCSV(global.labels, local.labels), + ), + query: PatternUtil.substitute( + ctx, + `${global.query} ${local.query}`.trim(), + ), + maxMessageCount: this.effectiveMaxNumber( global.maxMessageCount, local.maxMessageCount, -1, ), - minMessageCount: this.effectiveNumber( + minMessageCount: this.effectiveMinNumber( global.minMessageCount, local.minMessageCount, -1, ), - }) + } + return matchConfig } public static getRegexMapFromThreadMatchConfig( @@ -257,13 +273,41 @@ export class ThreadProcessor extends BaseProcessor { } public static matches( + ctx: ProcessingContext, matchConfig: RequiredThreadMatchConfig, thread: GoogleAppsScript.Gmail.GmailThread, ): boolean { if (!thread.getFirstMessageSubject().match(matchConfig.firstMessageSubject)) - return false - if (thread.getMessageCount() < matchConfig.minMessageCount) return false - if (thread.getMessageCount() > matchConfig.maxMessageCount) return false + return this.noMatch( + ctx, + `firstMessageSubject '${thread.getFirstMessageSubject()}' does not match '${ + matchConfig.firstMessageSubject + }'`, + ) + if ( + matchConfig.minMessageCount != -1 && + thread.getMessageCount() < matchConfig.minMessageCount + ) + return this.noMatch( + ctx, + `messageCount ${thread.getMessageCount()} < minMessageCount ${ + matchConfig.minMessageCount + }`, + ) + if ( + matchConfig.maxMessageCount != -1 && + thread.getMessageCount() > matchConfig.maxMessageCount + ) + return this.noMatch( + ctx, + `messageCount ${thread.getMessageCount()} > maxMessageCount ${ + matchConfig.maxMessageCount + }`, + ) + const threadLabels = thread + .getLabels() + .map((l) => l.getName()) + .join(",") if ( !matchConfig.labels .split(",") @@ -271,7 +315,10 @@ export class ThreadProcessor extends BaseProcessor { thread.getLabels().map((l) => l.getName() == matchLabel), ) ) - return true + return this.noMatch( + ctx, + `labels '${threadLabels}' do not contain all of '${matchConfig.labels}'`, + ) return true } @@ -286,6 +333,7 @@ export class ThreadProcessor extends BaseProcessor { `Processing of thread config index '${configIndex}' started ...`, ) const matchConfig = this.buildMatchConfig( + ctx, ctx.proc.config.global.thread.match, config.match, ) @@ -302,7 +350,7 @@ export class ThreadProcessor extends BaseProcessor { ctx.log.info(`-> got ${threads.length} threads`) for (let threadIndex = 0; threadIndex < threads.length; threadIndex++) { const thread = threads[threadIndex] - if (!this.matches(matchConfig, thread)) { + if (!this.matches(ctx, matchConfig, thread)) { ctx.log.debug( `Skipping non-matching thread id ${thread.getId()} (date:'${thread .getLastMessageDate() @@ -357,6 +405,7 @@ export class ThreadProcessor extends BaseProcessor { ctx.thread.config.actions, ctx.proc.config.global.thread.actions, ) + result.processedThreads += 1 ctx.log.info(`Processing of thread id ${thread.getId()} finished.`) return result }