@openclaw/bluebubbles 2026.3.11 → 2026.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/attachments.test.ts +24 -36
- package/src/attachments.ts +2 -7
- package/src/chat.test.ts +7 -18
- package/src/chat.ts +4 -17
- package/src/media-send.test.ts +91 -45
- package/src/monitor-normalize.test.ts +32 -10
- package/src/monitor-normalize.ts +45 -28
- package/src/monitor-processing.ts +53 -1
- package/src/monitor-self-chat-cache.test.ts +190 -0
- package/src/monitor-self-chat-cache.ts +127 -0
- package/src/monitor.test.ts +447 -0
- package/src/monitor.webhook-auth.test.ts +132 -283
- package/src/multipart.ts +8 -0
- package/src/reactions.test.ts +4 -38
package/src/monitor.test.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
5
5
|
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
|
|
6
6
|
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
|
|
7
7
|
import { fetchBlueBubblesHistory } from "./history.js";
|
|
8
|
+
import { resetBlueBubblesSelfChatCache } from "./monitor-self-chat-cache.js";
|
|
8
9
|
import {
|
|
9
10
|
handleBlueBubblesWebhookRequest,
|
|
10
11
|
registerBlueBubblesWebhookTarget,
|
|
@@ -246,6 +247,7 @@ describe("BlueBubbles webhook monitor", () => {
|
|
|
246
247
|
vi.clearAllMocks();
|
|
247
248
|
// Reset short ID state between tests for predictable behavior
|
|
248
249
|
_resetBlueBubblesShortIdState();
|
|
250
|
+
resetBlueBubblesSelfChatCache();
|
|
249
251
|
mockFetchBlueBubblesHistory.mockResolvedValue({ entries: [], resolved: true });
|
|
250
252
|
mockReadAllowFromStore.mockResolvedValue([]);
|
|
251
253
|
mockUpsertPairingRequest.mockResolvedValue({ code: "TESTCODE", created: true });
|
|
@@ -259,6 +261,7 @@ describe("BlueBubbles webhook monitor", () => {
|
|
|
259
261
|
|
|
260
262
|
afterEach(() => {
|
|
261
263
|
unregister?.();
|
|
264
|
+
vi.useRealTimers();
|
|
262
265
|
});
|
|
263
266
|
|
|
264
267
|
describe("DM pairing behavior vs allowFrom", () => {
|
|
@@ -2676,5 +2679,449 @@ describe("BlueBubbles webhook monitor", () => {
|
|
|
2676
2679
|
|
|
2677
2680
|
expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
|
|
2678
2681
|
});
|
|
2682
|
+
|
|
2683
|
+
it("drops reflected self-chat duplicates after a confirmed assistant outbound", async () => {
|
|
2684
|
+
const account = createMockAccount({ dmPolicy: "open" });
|
|
2685
|
+
const config: OpenClawConfig = {};
|
|
2686
|
+
const core = createMockRuntime();
|
|
2687
|
+
setBlueBubblesRuntime(core);
|
|
2688
|
+
|
|
2689
|
+
const { sendMessageBlueBubbles } = await import("./send.js");
|
|
2690
|
+
vi.mocked(sendMessageBlueBubbles).mockResolvedValueOnce({ messageId: "msg-self-1" });
|
|
2691
|
+
|
|
2692
|
+
mockDispatchReplyWithBufferedBlockDispatcher.mockImplementationOnce(async (params) => {
|
|
2693
|
+
await params.dispatcherOptions.deliver({ text: "replying now" }, { kind: "final" });
|
|
2694
|
+
return EMPTY_DISPATCH_RESULT;
|
|
2695
|
+
});
|
|
2696
|
+
|
|
2697
|
+
unregister = registerBlueBubblesWebhookTarget({
|
|
2698
|
+
account,
|
|
2699
|
+
config,
|
|
2700
|
+
runtime: { log: vi.fn(), error: vi.fn() },
|
|
2701
|
+
core,
|
|
2702
|
+
path: "/bluebubbles-webhook",
|
|
2703
|
+
});
|
|
2704
|
+
|
|
2705
|
+
const timestamp = Date.now();
|
|
2706
|
+
const inboundPayload = {
|
|
2707
|
+
type: "new-message",
|
|
2708
|
+
data: {
|
|
2709
|
+
text: "hello",
|
|
2710
|
+
handle: { address: "+15551234567" },
|
|
2711
|
+
isGroup: false,
|
|
2712
|
+
isFromMe: false,
|
|
2713
|
+
guid: "msg-self-0",
|
|
2714
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2715
|
+
date: timestamp,
|
|
2716
|
+
},
|
|
2717
|
+
};
|
|
2718
|
+
|
|
2719
|
+
await handleBlueBubblesWebhookRequest(
|
|
2720
|
+
createMockRequest("POST", "/bluebubbles-webhook", inboundPayload),
|
|
2721
|
+
createMockResponse(),
|
|
2722
|
+
);
|
|
2723
|
+
await flushAsync();
|
|
2724
|
+
|
|
2725
|
+
expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledTimes(1);
|
|
2726
|
+
mockDispatchReplyWithBufferedBlockDispatcher.mockClear();
|
|
2727
|
+
|
|
2728
|
+
const fromMePayload = {
|
|
2729
|
+
type: "new-message",
|
|
2730
|
+
data: {
|
|
2731
|
+
text: "replying now",
|
|
2732
|
+
handle: { address: "+15551234567" },
|
|
2733
|
+
isGroup: false,
|
|
2734
|
+
isFromMe: true,
|
|
2735
|
+
guid: "msg-self-1",
|
|
2736
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2737
|
+
date: timestamp,
|
|
2738
|
+
},
|
|
2739
|
+
};
|
|
2740
|
+
|
|
2741
|
+
await handleBlueBubblesWebhookRequest(
|
|
2742
|
+
createMockRequest("POST", "/bluebubbles-webhook", fromMePayload),
|
|
2743
|
+
createMockResponse(),
|
|
2744
|
+
);
|
|
2745
|
+
await flushAsync();
|
|
2746
|
+
|
|
2747
|
+
const reflectedPayload = {
|
|
2748
|
+
type: "new-message",
|
|
2749
|
+
data: {
|
|
2750
|
+
text: "replying now",
|
|
2751
|
+
handle: { address: "+15551234567" },
|
|
2752
|
+
isGroup: false,
|
|
2753
|
+
isFromMe: false,
|
|
2754
|
+
guid: "msg-self-2",
|
|
2755
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2756
|
+
date: timestamp,
|
|
2757
|
+
},
|
|
2758
|
+
};
|
|
2759
|
+
|
|
2760
|
+
await handleBlueBubblesWebhookRequest(
|
|
2761
|
+
createMockRequest("POST", "/bluebubbles-webhook", reflectedPayload),
|
|
2762
|
+
createMockResponse(),
|
|
2763
|
+
);
|
|
2764
|
+
await flushAsync();
|
|
2765
|
+
|
|
2766
|
+
expect(mockDispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
|
|
2767
|
+
});
|
|
2768
|
+
|
|
2769
|
+
it("does not drop inbound messages when no fromMe self-chat copy was seen", async () => {
|
|
2770
|
+
const account = createMockAccount({ dmPolicy: "open" });
|
|
2771
|
+
const config: OpenClawConfig = {};
|
|
2772
|
+
const core = createMockRuntime();
|
|
2773
|
+
setBlueBubblesRuntime(core);
|
|
2774
|
+
|
|
2775
|
+
unregister = registerBlueBubblesWebhookTarget({
|
|
2776
|
+
account,
|
|
2777
|
+
config,
|
|
2778
|
+
runtime: { log: vi.fn(), error: vi.fn() },
|
|
2779
|
+
core,
|
|
2780
|
+
path: "/bluebubbles-webhook",
|
|
2781
|
+
});
|
|
2782
|
+
|
|
2783
|
+
const inboundPayload = {
|
|
2784
|
+
type: "new-message",
|
|
2785
|
+
data: {
|
|
2786
|
+
text: "genuinely new message",
|
|
2787
|
+
handle: { address: "+15551234567" },
|
|
2788
|
+
isGroup: false,
|
|
2789
|
+
isFromMe: false,
|
|
2790
|
+
guid: "msg-inbound-1",
|
|
2791
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2792
|
+
date: Date.now(),
|
|
2793
|
+
},
|
|
2794
|
+
};
|
|
2795
|
+
|
|
2796
|
+
await handleBlueBubblesWebhookRequest(
|
|
2797
|
+
createMockRequest("POST", "/bluebubbles-webhook", inboundPayload),
|
|
2798
|
+
createMockResponse(),
|
|
2799
|
+
);
|
|
2800
|
+
await flushAsync();
|
|
2801
|
+
|
|
2802
|
+
expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled();
|
|
2803
|
+
});
|
|
2804
|
+
|
|
2805
|
+
it("does not drop reflected copies after the self-chat cache TTL expires", async () => {
|
|
2806
|
+
vi.useFakeTimers();
|
|
2807
|
+
vi.setSystemTime(new Date("2026-03-07T00:00:00Z"));
|
|
2808
|
+
|
|
2809
|
+
const account = createMockAccount({ dmPolicy: "open" });
|
|
2810
|
+
const config: OpenClawConfig = {};
|
|
2811
|
+
const core = createMockRuntime();
|
|
2812
|
+
setBlueBubblesRuntime(core);
|
|
2813
|
+
|
|
2814
|
+
unregister = registerBlueBubblesWebhookTarget({
|
|
2815
|
+
account,
|
|
2816
|
+
config,
|
|
2817
|
+
runtime: { log: vi.fn(), error: vi.fn() },
|
|
2818
|
+
core,
|
|
2819
|
+
path: "/bluebubbles-webhook",
|
|
2820
|
+
});
|
|
2821
|
+
|
|
2822
|
+
const timestamp = Date.now();
|
|
2823
|
+
const fromMePayload = {
|
|
2824
|
+
type: "new-message",
|
|
2825
|
+
data: {
|
|
2826
|
+
text: "ttl me",
|
|
2827
|
+
handle: { address: "+15551234567" },
|
|
2828
|
+
isGroup: false,
|
|
2829
|
+
isFromMe: true,
|
|
2830
|
+
guid: "msg-self-ttl-1",
|
|
2831
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2832
|
+
date: timestamp,
|
|
2833
|
+
},
|
|
2834
|
+
};
|
|
2835
|
+
|
|
2836
|
+
await handleBlueBubblesWebhookRequest(
|
|
2837
|
+
createMockRequest("POST", "/bluebubbles-webhook", fromMePayload),
|
|
2838
|
+
createMockResponse(),
|
|
2839
|
+
);
|
|
2840
|
+
await vi.runAllTimersAsync();
|
|
2841
|
+
|
|
2842
|
+
mockDispatchReplyWithBufferedBlockDispatcher.mockClear();
|
|
2843
|
+
vi.advanceTimersByTime(10_001);
|
|
2844
|
+
|
|
2845
|
+
const reflectedPayload = {
|
|
2846
|
+
type: "new-message",
|
|
2847
|
+
data: {
|
|
2848
|
+
text: "ttl me",
|
|
2849
|
+
handle: { address: "+15551234567" },
|
|
2850
|
+
isGroup: false,
|
|
2851
|
+
isFromMe: false,
|
|
2852
|
+
guid: "msg-self-ttl-2",
|
|
2853
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2854
|
+
date: timestamp,
|
|
2855
|
+
},
|
|
2856
|
+
};
|
|
2857
|
+
|
|
2858
|
+
await handleBlueBubblesWebhookRequest(
|
|
2859
|
+
createMockRequest("POST", "/bluebubbles-webhook", reflectedPayload),
|
|
2860
|
+
createMockResponse(),
|
|
2861
|
+
);
|
|
2862
|
+
await vi.runAllTimersAsync();
|
|
2863
|
+
|
|
2864
|
+
expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled();
|
|
2865
|
+
});
|
|
2866
|
+
|
|
2867
|
+
it("does not cache regular fromMe DMs as self-chat reflections", async () => {
|
|
2868
|
+
const account = createMockAccount({ dmPolicy: "open" });
|
|
2869
|
+
const config: OpenClawConfig = {};
|
|
2870
|
+
const core = createMockRuntime();
|
|
2871
|
+
setBlueBubblesRuntime(core);
|
|
2872
|
+
|
|
2873
|
+
unregister = registerBlueBubblesWebhookTarget({
|
|
2874
|
+
account,
|
|
2875
|
+
config,
|
|
2876
|
+
runtime: { log: vi.fn(), error: vi.fn() },
|
|
2877
|
+
core,
|
|
2878
|
+
path: "/bluebubbles-webhook",
|
|
2879
|
+
});
|
|
2880
|
+
|
|
2881
|
+
const timestamp = Date.now();
|
|
2882
|
+
const fromMePayload = {
|
|
2883
|
+
type: "new-message",
|
|
2884
|
+
data: {
|
|
2885
|
+
text: "shared text",
|
|
2886
|
+
handle: { address: "+15557654321" },
|
|
2887
|
+
isGroup: false,
|
|
2888
|
+
isFromMe: true,
|
|
2889
|
+
guid: "msg-normal-fromme",
|
|
2890
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2891
|
+
date: timestamp,
|
|
2892
|
+
},
|
|
2893
|
+
};
|
|
2894
|
+
|
|
2895
|
+
await handleBlueBubblesWebhookRequest(
|
|
2896
|
+
createMockRequest("POST", "/bluebubbles-webhook", fromMePayload),
|
|
2897
|
+
createMockResponse(),
|
|
2898
|
+
);
|
|
2899
|
+
await flushAsync();
|
|
2900
|
+
|
|
2901
|
+
mockDispatchReplyWithBufferedBlockDispatcher.mockClear();
|
|
2902
|
+
|
|
2903
|
+
const inboundPayload = {
|
|
2904
|
+
type: "new-message",
|
|
2905
|
+
data: {
|
|
2906
|
+
text: "shared text",
|
|
2907
|
+
handle: { address: "+15551234567" },
|
|
2908
|
+
isGroup: false,
|
|
2909
|
+
isFromMe: false,
|
|
2910
|
+
guid: "msg-normal-inbound",
|
|
2911
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2912
|
+
date: timestamp,
|
|
2913
|
+
},
|
|
2914
|
+
};
|
|
2915
|
+
|
|
2916
|
+
await handleBlueBubblesWebhookRequest(
|
|
2917
|
+
createMockRequest("POST", "/bluebubbles-webhook", inboundPayload),
|
|
2918
|
+
createMockResponse(),
|
|
2919
|
+
);
|
|
2920
|
+
await flushAsync();
|
|
2921
|
+
|
|
2922
|
+
expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled();
|
|
2923
|
+
});
|
|
2924
|
+
|
|
2925
|
+
it("does not drop user-authored self-chat prompts without a confirmed assistant outbound", async () => {
|
|
2926
|
+
const account = createMockAccount({ dmPolicy: "open" });
|
|
2927
|
+
const config: OpenClawConfig = {};
|
|
2928
|
+
const core = createMockRuntime();
|
|
2929
|
+
setBlueBubblesRuntime(core);
|
|
2930
|
+
|
|
2931
|
+
unregister = registerBlueBubblesWebhookTarget({
|
|
2932
|
+
account,
|
|
2933
|
+
config,
|
|
2934
|
+
runtime: { log: vi.fn(), error: vi.fn() },
|
|
2935
|
+
core,
|
|
2936
|
+
path: "/bluebubbles-webhook",
|
|
2937
|
+
});
|
|
2938
|
+
|
|
2939
|
+
const timestamp = Date.now();
|
|
2940
|
+
const fromMePayload = {
|
|
2941
|
+
type: "new-message",
|
|
2942
|
+
data: {
|
|
2943
|
+
text: "user-authored self prompt",
|
|
2944
|
+
handle: { address: "+15551234567" },
|
|
2945
|
+
isGroup: false,
|
|
2946
|
+
isFromMe: true,
|
|
2947
|
+
guid: "msg-self-user-1",
|
|
2948
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2949
|
+
date: timestamp,
|
|
2950
|
+
},
|
|
2951
|
+
};
|
|
2952
|
+
|
|
2953
|
+
await handleBlueBubblesWebhookRequest(
|
|
2954
|
+
createMockRequest("POST", "/bluebubbles-webhook", fromMePayload),
|
|
2955
|
+
createMockResponse(),
|
|
2956
|
+
);
|
|
2957
|
+
await flushAsync();
|
|
2958
|
+
|
|
2959
|
+
mockDispatchReplyWithBufferedBlockDispatcher.mockClear();
|
|
2960
|
+
|
|
2961
|
+
const reflectedPayload = {
|
|
2962
|
+
type: "new-message",
|
|
2963
|
+
data: {
|
|
2964
|
+
text: "user-authored self prompt",
|
|
2965
|
+
handle: { address: "+15551234567" },
|
|
2966
|
+
isGroup: false,
|
|
2967
|
+
isFromMe: false,
|
|
2968
|
+
guid: "msg-self-user-2",
|
|
2969
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
2970
|
+
date: timestamp,
|
|
2971
|
+
},
|
|
2972
|
+
};
|
|
2973
|
+
|
|
2974
|
+
await handleBlueBubblesWebhookRequest(
|
|
2975
|
+
createMockRequest("POST", "/bluebubbles-webhook", reflectedPayload),
|
|
2976
|
+
createMockResponse(),
|
|
2977
|
+
);
|
|
2978
|
+
await flushAsync();
|
|
2979
|
+
|
|
2980
|
+
expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled();
|
|
2981
|
+
});
|
|
2982
|
+
|
|
2983
|
+
it("does not treat a pending text-only match as confirmed assistant outbound", async () => {
|
|
2984
|
+
const account = createMockAccount({ dmPolicy: "open" });
|
|
2985
|
+
const config: OpenClawConfig = {};
|
|
2986
|
+
const core = createMockRuntime();
|
|
2987
|
+
setBlueBubblesRuntime(core);
|
|
2988
|
+
|
|
2989
|
+
const { sendMessageBlueBubbles } = await import("./send.js");
|
|
2990
|
+
vi.mocked(sendMessageBlueBubbles).mockResolvedValueOnce({ messageId: "ok" });
|
|
2991
|
+
|
|
2992
|
+
mockDispatchReplyWithBufferedBlockDispatcher.mockImplementationOnce(async (params) => {
|
|
2993
|
+
await params.dispatcherOptions.deliver({ text: "same text" }, { kind: "final" });
|
|
2994
|
+
return EMPTY_DISPATCH_RESULT;
|
|
2995
|
+
});
|
|
2996
|
+
|
|
2997
|
+
unregister = registerBlueBubblesWebhookTarget({
|
|
2998
|
+
account,
|
|
2999
|
+
config,
|
|
3000
|
+
runtime: { log: vi.fn(), error: vi.fn() },
|
|
3001
|
+
core,
|
|
3002
|
+
path: "/bluebubbles-webhook",
|
|
3003
|
+
});
|
|
3004
|
+
|
|
3005
|
+
const timestamp = Date.now();
|
|
3006
|
+
const inboundPayload = {
|
|
3007
|
+
type: "new-message",
|
|
3008
|
+
data: {
|
|
3009
|
+
text: "hello",
|
|
3010
|
+
handle: { address: "+15551234567" },
|
|
3011
|
+
isGroup: false,
|
|
3012
|
+
isFromMe: false,
|
|
3013
|
+
guid: "msg-self-race-0",
|
|
3014
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
3015
|
+
date: timestamp,
|
|
3016
|
+
},
|
|
3017
|
+
};
|
|
3018
|
+
|
|
3019
|
+
await handleBlueBubblesWebhookRequest(
|
|
3020
|
+
createMockRequest("POST", "/bluebubbles-webhook", inboundPayload),
|
|
3021
|
+
createMockResponse(),
|
|
3022
|
+
);
|
|
3023
|
+
await flushAsync();
|
|
3024
|
+
|
|
3025
|
+
expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledTimes(1);
|
|
3026
|
+
mockDispatchReplyWithBufferedBlockDispatcher.mockClear();
|
|
3027
|
+
|
|
3028
|
+
const fromMePayload = {
|
|
3029
|
+
type: "new-message",
|
|
3030
|
+
data: {
|
|
3031
|
+
text: "same text",
|
|
3032
|
+
handle: { address: "+15551234567" },
|
|
3033
|
+
isGroup: false,
|
|
3034
|
+
isFromMe: true,
|
|
3035
|
+
guid: "msg-self-race-1",
|
|
3036
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
3037
|
+
date: timestamp,
|
|
3038
|
+
},
|
|
3039
|
+
};
|
|
3040
|
+
|
|
3041
|
+
await handleBlueBubblesWebhookRequest(
|
|
3042
|
+
createMockRequest("POST", "/bluebubbles-webhook", fromMePayload),
|
|
3043
|
+
createMockResponse(),
|
|
3044
|
+
);
|
|
3045
|
+
await flushAsync();
|
|
3046
|
+
|
|
3047
|
+
const reflectedPayload = {
|
|
3048
|
+
type: "new-message",
|
|
3049
|
+
data: {
|
|
3050
|
+
text: "same text",
|
|
3051
|
+
handle: { address: "+15551234567" },
|
|
3052
|
+
isGroup: false,
|
|
3053
|
+
isFromMe: false,
|
|
3054
|
+
guid: "msg-self-race-2",
|
|
3055
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
3056
|
+
date: timestamp,
|
|
3057
|
+
},
|
|
3058
|
+
};
|
|
3059
|
+
|
|
3060
|
+
await handleBlueBubblesWebhookRequest(
|
|
3061
|
+
createMockRequest("POST", "/bluebubbles-webhook", reflectedPayload),
|
|
3062
|
+
createMockResponse(),
|
|
3063
|
+
);
|
|
3064
|
+
await flushAsync();
|
|
3065
|
+
|
|
3066
|
+
expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled();
|
|
3067
|
+
});
|
|
3068
|
+
|
|
3069
|
+
it("does not treat chatGuid-inferred sender ids as self-chat evidence", async () => {
|
|
3070
|
+
const account = createMockAccount({ dmPolicy: "open" });
|
|
3071
|
+
const config: OpenClawConfig = {};
|
|
3072
|
+
const core = createMockRuntime();
|
|
3073
|
+
setBlueBubblesRuntime(core);
|
|
3074
|
+
|
|
3075
|
+
unregister = registerBlueBubblesWebhookTarget({
|
|
3076
|
+
account,
|
|
3077
|
+
config,
|
|
3078
|
+
runtime: { log: vi.fn(), error: vi.fn() },
|
|
3079
|
+
core,
|
|
3080
|
+
path: "/bluebubbles-webhook",
|
|
3081
|
+
});
|
|
3082
|
+
|
|
3083
|
+
const timestamp = Date.now();
|
|
3084
|
+
const fromMePayload = {
|
|
3085
|
+
type: "new-message",
|
|
3086
|
+
data: {
|
|
3087
|
+
text: "shared inferred text",
|
|
3088
|
+
handle: null,
|
|
3089
|
+
isGroup: false,
|
|
3090
|
+
isFromMe: true,
|
|
3091
|
+
guid: "msg-inferred-fromme",
|
|
3092
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
3093
|
+
date: timestamp,
|
|
3094
|
+
},
|
|
3095
|
+
};
|
|
3096
|
+
|
|
3097
|
+
await handleBlueBubblesWebhookRequest(
|
|
3098
|
+
createMockRequest("POST", "/bluebubbles-webhook", fromMePayload),
|
|
3099
|
+
createMockResponse(),
|
|
3100
|
+
);
|
|
3101
|
+
await flushAsync();
|
|
3102
|
+
|
|
3103
|
+
mockDispatchReplyWithBufferedBlockDispatcher.mockClear();
|
|
3104
|
+
|
|
3105
|
+
const inboundPayload = {
|
|
3106
|
+
type: "new-message",
|
|
3107
|
+
data: {
|
|
3108
|
+
text: "shared inferred text",
|
|
3109
|
+
handle: { address: "+15551234567" },
|
|
3110
|
+
isGroup: false,
|
|
3111
|
+
isFromMe: false,
|
|
3112
|
+
guid: "msg-inferred-inbound",
|
|
3113
|
+
chatGuid: "iMessage;-;+15551234567",
|
|
3114
|
+
date: timestamp,
|
|
3115
|
+
},
|
|
3116
|
+
};
|
|
3117
|
+
|
|
3118
|
+
await handleBlueBubblesWebhookRequest(
|
|
3119
|
+
createMockRequest("POST", "/bluebubbles-webhook", inboundPayload),
|
|
3120
|
+
createMockResponse(),
|
|
3121
|
+
);
|
|
3122
|
+
await flushAsync();
|
|
3123
|
+
|
|
3124
|
+
expect(mockDispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalled();
|
|
3125
|
+
});
|
|
2679
3126
|
});
|
|
2680
3127
|
});
|