diff --git a/toot.log b/toot.log new file mode 100644 index 0000000..ae2231e --- /dev/null +++ b/toot.log @@ -0,0 +1,127 @@ +DEBUG:asyncio:Using selector: EpollSelector +DEBUG:toot: --> GET https://mastodon.social/api/v1/instance +DEBUG:toot: <-- GET https://mastodon.social/api/v1/instance HTTP 200 107ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/followed_tags +DEBUG:toot: <-- GET https://mastodon.social/api/v1/followed_tags HTTP 200 197ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/timelines/home?limit=40 +DEBUG:toot: <-- GET https://mastodon.social/api/v1/timelines/home?limit=40 HTTP 200 422ms +INFO:toot.tui.app:Favouriting Status(id='109946395816692687', uri='https://icosahedron.website/users/cpsdqs/statuses/109946395693741603', created_at=datetime.datetime(2023, 3, 1, 5, 34, 20, tzinfo=datetime.timezone.utc), account=Account(id='56142', username='cpsdqs', acct='cpsdqs@icosahedron.website', url='https://icosahedron.website/@cpsdqs', display_name='cat-shaped orb (', note='

80% cotton candy
yall ever hear bout animation??

note: youve been deceived. this is my computer yelling account. youre lucky i dont have linux on my computer

', avatar='https://files.mastodon.social/cache/accounts/avatars/000/056/142/original/629aa8a752430f90.png', avatar_static='https://files.mastodon.social/cache/accounts/avatars/000/056/142/original/629aa8a752430f90.png', header='https://files.mastodon.social/cache/accounts/headers/000/056/142/original/56ea591a7b2f09fa.jpg', header_static='https://files.mastodon.social/cache/accounts/headers/000/056/142/original/56ea591a7b2f09fa.jpg', locked=True, fields=[AccountField(name='pronouns', value='huh?? you want to refer to me in 3rd person???? how dare u talk abt me behind my back', verified_at=None), AccountField(name='languages', value='en · de · rs · js', verified_at=None), AccountField(name='videos game', value='i’ve plays them (at least 6)', verified_at=None), AccountField(name='cloud', value='cloudwithlightning dot net', verified_at=None)], emojis=[], bot=False, group=False, discoverable=False, noindex=None, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2017, 2, 14, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 3, 1), statuses_count=22012, followers_count=152, following_count=29), content='

what do i look like i can remember that anisotropic diffusion was called anisotropic diffusion

', visibility='unlisted', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=0, favourites_count=0, replies_count=0, url='https://icosahedron.website/@cpsdqs/109946395693741603', in_reply_to_id='109946390031097074', in_reply_to_account_id='56142', reblog=None, poll=None, card=None, language='en', text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=False, pinned=None, filtered=[], _meta=) +DEBUG:toot: --> POST https://mastodon.social/api/v1/statuses/109946395816692687/favourite +DEBUG:toot: <-- POST https://mastodon.social/api/v1/statuses/109946395816692687/favourite HTTP 200 178ms +DEBUG:toot:Updating status 109946395816692687, index 0 +INFO:toot.tui.app:Unfavouriting Status(id='109946395816692687', uri='https://icosahedron.website/users/cpsdqs/statuses/109946395693741603', created_at=datetime.datetime(2023, 3, 1, 5, 34, 20, tzinfo=datetime.timezone.utc), account=Account(id='56142', username='cpsdqs', acct='cpsdqs@icosahedron.website', url='https://icosahedron.website/@cpsdqs', display_name='cat-shaped orb (', note='

80% cotton candy
yall ever hear bout animation??

note: youve been deceived. this is my computer yelling account. youre lucky i dont have linux on my computer

