Compare commits

..

108 Commits

Author SHA1 Message Date
89985cf6bd Updated snapshot info 2019-02-19 22:24:25 -06:00
cc4568b968 Win10: Remapped session mute/umute to Control+win+alt+m 2019-02-19 17:50:05 -06:00
d7132ecaf6 Added 'open in Twitter' for tweets and users 2019-02-19 17:41:34 -06:00
720e0e6c24 Win10: Remapped shortcut for settings dialog to Ctrl+Win+Alt+o 2019-02-19 16:48:54 -06:00
9763d8b26c Fixed an error when trying to retrieve a deleted user while rendering a direct message 2019-02-15 09:04:52 -06:00
f512267b6d Add accessible_output2 to requirements file. Closes #280 2019-01-28 13:28:30 -06:00
241de0264d Use accessible_output2 as external dependency 2019-01-23 17:36:46 -06:00
5c7bce1258 Added sound_lib as an external dependency. #273 2019-01-23 17:28:05 -06:00
e24543be12 Use platform_utils as an external dependency 2019-01-15 17:54:29 -06:00
422c780d0c Merge pull request #279 from codeofdusk/i278
Fix #278
2019-01-15 17:29:28 -06:00
Bill Dengler
96a592a4f9 Fixed #278. 2018-12-31 21:14:09 +00:00
d4bf33ca6d Added support for playback from anyaudio.net 2018-11-25 13:00:01 -06:00
e5b33160e0 Added a custom fix for Libloader with changes made by @jmdaweb #273 2018-11-22 17:48:22 -06:00
c8d83ed9e7 Added new path to libloader in requirements file #273 2018-11-22 17:47:38 -06:00
16b2e16614 Removed libloader from the source repository. #273 2018-11-22 17:47:02 -06:00
221d1d413b Changed codebase's syntax before attempt the python3 migration later. #273 2018-11-22 13:35:51 -06:00
4391e3d3de Install Twython from a git repo instead of shipping it in the source code. #273 2018-11-22 12:19:23 -06:00
c5e9e97c84 Keep custom buffer ordering across restarts and changes in settings 2018-11-18 06:33:33 -06:00
4c1cad7f61 Updated snapshot information 2018-11-18 05:22:20 -06:00
fffd98e09e Pressing enter in the list will work when adding or removing someone from a list 2018-11-17 13:40:59 -06:00
a1a084bfda Fixed a small typo 2018-11-15 04:49:48 -06:00
85e575386e Long and quoted tweets are displayed properly in sent tweets. Fixes #253 2018-11-15 04:43:16 -06:00
2c64805eec Show list manager action in keystroke editor. Fixes #260 2018-11-13 17:32:12 -06:00
6f2e439ddc Invert slider movement when up/down arrows are pressed. Fixes #261 2018-11-13 17:16:51 -06:00
7e42a096a5 Finished implementing autoreading features. Closes #221 2018-11-13 16:33:17 -06:00
9d2cf05a41 Added more docstrings to buffers code 2018-11-13 15:34:45 -06:00
d7c095173d Improved autoreading functions for #221. Closes #271 2018-11-01 15:04:26 -06:00
36ba6eca92 Started refactoring of autoreading for buffers 2018-10-19 17:35:56 -05:00
b6fa131999 Fixed typos in changelog 2018-10-03 09:03:15 -05:00
c85c478595 Added date and time wen displaying tweets and dm's 2018-09-30 14:39:59 -05:00
Jose Manuel Delicado
74e020c090 Added certifi and backports.functools-lru-cache to requirements.txt 2018-09-30 11:56:47 +02:00
40105f37ed Added support for deleting dm's with the new Twitter API methods 2018-09-29 11:13:11 -05:00
ca3f8779b8 Separated twitter buffers from generic buffers code 2018-09-22 21:55:19 -05:00
a6a651d6f7 Install py2exe_py2 in appveyor script only. Removed it from requirements. Closes #266 2018-09-22 13:04:27 -05:00
7748b4bb5d Added attempt to update setuptools in AppVeyor script 2018-09-22 13:03:21 -05:00
01a6c65c82 Updated to Twython 3.7.0 2018-09-22 12:03:59 -05:00
211d43aa30 Updated changelog 2018-09-22 10:21:50 -05:00
c716f4aa96 When quoting a retweet, quote will be in original tweet 2018-09-22 10:18:25 -05:00
9fd9d2a120 Updated snapshot info in repo 2018-09-19 16:14:43 -05:00
5c75be20d3 Updated snapshot information 2018-09-19 15:41:05 -05:00
1ccb898f78 Updated changelog 2018-09-19 12:57:14 -05:00
d734a30a18 Merge pull request #265 from manuelcortez/appveyor
Generate snapshot versions with AppVeyor. Closes #264
2018-09-19 12:42:39 -05:00
3d310c0ee4 ADded other missing module from oauthlib (really?) 2018-09-19 12:00:29 -05:00
79512af350 Try to add a missing module by hand in setup 2018-09-19 11:47:13 -05:00
7a2ad3797d Added py2exe_py2 from PyPi 2018-09-19 11:12:51 -05:00
f87312fc53 Disable building in all commits 2018-09-19 00:09:52 -05:00
682f1e8bd4 Skipped folder option 2018-09-18 23:55:00 -05:00
cbc2e978c9 Fixed remote ftp path again 2018-09-18 23:29:01 -05:00
72fd93eaf6 Added appveyor badge 2018-09-18 23:26:15 -05:00
8bac4b8ec6 Commented and fixed everything. Ready for pr 2018-09-18 21:29:42 -05:00
6c29a4a18f Use absolute path to ftp destination 2018-09-18 17:48:18 -05:00
3ae581a19f Let's try with beta ftp lib 2018-09-18 17:39:09 -05:00
a9378988cb Second test with faked path 2018-09-18 17:28:33 -05:00
00f96bb7af Real deployment with fake path to ftp 2018-09-18 17:23:44 -05:00
ecd2984a61 Last test before real deployment 2018-09-18 17:14:47 -05:00
3be69cb6a2 Tried moving artifacts 2018-09-18 17:11:06 -05:00
099bc49761 Test ftp deployment 2018-09-18 16:50:10 -05:00
ec61f4b431 Cleaning up 2018-09-18 13:56:17 -05:00
b16a6c5ddb Add absolute path to artifacts 2018-09-18 13:27:48 -05:00
9a94b92018 Specify artifact name to appveyor 2018-09-18 13:13:20 -05:00
fe8a3b1565 Try to create an artifact after packaged 2018-09-18 13:07:54 -05:00
a3d1052193 More path issues 2018-09-18 12:43:51 -05:00
fbbd3cf00e Call documentation importer before attempt to build doc 2018-09-18 12:39:42 -05:00
8a6f73fdc2 Fixed a typo 2018-09-18 12:33:34 -05:00
c806f17484 Calls doc generator just before packaging 2018-09-18 12:31:28 -05:00
db231e07f8 Documentation should be generated in a folder called documentation 2018-09-18 12:31:02 -05:00
eaf13a4453 Changes in paths 2018-09-18 11:46:51 -05:00
d4a73fb3bb Add direct url to Py2exe 2018-09-18 11:41:36 -05:00
8459135002 Fixed other problem 2018-09-18 11:35:31 -05:00
4aef2595b2 Fixed syntax error 2018-09-18 11:32:25 -05:00
ccdcc7676e Adds py2exe and pyenchant to the party 2018-09-18 11:31:18 -05:00
967cc8da71 Fix 2018-09-18 11:18:10 -05:00
071bcf55ef Try to clone include submodules 2018-09-18 11:10:20 -05:00
cf735211c9 Initial file 2018-09-18 10:52:54 -05:00
bd56eb953b provide better control over cursored buffers when getting more items 2018-09-18 09:09:25 -05:00
940ace664c Fixed error when adding tweet to likes from the menu bar 2018-09-02 08:59:22 -05:00
Jose Manuel Delicado
8619deb6ee Updated readme to remove pywin32 as an installable module 2018-08-31 14:02:18 +02:00
Jose Manuel Delicado
9fcc5d36e5 Updated requirements.txt 2018-08-31 14:00:16 +02:00
Jose Manuel Delicado
c374cdefe9 Updated windows dependencies to remove pywin32 2018-08-31 13:59:42 +02:00
7b925b5909 Fixed a race contidion in the main twitter session 2018-08-26 08:09:55 -05:00
013a80a1f2 Show quoted tweets properly after being sent 2018-08-26 08:07:00 -05:00
ec860aa6fc Updated changelog 2018-08-26 02:59:12 -05:00
12e06eb52d Added option for visiting soundpacks section in website, from the help menu #247 2018-08-26 02:48:04 -05:00
464fce07b0 Added FreakyBlue as an option for next snapshot. #247 2018-08-26 02:28:30 -05:00
ad484a1f35 Display extended tweets properly after being sent. #253 2018-08-22 17:11:15 -05:00
7296d2a939 Extended tweets should be displayed properly in lists 2018-08-22 13:06:08 -05:00
cd596a6ad6 Merge pull request #251 from Menelion/geocode-i18n
Localize geocoding to the current language
2018-08-19 22:18:18 -05:00
Andre Polykanine
ed3b9c9538 Localize geocoding to the current language 2018-08-20 01:15:31 +03:00
df9d99edcd Merge pull request #250 from Mohamed00/next-gen
Convert audio.ogg in classic soundpack to mono #247
2018-08-19 07:01:34 -05:00
Mohamed Al-Hajamy
f5f4074409 Add files via upload 2018-08-19 07:53:04 -04:00
6439eac76c Win10: Remapped show location in tweet to ctrl+win+g as suggested in #177 2018-08-18 23:17:41 -05:00
5c794aaeff Added contents of client.py into twitter session 2018-08-18 23:09:08 -05:00
8cdd1b52d1 Added docstrings to long_tweets package 2018-08-18 20:49:40 -05:00
6928ac4b99 Sent dm's: Buffer name matches string in translation files. 2018-08-18 06:07:07 -05:00
ae42a53b41 Finixhed docstrings in Twitter main session 2018-08-17 21:01:58 -05:00
be7df1714d Added docstrings for some of the functions in twitter session 2018-08-17 17:42:41 -05:00
8cf7ca5ef2 Store created sessions in the sessions module 2018-08-17 12:16:51 -05:00
0cfa89b389 Moved Twitter's session from sessionmanager to sessions.twitter 2018-08-17 05:12:49 -05:00
f63ed6d0a7 Removed hold code related to application.streaming_lives(). Fixes #248 2018-08-17 02:24:02 -05:00
00440e9050 Removed long_tweets module from the src directory 2018-08-16 17:38:35 -05:00
76db14360a Removed hold modules from code. Now everything is in sessions 2018-08-16 17:27:02 -05:00
0966739296 Modified refs to hold Twitter modules to use the new sessions package 2018-08-16 17:26:19 -05:00
1e27056902 Added Twitter module and long_tweets as a session so we will have everything related to twitter in the same session 2018-08-16 17:25:16 -05:00
3b41e18573 Added experimental module with a base session 2018-08-16 16:57:37 -05:00
2668e47a6f tweets, replies and retweets will be added to sent tweets after being sent 2018-08-16 11:17:15 -05:00
b44e30ed28 Removed old compose_dm and compose_event 2018-08-16 10:44:27 -05:00
bfad5b82f0 Deleted code related to Streaming Features 2018-08-16 10:42:14 -05:00
41d4c97067 Fixed error when sent dm's are hidden. Fixes #246 2018-08-16 09:11:57 -05:00
243 changed files with 1433 additions and 8369 deletions

View File

