commit a44448d380493f1177736df6df23da001aed4e69 Author: bendwit <184859846+BenDWit@users.noreply.github.com> Date: Mon Jul 21 12:14:01 2025 +0200 le epic diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..cff7816 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +root = true + +[*] +charset = utf-8 +indent_style = tab +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[package.json] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..c84e3fd --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,146 @@ +/** + * @type {import('@types/eslint').ESLint.ConfigData} + */ +module.exports = { + root: true, + + env: { + browser: true, + es6: true, + node: true, + }, + + parser: '@typescript-eslint/parser', + + parserOptions: { + project: ['./tsconfig.json'], + sourceType: 'module', + extraFileExtensions: ['.json'], + }, + + ignorePatterns: ['.eslintrc.js', '**/*.js', '**/node_modules/**', '**/dist/**'], + + overrides: [ + { + files: ['package.json'], + plugins: ['eslint-plugin-n8n-nodes-base'], + extends: ['plugin:n8n-nodes-base/community'], + rules: { + 'n8n-nodes-base/community-package-json-name-still-default': 'off', + }, + }, + { + files: ['./credentials/**/*.ts'], + plugins: ['eslint-plugin-n8n-nodes-base'], + extends: ['plugin:n8n-nodes-base/credentials'], + rules: { + 'n8n-nodes-base/cred-class-field-authenticate-type-assertion': 'error', + 'n8n-nodes-base/cred-class-field-display-name-missing-oauth2': 'error', + 'n8n-nodes-base/cred-class-field-display-name-miscased': 'error', + 'n8n-nodes-base/cred-class-field-documentation-url-missing': 'error', + 'n8n-nodes-base/cred-class-field-documentation-url-miscased': 'off', + 'n8n-nodes-base/cred-class-field-name-missing-oauth2': 'error', + 'n8n-nodes-base/cred-class-field-name-unsuffixed': 'error', + 'n8n-nodes-base/cred-class-field-name-uppercase-first-char': 'error', + 'n8n-nodes-base/cred-class-field-properties-assertion': 'error', + 'n8n-nodes-base/cred-class-field-type-options-password-missing': 'error', + 'n8n-nodes-base/cred-class-name-missing-oauth2-suffix': 'error', + 'n8n-nodes-base/cred-class-name-unsuffixed': 'error', + 'n8n-nodes-base/cred-filename-against-convention': 'error', + }, + }, + { + files: ['./nodes/**/*.ts'], + plugins: ['eslint-plugin-n8n-nodes-base'], + extends: ['plugin:n8n-nodes-base/nodes'], + rules: { + 'n8n-nodes-base/node-class-description-credentials-name-unsuffixed': 'error', + 'n8n-nodes-base/node-class-description-display-name-unsuffixed-trigger-node': 'error', + 'n8n-nodes-base/node-class-description-empty-string': 'error', + 'n8n-nodes-base/node-class-description-icon-not-svg': 'error', + 'n8n-nodes-base/node-class-description-inputs-wrong-regular-node': 'off', + 'n8n-nodes-base/node-class-description-inputs-wrong-trigger-node': 'error', + 'n8n-nodes-base/node-class-description-missing-subtitle': 'error', + 'n8n-nodes-base/node-class-description-non-core-color-present': 'error', + 'n8n-nodes-base/node-class-description-name-miscased': 'error', + 'n8n-nodes-base/node-class-description-name-unsuffixed-trigger-node': 'error', + 'n8n-nodes-base/node-class-description-outputs-wrong': 'off', + 'n8n-nodes-base/node-dirname-against-convention': 'error', + 'n8n-nodes-base/node-execute-block-double-assertion-for-items': 'error', + 'n8n-nodes-base/node-execute-block-wrong-error-thrown': 'error', + 'n8n-nodes-base/node-filename-against-convention': 'error', + 'n8n-nodes-base/node-param-array-type-assertion': 'error', + 'n8n-nodes-base/node-param-color-type-unused': 'error', + 'n8n-nodes-base/node-param-default-missing': 'error', + 'n8n-nodes-base/node-param-default-wrong-for-boolean': 'error', + 'n8n-nodes-base/node-param-default-wrong-for-collection': 'error', + 'n8n-nodes-base/node-param-default-wrong-for-fixed-collection': 'error', + 'n8n-nodes-base/node-param-default-wrong-for-fixed-collection': 'error', + 'n8n-nodes-base/node-param-default-wrong-for-multi-options': 'error', + 'n8n-nodes-base/node-param-default-wrong-for-number': 'error', + 'n8n-nodes-base/node-param-default-wrong-for-simplify': 'error', + 'n8n-nodes-base/node-param-default-wrong-for-string': 'error', + 'n8n-nodes-base/node-param-description-boolean-without-whether': 'error', + 'n8n-nodes-base/node-param-description-comma-separated-hyphen': 'error', + 'n8n-nodes-base/node-param-description-empty-string': 'error', + 'n8n-nodes-base/node-param-description-excess-final-period': 'error', + 'n8n-nodes-base/node-param-description-excess-inner-whitespace': 'error', + 'n8n-nodes-base/node-param-description-identical-to-display-name': 'error', + 'n8n-nodes-base/node-param-description-line-break-html-tag': 'error', + 'n8n-nodes-base/node-param-description-lowercase-first-char': 'error', + 'n8n-nodes-base/node-param-description-miscased-id': 'error', + 'n8n-nodes-base/node-param-description-miscased-json': 'error', + 'n8n-nodes-base/node-param-description-miscased-url': 'error', + 'n8n-nodes-base/node-param-description-missing-final-period': 'error', + 'n8n-nodes-base/node-param-description-missing-for-ignore-ssl-issues': 'error', + 'n8n-nodes-base/node-param-description-missing-for-return-all': 'error', + 'n8n-nodes-base/node-param-description-missing-for-simplify': 'error', + 'n8n-nodes-base/node-param-description-missing-from-dynamic-multi-options': 'error', + 'n8n-nodes-base/node-param-description-missing-from-dynamic-options': 'error', + 'n8n-nodes-base/node-param-description-missing-from-limit': 'error', + 'n8n-nodes-base/node-param-description-unencoded-angle-brackets': 'error', + 'n8n-nodes-base/node-param-description-unneeded-backticks': 'error', + 'n8n-nodes-base/node-param-description-untrimmed': 'error', + 'n8n-nodes-base/node-param-description-url-missing-protocol': 'error', + 'n8n-nodes-base/node-param-description-weak': 'error', + 'n8n-nodes-base/node-param-description-wrong-for-dynamic-multi-options': 'error', + 'n8n-nodes-base/node-param-description-wrong-for-dynamic-options': 'error', + 'n8n-nodes-base/node-param-description-wrong-for-ignore-ssl-issues': 'error', + 'n8n-nodes-base/node-param-description-wrong-for-limit': 'error', + 'n8n-nodes-base/node-param-description-wrong-for-return-all': 'error', + 'n8n-nodes-base/node-param-description-wrong-for-simplify': 'error', + 'n8n-nodes-base/node-param-description-wrong-for-upsert': 'error', + 'n8n-nodes-base/node-param-display-name-excess-inner-whitespace': 'error', + 'n8n-nodes-base/node-param-display-name-miscased-id': 'error', + 'n8n-nodes-base/node-param-display-name-miscased': 'error', + 'n8n-nodes-base/node-param-display-name-not-first-position': 'error', + 'n8n-nodes-base/node-param-display-name-untrimmed': 'error', + 'n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options': 'error', + 'n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options': 'error', + 'n8n-nodes-base/node-param-display-name-wrong-for-simplify': 'error', + 'n8n-nodes-base/node-param-display-name-wrong-for-update-fields': 'error', + 'n8n-nodes-base/node-param-min-value-wrong-for-limit': 'error', + 'n8n-nodes-base/node-param-multi-options-type-unsorted-items': 'error', + 'n8n-nodes-base/node-param-name-untrimmed': 'error', + 'n8n-nodes-base/node-param-operation-option-action-wrong-for-get-many': 'error', + 'n8n-nodes-base/node-param-operation-option-description-wrong-for-get-many': 'error', + 'n8n-nodes-base/node-param-operation-option-without-action': 'error', + 'n8n-nodes-base/node-param-operation-without-no-data-expression': 'error', + 'n8n-nodes-base/node-param-option-description-identical-to-name': 'error', + 'n8n-nodes-base/node-param-option-name-containing-star': 'error', + 'n8n-nodes-base/node-param-option-name-duplicate': 'error', + 'n8n-nodes-base/node-param-option-name-wrong-for-get-many': 'error', + 'n8n-nodes-base/node-param-option-name-wrong-for-upsert': 'error', + 'n8n-nodes-base/node-param-option-value-duplicate': 'error', + 'n8n-nodes-base/node-param-options-type-unsorted-items': 'error', + 'n8n-nodes-base/node-param-placeholder-miscased-id': 'error', + 'n8n-nodes-base/node-param-placeholder-missing-email': 'error', + 'n8n-nodes-base/node-param-required-false': 'error', + 'n8n-nodes-base/node-param-resource-with-plural-option': 'error', + 'n8n-nodes-base/node-param-resource-without-no-data-expression': 'error', + 'n8n-nodes-base/node-param-type-options-missing-from-limit': 'error', + 'n8n-nodes-base/node-param-type-options-password-missing': 'error', + }, + }, + ], +}; diff --git a/.eslintrc.prepublish.js b/.eslintrc.prepublish.js new file mode 100644 index 0000000..2d319f0 --- /dev/null +++ b/.eslintrc.prepublish.js @@ -0,0 +1,16 @@ +/** + * @type {import('@types/eslint').ESLint.ConfigData} + */ +module.exports = { + extends: "./.eslintrc.js", + + overrides: [ + { + files: ['package.json'], + plugins: ['eslint-plugin-n8n-nodes-base'], + rules: { + 'n8n-nodes-base/community-package-json-name-still-default': 'error', + }, + }, + ], +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e72963 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules +.DS_Store +.tmp +tmp +dist +npm-debug.log* +yarn.lock +.vscode/launch.json diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..a7ac611 --- /dev/null +++ b/.npmignore @@ -0,0 +1,2 @@ +.DS_Store +*.tsbuildinfo diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..ebf28d8 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,51 @@ +module.exports = { + /** + * https://prettier.io/docs/en/options.html#semicolons + */ + semi: true, + + /** + * https://prettier.io/docs/en/options.html#trailing-commas + */ + trailingComma: 'all', + + /** + * https://prettier.io/docs/en/options.html#bracket-spacing + */ + bracketSpacing: true, + + /** + * https://prettier.io/docs/en/options.html#tabs + */ + useTabs: true, + + /** + * https://prettier.io/docs/en/options.html#tab-width + */ + tabWidth: 2, + + /** + * https://prettier.io/docs/en/options.html#arrow-function-parentheses + */ + arrowParens: 'always', + + /** + * https://prettier.io/docs/en/options.html#quotes + */ + singleQuote: true, + + /** + * https://prettier.io/docs/en/options.html#quote-props + */ + quoteProps: 'as-needed', + + /** + * https://prettier.io/docs/en/options.html#end-of-line + */ + endOfLine: 'lf', + + /** + * https://prettier.io/docs/en/options.html#print-width + */ + printWidth: 100, +}; diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..306a6af --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "EditorConfig.EditorConfig", + "esbenp.prettier-vscode", + ] +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3b0de04 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at jan@n8n.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..1e4b3a6 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,19 @@ +Copyright 2022 n8n + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c672021 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +![Banner image](https://user-images.githubusercontent.com/10284570/173569848-c624317f-42b1-45a6-ab09-f0ea3c247648.png) + +# n8n-nodes-starter + +This repo contains example nodes to help you get started building your own custom integrations for [n8n](https://n8n.io). It includes the node linter and other dependencies. + +To make your custom node available to the community, you must create it as an npm package, and [submit it to the npm registry](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry). + +If you would like your node to be available on n8n cloud you can also [submit your node for verification](https://docs.n8n.io/integrations/creating-nodes/deploy/submit-community-nodes/). + +## Prerequisites + +You need the following installed on your development machine: + +* [git](https://git-scm.com/downloads) +* Node.js and npm. Minimum version Node 20. You can find instructions on how to install both using nvm (Node Version Manager) for Linux, Mac, and WSL [here](https://github.com/nvm-sh/nvm). For Windows users, refer to Microsoft's guide to [Install NodeJS on Windows](https://docs.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-windows). +* Install n8n with: + ``` + npm install n8n -g + ``` +* Recommended: follow n8n's guide to [set up your development environment](https://docs.n8n.io/integrations/creating-nodes/build/node-development-environment/). + +## Using this starter + +These are the basic steps for working with the starter. For detailed guidance on creating and publishing nodes, refer to the [documentation](https://docs.n8n.io/integrations/creating-nodes/). + +1. [Generate a new repository](https://github.com/n8n-io/n8n-nodes-starter/generate) from this template repository. +2. Clone your new repo: + ``` + git clone https://github.com//.git + ``` +3. Run `npm i` to install dependencies. +4. Open the project in your editor. +5. Browse the examples in `/nodes` and `/credentials`. Modify the examples, or replace them with your own nodes. +6. Update the `package.json` to match your details. +7. Run `npm run lint` to check for errors or `npm run lintfix` to automatically fix errors when possible. +8. Test your node locally. Refer to [Run your node locally](https://docs.n8n.io/integrations/creating-nodes/test/run-node-locally/) for guidance. +9. Replace this README with documentation for your node. Use the [README_TEMPLATE](README_TEMPLATE.md) to get started. +10. Update the LICENSE file to use your details. +11. [Publish](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry) your package to npm. + +## More information + +Refer to our [documentation on creating nodes](https://docs.n8n.io/integrations/creating-nodes/) for detailed information on building your own nodes. + +## License + +[MIT](https://github.com/n8n-io/n8n-nodes-starter/blob/master/LICENSE.md) diff --git a/README_TEMPLATE.md b/README_TEMPLATE.md new file mode 100644 index 0000000..a8d7860 --- /dev/null +++ b/README_TEMPLATE.md @@ -0,0 +1,48 @@ +# n8n-nodes-_node-name_ + +This is an n8n community node. It lets you use _app/service name_ in your n8n workflows. + +_App/service name_ is _one or two sentences describing the service this node integrates with_. + +[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform. + +[Installation](#installation) +[Operations](#operations) +[Credentials](#credentials) +[Compatibility](#compatibility) +[Usage](#usage) +[Resources](#resources) +[Version history](#version-history) + +## Installation + +Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation. + +## Operations + +_List the operations supported by your node._ + +## Credentials + +_If users need to authenticate with the app/service, provide details here. You should include prerequisites (such as signing up with the service), available authentication methods, and how to set them up._ + +## Compatibility + +_State the minimum n8n version, as well as which versions you test against. You can also include any known version incompatibility issues._ + +## Usage + +_This is an optional section. Use it to help users with any difficult or confusing aspects of the node._ + +_By the time users are looking for community nodes, they probably already know n8n basics. But if you expect new users, you can link to the [Try it out](https://docs.n8n.io/try-it-out/) documentation to help them get started._ + +## Resources + +* [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes) +* _Link to app/service documentation._ + +## Version history + +_This is another optional section. If your node has multiple versions, include a short description of available versions and what changed, as well as any compatibility impact._ + + diff --git a/credentials/XmppApi.credentials.ts b/credentials/XmppApi.credentials.ts new file mode 100644 index 0000000..4aaf2bb --- /dev/null +++ b/credentials/XmppApi.credentials.ts @@ -0,0 +1,43 @@ +import { + IAuthenticateGeneric, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; + +export class XmppApi implements ICredentialType { + name = 'xmppApi'; + displayName = 'XMPP API'; + properties: INodeProperties[] = [ + { + displayName: 'Domain', + name: 'domain', + type: 'string', + default: '', + required: true, + }, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + required: true, + }, + { + displayName: 'Password', + name: 'password', + type: 'string', + typeOptions: { + password: true, + }, + default: '', + required: true, + }, + { + displayName: 'Port', + name: 'port', + type: 'number', + default: 5222, + }, + ]; +} diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..831c707 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,16 @@ +const path = require('path'); +const { task, src, dest } = require('gulp'); + +task('build:icons', copyIcons); + +function copyIcons() { + const nodeSource = path.resolve('nodes', '**', '*.{png,svg}'); + const nodeDestination = path.resolve('dist', 'nodes'); + + src(nodeSource).pipe(dest(nodeDestination)); + + const credSource = path.resolve('credentials', '**', '*.{png,svg}'); + const credDestination = path.resolve('dist', 'credentials'); + + return src(credSource).pipe(dest(credDestination)); +} diff --git a/index.js b/index.js new file mode 100644 index 0000000..e69de29 diff --git a/nodes/Xmpp/Xmpp.node.ts b/nodes/Xmpp/Xmpp.node.ts new file mode 100644 index 0000000..b399543 --- /dev/null +++ b/nodes/Xmpp/Xmpp.node.ts @@ -0,0 +1,139 @@ +import { IExecuteFunctions } from 'n8n-core'; +import { + INodeExecutionData, + INodeType, + INodeTypeDescription, + NodeOperationError, +} from 'n8n-workflow'; +import { client, xml } from '@xmpp/client'; + +export class Xmpp implements INodeType { + description: INodeTypeDescription = { + displayName: 'XMPP', + name: 'xmpp', + icon: 'file:xmpp.svg', + group: ['communication'], + version: 1, + subtitle: '={{$parameter["operation"]}}', + description: 'Send messages via XMPP protocol', + defaults: { + name: 'XMPP', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'xmppApi', + required: true, + }, + ], + requestDefaults: { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + }, + properties: [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: false, + options: [ + { + name: 'Send Message', + value: 'sendMessage', + description: 'Send a message to a user or room', + action: 'Send a message', + }, + ], + default: 'sendMessage', + }, + { + displayName: 'To', + name: 'to', + type: 'string', + default: '', + placeholder: 'user@domain.com', + description: 'Recipient JID', + required: true, + displayOptions: { + show: { + operation: ['sendMessage'], + }, + }, + }, + { + displayName: 'Message', + name: 'message', + type: 'string', + default: '', + description: 'Message to send', + required: true, + displayOptions: { + show: { + operation: ['sendMessage'], + }, + }, + }, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: INodeExecutionData[] = []; + + for (let i = 0; i < items.length; i++) { + try { + const operation = this.getNodeParameter('operation', i) as string; + const credentials = await this.getCredentials('xmppApi', i); + + if (operation === 'sendMessage') { + const to = this.getNodeParameter('to', i) as string; + const message = this.getNodeParameter('message', i) as string; + + // Create XMPP client + const xmppClient = client({ + service: `xmpp://${credentials.domain}:${credentials.port}`, + username: credentials.username, + password: credentials.password, + }); + + // Connect + await xmppClient.start(); + + // Send message + const messageStanza = xml( + 'message', + { to, type: 'chat' }, + xml('body', {}, message), + ); + + await xmppClient.send(messageStanza); + await xmppClient.stop(); + + returnData.push({ + json: { + success: true, + to, + message, + timestamp: new Date().toISOString(), + }, + }); + } + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ + json: { + error: error.message, + }, + }); + continue; + } + throw new NodeOperationError(this.getNode(), error); + } + } + + return [returnData]; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..98625ba --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "n8n-nodes-xmpp", + "version": "1.0.0", + "description": "n8n node for XMPP (Prosody) communication", + "keywords": [ + "n8n-community-node-package" + ], + "license": "MIT", + "homepage": "", + "author": { + "name": "Ben de Wit", + "email": "ben@bendwit.com" + }, + "repository": { + "type": "git", + "url": "" + }, + "main": "index.js", + "scripts": { + "build": "tsc && gulp build:icons", + "dev": "tsc --watch", + "format": "prettier nodes credentials --write", + "lint": "eslint nodes credentials package.json", + "lintfix": "eslint nodes credentials package.json --fix", + "prepublishOnly": "npm run build && npm run lint -s" + }, + "files": [ + "dist" + ], + "n8n": { + "n8nNodesApiVersion": 1, + "credentials": [ + "dist/credentials/XmppApi.credentials.js" + ], + "nodes": [ + "dist/nodes/Xmpp/Xmpp.node.js", + "dist/nodes/XmppTrigger/XmppTrigger.node.js" + ] + }, + "devDependencies": { + "@typescript-eslint/parser": "^5.45.0", + "eslint-plugin-n8n-nodes-base": "^1.11.0", + "n8n-workflow": "*", + "prettier": "^2.7.1", + "typescript": "^4.8.4" + }, + "dependencies": { + "@xmpp/client": "^0.13.1", + "@xmpp/xml": "^0.13.1", + "@xmpp/jid": "^0.13.1" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7469d24 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "strict": true, + "module": "commonjs", + "moduleResolution": "node", + "target": "es2019", + "lib": ["es2019", "es2020", "es2022.error"], + "removeComments": true, + "useUnknownInCatchVariables": false, + "forceConsistentCasingInFileNames": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "strictNullChecks": true, + "preserveConstEnums": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "incremental": true, + "declaration": true, + "sourceMap": true, + "skipLibCheck": true, + "outDir": "./dist/", + }, + "include": [ + "credentials/**/*", + "nodes/**/*", + "nodes/**/*.json", + "package.json", + ], +}