Compare commits

...

56 Commits

Author SHA1 Message Date
bea4096d16 Remove keys directory from setup file 2023-04-13 12:55:43 -06:00
6135412d4b Removed twitter config include from setup script 2023-04-13 12:38:46 -06:00
Riku
16d855ac56 Translated using Weblate (Japanese)
Currently translated at 100.0% (667 of 667 strings)

Translation: TWBlue/TWBlue
Translate-URL: https://weblate.mcvsoftware.com/projects/twblue/twblue/ja/
2023-04-11 01:45:23 -05:00
Corentin Bacqué-Cazenave
bfb705c18e Translated using Weblate (French)
Currently translated at 100.0% (712 of 712 strings)

Translation: TWBlue/TWBlue
Translate-URL: https://weblate.mcvsoftware.com/projects/twblue/twblue/fr/
2023-04-11 01:45:20 -05:00
77b2486877 Fixed conflict with translation catalog 2023-04-09 17:21:16 -06:00
manuelcortez
1a2a2fb3e2 Updated translation catalogs 2023-04-09 00:55:17 +00:00
Corentin Bacqué-Cazenave
a2660dd410 Translated using Weblate (French)
Currently translated at 100.0% (951 of 951 strings)

Translation: TWBlue/TWBlue
Translate-URL: https://weblate.mcvsoftware.com/projects/twblue/twblue/fr/
2023-04-05 14:36:48 -05:00
058ca1347a Fixed a typo 2023-04-05 13:36:35 -06:00
10e23b039b Mastodon: Added user aliases 2023-04-05 13:28:09 -06:00
00a5ad9e59 Mastodon: Implemented OCR for images in posts 2023-04-05 12:57:37 -06:00
db4607f17e Updated changelog 2023-04-05 11:38:45 -06:00
65aea3c43c Mastodon: Change buffer title properly after timelines are loaded during startup 2023-04-05 09:39:39 -06:00
972b851b93 Mastodon: Implemented hide emojis for usernames. 2023-04-05 09:17:37 -06:00
0764679164 Mastodon: Prefer remote_url before instance cached URL when playing media files 2023-04-05 08:29:21 -06:00
c6796874c2 core: Removed invisible keystrokes for Twitter related actions 2023-04-04 11:32:55 -06:00
5f07f3b9d0 Mastodon: Add attachments and reply settings to post when recovering from an error. Closes #527, #526, #377, #137, #108 2023-04-03 16:03:20 -06:00
52267562bc Core: Removed Twitter references from default menu bar 2023-04-03 15:32:57 -06:00
972b880931 Mastodon: Fixed Open URL Dialog. closes #529 2023-04-03 15:32:14 -06:00
1fa1313434 Mastodon: Raise exception when replying to a deleted post 2023-04-03 15:17:03 -06:00
96dc99a93b Updated changelog 2023-04-03 13:49:10 -06:00
8acebc290b Remove most of Twitter code as Twitter's API access has been removed 2023-04-03 13:35:05 -06:00
manuelcortez
74fe437684 Updated translation catalogs 2023-04-02 00:55:37 +00:00
manuelcortez
05b7e9fce4 Updated translation catalogs 2023-03-26 00:55:23 +00:00
8940825509 Merge branch 'next-gen' of github.com:mcv-software/twblue into next-gen 2023-03-24 07:19:08 -06:00
5af336fdcc Run catalog update every week 2023-03-24 07:17:45 -06:00
manuelcortez
8bfb7ef003 Updated translation catalogs 2023-03-24 00:20:53 +00:00
a57ea752d6 Mastodon: Finished first implementation of 'reattempt to post on failures'. Should be relevant for #527, #526, #377, #137, #108 2023-03-23 13:17:55 -06:00
3f0ee5650b Mastodon: prepare messages class to support dynamic adding of post data 2023-03-23 12:00:51 -06:00
d320daa6a1 Mastodon: Started working on recovering from errors when sending posts. #527, #526, #377, #137, #108 2023-03-23 11:58:42 -06:00
ae5515b6e1 Fixed a typo. Closes #519 2023-03-23 08:48:02 -06:00
d01856f436 Mastodon: Load mentions only if the notification contains a post 2023-03-23 08:26:40 -06:00
manuelcortez
5749b4c8e3 Updated translation catalogs 2023-03-23 11:09:08 +00:00
eec4b34f44 added action to update translation catalogs (test) 2023-03-23 05:07:06 -06:00
059e83a765 Merge branch 'next-gen' of github.com:mcv-software/twblue into next-gen 2023-03-04 22:40:20 -06:00
476df9cfdb Merge branch 'weblate-twblue-twblue' into 'next-gen'
Translations update from Translations hub

See merge request twblue/twblue!16
2023-03-05 04:40:46 +00:00
866da8425c Merge pull request #524 from Arfs6/fix_report_error_btn
Bind error reporting item on menu bar to github issues. Closes #520
2023-03-01 15:40:26 -06:00
Abdulqadir Ahmad
e9ae5b228b Fixed report an error button not working 2023-02-25 10:39:05 +01:00
Corentin Bacqué-Cazenave
4a420bf1e0 Translated using Weblate (French)
Currently translated at 100.0% (941 of 941 strings)

Translation: TWBlue/TWBlue
Translate-URL: https://weblate.mcvsoftware.com/projects/twblue/twblue/fr/
2023-02-17 23:20:20 -06:00
Riku
025d0467ae Translated using Weblate (Japanese)
Currently translated at 100.0% (941 of 941 strings)

Translation: TWBlue/TWBlue
Translate-URL: https://weblate.mcvsoftware.com/projects/twblue/twblue/ja/
2023-02-17 23:20:20 -06:00
Riku
727096ca91 Translated using Weblate (Japanese)
Currently translated at 100.0% (941 of 941 strings)

Translation: TWBlue/TWBlue
Translate-URL: https://weblate.mcvsoftware.com/projects/twblue/twblue/ja/
2023-02-17 23:20:20 -06:00
e31369e49a Mastodon: Added experimental support for voting in polls 2023-02-17 23:19:34 -06:00
f3fd1087b4 Mastodon: Allow adding descriptions to all supported media. Closes #516 2023-02-10 01:01:25 -06:00
e9dc02e868 Core: Display variables within templates. Closes #515 2023-02-10 00:46:08 -06:00
68c5b9affe core: Update Copyright year 2023-02-10 00:22:51 -06:00
09650f588a Twitter: Ignores twitter sessions if login doesn't work 2023-02-10 00:08:49 -06:00
310ba003c9 Twitter: Remove Twitter deprecation on feb 9 due to (more) changes on dates, features and stuff 2023-02-08 23:00:45 -06:00
d0fcf88b31 Generate docs even if some translation files are missing 2023-02-06 22:12:16 -06:00
d0e18178c6 Mastodon: Fixed media uploads. Closes #513 2023-02-06 05:02:40 -06:00
ad8667a13c Fixed locale conflicts. Closes #510 2023-02-06 04:32:46 -06:00
63ae496c39 Mastodon: Improved character count to match Mastodon's backend implementation. Remote users are counted only by username (domains are not taken into account), content warning text counts against character limit, and emoji&CJK characters are counted as 1 2023-02-06 04:09:58 -06:00
45cffd6a0b Doc: Updated changelog 2023-02-06 02:54:05 -06:00
7c959088e0 Core: Fixed small issue when switching between accounts on invisible interface 2023-02-06 02:43:00 -06:00
fda5250a52 Mastodon: Add admin.sign_up to supported notifications. 2023-02-06 02:38:18 -06:00
98aba2a4c4 Merge branch 'next-gen' of gitlab.com:twblue/twblue into next-gen 2023-02-06 02:23:51 -06:00
24e91235f3 Mastodon: Implemented setting to disable Streaming API endpoints on sessions 2023-02-05 19:09:27 -06:00
ef2e63e195 Mastodon: Avoid reconnecting to Streaming API manually (let the lib to do its job in their async code) 2023-02-05 18:59:16 -06:00
193 changed files with 48987 additions and 70523 deletions

View File

@@ -0,0 +1,32 @@
name: Update translation files
on:
workflow_dispatch:
schedule:
- cron: "35 0 * * 0"
permissions: write-all
jobs:
update_catalogs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
cache: 'pip'
- name: Install dependencies
run: pip install babel
- name: Extract messages
run: pybabel extract -o twblue.pot --msgid-bugs-address "manuel@manuelcortez.net" --copyright-holder "MCV software" --input-dirs .
working-directory: 'src'
- name: Update catalogs
run: pybabel update --input-file twblue.pot --domain twblue --output-dir locales
working-directory: 'src'
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Updated translation catalogs
repository: src/locales

View File

@@ -32,7 +32,6 @@ twblue32:
- cd ..\src - cd ..\src
- '&$env:PYTHON ..\doc\generator.py' - '&$env:PYTHON ..\doc\generator.py'
- '&$env:PYTHON write_version_data.py' - '&$env:PYTHON write_version_data.py'
- New-Item "appkeys.py" -ItemType File -Value "twitter_api_key='$TWITTER_API_KEY'`ntwitter_api_secret='$TWITTER_API_SECRET'"
- '&$env:PYTHON setup.py build' - '&$env:PYTHON setup.py build'
- cd .. - cd ..
- mkdir artifacts - mkdir artifacts

View File

@@ -3,7 +3,7 @@
[![Build status](https://ci.appveyor.com/api/projects/status/fml5fu7h1fj8vf6l?svg=true)](https://ci.appveyor.com/project/manuelcortez/twblue) [![Build status](https://ci.appveyor.com/api/projects/status/fml5fu7h1fj8vf6l?svg=true)](https://ci.appveyor.com/project/manuelcortez/twblue)
TWBlue is a free and open source application that allows you to interact with the main features of Twitter and mastodon from the comfort of a windows software, with 2 different interfaces specially designed for screen reader users. TWBlue is a free and open source application that allows you to interact with the main features of mastodon from the comfort of a windows software, with 2 different interfaces specially designed for screen reader users.
See [TWBlue's webpage](https://twblue.es) for more details. See [TWBlue's webpage](https://twblue.es) for more details.
@@ -11,15 +11,6 @@ See [TWBlue's webpage](https://twblue.es) for more details.
This document describes how to run tw blue from source and how to build a binary version which doesn't need Python and the other dependencies to run. This document describes how to run tw blue from source and how to build a binary version which doesn't need Python and the other dependencies to run.
### Generating application keys
In order to communicate with Twitter, you will need to generate a set of API keys in their [developer portal](https://developer.twitter.com/en/portal/dashboard) (If you haven't signed up, [visit this site to register as a developer](https://developer.twitter.com/en/docs/twitter-api/getting-started/getting-access-to-the-twitter-api)) and create a module called appkeys.py, within the src directory, with the following content, replacing the example values with your set of API keys:
```
twitter_api_key='xxxxxxxxxx'
twitter_api_secret='xxxxxxxxxx'
```
### Required dependencies. ### Required dependencies.
Although most dependencies can be found in the windows-dependencies directory, we provide links to their official websites. If you are cloning with git, don't forget to initialize and update the submodules to get the windows-dependencies folder. You can use these two commands to perform this task from git bash: Although most dependencies can be found in the windows-dependencies directory, we provide links to their official websites. If you are cloning with git, don't forget to initialize and update the submodules to get the windows-dependencies folder. You can use these two commands to perform this task from git bash:

View File

@@ -2,6 +2,34 @@ TWBlue Changelog
## changes in this version ## changes in this version
During the development of the current TWBlue version, Twitter has cut out access from their API, meaning TWBlue will no longer be able to communicate with Twitter. This is the end of the support of TWBlue for Twitter sessions. No new sessions will be available for this social network, and we will focus in adding more features to our Mastodon support and writing support for more websites and networks. Thank you everyone who have been using TWBlue to manage your Twitter accounts since 2013.
* TWBlue should be able to display variables within templates (for example, now it is possible to send a template inside a post's text). Before, it was removing $variables so it was difficult to show how to edit templates from the client. ([#515](https://github.com/MCV-Software/TWBlue/issues/515))
* Mastodon:
* it is possible to add descriptions for all media available on Mastodon (audio, photos, video and Givs). ([#516](https://github.com/MCV-Software/TWBlue/issues/516))
* TWBlue can now perform OCR in attached images.
* It is possible to add aliases to mastodon users. Also, the "manage user aliases" setting, located on the application menu within the menu bar can be used to add, edit or remove aliases.
* Implemented "Hide emojis on usernames" in both GUI and invisible interface.
* Added an experimental feature to recover from connection errors. When making a post, if the post cannot be published due to any kind of error, TWBlue will bring up the dialog where the post was composed, so you can give the post a second chance or save the post's text. This feature should work for threads, posts with attachments, polls and replies. ([#527,](https://github.com/MCV-Software/TWBlue/issues/527) [#526,](https://github.com/MCV-Software/TWBlue/issues/526) [#377,](https://github.com/MCV-Software/TWBlue/issues/377) [#137,](https://github.com/MCV-Software/TWBlue/issues/137) [#108](https://github.com/MCV-Software/TWBlue/issues/108))
* When playing media items, TWBlue will prefer remote URL streams and fall back to instance cached stream URL'S.
* Fixed an error on mentions buffer that was making TWBlue unable to load posts if there were mentions from a blocked or deleted account.
* Fixed an error when loading timelines during startup where TWBlue was unable to change the buffer title properly.
## Changes on version 2023.2.6
This release focuses on fixing some important bugs that have been reported in the previous version. Particularly, TWBlue should be able to authorize on some instances that have blocked the Mastodon.py library, and should be able to avoid repeatedly calling some endpoints that cause excessive connections for some instances. Additionally, it is possible to disable Streaming from the account options in Mastodon. This can be especially useful if TWBlue keeps making a lot of API calls for some instances.
* Fixed the update system.
* Fixed a bug when attempting to switch between different accounts using the invisible interface, if the focused account is not an active session.
* Mastodon:
* Improved the way TWBlue counts characters in Mastodon. Now it counts only the username part in a remote user (@domain is not counted against character limit), adds content warning text to character count, also emojis and CJK characters are counted as 1 as opposed to 2. ([#511](https://github.com/MCV-Software/TWBlue/issues/511))
* Added notification when a user joins an instance. This notification is only available for administrators.
* Added option to disable Streaming in the account options. This can be useful if TWBlue, for some reason, repeatedly calls the instance API.
* Improved the code that works with the Streaming API to reduce the number of reconnection attempts TWBlue performs.
* Fixed media uploads for audio, video and gifvs. ([#513](https://github.com/MCV-Software/TWBlue/issues/513))
## Changes in version 2023.2.3
In this version, TWBlue will no longer support Twitter sessions starting on February 9, due to Twitter's policies prohibiting third-party clients, in addition to the shutdown of the free access to the Twitter API. All Twitter sessions that are active on TWBlue will stop working as of February 9, when the free API access will finally be shut down. It will not be possible to display or add Twitter sessions from the Session manager. From the TWBlue team, we will continue working to improve our support for Mastodon instances and add other social networks in the near future. If you want to keep in touch with the project, you can follow us in our mastodon account, at [@twblue@maaw.social.](https://maaw.social/@twblue) In this version, TWBlue will no longer support Twitter sessions starting on February 9, due to Twitter's policies prohibiting third-party clients, in addition to the shutdown of the free access to the Twitter API. All Twitter sessions that are active on TWBlue will stop working as of February 9, when the free API access will finally be shut down. It will not be possible to display or add Twitter sessions from the Session manager. From the TWBlue team, we will continue working to improve our support for Mastodon instances and add other social networks in the near future. If you want to keep in touch with the project, you can follow us in our mastodon account, at [@twblue@maaw.social.](https://maaw.social/@twblue)
* In the graphical interface, TWBlue will update menu items, in the menu bar, depending on whether you are focusing a Twitter or Mastodon session. This makes it possible for TWBlue to display the correct terms in each social network. Take into account that there might be unavailable items for the currently active session. * In the graphical interface, TWBlue will update menu items, in the menu bar, depending on whether you are focusing a Twitter or Mastodon session. This makes it possible for TWBlue to display the correct terms in each social network. Take into account that there might be unavailable items for the currently active session.

View File

@@ -10,7 +10,6 @@ from importlib import reload
# Languages already translated or translating the documentation. # Languages already translated or translating the documentation.
documentation_languages = ["en", "es", "fr", "de", "it", "gl", "ja", "ru", "ro", "eu", "ca", "da", "sr"] documentation_languages = ["en", "es", "fr", "de", "it", "gl", "ja", "ru", "ro", "eu", "ca", "da", "sr"]
# Changelog translated languages. # Changelog translated languages.
changelog_languages = ["en", "ca", "de", "es", "eu", "fr", "gl", "ja", "ro", "ru", "sr"] changelog_languages = ["en", "ca", "de", "es", "eu", "fr", "gl", "ja", "ro", "ru", "sr"]
@@ -29,8 +28,13 @@ def get_translations(name):
langs = changelog_languages langs = changelog_languages
for l in langs: for l in langs:
if l != "en": if l != "en":
_ = gettext.translation(name, os.path.join(paths.app_path(), "locales"), languages=[l]) try:
translations[l] = _ _ = gettext.translation(name, os.path.join(paths.app_path(), "locales"), languages=[l])
translations[l] = _
print(l, name)
except FileNotFoundError:
_ = gettext.NullTranslations()
translations[l] = _
else: else:
_ = gettext.NullTranslations() _ = gettext.NullTranslations()
translations[l] = _ translations[l] = _

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,6 @@ oauthlib
requests-oauthlib requests-oauthlib
requests-toolbelt requests-toolbelt
pypubsub pypubsub
geopy
arrow arrow
python-dateutil python-dateutil
winpaths winpaths
@@ -29,7 +28,6 @@ pywin32
certifi certifi
backports.functools_lru_cache backports.functools_lru_cache
cx_freeze cx_freeze
tweepy
twitter-text-parser twitter-text-parser
mastodon.py mastodon.py
pyenchant pyenchant

View File

@@ -1,60 +0,0 @@
[twitter]
user_key = string(default="")
user_secret = string(default="")
user_name = string(default="")
ignored_clients = list(default=list())
[general]
relative_times = boolean(default=True)
max_api_calls = integer(default=1)
max_tweets_per_call = integer(default=100)
reverse_timelines = boolean(default=False)
announce_stream_status = boolean(default=True)
retweet_mode = string(default="ask")
persist_size = integer(default=0)
load_cache_in_memory=boolean(default=True)
show_screen_names = boolean(default=False)
hide_emojis = boolean(default=False)
buffer_order = list(default=list('home','mentions', 'dm', 'sent_dm', 'sent_tweets','favorites','followers','friends','blocks','muted'))
[sound]
volume = float(default=1.0)
input_device = string(default="Default")
output_device = string(default="Default")
session_mute = boolean(default=False)
current_soundpack = string(default="FreakyBlue")
indicate_audio = boolean(default=True)
indicate_geo = boolean(default=True)
indicate_img = boolean(default=True)
sndup_api_key = string(default="")
[other_buffers]
timelines = list(default=list())
tweet_searches = list(default=list())
lists = list(default=list())
favourites_timelines = list(default=list())
followers_timelines = list(default=list())
friends_timelines = list(default=list())
trending_topic_buffers = list(default=list())
muted_buffers = list(default=list())
autoread_buffers = list(default=list(mentions, direct_messages, events))
[mysc]
spelling_language = string(default="")
save_followers_in_autocompletion_db = boolean(default=False)
save_friends_in_autocompletion_db = boolean(default=False)
ocr_language = string(default="")
[reporting]
braille_reporting = boolean(default=True)
speech_reporting = boolean(default=True)
[templates]
tweet = string(default="$display_name, $text $image_descriptions $date. $source")
dm = string(default="$sender_display_name, $text $date")
dm_sent = string(default="Dm to $recipient_display_name, $text $date")
person = string(default="$display_name (@$screen_name). $followers followers, $following following, $tweets tweets. Joined Twitter $created_at.")
[filters]
[user-aliases]

View File

@@ -1,22 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import datetime
# Make date check for feb 9.
now = datetime.datetime.now()
end_of_twitter = datetime.datetime(2023, 2, 9)
twitter_support_enabled = True
if now >= end_of_twitter:
twitter_support_enabled = False
name = 'TWBlue' name = 'TWBlue'
short_name='twblue' short_name='twblue'
update_url = 'https://twblue.es/updates/updates.php' update_url = 'https://twblue.es/updates/updates.php'
mirror_update_url = 'https://raw.githubusercontent.com/mcv-software/TWBlue/next-gen/updates/updates.json' mirror_update_url = 'https://raw.githubusercontent.com/mcv-software/TWBlue/next-gen/updates/updates.json'
authors = ["Manuel Cortéz", "José Manuel Delicado"] authors = ["Manuel Cortéz", "José Manuel Delicado"]
authorEmail = "manuel@manuelcortez.net" authorEmail = "manuel@manuelcortez.net"
copyright = "Copyright (C) 2013-2022, MCV Software." copyright = "Copyright (C) 2013-2023, MCV Software."
description = name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features." description = name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features."
translators = ["Manuel Cortéz (English)", "Mohammed Al Shara, Hatoun Felemban (Arabic)", "Francisco Torres (Catalan)", "Manuel cortéz (Spanish)", "Sukil Etxenike Arizaleta (Basque)", "Jani Kinnunen (finnish)", "Corentin Bacqué-Cazenave (Français)", "Juan Buño (Galician)", "Steffen Schultz (German)", "Zvonimir Stanečić (Croatian)", "Robert Osztolykan (Hungarian)", "Christian Leo Mameli (Italian)", "Riku (Japanese)", "Paweł Masarczyk (Polish)", "Odenilton Júnior Santos (Portuguese)", "Florian Ionașcu, Nicușor Untilă (Romanian)", "Natalia Hedlund, Valeria Kuznetsova (Russian)", "Aleksandar Đurić (Serbian)", "Burak Yüksek (Turkish)"] translators = ["Manuel Cortéz (English)", "Mohammed Al Shara, Hatoun Felemban (Arabic)", "Francisco Torres (Catalan)", "Manuel cortéz (Spanish)", "Sukil Etxenike Arizaleta (Basque)", "Jani Kinnunen (finnish)", "Corentin Bacqué-Cazenave (Français)", "Juan Buño (Galician)", "Steffen Schultz (German)", "Zvonimir Stanečić (Croatian)", "Robert Osztolykan (Hungarian)", "Christian Leo Mameli (Italian)", "Riku (Japanese)", "Paweł Masarczyk (Polish)", "Odenilton Júnior Santos (Portuguese)", "Florian Ionașcu, Nicușor Untilă (Romanian)", "Natalia Hedlund, Valeria Kuznetsova (Russian)", "Aleksandar Đurić (Serbian)", "Burak Yüksek (Turkish)"]
url = "https://twblue.es" url = "https://twblue.es"
report_bugs_url = "https://github.com/mcvsoftware/twblue/issues" report_bugs_url = "https://github.com/MCV-Software/TWBlue/issues"
supported_languages = [] supported_languages = []
version = "11" version = "11"

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from . import base as base from . import base as base
from . import twitter as twitter
from . import mastodon as mastodon from . import mastodon as mastodon

View File

@@ -17,9 +17,11 @@ from sessions.mastodon import compose, utils, templates
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
from pubsub import pub from pubsub import pub
from extra import ocr from extra import ocr
from wxUI import buffers, dialogs, commonMessageDialogs from wxUI import buffers, commonMessageDialogs
from wxUI.dialogs.mastodon import menus from wxUI.dialogs.mastodon import menus
from wxUI.dialogs.mastodon import dialogs as mastodon_dialogs from wxUI.dialogs.mastodon import dialogs as mastodon_dialogs
from wxUI.dialogs.mastodon.postDialogs import attachedPoll
from wxUI.dialogs import urlList
log = logging.getLogger("controller.buffers.mastodon.base") log = logging.getLogger("controller.buffers.mastodon.base")
@@ -76,7 +78,7 @@ class BaseBuffer(base.Buffer):
safe = True safe = True
if self.session.settings["general"]["read_preferences_from_instance"]: if self.session.settings["general"]["read_preferences_from_instance"]:
safe = self.session.expand_spoilers == False safe = self.session.expand_spoilers == False
return self.compose_function(self.get_item(), self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)[1] return self.compose_function(self.get_item(), self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)[1]
def get_message(self): def get_message(self):
post = self.get_item() post = self.get_item()
@@ -89,7 +91,7 @@ class BaseBuffer(base.Buffer):
template = template.replace("$safe_text", "$text") template = template.replace("$safe_text", "$text")
elif self.session.expand_spoilers == False and "$text" in template: elif self.session.expand_spoilers == False and "$text" in template:
template = template.replace("$text", "$safe_text") template = template.replace("$text", "$safe_text")
t = templates.render_post(post, template, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"]) t = templates.render_post(post, template, self.session.settings, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"])
return t return t
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False): def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
@@ -117,6 +119,7 @@ class BaseBuffer(base.Buffer):
if hasattr(self, "finished_timeline") and self.finished_timeline == False: if hasattr(self, "finished_timeline") and self.finished_timeline == False:
if "-timeline" in self.name: if "-timeline" in self.name:
self.username = self.session.db[self.name][0]["account"].username self.username = self.session.db[self.name][0]["account"].username
pub.sendMessage("core.change_buffer_title", name=self.session.get_name(), buffer=self.name, title=_("Timeline for {}").format(self.username))
self.finished_timeline = True self.finished_timeline = True
self.put_items_on_list(number_of_items) self.put_items_on_list(number_of_items)
if number_of_items > 0 and self.name != "sent_posts" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True: if number_of_items > 0 and self.name != "sent_posts" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
@@ -136,7 +139,7 @@ class BaseBuffer(base.Buffer):
safe = True safe = True
if self.session.settings["general"]["read_preferences_from_instance"]: if self.session.settings["general"]["read_preferences_from_instance"]:
safe = self.session.expand_spoilers == False safe = self.session.expand_spoilers == False
output.speak(" ".join(self.compose_function(post, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe))) output.speak(" ".join(self.compose_function(post, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)))
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(_("{0} new posts in {1}.").format(number_of_items, self.get_buffer_name())) output.speak(_("{0} new posts in {1}.").format(number_of_items, self.get_buffer_name()))
@@ -167,11 +170,11 @@ class BaseBuffer(base.Buffer):
safe = self.session.expand_spoilers == False safe = self.session.expand_spoilers == False
if self.session.settings["general"]["reverse_timelines"] == False: if self.session.settings["general"]["reverse_timelines"] == False:
for i in elements: for i in elements:
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(True, *post) self.buffer.list.insert_item(True, *post)
else: else:
for i in elements: for i in elements:
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(False, *post) self.buffer.list.insert_item(False, *post)
self.buffer.list.select_item(selection) self.buffer.list.select_item(selection)
output.speak(_(u"%s items retrieved") % (str(len(elements))), True) output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
@@ -205,20 +208,20 @@ class BaseBuffer(base.Buffer):
safe = self.session.expand_spoilers == False safe = self.session.expand_spoilers == False
if self.buffer.list.get_count() == 0: if self.buffer.list.get_count() == 0:
for i in list_to_use: for i in list_to_use:
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(False, *post) self.buffer.list.insert_item(False, *post)
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"]) self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
elif self.buffer.list.get_count() > 0 and number_of_items > 0: elif self.buffer.list.get_count() > 0 and number_of_items > 0:
if self.session.settings["general"]["reverse_timelines"] == False: if self.session.settings["general"]["reverse_timelines"] == False:
items = list_to_use[len(list_to_use)-number_of_items:] items = list_to_use[len(list_to_use)-number_of_items:]
for i in items: for i in items:
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(False, *post) self.buffer.list.insert_item(False, *post)
else: else:
items = list_to_use[0:number_of_items] items = list_to_use[0:number_of_items]
items.reverse() items.reverse()
for i in items: for i in items:
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(True, *post) self.buffer.list.insert_item(True, *post)
log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),)) log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),))
@@ -226,7 +229,7 @@ class BaseBuffer(base.Buffer):
safe = True safe = True
if self.session.settings["general"]["read_preferences_from_instance"]: if self.session.settings["general"]["read_preferences_from_instance"]:
safe = self.session.expand_spoilers == False safe = self.session.expand_spoilers == False
post = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(item, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
if self.session.settings["general"]["reverse_timelines"] == False: if self.session.settings["general"]["reverse_timelines"] == False:
self.buffer.list.insert_item(False, *post) self.buffer.list.insert_item(False, *post)
else: else:
@@ -238,7 +241,7 @@ class BaseBuffer(base.Buffer):
safe = True safe = True
if self.session.settings["general"]["read_preferences_from_instance"]: if self.session.settings["general"]["read_preferences_from_instance"]:
safe = self.session.expand_spoilers == False safe = self.session.expand_spoilers == False
post = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(item, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.list.SetItem(position, 1, post[1]) self.buffer.list.list.SetItem(position, 1, post[1])
def bind_events(self): def bind_events(self):
@@ -414,7 +417,7 @@ class BaseBuffer(base.Buffer):
if len(urls) == 1: if len(urls) == 1:
url=urls[0] url=urls[0]
elif len(urls) > 1: elif len(urls) > 1:
urls_list = dialogs.urlList.urlList() urls_list = urlList.urlList()
urls_list.populate_list(urls) urls_list.populate_list(urls)
if urls_list.get_response() == widgetUtils.OK: if urls_list.get_response() == widgetUtils.OK:
url=urls_list.get_string() url=urls_list.get_string()
@@ -435,7 +438,7 @@ class BaseBuffer(base.Buffer):
if len(urls) == 1: if len(urls) == 1:
url=urls[0] url=urls[0]
elif len(urls) > 1: elif len(urls) > 1:
urls_list = dialogs.urlList.urlList() urls_list = urlList.urlList()
urls_list.populate_list(urls) urls_list.populate_list(urls)
if urls_list.get_response() == widgetUtils.OK: if urls_list.get_response() == widgetUtils.OK:
url=urls_list.get_string() url=urls_list.get_string()
@@ -538,4 +541,75 @@ class BaseBuffer(base.Buffer):
def ocr_image(self): def ocr_image(self):
post = self.get_item() post = self.get_item()
media_list = [] media_list = []
pass if post.reblog != None:
post = post.reblog
for media in post.get("media_attachments"):
if media.get("type", "") == "image":
media_list.append(media)
if len(media_list) > 1:
image_list = [_(u"Picture {0}").format(i+1,) for i in range(0, len(media_list))]
dialog = urlList.urlList(title=_(u"Select the picture"))
dialog.populate_list(image_list)
if dialog.get_response() == widgetUtils.OK:
img = media_list[dialog.get_item()]
else:
return
elif len(media_list) == 1:
img = media_list[0]
else:
return
if self.session.settings["mysc"]["ocr_language"] != "":
ocr_lang = self.session.settings["mysc"]["ocr_language"]
else:
ocr_lang = ocr.OCRSpace.short_langs.index(post.language)
ocr_lang = ocr.OCRSpace.OcrLangs[ocr_lang]
if img["remote_url"] != None:
url = img["remote_url"]
else:
url = img["url"]
api = ocr.OCRSpace.OCRSpaceAPI()
try:
text = api.OCR_URL(url)
except ocr.OCRSpace.APIError as er:
output.speak(_(u"Unable to extract text"))
return
viewer = messages.text(title=_("OCR Result"), text=text["ParsedText"])
response = viewer.message.ShowModal()
viewer.message.Destroy()
def vote(self):
post = self.get_item()
if not hasattr(post, "poll") or post.poll == None:
return
poll = post.poll
try:
poll = self.session.api.poll(id=poll.id)
except MastodonNotFoundError:
output.speak(_("this poll no longer exists."))
return
if poll.expired:
output.speak(_("This poll has already expired."))
return
if poll.voted:
output.speak(_("You have already voted on this poll."))
return
options = poll.options
dlg = attachedPoll(poll_options=[option.title for option in options], multiple=poll.multiple)
answer = dlg.ShowModal()
options = dlg.get_selected()
dlg.Destroy()
if answer != wx.ID_OK:
return
poll = self.session.api_call(call_name="poll_vote", id=poll.id, choices=options, preexec_message=_("Sending vote..."))
def post_from_error(self, visibility, reply_to, data):
title = _("Post")
caption = _("Write your post here")
post = messages.post(session=self.session, title=title, caption=caption)
post.set_post_data(visibility=visibility, data=data)
response = post.message.ShowModal()
if response == wx.ID_OK:
post_data = post.get_data()
call_threaded(self.session.send_post, posts=post_data, reply_to=reply_to, visibility=post.get_visibility())
if hasattr(post.message, "destroy"):
post.message.destroy()

View File

@@ -28,7 +28,7 @@ class ConversationListBuffer(BaseBuffer):
return self.session.db[self.name][index] return self.session.db[self.name][index]
def get_formatted_message(self): def get_formatted_message(self):
return self.compose_function(self.get_conversation(), self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])[1] return self.compose_function(self.get_conversation(), self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])[1]
def get_message(self): def get_message(self):
conversation = self.get_conversation() conversation = self.get_conversation()
@@ -36,7 +36,7 @@ class ConversationListBuffer(BaseBuffer):
return return
template = self.session.settings["templates"]["conversation"] template = self.session.settings["templates"]["conversation"]
post_template = self.session.settings["templates"]["post"] post_template = self.session.settings["templates"]["post"]
t = templates.render_conversation(conversation=conversation, template=template, post_template=post_template, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"]) t = templates.render_conversation(conversation=conversation, template=template, post_template=post_template, settings=self.session.settings, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"])
return t return t
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False): def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
@@ -89,11 +89,11 @@ class ConversationListBuffer(BaseBuffer):
log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function)) log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function))
if self.session.settings["general"]["reverse_timelines"] == False: if self.session.settings["general"]["reverse_timelines"] == False:
for i in elements: for i in elements:
conversation = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"]) conversation = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
self.buffer.list.insert_item(True, *conversation) self.buffer.list.insert_item(True, *conversation)
else: else:
for i in elements: for i in elements:
conversation = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"]) conversation = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
self.buffer.list.insert_item(False, *conversation) self.buffer.list.insert_item(False, *conversation)
self.buffer.list.select_item(selection) self.buffer.list.select_item(selection)
output.speak(_(u"%s items retrieved") % (str(len(elements))), True) output.speak(_(u"%s items retrieved") % (str(len(elements))), True)

