This repository was archived by the owner on Mar 28, 2022. It is now read-only.

Repository files navigation

⚠️ End-of-Life Notice ⚠️

Symphony will stop supporting the NodeJS SDK in the coming months, with the following timeline: In January 2022, only critical issues will be fixed in the legacy NodeJS SDK. In March 2022, the SDK will no longer be actively supported. At this date you will still be able to use the SDK, however, please consider our BDKs in Java, Python or .Net if you prefer to benefit from a better support.


symphony-api-client-node

Symphony API Client for NodeJS

Installation

npm install --save symphony-api-client-node

Usage

constSymphony=require('symphony-api-client-node')constonMessage=messages=>{messages.forEach(message=>{console.log('The BOT heard "'+message.messageText+'" from '+message.user.displayName)})}constonError=error=>{console.error('Error reading data feed',error)}Symphony.initBot(__dirname+'/config.json').then(()=>{Symphony.getDatafeedEventsService({ onMessage, onError })}).fail(err=>{console.error(err)})

Configuration

The config.json file is described on the Configuration page of the Symphony Developer Guide.

Create a config.json file in your project. Below is a sample configuration which includes the properties that can be configured:

{// Mandatory section"sessionAuthHost": "my-company-name-api.symphony.com","sessionAuthPort": 8444,"sessionAuthContextPath": "/app","keyAuthHost": "my-company-name-api.symphony.com","keyAuthPort": 8444,"keyAuthContextPath": "/app","podHost": "my-company-name.symphony.com","podPort": 443,"podContextPath": "/app","agentHost": "my-company-name.symphony.com","agentPort": 443,"agentContextPath": "/app",// For bots only"botUsername": "my-bot-name","botEmailAddress": "bot@company.com",// For bots using RSA authentication"botPrivateKeyPath": "/path/to/rsa/private-key/","botPrivateKeyName": "bot-private-key.pem",// For bots using certificate authentication"botCertPath": "/path/to/bot-cert/","botCertName": "bot-cert.p12","botCertPassword": "bot-cert-password",// For extension apps only"appId": "",// For extension apps using RSA authentication"appPrivateKeyPath": "","appPrivateKeyName": "",// For extension apps using certificate authentication"appCertPath": "/path/to/app-cert/","appCertName": "app-cert.p12","appCertPassword": "app-cert-password",// Optional: If the connection to the pod (but not the agent) needs to run through a proxy"podProxyURL": "http://localhost:3128","podProxyUsername": "proxy-username","podProxyPassword": "proxy-password",// Optional: If the connection to both the pod and the agent needs to run through a proxy// Do not include the podProxy properties if using this"proxyURL": "http://localhost:3128","proxyUsername": "proxy-username","proxyPassword": "proxy-password",// Optional: If the connection to the key manager needs to run through a proxy"keyManagerProxyURL": "http://localhost:3128","keyManagerProxyUsername": "proxy-username","keyManagerProxyPassword": "proxy-password",// Optional: Self Signed Certificates - Set to 0 to not reject invalid or self-signed certificates"nodeTlsRejectUnauthorized": 0,// Optional: maximum number of retries to reconnect to the agent, default value is 10"maxRetries": 10,// Optional: maximum waiting time between retries in the exponential backoff algorithm,// default is 64 seconds, like in Google Cloud Storage lib"maxWaitInterval": 64}

Datafeed resuming

Datafeeds buffer events on the agent up to a small limit. This allows bots to be restarted without missing any events, provided the datafeed ID is persisted and supplied when reconnecting.

constfs=require('fs')constlocalFile='/path/to/writable/file'// <- change thisconstloadId=()=>fs.readFileSync(localFile,{encoding: 'utf-8',flag: 'a+'})constsaveId=id=>fs.writeFile(localFile,id,err=>err&&console.error(err))constfeed=Symphony.getDatafeedEventsService({
onMessage,
onError,onCreated: saveId,feedId: loadId(),})

This simple example assumes that the file system is persisted across restarts which is not always the case (e.g. Docker containers). It is recommended to persist the datafeed ID to a database instead if available.

Shutting down a bot

There is a known issue that can cause messages to be lost when stopping and resuming a datafeed. To avoid this, the datafeed service must hook into Node's exit events and prevent the process from exiting until the data feed has closed cleanly (up to 30 seconds). This is done automatically if the NODE_ENV environment variable is set to 'production' and a datafeed 'created' listener has been registered (see above). In other cases the behaviour can be opted into by calling registerShutdownHooks() on the feed instance.

See Express Best Practices for examples of how to set environment variables.

Datafeed event types

As well as messages, the datafeed reports a number of other events. See the documentation for real time events. These can be accessed by adding corresponding event listeners to the feed service. For example:

Symphony.getDatafeedEventsService({
onMessage,
onError,onUserjoinedroom: events=>{for(leteventofevents){constinitiator=event.initiator.user.displayNameconstuser=event.payload.userJoinedRoom.affectedUser.displayNameconstroom=event.payload.userJoinedRoom.stream.roomNameconsole.log(`${initiator} added ${user} to ${room}`)}},})

Symphony Elements Example Usage

constmessageHandler=(event,messages)=>{messages.forEach((message,index)=>{letreply_message='Hello '+message.user.firstName+', hope you are doing well!!'Symphony.sendMessage(message.stream.streamId,reply_message,null,Symphony.MESSAGEML_FORMAT)})}constelementsHandler=(event,actions)=>{actions.forEach((action,index)=>{console.log(action)})}Symphony.initBot(__dirname+'/config.json').then((symAuth)=>{consthandlers={onMessageSent: messageHandler.bind(null,'MESSAGE_RECEIVED'),onSymphonyElementsAction: elementsHandler.bind(null,'ELEMENTS_ACTION_RECEIVED')}Symphony.getDatafeedEventsService(handlers)})

Symphony Elements Form Builder

constformML=Symphony.formBuilder('my-form').addDiv("hello").addLineBreaks(3).addHeader(6,"Yes").addTextField('my-text-field','','Type something').addButton('my-button','Button').addTextArea('my-ta','displayed','type here').addCheckBox('my-cb','Hey CB','x').addRadioButton('my-rb','Hey RB','rb-val').addPersonSelector('my-ps','Select people',true).addTableSelect('my-ts',).addLineBreak().formatElement()Symphony.sendMessage('streamId',formML)

Advanced Configuration for Load Balancing

Create an additional configuration file to support load balancing. There are 3 supported types:

  • Round-Robin
  • Random
  • External

Round-robin and random load balancing are managed by this library based on the servers provided in the agentServers array.

External load-balancing is managed by an external DNS, cloud provider or hardware-based solution. List only one load balancer frontend hostname in the agentServers array (subsequent server entries for the external method are ignored).

Note: that this method requires all underlying agent servers to implement an additional host.name switch with the current server's FQDN in their startup.sh script.

exec java $JAVA_OPTS -Dhost.name=sym-agent-01.my-company.com

There is also support for sticky sessions, which should be true for any bot that requires the datafeed loop. Using non-sticky load-balanced configuration with a datafeed loop will result in unexpected results.