', avatar='https://files.mastodon.social/cache/accounts/avatars/000/056/142/original/629aa8a752430f90.png', avatar_static='https://files.mastodon.social/cache/accounts/avatars/000/056/142/original/629aa8a752430f90.png', header='https://files.mastodon.social/cache/accounts/headers/000/056/142/original/56ea591a7b2f09fa.jpg', header_static='https://files.mastodon.social/cache/accounts/headers/000/056/142/original/56ea591a7b2f09fa.jpg', locked=True, fields=[AccountField(name='pronouns', value='huh?? you want to refer to me in 3rd person???? how dare u talk abt me behind my back', verified_at=None), AccountField(name='languages', value='en · de · rs · js', verified_at=None), AccountField(name='videos game', value='i’ve plays them (at least 6)', verified_at=None), AccountField(name='cloud', value='cloudwithlightning dot net', verified_at=None)], emojis=[], bot=False, group=False, discoverable=False, noindex=None, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2017, 2, 14, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 3, 1), statuses_count=22012, followers_count=152, following_count=29), content='

what do i look like i can remember that anisotropic diffusion was called anisotropic diffusion

', visibility='unlisted', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=0, favourites_count=0, replies_count=0, url='https://icosahedron.website/@cpsdqs/109946395693741603', in_reply_to_id='109946390031097074', in_reply_to_account_id='56142', reblog=None, poll=None, card=None, language='en', text=None, edited_at=None, favourited=True, reblogged=False, muted=False, bookmarked=False, pinned=None, filtered=[], _meta=) +DEBUG:toot: --> POST https://mastodon.social/api/v1/statuses/109946395816692687/unfavourite +DEBUG:toot: <-- POST https://mastodon.social/api/v1/statuses/109946395816692687/unfavourite HTTP 200 170ms +DEBUG:toot:Updating status 109946395816692687, index 0 +DEBUG:asyncio:Using selector: EpollSelector +DEBUG:toot: --> GET https://mastodon.social/api/v1/instance +DEBUG:toot: <-- GET https://mastodon.social/api/v1/instance HTTP 200 120ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/followed_tags +DEBUG:toot: <-- GET https://mastodon.social/api/v1/followed_tags HTTP 200 136ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/timelines/home?limit=40 +DEBUG:toot: <-- GET https://mastodon.social/api/v1/timelines/home?limit=40 HTTP 200 467ms +INFO:toot.tui.app:Translating Status(id='109946458408865288', uri='https://mastodon.social/users/mogwai_poet/statuses/109946458408865288/activity', created_at=datetime.datetime(2023, 3, 1, 5, 50, 17, 31000, tzinfo=datetime.timezone.utc), account=Account(id='11872', username='mogwai_poet', acct='mogwai_poet', url='https://mastodon.social/@mogwai_poet', display_name='Jim Stormdancer', note='

Sandwich Imagineer at Twinbeard. Made Frog Fractions. May or may not have already made Frog Fractions 2. He/him.

', avatar='https://files.mastodon.social/accounts/avatars/000/011/872/original/60493e33fff45ae7.jpg', avatar_static='https://files.mastodon.social/accounts/avatars/000/011/872/original/60493e33fff45ae7.jpg', header='https://mastodon.social/headers/original/missing.png', header_static='https://mastodon.social/headers/original/missing.png', locked=False, fields=[], emojis=[], bot=False, group=False, discoverable=True, noindex=False, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2016, 11, 22, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 3, 1), statuses_count=5272, followers_count=2357, following_count=320), content='', visibility='public', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=0, favourites_count=0, replies_count=0, url='https://mastodon.social/users/mogwai_poet/statuses/109946458408865288/activity', in_reply_to_id=None, in_reply_to_account_id=None, reblog=Status(id='109944505113538943', uri='https://worm.horse/users/Nurbsy/statuses/109944504858854081', created_at=datetime.datetime(2023, 2, 28, 21, 33, 28, tzinfo=datetime.timezone.utc), account=Account(id='109297930118798034', username='Nurbsy', acct='Nurbsy@worm.horse', url='https://worm.horse/@Nurbsy', display_name='Nurbsy', note='

I am tired of online. These websites. I am tired of being caught in the tangle of their posts.

', avatar='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', avatar_static='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', header='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', header_static='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', locked=False, fields=[AccountField(name='Twitter', value='twitter.com/Nurbsy', verified_at=None), AccountField(name='Cohost', value='cohost.org/Nurbsy', verified_at=None), AccountField(name='Discord', value='Nurbsy#3747', verified_at=None)], emojis=[], bot=False, group=False, discoverable=True, noindex=None, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2022, 11, 6, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 2, 28), statuses_count=199, followers_count=91, following_count=137), content='

