Merge remote-tracking branch 'origin/next-gen' into fr_020821

This commit is contained in:
Oreonan 2021-08-28 08:33:40 +02:00
commit 43578a32eb
12 changed files with 70 additions and 32 deletions

View File

@ -2,11 +2,14 @@
## changes in this version
* When reading a tweet, if the tweet contains more than 2 consecutive mentions, TWBlue will announce how many more users the tweet includes, as opposed to read every user in the conversation. You still can display the tweet to read all users.
* In the tweet displayer, It is possible to copy a link to the current tweet or person by pressing a button called "copy link to clipboard".
* Added a keymap capable to work under Windows 11. ([#391](https://github.com/manuelcortez/TWBlue/pull/391))
* Added user aliases to TWBlue. This feature allows you to rename user's display names on Twitter, so the next time you'll read an user it will be announced as you configured. For adding an alias to an user, select the "add alias" option in the user menu, located in the menu bar. This feature works only if you have set display screen names unchecked. Users are displayed with their display name in people buffers only. This action is supported in all keymaps, although it is undefined by default. ([#389](https://github.com/manuelcortez/TWBlue/pull/389))
* There are some changes to the autocomplete users feature:
* Now users can search for twitter screen names or display names in the database.
* It is possible to undefine keystrokes in the current keymap in TWBlue. This allows you, for example, to redefine keystrokes completely.
* We have changed our Geocoding service to the Nominatim API from OpenStreetMap. Addresses present in tweets are going to be determined by this service, as the Google Maps API now requires an API key. ([#390](https://github.com/manuelcortez/TWBlue/issues/390))
* Added a limited version of the Twitter's Streaming API: The Streaming API will work only for tweets, and will receive tweets only by people you follow. Protected users are not possible to be streamed. It is possible that during high tweet traffic, the Stream might get disconnected at times, but TWBlue should be capable of detecting this problem and reconnecting the stream again. ([#385](https://github.com/manuelcortez/TWBlue/pull/385))
* Fixed an issue that made TWBlue to not show a dialog when attempting to show a profile for a suspended user. ([#387](https://github.com/manuelcortez/TWBlue/issues/387))
* Added support for Twitter audio and videos: Tweets which contains audio or videos will be detected as audio items, and you can playback those with the regular command to play audios. ([#384,](https://github.com/manuelcortez/TWBlue/pull/384))

View File

@ -9,7 +9,7 @@ oauthlib
requests-oauthlib
requests-toolbelt
pypubsub
pygeocoder
geopy
arrow
python-dateutil
futures

View File

@ -9,7 +9,7 @@ if snapshot == False:
update_url = 'https://twblue.es/updates/stable.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
else:
version = "9"
version = "10"
update_url = 'https://twblue.es/updates/snapshot.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
authors = ["Manuel Cortéz", "José Manuel Delicado"]

View File

@ -644,8 +644,12 @@ class BaseBuffer(base.Buffer):
original_tweet.text = utils.find_urls_in_text(original_tweet.text, original_tweet.entities)
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, self.session.db, self.session.settings["general"]["relative_times"])
def open_in_browser(self, *args, **kwargs):
def get_item_url(self):
tweet = self.get_tweet()
output.speak(_(u"Opening item in web browser..."))
url = "https://twitter.com/{screen_name}/status/{tweet_id}".format(screen_name=self.session.get_user(tweet.user).screen_name, tweet_id=tweet.id)
return url
def open_in_browser(self, *args, **kwargs):
url = self.get_item_url()
output.speak(_(u"Opening item in web browser..."))
webbrowser.open(url)

View File

@ -252,8 +252,7 @@ class PeopleBuffer(base.BaseBuffer):
elif number_of_items > 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
output.speak(_(u"{0} new followers.").format(number_of_items))
def open_in_browser(self, *args, **kwargs):
def get_item_url(self, *args, **kwargs):
tweet = self.get_tweet()
output.speak(_(u"Opening item in web browser..."))
url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name)
webbrowser.open(url)
return url

View File

@ -35,17 +35,16 @@ from mysc.repeating_timer import RepeatingTimer
from mysc import restart
import config
import widgetUtils
import pygeocoder
from pygeolib import GeocoderError
import logging
import webbrowser
from geopy.geocoders import Nominatim
from mysc import localization
import os
import languageHandler
log = logging.getLogger("mainController")
geocoder = pygeocoder.Geocoder()
geocoder = Nominatim(user_agent="TWBlue")
class Controller(object):
@ -831,7 +830,7 @@ class Controller(object):
return
elif buffer.type == "baseBuffer" or buffer.type == "favourites_timeline" or buffer.type == "list" or buffer.type == "search":
tweet, tweetsList = buffer.get_full_tweet()
msg = messages.viewTweet(tweet, tweetsList, utc_offset=buffer.session.db["utc_offset"])
msg = messages.viewTweet(tweet, tweetsList, utc_offset=buffer.session.db["utc_offset"], item_url=buffer.get_item_url())
elif buffer.type == "dm":
non_tweet = buffer.get_formatted_message()
item = buffer.get_right_tweet()
@ -839,8 +838,11 @@ class Controller(object):
date = original_date.shift(seconds=buffer.session.db["utc_offset"]).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
msg = messages.viewTweet(non_tweet, [], False, date=date)
else:
item_url = ""
if hasattr(buffer, "get_item_url"):
item_url = buffer.get_item_url()
non_tweet = buffer.get_formatted_message()
msg = messages.viewTweet(non_tweet, [], False)
msg = messages.viewTweet(non_tweet, [], False, item_url=item_url)
def open_in_browser(self, *args, **kwargs):
buffer = self.get_current_buffer()
@ -998,19 +1000,17 @@ class Controller(object):
if tweet.coordinates != None:
x = tweet.coordinates["coordinates"][0]
y = tweet.coordinates["coordinates"][1]
address = geocoder.reverse_geocode(y, x, language = languageHandler.curLang)
if event == None: output.speak(address[0].__str__())
else: self.view.show_address(address[0].__str__())
address = geocoder.reverse("{}, {}".format(y, x), language = languageHandler.curLang)
if event == None: output.speak(address.address)
else: self.view.show_address(address.address)
else:
output.speak(_(u"There are no coordinates in this tweet"))
except GeocoderError:
output.speak(_(u"There are no results for the coordinates in this tweet"))
except ValueError:
output.speak(_(u"Error decoding coordinates. Try again later."))
except KeyError:
pass
# except KeyError:
# pass
except AttributeError:
pass
output.speak(_("Unable to find address in OpenStreetMap."))
def view_reverse_geocode(self, event=None):
try:

View File

@ -102,7 +102,7 @@ class basicTweet(object):
else:
self.message.disable_button("shortenButton")
self.message.disable_button("unshortenButton")
if self.message.get("long_tweet") == False:
if self.message.get("long_tweet") == False and hasattr(self, "max"):
text = self.message.get_text()
results = parse_tweet(text)
self.message.set_title(_(u"%s - %s of %d characters") % (self.title, results.weightedLength, self.max))
@ -205,7 +205,7 @@ class dm(basicTweet):
c.show_menu("dm")
class viewTweet(basicTweet):
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date=""):
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date="", item_url=""):
""" This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event.
param tweet: A dictionary that represents a full tweet or a string for non-tweets.
param tweetList: If is_tweet is set to True, this could be a list of quoted tweets.
@ -273,6 +273,10 @@ class viewTweet(basicTweet):
text = tweet
self.message = message.viewNonTweet(text, date)
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
if item_url != "":
self.message.enable_button("share")
widgetUtils.connect_event(self.message.share, widgetUtils.BUTTON_PRESSED, self.share)
self.item_url = item_url
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
if self.contain_urls() == True:
self.message.enable_button("unshortenButton")
@ -290,3 +294,8 @@ class viewTweet(basicTweet):
if "https://twitter.com/" in i:
text = text.replace(i, "\n")
return text
def share(self, *args, **kwargs):
if hasattr(self, "item_url"):
output.copy(self.item_url)
output.speak(_("Link copied to clipboard."))

View File

@ -43,6 +43,8 @@ class baseSession(object):
self.logged = False
self.settings = None
self.db={}
# Config specification file.
self.config_spec = "conf.defaults"
@property
def is_logged(self):
@ -52,7 +54,7 @@ class baseSession(object):
""" Get settings for a session."""
file_ = "%s/session.conf" % (self.session_id,)
log.debug("Creating config file %s" % (file_,))
self.settings = config_utils.load_config(os.path.join(paths.config_path(), file_), os.path.join(paths.app_path(), "Conf.defaults"))
self.settings = config_utils.load_config(os.path.join(paths.config_path(), file_), os.path.join(paths.app_path(), self.config_spec))
self.init_sound()
self.load_persistent_data()

View File

@ -45,9 +45,9 @@ def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=No
else:
value = "text"
if hasattr(tweet, "retweeted_status") and value != "message":
text = StripChars(getattr(tweet.retweeted_status, value))
text = utils.clean_mentions(StripChars(getattr(tweet.retweeted_status, value)))
else:
text = StripChars(getattr(tweet, value))
text = utils.clean_mentions(StripChars(getattr(tweet, value)))
if show_screen_names:
user = session.get_user(tweet.user).screen_name
else:
@ -111,7 +111,7 @@ def compose_quoted_tweet(quoted_tweet, original_tweet, show_screen_names=False,
value = "full_text"
else:
value = "text"
text = StripChars(getattr(quoted_tweet, value))
text = utils.clean_mentions(StripChars(getattr(quoted_tweet, value)))
if show_screen_names:
quoting_user = session.get_user(quoted_tweet.user).screen_name
else:
@ -124,9 +124,9 @@ def compose_quoted_tweet(quoted_tweet, original_tweet, show_screen_names=False,
if hasattr(original_tweet, "message"):
original_text = original_tweet.message
elif hasattr(original_tweet, "full_text"):
original_text = StripChars(original_tweet.full_text)
original_text = utils.clean_mentions(StripChars(original_tweet.full_text))
else:
original_text = StripChars(original_tweet.text)
original_text = utils.clean_mentions(StripChars(original_tweet.text))
quoted_tweet.message = _(u"{0}. Quoted tweet from @{1}: {2}").format( text, original_user, original_text)
quoted_tweet = tweets.clear_url(quoted_tweet)
if hasattr(original_tweet, "entities") and original_tweet.entities.get("urls"):

View File

@ -244,3 +244,20 @@ def expand_urls(text, entities):
if url["url"] in text:
text = text.replace(url["url"], url["expanded_url"])
return text
def clean_mentions(text):
new_text = text
mentionned_people = [u for u in re.finditer("(?<=^|(?<=[^a-zA-Z0-9-\.]))@([A-Za-z0-9_]+)", text)]
if len(mentionned_people) <= 2:
return text
end = -2
total_users = 0
for user in mentionned_people:
if abs(user.start()-end) < 3:
new_text = new_text.replace(user.group(0), "")
total_users = total_users+1
end = user.end()
if total_users < 1:
return text
new_text = _("{user_1}, {user_2} and {all_users} more: {text}").format(user_1=mentionned_people[0].group(0), user_2=mentionned_people[1].group(0), all_users=total_users-2, text=new_text)
return new_text

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from builtins import str
import wx
import widgetUtils
@ -356,6 +354,8 @@ class viewTweet(widgetUtils.BaseDialog):
infoBox.Add(sourceBox, 0, wx.ALL, 5)
mainBox.Add(infoBox, 0, wx.ALL, 5)
mainBox.Add(dateBox, 0, wx.ALL, 5)
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
self.share.Enable(False)
self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
self.unshortenButton = wx.Button(panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
self.unshortenButton.Disable()
@ -363,6 +363,7 @@ class viewTweet(widgetUtils.BaseDialog):
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
cancelButton.SetDefault()
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
buttonsBox.Add(self.share, 0, wx.ALL, 5)
buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5)
buttonsBox.Add(self.translateButton, 0, wx.ALL, 5)
@ -429,6 +430,8 @@ class viewNonTweet(widgetUtils.BaseDialog):
dateBox.Add(dateLabel, 0, wx.ALL, 5)
dateBox.Add(date, 0, wx.ALL, 5)
mainBox.Add(dateBox, 0, wx.ALL, 5)
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
self.share.Enable(False)
self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
self.unshortenButton = wx.Button(panel, -1, _(u"&Expand URL"), size=wx.DefaultSize)
self.unshortenButton.Disable()
@ -436,6 +439,7 @@ class viewNonTweet(widgetUtils.BaseDialog):
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
cancelButton.SetDefault()
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
buttonsBox.Add(self.share, 0, wx.ALL, 5)
buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5)
buttonsBox.Add(self.translateButton, 0, wx.ALL, 5)
@ -463,5 +467,5 @@ class viewNonTweet(widgetUtils.BaseDialog):
self.text.SetFocus()
def enable_button(self, buttonName):
if getattr(self, buttonName):
if hasattr(self, buttonName):
return getattr(self, buttonName).Enable()

View File

@ -1,4 +1,4 @@
{"current_version": "8",
{"current_version": "10",
"description": "Snapshot version.",
"date": "unknown",
"downloads":