Merge branch 'next-gen' into unittests

This commit is contained in:
Manuel Cortez 2022-01-27 09:11:33 -06:00
commit 1262a9d784
No known key found for this signature in database
GPG Key ID: 9E0735CA15EFE790
10 changed files with 189 additions and 151 deletions

View File

@ -7,6 +7,7 @@ TWBlue Changelog
* TWBlue can display image descriptions within Tweet templates. For that, you can use the $image_description variable in your 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. * 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.
* In the Windows 11 Keymap, the default shortcut to open the keystrokes editor is now CTRL+Alt+Windows+K to avoid conflicts with the new global mute microphone shortcut. * In the Windows 11 Keymap, the default shortcut to open the keystrokes editor is now CTRL+Alt+Windows+K to avoid conflicts with the new global mute microphone shortcut.
* TWBlue show display properly HTML entities in tweet's text.
* TWBlue should no longer load old tweets in buffers. * TWBlue should no longer load old tweets in buffers.
* Fixed issue when uploading attachments (images, videos or gif files) while sending tweets or replies. * Fixed issue when uploading attachments (images, videos or gif files) while sending tweets or replies.
* Fixed an error that was making TWBlue to ask for a restart after saving account settings, even if such restart was not required. ([#413,](https://github.com/manuelcortez/TWBlue/issues/413)) * Fixed an error that was making TWBlue to ask for a restart after saving account settings, even if such restart was not required. ([#413,](https://github.com/manuelcortez/TWBlue/issues/413))

View File

@ -125,6 +125,9 @@ class Buffer(object):
def share_item(self): def share_item(self):
pass pass
def can_share(self):
pass
def destroy_status(self): def destroy_status(self):
pass pass

View File

@ -174,9 +174,9 @@ class BaseBuffer(base.Buffer):
self.put_items_on_list(number_of_items) self.put_items_on_list(number_of_items)
if hasattr(self, "finished_timeline") and self.finished_timeline == False: if hasattr(self, "finished_timeline") and self.finished_timeline == False:
if "-timeline" in self.name: if "-timeline" in self.name:
self.username = val[0].user.screen_name self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name
elif "-favorite" in self.name: elif "-favorite" in self.name:
self.username = self.session.api_call("get_user", **self.kwargs).screen_name self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name
self.finished_timeline = True self.finished_timeline = True
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True: if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
self.session.sound.play(self.sound) self.session.sound.play(self.sound)
@ -391,6 +391,12 @@ class BaseBuffer(base.Buffer):
tweet = self.session.db[self.name][self.buffer.list.get_selected()] tweet = self.session.db[self.name][self.buffer.list.get_selected()]
return tweet return tweet
def can_share(self):
tweet = self.get_right_tweet()
user = self.session.get_user(tweet.user)
is_protected = user.protected
return is_protected==False
@_tweets_exist @_tweets_exist
def reply(self, *args, **kwargs): def reply(self, *args, **kwargs):
tweet = self.get_right_tweet() tweet = self.get_right_tweet()
@ -442,6 +448,8 @@ class BaseBuffer(base.Buffer):
@_tweets_exist @_tweets_exist
def share_item(self, *args, **kwargs): def share_item(self, *args, **kwargs):
if self.can_share() == False:
return output.speak(_("This action is not supported on protected accounts."))
tweet = self.get_right_tweet() tweet = self.get_right_tweet()
id = tweet.id id = tweet.id
if self.session.settings["general"]["retweet_mode"] == "ask": if self.session.settings["general"]["retweet_mode"] == "ask":
@ -483,6 +491,9 @@ class BaseBuffer(base.Buffer):
self.session.sound.play("geo.ogg") self.session.sound.play("geo.ogg")
if self.session.settings['sound']['indicate_img'] and utils.is_media(tweet): if self.session.settings['sound']['indicate_img'] and utils.is_media(tweet):
self.session.sound.play("image.ogg") self.session.sound.play("image.ogg")
can_share = self.can_share()
pub.sendMessage("toggleShare", shareable=can_share)
self.buffer.retweet.Enable(can_share)
def audio(self, url='', *args, **kwargs): def audio(self, url='', *args, **kwargs):
if sound.URLPlayer.player.is_playing(): if sound.URLPlayer.player.is_playing():

View File

@ -134,6 +134,7 @@ class Controller(object):
pub.subscribe(self.manage_blocked_user, "blocked-user") pub.subscribe(self.manage_blocked_user, "blocked-user")
pub.subscribe(self.manage_unblocked_user, "unblocked-user") pub.subscribe(self.manage_unblocked_user, "unblocked-user")
pub.subscribe(self.create_buffer, "createBuffer") pub.subscribe(self.create_buffer, "createBuffer")
pub.subscribe(self.toggle_share_settings, "toggleShare")
if system == "Windows": if system == "Windows":
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed") pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
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)
@ -1657,6 +1658,10 @@ class Controller(object):
if sound_to_play != None and buff not in buffer.session.settings["other_buffers"]["muted_buffers"]: if sound_to_play != None and buff not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, sound_to_play) self.notify(buffer.session, sound_to_play)
def toggle_share_settings(self, shareable=True):
self.view.retweet.Enable(shareable)
def check_streams(self): def check_streams(self):
if self.started == False: if self.started == False:
return return

View File

