Merge next-gen

This commit is contained in:
Jesús Pavón Abián
2026-02-01 09:07:27 +01:00
35 changed files with 693 additions and 93 deletions

View File

@@ -84,4 +84,10 @@ def compose_notification(notification, db, settings, relative_times, show_screen
filtered = utils.evaluate_filters(post=notification, current_context="notifications")
if filtered != None:
text = _("hidden by filter {}").format(filtered)
return [user, text, ts]
return [user, text, ts]
def compose_announcement(announcement, db, settings, relative_times, show_screen_names, safe=False):
# Use the default template or a configured one if available
template = settings.get("templates", {}).get("announcement", templates.announcement_default_template)
text = templates.render_announcement(announcement, template, settings, relative_times, db["utc_offset"])
return [text]

View File

@@ -217,37 +217,69 @@ class Session(base.baseSession):
self.sound.play(_sound)
return val
def send_post(self, reply_to=None, visibility=None, language=None, posts=[]):
def _send_quote_post(self, text, quote_id, visibility, sensitive, spoiler_text, language, scheduled_at, in_reply_to_id=None, media_ids=[], poll=None):
"""Internal helper to send a quote post using direct API call."""
params = {
'status': text,
'visibility': visibility,
'quoted_status_id': quote_id,
}
if in_reply_to_id:
params['in_reply_to_id'] = in_reply_to_id
if sensitive:
params['sensitive'] = sensitive
if spoiler_text:
params['spoiler_text'] = spoiler_text
if language:
params['language'] = language
if scheduled_at:
if hasattr(scheduled_at, 'isoformat'):
params['scheduled_at'] = scheduled_at.isoformat()
else:
params['scheduled_at'] = scheduled_at
if media_ids:
params['media_ids'] = media_ids
if poll:
params['poll'] = poll
# Use the internal API request method directly
return self.api._Mastodon__api_request('POST', '/api/v1/statuses', params)
def send_post(self, reply_to=None, quote_id=None, visibility=None, language=None, posts=[]):
""" Convenience function to send a thread. """
in_reply_to_id = reply_to
for obj in posts:
text = obj.get("text")
if len(obj["attachments"]) == 0:
scheduled_at = obj.get("scheduled_at")
# Prepare media and polls first as they are needed for both standard and quote posts
media_ids = []
poll = None
if len(obj["attachments"]) > 0:
try:
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, visibility=visibility, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language)
# If it fails, let's basically send an event with all passed info so we will catch it later.
except Exception as e:
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language)
return
if item != None:
in_reply_to_id = item["id"]
else:
media_ids = []
try:
poll = None
if len(obj["attachments"]) == 1 and obj["attachments"][0]["type"] == "poll":
poll = self.api.make_poll(options=obj["attachments"][0]["options"], expires_in=obj["attachments"][0]["expires_in"], multiple=obj["attachments"][0]["multiple"], hide_totals=obj["attachments"][0]["hide_totals"])
else:
for i in obj["attachments"]:
media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True)
media_ids.append(media.id)
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language)
if item != None:
in_reply_to_id = item["id"]
except Exception as e:
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language)
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, language=language)
return
try:
if quote_id:
item = self._send_quote_post(text, quote_id, visibility, obj["sensitive"], obj["spoiler_text"], language, scheduled_at, in_reply_to_id, media_ids, poll)
self.sound.play("tweet_send.ogg")
else:
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language, scheduled_at=scheduled_at)
if item != None:
in_reply_to_id = item["id"]
except Exception as e:
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, language=language)
return
def edit_post(self, post_id, posts=[]):
""" Convenience function to edit a post. Only the first item in posts list is used as threads cannot be edited.

View File

@@ -13,12 +13,14 @@ post_variables = ["date", "display_name", "screen_name", "source", "lang", "safe
person_variables = ["display_name", "screen_name", "description", "followers", "following", "favorites", "posts", "created_at"]
conversation_variables = ["users", "last_post"]
notification_variables = ["display_name", "screen_name", "text", "date"]
announcement_variables = ["text", "published_at", "updated_at", "starts_at", "ends_at", "read"]
# Default, translatable templates.
post_default_template = _("$display_name, $text $image_descriptions $date. $source")
dm_sent_default_template = _("Dm to $recipient_display_name, $text $date")
person_default_template = _("$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.")
notification_default_template = _("$display_name $text, $date")
announcement_default_template = _("$text. Published $published_at. $read")
def process_date(field, relative_times=True, offset_hours=0):
original_date = arrow.get(field)
@@ -185,3 +187,23 @@ def render_notification(notification, template, post_template, settings, relativ
result = Template(_(template)).safe_substitute(**available_data)
result = result.replace(" . ", "")
return result
def render_announcement(announcement, template, settings, relative_times=False, offset_hours=0):
""" Renders any given announcement according to the passed template. """
global announcement_variables
available_data = dict()
# Process dates
for date_field in ["published_at", "updated_at", "starts_at", "ends_at"]:
if hasattr(announcement, date_field) and getattr(announcement, date_field) is not None:
available_data[date_field] = process_date(getattr(announcement, date_field), relative_times, offset_hours)
else:
available_data[date_field] = ""
available_data["text"] = utils.html_filter(announcement.content)
if announcement.read:
available_data["read"] = _("Read")
else:
available_data["read"] = _("Unread")
result = Template(_(template)).safe_substitute(**available_data)
return result

View File

@@ -140,6 +140,11 @@ def evaluate_filters(post: dict, current_context: str) -> str | None:
- None if no applicable filters are found, meaning the post should be shown normally.
"""
filters = post.get("filtered", None)
# Automatically hide muted conversations from home timeline.
if current_context == "home" and post.get("muted") == True:
return "hide"
if filters == None:
return
warn_filter_title = None