Skip to content

Watch for changed files in SyncThing

For syncing files between my devices I'm using SyncThing. This tool is reliable, available on Linux, Android, Mac and iOS. And it encrypts the communication.


But sometimes I want to know when files in certain directories have changed - as example in my Obsidian vault. This allows me to post-process the files.

Some of the use cases I have in Obsidian:

  • Resolve links in Daily Notes: when I share a URL from my RSS reader or from other sources into Obsidian, the URL sometimes is just a link to a URL shortener. I then later need to resolve the link - or let a script do it right away for known shortlinks, and update the daily note.
  • Remove tracking information from URLs: many shared links include campaign and tracking information, and this can be removed straight away.
  • Extract content from certain Toots: I follow a couple interesting accounts on Mastodon, and when I share the Toot link into Obsidian, the script extracts the Toot content and adds it, along with the original link, to a pre-defined note.
  • Extract links from Toots: many news websites include a link (sometimes again with tracking information) in their Toots. When I share such a Toot into Obsidian, a script picks up the link, extract the target link and updates the daily note.

All of this is not very complicated, and a couple of lines in Python do the job. The main parts for the script are:

  • extract the API key from the SyncThing configuration
  • Open a connection to the local SyncThing instance
  • Watch for certain events

 

Continue reading "Watch for changed files in SyncThing"

Migration from Twitter to Mastodon

Many people (I don’t like this phrase) are leaving Twitter these days, and looking for a new social media home. One of these places is Mastodon. This blog post aims to summarize the steps necessary for a migration, and includes pointers to websites which can help with said move.

 

Image & CC: https://www.pexels.com/photo/3-grey-elephants-under-yellow-sky-68550/

 

What is Twitter?

Twitter: @ascherbaum

Twitter is a Microblogging service. Users post short texts with maximal 140 280 characters, optionally including media attachments. Tweets (that’s the name for the posting) are public by default, however Twitter implemented the ability to protect accounts (make the content private to followers only), or recently implemented functionality to target specific user groups for Tweets. In October 2022 Elon Musk completed the acquisition of Twitter, and took over as CEO. The following weeks have seen erratic and dramatic changes, which are not well-received by all users. Quite a number of users decided to leave Twitter. In addition, as a consequence of the turmoil some companies stopped doing advertisements on Twitter. The future will show if the users and advertisers will come back.

Users have a unique username on the platform, mine is @ascherbaum.

 

What is Mastodon?

 

Mastodon: @ascherbaum@mastodon.social

Mastodon is a Microblogging, which in contrast to the centralized Twitter, runs on decentralized (federated) instances (servers). The instances communicate with each other. The software is open source, and the project started around 2016.

Postings in Mastodon are named Toots, not Tweets. Or Trööt in German. Please let me know the word in other languages, I will update this posting.

After Elon Musk took over at Twitter, users started to migrate to Mastodon as an alternative, and every controversial announcement shows a new wave of users leaving. This will likely keep going for quite a while.

Mastodon users have a unique username on one instance, however the same username on a different instance can be used by someone else. There is no universal verification across instances, instances might implement their own verification. For example the social.bund.de instance is only open to other federal agencies of the German government - therefore every account on this instance is already validated as a government account.

My Mastodon account (the one I’m currently using) is ascherbaummastodon.social. The software allows users to move to new instances and migrate followers over, check the profile settings of your instance how to do that.
 

 

Continue reading "Migration from Twitter to Mastodon"

Hanging script at the end of Python multiprocessing

While writing and stress-testing a Python script which uses the multiprocessing library, I ran into the problem that occasionally the script hangs at the end. Literally runs past the last line of code and then hangs.

In this script I'm using Queues and Events, so I made sure that I properly close the queues in the forked/spawned (tried both) processes and also clean out all queues in the parent. Nevertheless occasionally - like seldom, but it happens - the script hangs. Checked the process list, checked remaining threads, checked the queues, all fine. Still ...

When I hit Ctrl+C, I get the following stack trace:

^CError in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/lib/python3.9/multiprocessing/util.py", line 300, in _run_finalizers
    finalizer()
  File "/usr/lib/python3.9/multiprocessing/util.py", line 224, in __call__
    res = self._callback(*self._args, **self._kwargs)
  File "/usr/lib/python3.9/multiprocessing/queues.py", line 201, in _finalize_join
    thread.join()
  File "/usr/lib/python3.9/threading.py", line 1053, in join
    self._wait_for_tstate_lock()
  File "/usr/lib/python3.9/threading.py", line 1073, in _wait_for_tstate_lock
    if lock.acquire(block, timeout):
KeyboardInterrupt

(Can't change the Python 3.9, as I don't control the target system where this is supposed to run)

There's nothing left to join, the only two remaining threads are MainThread and QueueFeederThread.

 

After some more debugging I had the simple idea not only to empty the queue before finishing the script, but setting the queue variable to None:

worker_queue = None

This helps. The script is no longer hanging at the end. At a later point I will see if the problem is fixed in newer versions.

Django: Remove default entries from admin menu

The Django administration site comes with a couple of default entries, depending on which apps and middleware is installed.

Usually there is at least "Users" and "Groups", and if the quite common "allauth" is installed, then there is also "Site", "SocialApp", "SocialAccount", and "SocialToken". They are not always necessary, especially when no Social Login is used. Or why have the "Site" administration when only Site=1 is used?

With a few tricks these can be removed from the admin menu.

 

Continue reading "Django: Remove default entries from admin menu"

Django: disable inline option to add new referenced objects

The Django Web Framework makes it quite easy to add new referenced objects in the admin menu.

Let's say the model has two foreign keys in it:

class TeamMember(models.Model):
    team = models.ForeignKey(Team, on_delete=models.CASCADE, verbose_name=_("Team"))
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, verbose_name=_("User"))

And the admin form:

class TeamMemberForm(forms.ModelForm):
    class Meta:
        model = TeamMember

class CustomTeamMemberAdmin(admin.ModelAdmin):
    form = TeamMemberForm
    add_form = TeamMemberForm
    model = TeamMember
    fieldsets = [
        (None,      {'fields': ['team', 'user']}),
    ]

admin.site.register(TeamMember, CustomTeamMemberAdmin)

Then Django will show small a small green "Django add reference" sign next to the fields:

Continue reading "Django: disable inline option to add new referenced objects"

Use namespace as global variable in Ansible Jinja templates

A simple task, or so I thought: in a Jinja template keep track of the number of items in a loop. And then use that count afterwards.

Disclaimer: the number of items is not equal the number of times the loop runs, so I can't use the loop variables.

Turns out that Jinja has other opinions, and variables inside a loop are all local. When a variable is changed inside the loop, the scoop of the variable stays local, and once the loop ends the original value of the variable is restored. That's even true for variables which are created outside the loop.

 

Continue reading "Use namespace as global variable in Ansible Jinja templates"

Certificate expiration date in Ansible

In one of my Ansible Playbooks I'm updating Let's Encrypt certificates. Because the Playbook is rather long, I want to make sure that Ansible is not spending cycles on a certificate which is not about to expire. Hence I wrote a small filter plugin, which receives the certificate path and the number of days the certificate must be still valid.

This plugin is used to filter out any "good" certificate which does not need to be updated.

 

Continue reading "Certificate expiration date in Ansible"