Repository: searxng/searxng

Issue:
Mastodon & Pixelfed
<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->

**Working URL to the engine**
<!-- Please check if the engine is responding correctly before submitting it. -->
I don't know yet. I'll find out later and update.

**Why do you want to add this engine?**
<!-- What's special about this engine? Is it open source or libre? -->
To encourage libre and decentralized www.

**Features of this engine**
<!-- Features of this engine: Doesn't track its users, fast, easy to integrate, ... -->
Photo sharing platform.

**How can SearXNG fetch the information from this engine?**
<!-- List API URL, example code (using the correct markdown) and more
that could be useful for the developers in order to implement this engine.
If you don't know what to write, let this part blank.>

**Applicable category of this engine**
<!-- Where should this new engine fit in SearXNG? Current categories in SearXNG:
general, files, images, it, map, music, news, science, social media and videos.
You can add multiple categories at the same time. -->
images, social media

**Additional context**
<!-- Add any other context about this engine here. -->
https://pixelfed.social/
https://pixelfed.org/


Discussion:
Apparently [Pixelfed's API](https://docs.pixelfed.org/technical-documentation/api-v1.html) is designed after the Mastodon API so it probably makes more sense to support Mastodon.

Ref: https://github.com/pixelfed/ideas/issues/12
I don't think you can search Mastodon/Pixelfed without logging in

For Mastodon, you *can* look at hashtags without logging in, but then it will largely depend on perspective (ie. which instance you choose)
> I don't think you can search Mastodon/Pixelfed without logging in

Only certain type of posts (e.g. classified as Public).

> but then it will largely depend on perspective (ie. which instance you choose)

Similarly to diaspora*, GNU Social, Movim (XMPP) and PeerTube (supported by searx), cross search is possible from one instance alone.
Yet, not every instance may have results from all other instances, so I encourage the practice of searching from multiple instances at once in order to reveal more results.
I'm a big fan of @austinhuang0131
Good job, Austin!
Would it be possible to enable search on Mastodon via Tootfinder API? 
https://tootfinder.ch/wiki/index.php?name=the-tootfinder-rest-api

Tootfinder (https://tootfinder.ch/) is a full-text search for Mastodon. Though it requires users to opt in (that's the only accepted approach in the Fediverse right now), it would definitely be a big plus for SearXNG.

/cc @mrpaulblack 




Resolving patch:
diff --git a/searx/engines/mastodon.py b/searx/engines/mastodon.py
new file mode 100644
--- /dev/null
+++ b/searx/engines/mastodon.py
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# lint: pylint
+"""Mastodon_ is an open source alternative to large social media platforms like
+Twitter/X, Facebook, ...
+
+Since it's federated and self-hostable, there's a large amount of available
+instances, which can be chosen instead by modifying ``base_url``.
+
+We use their official API_ for searching, but unfortunately, their Search API_
+forbids pagination without OAuth.
+
+That's why we use tootfinder.ch for finding posts, which doesn't support searching
+for users, accounts or other types of content on Mastodon however.
+
+.. _Mastodon: https://mastodon.social
+.. _API: https://docs.joinmastodon.org/api/
+
+"""
+
+from urllib.parse import urlencode
+from datetime import datetime
+
+about = {
+    "website": 'https://joinmastodon.org/',
+    "wikidata_id": 'Q27986619',
+    "official_api_documentation": 'https://docs.joinmastodon.org/api/',
+    "use_official_api": True,
+    "require_api_key": False,
+    "results": 'JSON',
+}
+categories = ['social media']
+
+base_url = "https://mastodon.social"
+mastodon_type = "accounts"
+
+# https://github.com/searxng/searxng/pull/2857#issuecomment-1741713999
+page_size = 40
+
+
+def request(query, params):
+    args = {
+        'q': query,
+        'resolve': 'false',
+        'type': mastodon_type,
+        'limit': page_size,
+    }
+    params['url'] = f"{base_url}/api/v2/search?{urlencode(args)}"
+    return params
+
+
+def response(resp):
+    results = []
+
+    json = resp.json()
+
+    for result in json[mastodon_type]:
+        if mastodon_type == "accounts":
+            results.append(
+                {
+                    'url': result['uri'],
+                    'title': result['username'] + f" ({result['followers_count']} followers)",
+                    'content': result['note'],
+                    'thumbnail': result.get('avatar'),
+                    'publishedDate': datetime.strptime(result['created_at'][:10], "%Y-%m-%d"),
+                }
+            )
+        elif mastodon_type == "hashtags":
+            uses_count = sum(int(entry['uses']) for entry in result['history'])
+            user_count = sum(int(entry['accounts']) for entry in result['history'])
+            results.append(
+                {
+                    'url': result['url'],
+                    'title': result['name'],
+                    'content': f"Hashtag has been used {uses_count} times by {user_count} different users",
+                }
+            )
+        else:
+            raise ValueError(f"Unsupported mastodon type: {mastodon_type}")
+
+    return results


Test patch: