Skip to content

Commit 657b8d4

Browse files
committed
timeline scrolle, rate limits, image options
1 parent 1ece054 commit 657b8d4

File tree

6 files changed

+173
-13
lines changed

6 files changed

+173
-13
lines changed

conf.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@
88
LANGUAGE = 'en'
99
SHOW_TOOTS_AT_ONCE = 10
1010

11+
# 300 requests per 5 minutes is the limit for mastodon.social
12+
# seconds to wait before the timeline scroller requests the next toot if there was no new toot
13+
SCROLLER_DELAY_HAS_NO_TOOTS = 10
14+
# time you have to read a new toot before the scroller requests the next toot
15+
SCROLLER_DELAY_HAS_TOOTS = 5
16+
1117
TERMINAL_IMAGES = True # if requirements-full.txt is not installed, this will be set to False automatically
1218
TERMINAL_IMAGE_OPTIMIZER = 'space'
13-
TERMINAL_IMAGE_MAX_WIDTH = 80
19+
20+
TERMINAL_IMAGE_MAX_WIDTH = 60 # -1 means auto-detected width
1421
TERMINAL_IMAGE_MAX_HEIGHT = 40

inc/RateLimit.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from datetime import datetime, timezone
2+
from dateutil.tz import tz
3+
4+
5+
class RateLimit:
6+
def __init__(self):
7+
self.has_been_set = False
8+
self.limit = 0
9+
self.remaining = 0
10+
self.reset_date_time: datetime = datetime.now()
11+
12+
def exceeded(self):
13+
# we do not know the current rate limit, so we can't check
14+
if not self.has_been_set:
15+
return False
16+
17+
if self.remaining == 0:
18+
if self.time_until_reset() > 0:
19+
return True
20+
21+
return False
22+
23+
def cleared(self):
24+
self.has_been_set = False
25+
self.limit = 0
26+
self.remaining = 0
27+
self.reset_date_time = 0
28+
29+
def set_rate_limit(self, limit, remaining, reset):
30+
self.has_been_set = True
31+
self.limit = limit
32+
self.remaining = remaining
33+
self.reset_date_time = reset
34+
35+
def time_until_reset(self):
36+
if not self.has_been_set:
37+
return 0
38+
39+
return (self.reset_date_time - datetime.now()).total_seconds()
40+
41+
def update(self, headers):
42+
reset_date_time = datetime.strptime(
43+
headers["X-RateLimit-Reset"], '%Y-%m-%dT%H:%M:%S.%f%z'
44+
).astimezone(tz=tz.tzlocal())
45+
reset_date_time = reset_date_time.replace(tzinfo=None)
46+
47+
self.set_rate_limit(
48+
int(headers["X-RateLimit-Limit"]),
49+
int(headers["X-RateLimit-Remaining"]),
50+
reset_date_time
51+
)
52+
53+
def __str__(self):
54+
return f'Limit: {self.limit}, Remaining: {self.remaining}, Reset: {self.reset_date_time}, Time until reset: {self.time_until_reset()}'
55+
56+
57+
rate_limit = RateLimit()

inc/TerminalImage.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,19 @@ def render_image(self, image_path):
6666
if not conf.TERMINAL_IMAGES:
6767
return
6868

69+
image_max_width = conf.TERMINAL_IMAGE_MAX_WIDTH
70+
if image_max_width == -1:
71+
try:
72+
image_max_width = helper.get_terminal_width()
73+
# image width debug
74+
#helper.print_color('image_max_width: {}'.format(image_max_width), helper.Color.YELLOW)
75+
except Exception as ex:
76+
helper.print_color(ex, helper.Color.RED)
77+
image_max_width = 80
78+
6979
renderer = img2unicode.Renderer(
7080
default_optimizer=self.optimizer,
71-
max_w=conf.TERMINAL_IMAGE_MAX_WIDTH,
81+
max_w=image_max_width,
7282
max_h=conf.TERMINAL_IMAGE_MAX_HEIGHT
7383
)
7484

inc/helper.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1-
# Define the colors using ANSI escape codes
1+
import shutil
22

3+
# Define the colors using ANSI escape codes
34
class Color:
45
GREEN = '\033[92m'
56
YELLOW = '\033[93m'
67
RED = '\033[91m'
78

9+
BLUE = '\033[94m'
10+
811
RESET = '\033[0m' # Reset color
912

1013

1114
def print_color(text, color):
1215
print(f'{color}{text}{Color.RESET}')
16+
17+
18+
def get_terminal_width():
19+
terminal_width = shutil.get_terminal_size().columns
20+
return terminal_width

main.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ def main_menu():
77
print('*************')
88
print('1. do Toot')
99
print('2. read toots')
10-
print('3. download user toots')
11-
print('4. search user toots')
12-
print('5. EXIT')
10+
print('3. timeline scroller')
11+
print('4. download user toots')
12+
print('5. search user toots')
13+
print('6. EXIT')
1314
print('*************')
1415
user_choice = input('> choice: ')
1516