View File

@@ -28,6 +28,8 @@ class MentionsBuffer(BaseBuffer):
except Exception as e: except Exception as e:
log.exception("Error %s" % (str(e))) log.exception("Error %s" % (str(e)))
return return
# Attempt to remove items with no statuses attached to them as it might happen when blocked accounts have notifications.
items = [item for item in items if item.status != None]
number_of_items = self.session.order_buffer(self.name, items) number_of_items = self.session.order_buffer(self.name, items)
log.debug("Number of items retrieved: %d" % (number_of_items,)) log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items) self.put_items_on_list(number_of_items)
@@ -49,7 +51,8 @@ class MentionsBuffer(BaseBuffer):
except Exception as e: except Exception as e:
log.exception("Error %s" % (str(e))) log.exception("Error %s" % (str(e)))
return return
print(items) # Attempt to remove items with no statuses attached to them as it might happen when blocked accounts have notifications.
items = [item for item in items if item.status != None]
items_db = self.session.db[self.name] items_db = self.session.db[self.name]
for i in items: for i in items:
if utils.find_item(i, self.session.db[self.name]) == None: if utils.find_item(i, self.session.db[self.name]) == None:
@@ -66,11 +69,11 @@ class MentionsBuffer(BaseBuffer):
safe = self.session.expand_spoilers == False safe = self.session.expand_spoilers == False
if self.session.settings["general"]["reverse_timelines"] == False: if self.session.settings["general"]["reverse_timelines"] == False:
for i in elements: for i in elements:
post = self.compose_function(i.status, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i.status, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(True, *post) self.buffer.list.insert_item(True, *post)
else: else:
for i in elements: for i in elements:
post = self.compose_function(i.status, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i.status, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(False, *post) self.buffer.list.insert_item(False, *post)
self.buffer.list.select_item(selection) self.buffer.list.select_item(selection)
output.speak(_(u"%s items retrieved") % (str(len(elements))), True) output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
@@ -85,20 +88,20 @@ class MentionsBuffer(BaseBuffer):
safe = self.session.expand_spoilers == False safe = self.session.expand_spoilers == False
if self.buffer.list.get_count() == 0: if self.buffer.list.get_count() == 0:
for i in list_to_use: for i in list_to_use:
post = self.compose_function(i.status, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i.status, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(False, *post) self.buffer.list.insert_item(False, *post)
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"]) self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
elif self.buffer.list.get_count() > 0 and number_of_items > 0: elif self.buffer.list.get_count() > 0 and number_of_items > 0:
if self.session.settings["general"]["reverse_timelines"] == False: if self.session.settings["general"]["reverse_timelines"] == False:
items = list_to_use[len(list_to_use)-number_of_items:] items = list_to_use[len(list_to_use)-number_of_items:]
for i in items: for i in items:
post = self.compose_function(i.status, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i.status, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(False, *post) self.buffer.list.insert_item(False, *post)
else: else:
items = list_to_use[0:number_of_items] items = list_to_use[0:number_of_items]
items.reverse() items.reverse()
for i in items: for i in items:
post = self.compose_function(i.status, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(i.status, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
self.buffer.list.insert_item(True, *post) self.buffer.list.insert_item(True, *post)
log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),)) log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),))
@@ -106,7 +109,7 @@ class MentionsBuffer(BaseBuffer):
safe = True safe = True
if self.session.settings["general"]["read_preferences_from_instance"]: if self.session.settings["general"]["read_preferences_from_instance"]:
safe = self.session.expand_spoilers == False safe = self.session.expand_spoilers == False
post = self.compose_function(item.status, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe) post = self.compose_function(item.status, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
if self.session.settings["general"]["reverse_timelines"] == False: if self.session.settings["general"]["reverse_timelines"] == False:
self.buffer.list.insert_item(False, *post) self.buffer.list.insert_item(False, *post)
else: else:

View File

@@ -18,7 +18,7 @@ class NotificationsBuffer(BaseBuffer):
return return
template = self.session.settings["templates"]["notification"] template = self.session.settings["templates"]["notification"]
post_template = self.session.settings["templates"]["post"] post_template = self.session.settings["templates"]["post"]
t = templates.render_notification(notification, template, post_template, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"]) t = templates.render_notification(notification, template, post_template, self.session.settings, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"])
return t return t
def create_buffer(self, parent, name): def create_buffer(self, parent, name):
@@ -42,6 +42,9 @@ class NotificationsBuffer(BaseBuffer):
def unfav(self): def unfav(self):
pass pass
def vote(self):
pass
def can_share(self): def can_share(self):
return False return False

View File

@@ -4,6 +4,7 @@ import logging
import wx import wx
import widgetUtils import widgetUtils
import output import output
from pubsub import pub
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
from controller.buffers.mastodon.base import BaseBuffer from controller.buffers.mastodon.base import BaseBuffer
from controller.mastodon import messages from controller.mastodon import messages
@@ -22,7 +23,7 @@ class UserBuffer(BaseBuffer):
if user == None: if user == None:
return return
template = self.session.settings["templates"]["person"] template = self.session.settings["templates"]["person"]
t = templates.render_user(user=user, template=template, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"]) t = templates.render_user(user=user, template=template, settings=self.session.settings, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"])
return t return t
def bind_events(self): def bind_events(self):
@@ -88,6 +89,11 @@ class UserBuffer(BaseBuffer):
if hasattr(self, "finished_timeline") and self.finished_timeline == False: if hasattr(self, "finished_timeline") and self.finished_timeline == False:
if "-followers" in self.name or "-following" in self.name: if "-followers" in self.name or "-following" in self.name:
self.username = self.session.api.account(id=self.kwargs.get("id")).username self.username = self.session.api.account(id=self.kwargs.get("id")).username
if "-followers" in self.name:
title=_("Followers for {}").format(self.username)
else:
title=_("Following for {}").format(self.username)
pub.sendMessage("core.change_buffer_title", name=self.session.get_name(), buffer=self.name, title=title)
self.finished_timeline = True self.finished_timeline = True
self.put_items_on_list(number_of_items) self.put_items_on_list(number_of_items)
if number_of_items > 0 and self.name != "sent_posts" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True: if number_of_items > 0 and self.name != "sent_posts" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
@@ -123,11 +129,11 @@ class UserBuffer(BaseBuffer):
log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function)) log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function))
if self.session.settings["general"]["reverse_timelines"] == False: if self.session.settings["general"]["reverse_timelines"] == False:
for i in elements: for i in elements:
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"]) post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
self.buffer.list.insert_item(True, *post) self.buffer.list.insert_item(True, *post)
else: else:
for i in elements: for i in elements:
post = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"]) post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"])
self.buffer.list.insert_item(False, *post) self.buffer.list.insert_item(False, *post)
self.buffer.list.select_item(selection) self.buffer.list.select_item(selection)
output.speak(_(u"%s items retrieved") % (str(len(elements))), True) output.speak(_(u"%s items retrieved") % (str(len(elements))), True)

View File

@@ -1,7 +0,0 @@
# -*- coding: utf-8 -*-
from .base import BaseBuffer
from .directMessages import DirectMessagesBuffer, SentDirectMessagesBuffer
from .list import ListBuffer
from .people import PeopleBuffer
from .trends import TrendsBuffer
from .search import SearchBuffer, SearchPeopleBuffer, ConversationBuffer

View File

