@kshema/communications-client 0.1.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/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@kshema/communications-client",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "dependencies": {
7
+ "@azure/communication-common": "^2.5.0",
8
+ "@react-native-async-storage/async-storage": "^3.1.1",
9
+ "axios": "^1.20.0",
10
+ "react-native-permissions": "^5.6.2",
11
+ "react-native-webrtc": "^124.0.8",
12
+ "socket.io-client": "^4.8.3"
13
+ },
14
+ "peerDependencies": {
15
+ "react": ">=18.0.0",
16
+ "react-native": ">=0.76.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/react": "^19.3.0",
20
+ "@types/react-native": "^0.72.8",
21
+ "typescript": "^5.9.2"
22
+ }
23
+ }
@@ -0,0 +1,82 @@
1
+ import AsyncStorage from
2
+ "@react-native-async-storage/async-storage";
3
+
4
+ import {
5
+ AcsEvents,
6
+ SessionData,
7
+ } from "./types";
8
+
9
+ import {
10
+ eventEmitter,
11
+ } from "./EventEmitter";
12
+
13
+ const STORAGE_KEY =
14
+ "@acs_session";
15
+
16
+ class AcsSessionManager {
17
+
18
+ async save(
19
+ session: SessionData
20
+ ) {
21
+
22
+ await AsyncStorage.setItem(
23
+ STORAGE_KEY,
24
+ JSON.stringify(session)
25
+ );
26
+
27
+ eventEmitter.emit(
28
+ AcsEvents.SESSION_CREATED,
29
+ session
30
+ );
31
+ }
32
+
33
+ async get() {
34
+
35
+ const value =
36
+ await AsyncStorage.getItem(
37
+ STORAGE_KEY
38
+ );
39
+
40
+ if (!value) {
41
+ return null;
42
+ }
43
+
44
+ return JSON.parse(
45
+ value
46
+ ) as SessionData;
47
+ }
48
+
49
+ async clear() {
50
+
51
+ await AsyncStorage.removeItem(
52
+ STORAGE_KEY
53
+ );
54
+
55
+ eventEmitter.emit(
56
+ AcsEvents.SESSION_CLEARED
57
+ );
58
+ }
59
+
60
+ async isExpired() {
61
+
62
+ const session =
63
+ await this.get();
64
+
65
+ if (!session) {
66
+ return true;
67
+ }
68
+
69
+ const now =
70
+ new Date().getTime();
71
+
72
+ const expiry =
73
+ new Date(
74
+ session.expiresOn
75
+ ).getTime();
76
+
77
+ return now >= expiry;
78
+ }
79
+ }
80
+
81
+ export const sessionManager =
82
+ new AcsSessionManager();
@@ -0,0 +1,236 @@
1
+ import {
2
+ AcsConfig,
3
+ AcsEvents,
4
+ } from "./types";
5
+
6
+ import {
7
+ sessionManager,
8
+ } from "./AcsSessionManager";
9
+
10
+ import {
11
+ eventEmitter,
12
+ } from "./EventEmitter";
13
+
14
+ import {
15
+ TokenService,
16
+ } from "../services/TokenService";
17
+
18
+ import {
19
+ PresenceService,
20
+ } from "../services/PresenceService";
21
+
22
+ import {
23
+ CallingService,
24
+ } from "../services/CallingService";
25
+
26
+ import {
27
+ NotificationService,
28
+ } from "../services/NotificationService";
29
+
30
+ import {
31
+ RecordingService,
32
+ } from "../services/RecordingService";
33
+
34
+ import {
35
+ RemoteControlService,
36
+ } from "../services/RemoteControlService";
37
+
38
+ import {
39
+ socketManager,
40
+ } from "../sockets/socket";
41
+
42
+ export class AcsSupport {
43
+
44
+ private tokenService: TokenService;
45
+
46
+ public presence!: PresenceService;
47
+
48
+ public calling!: CallingService;
49
+
50
+ public notifications!: NotificationService;
51
+
52
+ public recordings!: RecordingService;
53
+
54
+ public remoteControl!: RemoteControlService;
55
+
56
+ constructor(
57
+ private config: AcsConfig
58
+ ) {
59
+ this.tokenService =
60
+ new TokenService(config);
61
+ }
62
+
63
+ async initialize() {
64
+
65
+ let session =
66
+ await sessionManager.get();
67
+
68
+ if (!session) {
69
+
70
+ const token =
71
+ await this.tokenService
72
+ .generateToken();
73
+
74
+ session = {
75
+ acsUserId:
76
+ token.acsUserId,
77
+
78
+ acsToken:
79
+ token.token,
80
+
81
+ expiresOn:
82
+ token.expiresOn,
83
+
84
+ userId:
85
+ token.userId,
86
+ };
87
+
88
+ await sessionManager.save(
89
+ session
90
+ );
91
+
92
+ eventEmitter.emit(
93
+ AcsEvents.SESSION_CREATED,
94
+ session
95
+ );
96
+ }
97
+
98
+ const expired =
99
+ await sessionManager.isExpired();
100
+
101
+ if (expired) {
102
+
103
+ eventEmitter.emit(
104
+ AcsEvents.SESSION_EXPIRED
105
+ );
106
+
107
+ const token =
108
+ await this.tokenService
109
+ .generateToken();
110
+
111
+ session = {
112
+ acsUserId:
113
+ token.acsUserId,
114
+
115
+ acsToken:
116
+ token.token,
117
+
118
+ expiresOn:
119
+ token.expiresOn,
120
+
121
+ userId:
122
+ token.userId,
123
+ };
124
+
125
+ await sessionManager.save(
126
+ session
127
+ );
128
+
129
+ eventEmitter.emit(
130
+ AcsEvents.TOKEN_REFRESHED,
131
+ session
132
+ );
133
+ }
134
+
135
+ /**
136
+ * Initialize Services
137
+ */
138
+
139
+ this.presence =
140
+ new PresenceService(
141
+ this.config.apiBaseUrl,
142
+ this.config.jwtToken
143
+ );
144
+
145
+ this.calling =
146
+ new CallingService(
147
+ this.config.apiBaseUrl,
148
+ this.config.jwtToken
149
+ );
150
+
151
+ this.notifications =
152
+ new NotificationService();
153
+
154
+ this.recordings =
155
+ new RecordingService(
156
+ this.config.apiBaseUrl,
157
+ this.config.jwtToken
158
+ );
159
+
160
+ this.remoteControl =
161
+ new RemoteControlService();
162
+
163
+ /**
164
+ * Connect Socket.IO
165
+ */
166
+
167
+ if (this.config.socketUrl) {
168
+
169
+ socketManager.connect(
170
+ this.config.socketUrl,
171
+ this.config.jwtToken
172
+ );
173
+ }
174
+
175
+ /**
176
+ * Start Heartbeat
177
+ */
178
+
179
+ this.startHeartbeat();
180
+
181
+ return session;
182
+ }
183
+
184
+ private startHeartbeat() {
185
+
186
+ setInterval(async () => {
187
+
188
+ try {
189
+
190
+ await this.presence.heartbeat();
191
+
192
+ } catch (error) {
193
+
194
+ console.error(
195
+ "Presence heartbeat failed",
196
+ error
197
+ );
198
+ }
199
+
200
+ }, 30000);
201
+ }
202
+
203
+ async getSession() {
204
+
205
+ return sessionManager.get();
206
+ }
207
+
208
+ async logout() {
209
+
210
+ socketManager.disconnect();
211
+
212
+ await sessionManager.clear();
213
+ }
214
+
215
+ on(
216
+ event: string,
217
+ callback: (...args: any[]) => void
218
+ ) {
219
+
220
+ eventEmitter.on(
221
+ event,
222
+ callback
223
+ );
224
+ }
225
+
226
+ off(
227
+ event: string,
228
+ callback: (...args: any[]) => void
229
+ ) {
230
+
231
+ eventEmitter.off(
232
+ event,
233
+ callback
234
+ );
235
+ }
236
+ }
@@ -0,0 +1,49 @@
1
+ type EventCallback = (
2
+ payload?: any
3
+ ) => void;
4
+
5
+ class EventEmitter {
6
+ private listeners:
7
+ Record<string, EventCallback[]> = {};
8
+
9
+ on(
10
+ event: string,
11
+ callback: EventCallback
12
+ ) {
13
+ if (!this.listeners[event]) {
14
+ this.listeners[event] = [];
15
+ }
16
+
17
+ this.listeners[event].push(
18
+ callback
19
+ );
20
+ }
21
+
22
+ emit(
23
+ event: string,
24
+ payload?: any
25
+ ) {
26
+ const callbacks =
27
+ this.listeners[event] || [];
28
+
29
+ callbacks.forEach(cb =>
30
+ cb(payload)
31
+ );
32
+ }
33
+
34
+ off(
35
+ event: string,
36
+ callback: EventCallback
37
+ ) {
38
+ const callbacks =
39
+ this.listeners[event] || [];
40
+
41
+ this.listeners[event] =
42
+ callbacks.filter(
43
+ listener => listener !== callback
44
+ );
45
+ }
46
+ }
47
+
48
+ export const eventEmitter =
49
+ new EventEmitter();
@@ -0,0 +1,44 @@
1
+ export interface AcsTokenResponse {
2
+ source: "new" | "cached" | "refreshed";
3
+
4
+ userId: string;
5
+
6
+ username?: string;
7
+
8
+ displayName?: string;
9
+
10
+ acsUserId: string;
11
+
12
+ token: string;
13
+
14
+ expiresOn: string;
15
+ }
16
+
17
+ export interface AcsConfig {
18
+
19
+ apiBaseUrl: string;
20
+
21
+ jwtToken: string;
22
+
23
+ socketUrl?: string;
24
+ }
25
+
26
+ export interface SessionData {
27
+ acsUserId: string;
28
+
29
+ acsToken: string;
30
+
31
+ expiresOn: string;
32
+
33
+ userId: string;
34
+ }
35
+
36
+ export enum AcsEvents {
37
+ TOKEN_REFRESHED = "TOKEN_REFRESHED",
38
+
39
+ SESSION_CREATED = "SESSION_CREATED",
40
+
41
+ SESSION_EXPIRED = "SESSION_EXPIRED",
42
+
43
+ SESSION_CLEARED = "SESSION_CLEARED",
44
+ }
@@ -0,0 +1,57 @@
1
+ import { useState } from "react";
2
+
3
+ export const useCall = (
4
+ support: any
5
+ ) => {
6
+
7
+ const [call,
8
+ setCall] =
9
+ useState<any>(null);
10
+
11
+ const createCall =
12
+ async () => {
13
+
14
+ const response =
15
+ await support
16
+ .calling
17
+ .createCall();
18
+
19
+ setCall(
20
+ response.data.call
21
+ );
22
+
23
+ return response;
24
+ };
25
+
26
+ const acceptCall =
27
+ async (callId: string) => {
28
+
29
+ return support
30
+ .calling
31
+ .acceptCall(callId);
32
+ };
33
+
34
+ const rejectCall =
35
+ async (callId: string) => {
36
+
37
+ return support
38
+ .calling
39
+ .rejectCall(callId);
40
+ };
41
+
42
+ const endCall =
43
+ async (callId: string) => {
44
+
45
+ return support
46
+ .calling
47
+ .endCall(callId);
48
+ };
49
+
50
+ return {
51
+ call,
52
+ createCall,
53
+ acceptCall,
54
+ rejectCall,
55
+ endCall,
56
+ };
57
+ };
@@ -0,0 +1,29 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ export const usePresence = (
4
+ support: any
5
+ ) => {
6
+ const [agents, setAgents] =
7
+ useState([]);
8
+
9
+ const loadAgents =
10
+ async () => {
11
+ const response =
12
+ await support
13
+ .presence
14
+ .getAvailableAgents();
15
+
16
+ setAgents(
17
+ response.data || []
18
+ );
19
+ };
20
+
21
+ useEffect(() => {
22
+ loadAgents();
23
+ }, []);
24
+
25
+ return {
26
+ agents,
27
+ refresh: loadAgents,
28
+ };
29
+ };
@@ -0,0 +1,38 @@
1
+ import { useEffect } from "react";
2
+
3
+ export const useSocket = (
4
+ support: any,
5
+ handlers: {
6
+ onIncomingCall?: (
7
+ call: any
8
+ ) => void;
9
+
10
+ onCallAccepted?: (
11
+ call: any
12
+ ) => void;
13
+
14
+ onCallEnded?: (
15
+ call: any
16
+ ) => void;
17
+ }
18
+ ) => {
19
+
20
+ useEffect(() => {
21
+
22
+ support.notifications
23
+ .listenIncomingCall(
24
+ handlers.onIncomingCall
25
+ );
26
+
27
+ support.notifications
28
+ .listenCallAccepted(
29
+ handlers.onCallAccepted
30
+ );
31
+
32
+ support.notifications
33
+ .listenCallEnded(
34
+ handlers.onCallEnded
35
+ );
36
+
37
+ }, []);
38
+ };
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export * from "./core/AcsSupport";
2
+ export * from "./core/AcsSessionManager";
3
+ export * from "./core/EventEmitter";
4
+ export * from "./core/types";
5
+
6
+ export * from "./services/PresenceService";
7
+ export * from "./services/CallingService";
8
+ export * from "./services/NotificationService";
9
+ export * from "./services/RecordingService";
10
+ export * from "./services/RemoteControlService";
11
+
12
+ export * from "./hooks/useCall";
13
+ export * from "./hooks/usePresence";
14
+ export * from "./hooks/useSocket";
15
+
16
+ export * from "./screens/IncomingCallScreen";
17
+ export * from "./screens/ActiveCallScreen";
18
+ export * from "./screens/ScreenShareScreen";
19
+ export * from "./screens/CallHistoryScreen";
@@ -0,0 +1,34 @@
1
+ import { NativeModules } from "react-native";
2
+
3
+ export interface NativeAcsModule {
4
+ initialize(token: string): Promise<void>;
5
+
6
+ joinCall(groupId: string): Promise<void>;
7
+
8
+ leaveCall(): Promise<void>;
9
+
10
+ mute(): Promise<void>;
11
+
12
+ unmute(): Promise<void>;
13
+
14
+ startVideo(): Promise<void>;
15
+
16
+ stopVideo(): Promise<void>;
17
+
18
+ startScreenShare(): Promise<void>;
19
+
20
+ stopScreenShare(): Promise<void>;
21
+
22
+ startRecording(): Promise<void>;
23
+
24
+ stopRecording(): Promise<void>;
25
+
26
+ requestRemoteControl(): Promise<void>;
27
+
28
+ approveRemoteControl(): Promise<void>;
29
+
30
+ denyRemoteControl(): Promise<void>;
31
+ }
32
+
33
+ export const NativeAcsBridge =
34
+ NativeModules.KshemaAcsSupport as NativeAcsModule;
@@ -0,0 +1,74 @@
1
+ import { NativeAcsBridge }
2
+ from "./NativeAcsBridge";
3
+
4
+ export class RemoteControlSession {
5
+
6
+ async requestControl() {
7
+
8
+ await NativeAcsBridge.requestRemoteControl();
9
+ }
10
+
11
+ async approveControl() {
12
+
13
+ await NativeAcsBridge.approveRemoteControl();
14
+ }
15
+
16
+ async denyControl() {
17
+
18
+ await NativeAcsBridge.denyRemoteControl();
19
+ }
20
+
21
+ pointerMove(
22
+ x: number,
23
+ y: number
24
+ ) {
25
+
26
+ console.log(
27
+ "POINTER_MOVE",
28
+ x,
29
+ y
30
+ );
31
+ }
32
+
33
+ click(
34
+ x: number,
35
+ y: number
36
+ ) {
37
+
38
+ console.log(
39
+ "CLICK",
40
+ x,
41
+ y
42
+ );
43
+ }
44
+
45
+ scroll(
46
+ delta: number
47
+ ) {
48
+
49
+ console.log(
50
+ "SCROLL",
51
+ delta
52
+ );
53
+ }
54
+
55
+ keyboardInput(
56
+ text: string
57
+ ) {
58
+
59
+ console.log(
60
+ "KEYBOARD_INPUT",
61
+ text
62
+ );
63
+ }
64
+
65
+ revokeConsent() {
66
+
67
+ console.log(
68
+ "CONSENT_REVOKED"
69
+ );
70
+ }
71
+ }
72
+
73
+ export const remoteControlSession =
74
+ new RemoteControlSession();
@@ -0,0 +1,80 @@
1
+ import { NativeAcsBridge } from "./NativeAcsBridge";
2
+
3
+ export class SupportCallSession {
4
+
5
+ private callId?: string;
6
+
7
+ private groupId?: string;
8
+
9
+ async initialize(
10
+ acsToken: string
11
+ ) {
12
+
13
+ await NativeAcsBridge.initialize(
14
+ acsToken
15
+ );
16
+ }
17
+
18
+ async joinCall(
19
+ groupId: string
20
+ ) {
21
+
22
+ this.groupId = groupId;
23
+
24
+ await NativeAcsBridge.joinCall(
25
+ groupId
26
+ );
27
+ }
28
+
29
+ async leaveCall() {
30
+
31
+ await NativeAcsBridge.leaveCall();
32
+ }
33
+
34
+ async mute() {
35
+
36
+ await NativeAcsBridge.mute();
37
+ }
38
+
39
+ async unmute() {
40
+
41
+ await NativeAcsBridge.unmute();
42
+ }
43
+
44
+ async startVideo() {
45
+
46
+ await NativeAcsBridge.startVideo();
47
+ }
48
+
49
+ async stopVideo() {
50
+
51
+ await NativeAcsBridge.stopVideo();
52
+ }
53
+
54
+ async startScreenShare() {
55
+
56
+ await NativeAcsBridge.startScreenShare();
57
+ }
58
+
59
+ async stopScreenShare() {
60
+
61
+ await NativeAcsBridge.stopScreenShare();
62
+ }
63
+
64
+ async startRecording() {
65
+
66
+ await NativeAcsBridge.startRecording();
67
+ }
68
+
69
+ async stopRecording() {
70
+
71
+ await NativeAcsBridge.stopRecording();
72
+ }
73
+
74
+ getGroupId() {
75
+ return this.groupId;
76
+ }
77
+ }
78
+
79
+ export const supportCallSession =
80
+ new SupportCallSession();
@@ -0,0 +1,54 @@
1
+ import React from "react";
2
+
3
+ import {
4
+ View,
5
+ Button,
6
+ } from "react-native";
7
+
8
+ export default function
9
+ ActiveCallScreen({
10
+ onMute,
11
+ onUnmute,
12
+ onEnd,
13
+ onVideoOn,
14
+ onVideoOff,
15
+ onShareScreen,
16
+ }: any) {
17
+
18
+ return (
19
+ <View>
20
+
21
+ <Button
22
+ title="Mute"
23
+ onPress={onMute}
24
+ />
25
+
26
+ <Button
27
+ title="Unmute"
28
+ onPress={onUnmute}
29
+ />
30
+
31
+ <Button
32
+ title="Video On"
33
+ onPress={onVideoOn}
34
+ />
35
+
36
+ <Button
37
+ title="Video Off"
38
+ onPress={onVideoOff}
39
+ />
40
+
41
+ <Button
42
+ title="Share Screen"
43
+ onPress={
44
+ onShareScreen
45
+ }
46
+ />
47
+
48
+ <Button
49
+ title="End Call"
50
+ onPress={onEnd}
51
+ />
52
+ </View>
53
+ );
54
+ }
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+
3
+ import {
4
+ FlatList,
5
+ Text,
6
+ View,
7
+ } from "react-native";
8
+
9
+ export default function
10
+ CallHistoryScreen({
11
+ calls,
12
+ }: any) {
13
+
14
+ return (
15
+ <FlatList
16
+ data={calls}
17
+ keyExtractor={item =>
18
+ item.id
19
+ }
20
+ renderItem={({
21
+ item,
22
+ }) => (
23
+ <View>
24
+
25
+ <Text>
26
+ {item.customerName}
27
+ </Text>
28
+
29
+ <Text>
30
+ {item.status}
31
+ </Text>
32
+
33
+ <Text>
34
+ {item.createdAt}
35
+ </Text>
36
+
37
+ </View>
38
+ )}
39
+ />
40
+ );
41
+ }
@@ -0,0 +1,51 @@
1
+ import React from "react";
2
+
3
+ import {
4
+ View,
5
+ Text,
6
+ Button,
7
+ } from "react-native";
8
+
9
+ export default function
10
+ IncomingCallScreen({
11
+ call,
12
+ onAccept,
13
+ onReject,
14
+ }: any) {
15
+
16
+ return (
17
+ <View
18
+ style={{
19
+ flex: 1,
20
+ justifyContent:
21
+ "center",
22
+ }}
23
+ >
24
+ <Text>
25
+ Incoming Call
26
+ </Text>
27
+
28
+ <Text>
29
+ {call?.customerName}
30
+ </Text>
31
+
32
+ <Button
33
+ title="Accept"
34
+ onPress={() =>
35
+ onAccept(
36
+ call.id
37
+ )
38
+ }
39
+ />
40
+
41
+ <Button
42
+ title="Reject"
43
+ onPress={() =>
44
+ onReject(
45
+ call.id
46
+ )
47
+ }
48
+ />
49
+ </View>
50
+ );
51
+ }
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+
3
+ import {
4
+ View,
5
+ Text,
6
+ Button,
7
+ } from "react-native";
8
+
9
+ export default function
10
+ ScreenShareScreen({
11
+ onStop,
12
+ }: any) {
13
+
14
+ return (
15
+ <View>
16
+
17
+ <Text>
18
+ Screen Sharing Active
19
+ </Text>
20
+
21
+ <Button
22
+ title="Stop Sharing"
23
+ onPress={onStop}
24
+ />
25
+
26
+ </View>
27
+ );
28
+ }
@@ -0,0 +1,68 @@
1
+ import axios from "axios";
2
+
3
+ export class CallingService {
4
+ constructor(
5
+ private apiBaseUrl: string,
6
+ private jwtToken: string
7
+ ) {}
8
+
9
+ async createCall() {
10
+ const response = await axios.post(
11
+ `${this.apiBaseUrl}/api/v1/calls`,
12
+ {},
13
+ {
14
+ headers: {
15
+ Authorization:
16
+ `Bearer ${this.jwtToken}`,
17
+ },
18
+ }
19
+ );
20
+
21
+ return response.data;
22
+ }
23
+
24
+ async acceptCall(callId: string) {
25
+ const response = await axios.post(
26
+ `${this.apiBaseUrl}/api/v1/calls/${callId}/accept`,
27
+ {},
28
+ {
29
+ headers: {
30
+ Authorization:
31
+ `Bearer ${this.jwtToken}`,
32
+ },
33
+ }
34
+ );
35
+
36
+ return response.data;
37
+ }
38
+
39
+ async rejectCall(callId: string) {
40
+ const response = await axios.post(
41
+ `${this.apiBaseUrl}/api/v1/calls/${callId}/reject`,
42
+ {},
43
+ {
44
+ headers: {
45
+ Authorization:
46
+ `Bearer ${this.jwtToken}`,
47
+ },
48
+ }
49
+ );
50
+
51
+ return response.data;
52
+ }
53
+
54
+ async endCall(callId: string) {
55
+ const response = await axios.post(
56
+ `${this.apiBaseUrl}/api/v1/calls/${callId}/end`,
57
+ {},
58
+ {
59
+ headers: {
60
+ Authorization:
61
+ `Bearer ${this.jwtToken}`,
62
+ },
63
+ }
64
+ );
65
+
66
+ return response.data;
67
+ }
68
+ }
@@ -0,0 +1,58 @@
1
+ import { socketManager } from "../sockets/socket";
2
+
3
+ export class NotificationService {
4
+ listenIncomingCall(
5
+ callback: (payload: any) => void
6
+ ) {
7
+ socketManager
8
+ .getSocket()
9
+ ?.on(
10
+ "INCOMING_CALL",
11
+ callback
12
+ );
13
+ }
14
+
15
+ listenCallAccepted(
16
+ callback: (payload: any) => void
17
+ ) {
18
+ socketManager
19
+ .getSocket()
20
+ ?.on(
21
+ "CALL_ACCEPTED",
22
+ callback
23
+ );
24
+ }
25
+
26
+ listenCallRejected(
27
+ callback: (payload: any) => void
28
+ ) {
29
+ socketManager
30
+ .getSocket()
31
+ ?.on(
32
+ "CALL_REJECTED",
33
+ callback
34
+ );
35
+ }
36
+
37
+ listenCallTaken(
38
+ callback: (payload: any) => void
39
+ ) {
40
+ socketManager
41
+ .getSocket()
42
+ ?.on(
43
+ "CALL_TAKEN",
44
+ callback
45
+ );
46
+ }
47
+
48
+ listenCallEnded(
49
+ callback: (payload: any) => void
50
+ ) {
51
+ socketManager
52
+ .getSocket()
53
+ ?.on(
54
+ "CALL_ENDED",
55
+ callback
56
+ );
57
+ }
58
+ }
@@ -0,0 +1,58 @@
1
+ import axios from "axios";
2
+
3
+ export class PresenceService {
4
+ constructor(
5
+ private apiBaseUrl: string,
6
+ private jwtToken: string
7
+ ) {}
8
+
9
+ async heartbeat() {
10
+ const response = await axios.post(
11
+ `${this.apiBaseUrl}/api/v1/presence/heartbeat`,
12
+ {},
13
+ {
14
+ headers: {
15
+ Authorization:
16
+ `Bearer ${this.jwtToken}`,
17
+ },
18
+ }
19
+ );
20
+
21
+ return response.data;
22
+ }
23
+
24
+ async updateStatus(
25
+ status:
26
+ | "Available"
27
+ | "Busy"
28
+ | "Away"
29
+ | "Offline"
30
+ ) {
31
+ const response = await axios.put(
32
+ `${this.apiBaseUrl}/api/v1/presence/status`,
33
+ { status },
34
+ {
35
+ headers: {
36
+ Authorization:
37
+ `Bearer ${this.jwtToken}`,
38
+ },
39
+ }
40
+ );
41
+
42
+ return response.data;
43
+ }
44
+
45
+ async getAvailableAgents() {
46
+ const response = await axios.get(
47
+ `${this.apiBaseUrl}/api/v1/presence/agents`,
48
+ {
49
+ headers: {
50
+ Authorization:
51
+ `Bearer ${this.jwtToken}`,
52
+ },
53
+ }
54
+ );
55
+
56
+ return response.data;
57
+ }
58
+ }
@@ -0,0 +1,50 @@
1
+ import axios from "axios";
2
+
3
+ export class RecordingService {
4
+ constructor(
5
+ private apiBaseUrl: string,
6
+ private jwtToken: string
7
+ ) {}
8
+
9
+ async startRecording(
10
+ callId: string
11
+ ) {
12
+ return axios.post(
13
+ `${this.apiBaseUrl}/api/v1/recordings/start`,
14
+ { callId },
15
+ {
16
+ headers: {
17
+ Authorization:
18
+ `Bearer ${this.jwtToken}`,
19
+ },
20
+ }
21
+ );
22
+ }
23
+
24
+ async stopRecording(
25
+ recordingId: string
26
+ ) {
27
+ return axios.post(
28
+ `${this.apiBaseUrl}/api/v1/recordings/stop`,
29
+ { recordingId },
30
+ {
31
+ headers: {
32
+ Authorization:
33
+ `Bearer ${this.jwtToken}`,
34
+ },
35
+ }
36
+ );
37
+ }
38
+
39
+ async getHistory() {
40
+ return axios.get(
41
+ `${this.apiBaseUrl}/api/v1/recordings`,
42
+ {
43
+ headers: {
44
+ Authorization:
45
+ `Bearer ${this.jwtToken}`,
46
+ },
47
+ }
48
+ );
49
+ }
50
+ }
@@ -0,0 +1,64 @@
1
+ export class RemoteControlService {
2
+
3
+ requestControl() {
4
+ console.log(
5
+ "CONTROL_REQUEST"
6
+ );
7
+ }
8
+
9
+ approveControl() {
10
+ console.log(
11
+ "CONTROL_APPROVED"
12
+ );
13
+ }
14
+
15
+ denyControl() {
16
+ console.log(
17
+ "CONTROL_DENIED"
18
+ );
19
+ }
20
+
21
+ endControl() {
22
+ console.log(
23
+ "CONTROL_ENDED"
24
+ );
25
+ }
26
+
27
+ pointerMove(
28
+ x: number,
29
+ y: number
30
+ ) {
31
+ console.log(
32
+ "POINTER_MOVE",
33
+ x,
34
+ y
35
+ );
36
+ }
37
+
38
+ click(
39
+ x: number,
40
+ y: number
41
+ ) {
42
+ console.log(
43
+ "CLICK",
44
+ x,
45
+ y
46
+ );
47
+ }
48
+
49
+ scroll(
50
+ delta: number
51
+ ) {
52
+ console.log(
53
+ "SCROLL",
54
+ delta
55
+ );
56
+ }
57
+
58
+ keyInput(text: string) {
59
+ console.log(
60
+ "KEY_INPUT",
61
+ text
62
+ );
63
+ }
64
+ }
@@ -0,0 +1,47 @@
1
+ import axios from "axios";
2
+
3
+ import {
4
+ AcsConfig,
5
+ AcsTokenResponse,
6
+ } from "../core/types";
7
+
8
+ export class TokenService {
9
+
10
+ constructor(
11
+ private config: AcsConfig
12
+ ) {}
13
+
14
+ async generateToken() {
15
+
16
+ const response =
17
+ await axios.post(
18
+ `${this.config.apiBaseUrl}/api/v1/acs/token`,
19
+ {},
20
+ {
21
+ headers: {
22
+ Authorization:
23
+ `Bearer ${this.config.jwtToken}`,
24
+ },
25
+ }
26
+ );
27
+
28
+ return response.data
29
+ .data as AcsTokenResponse;
30
+ }
31
+
32
+ async checkTokenStatus() {
33
+
34
+ const response =
35
+ await axios.get(
36
+ `${this.config.apiBaseUrl}/api/v1/acs/token/status`,
37
+ {
38
+ headers: {
39
+ Authorization:
40
+ `Bearer ${this.config.jwtToken}`,
41
+ },
42
+ }
43
+ );
44
+
45
+ return response.data.data;
46
+ }
47
+ }
@@ -0,0 +1,31 @@
1
+ import { io, Socket } from "socket.io-client";
2
+
3
+ class SocketManager {
4
+ private socket: Socket | null = null;
5
+
6
+ connect(url: string, jwtToken: string) {
7
+ if (this.socket?.connected) {
8
+ return this.socket;
9
+ }
10
+
11
+ this.socket = io(url, {
12
+ transports: ["websocket"],
13
+ auth: {
14
+ token: jwtToken,
15
+ },
16
+ });
17
+
18
+ return this.socket;
19
+ }
20
+
21
+ getSocket() {
22
+ return this.socket;
23
+ }
24
+
25
+ disconnect() {
26
+ this.socket?.disconnect();
27
+ }
28
+ }
29
+
30
+ export const socketManager =
31
+ new SocketManager();