123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- /**
- * @description 天润呼叫中心对接接口
- */
- import { Fn, isClient, isWorker, MaybeRefOrGetter, toRef, tryOnScopeDispose, useIntervalFn } from '@vueuse/shared/index';
- import { ref, Ref, watch } from 'vue';
- import { WebSocketStatus } from '@/hooks/useWebsocket';
- import { ElMessage } from 'element-plus';
- const DEFAULT_PING_MESSAGE = 'ping';
- let UUID = '73836387-0000-0000-0000-0000-0000000000';
- let _extn = ''; // 分机号
- function resolveNestedOptions<T>(options: T | true): T {
- if (options === true) return {} as T;
- return options;
- }
- export function olaFn(url: MaybeRefOrGetter<string | URL | undefined>, options: any) {
- const {
- onConnected,
- onDisconnected,
- onError,
- onMessage,
- immediate = true,
- autoClose = true,
- protocols = [],
- username = '',
- password = '',
- } = options;
- const data: Ref<any | null> = ref(null);
- const status = ref<WebSocketStatus>('CLOSED');
- const wsRef = ref<WebSocket | undefined>();
- const urlRef = toRef(url);
- let heartbeatPause: Fn | undefined;
- let heartbeatResume: Fn | undefined;
- let explicitlyClosed = false;
- let retried = 0;
- let bufferedData: (string | ArrayBuffer | Blob)[] = [];
- let pongTimeoutWait: ReturnType<typeof setTimeout> | undefined;
- const _sendBuffer = () => {
- if (bufferedData.length && wsRef.value && status.value === 'OPEN') {
- for (const buffer of bufferedData) wsRef.value.send(buffer);
- bufferedData = [];
- }
- };
- const resetHeartbeat = () => {
- clearTimeout(pongTimeoutWait);
- pongTimeoutWait = undefined;
- };
- // Status code 1000 -> Normal Closure https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code
- const close: WebSocket['close'] = (code = 1000, reason) => {
- if (!isClient || !wsRef.value) return;
- explicitlyClosed = true;
- resetHeartbeat();
- heartbeatPause?.();
- wsRef.value.close(code, reason);
- wsRef.value = undefined;
- };
- const send = (data: string | ArrayBuffer | Blob | any, useBuffer = true) => {
- if (!wsRef.value || status.value !== 'OPEN') {
- if (useBuffer) bufferedData.push(data);
- ElMessage.error('请先签入');
- return false;
- }
- _sendBuffer();
- data.uuid = next_uuid();
- const sedMsg = JSON.stringify(data);
- wsRef.value.send(sedMsg);
- // console.log(`${getNowDateTime()}:呼叫中心发送消息:${sedMsg}`);
- return true;
- };
- const merge = (target: { [x: string]: any }, additional: { [x: string]: any; hasOwnProperty: (arg0: string) => any }) => {
- for (const i in additional) {
- if (additional.hasOwnProperty(i)) {
- target[i] = additional[i];
- }
- }
- };
- const next_uuid = () => {
- let u = (parseFloat(UUID.substring(29)) + 1).toString();
- u = u == '2147483647' ? '0' : u;
- while (u.length < 12) {
- u = '0' + u;
- }
- UUID = '73836387-0000-0000-0000-0000-' + u;
- return UUID;
- };
- const auth = (username: any, password: any) => {
- send({ cmd: 'auth', args: { username, password, accept: 'application/json' } });
- };
- const get_agent_state = (extn: any) => {
- send({ action: 'api', cmd: 'get_agent_state', args: { extn } });
- };
- const get_trunk_state = () => {
- send({ action: 'api', cmd: 'get_trunk_state' });
- };
- const login = (queue: any, extn: any, params: any) => {
- const args = { queue, extn };
- merge(args, params);
- _extn = extn;
- return send({ action: 'api', cmd: 'login', args: args });
- };
- const logout = (extn?: string) => {
- const extns = extn || _extn;
- send({ action: 'api', cmd: 'logout', args: { extn: extns } });
- };
- const collect_dtmf = (extn: any, soundfile: any) => {
- send({ action: 'api', cmd: 'collect_dtmf', args: { extn, soundfile } });
- };
- const collect_judge = (extn: any) => {
- send({ action: 'api', cmd: 'collect_judge', args: { extn: extn } });
- };
- const ping = () => {
- return send({ cmd: 'ping' }, false);
- };
- const subscribe = (k: any) => {
- return send({ cmd: 'subscribe', args: { key: k } });
- };
- const unsubscribe = (k: any) => {
- return send({ cmd: 'unsubscribe', args: { key: k } });
- };
- const go_ready = () => {
- send({ action: 'api', cmd: 'go_ready', args: { extn: _extn } });
- };
- const go_ready2 = (extn: any) => {
- send({ action: 'api', cmd: 'go_ready', args: { extn: extn } });
- };
- const go_break = (reason: any) => {
- send({ action: 'api', cmd: 'go_break', args: { extn: _extn, reason: reason } });
- };
- const go_break2 = (extn: any) => {
- send({ action: 'api', cmd: 'go_break', args: { extn: extn, reason: '' } });
- };
- const toggle_ready = () => {
- send({ action: 'api', cmd: 'toggle_ready', args: { extn: _extn } });
- };
- const answer = () => {
- send({ action: 'api', cmd: 'answer', args: { extn: _extn } });
- };
- const hangup = () => {
- send({ action: 'api', cmd: 'hangup_other', args: { extn: _extn } });
- };
- const dial = (dst: any, otherStr?: any, gateway?: any) => {
- send({ action: 'api', cmd: 'dial', args: { extn: _extn, dest: dst, gateway: gateway, otherStr: otherStr } });
- };
- const transfer = (dst: any, src?: any) => {
- let extn = src;
- if (extn == null || typeof extn == 'undefined') {
- extn = _extn;
- }
- send({ action: 'api', cmd: 'transfer', args: { extn: extn, dest: dst } });
- };
- const transfer_uuid = (uuid: any, dst: any) => {
- send({ action: 'api', cmd: 'transfer', args: { channel_uuid: uuid, dest: dst } });
- };
- const monitor = (dst: any, src: any) => {
- let extn = src;
- if (extn == null || typeof extn == 'undefined') {
- extn = _extn;
- }
- send({ action: 'api', cmd: 'monitor', args: { extn: extn, dest: dst } });
- };
- const monitor_uuid = (uuid: any) => {
- send({ action: 'api', cmd: 'monitor', args: { extn: _extn, channel_uuid: uuid } });
- };
- const intercept = (dst: any, src: any, gateway: any) => {
- let extn = src;
- if (extn == null || typeof extn == 'undefined') {
- extn = _extn;
- }
- send({ action: 'api', cmd: 'intercept', args: { extn: extn, dest: dst } });
- };
- const intercept_uuid = (uuid: any) => {
- send({ action: 'api', cmd: 'intercept', args: { extn: _extn, channel_uuid: uuid } });
- };
- const three_way = (dst: any, src: any, gateway?: any) => {
- let extn = src;
- if (extn == null || typeof extn == 'undefined') {
- extn = _extn;
- }
- send({ action: 'api', cmd: 'monitor', args: { extn: extn, dest: dst, three_way: 'true', goip_gateway: gateway } });
- };
- /* hangup the thrid party */
- const exit_three_way = (ext: any) => {
- send({ action: 'api', cmd: 'unmonitor', args: { extn: ext, three_way: 'true' } });
- };
- const three_way_uuid = (uuid: any) => {
- send({ action: 'api', cmd: 'monitor', args: { extn: _extn, channel_uuid: uuid, three_way: 'true' } });
- };
- const unmonitor = (ext: any) => {
- send({ action: 'api', cmd: 'unmonitor', args: { extn: ext } });
- };
- const whisper = (w: any) => {
- // who = "agent" / "caller" / "both" / "none"
- send({ action: 'api', cmd: 'whisper', args: { extn: _extn, who: w } });
- };
- const consult = (dst: any) => {
- send({ action: 'api', cmd: 'consult', args: { extn: _extn, dest: dst } });
- };
- const unconsult = (dst: any) => {
- send({ action: 'api', cmd: 'unconsult', args: { extn: _extn, dest: dst } });
- };
- const consult_to_three_way = (dst: any) => {
- send({ action: 'api', cmd: 'consult_to_three_way', args: { extn: _extn, dest: dst } });
- };
- const hold = () => {
- send({ action: 'api', cmd: 'hold', args: { extn: _extn } });
- };
- const unhold = () => {
- send({ action: 'api', cmd: 'unhold', args: { extn: _extn } });
- };
- const toggle_hold = () => {
- send({ action: 'api', cmd: 'toggle_hold', args: { extn: _extn } });
- };
- const take_call = (channel_uuid: any) => {
- send({ action: 'api', cmd: 'take_call', args: { extn: _extn, channel_uuid: channel_uuid } });
- };
- const next_queue = (uuid: any) => {
- send({ action: 'api', cmd: 'next_queue', args: { extn: _extn, channel_uuid: uuid } });
- };
- const conference = (dst: any) => {
- send({ action: 'api', cmd: 'conference', args: { extn: _extn, dest: dst } });
- };
- const conference_uuid = (uuid: any) => {
- send({ action: 'api', cmd: 'conference', args: { extn: _extn, channel_uuid: uuid } });
- };
- const broadcast = (numbers: any, mute: any) => {
- send({ action: 'api', cmd: 'broadcast', args: { extn: _extn, numbers, mute } });
- };
- /* send chat to a queue or an agent
- to = queue send to queue
- to = queue.agent send to agent
- */
- const chat = (to: any, message: any, content_type: any) => {
- send({ action: 'api', cmd: 'chat', args: { to: to, message: message, content_type: content_type } });
- };
- const message = (from: any, to: any, message: any, content_type: any) => {
- send({ action: 'api', cmd: 'message', args: { from: from, to: to, message: message, content_type: content_type } });
- };
- const alarm = (queue: any, state: any) => {
- send({ action: 'api', cmd: 'alarm', args: { queue, state } });
- };
- /* dispatching apis */
- const dlogin = (ext: any) => {
- send({ action: 'api', cmd: 'dlogin', args: { extn: ext } });
- };
- const dlogout = (ext: any) => {
- send({ action: 'api', cmd: 'dlogout', args: { extn: ext } });
- };
- const inject = (ext: any, uuid: any) => {
- send({ action: 'api', cmd: 'inject', args: { extn: ext, channel_uuid: uuid } });
- };
- const kill = (uuid: any, extn: any) => {
- send({ action: 'api', cmd: 'kill', args: { channel_uuid: uuid, extn: extn } });
- };
- const eavesdrop = (uuid: any) => {
- send({ action: 'api', cmd: 'eavesdrop', args: { channel_uuid: uuid } });
- };
- const conf = (name: any, action: any, member: any) => {
- send({ action: 'api', cmd: 'conf', args: { name: name, action: action, member: member } });
- };
- const answer_all = (ext: any, queue: any) => {
- send({ action: 'api', cmd: 'answer_all', args: { extn: ext, queue: queue } });
- };
- const group_call = (ext: any, queue: any, numbers: any, batch_accept: any) => {
- send({ action: 'api', cmd: 'group_call', args: { extn: ext, queue: queue, numbers: numbers, batch_accept: batch_accept } });
- };
- const sip_gateway = (profile: any, gateway: any, op: any) => {
- send({ action: 'api', cmd: 'sip_gateway', args: { profile: profile, gateway: gateway, op: op } });
- };
- const playback = (filename: any) => {
- send({ action: 'api', cmd: 'playback', args: { extn: _extn, soundfile: filename } });
- };
- const stop_playback = () => {
- send({ action: 'api', cmd: 'stop_playback', args: { extn: _extn } });
- };
- const merge_call = (ext1: any, ext2: any) => {
- send({ action: 'api', cmd: 'merge_call', args: { extn1: ext1, extn2: ext2 } });
- };
- /* common apis*/
- /*phone control api, only yealink support for now*/
- const api_handfree = (ext: any) => {
- send({ action: 'api', cmd: 'api_handfree', args: { extn: ext } });
- };
- const getStatus = () => {
- // @ts-ignore
- return wsRef.value.readyState;
- };
- const startSubscribe = () => {
- subscribe(`ola.agent.${options.username}`);
- subscribe(`ola.caller.${options.username}`);
- get_agent_state(options.username);
- };
- const _init = () => {
- if (explicitlyClosed || typeof urlRef.value === 'undefined') return;
- const ws = new WebSocket(urlRef.value, protocols);
- wsRef.value = ws;
- status.value = 'CONNECTING';
- ws.onopen = () => {
- status.value = 'OPEN';
- heartbeatResume?.();
- _sendBuffer();
- auth(options.username, options.password);
- startSubscribe();
- onConnected?.(ws!);
- };
- ws.onclose = (ev) => {
- status.value = 'CLOSED';
- onDisconnected?.(ev);
- if (!explicitlyClosed && options.autoReconnect) {
- const { retries = -1, delay = 1000, onFailed } = resolveNestedOptions(options.autoReconnect);
- retried += 1;
- // @ts-ignore
- if (retries < 0 || retried < retries) setTimeout(_init, delay);
- else if (typeof retries === 'function' && retries()) setTimeout(_init, delay);
- else onFailed?.();
- }
- };
- ws.onerror = (e) => {
- onError?.(e);
- };
- ws.onmessage = (e: MessageEvent) => {
- if (options.heartbeat) {
- resetHeartbeat();
- const { message = DEFAULT_PING_MESSAGE } = resolveNestedOptions(options.heartbeat);
- if (e.data === message) return;
- }
- data.value = e.data;
- onMessage?.(e.data);
- };
- };
- if (options.heartbeat) {
- const { message = DEFAULT_PING_MESSAGE, interval = 1000, pongTimeout = 1000 } = resolveNestedOptions(options.heartbeat);
- const { pause, resume } = useIntervalFn(
- () => {
- ping();
- if (pongTimeoutWait != null) return;
- pongTimeoutWait = setTimeout(() => {
- // auto-reconnect will be trigger with ws.onclose()
- close();
- explicitlyClosed = false;
- }, pongTimeout);
- },
- interval,
- { immediate: false }
- );
- heartbeatPause = pause;
- heartbeatResume = resume;
- }
- if (autoClose) {
- if (isClient) window.addEventListener('beforeunload', () => close());
- tryOnScopeDispose(close);
- }
- const open = () => {
- if (!isClient && !isWorker) return;
- close();
- explicitlyClosed = false;
- retried = 0;
- _init();
- };
- if (immediate) open();
- watch(urlRef, open);
- return {
- data,
- status,
- close,
- send,
- open,
- ws: wsRef,
- login,
- logout,
- go_ready,
- go_break,
- hangup,
- hold,
- unhold,
- dial,
- exit_three_way,
- transfer,
- monitor,
- collect_dtmf,
- collect_judge,
- get_trunk_state,
- ping,
- unsubscribe,
- go_ready2,
- toggle_ready,
- answer,
- transfer_uuid,
- monitor_uuid,
- intercept,
- intercept_uuid,
- three_way,
- three_way_uuid,
- unmonitor,
- whisper,
- consult,
- unconsult,
- consult_to_three_way,
- toggle_hold,
- take_call,
- next_queue,
- conference,
- conference_uuid,
- broadcast,
- chat,
- message,
- alarm,
- dlogin,
- dlogout,
- inject,
- kill,
- eavesdrop,
- conf,
- answer_all,
- group_call,
- sip_gateway,
- playback,
- stop_playback,
- merge_call,
- api_handfree,
- getStatus,
- username: options.username,
- password: options.password,
- next_uuid,
- };
- }
|