@@ -1,663 +0,0 @@
# -*- coding: utf-8 -*-
import time
import wx
import widgetUtils
import arrow
import webbrowser
import output
import config
import sound
import languageHandler
import logging
from audio_services import youtube_utils
from controller.buffers.base import base
from sessions.twitter import compose, utils, reduce, templates
from mysc.thread_utils import call_threaded
from tweepy.errors import TweepyException
from tweepy.cursor import Cursor
from pubsub import pub
from extra import ocr
from sessions.twitter.long_tweets import twishort, tweets
from wxUI import buffers, dialogs, commonMessageDialogs, menus
from controller.twitter import user, messages
log = logging.getLogger("controller.buffers")
def _tweets_exist(function):
""" A decorator to execute a function only if the selected buffer contains at least one item."""
def function_(self, *args, **kwargs):
if self.buffer.list.get_count() > 0:
function(self, *args, **kwargs)
return function_
class BaseBuffer(base.Buffer):
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, compose_func="compose_tweet", *args, **kwargs):
super(BaseBuffer, self).__init__(parent, function, *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
if bufferType != None:
self.buffer = getattr(buffers.twitter, bufferType)(parent, name)
else:
self.buffer = buffers.twitter.basePanel(parent, name)
self.invisible = True
self.name = name
self.type = self.buffer.type
self.session = sessionObject
self.compose_function = getattr(compose, compose_func)
log.debug("Compose_function: %s" % (self.compose_function,))
self.account = account
self.buffer.account = account
self.bind_events()
self.sound = sound
if "-timeline" in self.name or "-favorite" in self.name:
self.finished_timeline = False
# Add a compatibility layer for username based timelines from config.
# ToDo: Remove this in some new versions of the client, when user ID timelines become mandatory.
try:
int(self.kwargs["user_id"])
except ValueError:
self.is_screen_name = True
self.kwargs["screen_name"] = self.kwargs["user_id"]
self.kwargs.pop("user_id")
def get_buffer_name(self):
""" Get buffer name from a set of different techniques."""
# firstly let's take the easier buffers.
basic_buffers = dict(home_timeline=_(u"Home"), mentions=_(u"Mentions"), direct_messages=_(u"Direct messages"), sent_direct_messages=_(u"Sent direct messages"), sent_tweets=_(u"Sent tweets"), favourites=_(u"Likes"), followers=_(u"Followers"), friends=_(u"Friends"), blocked=_(u"Blocked users"), muted=_(u"Muted users"))
if self.name in list(basic_buffers.keys()):
return basic_buffers[self.name]
# Check user timelines
elif hasattr(self, "username"):
if "-timeline" in self.name:
return _(u"{username}'s timeline").format(username=self.username,)
elif "-favorite" in self.name:
return _(u"{username}'s likes").format(username=self.username,)
elif "-followers" in self.name:
return _(u"{username}'s followers").format(username=self.username,)
elif "-friends" in self.name:
return _(u"{username}'s friends").format(username=self.username,)
log.error("Error getting name for buffer %s" % (self.name,))
return _(u"Unknown buffer")
def post_status(self, *args, **kwargs):
title = _("Tweet")
caption = _("Write the tweet here")
tweet = messages.tweet(self.session, title, caption, "")
response = tweet.message.ShowModal()
if response == wx.ID_OK:
tweet_data = tweet.get_tweet_data()
call_threaded(self.session.send_tweet, *tweet_data)
if hasattr(tweet.message, "destroy"):
tweet.message.destroy()
def get_formatted_message(self):
if self.type == "dm" or self.name == "direct_messages":
return self.compose_function(self.get_right_tweet(), self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)[1]
return self.get_message()
def get_message(self):
template = self.session.settings["templates"]["tweet"]
tweet = self.get_right_tweet()
t = templates.render_tweet(tweet, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"])
return t
def get_full_tweet(self):
tweet = self.get_right_tweet()
tweetsList = []
tweet_id = tweet.id
message = None
if hasattr(tweet, "message"):
message = tweet.message
try:
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)
except TweepyException as e:
utils.twitter_error(e)
return
if message != None:
tweet.message = message
l = tweets.is_long(tweet)
while l != False:
tweetsList.append(tweet)
try:
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)
except TweepyException as e:
utils.twitter_error(e)
return
l = tweets.is_long(tweet)
if l == False:
tweetsList.append(tweet)
return (tweet, tweetsList)
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
if self.name != "direct_messages":
val = self.session.call_paged(self.function, self.name, *self.args, **self.kwargs)
else:
# 50 results are allowed per API call, so let's assume max value can be 50.
# reference: https://developer.twitter.com/en/docs/twitter-api/v1/direct-messages/sending-and-receiving/api-reference/list-events
if self.session.settings["general"]["max_tweets_per_call"] > 50:
count = 50
else:
count = self.session.settings["general"]["max_tweets_per_call"]
# try to retrieve the cursor for the current buffer.
try:
val = getattr(self.session.twitter, self.function)(return_cursors=True, count=count, *self.args, **self.kwargs)
if type(val) == tuple:
val, cursor = val
if type(cursor) == tuple:
cursor = cursor[1]
cursors = self.session.db["cursors"]
cursors[self.name] = cursor
self.session.db["cursors"] = cursors
results = [i for i in val]
val = results
val.reverse()
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]
self.session.save_users(user_ids)
except TweepyException as e:
log.exception("Error %s" % (str(e)))
return
number_of_items = self.session.order_buffer(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
if "-timeline" in self.name:
self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name
elif "-favorite" in self.name:
self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name
self.finished_timeline = True
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
self.session.sound.play(self.sound)
# Autoread settings
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
self.auto_read(number_of_items)
return number_of_items
def auto_read(self, number_of_items):
if 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:
if self.session.settings["general"]["reverse_timelines"] == False:
tweet = self.session.db[self.name][-1]
else:
tweet = self.session.db[self.name][0]
output.speak(_(u"New tweet in {0}").format(self.get_buffer_name()))
output.speak(" ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)))
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 tweets in {1}.").format(number_of_items, self.get_buffer_name()))
def get_more_items(self):
elements = []
if self.session.settings["general"]["reverse_timelines"] == False:
last_id = self.session.db[self.name][0].id
else:
last_id = self.session.db[self.name][-1].id
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)
except TweepyException as e:
log.exception("Error %s" % (str(e)))
return
if items == None:
return
items_db = self.session.db[self.name]
self.session.add_users_from_results(items)
for i in items:
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 = self.session.check_quoted_status(i)
i = self.session.check_long_tweet(i)
elements.append(i)
if self.session.settings["general"]["reverse_timelines"] == False:
items_db.insert(0, i)
else:
items_db.append(i)
self.session.db[self.name] = items_db
selection = self.buffer.list.get_selected()
log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function))
if self.session.settings["general"]["reverse_timelines"] == False:
for i in elements:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
self.buffer.list.insert_item(True, *tweet)
else:
for i in items:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
self.buffer.list.insert_item(False, *tweet)
self.buffer.list.select_item(selection)
output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
def remove_buffer(self, force=False):
if "-timeline" in self.name:
if force == False:
dlg = commonMessageDialogs.remove_buffer()
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.name[:-9] in self.session.settings["other_buffers"]["timelines"]:
self.session.settings["other_buffers"]["timelines"].remove(self.name[:-9])
self.session.settings.write()
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
elif "favorite" in self.name:
if force == False:
dlg = commonMessageDialogs.remove_buffer()
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.name[:-9] in self.session.settings["other_buffers"]["favourites_timelines"]:
self.session.settings["other_buffers"]["favourites_timelines"].remove(self.name[:-9])
if self.name in self.session.db:
self.session.db.pop(self.name)
self.session.settings.write()
return True
elif dlg == widgetUtils.NO:
return False
else:
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
return False
def remove_tweet(self, id):
if type(self.session.db[self.name]) == dict: return
items = self.session.db[self.name]
for i in range(0, len(items)):
if items[i].id == id:
items.pop(i)
self.remove_item(i)
self.session.db[self.name] = items
def put_items_on_list(self, number_of_items):
list_to_use = self.session.db[self.name]
if number_of_items == 0 and self.session.settings["general"]["persist_size"] == 0: return
log.debug("The list contains %d items " % (self.buffer.list.get_count(),))
log.debug("Putting %d items on the list" % (number_of_items,))
if self.buffer.list.get_count() == 0:
for i in list_to_use:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
self.buffer.list.insert_item(False, *tweet)
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
elif self.buffer.list.get_count() > 0 and number_of_items > 0:
if self.session.settings["general"]["reverse_timelines"] == False:
items = list_to_use[len(list_to_use)-number_of_items:]
for i in items:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
self.buffer.list.insert_item(False, *tweet)
else:
items = list_to_use[0:number_of_items]
items.reverse()
for i in items:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
self.buffer.list.insert_item(True, *tweet)
log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),))
def add_new_item(self, item):
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
if self.session.settings["general"]["reverse_timelines"] == False:
self.buffer.list.insert_item(False, *tweet)
else:
self.buffer.list.insert_item(True, *tweet)
if 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(" ".join(tweet[:2]), speech=self.session.settings["reporting"]["speech_reporting"], braille=self.session.settings["reporting"]["braille_reporting"])
def bind_events(self):
log.debug("Binding events...")
self.buffer.set_focus_function(self.onFocus)
widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_status, self.buffer.tweet)
# if self.type == "baseBuffer":
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.share_item, self.buffer.retweet)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.send_message, self.buffer.dm)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.reply, self.buffer.reply)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
def show_menu(self, ev, pos=0, *args, **kwargs):
if self.buffer.list.get_count() == 0: return
if self.name == "sent_tweets" or self.name == "direct_messages":
menu = menus.sentPanelMenu()
elif self.name == "direct_messages":
menu = menus.dmPanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.send_message, menuitem=menu.reply)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
else:
menu = menus.basePanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.retweet)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.fav, menuitem=menu.fav)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.unfav, menuitem=menu.unfav)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.url_, menuitem=menu.openUrl)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.audio, menuitem=menu.play)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.destroy_status, menuitem=menu.remove)
if hasattr(menu, "openInBrowser"):
widgetUtils.connect_event(menu, widgetUtils.MENU, self.open_in_browser, menuitem=menu.openInBrowser)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def view(self, *args, **kwargs):
pub.sendMessage("execute-action", action="view_item")
def copy(self, *args, **kwargs):
pub.sendMessage("execute-action", action="copy_to_clipboard")
def user_actions(self, *args, **kwargs):
pub.sendMessage("execute-action", action="follow")
def fav(self, *args, **kwargs):
pub.sendMessage("execute-action", action="add_to_favourites")
def unfav(self, *args, **kwargs):
pub.sendMessage("execute-action", action="remove_from_favourites")
def delete_item_(self, *args, **kwargs):
pub.sendMessage("execute-action", action="delete_item")
def url_(self, *args, **kwargs):
self.url()
def show_menu_by_key(self, ev):
if self.buffer.list.get_count() == 0:
return
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
def get_tweet(self):
if hasattr(self.session.db[self.name][self.buffer.list.get_selected()], "retweeted_status"):
tweet = self.session.db[self.name][self.buffer.list.get_selected()].retweeted_status
else:
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
return tweet
def get_right_tweet(self):
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
return tweet
def can_share(self):
tweet = self.get_right_tweet()
user = self.session.get_user(tweet.user)
is_protected = user.protected
return is_protected==False
@_tweets_exist
def reply(self, *args, **kwargs):
tweet = self.get_right_tweet()
user = self.session.get_user(tweet.user)
screen_name = user.screen_name
id = tweet.id
users = utils.get_all_mentioned(tweet, self.session.db, field="screen_name")
ids = utils.get_all_mentioned(tweet, self.session.db, field="id")
# Build the window title
if len(users) < 1:
title=_("Reply to {arg0}").format(arg0=screen_name)
else:
title=_("Reply")
message = messages.reply(self.session, title, _("Reply to %s") % (screen_name,), "", users=users, ids=ids)
if message.message.ShowModal() == widgetUtils.OK:
if config.app["app-settings"]["remember_mention_and_longtweet"]:
if len(users) > 0:
config.app["app-settings"]["mention_all"] = message.message.mention_all.GetValue()
config.app.write()
tweet_data = dict(text=message.message.text.GetValue(), attachments=message.attachments, poll_options=message.poll_options, poll_period=message.poll_period)
call_threaded(self.session.reply, in_reply_to_status_id=id, text=message.message.text.GetValue(), attachments=message.attachments, exclude_reply_user_ids=message.get_ids())
if hasattr(message.message, "destroy"): message.message.destroy()
self.session.settings.write()
@_tweets_exist
def send_message(self, *args, **kwargs):
tweet = self.get_right_tweet()
if self.type == "dm":
screen_name = self.session.get_user(tweet.message_create["sender_id"]).screen_name
users = [screen_name]
elif self.type == "people":
screen_name = tweet.screen_name
users = [screen_name]
else:
screen_name = self.session.get_user(tweet.user).screen_name
users = utils.get_all_users(tweet, self.session)
dm = messages.dm(self.session, _("Direct message to %s") % (screen_name,), _("New direct message"), users)
if dm.message.ShowModal() == widgetUtils.OK:
screen_name = dm.message.cb.GetValue()
user = self.session.get_user_by_screen_name(screen_name)
recipient_id = user
text = dm.message.text.GetValue()
if len(dm.attachments) > 0:
attachment = dm.attachments[0]
else:
attachment = None
call_threaded(self.session.direct_message, text=text, recipient=recipient_id, attachment=attachment)
if hasattr(dm.message, "destroy"): dm.message.destroy()
@_tweets_exist
def share_item(self, *args, **kwargs):
if self.can_share() == False:
return output.speak(_("This action is not supported on protected accounts."))
tweet = self.get_right_tweet()
id = tweet.id
if self.session.settings["general"]["retweet_mode"] == "ask":
answer = commonMessageDialogs.retweet_question(self.buffer)
if answer == widgetUtils.YES:
self._retweet_with_comment(tweet, id)
elif answer == widgetUtils.NO:
self._direct_retweet(id)
elif self.session.settings["general"]["retweet_mode"] == "direct":
self._direct_retweet(id)
else:
self._retweet_with_comment(tweet, id)
def _retweet_with_comment(self, tweet, id):
if hasattr(tweet, "retweeted_status"):
tweet = tweet.retweeted_status
retweet = messages.tweet(session=self.session, title=_("Quote"), caption=_("Add your comment to the tweet"), thread_mode=False)
if retweet.message.ShowModal() == widgetUtils.OK:
text = retweet.message.text.GetValue()
tweet_data = dict(text=text, attachments=retweet.attachments, poll_period=retweet.poll_period, poll_options=retweet.poll_options)
tweet_data.update(quote_tweet_id=id)
call_threaded(self.session.send_tweet, *[tweet_data])
if hasattr(retweet.message, "destroy"):
retweet.message.Destroy()
def _direct_retweet(self, id):
item = self.session.api_call(call_name="retweet", _sound="retweet_send.ogg", id=id)
def onFocus(self, *args, **kwargs):
tweet = self.get_tweet()
if self.session.settings["general"]["relative_times"] == True:
# fix this:
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at, locale="en")
ts = original_date.humanize(locale=languageHandler.getLanguage())
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 2, ts)
if self.session.settings['sound']['indicate_audio'] and utils.is_audio(tweet):
self.session.sound.play("audio.ogg")
if self.session.settings['sound']['indicate_geo'] and utils.is_geocoded(tweet):
self.session.sound.play("geo.ogg")
if self.session.settings['sound']['indicate_img'] and utils.is_media(tweet):
self.session.sound.play("image.ogg")
can_share = self.can_share()
pub.sendMessage("toggleShare", shareable=can_share)
self.buffer.retweet.Enable(can_share)
def audio(self, url='', *args, **kwargs):
if sound.URLPlayer.player.is_playing():
return sound.URLPlayer.stop_audio()
tweet = self.get_tweet()
if tweet == None: return
urls = utils.find_urls(tweet, twitter_media=True)
if len(urls) == 1:
url=urls[0]
elif len(urls) > 1:
urls_list = dialogs.urlList.urlList()
urls_list.populate_list(urls)
if urls_list.get_response() == widgetUtils.OK:
url=urls_list.get_string()
if hasattr(urls_list, "destroy"): urls_list.destroy()
if url != '':
# try:
sound.URLPlayer.play(url, self.session.settings["sound"]["volume"])
# except:
# log.error("Exception while executing audio method.")
# @_tweets_exist
def url(self, url='', announce=True, *args, **kwargs):
if url == '':
tweet = self.get_tweet()
urls = utils.find_urls(tweet)
if len(urls) == 1:
url=urls[0]
elif len(urls) > 1:
urls_list = dialogs.urlList.urlList()
urls_list.populate_list(urls)
if urls_list.get_response() == widgetUtils.OK:
url=urls_list.get_string()
if hasattr(urls_list, "destroy"): urls_list.destroy()
if url != '':
if announce:
output.speak(_(u"Opening URL..."), True)
webbrowser.open_new_tab(url)
def clear_list(self):
dlg = commonMessageDialogs.clear_list()
if dlg == widgetUtils.YES:
self.session.db[self.name] = []
self.buffer.list.clear()
@_tweets_exist
def destroy_status(self, *args, **kwargs):
index = self.buffer.list.get_selected()
if self.type == "events" or self.type == "people" or self.type == "empty" or self.type == "account": return
answer = commonMessageDialogs.delete_tweet_dialog(None)
if answer == widgetUtils.YES:
items = self.session.db[self.name]
try:
if self.name == "direct_messages" or self.name == "sent_direct_messages":
self.session.twitter.delete_direct_message(id=self.get_right_tweet().id)
items.pop(index)
else:
self.session.twitter.destroy_status(id=self.get_right_tweet().id)
items.pop(index)
self.buffer.list.remove_item(index)
except TweepyException:
self.session.sound.play("error.ogg")
self.session.db[self.name] = items
@_tweets_exist
def user_details(self):
tweet = self.get_right_tweet()
if self.type == "dm":
users = [self.session.get_user(tweet.message_create["sender_id"]).screen_name]
elif self.type == "people":
users = [tweet.screen_name]
else:
users = utils.get_all_users(tweet, self.session)
dlg = dialogs.utils.selectUserDialog(title=_(u"User details"), users=users)
if dlg.get_response() == widgetUtils.OK:
user.profileController(session=self.session, user=dlg.get_user())
if hasattr(dlg, "destroy"): dlg.destroy()
def get_quoted_tweet(self, tweet):
quoted_tweet = self.session.twitter.get_status(id=tweet.id)
quoted_tweet.text = utils.find_urls_in_text(quoted_tweet.text, quoted_tweet.entities)
l = tweets.is_long(quoted_tweet)
id = tweets.get_id(l)
original_tweet = self.session.twitter.get_status(id=id)
original_tweet.text = utils.find_urls_in_text(original_tweet.text, original_tweet.entities)
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, self.session.db, self.session.settings["general"]["relative_times"])
def get_item_url(self):
tweet = self.get_tweet()
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)
def add_to_favorites(self):
id = self.get_tweet().id
call_threaded(self.session.api_call, call_name="create_favorite", _sound="favourite.ogg", id=id)
def remove_from_favorites(self):
id = self.get_tweet().id
call_threaded(self.session.api_call, call_name="destroy_favorite", id=id)
def toggle_favorite(self):
id = self.get_tweet().id
tweet = self.session.twitter.get_status(id=id, include_ext_alt_text=True, tweet_mode="extended")
if tweet.favorited == False:
call_threaded(self.session.api_call, call_name="create_favorite", _sound="favourite.ogg", id=id)
else:
call_threaded(self.session.api_call, call_name="destroy_favorite", id=id)
def view_item(self):
if self.type == "dm" or self.name == "direct_messages":
non_tweet = self.get_formatted_message()
item = self.get_right_tweet()
original_date = arrow.get(int(item.created_timestamp))
date = original_date.shift(seconds=self.session.db["utc_offset"]).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
msg = messages.viewTweet(non_tweet, [], False, date=date)
else:
tweet, tweetsList = self.get_full_tweet()
msg = messages.viewTweet(tweet, tweetsList, utc_offset=self.session.db["utc_offset"], item_url=self.get_item_url())
def reverse_geocode(self, geocoder):
try:
tweet = self.get_tweet()
if tweet.coordinates != None:
x = tweet.coordinates["coordinates"][0]
y = tweet.coordinates["coordinates"][1]
address = geocoder.reverse_geocode(y, x, language = languageHandler.curLang)
return address
else:
output.speak(_("There are no coordinates in this tweet"))
# except GeocoderError:
# output.speak(_(u"There are no results for the coordinates in this tweet"))
except ValueError:
output.speak(_(u"Error decoding coordinates. Try again later."))
except KeyError:
pass
except AttributeError:
pass
def ocr_image(self):
tweet = self.get_tweet()
media_list = []
if hasattr(tweet, "entities") and tweet.entities.get("media") != None:
[media_list.append(i) for i in tweet.entities["media"] if i not in media_list]
elif hasattr(tweet, "retweeted_status") and tweet.retweeted_status.get("media") != None:
[media_list.append(i) for i in tweet.retweeted_status.entities["media"] if i not in media_list]
elif hasattr(tweet, "quoted_status") and tweet.quoted_status.entities.get("media") != None:
[media_list.append(i) for i in tweet.quoted_status.entities["media"] if i not in media_list]
if len(media_list) > 1:
image_list = [_(u"Picture {0}").format(i,) for i in range(0, len(media_list))]
dialog = dialogs.urlList.urlList(title=_(u"Select the picture"))
if dialog.get_response() == widgetUtils.OK:
img = media_list[dialog.get_item()]
else:
return
elif len(media_list) == 1:
img = media_list[0]
else:
output.speak(_(u"Invalid buffer"))
return
if self.session.settings["mysc"]["ocr_language"] != "":
ocr_lang = self.session.settings["mysc"]["ocr_language"]
else:
ocr_lang = ocr.OCRSpace.short_langs.index(tweet.lang)
ocr_lang = ocr.OCRSpace.OcrLangs[ocr_lang]
api = ocr.OCRSpace.OCRSpaceAPI()
try:
text = api.OCR_URL(img["media_url"], lang=ocr_lang)
except ocr.OCRSpace.APIError as er:
output.speak(_(u"Unable to extract text"))
return
msg = messages.viewTweet(text["ParsedText"], [], False)

View File

@@ -1,164 +0,0 @@
# -*- coding: utf-8 -*-
import widgetUtils
import arrow
import webbrowser
import output
import config
import languageHandler
import logging
from sessions.twitter import compose, utils, templates
from mysc.thread_utils import call_threaded
from tweepy.errors import TweepyException
from pubsub import pub
from wxUI import commonMessageDialogs
from controller.twitter import messages
from . import base
log = logging.getLogger("controller.buffers.twitter.dmBuffer")
class DirectMessagesBuffer(base.BaseBuffer):
def get_more_items(self):
# 50 results are allowed per API call, so let's assume max value can be 50.
# reference: https://developer.twitter.com/en/docs/twitter-api/v1/direct-messages/sending-and-receiving/api-reference/list-events
if self.session.settings["general"]["max_tweets_per_call"] > 50:
count = 50
else:
count = self.session.settings["general"]["max_tweets_per_call"]
total = 0
# try to retrieve the cursor for the current buffer.
cursor = self.session.db["cursors"].get(self.name)
try:
items = getattr(self.session.twitter, self.function)(return_cursors=True, cursor=cursor, count=count, *self.args, **self.kwargs)
if type(items) == tuple:
items, cursor = items
if type(cursor) == tuple:
cursor = cursor[1]
cursors = self.session.db["cursors"]
cursors[self.name] = cursor
self.session.db["cursors"] = cursors
results = [i for i in items]
items = results
log.debug("Retrieved %d items for cursored search in function %s" % (len(items), self.function))
except TweepyException as e:
log.exception("Error %s" % (str(e)))
return
if items == None:
return
sent = []
received = []
sent_dms = self.session.db["sent_direct_messages"]
received_dms = self.session.db["direct_messages"]
for i in items:
if int(i.message_create["sender_id"]) == self.session.db["user_id"]:
if self.session.settings["general"]["reverse_timelines"] == False:
sent_dms.insert(0, i)
sent.append(i)
else:
sent_dms.append(i)
sent.insert(0, i)
else:
if self.session.settings["general"]["reverse_timelines"] == False:
received_dms.insert(0, i)
received.append(i)
else:
received_dms.append(i)
received.insert(0, i)
total = total+1
self.session.db["direct_messages"] = received_dms
self.session.db["sent_direct_messages"] = sent_dms
user_ids = [item.message_create["sender_id"] for item in items]
self.session.save_users(user_ids)
pub.sendMessage("twitter.more_sent_dms", data=sent, account=self.session.db["user_name"])
selected = self.buffer.list.get_selected()
if self.session.settings["general"]["reverse_timelines"] == True:
for i in received:
if int(i.message_create["sender_id"]) == self.session.db["user_id"]:
continue
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
self.buffer.list.insert_item(True, *tweet)
self.buffer.list.select_item(selected)
else:
for i in received:
if int(i.message_create["sender_id"]) == self.session.db["user_id"]:
continue
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
self.buffer.list.insert_item(True, *tweet)
output.speak(_(u"%s items retrieved") % (total), True)
def reply(self, *args, **kwargs):
return self.send_message()
def onFocus(self, *args, **kwargs):
tweet = self.get_tweet()
if self.session.settings["general"]["relative_times"] == True:
# fix this:
original_date = arrow.get(int(tweet.created_timestamp))
ts = original_date.humanize(locale=languageHandler.getLanguage())
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 2, ts)
if self.session.settings['sound']['indicate_audio'] and utils.is_audio(tweet):
self.session.sound.play("audio.ogg")
if self.session.settings['sound']['indicate_img'] and utils.is_media(tweet):
self.session.sound.play("image.ogg")
def clear_list(self):
dlg = commonMessageDialogs.clear_list()
if dlg == widgetUtils.YES:
self.session.db[self.name] = []
self.buffer.list.clear()
def auto_read(self, number_of_items):
if 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:
if self.session.settings["general"]["reverse_timelines"] == False:
tweet = self.session.db[self.name][-1]
else:
tweet = self.session.db[self.name][0]
output.speak(_(u"New direct message"))
output.speak(" ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)))
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 direct messages.").format(number_of_items,))
def open_in_browser(self, *args, **kwargs):
output.speak(_(u"This action is not supported in the buffer yet."))
def get_message(self):
template = self.session.settings["templates"]["dm"]
dm = self.get_right_tweet()
t = templates.render_dm(dm, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"])
return t
class SentDirectMessagesBuffer(DirectMessagesBuffer):
def __init__(self, *args, **kwargs):
super(SentDirectMessagesBuffer, self).__init__(*args, **kwargs)
if ("sent_direct_messages" in self.session.db) == False:
self.session.db["sent_direct_messages"] = []
def get_more_items(self):
output.speak(_(u"Getting more items cannot be done in this buffer. Use the direct messages buffer instead."))
def start_stream(self, *args, **kwargs):
pass
def put_more_items(self, items):
if self.session.settings["general"]["reverse_timelines"] == True:
for i in items:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
self.buffer.list.insert_item(False, *tweet)
else:
for i in items:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
self.buffer.list.insert_item(False, *tweet)
def get_message(self):
template = self.session.settings["templates"]["dm_sent"]
dm = self.get_right_tweet()
t = templates.render_dm(dm, template, self.session, relative_times=self.session.settings["general"]["relative_times"], offset_seconds=self.session.db["utc_offset"])
return t
def view_item(self):
non_tweet = self.get_formatted_message()
item = self.get_right_tweet()
original_date = arrow.get(int(item.created_timestamp))
date = original_date.shift(seconds=self.session.db["utc_offset"]).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
msg = messages.viewTweet(non_tweet, [], False, date=date)

View File

@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
import widgetUtils
import logging
from tweepy.cursor import Cursor
from wxUI import dialogs, commonMessageDialogs
from . import base
log = logging.getLogger("controller.buffers.twitter.listBuffer")
class ListBuffer(base.BaseBuffer):
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, list_id=None, *args, **kwargs):
super(ListBuffer, self).__init__(parent, function, name, sessionObject, account, sound=None, bufferType=None, *args, **kwargs)
self.users = []
self.list_id = list_id
self.kwargs["list_id"] = list_id
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
self.get_user_ids()
super(ListBuffer, self).start_stream(mandatory, play_sound, avoid_autoreading)
def get_user_ids(self):
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:
self.users.append(i.id)
def remove_buffer(self, force=False):
if force == False:
dlg = commonMessageDialogs.remove_buffer()
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.name[:-5] in self.session.settings["other_buffers"]["lists"]:
self.session.settings["other_buffers"]["lists"].remove(self.name[:-5])
if self.name in self.session.db:
self.session.db.pop(self.name)
self.session.settings.write()
return True
elif dlg == widgetUtils.NO:
return False

View File

@@ -1,254 +0,0 @@
# -*- coding: utf-8 -*-
import time
import widgetUtils
import webbrowser
import output
import config
import logging
from mysc.thread_utils import call_threaded
from tweepy.errors import TweepyException
from pubsub import pub
from controller.twitter import user, messages
from sessions.twitter import compose, templates
from wxUI import commonMessageDialogs, menus
from . import base
log = logging.getLogger("controller.buffers.twitter.peopleBuffer")
def _tweets_exist(function):
""" A decorator to execute a function only if the selected buffer contains at least one item."""
def function_(self, *args, **kwargs):
if self.buffer.list.get_count() > 0:
function(self, *args, **kwargs)
return function_
class PeopleBuffer(base.BaseBuffer):
def __init__(self, parent, function, name, sessionObject, account, bufferType=None, *args, **kwargs):
super(PeopleBuffer, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.compose_function = compose.compose_followers_list
log.debug("Compose_function: %s" % (self.compose_function,))
self.get_tweet = self.get_right_tweet
self.url = self.interact
if "-followers" in self.name or "-friends" in self.name:
self.finished_timeline = False
# Add a compatibility layer for username based timelines from config.
# ToDo: Remove this in some new versions of the client, when user ID timelines become mandatory.
try:
int(self.kwargs["user_id"])
except ValueError:
self.is_screen_name = True
self.kwargs["screen_name"] = self.kwargs["user_id"]
self.kwargs.pop("user_id")
def remove_buffer(self, force=True):
if "-followers" in self.name:
if force == False:
dlg = commonMessageDialogs.remove_buffer()
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.name[:-10] in self.session.settings["other_buffers"]["followers_timelines"]:
self.session.settings["other_buffers"]["followers_timelines"].remove(self.name[:-10])
if self.name in self.session.db:
self.session.db.pop(self.name)
self.session.settings.write()
return True
elif dlg == widgetUtils.NO:
return False
elif "-friends" in self.name:
if force == False:
dlg = commonMessageDialogs.remove_buffer()
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.name[:-8] in self.session.settings["other_buffers"]["friends_timelines"]:
self.session.settings["other_buffers"]["friends_timelines"].remove(self.name[:-8])
if self.name in self.session.db:
self.session.db.pop(self.name)
self.session.settings.write()
return True
elif dlg == widgetUtils.NO:
return False
else:
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
return False
def onFocus(self, ev):
pass
def get_message(self):
template = self.session.settings["templates"]["person"]
user = self.get_right_tweet()
t = templates.render_person(user, template, self.session, relative_times=True, offset_seconds=self.session.db["utc_offset"])
return t
def delete_item(self): pass
@_tweets_exist
def reply(self, *args, **kwargs):
tweet = self.get_right_tweet()
screen_name = tweet.screen_name
message = messages.tweet(session=self.session, title=_("Mention"), caption=_("Mention to %s") % (screen_name,), text="@%s " % (screen_name,), thread_mode=False)
if message.message.ShowModal() == widgetUtils.OK:
tweet_data = message.get_tweet_data()
call_threaded(self.session.send_tweet, *tweet_data)
if hasattr(message.message, "destroy"):
message.message.destroy()
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
try:
val = getattr(self.session.twitter, self.function)(return_cursors=True, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs)
if type(val) == tuple:
val, cursor = val
if type(cursor) == tuple:
cursor = cursor[1]
cursors = self.session.db["cursors"]
cursors[self.name] = cursor
self.session.db["cursors"] = cursors
results = [i for i in val]
val = results
val.reverse()
log.debug("Retrieved %d items from cursored search in function %s" % (len(val), self.function))
except TweepyException as e:
log.exception("Error %s" % (str(e)))
return
number_of_items = self.session.order_people(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
self.username = self.session.api_call("get_user", **self.kwargs).screen_name
self.finished_timeline = True
if number_of_items > 0 and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
self.session.sound.play(self.sound)
# Autoread settings
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
self.auto_read(number_of_items)
return number_of_items
def get_more_items(self):
try:
cursor = self.session.db["cursors"].get(self.name)
items = getattr(self.session.twitter, self.function)(return_cursors=True, users=True, cursor=cursor, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs)
if type(items) == tuple:
items, cursor = items
if type(cursor) == tuple:
cursor = cursor[1]
cursors = self.session.db["cursors"]
cursors[self.name] = cursor
self.session.db["cursors"] = cursors
results = [i for i in items]
items = results
log.debug("Retrieved %d items from cursored search in function %s" % (len(items), self.function))
except TweepyException as e:
log.exception("Error %s" % (str(e)))
return
if items == None:
return
items_db = self.session.db[self.name]
for i in items:
if self.session.settings["general"]["reverse_timelines"] == False:
items_db.insert(0, i)
else:
items_db.append(i)
self.session.db[self.name] = items_db
selected = self.buffer.list.get_selected()
if self.session.settings["general"]["reverse_timelines"] == True:
for i in items:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session)
self.buffer.list.insert_item(True, *tweet)
self.buffer.list.select_item(selected)
else:
for i in items:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session)
self.buffer.list.insert_item(True, *tweet)
output.speak(_(u"%s items retrieved") % (len(items)), True)
def put_items_on_list(self, number_of_items):
log.debug("The list contains %d items" % (self.buffer.list.get_count(),))
# log.debug("Putting %d items on the list..." % (number_of_items,))
if self.buffer.list.get_count() == 0:
for i in self.session.db[self.name]:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session)
self.buffer.list.insert_item(False, *tweet)
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
# self.buffer.set_list_position()
elif self.buffer.list.get_count() > 0:
if self.session.settings["general"]["reverse_timelines"] == False:
for i in self.session.db[self.name][len(self.session.db[self.name])-number_of_items:]:
tweet = self.compose_function(i, self.session.db)
self.buffer.list.insert_item(False, *tweet)
else:
items = self.session.db[self.name][0:number_of_items]
items.reverse()
for i in items:
tweet = self.compose_function(i, self.session.db)
self.buffer.list.insert_item(True, *tweet)
log.debug("now the list contains %d items" % (self.buffer.list.get_count(),))
def get_right_tweet(self):
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
return tweet
def add_new_item(self, item):
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session)
if self.session.settings["general"]["reverse_timelines"] == False:
self.buffer.list.insert_item(False, *tweet)
else:
self.buffer.list.insert_item(True, *tweet)
if 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(" ".join(tweet))
def clear_list(self):
dlg = commonMessageDialogs.clear_list()
if dlg == widgetUtils.YES:
self.session.db[self.name] = []
self.session.db["cursors"][self.name] = -1
self.buffer.list.clear()
def interact(self):
user.profileController(self.session, user=self.get_right_tweet().screen_name)
def show_menu(self, ev, pos=0, *args, **kwargs):
menu = menus.peoplePanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.send_message, menuitem=menu.reply)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.details, menuitem=menu.details)
# widgetUtils.connect_event(menu, widgetUtils.MENU, self.lists, menuitem=menu.lists)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
if hasattr(menu, "openInBrowser"):
widgetUtils.connect_event(menu, widgetUtils.MENU, self.open_in_browser, menuitem=menu.openInBrowser)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def details(self, *args, **kwargs):
pub.sendMessage("execute-action", action="user_details")
def auto_read(self, number_of_items):
if 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:
if self.session.settings["general"]["reverse_timelines"] == False:
tweet = self.session.db[self.name][-1]
else:
tweet = self.session.db[self.name][0]
output.speak(" ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)))
elif number_of_items > 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
output.speak(_(u"{0} new followers.").format(number_of_items))
def get_item_url(self, *args, **kwargs):
tweet = self.get_tweet()
url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name)
return url
def view_item(self):
item_url = self.get_item_url()
non_tweet = self.get_formatted_message()
msg = messages.viewTweet(non_tweet, [], False, item_url=item_url)