@ -367,6 +367,7 @@ class viewTweet(basicTweet):
pass pass
def clear_text(self, text): def clear_text(self, text):
text = utils.StripChars(text)
urls = utils.find_urls_in_text(text) urls = utils.find_urls_in_text(text)
for i in urls: for i in urls:
if "https://twitter.com/" in i: if "https://twitter.com/" in i:

View File

@ -1,8 +1,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TW Blue 0.94\n" "Project-Id-Version: TW Blue 0.94\n"
"POT-Creation-Date: 2021-12-22 18:18+0100\n" "POT-Creation-Date: 2022-01-26 18:25+0100\n"
"PO-Revision-Date: 2021-12-27 23:24+0100\n" "PO-Revision-Date: 2022-01-26 18:26+0100\n"
"Last-Translator: Corentin Bacqué-Cazenave <corentin@progaccess.net>\n" "Last-Translator: Corentin Bacqué-Cazenave <corentin@progaccess.net>\n"
"Language-Team: Corentin Bacqué-Cazenave <corentin@progaccess.net>\n" "Language-Team: Corentin Bacqué-Cazenave <corentin@progaccess.net>\n"
"Language: fr\n" "Language: fr\n"
@ -21,56 +21,56 @@ msgid "This action is not supported for this buffer"
msgstr "Cette action n'est pas supportée pour ce tampon" msgstr "Cette action n'est pas supportée pour ce tampon"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:337 ../src\controller\settings.py:323 #: ../src\controller\mainController.py:338 ../src\controller\settings.py:323
msgid "Home" msgid "Home"
msgstr "Accueil" msgstr "Accueil"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:339 ../src\controller\settings.py:324 #: ../src\controller\mainController.py:340 ../src\controller\settings.py:324
msgid "Mentions" msgid "Mentions"
msgstr "Mentions" msgstr "Mentions"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:341 #: ../src\controller\mainController.py:342
msgid "Direct messages" msgid "Direct messages"
msgstr "Messages" msgstr "Messages"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:343 ../src\controller\settings.py:326 #: ../src\controller\mainController.py:344 ../src\controller\settings.py:326
msgid "Sent direct messages" msgid "Sent direct messages"
msgstr "Messages envoyés" msgstr "Messages envoyés"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:345 ../src\controller\settings.py:327 #: ../src\controller\mainController.py:346 ../src\controller\settings.py:327
msgid "Sent tweets" msgid "Sent tweets"
msgstr "Tweets envoyés" msgstr "Tweets envoyés"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:347 #: ../src\controller\mainController.py:348
#: ../src\controller\mainController.py:1390 ../src\controller\settings.py:328 #: ../src\controller\mainController.py:1391 ../src\controller\settings.py:328
msgid "Likes" msgid "Likes"
msgstr "Favoris" msgstr "Favoris"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:349 #: ../src\controller\mainController.py:350
#: ../src\controller\mainController.py:1395 ../src\controller\settings.py:329 #: ../src\controller\mainController.py:1396 ../src\controller\settings.py:329
msgid "Followers" msgid "Followers"
msgstr "Abonnés" msgstr "Abonnés"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:353 #: ../src\controller\mainController.py:354
#: ../src\controller\mainController.py:1405 ../src\controller\settings.py:331 #: ../src\controller\mainController.py:1406 ../src\controller\settings.py:331
msgid "Blocked users" msgid "Blocked users"
msgstr "Utilisateurs bloqués" msgstr "Utilisateurs bloqués"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:355 #: ../src\controller\mainController.py:356
#: ../src\controller\mainController.py:1410 ../src\controller\settings.py:332 #: ../src\controller\mainController.py:1411 ../src\controller\settings.py:332
msgid "Muted users" msgid "Muted users"
msgstr "Utilisateurs masqués" msgstr "Utilisateurs masqués"
#: ../src\controller\buffers\twitter\base.py:70 #: ../src\controller\buffers\twitter\base.py:70
#: ../src\controller\mainController.py:1400 ../src\controller\settings.py:330 #: ../src\controller\mainController.py:1401 ../src\controller\settings.py:330
msgid "Friends" msgid "Friends"
msgstr "Abonnements" msgstr "Abonnements"
@ -129,46 +129,50 @@ msgstr "%s éléments récupérés"
msgid "This buffer is not a timeline; it can't be deleted." msgid "This buffer is not a timeline; it can't be deleted."
msgstr "Ce tampon n'est pas une chronologie ; Impossible de le supprimé." msgstr "Ce tampon n'est pas une chronologie ; Impossible de le supprimé."
#: ../src\controller\buffers\twitter\base.py:404 #: ../src\controller\buffers\twitter\base.py:410
msgid "Reply to {arg0}" msgid "Reply to {arg0}"
msgstr "Répondre à {arg0}" msgstr "Répondre à {arg0}"
#: ../src\controller\buffers\twitter\base.py:406 #: ../src\controller\buffers\twitter\base.py:412
#: ../src\keystrokeEditor\constants.py:11 ../src\wxUI\buffers\base.py:27 #: ../src\keystrokeEditor\constants.py:11 ../src\wxUI\buffers\base.py:27
msgid "Reply" msgid "Reply"
msgstr "Répondre" msgstr "Répondre"
#: ../src\controller\buffers\twitter\base.py:407 #: ../src\controller\buffers\twitter\base.py:413
msgid "Reply to %s" msgid "Reply to %s"
msgstr "Répondre à %s" msgstr "Répondre à %s"
#: ../src\controller\buffers\twitter\base.py:430 #: ../src\controller\buffers\twitter\base.py:436
#: ../src\controller\buffers\twitter\directMessages.py:124 #: ../src\controller\buffers\twitter\directMessages.py:124
msgid "New direct message" msgid "New direct message"
msgstr "Nouveau message" msgstr "Nouveau message"
#: ../src\controller\buffers\twitter\base.py:430 #: ../src\controller\buffers\twitter\base.py:436
#: ../src\controller\messages.py:268 #: ../src\controller\messages.py:268
msgid "Direct message to %s" msgid "Direct message to %s"
msgstr "Message à %s" msgstr "Message à %s"
#: ../src\controller\buffers\twitter\base.py:461 #: ../src\controller\buffers\twitter\base.py:452
msgid "This action is not supported on protected accounts."
msgstr "Cette action n'est pas supportée pour les comptes protégés."
#: ../src\controller\buffers\twitter\base.py:469
msgid "Add your comment to the tweet" msgid "Add your comment to the tweet"
msgstr "Ajoutez votre commentaire pour le tweet" msgstr "Ajoutez votre commentaire pour le tweet"
#: ../src\controller\buffers\twitter\base.py:461 #: ../src\controller\buffers\twitter\base.py:469
msgid "Quote" msgid "Quote"
msgstr "Citer" msgstr "Citer"
#: ../src\controller\buffers\twitter\base.py:522 #: ../src\controller\buffers\twitter\base.py:533
msgid "Opening URL..." msgid "Opening URL..."
msgstr "Ouverture de l'URL..." msgstr "Ouverture de l'URL..."
#: ../src\controller\buffers\twitter\base.py:559 #: ../src\controller\buffers\twitter\base.py:570
msgid "User details" msgid "User details"
msgstr "Détails de l'utilisateur" msgstr "Détails de l'utilisateur"
#: ../src\controller\buffers\twitter\base.py:580 #: ../src\controller\buffers\twitter\base.py:591
msgid "Opening item in web browser..." msgid "Opening item in web browser..."
msgstr "Ouverture de l'élément dans le navigateur Web..." msgstr "Ouverture de l'élément dans le navigateur Web..."
@ -207,85 +211,85 @@ msgstr "{0} nouvel abonné"
msgid "This action is not supported in the buffer, yet." msgid "This action is not supported in the buffer, yet."
msgstr "Cette action n'est pas supportée pour le tampon actuel" msgstr "Cette action n'est pas supportée pour le tampon actuel"
#: ../src\controller\mainController.py:277 #: ../src\controller\mainController.py:278
msgid "Ready" msgid "Ready"
msgstr "Prêt" msgstr "Prêt"
#: ../src\controller\mainController.py:351 #: ../src\controller\mainController.py:352
msgid "Following" msgid "Following"
msgstr "Abonnements" msgstr "Abonnements"
#: ../src\controller\mainController.py:356 #: ../src\controller\mainController.py:357
msgid "Timelines" msgid "Timelines"
msgstr "Chronologies" msgstr "Chronologies"
#: ../src\controller\mainController.py:359 #: ../src\controller\mainController.py:360
#: ../src\controller\mainController.py:883 #: ../src\controller\mainController.py:884
#: ../src\controller\mainController.py:1582 #: ../src\controller\mainController.py:1583
msgid "Timeline for {}" msgid "Timeline for {}"
msgstr "Chronologie de {}" msgstr "Chronologie de {}"
#: ../src\controller\mainController.py:360 #: ../src\controller\mainController.py:361
msgid "Likes timelines" msgid "Likes timelines"
msgstr "Chronologies des favoris" msgstr "Chronologies des favoris"
#: ../src\controller\mainController.py:363 #: ../src\controller\mainController.py:364
#: ../src\controller\mainController.py:902 #: ../src\controller\mainController.py:903
#: ../src\controller\mainController.py:1584 #: ../src\controller\mainController.py:1585
msgid "Likes for {}" msgid "Likes for {}"
msgstr "Favoris de {}" msgstr "Favoris de {}"
#: ../src\controller\mainController.py:364 #: ../src\controller\mainController.py:365
msgid "Followers timelines" msgid "Followers timelines"
msgstr "Chronologies des abonnés" msgstr "Chronologies des abonnés"
#: ../src\controller\mainController.py:367 #: ../src\controller\mainController.py:368
#: ../src\controller\mainController.py:921 #: ../src\controller\mainController.py:922
#: ../src\controller\mainController.py:1586 #: ../src\controller\mainController.py:1587
msgid "Followers for {}" msgid "Followers for {}"
msgstr "Abonnés de {}" msgstr "Abonnés de {}"
#: ../src\controller\mainController.py:368 #: ../src\controller\mainController.py:369
msgid "Following timelines" msgid "Following timelines"
msgstr "Chronologies des abonnements" msgstr "Chronologies des abonnements"
#: ../src\controller\mainController.py:371 #: ../src\controller\mainController.py:372
#: ../src\controller\mainController.py:940 #: ../src\controller\mainController.py:941
#: ../src\controller\mainController.py:1588 #: ../src\controller\mainController.py:1589
msgid "Friends for {}" msgid "Friends for {}"
msgstr "Abonnements de {}" msgstr "Abonnements de {}"
#: ../src\controller\mainController.py:372 ../src\wxUI\dialogs\lists.py:13 #: ../src\controller\mainController.py:373 ../src\wxUI\dialogs\lists.py:13
msgid "Lists" msgid "Lists"
msgstr "Listes" msgstr "Listes"
#: ../src\controller\mainController.py:375 #: ../src\controller\mainController.py:376
#: ../src\controller\mainController.py:1422 #: ../src\controller\mainController.py:1423
msgid "List for {}" msgid "List for {}"
msgstr "Liste {}" msgstr "Liste {}"
#: ../src\controller\mainController.py:376 #: ../src\controller\mainController.py:377
msgid "Searches" msgid "Searches"
msgstr "Recherches" msgstr "Recherches"
#: ../src\controller\mainController.py:379 #: ../src\controller\mainController.py:380
#: ../src\controller\mainController.py:426 #: ../src\controller\mainController.py:427
#: ../src\controller\mainController.py:431 #: ../src\controller\mainController.py:432
msgid "Search for {}" msgid "Search for {}"
msgstr "Recherche de {}" msgstr "Recherche de {}"
#: ../src\controller\mainController.py:381 #: ../src\controller\mainController.py:382
#: ../src\controller\mainController.py:982 #: ../src\controller\mainController.py:983
#: ../src\controller\mainController.py:1590 #: ../src\controller\mainController.py:1591
msgid "Trending topics for %s" msgid "Trending topics for %s"
msgstr "Tendances pour %s" msgstr "Tendances pour %s"
#: ../src\controller\mainController.py:448 #: ../src\controller\mainController.py:449
#: ../src\controller\mainController.py:464 #: ../src\controller\mainController.py:465
#: ../src\controller\mainController.py:1080 #: ../src\controller\mainController.py:1081
#: ../src\controller\mainController.py:1099 #: ../src\controller\mainController.py:1100
#: ../src\controller\mainController.py:1118 #: ../src\controller\mainController.py:1119
#: ../src\controller\mainController.py:1137 #: ../src\controller\mainController.py:1138
msgid "" msgid ""
"No session is currently in focus. Focus a session with the next or previous " "No session is currently in focus. Focus a session with the next or previous "
"session shortcut." "session shortcut."
@ -293,152 +297,152 @@ msgstr ""
"Aucune session n'a actuellement le focus. Sélectionnez-en une avec le " "Aucune session n'a actuellement le focus. Sélectionnez-en une avec le "
"raccourci pour la session précédente ou suivante." "raccourci pour la session précédente ou suivante."
#: ../src\controller\mainController.py:452 #: ../src\controller\mainController.py:453
msgid "Empty buffer." msgid "Empty buffer."
msgstr "Tampon vide." msgstr "Tampon vide."
#: ../src\controller\mainController.py:459 #: ../src\controller\mainController.py:460
msgid "{0} not found." msgid "{0} not found."
msgstr "{0} introuvable." msgstr "{0} introuvable."
#: ../src\controller\mainController.py:469 #: ../src\controller\mainController.py:470
msgid "Filters cannot be applied on this buffer" msgid "Filters cannot be applied on this buffer"
msgstr "Les filtres ne peuvent pas s'appliquer à ce tampon" msgstr "Les filtres ne peuvent pas s'appliquer à ce tampon"
#: ../src\controller\mainController.py:522 #: ../src\controller\mainController.py:523
#: ../src\controller\mainController.py:539 #: ../src\controller\mainController.py:540
#: ../src\controller\mainController.py:568 #: ../src\controller\mainController.py:569
msgid "Select the user" msgid "Select the user"
msgstr "Sélectionnez l'utilisateur" msgstr "Sélectionnez l'utilisateur"
#: ../src\controller\mainController.py:753 #: ../src\controller\mainController.py:754
msgid "Add an user alias" msgid "Add an user alias"
msgstr "Ajoute un alias pour l'utilisateur" msgstr "Ajoute un alias pour l'utilisateur"
#: ../src\controller\mainController.py:761 #: ../src\controller\mainController.py:762
msgid "Alias has been set correctly for {}." msgid "Alias has been set correctly for {}."
msgstr "L'alias pour {} a correctement été définie" msgstr "L'alias pour {} a correctement été définie"
#: ../src\controller\mainController.py:829 ../src\controller\messages.py:327 #: ../src\controller\mainController.py:830 ../src\controller\messages.py:327
msgid "MMM D, YYYY. H:m" msgid "MMM D, YYYY. H:m"
msgstr "D MMM YYYY à H:m" msgstr "D MMM YYYY à H:m"
#: ../src\controller\mainController.py:957 #: ../src\controller\mainController.py:958
msgid "Conversation with {0}" msgid "Conversation with {0}"
msgstr "Conversation avec {0}" msgstr "Conversation avec {0}"
#: ../src\controller\mainController.py:998 #: ../src\controller\mainController.py:999
#: ../src\controller\mainController.py:1015 #: ../src\controller\mainController.py:1016
msgid "There are no coordinates in this tweet" msgid "There are no coordinates in this tweet"
msgstr "Il n'y a aucune coordonnée dans ce tweet" msgstr "Il n'y a aucune coordonnée dans ce tweet"
#: ../src\controller\mainController.py:1000 #: ../src\controller\mainController.py:1001
#: ../src\controller\mainController.py:1019 #: ../src\controller\mainController.py:1020
msgid "Error decoding coordinates. Try again later." msgid "Error decoding coordinates. Try again later."
msgstr "Erreur pendant le décodage des coordonnées. Réessayez plus tard." msgstr "Erreur pendant le décodage des coordonnées. Réessayez plus tard."
#: ../src\controller\mainController.py:1004 #: ../src\controller\mainController.py:1005
msgid "Unable to find address in OpenStreetMap." msgid "Unable to find address in OpenStreetMap."
msgstr "Impossible de trouver l'adresse dans OpenStreetMap" msgstr "Impossible de trouver l'adresse dans OpenStreetMap"
#: ../src\controller\mainController.py:1017 #: ../src\controller\mainController.py:1018
msgid "There are no results for the coordinates in this tweet" msgid "There are no results for the coordinates in this tweet"
msgstr "Il n'y a aucun résultat pour les coordonnées dans ce tweet" msgstr "Il n'y a aucun résultat pour les coordonnées dans ce tweet"
#: ../src\controller\mainController.py:1128 #: ../src\controller\mainController.py:1129
#: ../src\controller\mainController.py:1147 #: ../src\controller\mainController.py:1148
msgid "%s, %s of %s" msgid "%s, %s of %s"
msgstr "%s, %s de %s" msgstr "%s, %s de %s"
#: ../src\controller\mainController.py:1130 #: ../src\controller\mainController.py:1131
#: ../src\controller\mainController.py:1149 #: ../src\controller\mainController.py:1150
#: ../src\controller\mainController.py:1174 #: ../src\controller\mainController.py:1175
#: ../src\controller\mainController.py:1199 #: ../src\controller\mainController.py:1200
msgid "%s. Empty" msgid "%s. Empty"
msgstr "%s. Vide" msgstr "%s. Vide"
#: ../src\controller\mainController.py:1162 #: ../src\controller\mainController.py:1163
#: ../src\controller\mainController.py:1166 #: ../src\controller\mainController.py:1167
#: ../src\controller\mainController.py:1187 #: ../src\controller\mainController.py:1188
msgid "{0}: This account is not logged into Twitter." msgid "{0}: This account is not logged into Twitter."
msgstr "{0}: Ce compte n'est pas connecté à twitter." msgstr "{0}: Ce compte n'est pas connecté à twitter."
#: ../src\controller\mainController.py:1172 #: ../src\controller\mainController.py:1173
#: ../src\controller\mainController.py:1197 #: ../src\controller\mainController.py:1198
msgid "%s. %s, %s of %s" msgid "%s. %s, %s of %s"
msgstr "%s. %s, %s de %s" msgstr "%s. %s, %s de %s"
#: ../src\controller\mainController.py:1191 #: ../src\controller\mainController.py:1192
msgid "{0}: This account is not logged into twitter." msgid "{0}: This account is not logged into twitter."
msgstr "{0}: Ce compte n'est pas connecté à twitter." msgstr "{0}: Ce compte n'est pas connecté à twitter."
#: ../src\controller\mainController.py:1416 #: ../src\controller\mainController.py:1417
msgid "This list is already opened" msgid "This list is already opened"
msgstr "Cette liste est déjà ouverte" msgstr "Cette liste est déjà ouverte"
#: ../src\controller\mainController.py:1446 #: ../src\controller\mainController.py:1447
#: ../src\controller\mainController.py:1462 #: ../src\controller\mainController.py:1463
msgid "" msgid ""
"An error happened while trying to connect to the server. Please try later." "An error happened while trying to connect to the server. Please try later."
msgstr "" msgstr ""
"Une erreur s'est produite en essayant de se connecter au serveur. Veuillez " "Une erreur s'est produite en essayant de se connecter au serveur. Veuillez "
"réessayer plus tard." "réessayer plus tard."
#: ../src\controller\mainController.py:1498 #: ../src\controller\mainController.py:1499
msgid "The auto-reading of new tweets is enabled for this buffer" msgid "The auto-reading of new tweets is enabled for this buffer"
msgstr "La lecture automatique des nouveaux tweets est activée pour ce tampon" msgstr "La lecture automatique des nouveaux tweets est activée pour ce tampon"
#: ../src\controller\mainController.py:1501 #: ../src\controller\mainController.py:1502
msgid "The auto-reading of new tweets is disabled for this buffer" msgid "The auto-reading of new tweets is disabled for this buffer"
msgstr "" msgstr ""
"La lecture automatique des nouveaux tweets est désactivée pour ce tampon" "La lecture automatique des nouveaux tweets est désactivée pour ce tampon"
#: ../src\controller\mainController.py:1508 #: ../src\controller\mainController.py:1509
msgid "Session mute on" msgid "Session mute on"
msgstr "Session muet" msgstr "Session muet"
#: ../src\controller\mainController.py:1511 #: ../src\controller\mainController.py:1512
msgid "Session mute off" msgid "Session mute off"
msgstr "Session non muet" msgstr "Session non muet"
#: ../src\controller\mainController.py:1519 #: ../src\controller\mainController.py:1520
msgid "Buffer mute on" msgid "Buffer mute on"
msgstr "Tampon muet" msgstr "Tampon muet"
#: ../src\controller\mainController.py:1522 #: ../src\controller\mainController.py:1523
msgid "Buffer mute off" msgid "Buffer mute off"
msgstr "Tampon non muet" msgstr "Tampon non muet"
#: ../src\controller\mainController.py:1542 #: ../src\controller\mainController.py:1543
msgid "Copied" msgid "Copied"
msgstr "Copié" msgstr "Copié"
#: ../src\controller\mainController.py:1572 #: ../src\controller\mainController.py:1573
msgid "Unable to update this buffer." msgid "Unable to update this buffer."
msgstr "Impossible de mettre à jour ce tampon." msgstr "Impossible de mettre à jour ce tampon."
#: ../src\controller\mainController.py:1575 #: ../src\controller\mainController.py:1576
msgid "Updating buffer..." msgid "Updating buffer..."
msgstr "Actualisation..." msgstr "Actualisation..."
#: ../src\controller\mainController.py:1578 #: ../src\controller\mainController.py:1579
msgid "{0} items retrieved" msgid "{0} items retrieved"
msgstr "{0} éléments récupérés" msgstr "{0} éléments récupérés"
#: ../src\controller\mainController.py:1597 #: ../src\controller\mainController.py:1598
#: ../src\controller\mainController.py:1617 #: ../src\controller\mainController.py:1618
msgid "Invalid buffer" msgid "Invalid buffer"
msgstr "Tampon invalide" msgstr "Tampon invalide"
#: ../src\controller\mainController.py:1608 #: ../src\controller\mainController.py:1609
msgid "Picture {0}" msgid "Picture {0}"
msgstr "Photo {0}" msgstr "Photo {0}"
#: ../src\controller\mainController.py:1609 #: ../src\controller\mainController.py:1610
msgid "Select the picture" msgid "Select the picture"
msgstr "Sélectionner la photo" msgstr "Sélectionner la photo"
#: ../src\controller\mainController.py:1628 #: ../src\controller\mainController.py:1629
msgid "Unable to extract text" msgid "Unable to extract text"
msgstr "Impossible d'extraire le texte" msgstr "Impossible d'extraire le texte"
@ -454,7 +458,7 @@ msgstr "%s - %s/%d caractères"
msgid "View item" msgid "View item"
msgstr "Voir l'élément" msgstr "Voir l'élément"
#: ../src\controller\messages.py:379 #: ../src\controller\messages.py:380
msgid "Link copied to clipboard." msgid "Link copied to clipboard."
msgstr "Lien copié dans le Presse-papiers" msgstr "Lien copié dans le Presse-papiers"
@ -1968,27 +1972,27 @@ msgstr ""
"elle va être détruite et recréée automatiquement. Si ce problème perciste, " "elle va être détruite et recréée automatiquement. Si ce problème perciste, "
"veuillez envoyer le journal d'erreur aux développeurs de {app}." "veuillez envoyer le journal d'erreur aux développeurs de {app}."
#: ../src\sessions\twitter\compose.py:38 ../src\sessions\twitter\compose.py:81 #: ../src\sessions\twitter\compose.py:25 ../src\sessions\twitter\compose.py:68
#: ../src\sessions\twitter\compose.py:146 #: ../src\sessions\twitter\compose.py:133
#: ../src\sessions\twitter\compose.py:155 #: ../src\sessions\twitter\compose.py:142
#: ../src\sessions\twitter\templates.py:26 #: ../src\sessions\twitter\templates.py:26
msgid "dddd, MMMM D, YYYY H:m:s" msgid "dddd, MMMM D, YYYY H:m:s"
msgstr "dddd D MMMM YYYY à H:m:s" msgstr "dddd D MMMM YYYY à H:m:s"
#: ../src\sessions\twitter\compose.py:89 ../src\sessions\twitter\compose.py:91 #: ../src\sessions\twitter\compose.py:76 ../src\sessions\twitter\compose.py:78
msgid "Dm to %s " msgid "Dm to %s "
msgstr "Mp à %s" msgstr "Mp à %s"
#: ../src\sessions\twitter\compose.py:130 #: ../src\sessions\twitter\compose.py:117
msgid "{0}. Quoted tweet from @{1}: {2}" msgid "{0}. Quoted tweet from @{1}: {2}"
msgstr "{0}. Tweet de @{1} cité : {2}" msgstr "{0}. Tweet de @{1} cité : {2}"
#: ../src\sessions\twitter\compose.py:157 #: ../src\sessions\twitter\compose.py:144
#: ../src\sessions\twitter\compose.py:159 #: ../src\sessions\twitter\compose.py:146
msgid "Unavailable" msgid "Unavailable"
msgstr "Indisponible" msgstr "Indisponible"
#: ../src\sessions\twitter\compose.py:160 #: ../src\sessions\twitter\compose.py:147
msgid "" msgid ""
"%s (@%s). %s followers, %s friends, %s tweets. Last tweeted %s. Joined " "%s (@%s). %s followers, %s friends, %s tweets. Last tweeted %s. Joined "
"Twitter %s" "Twitter %s"
@ -1996,15 +2000,15 @@ msgstr ""
"%s (@%s). %s abonnés, %s abonnements, %s tweets. Dernier tweet envoyé %s. À " "%s (@%s). %s abonnés, %s abonnements, %s tweets. Dernier tweet envoyé %s. À "
"rejoint Twitter %s" "rejoint Twitter %s"
#: ../src\sessions\twitter\compose.py:164 #: ../src\sessions\twitter\compose.py:151
msgid "No description available" msgid "No description available"
msgstr "Aucune description disponible" msgstr "Aucune description disponible"
#: ../src\sessions\twitter\compose.py:168 #: ../src\sessions\twitter\compose.py:155
msgid "private" msgid "private"
msgstr "privé" msgstr "privé"
#: ../src\sessions\twitter\compose.py:169 #: ../src\sessions\twitter\compose.py:156
msgid "public" msgid "public"
msgstr "public" msgstr "public"
@ -2018,8 +2022,8 @@ msgstr "%s erreur. Raison: %s"
msgid "%s succeeded." msgid "%s succeeded."
msgstr "%s réussi." msgstr "%s réussi."
#: ../src\sessions\twitter\session.py:450 #: ../src\sessions\twitter\session.py:451
#: ../src\sessions\twitter\session.py:528 #: ../src\sessions\twitter\session.py:529
msgid "Deleted account" msgid "Deleted account"
msgstr "Compte supprimé" msgstr "Compte supprimé"
@ -2043,23 +2047,23 @@ msgstr ""
"$display_name (@$screen_name). $followers abonnés, $following abonnements, " "$display_name (@$screen_name). $followers abonnés, $following abonnements, "
"$tweets tweets. À rejoint Twitter $created_at." "$tweets tweets. À rejoint Twitter $created_at."
#: ../src\sessions\twitter\templates.py:50 #: ../src\sessions\twitter\templates.py:54
msgid "Image description: {}." msgid "Image description: {}."
msgstr "Description de l'image: {}." msgstr "Description de l'image: {}."
#: ../src\sessions\twitter\utils.py:229 #: ../src\sessions\twitter\utils.py:243
msgid "Sorry, you are not authorised to see this status." msgid "Sorry, you are not authorised to see this status."
msgstr "Désolé, vous n'êtes pas autorisé à voir ce Tweet." msgstr "Désolé, vous n'êtes pas autorisé à voir ce Tweet."
#: ../src\sessions\twitter\utils.py:231 #: ../src\sessions\twitter\utils.py:245
msgid "No status found with that ID" msgid "No status found with that ID"
msgstr "Aucun Tweet trouvée avec cet ID" msgstr "Aucun Tweet trouvée avec cet ID"
#: ../src\sessions\twitter\utils.py:233 #: ../src\sessions\twitter\utils.py:247
msgid "Error {0}" msgid "Error {0}"
msgstr "Erreur {0}" msgstr "Erreur {0}"
#: ../src\sessions\twitter\utils.py:260 #: ../src\sessions\twitter\utils.py:274
msgid "{user_1}, {user_2} and {all_users} more: {text}" msgid "{user_1}, {user_2} and {all_users} more: {text}"
msgstr "{user_1}, {user_2} et {all_users} de plus : {text}" msgstr "{user_1}, {user_2} et {all_users} de plus : {text}"

