whatsapp_notifier 0.7.0 → 0.8.0
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.
- checksums.yaml +4 -4
- data/lib/generators/whatsapp_notifier/install_service_generator.rb +2 -0
- data/lib/whatsapp_notifier/client.rb +12 -0
- data/lib/whatsapp_notifier/providers/web_automation.rb +33 -0
- data/lib/whatsapp_notifier/services/web_automation/history.test.ts +695 -0
- data/lib/whatsapp_notifier/services/web_automation/history.ts +323 -0
- data/lib/whatsapp_notifier/services/web_automation/inbound.test.ts +209 -2
- data/lib/whatsapp_notifier/services/web_automation/inbound.ts +138 -16
- data/lib/whatsapp_notifier/services/web_automation/index.ts +81 -12
- data/lib/whatsapp_notifier/services/web_automation/media.test.ts +175 -9
- data/lib/whatsapp_notifier/services/web_automation/media.ts +94 -4
- data/lib/whatsapp_notifier/services/web_automation/send.test.ts +20 -0
- data/lib/whatsapp_notifier/services/web_automation/send.ts +17 -0
- data/lib/whatsapp_notifier/version.rb +1 -1
- data/lib/whatsapp_notifier/web_adapter.rb +85 -5
- data/lib/whatsapp_notifier.rb +12 -0
- data/spec/client_spec.rb +28 -1
- data/spec/generators/install_service_generator_spec.rb +1 -1
- data/spec/providers/web_automation_spec.rb +61 -3
- data/spec/web_adapter_spec.rb +232 -1
- data/spec/whatsapp_notifier_spec.rb +27 -0
- metadata +5 -1
data/spec/web_adapter_spec.rb
CHANGED
|
@@ -24,6 +24,52 @@ RSpec.describe WhatsAppNotifier::WebAdapter do
|
|
|
24
24
|
expect(result).to include(success: true, message_id: "k1")
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# The 0.8.0 service returns the real WhatsApp message id — the key the host
|
|
28
|
+
# dedupes the send's own fromMe echo on. It must beat the fabricated id.
|
|
29
|
+
it "prefers the service-issued message id over the idempotency key" do
|
|
30
|
+
response = http_success(body: { "success" => true, "messageId" => "true_919@c.us_ABC" })
|
|
31
|
+
allow(Net::HTTP).to receive(:start).and_return(response)
|
|
32
|
+
|
|
33
|
+
result = adapter.send_message(
|
|
34
|
+
payload: { to: "+1", body: "hi", metadata: { user_id: 1 }, idempotency_key: "k1" },
|
|
35
|
+
session: {}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
expect(result).to include(success: true, message_id: "true_919@c.us_ABC")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "accepts the snake_case message_id alias in the send response" do
|
|
42
|
+
response = http_success(body: { "success" => true, "message_id" => "true_919@c.us_DEF" })
|
|
43
|
+
allow(Net::HTTP).to receive(:start).and_return(response)
|
|
44
|
+
|
|
45
|
+
result = adapter.send_message(payload: { to: "+1", body: "hi", metadata: {} }, session: {})
|
|
46
|
+
|
|
47
|
+
expect(result).to include(message_id: "true_919@c.us_DEF")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# A 0.8.0 service that could not read the sent message's id answers
|
|
51
|
+
# messageId: null — fall through to the 0.7.0 fabrication chain.
|
|
52
|
+
it "falls back to the idempotency key when the service returns a null id" do
|
|
53
|
+
response = http_success(body: { "success" => true, "messageId" => nil })
|
|
54
|
+
allow(Net::HTTP).to receive(:start).and_return(response)
|
|
55
|
+
|
|
56
|
+
result = adapter.send_message(
|
|
57
|
+
payload: { to: "+1", body: "hi", metadata: {}, idempotency_key: "k1" },
|
|
58
|
+
session: {}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
expect(result).to include(message_id: "k1")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "fabricates a local id when neither the service nor the payload offers one" do
|
|
65
|
+
response = http_success(body: { "success" => true })
|
|
66
|
+
allow(Net::HTTP).to receive(:start).and_return(response)
|
|
67
|
+
|
|
68
|
+
result = adapter.send_message(payload: { to: "+1", body: "hi", metadata: {} }, session: {})
|
|
69
|
+
|
|
70
|
+
expect(result[:message_id]).to match(/\Alocal-\d+\z/)
|
|
71
|
+
end
|
|
72
|
+
|
|
27
73
|
it "yields the http connection to run the request" do
|
|
28
74
|
response = http_success(body: { "success" => true })
|
|
29
75
|
http = instance_double(Net::HTTP, request: response)
|
|
@@ -131,7 +177,9 @@ RSpec.describe WhatsAppNotifier::WebAdapter do
|
|
|
131
177
|
|
|
132
178
|
# A 0.6.0 service sends no media keys at all — the mapped hash must omit
|
|
133
179
|
# them (not nil them), because hosts key-gate ingest on has_media presence.
|
|
134
|
-
|
|
180
|
+
# Same contract for the 0.8.0 two-way keys: a customer message carries no
|
|
181
|
+
# from_me/to, so they must be absent (hosts key-gate fromMe ingest too).
|
|
182
|
+
it "omits the media and two-way keys entirely for plain inbound payloads" do
|
|
135
183
|
body = [{ "from" => "919@c.us", "body" => "hi", "messageId" => "m1", "timestamp" => 123, "type" => "chat" }]
|
|
136
184
|
allow(Net::HTTP).to receive(:start).and_return(http_success(body: body))
|
|
137
185
|
|
|
@@ -140,6 +188,35 @@ RSpec.describe WhatsAppNotifier::WebAdapter do
|
|
|
140
188
|
expect(message.keys).to match_array(%i[from body message_id timestamp type])
|
|
141
189
|
end
|
|
142
190
|
|
|
191
|
+
# 0.8.0 two-way capture: operator-sent messages arrive with fromMe + to —
|
|
192
|
+
# `to` is the counterparty chat id the host threads the conversation on.
|
|
193
|
+
it "maps the 0.8.0 two-way keys when the wire payload carries them" do
|
|
194
|
+
body = { "messages" => [{
|
|
195
|
+
"from" => "919000000001@c.us", "to" => "919@c.us", "fromMe" => true,
|
|
196
|
+
"body" => "on my way", "messageId" => "true_919@c.us_OP1", "timestamp" => 5, "type" => "chat"
|
|
197
|
+
}] }
|
|
198
|
+
allow(Net::HTTP).to receive(:start).and_return(http_success(body: body))
|
|
199
|
+
|
|
200
|
+
message = adapter.fetch_inbound(metadata: { user_id: "u-1" }).first
|
|
201
|
+
|
|
202
|
+
expect(message).to include(
|
|
203
|
+
from: "919000000001@c.us", to: "919@c.us", from_me: true,
|
|
204
|
+
body: "on my way", message_id: "true_919@c.us_OP1"
|
|
205
|
+
)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "accepts the snake_case from_me wire alias" do
|
|
209
|
+
body = { "messages" => [{
|
|
210
|
+
"from" => "919000000001@c.us", "to" => "919@c.us", "from_me" => true,
|
|
211
|
+
"body" => "done", "message_id" => "m9", "timestamp" => 9, "type" => "chat"
|
|
212
|
+
}] }
|
|
213
|
+
allow(Net::HTTP).to receive(:start).and_return(http_success(body: body))
|
|
214
|
+
|
|
215
|
+
message = adapter.fetch_inbound(metadata: {}).first
|
|
216
|
+
|
|
217
|
+
expect(message).to include(from_me: true, to: "919@c.us")
|
|
218
|
+
end
|
|
219
|
+
|
|
143
220
|
def binary_response(code:, body: "", headers: {})
|
|
144
221
|
response = double("binary response", code: code, body: body)
|
|
145
222
|
allow(response).to receive(:is_a?) { |klass| code == "200" && klass == Net::HTTPSuccess }
|
|
@@ -242,6 +319,160 @@ RSpec.describe WhatsAppNotifier::WebAdapter do
|
|
|
242
319
|
.to raise_error(/service request failed \(500\)/)
|
|
243
320
|
end
|
|
244
321
|
|
|
322
|
+
it "refetches media via POST, mapping the success verdict and attaching the token" do
|
|
323
|
+
allow(ENV).to receive(:[]).and_call_original
|
|
324
|
+
allow(ENV).to receive(:[]).with("WHATSAPP_WEBHOOK_TOKEN").and_return("sekrit")
|
|
325
|
+
response = http_success(body: {
|
|
326
|
+
"success" => true, "messageId" => "true_919@c.us_ABC", "mediaStatus" => "available",
|
|
327
|
+
"mediaMime" => "image/jpeg", "mediaFilename" => "beach.jpg", "mediaSize" => 10
|
|
328
|
+
})
|
|
329
|
+
captured = nil
|
|
330
|
+
http = double("http")
|
|
331
|
+
allow(http).to receive(:request) { |req| captured = req; response }
|
|
332
|
+
allow(Net::HTTP).to receive(:start) { |*_args, **_kwargs, &blk| blk.call(http) }
|
|
333
|
+
|
|
334
|
+
result = adapter.refetch_media(message_id: "true_919@c.us_ABC", chat_id: "919@c.us", metadata: { user_id: "u-1" })
|
|
335
|
+
|
|
336
|
+
expect(result).to eq(mime: "image/jpeg", filename: "beach.jpg", size: 10, status: "available")
|
|
337
|
+
expect(captured).to be_a(Net::HTTP::Post)
|
|
338
|
+
expect(captured.path).to eq("/media/u-1/refetch")
|
|
339
|
+
expect(captured["X-WA-Token"]).to eq("sekrit")
|
|
340
|
+
expect(JSON.parse(captured.body)).to eq("messageId" => "true_919@c.us_ABC", "chatId" => "919@c.us")
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
it "accepts the snake_case media keys in the refetch response" do
|
|
344
|
+
body = { "success" => true, "media_status" => "available", "media_mime" => "audio/ogg",
|
|
345
|
+
"media_filename" => "vn.ogg", "media_size" => 3 }
|
|
346
|
+
allow(Net::HTTP).to receive(:start).and_return(http_success(body: body))
|
|
347
|
+
|
|
348
|
+
expect(adapter.refetch_media(message_id: "m1", chat_id: "919@c.us", metadata: {}))
|
|
349
|
+
.to eq(mime: "audio/ogg", filename: "vn.ogg", size: 3, status: "available")
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Media gone upstream → the service answers 404 success:false; refetch
|
|
353
|
+
# degrades to nil like fetch_media, so the host can grey the bubble out.
|
|
354
|
+
it "returns nil when the refetch reports the media is gone (404)" do
|
|
355
|
+
allow(Net::HTTP).to receive(:start)
|
|
356
|
+
.and_return(http_failure(code: "404", body: JSON.generate({ success: false, mediaStatus: "unavailable", mediaError: "gone" })))
|
|
357
|
+
|
|
358
|
+
expect(adapter.refetch_media(message_id: "m1", chat_id: "919@c.us", metadata: {})).to be_nil
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# A success:false body that somehow arrives with a 2xx still degrades to nil.
|
|
362
|
+
it "returns nil when the refetch response is unsuccessful" do
|
|
363
|
+
allow(Net::HTTP).to receive(:start).and_return(http_success(body: { "success" => false, "mediaError" => "gone" }))
|
|
364
|
+
|
|
365
|
+
expect(adapter.refetch_media(message_id: "m1", chat_id: "919@c.us", metadata: {})).to be_nil
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
it "still raises when the refetch fails with a non-404 code" do
|
|
369
|
+
allow(Net::HTTP).to receive(:start)
|
|
370
|
+
.and_return(http_failure(code: "401", body: JSON.generate({ error: "User not authenticated" })))
|
|
371
|
+
|
|
372
|
+
expect { adapter.refetch_media(message_id: "m1", chat_id: "919@c.us", metadata: {}) }
|
|
373
|
+
.to raise_error(/service request failed \(401\)/)
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
it "lists chats with the token attached and maps the discovery keys" do
|
|
377
|
+
allow(ENV).to receive(:[]).and_call_original
|
|
378
|
+
allow(ENV).to receive(:[]).with("WHATSAPP_WEBHOOK_TOKEN").and_return("sekrit")
|
|
379
|
+
response = http_success(body: { "success" => true, "chats" => [
|
|
380
|
+
{ "id" => "919@c.us", "name" => "Asha", "lastMessageAt" => 1_717_000_000 },
|
|
381
|
+
{ "id" => "918@c.us", "name" => nil, "lastMessageAt" => nil }
|
|
382
|
+
] })
|
|
383
|
+
captured = nil
|
|
384
|
+
http = double("http")
|
|
385
|
+
allow(http).to receive(:request) { |req| captured = req; response }
|
|
386
|
+
allow(Net::HTTP).to receive(:start) { |*_args, **_kwargs, &blk| blk.call(http) }
|
|
387
|
+
|
|
388
|
+
chats = adapter.list_chats(metadata: { user_id: "u-1" })
|
|
389
|
+
|
|
390
|
+
expect(chats).to eq([
|
|
391
|
+
{ id: "919@c.us", name: "Asha", last_message_at: 1_717_000_000 },
|
|
392
|
+
{ id: "918@c.us", name: nil, last_message_at: nil }
|
|
393
|
+
])
|
|
394
|
+
expect(captured).to be_a(Net::HTTP::Get)
|
|
395
|
+
expect(captured.path).to eq("/chats/u-1")
|
|
396
|
+
expect(captured["X-WA-Token"]).to eq("sekrit")
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
it "accepts the snake_case last_message_at wire alias" do
|
|
400
|
+
body = { "chats" => [{ "id" => "919@c.us", "name" => "Asha", "last_message_at" => 9 }] }
|
|
401
|
+
allow(Net::HTTP).to receive(:start).and_return(http_success(body: body))
|
|
402
|
+
|
|
403
|
+
expect(adapter.list_chats(metadata: {}).first).to include(last_message_at: 9)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
it "returns an empty chat list when the service omits the key" do
|
|
407
|
+
allow(Net::HTTP).to receive(:start).and_return(http_success(body: { "success" => true }))
|
|
408
|
+
|
|
409
|
+
expect(adapter.list_chats(metadata: {})).to eq([])
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# An unpaired or not-ready user answers 401 — the standard non-2xx raise
|
|
413
|
+
# passes straight through to the caller.
|
|
414
|
+
it "raises the standard error when the chat list is unauthorized" do
|
|
415
|
+
allow(Net::HTTP).to receive(:start)
|
|
416
|
+
.and_return(http_failure(code: "401", body: JSON.generate({ error: "User not authenticated" })))
|
|
417
|
+
|
|
418
|
+
expect { adapter.list_chats(metadata: {}) }
|
|
419
|
+
.to raise_error(/service request failed \(401\): User not authenticated/)
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
it "fetches history with the token, posting the chat id and clamped limit" do
|
|
423
|
+
allow(ENV).to receive(:[]).and_call_original
|
|
424
|
+
allow(ENV).to receive(:[]).with("WHATSAPP_WEBHOOK_TOKEN").and_return("sekrit")
|
|
425
|
+
response = http_success(body: { "success" => true, "messages" => [
|
|
426
|
+
{ "from" => "919@c.us", "body" => "old reply", "messageId" => "h1", "timestamp" => 1, "type" => "chat" },
|
|
427
|
+
{ "from" => "919000000001@c.us", "to" => "919@c.us", "fromMe" => true,
|
|
428
|
+
"body" => "old send", "messageId" => "h2", "timestamp" => 2, "type" => "chat" },
|
|
429
|
+
{ "from" => "919@c.us", "body" => "", "messageId" => "h3", "timestamp" => 3, "type" => "image",
|
|
430
|
+
"hasMedia" => true, "mediaStatus" => "unavailable", "mediaError" => "history" }
|
|
431
|
+
] })
|
|
432
|
+
captured = nil
|
|
433
|
+
http = double("http")
|
|
434
|
+
allow(http).to receive(:request) { |req| captured = req; response }
|
|
435
|
+
allow(Net::HTTP).to receive(:start) { |*_args, **_kwargs, &blk| blk.call(http) }
|
|
436
|
+
|
|
437
|
+
messages = adapter.fetch_history(chat_id: "919@c.us", limit: 100_000, metadata: { user_id: "u-1" })
|
|
438
|
+
|
|
439
|
+
expect(captured).to be_a(Net::HTTP::Post)
|
|
440
|
+
expect(captured.path).to eq("/history/u-1")
|
|
441
|
+
expect(captured["X-WA-Token"]).to eq("sekrit")
|
|
442
|
+
expect(JSON.parse(captured.body)).to eq("chatId" => "919@c.us", "limit" => 200)
|
|
443
|
+
|
|
444
|
+
# Same mapper as fetch_inbound: two-way keys on the operator's messages,
|
|
445
|
+
# the by-design unavailable media verdict on history media.
|
|
446
|
+
expect(messages[0]).to eq(from: "919@c.us", body: "old reply", message_id: "h1", timestamp: 1, type: "chat")
|
|
447
|
+
expect(messages[1]).to include(from_me: true, to: "919@c.us", message_id: "h2")
|
|
448
|
+
expect(messages[2]).to include(has_media: true, media_status: "unavailable", media_error: "history")
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
it "clamps the history limit into 1..200 and defaults garbage to 50" do
|
|
452
|
+
bodies = []
|
|
453
|
+
http = double("http")
|
|
454
|
+
allow(http).to receive(:request) { |req| bodies << JSON.parse(req.body); http_success(body: { "messages" => [] }) }
|
|
455
|
+
allow(Net::HTTP).to receive(:start) { |*_args, **_kwargs, &blk| blk.call(http) }
|
|
456
|
+
|
|
457
|
+
adapter.fetch_history(chat_id: "919@c.us", metadata: {}) # default
|
|
458
|
+
adapter.fetch_history(chat_id: "919@c.us", limit: 0, metadata: {}) # below floor
|
|
459
|
+
adapter.fetch_history(chat_id: "919@c.us", limit: 201, metadata: {}) # above cap
|
|
460
|
+
adapter.fetch_history(chat_id: "919@c.us", limit: "120", metadata: {}) # numeric string
|
|
461
|
+
adapter.fetch_history(chat_id: "919@c.us", limit: 75.9, metadata: {}) # float floors
|
|
462
|
+
adapter.fetch_history(chat_id: "919@c.us", limit: "lots", metadata: {}) # garbage
|
|
463
|
+
adapter.fetch_history(chat_id: "919@c.us", limit: nil, metadata: {}) # nil
|
|
464
|
+
|
|
465
|
+
expect(bodies.map { |b| b["limit"] }).to eq([50, 1, 200, 120, 75, 50, 50])
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
it "raises the standard error when the history fetch fails" do
|
|
469
|
+
allow(Net::HTTP).to receive(:start)
|
|
470
|
+
.and_return(http_failure(code: "422", body: JSON.generate({ error: "`chatId` is required" })))
|
|
471
|
+
|
|
472
|
+
expect { adapter.fetch_history(chat_id: "12@g.us", metadata: {}) }
|
|
473
|
+
.to raise_error(/service request failed \(422\)/)
|
|
474
|
+
end
|
|
475
|
+
|
|
245
476
|
it "logs out via the service" do
|
|
246
477
|
allow(Net::HTTP).to receive(:start).and_return(http_success(body: { "success" => true }))
|
|
247
478
|
|
|
@@ -96,6 +96,9 @@ RSpec.describe WhatsAppNotifier do
|
|
|
96
96
|
allow(fake_client).to receive(:fetch_inbound).and_return([{ from: "q@c.us" }])
|
|
97
97
|
allow(fake_client).to receive(:fetch_media).and_return(body: "bytes", mime: "image/jpeg", filename: nil, size: 5)
|
|
98
98
|
allow(fake_client).to receive(:delete_media).and_return(success: true)
|
|
99
|
+
allow(fake_client).to receive(:refetch_media).and_return(mime: "image/jpeg", filename: nil, size: 5, status: "available")
|
|
100
|
+
allow(fake_client).to receive(:list_chats).and_return([{ id: "919@c.us", name: "Asha", last_message_at: 9 }])
|
|
101
|
+
allow(fake_client).to receive(:fetch_history).and_return([{ from: "919@c.us", body: "old", message_id: "h1" }])
|
|
99
102
|
allow(fake_client).to receive(:logout).and_return(success: true)
|
|
100
103
|
described_class.instance_variable_set(:@client, fake_client)
|
|
101
104
|
|
|
@@ -105,9 +108,33 @@ RSpec.describe WhatsAppNotifier do
|
|
|
105
108
|
expect(described_class.fetch_inbound(provider: :web_automation, metadata: { user_id: 1 })).to eq([{ from: "q@c.us" }])
|
|
106
109
|
expect(described_class.fetch_media(message_id: "m1", provider: :web_automation, metadata: { user_id: 1 })).to include(mime: "image/jpeg")
|
|
107
110
|
expect(described_class.delete_media(message_id: "m1", provider: :web_automation, metadata: { user_id: 1 })).to eq(success: true)
|
|
111
|
+
expect(described_class.refetch_media(message_id: "m1", chat_id: "919@c.us", provider: :web_automation, metadata: { user_id: 1 })).to include(status: "available")
|
|
112
|
+
expect(described_class.list_chats(provider: :web_automation, metadata: { user_id: 1 })).to eq([{ id: "919@c.us", name: "Asha", last_message_at: 9 }])
|
|
113
|
+
expect(described_class.fetch_history(chat_id: "919@c.us", limit: 20, provider: :web_automation, metadata: { user_id: 1 })).to eq([{ from: "919@c.us", body: "old", message_id: "h1" }])
|
|
108
114
|
expect(described_class.logout(provider: :web_automation, metadata: { user_id: 1 })).to eq(success: true)
|
|
109
115
|
expect(fake_client).to have_received(:fetch_media).with(message_id: "m1", provider: :web_automation, metadata: { user_id: 1 })
|
|
110
116
|
expect(fake_client).to have_received(:delete_media).with(message_id: "m1", provider: :web_automation, metadata: { user_id: 1 })
|
|
117
|
+
expect(fake_client).to have_received(:refetch_media).with(message_id: "m1", chat_id: "919@c.us", provider: :web_automation, metadata: { user_id: 1 })
|
|
118
|
+
expect(fake_client).to have_received(:list_chats).with(provider: :web_automation, metadata: { user_id: 1 })
|
|
119
|
+
expect(fake_client).to have_received(:fetch_history).with(chat_id: "919@c.us", limit: 20, provider: :web_automation, metadata: { user_id: 1 })
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "fetches history through the module API end to end with the default limit" do
|
|
123
|
+
adapter = double(
|
|
124
|
+
send_message: { success: true, session: {} },
|
|
125
|
+
fetch_qr_code: "qr",
|
|
126
|
+
connection_status: { state: "AUTHENTICATED", authenticated: true }
|
|
127
|
+
)
|
|
128
|
+
allow(adapter).to receive(:fetch_history).and_return([{ from: "z@c.us", body: "old", message_id: "h1" }])
|
|
129
|
+
described_class.configure do |config|
|
|
130
|
+
config.provider = :web_automation
|
|
131
|
+
config.web_automation_enabled = true
|
|
132
|
+
config.web_adapter = adapter
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
expect(described_class.fetch_history(chat_id: "919@c.us", metadata: { user_id: 1 }))
|
|
136
|
+
.to eq([{ from: "z@c.us", body: "old", message_id: "h1" }])
|
|
137
|
+
expect(adapter).to have_received(:fetch_history).with(chat_id: "919@c.us", limit: 50, metadata: { user_id: 1 })
|
|
111
138
|
end
|
|
112
139
|
|
|
113
140
|
it "fetches inbound through the module API end to end" do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: whatsapp_notifier
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kshitiz Sinha
|
|
@@ -80,6 +80,8 @@ files:
|
|
|
80
80
|
- lib/whatsapp_notifier/railtie.rb
|
|
81
81
|
- lib/whatsapp_notifier/result.rb
|
|
82
82
|
- lib/whatsapp_notifier/services/web_automation/bun.lock
|
|
83
|
+
- lib/whatsapp_notifier/services/web_automation/history.test.ts
|
|
84
|
+
- lib/whatsapp_notifier/services/web_automation/history.ts
|
|
83
85
|
- lib/whatsapp_notifier/services/web_automation/inbound.test.ts
|
|
84
86
|
- lib/whatsapp_notifier/services/web_automation/inbound.ts
|
|
85
87
|
- lib/whatsapp_notifier/services/web_automation/index.ts
|
|
@@ -90,6 +92,8 @@ files:
|
|
|
90
92
|
- lib/whatsapp_notifier/services/web_automation/metrics.test.ts
|
|
91
93
|
- lib/whatsapp_notifier/services/web_automation/metrics.ts
|
|
92
94
|
- lib/whatsapp_notifier/services/web_automation/package.json
|
|
95
|
+
- lib/whatsapp_notifier/services/web_automation/send.test.ts
|
|
96
|
+
- lib/whatsapp_notifier/services/web_automation/send.ts
|
|
93
97
|
- lib/whatsapp_notifier/services/web_automation/sessions.test.ts
|
|
94
98
|
- lib/whatsapp_notifier/services/web_automation/sessions.ts
|
|
95
99
|
- lib/whatsapp_notifier/session/qr_service.rb
|