Compare commits

...

81 Commits

Author SHA1 Message Date
a5ba80feee Final touches 2021-10-26 16:18:53 -05:00
528ecc2a33 Updated NSI file 2021-10-26 16:16:03 -05:00
3519746078 Move installer Nsis file to a new name 2021-10-26 15:47:03 -05:00
ef79e0696e Move updated Nsis script to artifacts in build stage 2021-10-26 15:19:57 -05:00
b9ee0dae5b Fixed a typo in installer script 2021-10-26 14:48:58 -05:00
f31575a733 Merge pull request #408 from manuelcortez/calendar_versioning
Switch to Calendar versioning
2021-10-26 13:52:21 -05:00
e451bbd5e9 Merge pull request #407 from manuelcortez/alias_manager
Alias manager Dialog. Closes #401
2021-10-26 13:51:09 -05:00
f7f303929e Updated CI config 2021-10-26 13:41:22 -05:00
9f48784ce4 Added script to add next version in application.py and installer generation files 2021-10-26 13:39:50 -05:00
cb1312d0c9 Changed version in installer so it will be easier to replace it later by the authomatic script 2021-10-26 13:38:21 -05:00
a82efd4dcc Fixed a typo 2021-10-26 13:16:31 -05:00
72e6d030d5 Updater no longer will try to compare float numbers for updates 2021-10-26 13:10:57 -05:00
d222740887 Updated gitlab CI 2021-10-26 13:04:49 -05:00
2b059ee42e Modified Nsis scripts to remove references to old snapshots code 2021-10-26 13:02:24 -05:00
daac312658 Removes snapshot information from app info 2021-10-26 12:43:03 -05:00
b23be9c896 Started to remove stable and snapshot update information files 2021-10-26 12:40:57 -05:00
61b0dc34b8 Merge branch 'next-gen' into alias_manager 2021-10-26 10:48:43 -05:00
c5d13369eb Merge branch 'next-gen' of github.com:manuelcortez/twblue into next-gen 2021-10-26 10:47:55 -05:00
856ecf5eb9 Merge branch 'next-gen' of github.com:manuelcortez/twblue into alias_manager 2021-10-26 10:46:30 -05:00
e3e0ac9457 Added an alias manager dialog in the application menu 2021-10-26 10:45:11 -05:00
34c1f69ec1 Merge pull request #404 from nidza07/SerbianTranslation
Updated Serbian translation
2021-10-22 12:57:58 -05:00
7326ff88f9 Merge pull request #405 from Oreonan/fr_211021
Update french interface
2021-10-22 12:56:55 -05:00
Oreonan
a8d876a7b7 Fix last-translator e-mail 2021-10-21 17:02:03 +02:00
Oreonan
89fa6435b4 Update french interface 2021-10-21 16:58:32 +02:00
Nikola Jovic
d1bd393be2 Updated Serbian translation 2021-10-21 16:30:46 +02:00
301bd5fd39 Pushed a new snapshot 2021-10-20 15:52:58 -05:00
José Manuel Delicado Alcolea
a2f25bfbb5 Added charset-normalizer to requirements 2021-10-12 17:05:09 +02:00
286e030f40 Handle new Tweepy exceptions properly. #403 2021-10-07 09:20:06 -05:00
d8fca3b31a Initial work to Support Tweepy 4 2021-09-26 03:58:25 -05:00
0c27427843 Merge branch 'next-gen' of github.com:manuelcortez/twblue into next-gen 2021-09-10 15:05:28 -05:00
dfdbe3c5f4 Close VLC window after video playback ends. Close #399 2021-09-10 15:05:05 -05:00
José Manuel Delicado
2222a97451 Merge pull request #394 from Oreonan/fr_020821
Update french interface translation
2021-09-03 11:38:50 +02:00
fbe93ea4be Made FreakyBlue The default soundpack for new twitter sessions 2021-09-02 09:59:13 -05:00
4bcae1aa97 Fixed search filters. Closes #397 2021-09-02 09:38:05 -05:00
4cabf5b9cd Added code to handle user timelines in buffer creator 2021-08-31 10:31:18 -05:00
a9a4189295 Added improvements to tweet condense feature 2021-08-31 10:05:21 -05:00
c7b6d69518 Fixed an error in new buffer creation. Closes #396 2021-08-30 13:19:15 -05:00
65512a9862 Added a pubsub event to create sessions buffers 2021-08-30 10:51:26 -05:00
Oreonan
17ea8af050 Update translation 2021-08-28 08:37:26 +02:00
Oreonan
43578a32eb Merge remote-tracking branch 'origin/next-gen' into fr_020821 2021-08-28 08:33:40 +02:00
7c34204d17 Allow to specify alternative configuration specs for sessions 2021-08-27 13:45:59 -05:00
3a5c1c10d3 Released a new snapshot 2021-08-26 15:13:34 -05:00
f9864a887d Fixed a small traceback that was happening when translating a direct message 2021-08-26 09:16:02 -05:00
3d8519313e Switched Geocoding library to OpenStreetMap's Nominatim API. Closes #390 2021-08-26 08:56:51 -05:00
f4ecf10885 Allow to copy tweet URLS from tweet displayer dialog 2021-08-25 16:30:37 -05:00
c67b415934 Updated changelog 2021-08-25 12:49:53 -05:00
10511d3022 Improvements to reading tweets with many mentions on them 2021-08-25 11:13:12 -05:00
Oreonan
ddc80a29fd Update french interface translation 2021-08-02 19:30:24 +02:00
9ea36a26d2 Updated changelog with windows 11 keymap 2021-07-31 22:33:32 -05:00
97286496fc fixed changelog in diverged commits 2021-07-31 22:28:46 -05:00
José Manuel Delicado
6436af76f5 Merge pull request #393 from jpavonabian/windows11
Added a fix.
2021-07-28 07:50:38 +02:00
Jesus
576b5064c0 Added a fix. 2021-07-28 07:23:57 +02:00
José Manuel Delicado
342265b3c0 Merge pull request #392 from jpavonabian/windows11
Fixed shortkuts
2021-07-26 13:38:32 +02:00
Jesus
54938ecb6c Fixed shortkuts 2021-07-26 12:47:51 +02:00
José Manuel Delicado
7aff8252d2 Merge pull request #391 from jpavonabian/windows11
Keypad for windows 11
2021-07-24 12:46:43 +02:00
Jesus
9c680130f7 Keypad para windows 11 2021-07-24 06:24:08 +02:00
24d1ad093d Streaming API: Ignore retweets if original tweet is present in a buffer 2021-07-16 10:22:51 -05:00
b2b9cd810f Fixed an issue when indefined keystrokes 2021-07-13 17:53:01 -05:00
582be54dea Added add_alias action to keymaps as undefined 2021-07-13 17:25:48 -05:00
ff0fbeafa3 Modified keystrokeEditor to allow undefined keystrokes 2021-07-13 17:22:20 -05:00
e314cf0599 Allow search users by names in autocomplete users 2021-07-10 05:24:56 -05:00
José Manuel Delicado Alcolea
e7b72112cf Merge branch 'next-gen' of github.com:manuelcortez/TWBlue into next-gen 2021-07-07 08:42:14 +02:00
José Manuel Delicado Alcolea
70c095febe setup.py: patch cx_freeze to include our Microsoft Visual C++ runtime files 2021-07-07 08:41:08 +02:00
6119b029f8 Merge pull request #388 from zstanecic/next-gen
Updated russian interface
2021-07-06 17:13:59 -05:00
b74cd9a73d Ignore undefined actions in keymaps 2021-07-06 17:01:42 -05:00
8ff6809f08 Updated changelog 2021-07-06 16:22:52 -05:00
39af9d8623 Merge pull request #389 from manuelcortez/user_alias
User aliases within TWBlue
2021-07-06 15:58:54 -05:00
3688d7548c Check alias before returning any user object 2021-07-06 13:59:34 -05:00
07f9afb14e Added option to user menu in the menu bar 2021-07-06 13:58:34 -05:00
de12dadac2 Added GUI for user alias addition 2021-07-06 13:58:13 -05:00
877c909482 Added user-aliases section on session configs 2021-07-06 13:56:56 -05:00
José Manuel Delicado Alcolea
1206aba83b Added a few more dependencies to requirements.txt 2021-07-06 20:42:03 +02:00
zstanecic
fcd631b2de Merge remote-tracking branch 'upstream/next-gen' into next-gen 2021-07-06 19:16:47 +02:00
Jose Manuel Delicado
86130954d7 Updated Windows dependencies. Latest versions of NSIS and VLC, among others 2021-07-06 12:39:47 +02:00
Jose Manuel Delicado
23a56c637d Add some indirect dependencies to requirements.txt in order to easily keep them updated 2021-07-05 23:03:40 +02:00
zstanecic
d5ac0db67b updated the russian localization. 2021-07-05 16:04:19 +02:00
bb4869b7be Pushed a new snapshot 2021-07-04 17:05:06 -05:00
44b6e82183 Fix reply dialog issue 2021-07-04 11:50:03 -05:00
Jose Manuel Delicado
5268f166f8 Use Python 3.8.10 for automated builds 2021-07-04 18:43:33 +02:00
Jose Manuel Delicado
37ad6b5fbf paths.py: replace socializer by TW Blue in data_path function definition 2021-07-04 18:32:17 +02:00
Jose Manuel Delicado
bcc72c932d Updated Windows dependencies. Python 3.8.10 2021-07-04 18:26:49 +02:00
54 changed files with 2431 additions and 1990 deletions