Teeth are some of the most important bones in your mouth

', visibility='public', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=3, favourites_count=1, replies_count=0, url='https://worm.horse/@Nurbsy/109944504858854081', in_reply_to_id=None, in_reply_to_account_id=None, reblog=None, poll=None, card=None, language='en', text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=True, pinned=None, filtered=[], _meta=None), poll=None, card=None, language=None, text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=False, pinned=None, filtered=[], _meta=) +DEBUG:toot: --> POST https://mastodon.social/api/v1/statuses/109946458408865288/translate +DEBUG:toot: <-- POST https://mastodon.social/api/v1/statuses/109946458408865288/translate HTTP 200 204ms +INFO:toot.tui.app:Bookmarking Status(id='109946458408865288', uri='https://mastodon.social/users/mogwai_poet/statuses/109946458408865288/activity', created_at=datetime.datetime(2023, 3, 1, 5, 50, 17, 31000, tzinfo=datetime.timezone.utc), account=Account(id='11872', username='mogwai_poet', acct='mogwai_poet', url='https://mastodon.social/@mogwai_poet', display_name='Jim Stormdancer', note='

Sandwich Imagineer at Twinbeard. Made Frog Fractions. May or may not have already made Frog Fractions 2. He/him.

', avatar='https://files.mastodon.social/accounts/avatars/000/011/872/original/60493e33fff45ae7.jpg', avatar_static='https://files.mastodon.social/accounts/avatars/000/011/872/original/60493e33fff45ae7.jpg', header='https://mastodon.social/headers/original/missing.png', header_static='https://mastodon.social/headers/original/missing.png', locked=False, fields=[], emojis=[], bot=False, group=False, discoverable=True, noindex=False, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2016, 11, 22, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 3, 1), statuses_count=5272, followers_count=2357, following_count=320), content='', visibility='public', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=0, favourites_count=0, replies_count=0, url='https://mastodon.social/users/mogwai_poet/statuses/109946458408865288/activity', in_reply_to_id=None, in_reply_to_account_id=None, reblog=Status(id='109944505113538943', uri='https://worm.horse/users/Nurbsy/statuses/109944504858854081', created_at=datetime.datetime(2023, 2, 28, 21, 33, 28, tzinfo=datetime.timezone.utc), account=Account(id='109297930118798034', username='Nurbsy', acct='Nurbsy@worm.horse', url='https://worm.horse/@Nurbsy', display_name='Nurbsy', note='

I am tired of online. These websites. I am tired of being caught in the tangle of their posts.

', avatar='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', avatar_static='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', header='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', header_static='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', locked=False, fields=[AccountField(name='Twitter', value='twitter.com/Nurbsy', verified_at=None), AccountField(name='Cohost', value='cohost.org/Nurbsy', verified_at=None), AccountField(name='Discord', value='Nurbsy#3747', verified_at=None)], emojis=[], bot=False, group=False, discoverable=True, noindex=None, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2022, 11, 6, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 2, 28), statuses_count=199, followers_count=91, following_count=137), content='

Teeth are some of the most important bones in your mouth