View File

@@ -1,187 +0,0 @@
# -*- coding: utf-8 -*-
import time
import locale
import widgetUtils
import logging
from tweepy.errors import TweepyException
from wxUI import commonMessageDialogs
from . import base, people
log = logging.getLogger("controller.buffers.twitter.searchBuffer")
class SearchBuffer(base.BaseBuffer):
def remove_buffer(self, force=False):
if force == False:
dlg = commonMessageDialogs.remove_buffer()
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.name[:-11] in self.session.settings["other_buffers"]["tweet_searches"]:
self.session.settings["other_buffers"]["tweet_searches"].remove(self.name[:-11])
self.session.settings.write()
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
class SearchPeopleBuffer(people.PeopleBuffer):
""" This is identical to a normal peopleBufferController, except that uses the page parameter instead of a cursor."""
def __init__(self, parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs):
super(SearchPeopleBuffer, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs)
if ("page" in self.kwargs) == False:
self.page = 1
else:
self.page = self.kwargs.pop("page")
def get_more_items(self, *args, **kwargs):
# Add 1 to the page parameter, put it in kwargs and calls to get_more_items in the parent buffer.
self.page = self.page +1
self.kwargs["page"] = self.page
super(SearchPeopleBuffer, self).get_more_items(*args, **kwargs)
# remove the parameter again to make sure start_stream won't fetch items for this page indefinitely.
self.kwargs.pop("page")
def remove_buffer(self, force=False):
if force == False:
dlg = commonMessageDialogs.remove_buffer()
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.name[:-11] in self.session.settings["other_buffers"]["tweet_searches"]:
self.session.settings["other_buffers"]["tweet_searches"].remove(self.name[:-11])
self.session.settings.write()
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
class ConversationBuffer(SearchBuffer):
last_thread_id = None
last_reply_id = None
def __init__(self, tweet, *args, **kwargs):
self.tweet = tweet
super(ConversationBuffer, self).__init__(*args, **kwargs)
def start_stream(self, start=False, mandatory=False, play_sound=True, avoid_autoreading=False):
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
self.execution_time = current_time
log.debug("Retrieving conversation. Last thread ID is {}, last reply ID is {}".format(self.last_thread_id, self.last_reply_id))
results = self.get_replies(self.tweet)
log.debug("Retrieved {} items before filters.".format(len(results)))
number_of_items = self.session.order_buffer(self.name, results)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if number_of_items > 0 and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
self.session.sound.play(self.sound)
# Autoread settings
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
self.auto_read(number_of_items)
return number_of_items
def remove_buffer(self, force=False):
if force == False:
dlg = commonMessageDialogs.remove_buffer()
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
def get_replies(self, tweet):
""" Try to retrieve the whole conversation for the passed object by using a mix between calls to API V1.1 and V2 """
# firstly we would try to retrieve the whole thread, then we will get replies.
# this makes us to waste two search API calls, but there's no better option to retrieve the whole thread including replies, unfortunately.
thread_results = []
reply_results = []
# try to fetch conversation_id of the tweet initiating the buffer.
try:
tweet = self.session.twitter_v2.get_tweet(id=self.tweet.id, user_auth=True, tweet_fields=["conversation_id", "author_id"])
thread_results.append(tweet.data)
except TweepyException as e:
log.exception("Error attempting to retrieve tweet conversation ID")
thread_results.append(self.tweet)
# Return earlier cause we can't do anything if we cannot fetch the object from twitter.
return thread_results
# If tweet contains a conversation_id param, let's retrieve the original tweet which started the conversation so we will have the whole reference for later.
if hasattr(tweet.data, "conversation_id") and tweet.data.conversation_id != None:
conversation_id = tweet.data.conversation_id
original_tweet = self.session.twitter_v2.get_tweet(id=tweet.data.conversation_id, user_auth=True, tweet_fields=["conversation_id", "author_id"])
thread_results.insert(0, original_tweet.data)
else:
conversation_id = tweet.data.id
# find all tweets replying to the original thread only. Those tweets are sent by the same author who originally posted the first tweet.
try:
term = "conversation_id:{} from:{} to:{}".format(conversation_id, original_tweet.data.author_id, original_tweet.data.author_id)
thread_tweets = self.session.twitter_v2.search_recent_tweets(term, user_auth=True, max_results=98, since_id=self.last_thread_id, tweet_fields=["in_reply_to_user_id", "author_id", "conversation_id"])
if thread_tweets.data != None:
thread_results.extend(thread_tweets.data)
# Search only replies to conversation_id.
term = "conversation_id:{}".format(conversation_id, original_tweet.data.author_id)
reply_tweets = self.session.twitter_v2.search_recent_tweets(term, user_auth=True, max_results=50, since_id=self.last_reply_id, tweet_fields=["in_reply_to_user_id", "author_id", "conversation_id"])
if reply_tweets.data != None:
reply_results.extend(reply_tweets.data)
except TweepyException as e:
log.exception("There was an error when attempting to retrieve the whole conversation for buffer {}".format(self.buffer.name))
# convert v2 tweets in normal, V1.1 tweets so we don't have to deal with those kind of objects in our infrastructure.
# ToDo: Remove this last step once we support natively all objects fetched via Twitter API V2.
results = []
ids = [tweet.id for tweet in thread_results]
if len(ids) > 0:
try:
thread_results = self.session.twitter.lookup_statuses(ids, include_ext_alt_text=True, tweet_mode="extended")
thread_results.sort(key=lambda x: x.id)
self.last_thread_id = thread_results[-1].id
results.extend(thread_results)
except TweepyException as e:
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
return []
ids = [tweet.id for tweet in reply_results]
if len(ids) > 0:
try:
reply_results = self.session.twitter.lookup_statuses(ids, include_ext_alt_text=True, tweet_mode="extended")
reply_results.sort(key=lambda x: x.id)
self.last_reply_id = reply_results[-1].id
results.extend(reply_results)
except TweepyException as e:
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
results.sort(key=lambda x: x.id)
return results
def get_replies_v1(self, tweet):
try:
tweet = self.session.twitter.get_status(id=tweet.id, tweet_mode="extended")
except:
log.exception("Error getting tweet for making a conversation buffer.")
return []
results = []
results.append(tweet)
if hasattr(tweet, "in_reply_to_status_id") and tweet.in_reply_to_status_id != None:
while tweet.in_reply_to_status_id != None:
original_tweet = self.session.twitter.get_status(id=tweet.in_reply_to_status_id, tweet_mode="extended")
results.insert(0, original_tweet)
tweet = original_tweet
try:
term = "from:{} to:{}".format(tweet.user.screen_name, tweet.user.screen_name)
thread_tweets = self.session.twitter.search_tweets(term, count=100, since_id=tweet.id, tweet_mode="extended")
results.extend(thread_tweets)
except TweepyException as e:
log.exception("There was an error when attempting to retrieve the whole conversation for buffer {}".format(self.buffer.name))
try:
term = "to:{}".format(tweet.user.screen_name)
reply_tweets = self.session.twitter.search_tweets(term, count=100, since_id=tweet.id, tweet_mode="extended")
ids = [t.id for t in results]
reply_tweets = [t for t in reply_tweets if hasattr(t, "in_reply_to_status_id") and t.in_reply_to_status_id in ids]
results.extend(reply_tweets)
except TweepyException as e:
log.exception("There was an error when attempting to retrieve the whole conversation for buffer {}".format(self.buffer.name))
results.sort(key=lambda x: x.id)
return results

View File

@@ -1,144 +0,0 @@
# -*- coding: utf-8 -*-
import time
import wx
import widgetUtils
import output
import logging
from mysc.thread_utils import call_threaded
from tweepy.errors import TweepyException
from pubsub import pub
from wxUI import buffers, commonMessageDialogs, menus
from controller.twitter import user, messages
from controller.buffers import base
log = logging.getLogger("controller.buffers.twitter.trends")
class TrendsBuffer(base.Buffer):
def __init__(self, parent, name, sessionObject, account, trendsFor, *args, **kwargs):
super(TrendsBuffer, self).__init__(parent=parent, sessionObject=sessionObject)
self.trendsFor = trendsFor
self.session = sessionObject
self.account = account
self.invisible = True
self.buffer = buffers.twitter.trendsPanel(parent, name)
self.buffer.account = account
self.type = self.buffer.type
self.bind_events()
self.sound = "trends_updated.ogg"
self.trends = []
self.name = name
self.buffer.name = name
self.compose_function = self.compose_function_
self.get_formatted_message = self.get_message
self.reply = self.search_topic
def post_status(self, *args, **kwargs):
title = _("Tweet")
caption = _("Write the tweet here")
tweet = messages.tweet(self.session, title, caption, "")
response = tweet.message.ShowModal()
if response == wx.ID_OK:
tweet_data = tweet.get_tweet_data()
call_threaded(self.session.send_tweet, *tweet_data)
if hasattr(tweet.message, "destroy"):
tweet.message.destroy()
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
self.execution_time = current_time
try:
data = self.session.twitter.get_place_trends(id=self.trendsFor)
except TweepyException as err:
log.exception("Error %s" % (str(err)))
if not hasattr(self, "name_"):
self.name_ = data[0]["locations"][0]["name"]
pub.sendMessage("buffer-title-changed", buffer=self)
self.trends = data[0]["trends"]
self.put_items_on_the_list()
if self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
self.session.sound.play(self.sound)
def put_items_on_the_list(self):
selected_item = self.buffer.list.get_selected()
self.buffer.list.clear()
for i in self.trends:
tweet = self.compose_function(i)
self.buffer.list.insert_item(False, *tweet)
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
def compose_function_(self, trend):
return [trend["name"]]
def bind_events(self):
log.debug("Binding events...")
self.buffer.list.list.Bind(wx.EVT_CHAR_HOOK, self.get_event)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.tweet_about_this_trend, self.buffer.tweetTrendBtn)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_status, self.buffer.tweet)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.search_topic, self.buffer.search_topic)
def get_message(self):
return self.compose_function(self.trends[self.buffer.list.get_selected()])[0]
def remove_buffer(self, force=False):
if force == False:
dlg = commonMessageDialogs.remove_buffer()
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.name[:-3] in self.session.settings["other_buffers"]["trending_topic_buffers"]:
self.session.settings["other_buffers"]["trending_topic_buffers"].remove(self.name[:-3])
self.session.settings.write()
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
def url(self, *args, **kwargs):
self.tweet_about_this_trend()
def search_topic(self, *args, **kwargs):
topic = self.trends[self.buffer.list.get_selected()]["name"]
pub.sendMessage("search", term=topic)
def show_menu(self, ev, pos=0, *args, **kwargs):
menu = menus.trendsPanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.search_topic, menuitem=menu.search_topic)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.tweet_about_this_trend, menuitem=menu.tweetThisTrend)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def view(self, *args, **kwargs):
pub.sendMessage("execute-action", action="view_item")
def copy(self, *args, **kwargs):
pub.sendMessage("execute-action", action="copy_to_clipboard")
def tweet_about_this_trend(self, *args, **kwargs):
if self.buffer.list.get_count() == 0: return
title = _("Tweet")
caption = _("Write the tweet here")
tweet = messages.tweet(session=self.session, title=title, caption=caption, text=self.get_message()+ " ")
tweet.message.SetInsertionPoint(len(tweet.message.GetValue()))
if tweet.message.ShowModal() == widgetUtils.OK:
tweet_data = tweet.get_tweet_data()
call_threaded(self.session.send_tweet, *tweet_data)
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
def show_menu_by_key(self, ev):
if self.buffer.list.get_count() == 0:
return
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
def open_in_browser(self, *args, **kwargs):
output.speak(_(u"This action is not supported in the buffer, yet."))

View File