View File

@@ -8,7 +8,7 @@ stages:
- make_installer
- upload
snapshot32:
twblue32:
tags:
- shared-windows
- windows
@@ -17,11 +17,10 @@ snapshot32:
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
- echo ${time}
- echo "started by ${GITLAB_USER_NAME}"
- choco install python --version 3.8.7 -y -ForceX86
- choco install python --version 3.8.10 -y -ForceX86
- '&$env:PYTHON -V'
- '&$env:PYTHON -m pip install --upgrade pip'
- '&$env:PYTHON -m pip install --upgrade -r requirements.txt'
- '&$env:PYTHON -m pip uninstall enum34 -y'
stage: build
interruptible: true
script:
@@ -30,6 +29,7 @@ snapshot32:
- '&$env:PYTHON documentation_importer.py'
- cd ..\src
- '&$env:PYTHON ..\doc\generator.py'
- '&$env:PYTHON write_version_data.py'
- '&$env:PYTHON setup.py build'
- cd ..
- mkdir artifacts
@@ -37,7 +37,9 @@ snapshot32:
- '&$env:PYTHON make_archive.py'
- cd ..
- mv src/dist artifacts/TWBlue
- move src/twblue.zip artifacts/twblue_snapshot_x86.zip
- move src/twblue.zip artifacts/twblue_x86.zip
# Move the generated script nsis file to artifacts, so we won't need python when generating the installer.
- move scripts/twblue.nsi artifacts/twblue.nsi
only:
- tags
artifacts:
@@ -45,7 +47,7 @@ snapshot32:
- artifacts
expire_in: 1 day
snapshot64:
twblue64:
tags:
- shared-windows
- windows
@@ -54,7 +56,7 @@ snapshot64:
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
- echo ${time}
- echo "started by ${GITLAB_USER_NAME}"
- choco install python --version 3.8.7 -y
- choco install python --version 3.8.10 -y
- '&$env:PYTHON -V'
- '&$env:PYTHON -m pip install --upgrade pip'
- '&$env:PYTHON -m pip install --upgrade -r requirements.txt'
@@ -66,6 +68,7 @@ snapshot64:
- '&$env:PYTHON documentation_importer.py'
- cd ..\src
- '&$env:PYTHON ..\doc\generator.py'
- '&$env:PYTHON write_version_data.py'
- '&$env:PYTHON setup.py build'
- cd ..
- mkdir artifacts
@@ -73,7 +76,7 @@ snapshot64:
- '&$env:PYTHON make_archive.py'
- cd ..
- mv src/dist artifacts/TWBlue64
- move src/twblue.zip artifacts/twblue_snapshot_x64.zip
- move src/twblue.zip artifacts/twblue_x64.zip
only:
- tags
artifacts:
@@ -95,9 +98,10 @@ generate_versions:
script:
- move artifacts/TWBlue scripts/
- move artifacts/TWBlue64 scripts/
- move artifacts/twblue.nsi scripts/installer.nsi
- cd scripts
- '&$env:NSIS twblue_snapshot.nsi'
- move twblue_snapshot_setup.exe ../artifacts
- '&$env:NSIS installer.nsi'
- move twblue_setup.exe ../artifacts
only:
- tags
artifacts:

View File

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

View File

@@ -9,7 +9,7 @@ oauthlib
requests-oauthlib
requests-toolbelt
pypubsub
pygeocoder
geopy
arrow
python-dateutil
futures
@@ -25,6 +25,7 @@ urllib3
youtube-dl
python-vlc
pypiwin32
pywin32
certifi
backports.functools_lru_cache
cx_freeze
@@ -32,6 +33,21 @@ tweepy
twitter-text-parser
pyenchant
sqlitedict
cx-Logging
h11
h2
hpack
hstspreload
httpcore
httpx
hyperframe
rfc3986
sniffio
attrs
importlib-metadata
numpy
pillow
charset-normalizer
git+https://github.com/accessibleapps/libloader
git+https://github.com/accessibleapps/platform_utils
git+https://github.com/accessibleapps/accessible_output2

View File

@@ -15,10 +15,10 @@ SetCompressor /solid lzma
SetDatablockOptimize on
VIAddVersionKey ProductName "TWBlue"
VIAddVersionKey LegalCopyright "Copyright 2014-2021 Manuel Cortéz."
VIAddVersionKey ProductVersion "0.95"
VIAddVersionKey FileVersion "0.95"
VIProductVersion "0.95.0.0"
VIFileVersion "0.95.0.0"
VIAddVersionKey ProductVersion "0.95.0"
VIAddVersionKey FileVersion "0.95.0"
VIProductVersion "0.95.0"
VIFileVersion "0.95.0"
!insertmacro MUI_PAGE_WELCOME
!define MUI_LICENSEPAGE_RADIOBUTTONS
!insertmacro MUI_PAGE_LICENSE "license.txt"
@@ -32,7 +32,7 @@ var StartMenuFolder
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "French"
!insertmacro MUI_LANGUAGE "Spanish"
!insertmacro MUI_LANGUAGE "Italian"
@@ -73,9 +73,9 @@ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "U
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.95"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "http://twblue.es"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "https://twblue.es"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 95
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
SectionEnd

