2 |
3 |
7 |
21 | Trigger workflows on stream start or stream end, search for Twitch streams details… 22 |
23 | 24 |25 | Stars welcomed 😊 26 |
27 | 28 | # 👀 n8n Twitch node features 29 | 30 | Once installed, you will be able to add Twitch triggers and actions to your n8n workflows. 31 | 32 | 1. Search for Twitch node: 33 | 34 |
35 |
36 | 2. Select the desired action or trigger:
37 |
38 |
39 |
40 | 3. Parametrize it:
41 |
42 |
43 |
44 | # 🚀 Installation instructions
45 |
46 | This node is in the process to be officially verified by n8n.
47 | The installation process will be as simple as searching for "Twitch" in the nodes panel once we get that verification,
48 | but in the meantime, you have several options that depend on how you use n8n.
49 |
50 | We recommend checking out the [updated n8n instructions
51 | on how to install community nodes](https://docs.n8n.io/integrations/community-nodes/installation/) for possible updates to this process.
52 |
53 | ## a) n8n cloud instance
54 |
55 | It is not possible to install unverified community nodes in n8n cloud ([documentation](https://docs.n8n.io/integrations/community-nodes/installation/)).
56 | Once we get that verification, you will be able to install this node following [this step by step](https://docs.n8n.io/integrations/community-nodes/installation/verified-install/).
57 |
58 | ## b) Self-hosted n8n instance
59 |
60 | ### b.a) Not using queue mode: GUI installation
61 |
62 | Follow [the official instructions](https://docs.n8n.io/integrations/community-nodes/installation/gui-install/)
63 | specifying `@codelytv/n8n-nodes-twitch` as the node name to install:
64 |
65 |
66 |
67 | ## b.b) Using queue mode
68 |
69 | ### b.b.a) Install as npm package
70 |
71 | This is the officially recommended way for self-hosted n8n instances running in queue mode ([documentation](https://docs.n8n.io/integrations/community-nodes/installation/manual-install/).
72 |
73 | Go to the folder where n8n is installed (if you are using the standard Docker installation, it will probably be:
74 | `/usr/local/lib/node_modules/n8n`) and install the package as any other npm package:
75 |
76 | ```bash
77 | npm i @codelytv/n8n-nodes-twitch
78 | ```
79 |
80 | ### b.b.b) Install as Custom Docker image
81 |
82 | `Dockerfile` contents example for a custom image with this node added:
83 |
84 | ```dockerfile
85 | ARG N8N_VERSION
86 | FROM n8nio/n8n:${N8N_VERSION}
87 |
88 | RUN if [ -z "$N8N_VERSION" ]; then echo "💥 N8N_VERSION argument missing."; exit 1; fi && \
89 | mkdir -p /home/node/.n8n/nodes && \
90 | npm install --prefix /home/node/.n8n/nodes --production --silent @codelytv/n8n-nodes-twitch
91 | ```
92 |
93 | ### b.b.c) Install using Docker Compose / Docker Swarm with mapped volume
94 |
95 | Take into account that this option has a considerable downside:
96 | The workflows you create will contain `CUSTOM.twitchTrigger` as the node type reference instead of `@codelytv/n8n-nodes-twitch.twitchTrigger`. However, it could be the best approach if you want a faster feedback loop while developing.
97 | Take into account that localhost will not be reachable from Twitch, so you `probably are interested into exposing it with a tunnel using something like `cloudflared`, or just expose a remote host to Twitch.
98 |
99 | Docker Compose / Docker Swarm definition snippet:
100 |
101 | ```yaml
102 | volumes:
103 | n8n_data:
104 | name: '{{.Service.Name}}_{{.Task.Slot}}'
105 |
106 | services:
107 | n8n-main:
108 | volumes:
109 | - n8n_data:/home/node/.n8n
110 | - /home/codely/n8n-custom-nodes:/home/node/.n8n/custom
111 | ```
112 |
113 | Deploy process:
114 |
115 | ```bash
116 | CUSTOM_NODES_DIR="$HOME/n8n-custom-nodes"
117 |
118 | mkdir -p "$CUSTOM_NODES_DIR"
119 |
120 | docker run --rm \
121 | --user "$(id -u):$(id -g)" \
122 | -v "$CUSTOM_NODES_DIR":/data \
123 | -w /data \
124 | node:22-alpine \
125 | sh -c "npm install @codelytv/n8n-nodes-twitch --production --silent"
126 |
127 | docker stack deploy -c n8n-swarm.yml n8n
128 | ```
129 |
130 | # 🔑 How to get Twitch credentials
131 |
132 | You will need to create a new Twitch application to get Client ID and Client Secret following these steps:
133 |
134 | 1. Go to the [Twitch Developer Console](https://dev.twitch.tv/console/apps).
135 | 2. Log in using your Twitch account credentials.
136 | 3. Click on the "+ Register Your Application" button.
137 | 4. Fill out the form as follows and click "Create":
138 | - Name: Name your app (e.g., “n8nTwitchBot”).
139 | - OAuth Redirect URLs: Use a valid redirect URL.
140 | Something like http://localhost:5678/rest/oauth2-credential/callback works.
141 | We do not plan to display Twitch authentication to end users with Oauth.
142 | We're only interested in getting the Client ID and Client Secret, so it's fine to specify a local URL.
143 | - Category: Application Integration
144 | - Client Type: Confidential
145 | 5. Get your credentials:
146 | - Click on "Manage"
147 | - Client ID: Visible right away.
148 | - Client Secret: Click "New Secret" to generate one. Be sure to store this securely (it won’t be shown again).
149 |
150 | # 💻 Documentation for node contributors
151 |
152 | How to locally test this node (based on [the official n8n guide](https://docs.n8n.io/integrations/creating-nodes/test/run-node-locally/)):
153 |
154 | 1. Clone and move to the node development folder
155 | ```bash
156 | cd ~/Code/work/codely/public/
157 | git clone git@github.com:CodelyTV/n8n-nodes-twitch.git
158 | cd n8n-nodes-twitch
159 | ```
160 | 2. Build the node
161 | ```bash
162 | npm run build
163 | ```
164 | 3. Create a npm global symlink to the locally installed package
165 | ```bash
166 | npm link
167 | ```
168 | 4. Install n8n locally:
169 | ```bash
170 | npm install n8n -g
171 | ```
172 | 5. Move to your n8n local installation
173 | ```bash
174 | cd ~/.n8n/
175 | ```
176 | 6. Create a custom nodes folder
177 | ```bash
178 | mkdir custom
179 | cd custom
180 | 7. Link the node package to the symlink previously created
181 | ```bash
182 | npm link @codelytv/n8n-nodes-twitch
183 | ```
184 | 8. Validate that the local n8n instance has the Twitch node pointing to the local folder
185 | ```bash
186 | tree -L 3 -d
187 | ```
188 | Expected output:
189 | ```bash
190 | .
191 | └── node_modules
192 | └── @codelytv
193 | └── n8n-nodes-twitch -> ../../../../Code/work/codely/public/n8n-nodes-twitch
194 | ```
195 | 9. Run n8n
196 | ```bash
197 | n8n start
198 | ```
199 | 10. Enjoy!
200 |
201 | # 👌 Codely Code Quality Standards
202 |
203 | Publishing this package we are committing ourselves to the following code quality standards:
204 |
205 | - 🤝 Respect **Semantic Versioning**: No breaking changes in patch or minor versions
206 | - 🤏 No surprises in transitive dependencies: Use the **bare minimum dependencies** needed to meet the purpose
207 | - 🎯 **One specific purpose** to meet without having to carry a bunch of unnecessary other utilities
208 | - 📖 **Well documented ReadMe** showing how to install and use
209 | - ⚖️ **License favoring Open Source** and collaboration
210 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import globals from "globals";
2 | import tseslint from "typescript-eslint";
3 | import n8nNodesBasePlugin from "eslint-plugin-n8n-nodes-base";
4 | import jsoncParser from "jsonc-eslint-parser";
5 | import jsoncPlugin from "eslint-plugin-jsonc";
6 |
7 | export default tseslint.config(
8 | {
9 | ignores: [
10 | "dist/**",
11 | "node_modules/**",
12 | "**/*.js",
13 | "!.eslintrc.js",
14 | "eslint.config.js"
15 | ]
16 | },
17 | {
18 | files: ["**/*.ts", "**/*.tsx"],
19 | languageOptions: {
20 | parser: tseslint.parser,
21 | parserOptions: {
22 | project: ["./tsconfig.json"],
23 | sourceType: "module",
24 | extraFileExtensions: [],
25 | },
26 | globals: {
27 | ...globals.browser,
28 | ...globals.es6,
29 | ...globals.node,
30 | },
31 | },
32 | plugins: {
33 | "@typescript-eslint": tseslint.plugin,
34 | },
35 | rules: {
36 | ...tseslint.configs.recommended.rules,
37 | },
38 | },
39 | {
40 | files: ["package.json"],
41 | languageOptions: {
42 | parser: jsoncParser,
43 | },
44 | plugins: {
45 | "jsonc": jsoncPlugin,
46 | "n8n-nodes-base": n8nNodesBasePlugin,
47 | },
48 | rules: {
49 | ...jsoncPlugin.configs["recommended-with-jsonc"].rules,
50 | ...n8nNodesBasePlugin.configs.community.rules,
51 | "n8n-nodes-base/community-package-json-name-still-default": "off",
52 | },
53 | },
54 | {
55 | files: ["./credentials/**/*.ts"],
56 | plugins: {
57 | "n8n-nodes-base": n8nNodesBasePlugin,
58 | },
59 | rules: {
60 | ...n8nNodesBasePlugin.configs.credentials.rules,
61 | "n8n-nodes-base/cred-class-field-authenticate-type-assertion": "error",
62 | "n8n-nodes-base/cred-class-field-display-name-missing-oauth2": "error",
63 | "n8n-nodes-base/cred-class-field-display-name-miscased": "error",
64 | "n8n-nodes-base/cred-class-field-documentation-url-missing": "error",
65 | "n8n-nodes-base/cred-class-field-documentation-url-miscased": "off",
66 | "n8n-nodes-base/cred-class-field-name-missing-oauth2": "error",
67 | "n8n-nodes-base/cred-class-field-name-unsuffixed": "error",
68 | "n8n-nodes-base/cred-class-field-name-uppercase-first-char": "error",
69 | "n8n-nodes-base/cred-class-field-properties-assertion": "error",
70 | "n8n-nodes-base/cred-class-field-type-options-password-missing": "error",
71 | "n8n-nodes-base/cred-class-name-missing-oauth2-suffix": "error",
72 | "n8n-nodes-base/cred-class-name-unsuffixed": "error",
73 | "n8n-nodes-base/cred-filename-against-convention": "error",
74 | },
75 | },
76 | {
77 | files: ["./nodes/**/*.ts"],
78 | plugins: {
79 | "n8n-nodes-base": n8nNodesBasePlugin,
80 | },
81 | rules: {
82 | ...n8nNodesBasePlugin.configs.nodes.rules,
83 | "n8n-nodes-base/node-class-description-credentials-name-unsuffixed": "error",
84 | "n8n-nodes-base/node-class-description-display-name-unsuffixed-trigger-node": "error",
85 | "n8n-nodes-base/node-class-description-empty-string": "error",
86 | "n8n-nodes-base/node-class-description-icon-not-svg": "error",
87 | "n8n-nodes-base/node-class-description-inputs-wrong-regular-node": "off",
88 | "n8n-nodes-base/node-class-description-inputs-wrong-trigger-node": "error",
89 | "n8n-nodes-base/node-class-description-missing-subtitle": "error",
90 | "n8n-nodes-base/node-class-description-non-core-color-present": "error",
91 | "n8n-nodes-base/node-class-description-name-miscased": "error",
92 | "n8n-nodes-base/node-class-description-name-unsuffixed-trigger-node": "error",
93 | "n8n-nodes-base/node-class-description-outputs-wrong": "off",
94 | "n8n-nodes-base/node-dirname-against-convention": "error",
95 | "n8n-nodes-base/node-execute-block-double-assertion-for-items": "error",
96 | "n8n-nodes-base/node-execute-block-wrong-error-thrown": "error",
97 | "n8n-nodes-base/node-filename-against-convention": "error",
98 | "n8n-nodes-base/node-param-array-type-assertion": "error",
99 | "n8n-nodes-base/node-param-color-type-unused": "error",
100 | "n8n-nodes-base/node-param-default-missing": "error",
101 | "n8n-nodes-base/node-param-default-wrong-for-boolean": "error",
102 | "n8n-nodes-base/node-param-default-wrong-for-collection": "error",
103 | "n8n-nodes-base/node-param-default-wrong-for-fixed-collection": "error",
104 | "n8n-nodes-base/node-param-default-wrong-for-multi-options": "error",
105 | "n8n-nodes-base/node-param-default-wrong-for-number": "error",
106 | "n8n-nodes-base/node-param-default-wrong-for-simplify": "error",
107 | "n8n-nodes-base/node-param-default-wrong-for-string": "error",
108 | "n8n-nodes-base/node-param-description-boolean-without-whether": "error",
109 | "n8n-nodes-base/node-param-description-comma-separated-hyphen": "error",
110 | "n8n-nodes-base/node-param-description-empty-string": "error",
111 | "n8n-nodes-base/node-param-description-excess-final-period": "error",
112 | "n8n-nodes-base/node-param-description-excess-inner-whitespace": "error",
113 | "n8n-nodes-base/node-param-description-identical-to-display-name": "error",
114 | "n8n-nodes-base/node-param-description-line-break-html-tag": "error",
115 | "n8n-nodes-base/node-param-description-lowercase-first-char": "error",
116 | "n8n-nodes-base/node-param-description-miscased-id": "error",
117 | "n8n-nodes-base/node-param-description-miscased-json": "error",
118 | "n8n-nodes-base/node-param-description-miscased-url": "error",
119 | "n8n-nodes-base/node-param-description-missing-final-period": "error",
120 | "n8n-nodes-base/node-param-description-missing-for-ignore-ssl-issues": "error",
121 | "n8n-nodes-base/node-param-description-missing-for-return-all": "error",
122 | "n8n-nodes-base/node-param-description-missing-for-simplify": "error",
123 | "n8n-nodes-base/node-param-description-missing-from-dynamic-multi-options": "error",
124 | "n8n-nodes-base/node-param-description-missing-from-dynamic-options": "error",
125 | "n8n-nodes-base/node-param-description-missing-from-limit": "error",
126 | "n8n-nodes-base/node-param-description-unencoded-angle-brackets": "error",
127 | "n8n-nodes-base/node-param-description-unneeded-backticks": "error",
128 | "n8n-nodes-base/node-param-description-untrimmed": "error",
129 | "n8n-nodes-base/node-param-description-url-missing-protocol": "error",
130 | "n8n-nodes-base/node-param-description-weak": "error",
131 | "n8n-nodes-base/node-param-description-wrong-for-dynamic-multi-options": "error",
132 | "n8n-nodes-base/node-param-description-wrong-for-dynamic-options": "error",
133 | "n8n-nodes-base/node-param-description-wrong-for-ignore-ssl-issues": "error",
134 | "n8n-nodes-base/node-param-description-wrong-for-limit": "error",
135 | "n8n-nodes-base/node-param-description-wrong-for-return-all": "error",
136 | "n8n-nodes-base/node-param-description-wrong-for-simplify": "error",
137 | "n8n-nodes-base/node-param-description-wrong-for-upsert": "error",
138 | "n8n-nodes-base/node-param-display-name-excess-inner-whitespace": "error",
139 | "n8n-nodes-base/node-param-display-name-miscased-id": "error",
140 | "n8n-nodes-base/node-param-display-name-miscased": "error",
141 | "n8n-nodes-base/node-param-display-name-not-first-position": "error",
142 | "n8n-nodes-base/node-param-display-name-untrimmed": "error",
143 | "n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options": "error",
144 | "n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options": "error",
145 | "n8n-nodes-base/node-param-display-name-wrong-for-simplify": "error",
146 | "n8n-nodes-base/node-param-display-name-wrong-for-update-fields": "error",
147 | "n8n-nodes-base/node-param-min-value-wrong-for-limit": "error",
148 | "n8n-nodes-base/node-param-multi-options-type-unsorted-items": "error",
149 | "n8n-nodes-base/node-param-name-untrimmed": "error",
150 | "n8n-nodes-base/node-param-operation-option-action-wrong-for-get-many": "error",
151 | "n8n-nodes-base/node-param-operation-option-description-wrong-for-get-many": "error",
152 | "n8n-nodes-base/node-param-operation-option-without-action": "error",
153 | "n8n-nodes-base/node-param-operation-without-no-data-expression": "error",
154 | "n8n-nodes-base/node-param-option-description-identical-to-name": "error",
155 | "n8n-nodes-base/node-param-option-name-containing-star": "error",
156 | "n8n-nodes-base/node-param-option-name-duplicate": "error",
157 | "n8n-nodes-base/node-param-option-name-wrong-for-get-many": "error",
158 | "n8n-nodes-base/node-param-option-name-wrong-for-upsert": "error",
159 | "n8n-nodes-base/node-param-option-value-duplicate": "error",
160 | "n8n-nodes-base/node-param-options-type-unsorted-items": "error",
161 | "n8n-nodes-base/node-param-placeholder-miscased-id": "error",
162 | "n8n-nodes-base/node-param-placeholder-missing-email": "error",
163 | "n8n-nodes-base/node-param-required-false": "error",
164 | "n8n-nodes-base/node-param-resource-with-plural-option": "error",
165 | "n8n-nodes-base/node-param-resource-without-no-data-expression": "error",
166 | "n8n-nodes-base/node-param-type-options-missing-from-limit": "error",
167 | "n8n-nodes-base/node-param-type-options-password-missing": "error",
168 | },
169 | }
170 | );
--------------------------------------------------------------------------------