@@ -13,8 +13,6 @@ import application
import sound import sound
import output import output
from pubsub import pub from pubsub import pub
from tweepy.errors import TweepyException, Forbidden
from geopy.geocoders import Nominatim
from extra import SoundsTutorial from extra import SoundsTutorial
from update import updater from update import updater
from wxUI import view, dialogs, commonMessageDialogs, sysTrayIcon from wxUI import view, dialogs, commonMessageDialogs, sysTrayIcon
@@ -25,14 +23,11 @@ from mysc import restart
from mysc import localization from mysc import localization
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 controller.twitter import handler as TwitterHandler
from controller.mastodon import handler as MastodonHandler from controller.mastodon import handler as MastodonHandler
from . import settings, userAlias from . import settings, userAlias
log = logging.getLogger("mainController") log = logging.getLogger("mainController")
geocoder = Nominatim(user_agent="TWBlue")
class Controller(object): class Controller(object):
""" Main Controller for TWBlue. It manages the main window and sessions.""" """ Main Controller for TWBlue. It manages the main window and sessions."""
@@ -110,35 +105,19 @@ class Controller(object):
pub.subscribe(self.toggle_share_settings, "toggleShare") pub.subscribe(self.toggle_share_settings, "toggleShare")
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed") pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
pub.subscribe(self.create_account_buffer, "core.create_account") pub.subscribe(self.create_account_buffer, "core.create_account")
pub.subscribe(self.change_buffer_title, "core.change_buffer_title")
# Twitter specific events.
pub.subscribe(self.buffer_title_changed, "buffer-title-changed")
pub.subscribe(self.manage_sent_dm, "twitter.sent_dm")
pub.subscribe(self.update_sent_dms, "twitter.sent_dms_updated")
pub.subscribe(self.more_dms, "twitter.more_sent_dms")
pub.subscribe(self.manage_sent_tweets, "twitter.sent_tweet")
pub.subscribe(self.manage_new_tweet, "twitter.new_tweet")
pub.subscribe(self.manage_friend, "twitter.friend")
pub.subscribe(self.manage_unfollowing, "twitter.unfollowing")
pub.subscribe(self.manage_favourite, "twitter.favourite")
pub.subscribe(self.manage_unfavourite, "twitter.unfavourite")
pub.subscribe(self.manage_blocked_user, "twitter.blocked_user")
pub.subscribe(self.manage_unblocked_user, "twitter.unblocked_user")
pub.subscribe(self.restart_streaming, "twitter.restart_streaming")
# Mastodon specific events. # Mastodon specific events.
pub.subscribe(self.mastodon_new_item, "mastodon.new_item") pub.subscribe(self.mastodon_new_item, "mastodon.new_item")
pub.subscribe(self.mastodon_updated_item, "mastodon.updated_item") pub.subscribe(self.mastodon_updated_item, "mastodon.updated_item")
pub.subscribe(self.mastodon_new_conversation, "mastodon.conversation_received") pub.subscribe(self.mastodon_new_conversation, "mastodon.conversation_received")
pub.subscribe(self.mastodon_error_post, "mastodon.error_post")
# connect application events to GUI # connect application events to GUI
widgetUtils.connect_event(self.view, widgetUtils.CLOSE_EVENT, self.exit_) widgetUtils.connect_event(self.view, widgetUtils.CLOSE_EVENT, self.exit_)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.search, menuitem=self.view.menuitem_search) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.search, menuitem=self.view.menuitem_search)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.list_manager, menuitem=self.view.lists) # widgetUtils.connect_event(self.view, widgetUtils.MENU, self.list_manager, menuitem=self.view.lists)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_trending_topics, menuitem=self.view.trends)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.filter, menuitem=self.view.filter)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_filters, menuitem=self.view.manage_filters)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.find, menuitem=self.view.find) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.find, menuitem=self.view.find)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.accountConfiguration, menuitem=self.view.account_settings) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.accountConfiguration, menuitem=self.view.account_settings)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.configuration, menuitem=self.view.prefs) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.configuration, menuitem=self.view.prefs)
@@ -156,13 +135,10 @@ class Controller(object):
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_favourites, self.view.fav) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_favourites, self.view.fav)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_favourites, self.view.unfav) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_favourites, self.view.unfav)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_item, self.view.view) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_item, self.view.view)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_reverse_geocode, menuitem=self.view.view_coordinates)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.delete, self.view.delete) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.delete, self.view.delete)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.follow, menuitem=self.view.follow) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.follow, menuitem=self.view.follow)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.send_dm, self.view.dm) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.send_dm, self.view.dm)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_user_lists, menuitem=self.view.viewLists)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_more_items, menuitem=self.view.load_previous_items) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_more_items, menuitem=self.view.load_previous_items)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_user_lists, menuitem=self.view.viewLists)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.clear_buffer, menuitem=self.view.clear) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.clear_buffer, menuitem=self.view.clear)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_buffer, self.view.deleteTl) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_buffer, self.view.deleteTl)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.check_for_updates, self.view.check_for_updates) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.check_for_updates, self.view.check_for_updates)
@@ -170,8 +146,6 @@ class Controller(object):
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.visit_website, menuitem=self.view.visit_website) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.visit_website, menuitem=self.view.visit_website)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_soundpacks, menuitem=self.view.get_soundpacks) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_soundpacks, menuitem=self.view.get_soundpacks)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_accounts, self.view.manage_accounts) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_accounts, self.view.manage_accounts)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_profile, menuitem=self.view.updateProfile)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.user_details, menuitem=self.view.details)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.toggle_autoread, menuitem=self.view.autoread) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.toggle_autoread, menuitem=self.view.autoread)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.toggle_buffer_mute, self.view.mute_buffer) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.toggle_buffer_mute, self.view.mute_buffer)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_timeline, self.view.timeline) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_timeline, self.view.timeline)
@@ -184,17 +158,15 @@ class Controller(object):
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_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.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)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_aliases, self.view.manageAliases) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_aliases, self.view.manageAliases)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
def set_systray_icon(self): def set_systray_icon(self):
self.systrayIcon = sysTrayIcon.SysTrayIcon() self.systrayIcon = sysTrayIcon.SysTrayIcon()
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.post_tweet, menuitem=self.systrayIcon.tweet) widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.post_tweet, menuitem=self.systrayIcon.post)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.configuration, menuitem=self.systrayIcon.global_settings) widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.configuration, menuitem=self.systrayIcon.global_settings)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.accountConfiguration, menuitem=self.systrayIcon.account_settings) widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.accountConfiguration, menuitem=self.systrayIcon.account_settings)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.update_profile, menuitem=self.systrayIcon.update_profile)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.show_hide, menuitem=self.systrayIcon.show_hide) widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.show_hide, menuitem=self.systrayIcon.show_hide)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.check_for_updates, menuitem=self.systrayIcon.check_for_updates) widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.check_for_updates, menuitem=self.systrayIcon.check_for_updates)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.view_documentation, menuitem=self.systrayIcon.doc) widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.view_documentation, menuitem=self.systrayIcon.doc)
@@ -214,9 +186,7 @@ class Controller(object):
def get_handler(self, type): def get_handler(self, type):
handler = self.handlers.get(type) handler = self.handlers.get(type)
if handler == None: if handler == None:
if type == "twitter": if type == "mastodon":
handler = TwitterHandler.Handler()
elif type == "mastodon":
handler = MastodonHandler.Handler() handler = MastodonHandler.Handler()
self.handlers[type]=handler self.handlers[type]=handler
return handler return handler
@@ -261,9 +231,9 @@ class Controller(object):
if sessions.sessions[i].is_logged == False: if sessions.sessions[i].is_logged == False:
self.create_ignored_session_buffer(sessions.sessions[i]) self.create_ignored_session_buffer(sessions.sessions[i])
continue continue
# Valid types currently are twitter and mastodon (Work in progress) # Valid types currently are mastodon (Work in progress)
# More can be added later. # More can be added later.
valid_session_types = ["twitter", "mastodon"] valid_session_types = ["mastodon"]
if sessions.sessions[i].type in valid_session_types: if sessions.sessions[i].type in valid_session_types:
handler = self.get_handler(type=sessions.sessions[i].type) handler = self.get_handler(type=sessions.sessions[i].type)
handler.create_buffers(sessions.sessions[i], controller=self) handler.create_buffers(sessions.sessions[i], controller=self)
@@ -284,8 +254,6 @@ class Controller(object):
if config.app["app-settings"]["speak_ready_msg"] == True: if config.app["app-settings"]["speak_ready_msg"] == True:
output.speak(_(u"Ready")) output.speak(_(u"Ready"))
self.started = True self.started = True
self.streams_checker_function = RepeatingTimer(60, self.check_streams)
self.streams_checker_function.start()
if len(self.accounts) > 0: if len(self.accounts) > 0:
b = self.get_first_buffer(self.accounts[0]) b = self.get_first_buffer(self.accounts[0])
self.update_menus(handler=self.get_handler(b.session.type)) self.update_menus(handler=self.get_handler(b.session.type))
@@ -405,21 +373,6 @@ class Controller(object):
return output.speak(page.get_message(), True) return output.speak(page.get_message(), True)
output.speak(_(u"{0} not found.").format(string,), True) output.speak(_(u"{0} not found.").format(string,), True)
def filter(self, *args, **kwargs):
buffer = self.get_current_buffer()
if not hasattr(buffer.buffer, "list"):
output.speak(_(u"No session is currently in focus. Focus a session with the next or previous session shortcut."), True)
return
handler = self.get_handler(type=buffer.session.type)
if handler != None and hasattr(handler, "filter"):
return handler.filter(buffer=buffer)
def manage_filters(self, *args, **kwargs):
buffer = self.get_best_buffer()
handler = self.get_handler(type=buffer.session.type)
if handler != None and hasattr(handler, "manage_filters"):
return handler.manage_filters(session=buffer.session)
def seekLeft(self, *args, **kwargs): def seekLeft(self, *args, **kwargs):
try: try:
sound.URLPlayer.seek(-5000) sound.URLPlayer.seek(-5000)
@@ -456,31 +409,6 @@ class Controller(object):
buffer = self.get_best_buffer() buffer = self.get_best_buffer()
SoundsTutorial.soundsTutorial(buffer.session) SoundsTutorial.soundsTutorial(buffer.session)
def view_user_lists(self, *args, **kwargs):
buffer = self.get_best_buffer()
handler = self.get_handler(type=buffer.session.type)
if handler != None and hasattr(handler, "view_user_lists"):
return handler.view_user_lists(buffer=buffer)
def add_to_list(self, *args, **kwargs):
buffer = self.get_best_buffer()
handler = self.get_handler(type=buffer.session.type)
if handler != None and hasattr(handler, "add_to_list"):
return handler.add_to_list(controller=self, buffer=buffer)
def remove_from_list(self, *args, **kwargs):
buffer = self.get_best_buffer()
handler = self.get_handler(type=buffer.session.type)
if handler != None and hasattr(handler, "remove_from_list"):
return handler.remove_from_list(controller=self, buffer=buffer)
def list_manager(self, *args, **kwargs):
buffer = self.get_best_buffer()
handler = self.get_handler(type=buffer.session.type)
if handler != None and hasattr(handler, "list_manager"):
lists_buffer_position = self.view.search("lists", buffer.session.get_name())
return handler.list_manager(session=buffer.session, lists_buffer_position=lists_buffer_position)
def configuration(self, *args, **kwargs): def configuration(self, *args, **kwargs):
""" Opens the global settings dialogue.""" """ Opens the global settings dialogue."""
d = settings.globalSettingsController() d = settings.globalSettingsController()
@@ -526,10 +454,6 @@ class Controller(object):
for item in sessions.sessions: for item in sessions.sessions:
if sessions.sessions[item].logged == False: if sessions.sessions[item].logged == False:
continue continue
if hasattr(sessions.sessions[item], "stop_streaming"):
log.debug("Disconnecting streaming endpoint for session" + sessions.sessions[item].session_id)
sessions.sessions[item].stop_streaming()
log.debug("Disconnecting streams for %s session" % (sessions.sessions[item].session_id,))
sessions.sessions[item].sound.cleaner.cancel() sessions.sessions[item].sound.cleaner.cancel()
log.debug("Saving database for " + sessions.sessions[item].session_id) log.debug("Saving database for " + sessions.sessions[item].session_id)
sessions.sessions[item].save_persistent_data() sessions.sessions[item].save_persistent_data()
@@ -537,9 +461,6 @@ class Controller(object):
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name)) pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
if os.path.exists(pidpath): if os.path.exists(pidpath):
os.remove(pidpath) os.remove(pidpath)
if hasattr(self, "streams_checker_function"):
log.debug("Stopping stream checker...")
self.streams_checker_function.cancel()
widgetUtils.exit_application() widgetUtils.exit_application()
def follow(self, *args, **kwargs): def follow(self, *args, **kwargs):
@@ -593,6 +514,11 @@ class Controller(object):
if hasattr(buffer, "toggle_favorite"): if hasattr(buffer, "toggle_favorite"):
return buffer.toggle_favorite() return buffer.toggle_favorite()
def vote(self, *args, **kwargs):
buffer = self.get_current_buffer()
if hasattr(buffer, "vote"):
return buffer.vote()
def view_item(self, *args, **kwargs): def view_item(self, *args, **kwargs):
buffer = self.get_current_buffer() buffer = self.get_current_buffer()
if hasattr(buffer, "view_item"): if hasattr(buffer, "view_item"):
@@ -636,19 +562,6 @@ class Controller(object):
self.view.Show() self.view.Show()
self.showing = True self.showing = True
def get_trending_topics(self, *args, **kwargs):
buffer = self.get_best_buffer()
handler = self.get_handler(type=buffer.session.type)
if handler != None and hasattr(handler, "get_trending_topics"):
return handler.get_trending_topics(controller=self, session=buffer.session)
def view_reverse_geocode(self, event=None):
buffer = self.get_current_buffer()
if hasattr(buffer, "reverse_geocode"):
address = buffer.reverse_geocode()
if address != None:
dlg = commonMessageDialogs.view_geodata(address[0].__str__())
def get_more_items(self, *args, **kwargs): def get_more_items(self, *args, **kwargs):
buffer = self.get_current_buffer() buffer = self.get_current_buffer()
if hasattr(buffer, "get_more_items"): if hasattr(buffer, "get_more_items"):
@@ -801,7 +714,10 @@ class Controller(object):
output.speak(msg, True) output.speak(msg, True)
def next_account(self, *args, **kwargs): def next_account(self, *args, **kwargs):
index = self.accounts.index(self.current_account) try:
index = self.accounts.index(self.current_account)
except ValueError:
index = -1
if index+1 == len(self.accounts): if index+1 == len(self.accounts):
index = 0 index = 0
else: else:
@@ -826,7 +742,10 @@ class Controller(object):
output.speak(msg, True) output.speak(msg, True)
def previous_account(self, *args, **kwargs): def previous_account(self, *args, **kwargs):
index = self.accounts.index(self.current_account) try:
index = self.accounts.index(self.current_account)
except ValueError:
index = 0
if index-1 < 0: if index-1 < 0:
index = len(self.accounts)-1 index = len(self.accounts)-1
else: else:
@@ -941,70 +860,6 @@ class Controller(object):
if message != None: if message != None:
output.speak(message, speech=session.settings["reporting"]["speech_reporting"], braille=session.settings["reporting"]["braille_reporting"]) output.speak(message, speech=session.settings["reporting"]["speech_reporting"], braille=session.settings["reporting"]["braille_reporting"])
def manage_sent_dm(self, data, session_name):
buffer = self.search_buffer("sent_direct_messages", session_name)
if buffer == None:
return
play_sound = "dm_sent.ogg"
if "sent_direct_messages" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, play_sound=play_sound)
buffer.add_new_item(data)
def manage_sent_tweets(self, data, session_name):
buffer = self.search_buffer("sent_tweets", session_name)
if buffer == None:
return
data = buffer.session.check_quoted_status(data)
data = buffer.session.check_long_tweet(data)
if data == False: # Long tweet deleted from twishort.
return
items = buffer.session.db[buffer.name]
if buffer.session.settings["general"]["reverse_timelines"] == False:
items.append(data)
else:
items.insert(0, data)
buffer.session.db[buffer.name] = items
buffer.add_new_item(data)
def manage_friend(self, data, session_name):
buffer = self.search_buffer("friends", session_name)
if buffer == None:
return
buffer.add_new_item(data)
def manage_unfollowing(self, item, session_name):
buffer = self.search_buffer("friends", session_name)
if buffer == None:
return
buffer.remove_item(item)
def manage_favourite(self, data, session_name):
buffer = self.search_buffer("favourites", session_name)
if buffer == None:
return
play_sound = "favourite.ogg"
if "favourites" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, play_sound=play_sound)
buffer.add_new_item(data)
def manage_unfavourite(self, item, session_name):
buffer = self.search_buffer("favourites", session_name)
if buffer == None:
return
buffer.remove_item(item)
def manage_blocked_user(self, data, session_name):
buffer = self.search_buffer("blocked", session_name)
if buffer == None:
return
buffer.add_new_item(data)
def manage_unblocked_user(self, item, session_name):
buffer = self.search_buffer("blocked", session_name)
if buffer == None:
return
buffer.remove_item(item)
def start_buffers(self, session): def start_buffers(self, session):
log.debug("starting buffers... Session %s" % (session.session_id,)) log.debug("starting buffers... Session %s" % (session.session_id,))
handler = self.get_handler(type=session.type) handler = self.get_handler(type=session.type)
@@ -1016,17 +871,6 @@ class Controller(object):
for i in sessions.sessions: for i in sessions.sessions:
self.set_buffer_positions(i) self.set_buffer_positions(i)
def check_connection(self):
if self.started == False:
return
for i in sessions.sessions:
try:
if sessions.sessions[i].is_logged == False:
continue
sessions.sessions[i].check_connection()
except: # We shouldn't allow this function to die.
pass
def invisible_shorcuts_changed(self, registered): def invisible_shorcuts_changed(self, registered):
if registered == True: if registered == True:
km = self.create_invisible_keyboard_shorcuts() km = self.create_invisible_keyboard_shorcuts()
@@ -1087,17 +931,6 @@ class Controller(object):
self.accounts.remove(sessions.sessions[i].get_name()) self.accounts.remove(sessions.sessions[i].get_name())
sessions.sessions.pop(i) sessions.sessions.pop(i)
def update_profile(self, *args, **kwargs):
buffer = self.get_best_buffer()
handler = self.get_handler(type=buffer.session.type)
if hasattr(handler, "update_profile"):
return handler.update_profile(session=buffer.session)
def user_details(self, *args, **kwargs):
buffer = self.get_current_buffer()
if hasattr(buffer, "user_details"):
buffer.user_details()
def toggle_autoread(self, *args, **kwargs): def toggle_autoread(self, *args, **kwargs):
buffer = self.get_current_buffer() buffer = self.get_current_buffer()
if hasattr(buffer, "session") and buffer.session == None: if hasattr(buffer, "session") and buffer.session == None:
@@ -1162,16 +995,6 @@ class Controller(object):
i.start_stream(mandatory=True) i.start_stream(mandatory=True)
except Exception as err: except Exception as err:
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)) 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).
if type(err) == Forbidden:
buff = self.view.search(i.name, i.account)
i.remove_buffer(force=True)
commonMessageDialogs.blocked_timeline()
if self.get_current_buffer() == i:
self.right()
self.view.delete_buffer(buff)
self.buffers.remove(i)
del i
def update_buffer(self, *args, **kwargs): def update_buffer(self, *args, **kwargs):
bf = self.get_current_buffer() bf = self.get_current_buffer()
@@ -1186,16 +1009,12 @@ class Controller(object):
def buffer_title_changed(self, buffer): def buffer_title_changed(self, buffer):
if buffer.name.endswith("-timeline"): if buffer.name.endswith("-timeline"):
title = _(u"Timeline for {}").format(buffer.username,) title = _(u"Timeline for {}").format(buffer.username,)
elif buffer.name.endswith("-favorite"):
title = _(u"Likes for {}").format(buffer.username,)
elif buffer.name.endswith("-followers"): elif buffer.name.endswith("-followers"):
title = _(u"Followers for {}").format(buffer.username,) title = _(u"Followers for {}").format(buffer.username,)
elif buffer.name.endswith("-friends"): elif buffer.name.endswith("-friends"):
title = _(u"Friends for {}").format(buffer.username,) title = _(u"Friends for {}").format(buffer.username,)
elif buffer.name.endswith("-following"): elif buffer.name.endswith("-following"):
title = _(u"Following for {}").format(buffer.username,) title = _(u"Following for {}").format(buffer.username,)
elif buffer.name.endswith("_tt"):
title = _("Trending topics for %s") % (buffer.name_)
buffer_index = self.view.search(buffer.name, buffer.account) buffer_index = self.view.search(buffer.name, buffer.account)
self.view.set_page_title(buffer_index, title) self.view.set_page_title(buffer_index, title)
@@ -1204,55 +1023,13 @@ class Controller(object):
if hasattr(buffer, "ocr_image"): if hasattr(buffer, "ocr_image"):
return buffer.ocr_image() return buffer.ocr_image()
def update_sent_dms(self, total, session_name):
sent_dms = self.search_buffer("sent_direct_messages", session_name)
if sent_dms != None:
sent_dms.put_items_on_list(total)
def more_dms(self, data, account):
sent_dms = self.search_buffer("sent_direct_messages", account)
if sent_dms != None:
sent_dms.put_more_items(data)
def save_data_in_db(self): def save_data_in_db(self):
for i in sessions.sessions: for i in sessions.sessions:
sessions.sessions[i].save_persistent_data() sessions.sessions[i].save_persistent_data()
def manage_new_tweet(self, data, session_name, _buffers):
sound_to_play = None
for buff in _buffers:
buffer = self.search_buffer(buff, session_name)
if buffer == None or buffer.session.get_name() != session_name:
return
buffer.add_new_item(data)
if buff == "home_timeline": sound_to_play = "tweet_received.ogg"
elif buff == "mentions": sound_to_play = "mention_received.ogg"
elif buff == "sent_tweets": sound_to_play = "tweet_send.ogg"
elif "timeline" in buff: sound_to_play = "tweet_timeline.ogg"
else: sound_to_play = None
if sound_to_play != None and buff not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, sound_to_play)
def toggle_share_settings(self, shareable=True): def toggle_share_settings(self, shareable=True):
self.view.share.Enable(shareable) self.view.share.Enable(shareable)
def check_streams(self):
if self.started == False:
return
for i in sessions.sessions:
try:
if sessions.sessions[i].is_logged == False: continue
sessions.sessions[i].check_streams()
except TweepyException: # We shouldn't allow this function to die.
pass
def restart_streaming(self, session):
for s in sessions.sessions:
if sessions.sessions[s].session_id == session:
log.debug("Restarting stream in session %s" % (session))
sessions.sessions[s].stop_streaming()
sessions.sessions[s].start_streaming()
def mastodon_new_item(self, item, session_name, _buffers): def mastodon_new_item(self, item, session_name, _buffers):
sound_to_play = None sound_to_play = None
for buff in _buffers: for buff in _buffers:
@@ -1292,4 +1069,19 @@ class Controller(object):
# if number_of_items > 0: # if number_of_items > 0:
# sound_to_play = "dm_received.ogg" # sound_to_play = "dm_received.ogg"
# if "direct_messages" not in buffer.session.settings["other_buffers"]["muted_buffers"]: # if "direct_messages" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
# self.notify(buffer.session, sound_to_play) # self.notify(buffer.session, sound_to_play)
def mastodon_error_post(self, name, reply_to, visibility, posts):
home = self.search_buffer("home_timeline", name)
if home != None:
wx.CallAfter(home.post_from_error, visibility=visibility, reply_to=reply_to, data=posts)
def change_buffer_title(self, name, buffer, title):
buffer_index = self.view.search(buffer, name)
if buffer_index != None and buffer_index > -1:
self.view.set_page_title(buffer_index, title)
def report_error(self, *args, **kwargs):
"""Redirects the user to the issue page on github"""
log.debug("Redirecting the user to report an error...")
webbrowser.open_new_tab(application.report_bugs_url)

View File

@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import wx import wx
import logging import logging
import output
from pubsub import pub from pubsub import pub
from mysc import restart from mysc import restart
from wxUI.dialogs.mastodon import dialogs
from wxUI.dialogs.mastodon import search as search_dialogs from wxUI.dialogs.mastodon import search as search_dialogs
from wxUI.dialogs.mastodon import dialogs from wxUI.dialogs.mastodon import dialogs
from wxUI.dialogs import userAliasDialogs
from wxUI import commonMessageDialogs from wxUI import commonMessageDialogs
from sessions.twitter import utils
from . import userActions, settings from . import userActions, settings
log = logging.getLogger("controller.mastodon.handler") log = logging.getLogger("controller.mastodon.handler")
@@ -23,7 +23,7 @@ class Handler(object):
updateProfile=None, updateProfile=None,
menuitem_search=_("&Search"), menuitem_search=_("&Search"),
lists=None, lists=None,
manageAliases=None, manageAliases=_("Manage user aliases"),
# In item menu. # In item menu.
compose=_("&Post"), compose=_("&Post"),
reply=_("Re&ply"), reply=_("Re&ply"),
@@ -31,18 +31,16 @@ class Handler(object):
fav=_("&Add to favorites"), fav=_("&Add to favorites"),
unfav=_("Remove from favorites"), unfav=_("Remove from favorites"),
view=_("&Show post"), view=_("&Show post"),
view_coordinates=None,
view_conversation=_("View conversa&tion"), view_conversation=_("View conversa&tion"),
ocr=None, ocr=_("Read text in picture"),
delete=_("&Delete"), delete=_("&Delete"),
# In user menu. # In user menu.
follow=_("&Actions..."), follow=_("&Actions..."),
timeline=_("&View timeline..."), timeline=_("&View timeline..."),
dm=_("Direct me&ssage"), dm=_("Direct me&ssage"),
addAlias=None, addAlias=_("Add a&lias"),
addToList=None, addToList=None,
removeFromList=None, removeFromList=None,
viewLists=None,
details=None, details=None,
favs=None, favs=None,
# In buffer Menu. # In buffer Menu.
@@ -227,3 +225,33 @@ class Handler(object):
buffer.session.settings.write() buffer.session.settings.write()
buffer.session.save_persistent_data() buffer.session.save_persistent_data()
restart.restart_program() restart.restart_program()
def add_alias(self, buffer):
if not hasattr(buffer, "get_item"):
return
item = buffer.get_item()
if buffer.type == "user":
users = [item.acct]
elif buffer.type == "baseBuffer":
if item.reblog != None:
users = [user.acct for user in item.reblog.mentions if user.id != buffer.session.db["user_id"]]
if item.reblog.account.acct not in users and item.account.id != buffer.session.db["user_id"]:
users.insert(0, item.reblog.account.acct)
else:
users = [user.acct for user in item.mentions if user.id != buffer.session.db["user_id"]]
if item.account.acct not in users:
users.insert(0, item.account.acct)
dlg = userAliasDialogs.addAliasDialog(_("Add an user alias"), users)
if dlg.get_response() == wx.ID_OK:
user, alias = dlg.get_user()
if user == "" or alias == "":
return
try:
full_user = buffer.session.api.account_lookup(user)
except Exception as e:
log.exception("Error adding alias to user {}.".format(user))
return
buffer.session.settings["user-aliases"][str(full_user.id)] = alias
buffer.session.settings.write()
output.speak(_("Alias has been set correctly for {}.").format(user))
pub.sendMessage("alias-added")

View File

@@ -1,14 +1,29 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import re
import wx import wx
import widgetUtils import widgetUtils
import config import config
import output import output
from controller.twitter import messages from twitter_text import parse_tweet, config
from controller import messages
from sessions.mastodon import templates from sessions.mastodon import templates
from wxUI.dialogs.mastodon import postDialogs from wxUI.dialogs.mastodon import postDialogs
class post(messages.basicTweet): def character_count(post_text, post_cw, character_limit=500):
# We will use text for counting character limit only.
full_text = post_text+post_cw
# find remote users as Mastodon doesn't count the domain in char limit.
users = re.findall("@[\w\.-]+@[\w\.-]+", full_text)
for user in users:
domain = user.split("@")[-1]
full_text = full_text.replace("@"+domain, "")
options = config.config.get("defaults")
options.update(max_weighted_tweet_length=character_limit, default_weight=100)
parsed = parse_tweet(full_text, options=options)
return parsed.weightedLength
class post(messages.basicMessage):
def __init__(self, session, title, caption, text="", *args, **kwargs): def __init__(self, session, title, caption, text="", *args, **kwargs):
# take max character limit from session as this might be different for some instances. # take max character limit from session as this might be different for some instances.
self.max = session.char_limit self.max = session.char_limit
@@ -19,6 +34,7 @@ class post(messages.basicTweet):
self.message.text.SetInsertionPoint(len(self.message.text.GetValue())) self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck) widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor) widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
widgetUtils.connect_event(self.message.spoiler, widgetUtils.ENTERED_TEXT, self.text_processor)
widgetUtils.connect_event(self.message.translate, widgetUtils.BUTTON_PRESSED, self.translate) widgetUtils.connect_event(self.message.translate, widgetUtils.BUTTON_PRESSED, self.translate)
widgetUtils.connect_event(self.message.add, widgetUtils.BUTTON_PRESSED, self.on_attach) widgetUtils.connect_event(self.message.add, widgetUtils.BUTTON_PRESSED, self.on_attach)
widgetUtils.connect_event(self.message.remove_attachment, widgetUtils.BUTTON_PRESSED, self.remove_attachment) widgetUtils.connect_event(self.message.remove_attachment, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
@@ -48,8 +64,32 @@ class post(messages.basicTweet):
self.add_post(event=None, update_gui=False) self.add_post(event=None, update_gui=False)
return self.thread return self.thread
def set_post_data(self, visibility, data):
if len(data) == 0:
return
if len(data) > 1:
self.thread = data[:-1]
for p in self.thread:
self.message.add_item(item=[p.get("text") or "", len(p.get("attachments") or [])], list_type="post")
post = data[-1]
self.attachments = post.get("attachments") or []
self.message.text.SetValue(post.get("text") or "")
self.message.sensitive.SetValue(post.get("sensitive") or False)
self.message.spoiler.SetValue(post.get("spoiler_text") or "")
visibility_settings = dict(public=0, unlisted=1, private=2, direct=3)
self.message.visibility.SetSelection(visibility_settings.get(visibility))
self.message.on_sensitivity_changed()
for attachment in self.attachments:
self.message.add_item(item=[attachment["file"], attachment["type"], attachment["description"]])
self.text_processor()
def text_processor(self, *args, **kwargs): def text_processor(self, *args, **kwargs):
super(post, self).text_processor(*args, **kwargs) text = self.message.text.GetValue()
cw = self.message.spoiler.GetValue()
results = character_count(text, cw, character_limit=self.max)
self.message.SetTitle(_("%s - %s of %d characters") % (self.title, results, self.max))
if results > self.max:
self.session.sound.play("max_length.ogg")
if len(self.thread) > 0: if len(self.thread) > 0:
if hasattr(self.message, "posts"): if hasattr(self.message, "posts"):
self.message.posts.Enable(True) self.message.posts.Enable(True)
@@ -127,9 +167,9 @@ class post(messages.basicTweet):
break break
if can_attach == False or big_media_present == True: if can_attach == False or big_media_present == True:
return self.message.unable_to_attach_file() return self.message.unable_to_attach_file()
video = self.message.get_video() video, description = self.message.get_video()
if video != None: if video != None:
videoInfo = {"type": "video", "file": video, "description": ""} videoInfo = {"type": "video", "file": video, "description": description}
self.attachments.append(videoInfo) self.attachments.append(videoInfo)
self.message.add_item(item=[os.path.basename(videoInfo["file"]), videoInfo["type"], videoInfo["description"]]) self.message.add_item(item=[os.path.basename(videoInfo["file"]), videoInfo["type"], videoInfo["description"]])
self.text_processor() self.text_processor()
@@ -145,9 +185,9 @@ class post(messages.basicTweet):
break break
if can_attach == False or big_media_present == True: if can_attach == False or big_media_present == True:
return self.message.unable_to_attach_file() return self.message.unable_to_attach_file()
audio = self.message.get_audio() audio, description = self.message.get_audio()
if audio != None: if audio != None:
audioInfo = {"type": "audio", "file": audio, "description": ""} audioInfo = {"type": "audio", "file": audio, "description": description}
self.attachments.append(audioInfo) self.attachments.append(audioInfo)
self.message.add_item(item=[os.path.basename(audioInfo["file"]), audioInfo["type"], audioInfo["description"]]) self.message.add_item(item=[os.path.basename(audioInfo["file"]), audioInfo["type"], audioInfo["description"]])
self.text_processor() self.text_processor()
@@ -229,4 +269,12 @@ class viewPost(post):
def share(self, *args, **kwargs): def share(self, *args, **kwargs):
if hasattr(self, "item_url"): if hasattr(self, "item_url"):
output.copy(self.item_url) output.copy(self.item_url)
output.speak(_("Link copied to clipboard.")) output.speak(_("Link copied to clipboard."))
class text(messages.basicMessage):
def __init__(self, title, text="", *args, **kwargs):
self.title = title
self.message = postDialogs.viewText(title=title, text=text, *args, **kwargs)
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)

View File

@@ -9,7 +9,7 @@ import output
from collections import OrderedDict from collections import OrderedDict
from wxUI import commonMessageDialogs from wxUI import commonMessageDialogs
from wxUI.dialogs.mastodon import configuration from wxUI.dialogs.mastodon import configuration
from extra.autocompletionUsers import scan, manage #from extra.autocompletionUsers import scan, manage
from extra.ocr import OCRSpace from extra.ocr import OCRSpace
from controller.settings import globalSettingsController from controller.settings import globalSettingsController
from . templateEditor import EditTemplate from . templateEditor import EditTemplate
@@ -31,6 +31,7 @@ class accountSettingsController(globalSettingsController):
self.dialog.create_general_account() self.dialog.create_general_account()
# widgetUtils.connect_event(self.dialog.general.userAutocompletionScan, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_scan) # widgetUtils.connect_event(self.dialog.general.userAutocompletionScan, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_scan)
# widgetUtils.connect_event(self.dialog.general.userAutocompletionManage, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_manage) # widgetUtils.connect_event(self.dialog.general.userAutocompletionManage, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_manage)
self.dialog.set_value("general", "disable_streaming", self.config["general"]["disable_streaming"])
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"]) self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
self.dialog.set_value("general", "read_preferences_from_instance", self.config["general"]["read_preferences_from_instance"]) self.dialog.set_value("general", "read_preferences_from_instance", self.config["general"]["read_preferences_from_instance"])
self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"]) self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"])
@@ -112,6 +113,10 @@ class accountSettingsController(globalSettingsController):
self.needs_restart = True self.needs_restart = True
log.debug("Triggered app restart due to change in relative times.") log.debug("Triggered app restart due to change in relative times.")
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time") self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
if self.config["general"]["disable_streaming"] != self.dialog.get_value("general", "disable_streaming"):
self.needs_restart = True
log.debug("Triggered app restart due to change in streaming settings.")
self.config["general"]["disable_streaming"] = self.dialog.get_value("general", "disable_streaming")
self.config["general"]["read_preferences_from_instance"] = self.dialog.get_value("general", "read_preferences_from_instance") self.config["general"]["read_preferences_from_instance"] = self.dialog.get_value("general", "read_preferences_from_instance")
self.config["general"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names") self.config["general"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names")
self.config["general"]["hide_emojis"] = self.dialog.get_value("general", "hide_emojis") self.config["general"]["hide_emojis"] = self.dialog.get_value("general", "hide_emojis")