', visibility='public', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=3, favourites_count=1, replies_count=0, url='https://worm.horse/@Nurbsy/109944504858854081', in_reply_to_id=None, in_reply_to_account_id=None, reblog=None, poll=None, card=None, language='en', text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=True, pinned=None, filtered=[], _meta=None), poll=None, card=None, language=None, text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=False, pinned=None, filtered=[], _meta=) +DEBUG:toot: --> POST https://mastodon.social/api/v1/statuses/109946458408865288/bookmark +DEBUG:toot: <-- POST https://mastodon.social/api/v1/statuses/109946458408865288/bookmark HTTP 422 190ms +ERROR:toot.tui.app:Validation failed: Status has already been taken +Traceback (most recent call last): + File "/home/ihabunek/projects/ihabunek/toot/toot/tui/app.py", line 165, in _done + result = future.result() + File "/usr/lib/python3.9/concurrent/futures/_base.py", line 433, in result + return self.__get_result() + File "/usr/lib/python3.9/concurrent/futures/_base.py", line 389, in __get_result + raise self._exception + File "/usr/lib/python3.9/concurrent/futures/thread.py", line 52, in run + result = self.fn(*self.args, **self.kwargs) + File "/home/ihabunek/projects/ihabunek/toot/toot/tui/app.py", line 604, in _bookmark + api.bookmark(self.app, self.user, status.id) + File "/home/ihabunek/projects/ihabunek/toot/toot/api.py", line 216, in bookmark + return _status_action(app, user, status_id, 'bookmark') + File "/home/ihabunek/projects/ihabunek/toot/toot/api.py", line 20, in _status_action + return http.post(app, user, url, data=data).json() + File "/home/ihabunek/projects/ihabunek/toot/toot/http.py", line 80, in post + return anon_post(url, headers=headers, files=files, data=data, json=json, allow_redirects=allow_redirects) + File "/home/ihabunek/projects/ihabunek/toot/toot/http.py", line 99, in anon_post + return process_response(response) + File "/home/ihabunek/projects/ihabunek/toot/toot/http.py", line 50, in process_response + raise ApiError(error) +toot.exceptions.ApiError: Validation failed: Status has already been taken +INFO:toot.tui.app:Bookmarking Status(id='109946458408865288', uri='https://mastodon.social/users/mogwai_poet/statuses/109946458408865288/activity', created_at=datetime.datetime(2023, 3, 1, 5, 50, 17, 31000, tzinfo=datetime.timezone.utc), account=Account(id='11872', username='mogwai_poet', acct='mogwai_poet', url='https://mastodon.social/@mogwai_poet', display_name='Jim Stormdancer', note='

Sandwich Imagineer at Twinbeard. Made Frog Fractions. May or may not have already made Frog Fractions 2. He/him.

', avatar='https://files.mastodon.social/accounts/avatars/000/011/872/original/60493e33fff45ae7.jpg', avatar_static='https://files.mastodon.social/accounts/avatars/000/011/872/original/60493e33fff45ae7.jpg', header='https://mastodon.social/headers/original/missing.png', header_static='https://mastodon.social/headers/original/missing.png', locked=False, fields=[], emojis=[], bot=False, group=False, discoverable=True, noindex=False, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2016, 11, 22, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 3, 1), statuses_count=5272, followers_count=2357, following_count=320), content='', visibility='public', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=0, favourites_count=0, replies_count=0, url='https://mastodon.social/users/mogwai_poet/statuses/109946458408865288/activity', in_reply_to_id=None, in_reply_to_account_id=None, reblog=Status(id='109944505113538943', uri='https://worm.horse/users/Nurbsy/statuses/109944504858854081', created_at=datetime.datetime(2023, 2, 28, 21, 33, 28, tzinfo=datetime.timezone.utc), account=Account(id='109297930118798034', username='Nurbsy', acct='Nurbsy@worm.horse', url='https://worm.horse/@Nurbsy', display_name='Nurbsy', note='

I am tired of online. These websites. I am tired of being caught in the tangle of their posts.

', avatar='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', avatar_static='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', header='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', header_static='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', locked=False, fields=[AccountField(name='Twitter', value='twitter.com/Nurbsy', verified_at=None), AccountField(name='Cohost', value='cohost.org/Nurbsy', verified_at=None), AccountField(name='Discord', value='Nurbsy#3747', verified_at=None)], emojis=[], bot=False, group=False, discoverable=True, noindex=None, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2022, 11, 6, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 2, 28), statuses_count=199, followers_count=91, following_count=137), content='

Teeth are some of the most important bones in your mouth