View File

@ -3,7 +3,6 @@ import platform
system = platform.system() system = platform.system()
from . import utils from . import utils
import re import re
import html.entities
import time import time
import output import output
import languageHandler import languageHandler
@ -11,21 +10,9 @@ import arrow
import logging import logging
import config import config
from .long_tweets import twishort, tweets from .long_tweets import twishort, tweets
from .utils import StripChars
log = logging.getLogger("compose") log = logging.getLogger("compose")
def StripChars(s):
"""Converts any html entities in s to their unicode-decoded equivalents and returns a string."""
entity_re = re.compile(r"&(#\d+|\w+);")
def matchFunc(match):
"""Nested function to handle a match object.
If we match &blah; and it's not found, &blah; will be returned.
if we match #\d+, unichr(digits) will be returned.
Else, a unicode string will be returned."""
if match.group(1).startswith('#'): return chr(int(match.group(1)[1:]))
replacement = html.entities.entitydefs.get(match.group(1), "&%s;" % match.group(1))
return replacement
return str(entity_re.sub(matchFunc, s))
chars = "abcdefghijklmnopqrstuvwxyz" chars = "abcdefghijklmnopqrstuvwxyz"
def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=None): def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=None):

View File

@ -36,9 +36,9 @@ class Session(base.baseSession):
return self.order_direct_messages(data) return self.order_direct_messages(data)
num = 0 num = 0
last_id = None last_id = None
if (name in self.db) == False: if self.db.get(name) == None:
self.db[name] = [] self.db[name] = []
if ("users" in self.db) == False: if self.db.get("users") == None:
self.db["users"] = {} self.db["users"] = {}
objects = self.db[name] objects = self.db[name]
if ignore_older and len(self.db[name]) > 0: if ignore_older and len(self.db[name]) > 0:
@ -264,13 +264,13 @@ class Session(base.baseSession):
returns a list with all items retrieved.""" returns a list with all items retrieved."""
results = [] results = []
if self.db.get(name) == None or self.db.get(name) == []: if self.db.get(name) == None or self.db.get(name) == []:
last_id = None since_id = None
else: else:
if self.settings["general"]["reverse_timelines"] == False: if self.settings["general"]["reverse_timelines"] == False:
last_id = self.db[name][0].id since_id = self.db[name][-1].id
else: else:
last_id = self.db[name][-1].id since_id = self.db[name][0].id
data = getattr(self.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], since_id=last_id, *args, **kwargs) data = getattr(self.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], since_id=since_id, *args, **kwargs)
results.extend(data) results.extend(data)
results.reverse() results.reverse()
return results return results

