mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-08-26 01:49:22 +00:00
Compare commits
310 Commits
alpha1
...
v2021.10.3
Author | SHA1 | Date | |
---|---|---|---|
5a6dc23524 | |||
0e2ff4de8a | |||
36340d0596 | |||
92ec178754 | |||
5eb942981c | |||
6739045cce | |||
307ed093af | |||
d11fc44772 | |||
a3e5eec6de | |||
41a0935121 | |||
0b03e7505f | |||
![]() |
81c364c4e1 | ||
![]() |
483b196203 | ||
![]() |
b512c69447 | ||
2c1608322e | |||
c6bb851bce | |||
![]() |
66581f8b1c | ||
![]() |
c6a3a44c21 | ||
bd25cfa59b | |||
![]() |
39a02ea33a | ||
f672dca1cb | |||
19c63b7b07 | |||
e23a52e38f | |||
![]() |
d888563fda | ||
a5ba80feee | |||
528ecc2a33 | |||
3519746078 | |||
ef79e0696e | |||
b9ee0dae5b | |||
f31575a733 | |||
e451bbd5e9 | |||
f7f303929e | |||
9f48784ce4 | |||
cb1312d0c9 | |||
a82efd4dcc | |||
72e6d030d5 | |||
d222740887 | |||
2b059ee42e | |||
daac312658 | |||
b23be9c896 | |||
61b0dc34b8 | |||
c5d13369eb | |||
856ecf5eb9 | |||
e3e0ac9457 | |||
fccf02794c | |||
34c1f69ec1 | |||
7326ff88f9 | |||
![]() |
a8d876a7b7 | ||
![]() |
89fa6435b4 | ||
![]() |
d1bd393be2 | ||
301bd5fd39 | |||
![]() |
a2f25bfbb5 | ||
286e030f40 | |||
d8fca3b31a | |||
0c27427843 | |||
dfdbe3c5f4 | |||
![]() |
2222a97451 | ||
fbe93ea4be | |||
4bcae1aa97 | |||
4cabf5b9cd | |||
a9a4189295 | |||
c7b6d69518 | |||
65512a9862 | |||
![]() |
17ea8af050 | ||
![]() |
43578a32eb | ||
7c34204d17 | |||
3a5c1c10d3 | |||
f9864a887d | |||
3d8519313e | |||
f4ecf10885 | |||
c67b415934 | |||
10511d3022 | |||
![]() |
ddc80a29fd | ||
9ea36a26d2 | |||
97286496fc | |||
![]() |
6436af76f5 | ||
![]() |
576b5064c0 | ||
![]() |
342265b3c0 | ||
![]() |
54938ecb6c | ||
![]() |
7aff8252d2 | ||
![]() |
9c680130f7 | ||
24d1ad093d | |||
b2b9cd810f | |||
582be54dea | |||
ff0fbeafa3 | |||
e314cf0599 | |||
![]() |
e7b72112cf | ||
![]() |
70c095febe | ||
6119b029f8 | |||
b74cd9a73d | |||
8ff6809f08 | |||
39af9d8623 | |||
3688d7548c | |||
07f9afb14e | |||
de12dadac2 | |||
877c909482 | |||
![]() |
1206aba83b | ||
![]() |
fcd631b2de | ||
![]() |
86130954d7 | ||
![]() |
23a56c637d | ||
![]() |
d5ac0db67b | ||
bb4869b7be | |||
44b6e82183 | |||
![]() |
5268f166f8 | ||
![]() |
37ad6b5fbf | ||
![]() |
bcc72c932d | ||
b9a9bd03c2 | |||
e6543bcf77 | |||
03b61946f8 | |||
8fe2f4c64d | |||
37af722556 | |||
4312ad82e7 | |||
e9e8a8fba9 | |||
5cad4ab2a7 | |||
01dd93e076 | |||
d301f841e3 | |||
81d18d4656 | |||
ccba22cfd2 | |||
465b550c30 | |||
788811bf6c | |||
c926355048 | |||
84cbf5c497 | |||
7eb2d8930f | |||
864ebdf96d | |||
ee9a92bcb4 | |||
818bc243e4 | |||
062289a977 | |||
56a1c57e04 | |||
3c7063792c | |||
77eadb42bb | |||
9053fcd5de | |||
5f11467f27 | |||
55b1c7bdae | |||
ba90842185 | |||
8fd3041efd | |||
bb5ead80de | |||
168c7e7a5d | |||
a7838bbf7d | |||
fe8b58a7b9 | |||
a9f52b3a94 | |||
13c47f7b9f | |||
3515df9b15 | |||
f998fa62a6 | |||
a6032cae46 | |||
7935f79d77 | |||
ef443346d1 | |||
a27eee1fa2 | |||
b839dc077c | |||
2b719858c2 | |||
97afc379e8 | |||
7f401ba789 | |||
ff22ae5653 | |||
02d94fcea0 | |||
df2015f360 | |||
64a14c831b | |||
fbbe7852c2 | |||
9dfccd2bd0 | |||
969a75e9f3 | |||
a59ba5ef78 | |||
3ebfdbc48b | |||
8db14a95c1 | |||
1b9062d86f | |||
4b60a79e49 | |||
002e1ccb55 | |||
0bcdf88290 | |||
0612c653b8 | |||
![]() |
c5dadb063a | ||
35d6010298 | |||
40a63d9e16 | |||
5712dd735b | |||
2c75ea5005 | |||
e35f37fcc2 | |||
71358ea74d | |||
b8f822830f | |||
74e4fe6357 | |||
77bee64421 | |||
c761230566 | |||
49505fabcd | |||
4ad01d7833 | |||
ab1a13f886 | |||
![]() |
44c25e54f8 | ||
cdabd6f055 | |||
60144a6b08 | |||
382acf7c8c | |||
![]() |
03ba59028f | ||
![]() |
50125fc55a | ||
39e1fb017c | |||
2aaa4eced3 | |||
![]() |
6d2eac5b1c | ||
![]() |
40040d1b17 | ||
2a791d43bf | |||
b10aeb046d | |||
7d6e230fd9 | |||
9346bba7a0 | |||
30f739c42e | |||
eb0679cb96 | |||
45deae3402 | |||
5b0b26799d | |||
ee234b80a7 | |||
0065af2aef | |||
9c086cfa0f | |||
2f263a23b7 | |||
9cb6eafbbc | |||
cba7c39a0e | |||
e2e8b84e6a | |||
eca0c0dbbd | |||
36cc3f9365 | |||
63d7cbe7c4 | |||
ae57cc3404 | |||
23df8f8a7f | |||
2f278b7f3c | |||
9444939c35 | |||
6688dc1163 | |||
6a7300b35f | |||
c95a2feb5e | |||
8042d28b13 | |||
890359f8c7 | |||
91955e80d2 | |||
6c1c66226d | |||
1a76913aac | |||
f85af2cbd2 | |||
351f700927 | |||
![]() |
abdde4c1f0 | ||
![]() |
4899285eca | ||
![]() |
fb3e6b537c | ||
![]() |
22d1cc9ce9 | ||
304d91e8b7 | |||
cf650052e4 | |||
![]() |
10055788e4 | ||
![]() |
a67f3f037e | ||
![]() |
4d736c00fc | ||
![]() |
29b8a645db | ||
![]() |
2459a499ce | ||
28f4e3a534 | |||
e2931e96eb | |||
![]() |
fbcba39e69 | ||
![]() |
b59d262dca | ||
![]() |
7f4a13231f | ||
![]() |
4ab5af2ae9 | ||
![]() |
4c4eaf4012 | ||
![]() |
b03198a39f | ||
![]() |
06dc26e962 | ||
![]() |
bcc8f5968e | ||
![]() |
4c144d2f7e | ||
747290e16a | |||
![]() |
64b6d4df74 | ||
![]() |
22d48cb5d9 | ||
![]() |
fea01c44ca | ||
b80a342f92 | |||
60a13d974c | |||
4c9000839d | |||
b3c24c6734 | |||
a2a8cc5b79 | |||
da7a208c1f | |||
979a3d3e99 | |||
1f11ea7aa0 | |||
8bdc933bce | |||
999cbba464 | |||
7457521398 | |||
366b61134e | |||
52154ac271 | |||
cdc285e362 | |||
4643301764 | |||
f546543e9b | |||
85fe94ec0c | |||
db13c96baa | |||
33b6000b41 | |||
a261365682 | |||
4064582583 | |||
fc0da0bdbb | |||
76c678d4ba | |||
3db8b7b021 | |||
fa49637d0e | |||
65ec231349 | |||
93705bf534 | |||
29a905199d | |||
8569d9b0a0 | |||
1e1f2b089f | |||
78cc6e6784 | |||
a37f339fea | |||
d0cc12ef5c | |||
bcd51d6259 | |||
7ceb806af2 | |||
![]() |
8f35fc0ec0 | ||
![]() |
3f79fab8e5 | ||
![]() |
53dee8cb81 | ||
56103466fa | |||
13f5df3a48 | |||
a89cc35d40 | |||
276cd4b4dd | |||
c509433b2c | |||
7292b36137 | |||
![]() |
0111c8aae1 | ||
eefe5e6200 | |||
492f1d8aa5 | |||
ac20ced5fa | |||
866cd53328 | |||
f523e3e81e | |||
bdd7d617c3 | |||
a0bc5f6cb3 | |||
3f238635f2 | |||
c300476ad2 | |||
6a2e00c467 | |||
4fa983a314 | |||
ed5b66d033 | |||
bec45c37c1 | |||
ad42c09fef | |||
084fa1894c | |||
6bc63d79ff | |||
b1f06f6b1d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,3 +20,4 @@ release-snapshot/
|
||||
src/com_cache/
|
||||
doc/strings.py
|
||||
doc/changelog.py
|
||||
env/
|
123
.gitlab-ci.yml
Normal file
123
.gitlab-ci.yml
Normal file
@@ -0,0 +1,123 @@
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
PYTHON: "C:\\python37\\python.exe"
|
||||
NSIS: "C:\\program files (x86)\\nsis\\makensis.exe"
|
||||
|
||||
stages:
|
||||
- build
|
||||
- make_installer
|
||||
- upload
|
||||
|
||||
twblue32:
|
||||
tags:
|
||||
- shared-windows
|
||||
- windows
|
||||
- windows-1809
|
||||
before_script:
|
||||
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
|
||||
- echo ${time}
|
||||
- echo "started by ${GITLAB_USER_NAME}"
|
||||
- choco install python --version 3.7.9 -y -ForceX86
|
||||
- '&$env:PYTHON -V'
|
||||
- '&$env:PYTHON -m pip install --upgrade pip'
|
||||
- '&$env:PYTHON -m pip install --upgrade -r requirements.txt'
|
||||
stage: build
|
||||
interruptible: true
|
||||
script:
|
||||
# Create html documentation firstly.
|
||||
- cd doc
|
||||
- '&$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
|
||||
- cd scripts
|
||||
- '&$env:PYTHON make_archive.py'
|
||||
- cd ..
|
||||
- mv src/dist artifacts/TWBlue
|
||||
- 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:
|
||||
paths:
|
||||
- artifacts
|
||||
expire_in: 1 day
|
||||
|
||||
twblue64:
|
||||
tags:
|
||||
- shared-windows
|
||||
- windows
|
||||
- windows-1809
|
||||
before_script:
|
||||
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
|
||||
- echo ${time}
|
||||
- echo "started by ${GITLAB_USER_NAME}"
|
||||
- choco install python --version 3.7.9 -y
|
||||
- '&$env:PYTHON -V'
|
||||
- '&$env:PYTHON -m pip install --upgrade pip'
|
||||
- '&$env:PYTHON -m pip install --upgrade -r requirements.txt'
|
||||
stage: build
|
||||
interruptible: true
|
||||
script:
|
||||
# Create html documentation firstly.
|
||||
- cd doc
|
||||
- '&$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
|
||||
- cd scripts
|
||||
- '&$env:PYTHON make_archive.py'
|
||||
- cd ..
|
||||
- mv src/dist artifacts/TWBlue64
|
||||
- move src/twblue.zip artifacts/twblue_x64.zip
|
||||
only:
|
||||
- tags
|
||||
artifacts:
|
||||
paths:
|
||||
- artifacts
|
||||
expire_in: 1 day
|
||||
|
||||
generate_versions:
|
||||
stage: make_installer
|
||||
tags:
|
||||
- shared-windows
|
||||
- windows
|
||||
- windows-1809
|
||||
before_script:
|
||||
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
|
||||
- echo ${time}
|
||||
- echo "started by ${GITLAB_USER_NAME}"
|
||||
- choco install nsis -y -ForceX86
|
||||
script:
|
||||
- move artifacts/TWBlue scripts/
|
||||
- move artifacts/TWBlue64 scripts/
|
||||
- move artifacts/twblue.nsi scripts/installer.nsi
|
||||
- cd scripts
|
||||
- '&$env:NSIS installer.nsi'
|
||||
- move twblue_setup.exe ../artifacts
|
||||
only:
|
||||
- tags
|
||||
artifacts:
|
||||
paths:
|
||||
- artifacts
|
||||
expire_in: 1 day
|
||||
|
||||
upload:
|
||||
stage: upload
|
||||
tags:
|
||||
- linux
|
||||
image: python
|
||||
interruptible: true
|
||||
script:
|
||||
- cd artifacts
|
||||
- python ../scripts/upload.py
|
||||
only:
|
||||
- tags
|
||||
- schedules
|
42
README.md
42
README.md
@@ -24,45 +24,37 @@ This document describes how to run tw blue from source and how to build a binary
|
||||
### Required dependencies.
|
||||
|
||||
Although most dependencies can be found in the windows-dependencies directory, we provide links to their official websites. If you are cloning with git, don't forget to initialize and update the submodules to get the windows-dependencies folder. You can use these two commands to perform this task from git bash:
|
||||
```
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
All the dependencies provided in this folder are prebuilt. If you want to build them from source, you will need Microsoft visual Studio 2008.
|
||||
```
|
||||
|
||||
#### Dependencies packaged in windows installers
|
||||
|
||||
* [Python,](http://python.org) version 2.7.16
|
||||
If you want to build both x86 and x64 binaries, you can install python x86 to C:\python27 and python x64 to C:\python27x64, for example.
|
||||
* [PyEnchant,](http://pythonhosted.org/pyenchant/) version 1.6.6.
|
||||
x64 version has been built by TWBlue developers, so you only will find it in windows-dependencies folder
|
||||
|
||||
The windows installers are available only in the windows-dependencies folder.
|
||||
|
||||
To build a binary version:
|
||||
|
||||
* [Py2exe](http://www.sourceforge.net/projects/py2exe/) for Python 2.7, version 0.6.9
|
||||
* [Python,](https://python.org) version 3.8.7
|
||||
If you want to build both x86 and x64 binaries, you can install python x86 to C:\python38 and python x64 to C:\python38x64, for example.
|
||||
|
||||
#### Dependencies that must be installed using pip
|
||||
|
||||
Python installs a tool called Pip that allows to install packages in a simple way. You can find it in the python scripts directory. To install packages using Pip, you have to navigate to the scripts directory using a command prompt, for example:
|
||||
|
||||
cd C:\python27x64\scripts
|
||||
`cd C:\python37x64\scripts`
|
||||
|
||||
You can also add the scripts folder to your path environment variable or choose the corresponding option when installing Python.
|
||||
Note: pip and setuptools are included in the Python installer since version 2.7.9.
|
||||
|
||||
Pip is able to install packages listed in a special text file, called the requirements file. To install all remaining dependencies, perform the following command:
|
||||
|
||||
pip install -r requirements.txt
|
||||
`pip install -r requirements.txt`
|
||||
|
||||
Note that if you perform the command from the path where Pip is located, you need to specify the path to your Tw Blue root folder where the requirements file is located, for example:
|
||||
|
||||
pip install -r D:\repos\TwBlue\requirements.txt
|
||||
`pip install -r D:\repos\TwBlue\requirements.txt`
|
||||
|
||||
Pip will automatically get the additional libraries that the listed packages need to work properly.
|
||||
If you need to update your dependencies, perform the following command:
|
||||
|
||||
pip install --upgrade -r requirements.txt
|
||||
`pip install --upgrade -r requirements.txt`
|
||||
|
||||
#### Other dependencies
|
||||
|
||||
@@ -71,7 +63,7 @@ These dependencies are located in the windows-dependencies directory. You don't
|
||||
* Bootstrap 1.2.1: included in dependencies directory.
|
||||
This dependency has been built using pure basic 4.61. Its source can be found at http://hg.q-continuum.net/updater
|
||||
* [oggenc2.exe,](http://www.rarewares.org/ogg-oggenc.php) version 2.87
|
||||
* Microsoft Visual c++ 2008 redistributable dlls.
|
||||
* Microsoft Visual c++ 2019 redistributable dlls.
|
||||
* VLC plugins and DLL libraries.
|
||||
|
||||
#### Dependencies required to build the installer
|
||||
@@ -94,7 +86,7 @@ In order to add the support for spell checking in more languages than english yo
|
||||
|
||||
Now that you have installed all these packages, you can run TW Blue from source using a command prompt. Navigate to the repo's `src` directory, and type the following command:
|
||||
|
||||
python main.py
|
||||
`python main.py`
|
||||
|
||||
If necessary, change the first part of the command to reflect the location of your python executable. You can run TW Blue using python x86 and x64.
|
||||
|
||||
@@ -102,8 +94,8 @@ Now that you have installed all these packages, you can run TW Blue from source
|
||||
|
||||
To generate the documentation in html format, navigate to the doc folder inside this repo. After that, run these commands:
|
||||
|
||||
python document_importer.py
|
||||
python generator.py
|
||||
`python document_importer.py`
|
||||
`python generator.py`
|
||||
|
||||
The documentation will be generated, placing each language in a separate folder in the doc directory. Move these folders (for example `de`, `en`, `es`, `fr`, `it`, ...) to `src/documentation`, creating the directory if necessary.
|
||||
Also, copy the `license.txt` file located in the root of the repo to the documentation folder.
|
||||
@@ -114,7 +106,7 @@ A binary version doesn't need python and the other dependencies to run, it's the
|
||||
|
||||
To build it, run the following command from the src folder:
|
||||
|
||||
python setup.py py2exe
|
||||
`python setup.py build`
|
||||
|
||||
You will find the binaries in the dist directory.
|
||||
|
||||
@@ -122,9 +114,9 @@ To build it, run the following command from the src folder:
|
||||
|
||||
If you want to install TWBlue on your computer, you must create the installer first. Follow these steps:
|
||||
|
||||
* Navigate to the src directory, and create a binary version for x86: C:\python27\python setup.py py2exe
|
||||
* Navigate to the src directory, and create a binary version for x86: C:\python37\python setup.py build
|
||||
* Move the dist directory to the scripts folder in this repo, and rename it to twblue
|
||||
* Repeat these steps with Python for x64: C:\python27x64\python setup.py py2exe
|
||||
* Repeat these steps with Python for x64: C:\python37x64\python setup.py build
|
||||
* Move the new dist directory to the scripts folder, and rename it to twblue64
|
||||
* Go to the scripts folder, right click on the twblue.nsi file, and choose compyle unicode NSIS script
|
||||
* This may take a while. After the process, you will find the installer in the scripts folder
|
||||
@@ -137,9 +129,9 @@ Run the gen_pot.bat file, located in the tools directory. Your python installati
|
||||
|
||||
If you want to have TWBlue on your PortableApps.com platform, follow these steps:
|
||||
|
||||
* Navigate to the src directory, and create a binary version for x86: C:\python27\python setup.py py2exe
|
||||
* Navigate to the src directory, and create a binary version for x86: C:\python37\python setup.py build
|
||||
* Move the dist directory to the misc\pa.c format\app folder in this repo, and rename it to twblue
|
||||
* Repeat these steps with Python for x64: C:\python27x64\python setup.py py2exe
|
||||
* Repeat these steps with Python for x64: C:\python37x64\python setup.py build
|
||||
* Move the new dist directory to the misc\pa.c format\app folder, and rename it to twblue64
|
||||
* Run the PortableApps.com Launcher Generator, and follow the wizard. Choose the pa.c format folder and continue to generate the launcher. If the wizard is completed, you will see a file named TWBlue portable.exe inside the pa.c format folder.
|
||||
* Run the PortableApps.com Installer, and follow the wizard. As in the above step, choose the pa.c format folder. When it completes, you will see a file named TWBluePortable_x.y.paf.exe inside the misc folder, where x.y is the version number.
|
||||
|
84
appveyor.yml
84
appveyor.yml
@@ -1,84 +0,0 @@
|
||||
pull_requests:
|
||||
# Avoid building after pull requests. Shall we disable this option?
|
||||
do_not_increment_build_number: true
|
||||
|
||||
# Only build whenever we add tags to the repo.
|
||||
skip_non_tags: true
|
||||
|
||||
environment:
|
||||
|
||||
matrix:
|
||||
|
||||
# List of python versions we want to work with.
|
||||
- PYTHON: "C:\\Python37"
|
||||
PYTHON_VERSION: "3.7.x" # currently 2.7.9
|
||||
PYTHON_ARCH: "32"
|
||||
|
||||
# perhaps we may enable this one in future?
|
||||
# - PYTHON: "C:\\Python37-x64"
|
||||
# PYTHON_VERSION: "3.7.x" # currently 2.7.9
|
||||
# PYTHON_ARCH: "64"
|
||||
|
||||
# This is important so we will retrieve everything in submodules as opposed to default method.
|
||||
clone_script:
|
||||
- cmd: >-
|
||||
git clone -q --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER%
|
||||
&& cd %APPVEYOR_BUILD_FOLDER%
|
||||
&& git checkout -qf %APPVEYOR_REPO_COMMIT%
|
||||
&& git submodule update --init --recursive
|
||||
|
||||
install:
|
||||
# If there is a newer build queued for the same PR, cancel this one.
|
||||
# The AppVeyor 'rollout builds' option is supposed to serve the same
|
||||
# purpose but it is problematic because it tends to cancel builds pushed
|
||||
# directly to master instead of just PR builds (or the converse).
|
||||
# credits: JuliaLang developers.
|
||||
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
|
||||
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
|
||||
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
|
||||
throw "There are newer queued builds for this pull request, failing early." }
|
||||
# - ECHO "Filesystem root:"
|
||||
# - ps: "ls \"C:/\""
|
||||
|
||||
# Check that we have the expected version and architecture for Python
|
||||
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
||||
- "python --version"
|
||||
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
|
||||
|
||||
# Upgrade to the latest version of pip to avoid it displaying warnings
|
||||
# about it being out of date.
|
||||
- "python -m pip install --upgrade pip setuptools"
|
||||
|
||||
# Install the build dependencies of the project. If some dependencies contain
|
||||
# compiled extensions and are not provided as pre-built wheel packages,
|
||||
# pip will build them from source using the MSVC compiler matching the
|
||||
# target Python version and architecture
|
||||
- "%CMD_IN_ENV% pip install -r requirements.txt"
|
||||
- "%CMD_IN_ENV% pip install pyenchant"
|
||||
|
||||
build_script:
|
||||
# Build documentation at first, so setup.py won't fail when copying everything.
|
||||
- "cd doc"
|
||||
# Import documentation before building, so strings.py will be created.
|
||||
- "%CMD_IN_ENV% python documentation_importer.py"
|
||||
# build doc from src folder so it will generate result files right there.
|
||||
- "cd ..\\src"
|
||||
- "%CMD_IN_ENV% python ..\\doc\\generator.py"
|
||||
# Build distributable files.
|
||||
- "%CMD_IN_ENV% python setup.py build"
|
||||
- "cd dist"
|
||||
# Zip it all.
|
||||
- cmd: 7z a ..\..\snapshot_python3.zip *
|
||||
|
||||
artifacts:
|
||||
- path: snapshot.zip
|
||||
|
||||
deploy:
|
||||
- provider: FTP
|
||||
host: twblue.es
|
||||
protocol: ftp
|
||||
beta: true
|
||||
username: twblue
|
||||
password:
|
||||
secure: ml/xB8YEoZ7DmjzDr+KSNw==
|
||||
folder: 'web/pubs'
|
@@ -39,4 +39,5 @@ florian Ionașcu
|
||||
Christian Leo Mameli
|
||||
Natalia Hedlund (Наталья Хедлунд)
|
||||
Valeria (Валерия)
|
||||
Corentin Bacqué-Cazenave
|
||||
Oreonan
|
||||
Artem Plaksin (maniyax)
|
||||
|
@@ -2,7 +2,43 @@
|
||||
|
||||
## changes in this version
|
||||
|
||||
* Fixed error when displaying an URL at the end of a line, when the tweet or direct message contained multiple lines. Now the URL should be displayed correctly. ((#305,)[https://github.com/manuelcortez/TWBlue/issues/305])
|
||||
* 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))
|
||||
* We just implemented some changes in the way TWBlue handles tweets in order to reduce its RAM memory usage [#380](https://github.com/manuelcortez/TWBlue/pull/380):
|
||||
* We reduced the tweets size by storing only the tweet fields we currently use. This should reduce tweet's size in memory for every object up to 75%.
|
||||
* When using the cache database to store your tweets, there is a new setting present in the account settings dialog, in the general tab. This setting allows you to control whether TWBlue will load the whole database into memory (which is the current behaviour) or not.
|
||||
* Loading the whole database into memory has the advantage of being extremely fast to access any element (for example when moving through tweets in a buffer), but it requires more memory as the tweet buffers grow up. This should, however, use less memory than before thanks to the optimizations performed in tweet objects. If you have a machine with enough memory, this should be a good option for your case.
|
||||
* If you uncheck this setting, TWBlue will read the whole database from disk. This is significantly slower, but the advantage of this setting is that it will consume almost no extra memory, no matter how big is the tweets dataset. Be ware, though, that TWBlue might start to feel slower when accessing elements (for example when reading tweets) as the buffers grow up. This setting is suggested for computers with low memory or for those people not wanting to keep a really big amount of tweets stored.
|
||||
* Changed the label in the direct message's text control so it will indicate that the user needs to write the text there, without referring to any username in particular. ([#366,](https://github.com/manuelcortez/TWBlue/issues/366))
|
||||
* TWBlue will take Shift+F10 again as the contextual menu key in the list of items in a buffer. This stopped working after we have migrated to WX 4.1. ([#353,](https://github.com/manuelcortez/TWBlue/issues/353))
|
||||
* TWBlue should render correctly retweets of quoted tweets. ([#365,](https://github.com/manuelcortez/TWBlue/issues/365))
|
||||
* Fixed an error that was causing TWBlue to be unable to output to screen readers at times. ([#369,](https://github.com/manuelcortez/TWBlue/issues/369))
|
||||
* Fixed autocomplete users feature. ([#367,](https://github.com/manuelcortez/TWBlue/issues/367))
|
||||
* Fixed error when displaying an URL at the end of a line, when the tweet or direct message contained multiple lines. Now the URL should be displayed correctly. ([#305,](https://github.com/manuelcortez/TWBlue/issues/305) [#272,](https://github.com/manuelcortez/TWBlue/issues/272))
|
||||
* TWBlue has been migrated completely to Python 3 (currently, the software builds with Python 3.8).
|
||||
* TWBlue should be restarted gracefully. Before, the application was alerting users of not being closed properly every time the application restarted by itself.
|
||||
* If TWBlue attemps to load an account with invalid tokens (this happens when reactivating a previously deactivated account, or when access to the ap is revoqued), TWBlue will inform the user about this error and will skip the account. Before, the app was unable to start due to a critical error. ([#328,](https://github.com/manuelcortez/TWBlue/issues/328))
|
||||
* When sending a direct message, the title of the window will change appropiately when the recipient is edited. ([#276,](https://github.com/manuelcortez/TWBlue/issues/276))
|
||||
* URL'S in user profiles are expanded automatically. ([#275,](https://github.com/manuelcortez/TWBlue/issues/275))
|
||||
* TWBlue now uses [Tweepy,](https://github.com/tweepy/tweepy) to connect with Twitter. We have adopted this change in order to support Twitter'S API V 2 in the very near future. ([#333,](https://github.com/manuelcortez/TWBlue/issues/337) [#347](https://github.com/manuelcortez/TWBlue/pull/347))
|
||||
* TWBlue can upload images in Tweets and replies again. ([#240,](https://github.com/manuelcortez/TWBlue/issues/240))
|
||||
* Fixed the way we use to count characters in Twitter. The new methods in TWBlue take into account special characters and URLS as documented in Twitter. ([#199,](https://github.com/manuelcortez/TWBlue/issues/199) [#315](https://github.com/manuelcortez/TWBlue/issues/315))
|
||||
* Proxy support now works as expected.
|
||||
* Changed translation service from yandex.translate to Google Translator. ([#355,](https://github.com/manuelcortez/TWBlue/issues/355))
|
||||
* Improved method to load direct messages in the buffers. Now it should be faster due to less calls to Twitter API performed from the client.
|
||||
* And more. ([#352,](https://github.com/manuelcortez/TWBlue/issues/352))
|
||||
|
||||
## Changes in version 0.95
|
||||
|
||||
|
@@ -16,9 +16,9 @@ def prepare_documentation_in_file(fileSource, fileDest):
|
||||
if "\n" == i:
|
||||
newvar = "\"\","
|
||||
elif "\n" == i[-1]:
|
||||
newvar = "_(u\"\"\"%s\"\"\"),\n" % (i[:-1])
|
||||
newvar = "\"\"\"%s\"\"\",\n" % (i[:-1])
|
||||
else:
|
||||
newvar = "_(u\"\"\"%s\"\"\"),\n" % (i)
|
||||
newvar = "\"\"\"%s\"\"\",\n" % (i)
|
||||
f2.write(newvar)
|
||||
f1.close()
|
||||
f2.write("]")
|
||||
|
@@ -8,28 +8,27 @@ import shutil
|
||||
from codecs import open as _open
|
||||
from importlib import reload
|
||||
|
||||
def change_language(name, language):
|
||||
global _
|
||||
os.environ["lang"] = language
|
||||
_ = gettext.install(name, os.path.join(paths.app_path(), "locales"))
|
||||
def get_translation_function(name, language):
|
||||
if language == "en":
|
||||
return gettext.NullTranslations()
|
||||
translation_function = gettext.translation(name, os.path.join(paths.app_path(), "locales"), languages=[language])
|
||||
return translation_function
|
||||
|
||||
# the list of supported language codes of TW Blue
|
||||
languages = ["en", "es", "fr", "de", "it", "gl", "ja", "ru", "ro", "eu", "ca", "da"]
|
||||
languages = ["en", "es", "fr", "de", "it", "gl", "ja", "ru", "ro", "eu", "ca", "da", "sr"]
|
||||
|
||||
def generate_document(language, document_type="documentation"):
|
||||
if document_type == "documentation":
|
||||
translation_file = "twblue-documentation"
|
||||
change_language(translation_file, language)
|
||||
reload(strings)
|
||||
markdown_file = markdown.markdown("\n".join(strings.documentation[1:]), extensions=["markdown.extensions.toc"])
|
||||
title = strings.documentation[0]
|
||||
translation_function = get_translation_function(translation_file, language)
|
||||
markdown_file = markdown.markdown("\n".join([translation_function.gettext(s[:-1]) if s != "\n" else s for s in strings.documentation[1:]]), extensions=["markdown.extensions.toc"])
|
||||
title = translation_function.gettext(strings.documentation[0][:-1])
|
||||
filename = "manual.html"
|
||||
elif document_type == "changelog":
|
||||
translation_file = "twblue-changelog"
|
||||
change_language(translation_file, language)
|
||||
reload(changelog)
|
||||
markdown_file = markdown.markdown("\n".join(changelog.documentation[1:]), extensions=["markdown.extensions.toc"])
|
||||
title = changelog.documentation[0]
|
||||
translation_function = get_translation_function(translation_file, language)
|
||||
markdown_file = markdown.markdown("\n".join([translation_function.gettext(s[:-1]) if s != "\n" else s for s in changelog.documentation[1:]]), extensions=["markdown.extensions.toc"])
|
||||
title = translation_function.gettext(changelog.documentation[0][:-1])
|
||||
filename = "changelog.html"
|
||||
first_html_block = """<!doctype html>
|
||||
<html lang="%s">
|
||||
@@ -56,11 +55,13 @@ def create_documentation():
|
||||
shutil.copy(os.path.join("..", "license.txt"), os.path.join("documentation", "license.txt"))
|
||||
for i in languages:
|
||||
print("Creating documentation for: %s" % (i,))
|
||||
try:
|
||||
generate_document(i)
|
||||
generate_document(i, "changelog")
|
||||
except:
|
||||
continue
|
||||
print("Done")
|
||||
|
||||
change_language("twblue-documentation", "en")
|
||||
import strings
|
||||
import changelog
|
||||
create_documentation()
|
BIN
doc/locales/sr/lc_messages/twblue-changelog.mo
Normal file
BIN
doc/locales/sr/lc_messages/twblue-changelog.mo
Normal file
Binary file not shown.
1309
doc/locales/sr/lc_messages/twblue-changelog.po
Normal file
1309
doc/locales/sr/lc_messages/twblue-changelog.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
doc/locales/sr/lc_messages/twblue-documentation.mo
Normal file
BIN
doc/locales/sr/lc_messages/twblue-documentation.mo
Normal file
Binary file not shown.
1885
doc/locales/sr/lc_messages/twblue-documentation.po
Normal file
1885
doc/locales/sr/lc_messages/twblue-documentation.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,19 +5,6 @@ return "key\0";
|
||||
char *get_api_secret(){
|
||||
return "secret_key\0";
|
||||
}
|
||||
char *get_dropbox_api_key(){
|
||||
return "key\0";
|
||||
}
|
||||
char *get_dropbox_api_secret(){
|
||||
return "secret_key\0";
|
||||
}
|
||||
char *get_twishort_api_key(){
|
||||
return "key\0";
|
||||
}
|
||||
char *get_bts_user(){
|
||||
return "user\0";
|
||||
}
|
||||
char *get_bts_password(){
|
||||
return "pass\0";
|
||||
}
|
||||
|
||||
|
@@ -3,10 +3,6 @@
|
||||
|
||||
char *get_api_key();
|
||||
char *get_api_secret();
|
||||
char *get_dropbox_api_key();
|
||||
char *get_dropbox_api_secret();
|
||||
char *get_twishort_api_key();
|
||||
char *get_bts_user();
|
||||
char *get_bts_password();
|
||||
|
||||
#endif
|
||||
|
@@ -1,4 +1,5 @@
|
||||
wxpython==4.0.3
|
||||
wxpython
|
||||
wheel
|
||||
six
|
||||
configobj
|
||||
markdown
|
||||
@@ -8,25 +9,46 @@ oauthlib
|
||||
requests-oauthlib
|
||||
requests-toolbelt
|
||||
pypubsub
|
||||
pygeocoder
|
||||
geopy
|
||||
arrow
|
||||
python-dateutil
|
||||
futures
|
||||
winpaths
|
||||
PySocks
|
||||
win_inet_pton
|
||||
yandex.translate
|
||||
idna
|
||||
# Install the latest RC of this lib
|
||||
# see https://github.com/ssut/py-googletrans/issues/234
|
||||
googletrans==4.0.0-rc1
|
||||
idna<3,>=2.5
|
||||
chardet
|
||||
urllib3
|
||||
youtube-dl
|
||||
python-vlc
|
||||
pypiwin32
|
||||
pywin32
|
||||
certifi
|
||||
backports.functools_lru_cache
|
||||
cx_freeze
|
||||
git+https://github.com/manuelcortez/twython
|
||||
git+https://github.com/manuelcortez/libloader
|
||||
git+https://github.com/manuelcortez/platform_utils
|
||||
git+https://github.com/manuelcortez/accessible_output2
|
||||
git+https://github.com/jmdaweb/sound_lib
|
||||
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
|
||||
git+https://github.com/accessibleapps/sound_lib
|
12
scripts/make_archive.py
Normal file
12
scripts/make_archive.py
Normal file
@@ -0,0 +1,12 @@
|
||||
import shutil
|
||||
import os
|
||||
import sys
|
||||
|
||||
def create_archive():
|
||||
os.chdir("..\\src")
|
||||
print("Creating zip archive...")
|
||||
folder = "dist"
|
||||
shutil.make_archive("twblue", "zip", folder)
|
||||
os.chdir("..\\scripts")
|
||||
|
||||
create_archive()
|
@@ -14,11 +14,11 @@ SetCompress auto
|
||||
SetCompressor /solid lzma
|
||||
SetDatablockOptimize on
|
||||
VIAddVersionKey ProductName "TWBlue"
|
||||
VIAddVersionKey LegalCopyright "Copyright 2018 Manuel Cortéz."
|
||||
VIAddVersionKey ProductVersion "0.95"
|
||||
VIAddVersionKey FileVersion "0.95"
|
||||
VIProductVersion "0.95.0.0"
|
||||
VIFileVersion "0.95.0.0"
|
||||
VIAddVersionKey LegalCopyright "Copyright 2014-2021 Manuel Cortéz."
|
||||
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"
|
||||
@@ -27,12 +27,12 @@ var StartMenuFolder
|
||||
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!define MUI_FINISHPAGE_LINK "Visit TWBlue website"
|
||||
!define MUI_FINISHPAGE_LINK_LOCATION "http://twblue.es"
|
||||
!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 "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
|
||||
|
48
scripts/upload.py
Normal file
48
scripts/upload.py
Normal file
@@ -0,0 +1,48 @@
|
||||
#! /usr/bin/env python
|
||||
import sys
|
||||
import os
|
||||
import glob
|
||||
import ftplib
|
||||
|
||||
transferred=0
|
||||
|
||||
def convert_bytes(n):
|
||||
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
|
||||
if n >= P:
|
||||
return '%.2fPb' % (float(n) / T)
|
||||
elif n >= T:
|
||||
return '%.2fTb' % (float(n) / T)
|
||||
elif n >= G:
|
||||
return '%.2fGb' % (float(n) / G)
|
||||
elif n >= M:
|
||||
return '%.2fMb' % (float(n) / M)
|
||||
elif n >= K:
|
||||
return '%.2fKb' % (float(n) / K)
|
||||
else:
|
||||
return '%d' % n
|
||||
|
||||
def callback(progress):
|
||||
global transferred
|
||||
transferred = transferred+len(progress)
|
||||
print("Uploaded {}".format(convert_bytes(transferred),))
|
||||
|
||||
ftp_server = os.environ.get("FTP_SERVER") or sys.argv[1]
|
||||
ftp_username = os.environ.get("FTP_USERNAME") or sys.argv[2]
|
||||
ftp_password = os.environ.get("FTP_PASSWORD") or sys.argv[3]
|
||||
|
||||
print("Uploading files to the TWBlue server...")
|
||||
print("Connecting to %s" % (ftp_server,))
|
||||
connection = ftplib.FTP(ftp_server)
|
||||
print("Connected to FTP server {}".format(ftp_server,))
|
||||
connection.login(user=ftp_username, passwd=ftp_password)
|
||||
print("Logged in successfully")
|
||||
connection.cwd("web/pubs")
|
||||
files = glob.glob("*.zip")+glob.glob("*.exe")
|
||||
print("These files will be uploaded into the version folder: {}".format(files,))
|
||||
for file in files:
|
||||
transferred = 0
|
||||
print("Uploading {}".format(file,))
|
||||
with open(file, "rb") as f:
|
||||
connection.storbinary('STOR %s' % file, f, callback=callback, blocksize=1024*1024)
|
||||
print("Upload completed. exiting...")
|
||||
connection.quit()
|
@@ -12,6 +12,7 @@ reverse_timelines = boolean(default=False)
|
||||
announce_stream_status = boolean(default=True)
|
||||
retweet_mode = string(default="ask")
|
||||
persist_size = integer(default=0)
|
||||
load_cache_in_memory=boolean(default=True)
|
||||
show_screen_names = boolean(default=False)
|
||||
buffer_order = list(default=list('home','mentions', 'dm', 'sent_dm', 'sent_tweets','favorites','followers','friends','blocks','muted','events'))
|
||||
|
||||
@@ -20,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)
|
||||
@@ -48,3 +49,5 @@ braille_reporting = boolean(default=True)
|
||||
speech_reporting = boolean(default=True)
|
||||
|
||||
[filters]
|
||||
|
||||
[user-aliases]
|
@@ -24,8 +24,8 @@ mention_all = boolean(default=False)
|
||||
no_streaming = boolean(default=False)
|
||||
|
||||
[proxy]
|
||||
type = string(default="Direct connection")
|
||||
type = integer(default=0)
|
||||
server = string(default="")
|
||||
port = string(default="")
|
||||
port = integer(default=8080)
|
||||
user = string(default="")
|
||||
password = string(default="")
|
@@ -3,20 +3,14 @@ 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 = "1"
|
||||
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-2018, Manuel cortéz."
|
||||
copyright = "Copyright (C) 2013-2021, Manuel cortéz."
|
||||
description = name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features."
|
||||
translators = ["Manuel Cortéz (English)", "Mohammed Al Shara, Hatoun Felemban (Arabic)", "Francisco Torres (Catalan)", "Manuel cortéz (Spanish)", "Sukil Etxenike Arizaleta (Basque)", "Jani Kinnunen (finnish)", "Rémy Ruiz (French)", "Juan Buño (Galician)", "Steffen Schultz (German)", "Zvonimir Stanečić (Croatian)", "Robert Osztolykan (Hungarian)", "Christian Leo Mameli (Italian)", "Riku (Japanese)", "Paweł Masarczyk (Polish)", "Odenilton Júnior Santos (Portuguese)", "Florian Ionașcu, Nicușor Untilă (Romanian)", "Natalia Hedlund, Valeria Kuznetsova (Russian)", "Aleksandar Đurić (Serbian)", "Burak Yüksek (Turkish)"]
|
||||
translators = ["Manuel Cortéz (English)", "Mohammed Al Shara, Hatoun Felemban (Arabic)", "Francisco Torres (Catalan)", "Manuel cortéz (Spanish)", "Sukil Etxenike Arizaleta (Basque)", "Jani Kinnunen (finnish)", "Oreonan (Français)", "Juan Buño (Galician)", "Steffen Schultz (German)", "Zvonimir Stanečić (Croatian)", "Robert Osztolykan (Hungarian)", "Christian Leo Mameli (Italian)", "Riku (Japanese)", "Paweł Masarczyk (Polish)", "Odenilton Júnior Santos (Portuguese)", "Florian Ionașcu, Nicușor Untilă (Romanian)", "Natalia Hedlund, Valeria Kuznetsova (Russian)", "Aleksandar Đurić (Serbian)", "Burak Yüksek (Turkish)"]
|
||||
url = u"https://twblue.es"
|
||||
report_bugs_url = "https://github.com/manuelcortez/twblue/issues"
|
||||
supported_languages = []
|
||||
version = "11"
|
||||
|
@@ -1,9 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
from audio_services import matches_url
|
||||
import youtube_utils
|
||||
import requests
|
||||
|
||||
|
||||
from . import youtube_utils
|
||||
|
||||
@matches_url('https://audioboom.com')
|
||||
def convert_audioboom(url):
|
||||
|
@@ -9,7 +9,7 @@ log = logging.getLogger("config")
|
||||
|
||||
MAINFILE = "twblue.conf"
|
||||
MAINSPEC = "app-configuration.defaults"
|
||||
proxyTypes=[u"http", u"https", u"socks4", u"socks5"]
|
||||
proxyTypes = ["system", "http", "socks4", "socks4a", "socks5", "socks5h"]
|
||||
app = None
|
||||
keymap=None
|
||||
changed_keymap = False
|
||||
|
@@ -1,8 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" this package contains logic related to buffers. A buffer is a virtual representation of a group of items retrieved through the Social network API'S.
|
||||
Ideally, new social networks added to TWBlue will have its own "buffers", and these buffers should be defined within this package, following the Twitter example.
|
||||
Currently, the package contains the following modules:
|
||||
* baseBuffers: Define a set of functions and structure to be expected in all buffers. New buffers should inherit its classes from one of the classes present here.
|
||||
* twitterBuffers: All other code, specific to Twitter.
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
from . import base as base
|
||||
from . import twitter as twitter
|
4
src/controller/buffers/base/__init__.py
Normal file
4
src/controller/buffers/base/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .account import AccountBuffer
|
||||
from .base import Buffer
|
||||
from .empty import EmptyBuffer
|
56
src/controller/buffers/base/account.py
Normal file
56
src/controller/buffers/base/account.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Common logic to all buffers in TWBlue."""
|
||||
import logging
|
||||
import config
|
||||
import widgetUtils
|
||||
from pubsub import pub
|
||||
from wxUI import buffers
|
||||
from . import base
|
||||
|
||||
log = logging.getLogger("controller.buffers.base.account")
|
||||
|
||||
class AccountBuffer(base.Buffer):
|
||||
def __init__(self, parent, name, account, account_id):
|
||||
super(AccountBuffer, self).__init__(parent, None, name)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
self.buffer = buffers.accountPanel(parent, name)
|
||||
self.type = self.buffer.type
|
||||
self.compose_function = None
|
||||
self.session = None
|
||||
self.needs_init = False
|
||||
self.account = account
|
||||
self.buffer.account = account
|
||||
self.name = name
|
||||
self.account_id = account_id
|
||||
|
||||
def setup_account(self):
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.CHECKBOX, self.autostart, menuitem=self.buffer.autostart_account)
|
||||
if self.account_id in config.app["sessions"]["ignored_sessions"]:
|
||||
self.buffer.change_autostart(False)
|
||||
else:
|
||||
self.buffer.change_autostart(True)
|
||||
if not hasattr(self, "logged"):
|
||||
self.buffer.change_login(login=False)
|
||||
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.logout)
|
||||
else:
|
||||
self.buffer.change_login(login=True)
|
||||
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.login)
|
||||
|
||||
def login(self, *args, **kwargs):
|
||||
del self.logged
|
||||
self.setup_account()
|
||||
pub.sendMessage("login", session_id=self.account_id)
|
||||
|
||||
def logout(self, *args, **kwargs):
|
||||
self.logged = False
|
||||
self.setup_account()
|
||||
pub.sendMessage("logout", session_id=self.account_id)
|
||||
|
||||
def autostart(self, *args, **kwargs):
|
||||
if self.account_id in config.app["sessions"]["ignored_sessions"]:
|
||||
self.buffer.change_autostart(True)
|
||||
config.app["sessions"]["ignored_sessions"].remove(self.account_id)
|
||||
else:
|
||||
self.buffer.change_autostart(False)
|
||||
config.app["sessions"]["ignored_sessions"].append(self.account_id)
|
||||
config.app.write()
|
138
src/controller/buffers/base/base.py
Normal file
138
src/controller/buffers/base/base.py
Normal file
@@ -0,0 +1,138 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Common logic to all buffers in TWBlue."""
|
||||
import logging
|
||||
import wx
|
||||
import output
|
||||
import sound
|
||||
import widgetUtils
|
||||
|
||||
log = logging.getLogger("controller.buffers.base.base")
|
||||
|
||||
class Buffer(object):
|
||||
""" A basic buffer object. This should be the base class for all other derived buffers."""
|
||||
|
||||
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
|
||||
"""Inits the main controller for this buffer:
|
||||
@ parent wx.Treebook object: Container where we will put this buffer.
|
||||
@ function str or None: function to be called periodically and update items on this buffer.
|
||||
@ session sessionmanager.session object or None: Session handler for settings, database and data access.
|
||||
"""
|
||||
super(Buffer, self).__init__()
|
||||
self.function = function
|
||||
# Compose_function will be used to render an object on this buffer. Normally, signature is as follows:
|
||||
# compose_function(item, db, relative_times, show_screen_names=False, session=None)
|
||||
# Read more about compose functions in sessions/twitter/compose.py.
|
||||
self.compose_function = None
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
# This will be used as a reference to the wx.Panel object wich stores the buffer GUI.
|
||||
self.buffer = None
|
||||
# This should countains the account associated to this buffer.
|
||||
self.account = ""
|
||||
# This controls whether the start_stream function should be called when starting the program.
|
||||
self.needs_init = True
|
||||
# if this is set to False, the buffer will be ignored on the invisible interface.
|
||||
self.invisible = False
|
||||
# Control variable, used to track time of execution for calls to start_stream.
|
||||
self.execution_time = 0
|
||||
|
||||
def clear_list(self):
|
||||
pass
|
||||
|
||||
def get_event(self, ev):
|
||||
""" Catch key presses in the WX interface and generate the corresponding event names."""
|
||||
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
|
||||
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
|
||||
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
||||
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
||||
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
|
||||
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
|
||||
# Raise a Special event when pressed Shift+F10 because Wx==4.1.x does not seems to trigger this by itself.
|
||||
# See https://github.com/manuelcortez/TWBlue/issues/353
|
||||
elif ev.GetKeyCode() == wx.WXK_F10 and ev.ShiftDown(): event = "show_menu"
|
||||
else:
|
||||
event = None
|
||||
ev.Skip()
|
||||
if event != None:
|
||||
try:
|
||||
### ToDo: Remove after WX fixes issue #353 in the widgets.
|
||||
if event == "show_menu":
|
||||
return self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
|
||||
getattr(self, event)()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def volume_down(self):
|
||||
""" Decreases volume by 5%"""
|
||||
if self.session.settings["sound"]["volume"] > 0.0:
|
||||
if self.session.settings["sound"]["volume"] <= 0.05:
|
||||
self.session.settings["sound"]["volume"] = 0.0
|
||||
else:
|
||||
self.session.settings["sound"]["volume"] -=0.05
|
||||
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100.0))
|
||||
self.session.sound.play("volume_changed.ogg")
|
||||
self.session.settings.write()
|
||||
|
||||
def volume_up(self):
|
||||
""" Increases volume by 5%."""
|
||||
if self.session.settings["sound"]["volume"] < 1.0:
|
||||
if self.session.settings["sound"]["volume"] >= 0.95:
|
||||
self.session.settings["sound"]["volume"] = 1.0
|
||||
else:
|
||||
self.session.settings["sound"]["volume"] +=0.05
|
||||
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100))
|
||||
self.session.sound.play("volume_changed.ogg")
|
||||
self.session.settings.write()
|
||||
|
||||
def start_stream(self, mandatory=False, play_sound=True):
|
||||
pass
|
||||
|
||||
def get_more_items(self):
|
||||
output.speak(_(u"This action is not supported for this buffer"), True)
|
||||
|
||||
def put_items_on_list(self, items):
|
||||
pass
|
||||
|
||||
def remove_buffer(self):
|
||||
return False
|
||||
|
||||
def remove_item(self, item):
|
||||
f = self.buffer.list.get_selected()
|
||||
self.buffer.list.remove_item(item)
|
||||
self.buffer.list.select_item(f)
|
||||
|
||||
def bind_events(self):
|
||||
pass
|
||||
|
||||
def get_object(self):
|
||||
return self.buffer
|
||||
|
||||
def get_message(self):
|
||||
pass
|
||||
|
||||
def set_list_position(self, reversed=False):
|
||||
if reversed == False:
|
||||
self.buffer.list.select_item(-1)
|
||||
else:
|
||||
self.buffer.list.select_item(0)
|
||||
|
||||
def reply(self):
|
||||
pass
|
||||
|
||||
def send_message(self):
|
||||
pass
|
||||
|
||||
def share_item(self):
|
||||
pass
|
||||
|
||||
def destroy_status(self):
|
||||
pass
|
||||
|
||||
def post_status(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def save_positions(self):
|
||||
try:
|
||||
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
|
||||
except AttributeError:
|
||||
pass
|
19
src/controller/buffers/base/empty.py
Normal file
19
src/controller/buffers/base/empty.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
from wxUI import buffers
|
||||
from . import base
|
||||
|
||||
log = logging.getLogger("controller.buffers.base.empty")
|
||||
|
||||
class EmptyBuffer(base.Buffer):
|
||||
def __init__(self, parent, name, account):
|
||||
super(EmptyBuffer, self).__init__(parent=parent)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
self.buffer = buffers.emptyPanel(parent, name)
|
||||
self.type = self.buffer.type
|
||||
self.compose_function = None
|
||||
self.account = account
|
||||
self.buffer.account = account
|
||||
self.name = name
|
||||
self.session = None
|
||||
self.needs_init = True
|
@@ -1,203 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Common logic to all buffers in TWBlue."""
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import logging
|
||||
import wx
|
||||
import output
|
||||
import config
|
||||
import sound
|
||||
import widgetUtils
|
||||
from pubsub import pub
|
||||
from wxUI import buffers
|
||||
|
||||
log = logging.getLogger("controller.buffers.baseBuffers")
|
||||
|
||||
def _items_exist(function):
|
||||
""" A decorator to execute a function only if the selected buffer contains at least one item."""
|
||||
def function_(self, *args, **kwargs):
|
||||
if self.buffer.list.get_count() > 0:
|
||||
function(self, *args, **kwargs)
|
||||
return function_
|
||||
|
||||
class buffer(object):
|
||||
""" A basic buffer object. This should be the base class for all other derived buffers."""
|
||||
|
||||
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
|
||||
"""Inits the main controller for this buffer:
|
||||
@ parent wx.Treebook object: Container where we will put this buffer.
|
||||
@ function str or None: function to be called periodically and update items on this buffer.
|
||||
@ session sessionmanager.session object or None: Session handler for settings, database and data access.
|
||||
"""
|
||||
super(buffer, self).__init__()
|
||||
self.function = function
|
||||
# Compose_function will be used to render an object on this buffer. Normally, signature is as follows:
|
||||
# compose_function(item, db, relative_times, show_screen_names=False, session=None)
|
||||
# Read more about compose functions in twitter/compose.py.
|
||||
self.compose_function = None
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
# This will be used as a reference to the wx.Panel object wich stores the buffer GUI.
|
||||
self.buffer = None
|
||||
# This should countains the account associated to this buffer.
|
||||
self.account = ""
|
||||
# This controls whether the start_stream function should be called when starting the program.
|
||||
self.needs_init = True
|
||||
# if this is set to False, the buffer will be ignored on the invisible interface.
|
||||
self.invisible = False
|
||||
# Control variable, used to track time of execution for calls to start_stream.
|
||||
self.execution_time = 0
|
||||
|
||||
def clear_list(self):
|
||||
pass
|
||||
|
||||
def get_event(self, ev):
|
||||
""" Catch key presses in the WX interface and generate the corresponding event names."""
|
||||
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
|
||||
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
|
||||
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
||||
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
||||
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
|
||||
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
|
||||
else:
|
||||
event = None
|
||||
ev.Skip()
|
||||
if event != None:
|
||||
try:
|
||||
getattr(self, event)()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def volume_down(self):
|
||||
""" Decreases volume by 5%"""
|
||||
if self.session.settings["sound"]["volume"] > 0.0:
|
||||
if self.session.settings["sound"]["volume"] <= 0.05:
|
||||
self.session.settings["sound"]["volume"] = 0.0
|
||||
else:
|
||||
self.session.settings["sound"]["volume"] -=0.05
|
||||
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100.0))
|
||||
self.session.sound.play("volume_changed.ogg")
|
||||
self.session.settings.write()
|
||||
|
||||
def volume_up(self):
|
||||
""" Increases volume by 5%."""
|
||||
if self.session.settings["sound"]["volume"] < 1.0:
|
||||
if self.session.settings["sound"]["volume"] >= 0.95:
|
||||
self.session.settings["sound"]["volume"] = 1.0
|
||||
else:
|
||||
self.session.settings["sound"]["volume"] +=0.05
|
||||
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100))
|
||||
self.session.sound.play("volume_changed.ogg")
|
||||
self.session.settings.write()
|
||||
|
||||
def start_stream(self, mandatory=False, play_sound=True):
|
||||
pass
|
||||
|
||||
def get_more_items(self):
|
||||
output.speak(_(u"This action is not supported for this buffer"), True)
|
||||
|
||||
def put_items_on_list(self, items):
|
||||
pass
|
||||
|
||||
def remove_buffer(self):
|
||||
return False
|
||||
|
||||
def remove_item(self, item):
|
||||
f = self.buffer.list.get_selected()
|
||||
self.buffer.list.remove_item(item)
|
||||
self.buffer.list.select_item(f)
|
||||
|
||||
def bind_events(self):
|
||||
pass
|
||||
|
||||
def get_object(self):
|
||||
return self.buffer
|
||||
|
||||
def get_message(self):
|
||||
pass
|
||||
|
||||
def set_list_position(self, reversed=False):
|
||||
if reversed == False:
|
||||
self.buffer.list.select_item(-1)
|
||||
else:
|
||||
self.buffer.list.select_item(0)
|
||||
|
||||
def reply(self):
|
||||
pass
|
||||
|
||||
def send_message(self):
|
||||
pass
|
||||
|
||||
def share_item(self):
|
||||
pass
|
||||
|
||||
def destroy_status(self):
|
||||
pass
|
||||
|
||||
def post_status(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def save_positions(self):
|
||||
try:
|
||||
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
class accountPanel(buffer):
|
||||
def __init__(self, parent, name, account, account_id):
|
||||
super(accountPanel, self).__init__(parent, None, name)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
self.buffer = buffers.accountPanel(parent, name)
|
||||
self.type = self.buffer.type
|
||||
self.compose_function = None
|
||||
self.session = None
|
||||
self.needs_init = False
|
||||
self.account = account
|
||||
self.buffer.account = account
|
||||
self.name = name
|
||||
self.account_id = account_id
|
||||
|
||||
def setup_account(self):
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.CHECKBOX, self.autostart, menuitem=self.buffer.autostart_account)
|
||||
if self.account_id in config.app["sessions"]["ignored_sessions"]:
|
||||
self.buffer.change_autostart(False)
|
||||
else:
|
||||
self.buffer.change_autostart(True)
|
||||
if not hasattr(self, "logged"):
|
||||
self.buffer.change_login(login=False)
|
||||
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.logout)
|
||||
else:
|
||||
self.buffer.change_login(login=True)
|
||||
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.login)
|
||||
|
||||
def login(self, *args, **kwargs):
|
||||
del self.logged
|
||||
self.setup_account()
|
||||
pub.sendMessage("login", session_id=self.account_id)
|
||||
|
||||
def logout(self, *args, **kwargs):
|
||||
self.logged = False
|
||||
self.setup_account()
|
||||
pub.sendMessage("logout", session_id=self.account_id)
|
||||
|
||||
def autostart(self, *args, **kwargs):
|
||||
if self.account_id in config.app["sessions"]["ignored_sessions"]:
|
||||
self.buffer.change_autostart(True)
|
||||
config.app["sessions"]["ignored_sessions"].remove(self.account_id)
|
||||
else:
|
||||
self.buffer.change_autostart(False)
|
||||
config.app["sessions"]["ignored_sessions"].append(self.account_id)
|
||||
config.app.write()
|
||||
|
||||
class emptyPanel(buffer):
|
||||
def __init__(self, parent, name, account):
|
||||
super(emptyPanel, self).__init__(parent=parent)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
self.buffer = buffers.emptyPanel(parent, name)
|
||||
self.type = self.buffer.type
|
||||
self.compose_function = None
|
||||
self.account = account
|
||||
self.buffer.account = account
|
||||
self.name = name
|
||||
self.session = None
|
||||
self.needs_init = True
|
7
src/controller/buffers/twitter/__init__.py
Normal file
7
src/controller/buffers/twitter/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .base import BaseBuffer
|
||||
from .directMessages import DirectMessagesBuffer, SentDirectMessagesBuffer
|
||||
from .list import ListBuffer
|
||||
from .people import PeopleBuffer
|
||||
from .trends import TrendsBuffer
|
||||
from .search import SearchBuffer, SearchPeopleBuffer, ConversationBuffer
|
655
src/controller/buffers/twitter/base.py
Normal file
655
src/controller/buffers/twitter/base.py
Normal file
@@ -0,0 +1,655 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
import platform
|
||||
if platform.system() == "Windows":
|
||||
import wx
|
||||
from wxUI import buffers, dialogs, commonMessageDialogs, menus
|
||||
from controller import user
|
||||
elif platform.system() == "Linux":
|
||||
from gi.repository import Gtk
|
||||
from gtkUI import buffers, dialogs, commonMessageDialogs
|
||||
from controller import messages
|
||||
import widgetUtils
|
||||
import arrow
|
||||
import webbrowser
|
||||
import output
|
||||
import config
|
||||
import sound
|
||||
import languageHandler
|
||||
import logging
|
||||
from audio_services import youtube_utils
|
||||
from controller.buffers.base import base
|
||||
from sessions.twitter import compose, utils, reduce
|
||||
from mysc.thread_utils import call_threaded
|
||||
from tweepy.errors import TweepyException
|
||||
from tweepy.cursor import Cursor
|
||||
from pubsub import pub
|
||||
from sessions.twitter.long_tweets import twishort, tweets
|
||||
|
||||
log = logging.getLogger("controller.buffers")
|
||||
|
||||
def _tweets_exist(function):
|
||||
""" A decorator to execute a function only if the selected buffer contains at least one item."""
|
||||
def function_(self, *args, **kwargs):
|
||||
if self.buffer.list.get_count() > 0:
|
||||
function(self, *args, **kwargs)
|
||||
return function_
|
||||
|
||||
class BaseBuffer(base.Buffer):
|
||||
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, compose_func="compose_tweet", *args, **kwargs):
|
||||
super(BaseBuffer, self).__init__(parent, function, *args, **kwargs)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
if bufferType != None:
|
||||
self.buffer = getattr(buffers, bufferType)(parent, name)
|
||||
else:
|
||||
self.buffer = buffers.basePanel(parent, name)
|
||||
self.invisible = True
|
||||
self.name = name
|
||||
self.type = self.buffer.type
|
||||
self.session = sessionObject
|
||||
self.compose_function = getattr(compose, compose_func)
|
||||
log.debug("Compose_function: %s" % (self.compose_function,))
|
||||
self.account = account
|
||||
self.buffer.account = account
|
||||
self.bind_events()
|
||||
self.sound = sound
|
||||
if "-timeline" in self.name or "-favorite" in self.name:
|
||||
self.finished_timeline = False
|
||||
# Add a compatibility layer for username based timelines from config.
|
||||
# ToDo: Remove this in some new versions of the client, when user ID timelines become mandatory.
|
||||
try:
|
||||
int(self.kwargs["user_id"])
|
||||
except ValueError:
|
||||
self.is_screen_name = True
|
||||
self.kwargs["screen_name"] = self.kwargs["user_id"]
|
||||
self.kwargs.pop("user_id")
|
||||
|
||||
def get_buffer_name(self):
|
||||
""" Get buffer name from a set of different techniques."""
|
||||
# firstly let's take the easier buffers.
|
||||
basic_buffers = dict(home_timeline=_(u"Home"), mentions=_(u"Mentions"), direct_messages=_(u"Direct messages"), sent_direct_messages=_(u"Sent direct messages"), sent_tweets=_(u"Sent tweets"), favourites=_(u"Likes"), followers=_(u"Followers"), friends=_(u"Friends"), blocked=_(u"Blocked users"), muted=_(u"Muted users"))
|
||||
if self.name in list(basic_buffers.keys()):
|
||||
return basic_buffers[self.name]
|
||||
# Check user timelines
|
||||
elif hasattr(self, "username"):
|
||||
if "-timeline" in self.name:
|
||||
return _(u"{username}'s timeline").format(username=self.username,)
|
||||
elif "-favorite" in self.name:
|
||||
return _(u"{username}'s likes").format(username=self.username,)
|
||||
elif "-followers" in self.name:
|
||||
return _(u"{username}'s followers").format(username=self.username,)
|
||||
elif "-friends" in self.name:
|
||||
return _(u"{username}'s friends").format(username=self.username,)
|
||||
log.error("Error getting name for buffer %s" % (self.name,))
|
||||
return _(u"Unknown buffer")
|
||||
|
||||
def post_status(self, *args, **kwargs):
|
||||
item = None
|
||||
title = _(u"Tweet")
|
||||
caption = _(u"Write the tweet here")
|
||||
tweet = messages.tweet(self.session, title, caption, "")
|
||||
if tweet.message.get_response() == widgetUtils.OK:
|
||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||
config.app["app-settings"]["longtweet"] = tweet.message.long_tweet.GetValue()
|
||||
config.app.write()
|
||||
text = tweet.message.get_text()
|
||||
if len(text) > 280 and tweet.message.get("long_tweet") == True:
|
||||
if not hasattr(tweet, "attachments"):
|
||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
|
||||
else:
|
||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
|
||||
if not hasattr(tweet, "attachments") or len(tweet.attachments) == 0:
|
||||
item = self.session.api_call(call_name="update_status", status=text, _sound="tweet_send.ogg", tweet_mode="extended")
|
||||
else:
|
||||
call_threaded(self.post_with_media, text=text, attachments=tweet.attachments)
|
||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
||||
# if item != None:
|
||||
# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
||||
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
|
||||
self.session.settings.write()
|
||||
|
||||
def post_with_media(self, text, attachments):
|
||||
media_ids = []
|
||||
for i in attachments:
|
||||
img = self.session.twitter.media_upload(i["file"])
|
||||
self.session.twitter.create_media_metadata(media_id=img.media_id, alt_text=i["description"])
|
||||
media_ids.append(img.media_id)
|
||||
item = self.session.twitter.update_status(status=text, media_ids=media_ids)
|
||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
||||
# if item != None:
|
||||
# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
||||
|
||||
def get_formatted_message(self):
|
||||
if self.type == "dm" or self.name == "direct_messages":
|
||||
return self.compose_function(self.get_right_tweet(), self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)[1]
|
||||
return self.get_message()
|
||||
|
||||
def get_message(self):
|
||||
tweet = self.get_right_tweet()
|
||||
return " ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session))
|
||||
|
||||
def get_full_tweet(self):
|
||||
tweet = self.get_right_tweet()
|
||||
tweetsList = []
|
||||
tweet_id = tweet.id
|
||||
message = None
|
||||
if hasattr(tweet, "message"):
|
||||
message = tweet.message
|
||||
try:
|
||||
tweet = self.session.twitter.get_status(id=tweet_id, include_ext_alt_text=True, tweet_mode="extended")
|
||||
tweet.full_text = utils.expand_urls(tweet.full_text, tweet.entities)
|
||||
except TweepyException as e:
|
||||
utils.twitter_error(e)
|
||||
return
|
||||
if message != None:
|
||||
tweet.message = message
|
||||
l = tweets.is_long(tweet)
|
||||
while l != False:
|
||||
tweetsList.append(tweet)
|
||||
try:
|
||||
tweet = self.session.twitter.get_status(id=l, include_ext_alt_text=True, tweet_mode="extended")
|
||||
tweet.full_text = utils.expand_urls(tweet.full_text, tweet.entities)
|
||||
except TweepyException as e:
|
||||
utils.twitter_error(e)
|
||||
return
|
||||
l = tweets.is_long(tweet)
|
||||
if l == False:
|
||||
tweetsList.append(tweet)
|
||||
return (tweet, tweetsList)
|
||||
|
||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||
# starts stream every 3 minutes.
|
||||
current_time = time.time()
|
||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
|
||||
self.execution_time = current_time
|
||||
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
|
||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
||||
if self.name != "direct_messages":
|
||||
val = self.session.call_paged(self.function, *self.args, **self.kwargs)
|
||||
else:
|
||||
# 50 results are allowed per API call, so let's assume max value can be 50.
|
||||
# reference: https://developer.twitter.com/en/docs/twitter-api/v1/direct-messages/sending-and-receiving/api-reference/list-events
|
||||
if self.session.settings["general"]["max_tweets_per_call"] > 50:
|
||||
count = 50
|
||||
else:
|
||||
count = self.session.settings["general"]["max_tweets_per_call"]
|
||||
# try to retrieve the cursor for the current buffer.
|
||||
try:
|
||||
val = getattr(self.session.twitter, self.function)(return_cursors=True, count=count, *self.args, **self.kwargs)
|
||||
if type(val) == tuple:
|
||||
val, cursor = val
|
||||
if type(cursor) == tuple:
|
||||
cursor = cursor[1]
|
||||
cursors = self.session.db["cursors"]
|
||||
cursors[self.name] = cursor
|
||||
self.session.db["cursors"] = cursors
|
||||
results = [i for i in val]
|
||||
val = results
|
||||
val.reverse()
|
||||
log.debug("Retrieved %d items from the cursored search on function %s." %(len(val), self.function))
|
||||
user_ids = [item.message_create["sender_id"] for item in val]
|
||||
self.session.save_users(user_ids)
|
||||
except TweepyException as e:
|
||||
log.exception("Error %s" % (str(e)))
|
||||
return
|
||||
number_of_items = self.session.order_buffer(self.name, val)
|
||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||
self.put_items_on_list(number_of_items)
|
||||
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
||||
if "-timeline" in self.name:
|
||||
self.username = val[0].user.screen_name
|
||||
elif "-favorite" in self.name:
|
||||
self.username = self.session.api_call("get_user", **self.kwargs).screen_name
|
||||
self.finished_timeline = True
|
||||
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
||||
self.session.sound.play(self.sound)
|
||||
# Autoread settings
|
||||
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
|
||||
self.auto_read(number_of_items)
|
||||
return number_of_items
|
||||
|
||||
def auto_read(self, number_of_items):
|
||||
if number_of_items == 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
tweet = self.session.db[self.name][-1]
|
||||
else:
|
||||
tweet = self.session.db[self.name][0]
|
||||
output.speak(_(u"New tweet in {0}").format(self.get_buffer_name()))
|
||||
output.speak(" ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)))
|
||||
elif number_of_items > 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
output.speak(_(u"{0} new tweets in {1}.").format(number_of_items, self.get_buffer_name()))
|
||||
|
||||
def get_more_items(self):
|
||||
elements = []
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
last_id = self.session.db[self.name][0].id
|
||||
else:
|
||||
last_id = self.session.db[self.name][-1].id
|
||||
try:
|
||||
items = getattr(self.session.twitter, self.function)(max_id=last_id, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs)
|
||||
except TweepyException as e:
|
||||
log.exception("Error %s" % (str(e)))
|
||||
return
|
||||
if items == None:
|
||||
return
|
||||
items_db = self.session.db[self.name]
|
||||
self.session.add_users_from_results(items)
|
||||
for i in items:
|
||||
if utils.is_allowed(i, self.session.settings, self.name) == True and utils.find_item(i, self.session.db[self.name]) == None:
|
||||
i = reduce.reduce_tweet(i)
|
||||
i = self.session.check_quoted_status(i)
|
||||
i = self.session.check_long_tweet(i)
|
||||
elements.append(i)
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
items_db.insert(0, i)
|
||||
else:
|
||||
items_db.append(i)
|
||||
self.session.db[self.name] = items_db
|
||||
selection = self.buffer.list.get_selected()
|
||||
log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function))
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
for i in elements:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
else:
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
self.buffer.list.select_item(selection)
|
||||
output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
|
||||
|
||||
def remove_buffer(self, force=False):
|
||||
if "-timeline" in self.name:
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-9] in self.session.settings["other_buffers"]["timelines"]:
|
||||
self.session.settings["other_buffers"]["timelines"].remove(self.name[:-9])
|
||||
self.session.settings.write()
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
elif "favorite" in self.name:
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-9] in self.session.settings["other_buffers"]["favourites_timelines"]:
|
||||
self.session.settings["other_buffers"]["favourites_timelines"].remove(self.name[:-9])
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
self.session.settings.write()
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
else:
|
||||
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
|
||||
return False
|
||||
|
||||
def remove_tweet(self, id):
|
||||
if type(self.session.db[self.name]) == dict: return
|
||||
items = self.session.db[self.name]
|
||||
for i in range(0, len(items)):
|
||||
if items[i].id == id:
|
||||
items.pop(i)
|
||||
self.remove_item(i)
|
||||
self.session.db[self.name] = items
|
||||
|
||||
def put_items_on_list(self, number_of_items):
|
||||
list_to_use = self.session.db[self.name]
|
||||
if number_of_items == 0 and self.session.settings["general"]["persist_size"] == 0: return
|
||||
log.debug("The list contains %d items " % (self.buffer.list.get_count(),))
|
||||
log.debug("Putting %d items on the list" % (number_of_items,))
|
||||
if self.buffer.list.get_count() == 0:
|
||||
for i in list_to_use:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
|
||||
elif self.buffer.list.get_count() > 0 and number_of_items > 0:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
items = list_to_use[len(list_to_use)-number_of_items:]
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
items = list_to_use[0:number_of_items]
|
||||
items.reverse()
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),))
|
||||
|
||||
def add_new_item(self, item):
|
||||
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
output.speak(" ".join(tweet[:2]), speech=self.session.settings["reporting"]["speech_reporting"], braille=self.session.settings["reporting"]["braille_reporting"])
|
||||
#Improve performance on Windows
|
||||
# if platform.system() == "Windows":
|
||||
# call_threaded(utils.is_audio,item)
|
||||
|
||||
def bind_events(self):
|
||||
log.debug("Binding events...")
|
||||
self.buffer.set_focus_function(self.onFocus)
|
||||
widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_status, self.buffer.tweet)
|
||||
# if self.type == "baseBuffer":
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.share_item, self.buffer.retweet)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.send_message, self.buffer.dm)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.reply, self.buffer.reply)
|
||||
# Replace for the correct way in other platforms.
|
||||
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
|
||||
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
|
||||
|
||||
def show_menu(self, ev, pos=0, *args, **kwargs):
|
||||
if self.buffer.list.get_count() == 0: return
|
||||
if self.name == "sent_tweets" or self.name == "direct_messages":
|
||||
menu = menus.sentPanelMenu()
|
||||
elif self.name == "direct_messages":
|
||||
menu = menus.dmPanelMenu()
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.send_message, menuitem=menu.reply)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
|
||||
else:
|
||||
menu = menus.basePanelMenu()
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.retweet)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.fav, menuitem=menu.fav)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.unfav, menuitem=menu.unfav)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.url_, menuitem=menu.openUrl)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.audio, menuitem=menu.play)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.destroy_status, menuitem=menu.remove)
|
||||
if hasattr(menu, "openInBrowser"):
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.open_in_browser, menuitem=menu.openInBrowser)
|
||||
if pos != 0:
|
||||
self.buffer.PopupMenu(menu, pos)
|
||||
else:
|
||||
self.buffer.PopupMenu(menu, ev.GetPosition())
|
||||
|
||||
def view(self, *args, **kwargs):
|
||||
pub.sendMessage("execute-action", action="view_item")
|
||||
|
||||
def copy(self, *args, **kwargs):
|
||||
pub.sendMessage("execute-action", action="copy_to_clipboard")
|
||||
|
||||
def user_actions(self, *args, **kwargs):
|
||||
pub.sendMessage("execute-action", action="follow")
|
||||
|
||||
def fav(self, *args, **kwargs):
|
||||
pub.sendMessage("execute-action", action="add_to_favourites")
|
||||
|
||||
def unfav(self, *args, **kwargs):
|
||||
pub.sendMessage("execute-action", action="remove_from_favourites")
|
||||
|
||||
def delete_item_(self, *args, **kwargs):
|
||||
pub.sendMessage("execute-action", action="delete_item")
|
||||
|
||||
def url_(self, *args, **kwargs):
|
||||
self.url()
|
||||
|
||||
def show_menu_by_key(self, ev):
|
||||
if self.buffer.list.get_count() == 0:
|
||||
return
|
||||
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
|
||||
self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
|
||||
|
||||
def get_tweet(self):
|
||||
if hasattr(self.session.db[self.name][self.buffer.list.get_selected()], "retweeted_status"):
|
||||
tweet = self.session.db[self.name][self.buffer.list.get_selected()].retweeted_status
|
||||
else:
|
||||
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
|
||||
return tweet
|
||||
|
||||
def get_right_tweet(self):
|
||||
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
|
||||
return tweet
|
||||
|
||||
@_tweets_exist
|
||||
def reply(self, *args, **kwargs):
|
||||
tweet = self.get_right_tweet()
|
||||
user = self.session.get_user(tweet.user)
|
||||
screen_name = user.screen_name
|
||||
id = tweet.id
|
||||
twishort_enabled = hasattr(tweet, "twishort")
|
||||
users = utils.get_all_mentioned(tweet, self.session.db, field="screen_name")
|
||||
ids = utils.get_all_mentioned(tweet, self.session.db, field="id")
|
||||
# Build the window title
|
||||
if len(users) < 1:
|
||||
title=_("Reply to {arg0}").format(arg0=screen_name)
|
||||
else:
|
||||
title=_("Reply")
|
||||
message = messages.reply(self.session, title, _(u"Reply to %s") % (screen_name,), "", users=users, ids=ids)
|
||||
if message.message.get_response() == widgetUtils.OK:
|
||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||
config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue()
|
||||
if len(users) > 0:
|
||||
config.app["app-settings"]["mention_all"] = message.message.mentionAll.GetValue()
|
||||
config.app.write()
|
||||
params = {"_sound": "reply_send.ogg", "in_reply_to_status_id": id, "tweet_mode": "extended"}
|
||||
text = message.message.get_text()
|
||||
if twishort_enabled == False:
|
||||
excluded_ids = message.get_ids()
|
||||
params["exclude_reply_user_ids"] =excluded_ids
|
||||
params["auto_populate_reply_metadata"] =True
|
||||
else:
|
||||
mentioned_people = message.get_people()
|
||||
text = "@"+screen_name+" "+mentioned_people+u" "+text
|
||||
if len(text) > 280 and message.message.get("long_tweet") == True:
|
||||
if message.image == None:
|
||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
|
||||
else:
|
||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
|
||||
params["status"] = text
|
||||
if message.image == None:
|
||||
params["call_name"] = "update_status"
|
||||
else:
|
||||
params["call_name"] = "update_status_with_media"
|
||||
params["media"] = message.file
|
||||
item = self.session.api_call(**params)
|
||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
||||
# if item != None:
|
||||
# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
||||
if hasattr(message.message, "destroy"): message.message.destroy()
|
||||
self.session.settings.write()
|
||||
|
||||
@_tweets_exist
|
||||
def send_message(self, *args, **kwargs):
|
||||
tweet = self.get_right_tweet()
|
||||
if self.type == "dm":
|
||||
screen_name = self.session.get_user(tweet.message_create["sender_id"]).screen_name
|
||||
users = [screen_name]
|
||||
elif self.type == "people":
|
||||
screen_name = tweet.screen_name
|
||||
users = [screen_name]
|
||||
else:
|
||||
screen_name = self.session.get_user(tweet.user).screen_name
|
||||
users = utils.get_all_users(tweet, self.session)
|
||||
dm = messages.dm(self.session, _(u"Direct message to %s") % (screen_name,), _(u"New direct message"), users)
|
||||
if dm.message.get_response() == widgetUtils.OK:
|
||||
screen_name = dm.message.get("cb")
|
||||
user = self.session.get_user_by_screen_name(screen_name)
|
||||
recipient_id = user
|
||||
text = dm.message.get_text()
|
||||
val = self.session.api_call(call_name="send_direct_message", recipient_id=recipient_id, text=text)
|
||||
if val != None:
|
||||
sent_dms = self.session.db["sent_direct_messages"]
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
sent_dms.append(val)
|
||||
else:
|
||||
sent_dms.insert(0, val)
|
||||
self.session.db["sent_direct_messages"] = sent_dms
|
||||
pub.sendMessage("sent-dm", data=val, user=self.session.db["user_name"])
|
||||
if hasattr(dm.message, "destroy"): dm.message.destroy()
|
||||
|
||||
@_tweets_exist
|
||||
def share_item(self, *args, **kwargs):
|
||||
tweet = self.get_right_tweet()
|
||||
id = tweet.id
|
||||
if self.session.settings["general"]["retweet_mode"] == "ask":
|
||||
answer = commonMessageDialogs.retweet_question(self.buffer)
|
||||
if answer == widgetUtils.YES:
|
||||
self._retweet_with_comment(tweet, id)
|
||||
elif answer == widgetUtils.NO:
|
||||
self._direct_retweet(id)
|
||||
elif self.session.settings["general"]["retweet_mode"] == "direct":
|
||||
self._direct_retweet(id)
|
||||
else:
|
||||
self._retweet_with_comment(tweet, id)
|
||||
|
||||
def _retweet_with_comment(self, tweet, id, comment=''):
|
||||
# If quoting a retweet, let's quote the original tweet instead the retweet.
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
tweet = tweet.retweeted_status
|
||||
if hasattr(tweet, "full_text"):
|
||||
comments = tweet.full_text
|
||||
else:
|
||||
comments = tweet.text
|
||||
retweet = messages.tweet(self.session, _(u"Quote"), _(u"Add your comment to the tweet"), u"“@%s: %s ”" % (self.session.get_user(tweet.user).screen_name, comments), max=256, messageType="retweet")
|
||||
if comment != '':
|
||||
retweet.message.set_text(comment)
|
||||
if retweet.message.get_response() == widgetUtils.OK:
|
||||
text = retweet.message.get_text()
|
||||
text = text+" https://twitter.com/{0}/status/{1}".format(self.session.get_user(tweet.user).screen_name, id)
|
||||
if retweet.image == None:
|
||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
||||
item = self.session.api_call(call_name="update_status", _sound="retweet_send.ogg", status=text, in_reply_to_status_id=id, tweet_mode="extended")
|
||||
# if item != None:
|
||||
# new_item = self.session.twitter.get_status(id=item.id, include_ext_alt_text=True, tweet_mode="extended")
|
||||
# pub.sendMessage("sent-tweet", data=new_item, user=self.session.db["user_name"])
|
||||
else:
|
||||
call_threaded(self.session.api_call, call_name="update_status", _sound="retweet_send.ogg", status=text, media=retweet.image)
|
||||
if hasattr(retweet.message, "destroy"): retweet.message.destroy()
|
||||
|
||||
def _direct_retweet(self, id):
|
||||
item = self.session.api_call(call_name="retweet", _sound="retweet_send.ogg", id=id)
|
||||
# We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item.
|
||||
# but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly.
|
||||
# if item != None:
|
||||
# Retweets are returned as non-extended tweets, so let's get the object as extended
|
||||
# just before sending the event message. See https://github.com/manuelcortez/TWBlue/issues/253
|
||||
# item = self.session.twitter.get_status(id=item.id, include_ext_alt_text=True, tweet_mode="extended")
|
||||
# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
||||
|
||||
def onFocus(self, *args, **kwargs):
|
||||
tweet = self.get_tweet()
|
||||
if platform.system() == "Windows" and self.session.settings["general"]["relative_times"] == True:
|
||||
# fix this:
|
||||
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at, locale="en")
|
||||
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
||||
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 2, ts)
|
||||
if self.session.settings['sound']['indicate_audio'] and utils.is_audio(tweet):
|
||||
self.session.sound.play("audio.ogg")
|
||||
if self.session.settings['sound']['indicate_geo'] and utils.is_geocoded(tweet):
|
||||
self.session.sound.play("geo.ogg")
|
||||
if self.session.settings['sound']['indicate_img'] and utils.is_media(tweet):
|
||||
self.session.sound.play("image.ogg")
|
||||
|
||||
def audio(self, url='', *args, **kwargs):
|
||||
if sound.URLPlayer.player.is_playing():
|
||||
return sound.URLPlayer.stop_audio()
|
||||
tweet = self.get_tweet()
|
||||
if tweet == None: return
|
||||
urls = utils.find_urls(tweet, twitter_media=True)
|
||||
if len(urls) == 1:
|
||||
url=urls[0]
|
||||
elif len(urls) > 1:
|
||||
urls_list = dialogs.urlList.urlList()
|
||||
urls_list.populate_list(urls)
|
||||
if urls_list.get_response() == widgetUtils.OK:
|
||||
url=urls_list.get_string()
|
||||
if hasattr(urls_list, "destroy"): urls_list.destroy()
|
||||
if url != '':
|
||||
# try:
|
||||
sound.URLPlayer.play(url, self.session.settings["sound"]["volume"])
|
||||
# except:
|
||||
# log.error("Exception while executing audio method.")
|
||||
|
||||
# @_tweets_exist
|
||||
def url(self, url='', announce=True, *args, **kwargs):
|
||||
if url == '':
|
||||
tweet = self.get_tweet()
|
||||
urls = utils.find_urls(tweet)
|
||||
if len(urls) == 1:
|
||||
url=urls[0]
|
||||
elif len(urls) > 1:
|
||||
urls_list = dialogs.urlList.urlList()
|
||||
urls_list.populate_list(urls)
|
||||
if urls_list.get_response() == widgetUtils.OK:
|
||||
url=urls_list.get_string()
|
||||
if hasattr(urls_list, "destroy"): urls_list.destroy()
|
||||
if url != '':
|
||||
if announce:
|
||||
output.speak(_(u"Opening URL..."), True)
|
||||
webbrowser.open_new_tab(url)
|
||||
|
||||
def clear_list(self):
|
||||
dlg = commonMessageDialogs.clear_list()
|
||||
if dlg == widgetUtils.YES:
|
||||
self.session.db[self.name] = []
|
||||
self.buffer.list.clear()
|
||||
|
||||
@_tweets_exist
|
||||
def destroy_status(self, *args, **kwargs):
|
||||
index = self.buffer.list.get_selected()
|
||||
if self.type == "events" or self.type == "people" or self.type == "empty" or self.type == "account": return
|
||||
answer = commonMessageDialogs.delete_tweet_dialog(None)
|
||||
if answer == widgetUtils.YES:
|
||||
items = self.session.db[self.name]
|
||||
try:
|
||||
if self.name == "direct_messages" or self.name == "sent_direct_messages":
|
||||
self.session.twitter.delete_direct_message(id=self.get_right_tweet().id)
|
||||
items.pop(index)
|
||||
else:
|
||||
self.session.twitter.destroy_status(id=self.get_right_tweet().id)
|
||||
items.pop(index)
|
||||
self.buffer.list.remove_item(index)
|
||||
except TweepyException:
|
||||
self.session.sound.play("error.ogg")
|
||||
self.session.db[self.name] = items
|
||||
|
||||
@_tweets_exist
|
||||
def user_details(self):
|
||||
tweet = self.get_right_tweet()
|
||||
if self.type == "dm":
|
||||
users = [self.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
elif self.type == "people":
|
||||
users = [tweet.screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, self.session)
|
||||
dlg = dialogs.utils.selectUserDialog(title=_(u"User details"), users=users)
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
user.profileController(session=self.session, user=dlg.get_user())
|
||||
if hasattr(dlg, "destroy"): dlg.destroy()
|
||||
|
||||
def get_quoted_tweet(self, tweet):
|
||||
quoted_tweet = self.session.twitter.get_status(id=tweet.id)
|
||||
quoted_tweet.text = utils.find_urls_in_text(quoted_tweet.text, quoted_tweet.entities)
|
||||
l = tweets.is_long(quoted_tweet)
|
||||
id = tweets.get_id(l)
|
||||
original_tweet = self.session.twitter.get_status(id=id)
|
||||
original_tweet.text = utils.find_urls_in_text(original_tweet.text, original_tweet.entities)
|
||||
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
|
||||
def get_item_url(self):
|
||||
tweet = self.get_tweet()
|
||||
url = "https://twitter.com/{screen_name}/status/{tweet_id}".format(screen_name=self.session.get_user(tweet.user).screen_name, tweet_id=tweet.id)
|
||||
return url
|
||||
|
||||
def open_in_browser(self, *args, **kwargs):
|
||||
url = self.get_item_url()
|
||||
output.speak(_(u"Opening item in web browser..."))
|
||||
webbrowser.open(url)
|
158
src/controller/buffers/twitter/directMessages.py
Normal file
158
src/controller/buffers/twitter/directMessages.py
Normal file
@@ -0,0 +1,158 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import platform
|
||||
import widgetUtils
|
||||
import arrow
|
||||
import webbrowser
|
||||
import output
|
||||
import config
|
||||
import languageHandler
|
||||
import logging
|
||||
from controller import messages
|
||||
from sessions.twitter import compose, utils
|
||||
from mysc.thread_utils import call_threaded
|
||||
from tweepy.errors import TweepyException
|
||||
from pubsub import pub
|
||||
from . import base
|
||||
|
||||
log = logging.getLogger("controller.buffers.twitter.dmBuffer")
|
||||
|
||||
class DirectMessagesBuffer(base.BaseBuffer):
|
||||
|
||||
def get_more_items(self):
|
||||
# 50 results are allowed per API call, so let's assume max value can be 50.
|
||||
# reference: https://developer.twitter.com/en/docs/twitter-api/v1/direct-messages/sending-and-receiving/api-reference/list-events
|
||||
if self.session.settings["general"]["max_tweets_per_call"] > 50:
|
||||
count = 50
|
||||
else:
|
||||
count = self.session.settings["general"]["max_tweets_per_call"]
|
||||
total = 0
|
||||
# try to retrieve the cursor for the current buffer.
|
||||
cursor = self.session.db["cursors"].get(self.name)
|
||||
try:
|
||||
items = getattr(self.session.twitter, self.function)(return_cursors=True, cursor=cursor, count=count, *self.args, **self.kwargs)
|
||||
if type(items) == tuple:
|
||||
items, cursor = items
|
||||
if type(cursor) == tuple:
|
||||
cursor = cursor[1]
|
||||
cursors = self.session.db["cursors"]
|
||||
cursors[self.name] = cursor
|
||||
self.session.db["cursors"] = cursors
|
||||
results = [i for i in items]
|
||||
items = results
|
||||
log.debug("Retrieved %d items for cursored search in function %s" % (len(items), self.function))
|
||||
except TweepyException as e:
|
||||
log.exception("Error %s" % (str(e)))
|
||||
return
|
||||
if items == None:
|
||||
return
|
||||
sent = []
|
||||
received = []
|
||||
sent_dms = self.session.db["sent_direct_messages"]
|
||||
received_dms = self.session.db["direct_messages"]
|
||||
for i in items:
|
||||
if int(i.message_create["sender_id"]) == self.session.db["user_id"]:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
sent_dms.insert(0, i)
|
||||
sent.append(i)
|
||||
else:
|
||||
sent_dms.append(i)
|
||||
sent.insert(0, i)
|
||||
else:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
received_dms.insert(0, i)
|
||||
received.append(i)
|
||||
else:
|
||||
received_dms.append(i)
|
||||
received.insert(0, i)
|
||||
total = total+1
|
||||
self.session.db["direct_messages"] = received_dms
|
||||
self.session.db["sent_direct_messages"] = sent_dms
|
||||
user_ids = [item.message_create["sender_id"] for item in items]
|
||||
self.session.save_users(user_ids)
|
||||
pub.sendMessage("more-sent-dms", data=sent, account=self.session.db["user_name"])
|
||||
selected = self.buffer.list.get_selected()
|
||||
if self.session.settings["general"]["reverse_timelines"] == True:
|
||||
for i in received:
|
||||
if int(i.message_create["sender_id"]) == self.session.db["user_id"]:
|
||||
continue
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
self.buffer.list.select_item(selected)
|
||||
else:
|
||||
for i in received:
|
||||
if int(i.message_create["sender_id"]) == self.session.db["user_id"]:
|
||||
continue
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
output.speak(_(u"%s items retrieved") % (total), True)
|
||||
|
||||
def reply(self, *args, **kwargs):
|
||||
tweet = self.get_right_tweet()
|
||||
screen_name = self.session.get_user(tweet.message_create["sender_id"]).screen_name
|
||||
message = messages.reply(self.session, _(u"Mention"), _(u"Mention to %s") % (screen_name,), "@%s " % (screen_name,), [screen_name,])
|
||||
if message.message.get_response() == widgetUtils.OK:
|
||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||
config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue()
|
||||
config.app.write()
|
||||
if message.image == None:
|
||||
item = self.session.api_call(call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text(), tweet_mode="extended")
|
||||
if item != None:
|
||||
pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
||||
else:
|
||||
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file)
|
||||
if hasattr(message.message, "destroy"): message.message.destroy()
|
||||
|
||||
def onFocus(self, *args, **kwargs):
|
||||
tweet = self.get_tweet()
|
||||
if platform.system() == "Windows" and self.session.settings["general"]["relative_times"] == True:
|
||||
# fix this:
|
||||
original_date = arrow.get(int(tweet.created_timestamp))
|
||||
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
||||
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 2, ts)
|
||||
if self.session.settings['sound']['indicate_audio'] and utils.is_audio(tweet):
|
||||
self.session.sound.play("audio.ogg")
|
||||
if self.session.settings['sound']['indicate_img'] and utils.is_media(tweet):
|
||||
self.session.sound.play("image.ogg")
|
||||
|
||||
def clear_list(self):
|
||||
dlg = commonMessageDialogs.clear_list()
|
||||
if dlg == widgetUtils.YES:
|
||||
self.session.db[self.name] = []
|
||||
self.buffer.list.clear()
|
||||
|
||||
def auto_read(self, number_of_items):
|
||||
if number_of_items == 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
tweet = self.session.db[self.name][-1]
|
||||
else:
|
||||
tweet = self.session.db[self.name][0]
|
||||
output.speak(_(u"New direct message"))
|
||||
output.speak(" ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)))
|
||||
elif number_of_items > 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
output.speak(_(u"{0} new direct messages.").format(number_of_items,))
|
||||
|
||||
def open_in_browser(self, *args, **kwargs):
|
||||
output.speak(_(u"This action is not supported in the buffer yet."))
|
||||
|
||||
class SentDirectMessagesBuffer(DirectMessagesBuffer):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SentDirectMessagesBuffer, self).__init__(*args, **kwargs)
|
||||
if ("sent_direct_messages" in self.session.db) == False:
|
||||
self.session.db["sent_direct_messages"] = []
|
||||
|
||||
def get_more_items(self):
|
||||
output.speak(_(u"Getting more items cannot be done in this buffer. Use the direct messages buffer instead."))
|
||||
|
||||
def start_stream(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def put_more_items(self, items):
|
||||
if self.session.settings["general"]["reverse_timelines"] == True:
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
44
src/controller/buffers/twitter/list.py
Normal file
44
src/controller/buffers/twitter/list.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import platform
|
||||
if platform.system() == "Windows":
|
||||
from wxUI import dialogs, commonMessageDialogs
|
||||
elif platform.system() == "Linux":
|
||||
from gi.repository import Gtk
|
||||
from gtkUI import dialogs, commonMessageDialogs
|
||||
import widgetUtils
|
||||
import logging
|
||||
from tweepy.cursor import Cursor
|
||||
from . import base
|
||||
|
||||
log = logging.getLogger("controller.buffers.twitter.listBuffer")
|
||||
|
||||
class ListBuffer(base.BaseBuffer):
|
||||
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, list_id=None, *args, **kwargs):
|
||||
super(ListBuffer, self).__init__(parent, function, name, sessionObject, account, sound=None, bufferType=None, *args, **kwargs)
|
||||
self.users = []
|
||||
self.list_id = list_id
|
||||
self.kwargs["list_id"] = list_id
|
||||
|
||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||
self.get_user_ids()
|
||||
super(ListBuffer, self).start_stream(mandatory, play_sound, avoid_autoreading)
|
||||
|
||||
def get_user_ids(self):
|
||||
for i in Cursor(self.session.twitter.get_list_members, list_id=self.list_id, include_entities=False, skip_status=True, count=5000).items():
|
||||
if i.id not in self.users:
|
||||
self.users.append(i.id)
|
||||
|
||||
def remove_buffer(self, force=False):
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-5] in self.session.settings["other_buffers"]["lists"]:
|
||||
self.session.settings["other_buffers"]["lists"].remove(self.name[:-5])
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
self.session.settings.write()
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
258
src/controller/buffers/twitter/people.py
Normal file
258
src/controller/buffers/twitter/people.py
Normal file
@@ -0,0 +1,258 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
import platform
|
||||
if platform.system() == "Windows":
|
||||
from wxUI import commonMessageDialogs, menus
|
||||
from controller import user
|
||||
elif platform.system() == "Linux":
|
||||
from gi.repository import Gtk
|
||||
from gtkUI import dialogs, commonMessageDialogs
|
||||
from controller import messages
|
||||
import widgetUtils
|
||||
import webbrowser
|
||||
import output
|
||||
import config
|
||||
import logging
|
||||
from mysc.thread_utils import call_threaded
|
||||
from tweepy.errors import TweepyException
|
||||
from pubsub import pub
|
||||
from sessions.twitter import compose
|
||||
from . import base
|
||||
|
||||
log = logging.getLogger("controller.buffers.twitter.peopleBuffer")
|
||||
|
||||
def _tweets_exist(function):
|
||||
""" A decorator to execute a function only if the selected buffer contains at least one item."""
|
||||
def function_(self, *args, **kwargs):
|
||||
if self.buffer.list.get_count() > 0:
|
||||
function(self, *args, **kwargs)
|
||||
return function_
|
||||
|
||||
class PeopleBuffer(base.BaseBuffer):
|
||||
def __init__(self, parent, function, name, sessionObject, account, bufferType=None, *args, **kwargs):
|
||||
super(PeopleBuffer, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
self.compose_function = compose.compose_followers_list
|
||||
log.debug("Compose_function: %s" % (self.compose_function,))
|
||||
self.get_tweet = self.get_right_tweet
|
||||
self.url = self.interact
|
||||
if "-followers" in self.name or "-friends" in self.name:
|
||||
self.finished_timeline = False
|
||||
# Add a compatibility layer for username based timelines from config.
|
||||
# ToDo: Remove this in some new versions of the client, when user ID timelines become mandatory.
|
||||
try:
|
||||
int(self.kwargs["user_id"])
|
||||
except ValueError:
|
||||
self.is_screen_name = True
|
||||
self.kwargs["screen_name"] = self.kwargs["user_id"]
|
||||
self.kwargs.pop("user_id")
|
||||
|
||||
def remove_buffer(self, force=True):
|
||||
if "-followers" in self.name:
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-10] in self.session.settings["other_buffers"]["followers_timelines"]:
|
||||
self.session.settings["other_buffers"]["followers_timelines"].remove(self.name[:-10])
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
self.session.settings.write()
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
elif "-friends" in self.name:
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-8] in self.session.settings["other_buffers"]["friends_timelines"]:
|
||||
self.session.settings["other_buffers"]["friends_timelines"].remove(self.name[:-8])
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
self.session.settings.write()
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
else:
|
||||
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
|
||||
return False
|
||||
|
||||
def onFocus(self, ev):
|
||||
pass
|
||||
|
||||
def get_message(self):
|
||||
return " ".join(self.compose_function(self.get_tweet(), self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session))
|
||||
|
||||
def delete_item(self): pass
|
||||
|
||||
@_tweets_exist
|
||||
def reply(self, *args, **kwargs):
|
||||
tweet = self.get_right_tweet()
|
||||
screen_name = tweet.screen_name
|
||||
message = messages.reply(self.session, _(u"Mention"), _(u"Mention to %s") % (screen_name,), "@%s " % (screen_name,), [screen_name,])
|
||||
if message.message.get_response() == widgetUtils.OK:
|
||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||
config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue()
|
||||
config.app.write()
|
||||
if message.image == None:
|
||||
item = self.session.api_call(call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text(), tweet_mode="extended")
|
||||
if item != None:
|
||||
pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"])
|
||||
else:
|
||||
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file)
|
||||
if hasattr(message.message, "destroy"): message.message.destroy()
|
||||
|
||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||
# starts stream every 3 minutes.
|
||||
current_time = time.time()
|
||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
|
||||
self.execution_time = current_time
|
||||
log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,))
|
||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
||||
try:
|
||||
val = getattr(self.session.twitter, self.function)(return_cursors=True, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs)
|
||||
if type(val) == tuple:
|
||||
val, cursor = val
|
||||
if type(cursor) == tuple:
|
||||
cursor = cursor[1]
|
||||
cursors = self.session.db["cursors"]
|
||||
cursors[self.name] = cursor
|
||||
self.session.db["cursors"] = cursors
|
||||
results = [i for i in val]
|
||||
val = results
|
||||
val.reverse()
|
||||
log.debug("Retrieved %d items from cursored search in function %s" % (len(val), self.function))
|
||||
except TweepyException as e:
|
||||
log.exception("Error %s" % (str(e)))
|
||||
return
|
||||
number_of_items = self.session.order_people(self.name, val)
|
||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||
self.put_items_on_list(number_of_items)
|
||||
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
||||
self.username = self.session.api_call("get_user", **self.kwargs).screen_name
|
||||
self.finished_timeline = True
|
||||
if number_of_items > 0 and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
||||
self.session.sound.play(self.sound)
|
||||
# Autoread settings
|
||||
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
|
||||
self.auto_read(number_of_items)
|
||||
return number_of_items
|
||||
|
||||
def get_more_items(self):
|
||||
try:
|
||||
cursor = self.session.db["cursors"].get(self.name)
|
||||
items = getattr(self.session.twitter, self.function)(return_cursors=True, users=True, cursor=cursor, count=self.session.settings["general"]["max_tweets_per_call"], *self.args, **self.kwargs)
|
||||
if type(items) == tuple:
|
||||
items, cursor = items
|
||||
if type(cursor) == tuple:
|
||||
cursor = cursor[1]
|
||||
cursors = self.session.db["cursors"]
|
||||
cursors[self.name] = cursor
|
||||
self.session.db["cursors"] = cursors
|
||||
results = [i for i in items]
|
||||
items = results
|
||||
log.debug("Retrieved %d items from cursored search in function %s" % (len(items), self.function))
|
||||
except TweepyException as e:
|
||||
log.exception("Error %s" % (str(e)))
|
||||
return
|
||||
if items == None:
|
||||
return
|
||||
items_db = self.session.db[self.name]
|
||||
for i in items:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
items_db.insert(0, i)
|
||||
else:
|
||||
items_db.append(i)
|
||||
self.session.db[self.name] = items_db
|
||||
selected = self.buffer.list.get_selected()
|
||||
if self.session.settings["general"]["reverse_timelines"] == True:
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session)
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
self.buffer.list.select_item(selected)
|
||||
else:
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session)
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
output.speak(_(u"%s items retrieved") % (len(items)), True)
|
||||
|
||||
def put_items_on_list(self, number_of_items):
|
||||
log.debug("The list contains %d items" % (self.buffer.list.get_count(),))
|
||||
# log.debug("Putting %d items on the list..." % (number_of_items,))
|
||||
if self.buffer.list.get_count() == 0:
|
||||
for i in self.session.db[self.name]:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"], self.session)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
|
||||
# self.buffer.set_list_position()
|
||||
elif self.buffer.list.get_count() > 0:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
for i in self.session.db[self.name][len(self.session.db[self.name])-number_of_items:]:
|
||||
tweet = self.compose_function(i, self.session.db)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
items = self.session.db[self.name][0:number_of_items]
|
||||
items.reverse()
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db)
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
log.debug("now the list contains %d items" % (self.buffer.list.get_count(),))
|
||||
|
||||
def get_right_tweet(self):
|
||||
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
|
||||
return tweet
|
||||
|
||||
def add_new_item(self, item):
|
||||
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"], self.session)
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
output.speak(" ".join(tweet))
|
||||
|
||||
def clear_list(self):
|
||||
dlg = commonMessageDialogs.clear_list()
|
||||
if dlg == widgetUtils.YES:
|
||||
self.session.db[self.name] = []
|
||||
self.session.db["cursors"][self.name] = -1
|
||||
self.buffer.list.clear()
|
||||
|
||||
def interact(self):
|
||||
user.profileController(self.session, user=self.get_right_tweet().screen_name)
|
||||
|
||||
def show_menu(self, ev, pos=0, *args, **kwargs):
|
||||
menu = menus.peoplePanelMenu()
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.send_message, menuitem=menu.reply)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.details, menuitem=menu.details)
|
||||
# widgetUtils.connect_event(menu, widgetUtils.MENU, self.lists, menuitem=menu.lists)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
|
||||
if hasattr(menu, "openInBrowser"):
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.open_in_browser, menuitem=menu.openInBrowser)
|
||||
if pos != 0:
|
||||
self.buffer.PopupMenu(menu, pos)
|
||||
else:
|
||||
self.buffer.PopupMenu(menu, ev.GetPosition())
|
||||
|
||||
def details(self, *args, **kwargs):
|
||||
pub.sendMessage("execute-action", action="user_details")
|
||||
|
||||
def auto_read(self, number_of_items):
|
||||
if number_of_items == 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
tweet = self.session.db[self.name][-1]
|
||||
else:
|
||||
tweet = self.session.db[self.name][0]
|
||||
output.speak(" ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], self.session)))
|
||||
elif number_of_items > 1 and self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
output.speak(_(u"{0} new followers.").format(number_of_items))
|
||||
|
||||
def get_item_url(self, *args, **kwargs):
|
||||
tweet = self.get_tweet()
|
||||
url = "https://twitter.com/{screen_name}".format(screen_name=tweet.screen_name)
|
||||
return url
|
123
src/controller/buffers/twitter/search.py
Normal file
123
src/controller/buffers/twitter/search.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
import platform
|
||||
import locale
|
||||
if platform.system() == "Windows":
|
||||
from wxUI import commonMessageDialogs
|
||||
elif platform.system() == "Linux":
|
||||
from gi.repository import Gtk
|
||||
from gtkUI import commonMessageDialogs
|
||||
import widgetUtils
|
||||
import logging
|
||||
from tweepy.errors import TweepyException
|
||||
from . import base, people
|
||||
|
||||
log = logging.getLogger("controller.buffers.twitter.searchBuffer")
|
||||
|
||||
class SearchBuffer(base.BaseBuffer):
|
||||
|
||||
def remove_buffer(self, force=False):
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-11] in self.session.settings["other_buffers"]["tweet_searches"]:
|
||||
self.session.settings["other_buffers"]["tweet_searches"].remove(self.name[:-11])
|
||||
self.session.settings.write()
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
|
||||
class SearchPeopleBuffer(people.PeopleBuffer):
|
||||
""" This is identical to a normal peopleBufferController, except that uses the page parameter instead of a cursor."""
|
||||
def __init__(self, parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs):
|
||||
super(SearchPeopleBuffer, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs)
|
||||
if ("page" in self.kwargs) == False:
|
||||
self.page = 1
|
||||
else:
|
||||
self.page = self.kwargs.pop("page")
|
||||
|
||||
def get_more_items(self, *args, **kwargs):
|
||||
# Add 1 to the page parameter, put it in kwargs and calls to get_more_items in the parent buffer.
|
||||
self.page = self.page +1
|
||||
self.kwargs["page"] = self.page
|
||||
super(SearchPeopleBuffer, self).get_more_items(*args, **kwargs)
|
||||
# remove the parameter again to make sure start_stream won't fetch items for this page indefinitely.
|
||||
self.kwargs.pop("page")
|
||||
|
||||
def remove_buffer(self, force=False):
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-11] in self.session.settings["other_buffers"]["tweet_searches"]:
|
||||
self.session.settings["other_buffers"]["tweet_searches"].remove(self.name[:-11])
|
||||
self.session.settings.write()
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
|
||||
class ConversationBuffer(SearchBuffer):
|
||||
|
||||
def start_stream(self, start=False, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||
current_time = time.time()
|
||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
|
||||
self.execution_time = current_time
|
||||
results = self.get_replies(self.tweet)
|
||||
number_of_items = self.session.order_buffer(self.name, results)
|
||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||
self.put_items_on_list(number_of_items)
|
||||
if number_of_items > 0 and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
||||
self.session.sound.play(self.sound)
|
||||
# Autoread settings
|
||||
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
|
||||
self.auto_read(number_of_items)
|
||||
return number_of_items
|
||||
|
||||
def remove_buffer(self, force=False):
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
|
||||
def get_replies(self, tweet):
|
||||
""" Try to retrieve the whole conversation for the passed object by using a mix between calls to API V1.1 and V2 """
|
||||
results = []
|
||||
# If the tweet that starts the conversation is a reply to something else, let's try to get the parent tweet first.
|
||||
if hasattr(self, "in_reply_to_status_id") and self.tweet.in_reply_to_status_id != None:
|
||||
try:
|
||||
tweet2 = self.session.twitter_v2.get_tweet(id=self.tweet.in_reply_to_status_id, user_auth=True, tweet_fields=["conversation_id"])
|
||||
results.append(tweet2)
|
||||
except TweepyException as e:
|
||||
log.exception("There was an error attempting to retrieve a parent tweet for the conversation for {}".format(self.name))
|
||||
# Now, try to fetch the tweet initiating the conversation in V2 so we can get conversation_id
|
||||
try:
|
||||
tweet = self.session.twitter_v2.get_tweet(id=self.tweet.id, user_auth=True, tweet_fields=["conversation_id"])
|
||||
results.append(tweet.data)
|
||||
term = "conversation_id:{}".format(tweet.data.conversation_id)
|
||||
tweets = self.session.twitter_v2.search_recent_tweets(term, user_auth=True, max_results=98)
|
||||
if tweets.data != None:
|
||||
results.extend(tweets.data)
|
||||
except TweepyException as e:
|
||||
log.exception("There was an error when attempting to retrieve the whole conversation for buffer {}".format(self.buffer.name))
|
||||
new_results = []
|
||||
ids = [tweet.id for tweet in results]
|
||||
try:
|
||||
results = self.session.twitter.lookup_statuses(ids, include_ext_alt_text=True, tweet_mode="extended")
|
||||
results.sort(key=lambda x: x.id)
|
||||
except TweepyException as e:
|
||||
log.exception("There was an error attempting to retrieve tweets for Twitter API V1.1, in conversation buffer {}".format(self.name))
|
||||
return []
|
||||
return results
|
145
src/controller/buffers/twitter/trends.py
Normal file
145
src/controller/buffers/twitter/trends.py
Normal file
@@ -0,0 +1,145 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
import platform
|
||||
if platform.system() == "Windows":
|
||||
import wx
|
||||
from wxUI import buffers, commonMessageDialogs, menus
|
||||
from controller import user
|
||||
elif platform.system() == "Linux":
|
||||
from gi.repository import Gtk
|
||||
from gtkUI import buffers, commonMessageDialogs
|
||||
from controller import messages
|
||||
import widgetUtils
|
||||
import output
|
||||
import logging
|
||||
from mysc.thread_utils import call_threaded
|
||||
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, sessionObject, account, trendsFor, *args, **kwargs):
|
||||
super(TrendsBuffer, self).__init__(parent=parent, sessionObject=sessionObject)
|
||||
self.trendsFor = trendsFor
|
||||
self.session = sessionObject
|
||||
self.account = account
|
||||
self.invisible = True
|
||||
self.buffer = buffers.trendsPanel(parent, name)
|
||||
self.buffer.account = account
|
||||
self.type = self.buffer.type
|
||||
self.bind_events()
|
||||
self.sound = "trends_updated.ogg"
|
||||
self.trends = []
|
||||
self.name = name
|
||||
self.buffer.name = name
|
||||
self.compose_function = self.compose_function_
|
||||
self.get_formatted_message = self.get_message
|
||||
self.reply = self.search_topic
|
||||
|
||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||
# starts stream every 3 minutes.
|
||||
current_time = time.time()
|
||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
|
||||
self.execution_time = current_time
|
||||
try:
|
||||
data = self.session.twitter.get_place_trends(id=self.trendsFor)
|
||||
except TweepyException as err:
|
||||
log.exception("Error %s" % (str(err)))
|
||||
if not hasattr(self, "name_"):
|
||||
self.name_ = data[0]["locations"][0]["name"]
|
||||
self.trends = data[0]["trends"]
|
||||
self.put_items_on_the_list()
|
||||
if self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
||||
self.session.sound.play(self.sound)
|
||||
|
||||
def put_items_on_the_list(self):
|
||||
selected_item = self.buffer.list.get_selected()
|
||||
self.buffer.list.clear()
|
||||
for i in self.trends:
|
||||
tweet = self.compose_function(i)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
|
||||
|
||||
def compose_function_(self, trend):
|
||||
return [trend["name"]]
|
||||
|
||||
def bind_events(self):
|
||||
log.debug("Binding events...")
|
||||
self.buffer.list.list.Bind(wx.EVT_CHAR_HOOK, self.get_event)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.tweet_about_this_trend, self.buffer.tweetTrendBtn)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_status, self.buffer.tweet)
|
||||
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
|
||||
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.search_topic, self.buffer.search_topic)
|
||||
|
||||
def get_message(self):
|
||||
return self.compose_function(self.trends[self.buffer.list.get_selected()])[0]
|
||||
|
||||
def remove_buffer(self, force=False):
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-3] in self.session.settings["other_buffers"]["trending_topic_buffers"]:
|
||||
self.session.settings["other_buffers"]["trending_topic_buffers"].remove(self.name[:-3])
|
||||
self.session.settings.write()
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
|
||||
def url(self, *args, **kwargs):
|
||||
self.tweet_about_this_trend()
|
||||
|
||||
def search_topic(self, *args, **kwargs):
|
||||
topic = self.trends[self.buffer.list.get_selected()]["name"]
|
||||
pub.sendMessage("search", term=topic)
|
||||
|
||||
def show_menu(self, ev, pos=0, *args, **kwargs):
|
||||
menu = menus.trendsPanelMenu()
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.search_topic, menuitem=menu.search_topic)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.tweet_about_this_trend, menuitem=menu.tweetThisTrend)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
|
||||
if pos != 0:
|
||||
self.buffer.PopupMenu(menu, pos)
|
||||
else:
|
||||
self.buffer.PopupMenu(menu, ev.GetPosition())
|
||||
|
||||
def view(self, *args, **kwargs):
|
||||
pub.sendMessage("execute-action", action="view_item")
|
||||
|
||||
def copy(self, *args, **kwargs):
|
||||
pub.sendMessage("execute-action", action="copy_to_clipboard")
|
||||
|
||||
def tweet_about_this_trend(self, *args, **kwargs):
|
||||
if self.buffer.list.get_count() == 0: return
|
||||
title = _(u"Tweet")
|
||||
caption = _(u"Write the tweet here")
|
||||
tweet = messages.tweet(self.session, title, caption, self.get_message()+ " ")
|
||||
tweet.message.set_cursor_at_end()
|
||||
if tweet.message.get_response() == widgetUtils.OK:
|
||||
text = tweet.message.get_text()
|
||||
if len(text) > 280 and tweet.message.get("long_tweet") == True:
|
||||
if tweet.image == None:
|
||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
|
||||
else:
|
||||
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
|
||||
if tweet.image == None:
|
||||
call_threaded(self.session.api_call, call_name="update_status", status=text)
|
||||
else:
|
||||
call_threaded(self.session.api_call, call_name="update_status_with_media", status=text, media=tweet.image)
|
||||
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
|
||||
|
||||
def show_menu_by_key(self, ev):
|
||||
if self.buffer.list.get_count() == 0:
|
||||
return
|
||||
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
|
||||
self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
|
||||
|
||||
def open_in_browser(self, *args, **kwargs):
|
||||
output.speak(_(u"This action is not supported in the buffer, yet."))
|
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import widgetUtils
|
||||
import output
|
||||
import logging
|
||||
from wxUI.dialogs import lists
|
||||
from twython import TwythonError
|
||||
from tweepy.errors import TweepyException
|
||||
from sessions.twitter import compose, utils
|
||||
from pubsub import pub
|
||||
|
||||
log = logging.getLogger("controller.listsController")
|
||||
|
||||
class listsController(object):
|
||||
def __init__(self, session, user=None):
|
||||
super(listsController, self).__init__()
|
||||
@@ -31,7 +32,7 @@ class listsController(object):
|
||||
return [compose.compose_list(item) for item in self.session.db["lists"]]
|
||||
|
||||
def get_user_lists(self, user):
|
||||
self.lists = self.session.twitter.show_lists(reverse=True, screen_name=user)
|
||||
self.lists = self.session.twitter.lists_all(reverse=True, screen_name=user)
|
||||
return [compose.compose_list(item) for item in self.lists]
|
||||
|
||||
def create_list(self, *args, **kwargs):
|
||||
@@ -48,8 +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 TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.status_code, e.msg))
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
dialog.destroy()
|
||||
|
||||
def edit_list(self, *args, **kwargs):
|
||||
@@ -65,44 +67,48 @@ class listsController(object):
|
||||
else:
|
||||
mode = "private"
|
||||
try:
|
||||
self.session.twitter.update_list(list_id=list["id"], name=name, description=description, mode=mode)
|
||||
self.session.twitter.update_list(list_id=list.id, name=name, description=description, mode=mode)
|
||||
self.session.get_lists()
|
||||
self.dialog.populate_list(self.get_all_lists(), True)
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.error_code, e.msg))
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
dialog.destroy()
|
||||
|
||||
def remove_list(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()]["id"]
|
||||
list = self.session.db["lists"][self.dialog.get_item()].id
|
||||
if lists.remove_list() == widgetUtils.YES:
|
||||
try:
|
||||
self.session.twitter.delete_list(list_id=list)
|
||||
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 TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.error_code, e.msg))
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
|
||||
def open_list_as_buffer(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()]
|
||||
pub.sendMessage("create-new-buffer", buffer="list", account=self.session.db["user_name"], create=list["name"])
|
||||
pub.sendMessage("create-new-buffer", buffer="list", account=self.session.db["user_name"], create=list.name)
|
||||
|
||||
def subscribe(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list_id = self.lists[self.dialog.get_item()]["id"]
|
||||
list_id = self.lists[self.dialog.get_item()].id
|
||||
try:
|
||||
list = self.session.twitter.subscribe_to_list(list_id=list_id)
|
||||
item = utils.find_item(list["id"], self.session.db["lists"])
|
||||
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 TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.status_code, e.msg))
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
|
||||
def unsubscribe(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list_id = self.lists[self.dialog.get_item()]["id"]
|
||||
list_id = self.lists[self.dialog.get_item()].id
|
||||
try:
|
||||
list = self.session.twitter.unsubscribe_from_list(list_id=list_id)
|
||||
list = self.session.twitter.unsubscribe_list(list_id=list_id)
|
||||
self.session.db["lists"].remove(list)
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.status_code, e.msg))
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
|
@@ -1,11 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
from builtins import range
|
||||
from builtins import object
|
||||
import platform
|
||||
system = platform.system()
|
||||
import application
|
||||
import wx
|
||||
import requests
|
||||
from audio_services import youtube_utils
|
||||
import arrow
|
||||
@@ -26,31 +23,30 @@ elif system == "Linux":
|
||||
from gtkUI import (view, commonMessageDialogs)
|
||||
from sessions.twitter import utils, compose
|
||||
from sessionmanager import manager, sessionManager
|
||||
|
||||
from controller.buffers import baseBuffers, twitterBuffers
|
||||
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 twython import TwythonError, TwythonAuthError
|
||||
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):
|
||||
|
||||
@@ -130,12 +126,14 @@ class Controller(object):
|
||||
pub.subscribe(self.update_sent_dms, "sent-dms-updated")
|
||||
pub.subscribe(self.more_dms, "more-sent-dms")
|
||||
pub.subscribe(self.manage_sent_tweets, "sent-tweet")
|
||||
pub.subscribe(self.manage_new_tweet, "newTweet")
|
||||
pub.subscribe(self.manage_friend, "friend")
|
||||
pub.subscribe(self.manage_unfollowing, "unfollowing")
|
||||
pub.subscribe(self.manage_favourite, "favourite")
|
||||
pub.subscribe(self.manage_unfavourite, "unfavourite")
|
||||
pub.subscribe(self.manage_blocked_user, "blocked-user")
|
||||
pub.subscribe(self.manage_unblocked_user, "unblocked-user")
|
||||
pub.subscribe(self.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)
|
||||
@@ -189,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()
|
||||
@@ -257,9 +257,9 @@ class Controller(object):
|
||||
|
||||
# Connection checker executed each minute.
|
||||
self.checker_function = RepeatingTimer(60, self.check_connection)
|
||||
self.checker_function.start()
|
||||
self.save_db = RepeatingTimer(300, self.save_data_in_db)
|
||||
self.save_db.start()
|
||||
# self.checker_function.start()
|
||||
# self.save_db = RepeatingTimer(300, self.save_data_in_db)
|
||||
# self.save_db.start()
|
||||
log.debug("Setting updates to buffers every %d seconds..." % (60*config.app["app-settings"]["update_period"],))
|
||||
self.update_buffers_function = RepeatingTimer(60*config.app["app-settings"]["update_period"], self.update_buffers)
|
||||
self.update_buffers_function.start()
|
||||
@@ -270,15 +270,19 @@ class Controller(object):
|
||||
if sessions.sessions[i].is_logged == False: continue
|
||||
self.start_buffers(sessions.sessions[i])
|
||||
self.set_buffer_positions(sessions.sessions[i])
|
||||
sessions.sessions[i].start_streaming()
|
||||
if config.app["app-settings"]["play_ready_sound"] == True:
|
||||
sessions.sessions[list(sessions.sessions.keys())[0]].sound.play("ready.ogg")
|
||||
if config.app["app-settings"]["speak_ready_msg"] == True:
|
||||
output.speak(_(u"Ready"))
|
||||
self.started = True
|
||||
self.streams_checker_function = RepeatingTimer(60, self.check_streams)
|
||||
self.streams_checker_function.start()
|
||||
|
||||
|
||||
def create_ignored_session_buffer(self, session):
|
||||
self.accounts.append(session.settings["twitter"]["user_name"])
|
||||
account = baseBuffers.accountPanel(self.view.nb, session.settings["twitter"]["user_name"], session.settings["twitter"]["user_name"], session.session_id)
|
||||
account = buffers.base.AccountBuffer(self.view.nb, session.settings["twitter"]["user_name"], session.settings["twitter"]["user_name"], session.session_id)
|
||||
account.logged = False
|
||||
account.setup_account()
|
||||
self.buffers.append(account)
|
||||
@@ -292,106 +296,89 @@ 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"""
|
||||
session.get_user_info()
|
||||
if createAccounts == True:
|
||||
self.accounts.append(session.db["user_name"])
|
||||
account = baseBuffers.accountPanel(self.view.nb, session.db["user_name"], session.db["user_name"], session.session_id)
|
||||
account = buffers.base.AccountBuffer(self.view.nb, session.db["user_name"], session.db["user_name"], session.session_id)
|
||||
account.setup_account()
|
||||
self.buffers.append(account)
|
||||
self.view.add_buffer(account.buffer , name=session.db["user_name"])
|
||||
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 = twitterBuffers.baseBufferController(self.view.nb, "get_home_timeline", "home_timeline", session, session.db["user_name"], sound="tweet_received.ogg", tweet_mode="extended")
|
||||
self.buffers.append(home)
|
||||
self.view.insert_buffer(home.buffer, name=_(u"Home"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.baseBufferController(self.view.nb, "get_mentions_timeline", "mentions", session, session.db["user_name"], sound="mention_received.ogg", tweet_mode="extended")
|
||||
self.buffers.append(mentions)
|
||||
self.view.insert_buffer(mentions.buffer, name=_(u"Mentions"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.directMessagesController(self.view.nb, "get_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message", sound="dm_received.ogg", full_text=True, items="events")
|
||||
self.buffers.append(dm)
|
||||
self.view.insert_buffer(dm.buffer, name=_(u"Direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.sentDirectMessagesController(self.view.nb, "", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_direct_message")
|
||||
self.buffers.append(sent_dm)
|
||||
self.view.insert_buffer(sent_dm.buffer, name=_(u"Sent direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.baseBufferController(self.view.nb, "get_user_timeline", "sent_tweets", session, session.db["user_name"], screen_name=session.db["user_name"], tweet_mode="extended")
|
||||
self.buffers.append(sent_tweets)
|
||||
self.view.insert_buffer(sent_tweets.buffer, name=_(u"Sent tweets"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.baseBufferController(self.view.nb, "get_favorites", "favourites", session, session.db["user_name"], sound="favourite.ogg", tweet_mode="extended")
|
||||
self.buffers.append(favourites)
|
||||
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.peopleBufferController(self.view.nb, "get_followers_list", "followers", session, session.db["user_name"], sound="update_followers.ogg", screen_name=session.db["user_name"])
|
||||
self.buffers.append(followers)
|
||||
self.view.insert_buffer(followers.buffer, name=_(u"Followers"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.peopleBufferController(self.view.nb, "get_friends_list", "friends", session, session.db["user_name"], screen_name=session.db["user_name"])
|
||||
self.buffers.append(friends)
|
||||
self.view.insert_buffer(friends.buffer, name=_(u"Friends"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.peopleBufferController(self.view.nb, "list_blocks", "blocked", session, session.db["user_name"])
|
||||
self.buffers.append(blocks)
|
||||
self.view.insert_buffer(blocks.buffer, name=_(u"Blocked users"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.peopleBufferController(self.view.nb, "list_mutes", "muted", session, session.db["user_name"])
|
||||
self.buffers.append(muted)
|
||||
self.view.insert_buffer(muted.buffer, name=_(u"Muted users"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
timelines = baseBuffers.emptyPanel(self.view.nb, "timelines", session.db["user_name"])
|
||||
self.buffers.append(timelines)
|
||||
self.view.insert_buffer(timelines.buffer , name=_(u"Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (i,), session, session.db["user_name"], sound="tweet_timeline.ogg", bufferType=None, user_id=i, tweet_mode="extended")
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(i,), pos=self.view.search("timelines", session.db["user_name"]))
|
||||
favs_timelines = baseBuffers.emptyPanel(self.view.nb, "favs_timelines", session.db["user_name"])
|
||||
self.buffers.append(favs_timelines)
|
||||
self.view.insert_buffer(favs_timelines.buffer , name=_(u"Likes timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (i,), session, session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=i, tweet_mode="extended")
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Likes for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
|
||||
followers_timelines = baseBuffers.emptyPanel(self.view.nb, "followers_timelines", session.db["user_name"])
|
||||
self.buffers.append(followers_timelines)
|
||||
self.view.insert_buffer(followers_timelines.buffer , name=_(u"Followers' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Followers for {}").format(i,), pos=self.view.search("followers_timelines", session.db["user_name"]))
|
||||
friends_timelines = baseBuffers.emptyPanel(self.view.nb, "friends_timelines", session.db["user_name"])
|
||||
self.buffers.append(friends_timelines)
|
||||
self.view.insert_buffer(friends_timelines.buffer , name=_(u"Friends' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (i,), session, session.db["user_name"], sound="new_event.ogg", user_id=i)
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Friends for {}").format(i,), pos=self.view.search("friends_timelines", session.db["user_name"]))
|
||||
lists = baseBuffers.emptyPanel(self.view.nb, "lists", session.db["user_name"])
|
||||
self.buffers.append(lists)
|
||||
self.view.insert_buffer(lists.buffer , name=_(u"Lists"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.listBufferController(self.view.nb, "get_list_statuses", "%s-list" % (i,), session, session.db["user_name"], bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), tweet_mode="extended")
|
||||
session.lists.append(tl)
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"List for {}").format(i), pos=self.view.search("lists", session.db["user_name"]))
|
||||
searches = baseBuffers.emptyPanel(self.view.nb, "searches", session.db["user_name"])
|
||||
self.buffers.append(searches)
|
||||
self.view.insert_buffer(searches.buffer , name=_(u"Searches"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
|
||||
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 = twitterBuffers.searchBufferController(self.view.nb, "search", "%s-searchterm" % (i,), session, session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=i, tweet_mode="extended")
|
||||
self.buffers.append(tl)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Search for {}").format(i), pos=self.view.search("searches", session.db["user_name"]))
|
||||
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 = twitterBuffers.trendsBufferController(self.view.nb, "%s_tt" % (i,), session, session.db["user_name"], i, sound="trends_updated.ogg")
|
||||
buffer.start_stream(play_sound=False)
|
||||
buffer.searchfunction = self.search
|
||||
self.buffers.append(buffer)
|
||||
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 = twitterBuffers.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", q=term, tweet_mode="extended", **args)
|
||||
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 = twitterBuffers.searchPeopleBufferController(self.view.nb, "search_users", "%s-searchUser" % (term,), buffer.session, buffer.session.db["user_name"], bufferType=None, sound="search_updated.ogg", q=term)
|
||||
search.start_stream(mandatory=True)
|
||||
pos=self.view.search("searches", buffer.session.db["user_name"])
|
||||
self.insert_buffer(search, pos)
|
||||
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):
|
||||
@@ -530,11 +514,11 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
user = dlg.get_user()
|
||||
@@ -547,11 +531,11 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
user = dlg.get_user()
|
||||
@@ -561,25 +545,26 @@ class Controller(object):
|
||||
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
try:
|
||||
list = buff.session.twitter.add_list_member(list_id=buff.session.db["lists"][dlg.get_item()]["id"], screen_name=user)
|
||||
older_list = utils.find_item(buff.session.db["lists"][dlg.get_item()]["id"], buff.session.db["lists"])
|
||||
listBuffer = self.search_buffer("%s-list" % (buff.session.db["lists"][dlg.get_item()]["name"].lower()), buff.session.db["user_name"])
|
||||
list = buff.session.twitter.add_list_member(list_id=buff.session.db["lists"][dlg.get_item()].id, screen_name=user)
|
||||
older_list = utils.find_item(buff.session.db["lists"][dlg.get_item()].id, buff.session.db["lists"])
|
||||
listBuffer = self.search_buffer("%s-list" % (buff.session.db["lists"][dlg.get_item()].name.lower()), buff.session.db["user_name"])
|
||||
if listBuffer != None: listBuffer.get_user_ids()
|
||||
buff.session.db["lists"].pop(older_list)
|
||||
buff.session.db["lists"].append(list)
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.error_code, e.msg))
|
||||
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()
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
user = dlg.get_user()
|
||||
@@ -589,14 +574,15 @@ class Controller(object):
|
||||
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
try:
|
||||
list = buff.session.twitter.delete_list_member(list_id=buff.session.db["lists"][dlg.get_item()]["id"], screen_name=user)
|
||||
older_list = utils.find_item(buff.session.db["lists"][dlg.get_item()]["id"], buff.session.db["lists"])
|
||||
listBuffer = self.search_buffer("%s-list" % (buff.session.db["lists"][dlg.get_item()]["name"].lower()), buff.session.db["user_name"])
|
||||
list = buff.session.twitter.remove_list_member(list_id=buff.session.db["lists"][dlg.get_item()].id, screen_name=user)
|
||||
older_list = utils.find_item(buff.session.db["lists"][dlg.get_item()].id, buff.session.db["lists"])
|
||||
listBuffer = self.search_buffer("%s-list" % (buff.session.db["lists"][dlg.get_item()].name.lower()), buff.session.db["user_name"])
|
||||
if listBuffer != None: listBuffer.get_user_ids()
|
||||
buff.session.db["lists"].pop(older_list)
|
||||
buff.session.db["lists"].append(list)
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.error_code, e.msg))
|
||||
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
|
||||
@@ -621,6 +607,7 @@ class Controller(object):
|
||||
if d.needs_restart == True:
|
||||
commonMessageDialogs.needs_restart()
|
||||
buff.session.settings.write()
|
||||
buff.session.save_persistent_data()
|
||||
restart.restart_program()
|
||||
|
||||
def report_error(self, *args, **kwargs):
|
||||
@@ -653,15 +640,20 @@ class Controller(object):
|
||||
log.debug("Saving global configuration...")
|
||||
for item in sessions.sessions:
|
||||
if sessions.sessions[item].logged == False: continue
|
||||
log.debug("Disconnecting streaming endpoint for session" + sessions.sessions[item].session_id)
|
||||
sessions.sessions[item].stop_streaming()
|
||||
log.debug("Disconnecting streams for %s session" % (sessions.sessions[item].session_id,))
|
||||
sessions.sessions[item].sound.cleaner.cancel()
|
||||
log.debug("Shelving database for " + sessions.sessions[item].session_id)
|
||||
sessions.sessions[item].shelve()
|
||||
log.debug("Saving database for " + sessions.sessions[item].session_id)
|
||||
sessions.sessions[item].save_persistent_data()
|
||||
if system == "Windows":
|
||||
self.systrayIcon.RemoveIcon()
|
||||
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
|
||||
if os.path.exists(pidpath):
|
||||
os.remove(pidpath)
|
||||
if hasattr(self, "streams_checker_function"):
|
||||
log.debug("Stopping stream checker...")
|
||||
self.streams_checker_function.cancel()
|
||||
widgetUtils.exit_application()
|
||||
|
||||
def follow(self, *args, **kwargs):
|
||||
@@ -669,11 +661,11 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
u = userActionsController.userActionsController(buff, users)
|
||||
|
||||
def unfollow(self, *args, **kwargs):
|
||||
@@ -681,11 +673,11 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
u = userActionsController.userActionsController(buff, users, "unfollow")
|
||||
|
||||
def mute(self, *args, **kwargs):
|
||||
@@ -693,11 +685,11 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
u = userActionsController.userActionsController(buff, users, "mute")
|
||||
|
||||
def unmute(self, *args, **kwargs):
|
||||
@@ -705,11 +697,11 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
u = userActionsController.userActionsController(buff, users, "unmute")
|
||||
|
||||
def block(self, *args, **kwargs):
|
||||
@@ -717,11 +709,11 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
u = userActionsController.userActionsController(buff, users, "block")
|
||||
|
||||
def unblock(self, *args, **kwargs):
|
||||
@@ -729,11 +721,11 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
u = userActionsController.userActionsController(buff, users, "unblock")
|
||||
|
||||
def report(self, *args, **kwargs):
|
||||
@@ -741,13 +733,38 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
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()
|
||||
@@ -775,7 +792,7 @@ class Controller(object):
|
||||
if buffer.type == "dm" or buffer.type == "people" or buffer.type == "events":
|
||||
return
|
||||
else:
|
||||
id = buffer.get_tweet()["id"]
|
||||
id = buffer.get_tweet().id
|
||||
call_threaded(buffer.session.api_call, call_name="create_favorite", _sound="favourite.ogg", id=id)
|
||||
|
||||
def remove_from_favourites(self, *args, **kwargs):
|
||||
@@ -783,7 +800,7 @@ class Controller(object):
|
||||
if buffer.type == "dm" or buffer.type == "people" or buffer.type == "events":
|
||||
return
|
||||
else:
|
||||
id = buffer.get_tweet()["id"]
|
||||
id = buffer.get_tweet().id
|
||||
call_threaded(buffer.session.api_call, call_name="destroy_favorite", id=id)
|
||||
|
||||
def toggle_like(self, *args, **kwargs):
|
||||
@@ -791,9 +808,9 @@ class Controller(object):
|
||||
if buffer.type == "dm" or buffer.type == "people" or buffer.type == "events":
|
||||
return
|
||||
else:
|
||||
id = buffer.get_tweet()["id"]
|
||||
tweet = buffer.session.twitter.show_status(id=id, include_ext_alt_text=True, tweet_mode="extended")
|
||||
if tweet["favorited"] == False:
|
||||
id = buffer.get_tweet().id
|
||||
tweet = buffer.session.twitter.get_status(id=id, include_ext_alt_text=True, tweet_mode="extended")
|
||||
if tweet.favorited == False:
|
||||
call_threaded(buffer.session.api_call, call_name="create_favorite", _sound="favourite.ogg", id=id)
|
||||
else:
|
||||
call_threaded(buffer.session.api_call, call_name="destroy_favorite", id=id)
|
||||
@@ -804,16 +821,19 @@ 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()
|
||||
original_date = arrow.get(int(item["created_timestamp"][:-3]))
|
||||
original_date = arrow.get(int(item.created_timestamp))
|
||||
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()
|
||||
@@ -828,11 +848,11 @@ class Controller(object):
|
||||
if not hasattr(buff, "get_right_tweet"): return
|
||||
tweet = buff.get_right_tweet()
|
||||
if buff.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
users = [tweet.screen_name]
|
||||
elif buff.type == "dm":
|
||||
users = [buff.session.get_user(tweet["message_create"]["sender_id"])["screen_name"]]
|
||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, buff.session.db)
|
||||
users = utils.get_all_users(tweet, buff.session)
|
||||
dlg = dialogs.userSelection.selectUserDialog(users=users, default=default)
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
usr = utils.if_user_exists(buff.session.twitter, dlg.get_user())
|
||||
@@ -840,85 +860,85 @@ class Controller(object):
|
||||
if usr == dlg.get_user():
|
||||
commonMessageDialogs.suspended_user()
|
||||
return
|
||||
if usr["protected"] == True:
|
||||
if usr["following"] == False:
|
||||
if usr.protected == True:
|
||||
if usr.following == False:
|
||||
commonMessageDialogs.no_following()
|
||||
return
|
||||
tl_type = dlg.get_action()
|
||||
if tl_type == "tweets":
|
||||
if usr["statuses_count"] == 0:
|
||||
if usr.statuses_count == 0:
|
||||
commonMessageDialogs.no_tweets()
|
||||
return
|
||||
if usr["id_str"] in buff.session.settings["other_buffers"]["timelines"]:
|
||||
if usr.id_str in buff.session.settings["other_buffers"]["timelines"]:
|
||||
commonMessageDialogs.timeline_exist()
|
||||
return
|
||||
tl = twitterBuffers.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (usr["id_str"],), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr["id_str"], tweet_mode="extended")
|
||||
tl = buffers.twitter.BaseBuffer(self.view.nb, "user_timeline", "%s-timeline" % (usr.id_str,), buff.session, buff.session.db["user_name"], bufferType=None, sound="tweet_timeline.ogg", user_id=usr.id_str, tweet_mode="extended")
|
||||
try:
|
||||
tl.start_stream(play_sound=False)
|
||||
except TwythonAuthError:
|
||||
except ValueError:
|
||||
commonMessageDialogs.unauthorized()
|
||||
return
|
||||
pos=self.view.search("timelines", buff.session.db["user_name"])
|
||||
self.insert_buffer(tl, pos+1)
|
||||
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(dlg.get_user()), pos=pos)
|
||||
buff.session.settings["other_buffers"]["timelines"].append(usr["id_str"])
|
||||
buff.session.settings["other_buffers"]["timelines"].append(usr.id_str)
|
||||
pub.sendMessage("buffer-title-changed", buffer=tl)
|
||||
buff.session.sound.play("create_timeline.ogg")
|
||||
elif tl_type == "favourites":
|
||||
if usr["favourites_count"] == 0:
|
||||
if usr.favourites_count == 0:
|
||||
commonMessageDialogs.no_favs()
|
||||
return
|
||||
if usr["id_str"] in buff.session.settings["other_buffers"]["favourites_timelines"]:
|
||||
if usr.id_str in buff.session.settings["other_buffers"]["favourites_timelines"]:
|
||||
commonMessageDialogs.timeline_exist()
|
||||
return
|
||||
tl = twitterBuffers.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (usr["id_str"],), buff.session, buff.session.db["user_name"], bufferType=None, sound="favourites_timeline_updated.ogg", user_id=usr["id_str"], tweet_mode="extended")
|
||||
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 TwythonAuthError:
|
||||
except ValueError:
|
||||
commonMessageDialogs.unauthorized()
|
||||
return
|
||||
pos=self.view.search("favs_timelines", buff.session.db["user_name"])
|
||||
self.insert_buffer(tl, pos+1)
|
||||
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Likes for {}").format(dlg.get_user()), pos=pos)
|
||||
buff.session.settings["other_buffers"]["favourites_timelines"].append(usr["id_str"])
|
||||
buff.session.settings["other_buffers"]["favourites_timelines"].append(usr.id_str)
|
||||
pub.sendMessage("buffer-title-changed", buffer=buff)
|
||||
buff.session.sound.play("create_timeline.ogg")
|
||||
elif tl_type == "followers":
|
||||
if usr["followers_count"] == 0:
|
||||
if usr.followers_count == 0:
|
||||
commonMessageDialogs.no_followers()
|
||||
return
|
||||
if usr["id_str"] in buff.session.settings["other_buffers"]["followers_timelines"]:
|
||||
if usr.id_str in buff.session.settings["other_buffers"]["followers_timelines"]:
|
||||
commonMessageDialogs.timeline_exist()
|
||||
return
|
||||
tl = twitterBuffers.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (usr["id_str"],), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr["id_str"])
|
||||
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 TwythonAuthError:
|
||||
except ValueError:
|
||||
commonMessageDialogs.unauthorized()
|
||||
return
|
||||
pos=self.view.search("followers_timelines", buff.session.db["user_name"])
|
||||
self.insert_buffer(tl, pos+1)
|
||||
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Followers for {}").format(dlg.get_user()), pos=pos)
|
||||
buff.session.settings["other_buffers"]["followers_timelines"].append(usr["id_str"])
|
||||
buff.session.settings["other_buffers"]["followers_timelines"].append(usr.id_str)
|
||||
buff.session.sound.play("create_timeline.ogg")
|
||||
pub.sendMessage("buffer-title-changed", buffer=i)
|
||||
elif tl_type == "friends":
|
||||
if usr["friends_count"] == 0:
|
||||
if usr.friends_count == 0:
|
||||
commonMessageDialogs.no_friends()
|
||||
return
|
||||
if usr["id_str"] in buff.session.settings["other_buffers"]["friends_timelines"]:
|
||||
if usr.id_str in buff.session.settings["other_buffers"]["friends_timelines"]:
|
||||
commonMessageDialogs.timeline_exist()
|
||||
return
|
||||
tl = twitterBuffers.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (usr["id_str"],), buff.session, buff.session.db["user_name"], sound="new_event.ogg", user_id=usr["id_str"])
|
||||
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 TwythonAuthError:
|
||||
except ValueError:
|
||||
commonMessageDialogs.unauthorized()
|
||||
return
|
||||
pos=self.view.search("friends_timelines", buff.session.db["user_name"])
|
||||
self.insert_buffer(tl, pos+1)
|
||||
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Friends for {}").format(dlg.get_user()), pos=pos)
|
||||
buff.session.settings["other_buffers"]["friends_timelines"].append(usr["id_str"])
|
||||
buff.session.settings["other_buffers"]["friends_timelines"].append(usr.id_str)
|
||||
buff.session.sound.play("create_timeline.ogg")
|
||||
pub.sendMessage("buffer-title-changed", buffer=i)
|
||||
else:
|
||||
@@ -927,9 +947,9 @@ class Controller(object):
|
||||
|
||||
def open_conversation(self, *args, **kwargs):
|
||||
buffer = self.get_current_buffer()
|
||||
id = buffer.get_right_tweet()["id_str"]
|
||||
user = buffer.get_right_tweet()["user"]["screen_name"]
|
||||
search = twitterBuffers.conversationBufferController(self.view.nb, "search", "%s-searchterm" % (id,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", sound="search_updated.ogg", since_id=id, q="@{0}".format(user,))
|
||||
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_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"])
|
||||
@@ -956,7 +976,7 @@ class Controller(object):
|
||||
if trends.dialog.get_response() == widgetUtils.OK:
|
||||
woeid = trends.get_woeid()
|
||||
if woeid in buff.session.settings["other_buffers"]["trending_topic_buffers"]: return
|
||||
buffer = twitterBuffers.trendsBufferController(self.view.nb, "%s_tt" % (woeid,), buff.session, buff.account, woeid, sound="trends_updated.ogg")
|
||||
buffer = buffers.twitter.TrendsBuffer(self.view.nb, "%s_tt" % (woeid,), buff.session, buff.account, woeid, sound="trends_updated.ogg")
|
||||
buffer.searchfunction = self.search
|
||||
pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"])
|
||||
self.view.insert_buffer(buffer.buffer, name=_(u"Trending topics for %s") % (trends.get_string()), pos=pos)
|
||||
@@ -968,29 +988,27 @@ class Controller(object):
|
||||
def reverse_geocode(self, event=None):
|
||||
try:
|
||||
tweet = self.get_current_buffer().get_tweet()
|
||||
if tweet["coordinates"] != None:
|
||||
x = tweet["coordinates"]["coordinates"][0]
|
||||
y = tweet["coordinates"]["coordinates"][1]
|
||||
address = geocoder.reverse_geocode(y, x, language = languageHandler.curLang)
|
||||
if event == None: output.speak(address[0].__str__())
|
||||
else: self.view.show_address(address[0].__str__())
|
||||
if tweet.coordinates != None:
|
||||
x = tweet.coordinates["coordinates"][0]
|
||||
y = tweet.coordinates["coordinates"][1]
|
||||
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:
|
||||
tweet = self.get_current_buffer().get_right_tweet()
|
||||
if tweet["coordinates"] != None:
|
||||
x = tweet["coordinates"]["coordinates"][0]
|
||||
y = tweet["coordinates"]["coordinates"][1]
|
||||
if tweet.coordinates != None:
|
||||
x = tweet.coordinates["coordinates"][0]
|
||||
y = tweet.coordinates["coordinates"][1]
|
||||
address = geocoder.reverse_geocode(y, x, language = languageHandler.curLang)
|
||||
dlg = commonMessageDialogs.view_geodata(address[0].__str__())
|
||||
else:
|
||||
@@ -1238,14 +1256,17 @@ class Controller(object):
|
||||
keymap = {}
|
||||
for i in config.keymap["keymap"]:
|
||||
if hasattr(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:
|
||||
@@ -1272,16 +1293,16 @@ class Controller(object):
|
||||
def manage_sent_tweets(self, data, user):
|
||||
buffer = self.search_buffer("sent_tweets", user)
|
||||
if buffer == None: return
|
||||
# if "sent_tweets" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
||||
# self.notify(buffer.session, play_sound=play_sound)
|
||||
data = buffer.session.check_quoted_status(data)
|
||||
data = buffer.session.check_long_tweet(data)
|
||||
if data == False: # Long tweet deleted from twishort.
|
||||
return
|
||||
items = buffer.session.db[buffer.name]
|
||||
if buffer.session.settings["general"]["reverse_timelines"] == False:
|
||||
buffer.session.db[buffer.name].append(data)
|
||||
items.append(data)
|
||||
else:
|
||||
buffer.session.db[buffer.name].insert(0, data)
|
||||
items.insert(0, data)
|
||||
buffer.session.db[buffer.name] = items
|
||||
buffer.add_new_item(data)
|
||||
|
||||
def manage_friend(self, data, user):
|
||||
@@ -1330,7 +1351,10 @@ class Controller(object):
|
||||
i.start_stream()
|
||||
else:
|
||||
i.start_stream(play_sound=False)
|
||||
except TwythonAuthError:
|
||||
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).
|
||||
if type(err) == Forbidden:
|
||||
buff = self.view.search(i.name, i.account)
|
||||
i.remove_buffer(force=True)
|
||||
commonMessageDialogs.blocked_timeline()
|
||||
@@ -1354,48 +1378,44 @@ class Controller(object):
|
||||
try:
|
||||
if sessions.sessions[i].is_logged == False: continue
|
||||
sessions.sessions[i].check_connection()
|
||||
except TwythonError: # 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 = twitterBuffers.baseBufferController(self.view.nb, "get_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 = twitterBuffers.peopleBufferController(self.view.nb, "get_followers_list", "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 = twitterBuffers.peopleBufferController(self.view.nb, "get_friends_list", "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 = twitterBuffers.peopleBufferController(self.view.nb, "list_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 = twitterBuffers.peopleBufferController(self.view.nb, "get_muted_users_list", "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)
|
||||
elif buffer == "events":
|
||||
events = twitterBuffers.eventsBufferController(self.view.nb, "events", buff.session, buff.session.db["user_name"], bufferType="dmPanel", screen_name=buff.session.db["user_name"])
|
||||
self.buffers.append(events)
|
||||
self.view.insert_buffer(events.buffer, name=_(u"Events"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
|
||||
elif create == False:
|
||||
self.destroy_buffer(buffer, buff.session.db["user_name"])
|
||||
elif buffer == "list":
|
||||
if create in buff.session.settings["other_buffers"]["lists"]:
|
||||
output.speak(_(u"This list is already opened"), True)
|
||||
return
|
||||
tl = twitterBuffers.listBufferController(self.view.nb, "get_list_statuses", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]), tweet_mode="extended")
|
||||
tl = buffers.twitter.ListBuffer(self.view.nb, "list_timeline", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]), tweet_mode="extended")
|
||||
buff.session.lists.append(tl)
|
||||
pos=self.view.search("lists", buff.session.db["user_name"])
|
||||
self.insert_buffer(tl, pos)
|
||||
@@ -1536,7 +1556,10 @@ class Controller(object):
|
||||
if i.session != None and i.session.is_logged == True:
|
||||
try:
|
||||
i.start_stream(mandatory=True)
|
||||
except TwythonAuthError:
|
||||
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).
|
||||
if type(err) == Forbidden:
|
||||
buff = self.view.search(i.name, i.account)
|
||||
i.remove_buffer(force=True)
|
||||
commonMessageDialogs.blocked_timeline()
|
||||
@@ -1576,12 +1599,12 @@ class Controller(object):
|
||||
return
|
||||
tweet = buffer.get_tweet()
|
||||
media_list = []
|
||||
if ("entities" in tweet) and ("media" in tweet["entities"]):
|
||||
[media_list.append(i) for i in tweet["entities"]["media"] if i not in media_list]
|
||||
elif "retweeted_status" in tweet and "media" in tweet["retweeted_status"]["entities"]:
|
||||
[media_list.append(i) for i in tweet["retweeted_status"]["entities"]["media"] if i not in media_list]
|
||||
elif "quoted_status" in tweet and "media" in tweet["quoted_status"]["entities"]:
|
||||
[media_list.append(i) for i in tweet["quoted_status"]["entities"]["media"] if i not in media_list]
|
||||
if hasattr(tweet, "entities") and tweet.entities.get("media") != None:
|
||||
[media_list.append(i) for i in tweet.entities["media"] if i not in media_list]
|
||||
elif hasattr(tweet, "retweeted_status") and tweet.retweeted_status.get("media") != None:
|
||||
[media_list.append(i) for i in tweet.retweeted_status.entities["media"] if i not in media_list]
|
||||
elif hasattr(tweet, "quoted_status") and tweet.quoted_status.entities.get("media") != None:
|
||||
[media_list.append(i) for i in tweet.quoted_status.entities["media"] if i not in media_list]
|
||||
if len(media_list) > 1:
|
||||
image_list = [_(u"Picture {0}").format(i,) for i in range(0, len(media_list))]
|
||||
dialog = dialogs.urlList.urlList(title=_(u"Select the picture"))
|
||||
@@ -1597,7 +1620,7 @@ class Controller(object):
|
||||
if buffer.session.settings["mysc"]["ocr_language"] != "":
|
||||
ocr_lang = buffer.session.settings["mysc"]["ocr_language"]
|
||||
else:
|
||||
ocr_lang = ocr.OCRSpace.short_langs.index(tweet["lang"])
|
||||
ocr_lang = ocr.OCRSpace.short_langs.index(tweet.lang)
|
||||
ocr_lang = ocr.OCRSpace.OcrLangs[ocr_lang]
|
||||
api = ocr.OCRSpace.OCRSpaceAPI()
|
||||
try:
|
||||
@@ -1619,4 +1642,28 @@ class Controller(object):
|
||||
|
||||
def save_data_in_db(self):
|
||||
for i in sessions.sessions:
|
||||
sessions.sessions[i].shelve()
|
||||
sessions.sessions[i].save_persistent_data()
|
||||
|
||||
def manage_new_tweet(self, data, user, _buffers):
|
||||
sound_to_play = None
|
||||
for buff in _buffers:
|
||||
buffer = self.search_buffer(buff, user)
|
||||
if buffer == None or buffer.session.db["user_name"] != user: return
|
||||
buffer.add_new_item(data)
|
||||
if buff == "home_timeline": sound_to_play = "tweet_received.ogg"
|
||||
elif buff == "mentions": sound_to_play = "mention_received.ogg"
|
||||
elif buff == "sent_tweets": sound_to_play = "tweet_send.ogg"
|
||||
elif "timeline" in buff: sound_to_play = "tweet_timeline.ogg"
|
||||
else: sound_to_play = None
|
||||
if sound_to_play != None and buff not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
||||
self.notify(buffer.session, sound_to_play)
|
||||
|
||||
def check_streams(self):
|
||||
if self.started == False:
|
||||
return
|
||||
for i in sessions.sessions:
|
||||
try:
|
||||
if sessions.sessions[i].is_logged == False: continue
|
||||
sessions.sessions[i].check_streams()
|
||||
except TweepyException: # We shouldn't allow this function to die.
|
||||
pass
|
||||
|
@@ -1,12 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
from builtins import range
|
||||
from builtins import object
|
||||
import re
|
||||
import platform
|
||||
from . import attach
|
||||
import arrow
|
||||
import languageHandler
|
||||
system = platform.system()
|
||||
@@ -16,15 +10,16 @@ import url_shortener
|
||||
import sound
|
||||
import config
|
||||
from pubsub import pub
|
||||
from twitter_text import parse_tweet
|
||||
if system == "Windows":
|
||||
from wxUI.dialogs import message, urlList
|
||||
from wxUI import commonMessageDialogs
|
||||
|
||||
from extra import translator, SpellChecker, autocompletionUsers
|
||||
from extra.AudioUploader import audioUploader
|
||||
elif system == "Linux":
|
||||
from gtkUI.dialogs import message
|
||||
from sessions.twitter import utils
|
||||
from . import attach
|
||||
|
||||
class basicTweet(object):
|
||||
""" This class handles the tweet main features. Other classes should derive from this class."""
|
||||
@@ -50,8 +45,11 @@ class basicTweet(object):
|
||||
dlg = translator.gui.translateDialog()
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
text_to_translate = self.message.get_text()
|
||||
dest = [x[0] for x in translator.translator.available_languages()][dlg.get("dest_lang")]
|
||||
msg = translator.translator.translate(text=text_to_translate, target=dest)
|
||||
language_dict = translator.translator.available_languages()
|
||||
for k in language_dict:
|
||||
if language_dict[k] == dlg.dest_lang.GetStringSelection():
|
||||
dst = k
|
||||
msg = translator.translator.translate(text=text_to_translate, target=dst)
|
||||
self.message.set_text(msg)
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
@@ -104,9 +102,11 @@ class basicTweet(object):
|
||||
else:
|
||||
self.message.disable_button("shortenButton")
|
||||
self.message.disable_button("unshortenButton")
|
||||
if self.message.get("long_tweet") == False:
|
||||
self.message.set_title(_(u"%s - %s of %d characters") % (self.title, len(self.message.get_text()), self.max))
|
||||
if len(self.message.get_text()) > self.max:
|
||||
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))
|
||||
if results.weightedLength > self.max:
|
||||
self.session.sound.play("max_length.ogg")
|
||||
else:
|
||||
self.message.set_title(_(u"%s - %s characters") % (self.title, len(self.message.get_text())))
|
||||
@@ -194,13 +194,18 @@ class dm(basicTweet):
|
||||
super(dm, self).__init__(session, title, caption, text, messageType="dm", max=10000)
|
||||
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||
self.text_processor()
|
||||
widgetUtils.connect_event(self.message.cb, widgetUtils.ENTERED_TEXT, self.user_changed)
|
||||
|
||||
def user_changed(self, *args, **kwargs):
|
||||
self.title = _("Direct message to %s") % (self.message.get_user())
|
||||
self.text_processor()
|
||||
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
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.
|
||||
@@ -211,62 +216,67 @@ class viewTweet(basicTweet):
|
||||
text = ""
|
||||
for i in range(0, len(tweetList)):
|
||||
# tweets with message keys are longer tweets, the message value is the full messaje taken from twishort.
|
||||
if "message" in tweetList[i] and tweetList[i]["is_quote_status"] == False:
|
||||
if hasattr(tweetList[i], "message") and tweetList[i].is_quote_status == False:
|
||||
value = "message"
|
||||
else:
|
||||
value = "full_text"
|
||||
if "retweeted_status" in tweetList[i] and tweetList[i]["is_quote_status"] == False:
|
||||
if ("message" in tweetList[i]) == False:
|
||||
text = text + "rt @%s: %s\n" % (tweetList[i]["retweeted_status"]["user"]["screen_name"], tweetList[i]["retweeted_status"]["full_text"])
|
||||
if hasattr(tweetList[i], "retweeted_status") and tweetList[i].is_quote_status == False:
|
||||
if not hasattr(tweetList[i], "message"):
|
||||
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, tweetList[i].retweeted_status.full_text)
|
||||
else:
|
||||
text = text + "rt @%s: %s\n" % (tweetList[i]["retweeted_status"]["user"]["screen_name"], tweetList[i][value])
|
||||
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, getattr(tweetList[i], value))
|
||||
else:
|
||||
text = text + " @%s: %s\n" % (tweetList[i]["user"]["screen_name"], tweetList[i][value])
|
||||
text = text + " @%s: %s\n" % (tweetList[i].user.screen_name, getattr(tweetList[i], value))
|
||||
# tweets with extended_entities could include image descriptions.
|
||||
if "extended_entities" in tweetList[i] and "media" in tweetList[i]["extended_entities"]:
|
||||
for z in tweetList[i]["extended_entities"]["media"]:
|
||||
if hasattr(tweetList[i], "extended_entities") and "media" in tweetList[i].extended_entities:
|
||||
for z in tweetList[i].extended_entities["media"]:
|
||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||
image_description.append(z["ext_alt_text"])
|
||||
if "retweeted_status" in tweetList[i] and "extended_entities" in tweetList[i]["retweeted_status"] and "media" in tweetList[i]["retweeted_status"]["extended_entities"]:
|
||||
for z in tweetList[i]["retweeted_status"]["extended_entities"]["media"]:
|
||||
if hasattr(tweetList[i], "retweeted_status") and hasattr(tweetList[i].retweeted_status, "extended_entities") and "media" in tweetList[i].retweeted_status["extended_entities"]:
|
||||
for z in tweetList[i].retweeted_status.extended_entities["media"]:
|
||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||
image_description.append(z["ext_alt_text"])
|
||||
# set rt and likes counters.
|
||||
rt_count = str(tweet["retweet_count"])
|
||||
favs_count = str(tweet["favorite_count"])
|
||||
rt_count = str(tweet.retweet_count)
|
||||
favs_count = str(tweet.favorite_count)
|
||||
# Gets the client from where this tweet was made.
|
||||
source = re.sub(r"(?s)<.*?>", "", tweet["source"])
|
||||
original_date = arrow.get(tweet["created_at"], "ddd MMM DD H:m:s Z YYYY", locale="en")
|
||||
source = tweet.source
|
||||
original_date = arrow.get(tweet.created_at, locale="en")
|
||||
date = original_date.shift(seconds=utc_offset).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
|
||||
if text == "":
|
||||
if "message" in tweet:
|
||||
if hasattr(tweet, "message"):
|
||||
value = "message"
|
||||
else:
|
||||
value = "full_text"
|
||||
if "retweeted_status" in tweet:
|
||||
if ("message" in tweet) == False:
|
||||
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet["retweeted_status"]["full_text"])
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
if not hasattr(tweet, "message"):
|
||||
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, tweet.retweeted_status.full_text)
|
||||
else:
|
||||
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet[value])
|
||||
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, getattr(tweet, value))
|
||||
else:
|
||||
text = tweet[value]
|
||||
text = getattr(tweet, value)
|
||||
text = self.clear_text(text)
|
||||
if "extended_entities" in tweet and "media" in tweet["extended_entities"]:
|
||||
for z in tweet["extended_entities"]["media"]:
|
||||
if hasattr(tweet, "extended_entities") and "media" in tweet.extended_entities:
|
||||
for z in tweet.extended_entities["media"]:
|
||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||
image_description.append(z["ext_alt_text"])
|
||||
if "retweeted_status" in tweet and "extended_entities" in tweet["retweeted_status"] and "media" in tweet["retweeted_status"]["extended_entities"]:
|
||||
for z in tweet["retweeted_status"]["extended_entities"]["media"]:
|
||||
if hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "extended_entities") and "media" in tweet.retweeted_status.extended_entities:
|
||||
for z in tweet.retweeted_status.extended_entities["media"]:
|
||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||
image_description.append(z["ext_alt_text"])
|
||||
self.message = message.viewTweet(text, rt_count, favs_count, source, date)
|
||||
self.message.set_title(len(text))
|
||||
results = parse_tweet(text)
|
||||
self.message.set_title(results.weightedLength)
|
||||
[self.message.set_image_description(i) for i in image_description]
|
||||
else:
|
||||
self.title = _(u"View item")
|
||||
text = tweet
|
||||
self.message = 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")
|
||||
@@ -284,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."))
|
@@ -1,7 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
import os
|
||||
import webbrowser
|
||||
import sound_lib
|
||||
@@ -74,12 +71,12 @@ class globalSettingsController(object):
|
||||
self.dialog.set_value("general", "update_period", config.app["app-settings"]["update_period"])
|
||||
self.dialog.set_value("general", "check_for_updates", config.app["app-settings"]["check_for_updates"])
|
||||
self.dialog.set_value("general", "remember_mention_and_longtweet", config.app["app-settings"]["remember_mention_and_longtweet"])
|
||||
proxyTypes=config.proxyTypes
|
||||
self.dialog.create_proxy([_(u"Direct connection")]+proxyTypes)
|
||||
if config.app["proxy"]["type"] not in proxyTypes:
|
||||
proxyTypes = [_("System default"), _("HTTP"), _("SOCKS v4"), _("SOCKS v4 with DNS support"), _("SOCKS v5"), _("SOCKS v5 with DNS support")]
|
||||
self.dialog.create_proxy(proxyTypes)
|
||||
try:
|
||||
self.dialog.proxy.type.SetSelection(config.app["proxy"]["type"])
|
||||
except:
|
||||
self.dialog.proxy.type.SetSelection(0)
|
||||
else:
|
||||
self.dialog.proxy.type.SetSelection(proxyTypes.index(config.app["proxy"]["type"])+1)
|
||||
self.dialog.set_value("proxy", "server", config.app["proxy"]["server"])
|
||||
self.dialog.set_value("proxy", "port", config.app["proxy"]["port"])
|
||||
self.dialog.set_value("proxy", "user", config.app["proxy"]["user"])
|
||||
@@ -121,7 +118,7 @@ class globalSettingsController(object):
|
||||
if config.app["proxy"]["type"]!=self.dialog.get_value("proxy", "type") or config.app["proxy"]["server"] != self.dialog.get_value("proxy", "server") or config.app["proxy"]["port"] != self.dialog.get_value("proxy", "port") or config.app["proxy"]["user"] != self.dialog.get_value("proxy", "user") or config.app["proxy"]["password"] != self.dialog.get_value("proxy", "password"):
|
||||
if self.is_started == True:
|
||||
self.needs_restart = True
|
||||
config.app["proxy"]["type"]=self.dialog.get_value("proxy", "type")
|
||||
config.app["proxy"]["type"] = self.dialog.proxy.type.Selection
|
||||
config.app["proxy"]["server"] = self.dialog.get_value("proxy", "server")
|
||||
config.app["proxy"]["port"] = self.dialog.get_value("proxy", "port")
|
||||
config.app["proxy"]["user"] = self.dialog.get_value("proxy", "user")
|
||||
@@ -151,6 +148,7 @@ class accountSettingsController(globalSettingsController):
|
||||
else:
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
|
||||
self.dialog.set_value("general", "persist_size", str(self.config["general"]["persist_size"]))
|
||||
self.dialog.set_value("general", "load_cache_in_memory", self.config["general"]["load_cache_in_memory"])
|
||||
self.dialog.create_reporting()
|
||||
self.dialog.set_value("reporting", "speech_reporting", self.config["reporting"]["speech_reporting"])
|
||||
self.dialog.set_value("reporting", "braille_reporting", self.config["reporting"]["braille_reporting"])
|
||||
@@ -193,6 +191,9 @@ class accountSettingsController(globalSettingsController):
|
||||
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
|
||||
self.config["general"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names")
|
||||
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
|
||||
if self.config["general"]["load_cache_in_memory"] != self.dialog.get_value("general", "load_cache_in_memory"):
|
||||
self.config["general"]["load_cache_in_memory"] = self.dialog.get_value("general", "load_cache_in_memory")
|
||||
self.needs_restart = True
|
||||
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
|
||||
if self.dialog.get_value("general", "persist_size") == '':
|
||||
self.config["general"]["persist_size"] =-1
|
||||
|
@@ -1,6 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
from wxUI.dialogs import trends
|
||||
import widgetUtils
|
||||
|
||||
@@ -10,7 +8,7 @@ class trendingTopicsController(object):
|
||||
self.countries = {}
|
||||
self.cities = {}
|
||||
self.dialog = trends.trendingTopicsDialog()
|
||||
self.information = session.twitter.get_available_trends()
|
||||
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)
|
||||
|
@@ -1,6 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import wx
|
||||
import webbrowser
|
||||
import widgetUtils
|
||||
@@ -8,7 +6,8 @@ import output
|
||||
from wxUI.dialogs import update_profile, show_user
|
||||
import logging
|
||||
log = logging.getLogger("controller.user")
|
||||
from twython import TwythonError
|
||||
from tweepy.errors import TweepyException, Forbidden, NotFound
|
||||
from sessions.twitter import utils
|
||||
|
||||
class profileController(object):
|
||||
def __init__(self, session, user=None):
|
||||
@@ -25,36 +24,36 @@ class profileController(object):
|
||||
else:
|
||||
try:
|
||||
self.get_data(screen_name=self.user)
|
||||
except TwythonError as err:
|
||||
if err.error_code == 404:
|
||||
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.error_code == 403:
|
||||
if type(err) == Forbidden:
|
||||
wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
log.error("error %d: %s" % (err.error_code, err.msg))
|
||||
log.error("error %s" % (str(err)))
|
||||
return
|
||||
self.dialog = show_user.showUserProfile()
|
||||
string = self.get_user_info()
|
||||
self.dialog.set("text", string)
|
||||
self.dialog.set_title(_(u"Information for %s") % (self.data["screen_name"]))
|
||||
if self.data["url"] != None:
|
||||
self.dialog.set_title(_(u"Information for %s") % (self.data.screen_name))
|
||||
if self.data.url != None:
|
||||
self.dialog.enable_url()
|
||||
widgetUtils.connect_event(self.dialog.url, widgetUtils.BUTTON_PRESSED, self.visit_url)
|
||||
if self.dialog.get_response() == widgetUtils.OK and self.user == None:
|
||||
self.do_update()
|
||||
|
||||
def get_data(self, screen_name):
|
||||
self.data = self.session.twitter.show_user(screen_name=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"])
|
||||
if self.data["url"] != None:
|
||||
self.dialog.set_url(self.data["url"])
|
||||
if len(self.data["location"]) > 0:
|
||||
self.dialog.set_location(self.data["location"])
|
||||
if len(self.data["description"]) > 0:
|
||||
self.dialog.set_description(self.data["description"])
|
||||
self.dialog.set_name(self.data.name)
|
||||
if self.data.url != None:
|
||||
self.dialog.set_url(self.data.url)
|
||||
if len(self.data.location) > 0:
|
||||
self.dialog.set_location(self.data.location)
|
||||
if len(self.data.description) > 0:
|
||||
self.dialog.set_description(self.data.description)
|
||||
|
||||
def get_image(self):
|
||||
file = self.dialog.upload_picture()
|
||||
@@ -84,45 +83,46 @@ class profileController(object):
|
||||
if self.file != None:
|
||||
try:
|
||||
self.session.twitter.update_profile_image(image=self.file)
|
||||
except TwythonError as e:
|
||||
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
|
||||
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 TwythonError as e:
|
||||
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
|
||||
except TweepyException as e:
|
||||
output.speak(u"Error %s." % (str(e)))
|
||||
|
||||
def get_user_info(self):
|
||||
|
||||
string = u""
|
||||
string = string + _(u"Username: @%s\n") % (self.data["screen_name"])
|
||||
string = string + _(u"Name: %s\n") % (self.data["name"])
|
||||
if self.data["location"] != "":
|
||||
string = string + _(u"Location: %s\n") % (self.data["location"])
|
||||
if self.data["url"] != None:
|
||||
string = string+ _(u"URL: %s\n") % (self.data["url"])
|
||||
if self.data["description"] != "":
|
||||
string = string+ _(u"Bio: %s\n") % (self.data["description"])
|
||||
if self.data["protected"] == True: protected = _(u"Yes")
|
||||
string = string + _(u"Username: @%s\n") % (self.data.screen_name)
|
||||
string = string + _(u"Name: %s\n") % (self.data.name)
|
||||
if self.data.location != "":
|
||||
string = string + _(u"Location: %s\n") % (self.data.location)
|
||||
if self.data.url != None:
|
||||
string = string+ _(u"URL: %s\n") % (self.data.entities["url"]["urls"][0]["expanded_url"])
|
||||
if self.data.description != "":
|
||||
if self.data.entities.get("description") != None and self.data.entities["description"].get("urls"):
|
||||
self.data.description = utils.expand_urls(self.data.description, self.data.entities["description"])
|
||||
string = string+ _(u"Bio: %s\n") % (self.data.description)
|
||||
if self.data.protected == True: protected = _(u"Yes")
|
||||
else: protected = _(u"No")
|
||||
string = string+ _(u"Protected: %s\n") % (protected)
|
||||
if hasattr(self, "friendship_status"):
|
||||
relation = False
|
||||
friendship = "Relationship: "
|
||||
if self.friendship_status["relationship"]["target"]["followed_by"]:
|
||||
friendship += _(u"You follow {0}. ").format(self.data["name"],)
|
||||
if self.friendship_status[0].following:
|
||||
friendship += _(u"You follow {0}. ").format(self.data.name,)
|
||||
relation = True
|
||||
if self.friendship_status["relationship"]["target"]["following"]:
|
||||
friendship += _(u"{0} is following you.").format(self.data["name"],)
|
||||
if self.friendship_status[1].following:
|
||||
friendship += _(u"{0} is following you.").format(self.data.name,)
|
||||
relation = True
|
||||
if relation == True:
|
||||
string = string+friendship+"\n"
|
||||
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data["followers_count"], self.data["friends_count"])
|
||||
if self.data["verified"] == True: verified = _(u"Yes")
|
||||
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data.followers_count, self.data.friends_count)
|
||||
if self.data.verified == True: verified = _(u"Yes")
|
||||
else: verified = _(u"No")
|
||||
string = string+ _(u"Verified: %s\n") % (verified)
|
||||
string = string+ _(u"Tweets: %s\n") % (self.data["statuses_count"])
|
||||
string = string+ _(u"Likes: %s") % (self.data["favourites_count"])
|
||||
string = string+ _(u"Tweets: %s\n") % (self.data.statuses_count)
|
||||
string = string+ _(u"Likes: %s") % (self.data.favourites_count)
|
||||
return string
|
||||
|
||||
def visit_url(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab(self.data["url"])
|
||||
webbrowser.open_new_tab(self.data.url)
|
||||
|
@@ -1,12 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import re
|
||||
import widgetUtils
|
||||
import output
|
||||
from wxUI.dialogs import userActions
|
||||
from pubsub import pub
|
||||
from twython import TwythonError
|
||||
from tweepy.errors import TweepyException
|
||||
from extra import autocompletionUsers
|
||||
|
||||
class userActionsController(object):
|
||||
@@ -32,51 +29,51 @@ class userActionsController(object):
|
||||
def follow(self, user):
|
||||
try:
|
||||
self.session.twitter.create_friendship(screen_name=user )
|
||||
except TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), 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 TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), 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 TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), 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 TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), 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 TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), 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 TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), 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 TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||
except TweepyException as err:
|
||||
output.speak("Error %s" % (str(err)), True)
|
||||
|
||||
def ignore_client(self, user):
|
||||
tweet = self.buffer.get_right_tweet()
|
||||
if "sender" in tweet:
|
||||
if hasattr(tweet, "sender"):
|
||||
output.speak(_(u"You can't ignore direct messages"))
|
||||
return
|
||||
client = re.sub(r"(?s)<.*?>", "", tweet["source"])
|
||||
client = tweet.source
|
||||
if client not in self.session.settings["twitter"]["ignored_clients"]:
|
||||
self.session.settings["twitter"]["ignored_clients"].append(client)
|
||||
self.session.settings.write()
|
53
src/controller/userAliasController.py
Normal file
53
src/controller/userAliasController.py
Normal 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
|
@@ -13,8 +13,8 @@ actions = reverse_sort.reverse_sort([ ("audio", _(u"Audio tweet.")),
|
||||
("favourite", _(u"Tweet liked.")),
|
||||
("favourites_timeline_updated", _(u"Likes buffer updated.")),
|
||||
("geo", _(u"Geotweet.")),
|
||||
("image", _("Tweet contains one or more images")),
|
||||
("limit", _(u"Boundary reached.")),
|
||||
("image", _("Tweet contains one or more images")),
|
||||
("limit", _(u"Boundary reached.")),
|
||||
("list_tweet", _(u"List updated.")),
|
||||
("max_length", _(u"Too many characters.")),
|
||||
("mention_received", _(u"Mention received.")),
|
||||
|
@@ -1,7 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import output
|
||||
from . import storage
|
||||
from . import wx_menu
|
||||
|
@@ -1,11 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
# -*- coding: utf-8 -*-
|
||||
from builtins import object
|
||||
from . import storage
|
||||
import widgetUtils
|
||||
from . import wx_manage
|
||||
from tweepy.errors import TweepyException
|
||||
from . import storage, wx_manage
|
||||
from wxUI import commonMessageDialogs
|
||||
|
||||
class autocompletionManage(object):
|
||||
@@ -32,11 +28,12 @@ class autocompletionManage(object):
|
||||
if usr == False:
|
||||
return
|
||||
try:
|
||||
data = self.session.twitter.twitter.show_user(screen_name=usr)
|
||||
except:
|
||||
data = self.session.twitter.get_user(screen_name=usr)
|
||||
except TweepyException as e:
|
||||
log.exception("Exception raised when attempting to add an user to the autocomplete database manually.")
|
||||
self.dialog.show_invalid_user_error()
|
||||
return
|
||||
self.database.set_user(data["screen_name"], data["name"], 0)
|
||||
self.database.set_user(data.screen_name, data.name, 0)
|
||||
self.update_list()
|
||||
|
||||
def remove_user(self, ev):
|
||||
|
@@ -1,13 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
# -*- coding: utf-8 -*-
|
||||
from builtins import object
|
||||
from . import storage
|
||||
import widgetUtils
|
||||
import output
|
||||
from . import wx_settings
|
||||
from . import manage
|
||||
import output
|
||||
from . import storage
|
||||
from mysc.thread_utils import call_threaded
|
||||
|
||||
class autocompletionSettings(object):
|
||||
@@ -30,14 +26,14 @@ class autocompletionSettings(object):
|
||||
database = storage.storage(self.buffer.session.session_id)
|
||||
if self.dialog.get("followers_buffer") == True:
|
||||
buffer = self.window.search_buffer("followers", self.config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]["items"]:
|
||||
database.set_user(i["screen_name"], i["name"], 1)
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i.screen_name, i.name, 1)
|
||||
else:
|
||||
database.remove_by_buffer(1)
|
||||
if self.dialog.get("friends_buffer") == True:
|
||||
buffer = self.window.search_buffer("friends", self.config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]["items"]:
|
||||
database.set_user(i["screen_name"], i["name"], 2)
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i.screen_name, i.name, 2)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
||||
wx_settings.show_success_dialog()
|
||||
@@ -52,12 +48,12 @@ def execute_at_startup(window, buffer, config):
|
||||
if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True:
|
||||
buffer = window.search_buffer("followers", config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i["screen_name"], i["name"], 1)
|
||||
database.set_user(i.screen_name, i.name, 1)
|
||||
else:
|
||||
database.remove_by_buffer(1)
|
||||
if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True:
|
||||
buffer = window.search_buffer("friends", config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i["screen_name"], i["name"], 2)
|
||||
database.set_user(i.screen_name, i.name, 2)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
@@ -1,11 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import sqlite3, paths
|
||||
import os, sqlite3, paths
|
||||
|
||||
class storage(object):
|
||||
def __init__(self, session_id):
|
||||
self.connection = sqlite3.connect(paths.config_path("%s/autocompletionUsers.dat" % (session_id)))
|
||||
self.connection = sqlite3.connect(os.path.join(paths.config_path(), "%s/autocompletionUsers.dat" % (session_id)))
|
||||
self.cursor = self.connection.cursor()
|
||||
if self.table_exist("users") == False:
|
||||
self.create_table()
|
||||
@@ -23,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):
|
||||
|
@@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import wx
|
||||
import widgetUtils
|
||||
from multiplatform_widgets import widgets
|
||||
import application
|
||||
|
||||
class autocompletionManageDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self):
|
||||
super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocompletion database"))
|
||||
|
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import wx
|
||||
|
||||
class menu(wx.Menu):
|
||||
|
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import wx
|
||||
import widgetUtils
|
||||
import application
|
||||
|
@@ -1,15 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import zip
|
||||
from yandex_translate import YandexTranslate
|
||||
import logging
|
||||
from googletrans import Translator, LANGUAGES
|
||||
|
||||
log = logging.getLogger("extras.translator")
|
||||
|
||||
# create a single translator instance
|
||||
# see https://github.com/ssut/py-googletrans/issues/234
|
||||
t = None
|
||||
|
||||
def translate(text="", target="en"):
|
||||
t = YandexTranslate("trnsl.1.1.20161012T134532Z.d01b9c75fc39aa74.7d1be75a5166a80583eeb020e10f584168da6bf7")
|
||||
vars = dict(text=text, lang=target)
|
||||
return t.translate(**vars)["text"][0]
|
||||
global t
|
||||
log.debug("Received translation request for language %s, text=%s" % (target, text))
|
||||
if t == None:
|
||||
t = Translator()
|
||||
vars = dict(text=text, dest=target)
|
||||
return t.translate(**vars).text
|
||||
|
||||
supported_langs = None
|
||||
d = None
|
||||
|
||||
languages = {
|
||||
"af": _(u"Afrikaans"),
|
||||
"sq": _(u"Albanian"),
|
||||
@@ -105,11 +113,4 @@ languages = {
|
||||
}
|
||||
|
||||
def available_languages():
|
||||
global supported_langs, d
|
||||
if supported_langs == None and d == None:
|
||||
t = YandexTranslate("trnsl.1.1.20161012T134532Z.d01b9c75fc39aa74.7d1be75a5166a80583eeb020e10f584168da6bf7")
|
||||
supported_langs = t.langs
|
||||
d = []
|
||||
for i in supported_langs:
|
||||
d.append(languages[i])
|
||||
return sorted(zip(supported_langs, d))
|
||||
return dict(sorted(languages.items(), key=lambda x: x[1]))
|
||||
|
@@ -16,37 +16,21 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
from . import translator
|
||||
import wx
|
||||
from wxUI.dialogs import baseDialog
|
||||
|
||||
class translateDialog(baseDialog.BaseWXDialog):
|
||||
def __init__(self):
|
||||
languages = []
|
||||
language_dict = translator.available_languages()
|
||||
for k in language_dict:
|
||||
languages.append(language_dict[k])
|
||||
super(translateDialog, self).__init__(None, -1, title=_(u"Translate message"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
staticDest = wx.StaticText(panel, -1, _(u"Target language"))
|
||||
self.dest_lang = wx.ComboBox(panel, -1, choices=[x[1] for x in translator.available_languages()], style = wx.CB_READONLY)
|
||||
self.dest_lang = wx.ComboBox(panel, -1, choices=languages, style = wx.CB_READONLY)
|
||||
self.dest_lang.SetFocus()
|
||||
self.dest_lang.SetSelection(0)
|
||||
listSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from arrow import locales
|
||||
from arrow.locales import Locale
|
||||
|
||||
@@ -8,88 +7,43 @@ def fix():
|
||||
locales.get_locale = get_locale
|
||||
|
||||
def get_locale(name):
|
||||
locale_cls = locales._locales.get(name.lower())
|
||||
locale_cls = locales._locale_map.get(name.lower())
|
||||
if locale_cls is None:
|
||||
name = name[:2]
|
||||
locale_cls = locales._locales.get(name.lower())
|
||||
locale_cls = locales._locale_map.get(name.lower())
|
||||
if locale_cls == None:
|
||||
return locales.EnglishLocale()
|
||||
return locale_cls()
|
||||
|
||||
class CatalaLocale(Locale):
|
||||
names = ['ca', 'ca_es', 'ca_ca']
|
||||
past = 'Fa {0}'
|
||||
future = '{0}' # I don't know what's the right phrase in catala for the future.
|
||||
|
||||
timeframes = {
|
||||
'now': 'Ara mateix',
|
||||
'seconds': 'segons',
|
||||
'minute': '1 minut',
|
||||
'minutes': '{0} minuts',
|
||||
'hour': 'una hora',
|
||||
'hours': '{0} hores',
|
||||
'day': 'un dia',
|
||||
'days': '{0} dies',
|
||||
'month': 'un mes',
|
||||
'months': '{0} messos',
|
||||
'year': 'un any',
|
||||
'years': '{0} anys',
|
||||
}
|
||||
|
||||
month_names = ['', 'Jener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Decembre']
|
||||
month_abbreviations = ['', 'Jener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Decembre']
|
||||
day_names = ['', 'Dilluns', 'Dimars', 'Dimecres', 'Dijous', 'Divendres', 'Disabte', 'Diumenge']
|
||||
day_abbreviations = ['', 'Dilluns', 'Dimars', 'Dimecres', 'Dijous', 'Divendres', 'Disabte', 'Diumenge']
|
||||
|
||||
class GalicianLocale(Locale):
|
||||
class GalicianLocale(object):
|
||||
names = ['gl', 'gl_es', 'gl_gl']
|
||||
past = 'Fai {0}'
|
||||
past = 'Hai {0}'
|
||||
future = 'En {0}'
|
||||
and_word = "e"
|
||||
|
||||
timeframes = {
|
||||
'now': 'Agora mesmo',
|
||||
'seconds': 'segundos',
|
||||
'now': 'Agora',
|
||||
"second": "un segundo",
|
||||
'seconds': '{0} segundos',
|
||||
'minute': 'un minuto',
|
||||
'minutes': '{0} minutos',
|
||||
'hour': 'una hora',
|
||||
'hour': 'unha hora',
|
||||
'hours': '{0} horas',
|
||||
'day': 'un día',
|
||||
'days': '{0} días',
|
||||
"week": "unha semana",
|
||||
"weeks": "{0} semanas",
|
||||
'month': 'un mes',
|
||||
'months': '{0} meses',
|
||||
'year': 'un ano',
|
||||
'years': '{0} anos',
|
||||
}
|
||||
|
||||
month_names = ['', 'Xaneiro', 'Febreiro', 'Marzo', 'Abril', 'Maio', 'Xuño', 'Xullo', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Decembro']
|
||||
month_abbreviations = ['', 'Xan', 'Feb', 'Mar', 'Abr', 'Mai', 'Xun', 'Xul', 'Ago', 'Set', 'Out', 'Nov', 'Dec']
|
||||
day_names = ['', 'Luns', 'Martes', 'Mércores', 'Xoves', 'Venres', 'Sábado', 'Domingo']
|
||||
day_abbreviations = ['', 'Lun', 'Mar', 'Mer', 'xov', 'Ven' 'Sab', 'Dom']
|
||||
meridians = {"am": "am", "pm": "pm", "AM": "AM", "PM": "PM"}
|
||||
|
||||
class BasqueLocale(Locale):
|
||||
names = ['eu', 'eu_es', 'eu_eu']
|
||||
past = 'duela {0}'
|
||||
future = '{0} igarota'
|
||||
|
||||
timeframes = {
|
||||
'now': 'Orain',
|
||||
# 'second': 'segundu bat',
|
||||
'seconds': 'segundu batzuk', # without specifying a number.
|
||||
#'seconds': '{0} segundu', # specifying a number
|
||||
'minute': 'minutu bat',
|
||||
'minutes': '{0} minutu',
|
||||
'hour': 'ordu bat',
|
||||
'hours': '{0} ordu',
|
||||
'day': 'egun bat',
|
||||
'days': '{0} egun',
|
||||
'month': 'hilabete bat',
|
||||
'months': '{0} hilabete',
|
||||
'year': 'urte bat',
|
||||
'years': '{0} urte',
|
||||
}
|
||||
|
||||
month_names = ['', 'Urtarrilak', 'Otsailak', 'Martxoak', 'Apirilak', 'Maiatzak', 'Ekainak', 'Uztailak', 'Abuztuak', 'Irailak', 'Urriak', 'Azaroak', 'Abenduak']
|
||||
month_abbreviations = ['', 'urt', 'ots', 'mar', 'api', 'mai', 'eka', 'uzt', 'abu', 'ira', 'urr', 'aza', 'abe']
|
||||
day_names = ['', 'Asteleehna', 'Asteartea', 'Asteazkena', 'Osteguna', 'Ostirala', 'Larunbata', 'Igandea']
|
||||
day_abbreviations = ['', 'al', 'ar', 'az', 'og', 'ol', 'lr', 'ig']
|
||||
month_names = ['', 'xaneiro', 'febreiro', 'marzo', 'abril', 'maio', 'xuño', 'xullo', 'agosto', 'setembro', 'outubro', 'novembro', 'decembro']
|
||||
month_abbreviations = ['', 'xan', 'feb', 'mar', 'abr', 'mai', 'xun', 'xul', 'ago', 'set', 'out', 'nov', 'dec']
|
||||
day_names = ['', 'luns', 'martes', 'mércores', 'xoves', 'venres', 'sábado', 'domingo']
|
||||
day_abbreviations = ['', 'lun', 'mar', 'mer', 'xov', 'ven', 'sab', 'dom']
|
||||
ordinal_day_re = r"((?P<value>[1-3]?[0-9](?=[ºª]))[ºª])"
|
||||
|
||||
|
@@ -4,7 +4,7 @@ import thread
|
||||
import pyatspi
|
||||
def parse(s):
|
||||
"""parse a string like control+f into (modifier, key).
|
||||
Unknown modifiers will return ValueError."""
|
||||
Unknown modifiers will return ValueError."""
|
||||
m = 0
|
||||
lst = s.split('+')
|
||||
if not len(lst): return (0, s)
|
||||
@@ -13,7 +13,7 @@ Unknown modifiers will return ValueError."""
|
||||
"shift": 1<<pyatspi.MODIFIER_SHIFT,
|
||||
"control": 1<<pyatspi.MODIFIER_CONTROL,
|
||||
"alt": 1<<pyatspi.MODIFIER_ALT,
|
||||
"win":1<<pyatspi.MODIFIER_META3,
|
||||
"win":1<<pyatspi.MODIFIER_META3,
|
||||
}
|
||||
for item in lst:
|
||||
if item in d:
|
||||
@@ -45,8 +45,8 @@ class LinuxKeyboardHandler(KeyboardHandler):
|
||||
t.start()
|
||||
def register_key(self, key, function):
|
||||
"""key will be a string, such as control+shift+f.
|
||||
We need to convert that, using parse_key,
|
||||
into modifier and key to put into our dictionary."""
|
||||
We need to convert that, using parse_key,
|
||||
into modifier and key to put into our dictionary."""
|
||||
#register key so we know if we have it on event receive.
|
||||
t = parse(key)
|
||||
keys[t] = function
|
||||
|
@@ -35,3 +35,4 @@ 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")
|
||||
add_alias=string(default="")
|
@@ -54,3 +54,4 @@ 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")
|
||||
add_alias=string(default="")
|
@@ -55,3 +55,4 @@ 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="")
|
58
src/keymaps/Windows11.keymap
Normal file
58
src/keymaps/Windows11.keymap
Normal 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="")
|
@@ -56,3 +56,4 @@ 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")
|
||||
add_alias=string(default="")
|
@@ -57,3 +57,4 @@ 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")
|
||||
add_alias=string(default="")
|
@@ -1,60 +1,61 @@
|
||||
# -*- 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"),
|
||||
"left": _(u"Go to the previous buffer"),
|
||||
"right": _(u"Go to the next buffer"),
|
||||
"next_account": _(u"Focus the next session"),
|
||||
"previous_account": _(u"Focus the previous session"),
|
||||
"show_hide": _(u"Show or hide the GUI"),
|
||||
"post_tweet": _(u"New tweet"),
|
||||
"post_reply": _(u"Reply"),
|
||||
"post_retweet": _(u"Retweet"),
|
||||
"send_dm": _(u"Send direct message"),
|
||||
"add_to_favourites": _(u"Like a tweet"),
|
||||
"toggle_like": _(u"Like/unlike a tweet"),
|
||||
"remove_from_favourites": _(u"Unlike a tweet"),
|
||||
"follow": _(u"Open the user actions dialogue"),
|
||||
"user_details": _(u"See user details"),
|
||||
"view_item": _(u"Show tweet"),
|
||||
"exit": _(u"Quit"),
|
||||
"open_timeline": _(u"Open user timeline"),
|
||||
"remove_buffer": _(u"Destroy buffer"),
|
||||
"interact": _(u"Interact with the currently focused tweet."),
|
||||
"url": _(u"Open URL"),
|
||||
"open_in_browser": _(u"View in Twitter"),
|
||||
"volume_up": _(u"Increase volume by 5%"),
|
||||
"volume_down": _(u"Decrease volume by 5%"),
|
||||
"go_home": _(u"Jump to the first element of a buffer"),
|
||||
"go_end": _(u"Jump to the last element of the current buffer"),
|
||||
"go_page_up": _(u"Jump 20 elements up in the current buffer"),
|
||||
"go_page_down": _(u"Jump 20 elements down in the current buffer"),
|
||||
"update_profile": _(u"Edit profile"),
|
||||
"delete": _(u"Delete a tweet or direct message"),
|
||||
"clear_buffer": _(u"Empty the current buffer"),
|
||||
"repeat_item": _(u"Repeat last item"),
|
||||
"copy_to_clipboard": _(u"Copy to clipboard"),
|
||||
"add_to_list": _(u"Add to list"),
|
||||
"remove_from_list": _(u"Remove from list"),
|
||||
"toggle_buffer_mute": _(u"Mute/unmute the active buffer"),
|
||||
"toggle_session_mute": _(u"Mute/unmute the current session"),
|
||||
"toggle_autoread": _(u"toggle the automatic reading of incoming tweets in the active buffer"),
|
||||
"search": _(u"Search on twitter"),
|
||||
"find": _(u"Find a string in the currently focused buffer"),
|
||||
"edit_keystrokes": _(u"Show the keystroke editor"),
|
||||
"view_user_lists": _(u"Show lists for a specified user"),
|
||||
"get_more_items": _(u"load previous items"),
|
||||
"reverse_geocode": _(u"Get geolocation"),
|
||||
"view_reverse_geocode": _(u"Display the tweet's geolocation in a dialog"),
|
||||
"get_trending_topics": _(u"Create a trending topics buffer"),
|
||||
"open_conversation": _(u"View conversation"),
|
||||
"check_for_updates": _(u"Check and download updates"),
|
||||
"lists_manager": _(u"Opens the list manager, which allows you to create, edit, delete and open lists in buffers."),
|
||||
"configuration": _(u"Opens the global settings dialogue"),
|
||||
"list_manager": _(u"Opens the list manager"),
|
||||
"accountConfiguration": _(u"Opens the account settings dialogue"),
|
||||
"audio": _(u"Try to play an audio file"),
|
||||
"update_buffer": _(u"Updates the buffer and retrieves possible lost items there."),
|
||||
"ocr_image": _(u"Extracts the text from a picture and displays the result in a dialog."),
|
||||
"up": _(u"Go up in the current buffer"),
|
||||
"down": _(u"Go down in the current buffer"),
|
||||
"left": _(u"Go to the previous buffer"),
|
||||
"right": _(u"Go to the next buffer"),
|
||||
"next_account": _(u"Focus the next session"),
|
||||
"previous_account": _(u"Focus the previous session"),
|
||||
"show_hide": _(u"Show or hide the GUI"),
|
||||
"post_tweet": _(u"New tweet"),
|
||||
"post_reply": _(u"Reply"),
|
||||
"post_retweet": _(u"Retweet"),
|
||||
"send_dm": _(u"Send direct message"),
|
||||
"add_to_favourites": _(u"Like a tweet"),
|
||||
"toggle_like": _(u"Like/unlike a tweet"),
|
||||
"remove_from_favourites": _(u"Unlike a tweet"),
|
||||
"follow": _(u"Open the user actions dialogue"),
|
||||
"user_details": _(u"See user details"),
|
||||
"view_item": _(u"Show tweet"),
|
||||
"exit": _(u"Quit"),
|
||||
"open_timeline": _(u"Open user timeline"),
|
||||
"remove_buffer": _(u"Destroy buffer"),
|
||||
"interact": _(u"Interact with the currently focused tweet."),
|
||||
"url": _(u"Open URL"),
|
||||
"open_in_browser": _(u"View in Twitter"),
|
||||
"volume_up": _(u"Increase volume by 5%"),
|
||||
"volume_down": _(u"Decrease volume by 5%"),
|
||||
"go_home": _(u"Jump to the first element of a buffer"),
|
||||
"go_end": _(u"Jump to the last element of the current buffer"),
|
||||
"go_page_up": _(u"Jump 20 elements up in the current buffer"),
|
||||
"go_page_down": _(u"Jump 20 elements down in the current buffer"),
|
||||
"update_profile": _(u"Edit profile"),
|
||||
"delete": _(u"Delete a tweet or direct message"),
|
||||
"clear_buffer": _(u"Empty the current buffer"),
|
||||
"repeat_item": _(u"Repeat last item"),
|
||||
"copy_to_clipboard": _(u"Copy to clipboard"),
|
||||
"add_to_list": _(u"Add to list"),
|
||||
"remove_from_list": _(u"Remove from list"),
|
||||
"toggle_buffer_mute": _(u"Mute/unmute the active buffer"),
|
||||
"toggle_session_mute": _(u"Mute/unmute the current session"),
|
||||
"toggle_autoread": _(u"toggle the automatic reading of incoming tweets in the active buffer"),
|
||||
"search": _(u"Search on twitter"),
|
||||
"find": _(u"Find a string in the currently focused buffer"),
|
||||
"edit_keystrokes": _(u"Show the keystroke editor"),
|
||||
"view_user_lists": _(u"Show lists for a specified user"),
|
||||
"get_more_items": _(u"load previous items"),
|
||||
"reverse_geocode": _(u"Get geolocation"),
|
||||
"view_reverse_geocode": _(u"Display the tweet's geolocation in a dialog"),
|
||||
"get_trending_topics": _(u"Create a trending topics buffer"),
|
||||
"open_conversation": _(u"View conversation"),
|
||||
"check_for_updates": _(u"Check and download updates"),
|
||||
"lists_manager": _(u"Opens the list manager, which allows you to create, edit, delete and open lists in buffers."),
|
||||
"configuration": _(u"Opens the global settings dialogue"),
|
||||
"list_manager": _(u"Opens the list manager"),
|
||||
"accountConfiguration": _(u"Opens the account settings dialogue"),
|
||||
"audio": _(u"Try to play an audio file"),
|
||||
"update_buffer": _(u"Updates the buffer and retrieves possible lost items there."),
|
||||
"ocr_image": _(u"Extracts the text from a picture and displays the result in a dialog."),
|
||||
"add_alias": _("Adds an alias to an user"),
|
||||
}
|
||||
|
@@ -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):
|
||||
|
@@ -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"))
|
||||
|
@@ -195,7 +195,7 @@ def langToWindowsLocale(lang):
|
||||
languages = {"en": "eng",
|
||||
"ar": "ara",
|
||||
"ca": "cat",
|
||||
"de": "deu",
|
||||
"de": "deu",
|
||||
"es": "esp",
|
||||
"fi": "fin",
|
||||
"fr": "fre_FRA",
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -6,15 +6,15 @@ 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: 2018-08-15 09:47+0400\n"
|
||||
"Last-Translator: Manuel Cortez <manuel@manuelcortez.net>\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"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 1.5.7\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"
|
||||
@@ -84,24 +84,22 @@ msgid "Muted users"
|
||||
msgstr "Отключенные пользователи"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:75
|
||||
#, fuzzy
|
||||
msgid "{username}'s timeline"
|
||||
msgstr "Открыть ленту пользователя"
|
||||
msgstr "Лента твитов {username}"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:77
|
||||
msgid "{username}'s likes"
|
||||
msgstr ""
|
||||
msgstr "Понравившееся {username}"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:79
|
||||
msgid "{username}'s followers"
|
||||
msgstr ""
|
||||
msgstr "Читающие {username}"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:81
|
||||
msgid "{username}'s friends"
|
||||
msgstr ""
|
||||
msgstr "Список читаемых {username}"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:83
|
||||
#, fuzzy
|
||||
msgid "Unknown buffer"
|
||||
msgstr "Неизвестно"
|
||||
|
||||
@@ -119,14 +117,12 @@ msgid "Write the tweet here"
|
||||
msgstr "Напишите текст твита здесь"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:194
|
||||
#, fuzzy
|
||||
msgid "New tweet in {0}"
|
||||
msgstr "Новый твит"
|
||||
msgstr "Новый твит в {0}"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:197
|
||||
#, fuzzy
|
||||
msgid "{0} new tweets in {1}."
|
||||
msgstr "Пользователь @{0} процитировал ваш твит: {1}"
|
||||
msgstr "@{0} процитировал ваш твит: {1}"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:232
|
||||
#: ../src\controller\buffers\twitterBuffers.py:676
|
||||
@@ -182,7 +178,7 @@ msgstr "Профиль"
|
||||
#: ../src\controller\buffers\twitterBuffers.py:634
|
||||
#: ../src\controller\buffers\twitterBuffers.py:987
|
||||
msgid "Opening item in web browser..."
|
||||
msgstr ""
|
||||
msgstr "Открыть в браузере"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:688
|
||||
#: ../src\controller\buffers\twitterBuffers.py:855
|
||||
@@ -196,12 +192,10 @@ msgid "Mention"
|
||||
msgstr "Упомянуть"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:728
|
||||
#, fuzzy
|
||||
msgid "{0} new direct messages."
|
||||
msgstr "Новое личное сообщение"
|
||||
|
||||
#: ../src\controller\buffers\twitterBuffers.py:731
|
||||
#, fuzzy
|
||||
msgid "This action is not supported in the buffer yet."
|
||||
msgstr "Это действие не поддерживается в данном буфере"
|
||||
|
||||
@@ -210,16 +204,16 @@ msgid ""
|
||||
"Getting more items cannot be done in this buffer. Use the direct messages "
|
||||
"buffer instead."
|
||||
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"
|
||||
@@ -318,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}"
|
||||
@@ -379,12 +372,11 @@ msgstr "Список уже открыт"
|
||||
|
||||
#: ../src\controller\mainController.py:1423
|
||||
#: ../src\controller\mainController.py:1439
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"An error happened while trying to connect to the server. Please try later."
|
||||
msgstr ""
|
||||
"Что-то неожиданное произошло при попытке сообщить об ошибке. Пожалуйста, "
|
||||
"повторите попытку позже"
|
||||
"При попытке подключиться к серверу произошла ошибка. Пожалуйста, попробуйте "
|
||||
"позже."
|
||||
|
||||
#: ../src\controller\mainController.py:1475
|
||||
msgid "The auto-reading of new tweets is enabled for this buffer"
|
||||
@@ -874,19 +866,19 @@ msgstr "Игнорировать"
|
||||
|
||||
#: ../src\extra\SpellChecker\wx_ui.py:43
|
||||
msgid "I&gnore all"
|
||||
msgstr "Игнорировать все"
|
||||
msgstr "&Игнорировать все"
|
||||
|
||||
#: ../src\extra\SpellChecker\wx_ui.py:44
|
||||
msgid "&Replace"
|
||||
msgstr "Заменить"
|
||||
msgstr "&Заменить"
|
||||
|
||||
#: ../src\extra\SpellChecker\wx_ui.py:45
|
||||
msgid "R&eplace all"
|
||||
msgstr "Заменить все"
|
||||
msgstr "З&аменить все"
|
||||
|
||||
#: ../src\extra\SpellChecker\wx_ui.py:46
|
||||
msgid "&Add to personal dictionary"
|
||||
msgstr "Добавить в личный словарь"
|
||||
msgstr "&Добавить в личный словарь"
|
||||
|
||||
#: ../src\extra\SpellChecker\wx_ui.py:79
|
||||
msgid ""
|
||||
@@ -1587,9 +1579,8 @@ msgid "Open URL"
|
||||
msgstr "Открыть ссылку"
|
||||
|
||||
#: ../src\keystrokeEditor\constants.py:25
|
||||
#, fuzzy
|
||||
msgid "View in Twitter"
|
||||
msgstr "Поиск в твиттере"
|
||||
msgstr "Посмотреть в Твиттере"
|
||||
|
||||
#: ../src\keystrokeEditor\constants.py:26
|
||||
msgid "Increase volume by 5%"
|
||||
@@ -1693,7 +1684,7 @@ msgstr "Просмотр беседы"
|
||||
|
||||
#: ../src\keystrokeEditor\constants.py:51
|
||||
msgid "Check and download updates"
|
||||
msgstr "Проверить на наличие обновлений"
|
||||
msgstr "Проверить наличие обновлений"
|
||||
|
||||
#: ../src\keystrokeEditor\constants.py:52
|
||||
msgid ""
|
||||
@@ -1708,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"
|
||||
@@ -1816,6 +1806,9 @@ msgid ""
|
||||
"If you're sure that {0} isn't running, try deleting the file at {1}. If "
|
||||
"you're unsure of how to do this, contact the {0} developers."
|
||||
msgstr ""
|
||||
"{0} уже запущен. Закройте другой экземпляр, прежде чем запускать этот. Если "
|
||||
"вы уверены, что {0} не активен, попробуйте удалить файл по адресу {1}. Если "
|
||||
"вы не знаете, как это сделать, обратитесь к разработчикам {0}."
|
||||
|
||||
#: ../src\sessionmanager\wxUI.py:8
|
||||
msgid "Session manager"
|
||||
@@ -1920,9 +1913,8 @@ msgid "public"
|
||||
msgstr "Публичный"
|
||||
|
||||
#: ../src\sessions\twitter\session.py:169
|
||||
#, fuzzy
|
||||
msgid "There are no more items to retrieve in this buffer."
|
||||
msgstr "Координаты отсутствуют"
|
||||
msgstr "В этом буфере больше нет элементов для извлечения."
|
||||
|
||||
#: ../src\sessions\twitter\session.py:215
|
||||
msgid "%s failed. Reason: %s"
|
||||
@@ -2048,7 +2040,7 @@ msgstr "Тренды"
|
||||
|
||||
#: ../src\wxUI\buffers\trends.py:18
|
||||
msgid "Tweet about this trend"
|
||||
msgstr "Tweet о тренде"
|
||||
msgstr "Твитнуть о тренде"
|
||||
|
||||
#: ../src\wxUI\buffers\trends.py:19 ../src\wxUI\menus.py:96
|
||||
msgid "Search topic"
|
||||
@@ -2265,6 +2257,8 @@ msgid ""
|
||||
"{0} quit unexpectedly the last time it was run. If the problem persists, "
|
||||
"please report it to the {0} developers."
|
||||
msgstr ""
|
||||
"{0} неожиданно закончил свою работу при последнем запуске. Если проблема не "
|
||||
"устранена, пожалуйста, сообщите об этом разработчикам {0}."
|
||||
|
||||
#: ../src\wxUI\dialogs\attach.py:9
|
||||
msgid "Add an attachment"
|
||||
@@ -2362,6 +2356,7 @@ msgstr "Использование удлинителя твитов (может
|
||||
#: ../src\wxUI\dialogs\configuration.py:48
|
||||
msgid "Remember state for mention all and long tweet"
|
||||
msgstr ""
|
||||
"Запоминать состояние для упоминаний всех пользователей и длинных твитов"
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:51
|
||||
msgid "Keymap"
|
||||
@@ -2413,7 +2408,7 @@ msgstr ""
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:116
|
||||
msgid "Retweet mode"
|
||||
msgstr "Стиль ретвита"
|
||||
msgstr "Режим ретвита"
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:122
|
||||
msgid "Show screen names instead of full names"
|
||||
@@ -2429,11 +2424,11 @@ msgstr ""
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:134
|
||||
msgid "Enable automatic speech feedback"
|
||||
msgstr ""
|
||||
msgstr "Включить автоматический речевой вывод"
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:136
|
||||
msgid "Enable automatic Braille feedback"
|
||||
msgstr ""
|
||||
msgstr "Включить автоматический брайлевский вывод"
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:144
|
||||
msgid "Status"
|
||||
@@ -2515,7 +2510,7 @@ msgstr "Устройство записи"
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:299
|
||||
msgid "Sound pack"
|
||||
msgstr "Пакет звуков"
|
||||
msgstr "Звуковая схема"
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:305
|
||||
msgid "Indicate audio tweets with sound"
|
||||
@@ -2539,7 +2534,7 @@ msgstr "API ключ SndUp"
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:353
|
||||
msgid "{0} preferences"
|
||||
msgstr "Настройки {0}"
|
||||
msgstr "Параметры {0}"
|
||||
|
||||
#: ../src\wxUI\dialogs\configuration.py:364
|
||||
msgid "Proxy"
|
||||
@@ -2819,9 +2814,8 @@ msgid "Source: "
|
||||
msgstr "Клиент"
|
||||
|
||||
#: ../src\wxUI\dialogs\message.py:342 ../src\wxUI\dialogs\message.py:420
|
||||
#, fuzzy
|
||||
msgid "Date: "
|
||||
msgstr "Дата"
|
||||
msgstr "Дата: "
|
||||
|
||||
#: ../src\wxUI\dialogs\message.py:405
|
||||
msgid "View"
|
||||
@@ -2890,7 +2884,7 @@ msgstr "Детали"
|
||||
|
||||
#: ../src\wxUI\dialogs\show_user.py:16
|
||||
msgid "&Go to URL"
|
||||
msgstr "Перейти по ссылке"
|
||||
msgstr "&Перейти по ссылке"
|
||||
|
||||
#: ../src\wxUI\dialogs\trends.py:12
|
||||
msgid "View trending topics"
|
||||
@@ -3031,12 +3025,11 @@ msgstr "н&е нравится"
|
||||
|
||||
#: ../src\wxUI\menus.py:15 ../src\wxUI\menus.py:35 ../src\wxUI\menus.py:51
|
||||
msgid "&Open URL"
|
||||
msgstr "Открыть ссылку"
|
||||
msgstr "&Открыть ссылку"
|
||||
|
||||
#: ../src\wxUI\menus.py:17 ../src\wxUI\menus.py:53 ../src\wxUI\menus.py:86
|
||||
#, fuzzy
|
||||
msgid "&Open in Twitter"
|
||||
msgstr "Поиск в твиттере"
|
||||
msgstr "Открыть в &Твиттере"
|
||||
|
||||
#: ../src\wxUI\menus.py:19 ../src\wxUI\menus.py:37 ../src\wxUI\menus.py:55
|
||||
msgid "&Play audio"
|
||||
@@ -3246,7 +3239,7 @@ msgstr "Сайт {0}"
|
||||
|
||||
#: ../src\wxUI\view.py:77
|
||||
msgid "Get soundpacks for TWBlue"
|
||||
msgstr ""
|
||||
msgstr "Получить звуковые схемы для TWBlue"
|
||||
|
||||
#: ../src\wxUI\view.py:78
|
||||
msgid "About &{0}"
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
15
src/main.py
15
src/main.py
@@ -65,6 +65,7 @@ log = logging.getLogger("main")
|
||||
def setup():
|
||||
log.debug("Starting " + application.name + " %s" % (application.version,))
|
||||
config.setup()
|
||||
proxy_setup()
|
||||
log.debug("Using %s %s" % (platform.system(), platform.architecture()[0]))
|
||||
log.debug("Application path is %s" % (paths.app_path(),))
|
||||
log.debug("config path is %s" % (paths.config_path(),))
|
||||
@@ -77,6 +78,7 @@ def setup():
|
||||
from controller import mainController
|
||||
from sessionmanager import sessionManager
|
||||
app = widgetUtils.mainLoopObject()
|
||||
check_pid()
|
||||
if system == "Windows":
|
||||
if config.app["app-settings"]["donation_dialog_displayed"] == False:
|
||||
donation()
|
||||
@@ -90,7 +92,6 @@ def setup():
|
||||
if hasattr(sm.view, "destroy"):
|
||||
sm.view.destroy()
|
||||
del sm
|
||||
check_pid()
|
||||
r = mainController.Controller()
|
||||
r.view.show()
|
||||
r.do_work()
|
||||
@@ -101,6 +102,18 @@ def setup():
|
||||
GLib.idle_add(r.start)
|
||||
app.run()
|
||||
|
||||
def proxy_setup():
|
||||
if config.app["proxy"]["server"] != "" and config.app["proxy"]["type"] > 0:
|
||||
log.debug("Loading proxy settings")
|
||||
proxy_url = config.app["proxy"]["server"] + ":" + str(config.app["proxy"]["port"])
|
||||
if config.app["proxy"]["user"] != "" and config.app["proxy"]["password"] != "":
|
||||
proxy_url = config.app["proxy"]["user"] + ":" + config.app["proxy"]["password"] + "@" + proxy_url
|
||||
elif config.app["proxy"]["user"] != "" and config.proxyTypes[config.app["proxy"]["type"]] in ["socks4", "socks4a"]:
|
||||
proxy_url = config.app["proxy"]["user"] + "@" + proxy_url
|
||||
proxy_url = config.proxyTypes[config.app["proxy"]["type"]] + "://" + proxy_url
|
||||
os.environ["HTTP_PROXY"] = proxy_url
|
||||
os.environ["HTTPS_PROXY"] = proxy_url
|
||||
|
||||
def donation():
|
||||
dlg = commonMessageDialogs.donation()
|
||||
if dlg == widgetUtils.YES:
|
||||
|
@@ -17,10 +17,10 @@ class list(object):
|
||||
# self.set_size()
|
||||
|
||||
def set_windows_size(self, column, characters_max):
|
||||
# it = wx.ListItem()
|
||||
# dc = wx.WindowDC(self.list)
|
||||
# dc.SetFont(it.GetFont())
|
||||
# (x, y) = dc.GetTextExtent("r"*characters_max)
|
||||
# it = wx.ListItem()
|
||||
# dc = wx.WindowDC(self.list)
|
||||
# dc.SetFont(it.GetFont())
|
||||
# (x, y) = dc.GetTextExtent("r"*characters_max)
|
||||
self.list.SetColumnWidth(column, characters_max*2)
|
||||
|
||||
def set_size(self):
|
||||
|
@@ -1,6 +1,7 @@
|
||||
# -*- coding: cp1252
|
||||
from __future__ import unicode_literals
|
||||
import sys, os
|
||||
import application
|
||||
|
||||
def restart_program():
|
||||
""" Function that restarts the application if is executed."""
|
||||
@@ -9,4 +10,7 @@ def restart_program():
|
||||
args.insert(0, sys.executable)
|
||||
if sys.platform == 'win32':
|
||||
args = ['"%s"' % arg for arg in args]
|
||||
pidpath = os.path.join(os.getenv("temp"), "{}.pid".format(application.name))
|
||||
if os.path.exists(pidpath):
|
||||
os.remove(pidpath)
|
||||
os.execv(sys.executable, args)
|
@@ -1,21 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
import logging
|
||||
log = logging.getLogger("mysc.thread_utils")
|
||||
import threading
|
||||
import wx
|
||||
from pubsub import pub
|
||||
from twython import TwythonRateLimitError
|
||||
|
||||
def call_threaded(func, *args, **kwargs):
|
||||
#Call the given function in a daemonized thread and return the thread.
|
||||
def new_func(*a, **k):
|
||||
try:
|
||||
func(*a, **k)
|
||||
except TwythonRateLimitError:
|
||||
pass
|
||||
except:
|
||||
log.exception("Thread %d with function %r, args of %r, and kwargs of %r failed to run." % (threading.current_thread().ident, func, a, k))
|
||||
# pass
|
||||
@@ -23,17 +17,3 @@ def call_threaded(func, *args, **kwargs):
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
def stream_threaded(func, *args, **kwargs):
|
||||
def new_func(*a, **k):
|
||||
try:
|
||||
func(**k)
|
||||
except Exception as msg:
|
||||
log.error("Error in stream with args: %r" % (a,))
|
||||
log.error(msg.message)
|
||||
pub.sendMessage("stream-error", session=a[0])
|
||||
|
||||
thread = threading.Thread(target=new_func, args=args, kwargs=kwargs)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
return thread
|
@@ -20,8 +20,8 @@ def setup ():
|
||||
global speaker
|
||||
logging.debug("Initializing output subsystem.")
|
||||
try:
|
||||
# speaker = speech.Speaker(speech.outputs.Sapi5())
|
||||
# else:
|
||||
# speaker = speech.Speaker(speech.outputs.Sapi5())
|
||||
# else:
|
||||
speaker = outputs.auto.Auto()
|
||||
except:
|
||||
return logging.exception("Output: Error during initialization.")
|
||||
|
@@ -23,7 +23,7 @@ def config_path():
|
||||
elif mode == "installed":
|
||||
path = os.path.join(data_path(), "config")
|
||||
if not os.path.exists(path):
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
os.mkdir(path)
|
||||
return path
|
||||
|
||||
@@ -35,11 +35,11 @@ def logs_path():
|
||||
elif mode == "installed":
|
||||
path = os.path.join(data_path(), "logs")
|
||||
if not os.path.exists(path):
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
# log.debug("%s path does not exist, creating..." % (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:
|
||||
@@ -62,6 +62,6 @@ def com_path():
|
||||
elif mode == "installed":
|
||||
path = os.path.join(data_path(), "com_cache")
|
||||
if not os.path.exists(path):
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
os.mkdir(path)
|
||||
return path
|
||||
|
18
src/run_tests.py
Normal file
18
src/run_tests.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import unittest
|
||||
|
||||
testmodules = ["test.test_cache"]
|
||||
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
for t in testmodules:
|
||||
try:
|
||||
# If the module defines a suite() function, call it to get the suite.
|
||||
mod = __import__(t, globals(), locals(), ['suite'])
|
||||
suitefn = getattr(mod, 'suite')
|
||||
suite.addTest(suitefn())
|
||||
except (ImportError, AttributeError):
|
||||
# else, just load all the test cases from the module.
|
||||
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t))
|
||||
|
||||
unittest.TextTestRunner(verbosity=2).run(suite)
|
@@ -16,13 +16,13 @@ def setup():
|
||||
manager = sessionManager()
|
||||
|
||||
class sessionManager(object):
|
||||
# def __init__(self):
|
||||
# FILE = "sessions.conf"
|
||||
# SPEC = "app-configuration.defaults"
|
||||
# try:
|
||||
# self.main = Configuration(paths.config_path(FILE), paths.app_path(SPEC))
|
||||
# except ConfigurationResetException:
|
||||
# pass
|
||||
# def __init__(self):
|
||||
# FILE = "sessions.conf"
|
||||
# SPEC = "app-configuration.defaults"
|
||||
# try:
|
||||
# self.main = Configuration(paths.config_path(FILE), paths.app_path(SPEC))
|
||||
# except ConfigurationResetException:
|
||||
# pass
|
||||
|
||||
def get_current_session(self):
|
||||
if self.is_valid(config.app["sessions"]["current_session"]):
|
||||
|
@@ -1,8 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
import shutil
|
||||
import widgetUtils
|
||||
import platform
|
||||
@@ -21,7 +17,7 @@ from sessions.twitter import session
|
||||
from . import manager
|
||||
import config_utils
|
||||
import config
|
||||
|
||||
from tweepy.errors import TweepyException
|
||||
log = logging.getLogger("sessionmanager.sessionManager")
|
||||
|
||||
class sessionManagerController(object):
|
||||
@@ -85,11 +81,18 @@ class sessionManagerController(object):
|
||||
s = session.Session(i)
|
||||
s.get_configuration()
|
||||
if i not in config.app["sessions"]["ignored_sessions"]:
|
||||
try:
|
||||
s.login()
|
||||
except TweepyException:
|
||||
self.show_auth_error(s.settings["twitter"]["user_name"])
|
||||
continue
|
||||
sessions.sessions[i] = s
|
||||
self.new_sessions[i] = s
|
||||
# self.view.destroy()
|
||||
|
||||
def show_auth_error(self, user_name):
|
||||
error = view.auth_error(user_name)
|
||||
|
||||
def manage_new_account(self, *args, **kwargs):
|
||||
if self.view.new_account_dialog() == widgetUtils.YES:
|
||||
location = (str(time.time())[-6:])
|
||||
|
@@ -76,3 +76,6 @@ class sessionManagerWindow(wx.Dialog):
|
||||
|
||||
def destroy(self):
|
||||
self.Destroy()
|
||||
|
||||
def auth_error(user_name):
|
||||
return wx.MessageDialog(None, _("TWBlue is unable to authenticate the account for {} in Twitter. It might be due to an invalid or expired token, revoqued access to the application, or after an account reactivation. Please remove the account manually from your Twitter sessions in order to stop seeing this message.").format(user_name,), _("Authentication error for session {}").format(user_name,), wx.OK).ShowModal()
|
||||
|
@@ -1,9 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" A base class to be derived in possible new sessions for TWBlue and services."""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
import os
|
||||
import paths
|
||||
import output
|
||||
@@ -11,9 +7,8 @@ import time
|
||||
import sound
|
||||
import logging
|
||||
import config_utils
|
||||
import shelve
|
||||
import sqlitedict
|
||||
import application
|
||||
import os
|
||||
from . import session_exceptions as Exceptions
|
||||
log = logging.getLogger("sessionmanager.session")
|
||||
|
||||
@@ -48,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):
|
||||
@@ -57,9 +56,9 @@ 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.deshelve()
|
||||
self.load_persistent_data()
|
||||
|
||||
def init_sound(self):
|
||||
try: self.sound = sound.soundSystem(self.settings["sound"])
|
||||
@@ -73,48 +72,88 @@ class baseSession(object):
|
||||
def authorise(self):
|
||||
pass
|
||||
|
||||
def shelve(self):
|
||||
"""Shelve the database to allow for persistance."""
|
||||
shelfname=os.path.join(paths.config_path(), str(self.session_id)+"/cache")
|
||||
if self.settings["general"]["persist_size"] == 0:
|
||||
if os.path.exists(shelfname+".dat"):
|
||||
os.remove(shelfname+".dat")
|
||||
return
|
||||
try:
|
||||
if not os.path.exists(shelfname+".dat"):
|
||||
output.speak("Generating database, this might take a while.",True)
|
||||
shelf=shelve.open(os.path.join(paths.config_path(), shelfname),'c')
|
||||
for key,value in list(self.db.items()):
|
||||
if type(key) != str and type(key) != str:
|
||||
output.speak("Uh oh, while shelving the database, a key of type " + str(type(key)) + " has been found. It will be converted to type str, but this will cause all sorts of problems on deshelve. Please bring this to the attention of the " + application.name + " developers immediately. More information about the error will be written to the error log.",True)
|
||||
log.error("Uh oh, " + str(key) + " is of type " + str(type(key)) + "!")
|
||||
if type(value) == list and self.settings["general"]["persist_size"] != -1 and len(value) > self.settings["general"]["persist_size"]:
|
||||
shelf[key]=value[self.settings["general"]["persist_size"]:]
|
||||
def get_sized_buffer(self, buffer, size, reversed=False):
|
||||
""" Returns a list with the amount of items specified by size."""
|
||||
if isinstance(buffer, list) and size != -1 and len(buffer) > size:
|
||||
log.debug("Requesting {} items from a list of {} items. Reversed mode: {}".format(size, len(buffer), reversed))
|
||||
if reversed == True:
|
||||
return buffer[:size]
|
||||
else:
|
||||
shelf[key]=value
|
||||
shelf.close()
|
||||
except:
|
||||
output.speak("An exception occurred while shelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
|
||||
log.exception("Exception while shelving" + shelfname)
|
||||
os.remove(shelfname)
|
||||
return buffer[len(buffer)-size:]
|
||||
else:
|
||||
return buffer
|
||||
|
||||
def deshelve(self):
|
||||
"""Import a shelved database."""
|
||||
shelfname=os.path.join(paths.config_path(), str(self.session_id)+"/cache")
|
||||
def save_persistent_data(self):
|
||||
""" Save the data to a persistent sqlite backed file. ."""
|
||||
dbname=os.path.join(paths.config_path(), str(self.session_id), "cache.db")
|
||||
log.debug("Saving storage information...")
|
||||
# persist_size set to 0 means not saving data actually.
|
||||
if self.settings["general"]["persist_size"] == 0:
|
||||
if os.path.exists(shelfname+".dat"):
|
||||
os.remove(shelfname+".dat")
|
||||
if os.path.exists(dbname):
|
||||
os.remove(dbname)
|
||||
return
|
||||
# Let's check if we need to create a new SqliteDict object (when loading db in memory) or we just need to call to commit in self (if reading from disk).db.
|
||||
# If we read from disk, we cannot modify the buffer size here as we could damage the app's integrity.
|
||||
# We will modify buffer's size (managed by persist_size) upon loading the db into memory in app startup.
|
||||
if self.settings["general"]["load_cache_in_memory"] and isinstance(self.db, dict):
|
||||
log.debug("Opening database to dump memory contents...")
|
||||
db=sqlitedict.SqliteDict(dbname, 'c')
|
||||
for k in self.db.keys():
|
||||
sized_buff = self.get_sized_buffer(self.db[k], self.settings["general"]["persist_size"], self.settings["general"]["reverse_timelines"])
|
||||
db[k] = sized_buff
|
||||
db.commit(blocking=True)
|
||||
db.close()
|
||||
log.debug("Data has been saved in the database.")
|
||||
else:
|
||||
try:
|
||||
shelf=shelve.open(os.path.join(paths.config_path(), shelfname),'c')
|
||||
for key,value in list(shelf.items()):
|
||||
self.db[key]=value
|
||||
shelf.close()
|
||||
log.debug("Syncing new data to disk...")
|
||||
if hasattr(self.db, "commit"):
|
||||
self.db.commit()
|
||||
except:
|
||||
output.speak("An exception occurred while deshelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
|
||||
log.exception("Exception while deshelving" + shelfname)
|
||||
output.speak(_("An exception occurred while saving the {app} database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the {app} developers.").format(app=application.name),True)
|
||||
log.exception("Exception while saving {}".format(dbname))
|
||||
os.remove(dbname)
|
||||
|
||||
def load_persistent_data(self):
|
||||
"""Import data from a database file from user config."""
|
||||
log.debug("Loading storage data...")
|
||||
dbname=os.path.join(paths.config_path(), str(self.session_id), "cache.db")
|
||||
# If persist_size is set to 0, we should remove the db file as we are no longer going to save anything.
|
||||
if self.settings["general"]["persist_size"] == 0:
|
||||
if os.path.exists(dbname):
|
||||
os.remove(dbname)
|
||||
# Let's return from here, as we are not loading anything.
|
||||
return
|
||||
# try to load the db file.
|
||||
try:
|
||||
os.remove(shelfname)
|
||||
log.debug("Opening database...")
|
||||
db=sqlitedict.SqliteDict(os.path.join(paths.config_path(), dbname), 'c')
|
||||
# If load_cache_in_memory is set to true, we will load the whole database into memory for faster access.
|
||||
# This is going to be faster when retrieving specific objects, at the cost of more memory.
|
||||
# Setting this to False will read the objects from database as they are needed, which might be slower for bigger datasets.
|
||||
if self.settings["general"]["load_cache_in_memory"]:
|
||||
log.debug("Loading database contents into memory...")
|
||||
for k in db.keys():
|
||||
self.db[k] = db[k]
|
||||
db.commit(blocking=True)
|
||||
db.close()
|
||||
log.debug("Contents were loaded successfully.")
|
||||
else:
|
||||
log.debug("Instantiating database from disk.")
|
||||
self.db = db
|
||||
# We must make sure we won't load more than the amount of buffer specified.
|
||||
log.debug("Checking if we will load all content...")
|
||||
for k in self.db.keys():
|
||||
sized_buffer = self.get_sized_buffer(self.db[k], self.settings["general"]["persist_size"], self.settings["general"]["reverse_timelines"])
|
||||
self.db[k] = sized_buffer
|
||||
if self.db.get("cursors") == None:
|
||||
cursors = dict(direct_messages=-1)
|
||||
self.db["cursors"] = cursors
|
||||
except:
|
||||
output.speak(_("An exception occurred while loading the {app} database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the {app} developers.").format(app=application.name), True)
|
||||
log.exception("Exception while loading {}".format(dbname))
|
||||
try:
|
||||
os.remove(dbname)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@@ -1,11 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
from builtins import chr
|
||||
from builtins import range
|
||||
import platform
|
||||
system = platform.system()
|
||||
from . import utils
|
||||
@@ -20,7 +13,6 @@ import config
|
||||
from .long_tweets import twishort, tweets
|
||||
log = logging.getLogger("compose")
|
||||
|
||||
|
||||
def StripChars(s):
|
||||
"""Converts any html entities in s to their unicode-decoded equivalents and returns a string."""
|
||||
entity_re = re.compile(r"&(#\d+|\w+);")
|
||||
@@ -39,129 +31,124 @@ chars = "abcdefghijklmnopqrstuvwxyz"
|
||||
def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=None):
|
||||
""" It receives a tweet and returns a list with the user, text for the tweet or message, date and the client where user is."""
|
||||
if system == "Windows":
|
||||
original_date = arrow.get(tweet["created_at"], "ddd MMM DD H:m:s Z YYYY", locale="en")
|
||||
original_date = arrow.get(tweet.created_at, locale="en")
|
||||
if relative_times == True:
|
||||
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||
else:
|
||||
ts = original_date.shift(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
||||
else:
|
||||
ts = tweet["created_at"]
|
||||
if "message" in tweet:
|
||||
ts = tweet.created_at
|
||||
if hasattr(tweet, "message"):
|
||||
value = "message"
|
||||
elif "full_text" in tweet:
|
||||
elif hasattr(tweet, "full_text"):
|
||||
value = "full_text"
|
||||
else:
|
||||
value = "text"
|
||||
if "retweeted_status" in tweet and value != "message":
|
||||
text = StripChars(tweet["retweeted_status"][value])
|
||||
if hasattr(tweet, "retweeted_status") and value != "message":
|
||||
text = utils.clean_mentions(StripChars(getattr(tweet.retweeted_status, value)))
|
||||
else:
|
||||
text = StripChars(tweet[value])
|
||||
text = utils.clean_mentions(StripChars(getattr(tweet, value)))
|
||||
if show_screen_names:
|
||||
user = tweet["user"]["screen_name"]
|
||||
user = session.get_user(tweet.user).screen_name
|
||||
else:
|
||||
user = tweet["user"]["name"]
|
||||
source = re.sub(r"(?s)<.*?>", "", tweet["source"])
|
||||
if "retweeted_status" in tweet:
|
||||
if ("message" in tweet) == False and tweet["retweeted_status"]["is_quote_status"] == False:
|
||||
text = "RT @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], text)
|
||||
elif tweet["retweeted_status"]["is_quote_status"]:
|
||||
user = session.get_user(tweet.user).name
|
||||
source = re.sub(r"(?s)<.*?>", "", tweet.source)
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
if hasattr(tweet, "message") == False and hasattr(tweet.retweeted_status, "is_quote_status") == False:
|
||||
text = "RT @%s: %s" % (session.get_user(tweet.retweeted_status.user).screen_name, text)
|
||||
elif hasattr(tweet.retweeted_status, "is_quote_status"):
|
||||
text = "%s" % (text)
|
||||
else:
|
||||
text = "RT @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], text)
|
||||
if ("message" in tweet) == False:
|
||||
urls = utils.find_urls_in_text(text)
|
||||
if "retweeted_status" in tweet:
|
||||
for url in range(0, len(urls)):
|
||||
try:
|
||||
text = text.replace(urls[url], tweet["retweeted_status"]["entities"]["urls"][url]["expanded_url"])
|
||||
except: pass
|
||||
text = "RT @%s: %s" % (session.get_user(tweet.retweeted_status.user).screen_name, text)
|
||||
if not hasattr(tweet, "message"):
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
if hasattr(tweet.retweeted_status, "entities"):
|
||||
text = utils.expand_urls(text, tweet.retweeted_status.entities)
|
||||
else:
|
||||
for url in range(0, len(urls)):
|
||||
try:
|
||||
text = text.replace(urls[url], tweet["entities"]["urls"][url]["expanded_url"])
|
||||
except: pass
|
||||
if hasattr(tweet, "entities"):
|
||||
text = utils.expand_urls(text, tweet.entities)
|
||||
if config.app['app-settings']['handle_longtweets']: pass
|
||||
return [user+", ", text, ts+", ", source]
|
||||
|
||||
def compose_direct_message(item, db, relative_times, show_screen_names=False, session=None):
|
||||
# for a while this function will be together with compose_dm.
|
||||
# this one composes direct messages based on events (new API Endpoints).
|
||||
if system == "Windows":
|
||||
# Let's remove the last 3 digits in the timestamp string.
|
||||
# Twitter sends their "epoch" timestamp with 3 digits for milliseconds and arrow doesn't like it.
|
||||
original_date = arrow.get(int(item["created_timestamp"][:-3]))
|
||||
original_date = arrow.get(int(item.created_timestamp))
|
||||
if relative_times == True:
|
||||
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||
else:
|
||||
ts = original_date.shift(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
||||
else:
|
||||
ts = item["created_timestamp"]
|
||||
text = StripChars(item["message_create"]["message_data"]["text"])
|
||||
ts = item.created_timestamp
|
||||
text = StripChars(item.message_create["message_data"]["text"])
|
||||
source = "DM"
|
||||
sender = session.get_user(item["message_create"]["sender_id"])
|
||||
if db["user_name"] == sender["screen_name"]:
|
||||
sender = session.get_user(item.message_create["sender_id"])
|
||||
if db["user_name"] == sender.screen_name:
|
||||
if show_screen_names:
|
||||
user = _(u"Dm to %s ") % (session.get_user(item["message_create"]["target"]["recipient_id"])["screen_name"])
|
||||
user = _(u"Dm to %s ") % (session.get_user(item.message_create["target"]["recipient_id"]).screen_name)
|
||||
else:
|
||||
user = _(u"Dm to %s ") % (session.get_user(item["message_create"]["target"]["recipient_id"])["name"])
|
||||
user = _(u"Dm to %s ") % (session.get_user(item.message_create["target"]["recipient_id"]).name)
|
||||
else:
|
||||
if show_screen_names:
|
||||
user = sender["screen_name"]
|
||||
user = sender.screen_name
|
||||
else:
|
||||
user = sender["name"]
|
||||
user = sender.name
|
||||
if text[-1] in chars: text=text+"."
|
||||
urls = utils.find_urls_in_text(text)
|
||||
for url in range(0, len(urls)):
|
||||
try: text = text.replace(urls[url], item["message_create"]["message_data"]["entities"]["urls"][url]["expanded_url"])
|
||||
except IndexError: pass
|
||||
text = utils.expand_urls(text, item.message_create["message_data"]["entities"])
|
||||
return [user+", ", text, ts+", ", source]
|
||||
|
||||
def compose_quoted_tweet(quoted_tweet, original_tweet, show_screen_names=False, session=None):
|
||||
""" It receives a tweet and returns a list with the user, text for the tweet or message, date and the client where user is."""
|
||||
if "retweeted_status" in quoted_tweet:
|
||||
if "full_text" in quoted_tweet["retweeted_status"]:
|
||||
if hasattr(quoted_tweet, "retweeted_status"):
|
||||
if hasattr(quoted_tweet.retweeted_status, "full_text"):
|
||||
value = "full_text"
|
||||
else:
|
||||
value = "text"
|
||||
text = StripChars(quoted_tweet["retweeted_status"][value])
|
||||
text = StripChars(getattr(quoted_tweet.retweeted_status, value))
|
||||
else:
|
||||
if "full_text" in quoted_tweet:
|
||||
if hasattr(quoted_tweet, "full_text"):
|
||||
value = "full_text"
|
||||
else:
|
||||
value = "text"
|
||||
text = StripChars(quoted_tweet[value])
|
||||
text = utils.clean_mentions(StripChars(getattr(quoted_tweet, value)))
|
||||
if show_screen_names:
|
||||
quoting_user = quoted_tweet["user"]["screen_name"]
|
||||
quoting_user = session.get_user(quoted_tweet.user).screen_name
|
||||
else:
|
||||
quoting_user = quoted_tweet["user"]["name"]
|
||||
source = re.sub(r"(?s)<.*?>", "", quoted_tweet["source"])
|
||||
if "retweeted_status" in quoted_tweet:
|
||||
text = "rt @%s: %s" % (quoted_tweet["retweeted_status"]["user"]["screen_name"], text)
|
||||
quoting_user = session.get_user(quoted_tweet.user).name
|
||||
source = quoted_tweet.source
|
||||
if hasattr(quoted_tweet, "retweeted_status"):
|
||||
text = "rt @%s: %s" % (session.get_user(quoted_tweet.retweeted_status.user).screen_name, text)
|
||||
if text[-1] in chars: text=text+"."
|
||||
original_user = original_tweet["user"]["screen_name"]
|
||||
if "message" in original_tweet:
|
||||
original_text = original_tweet["message"]
|
||||
elif "full_text" in original_tweet:
|
||||
original_text = StripChars(original_tweet["full_text"])
|
||||
original_user = session.get_user(original_tweet.user).screen_name
|
||||
if hasattr(original_tweet, "message"):
|
||||
original_text = original_tweet.message
|
||||
elif hasattr(original_tweet, "full_text"):
|
||||
original_text = utils.clean_mentions(StripChars(original_tweet.full_text))
|
||||
else:
|
||||
original_text = StripChars(original_tweet["text"])
|
||||
quoted_tweet["message"] = _(u"{0}. Quoted tweet from @{1}: {2}").format( text, original_user, original_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)
|
||||
quoted_tweet["entities"]["urls"].extend(original_tweet["entities"]["urls"])
|
||||
if hasattr(original_tweet, "entities") and original_tweet.entities.get("urls"):
|
||||
if hasattr(quoted_tweet, "entities") == False:
|
||||
quoted_tweet.entities = {}
|
||||
if quoted_tweet.entities.get("urls") == None:
|
||||
quoted_tweet.entities["urls"] = []
|
||||
quoted_tweet.entities["urls"].extend(original_tweet.entities["urls"])
|
||||
return quoted_tweet
|
||||
|
||||
def compose_followers_list(tweet, db, relative_times=True, show_screen_names=False, session=None):
|
||||
if system == "Windows":
|
||||
original_date = arrow.get(tweet["created_at"], "ddd MMM D H:m:s Z YYYY", locale="en")
|
||||
original_date = arrow.get(tweet.created_at, locale="en")
|
||||
if relative_times == True:
|
||||
ts = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||
else:
|
||||
ts = original_date.shift(seconds=db["utc_offset"]).format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.curLang[:2])
|
||||
else:
|
||||
ts = tweet["created_at"]
|
||||
if "status" in tweet:
|
||||
if len(tweet["status"]) > 4 and system == "Windows":
|
||||
original_date2 = arrow.get(tweet["status"]["created_at"], "ddd MMM D H:m:s Z YYYY", locale="en")
|
||||
ts = tweet.created_at
|
||||
if hasattr(tweet, "status"):
|
||||
if system == "Windows":
|
||||
original_date2 = arrow.get(tweet.status.created_at, locale="en")
|
||||
if relative_times:
|
||||
ts2 = original_date2.humanize(locale=languageHandler.curLang[:2])
|
||||
else:
|
||||
@@ -170,14 +157,14 @@ def compose_followers_list(tweet, db, relative_times=True, show_screen_names=Fal
|
||||
ts2 = _("Unavailable")
|
||||
else:
|
||||
ts2 = _("Unavailable")
|
||||
return [_(u"%s (@%s). %s followers, %s friends, %s tweets. Last tweeted %s. Joined Twitter %s") % (tweet["name"], tweet["screen_name"], tweet["followers_count"], tweet["friends_count"], tweet["statuses_count"], ts2, ts)]
|
||||
return [_(u"%s (@%s). %s followers, %s friends, %s tweets. Last tweeted %s. Joined Twitter %s") % (tweet.name, tweet.screen_name, tweet.followers_count, tweet.friends_count, tweet.statuses_count, ts2, ts)]
|
||||
|
||||
def compose_list(list):
|
||||
name = list["name"]
|
||||
if list["description"] == None: description = _(u"No description available")
|
||||
else: description = list["description"]
|
||||
user = list["user"]["name"]
|
||||
members = str(list["member_count"])
|
||||
if list["mode"] == "private": status = _(u"private")
|
||||
name = list.name
|
||||
if list.description == None: description = _(u"No description available")
|
||||
else: description = list.description
|
||||
user = list.user.name
|
||||
members = str(list.member_count)
|
||||
if list.mode == "private": status = _(u"private")
|
||||
else: status = _(u"public")
|
||||
return [name, description, user, members, status]
|
||||
|
@@ -23,30 +23,30 @@ def is_long(tweet):
|
||||
""" Check if the passed tweet contains a quote in its metadata.
|
||||
tweet dict: a tweet dictionary.
|
||||
returns True if a quote is detected, False otherwise."""
|
||||
if "quoted_status_id" in tweet and "quoted_status" in tweet:
|
||||
return tweet["quoted_status_id"]
|
||||
elif "retweeted_status" in tweet and "quoted_status_id" in tweet["retweeted_status"] and "quoted_status" in tweet["retweeted_status"]:
|
||||
return tweet["retweeted_status"]["quoted_status_id"]
|
||||
if hasattr(tweet, "quoted_status_id") and hasattr(tweet, "quoted_status"):
|
||||
return tweet.quoted_status_id
|
||||
elif hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "quoted_status_id") and hasattr(tweet.retweeted_status, "quoted_status"):
|
||||
return tweet.retweeted_status.quoted_status_id
|
||||
return False
|
||||
|
||||
def clear_url(tweet):
|
||||
""" Reads data from a quoted tweet and removes the link to the Status from the tweet's text.
|
||||
tweet dict: a tweet dictionary.
|
||||
returns a tweet dictionary without the URL to the status ID in its text to display."""
|
||||
if "retweeted_status" in tweet:
|
||||
if "full_text" in tweet["retweeted_status"]:
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
if hasattr(tweet.retweeted_status, "full_text"):
|
||||
value = "full_text"
|
||||
else:
|
||||
value = "text"
|
||||
urls = utils.find_urls_in_text(tweet["retweeted_status"][value])
|
||||
try: tweet["message"] = tweet["message"].replace(urls[-1], "")
|
||||
urls = utils.find_urls_in_text(getattr(tweet.retweeted_status, value))
|
||||
try: tweet.message = tweet.message.replace(urls[-1], "")
|
||||
except IndexError: pass
|
||||
else:
|
||||
if "full_text" in tweet:
|
||||
if hasattr(tweet, "full_text"):
|
||||
value = "full_text"
|
||||
else:
|
||||
value = "text"
|
||||
urls = utils.find_urls_in_text(tweet[value])
|
||||
try: tweet["message"] = tweet["message"].replace(urls[-1], "")
|
||||
urls = utils.find_urls_in_text(getattr(tweet, value))
|
||||
try: tweet.message = tweet.message.replace(urls[-1], "")
|
||||
except IndexError: pass
|
||||
return tweet
|
@@ -40,25 +40,19 @@ def is_long(tweet):
|
||||
""" Check if the passed tweet is made with Twishort.
|
||||
returns True if is a long tweet, False otherwise."""
|
||||
long = False
|
||||
for url in range(0, len(tweet["entities"]["urls"])):
|
||||
if hasattr(tweet, "entities") and tweet.entities.get("urls"):
|
||||
for url in range(0, len(tweet.entities["urls"])):
|
||||
try:
|
||||
if tweet["entities"]["urls"][url] != None and "twishort.com" in tweet["entities"]["urls"][url]["expanded_url"]:
|
||||
long = get_twishort_uri(tweet["entities"]["urls"][url]["expanded_url"])
|
||||
if tweet.entities["urls"][url] != None and "twishort.com" in tweet.entities["urls"][url]["expanded_url"]:
|
||||
long = get_twishort_uri(tweet.entities["urls"][url]["expanded_url"])
|
||||
except IndexError:
|
||||
pass
|
||||
# sometimes Twitter returns URL's with None objects, so let's take it.
|
||||
# see https://github.com/manuelcortez/TWBlue/issues/103
|
||||
except TypeError:
|
||||
pass
|
||||
if long == False and "retweeted_status" in tweet:
|
||||
for url in range(0, len(tweet["retweeted_status"]["entities"]["urls"])):
|
||||
try:
|
||||
if tweet["retweeted_status"]["entities"]["urls"][url] != None and "twishort.com" in tweet["retweeted_status"]["entities"]["urls"][url]["expanded_url"]:
|
||||
long = get_twishort_uri(tweet["retweeted_status"]["entities"]["urls"][url]["expanded_url"])
|
||||
except IndexError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
if long == False and hasattr(tweet, "retweeted_status"):
|
||||
return is_long(tweet.retweeted_status)
|
||||
return long
|
||||
|
||||
def get_full_text(uri):
|
||||
|
37
src/sessions/twitter/reduce.py
Normal file
37
src/sessions/twitter/reduce.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Strips unneeded tweet information in order to store tweet objects by using less memory. This is especially useful when buffers start to contain more than a certain amount of items. """
|
||||
from tweepy.models import Status
|
||||
|
||||
def reduce_tweet(tweet):
|
||||
""" generates a new Tweet model with the fields we currently need, excluding everything else including null values and empty collections. """
|
||||
allowed_values = ["created_at", "id", "full_text", "text", "message", "in_reply_to_status_id", "in_reply_to_user_id", "is_quote_status", "lang", "source", "coordinates", "quoted_status_id", "extended_entities"]
|
||||
allowed_entities = ["hashtags", "media", "urls", "user_mentions", "polls"]
|
||||
status_dict = {}
|
||||
for key in allowed_values:
|
||||
if tweet._json.get(key):
|
||||
status_dict[key] = tweet._json[key]
|
||||
entities = dict()
|
||||
for key in allowed_entities:
|
||||
if tweet._json["entities"].get(key) and tweet._json["entities"].get(key) != None:
|
||||
entities[key] = tweet._json["entities"][key]
|
||||
status_dict["entities"] = entities
|
||||
# If tweet comes from the cached database, it does not include an API, so we can pass None here as we do not use that reference to tweepy's API.
|
||||
if hasattr(tweet, "_api"):
|
||||
api = tweet._api
|
||||
else:
|
||||
api = None
|
||||
status = Status().parse(api=api, json=status_dict)
|
||||
# Quotes and retweets are different objects. So we parse a new tweet when we have a quoted or retweeted status here.
|
||||
if tweet._json.get("quoted_status"):
|
||||
quoted_tweet = reduce_tweet(tweet.quoted_status)
|
||||
status.quoted_status = quoted_tweet
|
||||
if tweet._json.get("retweeted_status"):
|
||||
retweeted_tweet = reduce_tweet(tweet.retweeted_status)
|
||||
status.retweeted_status = retweeted_tweet
|
||||
# Adds user ID to here so we can reference it later.
|
||||
# Sometimes, the conversations buffer would send an already reduced tweet here so we will need to return it as is.
|
||||
if isinstance(tweet.user, str) == False:
|
||||
status.user = tweet.user.id_str
|
||||
else:
|
||||
return tweet
|
||||
return status
|
@@ -1,8 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" This is the main session needed to access all Twitter Features."""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from builtins import range
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
@@ -11,13 +8,17 @@ import wx
|
||||
import config
|
||||
import output
|
||||
import application
|
||||
import appkeys
|
||||
from pubsub import pub
|
||||
from twython import Twython, TwythonError, TwythonRateLimitError, TwythonAuthError
|
||||
import tweepy
|
||||
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
|
||||
from sessions import base
|
||||
from sessions.twitter import utils, compose
|
||||
from sessions.twitter.long_tweets import tweets, twishort
|
||||
from . import reduce, streaming
|
||||
from .wxUI import authorisationDialog
|
||||
|
||||
log = logging.getLogger("sessions.twitterSession")
|
||||
@@ -31,52 +32,52 @@ class Session(base.baseSession):
|
||||
data list: A list with tweets.
|
||||
ignore_older bool: if set to True, items older than the first element on the list will be ignored.
|
||||
returns the number of items that have been added in this execution"""
|
||||
if name == "direct_messages":
|
||||
return self.order_direct_messages(data)
|
||||
num = 0
|
||||
last_id = None
|
||||
if (name in self.db) == False:
|
||||
self.db[name] = []
|
||||
if ("users" in self.db) == False:
|
||||
self.db["users"] = {}
|
||||
objects = self.db[name]
|
||||
if ignore_older and len(self.db[name]) > 0:
|
||||
if self.settings["general"]["reverse_timelines"] == False:
|
||||
last_id = self.db[name][0]["id"]
|
||||
last_id = self.db[name][0].id
|
||||
else:
|
||||
last_id = self.db[name][-1]["id"]
|
||||
last_id = self.db[name][-1].id
|
||||
self.add_users_from_results(data)
|
||||
for i in data:
|
||||
if ignore_older and last_id != None:
|
||||
if i["id"] < last_id:
|
||||
log.error("Ignoring an older tweet... Last id: {0}, tweet id: {1}".format(last_id, i["id"]))
|
||||
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:
|
||||
i = self.check_quoted_status(i)
|
||||
i = self.check_long_tweet(i)
|
||||
if utils.find_item(i, self.db[name]) == None and utils.is_allowed(i, self.settings, name) == True:
|
||||
if i == False: continue
|
||||
if self.settings["general"]["reverse_timelines"] == False: self.db[name].append(i)
|
||||
else: self.db[name].insert(0, i)
|
||||
reduced_object = reduce.reduce_tweet(i)
|
||||
reduced_object = self.check_quoted_status(reduced_object)
|
||||
reduced_object = self.check_long_tweet(reduced_object)
|
||||
if self.settings["general"]["reverse_timelines"] == False: objects.append(reduced_object)
|
||||
else: objects.insert(0, reduced_object)
|
||||
num = num+1
|
||||
if ("user" in i) == True:
|
||||
if (i["user"]["id"] in self.db["users"]) == False:
|
||||
self.db["users"][i["user"]["id"]] = i["user"]
|
||||
self.db[name] = objects
|
||||
return num
|
||||
|
||||
def order_cursored_buffer(self, name, data):
|
||||
def order_people(self, name, data):
|
||||
""" Put new items on the local database. Useful for cursored buffers (followers, friends, users of a list and searches)
|
||||
name str: The name for the buffer stored in the dictionary.
|
||||
data list: A list with items and some information about cursors.
|
||||
returns the number of items that have been added in this execution"""
|
||||
# Direct messages should be added to db in other function.
|
||||
# Because they will be populating two buffers with one endpoint.
|
||||
if name == "direct_messages":
|
||||
return self.order_direct_messages(data)
|
||||
num = 0
|
||||
if (name in self.db) == False:
|
||||
self.db[name] = {}
|
||||
self.db[name]["items"] = []
|
||||
self.db[name] = []
|
||||
objects = self.db[name]
|
||||
for i in data:
|
||||
if utils.find_item(i["id"], self.db[name]["items"]) == None:
|
||||
if self.settings["general"]["reverse_timelines"] == False: self.db[name]["items"].append(i)
|
||||
else: self.db[name]["items"].insert(0, i)
|
||||
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
|
||||
self.db[name] = objects
|
||||
return num
|
||||
|
||||
def order_direct_messages(self, data):
|
||||
@@ -86,27 +87,47 @@ class Session(base.baseSession):
|
||||
incoming = 0
|
||||
sent = 0
|
||||
if ("direct_messages" in self.db) == False:
|
||||
self.db["direct_messages"] = {}
|
||||
self.db["direct_messages"]["items"] = []
|
||||
self.db["direct_messages"] = []
|
||||
if ("sent_direct_messages" in self.db) == False:
|
||||
self.db["sent_direct_messages"] = []
|
||||
objects = self.db["direct_messages"]
|
||||
sent_objects = self.db["sent_direct_messages"]
|
||||
for i in data:
|
||||
if 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"]["items"]) == None:
|
||||
if self.settings["general"]["reverse_timelines"] == False: self.db["sent_direct_messages"]["items"].append(i)
|
||||
else: self.db["sent_direct_messages"]["items"].insert(0, i)
|
||||
# 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, 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"]["items"]) == None:
|
||||
if self.settings["general"]["reverse_timelines"] == False: self.db["direct_messages"]["items"].append(i)
|
||||
else: self.db["direct_messages"]["items"].insert(0, i)
|
||||
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
|
||||
self.db["direct_messages"] = objects
|
||||
|
||||
self.db["sent_direct_messages"] = sent_objects
|
||||
pub.sendMessage("sent-dms-updated", total=sent, account=self.db["user_name"])
|
||||
|
||||
return incoming
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Session, self).__init__(*args, **kwargs)
|
||||
# Adds here the optional cursors objects.
|
||||
cursors = dict(direct_messages=-1)
|
||||
self.db["cursors"] = cursors
|
||||
self.reconnection_function_active = False
|
||||
self.counter = 0
|
||||
self.lists = []
|
||||
# As users are cached for accessing them with not too many twitter calls,
|
||||
# there could be a weird situation where a deleted user who sent direct messages to the current account will not be able to be retrieved at twitter.
|
||||
# So we need to store an "user deleted" object in the cache, but have the ID of the deleted user in a local reference.
|
||||
# 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")
|
||||
|
||||
# @_require_configuration
|
||||
def login(self, verify_credentials=True):
|
||||
@@ -115,7 +136,10 @@ class Session(base.baseSession):
|
||||
if self.settings["twitter"]["user_key"] != None and self.settings["twitter"]["user_secret"] != None:
|
||||
try:
|
||||
log.debug("Logging in to twitter...")
|
||||
self.twitter = Twython(keyring.get("api_key"), keyring.get("api_secret"), self.settings["twitter"]["user_key"], self.settings["twitter"]["user_secret"])
|
||||
self.auth = tweepy.OAuthHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
||||
self.auth.set_access_token(self.settings["twitter"]["user_key"], self.settings["twitter"]["user_secret"])
|
||||
self.twitter = tweepy.API(self.auth)
|
||||
self.twitter_v2 = tweepy.Client(consumer_key=appkeys.twitter_api_key, consumer_secret=appkeys.twitter_api_secret, access_token=self.settings["twitter"]["user_key"], access_token_secret=self.settings["twitter"]["user_secret"])
|
||||
if verify_credentials == True:
|
||||
self.credentials = self.twitter.verify_credentials()
|
||||
self.logged = True
|
||||
@@ -134,19 +158,18 @@ class Session(base.baseSession):
|
||||
if self.logged == True:
|
||||
raise Exceptions.AlreadyAuthorisedError("The authorisation process is not needed at this time.")
|
||||
else:
|
||||
twitter = Twython(keyring.get("api_key"), keyring.get("api_secret"))
|
||||
self.auth = twitter.get_authentication_tokens(callback_url="oob")
|
||||
webbrowser.open_new_tab(self.auth['auth_url'])
|
||||
self.auth = tweepy.OAuthHandler(appkeys.twitter_api_key, appkeys.twitter_api_secret)
|
||||
redirect_url = self.auth.get_authorization_url()
|
||||
webbrowser.open_new_tab(redirect_url)
|
||||
self.authorisation_dialog = authorisationDialog()
|
||||
self.authorisation_dialog.cancel.Bind(wx.EVT_BUTTON, self.authorisation_cancelled)
|
||||
self.authorisation_dialog.ok.Bind(wx.EVT_BUTTON, self.authorisation_accepted)
|
||||
self.authorisation_dialog.ShowModal()
|
||||
|
||||
def verify_authorisation(self, pincode):
|
||||
twitter = Twython(keyring.get("api_key"), keyring.get("api_secret"), self.auth['oauth_token'], self.auth['oauth_token_secret'])
|
||||
final = twitter.get_authorized_tokens(pincode)
|
||||
self.settings["twitter"]["user_key"] = final["oauth_token"]
|
||||
self.settings["twitter"]["user_secret"] = final["oauth_token_secret"]
|
||||
self.auth.get_access_token(pincode)
|
||||
self.settings["twitter"]["user_key"] = self.auth.access_token
|
||||
self.settings["twitter"]["user_secret"] = self.auth.access_token_secret
|
||||
self.settings.write()
|
||||
del self.auth
|
||||
|
||||
@@ -161,35 +184,6 @@ class Session(base.baseSession):
|
||||
self.verify_authorisation(pincode)
|
||||
self.authorisation_dialog.Destroy()
|
||||
|
||||
def get_more_items(self, update_function, users=False, dm=False, name=None, *args, **kwargs):
|
||||
""" Get more items for twitter objects.
|
||||
update_function str: function to call for getting more items. Must be member of self.twitter.
|
||||
users, dm bool: If any of these is set to True, the function will treat items as users or dm (they need different handling).
|
||||
name str: name of the database item to put new element in."""
|
||||
results = []
|
||||
if "cursor" in kwargs and kwargs["cursor"] == 0:
|
||||
output.speak(_(u"There are no more items to retrieve in this buffer."))
|
||||
return
|
||||
data = getattr(self.twitter, update_function)(*args, **kwargs)
|
||||
if users == True:
|
||||
if type(data) == dict and "next_cursor" in data:
|
||||
if "next_cursor" in data: # There are more objects to retrieve.
|
||||
self.db[name]["cursor"] = data["next_cursor"]
|
||||
else: # Set cursor to 0, wich means no more items available.
|
||||
self.db[name]["cursor"] = 0
|
||||
for i in data["users"]: results.append(i)
|
||||
elif type(data) == list:
|
||||
results.extend(data[1:])
|
||||
elif dm == True:
|
||||
if "next_cursor" in data: # There are more objects to retrieve.
|
||||
self.db[name]["cursor"] = data["next_cursor"]
|
||||
else: # Set cursor to 0, wich means no more items available.
|
||||
self.db[name]["cursor"] = 0
|
||||
for i in data["events"]: results.append(i)
|
||||
else:
|
||||
results.extend(data[1:])
|
||||
return results
|
||||
|
||||
def api_call(self, call_name, action="", _sound=None, report_success=False, report_failure=True, preexec_message="", *args, **kwargs):
|
||||
""" Make a call to the Twitter API. If there is a connectionError or another exception not related to Twitter, It will call the method again at least 25 times, waiting a while between calls. Useful for post methods.
|
||||
If twitter returns an error, it will not call the method anymore.
|
||||
@@ -207,14 +201,14 @@ class Session(base.baseSession):
|
||||
try:
|
||||
val = getattr(self.twitter, call_name)(*args, **kwargs)
|
||||
finished = True
|
||||
except TwythonError as e:
|
||||
output.speak(e.msg)
|
||||
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, 'message'):
|
||||
output.speak(_("%s failed. Reason: %s") % (action, e.msg))
|
||||
elif report_failure:
|
||||
output.speak(_("%s failed. Reason: %s") % (action, str(e)))
|
||||
finished = True
|
||||
# except:
|
||||
# tries = tries + 1
|
||||
@@ -226,16 +220,16 @@ 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["statuses"].reverse()
|
||||
return tl["statuses"]
|
||||
tl = self.twitter.search_tweets(*args, **kwargs)
|
||||
tl.reverse()
|
||||
return tl
|
||||
|
||||
# @_require_login
|
||||
def get_favourites_timeline(self, name, *args, **kwargs):
|
||||
""" Gets favourites for the authenticated user or a friend or follower.
|
||||
name str: Name for storage in the database.
|
||||
args and kwargs are passed directly to the Twython function."""
|
||||
tl = self.call_paged("get_favorites", *args, **kwargs)
|
||||
tl = self.call_paged("favorites", *args, **kwargs)
|
||||
return self.order_buffer(name, tl)
|
||||
|
||||
def call_paged(self, update_function, *args, **kwargs):
|
||||
@@ -249,8 +243,8 @@ class Session(base.baseSession):
|
||||
data = getattr(self.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
|
||||
results.extend(data)
|
||||
for i in range(0, max):
|
||||
if i == 0: max_id = results[-1]["id"]
|
||||
else: max_id = results[0]["id"]
|
||||
if i == 0: max_id = results[-1].id
|
||||
else: max_id = results[0].id
|
||||
data = getattr(self.twitter, update_function)(max_id=max_id, count=self.settings["general"]["max_tweets_per_call"], *args, **kwargs)
|
||||
results.extend(data)
|
||||
results.reverse()
|
||||
@@ -259,11 +253,11 @@ class Session(base.baseSession):
|
||||
# @_require_login
|
||||
def get_user_info(self):
|
||||
""" Retrieves some information required by TWBlue for setup."""
|
||||
f = self.twitter.get_account_settings()
|
||||
f = self.twitter.get_settings()
|
||||
sn = f["screen_name"]
|
||||
self.settings["twitter"]["user_name"] = sn
|
||||
self.db["user_name"] = sn
|
||||
self.db["user_id"] = self.twitter.show_user(screen_name=sn)["id_str"]
|
||||
self.db["user_id"] = self.twitter.get_user(screen_name=sn).id
|
||||
try:
|
||||
self.db["utc_offset"] = f["time_zone"]["utc_offset"]
|
||||
except KeyError:
|
||||
@@ -271,7 +265,7 @@ class Session(base.baseSession):
|
||||
# Get twitter's supported languages and save them in a global variable
|
||||
#so we won't call to this method once per session.
|
||||
if len(application.supported_languages) == 0:
|
||||
application.supported_languages = self.twitter.get_supported_languages()
|
||||
application.supported_languages = self.twitter.supported_languages()
|
||||
self.get_lists()
|
||||
self.get_muted_users()
|
||||
self.settings.write()
|
||||
@@ -279,12 +273,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.show_lists(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.list_mute_ids()["ids"]
|
||||
self.db["muted_users"] = self.twitter.get_muted_ids()
|
||||
|
||||
# @_require_login
|
||||
def get_stream(self, name, function, *args, **kwargs):
|
||||
@@ -353,76 +347,111 @@ class Session(base.baseSession):
|
||||
return tweet
|
||||
|
||||
def get_quoted_tweet(self, tweet):
|
||||
""" Process a tweet and extract all information related to the quote."""
|
||||
""" Process a tweet and extract all information related to the quote. """
|
||||
quoted_tweet = tweet
|
||||
if "full_text" in tweet:
|
||||
if hasattr(tweet, "full_text"):
|
||||
value = "full_text"
|
||||
else:
|
||||
value = "text"
|
||||
urls = utils.find_urls_in_text(quoted_tweet[value])
|
||||
for url in range(0, len(urls)):
|
||||
try: quoted_tweet[value] = quoted_tweet[value].replace(urls[url], quoted_tweet["entities"]["urls"][url]["expanded_url"])
|
||||
except IndexError: pass
|
||||
if "quoted_status" in quoted_tweet:
|
||||
original_tweet = quoted_tweet["quoted_status"]
|
||||
elif "retweeted_status" in quoted_tweet and "quoted_status" in quoted_tweet["retweeted_status"]:
|
||||
original_tweet = quoted_tweet["retweeted_status"]["quoted_status"]
|
||||
if hasattr(quoted_tweet, "entities"):
|
||||
setattr(quoted_tweet, value, utils.expand_urls(getattr(quoted_tweet, value), quoted_tweet.entities))
|
||||
if hasattr(quoted_tweet, "is_quote_status") == True and hasattr(quoted_tweet, "quoted_status"):
|
||||
original_tweet = quoted_tweet.quoted_status
|
||||
elif hasattr(quoted_tweet, "retweeted_status") and hasattr(quoted_tweet.retweeted_status, "is_quote_status") == True and hasattr(quoted_tweet.retweeted_status, "quoted_status"):
|
||||
original_tweet = quoted_tweet.retweeted_status.quoted_status
|
||||
else:
|
||||
return quoted_tweet
|
||||
original_tweet = self.check_long_tweet(original_tweet)
|
||||
|
||||
if "full_text" in original_tweet:
|
||||
if hasattr(original_tweet, "full_text"):
|
||||
value = "full_text"
|
||||
elif "message" in original_tweet:
|
||||
elif hasattr(original_tweet, "message"):
|
||||
value = "message"
|
||||
else:
|
||||
value = "text"
|
||||
urls = utils.find_urls_in_text(original_tweet[value])
|
||||
for url in range(0, len(urls)):
|
||||
try: original_tweet[value] = original_tweet[value].replace(urls[url], original_tweet["entities"]["urls"][url]["expanded_url"])
|
||||
except IndexError: pass
|
||||
return compose.compose_quoted_tweet(quoted_tweet, original_tweet)
|
||||
if hasattr(original_tweet, "entities"):
|
||||
setattr(original_tweet, value, utils.expand_urls(getattr(original_tweet, value), original_tweet.entities))
|
||||
# ToDo: Shall we check whether we should add show_screen_names here?
|
||||
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, session=self)
|
||||
|
||||
def check_long_tweet(self, tweet):
|
||||
""" Process a tweet and add extra info if it's a long tweet made with Twyshort.
|
||||
tweet dict: a tweet object.
|
||||
returns a tweet with a new argument message, or original tweet if it's not a long tweet."""
|
||||
long = False
|
||||
if hasattr(tweet, "entities") and tweet.entities.get("urls"):
|
||||
long = twishort.is_long(tweet)
|
||||
if long != False and config.app["app-settings"]["handle_longtweets"]:
|
||||
message = twishort.get_full_text(long)
|
||||
if "quoted_status" in tweet:
|
||||
tweet["quoted_status"]["message"] = message
|
||||
if tweet["quoted_status"]["message"] == False: return False
|
||||
tweet["quoted_status"]["twishort"] = True
|
||||
for i in tweet["quoted_status"]["entities"]["user_mentions"]:
|
||||
if "@%s" % (i["screen_name"]) not in tweet["quoted_status"]["message"] and i["screen_name"] != tweet["user"]["screen_name"]:
|
||||
if "retweeted_status" in tweet["quoted_status"] and tweet["retweeted_status"]["user"]["screen_name"] == i["screen_name"]:
|
||||
if hasattr(tweet, "quoted_status"):
|
||||
tweet.quoted_status.message = message
|
||||
if tweet.quoted_status.message == False: return False
|
||||
tweet.quoted_status.twishort = True
|
||||
if hasattr(tweet.quoted_status, "entities") and tweet.quoted_status.entities.get("user_mentions"):
|
||||
for i in tweet.quoted_status.entities["user_mentions"]:
|
||||
if "@%s" % (i["screen_name"]) not in tweet.quoted_status.message and i["screen_name"] != self.get_user(tweet.user).screen_name:
|
||||
if hasattr(tweet.quoted_status, "retweeted_status") and self.get_user(tweet.retweeted_status.user).screen_name == i["screen_name"]:
|
||||
continue
|
||||
tweet["quoted_status"]["message"] = u"@%s %s" % (i["screen_name"], tweet["message"])
|
||||
tweet.quoted_status.message = u"@%s %s" % (i["screen_name"], tweet.message)
|
||||
else:
|
||||
tweet["message"] = message
|
||||
if tweet["message"] == False: return False
|
||||
tweet["twishort"] = True
|
||||
for i in tweet["entities"]["user_mentions"]:
|
||||
if "@%s" % (i["screen_name"]) not in tweet["message"] and i["screen_name"] != tweet["user"]["screen_name"]:
|
||||
if "retweeted_status" in tweet and tweet["retweeted_status"]["user"]["screen_name"] == i["screen_name"]:
|
||||
tweet.message = message
|
||||
if tweet.message == False: return False
|
||||
tweet.twishort = True
|
||||
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
||||
for i in tweet.entities["user_mentions"]:
|
||||
if "@%s" % (i["screen_name"]) not in tweet.message and i["screen_name"] != self.get_user(tweet.user).screen_name:
|
||||
if hasattr(tweet, "retweeted_status") and self.get_user(tweet.retweeted_status.user).screen_name == i["screen_name"]:
|
||||
continue
|
||||
tweet.message = u"@%s %s" % (i["screen_name"], tweet.message)
|
||||
return tweet
|
||||
|
||||
def get_user(self, id):
|
||||
""" Returns an user object associated with an ID.
|
||||
id str: User identifier, provided by Twitter.
|
||||
returns an user dict."""
|
||||
if ("users" in self.db) == False or (id in self.db["users"]) == False:
|
||||
returns a tweepy user object."""
|
||||
if hasattr(id, "id_str"):
|
||||
log.error("Called get_user function by passing a full user id as a parameter.")
|
||||
id = id.id_str
|
||||
# Check if the user has been added to the list of deleted users previously.
|
||||
if id in self.deleted_users:
|
||||
log.debug("Returning user {} from the list of deleted users.".format(id))
|
||||
return self.deleted_users[id]
|
||||
if ("users" in self.db) == False or (str(id) in self.db["users"]) == False:
|
||||
log.debug("Requesting user id {} as it is not present in the users database.".format(id))
|
||||
try:
|
||||
user = self.twitter.show_user(id=id)
|
||||
except TwythonError:
|
||||
user = dict(screen_name="deleted_account", name="Deleted account")
|
||||
return user
|
||||
self.db["users"][user["id_str"]] = user
|
||||
user = self.twitter.get_user(id=id)
|
||||
except TweepyException as err:
|
||||
user = UserModel(None)
|
||||
user.screen_name = "deleted_user"
|
||||
user.id = id
|
||||
user.name = _("Deleted account")
|
||||
if type(err) == NotFound:
|
||||
self.deleted_users[id] = user
|
||||
return user
|
||||
else:
|
||||
return self.db["users"][id]
|
||||
log.exception("Error when attempting to retrieve an user from Twitter.")
|
||||
return user
|
||||
users = self.db["users"]
|
||||
users[user.id_str] = user
|
||||
self.db["users"] = users
|
||||
user.name = self.get_user_alias(user)
|
||||
return user
|
||||
else:
|
||||
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.
|
||||
@@ -430,12 +459,135 @@ class Session(base.baseSession):
|
||||
returns an user ID."""
|
||||
if ("users" in self.db) == False:
|
||||
user = utils.if_user_exists(self.twitter, screen_name)
|
||||
self.db["users"][user["id_str"]] = user
|
||||
return user["id_str"]
|
||||
users = self.db["users"]
|
||||
users[user["id"]] = user
|
||||
self.db["users"] = users
|
||||
return user["id"]
|
||||
else:
|
||||
for i in list(self.db["users"].keys()):
|
||||
if self.db["users"][i]["screen_name"] == screen_name:
|
||||
return self.db["users"][i]["id_str"]
|
||||
if self.db["users"][i].screen_name == screen_name:
|
||||
return self.db["users"][i].id
|
||||
user = utils.if_user_exists(self.twitter, screen_name)
|
||||
self.db["users"][user["id_str"]] = user
|
||||
return user["id_str"]
|
||||
users = self.db["users"]
|
||||
users[user.id] = user
|
||||
self.db["users"] = users
|
||||
return user.id
|
||||
|
||||
def save_users(self, user_ids):
|
||||
""" Adds all new users to the users database. """
|
||||
if len(user_ids) == 0:
|
||||
return
|
||||
log.debug("Received %d user IDS to be added in the database." % (len(user_ids)))
|
||||
users_to_retrieve = [user_id for user_id in user_ids if (user_id not in self.db["users"] and user_id not in self.deleted_users)]
|
||||
# Remove duplicates
|
||||
users_to_retrieve = list(dict.fromkeys(users_to_retrieve))
|
||||
if len(users_to_retrieve) == 0:
|
||||
return
|
||||
log.debug("TWBlue will get %d new users from Twitter." % (len(users_to_retrieve)))
|
||||
try:
|
||||
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 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.
|
||||
# As deleted_users is not saved across restarts, when restarting TWBlue, it will retrieve the correct users if they enabled their accounts.
|
||||
for id in users_to_retrieve:
|
||||
user = UserModel(None)
|
||||
user.screen_name = "deleted_user"
|
||||
user.id = id
|
||||
user.name = _("Deleted account")
|
||||
self.deleted_users[id] = user
|
||||
else:
|
||||
log.exception("An exception happened while attempting to retrieve a list of users from direct messages in Twitter.")
|
||||
|
||||
def add_users_from_results(self, data):
|
||||
users = self.db["users"]
|
||||
for i in data:
|
||||
if hasattr(i, "user"):
|
||||
if isinstance(i.user, str):
|
||||
log.warning("A String was passed to be added as an user. This is normal only if TWBlue tried to load a conversation.")
|
||||
continue
|
||||
if (i.user.id_str in self.db["users"]) == False:
|
||||
users[i.user.id_str] = i.user
|
||||
if hasattr(i, "quoted_status") and (i.quoted_status.user.id_str in self.db["users"]) == False:
|
||||
users[i.quoted_status.user.id_str] = i.quoted_status.user
|
||||
|
||||
if hasattr(i, "retweeted_status") and (i.retweeted_status.user.id_str in self.db["users"]) == False:
|
||||
users[i.retweeted_status.user.id_str] = i.retweeted_status.user
|
||||
self.db["users"] = users
|
||||
|
||||
def start_streaming(self):
|
||||
if config.app["app-settings"]["no_streaming"]:
|
||||
return
|
||||
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=appkeys.twitter_api_key, consumer_secret=appkeys.twitter_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"]:
|
||||
return
|
||||
if hasattr(self, "stream"):
|
||||
self.stream.running = False
|
||||
log.debug("Stream stopped for accounr {}".format(self.db["user_name"]))
|
||||
|
||||
def handle_new_status(self, status, user):
|
||||
""" Handles a new status present in the Streaming API. """
|
||||
if self.logged == False:
|
||||
return
|
||||
# Discard processing the status if the streaming sends a tweet for another account.
|
||||
if self.db["user_name"] != user:
|
||||
return
|
||||
# the Streaming API sends non-extended tweets with an optional parameter "extended_tweets" which contains full_text and other data.
|
||||
# so we have to make sure we check it before processing the normal status.
|
||||
# As usual, we handle also quotes and retweets at first.
|
||||
if hasattr(status, "retweeted_status") and hasattr(status.retweeted_status, "extended_tweet"):
|
||||
status.retweeted_status._json = {**status.retweeted_status._json, **status.retweeted_status._json["extended_tweet"]}
|
||||
# compose.compose_tweet requires the parent tweet to have a full_text field, so we have to add it to retweets here.
|
||||
status._json["full_text"] = status._json["text"]
|
||||
if hasattr(status, "quoted_status") and hasattr(status.quoted_status, "extended_tweet"):
|
||||
status.quoted_status._json = {**status.quoted_status._json, **status.quoted_status._json["extended_tweet"]}
|
||||
if status.truncated:
|
||||
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.users:
|
||||
buffers_to_send.append("home_timeline")
|
||||
if status.user.id == self.db["user_id"]:
|
||||
buffers_to_send.append("sent_tweets")
|
||||
for user in status.entities["user_mentions"]:
|
||||
if user["id"] == self.db["user_id"]:
|
||||
buffers_to_send.append("mentions")
|
||||
users_with_timeline = [user.split("-")[0] for user in self.db.keys() if user.endswith("-timeline")]
|
||||
for user in users_with_timeline:
|
||||
if status.user.id_str == user:
|
||||
buffers_to_send.append("{}-timeline".format(user))
|
||||
for buffer in buffers_to_send[::]:
|
||||
num = self.order_buffer(buffer, [status])
|
||||
if num == 0:
|
||||
buffers_to_send.remove(buffer)
|
||||
# However, we have to do the "reduce and change" process here because the status we sent to the db is going to be a different object that the one sent to database.
|
||||
status = reduce.reduce_tweet(status)
|
||||
status = self.check_quoted_status(status)
|
||||
status = self.check_long_tweet(status)
|
||||
# Send it to the main controller object.
|
||||
pub.sendMessage("newTweet", data=status, user=self.db["user_name"], _buffers=buffers_to_send)
|
||||
|
||||
def check_streams(self):
|
||||
if config.app["app-settings"]["no_streaming"]:
|
||||
return
|
||||
if not hasattr(self, "stream"):
|
||||
return
|
||||
log.debug("Status of running stream for user {}: {}".format(self.db["user_name"], self.stream.running))
|
||||
if self.stream.running == False:
|
||||
self.start_streaming()
|
||||
|
||||
def handle_connected(self, user):
|
||||
if self.logged == False:
|
||||
return
|
||||
if user != self.db["user_name"]:
|
||||
log.debug("Connected streaming endpoint on account {}".format(user))
|
47
src/sessions/twitter/streaming.py
Normal file
47
src/sessions/twitter/streaming.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Streaming support for TWBlue. """
|
||||
import time
|
||||
import sys
|
||||
import six
|
||||
import requests
|
||||
import urllib3
|
||||
import ssl
|
||||
import tweepy
|
||||
import logging
|
||||
from pubsub import pub
|
||||
|
||||
log = logging.getLogger("sessions.twitter.streaming")
|
||||
|
||||
class Stream(tweepy.Stream):
|
||||
|
||||
def __init__(self, twitter_api, user, user_id, muted_users=[], *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.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)))
|
||||
for user in friends:
|
||||
if user not in muted_users:
|
||||
self.users.append(str(user))
|
||||
self.started = True
|
||||
log.debug("Streaming listener started with {} users to follow.".format(len(self.users)))
|
||||
|
||||
def on_connect(self):
|
||||
pub.sendMessage("streamConnected", user=self.user)
|
||||
|
||||
def on_exception(self, ex):
|
||||
log.exception("Exception received on streaming endpoint for user {}".format(self.user))
|
||||
|
||||
def on_status(self, status):
|
||||
""" Checks data arriving as a tweet. """
|
||||
# Hide replies to users not followed by current account.
|
||||
if status.in_reply_to_user_id_str != None and status.in_reply_to_user_id_str not in self.users and status.user.screen_name != self.user:
|
||||
return
|
||||
if status.user.id_str in self.users:
|
||||
pub.sendMessage("newStatus", status=status, user=self.user)
|
@@ -1,16 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
from builtins import range
|
||||
import url_shortener, re
|
||||
import output
|
||||
from twython import TwythonError
|
||||
import config
|
||||
import logging
|
||||
import requests
|
||||
import time
|
||||
import sound
|
||||
from tweepy.errors import TweepyException, NotFound, Forbidden
|
||||
log = logging.getLogger("twitter.utils")
|
||||
""" Some utilities for the twitter interface."""
|
||||
|
||||
@@ -25,164 +21,171 @@ bad_chars = '\'\\\n.,[](){}:;"'
|
||||
def find_urls_in_text(text):
|
||||
return url_re2.findall(text)
|
||||
|
||||
def find_urls (tweet):
|
||||
def find_urls (tweet, twitter_media=False):
|
||||
urls = []
|
||||
if twitter_media and hasattr(tweet, "extended_entities"):
|
||||
for mediaItem in tweet.extended_entities["media"]:
|
||||
if mediaItem["type"] == "video":
|
||||
for variant in mediaItem["video_info"]["variants"]:
|
||||
if variant["content_type"] == "video/mp4":
|
||||
urls.append(variant["url"])
|
||||
break
|
||||
# Let's add URLS from tweet entities.
|
||||
if "message_create" in tweet:
|
||||
entities = tweet["message_create"]["message_data"]["entities"]
|
||||
if hasattr(tweet, "message_create"):
|
||||
entities = tweet.message_create["message_data"]["entities"]
|
||||
else:
|
||||
entities = tweet["entities"]
|
||||
if hasattr(tweet, "entities") == True:
|
||||
entities = tweet.entities
|
||||
if entities.get("urls") != None:
|
||||
for i in entities["urls"]:
|
||||
if i["expanded_url"] not in urls:
|
||||
urls.append(i["expanded_url"])
|
||||
if "quoted_status" in tweet:
|
||||
for i in tweet["quoted_status"]["entities"]["urls"]:
|
||||
if i["expanded_url"] not in urls:
|
||||
urls.append(i["expanded_url"])
|
||||
if "retweeted_status" in tweet:
|
||||
for i in tweet["retweeted_status"]["entities"]["urls"]:
|
||||
if i["expanded_url"] not in urls:
|
||||
urls.append(i["expanded_url"])
|
||||
if "quoted_status" in tweet["retweeted_status"]:
|
||||
for i in tweet["retweeted_status"]["quoted_status"]["entities"]["urls"]:
|
||||
if i["expanded_url"] not in urls:
|
||||
urls.append(i["expanded_url"])
|
||||
if "message" in tweet:
|
||||
if hasattr(tweet, "quoted_status"):
|
||||
urls.extend(find_urls(tweet.quoted_status, twitter_media))
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
urls.extend(find_urls(tweet.retweeted_status, twitter_media))
|
||||
if hasattr(tweet, "message"):
|
||||
i = "message"
|
||||
elif "full_text" in tweet:
|
||||
elif hasattr(tweet, "full_text"):
|
||||
i = "full_text"
|
||||
else:
|
||||
i = "text"
|
||||
if "message_create" in tweet:
|
||||
extracted_urls = find_urls_in_text(tweet["message_create"]["message_data"]["text"])
|
||||
if hasattr(tweet, "message_create"):
|
||||
extracted_urls = find_urls_in_text(tweet.message_create["message_data"]["text"])
|
||||
else:
|
||||
extracted_urls = find_urls_in_text(tweet[i])
|
||||
extracted_urls = find_urls_in_text(getattr(tweet, i))
|
||||
# Don't include t.co links (mostly they are photos or shortened versions of already added URLS).
|
||||
for i in extracted_urls:
|
||||
if i not in urls and "https://t.co" not in i:
|
||||
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):
|
||||
for i in range(0, len(lists)):
|
||||
if lists[i]["name"] == name: return lists[i]["id"]
|
||||
|
||||
def find_previous_reply(id, listItem):
|
||||
for i in range(0, len(listItem)):
|
||||
if listItem[i]["id_str"] == str(id): return i
|
||||
return None
|
||||
|
||||
def find_next_reply(id, listItem):
|
||||
for i in range(0, len(listItem)):
|
||||
if listItem[i]["in_reply_to_status_id_str"] == str(id): return i
|
||||
return None
|
||||
if lists[i].name == name: return lists[i].id
|
||||
|
||||
def is_audio(tweet):
|
||||
if hasattr(tweet, "quoted_status") and hasattr(tweet.quoted_status, "extended_entities"):
|
||||
result = is_audio(tweet.quoted_status)
|
||||
if result != None:
|
||||
return result
|
||||
if hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "extended_entities"):
|
||||
result = is_audio(tweet.retweeted_status)
|
||||
if result == True:
|
||||
return result
|
||||
# Checks firstly for Twitter videos and audios.
|
||||
if hasattr(tweet, "extended_entities"):
|
||||
for mediaItem in tweet.extended_entities["media"]:
|
||||
if mediaItem["type"] == "video":
|
||||
return True
|
||||
try:
|
||||
if len(find_urls(tweet)) < 1:
|
||||
return False
|
||||
if "message_create" in tweet:
|
||||
entities = tweet["message_create"]["message_data"]["entities"]
|
||||
if hasattr(tweet, "message_create"):
|
||||
entities = tweet.message_create["message_data"]["entities"]
|
||||
else:
|
||||
entities = tweet["entities"]
|
||||
if hasattr(tweet, "entities") == False or tweet.entities.get("hashtags") == None:
|
||||
return False
|
||||
entities = tweet.entities
|
||||
if len(entities["hashtags"]) > 0:
|
||||
for i in entities["hashtags"]:
|
||||
if i["text"] == "audio":
|
||||
return True
|
||||
except IndexError:
|
||||
print(tweet["entities"]["hashtags"])
|
||||
log.exception("Exception while executing is_audio hashtag algorithm")
|
||||
|
||||
def is_geocoded(tweet):
|
||||
if "coordinates" in tweet and tweet["coordinates"] != None:
|
||||
if hasattr(tweet, "coordinates") and tweet.coordinates != None:
|
||||
return True
|
||||
|
||||
def is_media(tweet):
|
||||
if "message_create" in tweet:
|
||||
entities = tweet["message_create"]["message_data"]["entities"]
|
||||
if hasattr(tweet, "message_create"):
|
||||
entities = tweet.message_create["message_data"]["entities"]
|
||||
else:
|
||||
entities = tweet["entities"]
|
||||
if ("media" in entities) == False:
|
||||
if hasattr(tweet, "entities") == False or tweet.entities.get("hashtags") == None:
|
||||
return False
|
||||
entities = tweet.entities
|
||||
if entities.get("media") == None:
|
||||
return False
|
||||
for i in entities["media"]:
|
||||
if "type" in i and i["type"] == "photo":
|
||||
if i.get("type") != None and i.get("type") == "photo":
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_all_mentioned(tweet, conf, field="screen_name"):
|
||||
""" Gets all users that has been mentioned."""
|
||||
""" Gets all users that have been mentioned."""
|
||||
results = []
|
||||
for i in tweet["entities"]["user_mentions"]:
|
||||
if i["screen_name"] != conf["user_name"] and i["screen_name"] != tweet["user"]["screen_name"]:
|
||||
if i[field] not in results:
|
||||
results.append(i[field])
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
results.extend(get_all_mentioned(tweet.retweeted_status, conf, field))
|
||||
if hasattr(tweet, "quoted_status"):
|
||||
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:
|
||||
if i.get(field) not in results:
|
||||
results.append(i.get(field))
|
||||
return results
|
||||
|
||||
def get_all_users(tweet, conf):
|
||||
def get_all_users(tweet, session):
|
||||
string = []
|
||||
if "retweeted_status" in tweet:
|
||||
string.append(tweet["user"]["screen_name"])
|
||||
tweet = tweet["retweeted_status"]
|
||||
if "sender" in tweet:
|
||||
string.append(tweet["sender"]["screen_name"])
|
||||
else:
|
||||
if tweet["user"]["screen_name"] != conf["user_name"]:
|
||||
string.append(tweet["user"]["screen_name"])
|
||||
for i in tweet["entities"]["user_mentions"]:
|
||||
if i["screen_name"] != conf["user_name"] and i["screen_name"] != tweet["user"]["screen_name"]:
|
||||
user = session.get_user(tweet.user)
|
||||
if user.screen_name != session.db["user_name"]:
|
||||
string.append(user.screen_name)
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
string.extend(get_all_users(tweet.retweeted_status, session))
|
||||
if hasattr(tweet, "quoted_status"):
|
||||
string.extend(get_all_users(tweet.quoted_status, session))
|
||||
if hasattr(tweet, "entities") and tweet.entities.get("user_mentions"):
|
||||
for i in tweet.entities["user_mentions"]:
|
||||
if i["screen_name"] != session.db["user_name"] and i["screen_name"] != user.screen_name:
|
||||
if i["screen_name"] not in string:
|
||||
string.append(i["screen_name"])
|
||||
# Attempt to remove duplicates, tipically caused by nested tweets.
|
||||
string = list(dict.fromkeys(string))
|
||||
if len(string) == 0:
|
||||
string.append(tweet["user"]["screen_name"])
|
||||
string.append(user.screen_name)
|
||||
return string
|
||||
|
||||
def if_user_exists(twitter, user):
|
||||
try:
|
||||
data = twitter.show_user(screen_name=user)
|
||||
data = twitter.get_user(screen_name=user)
|
||||
return data
|
||||
except TwythonError as err:
|
||||
if err.error_code == 404:
|
||||
except TweepyException as err:
|
||||
if type(err) == NotFound:
|
||||
return None
|
||||
else:
|
||||
return user
|
||||
|
||||
def api_call(parent=None, call_name=None, preexec_message="", success="", success_snd="", *args, **kwargs):
|
||||
if preexec_message:
|
||||
output.speak(preexec_message, True)
|
||||
try:
|
||||
val = getattr(parent.twitter.twitter, call_name)(*args, **kwargs)
|
||||
output.speak(success)
|
||||
parent.parent.sound.play(success_snd)
|
||||
except TwythonError as e:
|
||||
output.speak("Error %s: %s" % (e.error_code, e.msg), True)
|
||||
parent.parent.sound.play("error.ogg")
|
||||
return val
|
||||
|
||||
def is_allowed(tweet, settings, buffer_name):
|
||||
clients = settings["twitter"]["ignored_clients"]
|
||||
if "sender" in tweet: return True
|
||||
if hasattr(tweet, "sender"): return True
|
||||
allowed = True
|
||||
tweet_data = {}
|
||||
if "retweeted_status" in tweet:
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
tweet_data["retweet"] = True
|
||||
if tweet["in_reply_to_status_id_str"] != None:
|
||||
if hasattr(tweet, "in_reply_to_status_id"):
|
||||
tweet_data["reply"] = True
|
||||
if "quoted_status" in tweet:
|
||||
if hasattr(tweet, "quoted_status"):
|
||||
tweet_data["quote"] = True
|
||||
if "retweeted_status" in tweet: tweet = tweet["retweeted_status"]
|
||||
source = re.sub(r"(?s)<.*?>", "", tweet["source"])
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
tweet = tweet.retweeted_status
|
||||
source = tweet.source
|
||||
for i in clients:
|
||||
if i.lower() == source.lower():
|
||||
return False
|
||||
return filter_tweet(tweet, tweet_data, settings, buffer_name)
|
||||
|
||||
def filter_tweet(tweet, tweet_data, settings, buffer_name):
|
||||
if "full_text" in tweet:
|
||||
if hasattr(tweet, "full_text"):
|
||||
value = "full_text"
|
||||
else:
|
||||
value = "text"
|
||||
@@ -210,24 +213,51 @@ def filter_tweet(tweet, tweet_data, settings, buffer_name):
|
||||
if allow_replies == "False" and "reply" in tweet_data:
|
||||
return False
|
||||
if word != "" and settings["filters"][i]["if_word_exists"]:
|
||||
if word in tweet[value]:
|
||||
if word in getattr(tweet, value):
|
||||
return False
|
||||
elif word != "" and settings["filters"][i]["if_word_exists"] == False:
|
||||
if word not in tweet[value]:
|
||||
if word not in getattr(tweet, value):
|
||||
return False
|
||||
if settings["filters"][i]["in_lang"] == "True":
|
||||
if tweet["lang"] not in settings["filters"][i]["languages"]:
|
||||
if getattr(tweet, lang) not in settings["filters"][i]["languages"]:
|
||||
return False
|
||||
elif settings["filters"][i]["in_lang"] == "False":
|
||||
if tweet["lang"] in settings["filters"][i]["languages"]:
|
||||
if tweet.lang in settings["filters"][i]["languages"]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def twitter_error(error):
|
||||
if error.error_code == 403:
|
||||
if type(error) == Forbidden:
|
||||
msg = _(u"Sorry, you are not authorised to see this status.")
|
||||
elif error.error_code == 404:
|
||||
elif type(error) == NotFound:
|
||||
msg = _(u"No status found with that ID")
|
||||
else:
|
||||
msg = _(u"Error code {0}").format(error.error_code,)
|
||||
msg = _(u"Error {0}").format(str(error),)
|
||||
output.speak(msg)
|
||||
|
||||
def expand_urls(text, entities):
|
||||
""" Expand all URLS present in text with information found in entities"""
|
||||
if entities.get("urls") == None:
|
||||
return text
|
||||
urls = find_urls_in_text(text)
|
||||
for url in entities["urls"]:
|
||||
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
|
11
src/setup.py
11
src/setup.py
@@ -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,16 +40,19 @@ 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"],
|
||||
)
|
||||
bin_path_excludes=["C:\\Program Files", "C:\Program Files (x86)"],
|
||||
)
|
||||
|
||||
executables = [
|
||||
Executable('main.py', base=base, targetName="twblue")
|
||||
Executable('main.py', base=base, target_name="twblue")
|
||||
]
|
||||
|
||||
winmsvcr.FILES = ()
|
||||
winmsvcr.FILES_TO_DUPLICATE = ()
|
||||
setup(name=application.name,
|
||||
version=application.version,
|
||||
description=application.description,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user