signalbird 1.7.0 → 1.8.1
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/README.md +1 -1
- package/dist/angular.d.mts +6 -0
- package/dist/angular.d.ts +6 -0
- package/dist/angular.js +251 -7
- package/dist/angular.js.map +1 -1
- package/dist/angular.mjs +251 -7
- package/dist/angular.mjs.map +1 -1
- package/dist/app.d.mts +94 -3
- package/dist/app.d.ts +94 -3
- package/dist/app.js +243 -7
- package/dist/app.js.map +1 -1
- package/dist/app.mjs +243 -7
- package/dist/app.mjs.map +1 -1
- package/dist/react-native.d.mts +2 -0
- package/dist/react-native.d.ts +2 -0
- package/dist/react-native.js +250 -8
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +250 -8
- package/dist/react-native.mjs.map +1 -1
- package/dist/react.d.mts +2 -0
- package/dist/react.d.ts +2 -0
- package/dist/react.js +250 -8
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +250 -8
- package/dist/react.mjs.map +1 -1
- package/dist/signalbird.js +185 -98
- package/dist/vue.d.mts +2 -0
- package/dist/vue.d.ts +2 -0
- package/dist/vue.js +250 -8
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +250 -8
- package/dist/vue.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ değil, kasıtlı bir duvardır — anahtar bir kez istemciye indiğinde herkesi
|
|
|
94
94
|
| Go | `go get github.com/Pariette-Inc/signalbird.sdk` |
|
|
95
95
|
| .NET, ASP.NET Core | `dotnet add package Signalbird.Sdk` |
|
|
96
96
|
| Swift (iOS, macOS) | SPM: `https://github.com/Pariette-Inc/signalbird.sdk` |
|
|
97
|
-
| Kotlin (Android) | `implementation("io.signalbird:signalbird-sdk:1.
|
|
97
|
+
| Kotlin (Android) | `implementation("io.signalbird:signalbird-sdk:1.8.1")` |
|
|
98
98
|
| Canlı sohbet widget'ı (herhangi bir site) | `<script async src="https://signalbird.io/sdk/v1/signalbird.js" data-app-key="sbw_pub_…"></script>` |
|
|
99
99
|
|
|
100
100
|
> Hepsi **bu repodan** çıkar ve **aynı sürümü** taşır — ayrı SDK reposu ya da
|
package/dist/angular.d.mts
CHANGED
|
@@ -34,6 +34,12 @@ declare class SignalbirdService {
|
|
|
34
34
|
markRead(): Promise<void>;
|
|
35
35
|
closeConversation(rating?: number, comment?: string): Promise<void>;
|
|
36
36
|
openSession(input: SessionInput): Promise<unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar (29 Ağu 2026).
|
|
39
|
+
* Kapanmış sohbet geri açılmadığı için arayüzün "yeni sohbet" düğmesine
|
|
40
|
+
* bağlanacak bir eyleme ihtiyacı var.
|
|
41
|
+
*/
|
|
42
|
+
resetConversation(): void;
|
|
37
43
|
identify(input: IdentifyInput): Promise<unknown>;
|
|
38
44
|
registerDevice(input: RegisterDeviceInput): Promise<unknown>;
|
|
39
45
|
/** Uygulama kapanırken ya da testte: yoklamayı durdurur. */
|
package/dist/angular.d.ts
CHANGED
|
@@ -34,6 +34,12 @@ declare class SignalbirdService {
|
|
|
34
34
|
markRead(): Promise<void>;
|
|
35
35
|
closeConversation(rating?: number, comment?: string): Promise<void>;
|
|
36
36
|
openSession(input: SessionInput): Promise<unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar (29 Ağu 2026).
|
|
39
|
+
* Kapanmış sohbet geri açılmadığı için arayüzün "yeni sohbet" düğmesine
|
|
40
|
+
* bağlanacak bir eyleme ihtiyacı var.
|
|
41
|
+
*/
|
|
42
|
+
resetConversation(): void;
|
|
37
43
|
identify(input: IdentifyInput): Promise<unknown>;
|
|
38
44
|
registerDevice(input: RegisterDeviceInput): Promise<unknown>;
|
|
39
45
|
/** Uygulama kapanırken ya da testte: yoklamayı durdurur. */
|
package/dist/angular.js
CHANGED
|
@@ -57,6 +57,16 @@ var SignalbirdApp = class {
|
|
|
57
57
|
bootstrap() {
|
|
58
58
|
return this.request("POST", "/v1/sdk/bootstrap", { locale: this.config.locale });
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Canlı bağlantı kanalı için imza.
|
|
62
|
+
*
|
|
63
|
+
* Ziyaretçinin oturumu yoktur; hangi kanalı dinleyebileceğine SUNUCU karar
|
|
64
|
+
* verir ve yalnız kendi `visitor.<id>` kanalını imzalar. Soket servisi
|
|
65
|
+
* kimseyi tanımaz, yalnız imzayı doğrular.
|
|
66
|
+
*/
|
|
67
|
+
socketAuth(socketId, channel) {
|
|
68
|
+
return this.request("POST", "/v1/sdk/chat/socket/auth", { socket_id: socketId, channel });
|
|
69
|
+
}
|
|
60
70
|
/**
|
|
61
71
|
* Ziyaretçi oturumu açar ya da mevcut olanı günceller.
|
|
62
72
|
*
|
|
@@ -298,9 +308,163 @@ function buildQuery(query) {
|
|
|
298
308
|
return encoded ? `?${encoded}` : "";
|
|
299
309
|
}
|
|
300
310
|
|
|
311
|
+
// src/shared/socket.ts
|
|
312
|
+
var BACKOFF = [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
313
|
+
var Socket = class {
|
|
314
|
+
constructor(config, auth, onEvent, onState, log = () => {
|
|
315
|
+
}) {
|
|
316
|
+
this.config = config;
|
|
317
|
+
this.auth = auth;
|
|
318
|
+
this.onEvent = onEvent;
|
|
319
|
+
this.onState = onState;
|
|
320
|
+
this.log = log;
|
|
321
|
+
this.ws = null;
|
|
322
|
+
this.sid = null;
|
|
323
|
+
this.attempt = 0;
|
|
324
|
+
this.closed = false;
|
|
325
|
+
this.timer = null;
|
|
326
|
+
this.pending = /* @__PURE__ */ new Set();
|
|
327
|
+
this.joined = /* @__PURE__ */ new Set();
|
|
328
|
+
}
|
|
329
|
+
get connected() {
|
|
330
|
+
return this.ws?.readyState === WebSocket.OPEN && this.sid !== null;
|
|
331
|
+
}
|
|
332
|
+
connect() {
|
|
333
|
+
if (!this.config.enabled || !this.config.url) return;
|
|
334
|
+
if (typeof WebSocket === "undefined") return;
|
|
335
|
+
if (this.ws) return;
|
|
336
|
+
this.closed = false;
|
|
337
|
+
const base = this.config.url.replace(/^http/, "ws").replace(/\/$/, "");
|
|
338
|
+
const url = `${base}/socket.io/?EIO=4&transport=websocket`;
|
|
339
|
+
try {
|
|
340
|
+
this.ws = new WebSocket(url);
|
|
341
|
+
} catch (e) {
|
|
342
|
+
this.log("socket open failed", e);
|
|
343
|
+
this.retry();
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
this.ws.onmessage = (ev) => this.handle(String(ev.data));
|
|
347
|
+
this.ws.onclose = () => this.retry();
|
|
348
|
+
this.ws.onerror = () => {
|
|
349
|
+
this.log("socket error");
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
/** Kanala abone ol. Bağlantı yoksa kuyruğa alınır, kurulunca gönderilir. */
|
|
353
|
+
subscribe(channel) {
|
|
354
|
+
if (this.joined.has(channel) || this.pending.has(channel)) return;
|
|
355
|
+
this.pending.add(channel);
|
|
356
|
+
if (this.connected) void this.flush();
|
|
357
|
+
}
|
|
358
|
+
close() {
|
|
359
|
+
this.closed = true;
|
|
360
|
+
if (this.timer) clearTimeout(this.timer);
|
|
361
|
+
this.timer = null;
|
|
362
|
+
this.joined.clear();
|
|
363
|
+
try {
|
|
364
|
+
this.ws?.close();
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
this.ws = null;
|
|
368
|
+
this.sid = null;
|
|
369
|
+
}
|
|
370
|
+
// ── Protokol ─────────────────────────────────────────────────────────
|
|
371
|
+
handle(frame) {
|
|
372
|
+
const type = frame[0];
|
|
373
|
+
if (type === "0") {
|
|
374
|
+
this.send("40");
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (type === "2") {
|
|
378
|
+
this.send("3");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (type !== "4") return;
|
|
382
|
+
const sub = frame[1];
|
|
383
|
+
const body = frame.slice(2);
|
|
384
|
+
if (sub === "0") {
|
|
385
|
+
try {
|
|
386
|
+
this.sid = String(JSON.parse(body || "{}").sid || "");
|
|
387
|
+
} catch {
|
|
388
|
+
this.sid = "";
|
|
389
|
+
}
|
|
390
|
+
this.attempt = 0;
|
|
391
|
+
this.onState(true);
|
|
392
|
+
void this.flush();
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (sub === "2") {
|
|
396
|
+
let parsed;
|
|
397
|
+
try {
|
|
398
|
+
parsed = JSON.parse(body);
|
|
399
|
+
} catch {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (!Array.isArray(parsed)) return;
|
|
403
|
+
const [name, data] = parsed;
|
|
404
|
+
if (typeof name === "string" && name.startsWith("chat.")) {
|
|
405
|
+
this.onEvent({ name, data: data ?? {} });
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Bekleyen kanallar için imza al ve `subscribe` yolla.
|
|
411
|
+
*
|
|
412
|
+
* İmza her BAĞLANTIDA yeniden alınır: `socket_id`e bağlı ve zaman damgalı.
|
|
413
|
+
* Saklamak, ikinci bağlantıda sessizce reddedilmek demekti.
|
|
414
|
+
*/
|
|
415
|
+
async flush() {
|
|
416
|
+
const sid = this.sid;
|
|
417
|
+
if (!sid) return;
|
|
418
|
+
for (const channel of Array.from(this.pending)) {
|
|
419
|
+
try {
|
|
420
|
+
const signed = await this.auth(sid, channel);
|
|
421
|
+
if (!signed) {
|
|
422
|
+
this.pending.delete(channel);
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
this.emit("subscribe", { channel, auth: signed.auth, at: signed.at });
|
|
426
|
+
this.pending.delete(channel);
|
|
427
|
+
this.joined.add(channel);
|
|
428
|
+
} catch (e) {
|
|
429
|
+
this.log("subscribe failed", channel, e);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Olay yolla — ACK İSTEMEDEN.
|
|
435
|
+
*
|
|
436
|
+
* `subscribe`in sonucunu beklemek bir tur daha protokol yönetmek demekti;
|
|
437
|
+
* oysa sonucu zaten davranıştan görüyoruz: imza tutmadıysa yayın gelmez ve
|
|
438
|
+
* polling merdiveni işini yapmaya devam eder.
|
|
439
|
+
*/
|
|
440
|
+
emit(event, payload) {
|
|
441
|
+
this.send(`42${JSON.stringify([event, payload])}`);
|
|
442
|
+
}
|
|
443
|
+
send(frame) {
|
|
444
|
+
if (this.ws?.readyState !== WebSocket.OPEN) return;
|
|
445
|
+
try {
|
|
446
|
+
this.ws.send(frame);
|
|
447
|
+
} catch {
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
retry() {
|
|
451
|
+
this.ws = null;
|
|
452
|
+
this.sid = null;
|
|
453
|
+
this.joined.forEach((c) => this.pending.add(c));
|
|
454
|
+
this.joined.clear();
|
|
455
|
+
this.onState(false);
|
|
456
|
+
if (this.closed) return;
|
|
457
|
+
const delay = BACKOFF[Math.min(this.attempt, BACKOFF.length - 1)];
|
|
458
|
+
this.attempt++;
|
|
459
|
+
if (this.timer) clearTimeout(this.timer);
|
|
460
|
+
this.timer = setTimeout(() => this.connect(), delay);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
301
464
|
// src/app/session.ts
|
|
302
465
|
var IDLE_LADDER = [2e4, 2e4, 2e4, 6e4, 6e4, 18e4];
|
|
303
466
|
var ACTIVE_INTERVAL = 3e3;
|
|
467
|
+
var ACTIVE_LIVE_INTERVAL = 45e3;
|
|
304
468
|
var ChatSession = class {
|
|
305
469
|
constructor(app, options = {}) {
|
|
306
470
|
this.app = app;
|
|
@@ -314,13 +478,24 @@ var ChatSession = class {
|
|
|
314
478
|
messages: [],
|
|
315
479
|
unread: 0,
|
|
316
480
|
agentTyping: false,
|
|
317
|
-
withinHours: true
|
|
481
|
+
withinHours: true,
|
|
482
|
+
settings: null
|
|
318
483
|
};
|
|
319
484
|
this.listeners = /* @__PURE__ */ new Set();
|
|
320
485
|
this.timer = null;
|
|
321
486
|
this.step = 0;
|
|
322
487
|
this.stopped = false;
|
|
323
488
|
this.polling = false;
|
|
489
|
+
this.socket = null;
|
|
490
|
+
this.live = false;
|
|
491
|
+
/**
|
|
492
|
+
* Bir sonraki `refresh()` İMLEÇSİZ olsun mu.
|
|
493
|
+
*
|
|
494
|
+
* "Var olan mesaj değişti" haberi geldiğinde açılır: imleçli çekim
|
|
495
|
+
* (`?after=<son mesaj>`) o mesajı bir daha getirmez, dolayısıyla çeviri ya
|
|
496
|
+
* da düzenleme ekrana hiç yansımaz.
|
|
497
|
+
*/
|
|
498
|
+
this.forceFull = false;
|
|
324
499
|
this.active = options.active ?? false;
|
|
325
500
|
}
|
|
326
501
|
// ── Abonelik ──────────────────────────────────────────────────────────
|
|
@@ -345,8 +520,10 @@ var ChatSession = class {
|
|
|
345
520
|
this.patch({
|
|
346
521
|
enabled: true,
|
|
347
522
|
withinHours: app.within_hours ?? true,
|
|
348
|
-
topics: boot.data?.topics ?? []
|
|
523
|
+
topics: boot.data?.topics ?? [],
|
|
524
|
+
settings: app.chat ?? null
|
|
349
525
|
});
|
|
526
|
+
this.openSocket(boot.data?.realtime);
|
|
350
527
|
if (await this.app.currentVisitor()) {
|
|
351
528
|
await this.refresh();
|
|
352
529
|
}
|
|
@@ -358,19 +535,42 @@ var ChatSession = class {
|
|
|
358
535
|
if (this.active === active) return;
|
|
359
536
|
this.active = active;
|
|
360
537
|
this.step = 0;
|
|
361
|
-
if (active)
|
|
538
|
+
if (active) {
|
|
539
|
+
if (this.state.conversation?.status === "closed") this.reset();
|
|
540
|
+
void this.refresh();
|
|
541
|
+
}
|
|
362
542
|
this.schedule();
|
|
363
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar.
|
|
546
|
+
*
|
|
547
|
+
* Ekranın "yeni sohbet" düğmesi de bunu çağırır. Sunucuda hiçbir şey
|
|
548
|
+
* silinmez — yalnız bu oturumun neye baktığı değişir.
|
|
549
|
+
*/
|
|
550
|
+
reset() {
|
|
551
|
+
this.patch({ conversation: null, messages: [], unread: 0, agentTyping: false, errorCode: void 0 });
|
|
552
|
+
this.step = 0;
|
|
553
|
+
}
|
|
364
554
|
stop() {
|
|
365
555
|
this.stopped = true;
|
|
366
556
|
if (this.timer) clearTimeout(this.timer);
|
|
367
557
|
this.timer = null;
|
|
558
|
+
this.socket?.close();
|
|
559
|
+
this.socket = null;
|
|
560
|
+
this.live = false;
|
|
561
|
+
}
|
|
562
|
+
/** Canlı bağlantı kurulu mu — arayüz isterse gösterir (zorunlu değil). */
|
|
563
|
+
get isLive() {
|
|
564
|
+
return this.live;
|
|
368
565
|
}
|
|
369
566
|
// ── Eylemler ──────────────────────────────────────────────────────────
|
|
370
567
|
/** Ön-form gönderildiğinde ya da uygulama kullanıcıyı tanıdığında. */
|
|
371
568
|
async openSession(input) {
|
|
372
569
|
const result = await this.app.startSession(input);
|
|
373
|
-
if (result.ok)
|
|
570
|
+
if (result.ok) {
|
|
571
|
+
void this.joinVisitorChannel();
|
|
572
|
+
await this.refresh();
|
|
573
|
+
}
|
|
374
574
|
return result;
|
|
375
575
|
}
|
|
376
576
|
/**
|
|
@@ -397,6 +597,7 @@ var ChatSession = class {
|
|
|
397
597
|
if (!await this.app.currentVisitor()) {
|
|
398
598
|
const session = await this.app.startSession(this.options.visitor ?? {});
|
|
399
599
|
if (!session.ok) return this.markFailed(cid, session);
|
|
600
|
+
void this.joinVisitorChannel();
|
|
400
601
|
}
|
|
401
602
|
const conversation = this.state.conversation;
|
|
402
603
|
const result = conversation ? await this.app.sendMessage(conversation.id, { body: trimmed, client_id: cid, attachments }) : await this.app.startConversation({
|
|
@@ -447,7 +648,7 @@ var ChatSession = class {
|
|
|
447
648
|
const current = this.state.conversation;
|
|
448
649
|
if (!current) {
|
|
449
650
|
const list = await this.app.listConversations();
|
|
450
|
-
const first = list.data?.data
|
|
651
|
+
const first = (list.data?.data ?? []).find((c) => c.status !== "closed");
|
|
451
652
|
if (!first) {
|
|
452
653
|
this.patch({ errorCode: list.ok ? void 0 : list.code });
|
|
453
654
|
return;
|
|
@@ -456,7 +657,8 @@ var ChatSession = class {
|
|
|
456
657
|
this.applyConversation(detail2.data?.conversation ?? first, true);
|
|
457
658
|
return;
|
|
458
659
|
}
|
|
459
|
-
const after = this.lastServerMessageId();
|
|
660
|
+
const after = this.forceFull ? void 0 : this.lastServerMessageId();
|
|
661
|
+
this.forceFull = false;
|
|
460
662
|
const detail = await this.app.getConversation(current.id, after ? { after } : void 0);
|
|
461
663
|
if (!detail.ok || !detail.data?.conversation) {
|
|
462
664
|
this.patch({ errorCode: detail.code });
|
|
@@ -497,10 +699,44 @@ var ChatSession = class {
|
|
|
497
699
|
});
|
|
498
700
|
return result;
|
|
499
701
|
}
|
|
702
|
+
// ── Canlı bağlantı ────────────────────────────────────────────────────
|
|
703
|
+
/**
|
|
704
|
+
* Ziyaretçinin kendi kanalına bağlanır (`visitor.<id>`).
|
|
705
|
+
*
|
|
706
|
+
* Kanal ziyaretçi kimliği kurulduktan SONRA bilinir; ilk mesajla kimlik
|
|
707
|
+
* doğduğunda `refresh()` üzerinden yeniden denenir. Bağlantı kurulamazsa
|
|
708
|
+
* hiçbir şey olmaz: yoklama zaten çalışıyor.
|
|
709
|
+
*/
|
|
710
|
+
openSocket(realtime) {
|
|
711
|
+
if (!realtime?.enabled || !realtime.url || this.socket) return;
|
|
712
|
+
this.socket = new Socket(
|
|
713
|
+
realtime,
|
|
714
|
+
async (socketId, channel) => {
|
|
715
|
+
const result = await this.app.socketAuth(socketId, channel);
|
|
716
|
+
return result.ok && result.data ? result.data : null;
|
|
717
|
+
},
|
|
718
|
+
(event) => {
|
|
719
|
+
if (event.data.updated === true) this.forceFull = true;
|
|
720
|
+
this.step = 0;
|
|
721
|
+
void this.refresh();
|
|
722
|
+
},
|
|
723
|
+
(connected) => {
|
|
724
|
+
this.live = connected;
|
|
725
|
+
this.schedule();
|
|
726
|
+
}
|
|
727
|
+
);
|
|
728
|
+
this.socket.connect();
|
|
729
|
+
void this.joinVisitorChannel();
|
|
730
|
+
}
|
|
731
|
+
/** Ziyaretçi kimliği varsa kendi kanalına katılır; yoksa sessizce döner. */
|
|
732
|
+
async joinVisitorChannel() {
|
|
733
|
+
const visitor = await this.app.currentVisitor();
|
|
734
|
+
if (visitor?.id) this.socket?.subscribe(`visitor.${visitor.id}`);
|
|
735
|
+
}
|
|
500
736
|
schedule() {
|
|
501
737
|
if (this.timer) clearTimeout(this.timer);
|
|
502
738
|
if (this.stopped) return;
|
|
503
|
-
const delay = this.active ? ACTIVE_INTERVAL : IDLE_LADDER[Math.min(this.step, IDLE_LADDER.length - 1)];
|
|
739
|
+
const delay = this.active ? this.live ? ACTIVE_LIVE_INTERVAL : ACTIVE_INTERVAL : this.live ? IDLE_LADDER[IDLE_LADDER.length - 1] : IDLE_LADDER[Math.min(this.step, IDLE_LADDER.length - 1)];
|
|
504
740
|
this.timer = setTimeout(() => void this.tick(), delay);
|
|
505
741
|
}
|
|
506
742
|
async tick() {
|
|
@@ -590,6 +826,14 @@ var SignalbirdService = class {
|
|
|
590
826
|
openSession(input) {
|
|
591
827
|
return this.session.openSession(input);
|
|
592
828
|
}
|
|
829
|
+
/**
|
|
830
|
+
* Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar (29 Ağu 2026).
|
|
831
|
+
* Kapanmış sohbet geri açılmadığı için arayüzün "yeni sohbet" düğmesine
|
|
832
|
+
* bağlanacak bir eyleme ihtiyacı var.
|
|
833
|
+
*/
|
|
834
|
+
resetConversation() {
|
|
835
|
+
this.session.reset();
|
|
836
|
+
}
|
|
593
837
|
identify(input) {
|
|
594
838
|
return this.client.identify(input);
|
|
595
839
|
}
|