Merge branch 'next-gen' of github.com:mcv-software/twblue into next-gen

This commit is contained in:
Manuel Cortez 2023-03-24 07:19:08 -06:00
commit 8940825509
No known key found for this signature in database
GPG Key ID: 9E0735CA15EFE790
29 changed files with 36642 additions and 39270 deletions

View File

@ -7,6 +7,7 @@ We had to release a 2023.2.8 version of TWblue, which removes the API deprecatio
* TWBlue should be able to display variables within templates (for example, now it is possible to send a template inside a post's text). Before, it was removing $variables so it was difficult to show how to edit templates from the client. ([#515](https://github.com/MCV-Software/TWBlue/issues/515)) * TWBlue should be able to display variables within templates (for example, now it is possible to send a template inside a post's text). Before, it was removing $variables so it was difficult to show how to edit templates from the client. ([#515](https://github.com/MCV-Software/TWBlue/issues/515))
* Mastodon: * Mastodon:
* it is possible to add descriptions for all media available on Mastodon (audio, photos, video and Givs). ([#516](https://github.com/MCV-Software/TWBlue/issues/516)) * it is possible to add descriptions for all media available on Mastodon (audio, photos, video and Givs). ([#516](https://github.com/MCV-Software/TWBlue/issues/516))
* Fixed an error on mentions buffer that was making TWBlue unable to load posts if there were mentions from a blocked or deleted account.
## Changes on version 2023.2.6 ## Changes on version 2023.2.6

View File

@ -564,4 +564,16 @@ class BaseBuffer(base.Buffer):
dlg.Destroy() dlg.Destroy()
if answer != wx.ID_OK: if answer != wx.ID_OK:
return return
poll = self.session.api_call(call_name="poll_vote", id=poll.id, choices=options, preexec_message=_("Sending vote...")) poll = self.session.api_call(call_name="poll_vote", id=poll.id, choices=options, preexec_message=_("Sending vote..."))
def post_from_error(self, visibility, data):
title = _("Post")
caption = _("Write your post here")
post = messages.post(session=self.session, title=title, caption=caption)
post.set_post_data(visibility=visibility, data=data)
response = post.message.ShowModal()
if response == wx.ID_OK:
post_data = post.get_data()
call_threaded(self.session.send_post, posts=post_data, visibility=post.get_visibility())
if hasattr(post.message, "destroy"):
post.message.destroy()

View File

@ -28,6 +28,8 @@ class MentionsBuffer(BaseBuffer):
except Exception as e: except Exception as e:
log.exception("Error %s" % (str(e))) log.exception("Error %s" % (str(e)))
return return
# Attempt to remove items with no statuses attached to them as it might happen when blocked accounts have notifications.
items = [item for item in items if item.status != None]
number_of_items = self.session.order_buffer(self.name, items) number_of_items = self.session.order_buffer(self.name, items)
log.debug("Number of items retrieved: %d" % (number_of_items,)) log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items) self.put_items_on_list(number_of_items)
@ -49,7 +51,8 @@ class MentionsBuffer(BaseBuffer):
except Exception as e: except Exception as e:
log.exception("Error %s" % (str(e))) log.exception("Error %s" % (str(e)))
return return
print(items) # Attempt to remove items with no statuses attached to them as it might happen when blocked accounts have notifications.
items = [item for item in items if item.status != None]
items_db = self.session.db[self.name] items_db = self.session.db[self.name]
for i in items: for i in items:
if utils.find_item(i, self.session.db[self.name]) == None: if utils.find_item(i, self.session.db[self.name]) == None:

View File

@ -130,7 +130,7 @@ class Controller(object):
pub.subscribe(self.mastodon_new_item, "mastodon.new_item") pub.subscribe(self.mastodon_new_item, "mastodon.new_item")
pub.subscribe(self.mastodon_updated_item, "mastodon.updated_item") pub.subscribe(self.mastodon_updated_item, "mastodon.updated_item")
pub.subscribe(self.mastodon_new_conversation, "mastodon.conversation_received") pub.subscribe(self.mastodon_new_conversation, "mastodon.conversation_received")
pub.subscribe(self.mastodon_error_post, "mastodon.error_post")
# connect application events to GUI # connect application events to GUI
widgetUtils.connect_event(self.view, widgetUtils.CLOSE_EVENT, self.exit_) widgetUtils.connect_event(self.view, widgetUtils.CLOSE_EVENT, self.exit_)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
@ -1306,6 +1306,10 @@ class Controller(object):
# if "direct_messages" not in buffer.session.settings["other_buffers"]["muted_buffers"]: # if "direct_messages" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
# self.notify(buffer.session, sound_to_play) # self.notify(buffer.session, sound_to_play)
def mastodon_error_post(self, name, reply_to, visibility, posts):
home = self.search_buffer("home_timeline", name)
wx.CallAfter(home.post_from_error, visibility=visibility, data=posts)
def report_error(self, *args, **kwargs): def report_error(self, *args, **kwargs):
"""Redirects the user to the issue page on github""" """Redirects the user to the issue page on github"""
log.debug("Redirecting the user to report an error...") log.debug("Redirecting the user to report an error...")

View File

@ -64,6 +64,23 @@ class post(messages.basicTweet):
self.add_post(event=None, update_gui=False) self.add_post(event=None, update_gui=False)
return self.thread return self.thread
def set_post_data(self, visibility, data):
if len(data) == 0:
return
if len(data) > 1:
self.thread = data[:-1]
for p in self.thread:
self.message.add_item(item=[p.get("text") or "", len(p.get("attachments") or [])], list_type="post")
post = data[-1]
self.attachments = post.get("attachments") or []
self.message.text.SetValue(post.get("text") or "")
self.message.sensitive.SetValue(post.get("sensitive") or False)
self.message.spoiler.SetValue(post.get("spoiler_text") or "")
visibility_settings = dict(public=0, unlisted=1, private=2, direct=3)
self.message.visibility.SetSelection(visibility_settings.get(visibility))
self.message.on_sensitivity_changed()
self.text_processor()
def text_processor(self, *args, **kwargs): def text_processor(self, *args, **kwargs):
text = self.message.text.GetValue() text = self.message.text.GetValue()
cw = self.message.spoiler.GetValue() cw = self.message.spoiler.GetValue()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -66,7 +66,7 @@ def compose_notification(notification, db, relative_times, show_screen_names, sa
if notification.type == "status": if notification.type == "status":
text = _("{username} has posted: {status}").format(username=user, status=",".join(compose_post(notification.status, db, relative_times, show_screen_names, safe=safe))) text = _("{username} has posted: {status}").format(username=user, status=",".join(compose_post(notification.status, db, relative_times, show_screen_names, safe=safe)))
elif notification.type == "mention": elif notification.type == "mention":
text = _("{username} has mentionned you: {status}").format(username=user, status=",".join(compose_post(notification.status, db, relative_times, show_screen_names, safe=safe))) text = _("{username} has mentioned you: {status}").format(username=user, status=",".join(compose_post(notification.status, db, relative_times, show_screen_names, safe=safe)))
elif notification.type == "reblog": elif notification.type == "reblog":
text = _("{username} has boosted: {status}").format(username=user, status=",".join(compose_post(notification.status, db, relative_times, show_screen_names, safe=safe))) text = _("{username} has boosted: {status}").format(username=user, status=",".join(compose_post(notification.status, db, relative_times, show_screen_names, safe=safe)))
elif notification.type == "favourite": elif notification.type == "favourite":

View File

@ -6,6 +6,7 @@ import logging
import webbrowser import webbrowser
import wx import wx
import mastodon import mastodon
import demoji
import config import config
import config_utils import config_utils
import output import output
@ -123,14 +124,23 @@ class Session(base.baseSession):
self.db["muted_users"] = self.api.mutes() self.db["muted_users"] = self.api.mutes()
def get_user_alias(self, user): def get_user_alias(self, user):
if user.display_name == None or user.display_name == "":
display_name = user.username
else:
display_name = user.display_name
aliases = self.settings.get("user-aliases") aliases = self.settings.get("user-aliases")
if aliases == None: if aliases == None:
log.error("Aliases are not defined for this config spec.") log.error("Aliases are not defined for this config spec.")
return user.name return self.demoji_user(display_name)
user_alias = aliases.get(user.id_str) user_alias = aliases.get(user.id)
if user_alias != None: if user_alias != None:
return user_alias return user_alias
return user.name return self.demoji_user(display_name)
def demoji_user(self, name):
if self.settings["general"]["hide_emojis"] == True:
return demoji.replace(name, "")
return name
def order_buffer(self, name, data, ignore_older=False): def order_buffer(self, name, data, ignore_older=False):
num = 0 num = 0
@ -173,48 +183,56 @@ class Session(base.baseSession):
tries = 0 tries = 0
if preexec_message: if preexec_message:
output.speak(preexec_message, True) output.speak(preexec_message, True)
while finished==False and tries < 25: while finished==False and tries < 5:
try: try:
val = getattr(self.api, call_name)(*args, **kwargs) val = getattr(self.api, call_name)(*args, **kwargs)
finished = True finished = True
except MastodonError as e: except Exception as e:
output.speak(str(e)) output.speak(str(e))
val = None val = None
if type(e) != MastodonNotFoundError and type(e) != MastodonUnauthorizedError : if type(e) != MastodonNotFoundError and type(e) != MastodonUnauthorizedError :
tries = tries+1 tries = tries+1
time.sleep(5) time.sleep(5)
elif report_failure: if tries == 4 and finished == False:
output.speak(_("%s failed. Reason: %s") % (action, str(e))) raise e
finished = True else:
# except: raise e
# tries = tries + 1
# time.sleep(5)
if report_success: if report_success:
output.speak(_("%s succeeded.") % action) output.speak(_("%s succeeded.") % action)
if _sound != None: self.sound.play(_sound) if _sound != None:
self.sound.play(_sound)
return val return val
def send_post(self, reply_to=None, users=None, visibility=None, posts=[]): def send_post(self, reply_to=None, visibility=None, posts=[]):
""" Convenience function to send a thread. """ """ Convenience function to send a thread. """
in_reply_to_id = reply_to in_reply_to_id = reply_to
for obj in posts: for obj in posts:
text = obj.get("text") text = obj.get("text")
if len(obj["attachments"]) == 0: if len(obj["attachments"]) == 0:
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"]) 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"])
# 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)
return
if item != None: if item != None:
in_reply_to_id = item["id"] in_reply_to_id = item["id"]
else: else:
media_ids = [] media_ids = []
poll = None try:
if len(obj["attachments"]) == 1 and obj["attachments"][0]["type"] == "poll": poll = None
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"]) if len(obj["attachments"]) == 1 and obj["attachments"][0]["type"] == "poll":
else: 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"])
for i in obj["attachments"]: else:
media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True) for i in obj["attachments"]:
media_ids.append(media.id) media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True)
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"]) media_ids.append(media.id)
if item != None: 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"])
in_reply_to_id = item["id"] 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)
return
def get_name(self): def get_name(self):
instance = self.settings["mastodon"]["instance"] instance = self.settings["mastodon"]["instance"]

View File

@ -152,7 +152,7 @@ def render_notification(notification, template, post_template, relative_times=Fa
if notification.type == "status": if notification.type == "status":
text = _("has posted: {status}").format(status=render_post(notification.status, post_template, relative_times, offset_hours)) text = _("has posted: {status}").format(status=render_post(notification.status, post_template, relative_times, offset_hours))
elif notification.type == "mention": elif notification.type == "mention":
text = _("has mentionned you: {status}").format(status=render_post(notification.status, post_template, relative_times, offset_hours)) text = _("has mentioned you: {status}").format(status=render_post(notification.status, post_template, relative_times, offset_hours))
elif notification.type == "reblog": elif notification.type == "reblog":
text = _("has boosted: {status}").format(status=render_post(notification.status, post_template, relative_times, offset_hours)) text = _("has boosted: {status}").format(status=render_post(notification.status, post_template, relative_times, offset_hours))
elif notification.type == "favourite": elif notification.type == "favourite":