View File

@@ -3,7 +3,7 @@ import re
import wx import wx
from typing import List from typing import List
from sessions.mastodon.templates import post_variables, conversation_variables, person_variables from sessions.mastodon.templates import post_variables, conversation_variables, person_variables
from wxUI.dialogs.twitterDialogs import templateDialogs from wxUI.dialogs import templateDialogs
class EditTemplate(object): class EditTemplate(object):
def __init__(self, template: str, type: str) -> None: def __init__(self, template: str, type: str) -> None:

View File

@@ -6,7 +6,7 @@ from wxUI.dialogs.mastodon import userActions as userActionsDialog
from wxUI.dialogs.mastodon import userTimeline as userTimelineDialog from wxUI.dialogs.mastodon import userTimeline as userTimelineDialog
from pubsub import pub from pubsub import pub
from mastodon import MastodonError, MastodonNotFoundError from mastodon import MastodonError, MastodonNotFoundError
from extra.autocompletionUsers import completion #from extra.autocompletionUsers import completion
log = logging.getLogger("controller.mastodon.userActions") log = logging.getLogger("controller.mastodon.userActions")

View File

@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
import widgetUtils
import output
from extra import translator, SpellChecker
class basicMessage(object):
def translate(self, event=None):
dlg = translator.gui.translateDialog()
if dlg.get_response() == widgetUtils.OK:
text_to_translate = self.message.text.GetValue()
language_dict = translator.translator.available_languages()
for k in language_dict:
if language_dict[k] == dlg.dest_lang.GetStringSelection():
dst = k
msg = translator.translator.translate(text=text_to_translate, target=dst)
self.message.text.ChangeValue(msg)
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
self.text_processor()
self.message.text.SetFocus()
output.speak(_(u"Translated"))
else:
return
def text_processor(self, *args, **kwargs):
pass
def spellcheck(self, event=None):
text = self.message.text.GetValue()
checker = SpellChecker.spellchecker.spellChecker(text, "")
if hasattr(checker, "fixed_text"):
self.message.text.ChangeValue(checker.fixed_text)
self.text_processor()
self.message.text.SetFocus()
def remove_attachment(self, *args, **kwargs):
attachment = self.message.attachments.GetFocusedItem()
if attachment > -1 and len(self.attachments) > attachment:
self.attachments.pop(attachment)
self.message.remove_item(list_type="attachment")
self.text_processor()
self.message.text.SetFocus()

View File

@@ -1 +0,0 @@
# -*- coding: utf-8 -*-

View File

@@ -1,76 +0,0 @@
# -*- coding: utf-8 -*-
import time
import widgetUtils
import application
from wxUI.dialogs import filterDialogs
from wxUI import commonMessageDialogs
class filter(object):
def __init__(self, buffer, filter_title=None, if_word_exists=None, in_lang=None, regexp=None, word=None, in_buffer=None):
self.buffer = buffer
self.dialog = filterDialogs.filterDialog(languages=[i["name"] for i in application.supported_languages])
if self.dialog.get_response() == widgetUtils.OK:
title = self.dialog.get("title")
contains = self.dialog.get("contains")
term = self.dialog.get("term")
regexp = self.dialog.get("regexp")
allow_rts = self.dialog.get("allow_rts")
allow_quotes = self.dialog.get("allow_quotes")
allow_replies = self.dialog.get("allow_replies")
load_language = self.dialog.get("load_language")
ignore_language = self.dialog.get("ignore_language")
lang_option = None
if ignore_language:
lang_option = False
elif load_language:
lang_option = True
langs = self.dialog.get_selected_langs()
langcodes = []
for i in application.supported_languages:
if i["name"] in langs:
langcodes.append(i["code"])
d = dict(in_buffer=self.buffer.name, word=term, regexp=regexp, in_lang=lang_option, languages=langcodes, if_word_exists=contains, allow_rts=allow_rts, allow_quotes=allow_quotes, allow_replies=allow_replies)
if title in self.buffer.session.settings["filters"]:
return commonMessageDialogs.existing_filter()
self.buffer.session.settings["filters"][title] = d
self.buffer.session.settings.write()
class filterManager(object):
def __init__(self, session):
self.session = session
self.dialog = filterDialogs.filterManagerDialog()
self.insert_filters(self.session.settings["filters"])
if self.dialog.filters.get_count() == 0:
self.dialog.edit.Enable(False)
self.dialog.delete.Enable(False)
else:
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_filter)
widgetUtils.connect_event(self.dialog.delete, widgetUtils.BUTTON_PRESSED, self.delete_filter)
response = self.dialog.get_response()
def insert_filters(self, filters):
self.dialog.filters.clear()
for f in list(filters.keys()):
filterName = f
buffer = filters[f]["in_buffer"]
if filters[f]["if_word_exists"] == "True" and filters[f]["word"] != "":
filter_by_word = "True"
else:
filter_by_word = "False"
filter_by_lang = ""
if filters[f]["in_lang"] != "None":
filter_by_lang = "True"
b = [f, buffer, filter_by_word, filter_by_lang]
self.dialog.filters.insert_item(False, *b)
def edit_filter(self, *args, **kwargs):
pass
def delete_filter(self, *args, **kwargs):
filter_title = self.dialog.filters.get_text_column(self.dialog.filters.get_selected(), 0)
response = commonMessageDialogs.delete_filter()
if response == widgetUtils.YES:
self.session.settings["filters"].pop(filter_title)
self.session.settings.write()
self.insert_filters(self.session.settings["filters"])

View File

@@ -1,377 +0,0 @@
# -*- coding: utf-8 -*-
import logging
import widgetUtils
import output
from pubsub import pub
from tweepy.errors import TweepyException, Forbidden
from mysc import restart
from sessions.twitter import utils, compose
from controller import userSelector
from wxUI import dialogs, commonMessageDialogs
from . import filters, lists, settings, userActions, trendingTopics, user
log = logging.getLogger("controller.twitter.handler")
class Handler(object):
def __init__(self):
super(Handler, self).__init__()
# Structure to hold names for menu bar items.
# empty names mean the item will be Disabled.
self.menus = dict(
# In application menu.
updateProfile=_("&Update profile"),
menuitem_search=_("&Search"),
lists=_("&Lists manager"),
manageAliases=_("Manage user aliases"),
# In Item Menu.
compose=_("&Tweet"),
reply=_("Re&ply"),
share=_("&Retweet"),
fav=_("&Like"),
unfav=_("&Unlike"),
view=_("&Show tweet"),
view_coordinates=_("View &address"),
view_conversation=_("View conversa&tion"),
ocr=_("Read text in picture"),
delete=_("&Delete"),
# In user menu.
follow=_("&Actions..."),
timeline=_("&View timeline..."),
dm=_("Direct me&ssage"),
addAlias=_("Add a&lias"),
addToList=_("&Add to list"),
removeFromList=_("R&emove from list"),
viewLists=_("&View lists"),
details=_("Show user &profile"),
favs=_("View likes"),
# In buffer menu.
trends=_("New &trending topics buffer..."),
filter=_("Create a &filter"),
manage_filters=_("&Manage filters"),
)
# Name for the "tweet" menu in the menu bar.
self.item_menu = _("&Tweet")
def create_buffers(self, session, createAccounts=True, controller=None):
session.get_user_info()
name = session.get_name()
controller.accounts.append(name)
if createAccounts == True:
pub.sendMessage("core.create_account", name=name, session_id=session.session_id, logged=True)
root_position =controller.view.search(name, name)
for i in session.settings['general']['buffer_order']:
if i == 'home':
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Home"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="home_timeline", name="home_timeline", sessionObject=session, account=session.get_name(), sound="tweet_received.ogg", include_ext_alt_text=True, tweet_mode="extended"))
elif i == 'mentions':
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Mentions"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="mentions_timeline", name="mentions", sessionObject=session, account=session.get_name(), sound="mention_received.ogg", include_ext_alt_text=True, tweet_mode="extended"))
elif i == 'dm':
pub.sendMessage("createBuffer", buffer_type="DirectMessagesBuffer", session_type=session.type, buffer_title=_("Direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_direct_messages", name="direct_messages", sessionObject=session, account=session.get_name(), bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg"))
elif i == 'sent_dm':
pub.sendMessage("createBuffer", buffer_type="SentDirectMessagesBuffer", session_type=session.type, buffer_title=_("Sent direct messages"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function=None, name="sent_direct_messages", sessionObject=session, account=session.get_name(), bufferType="dmPanel", compose_func="compose_direct_message"))
elif i == 'sent_tweets':
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Sent tweets"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="user_timeline", name="sent_tweets", sessionObject=session, account=session.get_name(), screen_name=session.db["user_name"], include_ext_alt_text=True, tweet_mode="extended"))
elif i == 'favorites':
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_favorites", name="favourites", sessionObject=session, account=session.get_name(), sound="favourite.ogg", include_ext_alt_text=True, tweet_mode="extended"))
elif i == 'followers':
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Followers"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_followers", name="followers", sessionObject=session, account=session.get_name(), sound="update_followers.ogg", screen_name=session.db["user_name"]))
elif i == 'friends':
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Following"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_friends", name="friends", sessionObject=session, account=session.get_name(), screen_name=session.db["user_name"]))
elif i == 'blocks':
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Blocked users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_blocks", name="blocked", sessionObject=session, account=session.get_name()))
elif i == 'muted':
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=session.type, buffer_title=_("Muted users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_mutes", name="muted", sessionObject=session, account=session.get_name()))
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="timelines", account=name))
timelines_position =controller.view.search("timelines", name)
for i in session.settings["other_buffers"]["timelines"]:
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_(u"Timeline for {}").format(i,), parent_tab=timelines_position, start=False, kwargs=dict(parent=controller.view.nb, function="user_timeline", name="%s-timeline" % (i,), sessionObject=session, account=session.get_name(), sound="tweet_timeline.ogg", bufferType=None, user_id=i, include_ext_alt_text=True, tweet_mode="extended"))
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Likes timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="favs_timelines", account=name))
favs_timelines_position =controller.view.search("favs_timelines", name)
for i in session.settings["other_buffers"]["favourites_timelines"]:
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=session.type, buffer_title=_("Likes for {}").format(i,), parent_tab=favs_timelines_position, start=False, kwargs=dict(parent=controller.view.nb, function="get_favorites", name="%s-favorite" % (i,), sessionObject=session, account=name, bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, include_ext_alt_text=True, tweet_mode="extended"))
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Followers timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="followers_timelines", account=session.get_name()))
followers_timelines_position =controller.view.search("followers_timelines", name)
for i in session.settings["other_buffers"]["followers_timelines"]:
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=controller.view.nb, function="get_followers", name="%s-followers" % (i,), sessionObject=session, account=session.get_name(), sound="new_event.ogg", user_id=i))
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Following timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="friends_timelines", account=name))
friends_timelines_position =controller.view.search("friends_timelines", name)
for i in session.settings["other_buffers"]["friends_timelines"]:
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=controller.view.nb, function="get_friends", name="%s-friends" % (i,), sessionObject=session, account=session.get_name(), sound="new_event.ogg", user_id=i))
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Lists"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="lists", account=name))
lists_position =controller.view.search("lists", name)
for i in session.settings["other_buffers"]["lists"]:
pub.sendMessage("createBuffer", buffer_type="ListBuffer", session_type=session.type, buffer_title=_(u"List for {}").format(i), parent_tab=lists_position, start=False, kwargs=dict(parent=controller.view.nb, function="list_timeline", name="%s-list" % (i,), sessionObject=session, account=session.get_name(), bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), include_ext_alt_text=True, tweet_mode="extended"))
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Searches"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="searches", account=name))
searches_position =controller.view.search("searches", name)
for i in session.settings["other_buffers"]["tweet_searches"]:
pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=session.type, buffer_title=_(u"Search for {}").format(i), parent_tab=searches_position, start=False, kwargs=dict(parent=controller.view.nb, function="search_tweets", name="%s-searchterm" % (i,), sessionObject=session, account=session.get_name(), bufferType="searchPanel", sound="search_updated.ogg", q=i, include_ext_alt_text=True, tweet_mode="extended"))
for i in session.settings["other_buffers"]["trending_topic_buffers"]:
pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (i), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="%s_tt" % (i,), sessionObject=session, account=session.get_name(), trendsFor=i, sound="trends_updated.ogg"))
def filter(self, buffer):
# Let's prevent filtering of some buffers (people buffers, direct messages, events and sent items).
if (buffer.name == "direct_messages" or buffer.name == "sent_tweets") or buffer.type == "people":
output.speak(_("Filters cannot be applied on this buffer"))
return
new_filter = filters.filter(buffer)
def manage_filters(self, session):
manage_filters = filters.filterManager(session)
def view_user_lists(self, buffer):
if not hasattr(buffer, "get_right_tweet"):
return
tweet = buffer.get_right_tweet()
if buffer.type == "people":
users = [tweet.screen_name]
elif buffer.type == "dm":
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
else:
users = utils.get_all_users(tweet, buffer.session)
selector = userSelector.userSelector(users=users, session_id=buffer.session.session_id)
user = selector.get_user()
if user == None:
return
l = lists.listsController(buffer.session, user=user)
def add_to_list(self, controller, buffer):
if not hasattr(buffer, "get_right_tweet"):
return
tweet = buffer.get_right_tweet()
if buffer.type == "people":
users = [tweet.screen_name]
elif buffer.type == "dm":
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
else:
users = utils.get_all_users(tweet, buffer.session)
selector = userSelector.userSelector(users=users, session_id=buffer.session.session_id)
user = selector.get_user()
if user == None:
return
dlg = dialogs.lists.addUserListDialog()
dlg.populate_list([compose.compose_list(item) for item in buffer.session.db["lists"]])
if dlg.get_response() == widgetUtils.OK:
try:
list = buffer.session.twitter.add_list_member(list_id=buffer.session.db["lists"][dlg.get_item()].id, screen_name=user)
older_list = utils.find_item(buffer.session.db["lists"][dlg.get_item()].id, buffer.session.db["lists"])
listBuffer = controller.search_buffer("%s-list" % (buffer.session.db["lists"][dlg.get_item()].name.lower()), buff.session.get_name())
if listBuffer != None:
listBuffer.get_user_ids()
buffer.session.db["lists"].pop(older_list)
buffer.session.db["lists"].append(list)
except TweepyException as e:
log.exception("error %s" % (str(e)))
output.speak("error %s" % (str(e)))
def remove_from_list(self, controller, buffer):
if not hasattr(buffer, "get_right_tweet"):
return
tweet = buffer.get_right_tweet()
if buffer.type == "people":
users = [tweet.screen_name]
elif buffer.type == "dm":
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
else:
users = utils.get_all_users(tweet, buffer.session)
selector = userSelector.userSelector(users=users, session_id=buffer.session.session_id)
user = selector.get_user()
if user == None:
return
dlg = dialogs.lists.removeUserListDialog()
dlg.populate_list([compose.compose_list(item) for item in buffer.session.db["lists"]])
if dlg.get_response() == widgetUtils.OK:
try:
list = buffer.session.twitter.remove_list_member(list_id=buffer.session.db["lists"][dlg.get_item()].id, screen_name=user)
older_list = utils.find_item(buffer.session.db["lists"][dlg.get_item()].id, buffer.session.db["lists"])
listBuffer = controller.search_buffer("%s-list" % (buffer.session.db["lists"][dlg.get_item()].name.lower()), buffer.session.get_name())
if listBuffer != None:
listBuffer.get_user_ids()
buffer.session.db["lists"].pop(older_list)
buffer.session.db["lists"].append(list)
except TweepyException as e:
output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))
def list_manager(self, session, lists_buffer_position):
return lists.listsController(session=session, lists_buffer_position=lists_buffer_position)
def account_settings(self, buffer, controller):
d = settings.accountSettingsController(buffer, controller)
if d.response == widgetUtils.OK:
d.save_configuration()
if d.needs_restart == True:
commonMessageDialogs.needs_restart()
buffer.session.settings.write()
buffer.session.save_persistent_data()
restart.restart_program()
def follow(self, buffer):
if not hasattr(buffer, "get_right_tweet"):
return
tweet = buffer.get_right_tweet()
if buffer.type == "people":
users = [tweet.screen_name]
elif buffer.type == "dm":
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
else:
users = utils.get_all_users(tweet, buffer.session)
u = userActions.userActionsController(buffer, users)
def add_alias(self, buffer):
if not hasattr(buffer, "get_right_tweet"):
return
tweet = buffer.get_right_tweet()
if buffer.type == "people":
users = [tweet.screen_name]
elif buffer.type == "dm":
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
else:
users = utils.get_all_users(tweet, buffer.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 = buffer.session.get_user_by_screen_name(user)
buffer.session.settings["user-aliases"][str(user_id)] = alias
buffer.session.settings.write()
output.speak(_("Alias has been set correctly for {}.").format(user))
pub.sendMessage("alias-added")
# ToDo: explore how to play sound & save config differently.
# currently, TWBlue will play the sound and save the config for the timeline even if the buffer did not load or something else.
def open_timeline(self, controller, buffer, default="tweets"):
if not hasattr(buffer, "get_right_tweet"):
return
tweet = buffer.get_right_tweet()
if buffer.type == "people":
users = [tweet.screen_name]
elif buffer.type == "dm":
users = [buffer.session.get_user(tweet.message_create["sender_id"]).screen_name]
else:
users = utils.get_all_users(tweet, buffer.session)
dlg = dialogs.userSelection.selectUserDialog(users=users, default=default)
if dlg.get_response() == widgetUtils.OK:
usr = utils.if_user_exists(buffer.session.twitter, dlg.get_user())
if usr != None:
if usr == dlg.get_user():
commonMessageDialogs.suspended_user()
return
if usr.protected == True:
if usr.following == False:
commonMessageDialogs.no_following()
return
tl_type = dlg.get_action()
if tl_type == "tweets":
if usr.statuses_count == 0:
commonMessageDialogs.no_tweets()
return
if usr.id_str in buffer.session.settings["other_buffers"]["timelines"]:
commonMessageDialogs.timeline_exist()
return
timelines_position =controller.view.search("timelines", buffer.session.get_name())
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=buffer.session.type, buffer_title=_("Timeline for {}").format(usr.screen_name,), parent_tab=timelines_position, start=True, kwargs=dict(parent=controller.view.nb, function="user_timeline", name="%s-timeline" % (usr.id_str,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="tweet_timeline.ogg", bufferType=None, user_id=usr.id_str, include_ext_alt_text=True, tweet_mode="extended"))
buffer.session.settings["other_buffers"]["timelines"].append(usr.id_str)
buffer.session.sound.play("create_timeline.ogg")
elif tl_type == "favourites":
if usr.favourites_count == 0:
commonMessageDialogs.no_favs()
return
if usr.id_str in buffer.session.settings["other_buffers"]["favourites_timelines"]:
commonMessageDialogs.timeline_exist()
return
favs_timelines_position =controller.view.search("favs_timelines", buffer.session.get_name())
pub.sendMessage("createBuffer", buffer_type="BaseBuffer", session_type=buffer.session.type, buffer_title=_("Likes for {}").format(usr.screen_name,), parent_tab=favs_timelines_position, start=True, kwargs=dict(parent=controller.view.nb, function="get_favorites", name="%s-favorite" % (usr.id_str,), sessionObject=buffer.session, account=buffer.session.get_name(), bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr.id_str, include_ext_alt_text=True, tweet_mode="extended"))
buffer.session.settings["other_buffers"]["favourites_timelines"].append(usr.id_str)
buffer.session.sound.play("create_timeline.ogg")
elif tl_type == "followers":
if usr.followers_count == 0:
commonMessageDialogs.no_followers()
return
if usr.id_str in buffer.session.settings["other_buffers"]["followers_timelines"]:
commonMessageDialogs.timeline_exist()
return
followers_timelines_position =controller.view.search("followers_timelines", buffer.session.get_name())
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=buffer.session.type, buffer_title=_("Followers for {}").format(usr.screen_name,), parent_tab=followers_timelines_position, start=True, kwargs=dict(parent=controller.view.nb, function="get_followers", name="%s-followers" % (usr.id_str,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="new_event.ogg", user_id=usr.id_str))
buffer.session.settings["other_buffers"]["followers_timelines"].append(usr.id_str)
buffer.session.sound.play("create_timeline.ogg")
elif tl_type == "friends":
if usr.friends_count == 0:
commonMessageDialogs.no_friends()
return
if usr.id_str in buffer.session.settings["other_buffers"]["friends_timelines"]:
commonMessageDialogs.timeline_exist()
return
friends_timelines_position =controller.view.search("friends_timelines", buffer.session.get_name())
pub.sendMessage("createBuffer", buffer_type="PeopleBuffer", session_type=buffer.session.type, buffer_title=_("Friends for {}").format(usr.screen_name,), parent_tab=friends_timelines_position, start=True, kwargs=dict(parent=controller.view.nb, function="get_friends", name="%s-friends" % (usr.id_str,), sessionObject=buffer.session, account=buffer.session.get_name(), sound="new_event.ogg", user_id=usr.id_str))
buffer.session.settings["other_buffers"]["friends_timelines"].append(usr.id_str)
buffer.session.sound.play("create_timeline.ogg")
else:
commonMessageDialogs.user_not_exist()
buffer.session.settings.write()
def open_conversation(self, controller, buffer):
tweet = buffer.get_right_tweet()
if hasattr(tweet, "retweeted_status") and tweet.retweeted_status != None:
tweet = tweet.retweeted_status
user = buffer.session.get_user(tweet.user).screen_name
searches_position =controller.view.search("searches", buffer.session.get_name())
pub.sendMessage("createBuffer", buffer_type="ConversationBuffer", session_type=buffer.session.type, buffer_title=_(u"Conversation with {0}").format(user), parent_tab=searches_position, start=True, kwargs=dict(tweet=tweet, parent=controller.view.nb, function="search_tweets", name="%s-searchterm" % (tweet.id,), sessionObject=buffer.session, account=buffer.session.get_name(), bufferType="searchPanel", sound="search_updated.ogg", since_id=tweet.id, q="@{0}".format(user)))
def get_trending_topics(self, controller, session):
trends = trendingTopics.trendingTopicsController(session)
if trends.dialog.get_response() == widgetUtils.OK:
woeid = trends.get_woeid()
if woeid in session.settings["other_buffers"]["trending_topic_buffers"]:
return
root_position =controller.view.search(session.get_name(), session.get_name())
pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (trends.get_string()), parent_tab=root_position, start=True, kwargs=dict(parent=controller.view.nb, name="%s_tt" % (woeid,), sessionObject=session, account=session.get_name(), trendsFor=woeid, sound="trends_updated.ogg"))
session.settings["other_buffers"]["trending_topic_buffers"].append(str(woeid))
session.settings.write()
def start_buffer(self, controller, buffer):
if hasattr(buffer, "finished_timeline") and buffer.finished_timeline == False:
change_title = True
else:
change_title = False
try:
if "mentions" in buffer.name or "direct_messages" in buffer.name:
buffer.start_stream()
else:
buffer.start_stream(play_sound=False)
except TweepyException as err:
log.exception("Error %s starting buffer %s on account %s, with args %r and kwargs %r." % (str(err), buffer.name, buffer.account, buffer.args, buffer.kwargs))
# Determine if this error was caused by a block applied to the current user (IE permission errors).
if type(err) == Forbidden:
buff = controller.view.search(buffer.name, buffer.account)
buffer.remove_buffer(force=True)
commonMessageDialogs.blocked_timeline()
if controller.get_current_buffer() == buffer:
controller.right()
controller.view.delete_buffer(buff)
controller.buffers.remove(buffer)
del buffer
if change_title:
pub.sendMessage("buffer-title-changed", buffer=buffer)
def update_profile(self, session):
r = user.profileController(session)
def search(self, controller, session, value):
log.debug("Creating a new search...")
dlg = dialogs.search.searchDialog(value)
if dlg.get_response() == widgetUtils.OK and dlg.get("term") != "":
term = dlg.get("term")
searches_position =controller.view.search("searches", session.get_name())
if dlg.get("tweets") == True:
if term not in session.settings["other_buffers"]["tweet_searches"]:
session.settings["other_buffers"]["tweet_searches"].append(term)
session.settings.write()
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=controller.view.nb, function="search_tweets", name="%s-searchterm" % (term,), sessionObject=session, account=session.get_name(), bufferType="searchPanel", sound="search_updated.ogg", q=term, include_ext_alt_text=True, tweet_mode="extended"))
else:
log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,))
return
elif dlg.get("users") == True:
pub.sendMessage("createBuffer", buffer_type="SearchPeopleBuffer", session_type=session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=controller.view.nb, function="search_users", name="%s-searchUser" % (term,), sessionObject=session, account=session.get_name(), bufferType=None, sound="search_updated.ogg", q=term))
dlg.Destroy()