@@ -18,8 +19,10 @@ def main_menu():
1819
elif user_choice == '2':
1920
readToots.read_toots()
2021
elif user_choice == '3':
21-
searchUserToots.do_download()
22+
readToots.scroller_choice()
2223
elif user_choice == '4':
24+
searchUserToots.do_download()
25+
elif user_choice == '5':
2326
searchUserToots.do_search()
2427
else:
2528
return False

readToots.py

+81-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import requests
22
import html2text
3-
from datetime import datetime
3+
import time
4+
import inc.helper as helper
5+
import inc.RateLimit as RateLimit
46
from inc.TerminalImage import TerminalImage
57
from inc.TootRendererBase import TootRendererBase
68
from inc.TootRendererSimple import TootRendererSimple
@@ -20,20 +22,32 @@
2022

2123

2224
# Get the latest toots from the home timeline
23-
def get_toots(max_id=None, limit=conf.SHOW_TOOTS_AT_ONCE):
25+
def get_toots(max_id=None, limit=conf.SHOW_TOOTS_AT_ONCE, timelines='home', min_id=None):
26+
if RateLimit.rate_limit.exceeded():
27+
helper.print_color("Rate limit exceeded. Please wait {} seconds.".format(RateLimit.rate_limit.time_until_reset()), helper.Color.RED)
28+
return []
29+
2430
params = {}
2531

2632
if max_id:
2733
params["max_id"] = max_id
2834

35+
if min_id:
36+
params["min_id"] = min_id
37+
2938
if limit:
3039
params["limit"] = limit
3140

32-
response = requests.get(base_url + "timelines/home", headers=headers, params=params)
41+
response = requests.get(base_url + "timelines/" + timelines, headers=headers, params=params)
42+
RateLimit.rate_limit.update(response.headers)
43+
44+
# rate limit debug
45+
#helper.print_color(RateLimit.rate_limit, helper.Color.BLUE)
46+
3347
if response.status_code == 200:
3448
return response.json()
3549
else:
36-
print("Error retrieving toots.")
50+
helper.print_color("Error retrieving toots. {} Code: {}".format(response.reason, response.status_code), helper.Color.RED)
3751
return []
3852

3953

@@ -92,11 +106,72 @@ def read_toots():
92106

93107
more = input("> Do you want to see more toots? (Y/n) ")
94108
if more.lower() != "n":
95-
toots = get_toots(toots[-1]["id"])
96-
display_toots(toots)
109+
toot_id = None
110+
if toots:
111+
toot_id = toots[-1]["id"]
112+
113+
new_toots = get_toots(toot_id)
114+
115+
if new_toots:
116+
toots = new_toots
117+
display_toots(toots)
97118
else:
98119
break
99120

100121

122+
def scroller_choice():
123+
print('1. Home / My Timeline')
124+
print('2. Server / Local')
125+
print('3. Tag / Hashtag')
126+
print('4. Cancel')
127+
print()
128+
129+
choice = input("> Choose a timeline (empty for own): ").strip()
130+
if choice == '1' or choice == '':
131+
scroller('home')
132+
elif choice == '2':
133+
scroller('public')
134+
elif choice == '3':
135+
tag = input("> Enter a # tag: ")
136+
tag = tag.replace('#', '')
137+
scroller('tag/' + tag)
138+
#elif choice == '5':
139+
# user = input("> Enter a user name: ")
140+
# scroller('accounts/' + user + '/statuses')
141+
#elif choice == '6':
142+
# search_term = input("> Enter a search term: ")
143+
# scroller('search?limit=40&q=' + search_term)
144+
elif choice == '4':
145+
return
146+
else:
147+
print('Invalid choice')
148+
149+
150+
def scroller(timelines='home'):
151+
# Get the first set of toots
152+
toots = get_toots(limit=1, timelines=timelines)
153+
toots.reverse()
154+
display_toots(toots)
155+
156+
try:
157+
while True:
158+
toot_id = None
159+
if toots:
160+
toot_id = toots[0]["id"]
161+
162+
new_toots = get_toots(limit=1, min_id=toot_id, timelines=timelines)
163+
164+
if new_toots:
165+
toots = new_toots
166+
display_toots(toots)
167+
time.sleep(conf.SCROLLER_DELAY_HAS_TOOTS)
168+
else:
169+
time.sleep(conf.SCROLLER_DELAY_HAS_NO_TOOTS)
170+
171+
except KeyboardInterrupt:
172+
# allow canceling the scroller
173+
return
174+
175+
101176
if __name__ == '__main__':
102177
read_toots()

0 commit comments

Comments
 (0)