Subscribe to queue event executing multiple times within seconds

Hi team,

I am using subscribe queue event to get an event when call got answered in one of my queue. For that I am using below JavascriptSDK.

And I do get events like "waiting in queue, alerting, accepted" events. My use case is when agent accepted the interaction I need to call one of the API called send Digit. But the problem is, this alerting event happening very often like within 1 seconds its giving us almost 5 events. So because of this, our send Digit API is getting hit multiple times. IS there any way we can control this event like only give one event at a time.

becuase of this multiple accepted events within milliseconds, our Send digit API alos excecuting same times. So our callers hearing so many digits.

send

Thanks,

Magu

Hi Magu,

Would you be able to send on your code so I can get a better understanding of what is happening.

Thanks,
Declan

It's not clear what notification topic you're using, so it's hard to suggest why it's being raised multiple times. However, in general, what you see as duplicate notifications may be completely normal. There is not a constraint internally in Genesys Cloud that notification events be unique or non-repeated. Eventing conditions can occur that will result in the same notification payload being sent for each event so they appear duplicated even though the events that triggered them were unique. If you can provide the notification topic you're using, we may be able to shed some additional light on the situation.

To resolve this issue, your app should keep track of its state so it can debounce these events. When your app decides to take action for a particular participant in a conversation, it should remember this so it doesn't do it again for the same participant if another event is received.

Hi tim and Declan,

Thanks for your responses. I somehow managed to handle the duplicate. But my concern is, if multiple conversation request coming in, I don't think this script can handle it. Because based on how I handled it, if more conversation coming at same time, the variables can conflict each other. If you have better way to handle this please suggest me guys.

My Javascript code:

const platformClient = require('purecloud-platform-client-v2');
var WebSocketClient = require('websocket').client;
const client = platformClient.ApiClient.instance;
let apiInstance = new platformClient.UsersApi();
let apiInstanceForArchitect = new platformClient.ArchitectApi();


require('dotenv').config();

// Get client credentials from environment variables
const CLIENT_ID = process.env.GENESYS_CLOUD_CLIENT_ID;
const CLIENT_SECRET = process.env.GENESYS_CLOUD_CLIENT_SECRET;
const ORG_REGION = process.env.GENESYS_CLOUD_REGION; // eg. us_east_1


const environment = platformClient.PureCloudRegionHosts[ORG_REGION];
if (environment) client.setEnvironment(environment);


const websocketClient = new WebSocketClient();

// API instances
const notificationsApi = new platformClient.NotificationsApi();

// Additional configuration variables
const queueId = 'ab9700bf-c33d-459b-8e36-ef2c31ea26f8';
const subscriptionTopic = `v2.routing.queues.${queueId}.conversations`;

// Define the callbacks for the websocket listener
websocketClient.on('connect', connection => {
	var conversationIdForCheck;
	var agentTrue = false;
	console.log('WebSocket client connected and listening...');
	connection.on('message', message => {
		let data = JSON.parse(message.utf8Data);
		let topic = data.topicName;
		let eventBody = data.eventBody;

		if (topic == subscriptionTopic) {
			let conversation = eventBody;
			let customer = conversation.participants
				.filter(p => p.purpose == 'customer')[0];
			let agent = conversation.participants
				.filter(p => p.purpose == 'agent')[0];

			if (agent && agent.connectedTime) {
				console.log("agentTrue", agentTrue);
				if (agent && (agentTrue == false) && !agent.endTime) {
					agentTrue = true;
					conversationIdForCheck = conversation.id;
					console.log(`Agent ${agent.userId} has accepted conversation with ${customer.id}`);
               //getting agent's email using userId. Because I stored Oauth 2 access token in my data table for the specific user. Oauth 2 access token only can be used to send DTMF digits.
					getUser(agent.userId).then(data => {
						getAccessToken(data).then(result => {
							console.log("getAccessToken result", result);
							sendDTMF(agent.id, conversation.id, result);
						})
					})
				}
				else if (conversationIdForCheck == conversation.id) {
					console.log("-------------Same conversation------------");
				}
				else if (conversationIdForCheck != conversation.id) {
					console.log("-------------New conversation------------");
					agentTrue = false;
				}
				else {
					console.log("-------------Last------------");
				}
			}
			else {
				console.log("---------Second time-------------");
			}
		}
		// For heartbeats
		if (topic == 'channel.metadata') {
			console.log(eventBody.message);
		}
	});
});

