signalbird 1.7.0 → 1.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.
- package/README.md +1 -1
- package/dist/angular.d.mts +6 -0
- package/dist/angular.d.ts +6 -0
- package/dist/angular.js +240 -6
- package/dist/angular.js.map +1 -1
- package/dist/angular.mjs +240 -6
- package/dist/angular.mjs.map +1 -1
- package/dist/app.d.mts +86 -3
- package/dist/app.d.ts +86 -3
- package/dist/app.js +232 -6
- package/dist/app.js.map +1 -1
- package/dist/app.mjs +232 -6
- 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 +239 -7
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +239 -7
- 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 +239 -7
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +239 -7
- 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 +239 -7
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +239 -7
- 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.0")` |
|
|
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,16 @@ 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;
|
|
324
491
|
this.active = options.active ?? false;
|
|
325
492
|
}
|
|
326
493
|
// ── Abonelik ──────────────────────────────────────────────────────────
|
|
@@ -345,8 +512,10 @@ var ChatSession = class {
|
|
|
345
512
|
this.patch({
|
|
346
513
|
enabled: true,
|
|
347
514
|
withinHours: app.within_hours ?? true,
|
|
348
|
-
topics: boot.data?.topics ?? []
|
|
515
|
+
topics: boot.data?.topics ?? [],
|
|
516
|
+
settings: app.chat ?? null
|
|
349
517
|
});
|
|
518
|
+
this.openSocket(boot.data?.realtime);
|
|
350
519
|
if (await this.app.currentVisitor()) {
|
|
351
520
|
await this.refresh();
|
|
352
521
|
}
|
|
@@ -358,19 +527,42 @@ var ChatSession = class {
|
|
|
358
527
|
if (this.active === active) return;
|
|
359
528
|
this.active = active;
|
|
360
529
|
this.step = 0;
|
|
361
|
-
if (active)
|
|
530
|
+
if (active) {
|
|
531
|
+
if (this.state.conversation?.status === "closed") this.reset();
|
|
532
|
+
void this.refresh();
|
|
533
|
+
}
|
|
362
534
|
this.schedule();
|
|
363
535
|
}
|
|
536
|
+
/**
|
|
537
|
+
* Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar.
|
|
538
|
+
*
|
|
539
|
+
* Ekranın "yeni sohbet" düğmesi de bunu çağırır. Sunucuda hiçbir şey
|
|
540
|
+
* silinmez — yalnız bu oturumun neye baktığı değişir.
|
|
541
|
+
*/
|
|
542
|
+
reset() {
|
|
543
|
+
this.patch({ conversation: null, messages: [], unread: 0, agentTyping: false, errorCode: void 0 });
|
|
544
|
+
this.step = 0;
|
|
545
|
+
}
|
|
364
546
|
stop() {
|
|
365
547
|
this.stopped = true;
|
|
366
548
|
if (this.timer) clearTimeout(this.timer);
|
|
367
549
|
this.timer = null;
|
|
550
|
+
this.socket?.close();
|
|
551
|
+
this.socket = null;
|
|
552
|
+
this.live = false;
|
|
553
|
+
}
|
|
554
|
+
/** Canlı bağlantı kurulu mu — arayüz isterse gösterir (zorunlu değil). */
|
|
555
|
+
get isLive() {
|
|
556
|
+
return this.live;
|
|
368
557
|
}
|
|
369
558
|
// ── Eylemler ──────────────────────────────────────────────────────────
|
|
370
559
|
/** Ön-form gönderildiğinde ya da uygulama kullanıcıyı tanıdığında. */
|
|
371
560
|
async openSession(input) {
|
|
372
561
|
const result = await this.app.startSession(input);
|
|
373
|
-
if (result.ok)
|
|
562
|
+
if (result.ok) {
|
|
563
|
+
void this.joinVisitorChannel();
|
|
564
|
+
await this.refresh();
|
|
565
|
+
}
|
|
374
566
|
return result;
|
|
375
567
|
}
|
|
376
568
|
/**
|
|
@@ -397,6 +589,7 @@ var ChatSession = class {
|
|
|
397
589
|
if (!await this.app.currentVisitor()) {
|
|
398
590
|
const session = await this.app.startSession(this.options.visitor ?? {});
|
|
399
591
|
if (!session.ok) return this.markFailed(cid, session);
|
|
592
|
+
void this.joinVisitorChannel();
|
|
400
593
|
}
|
|
401
594
|
const conversation = this.state.conversation;
|
|
402
595
|
const result = conversation ? await this.app.sendMessage(conversation.id, { body: trimmed, client_id: cid, attachments }) : await this.app.startConversation({
|
|
@@ -447,7 +640,7 @@ var ChatSession = class {
|
|
|
447
640
|
const current = this.state.conversation;
|
|
448
641
|
if (!current) {
|
|
449
642
|
const list = await this.app.listConversations();
|
|
450
|
-
const first = list.data?.data
|
|
643
|
+
const first = (list.data?.data ?? []).find((c) => c.status !== "closed");
|
|
451
644
|
if (!first) {
|
|
452
645
|
this.patch({ errorCode: list.ok ? void 0 : list.code });
|
|
453
646
|
return;
|
|
@@ -497,10 +690,43 @@ var ChatSession = class {
|
|
|
497
690
|
});
|
|
498
691
|
return result;
|
|
499
692
|
}
|
|
693
|
+
// ── Canlı bağlantı ────────────────────────────────────────────────────
|
|
694
|
+
/**
|
|
695
|
+
* Ziyaretçinin kendi kanalına bağlanır (`visitor.<id>`).
|
|
696
|
+
*
|
|
697
|
+
* Kanal ziyaretçi kimliği kurulduktan SONRA bilinir; ilk mesajla kimlik
|
|
698
|
+
* doğduğunda `refresh()` üzerinden yeniden denenir. Bağlantı kurulamazsa
|
|
699
|
+
* hiçbir şey olmaz: yoklama zaten çalışıyor.
|
|
700
|
+
*/
|
|
701
|
+
openSocket(realtime) {
|
|
702
|
+
if (!realtime?.enabled || !realtime.url || this.socket) return;
|
|
703
|
+
this.socket = new Socket(
|
|
704
|
+
realtime,
|
|
705
|
+
async (socketId, channel) => {
|
|
706
|
+
const result = await this.app.socketAuth(socketId, channel);
|
|
707
|
+
return result.ok && result.data ? result.data : null;
|
|
708
|
+
},
|
|
709
|
+
() => {
|
|
710
|
+
this.step = 0;
|
|
711
|
+
void this.refresh();
|
|
712
|
+
},
|
|
713
|
+
(connected) => {
|
|
714
|
+
this.live = connected;
|
|
715
|
+
this.schedule();
|
|
716
|
+
}
|
|
717
|
+
);
|
|
718
|
+
this.socket.connect();
|
|
719
|
+
void this.joinVisitorChannel();
|
|
720
|
+
}
|
|
721
|
+
/** Ziyaretçi kimliği varsa kendi kanalına katılır; yoksa sessizce döner. */
|
|
722
|
+
async joinVisitorChannel() {
|
|
723
|
+
const visitor = await this.app.currentVisitor();
|
|
724
|
+
if (visitor?.id) this.socket?.subscribe(`visitor.${visitor.id}`);
|
|
725
|
+
}
|
|
500
726
|
schedule() {
|
|
501
727
|
if (this.timer) clearTimeout(this.timer);
|
|
502
728
|
if (this.stopped) return;
|
|
503
|
-
const delay = this.active ? ACTIVE_INTERVAL : IDLE_LADDER[Math.min(this.step, IDLE_LADDER.length - 1)];
|
|
729
|
+
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
730
|
this.timer = setTimeout(() => void this.tick(), delay);
|
|
505
731
|
}
|
|
506
732
|
async tick() {
|
|
@@ -590,6 +816,14 @@ var SignalbirdService = class {
|
|
|
590
816
|
openSession(input) {
|
|
591
817
|
return this.session.openSession(input);
|
|
592
818
|
}
|
|
819
|
+
/**
|
|
820
|
+
* Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar (29 Ağu 2026).
|
|
821
|
+
* Kapanmış sohbet geri açılmadığı için arayüzün "yeni sohbet" düğmesine
|
|
822
|
+
* bağlanacak bir eyleme ihtiyacı var.
|
|
823
|
+
*/
|
|
824
|
+
resetConversation() {
|
|
825
|
+
this.session.reset();
|
|
826
|
+
}
|
|
593
827
|
identify(input) {
|
|
594
828
|
return this.client.identify(input);
|
|
595
829
|
}
|