@@ -1,6 +1,8 @@
TWBlue -
======
[![Build status](https://ci.appveyor.com/api/projects/status/fml5fu7h1fj8vf6l?svg=true)](https://ci.appveyor.com/project/manuelcortez/twblue)
TW Blue is an app designed to use Twitter simply and efficiently while using minimal system resources.
With this app youll have access to twitter features such as:
@@ -31,7 +33,6 @@ Although most dependencies can be found in the windows-dependencies directory, w
* [Python,](http://python.org) version 2.7.15
If you want to build both x86 and x64 binaries, you can install python x86 to C:\python27 and python x64 to C:\python27x64, for example.
* [Python windows extensions (pywin32)](http://www.sourceforge.net/projects/pywin32/) for python 2.7, build 223
* [PyEnchant,](http://pythonhosted.org/pyenchant/) version 1.6.6.
x64 version has been built by TWBlue developers, so you only will find it in windows-dependencies folder

83
appveyor.yml Normal file
View File

@@ -0,0 +1,83 @@
pull_requests:
# Avoid building after pull requests. Shall we disable this option?
do_not_increment_build_number: true
# Only build whenever we add tags to the repo.
skip_non_tags: true
environment:
matrix:
# List of python versions we want to work with.
- PYTHON: "C:\\Python27"
PYTHON_VERSION: "2.7.x" # currently 2.7.9
PYTHON_ARCH: "32"
# perhaps we may enable this one in future?
# - PYTHON: "C:\\Python27-x64"
# PYTHON_VERSION: "2.7.x" # currently 2.7.9
# PYTHON_ARCH: "64"
# This is important so we will retrieve everything in submodules as opposed to default method.
clone_script:
- cmd: >-
git clone -q --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER%
&& cd %APPVEYOR_BUILD_FOLDER%
&& git checkout -qf %APPVEYOR_REPO_COMMIT%
&& git submodule update --init --recursive
install:
# If there is a newer build queued for the same PR, cancel this one.
# The AppVeyor 'rollout builds' option is supposed to serve the same
# purpose but it is problematic because it tends to cancel builds pushed
# directly to master instead of just PR builds (or the converse).
# credits: JuliaLang developers.
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
throw "There are newer queued builds for this pull request, failing early." }
- ECHO "Filesystem root:"
- ps: "ls \"C:/\""
# Check that we have the expected version and architecture for Python
- "python --version"
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
# Upgrade to the latest version of pip to avoid it displaying warnings
# about it being out of date.
- "python -m pip install --upgrade pip setuptools"
# Install the build dependencies of the project. If some dependencies contain
# compiled extensions and are not provided as pre-built wheel packages,
# pip will build them from source using the MSVC compiler matching the
# target Python version and architecture
- "%CMD_IN_ENV% pip install -r requirements.txt"
- "%CMD_IN_ENV% pip install pyenchant py2exe_py2"
build_script:
# Build documentation at first, so setup.py won't fail when copying everything.
- "cd doc"
# Import documentation before building, so strings.py will be created.
- "%CMD_IN_ENV% python documentation_importer.py"
# build doc from src folder so it will generate result files right there.
- "cd ..\\src"
- "%CMD_IN_ENV% python ..\\doc\\generator.py"
# Build distributable files.
- "%CMD_IN_ENV% python setup.py py2exe"
- "cd dist"
# Zip it all.
- cmd: 7z a ..\..\snapshot.zip *
artifacts:
- path: snapshot.zip
deploy:
- provider: FTP
host: twblue.es
protocol: ftp
beta: true
username: twblue
password:
secure: ml/xB8YEoZ7DmjzDr+KSNw==
# folder: '//pubs'

View File

@@ -2,20 +2,48 @@
## changes in this version
* TWBlue can open a Tweet or user directly in Twitter. There is a new option in the context menu for people and tweet buffers, and also, the shortcut control+win+alt+Enter will open the focused item in Twitter.
* Some keystrokes were remapped in the Windows 10 Keymap:
* Read location of a tweet: Ctrl+Win+G. ([#177](https://github.com/manuelcortez/TWBlue/pull/177))
* Open global settings dialogue: Ctrl+Win+Alt+O.
* Mute/unmute current session: Control + Windows + Alt + M.
* Fixed an error that was preventing TWBlue to load the direct messages buffer if an user who sent a message has been deleted.
* Added support for playing audios posted in [AnyAudio.net](http://anyaudio.net) directly from TWBlue. Thanks to [Sam Tupy](http://www.samtupy.com/)
* Custom buffer ordering will not be reset every time the application restarts after an account setting has been modified.
* When adding or removing an user from a list, it is possible to press enter in the focused list instead of having to search for the "add" or "delete" button.
* Quoted and long tweets are displayed properly in the sent tweets buffer after being send. ([#253](https://github.com/manuelcortez/TWBlue/issues/253))
* Fixed an issue that was making the list manager keystroke unable to be shown in the keystroke editor. Now the keystroke is listed properly. ([#260](https://github.com/manuelcortez/TWBlue/issues/260))
* The volume slider, located in the account settings of TWBlue, now should decrease and increase value properly when up and down arrows are pressed. Before it was doing it in inverted order. ([#261](https://github.com/manuelcortez/TWBlue/issues/261))
* autoreading has been redesigned to work in a similar way for almost all buffers. Needs testing. ([#221](https://github.com/manuelcortez/TWBlue/issues/221))
* When displaying tweets or direct messages, a new field has been added to show the date when the item has been posted to Twitter.
* Added support for deleting direct messages by using the new Twitter API methods.
* When quoting a retweet, the quote will be made to the original tweet instead of the retweet.
* If the sent direct messages buffer is hidden, TWBlue should keep loading everything as expected. ([#246](https://github.com/manuelcortez/TWBlue/issues/246))
* There is a new soundpack, called FreakyBlue (Thanks to [Andre Louis](https://twitter.com/FreakyFwoof)) as a new option in TWBlue. This pack can be the default in the next stable, so users can take a look and share their opinion in snapshot versions. ([#247](https://github.com/manuelcortez/TWBlue/issues/247))
* There is a new option in the help menu that allows you to visit the soundpacks section in the TWBlue website. ([#247](https://github.com/manuelcortez/TWBlue/issues/247))
* When reading location of a geotagged tweet, it will be translated for users of other languages. ([#251](https://github.com/manuelcortez/TWBlue/pull/251))
* When there are no more items to retrieve in direct messages and people buffers, a message will announce it.
* Fixed an issue reported by some users that was making them unable to load more items in their direct messages.
* It is possible to add a tweet to the likes buffer from the menu bar again.
* Tweets, replies and retweets will be added to sent tweets right after being posted in Twitter.
* Extended Tweets should be displayed properly in list buffers.
## Changes in version 0.94
* Added an option in the global settings dialog to disable the Streaming features of TWBlue. TWBlue will remove all Streaming features after August 16, so this option will give people an idea about how it will be. ([#219](https://github.com/manuelcortez/TWBlue/issues/219))
* Due to Twitter API changes, Switched authorisation method to Pin-code based authorisation. When you add new accounts to TWBlue, you will be required to paste a code displayed in the Twitter website in order to grant access to TWBlue. ([#216](https://github.com/manuelcortez/TWBlue/issues/216))
* In order to comply with latest Twitter changes, TWBlue has switched to the new method used to send and receive direct messages, according to issue [#215.](https://github.com/manuelcortez/twblue/issues/215)
* The new method does not allow direct messages to be processed in real time. Direct messages will be updated periodically.
* After august 16 or when streaming is disabled, the events buffer will no longer be created in TWBlue.
* You can configure frequency for buffer updates in TWBlue. By default, TWBlue will update all buffers every 2 minutes, but you can change this setting in the global settings dialog. ([#223](https://github.com/manuelcortez/TWBlue/issues/223))
* Added a new tab called feedback, in the account settings dialog. This tab allows you to control wether automatic speech or Braille feedbak in certain events (mentions and direct messages received) is enabled. Take into account that this option will take preference over automatic reading of buffers and any kind of automatic output. ([#203](https://github.com/manuelcortez/TWBlue/issues/203))
* Added a new tab called feedback, in the account settings dialog. This tab allows you to control whether automatic speech or Braille feedbak in certain events (mentions and direct messages received) is enabled. Take into account that this option will take preference over automatic reading of buffers and any kind of automatic output. ([#203](https://github.com/manuelcortez/TWBlue/issues/203))
* The spell checking dialog now has access keys defined for the most important actions. ([#211](https://github.com/manuelcortez/TWBlue/issues/211))
* TWBlue now Uses WXPython 4.0.1. This will allow us to migrate all important components to Python 3 in the future. ([#207](https://github.com/manuelcortez/TWBlue/issues/207))
* When you quote a Tweet, if the original tweet was posted with Twishort, TWBlue should display properly the quoted tweet. Before it was displaying the original tweet only. ([#206](https://github.com/manuelcortez/TWBlue/issues/206))
* It is possible to filter by retweets, quotes and replies when creating a new filter.
* Added support for playing youtube Links directly from the client. ([#94](https://github.com/manuelcortez/TWBlue/issues/94))
* Replaced Bass with libVLC for playing URL streams.
* the checkbox for indicating wether TWBlue will include everyone in a reply or not, will be unchecked by default.
* the checkbox for indicating whether TWBlue will include everyone in a reply or not, will be unchecked by default.
* You can request TWBlue to save the state for two checkboxes: Long tweet and mention all, from the global settings dialogue.
* For windows 10 users, some keystrokes in the invisible user interface have been changed or merged:
* control+Windows+alt+F will be used for toggling between adding and removing a tweet to user's likes. This function will execute the needed action based in the current status for the focused tweet.

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import markdown
import os
import shutil
from codecs import open as _open
import languageHandler
languageHandler.setLanguage("en")
@@ -38,14 +39,18 @@ def generate_document(language, document_type="documentation"):
""" % (language, title, title)
first_html_block = first_html_block+ markdown_file
first_html_block = first_html_block + "\n</body>\n</html>"
if not os.path.exists(language):
os.mkdir(language)
mdfile = _open("%s/%s" % (language, filename), "w", encoding="utf-8")
if not os.path.exists(os.path.join("documentation", language)):
os.mkdir(os.path.join("documentation", language))
mdfile = _open(os.path.join("documentation", language, filename), "w", encoding="utf-8")
mdfile.write(first_html_block)
mdfile.close()
def create_documentation():
print("Creating documentation in the supported languages...\n")
if not os.path.exists("documentation"):
os.mkdir("documentation")
if os.path.exists(os.path.join("documentation", "license.txt")) == False:
shutil.copy(os.path.join("..", "license.txt"), os.path.join("documentation", "license.txt"))
for i in languages:
print("Creating documentation for: %s" % (i,))
generate_document(i)

View File

@@ -20,4 +20,12 @@ idna
chardet
urllib3
youtube-dl
python-vlc
python-vlc
pypiwin32
certifi
backports.functools_lru_cache
git+https://github.com/manuelcortez/twython
git+https://github.com/manuelcortez/libloader
git+https://github.com/manuelcortez/platform_utils
git+https://github.com/manuelcortez/accessible_output2
git+https://github.com/chrisnorman7/sound_lib

View File

@@ -1,33 +0,0 @@
from __future__ import absolute_import
import ctypes
import os
import types
from platform_utils import paths
def load_library(libname, cdll=False):
if paths.is_frozen():
libfile = os.path.join(paths.embedded_data_path(), 'accessible_output2', 'lib', libname)
else:
libfile = os.path.join(paths.module_path(), 'lib', libname)
if cdll:
return ctypes.cdll[libfile]
else:
return ctypes.windll[libfile]
def get_output_classes():
from . import outputs
module_type = types.ModuleType
classes = [m.output_class for m in outputs.__dict__.values() if type(m) == module_type and hasattr(m, 'output_class')]
return sorted(classes, key=lambda c: c.priority)
def find_datafiles():
import os
import platform
from glob import glob
import accessible_output2
if platform.system() != 'Windows':
return []
path = os.path.join(accessible_output2.__path__[0], 'lib', '*.dll')
results = glob(path)
dest_dir = os.path.join('accessible_output2', 'lib')
return [(dest_dir, results)]

View File

@@ -1,20 +0,0 @@
from __future__ import absolute_import
import platform
if platform.system() == 'Windows':
from . import nvda
from . import jaws
from . import sapi5
from . import window_eyes
from . import system_access
from . import dolphin
from . import pc_talker
#import sapi4
if platform.system() == 'Darwin':
from . import voiceover
from . import say
if platform.system() == 'Linux':
from . import e_speak
from . import auto

View File

@@ -1,42 +0,0 @@
from __future__ import absolute_import
import accessible_output2
from .base import Output, OutputError
class Auto(Output):
def __init__(self):
output_classes = accessible_output2.get_output_classes()
self.outputs = []
for output in output_classes:
try:
a=output()
self.outputs.append(a)
except:
pass
def get_first_available_output(self):
for output in self.outputs:
if output.is_active():
return output
return None
def speak(self, *args, **kwargs):
output = self.get_first_available_output()
if output:
output.speak(*args, **kwargs)
def braille(self, *args, **kwargs):
output = self.get_first_available_output()
if output:
output.braille(*args, **kwargs)
def output(self, *args, **kwargs):
output = self.get_first_available_output()
if output:
output.speak(*args, **kwargs)
output.braille(*args, **kwargs)
def is_system_output(self):
output = self.get_first_available_output()
if output:
return output.is_system_output()

View File

@@ -1,47 +0,0 @@
from accessible_output2 import load_library
import platform
class OutputError(Exception):
pass
class Output(object):
name = "Unnamed Output"
lib32 = None
lib64 = None
argtypes = {}
cdll = False
priority = 100
system_output = False
def __init__(self):
self.is_32bit = platform.architecture()[0] == "32bit"
if self.lib32 and self.is_32bit:
self.lib = load_library(self.lib32, cdll=self.cdll)
elif self.lib64:
self.lib = load_library(self.lib64, cdll=self.cdll)
else:
self.lib = None
if self.lib is not None:
for func in self.argtypes:
try:
getattr(self.lib, func).argtypes = self.argtypes[func]
except AttributeError:
pass
def output(self, text, **options):
output = False
if self.speak(text, **options):
output = True
if self.braille(text, **options):
output = True
if not output:
raise RuntimeError("Output %r does not have any method defined to output" % self)
def is_system_output(self):
return self.system_output
def speak(self, **optiont):
return False
def braille(self, *args, **options):
return False

View File

@@ -1,33 +0,0 @@
from __future__ import absolute_import
import os
import ctypes
from .base import Output
class Dolphin (Output):
"""Supports dolphin products."""
name = 'Dolphin'
lib32 = 'dolapi.dll'
argtypes = {
'DolAccess_Command': (ctypes.c_wchar_p, ctypes.c_int, ctypes.c_int),
'DolAccess_Action': (ctypes.c_int,),
}
def speak(self, text, interrupt=0):
if interrupt:
self.silence()
#If we don't call this, the API won't let us speak.
if self.is_active():
self.lib.DolAccess_Command(text, (len(text)*2)+2, 1)
def silence(self):
self.lib.DolAccess_Action(141)
def is_active(self):
try:
return self.lib.DolAccess_GetSystem() in (1, 4, 8)
except:
return False
output_class = Dolphin

View File

@@ -1,31 +0,0 @@
from __future__ import absolute_import
from .base import Output
try:
import espeak.core
except:
raise RuntimeError("Cannot find espeak.core. Please install python-espeak")
class ESpeak(Output):
"""Speech output supporting ESpeak on Linux
Note this requires python-espeak to be installed
This can be done on Debian distros by using apt-get install python-espeak
Or through this tarball: https://launchpad.net/python-espeak
"""
name = "Linux ESpeak"
def is_active(self):
try:
import espeak.core
except:
return False
return True
def speak(self, text, interrupt = 0):
if interrupt:
self.silence()
espeak.core.synth(text)
def silence(self):
espeak.core.cancel()
output_class = ESpeak

View File

@@ -1,34 +0,0 @@
from __future__ import absolute_import
import win32gui
from libloader.com import load_com
import pywintypes
from .base import Output, OutputError
class Jaws (Output):
"""Output supporting the Jaws for Windows screen reader."""
name = 'jaws'
def __init__(self, *args, **kwargs):
super (Jaws, self).__init__(*args, **kwargs)
try:
self.object = load_com("FreedomSci.JawsApi", "jfwapi")
except pywintypes.com_error:
raise OutputError
def braille(self, text, **options):
# HACK: replace " with ', Jaws doesn't seem to understand escaping them with \
text = text.replace('"', "'")
self.object.RunFunction("BrailleString(\"%s\")" % text)
def speak(self, text, interrupt=False):
self.object.SayString(' %s' % text, interrupt)
def is_active(self):
try:
return self.object.SayString('',0) == True or win32gui.FindWindow("JFWUI2", "JAWS") != 0
except:
return False
output_class = Jaws

View File

@@ -1,37 +0,0 @@
from __future__ import absolute_import
import os
import platform
import ctypes
from platform_utils import paths
from libloader import load_library
from .base import Output
class NVDA(Output):
"""Supports The NVDA screen reader"""
name = "NVDA"
lib32 = 'nvdaControllerClient32.dll'
lib64 = 'nvdaControllerClient64.dll'
argtypes = {
'nvdaController_brailleMessage': (ctypes.c_wchar_p,),
'nvdaController_speakText': (ctypes.c_wchar_p,),
}
def is_active(self):
try:
return self.lib.nvdaController_testIfRunning() == 0
except:
return False
def braille(self, text, **options):
self.lib.nvdaController_brailleMessage(text)
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.lib.nvdaController_speakText(text)
def silence(self):
self.lib.nvdaController_cancelSpeech()
output_class = NVDA

View File

@@ -1,24 +0,0 @@
from __future__ import absolute_import
import ctypes
from .base import Output
class PCTalker(Output):
lib32 = 'pctkusr.dll'
lib64 = 'pctkusr64.dll'
cdll = True
argtypes = {
'PCTKPRead': (ctypes.c_char_p, ctypes.c_int, ctypes.c_int)
}
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.lib.PCTKPRead(text.encode('cp932', 'replace'), 0, 1)
def silence(self):
self.lib.PCTKVReset()
def is_active(self):
return self.lib.PCTKStatus() != 0
output_class = PCTalker

View File

@@ -1,143 +0,0 @@
from __future__ import absolute_import
from builtins import range
from libloader.com import load_com
from .base import Output
import logging
log = logging.getLogger(__name__)
class Sapi4(Output):
name = 'sapi4'
priority = 102
def __init__(self):
sapi4 = load_com("{EEE78591-FE22-11D0-8BEF-0060081841DE}")
self._voiceNo = sapi4.Find(0)
sapi4.Select(self._voiceNo)
sapi4.Speak(" ")
self.__object = sapi4
self._voice_list = self._available_voices()
def _set_capabilities(self):
sapi4 = self.__object
try:
sapi4.Pitch = sapi4.Pitch
self._min_pitch = sapi4.MinPitch
self._max_pitch = sapi4.MaxPitch
self._has_pitch = True
except:
self._min_pitch = 0
self._max_pitch = 0
self._has_pitch = False
try:
sapi4.Speed = sapi4.Speed
self._min_rate = sapi4.MinSpeed
self._max_rate = sapi4.MaxSpeed
self._has_rate = True
except:
self._min_rate = 0
self._max_rate = 0
self._has_rate = False
try:
sapi4.VolumeLeft = sapi4.VolumeLeft
self._min_volume = sapi4.MinVolumeLeft
self._max_volume = sapi4.MaxVolumeLeft
self._has_volume = True
except:
self._min_volume = 0
self._max_volume = 0
self._has_volume = False
def _available_voices(self):
voice_list = []
for voice_no in range(1, self.__object.CountEngines):
voice_list.append(self.__object.ModeName(voice_no))
return voice_list
@property
def available_voices(self):
return self._voice_list
def list_voices(self):
return self.available_voices
def get_voice(self):
return self.__object.ModeName(self._voice_no)
def set_voice(self, value):
self._voice_no = self.list_voices().index(value) + 1
self.__object.Select(self._voice_no)
self.silence()
self.__object.Speak(" ")
self._set_capabilities()
def get_pitch(self):
if self.has_pitch:
return self.__object.Pitch
def set_pitch(self, value):
if self.has_pitch:
self.__object.Pitch = value
def get_rate(self):
if self.has_rate:
return self.__object.Speed
def set_rate(self, value):
if self.has_rate:
self.__object.Speed = value
def get_volume(self):
if self.has_volume:
return self.__object.VolumeLeft
def set_volume(self, value):
if self.has_volume:
self.__object.VolumeLeft = value
@property
def has_pitch(self):
return self._has_pitch
@property
def has_rate(self):
return self._has_rate
@property
def has_volume(self):
return self._has_volume
@property
def min_pitch(self):
return self._min_pitch
@property
def max_pitch(self):
return self._max_pitch
@property
def min_rate(self):
return self._min_rate
@property
def max_rate(self):
return self._max_rate
@property
def min_volume(self):
return self._min_volume
@property
def max_volume(self):
return self._max_volume
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.__object.Speak(text)
def silence(self):
self.__object.AudioReset()
output_class = Sapi4

View File

@@ -1,95 +0,0 @@
from __future__ import absolute_import
from collections import OrderedDict
from libloader.com import load_com
from .base import Output, OutputError
import pywintypes
import logging
log = logging.getLogger(__name__)
SVSFDefault = 0
SVSFlagsAsync = 1
SVSFPurgeBeforeSpeak = 2
SVSFIsFilename = 4
SVSFIsXML = 8
SVSFIsNotXML = 16
SVSFPersistXML = 32
class SAPI5(Output):
has_volume = True
has_rate = True
has_pitch = True
min_pitch = -10
max_pitch = 10
min_rate = -10
max_rate = 10
min_volume = 0
max_volume = 100
name = "sapi5"
priority = 101
system_output = True
def __init__(self):
try:
self.object = load_com("SAPI.SPVoice")
self._voices = self._available_voices()
except pywintypes.com_error:
raise OutputError
self._pitch = 0
def _available_voices(self):
_voices = OrderedDict()
for v in self.object.GetVoices():
_voices[v.GetDescription()] = v
return _voices
def list_voices(self):
return list(self._voices.keys())
def get_voice(self):
return self.object.Voice.GetDescription()
def set_voice(self, value):
log.debug("Setting SAPI5 voice to \"%s\"" % value)
self.object.Voice = self._voices[value]
# For some reason SAPI5 does not reset audio after changing the voice
# By setting the audio device after changing voices seems to fix this
# This was noted from information at:
# http://lists.nvaccess.org/pipermail/nvda-dev/2011-November/022464.html
self.object.AudioOutput = self.object.AudioOutput
def get_pitch(self):
return self._pitch
def set_pitch(self, value):
log.debug("Setting pitch to %d" % value)
self._pitch = value
def get_rate(self):
return self.object.Rate
def set_rate(self, value):
log.debug("Setting rate to %d" % value)
self.object.Rate = value
def get_volume(self):
return self.object.Volume
def set_volume(self, value):
self.object.Volume = value
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
# We need to do the pitch in XML here
textOutput = "<pitch absmiddle=\"%d\">%s</pitch>" % (round(self._pitch), text.replace("<", "&lt;"))
self.object.Speak(textOutput, SVSFlagsAsync | SVSFIsXML)
def silence(self):
self.object.Speak("", SVSFlagsAsync | SVSFPurgeBeforeSpeak)
def is_active(self):
if self.object:
return True
return False
output_class = SAPI5

View File

@@ -1,21 +0,0 @@
from __future__ import absolute_import
import os
from .base import Output
class AppleSay(Output):
"""Speech output supporting the Apple Say subsystem."""
name = 'Apple Say'
def __init__(self, voice = 'Alex', rate = '300'):
self.voice = voice
self.rate = rate
super(AppleSay, self).__init__()
def is_active(self):
return not os.system('which say')
def speak(self, text, interrupt = 0):
if interrupt:
self.silence()
os.system('say -v %s -r %s "%s" &' % (self.voice, self.rate, text))
def silence(self):
os.system('killall say')
output_class = AppleSay

View File

@@ -1,29 +0,0 @@
from __future__ import absolute_import
import ctypes
from .base import Output
class SystemAccess (Output):
"""Supports System Access and System Access Mobile"""
name = "System Access"
lib32 = 'saapi32.dll'
argtypes = {
'SA_BrlShowTextW': (ctypes.c_wchar_p,),
'SA_SayW': (ctypes.c_wchar_p,),
}
priority = 99
def braille(self, text, **options):
self.lib.SA_BrlShowTextW(text)
def speak(self, text, interrupt=False):
if self.is_active():
self.dll.SA_SayW(str(text))
def is_active(self):
try:
return self.dll.SA_IsRunning()
except:
return False
output_class = SystemAccess

View File

@@ -1 +0,0 @@
from __future__ import absolute_import

View File

@@ -1,33 +0,0 @@
from __future__ import absolute_import
import win32gui
from libloader.com import load_com
from .base import Output, OutputError
import pywintypes
class WindowEyes (Output):
"""Speech output supporting the WindowEyes screen reader"""
name = 'Window-Eyes'
def __init__(self, *args, **kwargs):
super(WindowEyes, self).__init__(*args, **kwargs)
try:
self.object = load_com("gwspeak.speak")
except pywintypes.com_error:
raise OutputError
def speak(self, text, interrupt=0):
if interrupt:
self.silence()
self.object.SpeakString(text)
def silence (self):
self.object.Silence()
def is_active(self):
try:
return win32gui.FindWindow("GWMExternalControl", "External Control") != 0
except:
return False
output_class = WindowEyes

View File

@@ -2,13 +2,13 @@
import datetime
name = 'TWBlue'
snapshot = False
snapshot = True
if snapshot == False:
version = "0.94"
update_url = 'https://twblue.es/updates/stable.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
else:
version = "8"
version = "12"
update_url = 'https://twblue.es/updates/snapshot.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
authors = [u"Manuel Cortéz", u"José Manuel Delicado"]
@@ -18,18 +18,4 @@ description = unicode(name+" is an app designed to use Twitter simply and effici
translators = [u"Manuel Cortéz (English)", u"Mohammed Al Shara, Hatoun Felemban (Arabic)", u"Francisco Torres (Catalan)", u"Manuel cortéz (Spanish)", u"Sukil Etxenike Arizaleta (Basque)", u"Jani Kinnunen (finnish)", u"Rémy Ruiz (French)", u"Juan Buño (Galician)", u"Steffen Schultz (German)", u"Zvonimir Stanečić (Croatian)", u"Robert Osztolykan (Hungarian)", u"Christian Leo Mameli (Italian)", u"Riku (Japanese)", u"Paweł Masarczyk (Polish)", u"Odenilton Júnior Santos (Portuguese)", u"Florian Ionașcu, Nicușor Untilă (Romanian)", u"Natalia Hedlund, Valeria Kuznetsova (Russian)", u"Aleksandar Đurić (Serbian)", u"Burak Yüksek (Turkish)"]
url = u"https://twblue.es"
report_bugs_url = "https://github.com/manuelcortez/twblue/issues"
supported_languages = []
def streaming_lives():
""" Check if we are in August 16.
ToDo: This method should be removed after deadline==True"""
# Let's import config here so we will avoid breaking things when setup.py is going to be used.
# Check if user has disabled the streaming API things from settings.
import config
if config.app != None:
no_streaming = config.app["app-settings"]["no_streaming"]
if no_streaming == True:
return False
deadline = datetime.date(2018, 8, 16)
now = datetime.datetime.now().date()
return deadline>now
supported_languages = []

View File

@@ -33,5 +33,12 @@ def convert_soundcloud (url):
def convert_youtube_long (url):
return youtube_utils.get_video_url(url)
@matches_url ('http://anyaudio.net/listen')
def convert_anyaudio(url):
values = url.split("audio=")
if len(values) != 2:
raise TypeError('%r is not streamable' % url)
return "http://anyaudio.net/audiodownload?audio=%s" % (values[1],)
def convert_generic_audio(url):
return url

View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
""" this package contains logic related to buffers. A buffer is a virtual representation of a group of items retrieved through the Social network API'S.
Ideally, new social networks added to TWBlue will have its own "buffers", and these buffers should be defined within this package, following the Twitter example.
Currently, the package contains the following modules:
* baseBuffers: Define a set of functions and structure to be expected in all buffers. New buffers should inherit its classes from one of the classes present here.
* twitterBuffers: All other code, specific to Twitter.
"""

View File

@@ -0,0 +1,201 @@
# -*- coding: utf-8 -*-
""" Common logic to all buffers in TWBlue."""
import logging
import wx
import output
import config
import sound
import widgetUtils
from pubsub import pub
from wxUI import buffers
log = logging.getLogger("controller.buffers.baseBuffers")
def _items_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 buffer(object):
""" A basic buffer object. This should be the base class for all other derived buffers."""
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
"""Inits the main controller for this buffer:
@ parent wx.Treebook object: Container where we will put this buffer.
@ function str or None: function to be called periodically and update items on this buffer.
@ session sessionmanager.session object or None: Session handler for settings, database and data access.
"""
super(buffer, self).__init__()
self.function = function
# Compose_function will be used to render an object on this buffer. Normally, signature is as follows:
# compose_function(item, db, relative_times, show_screen_names=False, session=None)
# Read more about compose functions in twitter/compose.py.
self.compose_function = None
self.args = args
self.kwargs = kwargs
# This will be used as a reference to the wx.Panel object wich stores the buffer GUI.
self.buffer = None
# This should countains the account associated to this buffer.
self.account = ""
# This controls whether the start_stream function should be called when starting the program.
self.needs_init = True
# if this is set to False, the buffer will be ignored on the invisible interface.
self.invisible = False
# Control variable, used to track time of execution for calls to start_stream.
self.execution_time = 0
def clear_list(self):
pass
def get_event(self, ev):
""" Catch key presses in the WX interface and generate the corresponding event names."""
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
else:
event = None
ev.Skip()
if event != None:
try:
getattr(self, event)()
except AttributeError:
pass
def volume_down(self):
""" Decreases volume by 5%"""
if self.session.settings["sound"]["volume"] > 0.0:
if self.session.settings["sound"]["volume"] <= 0.05:
self.session.settings["sound"]["volume"] = 0.0
else:
self.session.settings["sound"]["volume"] -=0.05
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100.0))
self.session.sound.play("volume_changed.ogg")
self.session.settings.write()
def volume_up(self):
""" Increases volume by 5%."""
if self.session.settings["sound"]["volume"] < 1.0:
if self.session.settings["sound"]["volume"] >= 0.95:
self.session.settings["sound"]["volume"] = 1.0
else:
self.session.settings["sound"]["volume"] +=0.05
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100))
self.session.sound.play("volume_changed.ogg")
self.session.settings.write()
def start_stream(self, mandatory=False, play_sound=True):
pass
def get_more_items(self):
output.speak(_(u"This action is not supported for this buffer"), True)
def put_items_on_list(self, items):
pass
def remove_buffer(self):
return False
def remove_item(self, item):
f = self.buffer.list.get_selected()
self.buffer.list.remove_item(item)
self.buffer.list.select_item(f)
def bind_events(self):
pass
def get_object(self):
return self.buffer
def get_message(self):
pass
def set_list_position(self, reversed=False):
if reversed == False:
self.buffer.list.select_item(-1)
else:
self.buffer.list.select_item(0)
def reply(self):
pass
def send_message(self):
pass
def share_item(self):
pass
def destroy_status(self):
pass
def post_status(self, *args, **kwargs):
pass
def save_positions(self):
try:
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
except AttributeError:
pass
class accountPanel(buffer):
def __init__(self, parent, name, account, account_id):
super(accountPanel, self).__init__(parent, None, name)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.accountPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.session = None
self.needs_init = False
self.account = account
self.buffer.account = account
self.name = name
self.account_id = account_id
def setup_account(self):
widgetUtils.connect_event(self.buffer, widgetUtils.CHECKBOX, self.autostart, menuitem=self.buffer.autostart_account)
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(False)
else:
self.buffer.change_autostart(True)
if not hasattr(self, "logged"):
self.buffer.change_login(login=False)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.logout)
else:
self.buffer.change_login(login=True)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.login)
def login(self, *args, **kwargs):
del self.logged
self.setup_account()
pub.sendMessage("login", session_id=self.account_id)
def logout(self, *args, **kwargs):
self.logged = False
self.setup_account()
pub.sendMessage("logout", session_id=self.account_id)
def autostart(self, *args, **kwargs):
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(True)
config.app["sessions"]["ignored_sessions"].remove(self.account_id)
else:
self.buffer.change_autostart(False)
config.app["sessions"]["ignored_sessions"].append(self.account_id)
config.app.write()
class emptyPanel(buffer):
def __init__(self, parent, name, account):
super(emptyPanel, self).__init__(parent=parent)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.emptyPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.account = account
self.buffer.account = account
self.name = name
self.session = None
self.needs_init = True

View File

@@ -4,11 +4,11 @@ import platform
if platform.system() == "Windows":
import wx
from wxUI import buffers, dialogs, commonMessageDialogs, menus
import user
from controller import user
elif platform.system() == "Linux":
from gi.repository import Gtk
from gtkUI import buffers, dialogs, commonMessageDialogs
import messages
from controller import messages
import widgetUtils
import arrow
import webbrowser
@@ -18,11 +18,12 @@ import sound
import languageHandler
import logging
import youtube_utils
from twitter import compose, utils
from controller.buffers import baseBuffers
from sessions.twitter import compose, utils
from mysc.thread_utils import call_threaded
from twython import TwythonError
from pubsub import pub
from long_tweets import twishort, tweets
from sessions.twitter.long_tweets import twishort, tweets
log = logging.getLogger("controller.buffers")
@@ -33,216 +34,7 @@ def _tweets_exist(function):
function(self, *args, **kwargs)
return function_
class bufferController(object):
""" A basic buffer object. This should be the base class for all other derived buffers."""
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
"""Inits the main controller for this buffer:
@ parent wx.Treebook object: Container where we will put this buffer.
@ function str or None: function to be called periodically and update items on this buffer.
@ session sessionmanager.session object or None: Session handler for settings, database and Twitter access.
"""
super(bufferController, self).__init__()
self.function = function
# Compose_function will be used to render an object on this buffer. Normally, signature is as follows:
# compose_function(item, db, relative_times, show_screen_names=False, session=None)
# Compose functions will be defined in every buffer if items are different than tweets.
# Read more about compose functions in twitter/compose.py.
self.compose_function = None
self.args = args
self.kwargs = kwargs
# This will be used as a reference to the wx.Panel object wich stores the buffer GUI.
self.buffer = None
# This should countains the account associated to this buffer.
self.account = ""
# This controls wether the start_stream function should be called when starting the program.
self.needs_init = True
# if this is set to False, the buffer will be ignored on the invisible interface.
self.invisible = False
# Control variable, used to track time of execution for calls to start_stream.
self.execution_time = 0
def clear_list(self): pass
def get_event(self, ev):
""" Catches key presses in the WX interface and generate the corresponding event names."""
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
else:
event = None
ev.Skip()
if event != None:
try:
getattr(self, event)()
except AttributeError:
pass
def volume_down(self):
if self.session.settings["sound"]["volume"] > 0.0:
if self.session.settings["sound"]["volume"] <= 0.05:
self.session.settings["sound"]["volume"] = 0.0
else:
self.session.settings["sound"]["volume"] -=0.05
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100.0))
self.session.sound.play("volume_changed.ogg")
self.session.settings.write()
def volume_up(self):
if self.session.settings["sound"]["volume"] < 1.0:
if self.session.settings["sound"]["volume"] >= 0.95:
self.session.settings["sound"]["volume"] = 1.0
else:
self.session.settings["sound"]["volume"] +=0.05
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100))
self.session.sound.play("volume_changed.ogg")
self.session.settings.write()
def start_stream(self, mandatory=False, play_sound=True):
# if mandatory == True:
# output.speak(_(u"Unable to update this buffer."))
pass
def get_more_items(self):
output.speak(_(u"This action is not supported for this buffer"), True)
def put_items_on_list(self, items):
pass
def remove_buffer(self):
return False
def remove_item(self, item):
f = self.buffer.list.get_selected()
self.buffer.list.remove_item(item)
self.buffer.list.select_item(f)
def bind_events(self):
pass
def get_object(self):
return self.buffer
def get_message(self):
pass
def set_list_position(self, reversed=False):
if reversed == False:
self.buffer.list.select_item(-1)
else:
self.buffer.list.select_item(0)
def reply(self):
pass
def direct_message(self):
pass
def retweet(self):
pass
def destroy_status(self):
pass
def post_tweet(self, *args, **kwargs):
title = _(u"Tweet")
caption = _(u"Write the tweet here")
tweet = messages.tweet(self.session, title, caption, "")
if tweet.message.get_response() == widgetUtils.OK:
if config.app["app-settings"]["remember_mention_and_longtweet"]:
config.app["app-settings"]["longtweet"] = tweet.message.long_tweet.GetValue()
config.app.write()
text = tweet.message.get_text()
if len(text) > 280 and tweet.message.get("long_tweet") == True:
if not hasattr(tweet, "attachments"):
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
else:
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
if not hasattr(tweet, "attachments") or len(tweet.attachments) == 0:
call_threaded(self.session.api_call, call_name="update_status", status=text, _sound="tweet_send.ogg")
else:
call_threaded(self.post_with_media, text=text, attachments=tweet.attachments, _sound="tweet_send.ogg")
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
self.session.settings.write()
def post_with_media(self, text, attachments):
media_ids = []
for i in attachments:
photo = open(i["file"], "rb")
img = self.session.twitter.twitter.upload_media(media=photo)
self.session.twitter.twitter.create_metadata(media_id=img["media_id"], alt_text=dict(text=i["description"]))
media_ids.append(img["media_id"])
self.session.twitter.twitter.update_status(status=text, media_ids=media_ids)
def save_positions(self):
try:
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
except AttributeError:
pass
class accountPanel(bufferController):
def __init__(self, parent, name, account, account_id):
super(accountPanel, self).__init__(parent, None, name)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.accountPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.session = None
self.needs_init = False
self.account = account
self.buffer.account = account
self.name = name
self.account_id = account_id
def setup_account(self):
widgetUtils.connect_event(self.buffer, widgetUtils.CHECKBOX, self.autostart, menuitem=self.buffer.autostart_account)
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(False)
else:
self.buffer.change_autostart(True)
if not hasattr(self, "logged"):
self.buffer.change_login(login=False)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.logout)
else:
self.buffer.change_login(login=True)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.login)
def login(self, *args, **kwargs):
del self.logged
self.setup_account()
pub.sendMessage("login", session_id=self.account_id)
def logout(self, *args, **kwargs):
self.logged = False
self.setup_account()
pub.sendMessage("logout", session_id=self.account_id)
def autostart(self, *args, **kwargs):
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(True)
config.app["sessions"]["ignored_sessions"].remove(self.account_id)
else:
self.buffer.change_autostart(False)
config.app["sessions"]["ignored_sessions"].append(self.account_id)
config.app.write()
class emptyPanel(bufferController):
def __init__(self, parent, name, account):
super(emptyPanel, self).__init__(parent=parent)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.emptyPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.account = account
self.buffer.account = account
self.name = name
self.session = None
self.needs_init = True
class baseBufferController(bufferController):
class baseBufferController(baseBuffers.buffer):
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, compose_func="compose_tweet", *args, **kwargs):
super(baseBufferController, self).__init__(parent, function, *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
@@ -271,6 +63,57 @@ class baseBufferController(bufferController):
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 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 = _(u"Tweet")
caption = _(u"Write the tweet here")
tweet = messages.tweet(self.session, title, caption, "")
if tweet.message.get_response() == widgetUtils.OK:
if config.app["app-settings"]["remember_mention_and_longtweet"]:
config.app["app-settings"]["longtweet"] = tweet.message.long_tweet.GetValue()
config.app.write()
text = tweet.message.get_text()
if len(text) > 280 and tweet.message.get("long_tweet") == True:
if not hasattr(tweet, "attachments"):
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
else:
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
if not hasattr(tweet, "attachments") or len(tweet.attachments) == 0:
item = self.session.api_call(call_name="update_status", status=text, _sound="tweet_send.ogg", tweet_mode="extended")
# else:
# call_threaded(self.post_with_media, text=text, attachments=tweet.attachments, _sound="tweet_send.ogg")
if item != None:
pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
self.session.settings.write()
def post_with_media(self, text, attachments):
media_ids = []
for i in attachments:
photo = open(i["file"], "rb")
img = self.session.twitter.upload_media(media=photo)
self.session.twitter.create_metadata(media_id=img["media_id"], alt_text=dict(text=i["description"]))
media_ids.append(img["media_id"])
self.session.twitter.update_status(status=text, media_ids=media_ids)
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]
@@ -285,10 +128,10 @@ class baseBufferController(bufferController):
tweetsList = []
tweet_id = tweet["id"]
message = None
if tweet.has_key("message"):
if "message" in tweet:
message = tweet["message"]
try:
tweet = self.session.twitter.twitter.show_status(id=tweet_id, include_ext_alt_text=True, tweet_mode="extended")
tweet = self.session.twitter.show_status(id=tweet_id, include_ext_alt_text=True, tweet_mode="extended")
urls = utils.find_urls_in_text(tweet["full_text"])
for url in range(0, len(urls)):
try: tweet["full_text"] = tweet["full_text"].replace(urls[url], tweet["entities"]["urls"][url]["expanded_url"])
@@ -302,7 +145,7 @@ class baseBufferController(bufferController):
while l != False:
tweetsList.append(tweet)
try:
tweet = self.session.twitter.twitter.show_status(id=l, include_ext_alt_text=True, tweet_mode="extended")
tweet = self.session.twitter.show_status(id=l, include_ext_alt_text=True, tweet_mode="extended")
urls = utils.find_urls_in_text(tweet["full_text"])
for url in range(0, len(urls)):
try: tweet["full_text"] = tweet["full_text"].replace(urls[url], tweet["entities"]["urls"][url]["expanded_url"])
@@ -315,7 +158,7 @@ class baseBufferController(bufferController):
tweetsList.append(tweet)
return (tweet, tweetsList)
def start_stream(self, mandatory=False, play_sound=True):
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:
@@ -337,8 +180,22 @@ class baseBufferController(bufferController):
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:
@@ -349,6 +206,8 @@ class baseBufferController(bufferController):
items = self.session.get_more_items(self.function, count=self.session.settings["general"]["max_tweets_per_call"], max_id=last_id, *self.args, **self.kwargs)
except TwythonError as e:
output.speak(e.message, True)
if items == None:
return
for i in items:
if utils.is_allowed(i, self.session.settings, self.name) == True and utils.find_item(i["id"], self.session.db[self.name]) == None:
i = self.session.check_quoted_status(i)
@@ -382,7 +241,7 @@ class baseBufferController(bufferController):
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.session.db.has_key(self.name):
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
@@ -395,7 +254,7 @@ class baseBufferController(bufferController):
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.session.db.has_key(self.name):
if self.name in self.session.db:
self.session.db.pop(self.name)
self.session.settings.write()
return True
@@ -456,10 +315,10 @@ class baseBufferController(bufferController):
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_tweet, self.buffer.tweet)
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.retweet, self.buffer.retweet)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.direct_message, self.buffer.dm)
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)
# Replace for the correct way in other platforms.
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
@@ -471,13 +330,13 @@ class baseBufferController(bufferController):
menu = menus.sentPanelMenu()
elif self.name == "direct_messages":
menu = menus.dmPanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.direct_message, menuitem=menu.reply)
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.retweet, menuitem=menu.retweet)
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)
@@ -485,6 +344,8 @@ class baseBufferController(bufferController):
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:
@@ -518,7 +379,7 @@ class baseBufferController(bufferController):
self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
def get_tweet(self):
if self.session.db[self.name][self.buffer.list.get_selected()].has_key("retweeted_status"):
if "retweeted_status" in self.session.db[self.name][self.buffer.list.get_selected()]:
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()]
@@ -533,7 +394,7 @@ class baseBufferController(bufferController):
tweet = self.get_right_tweet()
screen_name = tweet["user"]["screen_name"]
id = tweet["id"]
twishort_enabled = tweet.has_key("twishort")
twishort_enabled = "twishort" in tweet
users = utils.get_all_mentioned(tweet, self.session.db, field="screen_name")
ids = utils.get_all_mentioned(tweet, self.session.db, field="id_str")
# Build the window title
@@ -548,7 +409,7 @@ class baseBufferController(bufferController):
if len(users) > 0:
config.app["app-settings"]["mention_all"] = message.message.mentionAll.GetValue()
config.app.write()
params = {"_sound": "reply_send.ogg", "in_reply_to_status_id": id,}
params = {"_sound": "reply_send.ogg", "in_reply_to_status_id": id, "tweet_mode": "extended"}
text = message.message.get_text()
if twishort_enabled == False:
excluded_ids = message.get_ids()
@@ -568,12 +429,15 @@ class baseBufferController(bufferController):
else:
params["call_name"] = "update_status_with_media"
params["media"] = message.file
call_threaded(self.session.api_call, **params)
item = self.session.api_call(**params)
if item != None:
pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
if hasattr(message.message, "destroy"): message.message.destroy()
self.session.settings.write()
@_tweets_exist
def direct_message(self, *args, **kwargs):
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"]
@@ -611,7 +475,7 @@ class baseBufferController(bufferController):
if hasattr(dm.message, "destroy"): dm.message.destroy()
@_tweets_exist
def retweet(self, *args, **kwargs):
def share_item(self, *args, **kwargs):
tweet = self.get_right_tweet()
id = tweet["id"]
if self.session.settings["general"]["retweet_mode"] == "ask":
@@ -626,7 +490,10 @@ class baseBufferController(bufferController):
self._retweet_with_comment(tweet, id)
def _retweet_with_comment(self, tweet, id, comment=''):
if tweet.has_key("full_text"):
# If quoting a retweet, let's quote the original tweet instead the retweet.
if "retweeted_status" in tweet:
tweet = tweet["retweeted_status"]
if "full_text" in tweet:
comments = tweet["full_text"]
else:
comments = tweet["text"]
@@ -637,13 +504,21 @@ class baseBufferController(bufferController):
text = retweet.message.get_text()
text = text+" https://twitter.com/{0}/status/{1}".format(tweet["user"]["screen_name"], id)
if retweet.image == None:
call_threaded(self.session.api_call, call_name="update_status", _sound="retweet_send.ogg", status=text, in_reply_to_status_id=id)
item = self.session.api_call(call_name="update_status", _sound="retweet_send.ogg", status=text, in_reply_to_status_id=id, tweet_mode="extended")
if item != None:
new_item = self.session.twitter.show_status(id=item["id"], include_ext_alt_text=True, tweet_mode="extended")
pub.sendMessage("sent-tweet", data=new_item, user=self.session.db["user_name"])
else:
call_threaded(self.session.api_call, call_name="update_status", _sound="retweet_send.ogg", status=text, media=retweet.image)
if hasattr(retweet.message, "destroy"): retweet.message.destroy()
def _direct_retweet(self, id):
call_threaded(self.session.api_call, call_name="retweet", _sound="retweet_send.ogg", id=id)
item = self.session.api_call(call_name="retweet", _sound="retweet_send.ogg", id=id, tweet_mode="extended")
if item != None:
# Retweets are returned as non-extended tweets, so let's get the object as extended
# just before sending the event message. See https://github.com/manuelcortez/TWBlue/issues/253
item = self.session.twitter.show_status(id=item["id"], include_ext_alt_text=True, tweet_mode="extended")
pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
def onFocus(self, *args, **kwargs):
tweet = self.get_tweet()
@@ -711,13 +586,12 @@ class baseBufferController(bufferController):
if answer == widgetUtils.YES:
try:
if self.name == "direct_messages" or self.name == "sent_direct_messages":
self.session.twitter.twitter.destroy_direct_message(id=self.get_right_tweet()["id"])
self.session.twitter.destroy_direct_message(id=self.get_right_tweet()["id"])
self.session.db[self.name]["items"].pop(index)
else:
self.session.twitter.twitter.destroy_status(id=self.get_right_tweet()["id"])
self.session.twitter.destroy_status(id=self.get_right_tweet()["id"])
self.session.db[self.name].pop(index)
self.buffer.list.remove_item(index)
# if index > 0:
except TwythonError:
self.session.sound.play("error.ogg")
@@ -737,7 +611,7 @@ class baseBufferController(bufferController):
def get_quoted_tweet(self, tweet):
# try:
quoted_tweet = self.session.twitter.twitter.show_status(id=tweet["id"])
quoted_tweet = self.session.twitter.show_status(id=tweet["id"])
urls = utils.find_urls_in_text(quoted_tweet["text"])
for url in range(0, len(urls)):
try: quoted_tweet["text"] = quoted_tweet["text"].replace(urls[url], quoted_tweet["entities"]["urls"][url]["expanded_url"])
@@ -748,13 +622,19 @@ class baseBufferController(bufferController):
l = tweets.is_long(quoted_tweet)
id = tweets.get_id(l)
# try:
original_tweet = self.session.twitter.twitter.show_status(id=id)
original_tweet = self.session.twitter.show_status(id=id)
urls = utils.find_urls_in_text(original_tweet["text"])
for url in range(0, len(urls)):
try: original_tweet["text"] = original_tweet["text"].replace(urls[url], original_tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, self.session.db, self.session.settings["general"]["relative_times"])
def open_in_browser(self, *args, **kwargs):
tweet = self.get_tweet()
output.speak(_(u"Opening item in web browser..."))
url = "https://twitter.com/{screen_name}/status/{tweet_id}".format(screen_name=tweet["user"]["screen_name"], tweet_id=tweet["id"])
webbrowser.open(url)
class directMessagesController(baseBufferController):
def get_more_items(self):
@@ -763,6 +643,8 @@ class directMessagesController(baseBufferController):
except TwythonError as e:
output.speak(e.message, True)
return
if items == None:
return
sent = []
for i in items:
if i["message_create"]["sender_id"] == self.session.db["user_id"]:
@@ -809,7 +691,9 @@ class directMessagesController(baseBufferController):
config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue()
config.app.write()
if message.image == None:
call_threaded(self.session.api_call, call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text())
item = self.session.api_call(call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text(), tweet_mode="extended")
if item != None:
pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
else:
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file)
if hasattr(message.message, "destroy"): message.message.destroy()
@@ -832,11 +716,25 @@ class directMessagesController(baseBufferController):
self.session.db[self.name]["items"] = []
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]["items"][-1]
else:
tweet = self.session.db[self.name]["items"][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."))
class sentDirectMessagesController(directMessagesController):
def __init__(self, *args, **kwargs):
super(sentDirectMessagesController, self).__init__(*args, **kwargs)
if self.session.db.has_key("sent_direct_messages") == False:
if ("sent_direct_messages" in self.session.db) == False:
self.session.db["sent_direct_messages"] = {"items": []}
def get_more_items(self):
@@ -862,14 +760,14 @@ class listBufferController(baseBufferController):
self.list_id = list_id
self.kwargs["list_id"] = list_id
def start_stream(self, mandatory=False, play_sound=True):
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
self.get_user_ids()
super(listBufferController, self).start_stream(mandatory, play_sound)
super(listBufferController, self).start_stream(mandatory, play_sound, avoid_autoreading)
def get_user_ids(self):
next_cursor = -1
while(next_cursor):
users = self.session.twitter.twitter.get_list_members(list_id=self.list_id, cursor=next_cursor, include_entities=False, skip_status=True)
users = self.session.twitter.get_list_members(list_id=self.list_id, cursor=next_cursor, include_entities=False, skip_status=True)
for i in users['users']:
if i["id"] not in self.users:
self.users.append(i["id"])
@@ -883,65 +781,13 @@ class listBufferController(baseBufferController):
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.session.db.has_key(self.name):
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
class eventsBufferController(bufferController):
def __init__(self, parent, name, session, account, *args, **kwargs):
super(eventsBufferController, self).__init__(parent, *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.invisible = True
self.buffer = buffers.eventsPanel(parent, name)
self.name = name
self.account = account
self.buffer.account = self.account
self.compose_function = compose.compose_event
self.session = session
self.type = self.buffer.type
self.get_formatted_message = self.get_message
def get_message(self):
if self.buffer.list.get_count() == 0: return _(u"Empty")
# fix this:
return "%s. %s" % (self.buffer.list.list.GetItemText(self.buffer.list.get_selected()), self.buffer.list.list.GetItemText(self.buffer.list.get_selected(), 1))
def add_new_item(self, item):
tweet = self.compose_function(item, self.session.db["user_name"], self.session.settings["general"]["show_screen_names"])
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), speech=self.session.settings["reporting"]["speech_reporting"], braille=self.session.settings["reporting"]["braille_reporting"])
if self.buffer.list.get_count() == 1:
self.buffer.list.select_item(0)
def clear_list(self):
dlg = commonMessageDialogs.clear_list()
if dlg == widgetUtils.YES:
self.buffer.list.clear()
def show_menu(self, ev, pos=0, *args, **kwargs):
if self.buffer.list.get_count() == 0: return
menu = menus.eventsPanelMenu()
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 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")
class peopleBufferController(baseBufferController):
def __init__(self, parent, function, name, sessionObject, account, bufferType=None, *args, **kwargs):
super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs)
@@ -970,7 +816,7 @@ class peopleBufferController(baseBufferController):
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.session.db.has_key(self.name):
if self.name in self.session.db:
self.session.db.pop(self.name)
self.session.settings.write()
return True
@@ -984,7 +830,7 @@ class peopleBufferController(baseBufferController):
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.session.db.has_key(self.name):
if self.name in self.session.db:
self.session.db.pop(self.name)
self.session.settings.write()
return True
@@ -1012,12 +858,14 @@ class peopleBufferController(baseBufferController):
config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue()
config.app.write()
if message.image == None:
call_threaded(self.session.api_call, call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text())
item = self.session.api_call(call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text(), tweet_mode="extended")
if item != None:
pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
else:
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file)
if hasattr(message.message, "destroy"): message.message.destroy()
def start_stream(self, mandatory=False, play_sound=True):
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:
@@ -1031,6 +879,9 @@ class peopleBufferController(baseBufferController):
self.finished_timeline = True
if val > 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 val > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
self.auto_read(val)
return val
def get_more_items(self):
@@ -1039,6 +890,8 @@ class peopleBufferController(baseBufferController):
except TwythonError as e:
output.speak(e.message, True)
return
if items == None:
return
for i in items:
if self.session.settings["general"]["reverse_timelines"] == False:
self.session.db[self.name]["items"].insert(0, i)
@@ -1103,12 +956,14 @@ class peopleBufferController(baseBufferController):
def show_menu(self, ev, pos=0, *args, **kwargs):
menu = menus.peoplePanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.direct_message, menuitem=menu.reply)
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:
@@ -1117,8 +972,24 @@ class peopleBufferController(baseBufferController):
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]["items"][-1]
else:
tweet = self.session.db[self.name["items"]][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 open_in_browser(self, *args, **kwargs):
tweet = self.get_tweet()
output.speak(_(u"Opening item in web browser..."))
url = "https://twitter.com/{screen_name}".format(screen_name=tweet["screen_name"])
webbrowser.open(url)
class searchBufferController(baseBufferController):
def start_stream(self, mandatory=False, play_sound=True):
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:
@@ -1134,6 +1005,9 @@ class searchBufferController(baseBufferController):
self.put_items_on_list(num)
if num > 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 num > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
self.auto_read(num)
return num
def remove_buffer(self, force=False):
@@ -1145,7 +1019,7 @@ class searchBufferController(baseBufferController):
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.session.db.has_key(self.name):
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
@@ -1161,6 +1035,8 @@ class searchBufferController(baseBufferController):
items = self.session.search(self.name, count=self.session.settings["general"]["max_tweets_per_call"], max_id=last_id, *self.args, **self.kwargs)
except TwythonError as e:
output.speak(e.message, True)
if items == None:
return
for i in items:
if utils.is_allowed(i, self.session.settings, self.name) == True and utils.find_item(i["id"], self.session.db[self.name]) == None:
i = self.session.check_quoted_status(i)
@@ -1194,10 +1070,10 @@ class searchPeopleBufferController(peopleBufferController):
self.args = args
self.kwargs = kwargs
self.function = function
if self.kwargs.has_key("page") == False:
if ("page" in self.kwargs) == False:
self.kwargs["page"] = 1
def start_stream(self, mandatory=False, play_sound=True):
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=True):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
@@ -1214,6 +1090,9 @@ class searchPeopleBufferController(peopleBufferController):
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 get_more_items(self, *args, **kwargs):
@@ -1223,6 +1102,8 @@ class searchPeopleBufferController(peopleBufferController):
except TwythonError as e:
output.speak(e.message, True)
return
if items == None:
return
for i in items:
if self.session.settings["general"]["reverse_timelines"] == False:
self.session.db[self.name]["items"].insert(0, i)
@@ -1253,13 +1134,13 @@ class searchPeopleBufferController(peopleBufferController):
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.session.db.has_key(self.name):
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
class trendsBufferController(bufferController):
class trendsBufferController(baseBuffers.buffer):
def __init__(self, parent, name, session, account, trendsFor, *args, **kwargs):
super(trendsBufferController, self).__init__(parent=parent, session=session)
self.trendsFor = trendsFor
@@ -1278,7 +1159,7 @@ class trendsBufferController(bufferController):
self.get_formatted_message = self.get_message
self.reply = self.search_topic
def start_stream(self, mandatory=False, play_sound=True):
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:
@@ -1309,7 +1190,7 @@ class trendsBufferController(bufferController):
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_tweet, self.buffer.tweet)
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)
@@ -1326,7 +1207,7 @@ class trendsBufferController(bufferController):
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.session.db.has_key(self.name):
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
@@ -1381,9 +1262,12 @@ class trendsBufferController(bufferController):
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."))
class conversationBufferController(searchBufferController):
def start_stream(self, start=False, mandatory=False, play_sound=True):
def start_stream(self, start=False, 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:
@@ -1396,7 +1280,7 @@ class conversationBufferController(searchBufferController):
tweet = self.tweet
while tweet["in_reply_to_status_id"] != None:
try:
tweet = self.session.twitter.twitter.show_status(id=tweet["in_reply_to_status_id"], tweet_mode="extended")
tweet = self.session.twitter.show_status(id=tweet["in_reply_to_status_id"], tweet_mode="extended")
except TwythonError as err:
break
self.statuses.insert(0, tweet)
@@ -1415,6 +1299,9 @@ class conversationBufferController(searchBufferController):
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):
@@ -1423,7 +1310,7 @@ class conversationBufferController(searchBufferController):
else:
dlg = widgetUtils.YES
if dlg == widgetUtils.YES:
if self.session.db.has_key(self.name):
if self.name in self.session.db:
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:

View File

@@ -30,7 +30,7 @@ class filter(object):
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 self.buffer.session.settings["filters"].has_key(title):
if title in self.buffer.session.settings["filters"]:
return commonMessageDialogs.existing_filter()
self.buffer.session.settings["filters"][title] = d
self.buffer.session.settings.write()

View File

@@ -3,7 +3,7 @@ import widgetUtils
import output
from wxUI.dialogs import lists
from twython import TwythonError
from twitter import compose, utils
from sessions.twitter import compose, utils
from pubsub import pub
class listsController(object):
@@ -29,7 +29,7 @@ class listsController(object):
return [compose.compose_list(item) for item in self.session.db["lists"]]
def get_user_lists(self, user):
self.lists = self.session.twitter.twitter.show_lists(reverse=True, screen_name=user)
self.lists = self.session.twitter.show_lists(reverse=True, screen_name=user)
return [compose.compose_list(item) for item in self.lists]
def create_list(self, *args, **kwargs):
@@ -43,7 +43,7 @@ class listsController(object):
else:
mode = "private"
try:
new_list = self.session.twitter.twitter.create_list(name=name, description=description, mode=mode)
new_list = self.session.twitter.create_list(name=name, description=description, mode=mode)
self.session.db["lists"].append(new_list)
self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
except TwythonError as e:
@@ -63,7 +63,7 @@ class listsController(object):
else:
mode = "private"
try:
self.session.twitter.twitter.update_list(list_id=list["id"], name=name, description=description, mode=mode)
self.session.twitter.update_list(list_id=list["id"], name=name, description=description, mode=mode)
self.session.get_lists()
self.dialog.populate_list(self.get_all_lists(), True)
except TwythonError as e:
@@ -75,7 +75,7 @@ class listsController(object):
list = self.session.db["lists"][self.dialog.get_item()]["id"]
if lists.remove_list() == widgetUtils.YES:
try:
self.session.twitter.twitter.delete_list(list_id=list)
self.session.twitter.delete_list(list_id=list)
self.session.db["lists"].pop(self.dialog.get_item())
self.dialog.lista.remove_item(self.dialog.get_item())
except TwythonError as e:
@@ -90,7 +90,7 @@ class listsController(object):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()]["id"]
try:
list = self.session.twitter.twitter.subscribe_to_list(list_id=list_id)
list = self.session.twitter.subscribe_to_list(list_id=list_id)
item = utils.find_item(list["id"], self.session.db["lists"])
self.session.db["lists"].append(list)
except TwythonError as e:
@@ -100,7 +100,7 @@ class listsController(object):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()]["id"]
try:
list = self.session.twitter.twitter.unsubscribe_from_list(list_id=list_id)
list = self.session.twitter.unsubscribe_from_list(list_id=list_id)
self.session.db["lists"].remove(list)
except TwythonError as e:
output.speak("error %s: %s" % (e.status_code, e.msg))

View File

@@ -1,29 +1,33 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import platform
system = platform.system()
import application
import requests
import youtube_utils
import arrow
if system == "Windows":
from update import updater
from wxUI import (view, dialogs, commonMessageDialogs, sysTrayIcon)
import settings
from . import settings
from extra import SoundsTutorial, ocr
import keystrokeEditor
from keyboard_handler.wx_handler import WXKeyboardHandler
import userActionsController
import trendingTopics
import user
import listsController
import filterController
from . import userActionsController
from . import trendingTopics
from . import user
from . import listsController
from . import filterController
# from issueReporter import issueReporter
elif system == "Linux":
from gtkUI import (view, commonMessageDialogs)
from twitter import utils, compose
from sessions.twitter import utils, compose
from sessionmanager import manager, sessionManager
import buffersController
import messages
from sessionmanager import session as session_
from controller.buffers import baseBuffers, twitterBuffers
from . import messages
import sessions
from sessions.twitter import session as session_
from pubsub import pub
import sound
import output
@@ -39,6 +43,7 @@ import logging
import webbrowser
from mysc import localization
import os
import languageHandler
log = logging.getLogger("mainController")
@@ -54,13 +59,13 @@ class Controller(object):
name_ str: The name for the buffer
user str: The account for the buffer.
for example you may want to search the home_timeline buffer for the tw_blue2 user.
Return type: buffersController.buffer object."""
Return type: buffers.buffer object."""
for i in self.buffers:
if i.name == name_ and i.account == user: return i
def get_current_buffer(self):
""" Get the current focused bufferObject.
Return type: BuffersController.buffer object."""
Return type: buffers.buffer object."""
buffer = self.view.get_current_buffer()
if hasattr(buffer, "account"):
buffer = self.search_buffer(buffer.name, buffer.account)
@@ -69,7 +74,7 @@ class Controller(object):
def get_best_buffer(self):
""" Get the best buffer for doing something using the session object.
This function is useful when you need to open a timeline or post a tweet, and the user is in a buffer without a session, for example the events buffer.
Return type: buffersController.buffer object."""
Return type: twitterBuffers.buffer object."""
# Gets the parent buffer to know what account is doing an action
view_buffer = self.view.get_current_buffer()
# If the account has no session attached, we will need to search the first available non-empty buffer for that account to use its session.
@@ -108,27 +113,6 @@ class Controller(object):
[results.append(self.search_buffer(i.name, i.account)) for i in buffers if i.account == account and (i.type != "account")]
return results
def bind_stream_events(self):
""" Binds all the streaming events with their functions."""
log.debug("Binding events for the Twitter stream API...")
pub.subscribe(self.manage_home_timelines, "item-in-home")
pub.subscribe(self.manage_mentions, "mention")
pub.subscribe(self.manage_direct_messages, "direct-message")
pub.subscribe(self.manage_sent_tweets, "sent-tweet")
pub.subscribe(self.manage_events, "event")
pub.subscribe(self.manage_followers, "follower")
pub.subscribe(self.manage_friend, "friend")
pub.subscribe(self.manage_unfollowing, "unfollowing")
pub.subscribe(self.manage_favourite, "favourite")
pub.subscribe(self.manage_unfavourite, "unfavourite")
pub.subscribe(self.manage_blocked_user, "blocked-user")
pub.subscribe(self.manage_unblocked_user, "unblocked-user")
pub.subscribe(self.manage_item_in_timeline, "item-in-timeline")
pub.subscribe(self.manage_item_in_list, "item-in-list")
pub.subscribe(self.on_tweet_deleted, "tweet-deleted")
pub.subscribe(self.manage_stream_errors, "stream-error")
# pub.subscribe(self.restart_streams, "restart-streams")
def bind_other_events(self):
""" Binds the local application events with their functions."""
log.debug("Binding other application events...")
@@ -142,6 +126,13 @@ class Controller(object):
pub.subscribe(self.search_topic, "search")
pub.subscribe(self.update_sent_dms, "sent-dms-updated")
pub.subscribe(self.more_dms, "more-sent-dms")
pub.subscribe(self.manage_sent_tweets, "sent-tweet")
pub.subscribe(self.manage_friend, "friend")
pub.subscribe(self.manage_unfollowing, "unfollowing")
pub.subscribe(self.manage_favourite, "favourite")
pub.subscribe(self.manage_unfavourite, "unfavourite")
pub.subscribe(self.manage_blocked_user, "blocked-user")
pub.subscribe(self.manage_unblocked_user, "unblocked-user")
if system == "Windows":
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
@@ -165,7 +156,7 @@ class Controller(object):
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.post_reply, self.view.reply)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.post_retweet, self.view.retweet)
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.fav)
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.reverse_geocode, menuitem=self.view.view_coordinates)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.delete, self.view.delete)
@@ -179,6 +170,7 @@ class Controller(object):
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.check_for_updates, self.view.check_for_updates)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.about, menuitem=self.view.about)
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.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)
@@ -234,8 +226,6 @@ class Controller(object):
# This saves the current account (important in invisible mode)
self.current_account = ""
self.view.prepare()
if application.streaming_lives():
self.bind_stream_events()
self.bind_other_events()
if system == "Windows":
self.set_systray_icon()
@@ -255,12 +245,12 @@ class Controller(object):
def do_work(self):
""" Creates the buffer objects for all accounts. This does not starts the buffer streams, only creates the objects."""
log.debug("Creating buffers for all sessions...")
for i in session_.sessions:
for i in sessions.sessions:
log.debug("Working on session %s" % (i,))
if session_.sessions[i].is_logged == False:
self.create_ignored_session_buffer(session_.sessions[i])
if sessions.sessions[i].is_logged == False:
self.create_ignored_session_buffer(sessions.sessions[i])
continue
self.create_buffers(session_.sessions[i])
self.create_buffers(sessions.sessions[i])
# Connection checker executed each minute.
self.checker_function = RepeatingTimer(60, self.check_connection)
@@ -273,27 +263,27 @@ class Controller(object):
def start(self):
""" Starts all buffer objects. Loads their items."""
for i in session_.sessions:
if session_.sessions[i].is_logged == False: continue
self.start_buffers(session_.sessions[i])
self.set_buffer_positions(session_.sessions[i])
for i in sessions.sessions:
if sessions.sessions[i].is_logged == False: continue
self.start_buffers(sessions.sessions[i])
self.set_buffer_positions(sessions.sessions[i])
if config.app["app-settings"]["play_ready_sound"] == True:
session_.sessions[session_.sessions.keys()[0]].sound.play("ready.ogg")
sessions.sessions[sessions.sessions.keys()[0]].sound.play("ready.ogg")
if config.app["app-settings"]["speak_ready_msg"] == True:
output.speak(_(u"Ready"))
self.started = True
def create_ignored_session_buffer(self, session):
self.accounts.append(session.settings["twitter"]["user_name"])
account = buffersController.accountPanel(self.view.nb, session.settings["twitter"]["user_name"], session.settings["twitter"]["user_name"], session.session_id)
account = baseBuffers.accountPanel(self.view.nb, session.settings["twitter"]["user_name"], session.settings["twitter"]["user_name"], session.session_id)
account.logged = False
account.setup_account()
self.buffers.append(account)
self.view.add_buffer(account.buffer , name=session.settings["twitter"]["user_name"])
def login_account(self, session_id):
for i in session_.sessions:
if session_.sessions[i].session_id == session_id: session = session_.sessions[i]
for i in sessions.sessions:
if sessions.sessions[i].session_id == session_id: session = sessions.sessions[i]
session.login()
session.db = dict()
self.create_buffers(session, False)
@@ -305,100 +295,96 @@ class Controller(object):
session.get_user_info()
if createAccounts == True:
self.accounts.append(session.db["user_name"])
account = buffersController.accountPanel(self.view.nb, session.db["user_name"], session.db["user_name"], session.session_id)
account = baseBuffers.accountPanel(self.view.nb, session.db["user_name"], session.db["user_name"], session.session_id)
account.setup_account()
self.buffers.append(account)
self.view.add_buffer(account.buffer , name=session.db["user_name"])
for i in session.settings['general']['buffer_order']:
if i == 'home':
home = buffersController.baseBufferController(self.view.nb, "get_home_timeline", "home_timeline", session, session.db["user_name"], sound="tweet_received.ogg", tweet_mode="extended")
home = twitterBuffers.baseBufferController(self.view.nb, "get_home_timeline", "home_timeline", session, session.db["user_name"], sound="tweet_received.ogg", tweet_mode="extended")
self.buffers.append(home)
self.view.insert_buffer(home.buffer, name=_(u"Home"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'mentions':
mentions = buffersController.baseBufferController(self.view.nb, "get_mentions_timeline", "mentions", session, session.db["user_name"], sound="mention_received.ogg", tweet_mode="extended")
mentions = twitterBuffers.baseBufferController(self.view.nb, "get_mentions_timeline", "mentions", session, session.db["user_name"], sound="mention_received.ogg", tweet_mode="extended")
self.buffers.append(mentions)
self.view.insert_buffer(mentions.buffer, name=_(u"Mentions"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'dm':
dm = buffersController.directMessagesController(self.view.nb, "get_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg", full_text=True, items="events")
dm = twitterBuffers.directMessagesController(self.view.nb, "get_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg", full_text=True, items="events")
self.buffers.append(dm)
self.view.insert_buffer(dm.buffer, name=_(u"Direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'sent_dm':
sent_dm = buffersController.sentDirectMessagesController(self.view.nb, "", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message")
sent_dm = twitterBuffers.sentDirectMessagesController(self.view.nb, "", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message")
self.buffers.append(sent_dm)
self.view.insert_buffer(sent_dm.buffer, name=_(u"Sent Direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
self.view.insert_buffer(sent_dm.buffer, name=_(u"Sent direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'sent_tweets':
sent_tweets = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "sent_tweets", session, session.db["user_name"], screen_name=session.db["user_name"], tweet_mode="extended")
sent_tweets = twitterBuffers.baseBufferController(self.view.nb, "get_user_timeline", "sent_tweets", session, session.db["user_name"], screen_name=session.db["user_name"], tweet_mode="extended")
self.buffers.append(sent_tweets)
self.view.insert_buffer(sent_tweets.buffer, name=_(u"Sent tweets"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'favorites':
favourites = buffersController.baseBufferController(self.view.nb, "get_favorites", "favourites", session, session.db["user_name"], sound="favourite.ogg", tweet_mode="extended")
favourites = twitterBuffers.baseBufferController(self.view.nb, "get_favorites", "favourites", session, session.db["user_name"], sound="favourite.ogg", tweet_mode="extended")
self.buffers.append(favourites)
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'followers':
followers = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "followers", session, session.db["user_name"], sound="update_followers.ogg", screen_name=session.db["user_name"])
followers = twitterBuffers.peopleBufferController(self.view.nb, "get_followers_list", "followers", session, session.db["user_name"], sound="update_followers.ogg", screen_name=session.db["user_name"])
self.buffers.append(followers)
self.view.insert_buffer(followers.buffer, name=_(u"Followers"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'friends':
friends = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "friends", session, session.db["user_name"], screen_name=session.db["user_name"])
friends = twitterBuffers.peopleBufferController(self.view.nb, "get_friends_list", "friends", session, session.db["user_name"], screen_name=session.db["user_name"])
self.buffers.append(friends)
self.view.insert_buffer(friends.buffer, name=_(u"Friends"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'blocks':
blocks = buffersController.peopleBufferController(self.view.nb, "list_blocks", "blocked", session, session.db["user_name"])
blocks = twitterBuffers.peopleBufferController(self.view.nb, "list_blocks", "blocked", session, session.db["user_name"])
self.buffers.append(blocks)
self.view.insert_buffer(blocks.buffer, name=_(u"Blocked users"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'muted':
muted = buffersController.peopleBufferController(self.view.nb, "list_mutes", "muted", session, session.db["user_name"])
muted = twitterBuffers.peopleBufferController(self.view.nb, "list_mutes", "muted", session, session.db["user_name"])
self.buffers.append(muted)
self.view.insert_buffer(muted.buffer, name=_(u"Muted users"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'events' and application.streaming_lives():
events = buffersController.eventsBufferController(self.view.nb, "events", session, session.db["user_name"], bufferType="dmPanel", screen_name=session.db["user_name"])
self.buffers.append(events)
self.view.insert_buffer(events.buffer, name=_(u"Events"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
timelines = buffersController.emptyPanel(self.view.nb, "timelines", session.db["user_name"])
timelines = baseBuffers.emptyPanel(self.view.nb, "timelines", session.db["user_name"])
self.buffers.append(timelines)
self.view.insert_buffer(timelines.buffer , name=_(u"Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["timelines"]:
tl = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (i,), session, session.db["user_name"], sound="tweet_timeline.ogg", bufferType=None, user_id=i, tweet_mode="extended")
tl = twitterBuffers.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (i,), session, session.db["user_name"], sound="tweet_timeline.ogg", bufferType=None, user_id=i, tweet_mode="extended")
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(i,), pos=self.view.search("timelines", session.db["user_name"]))
favs_timelines = buffersController.emptyPanel(self.view.nb, "favs_timelines", session.db["user_name"])
favs_timelines = baseBuffers.emptyPanel(self.view.nb, "favs_timelines", session.db["user_name"])
self.buffers.append(favs_timelines)
self.view.insert_buffer(favs_timelines.buffer , name=_(u"Likes timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["favourites_timelines"]:
tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (i,), session, session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, tweet_mode="extended")
tl = twitterBuffers.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (i,), session, session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, tweet_mode="extended")
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Likes for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
followers_timelines = buffersController.emptyPanel(self.view.nb, "followers_timelines", session.db["user_name"])
followers_timelines = baseBuffers.emptyPanel(self.view.nb, "followers_timelines", session.db["user_name"])
self.buffers.append(followers_timelines)
self.view.insert_buffer(followers_timelines.buffer , name=_(u"Followers' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["followers_timelines"]:
tl = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
tl = twitterBuffers.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Followers for {}").format(i,), pos=self.view.search("followers_timelines", session.db["user_name"]))
friends_timelines = buffersController.emptyPanel(self.view.nb, "friends_timelines", session.db["user_name"])
friends_timelines = baseBuffers.emptyPanel(self.view.nb, "friends_timelines", session.db["user_name"])
self.buffers.append(friends_timelines)
self.view.insert_buffer(friends_timelines.buffer , name=_(u"Friends' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["friends_timelines"]:
tl = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
tl = twitterBuffers.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Friends for {}").format(i,), pos=self.view.search("friends_timelines", session.db["user_name"]))
lists = buffersController.emptyPanel(self.view.nb, "lists", session.db["user_name"])
lists = baseBuffers.emptyPanel(self.view.nb, "lists", session.db["user_name"])
self.buffers.append(lists)
self.view.insert_buffer(lists.buffer , name=_(u"Lists"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["lists"]:
tl = buffersController.listBufferController(self.view.nb, "get_list_statuses", "%s-list" % (i,), session, session.db["user_name"], bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), tweet_mode="extended")
tl = twitterBuffers.listBufferController(self.view.nb, "get_list_statuses", "%s-list" % (i,), session, session.db["user_name"], bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), tweet_mode="extended")
session.lists.append(tl)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"List for {}").format(i), pos=self.view.search("lists", session.db["user_name"]))
searches = buffersController.emptyPanel(self.view.nb, "searches", session.db["user_name"])
searches = baseBuffers.emptyPanel(self.view.nb, "searches", session.db["user_name"])
self.buffers.append(searches)
self.view.insert_buffer(searches.buffer , name=_(u"Searches"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["tweet_searches"]:
tl = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (i,), session, session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=i, tweet_mode="extended")
tl = twitterBuffers.searchBufferController(self.view.nb, "search", "%s-searchterm" % (i,), session, session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=i, tweet_mode="extended")
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Search for {}").format(i), pos=self.view.search("searches", session.db["user_name"]))
for i in session.settings["other_buffers"]["trending_topic_buffers"]:
buffer = buffersController.trendsBufferController(self.view.nb, "%s_tt" % (i,), session, session.db["user_name"], i, sound="trends_updated.ogg")
buffer = twitterBuffers.trendsBufferController(self.view.nb, "%s_tt" % (i,), session, session.db["user_name"], i, sound="trends_updated.ogg")
buffer.start_stream(play_sound=False)
buffer.searchfunction = self.search
self.buffers.append(buffer)
@@ -407,12 +393,12 @@ class Controller(object):
def set_buffer_positions(self, session):
"Sets positions for buffers if values exist in the database."
for i in self.buffers:
if i.account == session.db["user_name"] and session.db.has_key(i.name+"_pos") and hasattr(i.buffer,'list'):
if i.account == session.db["user_name"] and i.name+"_pos" in session.db and hasattr(i.buffer,'list'):
i.buffer.list.select_item(session.db[str(i.name+"_pos")])
def logout_account(self, session_id):
for i in session_.sessions:
if session_.sessions[i].session_id == session_id: session = session_.sessions[i]
for i in sessions.sessions:
if sessions.sessions[i].session_id == session_id: session = sessions.sessions[i]
user = session.db["user_name"]
delete_buffers = []
for i in self.buffers:
@@ -446,12 +432,12 @@ class Controller(object):
buffer.session.settings["other_buffers"]["tweet_searches"].append(term)
buffer.session.settings.write()
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
search = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=term, tweet_mode="extended", **args)
search = twitterBuffers.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=term, tweet_mode="extended", **args)
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:
search = buffersController.searchPeopleBufferController(self.view.nb, "search_users", "%s-searchUser" % (term,), buffer.session, buffer.session.db["user_name"], bufferType=None, sound="search_updated.ogg", q=term)
search = twitterBuffers.searchPeopleBufferController(self.view.nb, "search_users", "%s-searchUser" % (term,), buffer.session, buffer.session.db["user_name"], bufferType=None, sound="search_updated.ogg", q=term)
search.start_stream(mandatory=True)
pos=self.view.search("searches", buffer.session.db["user_name"])
self.insert_buffer(search, pos)
@@ -572,13 +558,12 @@ class Controller(object):
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
if dlg.get_response() == widgetUtils.OK:
try:
list = buff.session.twitter.twitter.add_list_member(list_id=buff.session.db["lists"][dlg.get_item()]["id"], screen_name=user)
list = buff.session.twitter.add_list_member(list_id=buff.session.db["lists"][dlg.get_item()]["id"], screen_name=user)
older_list = utils.find_item(buff.session.db["lists"][dlg.get_item()]["id"], buff.session.db["lists"])
listBuffer = self.search_buffer("%s-list" % (buff.session.db["lists"][dlg.get_item()]["name"].lower()), buff.session.db["user_name"])
if listBuffer != None: listBuffer.get_user_ids()
buff.session.db["lists"].pop(older_list)
buff.session.db["lists"].append(list)
if listBuffer != None: pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
@@ -601,13 +586,12 @@ class Controller(object):
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
if dlg.get_response() == widgetUtils.OK:
try:
list = buff.session.twitter.twitter.delete_list_member(list_id=buff.session.db["lists"][dlg.get_item()]["id"], screen_name=user)
list = buff.session.twitter.delete_list_member(list_id=buff.session.db["lists"][dlg.get_item()]["id"], screen_name=user)
older_list = utils.find_item(buff.session.db["lists"][dlg.get_item()]["id"], buff.session.db["lists"])
listBuffer = self.search_buffer("%s-list" % (buff.session.db["lists"][dlg.get_item()]["name"].lower()), buff.session.db["user_name"])
if listBuffer != None: listBuffer.get_user_ids()
buff.session.db["lists"].pop(older_list)
buff.session.db["lists"].append(list)
if listBuffer != None: pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
@@ -664,16 +648,17 @@ class Controller(object):
for i in self.buffers: i.save_positions()
log.debug("Exiting...")
log.debug("Saving global configuration...")
for item in session_.sessions:
if session_.sessions[item].logged == False: continue
log.debug("Disconnecting streams for %s session" % (session_.sessions[item].session_id,))
if hasattr(session_.sessions[item], "main_stream"): session_.sessions[item].main_stream.disconnect()
if hasattr(session_.sessions[item], "timelinesStream"): session_.sessions[item].timelinesStream.disconnect()
session_.sessions[item].sound.cleaner.cancel()
log.debug("Shelving database for " + session_.sessions[item].session_id)
session_.sessions[item].shelve()
for item in sessions.sessions:
if sessions.sessions[item].logged == False: continue
log.debug("Disconnecting streams for %s session" % (sessions.sessions[item].session_id,))
sessions.sessions[item].sound.cleaner.cancel()
log.debug("Shelving database for " + sessions.sessions[item].session_id)
sessions.sessions[item].shelve()
if system == "Windows":
self.systrayIcon.RemoveIcon()
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
if os.path.exists(pidpath):
os.remove(pidpath)
widgetUtils.exit_application()
def follow(self, *args, **kwargs):
@@ -762,25 +747,25 @@ class Controller(object):
def post_tweet(self, event=None):
buffer = self.get_best_buffer()
buffer.post_tweet()
buffer.post_status()
def post_reply(self, *args, **kwargs):
buffer = self.get_current_buffer()
if buffer.name == "direct_messages":
buffer.direct_message()
buffer.send_message()
else:
buffer.reply()
def send_dm(self, *args, **kwargs):
buffer = self.get_current_buffer()
buffer.direct_message()
buffer.send_message()
def post_retweet(self, *args, **kwargs):
buffer = self.get_current_buffer()
if buffer.type == "dm" or buffer.type == "people" or buffer.type == "events":
return
else:
buffer.retweet()
buffer.share_item()
def add_to_favourites(self, *args, **kwargs):
buffer = self.get_current_buffer()
@@ -804,7 +789,7 @@ class Controller(object):
return
else:
id = buffer.get_tweet()["id"]
tweet = buffer.session.twitter.twitter.show_status(id=id, include_ext_alt_text=True, tweet_mode="extended")
tweet = buffer.session.twitter.show_status(id=id, include_ext_alt_text=True, tweet_mode="extended")
if tweet["favorited"] == False:
call_threaded(buffer.session.api_call, call_name="create_favorite", _sound="favourite.ogg", id=id)
else:
@@ -812,18 +797,26 @@ class Controller(object):
def view_item(self, *args, **kwargs):
buffer = self.get_current_buffer()
if buffer.type == "baseBuffer" or buffer.type == "favourites_timeline" or buffer.type == "list" or buffer.type == "search":
tweet, tweetsList = buffer.get_full_tweet()
msg = messages.viewTweet(tweet, tweetsList)
elif buffer.type == "account" or buffer.type == "empty":
if buffer.type == "account" or buffer.type == "empty":
return
elif buffer.name == "sent_tweets":
elif buffer.type == "baseBuffer" or buffer.type == "favourites_timeline" or buffer.type == "list" or buffer.type == "search":
tweet, tweetsList = buffer.get_full_tweet()
msg = messages.viewTweet(tweet, tweetsList)
msg = messages.viewTweet(tweet, tweetsList, utc_offset=buffer.session.db["utc_offset"])
elif buffer.type == "dm":
non_tweet = buffer.get_formatted_message()
item = buffer.get_right_tweet()
original_date = arrow.get(item["created_timestamp"][:-3])
date = original_date.replace(seconds=buffer.session.db["utc_offset"]).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
msg = messages.viewTweet(non_tweet, [], False, date=date)
else:
non_tweet = buffer.get_formatted_message()
msg = messages.viewTweet(non_tweet, [], False)
def view_in_browser(self, *args, **kwargs):
buffer = self.get_current_buffer()
if hasattr(buffer, "open_in_browser"):
buffer.open_in_browser()
def open_favs_timeline(self, *args, **kwargs):
self.open_timeline(default="favourites")
@@ -839,7 +832,7 @@ class Controller(object):
users = utils.get_all_users(tweet, buff.session.db)
dlg = dialogs.userSelection.selectUserDialog(users=users, default=default)
if dlg.get_response() == widgetUtils.OK:
usr = utils.if_user_exists(buff.session.twitter.twitter, dlg.get_user())
usr = utils.if_user_exists(buff.session.twitter, dlg.get_user())
if usr != None:
if usr == dlg.get_user():
commonMessageDialogs.suspended_user()
@@ -848,9 +841,6 @@ class Controller(object):
if usr["following"] == False:
commonMessageDialogs.no_following()
return
if application.streaming_lives():
answer = commonMessageDialogs.protected_user()
if answer == widgetUtils.NO: return
tl_type = dlg.get_action()
if tl_type == "tweets":
if usr["statuses_count"] == 0:
@@ -859,7 +849,7 @@ class Controller(object):
if usr["id_str"] in buff.session.settings["other_buffers"]["timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (usr["id_str"],), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr["id_str"], tweet_mode="extended")
tl = twitterBuffers.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (usr["id_str"],), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr["id_str"], tweet_mode="extended")
try:
tl.start_stream(play_sound=False)
except TwythonAuthError:
@@ -870,7 +860,6 @@ class Controller(object):
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(dlg.get_user()), pos=pos)
buff.session.settings["other_buffers"]["timelines"].append(usr["id_str"])
pub.sendMessage("buffer-title-changed", buffer=tl)
pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
buff.session.sound.play("create_timeline.ogg")
elif tl_type == "favourites":
if usr["favourites_count"] == 0:
@@ -879,7 +868,7 @@ class Controller(object):
if usr["id_str"] in buff.session.settings["other_buffers"]["favourites_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (usr["id_str"],), buff.session, buff.session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr["id_str"], tweet_mode="extended")
tl = twitterBuffers.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (usr["id_str"],), buff.session, buff.session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr["id_str"], tweet_mode="extended")
try:
tl.start_stream(play_sound=False)
except TwythonAuthError:
@@ -898,7 +887,7 @@ class Controller(object):
if usr["id_str"] in buff.session.settings["other_buffers"]["followers_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (usr["id_str"],), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr["id_str"])
tl = twitterBuffers.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (usr["id_str"],), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr["id_str"])
try:
tl.start_stream(play_sound=False)
except TwythonAuthError:
@@ -917,7 +906,7 @@ class Controller(object):
if usr["id_str"] in buff.session.settings["other_buffers"]["friends_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (usr["id_str"],), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr["id_str"])
tl = twitterBuffers.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (usr["id_str"],), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr["id_str"])
try:
tl.start_stream(play_sound=False)
except TwythonAuthError:
@@ -937,11 +926,10 @@ class Controller(object):
buffer = self.get_current_buffer()
id = buffer.get_right_tweet()["id_str"]
user = buffer.get_right_tweet()["user"]["screen_name"]
search = buffersController.conversationBufferController(self.view.nb, "search", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,))
search = twitterBuffers.conversationBufferController(self.view.nb, "search", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,))
search.tweet = buffer.get_right_tweet()
search.start_stream(start=True)
pos=self.view.search("searches", buffer.session.db["user_name"])
# self.buffers.append(search)
self.insert_buffer(search, pos)
self.view.insert_buffer(search.buffer, name=_(u"Conversation with {0}").format(user), pos=pos)
@@ -965,7 +953,7 @@ class Controller(object):
if trends.dialog.get_response() == widgetUtils.OK:
woeid = trends.get_woeid()
if woeid in buff.session.settings["other_buffers"]["trending_topic_buffers"]: return
buffer = buffersController.trendsBufferController(self.view.nb, "%s_tt" % (woeid,), buff.session, buff.account, woeid, sound="trends_updated.ogg")
buffer = twitterBuffers.trendsBufferController(self.view.nb, "%s_tt" % (woeid,), buff.session, buff.account, woeid, sound="trends_updated.ogg")
buffer.searchfunction = self.search
pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"])
self.view.insert_buffer(buffer.buffer, name=_(u"Trending topics for %s") % (trends.get_string()), pos=pos)
@@ -980,7 +968,7 @@ class Controller(object):
if tweet["coordinates"] != None:
x = tweet["coordinates"]["coordinates"][0]
y = tweet["coordinates"]["coordinates"][1]
address = geocoder.reverse_geocode(y, x)
address = geocoder.reverse_geocode(y, x, language = languageHandler.curLang)
if event == None: output.speak(address[0].__str__().decode("utf-8"))
else: self.view.show_address(address[0].__str__().decode("utf-8"))
else:
@@ -1000,7 +988,7 @@ class Controller(object):
if tweet["coordinates"] != None:
x = tweet["coordinates"]["coordinates"][0]
y = tweet["coordinates"]["coordinates"][1]
address = geocoder.reverse_geocode(y, x)
address = geocoder.reverse_geocode(y, x, language = languageHandler.curLang)
dlg = commonMessageDialogs.view_geodata(address[0].__str__())
else:
output.speak(_(u"There are no coordinates in this tweet"))
@@ -1270,32 +1258,6 @@ class Controller(object):
if message != None:
output.speak(message, speech=session.settings["reporting"]["speech_reporting"], braille=session.settings["reporting"]["braille_reporting"])
def manage_home_timelines(self, data, user):
buffer = self.search_buffer("home_timeline", user)
if buffer == None: return
play_sound = "tweet_received.ogg"
if "home_timeline" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, play_sound=play_sound)
buffer.add_new_item(data)
def manage_mentions(self, data, user):
buffer = self.search_buffer("mentions", user)
if buffer == None: return
play_sound = "mention_received.ogg"
message = _(u"One mention from %s ") % (data["user"]["name"])
if "mentions" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, play_sound=play_sound, message=message)
buffer.add_new_item(data)
def manage_direct_messages(self, data, user):
buffer = self.search_buffer("direct_messages", user)
if buffer == None: return
play_sound = "dm_received.ogg"
message = _(u"New direct message")
if "direct_messages" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, play_sound=play_sound, message=message)
buffer.add_new_item(data)
def manage_sent_dm(self, data, user):
buffer = self.search_buffer("sent_direct_messages", user)
if buffer == None: return
@@ -1307,38 +1269,27 @@ class Controller(object):
def manage_sent_tweets(self, data, user):
buffer = self.search_buffer("sent_tweets", user)
if buffer == None: return
play_sound = "tweet_send.ogg"
if "sent_tweets" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, play_sound=play_sound)
buffer.add_new_item(data)
def manage_events(self, data, user):
buffer = self.search_buffer("events", user)
if buffer == None: return
play_sound = "new_event.ogg"
if "events" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, play_sound=play_sound)
buffer.add_new_item(data)
def manage_followers(self, data, user):
buffer = self.search_buffer("followers", user)
if buffer == None: return
play_sound = "update_followers.ogg"
if "followers" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, play_sound=play_sound)
# if "sent_tweets" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
# self.notify(buffer.session, play_sound=play_sound)
data = buffer.session.check_quoted_status(data)
data = buffer.session.check_long_tweet(data)
if data == False: # Long tweet deleted from twishort.
return
if buffer.session.settings["general"]["reverse_timelines"] == False:
buffer.session.db[buffer.name].append(data)
else:
buffer.session.db[buffer.name].insert(0, data)
buffer.add_new_item(data)
def manage_friend(self, data, user):
buffer = self.search_buffer("friends", user)
if buffer == None: return
buffer.add_new_item(data)
pub.sendMessage("restart-streams", streams=["main_stream"], session=buffer.session)
def manage_unfollowing(self, item, user):
buffer = self.search_buffer("friends", user)
if buffer == None: return
buffer.remove_item(item)
pub.sendMessage("restart-streams", streams=["main_stream"], session=buffer.session)
def manage_favourite(self, data, user):
buffer = self.search_buffer("favourites", user)
@@ -1357,29 +1308,11 @@ class Controller(object):
buffer = self.search_buffer("blocked", user)
if buffer == None: return
buffer.add_new_item(data)
pub.sendMessage("restart-streams", streams=["main_stream"], session=buffer.session)
def manage_unblocked_user(self, item, user):
buffer = self.search_buffer("blocked", user)
if buffer == None: return
buffer.remove_item(item)
pub.sendMessage("restart-streams", streams=["main_stream"], session=buffer.session)
def manage_item_in_timeline(self, data, user, who):
buffer = self.search_buffer("%s-timeline" % (who,), user)
if buffer == None: return
play_sound = "tweet_timeline.ogg"
if "%s-timeline" % (who,) not in buffer.session.settings["other_buffers"]["muted_buffers"] and buffer.session.settings["sound"]["session_mute"] == False:
self.notify(buffer.session, play_sound=play_sound, message=_(u"One tweet from %s") % (data["user"]["name"]))
buffer.add_new_item(data)
def manage_item_in_list(self, data, user, where):
buffer = self.search_buffer("%s" % (where,), user)
if buffer == None: return
play_sound = "list_tweet.ogg"
if "%s" % (where,) not in buffer.session.settings["other_buffers"]["muted_buffers"] and buffer.session.settings["sound"]["session_mute"] == False:
self.notify(buffer.session, play_sound=play_sound, message=_(u"One tweet from %s") % (data["user"]["name"]))
buffer.add_new_item(data)
def start_buffers(self, session):
log.debug("starting buffers... Session %s" % (session.session_id,))
@@ -1406,37 +1339,18 @@ class Controller(object):
continue
if change_title:
pub.sendMessage("buffer-title-changed", buffer=i)
if application.streaming_lives():
log.debug("Starting the streaming endpoint")
session.start_streaming()
def set_positions(self):
for i in session_.sessions:
for i in sessions.sessions:
self.set_buffer_positions(i)
def manage_stream_errors(self, session):
log.error(" Restarting %s session streams. It will be destroyed" % (session,))
s = session_.sessions[session]
try:
if hasattr(s, "main_stream"):
s.main_stream.disconnect()
log.error("main stream disconnected")
del s.main_stream
if hasattr(s, "timelinesStream"):
s.timelinesStream.disconnect()
del s.timelinesStream
except AttributeError:
pass
s.counter = 0
# s.listen_stream_error()
def check_connection(self):
if self.started == False:
return
for i in session_.sessions:
for i in sessions.sessions:
try:
if session_.sessions[i].is_logged == False: continue
session_.sessions[i].check_connection()
if sessions.sessions[i].is_logged == False: continue
sessions.sessions[i].check_connection()
except TwythonError: # We shouldn't allow this function to die.
pass
@@ -1444,32 +1358,32 @@ class Controller(object):
buff = self.search_buffer("home_timeline", account)
if create == True:
if buffer == "favourites":
favourites = buffersController.baseBufferController(self.view.nb, "get_favorites", "favourites", buff.session, buff.session.db["user_name"])
favourites = twitterBuffers.baseBufferController(self.view.nb, "get_favorites", "favourites", buff.session, buff.session.db["user_name"], tweet_mode="extended")
self.buffers.append(favourites)
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
favourites.start_stream(play_sound=False)
if buffer == "followers":
followers = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "followers", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
followers = twitterBuffers.peopleBufferController(self.view.nb, "get_followers_list", "followers", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
self.buffers.append(followers)
self.view.insert_buffer(followers.buffer, name=_(u"Followers"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
followers.start_stream(play_sound=False)
elif buffer == "friends":
friends = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "friends", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
friends = twitterBuffers.peopleBufferController(self.view.nb, "get_friends_list", "friends", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
self.buffers.append(friends)
self.view.insert_buffer(friends.buffer, name=_(u"Friends"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
friends.start_stream(play_sound=False)
elif buffer == "blocked":
blocks = buffersController.peopleBufferController(self.view.nb, "list_blocks", "blocked", buff.session, buff.session.db["user_name"])
blocks = twitterBuffers.peopleBufferController(self.view.nb, "list_blocks", "blocked", buff.session, buff.session.db["user_name"])
self.buffers.append(blocks)
self.view.insert_buffer(blocks.buffer, name=_(u"Blocked users"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
blocks.start_stream(play_sound=False)
elif buffer == "muted":
muted = buffersController.peopleBufferController(self.view.nb, "get_muted_users_list", "muted", buff.session, buff.session.db["user_name"])
muted = twitterBuffers.peopleBufferController(self.view.nb, "get_muted_users_list", "muted", buff.session, buff.session.db["user_name"])
self.buffers.append(muted)
self.view.insert_buffer(muted.buffer, name=_(u"Muted users"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
muted.start_stream(play_sound=False)
elif buffer == "events":
events = buffersController.eventsBufferController(self.view.nb, "events", buff.session, buff.session.db["user_name"], bufferType="dmPanel", screen_name=buff.session.db["user_name"])
events = twitterBuffers.eventsBufferController(self.view.nb, "events", buff.session, buff.session.db["user_name"], bufferType="dmPanel", screen_name=buff.session.db["user_name"])
self.buffers.append(events)
self.view.insert_buffer(events.buffer, name=_(u"Events"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
elif create == False:
@@ -1478,7 +1392,7 @@ class Controller(object):
if create in buff.session.settings["other_buffers"]["lists"]:
output.speak(_(u"This list is already opened"), True)
return
tl = buffersController.listBufferController(self.view.nb, "get_list_statuses", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]))
tl = twitterBuffers.listBufferController(self.view.nb, "get_list_statuses", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]), tweet_mode="extended")
buff.session.lists.append(tl)
pos=self.view.search("lists", buff.session.db["user_name"])
self.insert_buffer(tl, pos)
@@ -1486,13 +1400,6 @@ class Controller(object):
tl.start_stream(play_sound=False)
buff.session.settings["other_buffers"]["lists"].append(create)
buff.session.settings.write()
pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
def restart_streams(self, streams=[], session=None):
for i in streams:
log.debug("Restarting the %s stream" % (i,))
session.remove_stream(i)
session.check_connection()
def invisible_shorcuts_changed(self, registered):
if registered == True:
@@ -1505,22 +1412,51 @@ class Controller(object):
def about(self, *args, **kwargs):
self.view.about_dialog()
def get_soundpacks(self, *args, **kwargs):
# This should redirect users of other languages to the right version of the TWBlue website.
lang = languageHandler.curLang[:2]
url = application.url
final_url = "{0}/{1}/soundpacks".format(url, lang)
try:
response = requests.get(final_url)
except:
output.speak(_(u"An error happened while trying to connect to the server. Please try later."))
return
# There is no twblue.es/en, so if English is the language used this should be False anyway.
if response.status_code == 200 and lang != "en":
webbrowser.open_new_tab(final_url)
else:
webbrowser.open_new_tab(application.url+"/soundpacks")
def visit_website(self, *args, **kwargs):
webbrowser.open(application.url)
# This should redirect users of other languages to the right version of the TWBlue website.
lang = languageHandler.curLang[:2]
url = application.url
final_url = "{0}/{1}".format(url, lang)
try:
response = requests.get(final_url)
except:
output.speak(_(u"An error happened while trying to connect to the server. Please try later."))
return
# There is no twblue.es/en, so if English is the language used this should be False anyway.
if response.status_code == 200 and lang != "en":
webbrowser.open_new_tab(final_url)
else:
webbrowser.open_new_tab(application.url)
def manage_accounts(self, *args, **kwargs):
sm = sessionManager.sessionManagerController(started=True)
sm.fill_list()
sm.show()
for i in sm.new_sessions:
self.create_buffers(session_.sessions[i])
call_threaded(self.start_buffers, session_.sessions[i])
self.create_buffers(sessions.sessions[i])
call_threaded(self.start_buffers, sessions.sessions[i])
for i in sm.removed_sessions:
if session_.sessions[i].logged == True:
self.logout_account(session_.sessions[i].session_id)
self.destroy_buffer(session_.sessions[i].settings["twitter"]["user_name"], session_.sessions[i].settings["twitter"]["user_name"])
self.accounts.remove(session_.sessions[i].settings["twitter"]["user_name"])
session_.sessions.pop(i)
if sessions.sessions[i].logged == True:
self.logout_account(sessions.sessions[i].session_id)
self.destroy_buffer(sessions.sessions[i].settings["twitter"]["user_name"], sessions.sessions[i].settings["twitter"]["user_name"])
self.accounts.remove(sessions.sessions[i].settings["twitter"]["user_name"])
sessions.sessions.pop(i)
def update_profile(self, *args, **kwargs):
r = user.profileController(self.get_best_buffer().session)
@@ -1614,16 +1550,10 @@ class Controller(object):
return
else:
output.speak(_(u"Updating buffer..."))
n = bf.start_stream(mandatory=True)
n = bf.start_stream(mandatory=True, avoid_autoreading=True)
if n != None:
output.speak(_(u"{0} items retrieved").format(n,))
def on_tweet_deleted(self, data):
id = data["delete"]["status"]["id"]
for i in self.buffers:
if hasattr(i, "remove_tweet") and hasattr(i, "name"):
i.remove_tweet(id)
def buffer_title_changed(self, buffer):
if "-timeline" in buffer.name:
title = _(u"Timeline for {}").format(buffer.username,)
@@ -1642,7 +1572,7 @@ class Controller(object):
output.speak(_(u"Invalid buffer"))
return
tweet = buffer.get_tweet()
if tweet.has_key("entities") == False or tweet["entities"].has_key("media") == False:
if ("entities" in tweet) == False or ("media" in tweet["entities"]) == False:
output.speak(_(u"This tweet doesn't contain images"))
return
if len(tweet["entities"]["media"]) > 1:
@@ -1678,5 +1608,5 @@ class Controller(object):
sent_dms.put_more_items(data)
def save_data_in_db(self):
for i in session_.sessions:
session_.sessions[i].shelve()
for i in sessions.sessions:
sessions.sessions[i].shelve()

View File

@@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import re
import platform
import attach
from . import attach
import arrow
import languageHandler
system = platform.system()
import widgetUtils
import output
@@ -17,7 +20,7 @@ if system == "Windows":
from extra.AudioUploader import audioUploader
elif system == "Linux":
from gtkUI.dialogs import message
from twitter import utils
from sessions.twitter import utils
class basicTweet(object):
""" This class handles the tweet main features. Other classes should derive from this class."""
@@ -193,7 +196,7 @@ class dm(basicTweet):
c.show_menu("dm")
class viewTweet(basicTweet):
def __init__(self, tweet, tweetList, is_tweet=True):
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date=""):
""" 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.
@@ -204,59 +207,61 @@ class viewTweet(basicTweet):
text = ""
for i in xrange(0, len(tweetList)):
# tweets with message keys are longer tweets, the message value is the full messaje taken from twishort.
if tweetList[i].has_key("message") and tweetList[i]["is_quote_status"] == False:
if "message" in tweetList[i] and tweetList[i]["is_quote_status"] == False:
value = "message"
else:
value = "full_text"
if tweetList[i].has_key("retweeted_status") and tweetList[i]["is_quote_status"] == False:
if tweetList[i].has_key("message") == False:
if "retweeted_status" in tweetList[i] and tweetList[i]["is_quote_status"] == False:
if ("message" in tweetList[i]) == False:
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"], tweetList[i][value])
else:
text = text + " @%s: %s\n" % (tweetList[i]["user"]["screen_name"], tweetList[i][value])
# tweets with extended_entities could include image descriptions.
if tweetList[i].has_key("extended_entities") and tweetList[i]["extended_entities"].has_key("media"):
if "extended_entities" in tweetList[i] and "media" in tweetList[i]["extended_entities"]:
for z in tweetList[i]["extended_entities"]["media"]:
if z.has_key("ext_alt_text") and z["ext_alt_text"] != None:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
if tweetList[i].has_key("retweeted_status") and tweetList[i]["retweeted_status"].has_key("extended_entities") and tweetList[i]["retweeted_status"]["extended_entities"].has_key("media"):
if "retweeted_status" in tweetList[i] and "extended_entities" in tweetList[i]["retweeted_status"] and "media" in tweetList[i]["retweeted_status"]["extended_entities"]:
for z in tweetList[i]["retweeted_status"]["extended_entities"]["media"]:
if z.has_key("ext_alt_text") and z["ext_alt_text"] != None:
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 = str(re.sub(r"(?s)<.*?>", "", tweet["source"].encode("utf-8")))
original_date = arrow.get(tweet["created_at"], "ddd MMM DD H:m:s Z YYYY", locale="en")
date = original_date.replace(seconds=utc_offset).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
if text == "":
if tweet.has_key("message"):
if "message" in tweet:
value = "message"
else:
value = "full_text"
if tweet.has_key("retweeted_status"):
if tweet.has_key("message") == False:
if "retweeted_status" in tweet:
if ("message" in tweet) == False:
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"], tweet[value])
else:
text = tweet[value]
text = self.clear_text(text)
if tweet.has_key("extended_entities") and tweet["extended_entities"].has_key("media"):
if "extended_entities" in tweet and "media" in tweet["extended_entities"]:
for z in tweet["extended_entities"]["media"]:
if z.has_key("ext_alt_text") and z["ext_alt_text"] != None:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
if tweet.has_key("retweeted_status") and tweet["retweeted_status"].has_key("extended_entities") and tweet["retweeted_status"]["extended_entities"].has_key("media"):
if "retweeted_status" in tweet and "extended_entities" in tweet["retweeted_status"] and "media" in tweet["retweeted_status"]["extended_entities"]:
for z in tweet["retweeted_status"]["extended_entities"]["media"]:
if z.has_key("ext_alt_text") and z["ext_alt_text"] != None:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
self.message = message.viewTweet(text, rt_count, favs_count, source.decode("utf-8"))
self.message = message.viewTweet(text, rt_count, favs_count, source.decode("utf-8"), date)
self.message.set_title(len(text))
[self.message.set_image_description(i) for i in image_description]
else:
self.title = _(u"View item")
text = tweet
self.message = message.viewNonTweet(text)
self.message = message.viewNonTweet(text, date)
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
if self.contain_urls() == True:

View File

@@ -18,7 +18,7 @@ import config_utils
log = logging.getLogger("Settings")
import keys
from collections import OrderedDict
from platform_utils.autostart import windows as autostart_windows
from mysc import autostart as autostart_windows
class globalSettingsController(object):
def __init__(self):
@@ -138,8 +138,6 @@ class accountSettingsController(globalSettingsController):
widgetUtils.connect_event(self.dialog.general.au, widgetUtils.BUTTON_PRESSED, self.manage_autocomplete)
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"])
if application.streaming_lives():
self.dialog.set_value("general", "apiCalls", self.config["general"]["max_api_calls"])
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"]
@@ -191,8 +189,6 @@ class accountSettingsController(globalSettingsController):
self.needs_restart = True
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")
if application.streaming_lives():
self.config["general"]["max_api_calls"] = self.dialog.get_value("general", "apiCalls")
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
if self.dialog.get_value("general", "persist_size") == '':
@@ -215,7 +211,7 @@ class accountSettingsController(globalSettingsController):
else:
self.config["general"]["retweet_mode"] = "comment"
buffers_list = self.dialog.buffers.get_list()
if set(self.config["general"]["buffer_order"]) != set(buffers_list) or buffers_list != self.config["general"]["buffer_order"]:
if buffers_list != self.config["general"]["buffer_order"]:
self.needs_restart = True
self.config["general"]["buffer_order"] = buffers_list
self.config["reporting"]["speech_reporting"] = self.dialog.get_value("reporting", "speech_reporting")
@@ -293,14 +289,16 @@ class accountSettingsController(globalSettingsController):
all_buffers['friends']=_(u"Friends")
all_buffers['blocks']=_(u"Blocked users")
all_buffers['muted']=_(u"Muted users")
if application.streaming_lives():
all_buffers['events']=_(u"Events")
list_buffers = []
hidden_buffers=[]
for i in all_buffers.keys():
if i in self.config["general"]["buffer_order"]:
all_buffers_keys = 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))
else:
# 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

View File

@@ -8,7 +8,7 @@ class trendingTopicsController(object):
self.countries = {}
self.cities = {}
self.dialog = trends.trendingTopicsDialog()
self.information = session.twitter.twitter.get_available_trends()
self.information = session.twitter.get_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)

View File

@@ -41,9 +41,9 @@ class profileController(object):
self.do_update()
def get_data(self, screen_name):
self.data = self.session.twitter.twitter.show_user(screen_name=screen_name)
self.data = self.session.twitter.show_user(screen_name=screen_name)
if screen_name != self.session.db["user_name"]:
self.friendship_status = self.session.twitter.twitter.show_friendship(source_screen_name=self.session.db["user_name"], target_screen_name=screen_name)
self.friendship_status = self.session.twitter.show_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"])
@@ -81,11 +81,11 @@ class profileController(object):
url = self.dialog.get("url")
if self.file != None:
try:
self.session.twitter.twitter.update_profile_image(image=self.file)
self.session.twitter.update_profile_image(image=self.file)
except TwythonError as e:
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
try:
self.session.twitter.twitter.update_profile(name=name, description=description, location=location, url=url)
self.session.twitter.update_profile(name=name, description=description, location=location, url=url)
except TwythonError as e:
output.speak(u"Error %s. %s" % (e.error_code, e.msg))

View File

@@ -29,49 +29,49 @@ class userActionsController(object):
def follow(self, user):
try:
self.session.twitter.twitter.create_friendship(screen_name=user )
self.session.twitter.create_friendship(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def unfollow(self, user):
try:
id = self.session.twitter.twitter.destroy_friendship(screen_name=user )
id = self.session.twitter.destroy_friendship(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def mute(self, user):
try:
id = self.session.twitter.twitter.create_mute(screen_name=user )
id = self.session.twitter.create_mute(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def unmute(self, user):
try:
id = self.session.twitter.twitter.destroy_mute(screen_name=user )
id = self.session.twitter.destroy_mute(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def report(self, user):
try:
id = self.session.twitter.twitter.report_spam(screen_name=user )
id = self.session.twitter.report_spam(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def block(self, user):
try:
id = self.session.twitter.twitter.create_block(screen_name=user )
id = self.session.twitter.create_block(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def unblock(self, user):
try:
id = self.session.twitter.twitter.destroy_block(screen_name=user )
id = self.session.twitter.destroy_block(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def ignore_client(self, user):
tweet = self.buffer.get_right_tweet()
if tweet.has_key("sender"):
if "sender" in tweet:
output.speak(_(u"You can't ignore direct messages"))
return
client = re.sub(r"(?s)<.*?>", "", tweet["source"])

View File

@@ -16,10 +16,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
from __future__ import absolute_import
import widgetUtils
import wx_ui
import wx_transfer_dialogs
import transfer
from . import wx_ui
from . import wx_transfer_dialogs
from . import transfer
import output
import tempfile
import sound

View File

@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import sys
import threading
import time
import logging
from utils import convert_bytes
from .utils import convert_bytes
from pubsub import pub
log = logging.getLogger("extra.AudioUploader.transfer")
from requests_toolbelt.multipart.encoder import MultipartEncoder, MultipartEncoderMonitor
@@ -75,9 +76,9 @@ class Upload(object):
data = self.response.json()
except:
return _("Error in file upload: {0}").format(self.data.content,)
if data.has_key("url") and data["url"] != "0":
if "url" in data and data["url"] != "0":
return data["url"]
elif data.has_key("error") and data["error"] != "0":
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,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import wx
from utils import *
from .utils import *
import widgetUtils
class UploadDialog(widgetUtils.BaseDialog):

View File

@@ -1 +1,2 @@
from soundsTutorial import soundsTutorial
from __future__ import absolute_import
from .soundsTutorial import soundsTutorial

View File

@@ -1,15 +1,16 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import platform
import widgetUtils
import os
import paths
import logging
log = logging.getLogger("extra.SoundsTutorial.soundsTutorial")
import soundsTutorial_constants
from . import soundsTutorial_constants
if platform.system() == "Windows":
import wx_ui as UI
from . import wx_ui as UI
elif platform.system() == "Linux":
import gtk_ui as UI
from . import gtk_ui as UI
class soundsTutorial(object):
def __init__(self, sessionObject):

View File

@@ -1,5 +1,7 @@
#-*- coding: utf-8 -*-
import reverse_sort
from __future__ import absolute_import
#-*- coding: utf-8 -*-
from . import reverse_sort
import application
actions = reverse_sort.reverse_sort([ ("audio", _(u"Audio tweet.")),
("create_timeline", _(u"User timeline buffer created.")),

View File

@@ -1,4 +1,5 @@
import spellchecker
from __future__ import absolute_import
from . import spellchecker
import platform
if platform.system() == "Windows":
from wx_ui import *
from .wx_ui import *

View File

@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import os
import logging
import wx_ui
from . import wx_ui
import widgetUtils
import output
import config
import languageHandler
import enchant
import paths
import twitterFilter
from . import twitterFilter
from enchant.checker import SpellChecker
from enchant.errors import DictNotFoundError
from enchant import tokenize
@@ -52,7 +53,7 @@ class spellChecker(object):
def check(self):
try:
self.checker.next()
next(self.checker)
textToSay = _(u"Misspelled word: %s") % (self.checker.word,)
context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10))
self.dialog.set_title(textToSay)

View File

@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
import completion, settings
from __future__ import absolute_import
# -*- coding: utf-8 -*-
from . import completion, settings

View File

@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import output
import storage
import wx_menu
from . import storage
from . import wx_menu
class autocompletionUsers(object):
def __init__(self, window, session_id):

View File

@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
import storage
from __future__ import absolute_import
# -*- coding: utf-8 -*-
from . import storage
import widgetUtils
import wx_manage
from . import wx_manage
from wxUI import commonMessageDialogs
class autocompletionManage(object):

View File

@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
import storage
from __future__ import absolute_import
# -*- coding: utf-8 -*-
from . import storage
import widgetUtils
import wx_settings
import manage
from . import wx_settings
from . import manage
import output
from mysc.thread_utils import call_threaded

View File

@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
import OCRSpace
from __future__ import absolute_import
# -*- coding: utf-8 -*-
from . import OCRSpace

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import translator
from __future__ import absolute_import
from . import translator
import platform
if platform.system() == "Windows":
import wx_ui as gui
from . import wx_ui as gui

View File

@@ -16,7 +16,26 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import translator
from __future__ import absolute_import
# -*- 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 . import translator
import wx
from wxUI.dialogs import baseDialog

View File

@@ -1,15 +1,18 @@
# -*- coding: utf-8 -*-
""" This module contains some bugfixes for packages used in TWBlue."""
from __future__ import absolute_import
import sys
import fix_arrow # A few new locales for Three languages in arrow.
import fix_urllib3_warnings # Avoiding some SSL warnings related to Twython.
import fix_win32com
import fix_requests #fix cacert.pem location for TWBlue binary copies
from . import fix_arrow # A few new locales for Three languages in arrow.
from . import fix_libloader # Regenerates comcache properly.
from . import fix_urllib3_warnings # Avoiding some SSL warnings related to Twython.
from . import fix_win32com
from . import fix_requests #fix cacert.pem location for TWBlue binary copies
def setup():
fix_arrow.fix()
if hasattr(sys, "frozen"):
fix_win32com.fix()
fix_requests.fix(True)
fix_libloader.fix()
else:
fix_requests.fix(False)
fix_urllib3_warnings.fix()

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
import win32com
import paths
win32com.__gen_path__=paths.com_path()
import sys
import os
sys.path.append(os.path.join(win32com.__gen_path__, "."))
from win32com.client import gencache
from pywintypes import com_error
from libloader import com
fixed=False
def patched_getmodule(modname):
mod=__import__(modname)
return sys.modules[modname]
def load_com(*names):
global fixed
if fixed==False:
gencache._GetModule=patched_getmodule
com.prepare_gencache()
fixed=True
result = None
for name in names:
try:
result = gencache.EnsureDispatch(name)
break
except com_error:
continue
if result is None:
raise com_error("Unable to load any of the provided com objects.")
return result
def fix():
com.load_com = load_com

View File

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

View File

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

View File

@@ -39,18 +39,19 @@ copy_to_clipboard = string(default="alt+win+shift+c")
add_to_list = string(default="alt+win+a")
remove_from_list = string(default="alt+win+shift+a")
toggle_buffer_mute = string(default="alt+win+shift+m")
toggle_session_mute = string(default="alt+win+m")
toggle_session_mute = string(default="control+alt+win+m")
toggle_autoread = string(default="alt+win+e")
search = string(default="alt+win+-")
edit_keystrokes = string(default="alt+win+k")
view_user_lists = string(default="alt+win+l")
get_more_items = string(default="alt+win+pageup")
reverse_geocode = string(default="alt+win+g")
reverse_geocode = string(default="control+win+g")
view_reverse_geocode = string(default="alt+win+shift+g")
get_trending_topics = string(default="control+win+t")
check_for_updates = string(default="alt+win+u")
list_manager = string(default="alt+win+shift+l")
configuration = string(default="control+win+o")
configuration = string(default="control+win+alt+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+alt+shift+u")
ocr_image = string(default="win+alt+o")
ocr_image = string(default="win+alt+o")
open_in_browser = string(default="alt+control+win+return")

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from builtins import object
import application
import platform
import exceptions

View File

@@ -1 +1,2 @@
from keystrokeEditor import KeystrokeEditor
from __future__ import absolute_import
from .keystrokeEditor import KeystrokeEditor

View File

@@ -50,6 +50,7 @@ actions = {
"check_for_updates": _(u"Check and download updates"),
"lists_manager": _(u"Opens the list manager, which allows you to create, edit, delete and open lists in buffers."),
"configuration": _(u"Opens the global settings dialogue"),
"list_manager": _(u"Opens the list manager"),
"accountConfiguration": _(u"Opens the account settings dialogue"),
"audio": _(u"Try to play an audio file"),
"update_buffer": _(u"Updates the buffer and retrieves possible lost items there."),

View File

@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import widgetUtils
import config
import wx_ui
import constants
from . import wx_ui
from . import constants
from pubsub import pub
class KeystrokeEditor(object):

View File

@@ -32,7 +32,7 @@ class keystrokeEditorDialog(baseDialog.BaseWXDialog):
selection = self.keys.get_selected()
self.keys.clear()
for i in keystrokes:
if actions.has_key(i) == False:
if (i in actions) == False:
continue
action = actions[i]
self.actions.append(i)

View File

@@ -1,7 +0,0 @@
from .libloader import *
__version__ = 0.1
__author__ = 'Christopher Toth <q@q-continuum.net>'
__doc__ = """
Quickly and easily load shared libraries from various platforms. Also includes a libloader.com module for loading com modules on Windows.
"""

View File

@@ -1,35 +0,0 @@
from pywintypes import com_error
import win32com
import paths
win32com.__gen_path__=paths.com_path()
import sys
import os
sys.path.append(os.path.join(win32com.__gen_path__, "."))
from win32com.client import gencache
fixed=False
def prepare_gencache():
gencache.is_readonly = False
gencache.GetGeneratePath()
def patched_getmodule(modname):
mod=__import__(modname)
return sys.modules[modname]
def load_com(*names):
global fixed
if fixed==False:
gencache._GetModule=patched_getmodule
prepare_gencache()
fixed=True
result = None
for name in names:
try:
result = gencache.EnsureDispatch(name)
break
except com_error:
continue
if result is None:
raise com_error("Unable to load any of the provided com objects.")
return result

View File

@@ -1,56 +0,0 @@
import ctypes
import collections
import platform
import os
TYPES = {
'Linux': {
'loader': ctypes.CDLL,
'functype': ctypes.CFUNCTYPE,
'prefix': 'lib',
'extension': '.so'
},
'Darwin': {
'loader': ctypes.CDLL,
'functype': ctypes.CFUNCTYPE,
'prefix': 'lib',
'extension': '.dylib'
},
}
if platform.system() == 'Windows':
TYPES['Windows'] = {
'loader': ctypes.WinDLL,
'functype': ctypes.WINFUNCTYPE,
'prefix': "",
'extension': '.dll'
}
class LibraryLoadError(OSError): pass
def load_library(library, x86_path='.', x64_path='.', *args, **kwargs):
lib = find_library_path(library, x86_path=x86_path, x64_path=x64_path)
loaded = _do_load(str(lib), *args, **kwargs)
if loaded is not None:
return loaded
raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, lib))
def _do_load(file, *args, **kwargs):
loader = TYPES[platform.system()]['loader']
return loader(file, *args, **kwargs)
def find_library_path(libname, x86_path='.', x64_path='.'):
libname = '%s%s' % (TYPES[platform.system()]['prefix'], libname)
if platform.architecture()[0] == '64bit':
path = os.path.join(x64_path, libname)
else:
path = os.path.join(x86_path, libname)
ext = get_library_extension()
path = '%s%s' % (path, ext)
return os.path.abspath(path)
def get_functype():
return TYPES[platform.system()]['functype']
def get_library_extension():
return TYPES[platform.system()]['extension']

View File

@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
import platform
from win32com.client import GetObject
""" there are lots of things not implemented for Gtk+ yet.
We've started this effort 1 Apr 2015 so it isn't fully functional. We will remove the ifs statements when are no needed"""
@@ -86,6 +88,7 @@ def setup():
if hasattr(sm.view, "destroy"):
sm.view.destroy()
del sm
check_pid()
r = mainController.Controller()
r.view.show()
r.do_work()
@@ -102,4 +105,26 @@ def donation():
webbrowser.open_new_tab(_("https://twblue.es/donate"))
config.app["app-settings"]["donation_dialog_displayed"] = True
def is_running(pid):
"Check if the process with ID pid is running. Adapted from https://stackoverflow.com/a/568589"
WMI = GetObject('winmgmts:')
processes = WMI.InstancesOf('Win32_Process')
return [process.Properties_('ProcessID').Value for process in processes if process.Properties_('ProcessID').Value == pid]
def check_pid():
"Insures that only one copy of the application is running at a time."
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
if os.path.exists(pidpath):
with open(pidpath) as fin:
pid = int(fin.read())
if is_running(pid):
# Display warning dialog
commonMessageDialogs.common_error(_(u"{0} is already running. Close the other instance before starting this one. If you're sure that {0} isn't running, try deleting the file at {1}. If you're unsure of how to do this, contact the {0} developers.").format(application.name, pidpath))
sys.exit(1)
else:
commonMessageDialogs.dead_pid()
# Write the new PID
with open(pidpath,"w") as cam:
cam.write(str(os.getpid()))
setup()

View File

@@ -1 +1,2 @@
import widgets
from __future__ import absolute_import
from . import widgets

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
""" A cross platform notification system.
Under Linux, the wx.NotificationMessage does not show a notification on the taskbar, so we decided to use dbus for showing notifications for linux and wx for Windows."""
from __future__ import absolute_import
import platform
notify = None
@@ -8,10 +9,10 @@ notify = None
def setup():
global notify
if platform.system() == "Windows":
import windows
from . import windows
notify = windows.notification()
elif platform.system() == "Linux":
import linux
from . import linux
notify = linux.notification()
def send(title, text):

View File

@@ -1,16 +0,0 @@
# Replacement for py2exe distributed module
# Avoids the use of the standard py2exe console.
# Just import this file and it should go away
import sys
if hasattr(sys,"frozen"): # true only if we are running as a py2exe app
class Blackhole(object):
def write(self,text):
pass
def flush(self):
pass
sys.stdout = Blackhole()
sys.stderr = Blackhole()
del Blackhole
del sys

View File

@@ -1,51 +0,0 @@
import ctypes
import collections
import platform
import os
TYPES = {
'Linux': {
'loader': ctypes.CDLL,
'functype': ctypes.CFUNCTYPE,
'prefix': 'lib',
'extension': '.so'
},
'Darwin': {
'loader': ctypes.CDLL,
'functype': ctypes.CFUNCTYPE,
'prefix': 'lib',
'extension': '.dylib'
},
}
if platform.system() == 'Windows':
TYPES['Windows'] = {
'loader': ctypes.WinDLL,
'functype': ctypes.WINFUNCTYPE,
'prefix': "",
'extension': '.dll'
}
class LibraryLoadError(Exception): pass
def load_library(library, x86_path='.', x64_path='.', *args, **kwargs):
lib = find_library_path(library, x86_path=x86_path, x64_path=x64_path)
loaded = _do_load(lib, *args, **kwargs)
if loaded is not None:
return loaded
raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, lib))
def _do_load(file, *args, **kwargs):
loader = TYPES[platform.system()]['loader']
return loader(file, *args, **kwargs)
def find_library_path(libname, x86_path='.', x64_path='.'):
libname = '%s%s' % (TYPES[platform.system()]['prefix'], libname)
if platform.machine() == 'x86_64':
path = os.path.join(x64_path, libname)
else:
path = os.path.join(x86_path, libname)
ext = TYPES[platform.system()]['extension']
return '%s%s' % (path, ext)
def get_functype():
return TYPES[platform.system()]['functype']

View File

@@ -1,114 +0,0 @@
import inspect
import platform
import os
import subprocess
import sys
import string
import unicodedata
def app_data_path(app_name=None):
"""Cross-platform method for determining where to put application data."""
"""Requires the name of the application"""
plat = platform.system()
if plat == 'Windows':
import winpaths
path = winpaths.get_appdata()
elif plat == 'Darwin':
path = os.path.join(os.path.expanduser('~'), 'Library', 'Application Support')
elif plat == 'Linux':
path = os.path.expanduser('~')
app_name = '.%s' % app_name.replace(' ', '_')
return os.path.join(path, app_name)
def prepare_app_data_path(app_name):
"""Creates the application's data directory, given its name."""
dir = app_data_path(app_name)
return ensure_path(dir)
def embedded_data_path():
if platform.system() == 'Darwin' and is_frozen():
return os.path.abspath(os.path.join(executable_directory(), '..', 'Resources'))
return app_path()
def is_frozen():
"""Return a bool indicating if application is compressed"""
import imp
return hasattr(sys, 'frozen') or imp.is_frozen("__main__")
def get_executable():
"""Returns the full executable path/name if frozen, or the full path/name of the main module if not."""
if is_frozen():
if platform.system() != 'Darwin':
return sys.executable
#On darwin, sys.executable points to python. We want the full path to the exe we ran.
exedir = os.path.abspath(os.path.dirname(sys.executable))
items = os.listdir(exedir)
items.remove('python')
return os.path.join(exedir, items[0])
#Not frozen
try:
import __main__
return os.path.abspath(__main__.__file__)
except AttributeError:
return sys.argv[0]
def get_module(level=2):
"""Hacky method for deriving the caller of this function's module."""
return inspect.getmodule(inspect.stack()[level][0]).__file__
def executable_directory():
"""Always determine the directory of the executable, even when run with py2exe or otherwise frozen"""
executable = get_executable()
path = os.path.abspath(os.path.dirname(executable))
return path
def app_path():
"""Return the root of the application's directory"""
path = executable_directory()
if is_frozen() and platform.system() == 'Darwin':
path = os.path.abspath(os.path.join(path, '..', '..'))
return path
def module_path(level=2):
return os.path.abspath(os.path.dirname(get_module(level)))
def documents_path():
"""On windows, returns the path to My Documents. On OSX, returns the user's Documents folder. For anything else, returns the user's home directory."""
plat = platform.system()
if plat == 'Windows':
import winpaths
path = winpaths.get_my_documents()
elif plat == 'Darwin':
path = os.path.join(os.path.expanduser('~'), 'Documents')
else:
path = os.path.expanduser('~')
return path
def safe_filename(filename):
"""Given a filename, returns a safe version with no characters that would not work on different platforms."""
SAFE_FILE_CHARS = "'-_.()[]{}!@#$%^&+=`~ "
filename = unicode(filename)
new_filename = ''.join(c for c in filename if c in SAFE_FILE_CHARS or c.isalnum())
#Windows doesn't like directory names ending in space, macs consider filenames beginning with a dot as hidden, and windows removes dots at the ends of filenames.
return new_filename.strip(' .')
def ensure_path(path):
if not os.path.exists(path):
os.makedirs(path)
return path
def start_file(path):
if platform.system() == 'Windows':
os.startfile(path)
else:
subprocess.Popen(['open', path])
def get_applications_path():
"""Return the directory where applications are commonly installed on the system."""
plat = platform.system()
if plat == 'Windows':
import winpaths
return winpaths.get_program_files()
elif plat == 'Darwin':
return '/Applications'

View File

@@ -1,27 +0,0 @@
import platform
import ctypes
import os
import signal
def kill_windows_process(pid):
PROCESS_TERMINATE = 1
SYNCHRONIZE=1048576
handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, False, pid)
ctypes.windll.kernel32.TerminateProcess(handle, -1)
ctypes.windll.kernel32.WaitForSingleObject(handle, 1000)
ctypes.windll.kernel32.CloseHandle(handle)
def kill_unix_process(pid):
try:
os.kill(pid, signal.SIGKILL)
except OSError:
pass
def kill_process(pid):
if pid < 0:
return
if platform.system() == 'Windows':
kill_windows_process(pid)
else:
kill_unix_process(pid)

View File

@@ -1,10 +0,0 @@
import _winreg
SHELL_REGKEY = ur"Directory\shell"
def context_menu_integrate(item_key_name, item_display_text, item_command):
app_menu_key = _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, SHELL_REGKEY, 0, _winreg.KEY_WRITE)
menu_item_key = _winreg.CreateKey(app_menu_key, item_key_name)
_winreg.SetValueEx(menu_item_key, None, None, _winreg.REG_SZ, item_display_text)
item_command_key = _winreg.CreateKey(menu_item_key, 'command')
_winreg.SetValueEx(item_command_key, None, None, _winreg.REG_SZ, item_command)

View File

@@ -1,9 +0,0 @@
import platform
import webbrowser
def open(url):
if platform.system() == 'Windows':
browser = webbrowser.get('windows-default')
else:
browser = webbrowser
browser.open_new_tab(url)

View File

@@ -5,7 +5,7 @@ import paths
import os
import logging
log = logging.getLogger("sessionmanager.manager")
import session_exceptions
from sessions import session_exceptions
manager = None
def setup():

View File

@@ -1,19 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import shutil
import widgetUtils
import platform
import output
if platform.system() == "Windows":
import wxUI as view
from . import wxUI as view
from controller import settings
elif platform.system() == "Linux":
import gtkUI as view
from . import gtkUI as view
import paths
import time
import os
import logging
import session
import manager
import sessions
from sessions.twitter import session
from . import manager
import config_utils
import config
@@ -76,12 +78,12 @@ class sessionManagerController(object):
def do_ok(self):
log.debug("Starting sessions...")
for i in self.sessions:
if session.sessions.has_key(i) == True: continue
if (i in sessions.sessions) == True: continue
s = session.Session(i)
s.get_configuration()
if i not in config.app["sessions"]["ignored_sessions"]:
s.login()
session.sessions[i] = s
sessions.sessions[i] = s
self.new_sessions[i] = s
# self.view.destroy()

View File

@@ -2,6 +2,7 @@
import wx
from multiplatform_widgets import widgets
import application
class sessionManagerWindow(wx.Dialog):
def __init__(self):
super(sessionManagerWindow, self).__init__(parent=None, title=_(u"Session manager"), size=wx.DefaultSize)
@@ -73,19 +74,4 @@ class sessionManagerWindow(wx.Dialog):
self.configuration.Hide()
def destroy(self):
self.Destroy()
class authorisationDialog(wx.Dialog):
def __init__(self):
super(authorisationDialog, self).__init__(parent=None, title=_(u"Authorising account..."))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
static = wx.StaticText(panel, wx.NewId(), _(u"Enter your PIN code here"))
self.text = wx.TextCtrl(panel, -1)
self.ok = wx.Button(panel, wx.ID_OK)
self.cancel = wx.Button(panel, wx.ID_CANCEL)
sizer.Add(self.text, 0, wx.ALL, 5)
sizer.Add(self.cancel, 0, wx.ALL, 5)
panel.SetSizer(sizer)
min = sizer.CalcMin()
self.SetClientSize(min)
self.Destroy()

5
src/sessions/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
""" this package contains code related to Sessions.
In TWBlue, a session module defines everything a social network needs to be used in the program."""
# let's define a global object for storing sessions across the program.
sessions = {}

117
src/sessions/base.py Normal file
View File

@@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
""" A base class to be derived in possible new sessions for TWBlue and services."""
from __future__ import absolute_import
import paths
import output
import time
import sound
import logging
import config_utils
import shelve
import application
import os
from . import session_exceptions as Exceptions
log = logging.getLogger("sessionmanager.session")
class baseSession(object):
""" toDo: Decorators does not seem to be working when using them in an inherited class."""
# Decorators.
def _require_login(fn):
""" Decorator for checking if the user is logged in.
Some functions may need this to avoid making unneeded calls."""
def f(self, *args, **kwargs):
if self.logged == True:
fn(self, *args, **kwargs)
else:
raise Exceptions.NotLoggedSessionError("You are not logged in yet.")
return f
def _require_configuration(fn):
""" Check if the user has a configured session."""
def f(self, *args, **kwargs):
if self.settings != None:
fn(self, *args, **kwargs)
else:
raise Exceptions.NotConfiguredSessionError("Not configured.")
return f
def __init__(self, session_id):
""" session_id (str): The name of the folder inside the config directory where the session is located."""
super(baseSession, self).__init__()
self.session_id = session_id
self.logged = False
self.settings = None
self.db={}
@property
def is_logged(self):
return self.logged
def get_configuration(self):
""" Get settings for a session."""
file_ = "%s/session.conf" % (self.session_id,)
log.debug("Creating config file %s" % (file_,))
self.settings = config_utils.load_config(paths.config_path(file_), paths.app_path("Conf.defaults"))
self.init_sound()
self.deshelve()
def init_sound(self):
try: self.sound = sound.soundSystem(self.settings["sound"])
except: pass
@_require_configuration
def login(self, verify_credentials=True):
pass
@_require_configuration
def authorise(self):
pass
def shelve(self):
"""Shelve the database to allow for persistance."""
shelfname=paths.config_path(str(self.session_id)+"/cache.db")
if self.settings["general"]["persist_size"] == 0:
if os.path.exists(shelfname):
os.remove(shelfname)
return
try:
if not os.path.exists(shelfname):
output.speak("Generating database, this might take a while.",True)
shelf=shelve.open(paths.config_path(shelfname),'c')
for key,value in self.db.items():
if type(key) != str and type(key) != unicode:
output.speak("Uh oh, while shelving the database, a key of type " + str(type(key)) + " has been found. It will be converted to type str, but this will cause all sorts of problems on deshelve. Please bring this to the attention of the " + application.name + " developers immediately. More information about the error will be written to the error log.",True)
log.error("Uh oh, " + str(key) + " is of type " + str(type(key)) + "!")
# Convert unicode objects to UTF-8 strings before shelve these objects.
if type(value) == list and self.settings["general"]["persist_size"] != -1 and len(value) > self.settings["general"]["persist_size"]:
shelf[str(key.encode("utf-8"))]=value[self.settings["general"]["persist_size"]:]
else:
shelf[str(key.encode("utf-8"))]=value
shelf.close()
except:
output.speak("An exception occurred while shelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
log.exception("Exception while shelving" + shelfname)
os.remove(shelfname)
def deshelve(self):
"""Import a shelved database."""
shelfname=paths.config_path(str(self.session_id)+"/cache.db")
if self.settings["general"]["persist_size"] == 0:
if os.path.exists(shelfname):
os.remove(shelfname)
return
try:
shelf=shelve.open(paths.config_path(shelfname),'c')
for key,value in shelf.items():
self.db[key]=value
shelf.close()
except:
output.speak("An exception occurred while deshelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
log.exception("Exception while deshelving" + shelfname)
try:
os.remove(shelfname)
except:
pass

View File

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

View File

@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import platform
system = platform.system()
import utils
from . import utils
import re
import htmlentitydefs
import time
@@ -10,7 +11,7 @@ import languageHandler
import arrow
import logging
import config
from long_tweets import twishort, tweets
from .long_tweets import twishort, tweets
log = logging.getLogger("compose")
def StripChars(s):
@@ -38,13 +39,13 @@ def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=No
ts = original_date.replace(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.getLanguage())
else:
ts = tweet["created_at"]
if tweet.has_key("message"):
if "message" in tweet:
value = "message"
elif tweet.has_key("full_text"):
elif "full_text" in tweet:
value = "full_text"
else:
value = "text"
if tweet.has_key("retweeted_status") and value != "message":
if "retweeted_status" in tweet and value != "message":
text = StripChars(tweet["retweeted_status"][value])
else:
text = StripChars(tweet[value])
@@ -53,16 +54,16 @@ def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=No
else:
user = tweet["user"]["name"]
source = re.sub(r"(?s)<.*?>", "", tweet["source"])
if tweet.has_key("retweeted_status"):
if tweet.has_key("message") == False and tweet["retweeted_status"]["is_quote_status"] == False:
if "retweeted_status" in tweet:
if ("message" in tweet) == False and tweet["retweeted_status"]["is_quote_status"] == False:
text = "RT @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], text)
elif tweet["retweeted_status"]["is_quote_status"]:
text = "%s" % (text)
else:
text = "RT @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], text)
if tweet.has_key("message") == False:
if ("message" in tweet) == False:
urls = utils.find_urls_in_text(text)
if tweet.has_key("retweeted_status"):
if "retweeted_status" in tweet:
for url in range(0, len(urls)):
try:
text = text.replace(urls[url], tweet["retweeted_status"]["entities"]["urls"][url]["expanded_url"])
@@ -75,35 +76,6 @@ def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=No
if config.app['app-settings']['handle_longtweets']: pass
return [user+", ", text, ts+", ", source]
def compose_dm(tweet, db, relative_times, show_screen_names=False, session=None):
""" It receives a tweet and returns a list with the user, text for the tweet or message, date and the client where user is."""
if system == "Windows":
original_date = arrow.get(tweet["created_at"], "ddd MMM DD H:m:s Z YYYY", locale="en")
if relative_times == True:
ts = original_date.humanize(locale=languageHandler.getLanguage())
else:
ts = original_date.replace(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.getLanguage())
else:
ts = tweet["created_at"]
text = StripChars(tweet["text"])
source = "DM"
if db["user_name"] == tweet["sender"]["screen_name"]:
if show_screen_names:
user = _(u"Dm to %s ") % (tweet["recipient"]["screen_name"],)
else:
user = _(u"Dm to %s ") % (tweet["recipient"]["name"],)
else:
if show_screen_names:
user = tweet["sender"]["screen_name"]
else:
user = tweet["sender"]["name"]
if text[-1] in chars: text=text+"."
urls = utils.find_urls_in_text(text)
for url in range(0, len(urls)):
try: text = text.replace(urls[url], tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
return [user+", ", text, ts+", ", source]
def compose_direct_message(item, db, relative_times, show_screen_names=False, session=None):
# for a while this function will be together with compose_dm.
# this one composes direct messages based on events (new API Endpoints).
@@ -139,14 +111,14 @@ def compose_direct_message(item, db, relative_times, show_screen_names=False, se
def compose_quoted_tweet(quoted_tweet, original_tweet, show_screen_names=False, session=None):
""" It receives a tweet and returns a list with the user, text for the tweet or message, date and the client where user is."""
if quoted_tweet.has_key("retweeted_status"):
if quoted_tweet["retweeted_status"].has_key("full_text"):
if "retweeted_status" in quoted_tweet:
if "full_text" in quoted_tweet["retweeted_status"]:
value = "full_text"
else:
value = "text"
text = StripChars(quoted_tweet["retweeted_status"][value])
else:
if quoted_tweet.has_key("full_text"):
if "full_text" in quoted_tweet:
value = "full_text"
else:
value = "text"
@@ -156,13 +128,13 @@ def compose_quoted_tweet(quoted_tweet, original_tweet, show_screen_names=False,
else:
quoting_user = quoted_tweet["user"]["name"]
source = re.sub(r"(?s)<.*?>", "", quoted_tweet["source"])
if quoted_tweet.has_key("retweeted_status"):
if "retweeted_status" in quoted_tweet:
text = "rt @%s: %s" % (quoted_tweet["retweeted_status"]["user"]["screen_name"], text)
if text[-1] in chars: text=text+"."
original_user = original_tweet["user"]["screen_name"]
if original_tweet.has_key("message"):
if "message" in original_tweet:
original_text = original_tweet["message"]
elif original_tweet.has_key("full_text"):
elif "full_text" in original_tweet:
original_text = StripChars(original_tweet["full_text"])
else:
original_text = StripChars(original_tweet["text"])
@@ -180,7 +152,7 @@ def compose_followers_list(tweet, db, relative_times=True, show_screen_names=Fal
ts = original_date.replace(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.getLanguage())
else:
ts = tweet["created_at"]
if tweet.has_key("status"):
if "status" in tweet:
if len(tweet["status"]) > 4 and system == "Windows":
original_date2 = arrow.get(tweet["status"]["created_at"], "ddd MMM D H:m:s Z YYYY", locale="en")
if relative_times:
@@ -193,58 +165,6 @@ def compose_followers_list(tweet, db, relative_times=True, show_screen_names=Fal
ts2 = _("Unavailable")
return [_(u"%s (@%s). %s followers, %s friends, %s tweets. Last tweeted %s. Joined Twitter %s") % (tweet["name"], tweet["screen_name"], tweet["followers_count"], tweet["friends_count"], tweet["statuses_count"], ts2, ts)]
def compose_event(data, username, show_screen_names=False, session=None):
if show_screen_names:
value = "screen_name"
else:
value = "name"
if data["event"] == "block":
event = _("You've blocked %s") % (data["target"][value])
elif data["event"] == "unblock":
event = _(u"You've unblocked %s") % (data["target"][value])
elif data["event"] == "follow":
if data["target"]["screen_name"] == username:
event = _(u"%s(@%s) has followed you") % (data["source"]["name"], data["source"]["screen_name"])
elif data["source"]["screen_name"] == username:
event = _(u"You've followed %s(@%s)") % (data["target"]["name"], data["target"]["screen_name"])
elif data["event"] == "unfollow":
event = _(u"You've unfollowed %s (@%s)") % (data["target"]["name"], data["target"]["screen_name"])
elif data["event"] == "favorite":
if data["source"]["screen_name"] == username:
event = _(u"You've liked: %s, %s") % (data["target"][value], data["target_object"]["text"])
else:
event = _(u"%s(@%s) has liked: %s") % (data["source"]["name"], data["source"]["screen_name"], data["target_object"]["text"])
elif data["event"] == "unfavorite":
if data["source"]["screen_name"] == username: event = _(u"You've unliked: %s, %s") % (data["target"][value], data["target_object"]["text"])
else: event = _(u"%s(@%s) has unliked: %s") % (data["source"]["name"], data["source"]["screen_name"], data["target_object"]["text"])
elif data["event"] == "list_created":
event = _(u"You've created the list %s") % (data["target_object"]["name"])
elif data["event"] == "list_destroyed":
event = _("You've deleted the list %s") % (data["target_object"]["name"])
elif data["event"] == "list_updated":
event = _("You've updated the list %s") % (data["target_object"]["name"])
elif data["event"] == "list_member_added":
if data["source"]["screen_name"] == username: event = _(u"You've added %s(@%s) to the list %s") % (data["target"]["name"], data["target"]["screen_name"], data["target_object"]["name"])
else: event = _(u"%s(@%s) has added you to the list %s") % (data["source"]["name"], data["source"]["screen_name"], data["target_object"]["name"])
elif data["event"] == "list_member_removed":
if data["source"]["screen_name"] == username: event = _(u"You'be removed %s(@%s) from the list %s") % (data["target"]["name"], data["target"]["screen_name"], data["target_object"]["name"])
else: event = _(u"%s(@%s) has removed you from the list %s") % (data["source"]["name"], data["source"]["screen_name"], data["target_object"]["name"])
elif data["event"] == "list_user_subscribed":
if data["source"]["screen_name"] == username: event = _(u"You've subscribed to the list %s, which is owned by %s(@%s)") % (data["target_object"]["name"], data["target"]["name"], data["target"]["screen_name"])
else: event = _(u"%s(@%s) has subscribed you to the list %s") % (data["source"]["name"], data["source"]["screen_name"], data["target_object"]["name"])
elif data["event"] == "list_user_unsubscribed":
if data["source"]["screen_name"] == username: event = _(u"You've unsubscribed from the list %s, which is owned by %s(@%s)") % (data["target_object"]["name"], data["target"]["name"], data["target"]["screen_name"])
else: event = _("You've been unsubscribed from the list %s, which is owned by %s(@%s)") % (data["target_object"]["name"], data["source"]["name"], data["source"]["screen_name"])
elif data["event"] == "retweeted_retweet":
if data["source"]["screen_name"] == username: event = _(u"You have retweeted a retweet from %s(@%s): %s") % (data["target"]["name"], data["target"]["screen_name"], data["target_object"]["retweeted_status"]["text"])
else: event = _(u"%s(@%s) has retweeted your retweet: %s") % (data["source"]["name"], data["source"]["screen_name"], data["target_object"]["retweeted_status"]["text"])
elif data["event"] == "quoted_tweet":
event = _(u"@{0} quoted your tweet: {1}").format(data["source"]["screen_name"], data["target_object"]["text"])
else:
event = _("Unknown")
log.error("event: %s\n target: %s\n source: %s\n target_object: %s" % (data["event"], data["target"], data["source"], data["target_object"]))
return [time.strftime("%I:%M %p"), event]
def compose_list(list):
name = list["name"]
if list["description"] == None: description = _(u"No description available")

View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
""" this package holds different modules to extract information regarding long tweets. A long tweet contains more than one tweet (such a quoted tweet), or is made via services like twishort."""

View File

@@ -16,18 +16,24 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
from twitter import utils
from sessions.twitter import utils
def is_long(tweet):
if tweet.has_key("quoted_status_id") and tweet.has_key("quoted_status"):
""" Check if the passed tweet contains a quote in its metadata.
tweet dict: a tweet dictionary.
returns True if a quote is detected, False otherwise."""
if "quoted_status_id" in tweet and "quoted_status" in tweet:
return tweet["quoted_status_id"]
elif tweet.has_key("retweeted_status") and tweet["retweeted_status"].has_key("quoted_status_id") and tweet["retweeted_status"].has_key("quoted_status"):
elif "retweeted_status" in tweet and "quoted_status_id" in tweet["retweeted_status"] and "quoted_status" in tweet["retweeted_status"]:
return tweet["retweeted_status"]["quoted_status_id"]
return False
def clear_url(tweet):
if tweet.has_key("retweeted_status"):
if tweet["retweeted_status"].has_key("full_text"):
""" Reads data from a quoted tweet and removes the link to the Status from the tweet's text.
tweet dict: a tweet dictionary.
returns a tweet dictionary without the URL to the status ID in its text to display."""
if "retweeted_status" in tweet:
if "full_text" in tweet["retweeted_status"]:
value = "full_text"
else:
value = "text"
@@ -35,7 +41,7 @@ def clear_url(tweet):
try: tweet["message"] = tweet["message"].replace(urls[-1], "")
except IndexError: pass
else:
if tweet.has_key("full_text"):
if "full_text" in tweet:
value = "full_text"
else:
value = "text"

View File

@@ -16,20 +16,27 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
from __future__ import print_function
import logging
import requests
import keys
import logging
log = logging.getLogger("long_tweets.twishort")
from twitter import utils
from requests_oauthlib import OAuth1Session
from sessions.twitter import utils
log = logging.getLogger("long_tweets.twishort")
def get_twishort_uri(url):
""" Takes A twishort URl and returns the twishort ID.
url str: an url like http://twishort.com/id.
returns a twishort ID if the URL is valid, False otherwise."""
try:
return url.split("twishort.com/")[1]
except ValueError:
return False
def is_long(tweet):
""" Check if the passed tweet is made with Twishort.
returns True if is a long tweet, False otherwise."""
long = False
for url in range(0, len(tweet["entities"]["urls"])):
try:
@@ -41,7 +48,7 @@ def is_long(tweet):
# see https://github.com/manuelcortez/TWBlue/issues/103
except TypeError:
pass
if long == False and tweet.has_key("retweeted_status"):
if long == False and "retweeted_status" in tweet:
for url in range(0, len(tweet["retweeted_status"]["entities"]["urls"])):
try:
if tweet["retweeted_status"]["entities"]["urls"][url] != None and "twishort.com" in tweet["retweeted_status"]["entities"]["urls"][url]["expanded_url"]:
@@ -53,17 +60,25 @@ def is_long(tweet):
return long
def get_full_text(uri):
""" Get Twishort's full text.
uri str: Twishort's identifier.
returns the contents of the tweet."""
try:
r = requests.get("http://api.twishort.com/1.1/get.json", params={"uri": uri, "api_key": keys.keyring.get("twishort_api_key")})
msg = r.json()["text"]
# Try to parse possible HTML entities.
from twitter.compose import StripChars
from sessions.twitter.compose import StripChars
msg = StripChars(msg)
return msg
except:
return False
def create_tweet(user_token, user_secret, text, media=0):
""" Send a tweet to be extended by using Twishort.
user_token, user_secret str: Twitter user access key and secret, used by TWBlue to authorise against Twitter.
text str: Tweet text, max 10000 characters.
media int: Not used currently.
Returns text to be placed in the Tweet if the post has been succeeded, 0 otherwise."""
twitter = OAuth1Session(keys.keyring.get("api_key"), client_secret=keys.keyring.get("api_secret"), resource_owner_key=user_token, resource_owner_secret=user_secret)
twishort_key=keys.keyring.get("twishort_api_key")
x_auth_service_provider = "https://api.twitter.com/1.1/account/verify_credentials.json"
@@ -83,5 +98,5 @@ def create_tweet(user_token, user_secret, text, media=0):
try:
return response.json()["text_to_tweet"]
except:
print "There was a problem creating a long tweet"
return 0
print("There was a problem creating a long tweet")
return 0

View File

@@ -1,70 +1,39 @@
# -*- coding: utf-8 -*-
""" The main session object. Here are the twitter functions to interact with the "model" of TWBlue."""
import wx
import urllib2
import config
import twitter
from keys import keyring
import session_exceptions as Exceptions
import paths
import output
import time
import sound
import logging
from twitter import utils, compose
from twython import TwythonError, TwythonRateLimitError, TwythonAuthError
import config_utils
import shelve
import application
""" This is the main session needed to access all Twitter Features."""
from __future__ import absolute_import
import os
from mysc.thread_utils import stream_threaded, call_threaded
import time
import logging
import webbrowser
import wx
import config
import output
import application
from pubsub import pub
log = logging.getLogger("sessionmanager.session")
from long_tweets import tweets, twishort
from wxUI import authorisationDialog
from twython import Twython, TwythonError, TwythonRateLimitError, TwythonAuthError
from mysc.thread_utils import call_threaded
from keys import keyring
from sessions import base
from sessions.twitter import utils, compose
from sessions.twitter.long_tweets import tweets, twishort
from .wxUI import authorisationDialog
sessions = {}
log = logging.getLogger("sessions.twitterSession")
class Session(object):
class Session(base.baseSession):
""" A session object where we will save configuration, the twitter object and a local storage for saving the items retrieved through the Twitter API methods"""
# Decorators.
def _require_login(fn):
""" Decorator for checking if the user is logged in(a twitter object has credentials) on twitter.
Some functions may need this to avoid making unneeded twitter API calls."""
def f(self, *args, **kwargs):
if self.logged == True:
fn(self, *args, **kwargs)
else:
raise Exceptions.NotLoggedSessionError("You are not logged in yet.")
return f
def _require_configuration(fn):
""" Check if the user has a configured session."""
def f(self, *args, **kwargs):
if self.settings != None:
fn(self, *args, **kwargs)
else:
raise Exceptions.NotConfiguredSessionError("Not configured.")
return f
def order_buffer(self, name, data, ignore_older=True):
""" Put the new items in the local database.
""" Put new items in the local database.
name str: The name for the buffer stored in the dictionary.
data list: A list with tweets.
ignore_older bool: if set to True, items older than the first element on the list will be ignored.
returns the number of items that have been added in this execution"""
num = 0
last_id = None
if self.db.has_key(name) == False:
if (name in self.db) == False:
self.db[name] = []
if self.db.has_key("users") == False:
if ("users" in self.db) == False:
self.db["users"] = {}
if ignore_older and len(self.db[name]) > 0:
if self.settings["general"]["reverse_timelines"] == False:
@@ -83,24 +52,24 @@ class Session(object):
if self.settings["general"]["reverse_timelines"] == False: self.db[name].append(i)
else: self.db[name].insert(0, i)
num = num+1
if i.has_key("user") == True:
if self.db["users"].has_key(i["user"]["id"]) == False:
if ("user" in i) == True:
if (i["user"]["id"] in self.db["users"]) == False:
self.db["users"][i["user"]["id"]] = i["user"]
return num
def order_cursored_buffer(self, name, data):
""" Put the new items on the local database. Useful for cursored buffers (followers, friends, users of a list and searches)
""" Put new items on the local database. Useful for cursored buffers (followers, friends, users of a list and searches)
name str: The name for the buffer stored in the dictionary.
data list: A list with items and some information about cursors.
returns the number of items that have been added in this execution"""
# Direct messages should be added to db in other function.
# Because they will be populating two buffers with one endpoint.
if name == "direct_messages":
return self.order_direct_messages(data)
num = 0
if self.db.has_key(name) == False:
if (name in self.db) == False:
self.db[name] = {}
self.db[name]["items"] = []
# if len(self.db[name]["items"]) > 0:
for i in data:
if utils.find_item(i["id"], self.db[name]["items"]) == None:
if self.settings["general"]["reverse_timelines"] == False: self.db[name]["items"].append(i)
@@ -109,14 +78,17 @@ class Session(object):
return num
def order_direct_messages(self, data):
""" Add incoming and sent direct messages to their corresponding database items.
data list: A list of direct messages to add.
returns the number of incoming messages processed in this execution, and sends an event with data regarding amount of sent direct messages added."""
incoming = 0
sent = 0
if self.db.has_key("direct_messages") == False:
if ("direct_messages" in self.db) == False:
self.db["direct_messages"] = {}
self.db["direct_messages"]["items"] = []
for i in data:
if i["message_create"]["sender_id"] == self.db["user_id"]:
if utils.find_item(i["id"], self.db["sent_direct_messages"]["items"]) == None:
if "sent_direct_messages" in self.db and utils.find_item(i["id"], self.db["sent_direct_messages"]["items"]) == None:
if self.settings["general"]["reverse_timelines"] == False: self.db["sent_direct_messages"]["items"].append(i)
else: self.db["sent_direct_messages"]["items"].insert(0, i)
sent = sent+1
@@ -128,104 +100,95 @@ class Session(object):
pub.sendMessage("sent-dms-updated", total=sent, account=self.db["user_name"])
return incoming
def __init__(self, session_id):
""" session_id (str): The name of the folder inside the config directory where the session is located."""
super(Session, self).__init__()
self.session_id = session_id
self.logged = False
self.settings = None
self.twitter = twitter.twitter.twitter()
self.db={}
def __init__(self, *args, **kwargs):
super(Session, self).__init__(*args, **kwargs)
self.reconnection_function_active = False
self.counter = 0
self.lists = []
pub.subscribe(self.add_friends, "friends-receibed")
@property
def is_logged(self):
return self.logged
def get_configuration(self):
""" Gets settings for a session."""
file_ = "%s/session.conf" % (self.session_id,)
# try:
log.debug("Creating config file %s" % (file_,))
self.settings = config_utils.load_config(paths.config_path(file_), paths.app_path("Conf.defaults"))
self.init_sound()
self.deshelve()
# except:
# log.exception("The session configuration has failed.")
# self.settings = None
def init_sound(self):
try: self.sound = sound.soundSystem(self.settings["sound"])
except: pass
@_require_configuration
# @_require_configuration
def login(self, verify_credentials=True):
""" Log into twitter using credentials from settings.
if the user account isn't authorised, it needs to call self.authorise() before login."""
if self.settings["twitter"]["user_key"] != None and self.settings["twitter"]["user_secret"] != None:
try:
log.debug("Logging in to twitter...")
self.twitter.login(self.settings["twitter"]["user_key"], self.settings["twitter"]["user_secret"], verify_credentials)
self.twitter = Twython(keyring.get("api_key"), keyring.get("api_secret"), self.settings["twitter"]["user_key"], self.settings["twitter"]["user_secret"])
if verify_credentials == True:
self.credentials = self.twitter.verify_credentials()
self.logged = True
log.debug("Logged.")
self.counter = 0
except:
except IOError:
log.error("The login attempt failed.")
self.logged = False
else:
self.logged = False
raise Exceptions.RequireCredentialsSessionError
@_require_configuration
# @_require_configuration
def authorise(self):
""" Authorises a Twitter account. This function needs to be called for each new session, after self.get_configuration() and before self.login()"""
if self.logged == True:
raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.")
else:
self.twitter.authorise()
twitter = Twython(keyring.get("api_key"), keyring.get("api_secret"))
self.auth = twitter.get_authentication_tokens(callback_url="oob")
webbrowser.open_new_tab(self.auth['auth_url'])
self.authorisation_dialog = authorisationDialog()
self.authorisation_dialog.cancel.Bind(wx.EVT_BUTTON, self.authorisation_cancelled)
self.authorisation_dialog.ok.Bind(wx.EVT_BUTTON, self.authorisation_accepted)
self.authorisation_dialog.ShowModal()
def verify_authorisation(self, pincode):
twitter = Twython(keyring.get("api_key"), keyring.get("api_secret"), self.auth['oauth_token'], self.auth['oauth_token_secret'])
final = twitter.get_authorized_tokens(pincode)
self.settings["twitter"]["user_key"] = final["oauth_token"]
self.settings["twitter"]["user_secret"] = final["oauth_token_secret"]
self.settings.write()
del self.auth
def authorisation_cancelled(self, *args, **kwargs):
""" Destroy the authorization dialog. """
self.authorisation_dialog.Destroy()
del self.authorisation_dialog
def authorisation_accepted(self, *args, **kwargs):
""" Gets the PIN code entered by user and validate it through Twitter."""
pincode = self.authorisation_dialog.text.GetValue()
self.twitter.verify_authorisation(self.settings, pincode)
self.verify_authorisation(pincode)
self.authorisation_dialog.Destroy()
def get_more_items(self, update_function, users=False, dm=False, name=None, *args, **kwargs):
""" Get more items for twitter objects.
update_function str: function to call for getting more items. Must be member of self.twitter.
users, dm bool: If any of these is set to True, the function will treat items as users or dm (they need different handling).
name str: name of the database item to put new element in."""
results = []
data = getattr(self.twitter.twitter, update_function)(*args, **kwargs)
if "cursor" in kwargs and kwargs["cursor"] == 0:
output.speak(_(u"There are no more items to retrieve in this buffer."))
return
data = getattr(self.twitter, update_function)(*args, **kwargs)
if users == True:
if type(data) == dict and data.has_key("next_cursor"):
self.db[name]["cursor"] = data["next_cursor"]
if type(data) == dict and "next_cursor" in data:
if "next_cursor" in data: # There are more objects to retrieve.
self.db[name]["cursor"] = data["next_cursor"]
else: # Set cursor to 0, wich means no more items available.
self.db[name]["cursor"] = 0
for i in data["users"]: results.append(i)
elif type(data) == list:
results.extend(data[1:])
elif dm == True:
self.db[name]["cursor"] = data["next_cursor"]
if "next_cursor" in data: # There are more objects to retrieve.
self.db[name]["cursor"] = data["next_cursor"]
else: # Set cursor to 0, wich means no more items available.
self.db[name]["cursor"] = 0
for i in data["events"]: results.append(i)
else:
results.extend(data[1:])
return results
def api_call(self, call_name, action="", _sound=None, report_success=False, report_failure=True, preexec_message="", *args, **kwargs):
""" Make a call to the Twitter API. If there is a connectionError or another exception not related to Twitter, It will call the method again at least 25 times, waiting a while between calls. Useful for post methods.
If twitter returns an error, it will not call the method anymore.
call_name str: The method to call
@@ -234,17 +197,17 @@ class Session(object):
_sound str: a sound to play if the call is executed properly.
report_success and report_failure bool: These are self explanatory. True or False.
preexec_message str: A message to speak to the user while the method is running, example: "trying to follow x user"."""
finished = False
tries = 0
if preexec_message:
output.speak(preexec_message, True)
while finished==False and tries < 25:
try:
val = getattr(self.twitter.twitter, call_name)(*args, **kwargs)
val = getattr(self.twitter, call_name)(*args, **kwargs)
finished = True
except TwythonError as e:
output.speak(e.message)
val = None
if e.error_code != 403 and e.error_code != 404:
tries = tries+1
time.sleep(5)
@@ -260,50 +223,45 @@ class Session(object):
return val
def search(self, name, *args, **kwargs):
tl = self.twitter.twitter.search(*args, **kwargs)
""" Search in twitter, passing args and kwargs as arguments to the Twython function."""
tl = self.twitter.search(*args, **kwargs)
tl["statuses"].reverse()
return tl["statuses"]
@_require_login
# @_require_login
def get_favourites_timeline(self, name, *args, **kwargs):
""" Gets favourites for the authenticated user or a friend or follower.
name str: Name for storage in the database."""
tl = self.call_paged(self.twitter.twitter.get_favorites, *args, **kwargs)
name str: Name for storage in the database.
args and kwargs are passed directly to the Twython function."""
tl = self.call_paged("get_favorites", *args, **kwargs)
return self.order_buffer(name, tl)
def call_paged(self, update_function, *args, **kwargs):
""" Makes a call to the Twitter API methods several times. Useful for get methods.
this function is needed for retrieving more than 200 items.
update_function str: The function to call. This function must be child of self.twitter.twitter
update_function str: The function to call. This function must be child of self.twitter
args and kwargs are passed to update_function.
returns a list with all items retrieved."""
if application.streaming_lives():
max = int(self.settings["general"]["max_api_calls"])-1
else:
max = 0
max = 0
results = []
data = getattr(self.twitter.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
data = getattr(self.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
results.extend(data)
for i in range(0, max):
if i == 0: max_id = results[-1]["id"]
else: max_id = results[0]["id"]
data = getattr(self.twitter.twitter, update_function)(max_id=max_id, count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
data = getattr(self.twitter, update_function)(max_id=max_id, count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
results.extend(data)
results.reverse()
return results
@_require_login
# @_require_login
def get_user_info(self):
""" Retrieves some information required by TWBlue for setup."""
f = self.twitter.twitter.get_account_settings()
f = self.twitter.get_account_settings()
sn = f["screen_name"]
self.settings["twitter"]["user_name"] = sn
self.db["user_name"] = sn
self.db["user_id"] = self.twitter.twitter.show_user(screen_name=sn)["id_str"]
self.db["user_id"] = self.twitter.show_user(screen_name=sn)["id_str"]
try:
self.db["utc_offset"] = f["time_zone"]["utc_offset"]
except KeyError:
@@ -311,34 +269,28 @@ class Session(object):
# Get twitter's supported languages and save them in a global variable
#so we won't call to this method once per session.
if len(application.supported_languages) == 0:
application.supported_languages = self.twitter.twitter.get_supported_languages()
application.supported_languages = self.twitter.get_supported_languages()
self.get_lists()
self.get_muted_users()
self.settings.write()
@_require_login
# @_require_login
def get_lists(self):
""" Gets the lists that the user is subscribed to and stores them in the database. Returns None."""
self.db["lists"] = self.twitter.twitter.show_lists(reverse=True)
self.db["lists"] = self.twitter.show_lists(reverse=True)
@_require_login
# @_require_login
def get_muted_users(self):
""" Gets muted users (oh really?)."""
self.db["muted_users"] = self.twitter.list_mute_ids()["ids"]
self.db["muted_users"] = self.twitter.twitter.list_mute_ids()["ids"]
@_require_login
# @_require_login
def get_stream(self, name, function, *args, **kwargs):
""" Retrieves the items for a regular stream.
name str: Name to save items to the database.
function str: A function to get the items."""
last_id = -1
if self.db.has_key(name):
if name in self.db:
try:
if self.db[name][0]["id"] > self.db[name][-1]["id"]:
last_id = self.db[name][0]["id"]
@@ -350,159 +302,48 @@ class Session(object):
self.order_buffer(name, tl)
def get_cursored_stream(self, name, function, items="users", get_previous=False, *args, **kwargs):
""" Gets items for API calls that require using cursors to paginate the results.
name str: Name to save it in the database.
function str: Function that provides the items.
items: When the function returns the list with results, items will tell how the order function should be look.
for example get_followers_list returns a list and users are under list["users"], here the items should point to "users"."""
items: When the function returns the list with results, items will tell how the order function should be look. for example get_followers_list returns a list and users are under list["users"], here the items should point to "users".
get_previous bool: wether this function will be used to get previous items in a buffer or load the buffer from scratch.
returns number of items retrieved."""
items_ = []
try:
if self.db[name].has_key("cursor") and get_previous:
if "cursor" in self.db[name] and get_previous:
cursor = self.db[name]["cursor"]
else:
cursor = -1
except KeyError:
cursor = -1
if cursor != -1:
tl = getattr(self.twitter.twitter, function)(cursor=cursor, count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
tl = getattr(self.twitter, function)(cursor=cursor, count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
else:
tl = getattr(self.twitter.twitter, function)(count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
tl = getattr(self.twitter, function)(count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
tl[items].reverse()
num = self.order_cursored_buffer(name, tl[items])
# Recently, Twitter's new endpoints have cursor if there are more results.
if tl.has_key("next_cursor"):
if "next_cursor" in tl:
self.db[name]["cursor"] = tl["next_cursor"]
else:
self.db[name]["cursor"] = 0
return num
def start_streaming(self):
""" Start the streaming for sending tweets in realtime."""
if application.streaming_lives():
if not hasattr(self, "main_stream"):
self.get_timelines()
if not hasattr(self, "timelinesStream"):
self.get_main_stream()
def get_main_stream(self):
if application.streaming_lives():
log.debug("Starting the main stream...")
self.main_stream = twitter.buffers.stream.streamer(keyring.get("api_key"), keyring.get("api_secret"), self.settings["twitter"]["user_key"], self.settings["twitter"]["user_secret"], self)
stream_threaded(self.main_stream.user, self.session_id)
def get_timelines(self):
if application.streaming_lives():
log.debug("Starting the timelines stream...")
self.timelinesStream = twitter.buffers.indibidual.timelinesStreamer(keyring.get("api_key"), keyring.get("api_secret"), self.settings["twitter"]["user_key"], self.settings["twitter"]["user_secret"], session=self)
ids = ""
for i in self.settings["other_buffers"]["timelines"]:
ids = ids + "%s, " % (self.db[i+"-timeline"][0]["user"]["id_str"])
for i in self.lists:
for z in i.users:
ids += str(z) + ", "
if ids != "":
stream_threaded(self.timelinesStream.statuses.filter, self.session_id, follow=ids)
def add_friends(self):
if application.streaming_lives():
try:
self.timelinesStream.set_friends(self.main_stream.friends)
except AttributeError:
pass
def listen_stream_error(self):
if hasattr(self, "main_stream") and application.streaming_lives():
log.debug("Disconnecting the main stream...")
self.main_stream.disconnect()
del self.main_stream
if hasattr(self, "timelinesStream") and application.streaming_lives():
log.debug("disconnecting the timelines stream...")
self.timelinesStream.disconnect()
del self.timelinesStream
def check_connection(self):
""" Restart the Twitter object every 5 executions. It is useful for dealing with requests timeout and other oddities."""
log.debug("Executing check connection...")
instan = 0
self.counter += 1
if self.counter >= 4:
log.debug("Restarting connection after 5 minutes.")
del self.twitter
self.logged = False
self.twitter = twitter.twitter.twitter()
self.login(False)
self.counter = 0
if self.reconnection_function_active == True: return
self.reconnection_function_active = True
if not hasattr(self, "main_stream") and application.streaming_lives():
self.get_main_stream()
if not hasattr(self, "timelinesStream") and application.streaming_lives():
self.get_timelines()
self.reconnection_function_active = False
if hasattr(self, "timelinesStream") and not hasattr(self.timelinesStream, "friends"):
self.add_friends()
# try:
# urllib2.urlopen("http://74.125.228.231", timeout=5)
# except urllib2.URLError:
# pub.sendMessage("stream-error", session=self.session_id)
def remove_stream(self, stream):
if application.streaming_lives():
if stream == "timelinesStream":
if hasattr(self, "timelinesStream"):
self.timelinesStream.disconnect()
del self.timelinesStream
else:
self.main_stream.disconnect()
del self.main_stream
def shelve(self):
"Shelve the database to allow for persistance."
shelfname=paths.config_path(str(self.session_id)+"/cache.db")
if self.settings["general"]["persist_size"] == 0:
if os.path.exists(shelfname):
os.remove(shelfname)
return
try:
if not os.path.exists(shelfname):
output.speak("Generating database, this might take a while.",True)
shelf=shelve.open(paths.config_path(shelfname),'c')
for key,value in self.db.items():
if type(key) != str and type(key) != unicode:
output.speak("Uh oh, while shelving the database, a key of type " + str(type(key)) + " has been found. It will be converted to type str, but this will cause all sorts of problems on deshelve. Please bring this to the attention of the " + application.name + " developers immediately. More information about the error will be written to the error log.",True)
log.error("Uh oh, " + str(key) + " is of type " + str(type(key)) + "!")
# Convert unicode objects to UTF-8 strings before shelve these objects.
if type(value) == list and self.settings["general"]["persist_size"] != -1 and len(value) > self.settings["general"]["persist_size"]:
shelf[str(key.encode("utf-8"))]=value[self.settings["general"]["persist_size"]:]
else:
shelf[str(key.encode("utf-8"))]=value
shelf.close()
except:
output.speak("An exception occurred while shelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
log.exception("Exception while shelving" + shelfname)
os.remove(shelfname)
def deshelve(self):
"Import a shelved database."
shelfname=paths.config_path(str(self.session_id)+"/cache.db")
if self.settings["general"]["persist_size"] == 0:
if os.path.exists(shelfname):
os.remove(shelfname)
return
try:
shelf=shelve.open(paths.config_path(shelfname),'c')
for key,value in shelf.items():
self.db[key]=value
shelf.close()
except:
output.speak("An exception occurred while deshelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
log.exception("Exception while deshelving" + shelfname)
try:
os.remove(shelfname)
except:
pass
def check_quoted_status(self, tweet):
""" Helper for get_quoted_tweet. Get a quoted status inside a tweet and create a special tweet with all info available.
tweet dict: A tweet dictionary.
Returns a quoted tweet or the original tweet if is not a quote"""
status = tweets.is_long(tweet)
if status != False and config.app["app-settings"]["handle_longtweets"]:
quoted_tweet = self.get_quoted_tweet(tweet)
@@ -510,8 +351,9 @@ class Session(object):
return tweet
def get_quoted_tweet(self, tweet):
""" Process a tweet and extract all information related to the quote."""
quoted_tweet = tweet
if tweet.has_key("full_text"):
if "full_text" in tweet:
value = "full_text"
else:
value = "text"
@@ -519,16 +361,16 @@ class Session(object):
for url in range(0, len(urls)):
try: quoted_tweet[value] = quoted_tweet[value].replace(urls[url], quoted_tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
if quoted_tweet.has_key("quoted_status"):
if "quoted_status" in quoted_tweet:
original_tweet = quoted_tweet["quoted_status"]
elif quoted_tweet.has_key("retweeted_status") and quoted_tweet["retweeted_status"].has_key("quoted_status"):
elif "retweeted_status" in quoted_tweet and "quoted_status" in quoted_tweet["retweeted_status"]:
original_tweet = quoted_tweet["retweeted_status"]["quoted_status"]
else:
return quoted_tweet
original_tweet = self.check_long_tweet(original_tweet)
if original_tweet.has_key("full_text"):
if "full_text" in original_tweet:
value = "full_text"
elif original_tweet.has_key("message"):
elif "message" in original_tweet:
value = "message"
else:
value = "text"
@@ -539,16 +381,19 @@ class Session(object):
return compose.compose_quoted_tweet(quoted_tweet, original_tweet)
def check_long_tweet(self, tweet):
""" Process a tweet and add extra info if it's a long tweet made with Twyshort.
tweet dict: a tweet object.
returns a tweet with a new argument message, or original tweet if it's not a long tweet."""
long = twishort.is_long(tweet)
if long != False and config.app["app-settings"]["handle_longtweets"]:
message = twishort.get_full_text(long)
if tweet.has_key("quoted_status"):
if "quoted_status" in tweet:
tweet["quoted_status"]["message"] = message
if tweet["quoted_status"]["message"] == False: return False
tweet["quoted_status"]["twishort"] = True
for i in tweet["quoted_status"]["entities"]["user_mentions"]:
if "@%s" % (i["screen_name"]) not in tweet["quoted_status"]["message"] and i["screen_name"] != tweet["user"]["screen_name"]:
if tweet["quoted_status"].has_key("retweeted_status") and tweet["retweeted_status"]["user"]["screen_name"] == i["screen_name"]:
if "retweeted_status" in tweet["quoted_status"] and tweet["retweeted_status"]["user"]["screen_name"] == i["screen_name"]:
continue
tweet["quoted_status"]["message"] = u"@%s %s" % (i["screen_name"], tweet["message"])
else:
@@ -557,27 +402,37 @@ class Session(object):
tweet["twishort"] = True
for i in tweet["entities"]["user_mentions"]:
if "@%s" % (i["screen_name"]) not in tweet["message"] and i["screen_name"] != tweet["user"]["screen_name"]:
if tweet.has_key("retweeted_status") and tweet["retweeted_status"]["user"]["screen_name"] == i["screen_name"]:
if "retweeted_status" in tweet and tweet["retweeted_status"]["user"]["screen_name"] == i["screen_name"]:
continue
return tweet
def get_user(self, id):
if self.db.has_key("users") == False or self.db["users"].has_key(id) == False:
user = self.twitter.twitter.show_user(id=id)
""" Returns an user object associated with an ID.
id str: User identifier, provided by Twitter.
returns an user dict."""
if ("users" in self.db) == False or (id in self.db["users"]) == False:
try:
user = self.twitter.show_user(id=id)
except TwythonError:
user = dict(screen_name="deleted_account", name="Deleted account")
return user
self.db["users"][user["id_str"]] = user
return user
else:
return self.db["users"][id]
def get_user_by_screen_name(self, screen_name):
if self.db.has_key("users") == False:
user = utils.if_user_exists(self.twitter.twitter, screen_name)
""" Returns an user identifier associated with a screen_name.
screen_name str: User name, such as tw_blue2, provided by Twitter.
returns an user ID."""
if ("users" in self.db) == False:
user = utils.if_user_exists(self.twitter, screen_name)
self.db["users"][user["id_str"]] = user
return user["id_str"]
else:
for i in self.db["users"].keys():
if self.db["users"][i]["screen_name"] == screen_name:
return self.db["users"][i]["id_str"]
user = utils.if_user_exists(self.twitter.twitter, screen_name)
user = utils.if_user_exists(self.twitter, screen_name)
self.db["users"][user["id_str"]] = user
return user["id_str"]

View File

@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import url_shortener, re
import output
from twython import TwythonError
@@ -24,32 +25,32 @@ def find_urls_in_text(text):
def find_urls (tweet):
urls = []
# Let's add URLS from tweet entities.
if tweet.has_key("message_create"):
if "message_create" in tweet:
entities = tweet["message_create"]["message_data"]["entities"]
else:
entities = tweet["entities"]
for i in entities["urls"]:
if i["expanded_url"] not in urls:
urls.append(i["expanded_url"])
if tweet.has_key("quoted_status"):
if "quoted_status" in tweet:
for i in tweet["quoted_status"]["entities"]["urls"]:
if i["expanded_url"] not in urls:
urls.append(i["expanded_url"])
if tweet.has_key("retweeted_status"):
if "retweeted_status" in tweet:
for i in tweet["retweeted_status"]["entities"]["urls"]:
if i["expanded_url"] not in urls:
urls.append(i["expanded_url"])
if tweet["retweeted_status"].has_key("quoted_status"):
if "quoted_status" in tweet["retweeted_status"]:
for i in tweet["retweeted_status"]["quoted_status"]["entities"]["urls"]:
if i["expanded_url"] not in urls:
urls.append(i["expanded_url"])
if tweet.has_key("message"):
if "message" in tweet:
i = "message"
elif tweet.has_key("full_text"):
elif "full_text" in tweet:
i = "full_text"
else:
i = "text"
if tweet.has_key("message_create"):
if "message_create" in tweet:
extracted_urls = find_urls_in_text(tweet["message_create"]["message_data"]["text"])
else:
extracted_urls = find_urls_in_text(tweet[i])
@@ -82,7 +83,7 @@ def is_audio(tweet):
try:
if len(find_urls(tweet)) < 1:
return False
if tweet.has_key("message_create"):
if "message_create" in tweet:
entities = tweet["message_create"]["message_data"]["entities"]
else:
entities = tweet["entities"]
@@ -91,22 +92,22 @@ def is_audio(tweet):
if i["text"] == "audio":
return True
except IndexError:
print tweet["entities"]["hashtags"]
print(tweet["entities"]["hashtags"])
log.exception("Exception while executing is_audio hashtag algorithm")
def is_geocoded(tweet):
if tweet.has_key("coordinates") and tweet["coordinates"] != None:
if "coordinates" in tweet and tweet["coordinates"] != None:
return True
def is_media(tweet):
if tweet.has_key("message_create"):
if "message_create" in tweet:
entities = tweet["message_create"]["message_data"]["entities"]
else:
entities = tweet["entities"]
if entities.has_key("media") == False:
if ("media" in entities) == False:
return False
for i in entities["media"]:
if i.has_key("type") and i["type"] == "photo":
if "type" in i and i["type"] == "photo":
return True
return False
@@ -121,10 +122,10 @@ def get_all_mentioned(tweet, conf, field="screen_name"):
def get_all_users(tweet, conf):
string = []
if tweet.has_key("retweeted_status"):
if "retweeted_status" in tweet:
string.append(tweet["user"]["screen_name"])
tweet = tweet["retweeted_status"]
if tweet.has_key("sender"):
if "sender" in tweet:
string.append(tweet["sender"]["screen_name"])
else:
if tweet["user"]["screen_name"] != conf["user_name"]:
@@ -161,16 +162,16 @@ def api_call(parent=None, call_name=None, preexec_message="", success="", succes
def is_allowed(tweet, settings, buffer_name):
clients = settings["twitter"]["ignored_clients"]
if tweet.has_key("sender"): return True
if "sender" in tweet: return True
allowed = True
tweet_data = {}
if tweet.has_key("retweeted_status"):
if "retweeted_status" in tweet:
tweet_data["retweet"] = True
if tweet["in_reply_to_status_id_str"] != None:
tweet_data["reply"] = True
if tweet.has_key("quoted_status"):
if "quoted_status" in tweet:
tweet_data["quote"] = True
if tweet.has_key("retweeted_status"): tweet = tweet["retweeted_status"]
if "retweeted_status" in tweet: tweet = tweet["retweeted_status"]
source = re.sub(r"(?s)<.*?>", "", tweet["source"])
for i in clients:
if i.lower() == source.lower():
@@ -178,7 +179,7 @@ def is_allowed(tweet, settings, buffer_name):
return filter_tweet(tweet, tweet_data, settings, buffer_name)
def filter_tweet(tweet, tweet_data, settings, buffer_name):
if tweet.has_key("full_text"):
if "full_text" in tweet:
value = "full_text"
else:
value = "text"
@@ -187,23 +188,23 @@ def filter_tweet(tweet, tweet_data, settings, buffer_name):
regexp = settings["filters"][i]["regexp"]
word = settings["filters"][i]["word"]
# Added if/else for compatibility reasons.
if settings["filters"][i].has_key("allow_rts"):
if "allow_rts" in settings["filters"][i]:
allow_rts = settings["filters"][i]["allow_rts"]
else:
allow_rts = "True"
if settings["filters"][i].has_key("allow_quotes"):
if "allow_quotes" in settings["filters"][i]:
allow_quotes = settings["filters"][i]["allow_quotes"]
else:
allow_quotes = "True"
if settings["filters"][i].has_key("allow_replies"):
if "allow_replies" in settings["filters"][i]:
allow_replies = settings["filters"][i]["allow_replies"]
else:
allow_replies = "True"
if allow_rts == "False" and tweet_data.has_key("retweet"):
if allow_rts == "False" and "retweet" in tweet_data:
return False
if allow_quotes == "False" and tweet_data.has_key("quote"):
if allow_quotes == "False" and "quote" in tweet_data:
return False
if allow_replies == "False" and tweet_data.has_key("reply"):
if allow_replies == "False" and "reply" in tweet_data:
return False
if word != "" and settings["filters"][i]["if_word_exists"]:
if word in tweet[value]:

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
import wx
class authorisationDialog(wx.Dialog):
def __init__(self):
super(authorisationDialog, self).__init__(parent=None, title=_(u"Authorising account..."))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
static = wx.StaticText(panel, wx.NewId(), _(u"Enter your PIN code here"))
self.text = wx.TextCtrl(panel, -1)
self.ok = wx.Button(panel, wx.ID_OK)
self.cancel = wx.Button(panel, wx.ID_CANCEL)
sizer.Add(self.text, 0, wx.ALL, 5)
sizer.Add(self.cancel, 0, wx.ALL, 5)
panel.SetSizer(sizer)
min = sizer.CalcMin()
self.SetClientSize(min)

View File

@@ -112,7 +112,7 @@ data_files = get_data(),
options = {
'py2exe': {
'optimize':2,
'packages': ["pubsub", "pubsub.core", "pubsub.core.kwargs", "dbhash"],
'packages': ["pubsub", "pubsub.core", "pubsub.core.kwargs", "dbhash", "oauthlib.oauth1.rfc5849.endpoints.resource", "oauthlib.oauth2.rfc6749.endpoints.resource"],
'dll_excludes': ["MPR.dll", "api-ms-win-core-apiquery-l1-1-0.dll", "api-ms-win-core-console-l1-1-0.dll", "api-ms-win-core-delayload-l1-1-1.dll", "api-ms-win-core-errorhandling-l1-1-1.dll", "api-ms-win-core-file-l1-2-0.dll", "api-ms-win-core-handle-l1-1-0.dll", "api-ms-win-core-heap-obsolete-l1-1-0.dll", "api-ms-win-core-libraryloader-l1-1-1.dll", "api-ms-win-core-localization-l1-2-0.dll", "api-ms-win-core-processenvironment-l1-2-0.dll", "api-ms-win-core-processthreads-l1-1-1.dll", "api-ms-win-core-profile-l1-1-0.dll", "api-ms-win-core-registry-l1-1-0.dll", "api-ms-win-core-synch-l1-2-0.dll", "api-ms-win-core-sysinfo-l1-2-0.dll", "api-ms-win-security-base-l1-2-0.dll", "api-ms-win-core-heap-l1-2-0.dll", "api-ms-win-core-interlocked-l1-2-0.dll", "api-ms-win-core-localization-obsolete-l1-1-0.dll", "api-ms-win-core-string-l1-1-0.dll", "api-ms-win-core-string-obsolete-l1-1-0.dll", "WLDAP32.dll", "MSVCP90.dll", "CRYPT32.dll", "mfc90.dll"],
'compressed': True
},

View File

@@ -1,16 +1,18 @@
# -*- coding: utf-8 -*-
import sys
import url_shortener
import audio_services
import os
import logging as original_logger
log = original_logger.getLogger("sound")
import paths
import sound_lib
import sys
import subprocess
import platform
import tempfile
import glob
import url_shortener
import audio_services
import paths
import sound_lib
import output
import youtube_utils
import application
system = platform.system()
if system=="Windows" and not hasattr(sys, 'frozen'): # We are running from source on Windows
current_dir=os.getcwd()
@@ -18,13 +20,14 @@ if system=="Windows" and not hasattr(sys, 'frozen'): # We are running from sourc
import vlc
if system=="Windows" and not hasattr(sys, 'frozen'): # Restore the original folder
os.chdir(current_dir)
import sound_lib.output, sound_lib.input, sound_lib.stream
from mysc.repeating_timer import RepeatingTimer
from mysc.thread_utils import call_threaded
import application
import tempfile
import glob
URLPlayer = None
log = original_logger.getLogger("sound")
def setup():
global URLPlayer
if not URLPlayer:

View File

@@ -1,25 +0,0 @@
import output, input, recording, stream
__author__ = 'Christopher Toth'
__version__ = 0.73
def find_datafiles():
from glob import glob
import os
import platform
import sound_lib
path = os.path.join(sound_lib.__path__[0], 'lib')
system = platform.system()
if system == 'Windows':
file_ext = '*.dll'
elif system == 'Darwin':
file_ext = '*.dylib'
else:
file_ext = '*.so'
if platform.architecture()[0] == '32bit' or platform.system() == 'Darwin':
arch = 'x86'
else:
arch = 'x64'
dest_dir = os.path.join('sound_lib', 'lib', arch)
source = glob(os.path.join(path, arch, file_ext))
return [(dest_dir, source)]

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