async function getUser(agentUserId) {

	var userEmail;
	let userId = agentUserId; // String | User ID
	let opts = {
		"state": "active" // String | Search for a user with this state
	};

	await apiInstance.getUser(userId, opts)
		.then((data) => {
			//console.log(`getUser success! data: ${JSON.stringify(data, null, 2)}`);
			userEmail = data.email;
			console.log("email", userEmail);

		})
		.catch((err) => {
			console.log("There was a failure calling getUser");
			console.error(err);
		});

	return userEmail;
}

async function getAccessToken(userMail) {

	var user_access_token;
	console.log("-----------------------Inside token--------------------")
	let datatableId = "5d9ea952-f03b-4de0-8c72-b17adaf9fa22"; // String | id of datatable
	let rowId = userMail; // String | The key for the row
	let opts = {
		"showbrief": false // Boolean | if true returns just the key field for the row
	};
	await apiInstanceForArchitect.getFlowsDatatableRow(datatableId, rowId, opts)
		.then((data) => {
			//console.log(`getFlowsDatatableRow success! data: ${JSON.stringify(data, null, 2)}`);
			user_access_token = data.access_token;
		})
		.catch((err) => {
			console.log("There was a failure calling getFlowsDatatableRow");
			console.error(err);
		});
	// Returns a specific row for the datatable


	return user_access_token;
}

async function sendDTMF(participantId, conversationId, oauthToken) {
	const platformClientDtmf = require('purecloud-platform-client-v2');
	const clientDtmf = platformClientDtmf.ApiClient.instance;

	clientDtmf.setEnvironment(platformClientDtmf.PureCloudRegionHosts.us_west_2);

	console.log("OauthToken", oauthToken);
	clientDtmf.setAccessToken(oauthToken);
	let apiInstanceConversation = new platformClientDtmf.ConversationsApi();

	let opts = {

		"body": {
			"digits": "22"
		}

	};
	await apiInstanceConversation.postConversationParticipantDigits(conversationId, participantId, opts)
		.then(() => {
			console.log("postConversationParticipantDigits returned successfully.");
		})
		.catch((err) => {
			console.log("There was a failure calling postConversationParticipantDigits");
			console.error(err);
		});

	// Sends DTMF to the participant

}


websocketClient.on('connectFailed', function (error) {
	console.log('Connect Error: ' + error.toString());
});

// Set environment

// Log in with Client Credentials
client.loginClientCredentialsGrant(CLIENT_ID, CLIENT_SECRET)
	.then((result) => {
		console.log("result", result);
		client.setAccessToken(result.accessToken);
		console.log('Authentication successful');


		return notificationsApi.postNotificationsChannels()
	})
	.then(data => {
		console.log('Channel created');


		let websocketUri = data.connectUri;
		let channelId = data.id;
		websocketClient.connect(websocketUri);

		// Add the subscription for queue events
		let topic = [{ id: subscriptionTopic }];
		return notificationsApi.postNotificationsChannelSubscriptions(channelId, topic);
	})
	.then(data => {
		console.log('Subscribed to Queue');

	})
	.catch(e => console.error(e));


// NOTE: This next line is a simple way to make sure the app runs 'forever'
// In production you'd want the actual code running as a process/service
setInterval(() => { }, 1 << 30);

Keep track of your actions for the participant ID of the agent. I would suggest managing those IDs in a collection and set a TTL for each item to be removed so the list doesn't grow infinitely as the app runs.

Just a heads up, this is likely to be a bug in your code for conversations that have been handled by multiple agents. You're correct to check purpose in this way, but you should also validate that the segment hasn't ended. To ensure you're handling the right user in a conference, additionally check that the user ID on the segment matches the logged in user's user ID.

This topic was automatically closed 31 days after the last reply. New replies are no longer allowed.