1- # SPDX-FileCopyrightText: 2023 DJDevon3
1+ # SPDX-FileCopyrightText: 2024 DJDevon3
22# SPDX-License-Identifier: MIT
33# Coded for Circuit Python 8.2.x
4- # Twitch_API_Example
4+ """Twitch API Example"""
5+ # pylint: disable=import-error
56
67import os
7- import ssl
88import time
99
10- import socketpool
10+ import adafruit_connection_manager
1111import wifi
1212
1313import adafruit_requests
1414
15- # Initialize WiFi Pool (There can be only 1 pool & top of script)
16- pool = socketpool .SocketPool (wifi .radio )
17-
1815# Twitch Developer Account & oauth App Required:
1916# Visit https://dev.twitch.tv/console to create an app
20-
21- # Ensure these are in secrets.py or settings.toml
22- # "Twitch_ClientID": "Your Developer APP ID Here",
23- # "Twitch_Client_Secret": "APP ID secret here",
24- # "Twitch_UserID": "Your Twitch UserID here",
17+ # Ensure these are in settings.toml
18+ # TWITCH_CLIENT_ID = "Your Developer APP ID Here"
19+ # TWITCH_CLIENT_SECRET = "APP ID secret here"
20+ # TWITCH_USER_ID = "Your Twitch UserID here"
2521
2622# Get WiFi details, ensure these are setup in settings.toml
2723ssid = os .getenv ("CIRCUITPY_WIFI_SSID" )
2824password = os .getenv ("CIRCUITPY_WIFI_PASSWORD" )
29- twitch_client_id = os .getenv ("Twitch_ClientID " )
30- twitch_client_secret = os .getenv ("Twitch_Client_Secret " )
25+ TWITCH_CID = os .getenv ("TWITCH_CLIENT_ID " )
26+ TWITCH_CS = os .getenv ("TWITCH_CLIENT_SECRET " )
3127# For finding your Twitch User ID
3228# https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/
33- twitch_user_id = os .getenv ("Twitch_UserID" ) # User ID you want endpoints from
29+ TWITCH_UID = os .getenv ("TWITCH_USER_ID" )
3430
35- # Time between API refreshes
31+ # API Polling Rate
3632# 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour
37- sleep_time = 900
33+ SLEEP_TIME = 900
34+
35+ # Set DEBUG to True for full JSON response.
36+ # STREAMER WARNING: Credentials will be viewable
37+ DEBUG = False
38+
39+ # Initalize Wifi, Socket Pool, Request Session
40+ pool = adafruit_connection_manager .get_radio_socketpool (wifi .radio )
41+ ssl_context = adafruit_connection_manager .get_radio_ssl_context (wifi .radio )
42+ requests = adafruit_requests .Session (pool , ssl_context )
3843
3944
40- # Converts seconds to minutes/hours/days
4145def time_calc (input_time ):
46+ """Converts seconds to minutes/hours/days"""
4247 if input_time < 60 :
4348 return f"{ input_time :.0f} seconds"
4449 if input_time < 3600 :
@@ -48,42 +53,44 @@ def time_calc(input_time):
4853 return f"{ input_time / 60 / 60 / 24 :.1f} days"
4954
5055
56+ def _format_datetime (datetime ):
57+ """F-String formatted struct time conversion"""
58+ return (
59+ f"{ datetime .tm_mon :02} /"
60+ + f"{ datetime .tm_mday :02} /"
61+ + f"{ datetime .tm_year :02} "
62+ + f"{ datetime .tm_hour :02} :"
63+ + f"{ datetime .tm_min :02} :"
64+ + f"{ datetime .tm_sec :02} "
65+ )
66+
67+
5168# First we use Client ID & Client Secret to create a token with POST
5269# No user interaction is required for this type of scope (implicit grant flow)
5370twitch_0auth_header = {"Content-Type" : "application/x-www-form-urlencoded" }
5471TWITCH_0AUTH_TOKEN = "https://id.twitch.tv/oauth2/token"
5572
56- # Connect to Wi-Fi
57- print ("\n ===============================" )
58- print ("Connecting to WiFi..." )
59- requests = adafruit_requests .Session (pool , ssl .create_default_context ())
60- while not wifi .radio .connected :
61- try :
62- wifi .radio .connect (ssid , password )
63- except ConnectionError as e :
64- print ("Connection Error:" , e )
65- print ("Retrying in 10 seconds" )
66- time .sleep (10 )
67- print ("Connected!\n " )
68-
6973while True :
74+ # Connect to Wi-Fi
75+ print ("\n Connecting to WiFi..." )
76+ while not wifi .radio .ipv4_address :
77+ try :
78+ wifi .radio .connect (ssid , password )
79+ except ConnectionError as e :
80+ print ("❌ Connection Error:" , e )
81+ print ("Retrying in 10 seconds" )
82+ print ("✅ Wifi!" )
83+
7084 try :
71- # ----------------------------- POST FOR BEARER TOKEN -----------------------
72- print (
73- "Attempting Bearer Token Request!"
74- ) # ---------------------------------------
75- # Print Request to Serial
76- debug_bearer_request = (
77- False # STREAMER WARNING: your client secret will be viewable
78- )
79- if debug_bearer_request :
80- print ("Full API GET URL: " , TWITCH_0AUTH_TOKEN )
81- print ("===============================" )
85+ # ------------- POST FOR BEARER TOKEN -----------------
86+ print (" | Attempting Bearer Token Request!" )
87+ if DEBUG :
88+ print (f"Full API GET URL: { TWITCH_0AUTH_TOKEN } " )
8289 twitch_0auth_data = (
8390 "&client_id="
84- + twitch_client_id
91+ + TWITCH_CID
8592 + "&client_secret="
86- + twitch_client_secret
93+ + TWITCH_CS
8794 + "&grant_type=client_credentials"
8895 )
8996
@@ -95,70 +102,79 @@ def time_calc(input_time):
95102 twitch_0auth_json = twitch_0auth_response .json ()
96103 twitch_access_token = twitch_0auth_json ["access_token" ]
97104 except ConnectionError as e :
98- print ("Connection Error:" , e )
105+ print (f "Connection Error: { e } " )
99106 print ("Retrying in 10 seconds" )
107+ print (" | 🔑 Token Authorized!" )
100108
101- # Print Response to Serial
102- debug_bearer_response = (
103- False # STREAMER WARNING: your client secret will be viewable
104- )
105- if debug_bearer_response :
106- print ("JSON Dump: " , twitch_0auth_json )
107- print ("Header: " , twitch_0auth_header )
108- print ("Access Token: " , twitch_access_token )
109+ # STREAMER WARNING: your client secret will be viewable
110+ if DEBUG :
111+ print (f"JSON Dump: { twitch_0auth_json } " )
112+ print (f"Header: { twitch_0auth_header } " )
113+ print (f"Access Token: { twitch_access_token } " )
109114 twitch_token_type = twitch_0auth_json ["token_type" ]
110- print ("Token Type: " , twitch_token_type )
115+ print (f "Token Type: { twitch_token_type } " )
111116
112- print ("Board Uptime: " , time_calc (time .monotonic ()))
113117 twitch_token_expiration = twitch_0auth_json ["expires_in" ]
114- print (" Token Expires in: " , time_calc (twitch_token_expiration ))
118+ print (f" | Token Expires in: { time_calc (twitch_token_expiration )} " )
115119
116- # ----------------------------- GET DATA -------------------------------------
120+ # ----------------------------- GET DATA --------------------
117121 # Bearer token is refreshed every time script runs :)
118122 # Twitch sets token expiration to about 64 days
119123 # Helix is the name of the current Twitch API
120124 # Now that we have POST bearer token we can do a GET for data
121- # ----------------------------------------------------------------------------
125+ # -----------------------------------------------------------
122126 twitch_header = {
123127 "Authorization" : "Bearer " + twitch_access_token + "" ,
124- "Client-Id" : "" + twitch_client_id + "" ,
128+ "Client-Id" : "" + TWITCH_CID + "" ,
125129 }
126130 TWITCH_FOLLOWERS_SOURCE = (
127131 "https://api.twitch.tv/helix/channels"
128132 + "/followers?"
129133 + "broadcaster_id="
130- + twitch_user_id
134+ + TWITCH_UID
131135 )
132- print (
133- "\n Attempting to GET TWITCH Stats!"
134- ) # ------------------------------------------------
135- print ("===============================" )
136- twitch_followers_response = requests .get (
136+ print (" | Attempting to GET Twitch JSON!" )
137+ twitch_response = requests .get (
137138 url = TWITCH_FOLLOWERS_SOURCE , headers = twitch_header
138139 )
139140 try :
140- twitch_followers_json = twitch_followers_response .json ()
141+ twitch_json = twitch_response .json ()
141142 except ConnectionError as e :
142- print ("Connection Error:" , e )
143+ print (f "Connection Error: { e } " )
143144 print ("Retrying in 10 seconds" )
144145
145- # Print Response to Serial
146- debug_bearer_response = (
147- False # STREAMER WARNING: your bearer token will be viewable
148- )
149- if debug_bearer_response :
150- print ("Full API GET URL: " , TWITCH_FOLLOWERS_SOURCE )
151- print ("Header: " , twitch_header )
152- print ("JSON Full Response: " , twitch_followers_json )
153-
154- twitch_followers = twitch_followers_json ["total" ]
155- print ("Followers: " , twitch_followers )
156- print ("Finished!" )
157- print ("Next Update in: " , time_calc (sleep_time ))
146+ if DEBUG :
147+ print (f" | Full API GET URL: { TWITCH_FOLLOWERS_SOURCE } " )
148+ print (f" | Header: { twitch_header } " )
149+ print (f" | JSON Full Response: { twitch_json } " )
150+
151+ if "status" in twitch_json :
152+ twitch_error_status = twitch_json ["status" ]
153+ print (f"❌ Status: { twitch_error_status } " )
154+
155+ if "error" in twitch_json :
156+ twitch_error = twitch_json ["error" ]
157+ print (f"❌ Error: { twitch_error } " )
158+
159+ if "message" in twitch_json :
160+ twitch_error_msg = twitch_json ["message" ]
161+ print (f"❌ Message: { twitch_error_msg } " )
162+
163+ if "total" in twitch_json :
164+ print (" | ✅ Twitch JSON!" )
165+ twitch_followers = twitch_json ["total" ]
166+ print (f" | | Followers: { twitch_followers } " )
167+
168+ twitch_response .close ()
169+ print ("✂️ Disconnected from Twitch API" )
170+
171+ print ("\n Finished!" )
172+ print (f"Board Uptime: { time_calc (time .monotonic ())} " )
173+ print (f"Next Update: { time_calc (SLEEP_TIME )} " )
158174 print ("===============================" )
159175
160176 except (ValueError , RuntimeError ) as e :
161- print ("Failed to get data, retrying\n " , e )
177+ print (f "Failed to get data, retrying\n { e } " )
162178 time .sleep (60 )
163- continue
164- time .sleep (sleep_time )
179+ break
180+ time .sleep (SLEEP_TIME )
0 commit comments