', visibility='public', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=3, favourites_count=1, replies_count=0, url='https://worm.horse/@Nurbsy/109944504858854081', in_reply_to_id=None, in_reply_to_account_id=None, reblog=None, poll=None, card=None, language='en', text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=True, pinned=None, filtered=[], _meta=None), poll=None, card=None, language=None, text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=False, pinned=None, filtered=[], _meta=) +DEBUG:toot: --> POST https://mastodon.social/api/v1/statuses/109946458408865288/bookmark +DEBUG:toot: <-- POST https://mastodon.social/api/v1/statuses/109946458408865288/bookmark HTTP 422 135ms +ERROR:toot.tui.app:Validation failed: Status has already been taken +Traceback (most recent call last): + File "/home/ihabunek/projects/ihabunek/toot/toot/tui/app.py", line 165, in _done + result = future.result() + File "/usr/lib/python3.9/concurrent/futures/_base.py", line 433, in result + return self.__get_result() + File "/usr/lib/python3.9/concurrent/futures/_base.py", line 389, in __get_result + raise self._exception + File "/usr/lib/python3.9/concurrent/futures/thread.py", line 52, in run + result = self.fn(*self.args, **self.kwargs) + File "/home/ihabunek/projects/ihabunek/toot/toot/tui/app.py", line 604, in _bookmark + api.bookmark(self.app, self.user, status.id) + File "/home/ihabunek/projects/ihabunek/toot/toot/api.py", line 216, in bookmark + return _status_action(app, user, status_id, 'bookmark') + File "/home/ihabunek/projects/ihabunek/toot/toot/api.py", line 20, in _status_action + return http.post(app, user, url, data=data).json() + File "/home/ihabunek/projects/ihabunek/toot/toot/http.py", line 80, in post + return anon_post(url, headers=headers, files=files, data=data, json=json, allow_redirects=allow_redirects) + File "/home/ihabunek/projects/ihabunek/toot/toot/http.py", line 99, in anon_post + return process_response(response) + File "/home/ihabunek/projects/ihabunek/toot/toot/http.py", line 50, in process_response + raise ApiError(error) +toot.exceptions.ApiError: Validation failed: Status has already been taken +DEBUG:asyncio:Using selector: EpollSelector +DEBUG:toot: --> GET https://mastodon.social/api/v1/instance +DEBUG:toot: <-- GET https://mastodon.social/api/v1/instance HTTP 200 121ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/followed_tags +DEBUG:toot: <-- GET https://mastodon.social/api/v1/followed_tags HTTP 200 184ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/timelines/home?limit=40 +DEBUG:toot: <-- GET https://mastodon.social/api/v1/timelines/home?limit=40 HTTP 200 430ms +INFO:toot.tui.app:Bookmarking Status(id='109946458408865288', uri='https://mastodon.social/users/mogwai_poet/statuses/109946458408865288/activity', created_at=datetime.datetime(2023, 3, 1, 5, 50, 17, 31000, tzinfo=datetime.timezone.utc), account=Account(id='11872', username='mogwai_poet', acct='mogwai_poet', url='https://mastodon.social/@mogwai_poet', display_name='Jim Stormdancer', note='

Sandwich Imagineer at Twinbeard. Made Frog Fractions. May or may not have already made Frog Fractions 2. He/him.

', avatar='https://files.mastodon.social/accounts/avatars/000/011/872/original/60493e33fff45ae7.jpg', avatar_static='https://files.mastodon.social/accounts/avatars/000/011/872/original/60493e33fff45ae7.jpg', header='https://mastodon.social/headers/original/missing.png', header_static='https://mastodon.social/headers/original/missing.png', locked=False, fields=[], emojis=[], bot=False, group=False, discoverable=True, noindex=False, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2016, 11, 22, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 3, 1), statuses_count=5272, followers_count=2357, following_count=320), content='', visibility='public', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=0, favourites_count=0, replies_count=0, url='https://mastodon.social/users/mogwai_poet/statuses/109946458408865288/activity', in_reply_to_id=None, in_reply_to_account_id=None, reblog=Status(id='109944505113538943', uri='https://worm.horse/users/Nurbsy/statuses/109944504858854081', created_at=datetime.datetime(2023, 2, 28, 21, 33, 28, tzinfo=datetime.timezone.utc), account=Account(id='109297930118798034', username='Nurbsy', acct='Nurbsy@worm.horse', url='https://worm.horse/@Nurbsy', display_name='Nurbsy', note='

I am tired of online. These websites. I am tired of being caught in the tangle of their posts.

', avatar='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', avatar_static='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', header='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', header_static='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', locked=False, fields=[AccountField(name='Twitter', value='twitter.com/Nurbsy', verified_at=None), AccountField(name='Cohost', value='cohost.org/Nurbsy', verified_at=None), AccountField(name='Discord', value='Nurbsy#3747', verified_at=None)], emojis=[], bot=False, group=False, discoverable=True, noindex=None, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2022, 11, 6, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 2, 28), statuses_count=199, followers_count=91, following_count=137), content='