{"loadBalancing": {"method": "random",// or roundrobin or external"stickySessions": true},"agentServers": ["sym-agent-01.my-company.com","sym-agent-02.my-company.com","sym-agent-03.my-company.com"]}

Loading advanced configuration

To load the configuration

Symphony.initBot(__dirname + '/config.json', __dirname + '/lb-config.json')

About

Symphony API Client for NodeJS

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
This repository was archived by the owner on Mar 28, 2022. It is now read-only.

Repository files navigation

⚠️ End-of-Life Notice ⚠️

Symphony will stop supporting the NodeJS SDK in the coming months, with the following timeline: In January 2022, only critical issues will be fixed in the legacy NodeJS SDK. In March 2022, the SDK will no longer be actively supported. At this date you will still be able to use the SDK, however, please consider our BDKs in Java, Python or .Net if you prefer to benefit from a better support.


symphony-api-client-node

Symphony API Client for NodeJS

Installation

npm install --save symphony-api-client-node

Usage

constSymphony=require('symphony-api-client-node')constonMessage=messages=>{messages.forEach(message=>{console.log('The BOT heard "'+message.messageText+'" from '+message.user.displayName)})}constonError=error=>{console.error('Error reading data feed',error)}Symphony.initBot(__dirname+'/config.json').then(()=>{Symphony.getDatafeedEventsService({ onMessage, onError })}).fail(err=>{console.error(err)})

Configuration

The config.json file is described on the Configuration page of the Symphony Developer Guide.

Create a config.json file in your project. Below is a sample configuration which includes the properties that can be configured:

{// Mandatory section"sessionAuthHost": "my-company-name-api.symphony.com","sessionAuthPort": 8444,"sessionAuthContextPath": "/app","keyAuthHost": "my-company-name-api.symphony.com","keyAuthPort": 8444,"keyAuthContextPath": "/app","podHost": "my-company-name.symphony.com","podPort": 443,"podContextPath": "/app","agentHost": "my-company-name.symphony.com","agentPort": 443,"agentContextPath": "/app",// For bots only"botUsername": "my-bot-name","botEmailAddress": "bot@company.com",// For bots using RSA authentication"botPrivateKeyPath": "/path/to/rsa/private-key/","botPrivateKeyName": "bot-private-key.pem",// For bots using certificate authentication"botCertPath": "/path/to/bot-cert/","botCertName": "bot-cert.p12","botCertPassword": "bot-cert-password",// For extension apps only"appId": "",// For extension apps using RSA authentication"appPrivateKeyPath": "","appPrivateKeyName": "",// For extension apps using certificate authentication"appCertPath": "/path/to/app-cert/","appCertName": "app-cert.p12","appCertPassword": "app-cert-password",// Optional: If the connection to the pod (but not the agent) needs to run through a proxy"podProxyURL": "http://localhost:3128","podProxyUsername": "proxy-username","podProxyPassword": "proxy-password",// Optional: If the connection to both the pod and the agent needs to run through a proxy// Do not include the podProxy properties if using this"proxyURL": "http://localhost:3128","proxyUsername": "proxy-username","proxyPassword": "proxy-password",// Optional: If the connection to the key manager needs to run through a proxy"keyManagerProxyURL": "http://localhost:3128","keyManagerProxyUsername": "proxy-username","keyManagerProxyPassword": "proxy-password",// Optional: Self Signed Certificates - Set to 0 to not reject invalid or self-signed certificates"nodeTlsRejectUnauthorized": 0,// Optional: maximum number of retries to reconnect to the agent, default value is 10"maxRetries": 10,// Optional: maximum waiting time between retries in the exponential backoff algorithm,// default is 64 seconds, like in Google Cloud Storage lib"maxWaitInterval": 64}

Datafeed resuming

Datafeeds buffer events on the agent up to a small limit. This allows bots to be restarted without missing any events, provided the datafeed ID is persisted and supplied when reconnecting.

constfs=require('fs')constlocalFile='/path/to/writable/file'// <- change thisconstloadId=()=>fs.readFileSync(localFile,{encoding: 'utf-8',flag: 'a+'})constsaveId=id=>fs.writeFile(localFile,id,err=>err&&console.error(err))constfeed=Symphony.getDatafeedEventsService({
onMessage,
onError,onCreated: saveId,feedId: loadId(),})

This simple example assumes that the file system is persisted across restarts which is not always the case (e.g. Docker containers). It is recommended to persist the datafeed ID to a database instead if available.

Shutting down a bot

There is a known issue that can cause messages to be lost when stopping and resuming a datafeed. To avoid this, the datafeed service must hook into Node's exit events and prevent the process from exiting until the data feed has closed cleanly (up to 30 seconds). This is done automatically if the NODE_ENV environment variable is set to 'production' and a datafeed 'created' listener has been registered (see above). In other cases the behaviour can be opted into by calling registerShutdownHooks() on the feed instance.

See Express Best Practices for examples of how to set environment variables.

Datafeed event types

As well as messages, the datafeed reports a number of other events. See the documentation for real time events. These can be accessed by adding corresponding event listeners to the feed service. For example:

Symphony.getDatafeedEventsService({
onMessage,
onError,onUserjoinedroom: events=>{for(leteventofevents){constinitiator=event.initiator.user.displayNameconstuser=event.payload.userJoinedRoom.affectedUser.displayNameconstroom=event.payload.userJoinedRoom.stream.roomNameconsole.log(`${initiator} added ${user} to ${room}`)}},})

Symphony Elements Example Usage

constmessageHandler=(event,messages)=>{messages.forEach((message,index)=>{letreply_message='Hello '+message.user.firstName+', hope you are doing well!!'Symphony.sendMessage(message.stream.streamId,reply_message,null,Symphony.MESSAGEML_FORMAT)})}constelementsHandler=(event,actions)=>{actions.forEach((action,index)=>{console.log(action)})}Symphony.initBot(__dirname+'/config.json').then((symAuth)=>{consthandlers={onMessageSent: messageHandler.bind(null,'MESSAGE_RECEIVED'),onSymphonyElementsAction: elementsHandler.bind(null,'ELEMENTS_ACTION_RECEIVED')}Symphony.getDatafeedEventsService(handlers)})

Symphony Elements Form Builder

constformML=Symphony.formBuilder('my-form').addDiv("hello").addLineBreaks(3).addHeader(6,"Yes").addTextField('my-text-field','','Type something').addButton('my-button','Button').addTextArea('my-ta','displayed','type here').addCheckBox('my-cb','Hey CB','x').addRadioButton('my-rb','Hey RB','rb-val').addPersonSelector('my-ps','Select people',true).addTableSelect('my-ts',).addLineBreak().formatElement()Symphony.sendMessage('streamId',formML)

Advanced Configuration for Load Balancing

Create an additional configuration file to support load balancing. There are 3 supported types:

  • Round-Robin
  • Random
  • External

Round-robin and random load balancing are managed by this library based on the servers provided in the agentServers array.

External load-balancing is managed by an external DNS, cloud provider or hardware-based solution. List only one load balancer frontend hostname in the agentServers array (subsequent server entries for the external method are ignored).

Note: that this method requires all underlying agent servers to implement an additional host.name switch with the current server's FQDN in their startup.sh script.

exec java $JAVA_OPTS -Dhost.name=sym-agent-01.my-company.com

There is also support for sticky sessions, which should be true for any bot that requires the datafeed loop. Using non-sticky load-balanced configuration with a datafeed loop will result in unexpected results.

{"loadBalancing": {"method": "random",// or roundrobin or external"stickySessions": true},"agentServers": ["sym-agent-01.my-company.com","sym-agent-02.my-company.com","sym-agent-03.my-company.com"]}

Loading advanced configuration

To load the configuration

Symphony.initBot(__dirname + '/config.json', __dirname + '/lb-config.json')

About

Symphony API Client for NodeJS

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Mar 28, 2022. It is now read-only.

Repository files navigation

⚠️ End-of-Life Notice ⚠️

Symphony will stop supporting the NodeJS SDK in the coming months, with the following timeline: In January 2022, only critical issues will be fixed in the legacy NodeJS SDK. In March 2022, the SDK will no longer be actively supported. At this date you will still be able to use the SDK, however, please consider our BDKs in Java, Python or .Net if you prefer to benefit from a better support.


symphony-api-client-node

Symphony API Client for NodeJS

Installation

npm install --save symphony-api-client-node

Usage

constSymphony=require('symphony-api-client-node')constonMessage=messages=>{messages.forEach(message=>{console.log('The BOT heard "'+message.messageText+'" from '+message.user.displayName)})}constonError=error=>{console.error('Error reading data feed',error)}Symphony.initBot(__dirname+'/config.json').then(()=>{Symphony.getDatafeedEventsService({ onMessage, onError })}).fail(err=>{console.error(err)})

Configuration

The config.json file is described on the Configuration page of the Symphony Developer Guide.

Create a config.json file in your project. Below is a sample configuration which includes the properties that can be configured:

{// Mandatory section"sessionAuthHost": "my-company-name-api.symphony.com","sessionAuthPort": 8444,"sessionAuthContextPath": "/app","keyAuthHost": "my-company-name-api.symphony.com","keyAuthPort": 8444,"keyAuthContextPath": "/app","podHost": "my-company-name.symphony.com","podPort": 443,"podContextPath": "/app","agentHost": "my-company-name.symphony.com","agentPort": 443,"agentContextPath": "/app",// For bots only"botUsername": "my-bot-name","botEmailAddress": "bot@company.com",// For bots using RSA authentication"botPrivateKeyPath": "/path/to/rsa/private-key/","botPrivateKeyName": "bot-private-key.pem",// For bots using certificate authentication"botCertPath": "/path/to/bot-cert/","botCertName": "bot-cert.p12","botCertPassword": "bot-cert-password",// For extension apps only"appId": "",// For extension apps using RSA authentication"appPrivateKeyPath": "","appPrivateKeyName": "",// For extension apps using certificate authentication"appCertPath": "/path/to/app-cert/","appCertName": "app-cert.p12","appCertPassword": "app-cert-password",// Optional: If the connection to the pod (but not the agent) needs to run through a proxy"podProxyURL": "http://localhost:3128","podProxyUsername": "proxy-username","podProxyPassword": "proxy-password",// Optional: If the connection to both the pod and the agent needs to run through a proxy// Do not include the podProxy properties if using this"proxyURL": "http://localhost:3128","proxyUsername": "proxy-username","proxyPassword": "proxy-password",// Optional: If the connection to the key manager needs to run through a proxy"keyManagerProxyURL": "http://localhost:3128","keyManagerProxyUsername": "proxy-username","keyManagerProxyPassword": "proxy-password",// Optional: Self Signed Certificates - Set to 0 to not reject invalid or self-signed certificates"nodeTlsRejectUnauthorized": 0,// Optional: maximum number of retries to reconnect to the agent, default value is 10"maxRetries": 10,// Optional: maximum waiting time between retries in the exponential backoff algorithm,// default is 64 seconds, like in Google Cloud Storage lib"maxWaitInterval": 64}

Datafeed resuming

Datafeeds buffer events on the agent up to a small limit. This allows bots to be restarted without missing any events, provided the datafeed ID is persisted and supplied when reconnecting.

constfs=require('fs')constlocalFile='/path/to/writable/file'// <- change thisconstloadId=()=>fs.readFileSync(localFile,{encoding: 'utf-8',flag: 'a+'})constsaveId=id=>fs.writeFile(localFile,id,err=>err&&console.error(err))constfeed=Symphony.getDatafeedEventsService({
onMessage,
onError,onCreated: saveId,feedId: loadId(),})

This simple example assumes that the file system is persisted across restarts which is not always the case (e.g. Docker containers). It is recommended to persist the datafeed ID to a database instead if available.

Shutting down a bot

There is a known issue that can cause messages to be lost when stopping and resuming a datafeed. To avoid this, the datafeed service must hook into Node's exit events and prevent the process from exiting until the data feed has closed cleanly (up to 30 seconds). This is done automatically if the NODE_ENV environment variable is set to 'production' and a datafeed 'created' listener has been registered (see above). In other cases the behaviour can be opted into by calling registerShutdownHooks() on the feed instance.

See Express Best Practices for examples of how to set environment variables.

Datafeed event types

As well as messages, the datafeed reports a number of other events. See the documentation for real time events. These can be accessed by adding corresponding event listeners to the feed service. For example:

Symphony.getDatafeedEventsService({
onMessage,
onError,onUserjoinedroom: events=>{for(leteventofevents){constinitiator=event.initiator.user.displayNameconstuser=event.payload.userJoinedRoom.affectedUser.displayNameconstroom=event.payload.userJoinedRoom.stream.roomNameconsole.log(`${initiator} added ${user} to ${room}`)}},})

Symphony Elements Example Usage

constmessageHandler=(event,messages)=>{messages.forEach((message,index)=>{letreply_message='Hello '+message.user.firstName+', hope you are doing well!!'Symphony.sendMessage(message.stream.streamId,reply_message,null,Symphony.MESSAGEML_FORMAT)})}constelementsHandler=(event,actions)=>{actions.forEach((action,index)=>{console.log(action)})}Symphony.initBot(__dirname+'/config.json').then((symAuth)=>{consthandlers={onMessageSent: messageHandler.bind(null,'MESSAGE_RECEIVED'),onSymphonyElementsAction: elementsHandler.bind(null,'ELEMENTS_ACTION_RECEIVED')}Symphony.getDatafeedEventsService(handlers)})

Symphony Elements Form Builder

constformML=Symphony.formBuilder('my-form').addDiv("hello").addLineBreaks(3).addHeader(6,"Yes").addTextField('my-text-field','','Type something').addButton('my-button','Button').addTextArea('my-ta','displayed','type here').addCheckBox('my-cb','Hey CB','x').addRadioButton('my-rb','Hey RB','rb-val').addPersonSelector('my-ps','Select people',true).addTableSelect('my-ts',).addLineBreak().formatElement()Symphony.sendMessage('streamId',formML)

Advanced Configuration for Load Balancing

Create an additional configuration file to support load balancing. There are 3 supported types:

  • Round-Robin
  • Random
  • External

Round-robin and random load balancing are managed by this library based on the servers provided in the agentServers array.

External load-balancing is managed by an external DNS, cloud provider or hardware-based solution. List only one load balancer frontend hostname in the agentServers array (subsequent server entries for the external method are ignored).

Note: that this method requires all underlying agent servers to implement an additional host.name switch with the current server's FQDN in their startup.sh script.

exec java $JAVA_OPTS -Dhost.name=sym-agent-01.my-company.com

There is also support for sticky sessions, which should be true for any bot that requires the datafeed loop. Using non-sticky load-balanced configuration with a datafeed loop will result in unexpected results.

{"loadBalancing": {"method": "random",// or roundrobin or external"stickySessions": true},"agentServers": ["sym-agent-01.my-company.com","sym-agent-02.my-company.com","sym-agent-03.my-company.com"]}

Loading advanced configuration

To load the configuration

Symphony.initBot(__dirname + '/config.json', __dirname + '/lb-config.json')

About

Symphony API Client for NodeJS

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Mar 28, 2022. It is now read-only.

Repository files navigation

⚠️ End-of-Life Notice ⚠️

Symphony will stop supporting the NodeJS SDK in the coming months, with the following timeline: In January 2022, only critical issues will be fixed in the legacy NodeJS SDK. In March 2022, the SDK will no longer be actively supported. At this date you will still be able to use the SDK, however, please consider our BDKs in Java, Python or .Net if you prefer to benefit from a better support.


symphony-api-client-node

Symphony API Client for NodeJS

Installation

npm install --save symphony-api-client-node

Usage

constSymphony=require('symphony-api-client-node')constonMessage=messages=>{messages.forEach(message=>{console.log('The BOT heard "'+message.messageText+'" from '+message.user.displayName)})}constonError=error=>{console.error('Error reading data feed',error)}Symphony.initBot(__dirname+'/config.json').then(()=>{Symphony.getDatafeedEventsService({ onMessage, onError })}).fail(err=>{console.error(err)})

Configuration

The config.json file is described on the Configuration page of the Symphony Developer Guide.

Create a config.json file in your project. Below is a sample configuration which includes the properties that can be configured:

{// Mandatory section"sessionAuthHost": "my-company-name-api.symphony.com","sessionAuthPort": 8444,"sessionAuthContextPath": "/app","keyAuthHost": "my-company-name-api.symphony.com","keyAuthPort": 8444,"keyAuthContextPath": "/app","podHost": "my-company-name.symphony.com","podPort": 443,"podContextPath": "/app","agentHost": "my-company-name.symphony.com","agentPort": 443,"agentContextPath": "/app",// For bots only"botUsername": "my-bot-name","botEmailAddress": "bot@company.com",// For bots using RSA authentication"botPrivateKeyPath": "/path/to/rsa/private-key/","botPrivateKeyName": "bot-private-key.pem",// For bots using certificate authentication"botCertPath": "/path/to/bot-cert/","botCertName": "bot-cert.p12","botCertPassword": "bot-cert-password",// For extension apps only"appId": "",// For extension apps using RSA authentication"appPrivateKeyPath": "","appPrivateKeyName": "",// For extension apps using certificate authentication"appCertPath": "/path/to/app-cert/","appCertName": "app-cert.p12","appCertPassword": "app-cert-password",// Optional: If the connection to the pod (but not the agent) needs to run through a proxy"podProxyURL": "http://localhost:3128","podProxyUsername": "proxy-username","podProxyPassword": "proxy-password",// Optional: If the connection to both the pod and the agent needs to run through a proxy// Do not include the podProxy properties if using this"proxyURL": "http://localhost:3128","proxyUsername": "proxy-username","proxyPassword": "proxy-password",// Optional: If the connection to the key manager needs to run through a proxy"keyManagerProxyURL": "http://localhost:3128","keyManagerProxyUsername": "proxy-username","keyManagerProxyPassword": "proxy-password",// Optional: Self Signed Certificates - Set to 0 to not reject invalid or self-signed certificates"nodeTlsRejectUnauthorized": 0,// Optional: maximum number of retries to reconnect to the agent, default value is 10"maxRetries": 10,// Optional: maximum waiting time between retries in the exponential backoff algorithm,// default is 64 seconds, like in Google Cloud Storage lib"maxWaitInterval": 64}

Datafeed resuming

Datafeeds buffer events on the agent up to a small limit. This allows bots to be restarted without missing any events, provided the datafeed ID is persisted and supplied when reconnecting.

constfs=require('fs')constlocalFile='/path/to/writable/file'// <- change thisconstloadId=()=>fs.readFileSync(localFile,{encoding: 'utf-8',flag: 'a+'})constsaveId=id=>fs.writeFile(localFile,id,err=>err&&console.error(err))constfeed=Symphony.getDatafeedEventsService({
onMessage,
onError,onCreated: saveId,feedId: loadId(),})

This simple example assumes that the file system is persisted across restarts which is not always the case (e.g. Docker containers). It is recommended to persist the datafeed ID to a database instead if available.

Shutting down a bot

There is a known issue that can cause messages to be lost when stopping and resuming a datafeed. To avoid this, the datafeed service must hook into Node's exit events and prevent the process from exiting until the data feed has closed cleanly (up to 30 seconds). This is done automatically if the NODE_ENV environment variable is set to 'production' and a datafeed 'created' listener has been registered (see above). In other cases the behaviour can be opted into by calling registerShutdownHooks() on the feed instance.

See Express Best Practices for examples of how to set environment variables.

Datafeed event types

As well as messages, the datafeed reports a number of other events. See the documentation for real time events. These can be accessed by adding corresponding event listeners to the feed service. For example:

Symphony.getDatafeedEventsService({
onMessage,
onError,onUserjoinedroom: events=>{for(leteventofevents){constinitiator=event.initiator.user.displayNameconstuser=event.payload.userJoinedRoom.affectedUser.displayNameconstroom=event.payload.userJoinedRoom.stream.roomNameconsole.log(`${initiator} added ${user} to ${room}`)}},})

Symphony Elements Example Usage

constmessageHandler=(event,messages)=>{messages.forEach((message,index)=>{letreply_message='Hello '+message.user.firstName+', hope you are doing well!!'Symphony.sendMessage(message.stream.streamId,reply_message,null,Symphony.MESSAGEML_FORMAT)})}constelementsHandler=(event,actions)=>{actions.forEach((action,index)=>{console.log(action)})}Symphony.initBot(__dirname+'/config.json').then((symAuth)=>{consthandlers={onMessageSent: messageHandler.bind(null,'MESSAGE_RECEIVED'),onSymphonyElementsAction: elementsHandler.bind(null,'ELEMENTS_ACTION_RECEIVED')}Symphony.getDatafeedEventsService(handlers)})

Symphony Elements Form Builder

constformML=Symphony.formBuilder('my-form').addDiv("hello").addLineBreaks(3).addHeader(6,"Yes").addTextField('my-text-field','','Type something').addButton('my-button','Button').addTextArea('my-ta','displayed','type here').addCheckBox('my-cb','Hey CB','x').addRadioButton('my-rb','Hey RB','rb-val').addPersonSelector('my-ps','Select people',true).addTableSelect('my-ts',).addLineBreak().formatElement()Symphony.sendMessage('streamId',formML)

Advanced Configuration for Load Balancing

Create an additional configuration file to support load balancing. There are 3 supported types:

  • Round-Robin
  • Random
  • External

Round-robin and random load balancing are managed by this library based on the servers provided in the agentServers array.

External load-balancing is managed by an external DNS, cloud provider or hardware-based solution. List only one load balancer frontend hostname in the agentServers array (subsequent server entries for the external method are ignored).

Note: that this method requires all underlying agent servers to implement an additional host.name switch with the current server's FQDN in their startup.sh script.

exec java $JAVA_OPTS -Dhost.name=sym-agent-01.my-company.com

There is also support for sticky sessions, which should be true for any bot that requires the datafeed loop. Using non-sticky load-balanced configuration with a datafeed loop will result in unexpected results.

{"loadBalancing": {"method": "random",// or roundrobin or external"stickySessions": true},"agentServers": ["sym-agent-01.my-company.com","sym-agent-02.my-company.com","sym-agent-03.my-company.com"]}

Loading advanced configuration

To load the configuration

Symphony.initBot(__dirname + '/config.json', __dirname + '/lb-config.json')

About

Symphony API Client for NodeJS

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
This repository was archived by the owner on Mar 28, 2022. It is now read-only.

Repository files navigation

⚠️ End-of-Life Notice ⚠️

Symphony will stop supporting the NodeJS SDK in the coming months, with the following timeline: In January 2022, only critical issues will be fixed in the legacy NodeJS SDK. In March 2022, the SDK will no longer be actively supported. At this date you will still be able to use the SDK, however, please consider our BDKs in Java, Python or .Net if you prefer to benefit from a better support.


symphony-api-client-node

Symphony API Client for NodeJS

Installation

npm install --save symphony-api-client-node

Usage

constSymphony=require('symphony-api-client-node')constonMessage=messages=>{messages.forEach(message=>{console.log('The BOT heard "'+message.messageText+'" from '+message.user.displayName)})}constonError=error=>{console.error('Error reading data feed',error)}Symphony.initBot(__dirname+'/config.json').then(()=>{Symphony.getDatafeedEventsService({ onMessage, onError })}).fail(err=>{console.error(err)})

Configuration

The config.json file is described on the Configuration page of the Symphony Developer Guide.

Create a config.json file in your project. Below is a sample configuration which includes the properties that can be configured:

{// Mandatory section"sessionAuthHost": "my-company-name-api.symphony.com","sessionAuthPort": 8444,"sessionAuthContextPath": "/app","keyAuthHost": "my-company-name-api.symphony.com","keyAuthPort": 8444,"keyAuthContextPath": "/app","podHost": "my-company-name.symphony.com","podPort": 443,"podContextPath": "/app","agentHost": "my-company-name.symphony.com","agentPort": 443,"agentContextPath": "/app",// For bots only"botUsername": "my-bot-name","botEmailAddress": "bot@company.com",// For bots using RSA authentication"botPrivateKeyPath": "/path/to/rsa/private-key/","botPrivateKeyName": "bot-private-key.pem",// For bots using certificate authentication"botCertPath": "/path/to/bot-cert/","botCertName": "bot-cert.p12","botCertPassword": "bot-cert-password",// For extension apps only"appId": "",// For extension apps using RSA authentication"appPrivateKeyPath": "","appPrivateKeyName": "",// For extension apps using certificate authentication"appCertPath": "/path/to/app-cert/","appCertName": "app-cert.p12","appCertPassword": "app-cert-password",// Optional: If the connection to the pod (but not the agent) needs to run through a proxy"podProxyURL": "http://localhost:3128","podProxyUsername": "proxy-username","podProxyPassword": "proxy-password",// Optional: If the connection to both the pod and the agent needs to run through a proxy// Do not include the podProxy properties if using this"proxyURL": "http://localhost:3128","proxyUsername": "proxy-username","proxyPassword": "proxy-password",// Optional: If the connection to the key manager needs to run through a proxy"keyManagerProxyURL": "http://localhost:3128","keyManagerProxyUsername": "proxy-username","keyManagerProxyPassword": "proxy-password",// Optional: Self Signed Certificates - Set to 0 to not reject invalid or self-signed certificates"nodeTlsRejectUnauthorized": 0,// Optional: maximum number of retries to reconnect to the agent, default value is 10"maxRetries": 10,// Optional: maximum waiting time between retries in the exponential backoff algorithm,// default is 64 seconds, like in Google Cloud Storage lib"maxWaitInterval": 64}

Datafeed resuming

Datafeeds buffer events on the agent up to a small limit. This allows bots to be restarted without missing any events, provided the datafeed ID is persisted and supplied when reconnecting.

constfs=require('fs')constlocalFile='/path/to/writable/file'// <- change thisconstloadId=()=>fs.readFileSync(localFile,{encoding: 'utf-8',flag: 'a+'})constsaveId=id=>fs.writeFile(localFile,id,err=>err&&console.error(err))constfeed=Symphony.getDatafeedEventsService({
onMessage,
onError,onCreated: saveId,feedId: loadId(),})

This simple example assumes that the file system is persisted across restarts which is not always the case (e.g. Docker containers). It is recommended to persist the datafeed ID to a database instead if available.

Shutting down a bot

There is a known issue that can cause messages to be lost when stopping and resuming a datafeed. To avoid this, the datafeed service must hook into Node's exit events and prevent the process from exiting until the data feed has closed cleanly (up to 30 seconds). This is done automatically if the NODE_ENV environment variable is set to 'production' and a datafeed 'created' listener has been registered (see above). In other cases the behaviour can be opted into by calling registerShutdownHooks() on the feed instance.

See Express Best Practices for examples of how to set environment variables.

Datafeed event types

As well as messages, the datafeed reports a number of other events. See the documentation for real time events. These can be accessed by adding corresponding event listeners to the feed service. For example:

Symphony.getDatafeedEventsService({
onMessage,
onError,onUserjoinedroom: events=>{for(leteventofevents){constinitiator=event.initiator.user.displayNameconstuser=event.payload.userJoinedRoom.affectedUser.displayNameconstroom=event.payload.userJoinedRoom.stream.roomNameconsole.log(`${initiator} added ${user} to ${room}`)}},})

Symphony Elements Example Usage

constmessageHandler=(event,messages)=>{messages.forEach((message,index)=>{letreply_message='Hello '+message.user.firstName+', hope you are doing well!!'Symphony.sendMessage(message.stream.streamId,reply_message,null,Symphony.MESSAGEML_FORMAT)})}constelementsHandler=(event,actions)=>{actions.forEach((action,index)=>{console.log(action)})}Symphony.initBot(__dirname+'/config.json').then((symAuth)=>{consthandlers={onMessageSent: messageHandler.bind(null,'MESSAGE_RECEIVED'),onSymphonyElementsAction: elementsHandler.bind(null,'ELEMENTS_ACTION_RECEIVED')}Symphony.getDatafeedEventsService(handlers)})

Symphony Elements Form Builder

constformML=Symphony.formBuilder('my-form').addDiv("hello").addLineBreaks(3).addHeader(6,"Yes").addTextField('my-text-field','','Type something').addButton('my-button','Button').addTextArea('my-ta','displayed','type here').addCheckBox('my-cb','Hey CB','x').addRadioButton('my-rb','Hey RB','rb-val').addPersonSelector('my-ps','Select people',true).addTableSelect('my-ts',).addLineBreak().formatElement()Symphony.sendMessage('streamId',formML)

Advanced Configuration for Load Balancing

Create an additional configuration file to support load balancing. There are 3 supported types:

  • Round-Robin
  • Random
  • External

Round-robin and random load balancing are managed by this library based on the servers provided in the agentServers array.

External load-balancing is managed by an external DNS, cloud provider or hardware-based solution. List only one load balancer frontend hostname in the agentServers array (subsequent server entries for the external method are ignored).

Note: that this method requires all underlying agent servers to implement an additional host.name switch with the current server's FQDN in their startup.sh script.

exec java $JAVA_OPTS -Dhost.name=sym-agent-01.my-company.com

There is also support for sticky sessions, which should be true for any bot that requires the datafeed loop. Using non-sticky load-balanced configuration with a datafeed loop will result in unexpected results.

{"loadBalancing": {"method": "random",// or roundrobin or external"stickySessions": true},"agentServers": ["sym-agent-01.my-company.com","sym-agent-02.my-company.com","sym-agent-03.my-company.com"]}

Loading advanced configuration

To load the configuration

Symphony.initBot(__dirname + '/config.json', __dirname + '/lb-config.json')

About

Symphony API Client for NodeJS

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Mar 28, 2022. It is now read-only.

Repository files navigation

⚠️ End-of-Life Notice ⚠️

Symphony will stop supporting the NodeJS SDK in the coming months, with the following timeline: In January 2022, only critical issues will be fixed in the legacy NodeJS SDK. In March 2022, the SDK will no longer be actively supported. At this date you will still be able to use the SDK, however, please consider our BDKs in Java, Python or .Net if you prefer to benefit from a better support.


symphony-api-client-node

Symphony API Client for NodeJS

Installation

npm install --save symphony-api-client-node

Usage

constSymphony=require('symphony-api-client-node')constonMessage=messages=>{messages.forEach(message=>{console.log('The BOT heard "'+message.messageText+'" from '+message.user.displayName)})}constonError=error=>{console.error('Error reading data feed',error)}Symphony.initBot(__dirname+'/config.json').then(()=>{Symphony.getDatafeedEventsService({ onMessage, onError })}).fail(err=>{console.error(err)})

Configuration

The config.json file is described on the Configuration page of the Symphony Developer Guide.

Create a config.json file in your project. Below is a sample configuration which includes the properties that can be configured:

{// Mandatory section"sessionAuthHost": "my-company-name-api.symphony.com","sessionAuthPort": 8444,"sessionAuthContextPath": "/app","keyAuthHost": "my-company-name-api.symphony.com","keyAuthPort": 8444,"keyAuthContextPath": "/app","podHost": "my-company-name.symphony.com","podPort": 443,"podContextPath": "/app","agentHost": "my-company-name.symphony.com","agentPort": 443,"agentContextPath": "/app",// For bots only"botUsername": "my-bot-name","botEmailAddress": "bot@company.com",// For bots using RSA authentication"botPrivateKeyPath": "/path/to/rsa/private-key/","botPrivateKeyName": "bot-private-key.pem",// For bots using certificate authentication"botCertPath": "/path/to/bot-cert/","botCertName": "bot-cert.p12","botCertPassword": "bot-cert-password",// For extension apps only"appId": "",// For extension apps using RSA authentication"appPrivateKeyPath": "","appPrivateKeyName": "",// For extension apps using certificate authentication"appCertPath": "/path/to/app-cert/","appCertName": "app-cert.p12","appCertPassword": "app-cert-password",// Optional: If the connection to the pod (but not the agent) needs to run through a proxy"podProxyURL": "http://localhost:3128","podProxyUsername": "proxy-username","podProxyPassword": "proxy-password",// Optional: If the connection to both the pod and the agent needs to run through a proxy// Do not include the podProxy properties if using this"proxyURL": "http://localhost:3128","proxyUsername": "proxy-username","proxyPassword": "proxy-password",// Optional: If the connection to the key manager needs to run through a proxy"keyManagerProxyURL": "http://localhost:3128","keyManagerProxyUsername": "proxy-username","keyManagerProxyPassword": "proxy-password",// Optional: Self Signed Certificates - Set to 0 to not reject invalid or self-signed certificates"nodeTlsRejectUnauthorized": 0,// Optional: maximum number of retries to reconnect to the agent, default value is 10"maxRetries": 10,// Optional: maximum waiting time between retries in the exponential backoff algorithm,// default is 64 seconds, like in Google Cloud Storage lib"maxWaitInterval": 64}

Datafeed resuming

Datafeeds buffer events on the agent up to a small limit. This allows bots to be restarted without missing any events, provided the datafeed ID is persisted and supplied when reconnecting.

constfs=require('fs')constlocalFile='/path/to/writable/file'// <- change thisconstloadId=()=>fs.readFileSync(localFile,{encoding: 'utf-8',flag: 'a+'})constsaveId=id=>fs.writeFile(localFile,id,err=>err&&console.error(err))constfeed=Symphony.getDatafeedEventsService({
onMessage,
onError,onCreated: saveId,feedId: loadId(),})

This simple example assumes that the file system is persisted across restarts which is not always the case (e.g. Docker containers). It is recommended to persist the datafeed ID to a database instead if available.

Shutting down a bot

There is a known issue that can cause messages to be lost when stopping and resuming a datafeed. To avoid this, the datafeed service must hook into Node's exit events and prevent the process from exiting until the data feed has closed cleanly (up to 30 seconds). This is done automatically if the NODE_ENV environment variable is set to 'production' and a datafeed 'created' listener has been registered (see above). In other cases the behaviour can be opted into by calling registerShutdownHooks() on the feed instance.

See Express Best Practices for examples of how to set environment variables.

Datafeed event types

As well as messages, the datafeed reports a number of other events. See the documentation for real time events. These can be accessed by adding corresponding event listeners to the feed service. For example:

Symphony.getDatafeedEventsService({
onMessage,
onError,onUserjoinedroom: events=>{for(leteventofevents){constinitiator=event.initiator.user.displayNameconstuser=event.payload.userJoinedRoom.affectedUser.displayNameconstroom=event.payload.userJoinedRoom.stream.roomNameconsole.log(`${initiator} added ${user} to ${room}`)}},})

Symphony Elements Example Usage

constmessageHandler=(event,messages)=>{messages.forEach((message,index)=>{letreply_message='Hello '+message.user.firstName+', hope you are doing well!!'Symphony.sendMessage(message.stream.streamId,reply_message,null,Symphony.MESSAGEML_FORMAT)})}constelementsHandler=(event,actions)=>{actions.forEach((action,index)=>{console.log(action)})}Symphony.initBot(__dirname+'/config.json').then((symAuth)=>{consthandlers={onMessageSent: messageHandler.bind(null,'MESSAGE_RECEIVED'),onSymphonyElementsAction: elementsHandler.bind(null,'ELEMENTS_ACTION_RECEIVED')}Symphony.getDatafeedEventsService(handlers)})

Symphony Elements Form Builder

constformML=Symphony.formBuilder('my-form').addDiv("hello").addLineBreaks(3).addHeader(6,"Yes").addTextField('my-text-field','','Type something').addButton('my-button','Button').addTextArea('my-ta','displayed','type here').addCheckBox('my-cb','Hey CB','x').addRadioButton('my-rb','Hey RB','rb-val').addPersonSelector('my-ps','Select people',true).addTableSelect('my-ts',).addLineBreak().formatElement()Symphony.sendMessage('streamId',formML)

Advanced Configuration for Load Balancing

Create an additional configuration file to support load balancing. There are 3 supported types:

  • Round-Robin
  • Random
  • External

Round-robin and random load balancing are managed by this library based on the servers provided in the agentServers array.

External load-balancing is managed by an external DNS, cloud provider or hardware-based solution. List only one load balancer frontend hostname in the agentServers array (subsequent server entries for the external method are ignored).

Note: that this method requires all underlying agent servers to implement an additional host.name switch with the current server's FQDN in their startup.sh script.

exec java $JAVA_OPTS -Dhost.name=sym-agent-01.my-company.com

There is also support for sticky sessions, which should be true for any bot that requires the datafeed loop. Using non-sticky load-balanced configuration with a datafeed loop will result in unexpected results.

{"loadBalancing": {"method": "random",// or roundrobin or external"stickySessions": true},"agentServers": ["sym-agent-01.my-company.com","sym-agent-02.my-company.com","sym-agent-03.my-company.com"]}

Loading advanced configuration

To load the configuration

Symphony.initBot(__dirname + '/config.json', __dirname + '/lb-config.json')

About

Symphony API Client for NodeJS

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Mar 28, 2022. It is now read-only.

Repository files navigation

⚠️ End-of-Life Notice ⚠️

Symphony will stop supporting the NodeJS SDK in the coming months, with the following timeline: In January 2022, only critical issues will be fixed in the legacy NodeJS SDK. In March 2022, the SDK will no longer be actively supported. At this date you will still be able to use the SDK, however, please consider our BDKs in Java, Python or .Net if you prefer to benefit from a better support.


symphony-api-client-node

Symphony API Client for NodeJS

Installation

npm install --save symphony-api-client-node

Usage

constSymphony=require('symphony-api-client-node')constonMessage=messages=>{messages.forEach(message=>{console.log('The BOT heard "'+message.messageText+'" from '+message.user.displayName)})}constonError=error=>{console.error('Error reading data feed',error)}Symphony.initBot(__dirname+'/config.json').then(()=>{Symphony.getDatafeedEventsService({ onMessage, onError })}).fail(err=>{console.error(err)})

Configuration

The config.json file is described on the Configuration page of the Symphony Developer Guide.

Create a config.json file in your project. Below is a sample configuration which includes the properties that can be configured:

{// Mandatory section"sessionAuthHost": "my-company-name-api.symphony.com","sessionAuthPort": 8444,"sessionAuthContextPath": "/app","keyAuthHost": "my-company-name-api.symphony.com","keyAuthPort": 8444,"keyAuthContextPath": "/app","podHost": "my-company-name.symphony.com","podPort": 443,"podContextPath": "/app","agentHost": "my-company-name.symphony.com","agentPort": 443,"agentContextPath": "/app",// For bots only"botUsername": "my-bot-name","botEmailAddress": "bot@company.com",// For bots using RSA authentication"botPrivateKeyPath": "/path/to/rsa/private-key/","botPrivateKeyName": "bot-private-key.pem",// For bots using certificate authentication"botCertPath": "/path/to/bot-cert/","botCertName": "bot-cert.p12","botCertPassword": "bot-cert-password",// For extension apps only"appId": "",// For extension apps using RSA authentication"appPrivateKeyPath": "","appPrivateKeyName": "",// For extension apps using certificate authentication"appCertPath": "/path/to/app-cert/","appCertName": "app-cert.p12","appCertPassword": "app-cert-password",// Optional: If the connection to the pod (but not the agent) needs to run through a proxy"podProxyURL": "http://localhost:3128","podProxyUsername": "proxy-username","podProxyPassword": "proxy-password",// Optional: If the connection to both the pod and the agent needs to run through a proxy// Do not include the podProxy properties if using this"proxyURL": "http://localhost:3128","proxyUsername": "proxy-username","proxyPassword": "proxy-password",// Optional: If the connection to the key manager needs to run through a proxy"keyManagerProxyURL": "http://localhost:3128","keyManagerProxyUsername": "proxy-username","keyManagerProxyPassword": "proxy-password",// Optional: Self Signed Certificates - Set to 0 to not reject invalid or self-signed certificates"nodeTlsRejectUnauthorized": 0,// Optional: maximum number of retries to reconnect to the agent, default value is 10"maxRetries": 10,// Optional: maximum waiting time between retries in the exponential backoff algorithm,// default is 64 seconds, like in Google Cloud Storage lib"maxWaitInterval": 64}

Datafeed resuming

Datafeeds buffer events on the agent up to a small limit. This allows bots to be restarted without missing any events, provided the datafeed ID is persisted and supplied when reconnecting.

constfs=require('fs')constlocalFile='/path/to/writable/file'// <- change thisconstloadId=()=>fs.readFileSync(localFile,{encoding: 'utf-8',flag: 'a+'})constsaveId=id=>fs.writeFile(localFile,id,err=>err&&console.error(err))constfeed=Symphony.getDatafeedEventsService({
onMessage,
onError,onCreated: saveId,feedId: loadId(),})

This simple example assumes that the file system is persisted across restarts which is not always the case (e.g. Docker containers). It is recommended to persist the datafeed ID to a database instead if available.

Shutting down a bot

There is a known issue that can cause messages to be lost when stopping and resuming a datafeed. To avoid this, the datafeed service must hook into Node's exit events and prevent the process from exiting until the data feed has closed cleanly (up to 30 seconds). This is done automatically if the NODE_ENV environment variable is set to 'production' and a datafeed 'created' listener has been registered (see above). In other cases the behaviour can be opted into by calling registerShutdownHooks() on the feed instance.

See Express Best Practices for examples of how to set environment variables.

Datafeed event types

As well as messages, the datafeed reports a number of other events. See the documentation for real time events. These can be accessed by adding corresponding event listeners to the feed service. For example:

Symphony.getDatafeedEventsService({
onMessage,
onError,onUserjoinedroom: events=>{for(leteventofevents){constinitiator=event.initiator.user.displayNameconstuser=event.payload.userJoinedRoom.affectedUser.displayNameconstroom=event.payload.userJoinedRoom.stream.roomNameconsole.log(`${initiator} added ${user} to ${room}`)}},})

Symphony Elements Example Usage

constmessageHandler=(event,messages)=>{messages.forEach((message,index)=>{letreply_message='Hello '+message.user.firstName+', hope you are doing well!!'Symphony.sendMessage(message.stream.streamId,reply_message,null,Symphony.MESSAGEML_FORMAT)})}constelementsHandler=(event,actions)=>{actions.forEach((action,index)=>{console.log(action)})}Symphony.initBot(__dirname+'/config.json').then((symAuth)=>{consthandlers={onMessageSent: messageHandler.bind(null,'MESSAGE_RECEIVED'),onSymphonyElementsAction: elementsHandler.bind(null,'ELEMENTS_ACTION_RECEIVED')}Symphony.getDatafeedEventsService(handlers)})

Symphony Elements Form Builder

constformML=Symphony.formBuilder('my-form').addDiv("hello").addLineBreaks(3).addHeader(6,"Yes").addTextField('my-text-field','','Type something').addButton('my-button','Button').addTextArea('my-ta','displayed','type here').addCheckBox('my-cb','Hey CB','x').addRadioButton('my-rb','Hey RB','rb-val').addPersonSelector('my-ps','Select people',true).addTableSelect('my-ts',).addLineBreak().formatElement()Symphony.sendMessage('streamId',formML)

Advanced Configuration for Load Balancing

Create an additional configuration file to support load balancing. There are 3 supported types:

  • Round-Robin
  • Random
  • External

Round-robin and random load balancing are managed by this library based on the servers provided in the agentServers array.

External load-balancing is managed by an external DNS, cloud provider or hardware-based solution. List only one load balancer frontend hostname in the agentServers array (subsequent server entries for the external method are ignored).

Note: that this method requires all underlying agent servers to implement an additional host.name switch with the current server's FQDN in their startup.sh script.

exec java $JAVA_OPTS -Dhost.name=sym-agent-01.my-company.com

There is also support for sticky sessions, which should be true for any bot that requires the datafeed loop. Using non-sticky load-balanced configuration with a datafeed loop will result in unexpected results.

{"loadBalancing": {"method": "random",// or roundrobin or external"stickySessions": true},"agentServers": ["sym-agent-01.my-company.com","sym-agent-02.my-company.com","sym-agent-03.my-company.com"]}

Loading advanced configuration

To load the configuration

Symphony.initBot(__dirname + '/config.json', __dirname + '/lb-config.json')

About

Symphony API Client for NodeJS

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
This repository was archived by the owner on Mar 28, 2022. It is now read-only.

Repository files navigation

⚠️ End-of-Life Notice ⚠️

Symphony will stop supporting the NodeJS SDK in the coming months, with the following timeline: In January 2022, only critical issues will be fixed in the legacy NodeJS SDK. In March 2022, the SDK will no longer be actively supported. At this date you will still be able to use the SDK, however, please consider our BDKs in Java, Python or .Net if you prefer to benefit from a better support.


symphony-api-client-node

Symphony API Client for NodeJS

Installation

npm install --save symphony-api-client-node

Usage

constSymphony=require('symphony-api-client-node')constonMessage=messages=>{messages.forEach(message=>{console.log('The BOT heard "'+message.messageText+'" from '+message.user.displayName)})}constonError=error=>{console.error('Error reading data feed',error)}Symphony.initBot(__dirname+'/config.json').then(()=>{Symphony.getDatafeedEventsService({ onMessage, onError })}).fail(err=>{console.error(err)})

Configuration

The config.json file is described on the Configuration page of the Symphony Developer Guide.

Create a config.json file in your project. Below is a sample configuration which includes the properties that can be configured:

{// Mandatory section"sessionAuthHost": "my-company-name-api.symphony.com","sessionAuthPort": 8444,"sessionAuthContextPath": "/app","keyAuthHost": "my-company-name-api.symphony.com","keyAuthPort": 8444,"keyAuthContextPath": "/app","podHost": "my-company-name.symphony.com","podPort": 443,"podContextPath": "/app","agentHost": "my-company-name.symphony.com","agentPort": 443,"agentContextPath": "/app",// For bots only"botUsername": "my-bot-name","botEmailAddress": "bot@company.com",// For bots using RSA authentication"botPrivateKeyPath": "/path/to/rsa/private-key/","botPrivateKeyName": "bot-private-key.pem",// For bots using certificate authentication"botCertPath": "/path/to/bot-cert/","botCertName": "bot-cert.p12","botCertPassword": "bot-cert-password",// For extension apps only"appId": "",// For extension apps using RSA authentication"appPrivateKeyPath": "","appPrivateKeyName": "",// For extension apps using certificate authentication"appCertPath": "/path/to/app-cert/","appCertName": "app-cert.p12","appCertPassword": "app-cert-password",// Optional: If the connection to the pod (but not the agent) needs to run through a proxy"podProxyURL": "http://localhost:3128","podProxyUsername": "proxy-username","podProxyPassword": "proxy-password",// Optional: If the connection to both the pod and the agent needs to run through a proxy// Do not include the podProxy properties if using this"proxyURL": "http://localhost:3128","proxyUsername": "proxy-username","proxyPassword": "proxy-password",// Optional: If the connection to the key manager needs to run through a proxy"keyManagerProxyURL": "http://localhost:3128","keyManagerProxyUsername": "proxy-username","keyManagerProxyPassword": "proxy-password",// Optional: Self Signed Certificates - Set to 0 to not reject invalid or self-signed certificates"nodeTlsRejectUnauthorized": 0,// Optional: maximum number of retries to reconnect to the agent, default value is 10"maxRetries": 10,// Optional: maximum waiting time between retries in the exponential backoff algorithm,// default is 64 seconds, like in Google Cloud Storage lib"maxWaitInterval": 64}

Datafeed resuming

Datafeeds buffer events on the agent up to a small limit. This allows bots to be restarted without missing any events, provided the datafeed ID is persisted and supplied when reconnecting.

constfs=require('fs')constlocalFile='/path/to/writable/file'// <- change thisconstloadId=()=>fs.readFileSync(localFile,{encoding: 'utf-8',flag: 'a+'})constsaveId=id=>fs.writeFile(localFile,id,err=>err&&console.error(err))constfeed=Symphony.getDatafeedEventsService({
onMessage,
onError,onCreated: saveId,feedId: loadId(),})

This simple example assumes that the file system is persisted across restarts which is not always the case (e.g. Docker containers). It is recommended to persist the datafeed ID to a database instead if available.

Shutting down a bot

There is a known issue that can cause messages to be lost when stopping and resuming a datafeed. To avoid this, the datafeed service must hook into Node's exit events and prevent the process from exiting until the data feed has closed cleanly (up to 30 seconds). This is done automatically if the NODE_ENV environment variable is set to 'production' and a datafeed 'created' listener has been registered (see above). In other cases the behaviour can be opted into by calling registerShutdownHooks() on the feed instance.

See Express Best Practices for examples of how to set environment variables.

Datafeed event types

As well as messages, the datafeed reports a number of other events. See the documentation for real time events. These can be accessed by adding corresponding event listeners to the feed service. For example:

Symphony.getDatafeedEventsService({
onMessage,
onError,onUserjoinedroom: events=>{for(leteventofevents){constinitiator=event.initiator.user.displayNameconstuser=event.payload.userJoinedRoom.affectedUser.displayNameconstroom=event.payload.userJoinedRoom.stream.roomNameconsole.log(`${initiator} added ${user} to ${room}`)}},})

Symphony Elements Example Usage

constmessageHandler=(event,messages)=>{messages.forEach((message,index)=>{letreply_message='Hello '+message.user.firstName+', hope you are doing well!!'Symphony.sendMessage(message.stream.streamId,reply_message,null,Symphony.MESSAGEML_FORMAT)})}constelementsHandler=(event,actions)=>{actions.forEach((action,index)=>{console.log(action)})}Symphony.initBot(__dirname+'/config.json').then((symAuth)=>{consthandlers={onMessageSent: messageHandler.bind(null,'MESSAGE_RECEIVED'),onSymphonyElementsAction: elementsHandler.bind(null,'ELEMENTS_ACTION_RECEIVED')}Symphony.getDatafeedEventsService(handlers)})

Symphony Elements Form Builder

constformML=Symphony.formBuilder('my-form').addDiv("hello").addLineBreaks(3).addHeader(6,"Yes").addTextField('my-text-field','','Type something').addButton('my-button','Button').addTextArea('my-ta','displayed','type here').addCheckBox('my-cb','Hey CB','x').addRadioButton('my-rb','Hey RB','rb-val').addPersonSelector('my-ps','Select people',true).addTableSelect('my-ts',).addLineBreak().formatElement()Symphony.sendMessage('streamId',formML)

Advanced Configuration for Load Balancing

Create an additional configuration file to support load balancing. There are 3 supported types:

  • Round-Robin
  • Random
  • External

Round-robin and random load balancing are managed by this library based on the servers provided in the agentServers array.

External load-balancing is managed by an external DNS, cloud provider or hardware-based solution. List only one load balancer frontend hostname in the agentServers array (subsequent server entries for the external method are ignored).

Note: that this method requires all underlying agent servers to implement an additional host.name switch with the current server's FQDN in their startup.sh script.

exec java $JAVA_OPTS -Dhost.name=sym-agent-01.my-company.com

There is also support for sticky sessions, which should be true for any bot that requires the datafeed loop. Using non-sticky load-balanced configuration with a datafeed loop will result in unexpected results.

{"loadBalancing": {"method": "random",// or roundrobin or external"stickySessions": true},"agentServers": ["sym-agent-01.my-company.com","sym-agent-02.my-company.com","sym-agent-03.my-company.com"]}

Loading advanced configuration

To load the configuration

Symphony.initBot(__dirname + '/config.json', __dirname + '/lb-config.json')

About

Symphony API Client for NodeJS

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages