Compare commits

...

59 Commits

Author SHA1 Message Date
301bd5fd39 Pushed a new snapshot 2021-10-20 15:52:58 -05:00
286e030f40 Handle new Tweepy exceptions properly. #403 2021-10-07 09:20:06 -05:00
d8fca3b31a Initial work to Support Tweepy 4 2021-09-26 03:58:25 -05:00
0c27427843 Merge branch 'next-gen' of github.com:manuelcortez/twblue into next-gen 2021-09-10 15:05:28 -05:00
dfdbe3c5f4 Close VLC window after video playback ends. Close #399 2021-09-10 15:05:05 -05:00
José Manuel Delicado
2222a97451 Merge pull request #394 from Oreonan/fr_020821
Update french interface translation
2021-09-03 11:38:50 +02:00
fbe93ea4be Made FreakyBlue The default soundpack for new twitter sessions 2021-09-02 09:59:13 -05:00
4bcae1aa97 Fixed search filters. Closes #397 2021-09-02 09:38:05 -05:00
4cabf5b9cd Added code to handle user timelines in buffer creator 2021-08-31 10:31:18 -05:00
a9a4189295 Added improvements to tweet condense feature 2021-08-31 10:05:21 -05:00
c7b6d69518 Fixed an error in new buffer creation. Closes #396 2021-08-30 13:19:15 -05:00
65512a9862 Added a pubsub event to create sessions buffers 2021-08-30 10:51:26 -05:00
Oreonan
17ea8af050 Update translation 2021-08-28 08:37:26 +02:00
Oreonan
43578a32eb Merge remote-tracking branch 'origin/next-gen' into fr_020821 2021-08-28 08:33:40 +02:00
7c34204d17 Allow to specify alternative configuration specs for sessions 2021-08-27 13:45:59 -05:00
3a5c1c10d3 Released a new snapshot 2021-08-26 15:13:34 -05:00
f9864a887d Fixed a small traceback that was happening when translating a direct message 2021-08-26 09:16:02 -05:00
3d8519313e Switched Geocoding library to OpenStreetMap's Nominatim API. Closes #390 2021-08-26 08:56:51 -05:00
f4ecf10885 Allow to copy tweet URLS from tweet displayer dialog 2021-08-25 16:30:37 -05:00
c67b415934 Updated changelog 2021-08-25 12:49:53 -05:00
10511d3022 Improvements to reading tweets with many mentions on them 2021-08-25 11:13:12 -05:00
Oreonan
ddc80a29fd Update french interface translation 2021-08-02 19:30:24 +02:00
9ea36a26d2 Updated changelog with windows 11 keymap 2021-07-31 22:33:32 -05:00
97286496fc fixed changelog in diverged commits 2021-07-31 22:28:46 -05:00
José Manuel Delicado
6436af76f5 Merge pull request #393 from jpavonabian/windows11
Added a fix.
2021-07-28 07:50:38 +02:00
Jesus
576b5064c0 Added a fix. 2021-07-28 07:23:57 +02:00
José Manuel Delicado
342265b3c0 Merge pull request #392 from jpavonabian/windows11
Fixed shortkuts
2021-07-26 13:38:32 +02:00
Jesus
54938ecb6c Fixed shortkuts 2021-07-26 12:47:51 +02:00
José Manuel Delicado
7aff8252d2 Merge pull request #391 from jpavonabian/windows11
Keypad for windows 11
2021-07-24 12:46:43 +02:00
Jesus
9c680130f7 Keypad para windows 11 2021-07-24 06:24:08 +02:00
24d1ad093d Streaming API: Ignore retweets if original tweet is present in a buffer 2021-07-16 10:22:51 -05:00
b2b9cd810f Fixed an issue when indefined keystrokes 2021-07-13 17:53:01 -05:00
582be54dea Added add_alias action to keymaps as undefined 2021-07-13 17:25:48 -05:00
ff0fbeafa3 Modified keystrokeEditor to allow undefined keystrokes 2021-07-13 17:22:20 -05:00
e314cf0599 Allow search users by names in autocomplete users 2021-07-10 05:24:56 -05:00
José Manuel Delicado Alcolea
e7b72112cf Merge branch 'next-gen' of github.com:manuelcortez/TWBlue into next-gen 2021-07-07 08:42:14 +02:00
José Manuel Delicado Alcolea
70c095febe setup.py: patch cx_freeze to include our Microsoft Visual C++ runtime files 2021-07-07 08:41:08 +02:00
6119b029f8 Merge pull request #388 from zstanecic/next-gen
Updated russian interface
2021-07-06 17:13:59 -05:00
b74cd9a73d Ignore undefined actions in keymaps 2021-07-06 17:01:42 -05:00
8ff6809f08 Updated changelog 2021-07-06 16:22:52 -05:00
39af9d8623 Merge pull request #389 from manuelcortez/user_alias
User aliases within TWBlue
2021-07-06 15:58:54 -05:00
3688d7548c Check alias before returning any user object 2021-07-06 13:59:34 -05:00
07f9afb14e Added option to user menu in the menu bar 2021-07-06 13:58:34 -05:00
de12dadac2 Added GUI for user alias addition 2021-07-06 13:58:13 -05:00
877c909482 Added user-aliases section on session configs 2021-07-06 13:56:56 -05:00
José Manuel Delicado Alcolea
1206aba83b Added a few more dependencies to requirements.txt 2021-07-06 20:42:03 +02:00
zstanecic
fcd631b2de Merge remote-tracking branch 'upstream/next-gen' into next-gen 2021-07-06 19:16:47 +02:00
Jose Manuel Delicado
86130954d7 Updated Windows dependencies. Latest versions of NSIS and VLC, among others 2021-07-06 12:39:47 +02:00
Jose Manuel Delicado
23a56c637d Add some indirect dependencies to requirements.txt in order to easily keep them updated 2021-07-05 23:03:40 +02:00
zstanecic
d5ac0db67b updated the russian localization. 2021-07-05 16:04:19 +02:00
bb4869b7be Pushed a new snapshot 2021-07-04 17:05:06 -05:00
44b6e82183 Fix reply dialog issue 2021-07-04 11:50:03 -05:00
Jose Manuel Delicado
5268f166f8 Use Python 3.8.10 for automated builds 2021-07-04 18:43:33 +02:00
Jose Manuel Delicado
37ad6b5fbf paths.py: replace socializer by TW Blue in data_path function definition 2021-07-04 18:32:17 +02:00
Jose Manuel Delicado
bcc72c932d Updated Windows dependencies. Python 3.8.10 2021-07-04 18:26:49 +02:00
b9a9bd03c2 Pushed a new snapshot 2021-07-04 09:47:33 -05:00
e6543bcf77 Allow streaming API support to be disabled from global settings dialog 2021-07-04 09:44:48 -05:00
03b61946f8 Fixed user searches 2021-07-04 09:43:53 -05:00
8fe2f4c64d Exclude muted users from Streaming API 2021-07-04 09:15:04 -05:00
46 changed files with 1385 additions and 1111 deletions

View File

@@ -17,7 +17,7 @@ snapshot32:
- Set-Variable -Name "time" -Value (date -Format "%H:%m") - Set-Variable -Name "time" -Value (date -Format "%H:%m")
- echo ${time} - echo ${time}
- echo "started by ${GITLAB_USER_NAME}" - echo "started by ${GITLAB_USER_NAME}"
- choco install python --version 3.8.7 -y -ForceX86 - choco install python --version 3.8.10 -y -ForceX86
- '&$env:PYTHON -V' - '&$env:PYTHON -V'
- '&$env:PYTHON -m pip install --upgrade pip' - '&$env:PYTHON -m pip install --upgrade pip'
- '&$env:PYTHON -m pip install --upgrade -r requirements.txt' - '&$env:PYTHON -m pip install --upgrade -r requirements.txt'
@@ -54,7 +54,7 @@ snapshot64:
- Set-Variable -Name "time" -Value (date -Format "%H:%m") - Set-Variable -Name "time" -Value (date -Format "%H:%m")
- echo ${time} - echo ${time}
- echo "started by ${GITLAB_USER_NAME}" - echo "started by ${GITLAB_USER_NAME}"
- choco install python --version 3.8.7 -y - choco install python --version 3.8.10 -y
- '&$env:PYTHON -V' - '&$env:PYTHON -V'
- '&$env:PYTHON -m pip install --upgrade pip' - '&$env:PYTHON -m pip install --upgrade pip'
- '&$env:PYTHON -m pip install --upgrade -r requirements.txt' - '&$env:PYTHON -m pip install --upgrade -r requirements.txt'

View File

@@ -2,6 +2,16 @@
## changes in this version ## changes in this version
* TWBlue now closes the VLC player window automatically when a video reaches its end. ([#399](https://github.com/manuelcortez/TWBlue/issues/399))
* After a lot of time, TWBlue now uses a new default Soundpack, called FreakyBlue. This soundpack will be set by default in all new sessions created in the application. Thanks to [Andre Louis](https://twitter.com/FreakyFwoof) for the pack. ([#247](https://github.com/manuelcortez/TWBlue/issues/247))
* 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)) * 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)) * 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)) * 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-oauthlib
requests-toolbelt requests-toolbelt
pypubsub pypubsub
pygeocoder geopy
arrow arrow
python-dateutil python-dateutil
futures futures
@@ -25,6 +25,7 @@ urllib3
youtube-dl youtube-dl
python-vlc python-vlc
pypiwin32 pypiwin32
pywin32
certifi certifi
backports.functools_lru_cache backports.functools_lru_cache
cx_freeze cx_freeze
@@ -32,6 +33,20 @@ tweepy
twitter-text-parser twitter-text-parser
pyenchant pyenchant
sqlitedict sqlitedict
cx-Logging
h11
h2
hpack
hstspreload
httpcore
httpx
hyperframe
rfc3986
sniffio
attrs
importlib-metadata
numpy
pillow
git+https://github.com/accessibleapps/libloader git+https://github.com/accessibleapps/libloader
git+https://github.com/accessibleapps/platform_utils git+https://github.com/accessibleapps/platform_utils
git+https://github.com/accessibleapps/accessible_output2 git+https://github.com/accessibleapps/accessible_output2

View File

@@ -15,10 +15,10 @@ SetCompressor /solid lzma
SetDatablockOptimize on SetDatablockOptimize on
VIAddVersionKey ProductName "TWBlue Snapshot version" VIAddVersionKey ProductName "TWBlue Snapshot version"
VIAddVersionKey LegalCopyright "Copyright 2014-2021 Manuel Cortéz." VIAddVersionKey LegalCopyright "Copyright 2014-2021 Manuel Cortéz."
VIAddVersionKey ProductVersion "7" VIAddVersionKey ProductVersion "11"
VIAddVersionKey FileVersion "7" VIAddVersionKey FileVersion "11"
VIProductVersion "7.0.0.0" VIProductVersion "11.0.0.0"
VIFileVersion "7.0.0.0" VIFileVersion "11.0.0.0"
!insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_WELCOME
!define MUI_LICENSEPAGE_RADIOBUTTONS !define MUI_LICENSEPAGE_RADIOBUTTONS
!insertmacro MUI_PAGE_LICENSE "license.txt" !insertmacro MUI_PAGE_LICENSE "license.txt"
@@ -72,8 +72,8 @@ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "D
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "UninstallString" '"$INSTDIR\uninstall.exe"' WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "7" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "11"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "http://twblue.es" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "https://twblue.es"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 0 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1

View File

@@ -21,7 +21,7 @@ volume = float(default=1.0)
input_device = string(default="Default") input_device = string(default="Default")
output_device = string(default="Default") output_device = string(default="Default")
session_mute = boolean(default=False) session_mute = boolean(default=False)
current_soundpack = string(default="default") current_soundpack = string(default="FreakyBlue")
indicate_audio = boolean(default=True) indicate_audio = boolean(default=True)
indicate_geo = boolean(default=True) indicate_geo = boolean(default=True)
indicate_img = boolean(default=True) indicate_img = boolean(default=True)
@@ -49,3 +49,5 @@ braille_reporting = boolean(default=True)
speech_reporting = boolean(default=True) speech_reporting = boolean(default=True)
[filters] [filters]
[user-aliases]

View File

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

View File