View File

@ -32,7 +32,7 @@ def process_text(tweet):
elif hasattr(tweet, "text"): elif hasattr(tweet, "text"):
text = tweet.text text = tweet.text
# Cleanup mentions, so we'll remove more than 2 mentions to make the tweet easier to read. # Cleanup mentions, so we'll remove more than 2 mentions to make the tweet easier to read.
text = utils.clean_mentions(text) text = utils.clean_mentions(utils.StripChars(text))
# Replace URLS for extended version of those. # Replace URLS for extended version of those.
if hasattr(tweet, "entities"): if hasattr(tweet, "entities"):
text = utils.expand_urls(text, tweet.entities) text = utils.expand_urls(text, tweet.entities)
@ -45,11 +45,20 @@ def process_image_descriptions(entities):
for media in entities["media"]: for media in entities["media"]:
if media.get("ext_alt_text") != None: if media.get("ext_alt_text") != None:
image_descriptions.append(media.get("ext_alt_text")) image_descriptions.append(media.get("ext_alt_text"))
# Tweets retrieved via the Streaming API have a description field in media photos with image description available.
elif media.get("description") != None:
image_descriptions.append(media.get("description"))
idescriptions = "" idescriptions = ""
for image in image_descriptions: for image in image_descriptions:
idescriptions += _("Image description: {}.").format(image) idescriptions += _("Image description: {}.").format(image)
return idescriptions return idescriptions
def remove_unneeded_variables(template, variables):
for variable in variables:
template = re.sub("\$"+variable, "", template)
return template
def render_tweet(tweet, template, session, 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. """ Renders any given Tweet according to the passed template.
Available data for tweets will be stored in the following variables: Available data for tweets will be stored in the following variables:
@ -61,6 +70,7 @@ def render_tweet(tweet, template, session, relative_times=False, offset_seconds=
$text: Tweet text. $text: Tweet text.
$image_descriptions: Information regarding image descriptions added by twitter users. $image_descriptions: Information regarding image descriptions added by twitter users.
""" """
global tweet_variables
available_data = dict() available_data = dict()
created_at = process_date(tweet.created_at, relative_times, offset_seconds) created_at = process_date(tweet.created_at, relative_times, offset_seconds)
available_data.update(date=created_at) available_data.update(date=created_at)
@ -89,7 +99,7 @@ def render_tweet(tweet, template, session, relative_times=False, offset_seconds=
if image_descriptions != "": if image_descriptions != "":
available_data.update(image_descriptions=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) result = remove_unneeded_variables(result, tweet_variables)
return result return result
def render_dm(dm, template, session, relative_times=False, offset_seconds=0): def render_dm(dm, template, session, relative_times=False, offset_seconds=0):
@ -102,6 +112,7 @@ def render_dm(dm, template, session, relative_times=False, offset_seconds=0):
$recipient_screen_name: User screen name for user receiving the dm, this is the same name used to reference the user in Twitter. $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. $text: Text of the direct message.
""" """
global dm_variables
available_data = dict() available_data = dict()
available_data.update(text=utils.expand_urls(dm.message_create["message_data"]["text"], dm.message_create["message_data"]["entities"])) 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. # Let's remove the last 3 digits in the timestamp string.
@ -116,7 +127,7 @@ def render_dm(dm, template, session, relative_times=False, offset_seconds=0):
recipient = session.get_user(dm.message_create["target"]["recipient_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) 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) result = remove_unneeded_variables(result, dm_variables)
return result return result
# Sesion object is not used in this function but we keep compatibility across all rendering functions. # Sesion object is not used in this function but we keep compatibility across all rendering functions.
@ -134,6 +145,7 @@ def render_person(user, template, session=None, relative_times=True, offset_seco
$tweets: The number of Tweets (including retweets) issued by the user. 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. $created_at: The date and time that the user account was created on Twitter.
""" """
global person_variables
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) 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. # Nullable values.
nullables = ["location", "description"] nullables = ["location", "description"]
@ -143,5 +155,5 @@ def render_person(user, template, session=None, relative_times=True, offset_seco
created_at = process_date(user.created_at, relative_times=relative_times, offset_seconds=offset_seconds) created_at = process_date(user.created_at, relative_times=relative_times, offset_seconds=offset_seconds)
available_data.update(created_at=created_at) 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) result = remove_unneeded_variables(result, person_variables)
return result return result

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import re import re
import html.entities
import output import output
import logging import logging
import requests import requests
@ -16,6 +17,19 @@ url_re = re.compile(r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4
url_re2 = re.compile("(?:\w+://|www\.)[^ ,.?!#%=+][^ \\n\\t]*") url_re2 = re.compile("(?:\w+://|www\.)[^ ,.?!#%=+][^ \\n\\t]*")
bad_chars = '\'\\\n.,[](){}:;"' bad_chars = '\'\\\n.,[](){}:;"'
def StripChars(s):
"""Converts any html entities in s to their unicode-decoded equivalents and returns a string."""
entity_re = re.compile(r"&(#\d+|\w+);")
def matchFunc(match):
"""Nested function to handle a match object.
If we match &blah; and it's not found, &blah; will be returned.
if we match #\d+, unichr(digits) will be returned.
Else, a unicode string will be returned."""
if match.group(1).startswith('#'): return chr(int(match.group(1)[1:]))
replacement = html.entities.entitydefs.get(match.group(1), "&%s;" % match.group(1))
return replacement
return str(entity_re.sub(matchFunc, s))
def find_urls_in_text(text): def find_urls_in_text(text):
return url_re2.findall(text) return url_re2.findall(text)