View File

@@ -1,95 +0,0 @@
!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "x64.nsh"
Unicode true
CRCCheck on
ManifestSupportedOS all
XPStyle on
Name "TWBlue"
OutFile "TWBlue_snapshot_setup.exe"
InstallDir "$PROGRAMFILES\twblue"
InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "InstallLocation"
RequestExecutionLevel admin
SetCompress auto
SetCompressor /solid lzma
SetDatablockOptimize on
VIAddVersionKey ProductName "TWBlue Snapshot version"
VIAddVersionKey LegalCopyright "Copyright 2014-2021 Manuel Cortéz."
VIAddVersionKey ProductVersion "8"
VIAddVersionKey FileVersion "8"
VIProductVersion "8.0.0.0"
VIFileVersion "8.0.0.0"
!insertmacro MUI_PAGE_WELCOME
!define MUI_LICENSEPAGE_RADIOBUTTONS
!insertmacro MUI_PAGE_LICENSE "license.txt"
!insertmacro MUI_PAGE_DIRECTORY
var StartMenuFolder
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_LINK "Visit TWBlue website"
!define MUI_FINISHPAGE_LINK_LOCATION "https://twblue.es"
!define MUI_FINISHPAGE_RUN "$INSTDIR\TWBlue.exe"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "French"
!insertmacro MUI_LANGUAGE "Spanish"
!insertmacro MUI_LANGUAGE "Italian"
!insertmacro MUI_LANGUAGE "Finnish"
!insertmacro MUI_LANGUAGE "Russian"
!insertmacro MUI_LANGUAGE "PortugueseBR"
!insertmacro MUI_LANGUAGE "Polish"
!insertmacro MUI_LANGUAGE "German"
!insertmacro MUI_LANGUAGE "Hungarian"
!insertmacro MUI_LANGUAGE "Turkish"
!insertmacro MUI_LANGUAGE "Arabic"
!insertmacro MUI_LANGUAGE "Galician"
!insertmacro MUI_LANGUAGE "Catalan"
!insertmacro MUI_LANGUAGE "Basque"
!insertmacro MUI_LANGUAGE "Croatian"
!insertmacro MUI_LANGUAGE "Japanese"
!insertmacro MUI_LANGUAGE "SerbianLatin"
!insertmacro MUI_LANGUAGE "Romanian"
!insertmacro MUI_RESERVEFILE_LANGDLL
Section
SetShellVarContext All
SetOutPath "$INSTDIR"
${If} ${RunningX64}
File /r TWBlue64\*
${Else}
File /r TWBlue\*
${EndIf}
CreateShortCut "$DESKTOP\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue on the web.lnk" "http://twblue.es"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
!insertmacro MUI_STARTMENU_WRITE_END
WriteUninstaller "$INSTDIR\Uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayName" "TWBlue"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "8"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "https://twblue.es"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
SectionEnd
Section "Uninstall"
SetShellVarContext All
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue"
RMDir /r /REBOOTOK $INSTDIR
Delete "$DESKTOP\TWBlue.lnk"
!insertmacro MUI_STARTMENU_GETFOLDER startmenu $StartMenuFolder
RMDir /r "$SMPROGRAMS\$StartMenuFolder"
SectionEnd
Function .onInit
${If} ${RunningX64}
StrCpy $instdir "$programfiles64\twblue"
${EndIf}
!insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd

View File

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

View File

@@ -3,15 +3,8 @@ import datetime
name = 'TWBlue'
short_name='twblue'
snapshot = True
if snapshot == False:
version = "0.95"
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"
update_url = 'https://twblue.es/updates/snapshot.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
update_url = 'https://twblue.es/updates/updates.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/updates.json'
authors = ["Manuel Cortéz", "José Manuel Delicado"]
authorEmail = "manuel@manuelcortez.net"
copyright = "Copyright (C) 2013-2021, Manuel cortéz."
@@ -20,3 +13,4 @@ translators = ["Manuel Cortéz (English)", "Mohammed Al Shara, Hatoun Felemban (
url = u"https://twblue.es"
report_bugs_url = "https://github.com/manuelcortez/twblue/issues"
supported_languages = []
version = "11"

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ import output
import config
import logging
from mysc.thread_utils import call_threaded
from tweepy.error import TweepError
from tweepy.errors import TweepyException
from pubsub import pub
from sessions.twitter import compose
from . import base
@@ -125,8 +125,8 @@ class PeopleBuffer(base.BaseBuffer):
val = results
val.reverse()
log.debug("Retrieved %d items from cursored search in function %s" % (len(val), self.function))
except TweepError as e:
log.error("Error %s: %s" % (e.api_code, e.reason))
except TweepyException as e:
log.exception("Error %s" % (str(e)))
return
number_of_items = self.session.order_people(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,))
@@ -155,8 +155,8 @@ class PeopleBuffer(base.BaseBuffer):
results = [i for i in items]
items = results
log.debug("Retrieved %d items from cursored search in function %s" % (len(items), self.function))
except TweepError as e:
log.error("Error %s: %s" % (e.api_code, e.reason))
except TweepyException as e:
log.exception("Error %s" % (str(e)))
return
if items == None:
return
@@ -252,8 +252,7 @@ class PeopleBuffer(base.BaseBuffer):
elif number_of_items > 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
output.speak(_(u"{0} new followers.").format(number_of_items))
def open_in_browser(self, *args, **kwargs):
def get_item_url(self, *args, **kwargs):
tweet = self.get_tweet()
output.speak(_(u"Opening item in web browser..."))
url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name)
webbrowser.open(url)
return url

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
import widgetUtils
from pubsub import pub
from wxUI.dialogs import userAliasDialogs
from extra import autocompletionUsers
class userAliasController(object):
def __init__(self, settings):
super(userAliasController, self).__init__()
self.settings = settings
self.dialog = userAliasDialogs.userAliasEditorDialog()
self.update_aliases_manager()
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.on_add)
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.on_edit)
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.on_remove)
pub.subscribe(self.update_aliases_manager, "alias-added")
self.dialog.ShowModal()
def update_aliases_manager(self):
self.dialog.users.Clear()
aliases = [self.settings["user-aliases"].get(k) for k in self.settings["user-aliases"].keys()]
if len(aliases) > 0:
self.dialog.users.InsertItems(aliases, 0)
self.dialog.on_selection_changes()
def on_add(self, *args, **kwargs):
pub.sendMessage("execute-action", action="add_alias")
def on_edit(self, *args, **kwargs):
selection = self.dialog.get_selected_user()
if selection != "":
edited = self.dialog.edit_alias_dialog(_("Edit alias for {}").format(selection))
if edited == None or edited == "":
return
for user_key in self.settings["user-aliases"].keys():
if self.settings["user-aliases"][user_key] == selection:
self.settings["user-aliases"][user_key] = edited
self.settings.write()
self.update_aliases_manager()
break
def on_remove(self, *args, **kwargs):
selection = self.dialog.get_selected_user()
if selection == None or selection == "":
return
should_remove = self.dialog.remove_alias_dialog()
if should_remove:
for user_key in self.settings["user-aliases"].keys():
if self.settings["user-aliases"][user_key] == selection:
self.settings["user-aliases"].pop(user_key)
self.settings.write()
self.update_aliases_manager()
break

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

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

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,3 @@
from __future__ import unicode_literals
from logging import getLogger
logger = getLogger('update')
@@ -24,8 +23,8 @@ def perform_update(endpoint, current_version, app_name='', password=None, update
if not available_update:
logger.debug("No update available")
return False
available_version = float(available_update['current_version'])
if not float(available_version) > float(current_version) or platform.system()+platform.architecture()[0][:2] not in available_update['downloads']:
available_version = available_update['current_version']
if available_version == current_version or platform.system()+platform.architecture()[0][:2] not in available_update['downloads']:
logger.debug("No update for this architecture")
return False
available_description = available_update.get('description', None)
@@ -42,10 +41,10 @@ def perform_update(endpoint, current_version, app_name='', password=None, update
downloaded = download_update(update_url, download_path, requests_session=requests_session, progress_callback=progress_callback)
extracted = extract_update(downloaded, update_path, password=password)
bootstrap_path = move_bootstrap(extracted)
execute_bootstrap(bootstrap_path, extracted)
logger.info("Update prepared for installation.")
if callable(update_complete_callback):
update_complete_callback()
execute_bootstrap(bootstrap_path, extracted)
logger.info("Update prepared for installation.")
def create_requests_session(app_name=None, version=None):
user_agent = ''

View File

@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
import application
from . import update
import platform

29
src/write_version_data.py Normal file
View File

@@ -0,0 +1,29 @@
#! /usr/bin/env python# -*- coding: iso-8859-1 -*-
""" Write version info (taken from the last commit) to application.py. This method has been implemented this way for running updates.
This file is not intended to be called by the user. It will be used only by the Gitlab CI runner."""
import requests
from codecs import open
print("Writing version data for update...")
commit_info = requests.get("https://gitlab.com/api/v4/projects/23482196/repository/commits/next-gen")
commit_info = commit_info.json()
commit = commit_info["short_id"]
print("Got new version info: {commit}".format(commit=commit,))
file = open("application.py", "r", encoding="utf-8")
lines = file.readlines()
lines[-1] = 'version = "{}"'.format(commit_info["created_at"][:10].replace("-", "."))
file.close()
file2 = open("application.py", "w", encoding="utf-8")
file2.writelines(lines)
file2.close()
print("Wrote application.py with the new version info.")
print("Updating next version on installer setup...")
file = open("..\\scripts\\twblue.nsi", "r", encoding="utf-8")
contents = file.read()
contents = contents.replace("0.95", commit_info["created_at"][:10].replace("-", "."))
file.close()
file2 = open("..\\scripts\\twblue.nsi", "w", encoding="utf-8")
file2.write(contents)
file2.close()
print("done")

View File

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

View File

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

View File

@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
import wx
import gettext
from . import baseDialog
class addAliasDialog(baseDialog.BaseWXDialog):
def __init__(self, title, users):
super(addAliasDialog, self).__init__(parent=None, id=wx.ID_ANY, title=title)
panel = wx.Panel(self)
userSizer = wx.BoxSizer()
self.cb = wx.ComboBox(panel, -1, choices=users, value=users[0], size=wx.DefaultSize)
self.cb.SetFocus()
self.autocompletion = wx.Button(panel, -1, _(u"&Autocomplete users"))
userSizer.Add(wx.StaticText(panel, -1, _(u"User")), 0, wx.ALL, 5)
userSizer.Add(self.cb, 0, wx.ALL, 5)
userSizer.Add(self.autocompletion, 0, wx.ALL, 5)
aliasSizer = wx.BoxSizer(wx.HORIZONTAL)
aliasLabel = wx.StaticText(panel, wx.ID_ANY, _("Alias"))
self.alias = wx.TextCtrl(panel, wx.ID_ANY)
aliasSizer.Add(aliasLabel, 0, wx.ALL, 5)
aliasSizer.Add(self.alias, 0, wx.ALL, 5)
sizer = wx.BoxSizer(wx.VERTICAL)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
btnsizer = wx.BoxSizer()
btnsizer.Add(ok, 0, wx.ALL, 5)
btnsizer.Add(cancel, 0, wx.ALL, 5)
sizer.Add(userSizer, 0, wx.ALL, 5)
sizer.Add(aliasSizer, 0, wx.ALL, 5)
sizer.Add(btnsizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def get_user(self):
return (self.cb.GetValue(), self.alias.GetValue())
class userAliasEditorDialog(wx.Dialog):
def __init__(self, *args, **kwds):
super(userAliasEditorDialog, self).__init__(parent=None)
self.SetTitle(_("Edit user aliases"))
main_sizer = wx.BoxSizer(wx.VERTICAL)
userListSizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Users")), wx.VERTICAL)
main_sizer.Add(userListSizer, 1, wx.EXPAND, 0)
self.users = wx.ListBox(self, wx.ID_ANY, choices=[])
self.users.Bind(wx.EVT_LISTBOX, self.on_selection_changes)
userListSizer.Add(self.users, 0, 0, 0)
actionsSizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Actions")), wx.HORIZONTAL)
main_sizer.Add(actionsSizer, 1, wx.EXPAND, 0)
self.add = wx.Button(self, wx.ID_ANY, _("Add alias"))
self.add.SetToolTip(_("Adds a new user alias"))
actionsSizer.Add(self.add, 0, 0, 0)
self.edit = wx.Button(self, wx.ID_ANY, _("Edit"))
self.edit.SetToolTip(_("Edit the currently focused user Alias."))
self.edit.Enable(False)
actionsSizer.Add(self.edit, 0, 0, 0)
self.remove = wx.Button(self, wx.ID_ANY, _("Remove"))
self.remove.SetToolTip(_("Remove the currently focused user alias."))
self.remove.Enable(False)
actionsSizer.Add(self.remove, 0, 0, 0)
btnSizer = wx.StdDialogButtonSizer()
main_sizer.Add(btnSizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
self.button_CLOSE = wx.Button(self, wx.ID_CLOSE, "")
btnSizer.AddButton(self.button_CLOSE)
btnSizer.Realize()
self.SetSizer(main_sizer)
main_sizer.Fit(self)
self.SetEscapeId(self.button_CLOSE.GetId())
self.Layout()
def on_selection_changes(self, *args, **kwargs):
selection = self.users.GetSelection()
if selection == -1:
self.enable_action_buttons(False)
else:
self.enable_action_buttons(True)
def get_selected_user(self):
return self.users.GetStringSelection()
def remove_alias_dialog(self, *args, **kwargs):
dlg = wx.MessageDialog(self, _("Are you sure you want to delete this user alias?"), _("Remove user alias"), wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
return True
else:
return False
def enable_action_buttons(self, enabled=True):
self.edit.Enable(enabled)
self.remove.Enable(enabled)
def edit_alias_dialog(self, title):
dlg = wx.TextEntryDialog(self, title, _("User alias"))
if dlg.ShowModal() == wx.ID_OK:
return dlg.GetValue()

View File

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

View File

@@ -20,6 +20,7 @@ class mainFrame(wx.Frame):
self.show_hide = app.Append(wx.ID_ANY, _(u"&Hide window"))
self.menuitem_search = app.Append(wx.ID_ANY, _(u"&Search"))
self.lists = app.Append(wx.ID_ANY, _(u"&Lists manager"))
self.manageAliases = app.Append(wx.ID_ANY, _("Manage user aliases"))
self.keystroke_editor = app.Append(wx.ID_ANY, _(u"&Edit keystrokes"))
self.account_settings = app.Append(wx.ID_ANY, _(u"Account se&ttings"))
self.prefs = app.Append(wx.ID_PREFERENCES, _(u"&Global settings"))
@@ -43,6 +44,7 @@ class mainFrame(wx.Frame):
self.follow = user.Append(wx.ID_ANY, _(u"&Actions..."))
self.timeline = user.Append(wx.ID_ANY, _(u"&View timeline..."))
self.dm = user.Append(wx.ID_ANY, _(u"Direct me&ssage"))
self.addAlias = user.Append(wx.ID_ANY, _("Add a&lias"))
self.addToList = user.Append(wx.ID_ANY, _(u"&Add to list"))
self.removeFromList = user.Append(wx.ID_ANY, _(u"R&emove from list"))
self.viewLists = user.Append(wx.ID_ANY, _(u"&View lists"))

View File

@@ -1,6 +0,0 @@
{"current_version": "0.95",
"description": "The first version for the new generation of TWBlue.",
"date": "day_name_abr month day_numb, 2016",
"downloads":
{"Windows32": "http://twblue.es/pubs/twblue_ngen_0.80_x86.zip",
"Windows64": "http://twblue.es/pubs/twblue_ngen_0.80_x64.zip"}}

View File

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