From 0bd366c5392bb025c2b8e24ac00e730f7deee6e4 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Fri, 10 Dec 2021 13:32:46 -0600 Subject: [PATCH 01/18] Added first draft to a new templating system which could be used in invisible interface for now --- src/sessions/twitter/templates.py | 78 +++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/sessions/twitter/templates.py diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py new file mode 100644 index 00000000..bfccddb8 --- /dev/null +++ b/src/sessions/twitter/templates.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +import re +import arrow +import languageHandler +from string import Template +from . import utils + +def process_date(field, relative_times=True, offset_seconds=0): + original_date = arrow.get(field, locale="en") + if relative_times == True: + ts = original_date.humanize(locale=languageHandler.curLang[:2]) + else: + ts = original_date.shift(seconds=offset_seconds).format(_("dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2]) + return ts + +def process_text(tweet): + if hasattr(tweet, "text"): + text = tweet.text + elif hasattr(tweet, "full_text"): + text = tweet.full_text + # Cleanup mentions, so we'll remove more than 2 mentions to make the tweet easier to read. + text = utils.clean_mentions(text) + # Replace URLS for extended version of those. + if hasattr(tweet, "entities"): + text = utils.expand_urls(text, tweet.entities) + text = re.sub(r"https://twitter.com/\w+/status/\d+", "", text) + return text + +def process_image_descriptions(entities): + """ Attempt to extract information for image descriptions. """ + image_descriptions = [] + for media in entities["media"]: + if media.get("ext_alt_text") != None: + image_descriptions.append(media.get("ext_alt_text")) + idescriptions = "" + for image in image_descriptions: + idescriptions += "Image description: {}.".format(image) + return idescriptions + +def render_tweet(tweet, template, relative_times=False, offset_seconds=0): + """ Renders any given Tweet according to the passed template. + Available data for tweets will be stored in the following variables: + $date: Creation date. + $display_name: User profile name. + $screen_name: User screen name, this is the same name used to reference the user in Twitter. + $ source: Source client from where the current tweet was sent. + $lang: Two letter code for the automatically detected language for the tweet. This detection is performed by Twitter. + $text: Tweet text. + $image_descriptions: Information regarding image descriptions added by twitter users. + """ + available_data = dict() + created_at = process_date(tweet.created_at, relative_times, offset_seconds) + available_data.update(date=created_at) + # user. + available_data.update(display_name=tweet.user.name, screen_name=tweet.user.screen_name) + # Source client from where tweet was originated. + available_data.update(source=tweet.source) + if hasattr(tweet, "retweeted_status"): + if hasattr(tweet.retweeted_status, "quoted_status"): + text = "RT @{}: {} Quote from @{}: {}".format(tweet.retweeted_status.user.screen_name, process_text(tweet.retweeted_status), tweet.retweeted_status.quoted_status.user.screen_name, process_text(tweet.retweeted_status.quoted_status)) + else: + text = "RT @{}: {}".format(tweet.retweeted_status.user.screen_name, process_text(tweet.retweeted_status)) + elif hasattr(tweet, "quoted_status"): + text = "{} Quote from @{}: {}".format(process_text(tweet), tweet.quoted_status.user.screen_name, process_text(tweet.quoted_status)) + else: + text = process_text(tweet) + available_data.update(lang=tweet.lang, text=text) + # process image descriptions + image_descriptions = "" + if hasattr(tweet, "quoted_status") and hasattr(tweet.quoted_status, "extended_entities"): + image_descriptions = process_image_descriptions(tweet.quoted_status.extended_entities) + elif hasattr(tweet.retweeted_status, "quoted_status") and hasattr(tweet.retweeted_status.quoted_status, "extended_entities"): + image_descriptions = process_image_descriptions(tweet.retweeted_status.quoted_status.extended_entities) + if image_descriptions != "": + available_data.update(image_descriptions=image_descriptions) + result = Template(template).safe_substitute(**available_data) + result = re.sub(r"\$\w+", "", result) + return result \ No newline at end of file From 40e13250ca7cbcd6b1a9eb6a032b23f0cd80b759 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Fri, 10 Dec 2021 13:36:08 -0600 Subject: [PATCH 02/18] Added a base template for tweets --- src/Conf.defaults | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Conf.defaults b/src/Conf.defaults index a2b3760a..3d684a65 100644 --- a/src/Conf.defaults +++ b/src/Conf.defaults @@ -48,6 +48,9 @@ ocr_language = string(default="") braille_reporting = boolean(default=True) speech_reporting = boolean(default=True) +[templates] +tweet = string(default="$display_name, $text $image_descriptions $date. $source") + [filters] [user-aliases] \ No newline at end of file From a47fa3134616313620a8fc8de84965291b10e943 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Fri, 10 Dec 2021 15:03:15 -0600 Subject: [PATCH 03/18] Use session to retrieve users in templates, when applied to reduced tweets --- src/sessions/twitter/templates.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py index bfccddb8..b35c1e8c 100644 --- a/src/sessions/twitter/templates.py +++ b/src/sessions/twitter/templates.py @@ -37,7 +37,7 @@ def process_image_descriptions(entities): idescriptions += "Image description: {}.".format(image) return idescriptions -def render_tweet(tweet, template, relative_times=False, offset_seconds=0): +def render_tweet(tweet, template, session, relative_times=False, offset_seconds=0): """ Renders any given Tweet according to the passed template. Available data for tweets will be stored in the following variables: $date: Creation date. @@ -52,16 +52,16 @@ def render_tweet(tweet, template, relative_times=False, offset_seconds=0): created_at = process_date(tweet.created_at, relative_times, offset_seconds) available_data.update(date=created_at) # user. - available_data.update(display_name=tweet.user.name, screen_name=tweet.user.screen_name) + available_data.update(display_name=session.get_user(tweet.user).name, screen_name=session.get_user(tweet.user).screen_name) # Source client from where tweet was originated. available_data.update(source=tweet.source) if hasattr(tweet, "retweeted_status"): if hasattr(tweet.retweeted_status, "quoted_status"): - text = "RT @{}: {} Quote from @{}: {}".format(tweet.retweeted_status.user.screen_name, process_text(tweet.retweeted_status), tweet.retweeted_status.quoted_status.user.screen_name, process_text(tweet.retweeted_status.quoted_status)) + text = "RT @{}: {} Quote from @{}: {}".format(session.get_user(tweet.retweeted_status.user).screen_name, process_text(tweet.retweeted_status), session.get_user(tweet.retweeted_status.quoted_status.user).screen_name, process_text(tweet.retweeted_status.quoted_status)) else: - text = "RT @{}: {}".format(tweet.retweeted_status.user.screen_name, process_text(tweet.retweeted_status)) + text = "RT @{}: {}".format(session.get_user(tweet.retweeted_status.user).screen_name, process_text(tweet.retweeted_status)) elif hasattr(tweet, "quoted_status"): - text = "{} Quote from @{}: {}".format(process_text(tweet), tweet.quoted_status.user.screen_name, process_text(tweet.quoted_status)) + text = "{} Quote from @{}: {}".format(process_text(tweet), session.get_user(tweet.quoted_status.user).screen_name, process_text(tweet.quoted_status)) else: text = process_text(tweet) available_data.update(lang=tweet.lang, text=text) @@ -69,8 +69,10 @@ def render_tweet(tweet, template, relative_times=False, offset_seconds=0): image_descriptions = "" if hasattr(tweet, "quoted_status") and hasattr(tweet.quoted_status, "extended_entities"): image_descriptions = process_image_descriptions(tweet.quoted_status.extended_entities) - elif hasattr(tweet.retweeted_status, "quoted_status") and hasattr(tweet.retweeted_status.quoted_status, "extended_entities"): + elif hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "quoted_status") and hasattr(tweet.retweeted_status.quoted_status, "extended_entities"): image_descriptions = process_image_descriptions(tweet.retweeted_status.quoted_status.extended_entities) + elif hasattr(tweet, "extended_entities"): + image_descriptions = process_image_descriptions(tweet.extended_entities) if image_descriptions != "": available_data.update(image_descriptions=image_descriptions) result = Template(template).safe_substitute(**available_data) From af4594f16c5d4e95d524fb0bb0ca3c35f7146dd6 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Fri, 10 Dec 2021 15:22:42 -0600 Subject: [PATCH 04/18] Render tweets for invisible interface from template specified in config --- src/controller/buffers/twitter/base.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/controller/buffers/twitter/base.py b/src/controller/buffers/twitter/base.py index 3ad5b77c..3d7c1767 100644 --- a/src/controller/buffers/twitter/base.py +++ b/src/controller/buffers/twitter/base.py @@ -19,7 +19,7 @@ import languageHandler import logging from audio_services import youtube_utils from controller.buffers.base import base -from sessions.twitter import compose, utils, reduce +from sessions.twitter import compose, utils, reduce, templates from mysc.thread_utils import call_threaded from tweepy.errors import TweepyException from tweepy.cursor import Cursor @@ -100,8 +100,10 @@ class BaseBuffer(base.Buffer): return self.get_message() def get_message(self): + template = self.session.settings["templates"]["tweet"] tweet = self.get_right_tweet() - return " ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)) + t = templates.render_tweet(tweet, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"]) + return t def get_full_tweet(self): tweet = self.get_right_tweet() From 416151570c82735dd373a566844add638228dba0 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Fri, 10 Dec 2021 15:23:41 -0600 Subject: [PATCH 05/18] Request image description features for all created buffers by default --- src/controller/mainController.py | 82 ++++++++++++++++---------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/controller/mainController.py b/src/controller/mainController.py index ddedb068..a8f4130d 100644 --- a/src/controller/mainController.py +++ b/src/controller/mainController.py @@ -334,17 +334,17 @@ class Controller(object): root_position =self.view.search(session.db["user_name"], session.db["user_name"]) for i in session.settings['general']['buffer_order']: if i == 'home': - pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Home"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="home_timeline", name="home_timeline", sessionObject=session, account=session.db["user_name"], sound="tweet_received.ogg", tweet_mode="extended")) + pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Home"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="home_timeline", name="home_timeline", sessionObject=session, account=session.db["user_name"], sound="tweet_received.ogg", include_ext_alt_text=True, tweet_mode="extended")) elif i == 'mentions': - pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Mentions"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="mentions_timeline", name="mentions", sessionObject=session, account=session.db["user_name"], sound="mention_received.ogg", tweet_mode="extended")) + pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Mentions"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="mentions_timeline", name="mentions", sessionObject=session, account=session.db["user_name"], sound="mention_received.ogg", include_ext_alt_text=True, tweet_mode="extended")) elif i == 'dm': pub.sendMessage("createBuffer", buffer_type="DirectMessagesBuffer", session_type=session.type, buffer_title=_("Direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_direct_messages", name="direct_messages", sessionObject=session, account=session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg")) elif i == 'sent_dm': pub.sendMessage("createBuffer", buffer_type="SentDirectMessagesBuffer", session_type=session.type, buffer_title=_("Sent direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function=None, name="sent_direct_messages", sessionObject=session, account=session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message")) elif i == 'sent_tweets': - pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Sent tweets"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="user_timeline", name="sent_tweets", sessionObject=session, account=session.db["user_name"], screen_name=session.db["user_name"], tweet_mode="extended")) + pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Sent tweets"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="user_timeline", name="sent_tweets", sessionObject=session, account=session.db["user_name"], screen_name=session.db["user_name"], include_ext_alt_text=True, tweet_mode="extended")) elif i == 'favorites': - pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_favorites", name="favourites", sessionObject=session, account=session.db["user_name"], sound="favourite.ogg", tweet_mode="extended")) + pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_favorites", name="favourites", sessionObject=session, account=session.db["user_name"], sound="favourite.ogg", include_ext_alt_text=True, tweet_mode="extended")) elif i == 'followers': pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Followers"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_followers", name="followers", sessionObject=session, account=session.db["user_name"], sound="update_followers.ogg", screen_name=session.db["user_name"])) elif i == 'friends': @@ -356,11 +356,11 @@ class Controller(object): pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="timelines", account=session.db["user_name"])) timelines_position =self.view.search("timelines", session.db["user_name"]) for i in session.settings["other_buffers"]["timelines"]: - pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_(u"Timeline for {}").format(i,), parent_tab=timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="user_timeline", name="%s-timeline" % (i,), sessionObject=session, account=session.db["user_name"], sound="tweet_timeline.ogg", bufferType=None, user_id=i, tweet_mode="extended")) + pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_(u"Timeline for {}").format(i,), parent_tab=timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="user_timeline", name="%s-timeline" % (i,), sessionObject=session, account=session.db["user_name"], sound="tweet_timeline.ogg", bufferType=None, user_id=i, include_ext_alt_text=True, tweet_mode="extended")) pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Likes timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="favs_timelines", account=session.db["user_name"])) favs_timelines_position =self.view.search("favs_timelines", session.db["user_name"]) for i in session.settings["other_buffers"]["favourites_timelines"]: - pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes for {}").format(i,), parent_tab=favs_timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="get_favorites", name="%s-favorite" % (i,), sessionObject=session, account=session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, tweet_mode="extended")) + pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes for {}").format(i,), parent_tab=favs_timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="get_favorites", name="%s-favorite" % (i,), sessionObject=session, account=session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, include_ext_alt_text=True, tweet_mode="extended")) pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Followers timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="followers_timelines", account=session.db["user_name"])) followers_timelines_position =self.view.search("followers_timelines", session.db["user_name"]) for i in session.settings["other_buffers"]["followers_timelines"]: @@ -372,11 +372,11 @@ class Controller(object): pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Lists"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="lists", account=session.db["user_name"])) lists_position =self.view.search("lists", session.db["user_name"]) for i in session.settings["other_buffers"]["lists"]: - pub.sendMessage("createBuffer", buffer_type="ListBuffer", session_type=session.type, buffer_title=_(u"List for {}").format(i), parent_tab=lists_position, start=False, kwargs=dict(parent=self.view.nb, function="list_timeline", name="%s-list" % (i,), sessionObject=session, account=session.db["user_name"], bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), tweet_mode="extended")) + pub.sendMessage("createBuffer", buffer_type="ListBuffer", session_type=session.type, buffer_title=_(u"List for {}").format(i), parent_tab=lists_position, start=False, kwargs=dict(parent=self.view.nb, function="list_timeline", name="%s-list" % (i,), sessionObject=session, account=session.db["user_name"], bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), include_ext_alt_text=True, tweet_mode="extended")) pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Searches"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="searches", account=session.db["user_name"])) searches_position =self.view.search("searches", session.db["user_name"]) for i in session.settings["other_buffers"]["tweet_searches"]: - pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=session.type, buffer_title=_(u"Search for {}").format(i), parent_tab=searches_position, start=False, kwargs=dict(parent=self.view.nb, function="search_tweets", name="%s-searchterm" % (i,), sessionObject=session, account=session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=i, tweet_mode="extended")) + pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=session.type, buffer_title=_(u"Search for {}").format(i), parent_tab=searches_position, start=False, kwargs=dict(parent=self.view.nb, function="search_tweets", name="%s-searchterm" % (i,), sessionObject=session, account=session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=i, include_ext_alt_text=True, tweet_mode="extended")) for i in session.settings["other_buffers"]["trending_topic_buffers"]: pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (i), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="%s_tt" % (i,), sessionObject=session, account=session.db["user_name"], trendsFor=i, sound="trends_updated.ogg")) @@ -423,7 +423,7 @@ class Controller(object): buffer.session.settings["other_buffers"]["tweet_searches"].append(term) buffer.session.settings.write() args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()} - pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=buffer.session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=self.view.nb, function="search_tweets", name="%s-searchterm" % (term,), sessionObject=buffer.session, account=buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=term, tweet_mode="extended", **args)) + pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=buffer.session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=self.view.nb, function="search_tweets", name="%s-searchterm" % (term,), sessionObject=buffer.session, account=buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=term, include_ext_alt_text=True, tweet_mode="extended", **args)) else: log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,)) return @@ -872,7 +872,7 @@ class Controller(object): if usr.id_str in buff.session.settings["other_buffers"]["timelines"]: commonMessageDialogs.timeline_exist() return - tl = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "%s-timeline" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr.id_str, tweet_mode="extended") + tl = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "%s-timeline" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr.id_str, include_ext_alt_text=True, tweet_mode="extended") try: tl.start_stream(play_sound=False) except ValueError: @@ -891,7 +891,7 @@ class Controller(object): if usr.id_str in buff.session.settings["other_buffers"]["favourites_timelines"]: commonMessageDialogs.timeline_exist() return - tl = buffers.twitter.BaseBuffer(self.view.nb, "get_favorites", "%s-favorite" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr.id_str, tweet_mode="extended") + tl = buffers.twitter.BaseBuffer(self.view.nb, "get_favorites", "%s-favorite" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr.id_str, include_ext_alt_text=True, tweet_mode="extended") try: tl.start_stream(play_sound=False) except ValueError: @@ -1088,10 +1088,10 @@ class Controller(object): if position == page.buffer.list.get_selected(): page.session.sound.play("limit.ogg") - try: - output.speak(page.get_message(), True) - except: - pass +# try: + output.speak(page.get_message(), True) +# except: +# pass def down(self, *args, **kwargs): page = self.get_current_buffer() @@ -1100,16 +1100,16 @@ class Controller(object): return position = page.buffer.list.get_selected() index = position+1 - try: - page.buffer.list.select_item(index) - except: - pass +# try: + page.buffer.list.select_item(index) +# except: +# pass if position == page.buffer.list.get_selected(): page.session.sound.play("limit.ogg") - try: - output.speak(page.get_message(), True) - except: - pass +# try: + output.speak(page.get_message(), True) +# except: +# pass def left(self, *args, **kwargs): buff = self.view.get_current_buffer_pos() @@ -1202,18 +1202,18 @@ class Controller(object): def go_home(self): buffer = self.get_current_buffer() buffer.buffer.list.select_item(0) - try: - output.speak(buffer.get_message(), True) - except: - pass +# try: + output.speak(buffer.get_message(), True) +# except: +# pass def go_end(self): buffer = self.get_current_buffer() buffer.buffer.list.select_item(buffer.buffer.list.get_count()-1) - try: - output.speak(buffer.get_message(), True) - except: - pass +# try: + output.speak(buffer.get_message(), True) +# except: +# pass def go_page_up(self): buffer = self.get_current_buffer() @@ -1222,10 +1222,10 @@ class Controller(object): else: index = buffer.buffer.list.get_selected() - 20 buffer.buffer.list.select_item(index) - try: - output.speak(buffer.get_message(), True) - except: - pass +# try: + output.speak(buffer.get_message(), True) +# except: +# pass def go_page_down(self): buffer = self.get_current_buffer() @@ -1234,10 +1234,10 @@ class Controller(object): else: index = buffer.buffer.list.get_selected() + 20 buffer.buffer.list.select_item(index) - try: - output.speak(buffer.get_message(), True) - except: - pass +# try: + output.speak(buffer.get_message(), True) +# except: +# pass def url(self, *args, **kwargs): buffer = self.get_current_buffer() @@ -1385,7 +1385,7 @@ class Controller(object): buff = self.search_buffer("home_timeline", account) if create == True: if buffer == "favourites": - favourites = buffers.twitter.BaseBuffer(self.view.nb, "get_favorites", "favourites", buff.session, buff.session.db["user_name"], tweet_mode="extended") + favourites = buffers.twitter.BaseBuffer(self.view.nb, "get_favorites", "favourites", buff.session, buff.session.db["user_name"], include_ext_alt_text=True, tweet_mode="extended") self.buffers.append(favourites) self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"])) favourites.start_stream(play_sound=False) @@ -1415,7 +1415,7 @@ class Controller(object): if create in buff.session.settings["other_buffers"]["lists"]: output.speak(_(u"This list is already opened"), True) return - tl = buffers.twitter.ListBuffer(self.view.nb, "list_timeline", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]), tweet_mode="extended") + tl = buffers.twitter.ListBuffer(self.view.nb, "list_timeline", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]), include_ext_alt_text=True, tweet_mode="extended") buff.session.lists.append(tl) pos=self.view.search("lists", buff.session.db["user_name"]) self.insert_buffer(tl, pos) From 58ba722bd771cba973d265468ccd778697e388c1 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Fri, 10 Dec 2021 17:15:24 -0600 Subject: [PATCH 06/18] Added templates for direct messages and sent direct messages --- src/Conf.defaults | 2 ++ .../buffers/twitter/directMessages.py | 16 +++++++-- src/sessions/twitter/templates.py | 33 +++++++++++++++++-- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/Conf.defaults b/src/Conf.defaults index 3d684a65..8777227b 100644 --- a/src/Conf.defaults +++ b/src/Conf.defaults @@ -50,6 +50,8 @@ speech_reporting = boolean(default=True) [templates] tweet = string(default="$display_name, $text $image_descriptions $date. $source") +dm = string(default="$sender_display_name, $text $date") +dm_sent = string(default=">$recipient_display_name, $text $date") [filters] diff --git a/src/controller/buffers/twitter/directMessages.py b/src/controller/buffers/twitter/directMessages.py index 06cbed9a..776521d0 100644 --- a/src/controller/buffers/twitter/directMessages.py +++ b/src/controller/buffers/twitter/directMessages.py @@ -8,7 +8,7 @@ import config import languageHandler import logging from controller import messages -from sessions.twitter import compose, utils +from sessions.twitter import compose, utils, templates from mysc.thread_utils import call_threaded from tweepy.errors import TweepyException from pubsub import pub @@ -129,6 +129,12 @@ class DirectMessagesBuffer(base.BaseBuffer): def open_in_browser(self, *args, **kwargs): output.speak(_(u"This action is not supported in the buffer yet.")) + def get_message(self): + template = self.session.settings["templates"]["dm"] + dm = self.get_right_tweet() + t = templates.render_dm(dm, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"]) + return t + class SentDirectMessagesBuffer(DirectMessagesBuffer): def __init__(self, *args, **kwargs): @@ -150,4 +156,10 @@ class SentDirectMessagesBuffer(DirectMessagesBuffer): else: for i in items: tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session) - self.buffer.list.insert_item(False, *tweet) \ No newline at end of file + self.buffer.list.insert_item(False, *tweet) + + def get_message(self): + template = self.session.settings["templates"]["dm_sent"] + dm = self.get_right_tweet() + t = templates.render_dm(dm, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"]) + return t diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py index b35c1e8c..5a5cdf86 100644 --- a/src/sessions/twitter/templates.py +++ b/src/sessions/twitter/templates.py @@ -14,10 +14,10 @@ def process_date(field, relative_times=True, offset_seconds=0): return ts def process_text(tweet): - if hasattr(tweet, "text"): - text = tweet.text - elif hasattr(tweet, "full_text"): + if hasattr(tweet, "full_text"): text = tweet.full_text + elif hasattr(tweet, "text"): + text = tweet.text # Cleanup mentions, so we'll remove more than 2 mentions to make the tweet easier to read. text = utils.clean_mentions(text) # Replace URLS for extended version of those. @@ -77,4 +77,31 @@ def render_tweet(tweet, template, session, relative_times=False, offset_seconds= available_data.update(image_descriptions=image_descriptions) result = Template(template).safe_substitute(**available_data) result = re.sub(r"\$\w+", "", result) + return result + +def render_dm(dm, template, session, relative_times=False, offset_seconds=0): + """ Renders direct messages by using the provided template. + Available data will be stored in the following variables: + $date: Creation date. + $sender_display_name: User profile name for user who sent the dm. + $sender_screen_name: User screen name for user sending the dm, this is the same name used to reference the user in Twitter. + $recipient_display_name: User profile name for user who received the dm. + $recipient_screen_name: User screen name for user receiving the dm, this is the same name used to reference the user in Twitter. + $text: Text of the direct message. + """ + available_data = dict() + available_data.update(text=utils.expand_urls(dm.message_create["message_data"]["text"], dm.message_create["message_data"]["entities"])) + # Let's remove the last 3 digits in the timestamp string. + # Twitter sends their "epoch" timestamp with 3 digits for milliseconds and arrow doesn't like it. + original_date = arrow.get(int(dm.created_timestamp)) + if relative_times == True: + ts = original_date.humanize(locale=languageHandler.curLang[:2]) + else: + ts = original_date.shift(seconds=offset_seconds) + available_data.update(date=ts) + sender = session.get_user(dm.message_create["sender_id"]) + recipient = session.get_user(dm.message_create["target"]["recipient_id"]) + available_data.update(sender_display_name=sender.name, sender_screen_name=sender.screen_name, recipient_display_name=recipient.name, recipient_screen_name=recipient.screen_name) + result = Template(template).safe_substitute(**available_data) + result = re.sub(r"\$\w+", "", result) return result \ No newline at end of file From ab1a0946a49e684b97392e9d555377b930152515 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Mon, 13 Dec 2021 09:52:52 -0600 Subject: [PATCH 07/18] Added rendering function for twitter user objects --- src/sessions/twitter/templates.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py index 5a5cdf86..3dcaddfa 100644 --- a/src/sessions/twitter/templates.py +++ b/src/sessions/twitter/templates.py @@ -104,4 +104,31 @@ def render_dm(dm, template, session, relative_times=False, offset_seconds=0): available_data.update(sender_display_name=sender.name, sender_screen_name=sender.screen_name, recipient_display_name=recipient.name, recipient_screen_name=recipient.screen_name) result = Template(template).safe_substitute(**available_data) result = re.sub(r"\$\w+", "", result) + return result + +# Sesion object is not used in this function but we keep compatibility across all rendering functions. +def render_person(user, template, session=None, relative_times=True, offset_seconds=0): + """ Renders persons (any Twitter user) by using the provided template. + Available data will be stored in the following variables: + $display_name: The name of the user, as they’ve defined it. Not necessarily a person’s name. Typically capped at 50 characters, but subject to change. + $screen_name: The screen name, handle, or alias that this user identifies themselves with. + $location: The user-defined location for this account’s profile. Not necessarily a location, nor machine-parseable. + $description: The user-defined UTF-8 string describing their account. + $followers: The number of followers this account currently has. This value might be inaccurate. + $following: The number of users this account is following (AKA their “followings”). This value might be inaccurate. + $listed: The number of public lists that this user is a member of. This value might be inaccurate. + $likes: The number of Tweets this user has liked in the account’s lifetime. This value might be inaccurate. + $tweets: The number of Tweets (including retweets) issued by the user. This value might be inaccurate. + $created_at: The date and time that the user account was created on Twitter. + """ + available_data = dict(display_name=user.name, screen_name=user.screen_name, followers=user.followers_count, following=user.friends_count, likes=user.favourites_count, listed=user.listed_count, tweets=user.statuses_count) + # Nullable values. + nullables = ["location", "description"] + for nullable in nullables: + if hasattr(user, nullable) and getattr(user, nullable) != None: + available_data[nullable] = getattr(user, nullable) + created_at = process_date(user.created_at, relative_times=relative_times, offset_seconds=offset_seconds) + available_data.update(created_at=created_at) + result = Template(template).safe_substitute(**available_data) + result = re.sub(r"\$\w+", "", result) return result \ No newline at end of file From 03c330c0a4f2e0dd8829ddd59c9263954d32b555 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Mon, 13 Dec 2021 09:53:44 -0600 Subject: [PATCH 08/18] Added default template for Twitter user objects --- src/Conf.defaults | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Conf.defaults b/src/Conf.defaults index 8777227b..2264e8a9 100644 --- a/src/Conf.defaults +++ b/src/Conf.defaults @@ -52,6 +52,7 @@ speech_reporting = boolean(default=True) tweet = string(default="$display_name, $text $image_descriptions $date. $source") dm = string(default="$sender_display_name, $text $date") dm_sent = string(default=">$recipient_display_name, $text $date") +person = string(default="$display_name (@$screen_name). $followers followers, $following following, $tweets tweets. Joined Twitter $created_at.") [filters] From a6ecd37547f47b348538df190370f8d5915c77c3 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Mon, 13 Dec 2021 09:54:06 -0600 Subject: [PATCH 09/18] Implement users template in Twitter buffers --- src/controller/buffers/twitter/people.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/controller/buffers/twitter/people.py b/src/controller/buffers/twitter/people.py index d29a46dc..6d6c387d 100644 --- a/src/controller/buffers/twitter/people.py +++ b/src/controller/buffers/twitter/people.py @@ -16,7 +16,7 @@ import logging from mysc.thread_utils import call_threaded from tweepy.errors import TweepyException from pubsub import pub -from sessions.twitter import compose +from sessions.twitter import compose, templates from . import base log = logging.getLogger("controller.buffers.twitter.peopleBuffer") @@ -84,7 +84,10 @@ class PeopleBuffer(base.BaseBuffer): pass def get_message(self): - return " ".join(self.compose_function(self.get_tweet(), self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)) + template = self.session.settings["templates"]["person"] + user = self.get_right_tweet() + t = templates.render_person(user, template, self.session, relative_times=True, offset_seconds=self.session.db["utc_offset"]) + return t def delete_item(self): pass From 2de9f8f9b34bbf4d91be04e3d0faeb424abd3666 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Mon, 20 Dec 2021 16:03:21 -0600 Subject: [PATCH 10/18] added all variables available for template objects --- src/sessions/twitter/templates.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py index 3dcaddfa..8d6929d0 100644 --- a/src/sessions/twitter/templates.py +++ b/src/sessions/twitter/templates.py @@ -5,6 +5,13 @@ import languageHandler from string import Template from . import utils +# Define variables that would be available for all template objects. +# This will be used for the edit template dialog. +# Available variables for tweet objects. +tweet_variables = ["date", "display_name", "screen_name", "source", "lang", "text", "image_descriptions"] +dm_variables = ["date", "sender_display_name", "sender_screen_name", "recipient_display_name", "recipient_display_name", "text"] +person_variables = ["display_name", "screen_name", "location", "description", "followers", "following", "listed", "likes", "tweets", "created_at"] + def process_date(field, relative_times=True, offset_seconds=0): original_date = arrow.get(field, locale="en") if relative_times == True: From 4434a5b971d0915f4ddf9496759ab664566593ca Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 21 Dec 2021 09:20:59 -0600 Subject: [PATCH 11/18] Added dialog for editing templates --- src/wxUI/dialogs/twitterDialogs/__init__.py | 3 +- .../dialogs/twitterDialogs/templateDialogs.py | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/wxUI/dialogs/twitterDialogs/templateDialogs.py diff --git a/src/wxUI/dialogs/twitterDialogs/__init__.py b/src/wxUI/dialogs/twitterDialogs/__init__.py index 6a829f06..f067ea11 100644 --- a/src/wxUI/dialogs/twitterDialogs/__init__.py +++ b/src/wxUI/dialogs/twitterDialogs/__init__.py @@ -1 +1,2 @@ -from .tweetDialogs import tweet, reply, dm, viewTweet, viewNonTweet, poll \ No newline at end of file +from .tweetDialogs import tweet, reply, dm, viewTweet, viewNonTweet, poll +from .templateDialogs import EditTemplateDialog \ No newline at end of file diff --git a/src/wxUI/dialogs/twitterDialogs/templateDialogs.py b/src/wxUI/dialogs/twitterDialogs/templateDialogs.py new file mode 100644 index 00000000..3b9d5243 --- /dev/null +++ b/src/wxUI/dialogs/twitterDialogs/templateDialogs.py @@ -0,0 +1,37 @@ +# -*- coding: UTF-8 -*- +import wx + +class EditTemplateDialog(wx.Dialog): + def __init__(self, template, variables=[], *args, **kwds): + super(EditTemplateDialog, self).__init__(parent=None, title=_("Edit Template"), *args, **kwds) + mainSizer = wx.BoxSizer(wx.VERTICAL) + sizer_1 = wx.BoxSizer(wx.HORIZONTAL) + mainSizer.Add(sizer_1, 1, wx.EXPAND, 0) + label_1 = wx.StaticText(self, wx.ID_ANY, _("Edit template")) + sizer_1.Add(label_1, 0, 0, 0) + self.template = wx.TextCtrl(self, wx.ID_ANY, template) + sizer_1.Add(self.template, 0, 0, 0) + sizer_2 = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Available variables")), wx.HORIZONTAL) + mainSizer.Add(sizer_2, 1, wx.EXPAND, 0) + self.variables = wx.ListBox(self, wx.ID_ANY, choices=["$"+v for v in variables]) + self.variables.Bind(wx.EVT_CHAR_HOOK, self.on_keypress) + sizer_2.Add(self.variables, 0, 0, 0) + sizer_3 = wx.StdDialogButtonSizer() + mainSizer.Add(sizer_3, 0, wx.ALIGN_RIGHT | wx.ALL, 4) + self.button_SAVE = wx.Button(self, wx.ID_SAVE) + self.button_SAVE.SetDefault() + sizer_3.AddButton(self.button_SAVE) + self.button_CANCEL = wx.Button(self, wx.ID_CANCEL) + sizer_3.AddButton(self.button_CANCEL) + sizer_3.Realize() + self.SetSizer(mainSizer) + mainSizer.Fit(self) + self.SetAffirmativeId(self.button_SAVE.GetId()) + self.SetEscapeId(self.button_CANCEL.GetId()) + self.Layout() + + def on_keypress(self, event, *args, **kwargs): + if event.GetKeyCode() == wx.WXK_RETURN: + self.template.ChangeValue(self.template.GetValue()+self.variables.GetStringSelection()+", ") + return + event.Skip() \ No newline at end of file From 95063cd472918be083bfbb8be4ff829c33b4f929 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 21 Dec 2021 12:10:15 -0600 Subject: [PATCH 12/18] Added restore button for template edit dialog --- .../dialogs/twitterDialogs/templateDialogs.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/wxUI/dialogs/twitterDialogs/templateDialogs.py b/src/wxUI/dialogs/twitterDialogs/templateDialogs.py index 3b9d5243..9efa96aa 100644 --- a/src/wxUI/dialogs/twitterDialogs/templateDialogs.py +++ b/src/wxUI/dialogs/twitterDialogs/templateDialogs.py @@ -1,9 +1,12 @@ # -*- coding: UTF-8 -*- import wx +import output +from typing import List class EditTemplateDialog(wx.Dialog): - def __init__(self, template, variables=[], *args, **kwds): + def __init__(self, template: str, variables: List[str] = [], default_template: str = "", *args, **kwds) -> None: super(EditTemplateDialog, self).__init__(parent=None, title=_("Edit Template"), *args, **kwds) + self.default_template = default_template mainSizer = wx.BoxSizer(wx.VERTICAL) sizer_1 = wx.BoxSizer(wx.HORIZONTAL) mainSizer.Add(sizer_1, 1, wx.EXPAND, 0) @@ -23,6 +26,9 @@ class EditTemplateDialog(wx.Dialog): sizer_3.AddButton(self.button_SAVE) self.button_CANCEL = wx.Button(self, wx.ID_CANCEL) sizer_3.AddButton(self.button_CANCEL) + self.button_RESTORE = wx.Button(self, wx.ID_ANY, _("Restore template")) + self.button_RESTORE.Bind(wx.EVT_BUTTON, self.on_restore) + sizer_3.AddButton(self.button_CANCEL) sizer_3.Realize() self.SetSizer(mainSizer) mainSizer.Fit(self) @@ -33,5 +39,14 @@ class EditTemplateDialog(wx.Dialog): def on_keypress(self, event, *args, **kwargs): if event.GetKeyCode() == wx.WXK_RETURN: self.template.ChangeValue(self.template.GetValue()+self.variables.GetStringSelection()+", ") + output.speak(self.template.GetValue()+self.variables.GetStringSelection()+", ") return - event.Skip() \ No newline at end of file + event.Skip() + + def on_restore(self, *args, **kwargs) -> None: + self.template.ChangeValue(self.default_template) + output.speak(_("Restored template to {}.").format(self.default_template)) + self.template.SetFocus() + +def invalid_template() -> None: + wx.MessageDialog(None, _("the template you have specified include variables that do not exists for the object. Please fix the template and try again. For your reference, you can see a list of all available variables in the variables list while editing your template."), _("Invalid template"), wx.ICON_ERROR).ShowModal() \ No newline at end of file From b2f9aef7f79ebe8733599485493f36c310852a32 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 21 Dec 2021 12:11:12 -0600 Subject: [PATCH 13/18] Added edit template controller --- src/controller/editTemplateController.py | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/controller/editTemplateController.py diff --git a/src/controller/editTemplateController.py b/src/controller/editTemplateController.py new file mode 100644 index 00000000..9d85bbda --- /dev/null +++ b/src/controller/editTemplateController.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +import re +import wx +from typing import List +from sessions.twitter.templates import tweet_variables, dm_variables, person_variables +from wxUI.dialogs.twitterDialogs import templateDialogs + +class editTemplateController(object): + def __init__(self, template: str, type: str) -> None: + super(editTemplateController, self).__init__() + self.default_template = template + if type == "tweet": + self.variables = tweet_variables + elif type == "dm": + self.variables = dm_variables + else: + self.variables = person_variables + self.template: str = template + + def validate_template(self, template: str) -> bool: + used_variables: List[str] = re.findall("\$\w+", template) + validated: bool = True + for var in used_variables: + if var[1:] not in self.variables: + validated = False + return validated + + def run_dialog(self) -> str: + dialog = templateDialogs.EditTemplateDialog(template=self.template, variables=self.variables, default_template=self.default_template) + response = dialog.ShowModal() + if response == wx.ID_SAVE: + validated: bool = self.validate_template(dialog.template.GetValue()) + if validated == False: + templateDialogs.invalid_template() + self.template = dialog.template.GetValue() + return self.run_dialog() + else: + return dialog.template.GetValue() + else: + return "" \ No newline at end of file From b4526c12c9cfd7f3466e090bb21207b4ebda4f25 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 21 Dec 2021 13:21:58 -0600 Subject: [PATCH 14/18] Integrate template edition into account settings dialog --- src/controller/editTemplateController.py | 4 +- src/controller/settings.py | 60 +++++++++++++++++++++--- src/wxUI/dialogs/configuration.py | 18 +++++++ 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/controller/editTemplateController.py b/src/controller/editTemplateController.py index 9d85bbda..7251153c 100644 --- a/src/controller/editTemplateController.py +++ b/src/controller/editTemplateController.py @@ -5,9 +5,9 @@ from typing import List from sessions.twitter.templates import tweet_variables, dm_variables, person_variables from wxUI.dialogs.twitterDialogs import templateDialogs -class editTemplateController(object): +class EditTemplate(object): def __init__(self, template: str, type: str) -> None: - super(editTemplateController, self).__init__() + super(EditTemplate, self).__init__() self.default_template = template if type == "tweet": self.variables = tweet_variables diff --git a/src/controller/settings.py b/src/controller/settings.py index 84040d2e..d203adc3 100644 --- a/src/controller/settings.py +++ b/src/controller/settings.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import os import webbrowser +import logging import sound_lib import paths import widgetUtils @@ -8,17 +9,18 @@ import config import languageHandler import output import application +import config_utils +import keys +from collections import OrderedDict +from pubsub import pub +from mysc import autostart as autostart_windows from wxUI.dialogs import configuration from wxUI import commonMessageDialogs from extra.autocompletionUsers import settings from extra.ocr import OCRSpace -from pubsub import pub -import logging -import config_utils +from .editTemplateController import EditTemplate + log = logging.getLogger("Settings") -import keys -from collections import OrderedDict -from mysc import autostart as autostart_windows class globalSettingsController(object): def __init__(self): @@ -152,6 +154,15 @@ class accountSettingsController(globalSettingsController): self.dialog.create_reporting() self.dialog.set_value("reporting", "speech_reporting", self.config["reporting"]["speech_reporting"]) self.dialog.set_value("reporting", "braille_reporting", self.config["reporting"]["braille_reporting"]) + tweet_template = self.config["templates"]["tweet"] + dm_template = self.config["templates"]["dm"] + sent_dm_template = self.config["templates"]["dm_sent"] + person_template = self.config["templates"]["person"] + self.dialog.create_templates(tweet_template=tweet_template, dm_template=dm_template, sent_dm_template=sent_dm_template, person_template=person_template) + widgetUtils.connect_event(self.dialog.templates.tweet, widgetUtils.BUTTON_PRESSED, self.edit_tweet_template) + widgetUtils.connect_event(self.dialog.templates.dm, widgetUtils.BUTTON_PRESSED, self.edit_dm_template) + widgetUtils.connect_event(self.dialog.templates.sent_dm, widgetUtils.BUTTON_PRESSED, self.edit_sent_dm_template) + widgetUtils.connect_event(self.dialog.templates.person, widgetUtils.BUTTON_PRESSED, self.edit_person_template) self.dialog.create_other_buffers() buffer_values = self.get_buffers_list() self.dialog.buffers.insert_buffers(buffer_values) @@ -160,7 +171,6 @@ class accountSettingsController(globalSettingsController): widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up) widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down) - self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"]) widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client) widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client) @@ -185,6 +195,42 @@ class accountSettingsController(globalSettingsController): self.dialog.set_title(_(u"Account settings for %s") % (self.user,)) self.response = self.dialog.get_response() + def edit_tweet_template(self, *args, **kwargs): + template = self.config["templates"]["tweet"] + control = EditTemplate(template=template, type="tweet") + result = control.run_dialog() + if result != "": # Template has been saved. + self.config["templates"]["tweet"] = result + self.config.write() + self.dialog.templates.tweet.SetLabel(_("Edit template for tweets. Current template: {}").format(result)) + + def edit_dm_template(self, *args, **kwargs): + template = self.config["templates"]["dm"] + control = EditTemplate(template=template, type="dm") + result = control.run_dialog() + if result != "": # Template has been saved. + self.config["templates"]["dm"] = result + self.config.write() + self.dialog.templates.dm.SetLabel(_("Edit template for direct messages. Current template: {}").format(result)) + + def edit_sent_dm_template(self, *args, **kwargs): + template = self.config["templates"]["dm_sent"] + control = EditTemplate(template=template, type="dm") + result = control.run_dialog() + if result != "": # Template has been saved. + self.config["templates"]["dm_sent"] = result + self.config.write() + self.dialog.templates.sent_dm.SetLabel(_("Edit template for sent direct messages. Current template: {}").format(result)) + + def edit_person_template(self, *args, **kwargs): + template = self.settings["templates"]["person"] + control = EditTemplate(template=template, type="person") + result = control.run_dialog() + if result != "": # Template has been saved. + self.config["templates"]["person"] = result + self.config.write() + self.dialog.templates.person.SetLabel(_("Edit template for persons. Current template: {}").format(result)) + def save_configuration(self): if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"): self.needs_restart = True diff --git a/src/wxUI/dialogs/configuration.py b/src/wxUI/dialogs/configuration.py index 1bc5de9c..e8220ae9 100644 --- a/src/wxUI/dialogs/configuration.py +++ b/src/wxUI/dialogs/configuration.py @@ -233,6 +233,20 @@ class other_buffers(wx.Panel): buffers_list.append(self.buffers.get_text_column(i, 0)) return buffers_list +class templates(wx.Panel, baseDialog.BaseWXDialog): + def __init__(self, parent, tweet_template, dm_template, sent_dm_template, person_template): + super(templates, self).__init__(parent) + sizer = wx.BoxSizer(wx.VERTICAL) + self.tweet = wx.Button(self, wx.ID_ANY, _("Edit template for tweets. Current template: {}").format(tweet_template)) + sizer.Add(self.tweet, 0, wx.ALL, 5) + self.dm = wx.Button(self, wx.ID_ANY, _("Edit template for direct messages. Current template: {}").format(dm_template)) + sizer.Add(self.dm, 0, wx.ALL, 5) + self.sent_dm = wx.Button(self, wx.ID_ANY, _("Edit template for sent direct messages. Current template: {}").format(sent_dm_template)) + sizer.Add(self.sent_dm, 0, wx.ALL, 5) + self.person = wx.Button(self, wx.ID_ANY, _("Edit template for persons. Current template: {}").format(person_template)) + sizer.Add(self.person, 0, wx.ALL, 5) + self.SetSizer(sizer) + class ignoredClients(wx.Panel): def __init__(self, parent, choices): super(ignoredClients, self).__init__(parent=parent) @@ -380,6 +394,10 @@ class configurationDialog(baseDialog.BaseWXDialog): self.ignored_clients = ignoredClients(self.notebook, ignored_clients_list) self.notebook.AddPage(self.ignored_clients, _(u"Ignored clients")) + def create_templates(self, tweet_template, dm_template, sent_dm_template, person_template): + self.templates = templates(self.notebook, tweet_template=tweet_template, dm_template=dm_template, sent_dm_template=sent_dm_template, person_template=person_template) + self.notebook.AddPage(self.templates, _("Templates")) + def create_sound(self, output_devices, input_devices, soundpacks): self.sound = sound(self.notebook, output_devices, input_devices, soundpacks) self.notebook.AddPage(self.sound, _(u"Sound")) From 81963dbb53ddc418f980e09aab17734123340120 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 21 Dec 2021 13:55:48 -0600 Subject: [PATCH 15/18] Updated sent direct messages template --- src/Conf.defaults | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Conf.defaults b/src/Conf.defaults index 2264e8a9..e25c5002 100644 --- a/src/Conf.defaults +++ b/src/Conf.defaults @@ -51,7 +51,7 @@ speech_reporting = boolean(default=True) [templates] tweet = string(default="$display_name, $text $image_descriptions $date. $source") dm = string(default="$sender_display_name, $text $date") -dm_sent = string(default=">$recipient_display_name, $text $date") +dm_sent = string(default="Dm to $recipient_display_name, $text $date") person = string(default="$display_name (@$screen_name). $followers followers, $following following, $tweets tweets. Joined Twitter $created_at.") [filters] From 445c33f003020ab78ac6249db7cf055d5abb8654 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 21 Dec 2021 13:56:29 -0600 Subject: [PATCH 16/18] Add default templates to translation catalog --- src/sessions/twitter/templates.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py index 8d6929d0..7c20d608 100644 --- a/src/sessions/twitter/templates.py +++ b/src/sessions/twitter/templates.py @@ -12,6 +12,12 @@ tweet_variables = ["date", "display_name", "screen_name", "source", "lang", "tex dm_variables = ["date", "sender_display_name", "sender_screen_name", "recipient_display_name", "recipient_display_name", "text"] person_variables = ["display_name", "screen_name", "location", "description", "followers", "following", "listed", "likes", "tweets", "created_at"] +# Default, translatable templates. +tweet_default_template = _("$display_name, $text $image_descriptions $date. $source") +dm_default_template = _("$sender_display_name, $text $date") +dm_sent_default_template = _("Dm to $recipient_display_name, $text $date") +person_default_template = _("$display_name (@$screen_name). $followers followers, $following following, $tweets tweets. Joined Twitter $created_at.") + def process_date(field, relative_times=True, offset_seconds=0): original_date = arrow.get(field, locale="en") if relative_times == True: @@ -82,7 +88,7 @@ def render_tweet(tweet, template, session, relative_times=False, offset_seconds= image_descriptions = process_image_descriptions(tweet.extended_entities) if image_descriptions != "": available_data.update(image_descriptions=image_descriptions) - result = Template(template).safe_substitute(**available_data) + result = Template(_(template)).safe_substitute(**available_data) result = re.sub(r"\$\w+", "", result) return result @@ -109,7 +115,7 @@ def render_dm(dm, template, session, relative_times=False, offset_seconds=0): sender = session.get_user(dm.message_create["sender_id"]) recipient = session.get_user(dm.message_create["target"]["recipient_id"]) available_data.update(sender_display_name=sender.name, sender_screen_name=sender.screen_name, recipient_display_name=recipient.name, recipient_screen_name=recipient.screen_name) - result = Template(template).safe_substitute(**available_data) + result = Template(_(template)).safe_substitute(**available_data) result = re.sub(r"\$\w+", "", result) return result @@ -136,6 +142,6 @@ def render_person(user, template, session=None, relative_times=True, offset_seco available_data[nullable] = getattr(user, nullable) created_at = process_date(user.created_at, relative_times=relative_times, offset_seconds=offset_seconds) available_data.update(created_at=created_at) - result = Template(template).safe_substitute(**available_data) + result = Template(_(template)).safe_substitute(**available_data) result = re.sub(r"\$\w+", "", result) return result \ No newline at end of file From a74825a0f614d9c58e9b08cdff0ddeffb08f87ef Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 21 Dec 2021 13:57:32 -0600 Subject: [PATCH 17/18] Add missing string to translation catalogs --- src/sessions/twitter/templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py index 7c20d608..156a2d67 100644 --- a/src/sessions/twitter/templates.py +++ b/src/sessions/twitter/templates.py @@ -47,7 +47,7 @@ def process_image_descriptions(entities): image_descriptions.append(media.get("ext_alt_text")) idescriptions = "" for image in image_descriptions: - idescriptions += "Image description: {}.".format(image) + idescriptions += _("Image description: {}.").format(image) return idescriptions def render_tweet(tweet, template, session, relative_times=False, offset_seconds=0): From b6ae9f4b79ce6473c2ad528e64487730053a9ef9 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 21 Dec 2021 15:10:12 -0600 Subject: [PATCH 18/18] Updated changelog --- doc/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/changelog.md b/doc/changelog.md index 13d35a74..d8b47484 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -2,6 +2,10 @@ TWBlue Changelog ## changes in this version +* We have added Experimental support for templates in the invisible interface. The GUI will remain unchanged for now: + * Each object (tweet, received direct message, sent direct message and people) has its own template in the settings. You can edit those templates from the account settings dialog, in the new "templates" tab. + * Every template is composed of the group of variables you want to display for each object. Each variable will start with a dollar sign ($) and cannot contain spaces or special characters. Templates can include arbitrary text that will not be processed. When editing the example templates, you can get an idea of the variables that are available for each object by using the template editing dialog. When you press enter on a variable from the list of available variables, it will be added to the template automatically. When you try to save a template, TWBlue will warn you if the template is incorrectly formatted or if it includes variables that do not exist in the information provided by objects. It is also possible to return to default values from the same dialog when editing a template. + * TWBlue can display image descriptions within Tweet templates. For that, you can use the $image_description variable in your template. * We have restored conversation and threads support powered by Twitter API V2 thanks to a set of improvements we have done in the application, as well as more generous limits to Tweet monthly cap by Twitter. * Fixed issue when uploading attachments (images, videos or gif files) while sending tweets or replies.