Teeth are some of the most important bones in your mouth

', visibility='public', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=3, favourites_count=1, replies_count=0, url='https://worm.horse/@Nurbsy/109944504858854081', in_reply_to_id=None, in_reply_to_account_id=None, reblog=None, poll=None, card=None, language='en', text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=True, pinned=None, filtered=[], _meta=None), poll=None, card=None, language=None, text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=False, pinned=None, filtered=[], _meta=) +DEBUG:toot: --> POST https://mastodon.social/api/v1/statuses/109946458408865288/bookmark +DEBUG:toot: <-- POST https://mastodon.social/api/v1/statuses/109946458408865288/bookmark HTTP 422 130ms +ERROR:toot.tui.app:Validation failed: Status has already been taken +Traceback (most recent call last): + File "/home/ihabunek/projects/ihabunek/toot/toot/tui/app.py", line 165, in _done + result = future.result() + File "/usr/lib/python3.9/concurrent/futures/_base.py", line 433, in result + return self.__get_result() + File "/usr/lib/python3.9/concurrent/futures/_base.py", line 389, in __get_result + raise self._exception + File "/usr/lib/python3.9/concurrent/futures/thread.py", line 52, in run + result = self.fn(*self.args, **self.kwargs) + File "/home/ihabunek/projects/ihabunek/toot/toot/tui/app.py", line 604, in _bookmark + api.bookmark(self.app, self.user, status.id) + File "/home/ihabunek/projects/ihabunek/toot/toot/api.py", line 216, in bookmark + return _status_action(app, user, status_id, 'bookmark') + File "/home/ihabunek/projects/ihabunek/toot/toot/api.py", line 20, in _status_action + return http.post(app, user, url, data=data).json() + File "/home/ihabunek/projects/ihabunek/toot/toot/http.py", line 80, in post + return anon_post(url, headers=headers, files=files, data=data, json=json, allow_redirects=allow_redirects) + File "/home/ihabunek/projects/ihabunek/toot/toot/http.py", line 99, in anon_post + return process_response(response) + File "/home/ihabunek/projects/ihabunek/toot/toot/http.py", line 50, in process_response + raise ApiError(error) +toot.exceptions.ApiError: Validation failed: Status has already been taken +DEBUG:asyncio:Using selector: EpollSelector +DEBUG:toot: --> GET https://mastodon.social/api/v1/instance +DEBUG:toot: <-- GET https://mastodon.social/api/v1/instance HTTP 200 115ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/followed_tags +DEBUG:toot: <-- GET https://mastodon.social/api/v1/followed_tags HTTP 200 122ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/timelines/home?limit=40 +DEBUG:toot: <-- GET https://mastodon.social/api/v1/timelines/home?limit=40 HTTP 200 391ms +DEBUG:asyncio:Using selector: EpollSelector +DEBUG:toot: --> GET https://mastodon.social/api/v1/instance +DEBUG:toot: <-- GET https://mastodon.social/api/v1/instance HTTP 200 127ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/followed_tags +DEBUG:toot: <-- GET https://mastodon.social/api/v1/followed_tags HTTP 200 550ms +DEBUG:toot: --> GET https://mastodon.social/api/v1/timelines/home?limit=40 +DEBUG:toot: <-- GET https://mastodon.social/api/v1/timelines/home?limit=40 HTTP 200 771ms +INFO:toot.tui.app:Unbookmarking Status(id='109944505113538943', uri='https://worm.horse/users/Nurbsy/statuses/109944504858854081', created_at=datetime.datetime(2023, 2, 28, 21, 33, 28, tzinfo=datetime.timezone.utc), account=Account(id='109297930118798034', username='Nurbsy', acct='Nurbsy@worm.horse', url='https://worm.horse/@Nurbsy', display_name='Nurbsy', note='

I am tired of online. These websites. I am tired of being caught in the tangle of their posts.