@@ -21,7 +21,7 @@ from audio_services import youtube_utils
from controller.buffers.base import base from controller.buffers.base import base
from sessions.twitter import compose, utils, reduce from sessions.twitter import compose, utils, reduce
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
from tweepy.error import TweepError from tweepy.errors import TweepyException
from tweepy.cursor import Cursor from tweepy.cursor import Cursor
from pubsub import pub from pubsub import pub
from sessions.twitter.long_tweets import twishort, tweets from sessions.twitter.long_tweets import twishort, tweets
@@ -140,7 +140,7 @@ class BaseBuffer(base.Buffer):
try: try:
tweet = self.session.twitter.get_status(id=tweet_id, include_ext_alt_text=True, tweet_mode="extended") tweet = self.session.twitter.get_status(id=tweet_id, include_ext_alt_text=True, tweet_mode="extended")
tweet.full_text = utils.expand_urls(tweet.full_text, tweet.entities) tweet.full_text = utils.expand_urls(tweet.full_text, tweet.entities)
except TweepError as e: except TweepyException as e:
utils.twitter_error(e) utils.twitter_error(e)
return return
if message != None: if message != None:
@@ -151,7 +151,7 @@ class BaseBuffer(base.Buffer):
try: try:
tweet = self.session.twitter.get_status(id=l, include_ext_alt_text=True, tweet_mode="extended") tweet = self.session.twitter.get_status(id=l, include_ext_alt_text=True, tweet_mode="extended")
tweet.full_text = utils.expand_urls(tweet.full_text, tweet.entities) tweet.full_text = utils.expand_urls(tweet.full_text, tweet.entities)
except TweepError as e: except TweepyException as e:
utils.twitter_error(e) utils.twitter_error(e)
return return
l = tweets.is_long(tweet) l = tweets.is_long(tweet)
@@ -191,8 +191,8 @@ class BaseBuffer(base.Buffer):
log.debug("Retrieved %d items from the cursored search on function %s." %(len(val), self.function)) log.debug("Retrieved %d items from the cursored search on function %s." %(len(val), self.function))
user_ids = [item.message_create["sender_id"] for item in val] user_ids = [item.message_create["sender_id"] for item in val]
self.session.save_users(user_ids) self.session.save_users(user_ids)
except TweepError as e: except TweepyException as e:
log.error("Error %s: %s" % (e.api_code, e.reason)) log.exception("Error %s" % (str(e)))
return return
number_of_items = self.session.order_buffer(self.name, val) number_of_items = self.session.order_buffer(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,)) log.debug("Number of items retrieved: %d" % (number_of_items,))
@@ -229,15 +229,15 @@ class BaseBuffer(base.Buffer):
last_id = self.session.db[self.name][-1].id last_id = self.session.db[self.name][-1].id
try: try:
items = getattr(self.session.twitter, self.function)(max_id=last_id, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs) items = getattr(self.session.twitter, self.function)(max_id=last_id, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs)
except TweepError as e: except TweepyException as e:
log.error("Error %s: %s" % (e.api_code, e.reason)) log.exception("Error %s" % (str(e)))
return return
if items == None: if items == None:
return return
items_db = self.session.db[self.name] items_db = self.session.db[self.name]
self.session.add_users_from_results(items) self.session.add_users_from_results(items)
for i in items: for i in items:
if utils.is_allowed(i, self.session.settings, self.name) == True and utils.find_item(i.id, self.session.db[self.name]) == None: if utils.is_allowed(i, self.session.settings, self.name) == True and utils.find_item(i, self.session.db[self.name]) == None:
i = reduce.reduce_tweet(i) i = reduce.reduce_tweet(i)
i = self.session.check_quoted_status(i) i = self.session.check_quoted_status(i)
i = self.session.check_long_tweet(i) i = self.session.check_long_tweet(i)
@@ -611,13 +611,13 @@ class BaseBuffer(base.Buffer):
items = self.session.db[self.name] items = self.session.db[self.name]
try: try:
if self.name == "direct_messages" or self.name == "sent_direct_messages": if self.name == "direct_messages" or self.name == "sent_direct_messages":
self.session.twitter.destroy_direct_message(id=self.get_right_tweet().id) self.session.twitter.delete_direct_message(id=self.get_right_tweet().id)
items.pop(index) items.pop(index)
else: else:
self.session.twitter.destroy_status(id=self.get_right_tweet().id) self.session.twitter.destroy_status(id=self.get_right_tweet().id)
items.pop(index) items.pop(index)
self.buffer.list.remove_item(index) self.buffer.list.remove_item(index)
except TweepError: except TweepyException:
self.session.sound.play("error.ogg") self.session.sound.play("error.ogg")
self.session.db[self.name] = items self.session.db[self.name] = items
@@ -644,8 +644,12 @@ class BaseBuffer(base.Buffer):
original_tweet.text = utils.find_urls_in_text(original_tweet.text, original_tweet.entities) 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"]) 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() 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) 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) webbrowser.open(url)

View File

@@ -10,7 +10,7 @@ import logging
from controller import messages from controller import messages
from sessions.twitter import compose, utils from sessions.twitter import compose, utils
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
from tweepy.error import TweepError from tweepy.errors import TweepyException
from pubsub import pub from pubsub import pub
from . import base from . import base
@@ -40,8 +40,8 @@ class DirectMessagesBuffer(base.BaseBuffer):
results = [i for i in items] results = [i for i in items]
items = results items = results
log.debug("Retrieved %d items for cursored search in function %s" % (len(items), self.function)) log.debug("Retrieved %d items for cursored search in function %s" % (len(items), self.function))
except TweepError as e: except TweepyException as e:
log.error("Error %s: %s" % (e.api_code, e.reason)) log.exception("Error %s" % (str(e)))
return return
if items == None: if items == None:
return return

View File

@@ -24,7 +24,7 @@ class ListBuffer(base.BaseBuffer):
super(ListBuffer, self).start_stream(mandatory, play_sound, avoid_autoreading) super(ListBuffer, self).start_stream(mandatory, play_sound, avoid_autoreading)
def get_user_ids(self): def get_user_ids(self):
for i in Cursor(self.session.twitter.list_members, list_id=self.list_id, include_entities=False, skip_status=True, count=5000).items(): for i in Cursor(self.session.twitter.get_list_members, list_id=self.list_id, include_entities=False, skip_status=True, count=5000).items():
if i.id not in self.users: if i.id not in self.users:
self.users.append(i.id) self.users.append(i.id)

View File

@@ -14,7 +14,7 @@ import output
import config import config
import logging import logging
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
from tweepy.error import TweepError from tweepy.errors import TweepyException
from pubsub import pub from pubsub import pub
from sessions.twitter import compose from sessions.twitter import compose
from . import base from . import base
@@ -125,8 +125,8 @@ class PeopleBuffer(base.BaseBuffer):
val = results val = results
val.reverse() val.reverse()
log.debug("Retrieved %d items from cursored search in function %s" % (len(val), self.function)) log.debug("Retrieved %d items from cursored search in function %s" % (len(val), self.function))
except TweepError as e: except TweepyException as e:
log.error("Error %s: %s" % (e.api_code, e.reason)) log.exception("Error %s" % (str(e)))
return return
number_of_items = self.session.order_people(self.name, val) number_of_items = self.session.order_people(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,)) log.debug("Number of items retrieved: %d" % (number_of_items,))
@@ -155,8 +155,8 @@ class PeopleBuffer(base.BaseBuffer):
results = [i for i in items] results = [i for i in items]
items = results items = results
log.debug("Retrieved %d items from cursored search in function %s" % (len(items), self.function)) log.debug("Retrieved %d items from cursored search in function %s" % (len(items), self.function))
except TweepError as e: except TweepyException as e:
log.error("Error %s: %s" % (e.api_code, e.reason)) log.exception("Error %s" % (str(e)))
return return
if items == None: if items == None:
return return
@@ -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: 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)) 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() tweet = self.get_tweet()
output.speak(_(u"Opening item in web browser..."))
url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name) url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name)
webbrowser.open(url) return url

View File

@@ -13,17 +13,17 @@ import widgetUtils
import output import output
import logging import logging
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
from tweepy.error import TweepError from tweepy.errors import TweepyException
from pubsub import pub from pubsub import pub
from controller.buffers import base from controller.buffers import base
log = logging.getLogger("controller.buffers.twitter.trends") log = logging.getLogger("controller.buffers.twitter.trends")
class TrendsBuffer(base.Buffer): class TrendsBuffer(base.Buffer):
def __init__(self, parent, name, session, account, trendsFor, *args, **kwargs): def __init__(self, parent, name, sessionObject, account, trendsFor, *args, **kwargs):
super(TrendsBuffer, self).__init__(parent=parent, session=session) super(TrendsBuffer, self).__init__(parent=parent, sessionObject=sessionObject)
self.trendsFor = trendsFor self.trendsFor = trendsFor
self.session = session self.session = sessionObject
self.account = account self.account = account
self.invisible = True self.invisible = True
self.buffer = buffers.trendsPanel(parent, name) self.buffer = buffers.trendsPanel(parent, name)
@@ -44,9 +44,9 @@ class TrendsBuffer(base.Buffer):
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True: if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
self.execution_time = current_time self.execution_time = current_time
try: try:
data = self.session.twitter.trends_place(id=self.trendsFor) data = self.session.twitter.get_place_trends(id=self.trendsFor)
except TweepError as err: except TweepyException as err:
log.error("Error %s: %s" % (err.api_code, err.reason)) log.exception("Error %s" % (str(err)))
if not hasattr(self, "name_"): if not hasattr(self, "name_"):
self.name_ = data[0]["locations"][0]["name"] self.name_ = data[0]["locations"][0]["name"]
self.trends = data[0]["trends"] self.trends = data[0]["trends"]

View File

@@ -3,7 +3,7 @@ import widgetUtils
import output import output
import logging import logging
from wxUI.dialogs import lists from wxUI.dialogs import lists
from tweepy.error import TweepError from tweepy.errors import TweepyException
from sessions.twitter import compose, utils from sessions.twitter import compose, utils
from pubsub import pub from pubsub import pub
@@ -49,9 +49,9 @@ class listsController(object):
new_list = self.session.twitter.create_list(name=name, description=description, mode=mode) new_list = self.session.twitter.create_list(name=name, description=description, mode=mode)
self.session.db["lists"].append(new_list) self.session.db["lists"].append(new_list)
self.dialog.lista.insert_item(False, *compose.compose_list(new_list)) self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
except TweepError as e: except TweepyException as e:
output.speak("error %s: %s" % (e.api_code, e.reason)) output.speak("error %s" % (str(e)))
log.exception("error %s: %s" % (e.api_code, e.reason)) log.exception("error %s" % (str(e)))
dialog.destroy() dialog.destroy()
def edit_list(self, *args, **kwargs): def edit_list(self, *args, **kwargs):
@@ -70,8 +70,9 @@ class listsController(object):
self.session.twitter.update_list(list_id=list.id, name=name, description=description, mode=mode) self.session.twitter.update_list(list_id=list.id, name=name, description=description, mode=mode)
self.session.get_lists() self.session.get_lists()
self.dialog.populate_list(self.get_all_lists(), True) self.dialog.populate_list(self.get_all_lists(), True)
except TweepError as e: except TweepyException as e:
output.speak("error %s: %s" % (e.api_code, e.reason)) output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))
dialog.destroy() dialog.destroy()
def remove_list(self, *args, **kwargs): def remove_list(self, *args, **kwargs):
@@ -82,8 +83,9 @@ class listsController(object):
self.session.twitter.destroy_list(list_id=list) self.session.twitter.destroy_list(list_id=list)
self.session.db["lists"].pop(self.dialog.get_item()) self.session.db["lists"].pop(self.dialog.get_item())
self.dialog.lista.remove_item(self.dialog.get_item()) self.dialog.lista.remove_item(self.dialog.get_item())
except TweepError as e: except TweepyException as e:
output.speak("error %s: %s" % (e.api_code, e.reason)) output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))
def open_list_as_buffer(self, *args, **kwargs): def open_list_as_buffer(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return if self.dialog.lista.get_count() == 0: return
@@ -97,8 +99,9 @@ class listsController(object):
list = self.session.twitter.subscribe_list(list_id=list_id) list = self.session.twitter.subscribe_list(list_id=list_id)
item = utils.find_item(list.id, self.session.db["lists"]) item = utils.find_item(list.id, self.session.db["lists"])
self.session.db["lists"].append(list) self.session.db["lists"].append(list)
except TweepError as e: except TweepyException as e:
output.speak("error %s: %s" % (e.api_code, e.reason)) output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))
def unsubscribe(self, *args, **kwargs): def unsubscribe(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return if self.dialog.lista.get_count() == 0: return
@@ -106,5 +109,6 @@ class listsController(object):
try: try:
list = self.session.twitter.unsubscribe_list(list_id=list_id) list = self.session.twitter.unsubscribe_list(list_id=list_id)
self.session.db["lists"].remove(list) self.session.db["lists"].remove(list)
except TweepError as e: except TweepyException as e:
output.speak("error %s: %s" % (e.api_code, e.reason)) output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))

