# I am the Watcher. I am your guide through this vast new twtiverse.
# 
# Usage:
#     https://watcher.sour.is/api/plain/users              View list of users and latest twt date.
#     https://watcher.sour.is/api/plain/twt                View all twts.
#     https://watcher.sour.is/api/plain/mentions?uri=:uri  View all mentions for uri.
#     https://watcher.sour.is/api/plain/conv/:hash         View all twts for a conversation subject.
# 
# Options:
#     uri     Filter to show a specific users twts.
#     offset  Start index for quey.
#     limit   Count of items to return (going back in time).
# 
# twt range = 1 12
# self = https://watcher.sour.is/conv/xetzbua
Now that I lost my by far longest twt yesterday night just before publishing it, I implemented a crude automatic backup of composed twts. Every 20 seconds the text will be saved to disk if it changed from the previous snapshot. This way I'm able to recover manually from a crashed tt in the future. Let's see if I find some motivation today, to write it up all again what I lost last night. Wasted an exact hour to the minute.
Alright, I now made the interval at which composed twts are automatically backed up configurable in, well, the configuration file. This also brings me back to my original twt from yesterday night, that got destroyed when I wanted to publish it. But first, let's quickly talk about the crash. In tt the creation timestamp of a twt can be changed, in case one wants to. And I certainly do. Because of privacy reasons I *fancy*. My timestamps are all (with one single, but important exception) in five minutes granularity. Yup, since the very beginning. You probably haven't noticed, because you don't care too much about these timestamps anyway. So it's alright that I continue this fashion, you might consider silly. And now some of you got curious and checked my raw feed. Got you! ;-)

Last night I also wanted to fiddle with the creation timestamp and somehow something went wrong. At some place in the `dateutil` library, which I also mentioned in said blown up twt. No wonder it was cursed. The error didn't happen in my own code but rather occurred in the dateutil library when it tried to handle an error about an invalid timestamp. Very weird, I wasn't able to reproduce this ever since. No idea what went wrong here. The stripped and annotated stacktrace is as follows:


    Traceback (most recent call last):
      File "/usr/lib/python3/dist-packages/dateutil/parser/_parser.py", line 655, in parse
        ret = self._build_naive(res, default)
      File "/usr/lib/python3/dist-packages/dateutil/parser/_parser.py", line 1238, in _build_naive
        if cday > monthrange(cyear, cmonth)[1]:
      File "/usr/lib/python3.9/calendar.py", line 124, in monthrange
(3)     raise IllegalMonthError(month)
(4) calendar.IllegalMonthError: bad month number 0; must be 1-12

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      …cut off for brevity…
      File "/usr/lib/python3/dist-packages/urwid/widget.py", line 461, in _emit
        signals.emit_signal(self, name, self, *args)
      File "/usr/lib/python3/dist-packages/urwid/signals.py", line 265, in emit
        result |= self._call_callback(callback, user_arg, user_args, args)
      File "/usr/lib/python3/dist-packages/urwid/signals.py", line 295, in _call_callback
        return bool(callback(*args_to_pass))
      File "/usr/local/bin/tt", line 552, in update_preview
        preview_widgets, rows_calculating_delegate_widget = self._render_new_twt()
      File "/usr/local/bin/tt", line 565, in _render_new_twt
        created_at = self._created_at
      File "/usr/local/bin/tt", line 627, in _created_at
        created_at = dateutil.parser.parse(self._created_at_edit.edit_text)
      File "/usr/lib/python3/dist-packages/dateutil/parser/_parser.py", line 1374, in parse
        return DEFAULTPARSER.parse(timestr, **kwargs)
      File "/usr/lib/python3/dist-packages/dateutil/parser/_parser.py", line 657, in parse
(1)     six.raise_from(ParserError(e.args[0] + ": %s", timestr), e)
(2) TypeError: unsupported operand type(s) for +: 'int' and 'str'


So as seen in (1) e.args[0] must have been an integer rather than a string (2) as it was expected by the dateutil programmer and so the TypeError was raised by the Python interpreter. To me it appears as if the calendar.IllegalMonthError from above in (3) was the culprit. Okay, it wasn't expected to be caught down there at around (1) and (2). Maybe. Judging by its message (4), the bad month number 0 is the first and only argument, as seen in the constructor call in (3).

If everything had gone to plan, the ParserError in (1) would have been caught in tt and everything would have been dandy. The field would have just appeared in red and the "Publish" command button deactivated. But I just caught the ParserError, not the TypeError. So it blew apart.

Now again, I wasn't able to reproduce this so far. No luck whatsoever. Writing this twt gave me an idea that I could now look into the code to see how the calendar.IllegalMonthError will be triggered in the first place. I have the slight feeling that this could be the ticket. So please excuse me now, I have to get my scuba gear, I'm about to dive deep. The original twt will be reconsidered another time.

On a closing note, since starting this post exactly 45 minutes have passed. I will hit the "Publish" button in a bit more than 20 seconds to avoid another lost twt.
Whoops, this is long. Verrrrrrrrrrrrrrrrry long. It doesn't even fit on one page in tt. I will try to split this up next time.
I managed to reproduce this in the debugger (pudb FTW!) with dateutil.parser.parse("2022--31T23:59:00+01:00") where the day is missing. Turns out I'm running version 2.8.1 and somebody fixed this bug already half a year ago in version 2.8.2. In my virtual environment I setup on Friday I already had the fix, but not on my system level Python installation. The Debian package even for sid still ships the outdated version with the bug. Instead I had to use pip to upgrade. Sigh.
@lyse very long, but a very good read. I am sure the programmers around here will have a blast, because me—not a programmer—did! I have noticed that your timestamps were way too neat before, and have thought that tt was designed to round to the next 5 minutes for timestamps, on twt creation. Knowing now that's a manual process surprises, and intrigues me.

Surprises me because it is something I am sure you can do programmatically, but then it wouldn't be random, would it? Now, what kind of privacy are you protecting—and this is the intriguing piece—by changing a few minutes here and there? Of course, I am assuming that, but for all we know you might be changing hours on your twt. When are you actually twting, @lyse when? Tell us, tell us!

Are you a time traveler, spartan Lysander? Maybe you didn't die on 395BC, but simply flashed a "few" years into the future, did you? Hmm, I don't know what to believe anymore!
I'm also curious on the privacy concerns here?
I'm also curious on the privacy concerns here?
@lyse I haven’t noticed the timestamps. 🥴 I kind of like the idea, but I can’t really tell why. 🤔
@lyse I haven’t noticed the timestamps. 🥴 I kind of like the idea, but I can’t really tell why. 🤔
@lyse I haven’t noticed the timestamps. 🥴 I kind of like the idea, but I can’t really tell why. 🤔
@david Currently, tt rounds to the next minute. But it shouldn't be very hard to go to the next five minutes, no. The only problem here is that I manually have to track that I don't create duplicates in the sense of timestamps. Already have an item on my todo list that tt will help with that by coloring the timestamp field if a duplicate is found. I'm always posting from the future. Or am I!?!? @movq Maybe because they look nice. :-P
@david @prologic Admittedly, nothing super fancy. I just don't want random people know when *exactly* I'm active or how much time I spent on twtxt. It's public after all. Just think of increasing the noise. So analyses such as "how quickly is this guy responding" etc. result in a much more incorrect and thus hopefully less valuable data point. But yes, a trend is clearly visible, I'm aware of that.