', avatar='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', avatar_static='https://files.mastodon.social/cache/accounts/avatars/109/297/930/118/798/034/original/2b47ef05e7214278.png', header='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', header_static='https://files.mastodon.social/cache/accounts/headers/109/297/930/118/798/034/original/4f24337e5260eed8.jpeg', locked=False, fields=[AccountField(name='Twitter', value='twitter.com/Nurbsy', verified_at=None), AccountField(name='Cohost', value='cohost.org/Nurbsy', verified_at=None), AccountField(name='Discord', value='Nurbsy#3747', verified_at=None)], emojis=[], bot=False, group=False, discoverable=True, noindex=None, moved=None, suspended=None, limited=None, created_at=datetime.datetime(2022, 11, 6, 0, 0, tzinfo=datetime.timezone.utc), last_status_at=datetime.date(2023, 2, 28), statuses_count=199, followers_count=91, following_count=137), content='

Teeth are some of the most important bones in your mouth

', visibility='public', sensitive=False, spoiler_text='', media_attachments=[], application=None, mentions=[], tags=[], emojis=[], reblogs_count=3, favourites_count=1, replies_count=0, url='https://worm.horse/@Nurbsy/109944504858854081', in_reply_to_id=None, in_reply_to_account_id=None, reblog=None, poll=None, card=None, language='en', text=None, edited_at=None, favourited=False, reblogged=False, muted=False, bookmarked=True, pinned=None, filtered=[], _meta=None) +DEBUG:toot: --> POST https://mastodon.social/api/v1/statuses/109944505113538943/unbookmark +DEBUG:toot: <-- POST https://mastodon.social/api/v1/statuses/109944505113538943/unbookmark HTTP 200 160ms diff --git a/toot/tui/app.py b/toot/tui/app.py index fc9aa08..5a29c1c 100644 --- a/toot/tui/app.py +++ b/toot/tui/app.py @@ -258,9 +258,9 @@ class TUI(urwid.Frame): return timeline - def make_status(self, status_data): - status = from_dict(Status, status_data) - status._meta = StatusMeta(status, self.app, self.user) + def make_status(self, data): + status = from_dict(Status, data) + status._meta = StatusMeta(status, data, self.app, self.user) return status def show_thread(self, status): @@ -529,11 +529,8 @@ class TUI(urwid.Frame): api.unfavourite(self.app, self.user, status.id) def _done(loop): - # Create a new Status with flipped favourited flag - new_data = status.data - new_data["favourited"] = not status.favourited - new_status = self.make_status(new_data) - timeline.update_status(new_status) + status.favourited = not status.favourited + timeline.redraw_status(status) self.run_in_thread( _unfavourite if status.favourited else _favourite, @@ -550,11 +547,8 @@ class TUI(urwid.Frame): api.unreblog(self.app, self.user, status.id) def _done(loop): - # Create a new Status with flipped reblogged flag - new_data = status.data - new_data["reblogged"] = not status.reblogged - new_status = self.make_status(new_data) - timeline.update_status(new_status) + status.reblogged = not status.reblogged + timeline.redraw_status(status) # Check if status is rebloggable no_reblog_because_private = status.visibility == "private" and not status._meta.is_mine @@ -568,7 +562,10 @@ class TUI(urwid.Frame): done_callback=_done ) - def async_translate(self, timeline, status): + def async_translate(self, timeline: Timeline, status: Status): + meta = status._meta + assert meta + def _translate(): logger.info("Translating {}".format(status)) self.footer.set_message("Translating status {}".format(status.id)) @@ -589,15 +586,15 @@ class TUI(urwid.Frame): def _done(response): if response is not None: - status._meta.translation = response["content"] - status._meta.translated_from = response["detected_source_language"] - status._meta.show_translation = True - timeline.update_status(status) + meta.translation = response["content"] + meta.translated_from = response["detected_source_language"] + meta.show_translation = True + timeline.redraw_status(status) # If already translated, toggle showing translation - if status.translation: - status.show_translation = not status.show_translation - timeline.update_status(status) + if meta.translation: + meta.show_translation = not meta.show_translation + timeline.redraw_status(status) else: self.run_in_thread(_translate, done_callback=_done) @@ -611,11 +608,8 @@ class TUI(urwid.Frame): api.unbookmark(self.app, self.user, status.id) def _done(loop): - # Create a new Status with flipped bookmarked flag - new_data = status.data - new_data["bookmarked"] = not status.bookmarked - new_status = self.make_status(new_data) - timeline.update_status(new_status) + status.bookmarked = not status.bookmarked + timeline.redraw_status(status) self.run_in_thread( _unbookmark if status.bookmarked else _bookmark, diff --git a/toot/tui/entities.py b/toot/tui/entities.py index ec39d80..36d1b79 100644 --- a/toot/tui/entities.py +++ b/toot/tui/entities.py @@ -74,6 +74,9 @@ class Account: @dataclass class Application: + """ + https://docs.joinmastodon.org/entities/Status/#application + """ name: str website: Optional[str] @@ -162,6 +165,9 @@ class PreviewCard: @dataclass class FilterKeyword: + """ + https://docs.joinmastodon.org/entities/FilterKeyword/ + """ id: str keyword: str whole_word: str @@ -169,12 +175,18 @@ class FilterKeyword: @dataclass class FilterStatus: + """ + https://docs.joinmastodon.org/entities/FilterStatus/ + """ id: str status_id: str @dataclass class Filter: + """ + https://docs.joinmastodon.org/entities/Filter/ + """ id: str title: str context: List[str] @@ -186,6 +198,9 @@ class Filter: @dataclass class FilterResult: + """ + https://docs.joinmastodon.org/entities/FilterResult/ + """ filter: Filter keyword_matches: Optional[List[str]] status_matches: Optional[str] @@ -195,13 +210,20 @@ class StatusMeta: """ Additional information kept for a status. """ + # TODO: it's not perfect keeping the status source as a dict since encoding + # to and from json can be lossy. Ideally we'd keep the status source as + # JSON-encoded string as returned by the server but this would require + # api.* methods not to already decode them. + source: dict + is_mine: bool show_sensitive: bool = False show_translation: bool = False translated_from: Optional[str] = None translation: Optional[str] = None - def __init__(self, status: "Status", app: App, user: User): + def __init__(self, status: "Status", source: dict, app: App, user: User): + self.source = source self.is_mine = user.username == status.account.acct @@ -258,13 +280,12 @@ def from_dict(cls: Type[T], data: Dict) -> T: def _fields(): hints = get_type_hints(cls) for field in dataclasses.fields(cls): - if not field.name.startswith("_"): - field_type = prune_optional(hints[field.name]) - default_value = get_default_value(field) - value = data.get(field.name, default_value) - yield convert(field_type, value) + field_type = prune_optional(hints[field.name]) + default_value = get_default_value(field) + value = data.get(field.name, default_value) + yield field.name, convert(field_type, value) - return cls(*_fields()) + return cls(**dict(_fields())) def get_default_value(field): diff --git a/toot/tui/overlays.py b/toot/tui/overlays.py index ca5eb8e..2cd0cba 100644 --- a/toot/tui/overlays.py +++ b/toot/tui/overlays.py @@ -13,7 +13,8 @@ from .widgets import Button, EditBox, SelectableText class StatusSource(urwid.Padding): """Shows status data, as returned by the server, as formatted JSON.""" def __init__(self, status: Status): - self.source = json.dumps(status.data, indent=4) + assert status._meta + self.source = json.dumps(status._meta.source, indent=4) self.filename_edit = EditBox(caption="Filename: ", edit_text=f"status-{status.id}.json") self.status_text = urwid.Text("") diff --git a/toot/tui/timeline.py b/toot/tui/timeline.py index 3681660..851191b 100644 --- a/toot/tui/timeline.py +++ b/toot/tui/timeline.py @@ -291,6 +291,17 @@ class Timeline(urwid.Columns): if index == self.status_list.body.focus: self.draw_status_details(status) + # TODO: dedupe code with update_status + def redraw_status(self, status: Status): + index = self.get_status_index(status.id) + + # Redraw list item + self.status_list.body[index] = self.build_list_item(status) + + # Redraw status details if status is focused + if index == self.status_list.body.focus: + self.draw_status_details(status) + def remove_status(self, status): index = self.get_status_index(status.id) assert self.statuses[index].id == status.id # Sanity check