View File

@@ -29,23 +29,22 @@ from sessions.twitter import session as session_
from pubsub import pub from pubsub import pub
import sound import sound
import output import output
from tweepy.error import TweepError from tweepy.errors import TweepyException, Forbidden
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
from mysc.repeating_timer import RepeatingTimer from mysc.repeating_timer import RepeatingTimer
from mysc import restart from mysc import restart
import config import config
import widgetUtils import widgetUtils
import pygeocoder
from pygeolib import GeocoderError
import logging import logging
import webbrowser import webbrowser
from geopy.geocoders import Nominatim
from mysc import localization from mysc import localization
import os import os
import languageHandler import languageHandler
log = logging.getLogger("mainController") log = logging.getLogger("mainController")
geocoder = pygeocoder.Geocoder() geocoder = Nominatim(user_agent="TWBlue")
class Controller(object): class Controller(object):
@@ -132,6 +131,7 @@ class Controller(object):
pub.subscribe(self.manage_unfavourite, "unfavourite") pub.subscribe(self.manage_unfavourite, "unfavourite")
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")
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)
@@ -185,6 +185,7 @@ class Controller(object):
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_changelog, self.view.changelog) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_changelog, self.view.changelog)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_alias, self.view.addAlias)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_list, self.view.addToList) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_list, self.view.addToList)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_list, self.view.removeFromList) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_list, self.view.removeFromList)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_buffer, self.view.update_buffer) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_buffer, self.view.update_buffer)
@@ -292,6 +293,31 @@ class Controller(object):
self.create_buffers(session, False) self.create_buffers(session, False)
self.start_buffers(session) self.start_buffers(session)
def create_buffer(self, buffer_type="baseBuffer", session_type="twitter", buffer_title="", parent_tab=None, start=False, kwargs={}):
log.debug("Creating buffer of type {0} with parent_tab of {2} arguments {1}".format(buffer_type, kwargs, parent_tab))
if not hasattr(buffers, session_type):
raise AttributeError("Session type %s does not exist yet." % (session_type))
available_buffers = getattr(buffers, session_type)
if not hasattr(available_buffers, buffer_type):
raise AttributeError("Specified buffer type does not exist: %s" % (buffer_type,))
buffer = getattr(available_buffers, buffer_type)(**kwargs)
if start:
if kwargs.get("function") == "user_timeline":
try:
buffer.start_stream(play_sound=False)
except ValueError:
commonMessageDialogs.unauthorized()
return
else:
call_threaded(buffer.start_stream)
self.buffers.append(buffer)
if parent_tab == None:
log.debug("Appending buffer {}...".format(buffer,))
self.view.add_buffer(buffer.buffer, buffer_title)
else:
self.view.insert_buffer(buffer.buffer, buffer_title, parent_tab)
log.debug("Inserting buffer {0} into control {1}".format(buffer, parent_tab))
def create_buffers(self, session, createAccounts=True): def create_buffers(self, session, createAccounts=True):
""" Generates buffer objects for an user account. """ Generates buffer objects for an user account.
session SessionObject: a sessionmanager.session.Session Object""" session SessionObject: a sessionmanager.session.Session Object"""
@@ -302,96 +328,54 @@ class Controller(object):
account.setup_account() account.setup_account()
self.buffers.append(account) self.buffers.append(account)
self.view.add_buffer(account.buffer , name=session.db["user_name"]) self.view.add_buffer(account.buffer , name=session.db["user_name"])
root_position =self.view.search(session.db["user_name"], session.db["user_name"])
for i in session.settings['general']['buffer_order']: for i in session.settings['general']['buffer_order']:
if i == 'home': if i == 'home':
home = buffers.twitter.BaseBuffer(self.view.nb, "home_timeline", "home_timeline", session, 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", tweet_mode="extended"))
self.buffers.append(home)
self.view.insert_buffer(home.buffer, name=_(u"Home"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'mentions': elif i == 'mentions':
mentions = buffers.twitter.BaseBuffer(self.view.nb, "mentions_timeline", "mentions", session, 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", tweet_mode="extended"))
self.buffers.append(mentions)
self.view.insert_buffer(mentions.buffer, name=_(u"Mentions"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'dm': elif i == 'dm':
dm = buffers.twitter.DirectMessagesBuffer(self.view.nb, "list_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg") 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"))
self.buffers.append(dm)
self.view.insert_buffer(dm.buffer, name=_(u"Direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'sent_dm': elif i == 'sent_dm':
sent_dm = buffers.twitter.SentDirectMessagesBuffer(self.view.nb, "", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message") 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"))
self.buffers.append(sent_dm)
self.view.insert_buffer(sent_dm.buffer, name=_(u"Sent direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'sent_tweets': elif i == 'sent_tweets':
sent_tweets = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "sent_tweets", session, 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"], tweet_mode="extended"))
self.buffers.append(sent_tweets)
self.view.insert_buffer(sent_tweets.buffer, name=_(u"Sent tweets"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'favorites': elif i == 'favorites':
favourites = buffers.twitter.BaseBuffer(self.view.nb, "favorites", "favourites", session, 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", tweet_mode="extended"))
self.buffers.append(favourites)
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'followers': elif i == 'followers':
followers = buffers.twitter.PeopleBuffer(self.view.nb, "followers", "followers", session, session.db["user_name"], sound="update_followers.ogg", screen_name=session.db["user_name"]) 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"]))
self.buffers.append(followers)
self.view.insert_buffer(followers.buffer, name=_(u"Followers"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'friends': elif i == 'friends':
friends = buffers.twitter.PeopleBuffer(self.view.nb, "friends", "friends", session, session.db["user_name"], screen_name=session.db["user_name"]) pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Following"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_friends", name="friends", sessionObject=session, account=session.db["user_name"], screen_name=session.db["user_name"]))
self.buffers.append(friends)
self.view.insert_buffer(friends.buffer, name=_(u"Friends"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'blocks': elif i == 'blocks':
blocks = buffers.twitter.PeopleBuffer(self.view.nb, "blocks", "blocked", session, session.db["user_name"]) pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Blocked users"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_blocks", name="blocked", sessionObject=session, account=session.db["user_name"]))
self.buffers.append(blocks)
self.view.insert_buffer(blocks.buffer, name=_(u"Blocked users"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'muted': elif i == 'muted':
muted = buffers.twitter.PeopleBuffer(self.view.nb, "mutes", "muted", session, session.db["user_name"]) pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Muted users"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, function="get_mutes", name="muted", sessionObject=session, account=session.db["user_name"]))
self.buffers.append(muted) 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"]))
self.view.insert_buffer(muted.buffer, name=_(u"Muted users"), pos=self.view.search(session.db["user_name"], session.db["user_name"])) timelines_position =self.view.search("timelines", session.db["user_name"])
timelines = buffers.base.EmptyBuffer(self.view.nb, "timelines", session.db["user_name"])
self.buffers.append(timelines)
self.view.insert_buffer(timelines.buffer , name=_(u"Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["timelines"]: for i in session.settings["other_buffers"]["timelines"]:
tl = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "%s-timeline" % (i,), session, 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, tweet_mode="extended"))
self.buffers.append(tl) 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"]))
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(i,), pos=self.view.search("timelines", session.db["user_name"])) favs_timelines_position =self.view.search("favs_timelines", session.db["user_name"])
favs_timelines = buffers.base.EmptyBuffer(self.view.nb, "favs_timelines", session.db["user_name"])
self.buffers.append(favs_timelines)
self.view.insert_buffer(favs_timelines.buffer , name=_(u"Likes timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["favourites_timelines"]: for i in session.settings["other_buffers"]["favourites_timelines"]:
tl = buffers.twitter.BaseBuffer(self.view.nb, "favorites", "%s-favorite" % (i,), session, 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, tweet_mode="extended"))
self.buffers.append(tl) 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"]))
self.view.insert_buffer(tl.buffer, name=_(u"Likes for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"])) followers_timelines_position =self.view.search("followers_timelines", session.db["user_name"])
followers_timelines = buffers.base.EmptyBuffer(self.view.nb, "followers_timelines", session.db["user_name"])
self.buffers.append(followers_timelines)
self.view.insert_buffer(followers_timelines.buffer , name=_(u"Followers' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["followers_timelines"]: for i in session.settings["other_buffers"]["followers_timelines"]:
tl = buffers.twitter.PeopleBuffer(self.view.nb, "followers", "%s-followers" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i) pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Followers for {}").format(i,), parent_tab=followers_timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="get_followers", name="%s-followers" % (i,), sessionObject=session, account=session.db["user_name"], sound="new_event.ogg", user_id=i))
self.buffers.append(tl) pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Following timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=self.view.nb, name="friends_timelines", account=session.db["user_name"]))
self.view.insert_buffer(tl.buffer, name=_(u"Followers for {}").format(i,), pos=self.view.search("followers_timelines", session.db["user_name"])) friends_timelines_position =self.view.search("friends_timelines", session.db["user_name"])
friends_timelines = buffers.base.EmptyBuffer(self.view.nb, "friends_timelines", session.db["user_name"])
self.buffers.append(friends_timelines)
self.view.insert_buffer(friends_timelines.buffer , name=_(u"Friends' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["friends_timelines"]: for i in session.settings["other_buffers"]["friends_timelines"]:
tl = buffers.twitter.PeopleBuffer(self.view.nb, "friends", "%s-friends" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i) pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_(u"Friends for {}").format(i,), parent_tab=friends_timelines_position, start=False, kwargs=dict(parent=self.view.nb, function="get_friends", name="%s-friends" % (i,), sessionObject=session, account=session.db["user_name"], sound="new_event.ogg", user_id=i))
self.buffers.append(tl) 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"]))
self.view.insert_buffer(tl.buffer, name=_(u"Friends for {}").format(i,), pos=self.view.search("friends_timelines", session.db["user_name"])) lists_position =self.view.search("lists", session.db["user_name"])
lists = buffers.base.EmptyBuffer(self.view.nb, "lists", session.db["user_name"])
self.buffers.append(lists)
self.view.insert_buffer(lists.buffer , name=_(u"Lists"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["lists"]: for i in session.settings["other_buffers"]["lists"]:
tl = buffers.twitter.ListBuffer(self.view.nb, "list_timeline", "%s-list" % (i,), session, 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"]), tweet_mode="extended"))
session.lists.append(tl) 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"]))
self.buffers.append(tl) searches_position =self.view.search("searches", session.db["user_name"])
self.view.insert_buffer(tl.buffer, name=_(u"List for {}").format(i), pos=self.view.search("lists", session.db["user_name"]))
searches = buffers.base.EmptyBuffer(self.view.nb, "searches", session.db["user_name"])
self.buffers.append(searches)
self.view.insert_buffer(searches.buffer , name=_(u"Searches"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["tweet_searches"]: for i in session.settings["other_buffers"]["tweet_searches"]:
tl = buffers.twitter.SearchBuffer(self.view.nb, "search", "%s-searchterm" % (i,), session, 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, tweet_mode="extended"))
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Search for {}").format(i), pos=self.view.search("searches", session.db["user_name"]))
for i in session.settings["other_buffers"]["trending_topic_buffers"]: for i in session.settings["other_buffers"]["trending_topic_buffers"]:
buffer = buffers.twitter.TrendsBuffer(self.view.nb, "%s_tt" % (i,), session, session.db["user_name"], i, sound="trends_updated.ogg") 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"))
buffer.start_stream(play_sound=False)
buffer.searchfunction = self.search
self.buffers.append(buffer)
self.view.insert_buffer(buffer.buffer, name=_(u"Trending topics for %s") % (buffer.name_), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
def set_buffer_positions(self, session): def set_buffer_positions(self, session):
"Sets positions for buffers if values exist in the database." "Sets positions for buffers if values exist in the database."
@@ -430,21 +414,18 @@ class Controller(object):
if dlg.get_response() == widgetUtils.OK and dlg.get("term") != "": if dlg.get_response() == widgetUtils.OK and dlg.get("term") != "":
term = dlg.get("term") term = dlg.get("term")
buffer = self.get_best_buffer() buffer = self.get_best_buffer()
searches_position =self.view.search("searches", buffer.session.db["user_name"])
if dlg.get("tweets") == True: if dlg.get("tweets") == True:
if term not in buffer.session.settings["other_buffers"]["tweet_searches"]: if term not in buffer.session.settings["other_buffers"]["tweet_searches"]:
buffer.session.settings["other_buffers"]["tweet_searches"].append(term) buffer.session.settings["other_buffers"]["tweet_searches"].append(term)
buffer.session.settings.write() buffer.session.settings.write()
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()} args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
search = buffers.twitter.SearchBuffer(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, 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, tweet_mode="extended", **args))
else: else:
log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,)) log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,))
return return
elif dlg.get("users") == True: elif dlg.get("users") == True:
search = buffers.twitter.SearchBuffer(self.view.nb, "search_users", "%s-searchUser" % (term,), buffer.session, buffer.session.db["user_name"], bufferType=None, sound="search_updated.ogg", q=term) pub.sendMessage("createBuffer", buffer_type="SearchPeopleBuffer", 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_users", name="%s-searchUser" % (term,), sessionObject=buffer.session, account=buffer.session.db["user_name"], bufferType=None, sound="search_updated.ogg", q=term))
search.start_stream(mandatory=True)
pos=self.view.search("searches", buffer.session.db["user_name"])
self.insert_buffer(search, pos)
self.view.insert_buffer(search.buffer, name=_(u"Search for {}").format(term), pos=pos)
dlg.Destroy() dlg.Destroy()
def find(self, *args, **kwargs): def find(self, *args, **kwargs):
@@ -567,8 +548,9 @@ class Controller(object):
if listBuffer != None: listBuffer.get_user_ids() if listBuffer != None: listBuffer.get_user_ids()
buff.session.db["lists"].pop(older_list) buff.session.db["lists"].pop(older_list)
buff.session.db["lists"].append(list) buff.session.db["lists"].append(list)
except TweepError as e: except TweepyException as e:
output.speak("error %s: %s" % (e.api_code, e.reason)) log.exception("error %s" % (str(e)))
output.speak("error %s" % (str(e)))
def remove_from_list(self, *args, **kwargs): def remove_from_list(self, *args, **kwargs):
buff = self.get_best_buffer() buff = self.get_best_buffer()
@@ -595,8 +577,9 @@ class Controller(object):
if listBuffer != None: listBuffer.get_user_ids() if listBuffer != None: listBuffer.get_user_ids()
buff.session.db["lists"].pop(older_list) buff.session.db["lists"].pop(older_list)
buff.session.db["lists"].append(list) buff.session.db["lists"].append(list)
except TweepError as e: except TweepyException as e:
output.speak("error %s: %s" % (e.api_code, e.reason)) output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))
def list_manager(self, *args, **kwargs): def list_manager(self, *args, **kwargs):
s = self.get_best_buffer().session s = self.get_best_buffer().session
@@ -754,6 +737,26 @@ class Controller(object):
users = utils.get_all_users(tweet, buff.session) users = utils.get_all_users(tweet, buff.session)
u = userActionsController.userActionsController(buff, users, "report") u = userActionsController.userActionsController(buff, users, "report")
def add_alias(self, *args, **kwargs):
buff = self.get_best_buffer()
if not hasattr(buff, "get_right_tweet"): return
tweet = buff.get_right_tweet()
if buff.type == "people":
users = [tweet.screen_name]
elif buff.type == "dm":
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
else:
users = utils.get_all_users(tweet, buff.session)
dlg = dialogs.userAliasDialogs.addAliasDialog(_("Add an user alias"), users)
if dlg.get_response() == widgetUtils.OK:
user, alias = dlg.get_user()
if user == "" or alias == "":
return
user_id = buff.session.get_user_by_screen_name(user)
buff.session.settings["user-aliases"][str(user_id)] = alias
buff.session.settings.write()
output.speak(_("Alias has been set correctly for {}.").format(user))
def post_tweet(self, event=None): def post_tweet(self, event=None):
buffer = self.get_best_buffer() buffer = self.get_best_buffer()
buffer.post_status() buffer.post_status()
@@ -810,7 +813,7 @@ class Controller(object):
return return
elif buffer.type == "baseBuffer" or buffer.type == "favourites_timeline" or buffer.type == "list" or buffer.type == "search": elif buffer.type == "baseBuffer" or buffer.type == "favourites_timeline" or buffer.type == "list" or buffer.type == "search":
tweet, tweetsList = buffer.get_full_tweet() 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": elif buffer.type == "dm":
non_tweet = buffer.get_formatted_message() non_tweet = buffer.get_formatted_message()
item = buffer.get_right_tweet() item = buffer.get_right_tweet()
@@ -818,8 +821,11 @@ class Controller(object):
date = original_date.shift(seconds=buffer.session.db["utc_offset"]).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage()) 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) msg = messages.viewTweet(non_tweet, [], False, date=date)
else: else:
item_url = ""
if hasattr(buffer, "get_item_url"):
item_url = buffer.get_item_url()
non_tweet = buffer.get_formatted_message() 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): def open_in_browser(self, *args, **kwargs):
buffer = self.get_current_buffer() buffer = self.get_current_buffer()
@@ -877,7 +883,7 @@ class Controller(object):
if usr.id_str in buff.session.settings["other_buffers"]["favourites_timelines"]: if usr.id_str in buff.session.settings["other_buffers"]["favourites_timelines"]:
commonMessageDialogs.timeline_exist() commonMessageDialogs.timeline_exist()
return return
tl = buffers.twitter.BaseBuffer(self.view.nb, "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, tweet_mode="extended")
try: try:
tl.start_stream(play_sound=False) tl.start_stream(play_sound=False)
except ValueError: except ValueError:
@@ -896,7 +902,7 @@ class Controller(object):
if usr.id_str in buff.session.settings["other_buffers"]["followers_timelines"]: if usr.id_str in buff.session.settings["other_buffers"]["followers_timelines"]:
commonMessageDialogs.timeline_exist() commonMessageDialogs.timeline_exist()
return return
tl = buffers.twitter.PeopleBuffer(self.view.nb, "followers", "%s-followers" % (usr.id_str,), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr.id_str) tl = buffers.twitter.PeopleBuffer(self.view.nb, "get_followers", "%s-followers" % (usr.id_str,), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr.id_str)
try: try:
tl.start_stream(play_sound=False) tl.start_stream(play_sound=False)
except ValueError: except ValueError:
@@ -915,7 +921,7 @@ class Controller(object):
if usr.id_str in buff.session.settings["other_buffers"]["friends_timelines"]: if usr.id_str in buff.session.settings["other_buffers"]["friends_timelines"]:
commonMessageDialogs.timeline_exist() commonMessageDialogs.timeline_exist()
return return
tl = buffers.twitter.PeopleBuffer(self.view.nb, "friends", "%s-friends" % (usr.id_str,), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr.id_str) tl = buffers.twitter.PeopleBuffer(self.view.nb, "get_friends", "%s-friends" % (usr.id_str,), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr.id_str)
try: try:
tl.start_stream(play_sound=False) tl.start_stream(play_sound=False)
except ValueError: except ValueError:
@@ -935,7 +941,7 @@ class Controller(object):
buffer = self.get_current_buffer() buffer = self.get_current_buffer()
id = buffer.get_right_tweet().id id = buffer.get_right_tweet().id
user = buffer.session.get_user(buffer.get_right_tweet().user).screen_name user = buffer.session.get_user(buffer.get_right_tweet().user).screen_name
search = buffers.twitter.ConversationBuffer(self.view.nb, "search", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,)) search = buffers.twitter.ConversationBuffer(self.view.nb, "search_tweets", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,))
search.tweet = buffer.get_right_tweet() search.tweet = buffer.get_right_tweet()
search.start_stream(start=True) search.start_stream(start=True)
pos=self.view.search("searches", buffer.session.db["user_name"]) pos=self.view.search("searches", buffer.session.db["user_name"])
@@ -977,19 +983,17 @@ class Controller(object):
if tweet.coordinates != None: if tweet.coordinates != None:
x = tweet.coordinates["coordinates"][0] x = tweet.coordinates["coordinates"][0]
y = tweet.coordinates["coordinates"][1] y = tweet.coordinates["coordinates"][1]
address = geocoder.reverse_geocode(y, x, language = languageHandler.curLang) address = geocoder.reverse("{}, {}".format(y, x), language = languageHandler.curLang)
if event == None: output.speak(address[0].__str__()) if event == None: output.speak(address.address)
else: self.view.show_address(address[0].__str__()) else: self.view.show_address(address.address)
else: else:
output.speak(_(u"There are no coordinates in this tweet")) 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: except ValueError:
output.speak(_(u"Error decoding coordinates. Try again later.")) output.speak(_(u"Error decoding coordinates. Try again later."))
except KeyError: # except KeyError:
pass # pass
except AttributeError: except AttributeError:
pass output.speak(_("Unable to find address in OpenStreetMap."))
def view_reverse_geocode(self, event=None): def view_reverse_geocode(self, event=None):
try: try:
@@ -1244,14 +1248,17 @@ class Controller(object):
keymap = {} keymap = {}
for i in config.keymap["keymap"]: for i in config.keymap["keymap"]:
if hasattr(self, i): if hasattr(self, i):
keymap[config.keymap["keymap"][i]] = getattr(self, i) if config.keymap["keymap"][i] != "":
keymap[config.keymap["keymap"][i]] = getattr(self, i)
return keymap return keymap
def register_invisible_keyboard_shorcuts(self, keymap): def register_invisible_keyboard_shorcuts(self, keymap):
if config.changed_keymap: if config.changed_keymap:
commonMessageDialogs.changed_keymap() commonMessageDialogs.changed_keymap()
# Make sure we pass a keymap without undefined keystrokes.
new_keymap = {key: keymap[key] for key in keymap.keys() if keymap[key] != ""}
self.keyboard_handler = WXKeyboardHandler(self.view) self.keyboard_handler = WXKeyboardHandler(self.view)
self.keyboard_handler.register_keys(keymap) self.keyboard_handler.register_keys(new_keymap)
def unregister_invisible_keyboard_shorcuts(self, keymap): def unregister_invisible_keyboard_shorcuts(self, keymap):
try: try:
@@ -1336,11 +1343,10 @@ class Controller(object):
i.start_stream() i.start_stream()
else: else:
i.start_stream(play_sound=False) i.start_stream(play_sound=False)
except TweepError as err: except TweepyException as err:
log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r due to the following reason: %s" % (err.api_code, i.name, i.account, i.args, i.kwargs, err.reason)) log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r." % (str(err), i.name, i.account, i.args, i.kwargs))
# Determine if this error was caused by a block applied to the current user (IE permission errors). # Determine if this error was caused by a block applied to the current user (IE permission errors).
errors_allowed = [130] if type(err) == Forbidden:
if (err.api_code != None and err.api_code not in errors_allowed) or (err.api_code == None and 'Not authorized' in err.reason): # A twitter error, so safely try to remove the buffer.
buff = self.view.search(i.name, i.account) buff = self.view.search(i.name, i.account)
i.remove_buffer(force=True) i.remove_buffer(force=True)
commonMessageDialogs.blocked_timeline() commonMessageDialogs.blocked_timeline()
@@ -1364,34 +1370,34 @@ class Controller(object):
try: try:
if sessions.sessions[i].is_logged == False: continue if sessions.sessions[i].is_logged == False: continue
sessions.sessions[i].check_connection() sessions.sessions[i].check_connection()
except TweepError: # We shouldn't allow this function to die. except TweepyException: # We shouldn't allow this function to die.
pass pass
def create_new_buffer(self, buffer, account, create): def create_new_buffer(self, buffer, account, create):
buff = self.search_buffer("home_timeline", account) buff = self.search_buffer("home_timeline", account)
if create == True: if create == True:
if buffer == "favourites": if buffer == "favourites":
favourites = buffers.twitter.BaseBuffer(self.view.nb, "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"], tweet_mode="extended")
self.buffers.append(favourites) 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"])) 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) favourites.start_stream(play_sound=False)
if buffer == "followers": if buffer == "followers":
followers = buffers.twitter.PeopleBuffer(self.view.nb, "followers", "followers", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"]) followers = buffers.twitter.PeopleBuffer(self.view.nb, "get_followers", "followers", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
self.buffers.append(followers) self.buffers.append(followers)
self.view.insert_buffer(followers.buffer, name=_(u"Followers"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"])) self.view.insert_buffer(followers.buffer, name=_(u"Followers"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
followers.start_stream(play_sound=False) followers.start_stream(play_sound=False)
elif buffer == "friends": elif buffer == "friends":
friends = buffers.twitter.PeopleBuffer(self.view.nb, "friends", "friends", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"]) friends = buffers.twitter.PeopleBuffer(self.view.nb, "get_friends", "friends", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
self.buffers.append(friends) self.buffers.append(friends)
self.view.insert_buffer(friends.buffer, name=_(u"Friends"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"])) self.view.insert_buffer(friends.buffer, name=_(u"Friends"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
friends.start_stream(play_sound=False) friends.start_stream(play_sound=False)
elif buffer == "blocked": elif buffer == "blocked":
blocks = buffers.twitter.PeopleBuffer(self.view.nb, "blocks", "blocked", buff.session, buff.session.db["user_name"]) blocks = buffers.twitter.PeopleBuffer(self.view.nb, "get_blocks", "blocked", buff.session, buff.session.db["user_name"])
self.buffers.append(blocks) self.buffers.append(blocks)
self.view.insert_buffer(blocks.buffer, name=_(u"Blocked users"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"])) self.view.insert_buffer(blocks.buffer, name=_(u"Blocked users"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
blocks.start_stream(play_sound=False) blocks.start_stream(play_sound=False)
elif buffer == "muted": elif buffer == "muted":
muted = buffers.twitter.PeopleBuffer(self.view.nb, "mutes", "muted", buff.session, buff.session.db["user_name"]) muted = buffers.twitter.PeopleBuffer(self.view.nb, "get_mutes", "muted", buff.session, buff.session.db["user_name"])
self.buffers.append(muted) self.buffers.append(muted)
self.view.insert_buffer(muted.buffer, name=_(u"Muted users"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"])) self.view.insert_buffer(muted.buffer, name=_(u"Muted users"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
muted.start_stream(play_sound=False) muted.start_stream(play_sound=False)
@@ -1542,11 +1548,10 @@ class Controller(object):
if i.session != None and i.session.is_logged == True: if i.session != None and i.session.is_logged == True:
try: try:
i.start_stream(mandatory=True) i.start_stream(mandatory=True)
except TweepError as err: except TweepyException as err:
log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r due to the following reason: %s" % (err.api_code, i.name, i.account, i.args, i.kwargs, err.reason)) log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r." % (str(err), i.name, i.account, i.args, i.kwargs))
# Determine if this error was caused by a block applied to the current user (IE permission errors). # Determine if this error was caused by a block applied to the current user (IE permission errors).
errors_allowed = [130] if type(err) == Forbidden:
if (err.api_code != None and err.api_code not in errors_allowed) or (err.api_code == None and 'Not authorized' in err.reason): # A twitter error, so safely try to remove the buffer.
buff = self.view.search(i.name, i.account) buff = self.view.search(i.name, i.account)
i.remove_buffer(force=True) i.remove_buffer(force=True)
commonMessageDialogs.blocked_timeline() commonMessageDialogs.blocked_timeline()
@@ -1652,5 +1657,5 @@ class Controller(object):
try: try:
if sessions.sessions[i].is_logged == False: continue if sessions.sessions[i].is_logged == False: continue
sessions.sessions[i].check_streams() sessions.sessions[i].check_streams()
except TweepError: # We shouldn't allow this function to die. except TweepyException: # We shouldn't allow this function to die.
pass pass

View File

@@ -102,7 +102,7 @@ class basicTweet(object):
else: else:
self.message.disable_button("shortenButton") self.message.disable_button("shortenButton")
self.message.disable_button("unshortenButton") 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() text = self.message.get_text()
results = parse_tweet(text) results = parse_tweet(text)
self.message.set_title(_(u"%s - %s of %d characters") % (self.title, results.weightedLength, self.max)) 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") c.show_menu("dm")
class viewTweet(basicTweet): 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. """ 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 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. 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 text = tweet
self.message = message.viewNonTweet(text, date) self.message = message.viewNonTweet(text, date)
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck) 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) widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
if self.contain_urls() == True: if self.contain_urls() == True:
self.message.enable_button("unshortenButton") self.message.enable_button("unshortenButton")
@@ -290,3 +294,8 @@ class viewTweet(basicTweet):
if "https://twitter.com/" in i: if "https://twitter.com/" in i:
text = text.replace(i, "\n") text = text.replace(i, "\n")
return text 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

@@ -8,7 +8,7 @@ class trendingTopicsController(object):
self.countries = {} self.countries = {}
self.cities = {} self.cities = {}
self.dialog = trends.trendingTopicsDialog() self.dialog = trends.trendingTopicsDialog()
self.information = session.twitter.trends_available() self.information = session.twitter.available_trends()
self.split_information() self.split_information()
widgetUtils.connect_event(self.dialog.country, widgetUtils.RADIOBUTTON, self.get_places) widgetUtils.connect_event(self.dialog.country, widgetUtils.RADIOBUTTON, self.get_places)
widgetUtils.connect_event(self.dialog.city, widgetUtils.RADIOBUTTON, self.get_places) widgetUtils.connect_event(self.dialog.city, widgetUtils.RADIOBUTTON, self.get_places)

View File

@@ -6,7 +6,7 @@ import output
from wxUI.dialogs import update_profile, show_user from wxUI.dialogs import update_profile, show_user
import logging import logging
log = logging.getLogger("controller.user") log = logging.getLogger("controller.user")
from tweepy.error import TweepError from tweepy.errors import TweepyException, Forbidden, NotFound
from sessions.twitter import utils from sessions.twitter import utils
class profileController(object): class profileController(object):
@@ -24,12 +24,12 @@ class profileController(object):
else: else:
try: try:
self.get_data(screen_name=self.user) self.get_data(screen_name=self.user)
except TweepError as err: except TweepyException as err:
if err.api_code == 50: if type(err) == NotFound:
wx.MessageDialog(None, _(u"That user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal() wx.MessageDialog(None, _(u"That user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
if err.api_code == 63: if type(err) == Forbidden:
wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal() wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal()
log.error("error %d: %s" % (err.api_code, err.reason)) log.error("error %s" % (str(err)))
return return
self.dialog = show_user.showUserProfile() self.dialog = show_user.showUserProfile()
string = self.get_user_info() string = self.get_user_info()
@@ -44,7 +44,7 @@ class profileController(object):
def get_data(self, screen_name): def get_data(self, screen_name):
self.data = self.session.twitter.get_user(screen_name=screen_name) self.data = self.session.twitter.get_user(screen_name=screen_name)
if screen_name != self.session.db["user_name"]: if screen_name != self.session.db["user_name"]:
self.friendship_status = self.session.twitter.show_friendship(source_screen_name=self.session.db["user_name"], target_screen_name=screen_name) self.friendship_status = self.session.twitter.get_friendship(source_screen_name=self.session.db["user_name"], target_screen_name=screen_name)
def fill_profile_fields(self): def fill_profile_fields(self):
self.dialog.set_name(self.data.name) self.dialog.set_name(self.data.name)
@@ -83,12 +83,12 @@ class profileController(object):
if self.file != None: if self.file != None:
try: try:
self.session.twitter.update_profile_image(image=self.file) self.session.twitter.update_profile_image(image=self.file)
except TweepError as e: except TweepyException as e:
output.speak(u"Error %s. %s" % (e.api_code, e.reason)) output.speak(u"Error %s" % (str(e)))
try: try:
self.session.twitter.update_profile(name=name, description=description, location=location, url=url) self.session.twitter.update_profile(name=name, description=description, location=location, url=url)
except TweepError as e: except TweepyException as e:
output.speak(u"Error %s. %s" % (e.api_code, e.reason)) output.speak(u"Error %s." % (str(e)))
def get_user_info(self): def get_user_info(self):
string = u"" string = u""

View File

@@ -3,7 +3,7 @@ import widgetUtils
import output import output
from wxUI.dialogs import userActions from wxUI.dialogs import userActions
from pubsub import pub from pubsub import pub
from tweepy.error import TweepError from tweepy.errors import TweepyException
from extra import autocompletionUsers from extra import autocompletionUsers
class userActionsController(object): class userActionsController(object):
@@ -29,44 +29,44 @@ class userActionsController(object):
def follow(self, user): def follow(self, user):
try: try:
self.session.twitter.create_friendship(screen_name=user ) self.session.twitter.create_friendship(screen_name=user )
except TweepError as err: except TweepyException as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True) output.speak("Error %s" % (str(err)), True)
def unfollow(self, user): def unfollow(self, user):
try: try:
id = self.session.twitter.destroy_friendship(screen_name=user ) id = self.session.twitter.destroy_friendship(screen_name=user )
except TweepError as err: except TweepyException as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True) output.speak("Error %s" % (str(err)), True)
def mute(self, user): def mute(self, user):
try: try:
id = self.session.twitter.create_mute(screen_name=user ) id = self.session.twitter.create_mute(screen_name=user )
except TweepError as err: except TweepyException as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True) output.speak("Error %s" % (str(err)), True)
def unmute(self, user): def unmute(self, user):
try: try:
id = self.session.twitter.destroy_mute(screen_name=user ) id = self.session.twitter.destroy_mute(screen_name=user )
except TweepError as err: except TweepyException as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True) output.speak("Error %s" % (str(err)), True)
def report(self, user): def report(self, user):
try: try:
id = self.session.twitter.report_spam(screen_name=user ) id = self.session.twitter.report_spam(screen_name=user )
except TweepError as err: except TweepyException as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True) output.speak("Error %s" % (str(err)), True)
def block(self, user): def block(self, user):
try: try:
id = self.session.twitter.create_block(screen_name=user ) id = self.session.twitter.create_block(screen_name=user )
except TweepError as err: except TweepyException as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True) output.speak("Error %s" % (str(err)), True)
def unblock(self, user): def unblock(self, user):
try: try:
id = self.session.twitter.destroy_block(screen_name=user ) id = self.session.twitter.destroy_block(screen_name=user )
except TweepError as err: except TweepyException as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True) output.speak("Error %s" % (str(err)), True)
def ignore_client(self, user): def ignore_client(self, user):
tweet = self.buffer.get_right_tweet() tweet = self.buffer.get_right_tweet()

View File

@@ -21,7 +21,7 @@ class storage(object):
return self.cursor.fetchall() return self.cursor.fetchall()
def get_users(self, term): def get_users(self, term):
self.cursor.execute("""SELECT * FROM users WHERE user LIKE ?""", ('{}%'.format(term),)) self.cursor.execute("""SELECT * FROM users WHERE UPPER(user) LIKE :term OR UPPER(name) LIKE :term""", {"term": "%{}%".format(term.upper())})
return self.cursor.fetchall() return self.cursor.fetchall()
def set_user(self, screen_name, user_name, from_a_buffer): def set_user(self, screen_name, user_name, from_a_buffer):

View File

@@ -34,4 +34,5 @@ configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o") accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u") update_buffer = string(default="control+win+shift+u")
ocr_image = string(default="win+alt+o") ocr_image = string(default="win+alt+o")
open_in_browser = string(default="alt+control+win+return") open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")

View File

@@ -53,4 +53,5 @@ configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o") accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u") update_buffer = string(default="control+win+shift+u")
ocr_image = string(default="win+alt+o") ocr_image = string(default="win+alt+o")
open_in_browser = string(default="alt+control+win+return") open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")

View File

@@ -54,4 +54,5 @@ configuration = string(default="control+win+alt+o")
accountConfiguration = string(default="control+win+shift+o") accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+alt+shift+u") update_buffer = string(default="control+alt+shift+u")
ocr_image = string(default="win+alt+o") ocr_image = string(default="win+alt+o")
open_in_browser = string(default="alt+control+win+return") open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")

View File

@@ -0,0 +1,58 @@
[info]
name = string(default="Windows 11")
desc = string(default="A keymap with remapped modifiers for Windows 11 compatibility.")
author = string(default="Bill Jesús <galorasd@gmail.com>")
[keymap]
up = string(default="control+alt+win+up")
down = string(default="control+alt+win+down")
left = string(default="control+alt+win+left")
right = string(default="control+alt+win+right")
next_account = string(default="control+alt+win+shift+right")
previous_account = string(default="control+alt+win+shift+left")
open_conversation = string(default="control+alt+win+c")
show_hide = string(default="control+win+w")
post_tweet = string(default="alt+win+n")
post_reply = string(default="control+win+r")
post_retweet = string(default="alt+win+shift+r")
send_dm = string(default="alt+win+shift+d")
toggle_like = string(default="control+alt+win+f")
follow = string(default="alt+win+shift+s")
user_details = string(default="alt+win+shift+n")
view_item = string(default="alt+win+v")
exit = string(default="alt+win+f4")
open_timeline = string(default="alt+win+i")
remove_buffer = string(default="alt+win+shift+i")
url = string(default="alt+win+return")
audio = string(default="alt+shift+win+return")
volume_up = string(default="control+alt+win+shift+up")
go_home = string(default="control+alt+win+home")
volume_down = string(default="control+alt+win+shift+down")
go_end = string(default="control+alt+win+end")
go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="alt+win+p")
delete = string(default="alt+win+delete")
clear_buffer = string(default="alt+win+shift+delete")
repeat_item = string(default="control+alt+win+space")
copy_to_clipboard = string(default="alt+win+shift+c")
add_to_list = string(default="alt+win+a")
remove_from_list = string(default="alt+win+shift+a")
toggle_buffer_mute = string(default="alt+win+shift+m")
toggle_session_mute = string(default="control+alt+win+m")
toggle_autoread = string(default="alt+win+e")
search = string(default="alt+win+-")
edit_keystrokes = string(default="alt+win+k")
view_user_lists = string(default="alt+win+l")
get_more_items = string(default="alt+win+pageup")
reverse_geocode = string(default="control+win+g")
view_reverse_geocode = string(default="alt+win+shift+g")
get_trending_topics = string(default="control+win+t")
check_for_updates = string(default="alt+win+u")
list_manager = string(default="alt+win+shift+l")
configuration = string(default="control+win+alt+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+alt+shift+u")
ocr_image = string(default="win+alt+o")
open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")

View File

@@ -55,4 +55,5 @@ list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o") configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o") accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u") update_buffer = string(default="control+win+shift+u")
open_in_browser = string(default="alt+control+win+return") open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")

View File

@@ -56,4 +56,5 @@ configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o") accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u") update_buffer = string(default="control+win+shift+u")
ocr_image = string(default="win+alt+o") ocr_image = string(default="win+alt+o")
open_in_browser = string(default="alt+control+win+return") open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")

View File

@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals
actions = { actions = {
"up": _(u"Go up in the current buffer"), "up": _(u"Go up in the current buffer"),
"down": _(u"Go down in the current buffer"), "down": _(u"Go down in the current buffer"),
@@ -57,4 +56,6 @@ actions = {
"audio": _(u"Try to play an audio file"), "audio": _(u"Try to play an audio file"),
"update_buffer": _(u"Updates the buffer and retrieves possible lost items there."), "update_buffer": _(u"Updates the buffer and retrieves possible lost items there."),
"ocr_image": _(u"Extracts the text from a picture and displays the result in a dialog."), "ocr_image": _(u"Extracts the text from a picture and displays the result in a dialog."),
"add_alias": _("Adds an alias to an user"),
} }

View File

@@ -1,7 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import object
import widgetUtils import widgetUtils
import config import config
from . import wx_ui from . import wx_ui
@@ -18,6 +15,7 @@ class KeystrokeEditor(object):
self.hold_map = self.map.copy() self.hold_map = self.map.copy()
self.dialog.put_keystrokes(constants.actions, self.map) self.dialog.put_keystrokes(constants.actions, self.map)
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_keystroke) widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_keystroke)
widgetUtils.connect_event(self.dialog.undefine, widgetUtils.BUTTON_PRESSED, self.undefine_keystroke)
widgetUtils.connect_event(self.dialog.execute, widgetUtils.BUTTON_PRESSED, self.execute_action) widgetUtils.connect_event(self.dialog.execute, widgetUtils.BUTTON_PRESSED, self.execute_action)
self.dialog.get_response() self.dialog.get_response()
@@ -33,6 +31,17 @@ class KeystrokeEditor(object):
self.map[action] = new_keystroke self.map[action] = new_keystroke
self.dialog.put_keystrokes(constants.actions, self.map) self.dialog.put_keystrokes(constants.actions, self.map)
def undefine_keystroke(self, *args, **kwargs):
action = self.dialog.actions[self.dialog.get_action()]
keystroke = self.map.get(action)
if keystroke == None:
return
answer = self.dialog.undefine_keystroke_confirmation()
if answer == widgetUtils.YES:
self.map[action] = ""
self.changed = True
self.dialog.put_keystrokes(constants.actions, self.map)
def set_keystroke(self, keystroke, dialog): def set_keystroke(self, keystroke, dialog):
for i in keystroke.split("+"): for i in keystroke.split("+"):
if hasattr(dialog, i): if hasattr(dialog, i):

View File

@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals
import wx import wx
from multiplatform_widgets import widgets from multiplatform_widgets import widgets
from wxUI.dialogs import baseDialog from wxUI.dialogs import baseDialog
@@ -18,6 +17,7 @@ class keystrokeEditorDialog(baseDialog.BaseWXDialog):
firstSizer.Add(self.keys.list, 0, wx.ALL, 5) firstSizer.Add(self.keys.list, 0, wx.ALL, 5)
self.edit = wx.Button(panel, -1, _(u"Edit")) self.edit = wx.Button(panel, -1, _(u"Edit"))
self.edit.SetDefault() self.edit.SetDefault()
self.undefine = wx.Button(panel, -1, _("Undefine keystroke"))
self.execute = wx.Button(panel, -1, _(u"Execute action")) self.execute = wx.Button(panel, -1, _(u"Execute action"))
close = wx.Button(panel, wx.ID_CANCEL, _(u"Close")) close = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
secondSizer = wx.BoxSizer(wx.HORIZONTAL) secondSizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -37,13 +37,18 @@ class keystrokeEditorDialog(baseDialog.BaseWXDialog):
continue continue
action = actions[i] action = actions[i]
self.actions.append(i) self.actions.append(i)
keystroke = keystrokes[i] keystroke = keystrokes.get(i)
if keystroke == "":
keystroke = _("Undefined")
self.keys.insert_item(False, *[action, keystroke]) self.keys.insert_item(False, *[action, keystroke])
self.keys.select_item(selection) self.keys.select_item(selection)
def get_action(self): def get_action(self):
return self.keys.get_selected() return self.keys.get_selected()
def undefine_keystroke_confirmation(self):
return wx.MessageDialog(self, _("Are you sure you want to undefine this keystroke?"), _("Undefine keystroke"), wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION).ShowModal()
class editKeystrokeDialog(baseDialog.BaseWXDialog): class editKeystrokeDialog(baseDialog.BaseWXDialog):
def __init__(self): def __init__(self):
super(editKeystrokeDialog, self).__init__(parent=None, id=-1, title=_(u"Editing keystroke")) super(editKeystrokeDialog, self).__init__(parent=None, id=-1, title=_(u"Editing keystroke"))

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TW Blue 0.85\n" "Project-Id-Version: TW Blue 0.85\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n" "POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2020-10-23 14:30+0300\n" "PO-Revision-Date: 2021-07-05 16:03+0200\n"
"Last-Translator: Artem Plaksin <admin@maniyax.ru>\n" "Last-Translator: Artem Plaksin <admin@maniyax.ru>\n"
"Language-Team: Alexander Jaszyn <a.jaszyn@ya.ru>\n" "Language-Team: Alexander Jaszyn <a.jaszyn@ya.ru>\n"
"Language: ru\n" "Language: ru\n"
@@ -14,7 +14,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 1.8.8\n" "X-Generator: Poedit 3.0\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-SourceCharset: UTF-8\n"
@@ -208,14 +208,12 @@ msgstr ""
"личных сообщений вместо этого." "личных сообщений вместо этого."
#: ../src\controller\buffers\twitterBuffers.py:983 #: ../src\controller\buffers\twitterBuffers.py:983
#, fuzzy
msgid "{0} new followers." msgid "{0} new followers."
msgstr "Новый читатель." msgstr "{0} новых читателей."
#: ../src\controller\buffers\twitterBuffers.py:1266 #: ../src\controller\buffers\twitterBuffers.py:1266
#, fuzzy
msgid "This action is not supported in the buffer, yet." msgid "This action is not supported in the buffer, yet."
msgstr "Это действие не поддерживается в данном буфере" msgstr "Это действие пока не поддерживается в буфере."
#: ../src\controller\mainController.py:273 #: ../src\controller\mainController.py:273
msgid "Ready" msgid "Ready"
@@ -314,9 +312,8 @@ msgid "Select the user"
msgstr "Выберите пользователя" msgstr "Выберите пользователя"
#: ../src\controller\mainController.py:809 ../src\controller\messages.py:236 #: ../src\controller\mainController.py:809 ../src\controller\messages.py:236
#, fuzzy
msgid "MMM D, YYYY. H:m" msgid "MMM D, YYYY. H:m"
msgstr "dddd, MMMM D, YYYY H:m:s" msgstr "MMM D, YYYY. H:m"
#: ../src\controller\mainController.py:934 #: ../src\controller\mainController.py:934
msgid "Conversation with {0}" msgid "Conversation with {0}"
@@ -1702,9 +1699,8 @@ msgid "Opens the global settings dialogue"
msgstr "Открыть основные настройки" msgstr "Открыть основные настройки"
#: ../src\keystrokeEditor\constants.py:54 #: ../src\keystrokeEditor\constants.py:54
#, fuzzy
msgid "Opens the list manager" msgid "Opens the list manager"
msgstr "Менеджер Списков" msgstr "Открывает менеджер списков"
#: ../src\keystrokeEditor\constants.py:55 #: ../src\keystrokeEditor\constants.py:55
msgid "Opens the account settings dialogue" msgid "Opens the account settings dialogue"

View File

@@ -39,7 +39,7 @@ def logs_path():
os.mkdir(path) os.mkdir(path)
return path return path
def data_path(app_name='socializer'): def data_path(app_name='TW Blue'):
if platform.system() == "Windows": if platform.system() == "Windows":
data_path = os.path.join(os.getenv("AppData"), app_name) data_path = os.path.join(os.getenv("AppData"), app_name)
else: else:

View File

@@ -17,7 +17,7 @@ from sessions.twitter import session
from . import manager from . import manager
import config_utils import config_utils
import config import config
from tweepy.error import TweepError from tweepy.errors import TweepyException
log = logging.getLogger("sessionmanager.sessionManager") log = logging.getLogger("sessionmanager.sessionManager")
class sessionManagerController(object): class sessionManagerController(object):
@@ -83,7 +83,7 @@ class sessionManagerController(object):
if i not in config.app["sessions"]["ignored_sessions"]: if i not in config.app["sessions"]["ignored_sessions"]:
try: try:
s.login() s.login()
except TweepError: except TweepyException:
self.show_auth_error(s.settings["twitter"]["user_name"]) self.show_auth_error(s.settings["twitter"]["user_name"])
continue continue
sessions.sessions[i] = s sessions.sessions[i] = s

View File

@@ -43,6 +43,10 @@ class baseSession(object):
self.logged = False self.logged = False
self.settings = None self.settings = None
self.db={} self.db={}
# Config specification file.
self.config_spec = "conf.defaults"
# Session type.
self.type = "base"
@property @property
def is_logged(self): def is_logged(self):
@@ -52,7 +56,7 @@ class baseSession(object):
""" Get settings for a session.""" """ Get settings for a session."""
file_ = "%s/session.conf" % (self.session_id,) file_ = "%s/session.conf" % (self.session_id,)
log.debug("Creating config file %s" % (file_,)) 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.init_sound()
self.load_persistent_data() self.load_persistent_data()

View File

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

View File

@@ -10,7 +10,7 @@ import output
import application import application
from pubsub import pub from pubsub import pub
import tweepy import tweepy
from tweepy.error import TweepError from tweepy.errors import TweepyException, Forbidden, NotFound
from tweepy.models import User as UserModel from tweepy.models import User as UserModel
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
from keys import keyring from keys import keyring
@@ -51,7 +51,7 @@ class Session(base.baseSession):
if i.id < last_id: if i.id < last_id:
log.error("Ignoring an older tweet... Last id: {0}, tweet id: {1}".format(last_id, i.id)) log.error("Ignoring an older tweet... Last id: {0}, tweet id: {1}".format(last_id, i.id))
continue continue
if utils.find_item(i.id, self.db[name]) == None and utils.is_allowed(i, self.settings, name) == True: if utils.find_item(i, self.db[name]) == None and utils.is_allowed(i, self.settings, name) == True:
if i == False: continue if i == False: continue
reduced_object = reduce.reduce_tweet(i) reduced_object = reduce.reduce_tweet(i)
reduced_object = self.check_quoted_status(reduced_object) reduced_object = self.check_quoted_status(reduced_object)
@@ -72,7 +72,7 @@ class Session(base.baseSession):
self.db[name] = [] self.db[name] = []
objects = self.db[name] objects = self.db[name]
for i in data: for i in data:
if utils.find_item(i.id, self.db[name]) == None: if utils.find_item(i, self.db[name]) == None:
if self.settings["general"]["reverse_timelines"] == False: objects.append(i) if self.settings["general"]["reverse_timelines"] == False: objects.append(i)
else: objects.insert(0, i) else: objects.insert(0, i)
num = num+1 num = num+1
@@ -94,12 +94,12 @@ class Session(base.baseSession):
for i in data: for i in data:
# Twitter returns sender_id as str, which must be converted to int in order to match to our user_id object. # Twitter returns sender_id as str, which must be converted to int in order to match to our user_id object.
if int(i.message_create["sender_id"]) == self.db["user_id"]: if int(i.message_create["sender_id"]) == self.db["user_id"]:
if "sent_direct_messages" in self.db and utils.find_item(i.id, self.db["sent_direct_messages"]) == None: if "sent_direct_messages" in self.db and utils.find_item(i, self.db["sent_direct_messages"]) == None:
if self.settings["general"]["reverse_timelines"] == False: sent_objects.append(i) if self.settings["general"]["reverse_timelines"] == False: sent_objects.append(i)
else: sent_objects.insert(0, i) else: sent_objects.insert(0, i)
sent = sent+1 sent = sent+1
else: else:
if utils.find_item(i.id, self.db["direct_messages"]) == None: if utils.find_item(i, self.db["direct_messages"]) == None:
if self.settings["general"]["reverse_timelines"] == False: objects.append(i) if self.settings["general"]["reverse_timelines"] == False: objects.append(i)
else: objects.insert(0, i) else: objects.insert(0, i)
incoming = incoming+1 incoming = incoming+1
@@ -124,6 +124,7 @@ class Session(base.baseSession):
# This will be especially useful because if the user reactivates their account later, TWblue will try to retrieve such user again at startup. # This will be especially useful because if the user reactivates their account later, TWblue will try to retrieve such user again at startup.
# If we wouldn't implement this approach, TWBlue would save permanently the "deleted user" object. # If we wouldn't implement this approach, TWBlue would save permanently the "deleted user" object.
self.deleted_users = {} self.deleted_users = {}
self.type = "twitter"
pub.subscribe(self.handle_new_status, "newStatus") pub.subscribe(self.handle_new_status, "newStatus")
pub.subscribe(self.handle_connected, "streamConnected") pub.subscribe(self.handle_connected, "streamConnected")
@@ -198,14 +199,14 @@ class Session(base.baseSession):
try: try:
val = getattr(self.twitter, call_name)(*args, **kwargs) val = getattr(self.twitter, call_name)(*args, **kwargs)
finished = True finished = True
except TweepError as e: except TweepyException as e:
output.speak(e.reason) output.speak(str(e))
val = None val = None
if e.error_code != 403 and e.error_code != 404: if type(e) != NotFound and type(e) != Forvidden:
tries = tries+1 tries = tries+1
time.sleep(5) time.sleep(5)
elif report_failure and hasattr(e, 'reason'): elif report_failure:
output.speak(_("%s failed. Reason: %s") % (action, e.reason)) output.speak(_("%s failed. Reason: %s") % (action, str(e)))
finished = True finished = True
# except: # except:
# tries = tries + 1 # tries = tries + 1
@@ -217,7 +218,7 @@ class Session(base.baseSession):
def search(self, name, *args, **kwargs): def search(self, name, *args, **kwargs):
""" Search in twitter, passing args and kwargs as arguments to the Twython function.""" """ Search in twitter, passing args and kwargs as arguments to the Twython function."""
tl = self.twitter.search(*args, **kwargs) tl = self.twitter.search_tweets(*args, **kwargs)
tl.reverse() tl.reverse()
return tl return tl
@@ -270,12 +271,12 @@ class Session(base.baseSession):
# @_require_login # @_require_login
def get_lists(self): def get_lists(self):
""" Gets the lists that the user is subscribed to and stores them in the database. Returns None.""" """ Gets the lists that the user is subscribed to and stores them in the database. Returns None."""
self.db["lists"] = self.twitter.lists_all(reverse=True) self.db["lists"] = self.twitter.get_lists(reverse=True)
# @_require_login # @_require_login
def get_muted_users(self): def get_muted_users(self):
""" Gets muted users (oh really?).""" """ Gets muted users (oh really?)."""
self.db["muted_users"] = self.twitter.mutes_ids() self.db["muted_users"] = self.twitter.get_muted_ids()
# @_require_login # @_require_login
def get_stream(self, name, function, *args, **kwargs): def get_stream(self, name, function, *args, **kwargs):
@@ -416,12 +417,12 @@ class Session(base.baseSession):
log.debug("Requesting user id {} as it is not present in the users database.".format(id)) log.debug("Requesting user id {} as it is not present in the users database.".format(id))
try: try:
user = self.twitter.get_user(id=id) user = self.twitter.get_user(id=id)
except TweepError as err: except TweepyException as err:
user = UserModel(None) user = UserModel(None)
user.screen_name = "deleted_user" user.screen_name = "deleted_user"
user.id = id user.id = id
user.name = _("Deleted account") user.name = _("Deleted account")
if hasattr(err, "api_code") and err.api_code == 50: if type(err) == NotFound:
self.deleted_users[id] = user self.deleted_users[id] = user
return user return user
else: else:
@@ -430,9 +431,25 @@ class Session(base.baseSession):
users = self.db["users"] users = self.db["users"]
users[user.id_str] = user users[user.id_str] = user
self.db["users"] = users self.db["users"] = users
user.name = self.get_user_alias(user)
return user return user
else: else:
return self.db["users"][str(id)] user = self.db["users"][str(id)]
user.name = self.get_user_alias(user)
return user
def get_user_alias(self, user):
""" Retrieves an alias for the passed user model, if exists.
@ user Tweepy.models.user: An user object.
"""
aliases = self.settings.get("user-aliases")
if aliases == None:
log.error("Aliases are not defined for this config spec.")
return user.name
user_alias = aliases.get(user.id_str)
if user_alias != None:
return user_alias
return user.name
def get_user_by_screen_name(self, screen_name): def get_user_by_screen_name(self, screen_name):
""" Returns an user identifier associated with a screen_name. """ Returns an user identifier associated with a screen_name.
@@ -466,14 +483,14 @@ class Session(base.baseSession):
return return
log.debug("TWBlue will get %d new users from Twitter." % (len(users_to_retrieve))) log.debug("TWBlue will get %d new users from Twitter." % (len(users_to_retrieve)))
try: try:
users = self.twitter.lookup_users(user_ids=users_to_retrieve, tweet_mode="extended") users = self.twitter.lookup_users(user_id=users_to_retrieve, tweet_mode="extended")
users_db = self.db["users"] users_db = self.db["users"]
for user in users: for user in users:
users_db[user.id_str] = user users_db[user.id_str] = user
log.debug("Added %d new users" % (len(users))) log.debug("Added %d new users" % (len(users)))
self.db["users"] = users_db self.db["users"] = users_db
except TweepError as err: except TweepyException as err:
if hasattr(err, "api_code") and err.api_code == 17: # Users not found. if type(err) == NotFound: # User not found.
log.error("The specified users {} were not found in twitter.".format(user_ids)) log.error("The specified users {} were not found in twitter.".format(user_ids))
# Creates a deleted user object for every user_id not found here. # Creates a deleted user object for every user_id not found here.
# This will make TWBlue to not waste Twitter API calls when attempting to retrieve those users again. # This will make TWBlue to not waste Twitter API calls when attempting to retrieve those users again.
@@ -504,13 +521,17 @@ class Session(base.baseSession):
self.db["users"] = users self.db["users"] = users
def start_streaming(self): def start_streaming(self):
self.stream_listener = streaming.StreamListener(twitter_api=self.twitter, user=self.db["user_name"], user_id=self.db["user_id"]) if config.app["app-settings"]["no_streaming"]:
self.stream = streaming.Stream(auth = self.auth, listener=self.stream_listener, chunk_size=1025) return
self.stream_thread = call_threaded(self.stream.filter, follow=self.stream_listener.users, stall_warnings=True) self.stream = streaming.Stream(twitter_api=self.twitter, user=self.db["user_name"], user_id=self.db["user_id"], muted_users=self.db["muted_users"], consumer_key=keyring.get("api_key"), consumer_secret=keyring.get("api_secret"), access_token=self.settings["twitter"]["user_key"], access_token_secret=self.settings["twitter"]["user_secret"], chunk_size=1025)
self.stream_thread = call_threaded(self.stream.filter, follow=self.stream.users, stall_warnings=True)
def stop_streaming(self): def stop_streaming(self):
self.stream.running = False if config.app["app-settings"]["no_streaming"]:
log.debug("Stream stopped for accounr {}".format(self.db["user_name"])) return
if hasattr(self, "stream"):
self.stream.running = False
log.debug("Stream stopped for accounr {}".format(self.db["user_name"]))
def handle_new_status(self, status, user): def handle_new_status(self, status, user):
""" Handles a new status present in the Streaming API. """ """ Handles a new status present in the Streaming API. """
@@ -532,7 +553,7 @@ class Session(base.baseSession):
status._json = {**status._json, **status._json["extended_tweet"]} status._json = {**status._json, **status._json["extended_tweet"]}
# Sends status to database, where it will be reduced and changed according to our needs. # Sends status to database, where it will be reduced and changed according to our needs.
buffers_to_send = [] buffers_to_send = []
if status.user.id_str in self.stream_listener.users: if status.user.id_str in self.stream.users:
buffers_to_send.append("home_timeline") buffers_to_send.append("home_timeline")
if status.user.id == self.db["user_id"]: if status.user.id == self.db["user_id"]:
buffers_to_send.append("sent_tweets") buffers_to_send.append("sent_tweets")
@@ -555,6 +576,10 @@ class Session(base.baseSession):
pub.sendMessage("newTweet", data=status, user=self.db["user_name"], _buffers=buffers_to_send) pub.sendMessage("newTweet", data=status, user=self.db["user_name"], _buffers=buffers_to_send)
def check_streams(self): def check_streams(self):
if config.app["app-settings"]["no_streaming"]:
return
if not hasattr(self, "stream"):
return
log.debug("Status of running stream for user {}: {}".format(self.db["user_name"], self.stream.running)) log.debug("Status of running stream for user {}: {}".format(self.db["user_name"], self.stream.running))
if self.stream.running == False: if self.stream.running == False:
self.start_streaming() self.start_streaming()

View File

@@ -12,16 +12,25 @@ from pubsub import pub
log = logging.getLogger("sessions.twitter.streaming") log = logging.getLogger("sessions.twitter.streaming")
class StreamListener(tweepy.StreamListener): class Stream(tweepy.Stream):
def __init__(self, twitter_api, user, user_id, *args, **kwargs): def __init__(self, twitter_api, user, user_id, muted_users=[], *args, **kwargs):
super(StreamListener, self).__init__(*args, **kwargs) super(Stream, self).__init__(*args, **kwargs)
log.debug("Starting streaming listener for account {}".format(user))
self.started = False
self.users = []
self.api = twitter_api self.api = twitter_api
self.user = user self.user = user
self.user_id = user_id self.user_id = user_id
self.users = [str(id) for id in self.api.friends_ids()] friends = self.api.get_friend_ids()
log.debug("Retrieved {} friends to add to the streaming listener.".format(len(friends)))
self.users.append(str(self.user_id)) self.users.append(str(self.user_id))
log.debug("Started streaming object for user {}".format(self.user)) log.debug("Got {} muted users.".format(len(muted_users)))
for user in friends:
if user not in muted_users:
self.users.append(str(user))
self.started = True
log.debug("Streaming listener started with {} users to follow.".format(len(self.users)))
def on_connect(self): def on_connect(self):
pub.sendMessage("streamConnected", user=self.user) pub.sendMessage("streamConnected", user=self.user)
@@ -36,78 +45,3 @@ class StreamListener(tweepy.StreamListener):
return return
if status.user.id_str in self.users: if status.user.id_str in self.users:
pub.sendMessage("newStatus", status=status, user=self.user) pub.sendMessage("newStatus", status=status, user=self.user)
class Stream(tweepy.Stream):
def _run(self):
# Authenticate
url = "https://%s%s" % (self.host, self.url)
# Connect and process the stream
error_counter = 0
resp = None
exc_info = None
while self.running:
if self.retry_count is not None:
if error_counter > self.retry_count:
# quit if error count greater than retry count
break
try:
auth = self.auth.apply_auth()
resp = self.session.request('POST',
url,
data=self.body,
timeout=self.timeout,
stream=True,
auth=auth,
verify=self.verify,
proxies = self.proxies)
if resp.status_code != 200:
if self.listener.on_error(resp.status_code) is False:
break
error_counter += 1
if resp.status_code == 420:
self.retry_time = max(self.retry_420_start,
self.retry_time)
time.sleep(self.retry_time)
self.retry_time = min(self.retry_time * 2,
self.retry_time_cap)
else:
error_counter = 0
self.retry_time = self.retry_time_start
self.snooze_time = self.snooze_time_step
self.listener.on_connect()
self._read_loop(resp)
except (requests.ConnectionError, requests.Timeout, ssl.SSLError, urllib3.exceptions.ReadTimeoutError, urllib3.exceptions.ProtocolError) as exc:
# This is still necessary, as a SSLError can actually be
# thrown when using Requests
# If it's not time out treat it like any other exception
if isinstance(exc, ssl.SSLError):
if not (exc.args and 'timed out' in str(exc.args[0])):
exc_info = sys.exc_info()
break
if self.listener.on_timeout() is False:
break
if self.running is False:
break
time.sleep(self.snooze_time)
self.snooze_time = min(self.snooze_time + self.snooze_time_step,
self.snooze_time_cap)
except Exception as exc:
exc_info = sys.exc_info()
# any other exception is fatal, so kill loop
break
# cleanup
self.running = False
if resp:
resp.close()
self.new_session()
if exc_info:
# call a handler first so that the exception can be logged.
self.listener.on_exception(exc_info[1])
six.reraise(*exc_info)

View File

@@ -6,7 +6,7 @@ import logging
import requests import requests
import time import time
import sound import sound
from tweepy.error import TweepError from tweepy.errors import TweepyException, NotFound, Forbidden
log = logging.getLogger("twitter.utils") log = logging.getLogger("twitter.utils")
""" Some utilities for the twitter interface.""" """ Some utilities for the twitter interface."""
@@ -60,9 +60,13 @@ def find_urls (tweet, twitter_media=False):
urls.append(i) urls.append(i)
return urls return urls
def find_item(id, listItem): def find_item(item, listItems):
for i in range(0, len(listItem)): for i in range(0, len(listItems)):
if listItem[i].id == id: return i if listItems[i].id == item.id:
return i
# Check also retweets.
if hasattr(item, "retweeted_status") and item.retweeted_status.id == listItems[i].id:
return i
return None return None
def find_list(name, lists): def find_list(name, lists):
@@ -121,9 +125,9 @@ def get_all_mentioned(tweet, conf, field="screen_name"):
""" Gets all users that have been mentioned.""" """ Gets all users that have been mentioned."""
results = [] results = []
if hasattr(tweet, "retweeted_status"): if hasattr(tweet, "retweeted_status"):
results.extend(get_all_mentionned(tweet.retweeted_status, conf, field)) results.extend(get_all_mentioned(tweet.retweeted_status, conf, field))
if hasattr(tweet, "quoted_status"): if hasattr(tweet, "quoted_status"):
results.extend(tweet.quoted_status, conf, field) results.extend(get_all_mentioned(tweet.quoted_status, conf, field))
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"): if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
for i in tweet.entities["user_mentions"]: for i in tweet.entities["user_mentions"]:
if i["screen_name"] != conf["user_name"] and i["id_str"] != tweet.user: if i["screen_name"] != conf["user_name"] and i["id_str"] != tweet.user:
@@ -155,8 +159,8 @@ def if_user_exists(twitter, user):
try: try:
data = twitter.get_user(screen_name=user) data = twitter.get_user(screen_name=user)
return data return data
except TweepError as err: except TweepyException as err:
if err.api_code == 50: if type(err) == NotFound:
return None return None
else: else:
return user return user
@@ -168,7 +172,7 @@ def is_allowed(tweet, settings, buffer_name):
tweet_data = {} tweet_data = {}
if hasattr(tweet, "retweeted_status"): if hasattr(tweet, "retweeted_status"):
tweet_data["retweet"] = True tweet_data["retweet"] = True
if tweet.in_reply_to_status_id != None: if hasattr(tweet, "in_reply_to_status_id"):
tweet_data["reply"] = True tweet_data["reply"] = True
if hasattr(tweet, "quoted_status"): if hasattr(tweet, "quoted_status"):
tweet_data["quote"] = True tweet_data["quote"] = True
@@ -223,12 +227,12 @@ def filter_tweet(tweet, tweet_data, settings, buffer_name):
return True return True
def twitter_error(error): def twitter_error(error):
if error.api_code == 179: if type(error) == Forbidden:
msg = _(u"Sorry, you are not authorised to see this status.") msg = _(u"Sorry, you are not authorised to see this status.")
elif error.api_code == 144: elif type(error) == NotFound:
msg = _(u"No status found with that ID") msg = _(u"No status found with that ID")
else: else:
msg = _(u"Error code {0}").format(error.api_code,) msg = _(u"Error {0}").format(str(error),)
output.speak(msg) output.speak(msg)
def expand_urls(text, entities): def expand_urls(text, entities):
@@ -240,3 +244,20 @@ def expand_urls(text, entities):
if url["url"] in text: if url["url"] in text:
text = text.replace(url["url"], url["expanded_url"]) text = text.replace(url["url"], url["expanded_url"])
return text 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), "", 1)
total_users = total_users+1
end = user.end()
if total_users-2 < 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

@@ -3,7 +3,7 @@ import sys
import application import application
import platform import platform
import os import os
from cx_Freeze import setup, Executable from cx_Freeze import setup, Executable, winmsvcr
from requests import certs from requests import certs
def get_architecture_files(): def get_architecture_files():
@@ -40,7 +40,7 @@ build_exe_options = dict(
build_exe="dist", build_exe="dist",
optimize=1, optimize=1,
includes=["enchant.tokenize.en"], # This is not handled automatically by cx_freeze. includes=["enchant.tokenize.en"], # This is not handled automatically by cx_freeze.
include_msvcr=True, include_msvcr=False,
replace_paths = [("*", "")], replace_paths = [("*", "")],
include_files=["icon.ico", "conf.defaults", "app-configuration.defaults", "keymaps", "locales", "sounds", "documentation", ("keys/lib", "keys/lib"), find_sound_lib_datafiles(), find_accessible_output2_datafiles()]+get_architecture_files(), include_files=["icon.ico", "conf.defaults", "app-configuration.defaults", "keymaps", "locales", "sounds", "documentation", ("keys/lib", "keys/lib"), find_sound_lib_datafiles(), find_accessible_output2_datafiles()]+get_architecture_files(),
packages=["wxUI"], packages=["wxUI"],
@@ -50,6 +50,8 @@ executables = [
Executable('main.py', base=base, targetName="twblue") Executable('main.py', base=base, targetName="twblue")
] ]
winmsvcr.FILES = ()
winmsvcr.FILES_TO_DUPLICATE = ()
setup(name=application.name, setup(name=application.name,
version=application.version, version=application.version,
description=application.description, description=application.description,

View File

@@ -114,6 +114,8 @@ class URLStream(object):
# LibVLC controls. # LibVLC controls.
self.instance = vlc.Instance() self.instance = vlc.Instance()
self.player = self.instance.media_player_new() self.player = self.instance.media_player_new()
self.event_manager = self.player.event_manager()
self.event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback)
def prepare(self, url): def prepare(self, url):
""" Takes an URL and prepares it to be streamed. This function will try to unshorten the passed URL and, if needed, to transform it into a valid URL.""" """ Takes an URL and prepares it to be streamed. This function will try to unshorten the passed URL and, if needed, to transform it into a valid URL."""
@@ -158,3 +160,9 @@ class URLStream(object):
def stop_audio(self): def stop_audio(self):
output.speak(_(u"Stopped."), True) output.speak(_(u"Stopped."), True)
self.player.stop() self.player.stop()
def end_callback(self, event, *args, **kwargs):
call_threaded(self.player.stop)
def __del__(self):
self.event_manager.event_detach(vlc.EventType.MediaPlayerEndReached)

View File

@@ -1,3 +1,3 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import unicode_literals from __future__ import unicode_literals
from . import baseDialog, trends, configuration, lists, message, search, find, show_user, update_profile, urlList, userSelection, utils, filterDialogs from . import baseDialog, trends, configuration, lists, message, search, find, show_user, update_profile, urlList, userSelection, utils, filterDialogs, userAliasDialogs

View File

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

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
import wx
from . import baseDialog
class addAliasDialog(baseDialog.BaseWXDialog):
def __init__(self, title, users):
super(addAliasDialog, self).__init__(parent=None, id=wx.ID_ANY, title=title)
panel = wx.Panel(self)
userSizer = wx.BoxSizer()
self.cb = wx.ComboBox(panel, -1, choices=users, value=users[0], size=wx.DefaultSize)
self.cb.SetFocus()
self.autocompletion = wx.Button(panel, -1, _(u"&Autocomplete users"))
userSizer.Add(wx.StaticText(panel, -1, _(u"User")), 0, wx.ALL, 5)
userSizer.Add(self.cb, 0, wx.ALL, 5)
userSizer.Add(self.autocompletion, 0, wx.ALL, 5)
aliasSizer = wx.BoxSizer(wx.HORIZONTAL)
aliasLabel = wx.StaticText(panel, wx.ID_ANY, _("Alias"))
self.alias = wx.TextCtrl(panel, wx.ID_ANY)
aliasSizer.Add(aliasLabel, 0, wx.ALL, 5)
aliasSizer.Add(self.alias, 0, wx.ALL, 5)
sizer = wx.BoxSizer(wx.VERTICAL)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
btnsizer = wx.BoxSizer()
btnsizer.Add(ok, 0, wx.ALL, 5)
btnsizer.Add(cancel, 0, wx.ALL, 5)
sizer.Add(userSizer, 0, wx.ALL, 5)
sizer.Add(aliasSizer, 0, wx.ALL, 5)
sizer.Add(btnsizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def get_user(self):
return (self.cb.GetValue(), self.alias.GetValue())

View File

@@ -46,5 +46,4 @@ class selectUserDialog(baseDialog.BaseWXDialog):
self.SetClientSize(sizer.CalcMin()) self.SetClientSize(sizer.CalcMin())
def get_user(self): def get_user(self):
return self.cb.GetValue() return self.cb.GetValue()

View File

@@ -43,6 +43,7 @@ class mainFrame(wx.Frame):
self.follow = user.Append(wx.ID_ANY, _(u"&Actions...")) self.follow = user.Append(wx.ID_ANY, _(u"&Actions..."))
self.timeline = user.Append(wx.ID_ANY, _(u"&View timeline...")) self.timeline = user.Append(wx.ID_ANY, _(u"&View timeline..."))
self.dm = user.Append(wx.ID_ANY, _(u"Direct me&ssage")) self.dm = user.Append(wx.ID_ANY, _(u"Direct me&ssage"))
self.addAlias = user.Append(wx.ID_ANY, _("Add a&lias"))
self.addToList = user.Append(wx.ID_ANY, _(u"&Add to list")) self.addToList = user.Append(wx.ID_ANY, _(u"&Add to list"))
self.removeFromList = user.Append(wx.ID_ANY, _(u"R&emove from list")) self.removeFromList = user.Append(wx.ID_ANY, _(u"R&emove from list"))
self.viewLists = user.Append(wx.ID_ANY, _(u"&View lists")) self.viewLists = user.Append(wx.ID_ANY, _(u"&View lists"))

View File

@@ -1,7 +1,7 @@
{"current_version": "7", {"current_version": "11",
"description": "Snapshot version.", "description": "Snapshot version.",
"date": "unknown", "date": "unknown",
"downloads": "downloads":
{"Windows32": "https://twblue.es/pubs/twblue_snapshot_x86.zip"}, {"Windows32": "https://twblue.es/pubs/twblue_snapshot_x86.zip",
{"Windows64": "https://twblue.es/pubs/twblue_snapshot_x64.zip"} {"Windows64": "https://twblue.es/pubs/twblue_snapshot_x64.zip"
} }