EventSub webhook: Echo Dot TTS when followed Twitch channels go live

You need to provide your Twitch username, the hostname of your node-red server, your Twitch developer Client-ID, the name of your Echo Dot device, and then need to set up a proxy in the Account node to automatically have your Alexa login cookie refreshed.

Note that this can be used as a template for responding in any way - not just Echo Dot TTS - to WebSub / PubSubHubbub topics offered by any publishers - not just Twitch. **Edit: They moved to EventSub, so I updated this gist accordingly

[{"id":"f12ae3f2.f468a","type":"subflow","name":"Twitch Access Token","info":"","category":"","in":[{"x":40,"y":40,"wires":[{"id":"d60818e9.5a4dd8"}]}],"out":[{"x":340,"y":40,"wires":[{"id":"d60818e9.5a4dd8","port":0}]}],"env":[]},{"id":"d60818e9.5a4dd8","type":"function","z":"f12ae3f2.f468a","name":"Add Access Token","func":"msg.headers['Authorization'] = 'Bearer ' + global.get('access_token');\nreturn msg;","outputs":1,"noerr":0,"x":190,"y":40,"wires":[[]]},{"id":"c6ee744a.19ce08","type":"subflow","name":"Extract Display Names","info":"","category":"","in":[{"x":40,"y":20,"wires":[{"id":"60c60f4e.93317"}]}],"out":[{"x":700,"y":20,"wires":[{"id":"34a1b145.f015ae","port":0}]}]},{"id":"b6fb9ea2.04518","type":"split","z":"c6ee744a.19ce08","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":390,"y":20,"wires":[["34a1b145.f015ae"]]},{"id":"34a1b145.f015ae","type":"change","z":"c6ee744a.19ce08","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.to_name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":20,"wires":[[]]},{"id":"60c60f4e.93317","type":"change","z":"c6ee744a.19ce08","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.data","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":220,"y":20,"wires":[["b6fb9ea2.04518"]]},{"id":"eaa2370eb4284992","type":"subflow","name":"Spit Out Followed Channels (2)","info":"","category":"","in":[{"x":20,"y":40,"wires":[{"id":"f1827fa60f3df9f7"}]}],"out":[{"x":960,"y":120,"wires":[{"id":"70ed07b79d77c7d2","port":0}]}]},{"id":"6971b255318a2599","type":"http request","z":"eaa2370eb4284992","name":"Twitch get followed channels","method":"GET","ret":"obj","paytoqs":false,"url":"https://api.twitch.tv/helix/users/follows?from_id={{payload}}","tls":"","proxy":"","authType":"","x":580,"y":120,"wires":[["70ed07b79d77c7d2"]]},{"id":"ecd2a81b65f31b23","type":"subflow:868ded6.0847e1","z":"eaa2370eb4284992","name":"","env":[],"x":300,"y":40,"wires":[["93195f4f16c6f121"]]},{"id":"70ed07b79d77c7d2","type":"subflow:c6ee744a.19ce08","z":"eaa2370eb4284992","name":"","env":[],"x":820,"y":120,"wires":[[]]},{"id":"f1827fa60f3df9f7","type":"change","z":"eaa2370eb4284992","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":130,"y":40,"wires":[["ecd2a81b65f31b23"]]},{"id":"ce008dddb4db4876","type":"http request","z":"eaa2370eb4284992","name":"Twitch get User ID","method":"GET","ret":"obj","paytoqs":false,"url":"https://api.twitch.tv/helix/users?login={{topic}}","tls":"","proxy":"","authType":"","x":710,"y":40,"wires":[["c7ef5f364e54521c"]]},{"id":"c7ef5f364e54521c","type":"subflow:868ded6.0847e1","z":"eaa2370eb4284992","name":"","env":[],"x":920,"y":40,"wires":[["cf61a5ae6d3cf6a8"]]},{"id":"71ba96efe6649e81","type":"change","z":"eaa2370eb4284992","name":"Pull up your user id","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.data[0].id","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":120,"wires":[["6971b255318a2599"]]},{"id":"93195f4f16c6f121","type":"subflow:f12ae3f2.f468a","z":"eaa2370eb4284992","name":"","x":490,"y":40,"wires":[["ce008dddb4db4876"]]},{"id":"cf61a5ae6d3cf6a8","type":"subflow:f12ae3f2.f468a","z":"eaa2370eb4284992","name":"","x":130,"y":120,"wires":[["71ba96efe6649e81"]]},{"id":"868ded6.0847e1","type":"subflow","name":"Twitch Client-ID","info":"","category":"","in":[{"x":60,"y":60,"wires":[{"id":"b597d765.6b5bd8"}]}],"out":[{"x":300,"y":60,"wires":[{"id":"b597d765.6b5bd8","port":0}]}],"icon":"node-red-contrib-credentials/credentials.png"},{"id":"b597d765.6b5bd8","type":"function","z":"868ded6.0847e1","name":"Add Client ID","func":"msg.headers = {};\nmsg.headers['Client-ID'] = global.get('clientid');\nmsg.clientid = global.get('clientid');\nmsg.clientsecret = global.get('clientsecret');\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":60,"wires":[[]]},{"id":"3b665733ed3a4d48","type":"tab","label":"Twitch EventSub","disabled":false,"info":""},{"id":"7ac582bd158e76f7","type":"http response","z":"3b665733ed3a4d48","name":"Respond to Twitch","statusCode":"","headers":{},"x":850,"y":460,"wires":[]},{"id":"ec04d7efef275e7b","type":"http request","z":"3b665733ed3a4d48","name":"Twitch Webhook","method":"POST","ret":"txt","paytoqs":"ignore","url":"https://api.twitch.tv/helix/eventsub/subscriptions","tls":"","persist":false,"proxy":"","authType":"","x":1020,"y":340,"wires":[[]]},{"id":"673cae9ff72f6592","type":"function","z":"3b665733ed3a4d48","name":"Subscribe to stream changes","func":"msg.payload = {\n    \"type\": \"channel.update\",\n    \"version\": \"1\",\n    \"condition\": {\n        \"broadcaster_user_id\": flow.get(\"userid.\"+msg.topic)\n    },    \n    \"transport\": {\n        \"method\": \"webhook\",\n        \"callback\": msg.host + \"/twitch\",\n        \"secret\": flow.get(\"eventsub_secret\")\n    }\n};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":340,"wires":[["ec04d7efef275e7b"]]},{"id":"b61ffcbbb8463cfc","type":"http in","z":"3b665733ed3a4d48","name":"","url":"/twitch","method":"post","upload":false,"swaggerDoc":"","x":100,"y":520,"wires":[["abeda593118b7bfb","4a3c1897e3df761e"]]},{"id":"cb8412a16ea8ae41","type":"comment","z":"3b665733ed3a4d48","name":"Respond to events","info":"","x":90,"y":480,"wires":[]},{"id":"4f857dac331f04d0","type":"comment","z":"3b665733ed3a4d48","name":"Every 10 min, resub for events","info":"","x":120,"y":180,"wires":[]},{"id":"fae69e05a31a48a9","type":"http request","z":"3b665733ed3a4d48","name":"Twitch get User ID","method":"GET","ret":"obj","url":"https://api.twitch.tv/helix/users?login={{topic}}","tls":"","x":590,"y":280,"wires":[["656715a28710a89e"]]},{"id":"656715a28710a89e","type":"change","z":"3b665733ed3a4d48","name":"","rules":[{"t":"set","p":"userid","pt":"flow","to":"$merge([$flowContext(\"userid\"),$.{topic: payload.data[0].id}])","tot":"jsonata"},{"t":"set","p":"username","pt":"flow","to":"$merge([$flowContext(\"username\"),$.{payload.data[0].id: topic}])","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":780,"y":280,"wires":[["738778e7f217fdec"]]},{"id":"ad164220334bfd43","type":"subflow:868ded6.0847e1","z":"3b665733ed3a4d48","name":"","env":[],"x":120,"y":280,"wires":[["ce4babd59d7e73c0"]]},{"id":"738778e7f217fdec","type":"subflow:868ded6.0847e1","z":"3b665733ed3a4d48","name":"","x":960,"y":280,"wires":[["336421927e2ffad7"]]},{"id":"a704cf75d7b5f64c","type":"credentials","z":"3b665733ed3a4d48","name":"Echo Dot Device Name","props":[{"value":"devicename","type":"msg"}],"x":550,"y":520,"wires":[["b0167c41ebac118f"]]},{"id":"e16f97a95c7d35fb","type":"credentials","z":"3b665733ed3a4d48","name":"https://yournoderedserver.com/node-red","props":[{"value":"host","type":"msg"}],"x":480,"y":340,"wires":[["673cae9ff72f6592"]]},{"id":"5400dc2fd1b189d4","type":"inject","z":"3b665733ed3a4d48","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"7","topic":"","payloadType":"date","x":70,"y":220,"wires":[["e48cd9e87731f4a8"]]},{"id":"e48cd9e87731f4a8","type":"credentials","z":"3b665733ed3a4d48","name":"Your Twitch Name","props":[{"value":"payload","type":"msg"}],"x":250,"y":220,"wires":[["674424d5db8a7107"]]},{"id":"2e1c97687c1bacb0","type":"change","z":"3b665733ed3a4d48","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":730,"y":220,"wires":[["ad164220334bfd43"]]},{"id":"674424d5db8a7107","type":"subflow:eaa2370eb4284992","z":"3b665733ed3a4d48","name":"","env":[],"x":500,"y":220,"wires":[["2e1c97687c1bacb0"]]},{"id":"e6b88c27e7d321df","type":"bigtimer","z":"3b665733ed3a4d48","outtopic":"control","outpayload1":"open","outpayload2":"close","name":"Sunrise - Night","comment":"","lat":"26.3683","lon":"-80.1289","starttime":"5003","endtime":"5005","starttime2":0,"endtime2":0,"startoff":0,"endoff":0,"startoff2":0,"endoff2":0,"offs":0,"outtext1":"","outtext2":"","timeout":1440,"sun":true,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"jan":true,"feb":true,"mar":true,"apr":true,"may":true,"jun":true,"jul":true,"aug":true,"sep":true,"oct":true,"nov":true,"dec":true,"day1":0,"month1":0,"day2":0,"month2":0,"day3":0,"month3":0,"day4":0,"month4":0,"day5":0,"month5":0,"day6":0,"month6":0,"day7":"","month7":"","day8":"","month8":"","day9":"","month9":"","day10":"","month10":"","day11":"","month11":"","day12":"","month12":"","d1":0,"w1":0,"d2":0,"w2":0,"d3":0,"w3":0,"d4":0,"w4":0,"d5":0,"w5":0,"d6":0,"w6":0,"xday1":0,"xmonth1":0,"xday2":0,"xmonth2":0,"xday3":0,"xmonth3":0,"xday4":0,"xmonth4":0,"xday5":0,"xmonth5":0,"xday6":0,"xmonth6":0,"xd1":0,"xw1":0,"xd2":0,"xw2":0,"xd3":0,"xw3":0,"xd4":0,"xw4":0,"xd5":0,"xw5":0,"xd6":0,"xw6":0,"suspend":false,"random":false,"repeat":true,"atstart":true,"odd":false,"even":false,"x":560,"y":620,"wires":[["b0167c41ebac118f"],[],[]]},{"id":"b0167c41ebac118f","type":"gate","z":"3b665733ed3a4d48","name":"","controlTopic":"control","defaultState":"open","openCmd":"open","closeCmd":"close","toggleCmd":"toggle","defaultCmd":"default","persist":false,"x":550,"y":560,"wires":[["826ca24b6ea9d398"]]},{"id":"ec45aeac15645208","type":"comment","z":"3b665733ed3a4d48","name":"Every hour, re-authorize","info":"","x":100,"y":20,"wires":[]},{"id":"8a4d3255edd84320","type":"inject","z":"3b665733ed3a4d48","name":"","props":[{"p":"payload","v":"","vt":"date"},{"p":"topic","v":"","vt":"string"}],"repeat":"3600","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":90,"y":60,"wires":[["a2802046fc631db1"]]},{"id":"a2802046fc631db1","type":"credentials","z":"3b665733ed3a4d48","name":"Client Secret","props":[{"value":"clientsecret","type":"global"}],"x":250,"y":60,"wires":[["7a0ee020180bf655"]]},{"id":"a47eb7e655126d89","type":"http request","z":"3b665733ed3a4d48","name":"Twitch get app access token","method":"POST","ret":"obj","paytoqs":"ignore","url":"https://id.twitch.tv/oauth2/token?client_id={{clientid}}&client_secret={{clientsecret}}&grant_type=client_credentials","tls":"","persist":false,"proxy":"","authType":"","x":180,"y":120,"wires":[["7c103e5295c97c1f"]]},{"id":"7a0ee020180bf655","type":"credentials","z":"3b665733ed3a4d48","name":"Client ID","props":[{"value":"clientid","type":"global"}],"x":420,"y":60,"wires":[["e8f99ac5dc990dad"]]},{"id":"7c103e5295c97c1f","type":"change","z":"3b665733ed3a4d48","name":"","rules":[{"t":"set","p":"access_token","pt":"global","to":"payload.access_token","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":120,"wires":[[]]},{"id":"c701d48063210fed","type":"subflow:868ded6.0847e1","z":"3b665733ed3a4d48","name":"","env":[],"x":800,"y":60,"wires":[["a47eb7e655126d89"]]},{"id":"336421927e2ffad7","type":"subflow:f12ae3f2.f468a","z":"3b665733ed3a4d48","name":"","env":[],"x":170,"y":340,"wires":[["e16f97a95c7d35fb"]]},{"id":"ce4babd59d7e73c0","type":"subflow:f12ae3f2.f468a","z":"3b665733ed3a4d48","name":"","env":[],"x":350,"y":280,"wires":[["fae69e05a31a48a9"]]},{"id":"826ca24b6ea9d398","type":"alexa-remote-routine","z":"3b665733ed3a4d48","name":"","account":"c969cb45.22f3d8","routineNode":{"type":"speak","payload":{"type":"regular","text":{"type":"jsonata","value":"$join([payload.event.broadcaster_user_name, ' Is Streaming ', payload.event.category_name])"},"devices":{"type":"msg","value":"devicename"}}},"x":770,"y":560,"wires":[[]]},{"id":"abeda593118b7bfb","type":"function","z":"3b665733ed3a4d48","name":"Build Signature","func":"var hmac_message = msg.req.headers['twitch-eventsub-message-signature'] + msg.req.headers['twitch-eventsub-message-timestamp'] + JSON.stringify(msg.req.body)\nmsg.payload = hmac_message;\nmsg.hmac_comb = hmac_message;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":460,"wires":[["11c4e748cce906b9"]]},{"id":"11c4e748cce906b9","type":"hmac","z":"3b665733ed3a4d48","name":"","algorithm":"HmacSHA256","key":"flow.get(\"eventsub_secret\")","x":510,"y":460,"wires":[["9de094aa2950f8c7"]]},{"id":"9de094aa2950f8c7","type":"function","z":"3b665733ed3a4d48","name":"Verify Signature","func":"var expected_signature_header = 'sha256=' + msg.payload;\n\nmsg.sigheader = expected_signature_header;\nmsg.rcvdhd = msg.req.headers['twitch-eventsub-message-signature'];\n\n// In case of a sub challenge, include that too\nif( 'challenge' in msg.req.body )\n    msg.payload = msg.req.body[ \"challenge\" ];\n\n// if ( msg.req.headers['twitch-eventsub-message-signature'] != expected_signature_header ) {\n//     msg.statusCode = 403;\n// }\n// else\n//     msg.statusCode = 200;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":460,"wires":[["7ac582bd158e76f7"]]},{"id":"e8f99ac5dc990dad","type":"credentials","z":"3b665733ed3a4d48","name":"eventsub_secret","props":[{"value":"eventsub_secret","type":"flow"}],"x":600,"y":60,"wires":[["c701d48063210fed"]]},{"id":"4a3c1897e3df761e","type":"switch","z":"3b665733ed3a4d48","name":"Announce?","property":"payload","propertyType":"msg","rules":[{"t":"hask","v":"event","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":330,"y":520,"wires":[["a704cf75d7b5f64c"]]},{"id":"c969cb45.22f3d8","type":"alexa-remote-account","name":"","authMethod":"proxy","proxyOwnIp":"127.0.0.1","proxyPort":"3456","cookieFile":".node-red/alexa_auth","refreshInterval":"","alexaServiceHost":"pitangui.amazon.com","amazonPage":"amazon.com","acceptLanguage":"en-US","userAgent":"","useWsMqtt":"off","autoInit":"on"}]

Flow Info

Created 6 years, 4 months ago
Updated 3 years, 9 months ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • change (x7)
  • comment (x3)
  • function (x5)
  • http in (x1)
  • http request (x5)
  • http response (x1)
  • inject (x2)
  • split (x1)
  • switch (x1)
Other
  • alexa-remote-account (x1)
  • alexa-remote-routine (x1)
  • bigtimer (x1)
  • credentials (x6)
  • gate (x1)
  • hmac (x1)
  • subflow (x4)
  • subflow:868ded6.0847e1 (x5)
  • subflow:c6ee744a.19ce08 (x1)
  • subflow:eaa2370eb4284992 (x1)
  • subflow:f12ae3f2.f468a (x4)
  • tab (x1)

Tags

  • twitch
  • alexa
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option