View File

@@ -1,117 +0,0 @@
# -*- coding: utf-8 -*-
import widgetUtils
import output
import logging
from wxUI.dialogs import lists
from tweepy.errors import TweepyException
from sessions.twitter import compose, utils
from pubsub import pub
log = logging.getLogger("controller.listsController")
class listsController(object):
def __init__(self, session, user=None, lists_buffer_position=0):
super(listsController, self).__init__()
self.session = session
self.lists_buffer_position = lists_buffer_position
if user == None:
self.dialog = lists.listViewer()
self.dialog.populate_list(self.get_all_lists())
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.create_list)
widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
else:
self.dialog = lists.userListViewer(user)
self.dialog.populate_list(self.get_user_lists(user))
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.subscribe)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.unsubscribe)
self.dialog.get_response()
def get_all_lists(self):
return [compose.compose_list(item) for item in self.session.db["lists"]]
def get_user_lists(self, user):
self.lists = self.session.twitter.get_lists(reverse=True, screen_name=user)
return [compose.compose_list(item) for item in self.lists]
def create_list(self, *args, **kwargs):
dialog = lists.createListDialog()
if dialog.get_response() == widgetUtils.OK:
name = dialog.get("name")
description = dialog.get("description")
p = dialog.get("public")
if p == True:
mode = "public"
else:
mode = "private"
try:
new_list = self.session.twitter.create_list(name=name, description=description, mode=mode)
self.session.db["lists"].append(new_list)
self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
except TweepyException as e:
output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))
dialog.destroy()
def edit_list(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()]
dialog = lists.editListDialog(list)
if dialog.get_response() == widgetUtils.OK:
name = dialog.get("name")
description = dialog.get("description")
p = dialog.get("public")
if p == True:
mode = "public"
else:
mode = "private"
try:
self.session.twitter.update_list(list_id=list.id, name=name, description=description, mode=mode)
self.session.get_lists()
self.dialog.populate_list(self.get_all_lists(), True)
except TweepyException as e:
output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))
dialog.destroy()
def remove_list(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()].id
if lists.remove_list() == widgetUtils.YES:
try:
self.session.twitter.destroy_list(list_id=list)
self.session.db["lists"].pop(self.dialog.get_item())
self.dialog.lista.remove_item(self.dialog.get_item())
except TweepyException as e:
output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))
def open_list_as_buffer(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()]
pub.sendMessage("createBuffer", buffer_type="ListBuffer", session_type=self.session.type, buffer_title=_("List for {}").format(list.name), parent_tab=self.lists_buffer_position, start=True, kwargs=dict(function="list_timeline", name="%s-list" % (list.name,), sessionObject=self.session, account=self.session.get_name(), bufferType=None, sound="list_tweet.ogg", list_id=list.id, include_ext_alt_text=True, tweet_mode="extended"))
self.session.settings["other_buffers"]["lists"].append(list.name)
self.session.settings.write()
def subscribe(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()].id
try:
list = self.session.twitter.subscribe_list(list_id=list_id)
item = utils.find_item(list.id, self.session.db["lists"])
self.session.db["lists"].append(list)
except TweepyException as e:
output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))
def unsubscribe(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()].id
try:
list = self.session.twitter.unsubscribe_list(list_id=list_id)
self.session.db["lists"].remove(list)
except TweepyException as e:
output.speak("error %s" % (str(e)))
log.exception("error %s" % (str(e)))

View File

