Skip to content

Commit b122f48

Browse files
committed
Initial addition
1 parent c3fe95b commit b122f48

5 files changed

Lines changed: 4436 additions & 0 deletions

File tree

Twitter_API/binascii.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
2+
PAD = '='
3+
4+
table_a2b_base64 = [
5+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
6+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
7+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
8+
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, # Note PAD->-1 here
9+
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
10+
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
11+
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
12+
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1,
13+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
14+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
15+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
16+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
17+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
18+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
19+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
20+
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
21+
]
22+
def _transform(n):
23+
if n == -1:
24+
return '\xff'
25+
else:
26+
return chr(n)
27+
table_a2b_base64 = ''.join(map(_transform, table_a2b_base64))
28+
assert len(table_a2b_base64) == 256
29+
30+
def a2b_base64(ascii):
31+
"Decode a line of base64 data."
32+
33+
res = []
34+
quad_pos = 0
35+
leftchar = 0
36+
leftbits = 0
37+
last_char_was_a_pad = False
38+
39+
for c in ascii:
40+
c = chr(c)
41+
if c == PAD:
42+
if quad_pos > 2 or (quad_pos == 2 and last_char_was_a_pad):
43+
break # stop on 'xxx=' or on 'xx=='
44+
last_char_was_a_pad = True
45+
else:
46+
n = ord(table_a2b_base64[ord(c)])
47+
if n == 0xff:
48+
continue # ignore strange characters
49+
#
50+
# Shift it in on the low end, and see if there's
51+
# a byte ready for output.
52+
quad_pos = (quad_pos + 1) & 3
53+
leftchar = (leftchar << 6) | n
54+
leftbits += 6
55+
#
56+
if leftbits >= 8:
57+
leftbits -= 8
58+
res.append((leftchar >> leftbits).to_bytes(1, 'big'))
59+
leftchar &= ((1 << leftbits) - 1)
60+
#
61+
last_char_was_a_pad = False
62+
else:
63+
if leftbits != 0:
64+
raise Exception("Incorrect padding")
65+
66+
return b''.join(res)
67+
68+
# ____________________________________________________________
69+
70+
table_b2a_base64 = (
71+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
72+
73+
def b2a_base64(bin_data):
74+
"Base64-code line of data."
75+
76+
newlength = (len(bin_data) + 2) // 3
77+
newlength = newlength * 4 + 1
78+
res = []
79+
80+
leftchar = 0
81+
leftbits = 0
82+
for c in bin_data:
83+
# Shift into our buffer, and output any 6bits ready
84+
leftchar = (leftchar << 8) | c
85+
leftbits += 8
86+
res.append(table_b2a_base64[(leftchar >> (leftbits-6)) & 0x3f])
87+
leftbits -= 6
88+
if leftbits >= 6:
89+
res.append(table_b2a_base64[(leftchar >> (leftbits-6)) & 0x3f])
90+
leftbits -= 6
91+
#
92+
if leftbits == 2:
93+
res.append(table_b2a_base64[(leftchar & 3) << 4])
94+
res.append(PAD)
95+
res.append(PAD)
96+
elif leftbits == 4:
97+
res.append(table_b2a_base64[(leftchar & 0xf) << 2])
98+
res.append(PAD)
99+
return ''.join(res).encode('ascii')

Twitter_API/code.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""
2+
Halloween countdown for PyPortal.
3+
4+
Adafruit invests time and resources providing this open source code.
5+
Please support Adafruit and open source hardware by purchasing
6+
products from Adafruit!
7+
8+
Written by Dave Astels for Adafruit Industries
9+
Copyright (c) 2019 Adafruit Industries
10+
Licensed under the MIT license.
11+
12+
All text above must be included in any redistribution.
13+
"""
14+
15+
import time
16+
import random
17+
import board
18+
import busio
19+
import binascii
20+
import json
21+
from adafruit_pyportal import PyPortal
22+
from adafruit_bitmap_font import bitmap_font
23+
from adafruit_display_text.label import Label
24+
from digitalio import DigitalInOut
25+
from adafruit_esp32spi import adafruit_esp32spi
26+
import adafruit_esp32spi.adafruit_esp32spi_requests as requests
27+
import adafruit_logging as logging
28+
29+
username = 'codewisdom'
30+
31+
logger = logging.getLogger('main')
32+
logger.setLevel(logging.DEBUG)
33+
try:
34+
from secrets import secrets
35+
except ImportError:
36+
logger.critical("""WiFi settings are kept in secrets.py, please add them there!
37+
the secrets dictionary must contain 'ssid' and 'password' at a minimum""")
38+
raise
39+
40+
def halt_and_catch_fire(message, *args):
41+
"""Log a critical error and stall the system."""
42+
logger.critical(message, *args)
43+
while True:
44+
pass
45+
46+
def connect_esp():
47+
"""Connect the ESP to the network."""
48+
pyportal.neo_status((0, 0, 100))
49+
while not esp.is_connected:
50+
# secrets dictionary must contain 'ssid' and 'password' at a minimum
51+
if secrets['ssid'] == 'CHANGE ME' or secrets['ssid'] == 'CHANGE ME':
52+
change_me = "\n"+"*"*45
53+
change_me += "\nPlease update the 'secrets.py' file on your\n"
54+
change_me += "CIRCUITPY drive to include your local WiFi\n"
55+
change_me += "access point SSID name in 'ssid' and SSID\n"
56+
change_me += "password in 'password'. Then save to reload!\n"
57+
change_me += "*"*45
58+
raise OSError(change_me)
59+
pyportal.neo_status((100, 0, 0)) # red = not connected
60+
try:
61+
esp.connect(secrets)
62+
except RuntimeError as error:
63+
logger.error("Could not connect to internet: %s", error)
64+
logger.error("Retrying in 3 seconds...")
65+
time.sleep(3)
66+
67+
#pylint:disable=redefined-outer-name
68+
def get_bearer_token():
69+
"""Get the bearer authentication token from twitter."""
70+
raw_key = secrets['twitter_api_key'] + ':' + secrets['twitter_secret_key']
71+
encoded_key = binascii.b2a_base64(bytes(raw_key, 'utf8'))
72+
string_key = bytes.decode(encoded_key)
73+
headers= {'Authorization': 'Basic ' + string_key,
74+
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}
75+
response = requests.post('https://api.twitter.com/oauth2/token',
76+
headers=headers,
77+
data='grant_type=client_credentials')
78+
response_dict = json.loads(response.content)
79+
if response_dict['token_type'] != 'bearer':
80+
halt_and_catch_fire('Wrong token type from twitter: %s', response_dict['token_type'])
81+
return response_dict['access_token']
82+
#pylint:enable=redefined-outer-name
83+
84+
#setup esp interface
85+
esp32_ready = DigitalInOut(board.ESP_BUSY)
86+
esp32_gpio0 = DigitalInOut(board.ESP_GPIO0)
87+
esp32_reset = DigitalInOut(board.ESP_RESET)
88+
esp32_cs = DigitalInOut(board.ESP_CS)
89+
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
90+
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready,
91+
esp32_reset, esp32_gpio0)
92+
93+
# determine the current working directory
94+
# needed so we know where to find files
95+
cwd = ("/"+__file__).rsplit('/', 1)[0]
96+
97+
98+
# Initialize the pyportal object and let us know what data to fetch and where
99+
# to display it
100+
pyportal = PyPortal(status_neopixel=board.NEOPIXEL,
101+
default_bg=cwd + '/twitter_background.bmp',
102+
external_spi=spi,
103+
esp=esp,
104+
text_font=cwd+'/fonts/Helvetica-Bold-16.bdf',
105+
text_position=(20, 60),
106+
text_color=0xFFFFFF,
107+
caption_text='@' + username,
108+
caption_font=cwd+'/fonts/Helvetica-Bold-16.bdf',
109+
caption_position=(5, 210),
110+
caption_color=0x808080)
111+
112+
logger.debug("Connecting to AP %s...", secrets['ssid'])
113+
connect_esp()
114+
logger.debug('Connected')
115+
116+
logger.debug('Getting bearer token...')
117+
bearer_token = get_bearer_token()
118+
logger.debug('Got it')
119+
120+
headers = {'Authorization': 'Bearer ' + bearer_token}
121+
122+
while True:
123+
logger.debug('fetching latest tweet from @%s', username)
124+
response = requests.get('https://api.twitter.com/1.1/statuses/user_timeline.json?count=1&screen_name=' + username,
125+
headers=headers)
126+
if response.status_code != 200:
127+
logger.error('Tweet fetch status: %d', response.status_code)
128+
else:
129+
tweet = json.loads(response.content)[0]['text']
130+
logger.debug('Tweet: %s', tweet)
131+
lines = PyPortal.wrap_nicely(tweet, 35)
132+
pyportal.set_text('\n'.join(lines))
133+
# check every hour
134+
time.sleep(3600)

0 commit comments

Comments
 (0)