@@ -1,382 +0,0 @@
# -*- coding: utf-8 -*-
import os
import arrow
import languageHandler
import wx
import widgetUtils
import output
import sound
import config
from pubsub import pub
from twitter_text.parse_tweet import parse_tweet
from wxUI.dialogs import twitterDialogs, urlList
from wxUI import commonMessageDialogs
from extra import translator, SpellChecker
from extra.AudioUploader import audioUploader
from extra.autocompletionUsers import completion
from sessions.twitter import utils
class basicTweet(object):
""" This class handles the tweet main features. Other classes should derive from this class."""
def __init__(self, session, title, caption, text="", messageType="tweet", max=280, *args, **kwargs):
super(basicTweet, self).__init__()
self.max = max
self.title = title
self.session = session
self.message = getattr(twitterDialogs, messageType)(title=title, caption=caption, message=text, max_length=max, *args, **kwargs)
self.message.text.SetValue(text)
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.add_audio, widgetUtils.BUTTON_PRESSED, self.attach)
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
widgetUtils.connect_event(self.message.translate, widgetUtils.BUTTON_PRESSED, self.translate)
if hasattr(self.message, "add"):
widgetUtils.connect_event(self.message.add, widgetUtils.BUTTON_PRESSED, self.on_attach)
self.attachments = []
def translate(self, event=None):
dlg = translator.gui.translateDialog()
if dlg.get_response() == widgetUtils.OK:
text_to_translate = self.message.text.GetValue()
language_dict = translator.translator.available_languages()
for k in language_dict:
if language_dict[k] == dlg.dest_lang.GetStringSelection():
dst = k
msg = translator.translator.translate(text=text_to_translate, target=dst)
self.message.text.ChangeValue(msg)
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
self.text_processor()
self.message.text.SetFocus()
output.speak(_(u"Translated"))
else:
return
def text_processor(self, *args, **kwargs):
text = self.message.text.GetValue()
results = parse_tweet(text)
self.message.SetTitle(_("%s - %s of %d characters") % (self.title, results.weightedLength, self.max))
if results.weightedLength > self.max:
self.session.sound.play("max_length.ogg")
def spellcheck(self, event=None):
text = self.message.text.GetValue()
checker = SpellChecker.spellchecker.spellChecker(text, "")
if hasattr(checker, "fixed_text"):
self.message.text.ChangeValue(checker.fixed_text)
self.text_processor()
self.message.text.SetFocus()
def attach(self, *args, **kwargs):
def completed_callback(dlg):
url = dlg.uploaderFunction.get_url()
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
dlg.uploaderDialog.destroy()
if "sndup.net/" in url:
self.message.text.ChangeValue(self.message.text.GetValue()+url+" #audio")
self.text_processor()
else:
commonMessageDialogs.common_error(url)
dlg.cleanup()
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
self.message.text.SetFocus()
def can_attach(self):
if len(self.attachments) == 0:
return True
elif len(self.attachments) == 1 and (self.attachments[0]["type"] == "video" or self.attachments[0]["type"] == "gif"):
return False
elif len(self.attachments) < 4:
return True
return False
def on_attach(self, *args, **kwargs):
can_attach = self.can_attach()
menu = self.message.attach_menu(can_attach)
self.message.Bind(wx.EVT_MENU, self.on_attach_image, self.message.add_image)
self.message.Bind(wx.EVT_MENU, self.on_attach_video, self.message.add_video)
if hasattr(self.message, "add_poll"):
self.message.Bind(wx.EVT_MENU, self.on_attach_poll, self.message.add_poll)
self.message.PopupMenu(menu, self.message.add.GetPosition())
def on_attach_image(self, *args, **kwargs):
can_attach = self.can_attach()
video_or_gif_present = False
for a in self.attachments:
if a["type"] == "video" or a["type"] == "gif":
video_or_gif = True
break
if can_attach == False or video_or_gif_present == True:
return self.message.unable_to_attach_file()
image, description = self.message.get_image()
if image != None:
if image.endswith("gif"):
image_type = "gif"
else:
image_type = "photo"
imageInfo = {"type": image_type, "file": image, "description": description}
if len(self.attachments) > 0 and image_type == "gif":
return self.message.unable_to_attach_file()
self.attachments.append(imageInfo)
self.message.add_item(item=[os.path.basename(imageInfo["file"]), imageInfo["type"], imageInfo["description"]])
self.text_processor()
def on_attach_video(self, *args, **kwargs):
if len(self.attachments) > 0:
return self.message.unable_to_attach_file()
video = self.message.get_video()
if video != None:
videoInfo = {"type": "video", "file": video, "description": ""}
if len(self.attachments) > 0:
return self.message.unable_to_attach_file()
self.attachments.append(videoInfo)
self.message.add_item(item=[os.path.basename(videoInfo["file"]), videoInfo["type"], videoInfo["description"]])
self.text_processor()
def on_attach_poll(self, *args, **kwargs):
dlg = twitterDialogs.poll()
if dlg.ShowModal() == wx.ID_OK:
self.poll_options = dlg.get_options()
self.poll_period = 60*24*dlg.period.GetValue()
dlg.Destroy()
def remove_attachment(self, *args, **kwargs):
attachment = self.message.attachments.GetFocusedItem()
if attachment > -1 and len(self.attachments) > attachment:
self.attachments.pop(attachment)
self.message.remove_item(list_type="attachment")
self.text_processor()
self.message.text.SetFocus()
class tweet(basicTweet):
def __init__(self, session, title, caption, text="", max=280, messageType="tweet", *args, **kwargs):
self.thread = []
self.poll_options = None
self.poll_period = None
super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs)
widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
if hasattr(self.message, "add_tweet"):
widgetUtils.connect_event(self.message.add_tweet, widgetUtils.BUTTON_PRESSED, self.add_tweet)
widgetUtils.connect_event(self.message.remove_tweet, widgetUtils.BUTTON_PRESSED, self.remove_tweet)
widgetUtils.connect_event(self.message.remove_attachment, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
self.text_processor()
def autocomplete_users(self, *args, **kwargs):
c = completion.autocompletionUsers(self.message, self.session.session_id)
c.show_menu()
def add_tweet(self, event, update_gui=True, *args, **kwargs):
text = self.message.text.GetValue()
attachments = self.attachments[::]
tweetdata = dict(text=text, attachments=attachments, poll_options=self.poll_options, poll_period=self.poll_period)
self.thread.append(tweetdata)
self.attachments = []
self.poll_options = None
self.poll_period = None
if update_gui:
self.message.reset_controls()
self.message.add_item(item=[text, len(attachments)], list_type="tweet")
self.message.text.SetFocus()
self.text_processor()
def get_tweet_data(self):
self.add_tweet(event=None, update_gui=False)
return self.thread
def text_processor(self, *args, **kwargs):
super(tweet, self).text_processor(*args, **kwargs)
if len(self.thread) > 0:
if hasattr(self.message, "tweets"):
self.message.tweets.Enable(True)
self.message.remove_tweet.Enable(True)
else:
self.message.tweets.Enable(False)
self.message.remove_tweet.Enable(False)
if len(self.attachments) > 0:
self.message.attachments.Enable(True)
self.message.remove_attachment.Enable(True)
else:
self.message.attachments.Enable(False)
self.message.remove_attachment.Enable(False)
if hasattr(self.message, "add_tweet"):
if len(self.message.text.GetValue()) > 0 or len(self.attachments) > 0:
self.message.add_tweet.Enable(True)
else:
self.message.add_tweet.Enable(False)
def remove_tweet(self, *args, **kwargs):
tweet = self.message.tweets.GetFocusedItem()
if tweet > -1 and len(self.thread) > tweet:
self.thread.pop(tweet)
self.message.remove_item(list_type="tweet")
self.text_processor()
self.message.text.SetFocus()
class reply(tweet):
def __init__(self, session, title, caption, text, users=[], ids=[]):
super(reply, self).__init__(session, title, caption, text, messageType="reply", users=users)
self.ids = ids
self.users = users
if len(users) > 0:
widgetUtils.connect_event(self.message.mention_all, widgetUtils.CHECKBOX, self.mention_all)
self.message.mention_all.Enable(True)
if config.app["app-settings"]["remember_mention_and_longtweet"]:
self.message.mention_all.SetValue(config.app["app-settings"]["mention_all"])
self.mention_all()
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
self.text_processor()
def text_processor(self, *args, **kwargs):
super(tweet, self).text_processor(*args, **kwargs)
if len(self.attachments) > 0:
self.message.attachments.Enable(True)
self.message.remove_attachment.Enable(True)
else:
self.message.attachments.Enable(False)
self.message.remove_attachment.Enable(False)
def mention_all(self, *args, **kwargs):
if self.message.mention_all.GetValue() == True:
for i in self.message.checkboxes:
i.SetValue(True)
i.Hide()
else:
for i in self.message.checkboxes:
i.SetValue(False)
i.Show()
def get_ids(self):
excluded_ids = []
for i in range(0, len(self.message.checkboxes)):
if self.message.checkboxes[i].GetValue() == False:
excluded_ids.append(self.ids[i])
return excluded_ids
def get_people(self):
people = ""
for i in range(0, len(self.message.checkboxes)):
if self.message.checkboxes[i].GetValue() == True:
people = people + "{0} ".format(self.message.checkboxes[i].GetLabel(),)
return people
class dm(basicTweet):
def __init__(self, session, title, caption, users):
super(dm, self).__init__(session, title, caption, messageType="dm", max=10000, users=users)
widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
self.text_processor()
widgetUtils.connect_event(self.message.cb, widgetUtils.ENTERED_TEXT, self.user_changed)
def user_changed(self, *args, **kwargs):
self.title = _("Direct message to %s") % (self.message.cb.GetValue())
self.text_processor()
def autocomplete_users(self, *args, **kwargs):
c = completion.autocompletionUsers(self.message, self.session.session_id)
c.show_menu("dm")
def text_processor(self, *args, **kwargs):
super(dm, self).text_processor(*args, **kwargs)
if len(self.attachments) > 0:
self.message.attachments.Enable(True)
self.message.remove_attachment.Enable(True)
else:
self.message.attachments.Enable(False)
self.message.remove_attachment.Enable(False)
def can_attach(self):
if len(self.attachments) == 0:
return True
return False
class viewTweet(basicTweet):
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date="", item_url=""):
""" This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event.
param tweet: A dictionary that represents a full tweet or a string for non-tweets.
param tweetList: If is_tweet is set to True, this could be a list of quoted tweets.
param is_tweet: True or false, depending wether the passed object is a tweet or not."""
if is_tweet == True:
self.title = _(u"Tweet")
image_description = []
text = ""
for i in range(0, len(tweetList)):
# tweets with message keys are longer tweets, the message value is the full messaje taken from twishort.
if hasattr(tweetList[i], "message") and tweetList[i].is_quote_status == False:
value = "message"
else:
value = "full_text"
if hasattr(tweetList[i], "retweeted_status") and tweetList[i].is_quote_status == False:
if not hasattr(tweetList[i], "message"):
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, tweetList[i].retweeted_status.full_text)
else:
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, getattr(tweetList[i], value))
else:
text = text + " @%s: %s\n" % (tweetList[i].user.screen_name, getattr(tweetList[i], value))
# tweets with extended_entities could include image descriptions.
if hasattr(tweetList[i], "extended_entities") and "media" in tweetList[i].extended_entities:
for z in tweetList[i].extended_entities["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
if hasattr(tweetList[i], "retweeted_status") and hasattr(tweetList[i].retweeted_status, "extended_entities") and "media" in tweetList[i].retweeted_status["extended_entities"]:
for z in tweetList[i].retweeted_status.extended_entities["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
# set rt and likes counters.
rt_count = str(tweet.retweet_count)
favs_count = str(tweet.favorite_count)
# Gets the client from where this tweet was made.
source = tweet.source
original_date = arrow.get(tweet.created_at, locale="en")
date = original_date.shift(seconds=utc_offset).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
if text == "":
if hasattr(tweet, "message"):
value = "message"
else:
value = "full_text"
if hasattr(tweet, "retweeted_status"):
if not hasattr(tweet, "message"):
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, tweet.retweeted_status.full_text)
else:
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, getattr(tweet, value))
else:
text = getattr(tweet, value)
text = self.clear_text(text)
if hasattr(tweet, "extended_entities") and "media" in tweet.extended_entities:
for z in tweet.extended_entities["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
if hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "extended_entities") and "media" in tweet.retweeted_status.extended_entities:
for z in tweet.retweeted_status.extended_entities["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
self.message = twitterDialogs.viewTweet(text, rt_count, favs_count, source, date)
results = parse_tweet(text)
self.message.set_title(results.weightedLength)
[self.message.set_image_description(i) for i in image_description]
else:
self.title = _(u"View item")
text = tweet
self.message = twitterDialogs.viewNonTweet(text, date)
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
if item_url != "":
self.message.enable_button("share")
widgetUtils.connect_event(self.message.share, widgetUtils.BUTTON_PRESSED, self.share)
self.item_url = item_url
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
self.message.ShowModal()
# We won't need text_processor in this dialog, so let's avoid it.
def text_processor(self):
pass
def clear_text(self, text):
text = utils.StripChars(text)
urls = utils.find_urls_in_text(text)
for i in urls:
if "https://twitter.com/" in i:
text = text.replace(i, "\n")
return text
def share(self, *args, **kwargs):
if hasattr(self, "item_url"):
output.copy(self.item_url)
output.speak(_("Link copied to clipboard."))

View File

@@ -1,247 +0,0 @@
# -*- coding: utf-8 -*-
import os
import threading
import logging
import sound_lib
import paths
import widgetUtils
import output
from collections import OrderedDict
from wxUI import commonMessageDialogs
from extra.autocompletionUsers import scan, manage
from extra.ocr import OCRSpace
from controller.settings import globalSettingsController
from . templateEditor import EditTemplate
log = logging.getLogger("Settings")
class accountSettingsController(globalSettingsController):
def __init__(self, buffer, window):
self.user = buffer.session.db["user_name"]
self.buffer = buffer
self.window = window
self.config = buffer.session.settings
super(accountSettingsController, self).__init__()
def create_config(self):
self.dialog.create_general_account()
widgetUtils.connect_event(self.dialog.general.userAutocompletionScan, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_scan)
widgetUtils.connect_event(self.dialog.general.userAutocompletionManage, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_manage)
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"])
self.dialog.set_value("general", "hide_emojis", self.config["general"]["hide_emojis"])
self.dialog.set_value("general", "itemsPerApiCall", self.config["general"]["max_tweets_per_call"])
self.dialog.set_value("general", "reverse_timelines", self.config["general"]["reverse_timelines"])
rt = self.config["general"]["retweet_mode"]
if rt == "ask":
self.dialog.set_value("general", "retweet_mode", _(u"Ask"))
elif rt == "direct":
self.dialog.set_value("general", "retweet_mode", _(u"Retweet without comments"))
else:
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
self.dialog.set_value("general", "persist_size", str(self.config["general"]["persist_size"]))
self.dialog.set_value("general", "load_cache_in_memory", self.config["general"]["load_cache_in_memory"])
self.dialog.create_reporting()
self.dialog.set_value("reporting", "speech_reporting", self.config["reporting"]["speech_reporting"])
self.dialog.set_value("reporting", "braille_reporting", self.config["reporting"]["braille_reporting"])
tweet_template = self.config["templates"]["tweet"]
dm_template = self.config["templates"]["dm"]
sent_dm_template = self.config["templates"]["dm_sent"]
person_template = self.config["templates"]["person"]
self.dialog.create_templates(tweet_template=tweet_template, dm_template=dm_template, sent_dm_template=sent_dm_template, person_template=person_template)
widgetUtils.connect_event(self.dialog.templates.tweet, widgetUtils.BUTTON_PRESSED, self.edit_tweet_template)
widgetUtils.connect_event(self.dialog.templates.dm, widgetUtils.BUTTON_PRESSED, self.edit_dm_template)
widgetUtils.connect_event(self.dialog.templates.sent_dm, widgetUtils.BUTTON_PRESSED, self.edit_sent_dm_template)
widgetUtils.connect_event(self.dialog.templates.person, widgetUtils.BUTTON_PRESSED, self.edit_person_template)
self.dialog.create_other_buffers()
buffer_values = self.get_buffers_list()
self.dialog.buffers.insert_buffers(buffer_values)
self.dialog.buffers.connect_hook_func(self.toggle_buffer_active)
widgetUtils.connect_event(self.dialog.buffers.toggle_state, widgetUtils.BUTTON_PRESSED, self.toggle_state)
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
self.input_devices = sound_lib.input.Input.get_device_names()
self.output_devices = sound_lib.output.Output.get_device_names()
self.soundpacks = []
[self.soundpacks.append(i) for i in os.listdir(paths.sound_path()) if os.path.isdir(os.path.join(paths.sound_path(), i)) == True ]
self.dialog.create_sound(self.input_devices, self.output_devices, self.soundpacks)
self.dialog.set_value("sound", "volumeCtrl", int(self.config["sound"]["volume"]*100))
self.dialog.set_value("sound", "input", self.config["sound"]["input_device"])
self.dialog.set_value("sound", "output", self.config["sound"]["output_device"])
self.dialog.set_value("sound", "session_mute", self.config["sound"]["session_mute"])
self.dialog.set_value("sound", "soundpack", self.config["sound"]["current_soundpack"])
self.dialog.set_value("sound", "indicate_audio", self.config["sound"]["indicate_audio"])
self.dialog.set_value("sound", "indicate_geo", self.config["sound"]["indicate_geo"])
self.dialog.set_value("sound", "indicate_img", self.config["sound"]["indicate_img"])
self.dialog.create_extras(OCRSpace.translatable_langs)
self.dialog.set_value("extras", "sndup_apiKey", self.config["sound"]["sndup_api_key"])
language_index = OCRSpace.OcrLangs.index(self.config["mysc"]["ocr_language"])
self.dialog.extras.ocr_lang.SetSelection(language_index)
self.dialog.realize()
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
self.response = self.dialog.get_response()
def edit_tweet_template(self, *args, **kwargs):
template = self.config["templates"]["tweet"]
control = EditTemplate(template=template, type="tweet")
result = control.run_dialog()
if result != "": # Template has been saved.
self.config["templates"]["tweet"] = result
self.config.write()
self.dialog.templates.tweet.SetLabel(_("Edit template for tweets. Current template: {}").format(result))
def edit_dm_template(self, *args, **kwargs):
template = self.config["templates"]["dm"]
control = EditTemplate(template=template, type="dm")
result = control.run_dialog()
if result != "": # Template has been saved.
self.config["templates"]["dm"] = result
self.config.write()
self.dialog.templates.dm.SetLabel(_("Edit template for direct messages. Current template: {}").format(result))
def edit_sent_dm_template(self, *args, **kwargs):
template = self.config["templates"]["dm_sent"]
control = EditTemplate(template=template, type="dm")
result = control.run_dialog()
if result != "": # Template has been saved.
self.config["templates"]["dm_sent"] = result
self.config.write()
self.dialog.templates.sent_dm.SetLabel(_("Edit template for sent direct messages. Current template: {}").format(result))
def edit_person_template(self, *args, **kwargs):
template = self.config["templates"]["person"]
control = EditTemplate(template=template, type="person")
result = control.run_dialog()
if result != "": # Template has been saved.
self.config["templates"]["person"] = result
self.config.write()
self.dialog.templates.person.SetLabel(_("Edit template for persons. Current template: {}").format(result))
def save_configuration(self):
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
self.needs_restart = True
log.debug("Triggered app restart due to change in relative times.")
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
self.config["general"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names")
self.config["general"]["hide_emojis"] = self.dialog.get_value("general", "hide_emojis")
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
if self.config["general"]["load_cache_in_memory"] != self.dialog.get_value("general", "load_cache_in_memory"):
self.config["general"]["load_cache_in_memory"] = self.dialog.get_value("general", "load_cache_in_memory")
self.needs_restart = True
log.debug("Triggered app restart due to change in database strategy management.")
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
if self.dialog.get_value("general", "persist_size") == '':
self.config["general"]["persist_size"] =-1
else:
try:
self.config["general"]["persist_size"] = int(self.dialog.get_value("general", "persist_size"))
except ValueError:
output.speak("Invalid cache size, setting to default.",True)
self.config["general"]["persist_size"] =1764
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
self.needs_restart = True
log.debug("Triggered app restart due to change in timeline order.")
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
rt = self.dialog.get_value("general", "retweet_mode")
if rt == _(u"Ask"):
self.config["general"]["retweet_mode"] = "ask"
elif rt == _(u"Retweet without comments"):
self.config["general"]["retweet_mode"] = "direct"
else:
self.config["general"]["retweet_mode"] = "comment"
buffers_list = self.dialog.buffers.get_list()
if buffers_list != self.config["general"]["buffer_order"]:
self.needs_restart = True
log.debug("Triggered app restart due to change in buffer ordering.")
self.config["general"]["buffer_order"] = buffers_list
self.config["reporting"]["speech_reporting"] = self.dialog.get_value("reporting", "speech_reporting")
self.config["reporting"]["braille_reporting"] = self.dialog.get_value("reporting", "braille_reporting")
self.config["mysc"]["ocr_language"] = OCRSpace.OcrLangs[self.dialog.extras.ocr_lang.GetSelection()]
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
try:
self.buffer.session.sound.input.set_device(self.buffer.session.sound.input.find_device_by_name(self.config["sound"]["input_device"]))
except:
self.config["sound"]["input_device"] = "default"
if self.config["sound"]["output_device"] != self.dialog.sound.get("output"):
self.config["sound"]["output_device"] = self.dialog.sound.get("output")
try:
self.buffer.session.sound.output.set_device(self.buffer.session.sound.output.find_device_by_name(self.config["sound"]["output_device"]))
except:
self.config["sound"]["output_device"] = "default"
self.config["sound"]["volume"] = self.dialog.get_value("sound", "volumeCtrl")/100.0
self.config["sound"]["session_mute"] = self.dialog.get_value("sound", "session_mute")
self.config["sound"]["current_soundpack"] = self.dialog.sound.get("soundpack")
self.config["sound"]["indicate_audio"] = self.dialog.get_value("sound", "indicate_audio")
self.config["sound"]["indicate_geo"] = self.dialog.get_value("sound", "indicate_geo")
self.config["sound"]["indicate_img"] = self.dialog.get_value("sound", "indicate_img")
self.config["sound"]["sndup_api_key"] = self.dialog.get_value("extras", "sndup_apiKey")
self.buffer.session.sound.config = self.config["sound"]
self.buffer.session.sound.check_soundpack()
self.config.write()
def toggle_state(self,*args,**kwargs):
return self.dialog.buffers.change_selected_item()
def on_autocompletion_scan(self, *args, **kwargs):
configuration = scan.autocompletionScan(self.buffer.session.settings, self.buffer, self.window)
to_scan = configuration.show_dialog()
if to_scan == True:
configuration.prepare_progress_dialog()
t = threading.Thread(target=configuration.scan)
t.start()
def on_autocompletion_manage(self, *args, **kwargs):
configuration = manage.autocompletionManage(self.buffer.session)
configuration.show_settings()
def add_ignored_client(self, *args, **kwargs):
client = commonMessageDialogs.get_ignored_client()
if client == None: return
if client not in self.config["twitter"]["ignored_clients"]:
self.config["twitter"]["ignored_clients"].append(client)
self.dialog.ignored_clients.append(client)
def remove_ignored_client(self, *args, **kwargs):
if self.dialog.ignored_clients.get_clients() == 0: return
id = self.dialog.ignored_clients.get_client_id()
self.config["twitter"]["ignored_clients"].pop(id)
self.dialog.ignored_clients.remove_(id)
def get_buffers_list(self):
all_buffers=OrderedDict()
all_buffers['home']=_(u"Home")
all_buffers['mentions']=_(u"Mentions")
all_buffers['dm']=_(u"Direct Messages")
all_buffers['sent_dm']=_(u"Sent direct messages")
all_buffers['sent_tweets']=_(u"Sent tweets")
all_buffers['favorites']=_(u"Likes")
all_buffers['followers']=_(u"Followers")
all_buffers['friends']=_(u"Friends")
all_buffers['blocks']=_(u"Blocked users")
all_buffers['muted']=_(u"Muted users")
list_buffers = []
hidden_buffers=[]
all_buffers_keys = list(all_buffers.keys())
# Check buffers shown first.
for i in self.config["general"]["buffer_order"]:
if i in all_buffers_keys:
list_buffers.append((i, all_buffers[i], True))
# This second pass will retrieve all hidden buffers.
for i in all_buffers_keys:
if i not in self.config["general"]["buffer_order"]:
hidden_buffers.append((i, all_buffers[i], False))
list_buffers.extend(hidden_buffers)
return list_buffers
def toggle_buffer_active(self, ev):
change = self.dialog.buffers.get_event(ev)
if change == True:
self.dialog.buffers.change_selected_item()

View File

@@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
import re
import wx
from typing import List
from sessions.twitter.templates import tweet_variables, dm_variables, person_variables
from wxUI.dialogs.twitterDialogs import templateDialogs
class EditTemplate(object):
def __init__(self, template: str, type: str) -> None:
super(EditTemplate, self).__init__()
self.default_template = template
if type == "tweet":
self.variables = tweet_variables
elif type == "dm":
self.variables = dm_variables
else:
self.variables = person_variables
self.template: str = template
def validate_template(self, template: str) -> bool:
used_variables: List[str] = re.findall("\$\w+", template)
validated: bool = True
for var in used_variables:
if var[1:] not in self.variables:
validated = False
return validated
def run_dialog(self) -> str:
dialog = templateDialogs.EditTemplateDialog(template=self.template, variables=self.variables, default_template=self.default_template)
response = dialog.ShowModal()
if response == wx.ID_SAVE:
validated: bool = self.validate_template(dialog.template.GetValue())
if validated == False:
templateDialogs.invalid_template()
self.template = dialog.template.GetValue()
return self.run_dialog()
else:
return dialog.template.GetValue()
else:
return ""

View File

@@ -1,45 +0,0 @@
# -*- coding: utf-8 -*-
from wxUI.dialogs import trends
import widgetUtils
class trendingTopicsController(object):
def __init__(self, session):
super(trendingTopicsController, self).__init__()
self.countries = {}
self.cities = {}
self.dialog = trends.trendingTopicsDialog()
self.information = session.twitter.available_trends()
self.split_information()
widgetUtils.connect_event(self.dialog.country, widgetUtils.RADIOBUTTON, self.get_places)
widgetUtils.connect_event(self.dialog.city, widgetUtils.RADIOBUTTON, self.get_places)
self.get_places()
def split_information(self):
for i in self.information:
if i["placeType"]["name"] == "Country":
self.countries[i["name"]] = i["woeid"]
else:
self.cities[i["name"]] = i["woeid"]
def get_places(self, event=None):
values = []
if self.dialog.get_active() == "country":
for i in self.information:
if i["placeType"]["name"] == "Country":
values.append(i["name"])
elif self.dialog.get_active() == "city":
for i in self.information:
if i["placeType"]["name"] != "Country":
values.append(i["name"])
self.dialog.set(values)
def get_woeid(self):
selected = self.dialog.get_item()
if self.dialog.get_active() == "country":
woeid = self.countries[selected]
else:
woeid = self.cities[selected]
return woeid
def get_string(self):
return self.dialog.get_item()

View File

@@ -1,128 +0,0 @@
# -*- coding: utf-8 -*-
import wx
import webbrowser
import widgetUtils
import output
from wxUI.dialogs import update_profile, show_user
import logging
log = logging.getLogger("controller.user")
from tweepy.errors import TweepyException, Forbidden, NotFound
from sessions.twitter import utils
class profileController(object):
def __init__(self, session, user=None):
super(profileController, self).__init__()
self.file = None
self.session = session
self.user = user
if user == None:
self.get_data(screen_name=self.session.db["user_name"])
self.dialog = update_profile.updateProfileDialog()
self.fill_profile_fields()
self.uploaded = False
widgetUtils.connect_event(self.dialog.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
else:
try:
self.get_data(screen_name=self.user)
except TweepyException as err:
if type(err) == NotFound:
wx.MessageDialog(None, _(u"That user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
if type(err) == Forbidden:
wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal()
log.error("error %s" % (str(err)))
return
self.dialog = show_user.showUserProfile()
string = self.get_user_info()
self.dialog.set("text", string)
self.dialog.set_title(_(u"Information for %s") % (self.data.screen_name))
if self.data.url != None:
self.dialog.enable_url()
widgetUtils.connect_event(self.dialog.url, widgetUtils.BUTTON_PRESSED, self.visit_url)
if self.dialog.get_response() == widgetUtils.OK and self.user == None:
self.do_update()
def get_data(self, screen_name):
self.data = self.session.twitter.get_user(screen_name=screen_name)
if screen_name != self.session.db["user_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):
self.dialog.set_name(self.data.name)
if self.data.url != None:
self.dialog.set_url(self.data.url)
if len(self.data.location) > 0:
self.dialog.set_location(self.data.location)
if len(self.data.description) > 0:
self.dialog.set_description(self.data.description)
def get_image(self):
file = self.dialog.upload_picture()
if file != None:
self.file = open(file, "rb")
self.uploaded = True
self.dialog.change_upload_button(self.uploaded)
def discard_image(self):
self.file = None
output.speak(_(u"Discarded"))
self.uploaded = False
self.dialog.change_upload_button(self.uploaded)
def upload_image(self, *args, **kwargs):
if self.uploaded == False:
self.get_image()
elif self.uploaded == True:
self.discard_image()
def do_update(self):
if self.user != None: return
name = self.dialog.get("name")
description = self.dialog.get("description")
location = self.dialog.get("location")
url = self.dialog.get("url")
if self.file != None:
try:
self.session.twitter.update_profile_image(image=self.file)
except TweepyException as e:
output.speak(u"Error %s" % (str(e)))
try:
self.session.twitter.update_profile(name=name, description=description, location=location, url=url)
except TweepyException as e:
output.speak(u"Error %s." % (str(e)))
def get_user_info(self):
string = u""
string = string + _(u"Username: @%s\n") % (self.data.screen_name)
string = string + _(u"Name: %s\n") % (self.data.name)
if self.data.location != "":
string = string + _(u"Location: %s\n") % (self.data.location)
if self.data.url != None:
string = string+ _(u"URL: %s\n") % (self.data.entities["url"]["urls"][0]["expanded_url"])
if self.data.description != "":
if self.data.entities.get("description") != None and self.data.entities["description"].get("urls"):
self.data.description = utils.expand_urls(self.data.description, self.data.entities["description"])
string = string+ _(u"Bio: %s\n") % (self.data.description)
if self.data.protected == True: protected = _(u"Yes")
else: protected = _(u"No")
string = string+ _(u"Protected: %s\n") % (protected)
if hasattr(self, "friendship_status"):
relation = False
friendship = _(u"Relationship: ")
if self.friendship_status[0].following:
friendship += _(u"You follow {0}. ").format(self.data.name,)
relation = True
if self.friendship_status[1].following:
friendship += _(u"{0} is following you.").format(self.data.name,)
relation = True
if relation == True:
string = string+friendship+"\n"
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data.followers_count, self.data.friends_count)
if self.data.verified == True: verified = _(u"Yes")
else: verified = _(u"No")
string = string+ _(u"Verified: %s\n") % (verified)
string = string+ _(u"Tweets: %s\n") % (self.data.statuses_count)
string = string+ _(u"Likes: %s") % (self.data.favourites_count)
return string
def visit_url(self, *args, **kwargs):
webbrowser.open_new_tab(self.data.url)

View File

@@ -1,85 +0,0 @@
# -*- coding: utf-8 -*-
import widgetUtils
import output
from wxUI.dialogs import userActions
from pubsub import pub
from tweepy.errors import TweepyException
from extra.autocompletionUsers import completion
class userActionsController(object):
def __init__(self, buffer, users=[], default="follow"):
super(userActionsController, self).__init__()
self.buffer = buffer
self.session = buffer.session
self.dialog = userActions.UserActionsDialog(users, default)
widgetUtils.connect_event(self.dialog.autocompletion, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
if self.dialog.get_response() == widgetUtils.OK:
self.process_action()
def autocomplete_users(self, *args, **kwargs):
c = completion.autocompletionUsers(self.dialog, self.session.session_id)
c.show_menu("dm")
def process_action(self):
action = self.dialog.get_action()
user = self.dialog.get_user()
if user == "": return
getattr(self, action)(user)
def follow(self, user):
try:
self.session.twitter.create_friendship(screen_name=user )
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
except TweepyException as err:
output.speak("Error %s" % (str(err)), True)
def unfollow(self, user):
try:
id = self.session.twitter.destroy_friendship(screen_name=user )
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
except TweepyException as err:
output.speak("Error %s" % (str(err)), True)
def mute(self, user):
try:
id = self.session.twitter.create_mute(screen_name=user )
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
except TweepyException as err:
output.speak("Error %s" % (str(err)), True)
def unmute(self, user):
try:
id = self.session.twitter.destroy_mute(screen_name=user )
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
except TweepyException as err:
output.speak("Error %s" % (str(err)), True)
def report(self, user):
try:
id = self.session.twitter.report_spam(screen_name=user )
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
except TweepyException as err:
output.speak("Error %s" % (str(err)), True)
def block(self, user):
try:
id = self.session.twitter.create_block(screen_name=user )
pub.sendMessage("twitter.restart_streaming", session=self.session.session_id)
except TweepyException as err:
output.speak("Error %s" % (str(err)), True)
def unblock(self, user):
try:
id = self.session.twitter.destroy_block(screen_name=user )
except TweepyException as err:
output.speak("Error %s" % (str(err)), True)
def ignore_client(self, user):
tweet = self.buffer.get_right_tweet()
if hasattr(tweet, "sender"):
output.speak(_(u"You can't ignore direct messages"))
return
client = tweet.source
if client not in self.session.settings["twitter"]["ignored_clients"]:
self.session.settings["twitter"]["ignored_clients"].append(client)
self.session.settings.write()

View File

@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
""" Small utility dessigned to select users from the currently focused item or the autocomplete database. """
import wx
import widgetUtils
from wxUI.dialogs import utils
from extra.autocompletionUsers import completion
class userSelector(object):
def __init__(self, users, session_id, title=_("Select user")):
""" Creates a dialog that chooses an user selector, from where users who have the autocomplete database already filled can also use that feature.
:param users: lists of users extracted from the currently focused item.
:type users: list
:param session_id: ID of the session to instantiate autocomplete database.
:type session_id: str
:param title: Title of the user selector dialog.
:type title: str
"""
self.session_id = session_id
self.dlg = utils.selectUserDialog(users=users, title=title)
widgetUtils.connect_event(self.dlg.autocompletion, widgetUtils.BUTTON_PRESSED, self.on_autocomplete_users)
def on_autocomplete_users(self, *args, **kwargs):
""" performs user autocompletion, if configured properly. """
c = completion.autocompletionUsers(self.dlg, self.session_id)
c.show_menu("dm")
def get_user(self):
""" Actually shows the dialog and returns an user if the dialog was accepted, None otherwise.
:rtype: str or None
"""
if self.dlg.ShowModal() == wx.ID_OK:
user = self.dlg.get_user()
else:
user = None
self.dlg.Destroy()
return user

View File

@@ -1,182 +0,0 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import str
from builtins import object
import widgetUtils
from . import wx_ui
from . import wx_transfer_dialogs
from . import transfer
import output
import tempfile
import sound
import os
import config
from pubsub import pub
from mysc.thread_utils import call_threaded
import sound_lib
import logging
log = logging.getLogger("extra.AudioUploader.audioUploader")
class audioUploader(object):
def __init__(self, configFile, completed_callback):
self.config = configFile
super(audioUploader, self).__init__()
self.dialog = wx_ui.audioDialog(services=self.get_available_services())
self.file = None
self.recorded = False
self.recording = None
self.playing = None
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause)
widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record)
widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists)
widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard)
if self.dialog.get_response() == widgetUtils.OK:
self.postprocess()
log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services")))
self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file)
output.speak(_(u"Attaching..."))
if self.dialog.get("services") == "SNDUp":
base_url = "https://sndup.net/post.php"
if len(self.config["sound"]["sndup_api_key"]) > 0:
url = base_url + '?apikey=' + self.config['sound']['sndup_api_key']
else:
url = base_url
self.uploaderFunction = transfer.Upload(obj=self, field='file', url=url, filename=self.file, completed_callback=completed_callback)
pub.subscribe(self.uploaderDialog.update, "uploading")
self.uploaderDialog.get_response(self.uploaderFunction.perform_threaded)
def get_available_services(self):
services = []
services.append("SNDUp")
return services
def on_pause(self, *args, **kwargs):
if self.dialog.get("pause") == _(u"Pause"):
self.recording.pause()
self.dialog.set("pause", _(u"&Resume"))
elif self.dialog.get("pause") == _(u"Resume"):
self.recording.play()
self.dialog.set("pause", _(U"&Pause"))
def on_record(self, *args, **kwargs):
if self.recording != None:
self.stop_recording()
self.dialog.disable_control("pause")
else:
self.start_recording()
self.dialog.enable_control("pause")
def start_recording(self):
self.dialog.disable_control("attach_exists")
self.file = tempfile.mktemp(suffix='.wav')
self.recording = sound.recording(self.file)
self.recording.play()
self.dialog.set("record", _(u"&Stop"))
output.speak(_(u"Recording"))
def stop_recording(self):
self.recording.stop()
self.recording.free()
output.speak(_(u"Stopped"))
self.recorded = True
self.dialog.set("record", _(u"&Record"))
self.file_attached()
def file_attached(self):
self.dialog.set("pause", _(u"&Pause"))
self.dialog.disable_control("record")
self.dialog.enable_control("play")
self.dialog.enable_control("discard")
self.dialog.disable_control("attach_exists")
self.dialog.enable_control("attach")
self.dialog.play.SetFocus()
def on_discard(self, *args, **kwargs):
if self.playing:
self._stop()
if self.recording != None:
self.cleanup()
self.dialog.disable_control("attach")
self.dialog.disable_control("play")
self.file = None
self.dialog.enable_control("record")
self.dialog.enable_control("attach_exists")
self.dialog.record.SetFocus()
self.dialog.disable_control("discard")
self.recording = None
output.speak(_(u"Discarded"))
def on_play(self, *args, **kwargs):
if not self.playing:
call_threaded(self._play)
else:
self._stop()
def _play(self):
output.speak(_(u"Playing..."))
# try:
self.playing = sound_lib.stream.FileStream(file=str(self.file), flags=sound_lib.stream.BASS_UNICODE)
self.playing.play()
self.dialog.set("play", _(u"&Stop"))
try:
while self.playing.is_playing:
pass
self.dialog.set("play", _(u"&Play"))
self.playing.free()
self.playing = None
except:
pass
def _stop(self):
output.speak(_(u"Stopped"))
self.playing.stop()
self.playing.free()
self.dialog.set("play", _(u"&Play"))
self.playing = None
def postprocess(self):
if self.file.lower().endswith('.wav'):
output.speak(_(u"Recoding audio..."))
sound.recode_audio(self.file)
self.wav_file = self.file
self.file = '%s.ogg' % self.file[:-4]
def cleanup(self):
if self.playing and self.playing.is_playing:
self.playing.stop()
if self.recording != None:
if self.recording.is_playing:
self.recording.stop()
try:
self.recording.free()
except:
pass
os.remove(self.file)
if hasattr(self, 'wav_file'):
os.remove(self.wav_file)
def on_attach_exists(self, *args, **kwargs):
self.file = self.dialog.get_file()
if self.file != False:
self.file_attached()

View File

@@ -1,88 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from builtins import object
from past.utils import old_div
import sys
import threading
import time
import logging
from .utils import convert_bytes
from pubsub import pub
log = logging.getLogger("extra.AudioUploader.transfer")
from requests_toolbelt.multipart.encoder import MultipartEncoder, MultipartEncoderMonitor
import requests
import os
class Upload(object):
def __init__(self, field=None, obj=None, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs):
super(Upload, self).__init__(*args, **kwargs)
self.url=url
self.filename=filename
log.debug("Uploading audio to %s, filename %s" % (url, filename))
self.start_time = None
self.completed_callback = completed_callback
self.background_thread = None
self.transfer_rate = 0
self.local_filename=os.path.basename(self.filename)
if isinstance(self.local_filename, str):
self.local_filename=self.local_filename.encode(sys.getfilesystemencoding())
self.fin=open(self.filename, 'rb')
self.m = MultipartEncoder(fields={field:(self.local_filename, self.fin, "application/octet-stream")})
self.monitor = MultipartEncoderMonitor(self.m, self.progress_callback)
self.response=None
self.obj=obj
self.follow_location=follow_location
#the verbose parameter is deprecated and will be removed soon
def elapsed_time(self):
if not self.start_time:
return 0
return time.time() - self.start_time
def progress_callback(self, monitor):
progress = {}
progress["total"] = monitor.len
progress["current"] = monitor.bytes_read
if progress["current"] == 0:
progress["percent"] = 0
self.transfer_rate = 0
else:
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
self.transfer_rate = old_div(progress["current"], self.elapsed_time())
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
if self.transfer_rate:
progress["eta"] = old_div((progress["total"] - progress["current"]), self.transfer_rate)
else:
progress["eta"] = 0
pub.sendMessage("uploading", data=progress)
def perform_transfer(self):
log.debug("starting upload...")
self.start_time = time.time()
self.response=requests.post(url=self.url, data=self.monitor, headers={"Content-Type":self.m.content_type}, allow_redirects=self.follow_location, stream=True)
log.debug("Upload finished.")
self.complete_transfer()
def perform_threaded(self, *args, **kwargs):
self.background_thread = threading.Thread(target=self.perform_transfer)
self.background_thread.daemon = True
self.background_thread.start()
def complete_transfer(self):
if callable(self.completed_callback):
self.completed_callback(self.obj)
if hasattr(self,'fin') and callable(self.fin.close):
self.fin.close()
def get_url(self):
try:
data = self.response.json()
except:
return _("Error in file upload: {0}").format(self.data.content,)
if "url" in data and data["url"] != "0":
return data["url"]
elif "error" in data and data["error"] != "0":
return data["error"]
else:
return _("Error in file upload: {0}").format(self.data.content,)

View File

@@ -1,44 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from builtins import str
def convert_bytes(n):
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
if n >= P:
return '%.2fPb' % (float(n) / T)
elif n >= T:
return '%.2fTb' % (float(n) / T)
elif n >= G:
return '%.2fGb' % (float(n) / G)
elif n >= M:
return '%.2fMb' % (float(n) / M)
elif n >= K:
return '%.2fKb' % (float(n) / K)
else:
return '%d' % n
def seconds_to_string(seconds, precision=0):
day = seconds // 86400
hour = seconds // 3600
min = (seconds // 60) % 60
sec = seconds - (hour * 3600) - (min * 60)
sec_spec = "." + str(precision) + "f"
sec_string = sec.__format__(sec_spec)
string = ""
if day == 1:
string += _(u"%d day, ") % day
elif day >= 2:
string += _(u"%d days, ") % day
if (hour == 1):
string += _(u"%d hour, ") % hour
elif (hour >= 2):
string += _("%d hours, ") % hour
if (min == 1):
string += _(u"%d minute, ") % min
elif (min >= 2):
string += _(u"%d minutes, ") % min
if sec >= 0 and sec <= 2:
string += _(u"%s second") % sec_string
else:
string += _(u"%s seconds") % sec_string
return string

View File

@@ -1,63 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
import wx
from .utils import *
import widgetUtils
class UploadDialog(widgetUtils.BaseDialog):
def __init__(self, filename, *args, **kwargs):
super(UploadDialog, self).__init__(parent=None, id=wx.ID_ANY, *args, **kwargs)
self.pane = wx.Panel(self)
self.progress_bar = wx.Gauge(parent=self.pane)
fileBox = wx.BoxSizer(wx.HORIZONTAL)
fileLabel = wx.StaticText(self.pane, -1, _(u"File"))
self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100))
self.file.SetFocus()
fileBox.Add(fileLabel)
fileBox.Add(self.file)
currentAmountBox = wx.BoxSizer(wx.HORIZONTAL)
current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred"))
self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
currentAmountBox.Add(current_amount_label)
currentAmountBox.Add(self.current_amount)
totalSizeBox = wx.BoxSizer(wx.HORIZONTAL)
total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size"))
self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
totalSizeBox.Add(total_size_label)
totalSizeBox.Add(self.total_size)
speedBox = wx.BoxSizer(wx.HORIZONTAL)
speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate"))
self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s")
speedBox.Add(speedLabel)
speedBox.Add(self.speed)
etaBox = wx.BoxSizer(wx.HORIZONTAL)
etaLabel = wx.StaticText(self.pane, -1, _(u"Time left"))
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
etaBox.Add(etaLabel)
etaBox.Add(self.eta)
self.create_buttons()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(fileBox)
sizer.Add(currentAmountBox)
sizer.Add(totalSizeBox)
sizer.Add(speedBox)
sizer.Add(etaBox)
sizer.Add(self.progress_bar)
self.pane.SetSizerAndFit(sizer)
def update(self, data):
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
wx.CallAfter(self.speed.SetValue, data["speed"])
if data["eta"]:
wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"]))
def create_buttons(self):
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
def get_response(self, fn):
wx.CallAfter(fn, 0.01)
self.ShowModal()

Some files were not shown because too many files have changed in this diff Show More