mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-08-25 09:29:22 +00:00
Compare commits
301 Commits
alpha2
...
v2021.10.2
Author | SHA1 | Date | |
---|---|---|---|
5eb942981c | |||
6739045cce | |||
307ed093af | |||
d11fc44772 | |||
a3e5eec6de | |||
41a0935121 | |||
0b03e7505f | |||
![]() |
81c364c4e1 | ||
![]() |
483b196203 | ||
![]() |
b512c69447 | ||
2c1608322e | |||
c6bb851bce | |||
![]() |
66581f8b1c | ||
![]() |
c6a3a44c21 | ||
bd25cfa59b | |||
![]() |
39a02ea33a | ||
e23a52e38f | |||
![]() |
d888563fda | ||
a5ba80feee | |||
528ecc2a33 | |||
3519746078 | |||
ef79e0696e | |||
b9ee0dae5b | |||
f31575a733 | |||
e451bbd5e9 | |||
f7f303929e | |||
9f48784ce4 | |||
cb1312d0c9 | |||
a82efd4dcc | |||
72e6d030d5 | |||
d222740887 | |||
2b059ee42e | |||
daac312658 | |||
b23be9c896 | |||
61b0dc34b8 | |||
c5d13369eb | |||
856ecf5eb9 | |||
e3e0ac9457 | |||
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 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -19,4 +19,5 @@ src/sounds/iOs
|
||||
release-snapshot/
|
||||
src/com_cache/
|
||||
doc/strings.py
|
||||
doc/changelog.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:\\python38\\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.8.10 -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.8.10 -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.zip *
|
||||
|
||||
artifacts:
|
||||
- path: snapshot.zip
|
||||
|
||||
deploy:
|
||||
- provider: FTP
|
||||
host: twblue.es
|
||||
protocol: ftp
|
||||
beta: true
|
||||
username: twblue.es
|
||||
password:
|
||||
secure: lQZqpYRnHf4LLVOg0C42NQ==
|
||||
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,))
|
||||
generate_document(i)
|
||||
generate_document(i, "changelog")
|
||||
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 = []
|
||||
supported_languages = []
|
||||
version = "11"
|
||||
|
@@ -2,22 +2,22 @@ from __future__ import unicode_literals
|
||||
from functools import wraps
|
||||
|
||||
def matches_url(url):
|
||||
def url_setter(func):
|
||||
@wraps(func)
|
||||
def internal_url_setter(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
internal_url_setter.url = url
|
||||
return internal_url_setter
|
||||
return url_setter
|
||||
def url_setter(func):
|
||||
@wraps(func)
|
||||
def internal_url_setter(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
internal_url_setter.url = url
|
||||
return internal_url_setter
|
||||
return url_setter
|
||||
|
||||
def find_url_transformer(url):
|
||||
from audio_services import services
|
||||
funcs = []
|
||||
for i in dir(services):
|
||||
possible = getattr(services, i)
|
||||
if callable(possible) and hasattr(possible, 'url'):
|
||||
funcs.append(possible)
|
||||
for f in funcs:
|
||||
if url.lower().startswith(f.url.lower()):
|
||||
return f
|
||||
return services.convert_generic_audio
|
||||
from audio_services import services
|
||||
funcs = []
|
||||
for i in dir(services):
|
||||
possible = getattr(services, i)
|
||||
if callable(possible) and hasattr(possible, 'url'):
|
||||
funcs.append(possible)
|
||||
for f in funcs:
|
||||
if url.lower().startswith(f.url.lower()):
|
||||
return f
|
||||
return services.convert_generic_audio
|
||||
|
@@ -1,43 +1,41 @@
|
||||
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):
|
||||
if "audioboom.com" not in url.lower():
|
||||
raise TypeError('%r is not a valid URL' % url)
|
||||
audio_id = url.split('.com/')[-1]
|
||||
return 'https://audioboom.com/%s.mp3' % audio_id
|
||||
if "audioboom.com" not in url.lower():
|
||||
raise TypeError('%r is not a valid URL' % url)
|
||||
audio_id = url.split('.com/')[-1]
|
||||
return 'https://audioboom.com/%s.mp3' % audio_id
|
||||
|
||||
@matches_url ('https://soundcloud.com/')
|
||||
def convert_soundcloud (url):
|
||||
client_id = "df8113ca95c157b6c9731f54b105b473"
|
||||
with requests.get('http://api.soundcloud.com/resolve.json', client_id=client_id, url=url) as permalink:
|
||||
if permalink.status_code==404:
|
||||
raise TypeError('%r is not a valid URL' % permalink.url)
|
||||
else:
|
||||
resolved_url = permalink.url
|
||||
with requests.get(resolved_url) as track_url:
|
||||
track_data = track_url.json()
|
||||
client_id = "df8113ca95c157b6c9731f54b105b473"
|
||||
with requests.get('http://api.soundcloud.com/resolve.json', client_id=client_id, url=url) as permalink:
|
||||
if permalink.status_code==404:
|
||||
raise TypeError('%r is not a valid URL' % permalink.url)
|
||||
else:
|
||||
resolved_url = permalink.url
|
||||
with requests.get(resolved_url) as track_url:
|
||||
track_data = track_url.json()
|
||||
|
||||
if track_data ['streamable']:
|
||||
return track_data ['stream_url'] + "?client_id=%s" %client_id
|
||||
else:
|
||||
raise TypeError('%r is not streamable' % url)
|
||||
if track_data ['streamable']:
|
||||
return track_data ['stream_url'] + "?client_id=%s" %client_id
|
||||
else:
|
||||
raise TypeError('%r is not streamable' % url)
|
||||
|
||||
@matches_url ('https://www.youtube.com/watch')
|
||||
def convert_youtube_long (url):
|
||||
return youtube_utils.get_video_url(url)
|
||||
return youtube_utils.get_video_url(url)
|
||||
|
||||
@matches_url ('http://anyaudio.net/listen')
|
||||
def convert_anyaudio(url):
|
||||
values = url.split("audio=")
|
||||
if len(values) != 2:
|
||||
raise TypeError('%r is not streamable' % url)
|
||||
return "http://anyaudio.net/audiodownload?audio=%s" % (values[1],)
|
||||
values = url.split("audio=")
|
||||
if len(values) != 2:
|
||||
raise TypeError('%r is not streamable' % url)
|
||||
return "http://anyaudio.net/audiodownload?audio=%s" % (values[1],)
|
||||
|
||||
def convert_generic_audio(url):
|
||||
return url
|
||||
return url
|
||||
|
@@ -3,11 +3,11 @@ from __future__ import unicode_literals
|
||||
import youtube_dl
|
||||
|
||||
def get_video_url(url):
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
result = ydl.extract_info(url, download=False)
|
||||
if 'entries' in result:
|
||||
video = result['entries'][0]
|
||||
else:
|
||||
video = result
|
||||
return video["formats"][0]["url"]
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
result = ydl.extract_info(url, download=False)
|
||||
if 'entries' in result:
|
||||
video = result['entries'][0]
|
||||
else:
|
||||
video = result
|
||||
return video["formats"][0]["url"]
|
||||
|
@@ -9,20 +9,20 @@ 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
|
||||
|
||||
def setup ():
|
||||
global app
|
||||
log.debug("Loading global app settings...")
|
||||
app = config_utils.load_config(os.path.join(paths.config_path(), MAINFILE), os.path.join(paths.app_path(), MAINSPEC))
|
||||
log.debug("Loading keymap...")
|
||||
global keymap
|
||||
if float(platform.version()[:2]) >= 10 and app["app-settings"]["load_keymap"] == "default.keymap":
|
||||
app["app-settings"]["load_keymap"] = "Windows 10.keymap"
|
||||
app.write()
|
||||
global changed_keymap
|
||||
changed_keymap = True
|
||||
keymap = config_utils.load_config(os.path.join(paths.config_path(), "keymap.keymap"), os.path.join(paths.app_path(), "keymaps/"+app['app-settings']['load_keymap']), copy=False)
|
||||
global app
|
||||
log.debug("Loading global app settings...")
|
||||
app = config_utils.load_config(os.path.join(paths.config_path(), MAINFILE), os.path.join(paths.app_path(), MAINSPEC))
|
||||
log.debug("Loading keymap...")
|
||||
global keymap
|
||||
if float(platform.version()[:2]) >= 10 and app["app-settings"]["load_keymap"] == "default.keymap":
|
||||
app["app-settings"]["load_keymap"] = "Windows 10.keymap"
|
||||
app.write()
|
||||
global changed_keymap
|
||||
changed_keymap = True
|
||||
keymap = config_utils.load_config(os.path.join(paths.config_path(), "keymap.keymap"), os.path.join(paths.app_path(), "keymaps/"+app['app-settings']['load_keymap']), copy=False)
|
||||
|
@@ -9,69 +9,69 @@ log = getLogger("config_utils")
|
||||
class ConfigLoadError(Exception): pass
|
||||
|
||||
def load_config(config_path, configspec_path=None, copy=True, *args, **kwargs):
|
||||
spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True)
|
||||
try:
|
||||
config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs)
|
||||
except ParseError:
|
||||
raise ConfigLoadError("Unable to load %r" % config_path)
|
||||
validator = Validator()
|
||||
validated = config.validate(validator, preserve_errors=False, copy=copy)
|
||||
if validated == True:
|
||||
config.write()
|
||||
return config
|
||||
else:
|
||||
log.exception("Error in config file: {0}".format(validated,))
|
||||
spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True)
|
||||
try:
|
||||
config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs)
|
||||
except ParseError:
|
||||
raise ConfigLoadError("Unable to load %r" % config_path)
|
||||
validator = Validator()
|
||||
validated = config.validate(validator, preserve_errors=False, copy=copy)
|
||||
if validated == True:
|
||||
config.write()
|
||||
return config
|
||||
else:
|
||||
log.exception("Error in config file: {0}".format(validated,))
|
||||
|
||||
def is_blank(arg):
|
||||
"Check if a line is blank."
|
||||
for c in arg:
|
||||
if c not in string.whitespace:
|
||||
return False
|
||||
return True
|
||||
"Check if a line is blank."
|
||||
for c in arg:
|
||||
if c not in string.whitespace:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_keys(path):
|
||||
"Gets the keys of a configobj config file."
|
||||
res=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
if not is_blank(line):
|
||||
res.append(line[0:line.find('=')].strip())
|
||||
fin.close()
|
||||
return res
|
||||
"Gets the keys of a configobj config file."
|
||||
res=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
if not is_blank(line):
|
||||
res.append(line[0:line.find('=')].strip())
|
||||
fin.close()
|
||||
return res
|
||||
|
||||
def hist(keys):
|
||||
"Generates a histogram of an iterable."
|
||||
res={}
|
||||
for k in keys:
|
||||
res[k]=res.setdefault(k,0)+1
|
||||
return res
|
||||
"Generates a histogram of an iterable."
|
||||
res={}
|
||||
for k in keys:
|
||||
res[k]=res.setdefault(k,0)+1
|
||||
return res
|
||||
|
||||
def find_problems(hist):
|
||||
"Takes a histogram and returns a list of items occurring more than once."
|
||||
res=[]
|
||||
for k,v in hist.items():
|
||||
if v>1:
|
||||
res.append(k)
|
||||
return res
|
||||
"Takes a histogram and returns a list of items occurring more than once."
|
||||
res=[]
|
||||
for k,v in hist.items():
|
||||
if v>1:
|
||||
res.append(k)
|
||||
return res
|
||||
|
||||
def clean_config(path):
|
||||
"Cleans a config file. If duplicate values are found, delete all of them and just use the default."
|
||||
orig=[]
|
||||
cleaned=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
orig.append(line)
|
||||
fin.close()
|
||||
for p in find_problems(hist(get_keys(path))):
|
||||
for o in orig:
|
||||
o.strip()
|
||||
if p not in o:
|
||||
cleaned.append(o)
|
||||
if len(cleaned) != 0:
|
||||
cam=open(path,'w')
|
||||
for c in cleaned:
|
||||
cam.write(c)
|
||||
cam.close()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
"Cleans a config file. If duplicate values are found, delete all of them and just use the default."
|
||||
orig=[]
|
||||
cleaned=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
orig.append(line)
|
||||
fin.close()
|
||||
for p in find_problems(hist(get_keys(path))):
|
||||
for o in orig:
|
||||
o.strip()
|
||||
if p not in o:
|
||||
cleaned.append(o)
|
||||
if len(cleaned) != 0:
|
||||
cam=open(path,'w')
|
||||
for c in cleaned:
|
||||
cam.write(c)
|
||||
cam.close()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
@@ -8,33 +8,33 @@ from wxUI.dialogs import attach as gui
|
||||
log = logging.getLogger("controller.attach")
|
||||
|
||||
class attach(object):
|
||||
def __init__(self):
|
||||
self.attachments = list()
|
||||
self.dialog = gui.attachDialog()
|
||||
widgetUtils.connect_event(self.dialog.photo, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
||||
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
|
||||
self.dialog.get_response()
|
||||
log.debug("Attachments controller started.")
|
||||
def __init__(self):
|
||||
self.attachments = list()
|
||||
self.dialog = gui.attachDialog()
|
||||
widgetUtils.connect_event(self.dialog.photo, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
||||
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
|
||||
self.dialog.get_response()
|
||||
log.debug("Attachments controller started.")
|
||||
|
||||
def upload_image(self, *args, **kwargs):
|
||||
image, description = self.dialog.get_image()
|
||||
if image != None:
|
||||
imageInfo = {"type": "photo", "file": image, "description": description}
|
||||
log.debug("Image data to upload: %r" % (imageInfo,))
|
||||
self.attachments.append(imageInfo)
|
||||
info = [_(u"Photo"), description]
|
||||
self.dialog.attachments.insert_item(False, *info)
|
||||
self.dialog.remove.Enable(True)
|
||||
def upload_image(self, *args, **kwargs):
|
||||
image, description = self.dialog.get_image()
|
||||
if image != None:
|
||||
imageInfo = {"type": "photo", "file": image, "description": description}
|
||||
log.debug("Image data to upload: %r" % (imageInfo,))
|
||||
self.attachments.append(imageInfo)
|
||||
info = [_(u"Photo"), description]
|
||||
self.dialog.attachments.insert_item(False, *info)
|
||||
self.dialog.remove.Enable(True)
|
||||
|
||||
def remove_attachment(self, *args, **kwargs):
|
||||
current_item = self.dialog.attachments.get_selected()
|
||||
log.debug("Removing item %d" % (current_item,))
|
||||
if current_item == -1: current_item = 0
|
||||
self.attachments.pop(current_item)
|
||||
self.dialog.attachments.remove_item(current_item)
|
||||
self.check_remove_status()
|
||||
log.debug("Removed")
|
||||
def remove_attachment(self, *args, **kwargs):
|
||||
current_item = self.dialog.attachments.get_selected()
|
||||
log.debug("Removing item %d" % (current_item,))
|
||||
if current_item == -1: current_item = 0
|
||||
self.attachments.pop(current_item)
|
||||
self.dialog.attachments.remove_item(current_item)
|
||||
self.check_remove_status()
|
||||
log.debug("Removed")
|
||||
|
||||
def check_remove_status(self):
|
||||
if len(self.attachments) == 0 and self.dialog.attachments.get_count() == 0:
|
||||
self.dialog.remove.Enable(False)
|
||||
def check_remove_status(self):
|
||||
if len(self.attachments) == 0 and self.dialog.attachments.get_count() == 0:
|
||||
self.dialog.remove.Enable(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
@@ -8,71 +8,71 @@ from wxUI.dialogs import filterDialogs
|
||||
from wxUI import commonMessageDialogs
|
||||
|
||||
class filter(object):
|
||||
def __init__(self, buffer, filter_title=None, if_word_exists=None, in_lang=None, regexp=None, word=None, in_buffer=None):
|
||||
self.buffer = buffer
|
||||
self.dialog = filterDialogs.filterDialog(languages=[i["name"] for i in application.supported_languages])
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
title = self.dialog.get("title")
|
||||
contains = self.dialog.get("contains")
|
||||
term = self.dialog.get("term")
|
||||
regexp = self.dialog.get("regexp")
|
||||
allow_rts = self.dialog.get("allow_rts")
|
||||
allow_quotes = self.dialog.get("allow_quotes")
|
||||
allow_replies = self.dialog.get("allow_replies")
|
||||
load_language = self.dialog.get("load_language")
|
||||
ignore_language = self.dialog.get("ignore_language")
|
||||
lang_option = None
|
||||
if ignore_language:
|
||||
lang_option = False
|
||||
elif load_language:
|
||||
lang_option = True
|
||||
langs = self.dialog.get_selected_langs()
|
||||
langcodes = []
|
||||
for i in application.supported_languages:
|
||||
if i["name"] in langs:
|
||||
langcodes.append(i["code"])
|
||||
d = dict(in_buffer=self.buffer.name, word=term, regexp=regexp, in_lang=lang_option, languages=langcodes, if_word_exists=contains, allow_rts=allow_rts, allow_quotes=allow_quotes, allow_replies=allow_replies)
|
||||
if title in self.buffer.session.settings["filters"]:
|
||||
return commonMessageDialogs.existing_filter()
|
||||
self.buffer.session.settings["filters"][title] = d
|
||||
self.buffer.session.settings.write()
|
||||
def __init__(self, buffer, filter_title=None, if_word_exists=None, in_lang=None, regexp=None, word=None, in_buffer=None):
|
||||
self.buffer = buffer
|
||||
self.dialog = filterDialogs.filterDialog(languages=[i["name"] for i in application.supported_languages])
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
title = self.dialog.get("title")
|
||||
contains = self.dialog.get("contains")
|
||||
term = self.dialog.get("term")
|
||||
regexp = self.dialog.get("regexp")
|
||||
allow_rts = self.dialog.get("allow_rts")
|
||||
allow_quotes = self.dialog.get("allow_quotes")
|
||||
allow_replies = self.dialog.get("allow_replies")
|
||||
load_language = self.dialog.get("load_language")
|
||||
ignore_language = self.dialog.get("ignore_language")
|
||||
lang_option = None
|
||||
if ignore_language:
|
||||
lang_option = False
|
||||
elif load_language:
|
||||
lang_option = True
|
||||
langs = self.dialog.get_selected_langs()
|
||||
langcodes = []
|
||||
for i in application.supported_languages:
|
||||
if i["name"] in langs:
|
||||
langcodes.append(i["code"])
|
||||
d = dict(in_buffer=self.buffer.name, word=term, regexp=regexp, in_lang=lang_option, languages=langcodes, if_word_exists=contains, allow_rts=allow_rts, allow_quotes=allow_quotes, allow_replies=allow_replies)
|
||||
if title in self.buffer.session.settings["filters"]:
|
||||
return commonMessageDialogs.existing_filter()
|
||||
self.buffer.session.settings["filters"][title] = d
|
||||
self.buffer.session.settings.write()
|
||||
|
||||
class filterManager(object):
|
||||
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
self.dialog = filterDialogs.filterManagerDialog()
|
||||
self.insert_filters(self.session.settings["filters"])
|
||||
if self.dialog.filters.get_count() == 0:
|
||||
self.dialog.edit.Enable(False)
|
||||
self.dialog.delete.Enable(False)
|
||||
else:
|
||||
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_filter)
|
||||
widgetUtils.connect_event(self.dialog.delete, widgetUtils.BUTTON_PRESSED, self.delete_filter)
|
||||
response = self.dialog.get_response()
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
self.dialog = filterDialogs.filterManagerDialog()
|
||||
self.insert_filters(self.session.settings["filters"])
|
||||
if self.dialog.filters.get_count() == 0:
|
||||
self.dialog.edit.Enable(False)
|
||||
self.dialog.delete.Enable(False)
|
||||
else:
|
||||
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_filter)
|
||||
widgetUtils.connect_event(self.dialog.delete, widgetUtils.BUTTON_PRESSED, self.delete_filter)
|
||||
response = self.dialog.get_response()
|
||||
|
||||
def insert_filters(self, filters):
|
||||
self.dialog.filters.clear()
|
||||
for f in list(filters.keys()):
|
||||
filterName = f
|
||||
buffer = filters[f]["in_buffer"]
|
||||
if filters[f]["if_word_exists"] == "True" and filters[f]["word"] != "":
|
||||
filter_by_word = "True"
|
||||
else:
|
||||
filter_by_word = "False"
|
||||
filter_by_lang = ""
|
||||
if filters[f]["in_lang"] != "None":
|
||||
filter_by_lang = "True"
|
||||
b = [f, buffer, filter_by_word, filter_by_lang]
|
||||
self.dialog.filters.insert_item(False, *b)
|
||||
def insert_filters(self, filters):
|
||||
self.dialog.filters.clear()
|
||||
for f in list(filters.keys()):
|
||||
filterName = f
|
||||
buffer = filters[f]["in_buffer"]
|
||||
if filters[f]["if_word_exists"] == "True" and filters[f]["word"] != "":
|
||||
filter_by_word = "True"
|
||||
else:
|
||||
filter_by_word = "False"
|
||||
filter_by_lang = ""
|
||||
if filters[f]["in_lang"] != "None":
|
||||
filter_by_lang = "True"
|
||||
b = [f, buffer, filter_by_word, filter_by_lang]
|
||||
self.dialog.filters.insert_item(False, *b)
|
||||
|
||||
def edit_filter(self, *args, **kwargs):
|
||||
pass
|
||||
def edit_filter(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def delete_filter(self, *args, **kwargs):
|
||||
filter_title = self.dialog.filters.get_text_column(self.dialog.filters.get_selected(), 0)
|
||||
response = commonMessageDialogs.delete_filter()
|
||||
if response == widgetUtils.YES:
|
||||
self.session.settings["filters"].pop(filter_title)
|
||||
self.session.settings.write()
|
||||
self.insert_filters(self.session.settings["filters"])
|
||||
def delete_filter(self, *args, **kwargs):
|
||||
filter_title = self.dialog.filters.get_text_column(self.dialog.filters.get_selected(), 0)
|
||||
response = commonMessageDialogs.delete_filter()
|
||||
if response == widgetUtils.YES:
|
||||
self.session.settings["filters"].pop(filter_title)
|
||||
self.session.settings.write()
|
||||
self.insert_filters(self.session.settings["filters"])
|
||||
|
@@ -1,108 +1,114 @@
|
||||
# -*- 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__()
|
||||
self.session = session
|
||||
if user == None:
|
||||
self.dialog = lists.listViewer()
|
||||
self.dialog.populate_list(self.get_all_lists())
|
||||
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.create_list)
|
||||
widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list)
|
||||
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
|
||||
widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer)
|
||||
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
|
||||
else:
|
||||
self.dialog = lists.userListViewer(user)
|
||||
self.dialog.populate_list(self.get_user_lists(user))
|
||||
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.subscribe)
|
||||
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.unsubscribe)
|
||||
self.dialog.get_response()
|
||||
def __init__(self, session, user=None):
|
||||
super(listsController, self).__init__()
|
||||
self.session = session
|
||||
if user == None:
|
||||
self.dialog = lists.listViewer()
|
||||
self.dialog.populate_list(self.get_all_lists())
|
||||
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.create_list)
|
||||
widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list)
|
||||
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
|
||||
widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer)
|
||||
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
|
||||
else:
|
||||
self.dialog = lists.userListViewer(user)
|
||||
self.dialog.populate_list(self.get_user_lists(user))
|
||||
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.subscribe)
|
||||
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.unsubscribe)
|
||||
self.dialog.get_response()
|
||||
|
||||
def get_all_lists(self):
|
||||
return [compose.compose_list(item) for item in self.session.db["lists"]]
|
||||
def get_all_lists(self):
|
||||
return [compose.compose_list(item) for item in self.session.db["lists"]]
|
||||
|
||||
def get_user_lists(self, user):
|
||||
self.lists = self.session.twitter.show_lists(reverse=True, screen_name=user)
|
||||
return [compose.compose_list(item) for item in self.lists]
|
||||
def get_user_lists(self, 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):
|
||||
dialog = lists.createListDialog()
|
||||
if dialog.get_response() == widgetUtils.OK:
|
||||
name = dialog.get("name")
|
||||
description = dialog.get("description")
|
||||
p = dialog.get("public")
|
||||
if p == True:
|
||||
mode = "public"
|
||||
else:
|
||||
mode = "private"
|
||||
try:
|
||||
new_list = self.session.twitter.create_list(name=name, description=description, mode=mode)
|
||||
self.session.db["lists"].append(new_list)
|
||||
self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.status_code, e.msg))
|
||||
dialog.destroy()
|
||||
def create_list(self, *args, **kwargs):
|
||||
dialog = lists.createListDialog()
|
||||
if dialog.get_response() == widgetUtils.OK:
|
||||
name = dialog.get("name")
|
||||
description = dialog.get("description")
|
||||
p = dialog.get("public")
|
||||
if p == True:
|
||||
mode = "public"
|
||||
else:
|
||||
mode = "private"
|
||||
try:
|
||||
new_list = self.session.twitter.create_list(name=name, description=description, mode=mode)
|
||||
self.session.db["lists"].append(new_list)
|
||||
self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
dialog.destroy()
|
||||
|
||||
def edit_list(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()]
|
||||
dialog = lists.editListDialog(list)
|
||||
if dialog.get_response() == widgetUtils.OK:
|
||||
name = dialog.get("name")
|
||||
description = dialog.get("description")
|
||||
p = dialog.get("public")
|
||||
if p == True:
|
||||
mode = "public"
|
||||
else:
|
||||
mode = "private"
|
||||
try:
|
||||
self.session.twitter.update_list(list_id=list["id"], name=name, description=description, mode=mode)
|
||||
self.session.get_lists()
|
||||
self.dialog.populate_list(self.get_all_lists(), True)
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.error_code, e.msg))
|
||||
dialog.destroy()
|
||||
def edit_list(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()]
|
||||
dialog = lists.editListDialog(list)
|
||||
if dialog.get_response() == widgetUtils.OK:
|
||||
name = dialog.get("name")
|
||||
description = dialog.get("description")
|
||||
p = dialog.get("public")
|
||||
if p == True:
|
||||
mode = "public"
|
||||
else:
|
||||
mode = "private"
|
||||
try:
|
||||
self.session.twitter.update_list(list_id=list.id, name=name, description=description, mode=mode)
|
||||
self.session.get_lists()
|
||||
self.dialog.populate_list(self.get_all_lists(), True)
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
dialog.destroy()
|
||||
|
||||
def remove_list(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()]["id"]
|
||||
if lists.remove_list() == widgetUtils.YES:
|
||||
try:
|
||||
self.session.twitter.delete_list(list_id=list)
|
||||
self.session.db["lists"].pop(self.dialog.get_item())
|
||||
self.dialog.lista.remove_item(self.dialog.get_item())
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.error_code, e.msg))
|
||||
def remove_list(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()].id
|
||||
if lists.remove_list() == widgetUtils.YES:
|
||||
try:
|
||||
self.session.twitter.destroy_list(list_id=list)
|
||||
self.session.db["lists"].pop(self.dialog.get_item())
|
||||
self.dialog.lista.remove_item(self.dialog.get_item())
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
|
||||
def open_list_as_buffer(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()]
|
||||
pub.sendMessage("create-new-buffer", buffer="list", account=self.session.db["user_name"], create=list["name"])
|
||||
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)
|
||||
|
||||
def subscribe(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list_id = self.lists[self.dialog.get_item()]["id"]
|
||||
try:
|
||||
list = self.session.twitter.subscribe_to_list(list_id=list_id)
|
||||
item = utils.find_item(list["id"], self.session.db["lists"])
|
||||
self.session.db["lists"].append(list)
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.status_code, e.msg))
|
||||
def subscribe(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list_id = self.lists[self.dialog.get_item()].id
|
||||
try:
|
||||
list = self.session.twitter.subscribe_list(list_id=list_id)
|
||||
item = utils.find_item(list.id, self.session.db["lists"])
|
||||
self.session.db["lists"].append(list)
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
|
||||
def unsubscribe(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list_id = self.lists[self.dialog.get_item()]["id"]
|
||||
try:
|
||||
list = self.session.twitter.unsubscribe_from_list(list_id=list_id)
|
||||
self.session.db["lists"].remove(list)
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.status_code, e.msg))
|
||||
def unsubscribe(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list_id = self.lists[self.dialog.get_item()].id
|
||||
try:
|
||||
list = self.session.twitter.unsubscribe_list(list_id=list_id)
|
||||
self.session.db["lists"].remove(list)
|
||||
except TweepyException as e:
|
||||
output.speak("error %s" % (str(e)))
|
||||
log.exception("error %s" % (str(e)))
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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,271 +10,292 @@ 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
|
||||
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 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."""
|
||||
def __init__(self, session, title, caption, text, messageType="tweet", max=280, *args, **kwargs):
|
||||
super(basicTweet, self).__init__()
|
||||
self.max = max
|
||||
self.title = title
|
||||
self.session = session
|
||||
self.message = getattr(message, messageType)(title, caption, text, *args, **kwargs)
|
||||
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
||||
widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach)
|
||||
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
|
||||
widgetUtils.connect_event(self.message.shortenButton, widgetUtils.BUTTON_PRESSED, self.shorten)
|
||||
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
|
||||
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
||||
if hasattr(self.message, "long_tweet"):
|
||||
widgetUtils.connect_event(self.message.long_tweet, widgetUtils.CHECKBOX, self.text_processor)
|
||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||
self.message.long_tweet.SetValue(config.app["app-settings"]["longtweet"])
|
||||
self.attachments = []
|
||||
""" This class handles the tweet main features. Other classes should derive from this class."""
|
||||
def __init__(self, session, title, caption, text, messageType="tweet", max=280, *args, **kwargs):
|
||||
super(basicTweet, self).__init__()
|
||||
self.max = max
|
||||
self.title = title
|
||||
self.session = session
|
||||
self.message = getattr(message, messageType)(title, caption, text, *args, **kwargs)
|
||||
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
||||
widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach)
|
||||
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
|
||||
widgetUtils.connect_event(self.message.shortenButton, widgetUtils.BUTTON_PRESSED, self.shorten)
|
||||
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
|
||||
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
||||
if hasattr(self.message, "long_tweet"):
|
||||
widgetUtils.connect_event(self.message.long_tweet, widgetUtils.CHECKBOX, self.text_processor)
|
||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||
self.message.long_tweet.SetValue(config.app["app-settings"]["longtweet"])
|
||||
self.attachments = []
|
||||
|
||||
def translate(self, event=None):
|
||||
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)
|
||||
self.message.set_text(msg)
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
output.speak(_(u"Translated"))
|
||||
else:
|
||||
return
|
||||
def translate(self, event=None):
|
||||
dlg = translator.gui.translateDialog()
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
text_to_translate = self.message.get_text()
|
||||
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()
|
||||
output.speak(_(u"Translated"))
|
||||
else:
|
||||
return
|
||||
|
||||
def shorten(self, event=None):
|
||||
urls = utils.find_urls_in_text(self.message.get_text())
|
||||
if len(urls) == 0:
|
||||
output.speak(_(u"There's no URL to be shortened"))
|
||||
self.message.text_focus()
|
||||
elif len(urls) == 1:
|
||||
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.shorten(urls[0])))
|
||||
output.speak(_(u"URL shortened"))
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
elif len(urls) > 1:
|
||||
list_urls = urlList.urlList()
|
||||
list_urls.populate_list(urls)
|
||||
if list_urls.get_response() == widgetUtils.OK:
|
||||
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.shorten(list_urls.get_string())))
|
||||
output.speak(_(u"URL shortened"))
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
def shorten(self, event=None):
|
||||
urls = utils.find_urls_in_text(self.message.get_text())
|
||||
if len(urls) == 0:
|
||||
output.speak(_(u"There's no URL to be shortened"))
|
||||
self.message.text_focus()
|
||||
elif len(urls) == 1:
|
||||
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.shorten(urls[0])))
|
||||
output.speak(_(u"URL shortened"))
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
elif len(urls) > 1:
|
||||
list_urls = urlList.urlList()
|
||||
list_urls.populate_list(urls)
|
||||
if list_urls.get_response() == widgetUtils.OK:
|
||||
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.shorten(list_urls.get_string())))
|
||||
output.speak(_(u"URL shortened"))
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
|
||||
def unshorten(self, event=None):
|
||||
urls = utils.find_urls_in_text(self.message.get_text())
|
||||
if len(urls) == 0:
|
||||
output.speak(_(u"There's no URL to be expanded"))
|
||||
self.message.text_focus()
|
||||
elif len(urls) == 1:
|
||||
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.unshorten(urls[0])))
|
||||
output.speak(_(u"URL expanded"))
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
elif len(urls) > 1:
|
||||
list_urls = urlList.urlList()
|
||||
list_urls.populate_list(urls)
|
||||
if list_urls.get_response() == widgetUtils.OK:
|
||||
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.unshorten(list_urls.get_string())))
|
||||
output.speak(_(u"URL expanded"))
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
def unshorten(self, event=None):
|
||||
urls = utils.find_urls_in_text(self.message.get_text())
|
||||
if len(urls) == 0:
|
||||
output.speak(_(u"There's no URL to be expanded"))
|
||||
self.message.text_focus()
|
||||
elif len(urls) == 1:
|
||||
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.unshorten(urls[0])))
|
||||
output.speak(_(u"URL expanded"))
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
elif len(urls) > 1:
|
||||
list_urls = urlList.urlList()
|
||||
list_urls.populate_list(urls)
|
||||
if list_urls.get_response() == widgetUtils.OK:
|
||||
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.unshorten(list_urls.get_string())))
|
||||
output.speak(_(u"URL expanded"))
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
|
||||
def text_processor(self, *args, **kwargs):
|
||||
if len(self.message.get_text()) > 1:
|
||||
self.message.enable_button("shortenButton")
|
||||
self.message.enable_button("unshortenButton")
|
||||
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:
|
||||
self.session.sound.play("max_length.ogg")
|
||||
else:
|
||||
self.message.set_title(_(u"%s - %s characters") % (self.title, len(self.message.get_text())))
|
||||
def text_processor(self, *args, **kwargs):
|
||||
if len(self.message.get_text()) > 1:
|
||||
self.message.enable_button("shortenButton")
|
||||
self.message.enable_button("unshortenButton")
|
||||
else:
|
||||
self.message.disable_button("shortenButton")
|
||||
self.message.disable_button("unshortenButton")
|
||||
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())))
|
||||
|
||||
def spellcheck(self, event=None):
|
||||
text = self.message.get_text()
|
||||
checker = SpellChecker.spellchecker.spellChecker(text, "")
|
||||
if hasattr(checker, "fixed_text"):
|
||||
self.message.set_text(checker.fixed_text)
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
def spellcheck(self, event=None):
|
||||
text = self.message.get_text()
|
||||
checker = SpellChecker.spellchecker.spellChecker(text, "")
|
||||
if hasattr(checker, "fixed_text"):
|
||||
self.message.set_text(checker.fixed_text)
|
||||
self.text_processor()
|
||||
self.message.text_focus()
|
||||
|
||||
def attach(self, *args, **kwargs):
|
||||
def completed_callback(dlg):
|
||||
url = dlg.uploaderFunction.get_url()
|
||||
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
|
||||
dlg.uploaderDialog.destroy()
|
||||
if "sndup.net/" in url:
|
||||
self.message.set_text(self.message.get_text()+url+" #audio")
|
||||
self.text_processor()
|
||||
else:
|
||||
commonMessageDialogs.common_error(url)
|
||||
def attach(self, *args, **kwargs):
|
||||
def completed_callback(dlg):
|
||||
url = dlg.uploaderFunction.get_url()
|
||||
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
|
||||
dlg.uploaderDialog.destroy()
|
||||
if "sndup.net/" in url:
|
||||
self.message.set_text(self.message.get_text()+url+" #audio")
|
||||
self.text_processor()
|
||||
else:
|
||||
commonMessageDialogs.common_error(url)
|
||||
|
||||
dlg.cleanup()
|
||||
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
|
||||
self.message.text_focus()
|
||||
dlg.cleanup()
|
||||
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
|
||||
self.message.text_focus()
|
||||
|
||||
class tweet(basicTweet):
|
||||
def __init__(self, session, title, caption, text, max=280, messageType="tweet", *args, **kwargs):
|
||||
super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs)
|
||||
self.image = None
|
||||
widgetUtils.connect_event(self.message.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
||||
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||
self.text_processor()
|
||||
def __init__(self, session, title, caption, text, max=280, messageType="tweet", *args, **kwargs):
|
||||
super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs)
|
||||
self.image = None
|
||||
widgetUtils.connect_event(self.message.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
||||
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||
self.text_processor()
|
||||
|
||||
def upload_image(self, *args, **kwargs):
|
||||
a = attach.attach()
|
||||
if len(a.attachments) != 0:
|
||||
self.attachments = a.attachments
|
||||
def upload_image(self, *args, **kwargs):
|
||||
a = attach.attach()
|
||||
if len(a.attachments) != 0:
|
||||
self.attachments = a.attachments
|
||||
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
c.show_menu()
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
c.show_menu()
|
||||
|
||||
class reply(tweet):
|
||||
def __init__(self, session, title, caption, text, users=[], ids=[]):
|
||||
super(reply, self).__init__(session, title, caption, text, messageType="reply", users=users)
|
||||
self.ids = ids
|
||||
self.users = users
|
||||
if len(users) > 0:
|
||||
widgetUtils.connect_event(self.message.mentionAll, widgetUtils.CHECKBOX, self.mention_all)
|
||||
self.message.enable_button("mentionAll")
|
||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||
self.message.mentionAll.SetValue(config.app["app-settings"]["mention_all"])
|
||||
self.mention_all()
|
||||
self.message.set_cursor_at_end()
|
||||
self.text_processor()
|
||||
def __init__(self, session, title, caption, text, users=[], ids=[]):
|
||||
super(reply, self).__init__(session, title, caption, text, messageType="reply", users=users)
|
||||
self.ids = ids
|
||||
self.users = users
|
||||
if len(users) > 0:
|
||||
widgetUtils.connect_event(self.message.mentionAll, widgetUtils.CHECKBOX, self.mention_all)
|
||||
self.message.enable_button("mentionAll")
|
||||
if config.app["app-settings"]["remember_mention_and_longtweet"]:
|
||||
self.message.mentionAll.SetValue(config.app["app-settings"]["mention_all"])
|
||||
self.mention_all()
|
||||
self.message.set_cursor_at_end()
|
||||
self.text_processor()
|
||||
|
||||
def mention_all(self, *args, **kwargs):
|
||||
if self.message.mentionAll.GetValue() == True:
|
||||
for i in self.message.checkboxes:
|
||||
i.SetValue(True)
|
||||
i.Hide()
|
||||
else:
|
||||
for i in self.message.checkboxes:
|
||||
i.SetValue(False)
|
||||
i.Show()
|
||||
def mention_all(self, *args, **kwargs):
|
||||
if self.message.mentionAll.GetValue() == True:
|
||||
for i in self.message.checkboxes:
|
||||
i.SetValue(True)
|
||||
i.Hide()
|
||||
else:
|
||||
for i in self.message.checkboxes:
|
||||
i.SetValue(False)
|
||||
i.Show()
|
||||
|
||||
def get_ids(self):
|
||||
excluded_ids = ""
|
||||
for i in range(0, len(self.message.checkboxes)):
|
||||
if self.message.checkboxes[i].GetValue() == False:
|
||||
excluded_ids = excluded_ids + "{0},".format(self.ids[i],)
|
||||
return excluded_ids
|
||||
def get_ids(self):
|
||||
excluded_ids = ""
|
||||
for i in range(0, len(self.message.checkboxes)):
|
||||
if self.message.checkboxes[i].GetValue() == False:
|
||||
excluded_ids = excluded_ids + "{0},".format(self.ids[i],)
|
||||
return excluded_ids
|
||||
|
||||
def get_people(self):
|
||||
people = ""
|
||||
for i in range(0, len(self.message.checkboxes)):
|
||||
if self.message.checkboxes[i].GetValue() == True:
|
||||
people = people + "{0} ".format(self.message.checkboxes[i].GetLabel(),)
|
||||
return people
|
||||
def get_people(self):
|
||||
people = ""
|
||||
for i in range(0, len(self.message.checkboxes)):
|
||||
if self.message.checkboxes[i].GetValue() == True:
|
||||
people = people + "{0} ".format(self.message.checkboxes[i].GetLabel(),)
|
||||
return people
|
||||
|
||||
class dm(basicTweet):
|
||||
def __init__(self, session, title, caption, text):
|
||||
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()
|
||||
def __init__(self, session, title, caption, text):
|
||||
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 autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
c.show_menu("dm")
|
||||
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=""):
|
||||
""" This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event.
|
||||
param tweet: A dictionary that represents a full tweet or a string for non-tweets.
|
||||
param tweetList: If is_tweet is set to True, this could be a list of quoted tweets.
|
||||
param is_tweet: True or false, depending wether the passed object is a tweet or not."""
|
||||
if is_tweet == True:
|
||||
self.title = _(u"Tweet")
|
||||
image_description = []
|
||||
text = ""
|
||||
for i in range(0, len(tweetList)):
|
||||
# tweets with message keys are longer tweets, the message value is the full messaje taken from twishort.
|
||||
if "message" in tweetList[i] 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"])
|
||||
else:
|
||||
text = text + "rt @%s: %s\n" % (tweetList[i]["retweeted_status"]["user"]["screen_name"], tweetList[i][value])
|
||||
else:
|
||||
text = text + " @%s: %s\n" % (tweetList[i]["user"]["screen_name"], tweetList[i][value])
|
||||
# tweets with extended_entities could include image descriptions.
|
||||
if "extended_entities" in tweetList[i] 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 "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||
image_description.append(z["ext_alt_text"])
|
||||
# set rt and likes counters.
|
||||
rt_count = str(tweet["retweet_count"])
|
||||
favs_count = str(tweet["favorite_count"])
|
||||
# Gets the client from where this tweet was made.
|
||||
source = re.sub(r"(?s)<.*?>", "", tweet["source"])
|
||||
original_date = arrow.get(tweet["created_at"], "ddd MMM DD H:m:s Z YYYY", 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:
|
||||
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"])
|
||||
else:
|
||||
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet[value])
|
||||
else:
|
||||
text = 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 "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 "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))
|
||||
[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)
|
||||
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
||||
if self.contain_urls() == True:
|
||||
self.message.enable_button("unshortenButton")
|
||||
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
|
||||
self.message.get_response()
|
||||
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date="", item_url=""):
|
||||
""" This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event.
|
||||
param tweet: A dictionary that represents a full tweet or a string for non-tweets.
|
||||
param tweetList: If is_tweet is set to True, this could be a list of quoted tweets.
|
||||
param is_tweet: True or false, depending wether the passed object is a tweet or not."""
|
||||
if is_tweet == True:
|
||||
self.title = _(u"Tweet")
|
||||
image_description = []
|
||||
text = ""
|
||||
for i in range(0, len(tweetList)):
|
||||
# tweets with message keys are longer tweets, the message value is the full messaje taken from twishort.
|
||||
if hasattr(tweetList[i], "message") and tweetList[i].is_quote_status == False:
|
||||
value = "message"
|
||||
else:
|
||||
value = "full_text"
|
||||
if hasattr(tweetList[i], "retweeted_status") and tweetList[i].is_quote_status == False:
|
||||
if not hasattr(tweetList[i], "message"):
|
||||
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, tweetList[i].retweeted_status.full_text)
|
||||
else:
|
||||
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, getattr(tweetList[i], value))
|
||||
else:
|
||||
text = text + " @%s: %s\n" % (tweetList[i].user.screen_name, getattr(tweetList[i], value))
|
||||
# tweets with extended_entities could include image descriptions.
|
||||
if hasattr(tweetList[i], "extended_entities") and "media" in tweetList[i].extended_entities:
|
||||
for z in tweetList[i].extended_entities["media"]:
|
||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||
image_description.append(z["ext_alt_text"])
|
||||
if hasattr(tweetList[i], "retweeted_status") and hasattr(tweetList[i].retweeted_status, "extended_entities") and "media" in tweetList[i].retweeted_status["extended_entities"]:
|
||||
for z in tweetList[i].retweeted_status.extended_entities["media"]:
|
||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||
image_description.append(z["ext_alt_text"])
|
||||
# set rt and likes counters.
|
||||
rt_count = str(tweet.retweet_count)
|
||||
favs_count = str(tweet.favorite_count)
|
||||
# Gets the client from where this tweet was made.
|
||||
source = tweet.source
|
||||
original_date = arrow.get(tweet.created_at, locale="en")
|
||||
date = original_date.shift(seconds=utc_offset).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
|
||||
if text == "":
|
||||
if hasattr(tweet, "message"):
|
||||
value = "message"
|
||||
else:
|
||||
value = "full_text"
|
||||
if hasattr(tweet, "retweeted_status"):
|
||||
if not hasattr(tweet, "message"):
|
||||
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, tweet.retweeted_status.full_text)
|
||||
else:
|
||||
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, getattr(tweet, value))
|
||||
else:
|
||||
text = getattr(tweet, value)
|
||||
text = self.clear_text(text)
|
||||
if hasattr(tweet, "extended_entities") and "media" in tweet.extended_entities:
|
||||
for z in tweet.extended_entities["media"]:
|
||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||
image_description.append(z["ext_alt_text"])
|
||||
if hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "extended_entities") and "media" in tweet.retweeted_status.extended_entities:
|
||||
for z in tweet.retweeted_status.extended_entities["media"]:
|
||||
if "ext_alt_text" in z and z["ext_alt_text"] != None:
|
||||
image_description.append(z["ext_alt_text"])
|
||||
self.message = message.viewTweet(text, rt_count, favs_count, source, date)
|
||||
results = parse_tweet(text)
|
||||
self.message.set_title(results.weightedLength)
|
||||
[self.message.set_image_description(i) for i in image_description]
|
||||
else:
|
||||
self.title = _(u"View item")
|
||||
text = tweet
|
||||
self.message = 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")
|
||||
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
|
||||
self.message.get_response()
|
||||
|
||||
def contain_urls(self):
|
||||
if len(utils.find_urls_in_text(self.message.get_text())) > 0:
|
||||
return True
|
||||
return False
|
||||
def contain_urls(self):
|
||||
if len(utils.find_urls_in_text(self.message.get_text())) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def clear_text(self, text):
|
||||
urls = utils.find_urls_in_text(text)
|
||||
for i in urls:
|
||||
if "https://twitter.com/" in i:
|
||||
text = text.replace(i, "\n")
|
||||
return text
|
||||
def clear_text(self, text):
|
||||
urls = utils.find_urls_in_text(text)
|
||||
for i in urls:
|
||||
if "https://twitter.com/" in i:
|
||||
text = text.replace(i, "\n")
|
||||
return text
|
||||
|
||||
def share(self, *args, **kwargs):
|
||||
if hasattr(self, "item_url"):
|
||||
output.copy(self.item_url)
|
||||
output.speak(_("Link copied to clipboard."))
|
@@ -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
|
||||
@@ -24,202 +21,206 @@ from collections import OrderedDict
|
||||
from mysc import autostart as autostart_windows
|
||||
|
||||
class globalSettingsController(object):
|
||||
def __init__(self):
|
||||
super(globalSettingsController, self).__init__()
|
||||
self.dialog = configuration.configurationDialog()
|
||||
self.create_config()
|
||||
self.needs_restart = False
|
||||
self.is_started = True
|
||||
def __init__(self):
|
||||
super(globalSettingsController, self).__init__()
|
||||
self.dialog = configuration.configurationDialog()
|
||||
self.create_config()
|
||||
self.needs_restart = False
|
||||
self.is_started = True
|
||||
|
||||
def make_kmmap(self):
|
||||
res={}
|
||||
for i in os.listdir(os.path.join(paths.app_path(), 'keymaps')):
|
||||
if ".keymap" not in i:
|
||||
continue
|
||||
try:
|
||||
res[i[:-7]] =i
|
||||
except:
|
||||
log.exception("Exception while loading keymap " + i)
|
||||
return res
|
||||
def make_kmmap(self):
|
||||
res={}
|
||||
for i in os.listdir(os.path.join(paths.app_path(), 'keymaps')):
|
||||
if ".keymap" not in i:
|
||||
continue
|
||||
try:
|
||||
res[i[:-7]] =i
|
||||
except:
|
||||
log.exception("Exception while loading keymap " + i)
|
||||
return res
|
||||
|
||||
def create_config(self):
|
||||
self.kmmap=self.make_kmmap()
|
||||
self.langs = languageHandler.getAvailableLanguages()
|
||||
langs = []
|
||||
[langs.append(i[1]) for i in self.langs]
|
||||
self.codes = []
|
||||
[self.codes.append(i[0]) for i in self.langs]
|
||||
id = self.codes.index(config.app["app-settings"]["language"])
|
||||
self.kmfriendlies=[]
|
||||
self.kmnames=[]
|
||||
for k,v in list(self.kmmap.items()):
|
||||
self.kmfriendlies.append(k)
|
||||
self.kmnames.append(v)
|
||||
self.kmid=self.kmnames.index(config.app['app-settings']['load_keymap'])
|
||||
self.dialog.create_general(langs,self.kmfriendlies)
|
||||
self.dialog.general.language.SetSelection(id)
|
||||
self.dialog.general.km.SetSelection(self.kmid)
|
||||
if paths.mode == "installed":
|
||||
self.dialog.set_value("general", "autostart", config.app["app-settings"]["autostart"])
|
||||
else:
|
||||
self.dialog.general.autostart.Enable(False)
|
||||
self.dialog.set_value("general", "ask_at_exit", config.app["app-settings"]["ask_at_exit"])
|
||||
self.dialog.set_value("general", "no_streaming", config.app["app-settings"]["no_streaming"])
|
||||
self.dialog.set_value("general", "play_ready_sound", config.app["app-settings"]["play_ready_sound"])
|
||||
self.dialog.set_value("general", "speak_ready_msg", config.app["app-settings"]["speak_ready_msg"])
|
||||
self.dialog.set_value("general", "handle_longtweets", config.app["app-settings"]["handle_longtweets"])
|
||||
self.dialog.set_value("general", "use_invisible_shorcuts", config.app["app-settings"]["use_invisible_keyboard_shorcuts"])
|
||||
self.dialog.set_value("general", "disable_sapi5", config.app["app-settings"]["voice_enabled"])
|
||||
self.dialog.set_value("general", "hide_gui", config.app["app-settings"]["hide_gui"])
|
||||
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:
|
||||
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"])
|
||||
self.dialog.set_value("proxy", "password", config.app["proxy"]["password"])
|
||||
def create_config(self):
|
||||
self.kmmap=self.make_kmmap()
|
||||
self.langs = languageHandler.getAvailableLanguages()
|
||||
langs = []
|
||||
[langs.append(i[1]) for i in self.langs]
|
||||
self.codes = []
|
||||
[self.codes.append(i[0]) for i in self.langs]
|
||||
id = self.codes.index(config.app["app-settings"]["language"])
|
||||
self.kmfriendlies=[]
|
||||
self.kmnames=[]
|
||||
for k,v in list(self.kmmap.items()):
|
||||
self.kmfriendlies.append(k)
|
||||
self.kmnames.append(v)
|
||||
self.kmid=self.kmnames.index(config.app['app-settings']['load_keymap'])
|
||||
self.dialog.create_general(langs,self.kmfriendlies)
|
||||
self.dialog.general.language.SetSelection(id)
|
||||
self.dialog.general.km.SetSelection(self.kmid)
|
||||
if paths.mode == "installed":
|
||||
self.dialog.set_value("general", "autostart", config.app["app-settings"]["autostart"])
|
||||
else:
|
||||
self.dialog.general.autostart.Enable(False)
|
||||
self.dialog.set_value("general", "ask_at_exit", config.app["app-settings"]["ask_at_exit"])
|
||||
self.dialog.set_value("general", "no_streaming", config.app["app-settings"]["no_streaming"])
|
||||
self.dialog.set_value("general", "play_ready_sound", config.app["app-settings"]["play_ready_sound"])
|
||||
self.dialog.set_value("general", "speak_ready_msg", config.app["app-settings"]["speak_ready_msg"])
|
||||
self.dialog.set_value("general", "handle_longtweets", config.app["app-settings"]["handle_longtweets"])
|
||||
self.dialog.set_value("general", "use_invisible_shorcuts", config.app["app-settings"]["use_invisible_keyboard_shorcuts"])
|
||||
self.dialog.set_value("general", "disable_sapi5", config.app["app-settings"]["voice_enabled"])
|
||||
self.dialog.set_value("general", "hide_gui", config.app["app-settings"]["hide_gui"])
|
||||
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 = [_("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)
|
||||
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"])
|
||||
self.dialog.set_value("proxy", "password", config.app["proxy"]["password"])
|
||||
|
||||
self.dialog.realize()
|
||||
self.response = self.dialog.get_response()
|
||||
self.dialog.realize()
|
||||
self.response = self.dialog.get_response()
|
||||
|
||||
def save_configuration(self):
|
||||
if self.codes[self.dialog.general.language.GetSelection()] != config.app["app-settings"]["language"]:
|
||||
config.app["app-settings"]["language"] = self.codes[self.dialog.general.language.GetSelection()]
|
||||
languageHandler.setLanguage(config.app["app-settings"]["language"])
|
||||
self.needs_restart = True
|
||||
if self.kmnames[self.dialog.general.km.GetSelection()] != config.app["app-settings"]["load_keymap"]:
|
||||
config.app["app-settings"]["load_keymap"] =self.kmnames[self.dialog.general.km.GetSelection()]
|
||||
kmFile = open(os.path.join(paths.config_path(), "keymap.keymap"), "w")
|
||||
kmFile.close()
|
||||
self.needs_restart = True
|
||||
if config.app["app-settings"]["autostart"] != self.dialog.get_value("general", "autostart") and paths.mode == "installed":
|
||||
config.app["app-settings"]["autostart"] = self.dialog.get_value("general", "autostart")
|
||||
autostart_windows.setAutoStart(application.name, enable=self.dialog.get_value("general", "autostart"))
|
||||
if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"):
|
||||
config.app["app-settings"]["use_invisible_keyboard_shorcuts"] = self.dialog.get_value("general", "use_invisible_shorcuts")
|
||||
pub.sendMessage("invisible-shorcuts-changed", registered=self.dialog.get_value("general", "use_invisible_shorcuts"))
|
||||
if config.app["app-settings"]["no_streaming"] != self.dialog.get_value("general", "no_streaming"):
|
||||
config.app["app-settings"]["no_streaming"] = self.dialog.get_value("general", "no_streaming")
|
||||
self.needs_restart = True
|
||||
if config.app["app-settings"]["update_period"] != self.dialog.get_value("general", "update_period"):
|
||||
config.app["app-settings"]["update_period"] = self.dialog.get_value("general", "update_period")
|
||||
self.needs_restart = True
|
||||
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
|
||||
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
|
||||
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
|
||||
config.app["app-settings"]["handle_longtweets"] = self.dialog.get_value("general", "handle_longtweets")
|
||||
config.app["app-settings"]["play_ready_sound"] = self.dialog.get_value("general", "play_ready_sound")
|
||||
config.app["app-settings"]["speak_ready_msg"] = self.dialog.get_value("general", "speak_ready_msg")
|
||||
config.app["app-settings"]["check_for_updates"] = self.dialog.get_value("general", "check_for_updates")
|
||||
config.app["app-settings"]["remember_mention_and_longtweet"] = self.dialog.get_value("general", "remember_mention_and_longtweet")
|
||||
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"]["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")
|
||||
config.app["proxy"]["password"] = self.dialog.get_value("proxy", "password")
|
||||
config.app.write()
|
||||
def save_configuration(self):
|
||||
if self.codes[self.dialog.general.language.GetSelection()] != config.app["app-settings"]["language"]:
|
||||
config.app["app-settings"]["language"] = self.codes[self.dialog.general.language.GetSelection()]
|
||||
languageHandler.setLanguage(config.app["app-settings"]["language"])
|
||||
self.needs_restart = True
|
||||
if self.kmnames[self.dialog.general.km.GetSelection()] != config.app["app-settings"]["load_keymap"]:
|
||||
config.app["app-settings"]["load_keymap"] =self.kmnames[self.dialog.general.km.GetSelection()]
|
||||
kmFile = open(os.path.join(paths.config_path(), "keymap.keymap"), "w")
|
||||
kmFile.close()
|
||||
self.needs_restart = True
|
||||
if config.app["app-settings"]["autostart"] != self.dialog.get_value("general", "autostart") and paths.mode == "installed":
|
||||
config.app["app-settings"]["autostart"] = self.dialog.get_value("general", "autostart")
|
||||
autostart_windows.setAutoStart(application.name, enable=self.dialog.get_value("general", "autostart"))
|
||||
if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"):
|
||||
config.app["app-settings"]["use_invisible_keyboard_shorcuts"] = self.dialog.get_value("general", "use_invisible_shorcuts")
|
||||
pub.sendMessage("invisible-shorcuts-changed", registered=self.dialog.get_value("general", "use_invisible_shorcuts"))
|
||||
if config.app["app-settings"]["no_streaming"] != self.dialog.get_value("general", "no_streaming"):
|
||||
config.app["app-settings"]["no_streaming"] = self.dialog.get_value("general", "no_streaming")
|
||||
self.needs_restart = True
|
||||
if config.app["app-settings"]["update_period"] != self.dialog.get_value("general", "update_period"):
|
||||
config.app["app-settings"]["update_period"] = self.dialog.get_value("general", "update_period")
|
||||
self.needs_restart = True
|
||||
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
|
||||
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
|
||||
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
|
||||
config.app["app-settings"]["handle_longtweets"] = self.dialog.get_value("general", "handle_longtweets")
|
||||
config.app["app-settings"]["play_ready_sound"] = self.dialog.get_value("general", "play_ready_sound")
|
||||
config.app["app-settings"]["speak_ready_msg"] = self.dialog.get_value("general", "speak_ready_msg")
|
||||
config.app["app-settings"]["check_for_updates"] = self.dialog.get_value("general", "check_for_updates")
|
||||
config.app["app-settings"]["remember_mention_and_longtweet"] = self.dialog.get_value("general", "remember_mention_and_longtweet")
|
||||
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.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")
|
||||
config.app["proxy"]["password"] = self.dialog.get_value("proxy", "password")
|
||||
config.app.write()
|
||||
|
||||
class accountSettingsController(globalSettingsController):
|
||||
def __init__(self, buffer, window):
|
||||
self.user = buffer.session.db["user_name"]
|
||||
self.buffer = buffer
|
||||
self.window = window
|
||||
self.config = buffer.session.settings
|
||||
super(accountSettingsController, self).__init__()
|
||||
def __init__(self, buffer, window):
|
||||
self.user = buffer.session.db["user_name"]
|
||||
self.buffer = buffer
|
||||
self.window = window
|
||||
self.config = buffer.session.settings
|
||||
super(accountSettingsController, self).__init__()
|
||||
|
||||
def create_config(self):
|
||||
self.dialog.create_general_account()
|
||||
widgetUtils.connect_event(self.dialog.general.au, widgetUtils.BUTTON_PRESSED, self.manage_autocomplete)
|
||||
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
|
||||
self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"])
|
||||
self.dialog.set_value("general", "itemsPerApiCall", self.config["general"]["max_tweets_per_call"])
|
||||
self.dialog.set_value("general", "reverse_timelines", self.config["general"]["reverse_timelines"])
|
||||
rt = self.config["general"]["retweet_mode"]
|
||||
if rt == "ask":
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Ask"))
|
||||
elif rt == "direct":
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet without comments"))
|
||||
else:
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
|
||||
self.dialog.set_value("general", "persist_size", str(self.config["general"]["persist_size"]))
|
||||
self.dialog.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"])
|
||||
self.dialog.create_other_buffers()
|
||||
buffer_values = self.get_buffers_list()
|
||||
self.dialog.buffers.insert_buffers(buffer_values)
|
||||
self.dialog.buffers.connect_hook_func(self.toggle_buffer_active)
|
||||
widgetUtils.connect_event(self.dialog.buffers.toggle_state, widgetUtils.BUTTON_PRESSED, self.toggle_state)
|
||||
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
|
||||
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
|
||||
def create_config(self):
|
||||
self.dialog.create_general_account()
|
||||
widgetUtils.connect_event(self.dialog.general.au, widgetUtils.BUTTON_PRESSED, self.manage_autocomplete)
|
||||
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
|
||||
self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"])
|
||||
self.dialog.set_value("general", "itemsPerApiCall", self.config["general"]["max_tweets_per_call"])
|
||||
self.dialog.set_value("general", "reverse_timelines", self.config["general"]["reverse_timelines"])
|
||||
rt = self.config["general"]["retweet_mode"]
|
||||
if rt == "ask":
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Ask"))
|
||||
elif rt == "direct":
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet without comments"))
|
||||
else:
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
|
||||
self.dialog.set_value("general", "persist_size", str(self.config["general"]["persist_size"]))
|
||||
self.dialog.set_value("general", "load_cache_in_memory", self.config["general"]["load_cache_in_memory"])
|
||||
self.dialog.create_reporting()
|
||||
self.dialog.set_value("reporting", "speech_reporting", self.config["reporting"]["speech_reporting"])
|
||||
self.dialog.set_value("reporting", "braille_reporting", self.config["reporting"]["braille_reporting"])
|
||||
self.dialog.create_other_buffers()
|
||||
buffer_values = self.get_buffers_list()
|
||||
self.dialog.buffers.insert_buffers(buffer_values)
|
||||
self.dialog.buffers.connect_hook_func(self.toggle_buffer_active)
|
||||
widgetUtils.connect_event(self.dialog.buffers.toggle_state, widgetUtils.BUTTON_PRESSED, self.toggle_state)
|
||||
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
|
||||
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
|
||||
|
||||
|
||||
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
|
||||
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
|
||||
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
|
||||
self.input_devices = sound_lib.input.Input.get_device_names()
|
||||
self.output_devices = sound_lib.output.Output.get_device_names()
|
||||
self.soundpacks = []
|
||||
[self.soundpacks.append(i) for i in os.listdir(paths.sound_path()) if os.path.isdir(os.path.join(paths.sound_path(), i)) == True ]
|
||||
self.dialog.create_sound(self.input_devices, self.output_devices, self.soundpacks)
|
||||
self.dialog.set_value("sound", "volumeCtrl", self.config["sound"]["volume"]*100)
|
||||
self.dialog.set_value("sound", "input", self.config["sound"]["input_device"])
|
||||
self.dialog.set_value("sound", "output", self.config["sound"]["output_device"])
|
||||
self.dialog.set_value("sound", "session_mute", self.config["sound"]["session_mute"])
|
||||
self.dialog.set_value("sound", "soundpack", self.config["sound"]["current_soundpack"])
|
||||
self.dialog.set_value("sound", "indicate_audio", self.config["sound"]["indicate_audio"])
|
||||
self.dialog.set_value("sound", "indicate_geo", self.config["sound"]["indicate_geo"])
|
||||
self.dialog.set_value("sound", "indicate_img", self.config["sound"]["indicate_img"])
|
||||
self.dialog.create_extras(OCRSpace.translatable_langs)
|
||||
self.dialog.set_value("extras", "sndup_apiKey", self.config["sound"]["sndup_api_key"])
|
||||
language_index = OCRSpace.OcrLangs.index(self.config["mysc"]["ocr_language"])
|
||||
self.dialog.extras.ocr_lang.SetSelection(language_index)
|
||||
self.dialog.realize()
|
||||
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
|
||||
self.response = self.dialog.get_response()
|
||||
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
|
||||
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
|
||||
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
|
||||
self.input_devices = sound_lib.input.Input.get_device_names()
|
||||
self.output_devices = sound_lib.output.Output.get_device_names()
|
||||
self.soundpacks = []
|
||||
[self.soundpacks.append(i) for i in os.listdir(paths.sound_path()) if os.path.isdir(os.path.join(paths.sound_path(), i)) == True ]
|
||||
self.dialog.create_sound(self.input_devices, self.output_devices, self.soundpacks)
|
||||
self.dialog.set_value("sound", "volumeCtrl", self.config["sound"]["volume"]*100)
|
||||
self.dialog.set_value("sound", "input", self.config["sound"]["input_device"])
|
||||
self.dialog.set_value("sound", "output", self.config["sound"]["output_device"])
|
||||
self.dialog.set_value("sound", "session_mute", self.config["sound"]["session_mute"])
|
||||
self.dialog.set_value("sound", "soundpack", self.config["sound"]["current_soundpack"])
|
||||
self.dialog.set_value("sound", "indicate_audio", self.config["sound"]["indicate_audio"])
|
||||
self.dialog.set_value("sound", "indicate_geo", self.config["sound"]["indicate_geo"])
|
||||
self.dialog.set_value("sound", "indicate_img", self.config["sound"]["indicate_img"])
|
||||
self.dialog.create_extras(OCRSpace.translatable_langs)
|
||||
self.dialog.set_value("extras", "sndup_apiKey", self.config["sound"]["sndup_api_key"])
|
||||
language_index = OCRSpace.OcrLangs.index(self.config["mysc"]["ocr_language"])
|
||||
self.dialog.extras.ocr_lang.SetSelection(language_index)
|
||||
self.dialog.realize()
|
||||
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
|
||||
self.response = self.dialog.get_response()
|
||||
|
||||
def save_configuration(self):
|
||||
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
|
||||
self.needs_restart = True
|
||||
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
|
||||
self.config["general"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names")
|
||||
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
|
||||
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
|
||||
if self.dialog.get_value("general", "persist_size") == '':
|
||||
self.config["general"]["persist_size"] =-1
|
||||
else:
|
||||
try:
|
||||
self.config["general"]["persist_size"] = int(self.dialog.get_value("general", "persist_size"))
|
||||
except ValueError:
|
||||
output.speak("Invalid cache size, setting to default.",True)
|
||||
self.config["general"]["persist_size"] =1764
|
||||
def save_configuration(self):
|
||||
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
|
||||
self.needs_restart = True
|
||||
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
|
||||
self.config["general"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names")
|
||||
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
|
||||
else:
|
||||
try:
|
||||
self.config["general"]["persist_size"] = int(self.dialog.get_value("general", "persist_size"))
|
||||
except ValueError:
|
||||
output.speak("Invalid cache size, setting to default.",True)
|
||||
self.config["general"]["persist_size"] =1764
|
||||
|
||||
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
|
||||
self.needs_restart = True
|
||||
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
|
||||
rt = self.dialog.get_value("general", "retweet_mode")
|
||||
if rt == _(u"Ask"):
|
||||
self.config["general"]["retweet_mode"] = "ask"
|
||||
elif rt == _(u"Retweet without comments"):
|
||||
self.config["general"]["retweet_mode"] = "direct"
|
||||
else:
|
||||
self.config["general"]["retweet_mode"] = "comment"
|
||||
buffers_list = self.dialog.buffers.get_list()
|
||||
if buffers_list != self.config["general"]["buffer_order"]:
|
||||
self.needs_restart = True
|
||||
self.config["general"]["buffer_order"] = buffers_list
|
||||
self.config["reporting"]["speech_reporting"] = self.dialog.get_value("reporting", "speech_reporting")
|
||||
self.config["reporting"]["braille_reporting"] = self.dialog.get_value("reporting", "braille_reporting")
|
||||
self.config["mysc"]["ocr_language"] = OCRSpace.OcrLangs[self.dialog.extras.ocr_lang.GetSelection()]
|
||||
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
|
||||
self.needs_restart = True
|
||||
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
|
||||
rt = self.dialog.get_value("general", "retweet_mode")
|
||||
if rt == _(u"Ask"):
|
||||
self.config["general"]["retweet_mode"] = "ask"
|
||||
elif rt == _(u"Retweet without comments"):
|
||||
self.config["general"]["retweet_mode"] = "direct"
|
||||
else:
|
||||
self.config["general"]["retweet_mode"] = "comment"
|
||||
buffers_list = self.dialog.buffers.get_list()
|
||||
if buffers_list != self.config["general"]["buffer_order"]:
|
||||
self.needs_restart = True
|
||||
self.config["general"]["buffer_order"] = buffers_list
|
||||
self.config["reporting"]["speech_reporting"] = self.dialog.get_value("reporting", "speech_reporting")
|
||||
self.config["reporting"]["braille_reporting"] = self.dialog.get_value("reporting", "braille_reporting")
|
||||
self.config["mysc"]["ocr_language"] = OCRSpace.OcrLangs[self.dialog.extras.ocr_lang.GetSelection()]
|
||||
# if self.config["other_buffers"]["show_followers"] != self.dialog.get_value("buffers", "followers"):
|
||||
# self.config["other_buffers"]["show_followers"] = self.dialog.get_value("buffers", "followers")
|
||||
# pub.sendMessage("create-new-buffer", buffer="followers", account=self.user, create=self.config["other_buffers"]["show_followers"])
|
||||
@@ -238,75 +239,75 @@ class accountSettingsController(globalSettingsController):
|
||||
# if self.config["other_buffers"]["show_events"] != self.dialog.get_value("buffers", "events"):
|
||||
# self.config["other_buffers"]["show_events"] = self.dialog.get_value("buffers", "events")
|
||||
# pub.sendMessage("create-new-buffer", buffer="events", account=self.user, create=self.config["other_buffers"]["show_events"])
|
||||
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
|
||||
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
|
||||
try:
|
||||
self.buffer.session.sound.input.set_device(self.buffer.session.sound.input.find_device_by_name(self.config["sound"]["input_device"]))
|
||||
except:
|
||||
self.config["sound"]["input_device"] = "default"
|
||||
if self.config["sound"]["output_device"] != self.dialog.sound.get("output"):
|
||||
self.config["sound"]["output_device"] = self.dialog.sound.get("output")
|
||||
try:
|
||||
self.buffer.session.sound.output.set_device(self.buffer.session.sound.output.find_device_by_name(self.config["sound"]["output_device"]))
|
||||
except:
|
||||
self.config["sound"]["output_device"] = "default"
|
||||
self.config["sound"]["volume"] = self.dialog.get_value("sound", "volumeCtrl")/100.0
|
||||
self.config["sound"]["session_mute"] = self.dialog.get_value("sound", "session_mute")
|
||||
self.config["sound"]["current_soundpack"] = self.dialog.sound.get("soundpack")
|
||||
self.config["sound"]["indicate_audio"] = self.dialog.get_value("sound", "indicate_audio")
|
||||
self.config["sound"]["indicate_geo"] = self.dialog.get_value("sound", "indicate_geo")
|
||||
self.config["sound"]["indicate_img"] = self.dialog.get_value("sound", "indicate_img")
|
||||
self.config["sound"]["sndup_api_key"] = self.dialog.get_value("extras", "sndup_apiKey")
|
||||
self.buffer.session.sound.config = self.config["sound"]
|
||||
self.buffer.session.sound.check_soundpack()
|
||||
self.config.write()
|
||||
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
|
||||
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
|
||||
try:
|
||||
self.buffer.session.sound.input.set_device(self.buffer.session.sound.input.find_device_by_name(self.config["sound"]["input_device"]))
|
||||
except:
|
||||
self.config["sound"]["input_device"] = "default"
|
||||
if self.config["sound"]["output_device"] != self.dialog.sound.get("output"):
|
||||
self.config["sound"]["output_device"] = self.dialog.sound.get("output")
|
||||
try:
|
||||
self.buffer.session.sound.output.set_device(self.buffer.session.sound.output.find_device_by_name(self.config["sound"]["output_device"]))
|
||||
except:
|
||||
self.config["sound"]["output_device"] = "default"
|
||||
self.config["sound"]["volume"] = self.dialog.get_value("sound", "volumeCtrl")/100.0
|
||||
self.config["sound"]["session_mute"] = self.dialog.get_value("sound", "session_mute")
|
||||
self.config["sound"]["current_soundpack"] = self.dialog.sound.get("soundpack")
|
||||
self.config["sound"]["indicate_audio"] = self.dialog.get_value("sound", "indicate_audio")
|
||||
self.config["sound"]["indicate_geo"] = self.dialog.get_value("sound", "indicate_geo")
|
||||
self.config["sound"]["indicate_img"] = self.dialog.get_value("sound", "indicate_img")
|
||||
self.config["sound"]["sndup_api_key"] = self.dialog.get_value("extras", "sndup_apiKey")
|
||||
self.buffer.session.sound.config = self.config["sound"]
|
||||
self.buffer.session.sound.check_soundpack()
|
||||
self.config.write()
|
||||
|
||||
def toggle_state(self,*args,**kwargs):
|
||||
return self.dialog.buffers.change_selected_item()
|
||||
def toggle_state(self,*args,**kwargs):
|
||||
return self.dialog.buffers.change_selected_item()
|
||||
|
||||
def manage_autocomplete(self, *args, **kwargs):
|
||||
configuration = settings.autocompletionSettings(self.buffer.session.settings, self.buffer, self.window)
|
||||
def manage_autocomplete(self, *args, **kwargs):
|
||||
configuration = settings.autocompletionSettings(self.buffer.session.settings, self.buffer, self.window)
|
||||
|
||||
def add_ignored_client(self, *args, **kwargs):
|
||||
client = commonMessageDialogs.get_ignored_client()
|
||||
if client == None: return
|
||||
if client not in self.config["twitter"]["ignored_clients"]:
|
||||
self.config["twitter"]["ignored_clients"].append(client)
|
||||
self.dialog.ignored_clients.append(client)
|
||||
def add_ignored_client(self, *args, **kwargs):
|
||||
client = commonMessageDialogs.get_ignored_client()
|
||||
if client == None: return
|
||||
if client not in self.config["twitter"]["ignored_clients"]:
|
||||
self.config["twitter"]["ignored_clients"].append(client)
|
||||
self.dialog.ignored_clients.append(client)
|
||||
|
||||
def remove_ignored_client(self, *args, **kwargs):
|
||||
if self.dialog.ignored_clients.get_clients() == 0: return
|
||||
id = self.dialog.ignored_clients.get_client_id()
|
||||
self.config["twitter"]["ignored_clients"].pop(id)
|
||||
self.dialog.ignored_clients.remove_(id)
|
||||
def remove_ignored_client(self, *args, **kwargs):
|
||||
if self.dialog.ignored_clients.get_clients() == 0: return
|
||||
id = self.dialog.ignored_clients.get_client_id()
|
||||
self.config["twitter"]["ignored_clients"].pop(id)
|
||||
self.dialog.ignored_clients.remove_(id)
|
||||
|
||||
def get_buffers_list(self):
|
||||
all_buffers=OrderedDict()
|
||||
all_buffers['home']=_(u"Home")
|
||||
all_buffers['mentions']=_(u"Mentions")
|
||||
all_buffers['dm']=_(u"Direct Messages")
|
||||
all_buffers['sent_dm']=_(u"Sent direct messages")
|
||||
all_buffers['sent_tweets']=_(u"Sent tweets")
|
||||
all_buffers['favorites']=_(u"Likes")
|
||||
all_buffers['followers']=_(u"Followers")
|
||||
all_buffers['friends']=_(u"Friends")
|
||||
all_buffers['blocks']=_(u"Blocked users")
|
||||
all_buffers['muted']=_(u"Muted users")
|
||||
list_buffers = []
|
||||
hidden_buffers=[]
|
||||
all_buffers_keys = list(all_buffers.keys())
|
||||
# Check buffers shown first.
|
||||
for i in self.config["general"]["buffer_order"]:
|
||||
if i in all_buffers_keys:
|
||||
list_buffers.append((i, all_buffers[i], True))
|
||||
# This second pass will retrieve all hidden buffers.
|
||||
for i in all_buffers_keys:
|
||||
if i not in self.config["general"]["buffer_order"]:
|
||||
hidden_buffers.append((i, all_buffers[i], False))
|
||||
list_buffers.extend(hidden_buffers)
|
||||
return list_buffers
|
||||
def get_buffers_list(self):
|
||||
all_buffers=OrderedDict()
|
||||
all_buffers['home']=_(u"Home")
|
||||
all_buffers['mentions']=_(u"Mentions")
|
||||
all_buffers['dm']=_(u"Direct Messages")
|
||||
all_buffers['sent_dm']=_(u"Sent direct messages")
|
||||
all_buffers['sent_tweets']=_(u"Sent tweets")
|
||||
all_buffers['favorites']=_(u"Likes")
|
||||
all_buffers['followers']=_(u"Followers")
|
||||
all_buffers['friends']=_(u"Friends")
|
||||
all_buffers['blocks']=_(u"Blocked users")
|
||||
all_buffers['muted']=_(u"Muted users")
|
||||
list_buffers = []
|
||||
hidden_buffers=[]
|
||||
all_buffers_keys = list(all_buffers.keys())
|
||||
# Check buffers shown first.
|
||||
for i in self.config["general"]["buffer_order"]:
|
||||
if i in all_buffers_keys:
|
||||
list_buffers.append((i, all_buffers[i], True))
|
||||
# This second pass will retrieve all hidden buffers.
|
||||
for i in all_buffers_keys:
|
||||
if i not in self.config["general"]["buffer_order"]:
|
||||
hidden_buffers.append((i, all_buffers[i], False))
|
||||
list_buffers.extend(hidden_buffers)
|
||||
return list_buffers
|
||||
|
||||
def toggle_buffer_active(self, ev):
|
||||
change = self.dialog.buffers.get_event(ev)
|
||||
if change == True:
|
||||
self.dialog.buffers.change_selected_item()
|
||||
def toggle_buffer_active(self, ev):
|
||||
change = self.dialog.buffers.get_event(ev)
|
||||
if change == True:
|
||||
self.dialog.buffers.change_selected_item()
|
||||
|
@@ -1,47 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
from wxUI.dialogs import trends
|
||||
import widgetUtils
|
||||
|
||||
class trendingTopicsController(object):
|
||||
def __init__(self, session):
|
||||
super(trendingTopicsController, self).__init__()
|
||||
self.countries = {}
|
||||
self.cities = {}
|
||||
self.dialog = trends.trendingTopicsDialog()
|
||||
self.information = session.twitter.get_available_trends()
|
||||
self.split_information()
|
||||
widgetUtils.connect_event(self.dialog.country, widgetUtils.RADIOBUTTON, self.get_places)
|
||||
widgetUtils.connect_event(self.dialog.city, widgetUtils.RADIOBUTTON, self.get_places)
|
||||
self.get_places()
|
||||
def __init__(self, session):
|
||||
super(trendingTopicsController, self).__init__()
|
||||
self.countries = {}
|
||||
self.cities = {}
|
||||
self.dialog = trends.trendingTopicsDialog()
|
||||
self.information = session.twitter.available_trends()
|
||||
self.split_information()
|
||||
widgetUtils.connect_event(self.dialog.country, widgetUtils.RADIOBUTTON, self.get_places)
|
||||
widgetUtils.connect_event(self.dialog.city, widgetUtils.RADIOBUTTON, self.get_places)
|
||||
self.get_places()
|
||||
|
||||
def split_information(self):
|
||||
for i in self.information:
|
||||
if i["placeType"]["name"] == "Country":
|
||||
self.countries[i["name"]] = i["woeid"]
|
||||
else:
|
||||
self.cities[i["name"]] = i["woeid"]
|
||||
def split_information(self):
|
||||
for i in self.information:
|
||||
if i["placeType"]["name"] == "Country":
|
||||
self.countries[i["name"]] = i["woeid"]
|
||||
else:
|
||||
self.cities[i["name"]] = i["woeid"]
|
||||
|
||||
def get_places(self, event=None):
|
||||
values = []
|
||||
if self.dialog.get_active() == "country":
|
||||
for i in self.information:
|
||||
if i["placeType"]["name"] == "Country":
|
||||
values.append(i["name"])
|
||||
elif self.dialog.get_active() == "city":
|
||||
for i in self.information:
|
||||
if i["placeType"]["name"] != "Country":
|
||||
values.append(i["name"])
|
||||
self.dialog.set(values)
|
||||
def get_places(self, event=None):
|
||||
values = []
|
||||
if self.dialog.get_active() == "country":
|
||||
for i in self.information:
|
||||
if i["placeType"]["name"] == "Country":
|
||||
values.append(i["name"])
|
||||
elif self.dialog.get_active() == "city":
|
||||
for i in self.information:
|
||||
if i["placeType"]["name"] != "Country":
|
||||
values.append(i["name"])
|
||||
self.dialog.set(values)
|
||||
|
||||
def get_woeid(self):
|
||||
selected = self.dialog.get_item()
|
||||
if self.dialog.get_active() == "country":
|
||||
woeid = self.countries[selected]
|
||||
else:
|
||||
woeid = self.cities[selected]
|
||||
return woeid
|
||||
def get_woeid(self):
|
||||
selected = self.dialog.get_item()
|
||||
if self.dialog.get_active() == "country":
|
||||
woeid = self.countries[selected]
|
||||
else:
|
||||
woeid = self.cities[selected]
|
||||
return woeid
|
||||
|
||||
def get_string(self):
|
||||
return self.dialog.get_item()
|
||||
def get_string(self):
|
||||
return self.dialog.get_item()
|
||||
|
@@ -1,6 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
import wx
|
||||
import webbrowser
|
||||
import widgetUtils
|
||||
@@ -8,121 +6,123 @@ 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):
|
||||
super(profileController, self).__init__()
|
||||
self.file = None
|
||||
self.session = session
|
||||
self.user = user
|
||||
if user == None:
|
||||
self.get_data(screen_name=self.session.db["user_name"])
|
||||
self.dialog = update_profile.updateProfileDialog()
|
||||
self.fill_profile_fields()
|
||||
self.uploaded = False
|
||||
widgetUtils.connect_event(self.dialog.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
||||
else:
|
||||
try:
|
||||
self.get_data(screen_name=self.user)
|
||||
except TwythonError as err:
|
||||
if err.error_code == 404:
|
||||
wx.MessageDialog(None, _(u"That user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
if err.error_code == 403:
|
||||
wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
log.error("error %d: %s" % (err.error_code, err.msg))
|
||||
return
|
||||
self.dialog = show_user.showUserProfile()
|
||||
string = self.get_user_info()
|
||||
self.dialog.set("text", string)
|
||||
self.dialog.set_title(_(u"Information for %s") % (self.data["screen_name"]))
|
||||
if self.data["url"] != None:
|
||||
self.dialog.enable_url()
|
||||
widgetUtils.connect_event(self.dialog.url, widgetUtils.BUTTON_PRESSED, self.visit_url)
|
||||
if self.dialog.get_response() == widgetUtils.OK and self.user == None:
|
||||
self.do_update()
|
||||
def __init__(self, session, user=None):
|
||||
super(profileController, self).__init__()
|
||||
self.file = None
|
||||
self.session = session
|
||||
self.user = user
|
||||
if user == None:
|
||||
self.get_data(screen_name=self.session.db["user_name"])
|
||||
self.dialog = update_profile.updateProfileDialog()
|
||||
self.fill_profile_fields()
|
||||
self.uploaded = False
|
||||
widgetUtils.connect_event(self.dialog.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
||||
else:
|
||||
try:
|
||||
self.get_data(screen_name=self.user)
|
||||
except TweepyException as err:
|
||||
if type(err) == NotFound:
|
||||
wx.MessageDialog(None, _(u"That user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
if type(err) == Forbidden:
|
||||
wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
log.error("error %s" % (str(err)))
|
||||
return
|
||||
self.dialog = show_user.showUserProfile()
|
||||
string = self.get_user_info()
|
||||
self.dialog.set("text", string)
|
||||
self.dialog.set_title(_(u"Information for %s") % (self.data.screen_name))
|
||||
if self.data.url != None:
|
||||
self.dialog.enable_url()
|
||||
widgetUtils.connect_event(self.dialog.url, widgetUtils.BUTTON_PRESSED, self.visit_url)
|
||||
if self.dialog.get_response() == widgetUtils.OK and self.user == None:
|
||||
self.do_update()
|
||||
|
||||
def get_data(self, screen_name):
|
||||
self.data = self.session.twitter.show_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)
|
||||
def get_data(self, screen_name):
|
||||
self.data = self.session.twitter.get_user(screen_name=screen_name)
|
||||
if screen_name != self.session.db["user_name"]:
|
||||
self.friendship_status = self.session.twitter.get_friendship(source_screen_name=self.session.db["user_name"], target_screen_name=screen_name)
|
||||
|
||||
def fill_profile_fields(self):
|
||||
self.dialog.set_name(self.data["name"])
|
||||
if self.data["url"] != None:
|
||||
self.dialog.set_url(self.data["url"])
|
||||
if len(self.data["location"]) > 0:
|
||||
self.dialog.set_location(self.data["location"])
|
||||
if len(self.data["description"]) > 0:
|
||||
self.dialog.set_description(self.data["description"])
|
||||
def fill_profile_fields(self):
|
||||
self.dialog.set_name(self.data.name)
|
||||
if self.data.url != None:
|
||||
self.dialog.set_url(self.data.url)
|
||||
if len(self.data.location) > 0:
|
||||
self.dialog.set_location(self.data.location)
|
||||
if len(self.data.description) > 0:
|
||||
self.dialog.set_description(self.data.description)
|
||||
|
||||
def get_image(self):
|
||||
file = self.dialog.upload_picture()
|
||||
if file != None:
|
||||
self.file = open(file, "rb")
|
||||
self.uploaded = True
|
||||
self.dialog.change_upload_button(self.uploaded)
|
||||
def get_image(self):
|
||||
file = self.dialog.upload_picture()
|
||||
if file != None:
|
||||
self.file = open(file, "rb")
|
||||
self.uploaded = True
|
||||
self.dialog.change_upload_button(self.uploaded)
|
||||
|
||||
def discard_image(self):
|
||||
self.file = None
|
||||
output.speak(_(u"Discarded"))
|
||||
self.uploaded = False
|
||||
self.dialog.change_upload_button(self.uploaded)
|
||||
def discard_image(self):
|
||||
self.file = None
|
||||
output.speak(_(u"Discarded"))
|
||||
self.uploaded = False
|
||||
self.dialog.change_upload_button(self.uploaded)
|
||||
|
||||
def upload_image(self, *args, **kwargs):
|
||||
if self.uploaded == False:
|
||||
self.get_image()
|
||||
elif self.uploaded == True:
|
||||
self.discard_image()
|
||||
def upload_image(self, *args, **kwargs):
|
||||
if self.uploaded == False:
|
||||
self.get_image()
|
||||
elif self.uploaded == True:
|
||||
self.discard_image()
|
||||
|
||||
def do_update(self):
|
||||
if self.user != None: return
|
||||
name = self.dialog.get("name")
|
||||
description = self.dialog.get("description")
|
||||
location = self.dialog.get("location")
|
||||
url = self.dialog.get("url")
|
||||
if self.file != None:
|
||||
try:
|
||||
self.session.twitter.update_profile_image(image=self.file)
|
||||
except TwythonError as e:
|
||||
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
|
||||
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))
|
||||
def do_update(self):
|
||||
if self.user != None: return
|
||||
name = self.dialog.get("name")
|
||||
description = self.dialog.get("description")
|
||||
location = self.dialog.get("location")
|
||||
url = self.dialog.get("url")
|
||||
if self.file != None:
|
||||
try:
|
||||
self.session.twitter.update_profile_image(image=self.file)
|
||||
except TweepyException as e:
|
||||
output.speak(u"Error %s" % (str(e)))
|
||||
try:
|
||||
self.session.twitter.update_profile(name=name, description=description, location=location, url=url)
|
||||
except TweepyException as e:
|
||||
output.speak(u"Error %s." % (str(e)))
|
||||
|
||||
def get_user_info(self):
|
||||
|
||||
string = u""
|
||||
string = string + _(u"Username: @%s\n") % (self.data["screen_name"])
|
||||
string = string + _(u"Name: %s\n") % (self.data["name"])
|
||||
if self.data["location"] != "":
|
||||
string = string + _(u"Location: %s\n") % (self.data["location"])
|
||||
if self.data["url"] != None:
|
||||
string = string+ _(u"URL: %s\n") % (self.data["url"])
|
||||
if self.data["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"],)
|
||||
relation = True
|
||||
if self.friendship_status["relationship"]["target"]["following"]:
|
||||
friendship += _(u"{0} is following you.").format(self.data["name"],)
|
||||
relation = True
|
||||
if relation == True:
|
||||
string = string+friendship+"\n"
|
||||
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data["followers_count"], self.data["friends_count"])
|
||||
if self.data["verified"] == True: verified = _(u"Yes")
|
||||
else: verified = _(u"No")
|
||||
string = string+ _(u"Verified: %s\n") % (verified)
|
||||
string = string+ _(u"Tweets: %s\n") % (self.data["statuses_count"])
|
||||
string = string+ _(u"Likes: %s") % (self.data["favourites_count"])
|
||||
return string
|
||||
def get_user_info(self):
|
||||
string = u""
|
||||
string = string + _(u"Username: @%s\n") % (self.data.screen_name)
|
||||
string = string + _(u"Name: %s\n") % (self.data.name)
|
||||
if self.data.location != "":
|
||||
string = string + _(u"Location: %s\n") % (self.data.location)
|
||||
if self.data.url != None:
|
||||
string = string+ _(u"URL: %s\n") % (self.data.entities["url"]["urls"][0]["expanded_url"])
|
||||
if self.data.description != "":
|
||||
if self.data.entities.get("description") != None and self.data.entities["description"].get("urls"):
|
||||
self.data.description = utils.expand_urls(self.data.description, self.data.entities["description"])
|
||||
string = string+ _(u"Bio: %s\n") % (self.data.description)
|
||||
if self.data.protected == True: protected = _(u"Yes")
|
||||
else: protected = _(u"No")
|
||||
string = string+ _(u"Protected: %s\n") % (protected)
|
||||
if hasattr(self, "friendship_status"):
|
||||
relation = False
|
||||
friendship = "Relationship: "
|
||||
if self.friendship_status[0].following:
|
||||
friendship += _(u"You follow {0}. ").format(self.data.name,)
|
||||
relation = True
|
||||
if self.friendship_status[1].following:
|
||||
friendship += _(u"{0} is following you.").format(self.data.name,)
|
||||
relation = True
|
||||
if relation == True:
|
||||
string = string+friendship+"\n"
|
||||
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data.followers_count, self.data.friends_count)
|
||||
if self.data.verified == True: verified = _(u"Yes")
|
||||
else: verified = _(u"No")
|
||||
string = string+ _(u"Verified: %s\n") % (verified)
|
||||
string = string+ _(u"Tweets: %s\n") % (self.data.statuses_count)
|
||||
string = string+ _(u"Likes: %s") % (self.data.favourites_count)
|
||||
return string
|
||||
|
||||
def visit_url(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab(self.data["url"])
|
||||
def visit_url(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab(self.data.url)
|
||||
|
@@ -1,82 +1,79 @@
|
||||
# -*- 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):
|
||||
def __init__(self, buffer, users=[], default="follow"):
|
||||
super(userActionsController, self).__init__()
|
||||
self.buffer = buffer
|
||||
self.session = buffer.session
|
||||
self.dialog = userActions.UserActionsDialog(users, default)
|
||||
widgetUtils.connect_event(self.dialog.autocompletion, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
self.process_action()
|
||||
def __init__(self, buffer, users=[], default="follow"):
|
||||
super(userActionsController, self).__init__()
|
||||
self.buffer = buffer
|
||||
self.session = buffer.session
|
||||
self.dialog = userActions.UserActionsDialog(users, default)
|
||||
widgetUtils.connect_event(self.dialog.autocompletion, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
self.process_action()
|
||||
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.dialog, self.session.session_id)
|
||||
c.show_menu("dm")
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.dialog, self.session.session_id)
|
||||
c.show_menu("dm")
|
||||
|
||||
def process_action(self):
|
||||
action = self.dialog.get_action()
|
||||
user = self.dialog.get_user()
|
||||
if user == "": return
|
||||
getattr(self, action)(user)
|
||||
def process_action(self):
|
||||
action = self.dialog.get_action()
|
||||
user = self.dialog.get_user()
|
||||
if user == "": return
|
||||
getattr(self, action)(user)
|
||||
|
||||
def follow(self, user):
|
||||
try:
|
||||
self.session.twitter.create_friendship(screen_name=user )
|
||||
except TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||
def follow(self, user):
|
||||
try:
|
||||
self.session.twitter.create_friendship(screen_name=user )
|
||||
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)
|
||||
def unfollow(self, user):
|
||||
try:
|
||||
id = self.session.twitter.destroy_friendship(screen_name=user )
|
||||
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)
|
||||
def mute(self, user):
|
||||
try:
|
||||
id = self.session.twitter.create_mute(screen_name=user )
|
||||
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)
|
||||
def unmute(self, user):
|
||||
try:
|
||||
id = self.session.twitter.destroy_mute(screen_name=user )
|
||||
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)
|
||||
def report(self, user):
|
||||
try:
|
||||
id = self.session.twitter.report_spam(screen_name=user )
|
||||
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)
|
||||
def block(self, user):
|
||||
try:
|
||||
id = self.session.twitter.create_block(screen_name=user )
|
||||
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)
|
||||
def unblock(self, user):
|
||||
try:
|
||||
id = self.session.twitter.destroy_block(screen_name=user )
|
||||
except TweepyException as err:
|
||||
output.speak("Error %s" % (str(err)), True)
|
||||
|
||||
def ignore_client(self, user):
|
||||
tweet = self.buffer.get_right_tweet()
|
||||
if "sender" in tweet:
|
||||
output.speak(_(u"You can't ignore direct messages"))
|
||||
return
|
||||
client = re.sub(r"(?s)<.*?>", "", tweet["source"])
|
||||
if client not in self.session.settings["twitter"]["ignored_clients"]:
|
||||
self.session.settings["twitter"]["ignored_clients"].append(client)
|
||||
self.session.settings.write()
|
||||
def ignore_client(self, user):
|
||||
tweet = self.buffer.get_right_tweet()
|
||||
if hasattr(tweet, "sender"):
|
||||
output.speak(_(u"You can't ignore direct messages"))
|
||||
return
|
||||
client = tweet.source
|
||||
if client not in self.session.settings["twitter"]["ignored_clients"]:
|
||||
self.session.settings["twitter"]["ignored_clients"].append(client)
|
||||
self.session.settings.write()
|
||||
|
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
|
@@ -37,146 +37,146 @@ import logging
|
||||
log = logging.getLogger("extra.AudioUploader.audioUploader")
|
||||
|
||||
class audioUploader(object):
|
||||
def __init__(self, configFile, completed_callback):
|
||||
self.config = configFile
|
||||
super(audioUploader, self).__init__()
|
||||
self.dialog = wx_ui.audioDialog(services=self.get_available_services())
|
||||
self.file = None
|
||||
self.recorded = False
|
||||
self.recording = None
|
||||
self.playing = None
|
||||
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
|
||||
widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause)
|
||||
widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record)
|
||||
widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists)
|
||||
widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
self.postprocess()
|
||||
log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services")))
|
||||
self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file)
|
||||
output.speak(_(u"Attaching..."))
|
||||
if self.dialog.get("services") == "SNDUp":
|
||||
base_url = "https://sndup.net/post.php"
|
||||
if len(self.config["sound"]["sndup_api_key"]) > 0:
|
||||
url = base_url + '?apikey=' + self.config['sound']['sndup_api_key']
|
||||
else:
|
||||
url = base_url
|
||||
self.uploaderFunction = transfer.Upload(obj=self, field='file', url=url, filename=self.file, completed_callback=completed_callback)
|
||||
pub.subscribe(self.uploaderDialog.update, "uploading")
|
||||
self.uploaderDialog.get_response(self.uploaderFunction.perform_threaded)
|
||||
def __init__(self, configFile, completed_callback):
|
||||
self.config = configFile
|
||||
super(audioUploader, self).__init__()
|
||||
self.dialog = wx_ui.audioDialog(services=self.get_available_services())
|
||||
self.file = None
|
||||
self.recorded = False
|
||||
self.recording = None
|
||||
self.playing = None
|
||||
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
|
||||
widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause)
|
||||
widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record)
|
||||
widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists)
|
||||
widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
self.postprocess()
|
||||
log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services")))
|
||||
self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file)
|
||||
output.speak(_(u"Attaching..."))
|
||||
if self.dialog.get("services") == "SNDUp":
|
||||
base_url = "https://sndup.net/post.php"
|
||||
if len(self.config["sound"]["sndup_api_key"]) > 0:
|
||||
url = base_url + '?apikey=' + self.config['sound']['sndup_api_key']
|
||||
else:
|
||||
url = base_url
|
||||
self.uploaderFunction = transfer.Upload(obj=self, field='file', url=url, filename=self.file, completed_callback=completed_callback)
|
||||
pub.subscribe(self.uploaderDialog.update, "uploading")
|
||||
self.uploaderDialog.get_response(self.uploaderFunction.perform_threaded)
|
||||
|
||||
def get_available_services(self):
|
||||
services = []
|
||||
services.append("SNDUp")
|
||||
return services
|
||||
def get_available_services(self):
|
||||
services = []
|
||||
services.append("SNDUp")
|
||||
return services
|
||||
|
||||
def on_pause(self, *args, **kwargs):
|
||||
if self.dialog.get("pause") == _(u"Pause"):
|
||||
self.recording.pause()
|
||||
self.dialog.set("pause", _(u"&Resume"))
|
||||
elif self.dialog.get("pause") == _(u"Resume"):
|
||||
self.recording.play()
|
||||
self.dialog.set("pause", _(U"&Pause"))
|
||||
def on_pause(self, *args, **kwargs):
|
||||
if self.dialog.get("pause") == _(u"Pause"):
|
||||
self.recording.pause()
|
||||
self.dialog.set("pause", _(u"&Resume"))
|
||||
elif self.dialog.get("pause") == _(u"Resume"):
|
||||
self.recording.play()
|
||||
self.dialog.set("pause", _(U"&Pause"))
|
||||
|
||||
def on_record(self, *args, **kwargs):
|
||||
if self.recording != None:
|
||||
self.stop_recording()
|
||||
self.dialog.disable_control("pause")
|
||||
else:
|
||||
self.start_recording()
|
||||
self.dialog.enable_control("pause")
|
||||
def on_record(self, *args, **kwargs):
|
||||
if self.recording != None:
|
||||
self.stop_recording()
|
||||
self.dialog.disable_control("pause")
|
||||
else:
|
||||
self.start_recording()
|
||||
self.dialog.enable_control("pause")
|
||||
|
||||
def start_recording(self):
|
||||
self.dialog.disable_control("attach_exists")
|
||||
self.file = tempfile.mktemp(suffix='.wav')
|
||||
self.recording = sound.recording(self.file)
|
||||
self.recording.play()
|
||||
self.dialog.set("record", _(u"&Stop"))
|
||||
output.speak(_(u"Recording"))
|
||||
def start_recording(self):
|
||||
self.dialog.disable_control("attach_exists")
|
||||
self.file = tempfile.mktemp(suffix='.wav')
|
||||
self.recording = sound.recording(self.file)
|
||||
self.recording.play()
|
||||
self.dialog.set("record", _(u"&Stop"))
|
||||
output.speak(_(u"Recording"))
|
||||
|
||||
def stop_recording(self):
|
||||
self.recording.stop()
|
||||
self.recording.free()
|
||||
output.speak(_(u"Stopped"))
|
||||
self.recorded = True
|
||||
self.dialog.set("record", _(u"&Record"))
|
||||
self.file_attached()
|
||||
def stop_recording(self):
|
||||
self.recording.stop()
|
||||
self.recording.free()
|
||||
output.speak(_(u"Stopped"))
|
||||
self.recorded = True
|
||||
self.dialog.set("record", _(u"&Record"))
|
||||
self.file_attached()
|
||||
|
||||
def file_attached(self):
|
||||
self.dialog.set("pause", _(u"&Pause"))
|
||||
self.dialog.disable_control("record")
|
||||
self.dialog.enable_control("play")
|
||||
self.dialog.enable_control("discard")
|
||||
self.dialog.disable_control("attach_exists")
|
||||
self.dialog.enable_control("attach")
|
||||
self.dialog.play.SetFocus()
|
||||
def file_attached(self):
|
||||
self.dialog.set("pause", _(u"&Pause"))
|
||||
self.dialog.disable_control("record")
|
||||
self.dialog.enable_control("play")
|
||||
self.dialog.enable_control("discard")
|
||||
self.dialog.disable_control("attach_exists")
|
||||
self.dialog.enable_control("attach")
|
||||
self.dialog.play.SetFocus()
|
||||
|
||||
def on_discard(self, *args, **kwargs):
|
||||
if self.playing:
|
||||
self._stop()
|
||||
if self.recording != None:
|
||||
self.cleanup()
|
||||
self.dialog.disable_control("attach")
|
||||
self.dialog.disable_control("play")
|
||||
self.file = None
|
||||
self.dialog.enable_control("record")
|
||||
self.dialog.enable_control("attach_exists")
|
||||
self.dialog.record.SetFocus()
|
||||
self.dialog.disable_control("discard")
|
||||
self.recording = None
|
||||
output.speak(_(u"Discarded"))
|
||||
def on_discard(self, *args, **kwargs):
|
||||
if self.playing:
|
||||
self._stop()
|
||||
if self.recording != None:
|
||||
self.cleanup()
|
||||
self.dialog.disable_control("attach")
|
||||
self.dialog.disable_control("play")
|
||||
self.file = None
|
||||
self.dialog.enable_control("record")
|
||||
self.dialog.enable_control("attach_exists")
|
||||
self.dialog.record.SetFocus()
|
||||
self.dialog.disable_control("discard")
|
||||
self.recording = None
|
||||
output.speak(_(u"Discarded"))
|
||||
|
||||
def on_play(self, *args, **kwargs):
|
||||
if not self.playing:
|
||||
call_threaded(self._play)
|
||||
else:
|
||||
self._stop()
|
||||
def on_play(self, *args, **kwargs):
|
||||
if not self.playing:
|
||||
call_threaded(self._play)
|
||||
else:
|
||||
self._stop()
|
||||
|
||||
def _play(self):
|
||||
output.speak(_(u"Playing..."))
|
||||
def _play(self):
|
||||
output.speak(_(u"Playing..."))
|
||||
# try:
|
||||
self.playing = sound_lib.stream.FileStream(file=str(self.file), flags=sound_lib.stream.BASS_UNICODE)
|
||||
self.playing.play()
|
||||
self.dialog.set("play", _(u"&Stop"))
|
||||
try:
|
||||
while self.playing.is_playing:
|
||||
pass
|
||||
self.dialog.set("play", _(u"&Play"))
|
||||
self.playing.free()
|
||||
self.playing = None
|
||||
except:
|
||||
pass
|
||||
self.playing = sound_lib.stream.FileStream(file=str(self.file), flags=sound_lib.stream.BASS_UNICODE)
|
||||
self.playing.play()
|
||||
self.dialog.set("play", _(u"&Stop"))
|
||||
try:
|
||||
while self.playing.is_playing:
|
||||
pass
|
||||
self.dialog.set("play", _(u"&Play"))
|
||||
self.playing.free()
|
||||
self.playing = None
|
||||
except:
|
||||
pass
|
||||
|
||||
def _stop(self):
|
||||
output.speak(_(u"Stopped"))
|
||||
self.playing.stop()
|
||||
self.playing.free()
|
||||
self.dialog.set("play", _(u"&Play"))
|
||||
self.playing = None
|
||||
def _stop(self):
|
||||
output.speak(_(u"Stopped"))
|
||||
self.playing.stop()
|
||||
self.playing.free()
|
||||
self.dialog.set("play", _(u"&Play"))
|
||||
self.playing = None
|
||||
|
||||
def postprocess(self):
|
||||
if self.file.lower().endswith('.wav'):
|
||||
output.speak(_(u"Recoding audio..."))
|
||||
sound.recode_audio(self.file)
|
||||
self.wav_file = self.file
|
||||
self.file = '%s.ogg' % self.file[:-4]
|
||||
def postprocess(self):
|
||||
if self.file.lower().endswith('.wav'):
|
||||
output.speak(_(u"Recoding audio..."))
|
||||
sound.recode_audio(self.file)
|
||||
self.wav_file = self.file
|
||||
self.file = '%s.ogg' % self.file[:-4]
|
||||
|
||||
def cleanup(self):
|
||||
if self.playing and self.playing.is_playing:
|
||||
self.playing.stop()
|
||||
if self.recording != None:
|
||||
if self.recording.is_playing:
|
||||
self.recording.stop()
|
||||
try:
|
||||
self.recording.free()
|
||||
except:
|
||||
pass
|
||||
os.remove(self.file)
|
||||
if hasattr(self, 'wav_file'):
|
||||
os.remove(self.wav_file)
|
||||
def cleanup(self):
|
||||
if self.playing and self.playing.is_playing:
|
||||
self.playing.stop()
|
||||
if self.recording != None:
|
||||
if self.recording.is_playing:
|
||||
self.recording.stop()
|
||||
try:
|
||||
self.recording.free()
|
||||
except:
|
||||
pass
|
||||
os.remove(self.file)
|
||||
if hasattr(self, 'wav_file'):
|
||||
os.remove(self.wav_file)
|
||||
|
||||
def on_attach_exists(self, *args, **kwargs):
|
||||
self.file = self.dialog.get_file()
|
||||
if self.file != False:
|
||||
self.file_attached()
|
||||
def on_attach_exists(self, *args, **kwargs):
|
||||
self.file = self.dialog.get_file()
|
||||
if self.file != False:
|
||||
self.file_attached()
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
from past.utils import old_div
|
||||
from __future__ import division
|
||||
from __future__ import unicode_literals
|
||||
from builtins import object
|
||||
from past.utils import old_div
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
@@ -15,74 +15,74 @@ from requests_toolbelt.multipart.encoder import MultipartEncoder, MultipartEncod
|
||||
import requests
|
||||
import os
|
||||
class Upload(object):
|
||||
def __init__(self, field=None, obj=None, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs):
|
||||
super(Upload, self).__init__(*args, **kwargs)
|
||||
self.url=url
|
||||
self.filename=filename
|
||||
log.debug("Uploading audio to %s, filename %s" % (url, filename))
|
||||
self.start_time = None
|
||||
self.completed_callback = completed_callback
|
||||
self.background_thread = None
|
||||
self.transfer_rate = 0
|
||||
self.local_filename=os.path.basename(self.filename)
|
||||
if isinstance(self.local_filename, str):
|
||||
self.local_filename=self.local_filename.encode(sys.getfilesystemencoding())
|
||||
self.fin=open(self.filename, 'rb')
|
||||
self.m = MultipartEncoder(fields={field:(self.local_filename, self.fin, "application/octet-stream")})
|
||||
self.monitor = MultipartEncoderMonitor(self.m, self.progress_callback)
|
||||
self.response=None
|
||||
self.obj=obj
|
||||
self.follow_location=follow_location
|
||||
#the verbose parameter is deprecated and will be removed soon
|
||||
def __init__(self, field=None, obj=None, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs):
|
||||
super(Upload, self).__init__(*args, **kwargs)
|
||||
self.url=url
|
||||
self.filename=filename
|
||||
log.debug("Uploading audio to %s, filename %s" % (url, filename))
|
||||
self.start_time = None
|
||||
self.completed_callback = completed_callback
|
||||
self.background_thread = None
|
||||
self.transfer_rate = 0
|
||||
self.local_filename=os.path.basename(self.filename)
|
||||
if isinstance(self.local_filename, str):
|
||||
self.local_filename=self.local_filename.encode(sys.getfilesystemencoding())
|
||||
self.fin=open(self.filename, 'rb')
|
||||
self.m = MultipartEncoder(fields={field:(self.local_filename, self.fin, "application/octet-stream")})
|
||||
self.monitor = MultipartEncoderMonitor(self.m, self.progress_callback)
|
||||
self.response=None
|
||||
self.obj=obj
|
||||
self.follow_location=follow_location
|
||||
#the verbose parameter is deprecated and will be removed soon
|
||||
|
||||
def elapsed_time(self):
|
||||
if not self.start_time:
|
||||
return 0
|
||||
return time.time() - self.start_time
|
||||
def elapsed_time(self):
|
||||
if not self.start_time:
|
||||
return 0
|
||||
return time.time() - self.start_time
|
||||
|
||||
def progress_callback(self, monitor):
|
||||
progress = {}
|
||||
progress["total"] = monitor.len
|
||||
progress["current"] = monitor.bytes_read
|
||||
if progress["current"] == 0:
|
||||
progress["percent"] = 0
|
||||
self.transfer_rate = 0
|
||||
else:
|
||||
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
|
||||
self.transfer_rate = old_div(progress["current"], self.elapsed_time())
|
||||
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
|
||||
if self.transfer_rate:
|
||||
progress["eta"] = old_div((progress["total"] - progress["current"]), self.transfer_rate)
|
||||
else:
|
||||
progress["eta"] = 0
|
||||
pub.sendMessage("uploading", data=progress)
|
||||
def progress_callback(self, monitor):
|
||||
progress = {}
|
||||
progress["total"] = monitor.len
|
||||
progress["current"] = monitor.bytes_read
|
||||
if progress["current"] == 0:
|
||||
progress["percent"] = 0
|
||||
self.transfer_rate = 0
|
||||
else:
|
||||
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
|
||||
self.transfer_rate = old_div(progress["current"], self.elapsed_time())
|
||||
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
|
||||
if self.transfer_rate:
|
||||
progress["eta"] = old_div((progress["total"] - progress["current"]), self.transfer_rate)
|
||||
else:
|
||||
progress["eta"] = 0
|
||||
pub.sendMessage("uploading", data=progress)
|
||||
|
||||
def perform_transfer(self):
|
||||
log.debug("starting upload...")
|
||||
self.start_time = time.time()
|
||||
self.response=requests.post(url=self.url, data=self.monitor, headers={"Content-Type":self.m.content_type}, allow_redirects=self.follow_location, stream=True)
|
||||
log.debug("Upload finished.")
|
||||
self.complete_transfer()
|
||||
def perform_transfer(self):
|
||||
log.debug("starting upload...")
|
||||
self.start_time = time.time()
|
||||
self.response=requests.post(url=self.url, data=self.monitor, headers={"Content-Type":self.m.content_type}, allow_redirects=self.follow_location, stream=True)
|
||||
log.debug("Upload finished.")
|
||||
self.complete_transfer()
|
||||
|
||||
def perform_threaded(self, *args, **kwargs):
|
||||
self.background_thread = threading.Thread(target=self.perform_transfer)
|
||||
self.background_thread.daemon = True
|
||||
self.background_thread.start()
|
||||
def perform_threaded(self, *args, **kwargs):
|
||||
self.background_thread = threading.Thread(target=self.perform_transfer)
|
||||
self.background_thread.daemon = True
|
||||
self.background_thread.start()
|
||||
|
||||
def complete_transfer(self):
|
||||
if callable(self.completed_callback):
|
||||
self.completed_callback(self.obj)
|
||||
if hasattr(self,'fin') and callable(self.fin.close):
|
||||
self.fin.close()
|
||||
def complete_transfer(self):
|
||||
if callable(self.completed_callback):
|
||||
self.completed_callback(self.obj)
|
||||
if hasattr(self,'fin') and callable(self.fin.close):
|
||||
self.fin.close()
|
||||
|
||||
def get_url(self):
|
||||
try:
|
||||
data = self.response.json()
|
||||
except:
|
||||
return _("Error in file upload: {0}").format(self.data.content,)
|
||||
if "url" in data and data["url"] != "0":
|
||||
return data["url"]
|
||||
elif "error" in data and data["error"] != "0":
|
||||
return data["error"]
|
||||
else:
|
||||
return _("Error in file upload: {0}").format(self.data.content,)
|
||||
def get_url(self):
|
||||
try:
|
||||
data = self.response.json()
|
||||
except:
|
||||
return _("Error in file upload: {0}").format(self.data.content,)
|
||||
if "url" in data and data["url"] != "0":
|
||||
return data["url"]
|
||||
elif "error" in data and data["error"] != "0":
|
||||
return data["error"]
|
||||
else:
|
||||
return _("Error in file upload: {0}").format(self.data.content,)
|
||||
|
@@ -3,42 +3,42 @@
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
def convert_bytes(n):
|
||||
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
|
||||
if n >= P:
|
||||
return '%.2fPb' % (float(n) / T)
|
||||
elif n >= T:
|
||||
return '%.2fTb' % (float(n) / T)
|
||||
elif n >= G:
|
||||
return '%.2fGb' % (float(n) / G)
|
||||
elif n >= M:
|
||||
return '%.2fMb' % (float(n) / M)
|
||||
elif n >= K:
|
||||
return '%.2fKb' % (float(n) / K)
|
||||
else:
|
||||
return '%d' % n
|
||||
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
|
||||
if n >= P:
|
||||
return '%.2fPb' % (float(n) / T)
|
||||
elif n >= T:
|
||||
return '%.2fTb' % (float(n) / T)
|
||||
elif n >= G:
|
||||
return '%.2fGb' % (float(n) / G)
|
||||
elif n >= M:
|
||||
return '%.2fMb' % (float(n) / M)
|
||||
elif n >= K:
|
||||
return '%.2fKb' % (float(n) / K)
|
||||
else:
|
||||
return '%d' % n
|
||||
|
||||
def seconds_to_string(seconds, precision=0):
|
||||
day = seconds // 86400
|
||||
hour = seconds // 3600
|
||||
min = (seconds // 60) % 60
|
||||
sec = seconds - (hour * 3600) - (min * 60)
|
||||
sec_spec = "." + str(precision) + "f"
|
||||
sec_string = sec.__format__(sec_spec)
|
||||
string = ""
|
||||
if day == 1:
|
||||
string += _(u"%d day, ") % day
|
||||
elif day >= 2:
|
||||
string += _(u"%d days, ") % day
|
||||
if (hour == 1):
|
||||
string += _(u"%d hour, ") % hour
|
||||
elif (hour >= 2):
|
||||
string += _("%d hours, ") % hour
|
||||
if (min == 1):
|
||||
string += _(u"%d minute, ") % min
|
||||
elif (min >= 2):
|
||||
string += _(u"%d minutes, ") % min
|
||||
if sec >= 0 and sec <= 2:
|
||||
string += _(u"%s second") % sec_string
|
||||
else:
|
||||
string += _(u"%s seconds") % sec_string
|
||||
return string
|
||||
day = seconds // 86400
|
||||
hour = seconds // 3600
|
||||
min = (seconds // 60) % 60
|
||||
sec = seconds - (hour * 3600) - (min * 60)
|
||||
sec_spec = "." + str(precision) + "f"
|
||||
sec_string = sec.__format__(sec_spec)
|
||||
string = ""
|
||||
if day == 1:
|
||||
string += _(u"%d day, ") % day
|
||||
elif day >= 2:
|
||||
string += _(u"%d days, ") % day
|
||||
if (hour == 1):
|
||||
string += _(u"%d hour, ") % hour
|
||||
elif (hour >= 2):
|
||||
string += _("%d hours, ") % hour
|
||||
if (min == 1):
|
||||
string += _(u"%d minute, ") % min
|
||||
elif (min >= 2):
|
||||
string += _(u"%d minutes, ") % min
|
||||
if sec >= 0 and sec <= 2:
|
||||
string += _(u"%s second") % sec_string
|
||||
else:
|
||||
string += _(u"%s seconds") % sec_string
|
||||
return string
|
||||
|
@@ -7,57 +7,57 @@ import widgetUtils
|
||||
|
||||
class UploadDialog(widgetUtils.BaseDialog):
|
||||
|
||||
def __init__(self, filename, *args, **kwargs):
|
||||
super(UploadDialog, self).__init__(parent=None, id=wx.ID_ANY, *args, **kwargs)
|
||||
self.pane = wx.Panel(self)
|
||||
self.progress_bar = wx.Gauge(parent=self.pane)
|
||||
fileBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
fileLabel = wx.StaticText(self.pane, -1, _(u"File"))
|
||||
self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100))
|
||||
self.file.SetFocus()
|
||||
fileBox.Add(fileLabel)
|
||||
fileBox.Add(self.file)
|
||||
currentAmountBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred"))
|
||||
self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
currentAmountBox.Add(current_amount_label)
|
||||
currentAmountBox.Add(self.current_amount)
|
||||
totalSizeBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size"))
|
||||
self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
totalSizeBox.Add(total_size_label)
|
||||
totalSizeBox.Add(self.total_size)
|
||||
speedBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate"))
|
||||
self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s")
|
||||
speedBox.Add(speedLabel)
|
||||
speedBox.Add(self.speed)
|
||||
etaBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
etaLabel = wx.StaticText(self.pane, -1, _(u"Time left"))
|
||||
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
|
||||
etaBox.Add(etaLabel)
|
||||
etaBox.Add(self.eta)
|
||||
self.create_buttons()
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(fileBox)
|
||||
sizer.Add(currentAmountBox)
|
||||
sizer.Add(totalSizeBox)
|
||||
sizer.Add(speedBox)
|
||||
sizer.Add(etaBox)
|
||||
sizer.Add(self.progress_bar)
|
||||
self.pane.SetSizerAndFit(sizer)
|
||||
def __init__(self, filename, *args, **kwargs):
|
||||
super(UploadDialog, self).__init__(parent=None, id=wx.ID_ANY, *args, **kwargs)
|
||||
self.pane = wx.Panel(self)
|
||||
self.progress_bar = wx.Gauge(parent=self.pane)
|
||||
fileBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
fileLabel = wx.StaticText(self.pane, -1, _(u"File"))
|
||||
self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100))
|
||||
self.file.SetFocus()
|
||||
fileBox.Add(fileLabel)
|
||||
fileBox.Add(self.file)
|
||||
currentAmountBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred"))
|
||||
self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
currentAmountBox.Add(current_amount_label)
|
||||
currentAmountBox.Add(self.current_amount)
|
||||
totalSizeBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size"))
|
||||
self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
totalSizeBox.Add(total_size_label)
|
||||
totalSizeBox.Add(self.total_size)
|
||||
speedBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate"))
|
||||
self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s")
|
||||
speedBox.Add(speedLabel)
|
||||
speedBox.Add(self.speed)
|
||||
etaBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
etaLabel = wx.StaticText(self.pane, -1, _(u"Time left"))
|
||||
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
|
||||
etaBox.Add(etaLabel)
|
||||
etaBox.Add(self.eta)
|
||||
self.create_buttons()
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(fileBox)
|
||||
sizer.Add(currentAmountBox)
|
||||
sizer.Add(totalSizeBox)
|
||||
sizer.Add(speedBox)
|
||||
sizer.Add(etaBox)
|
||||
sizer.Add(self.progress_bar)
|
||||
self.pane.SetSizerAndFit(sizer)
|
||||
|
||||
def update(self, data):
|
||||
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
|
||||
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
|
||||
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
|
||||
wx.CallAfter(self.speed.SetValue, data["speed"])
|
||||
if data["eta"]:
|
||||
wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"]))
|
||||
def update(self, data):
|
||||
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
|
||||
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
|
||||
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
|
||||
wx.CallAfter(self.speed.SetValue, data["speed"])
|
||||
if data["eta"]:
|
||||
wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"]))
|
||||
|
||||
def create_buttons(self):
|
||||
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
|
||||
def create_buttons(self):
|
||||
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
|
||||
|
||||
def get_response(self, fn):
|
||||
wx.CallAfter(fn, 0.01)
|
||||
self.ShowModal()
|
||||
def get_response(self, fn):
|
||||
wx.CallAfter(fn, 0.01)
|
||||
self.ShowModal()
|
||||
|
@@ -24,56 +24,56 @@ import logging
|
||||
log = logging.getLogger("extra.AudioUploader.wx_UI")
|
||||
|
||||
class audioDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self, services):
|
||||
log.debug("creating audio dialog.")
|
||||
super(audioDialog, self).__init__(None, -1, _(u"Attach audio"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
def __init__(self, services):
|
||||
log.debug("creating audio dialog.")
|
||||
super(audioDialog, self).__init__(None, -1, _(u"Attach audio"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.play = wx.Button(panel, -1, _(u"&Play"))
|
||||
self.play.Disable()
|
||||
self.pause = wx.Button(panel, -1, _(u"&Pause"))
|
||||
self.pause.Disable()
|
||||
self.record = wx.Button(panel, -1, _(u"&Record"))
|
||||
self.record.SetFocus()
|
||||
self.attach_exists = wx.Button(panel, -1, _(u"&Add an existing file"))
|
||||
self.discard = wx.Button(panel, -1, _(u"&Discard"))
|
||||
self.discard.Disable()
|
||||
label = wx.StaticText(panel, -1, _(u"Upload to"))
|
||||
self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY)
|
||||
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
servicesBox.Add(label, 0, wx.ALL, 5)
|
||||
servicesBox.Add(self.services, 0, wx.ALL, 5)
|
||||
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
|
||||
self.attach.Disable()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Cancel"))
|
||||
btnSizer.Add(self.play, 0, wx.ALL, 5)
|
||||
btnSizer.Add(self.pause, 0, wx.ALL, 5)
|
||||
btnSizer.Add(self.record, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(self.attach_exists, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(self.discard, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(self.attach, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(servicesBox, 0, wx.ALL, 5)
|
||||
sizer.Add(btnSizer, 0, wx.ALL, 5)
|
||||
sizer.Add(btnSizer2, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
self.play = wx.Button(panel, -1, _(u"&Play"))
|
||||
self.play.Disable()
|
||||
self.pause = wx.Button(panel, -1, _(u"&Pause"))
|
||||
self.pause.Disable()
|
||||
self.record = wx.Button(panel, -1, _(u"&Record"))
|
||||
self.record.SetFocus()
|
||||
self.attach_exists = wx.Button(panel, -1, _(u"&Add an existing file"))
|
||||
self.discard = wx.Button(panel, -1, _(u"&Discard"))
|
||||
self.discard.Disable()
|
||||
label = wx.StaticText(panel, -1, _(u"Upload to"))
|
||||
self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY)
|
||||
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
servicesBox.Add(label, 0, wx.ALL, 5)
|
||||
servicesBox.Add(self.services, 0, wx.ALL, 5)
|
||||
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
|
||||
self.attach.Disable()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Cancel"))
|
||||
btnSizer.Add(self.play, 0, wx.ALL, 5)
|
||||
btnSizer.Add(self.pause, 0, wx.ALL, 5)
|
||||
btnSizer.Add(self.record, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(self.attach_exists, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(self.discard, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(self.attach, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(servicesBox, 0, wx.ALL, 5)
|
||||
sizer.Add(btnSizer, 0, wx.ALL, 5)
|
||||
sizer.Add(btnSizer2, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def enable_control(self, control):
|
||||
log.debug("Enabling control %s" % (control,))
|
||||
if hasattr(self, control):
|
||||
getattr(self, control).Enable()
|
||||
def enable_control(self, control):
|
||||
log.debug("Enabling control %s" % (control,))
|
||||
if hasattr(self, control):
|
||||
getattr(self, control).Enable()
|
||||
|
||||
def disable_control(self, control):
|
||||
log.debug("Disabling control %s" % (control,))
|
||||
if hasattr(self, control):
|
||||
getattr(self, control).Disable()
|
||||
def disable_control(self, control):
|
||||
log.debug("Disabling control %s" % (control,))
|
||||
if hasattr(self, control):
|
||||
getattr(self, control).Disable()
|
||||
|
||||
def get_file(self):
|
||||
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||
return False
|
||||
return openFileDialog.GetPath()
|
||||
def get_file(self):
|
||||
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||
return False
|
||||
return openFileDialog.GetPath()
|
||||
|
@@ -4,23 +4,23 @@ from gi.repository import Gtk
|
||||
import widgetUtils
|
||||
|
||||
class soundsTutorialDialog(Gtk.Dialog):
|
||||
def __init__(self, actions):
|
||||
super(soundsTutorialDialog, self).__init__("Sounds tutorial", None, 0, (Gtk.STOCK_CANCEL, widgetUtils.CANCEL))
|
||||
box = self.get_content_area()
|
||||
label = Gtk.Label("Press enter for listen the sound")
|
||||
self.list = widgetUtils.list("Action")
|
||||
self.populate_actions(actions)
|
||||
lBox = Gtk.Box(spacing=6)
|
||||
lBox.add(label)
|
||||
lBox.add(self.list.list)
|
||||
box.add(lBox)
|
||||
self.play = Gtk.Button("Play")
|
||||
box.add(self.play)
|
||||
self.show_all()
|
||||
def __init__(self, actions):
|
||||
super(soundsTutorialDialog, self).__init__("Sounds tutorial", None, 0, (Gtk.STOCK_CANCEL, widgetUtils.CANCEL))
|
||||
box = self.get_content_area()
|
||||
label = Gtk.Label("Press enter for listen the sound")
|
||||
self.list = widgetUtils.list("Action")
|
||||
self.populate_actions(actions)
|
||||
lBox = Gtk.Box(spacing=6)
|
||||
lBox.add(label)
|
||||
lBox.add(self.list.list)
|
||||
box.add(lBox)
|
||||
self.play = Gtk.Button("Play")
|
||||
box.add(self.play)
|
||||
self.show_all()
|
||||
|
||||
def populate_actions(self, actions):
|
||||
for i in actions:
|
||||
self.list.insert_item(i)
|
||||
def populate_actions(self, actions):
|
||||
for i in actions:
|
||||
self.list.insert_item(i)
|
||||
|
||||
def get_selected(self):
|
||||
return self.list.get_selected()
|
||||
def get_selected(self):
|
||||
return self.list.get_selected()
|
||||
|
@@ -1,4 +1,4 @@
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import unicode_literals
|
||||
#Reverse sort, by Bill Dengler <codeofdusk@gmail.com> for use in TWBlue http://twblue.es
|
||||
def invert_tuples(t):
|
||||
"Invert a list of tuples, so that the 0th element becomes the -1th, and the -1th becomes the 0th."
|
||||
@@ -9,4 +9,4 @@ def invert_tuples(t):
|
||||
|
||||
def reverse_sort(t):
|
||||
"Sorts a list of tuples/lists by their last elements, not their first."
|
||||
return invert_tuples(sorted(invert_tuples(t)))
|
||||
return invert_tuples(sorted(invert_tuples(t)))
|
||||
|
@@ -10,28 +10,28 @@ import logging
|
||||
log = logging.getLogger("extra.SoundsTutorial.soundsTutorial")
|
||||
from . import soundsTutorial_constants
|
||||
if platform.system() == "Windows":
|
||||
from . import wx_ui as UI
|
||||
from . import wx_ui as UI
|
||||
elif platform.system() == "Linux":
|
||||
from . import gtk_ui as UI
|
||||
from . import gtk_ui as UI
|
||||
|
||||
class soundsTutorial(object):
|
||||
def __init__(self, sessionObject):
|
||||
log.debug("Creating sounds tutorial object...")
|
||||
super(soundsTutorial, self).__init__()
|
||||
self.session = sessionObject
|
||||
self.actions = []
|
||||
log.debug("Loading actions for sounds tutorial...")
|
||||
[self.actions.append(i[1]) for i in soundsTutorial_constants.actions]
|
||||
self.files = []
|
||||
log.debug("Searching sound files...")
|
||||
[self.files.append(i[0]) for i in soundsTutorial_constants.actions]
|
||||
log.debug("Creating dialog...")
|
||||
self.dialog = UI.soundsTutorialDialog(self.actions)
|
||||
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
|
||||
self.dialog.get_response()
|
||||
def __init__(self, sessionObject):
|
||||
log.debug("Creating sounds tutorial object...")
|
||||
super(soundsTutorial, self).__init__()
|
||||
self.session = sessionObject
|
||||
self.actions = []
|
||||
log.debug("Loading actions for sounds tutorial...")
|
||||
[self.actions.append(i[1]) for i in soundsTutorial_constants.actions]
|
||||
self.files = []
|
||||
log.debug("Searching sound files...")
|
||||
[self.files.append(i[0]) for i in soundsTutorial_constants.actions]
|
||||
log.debug("Creating dialog...")
|
||||
self.dialog = UI.soundsTutorialDialog(self.actions)
|
||||
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
|
||||
self.dialog.get_response()
|
||||
|
||||
def on_play(self, *args, **kwargs):
|
||||
try:
|
||||
self.session.sound.play(self.files[self.dialog.get_selection()]+".ogg")
|
||||
except:
|
||||
log.exception("Error playing the %s sound" % (self.files[self.dialog.items.GetSelection()],))
|
||||
def on_play(self, *args, **kwargs):
|
||||
try:
|
||||
self.session.sound.play(self.files[self.dialog.get_selection()]+".ogg")
|
||||
except:
|
||||
log.exception("Error playing the %s sound" % (self.files[self.dialog.items.GetSelection()],))
|
||||
|
@@ -5,27 +5,27 @@ from __future__ import unicode_literals
|
||||
from . import reverse_sort
|
||||
import application
|
||||
actions = reverse_sort.reverse_sort([ ("audio", _(u"Audio tweet.")),
|
||||
("create_timeline", _(u"User timeline buffer created.")),
|
||||
("delete_timeline", _(u"Buffer destroied.")),
|
||||
("dm_received", _(u"Direct message received.")),
|
||||
("dm_sent", _(u"Direct message sent.")),
|
||||
("error", _(u"Error.")),
|
||||
("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.")),
|
||||
("list_tweet", _(u"List updated.")),
|
||||
("max_length", _(u"Too many characters.")),
|
||||
("mention_received", _(u"Mention received.")),
|
||||
("new_event", _(u"New event.")),
|
||||
("ready", _(u"{0} is ready.").format(application.name,)),
|
||||
("reply_send", _(u"Mention sent.")),
|
||||
("retweet_send", _(u"Tweet retweeted.")),
|
||||
("search_updated", _(u"Search buffer updated.")),
|
||||
("tweet_received", _(u"Tweet received.")),
|
||||
("tweet_send", _(u"Tweet sent.")),
|
||||
("trends_updated", _(u"Trending topics buffer updated.")),
|
||||
("tweet_timeline", _(u"New tweet in user timeline buffer.")),
|
||||
("update_followers", _(u"New follower.")),
|
||||
("volume_changed", _(u"Volume changed."))])
|
||||
("create_timeline", _(u"User timeline buffer created.")),
|
||||
("delete_timeline", _(u"Buffer destroied.")),
|
||||
("dm_received", _(u"Direct message received.")),
|
||||
("dm_sent", _(u"Direct message sent.")),
|
||||
("error", _(u"Error.")),
|
||||
("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.")),
|
||||
("list_tweet", _(u"List updated.")),
|
||||
("max_length", _(u"Too many characters.")),
|
||||
("mention_received", _(u"Mention received.")),
|
||||
("new_event", _(u"New event.")),
|
||||
("ready", _(u"{0} is ready.").format(application.name,)),
|
||||
("reply_send", _(u"Mention sent.")),
|
||||
("retweet_send", _(u"Tweet retweeted.")),
|
||||
("search_updated", _(u"Search buffer updated.")),
|
||||
("tweet_received", _(u"Tweet received.")),
|
||||
("tweet_send", _(u"Tweet sent.")),
|
||||
("trends_updated", _(u"Trending topics buffer updated.")),
|
||||
("tweet_timeline", _(u"New tweet in user timeline buffer.")),
|
||||
("update_followers", _(u"New follower.")),
|
||||
("volume_changed", _(u"Volume changed."))])
|
||||
|
@@ -4,27 +4,27 @@ import wx
|
||||
import widgetUtils
|
||||
|
||||
class soundsTutorialDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self, actions):
|
||||
super(soundsTutorialDialog, self).__init__(None, -1)
|
||||
self.SetTitle(_(u"Sounds tutorial"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
label = wx.StaticText(panel, -1, _(u"Press enter to listen to the sound for the selected event"))
|
||||
self.items = wx.ListBox(panel, 1, choices=actions, style=wx.LB_SINGLE)
|
||||
self.items.SetSelection(0)
|
||||
listBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
listBox.Add(label)
|
||||
listBox.Add(self.items)
|
||||
self.play = wx.Button(panel, 1, (u"Play"))
|
||||
self.play.SetDefault()
|
||||
close = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.play)
|
||||
btnBox.Add(close)
|
||||
sizer.Add(listBox)
|
||||
sizer.Add(btnBox)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
def __init__(self, actions):
|
||||
super(soundsTutorialDialog, self).__init__(None, -1)
|
||||
self.SetTitle(_(u"Sounds tutorial"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
label = wx.StaticText(panel, -1, _(u"Press enter to listen to the sound for the selected event"))
|
||||
self.items = wx.ListBox(panel, 1, choices=actions, style=wx.LB_SINGLE)
|
||||
self.items.SetSelection(0)
|
||||
listBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
listBox.Add(label)
|
||||
listBox.Add(self.items)
|
||||
self.play = wx.Button(panel, 1, (u"Play"))
|
||||
self.play.SetDefault()
|
||||
close = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.play)
|
||||
btnBox.Add(close)
|
||||
sizer.Add(listBox)
|
||||
sizer.Add(btnBox)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def get_selection(self):
|
||||
return self.items.GetSelection()
|
||||
def get_selection(self):
|
||||
return self.items.GetSelection()
|
||||
|
@@ -3,4 +3,4 @@ from __future__ import unicode_literals
|
||||
from . import spellchecker
|
||||
import platform
|
||||
if platform.system() == "Windows":
|
||||
from .wx_ui import *
|
||||
from .wx_ui import *
|
||||
|
@@ -19,69 +19,69 @@ from enchant import tokenize
|
||||
log = logging.getLogger("extra.SpellChecker.spellChecker")
|
||||
|
||||
class spellChecker(object):
|
||||
def __init__(self, text, dictionary):
|
||||
super(spellChecker, self).__init__()
|
||||
# Set Dictionary path if not set in a previous call to this method.
|
||||
# Dictionary path will be located in user config, see https://github.com/manuelcortez/twblue/issues/208
|
||||
def __init__(self, text, dictionary):
|
||||
super(spellChecker, self).__init__()
|
||||
# Set Dictionary path if not set in a previous call to this method.
|
||||
# Dictionary path will be located in user config, see https://github.com/manuelcortez/twblue/issues/208
|
||||
# dict_path = enchant.get_param("enchant.myspell.dictionary.path")
|
||||
# if dict_path == None:
|
||||
# enchant.set_param("enchant.myspell.dictionary.path", os.path.join(paths.config_path(), "dicts"))
|
||||
# log.debug("Dictionary path set to %s" % (os.path.join(paths.config_path(), "dicts"),))
|
||||
log.debug("Creating the SpellChecker object. Dictionary: %s" % (dictionary,))
|
||||
self.active = True
|
||||
try:
|
||||
if config.app["app-settings"]["language"] == "system":
|
||||
log.debug("Using the system language")
|
||||
self.dict = enchant.DictWithPWL(languageHandler.curLang[:2], os.path.join(paths.config_path(), "wordlist.dict"))
|
||||
else:
|
||||
log.debug("Using language: %s" % (languageHandler.getLanguage(),))
|
||||
self.dict = enchant.DictWithPWL(languageHandler.getLanguage()[:2], os.path.join(paths.config_path(), "wordlist.dict"))
|
||||
except DictNotFoundError:
|
||||
log.exception("Dictionary for language %s not found." % (dictionary,))
|
||||
wx_ui.dict_not_found_error()
|
||||
self.active = False
|
||||
self.checker = SpellChecker(self.dict, filters=[twitterFilter.TwitterFilter, tokenize.EmailFilter, tokenize.URLFilter])
|
||||
self.checker.set_text(text)
|
||||
if self.active == True:
|
||||
log.debug("Creating dialog...")
|
||||
self.dialog = wx_ui.spellCheckerDialog()
|
||||
widgetUtils.connect_event(self.dialog.ignore, widgetUtils.BUTTON_PRESSED, self.ignore)
|
||||
widgetUtils.connect_event(self.dialog.ignoreAll, widgetUtils.BUTTON_PRESSED, self.ignoreAll)
|
||||
widgetUtils.connect_event(self.dialog.replace, widgetUtils.BUTTON_PRESSED, self.replace)
|
||||
widgetUtils.connect_event(self.dialog.replaceAll, widgetUtils.BUTTON_PRESSED, self.replaceAll)
|
||||
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add)
|
||||
self.check()
|
||||
self.dialog.get_response()
|
||||
self.fixed_text = self.checker.get_text()
|
||||
log.debug("Creating the SpellChecker object. Dictionary: %s" % (dictionary,))
|
||||
self.active = True
|
||||
try:
|
||||
if config.app["app-settings"]["language"] == "system":
|
||||
log.debug("Using the system language")
|
||||
self.dict = enchant.DictWithPWL(languageHandler.curLang[:2], os.path.join(paths.config_path(), "wordlist.dict"))
|
||||
else:
|
||||
log.debug("Using language: %s" % (languageHandler.getLanguage(),))
|
||||
self.dict = enchant.DictWithPWL(languageHandler.getLanguage()[:2], os.path.join(paths.config_path(), "wordlist.dict"))
|
||||
except DictNotFoundError:
|
||||
log.exception("Dictionary for language %s not found." % (dictionary,))
|
||||
wx_ui.dict_not_found_error()
|
||||
self.active = False
|
||||
self.checker = SpellChecker(self.dict, filters=[twitterFilter.TwitterFilter, tokenize.EmailFilter, tokenize.URLFilter])
|
||||
self.checker.set_text(text)
|
||||
if self.active == True:
|
||||
log.debug("Creating dialog...")
|
||||
self.dialog = wx_ui.spellCheckerDialog()
|
||||
widgetUtils.connect_event(self.dialog.ignore, widgetUtils.BUTTON_PRESSED, self.ignore)
|
||||
widgetUtils.connect_event(self.dialog.ignoreAll, widgetUtils.BUTTON_PRESSED, self.ignoreAll)
|
||||
widgetUtils.connect_event(self.dialog.replace, widgetUtils.BUTTON_PRESSED, self.replace)
|
||||
widgetUtils.connect_event(self.dialog.replaceAll, widgetUtils.BUTTON_PRESSED, self.replaceAll)
|
||||
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add)
|
||||
self.check()
|
||||
self.dialog.get_response()
|
||||
self.fixed_text = self.checker.get_text()
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
next(self.checker)
|
||||
textToSay = _(u"Misspelled word: %s") % (self.checker.word,)
|
||||
context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10))
|
||||
self.dialog.set_title(textToSay)
|
||||
output.speak(textToSay)
|
||||
self.dialog.set_word_and_suggestions(word=self.checker.word, context=context, suggestions=self.checker.suggest())
|
||||
except StopIteration:
|
||||
log.debug("Process finished.")
|
||||
wx_ui.finished()
|
||||
self.dialog.Destroy()
|
||||
def check(self):
|
||||
try:
|
||||
next(self.checker)
|
||||
textToSay = _(u"Misspelled word: %s") % (self.checker.word,)
|
||||
context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10))
|
||||
self.dialog.set_title(textToSay)
|
||||
output.speak(textToSay)
|
||||
self.dialog.set_word_and_suggestions(word=self.checker.word, context=context, suggestions=self.checker.suggest())
|
||||
except StopIteration:
|
||||
log.debug("Process finished.")
|
||||
wx_ui.finished()
|
||||
self.dialog.Destroy()
|
||||
|
||||
def ignore(self, ev):
|
||||
self.check()
|
||||
def ignore(self, ev):
|
||||
self.check()
|
||||
|
||||
def ignoreAll(self, ev):
|
||||
self.checker.ignore_always(word=self.checker.word)
|
||||
self.check()
|
||||
def ignoreAll(self, ev):
|
||||
self.checker.ignore_always(word=self.checker.word)
|
||||
self.check()
|
||||
|
||||
def replace(self, ev):
|
||||
self.checker.replace(self.dialog.get_selected_suggestion())
|
||||
self.check()
|
||||
def replace(self, ev):
|
||||
self.checker.replace(self.dialog.get_selected_suggestion())
|
||||
self.check()
|
||||
|
||||
def replaceAll(self, ev):
|
||||
self.checker.replace_always(self.dialog.get_selected_suggestion())
|
||||
self.check()
|
||||
def replaceAll(self, ev):
|
||||
self.checker.replace_always(self.dialog.get_selected_suggestion())
|
||||
self.check()
|
||||
|
||||
def add(self, ev):
|
||||
self.checker.add()
|
||||
self.check()
|
||||
def add(self, ev):
|
||||
self.checker.add()
|
||||
self.check()
|
||||
|
@@ -21,63 +21,63 @@ import wx
|
||||
import application
|
||||
|
||||
class spellCheckerDialog(wx.Dialog):
|
||||
def __init__(self):
|
||||
super(spellCheckerDialog, self).__init__(None, 1)
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
word = wx.StaticText(panel, -1, _(u"Misspelled word"))
|
||||
self.word = wx.TextCtrl(panel, -1)
|
||||
wordBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
wordBox.Add(word, 0, wx.ALL, 5)
|
||||
wordBox.Add(self.word, 0, wx.ALL, 5)
|
||||
context = wx.StaticText(panel, -1, _(u"Context"))
|
||||
self.context = wx.TextCtrl(panel, -1)
|
||||
contextBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
contextBox.Add(context, 0, wx.ALL, 5)
|
||||
contextBox.Add(self.context, 0, wx.ALL, 5)
|
||||
suggest = wx.StaticText(panel, -1, _(u"Suggestions"))
|
||||
self.suggestions = wx.ListBox(panel, -1, choices=[], style=wx.LB_SINGLE)
|
||||
suggestionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
suggestionsBox.Add(suggest, 0, wx.ALL, 5)
|
||||
suggestionsBox.Add(self.suggestions, 0, wx.ALL, 5)
|
||||
self.ignore = wx.Button(panel, -1, _(u"&Ignore"))
|
||||
self.ignoreAll = wx.Button(panel, -1, _(u"I&gnore all"))
|
||||
self.replace = wx.Button(panel, -1, _(u"&Replace"))
|
||||
self.replaceAll = wx.Button(panel, -1, _(u"R&eplace all"))
|
||||
self.add = wx.Button(panel, -1, _(u"&Add to personal dictionary"))
|
||||
close = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.ignore, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.ignoreAll, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.replace, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.replaceAll, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.add, 0, wx.ALL, 5)
|
||||
btnBox.Add(close, 0, wx.ALL, 5)
|
||||
sizer.Add(wordBox, 0, wx.ALL, 5)
|
||||
sizer.Add(contextBox, 0, wx.ALL, 5)
|
||||
sizer.Add(suggestionsBox, 0, wx.ALL, 5)
|
||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
def __init__(self):
|
||||
super(spellCheckerDialog, self).__init__(None, 1)
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
word = wx.StaticText(panel, -1, _(u"Misspelled word"))
|
||||
self.word = wx.TextCtrl(panel, -1)
|
||||
wordBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
wordBox.Add(word, 0, wx.ALL, 5)
|
||||
wordBox.Add(self.word, 0, wx.ALL, 5)
|
||||
context = wx.StaticText(panel, -1, _(u"Context"))
|
||||
self.context = wx.TextCtrl(panel, -1)
|
||||
contextBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
contextBox.Add(context, 0, wx.ALL, 5)
|
||||
contextBox.Add(self.context, 0, wx.ALL, 5)
|
||||
suggest = wx.StaticText(panel, -1, _(u"Suggestions"))
|
||||
self.suggestions = wx.ListBox(panel, -1, choices=[], style=wx.LB_SINGLE)
|
||||
suggestionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
suggestionsBox.Add(suggest, 0, wx.ALL, 5)
|
||||
suggestionsBox.Add(self.suggestions, 0, wx.ALL, 5)
|
||||
self.ignore = wx.Button(panel, -1, _(u"&Ignore"))
|
||||
self.ignoreAll = wx.Button(panel, -1, _(u"I&gnore all"))
|
||||
self.replace = wx.Button(panel, -1, _(u"&Replace"))
|
||||
self.replaceAll = wx.Button(panel, -1, _(u"R&eplace all"))
|
||||
self.add = wx.Button(panel, -1, _(u"&Add to personal dictionary"))
|
||||
close = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.ignore, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.ignoreAll, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.replace, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.replaceAll, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.add, 0, wx.ALL, 5)
|
||||
btnBox.Add(close, 0, wx.ALL, 5)
|
||||
sizer.Add(wordBox, 0, wx.ALL, 5)
|
||||
sizer.Add(contextBox, 0, wx.ALL, 5)
|
||||
sizer.Add(suggestionsBox, 0, wx.ALL, 5)
|
||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
|
||||
def get_response(self):
|
||||
return self.ShowModal()
|
||||
def get_response(self):
|
||||
return self.ShowModal()
|
||||
|
||||
def set_title(self, title):
|
||||
return self.SetTitle(title)
|
||||
def set_title(self, title):
|
||||
return self.SetTitle(title)
|
||||
|
||||
def set_word_and_suggestions(self, word, context, suggestions):
|
||||
self.word.SetValue(word)
|
||||
self.context.ChangeValue(context)
|
||||
self.suggestions.Set(suggestions)
|
||||
self.suggestions.SetFocus()
|
||||
def set_word_and_suggestions(self, word, context, suggestions):
|
||||
self.word.SetValue(word)
|
||||
self.context.ChangeValue(context)
|
||||
self.suggestions.Set(suggestions)
|
||||
self.suggestions.SetFocus()
|
||||
|
||||
def get_selected_suggestion(self):
|
||||
return self.suggestions.GetStringSelection()
|
||||
def get_selected_suggestion(self):
|
||||
return self.suggestions.GetStringSelection()
|
||||
|
||||
def dict_not_found_error():
|
||||
wx.MessageDialog(None, _(u"An error has occurred. There are no dictionaries available for the selected language in {0}").format(application.name,), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
wx.MessageDialog(None, _(u"An error has occurred. There are no dictionaries available for the selected language in {0}").format(application.name,), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def finished():
|
||||
wx.MessageDialog(None, _(u"Spell check complete."), application.name, style=wx.OK).ShowModal()
|
||||
wx.MessageDialog(None, _(u"Spell check complete."), application.name, style=wx.OK).ShowModal()
|
||||
|
@@ -2,4 +2,4 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import completion, settings
|
||||
from . import completion, settings
|
||||
|
@@ -1,50 +1,47 @@
|
||||
# -*- 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
|
||||
|
||||
class autocompletionUsers(object):
|
||||
def __init__(self, window, session_id):
|
||||
super(autocompletionUsers, self).__init__()
|
||||
self.window = window
|
||||
self.db = storage.storage(session_id)
|
||||
def __init__(self, window, session_id):
|
||||
super(autocompletionUsers, self).__init__()
|
||||
self.window = window
|
||||
self.db = storage.storage(session_id)
|
||||
|
||||
def show_menu(self, mode="tweet"):
|
||||
position = self.window.get_position()
|
||||
if mode == "tweet":
|
||||
text = self.window.get_text()
|
||||
text = text[:position]
|
||||
try:
|
||||
pattern = text.split()[-1]
|
||||
except IndexError:
|
||||
output.speak(_(u"You have to start writing"))
|
||||
return
|
||||
if pattern.startswith("@") == True:
|
||||
menu = wx_menu.menu(self.window.text, pattern[1:], mode=mode)
|
||||
users = self.db.get_users(pattern[1:])
|
||||
if len(users) > 0:
|
||||
menu.append_options(users)
|
||||
self.window.popup_menu(menu)
|
||||
menu.destroy()
|
||||
else:
|
||||
output.speak(_(u"There are no results in your users database"))
|
||||
else:
|
||||
output.speak(_(u"Autocompletion only works for users."))
|
||||
elif mode == "dm":
|
||||
text = self.window.get_user()
|
||||
try:
|
||||
pattern = text.split()[-1]
|
||||
except IndexError:
|
||||
output.speak(_(u"You have to start writing"))
|
||||
return
|
||||
menu = wx_menu.menu(self.window.cb, pattern, mode=mode)
|
||||
users = self.db.get_users(pattern)
|
||||
if len(users) > 0:
|
||||
menu.append_options(users)
|
||||
self.window.popup_menu(menu)
|
||||
menu.destroy()
|
||||
else:
|
||||
output.speak(_(u"There are no results in your users database"))
|
||||
def show_menu(self, mode="tweet"):
|
||||
position = self.window.get_position()
|
||||
if mode == "tweet":
|
||||
text = self.window.get_text()
|
||||
text = text[:position]
|
||||
try:
|
||||
pattern = text.split()[-1]
|
||||
except IndexError:
|
||||
output.speak(_(u"You have to start writing"))
|
||||
return
|
||||
if pattern.startswith("@") == True:
|
||||
menu = wx_menu.menu(self.window.text, pattern[1:], mode=mode)
|
||||
users = self.db.get_users(pattern[1:])
|
||||
if len(users) > 0:
|
||||
menu.append_options(users)
|
||||
self.window.popup_menu(menu)
|
||||
menu.destroy()
|
||||
else:
|
||||
output.speak(_(u"There are no results in your users database"))
|
||||
else:
|
||||
output.speak(_(u"Autocompletion only works for users."))
|
||||
elif mode == "dm":
|
||||
text = self.window.get_user()
|
||||
try:
|
||||
pattern = text.split()[-1]
|
||||
except IndexError:
|
||||
output.speak(_(u"You have to start writing"))
|
||||
return
|
||||
menu = wx_menu.menu(self.window.cb, pattern, mode=mode)
|
||||
users = self.db.get_users(pattern)
|
||||
if len(users) > 0:
|
||||
menu.append_options(users)
|
||||
self.window.popup_menu(menu)
|
||||
menu.destroy()
|
||||
else:
|
||||
output.speak(_(u"There are no results in your users database"))
|
||||
|
@@ -1,47 +1,44 @@
|
||||
# -*- 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):
|
||||
def __init__(self, session):
|
||||
super(autocompletionManage, self).__init__()
|
||||
self.session = session
|
||||
self.dialog = wx_manage.autocompletionManageDialog()
|
||||
self.database = storage.storage(self.session.session_id)
|
||||
self.users = self.database.get_all_users()
|
||||
self.dialog.put_users(self.users)
|
||||
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user)
|
||||
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_user)
|
||||
self.dialog.get_response()
|
||||
def __init__(self, session):
|
||||
super(autocompletionManage, self).__init__()
|
||||
self.session = session
|
||||
self.dialog = wx_manage.autocompletionManageDialog()
|
||||
self.database = storage.storage(self.session.session_id)
|
||||
self.users = self.database.get_all_users()
|
||||
self.dialog.put_users(self.users)
|
||||
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user)
|
||||
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_user)
|
||||
self.dialog.get_response()
|
||||
|
||||
def update_list(self):
|
||||
item = self.dialog.users.get_selected()
|
||||
self.dialog.users.clear()
|
||||
self.users = self.database.get_all_users()
|
||||
self.dialog.put_users(self.users)
|
||||
self.dialog.users.select_item(item)
|
||||
def update_list(self):
|
||||
item = self.dialog.users.get_selected()
|
||||
self.dialog.users.clear()
|
||||
self.users = self.database.get_all_users()
|
||||
self.dialog.put_users(self.users)
|
||||
self.dialog.users.select_item(item)
|
||||
|
||||
def add_user(self, *args, **kwargs):
|
||||
usr = self.dialog.get_user()
|
||||
if usr == False:
|
||||
return
|
||||
try:
|
||||
data = self.session.twitter.twitter.show_user(screen_name=usr)
|
||||
except:
|
||||
self.dialog.show_invalid_user_error()
|
||||
return
|
||||
self.database.set_user(data["screen_name"], data["name"], 0)
|
||||
self.update_list()
|
||||
def add_user(self, *args, **kwargs):
|
||||
usr = self.dialog.get_user()
|
||||
if usr == False:
|
||||
return
|
||||
try:
|
||||
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.update_list()
|
||||
|
||||
def remove_user(self, ev):
|
||||
if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES:
|
||||
item = self.dialog.users.get_selected()
|
||||
user = self.users[item]
|
||||
self.database.remove_user(user[0])
|
||||
self.update_list()
|
||||
def remove_user(self, ev):
|
||||
if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES:
|
||||
item = self.dialog.users.get_selected()
|
||||
user = self.users[item]
|
||||
self.database.remove_user(user[0])
|
||||
self.update_list()
|
||||
|
@@ -1,63 +1,59 @@
|
||||
# -*- 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):
|
||||
def __init__(self, config, buffer, window):
|
||||
super(autocompletionSettings, self).__init__()
|
||||
self.config = config
|
||||
self.buffer = buffer
|
||||
self.window = window
|
||||
self.dialog = wx_settings.autocompletionSettingsDialog()
|
||||
self.dialog.set("friends_buffer", self.config["mysc"]["save_friends_in_autocompletion_db"])
|
||||
self.dialog.set("followers_buffer", self.config["mysc"]["save_followers_in_autocompletion_db"])
|
||||
widgetUtils.connect_event(self.dialog.viewList, widgetUtils.BUTTON_PRESSED, self.view_list)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
call_threaded(self.add_users_to_database)
|
||||
def __init__(self, config, buffer, window):
|
||||
super(autocompletionSettings, self).__init__()
|
||||
self.config = config
|
||||
self.buffer = buffer
|
||||
self.window = window
|
||||
self.dialog = wx_settings.autocompletionSettingsDialog()
|
||||
self.dialog.set("friends_buffer", self.config["mysc"]["save_friends_in_autocompletion_db"])
|
||||
self.dialog.set("followers_buffer", self.config["mysc"]["save_followers_in_autocompletion_db"])
|
||||
widgetUtils.connect_event(self.dialog.viewList, widgetUtils.BUTTON_PRESSED, self.view_list)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
call_threaded(self.add_users_to_database)
|
||||
|
||||
def add_users_to_database(self):
|
||||
self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends_buffer")
|
||||
self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers_buffer")
|
||||
output.speak(_(u"Updating database... You can close this window now. A message will tell you when the process finishes."))
|
||||
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)
|
||||
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)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
||||
wx_settings.show_success_dialog()
|
||||
self.dialog.destroy()
|
||||
|
||||
def view_list(self, ev):
|
||||
q = manage.autocompletionManage(self.buffer.session)
|
||||
def add_users_to_database(self):
|
||||
self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends_buffer")
|
||||
self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers_buffer")
|
||||
output.speak(_(u"Updating database... You can close this window now. A message will tell you when the process finishes."))
|
||||
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]:
|
||||
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]:
|
||||
database.set_user(i.screen_name, i.name, 2)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
||||
wx_settings.show_success_dialog()
|
||||
self.dialog.destroy()
|
||||
|
||||
def view_list(self, ev):
|
||||
q = manage.autocompletionManage(self.buffer.session)
|
||||
|
||||
|
||||
def execute_at_startup(window, buffer, config):
|
||||
database = storage.storage(buffer.session.session_id)
|
||||
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)
|
||||
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)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
||||
database = storage.storage(buffer.session.session_id)
|
||||
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)
|
||||
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)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
||||
|
@@ -1,54 +1,52 @@
|
||||
# -*- 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.cursor = self.connection.cursor()
|
||||
if self.table_exist("users") == False:
|
||||
self.create_table()
|
||||
def __init__(self, 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()
|
||||
|
||||
def table_exist(self, table):
|
||||
ask = self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='%s'" % (table))
|
||||
answer = ask.fetchone()
|
||||
if answer == None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
def table_exist(self, table):
|
||||
ask = self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='%s'" % (table))
|
||||
answer = ask.fetchone()
|
||||
if answer == None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_all_users(self):
|
||||
self.cursor.execute("""select * from users""")
|
||||
return self.cursor.fetchall()
|
||||
def get_all_users(self):
|
||||
self.cursor.execute("""select * from users""")
|
||||
return self.cursor.fetchall()
|
||||
|
||||
def get_users(self, term):
|
||||
self.cursor.execute("""SELECT * FROM users WHERE user LIKE ?""", ('{}%'.format(term),))
|
||||
return self.cursor.fetchall()
|
||||
def get_users(self, 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):
|
||||
self.cursor.execute("""insert or ignore into users values(?, ?, ?)""", (screen_name, user_name, from_a_buffer))
|
||||
self.connection.commit()
|
||||
def set_user(self, screen_name, user_name, from_a_buffer):
|
||||
self.cursor.execute("""insert or ignore into users values(?, ?, ?)""", (screen_name, user_name, from_a_buffer))
|
||||
self.connection.commit()
|
||||
|
||||
def remove_user(self, user):
|
||||
self.cursor.execute("""DELETE FROM users WHERE user = ?""", (user,))
|
||||
self.connection.commit()
|
||||
return self.cursor.fetchone()
|
||||
def remove_user(self, user):
|
||||
self.cursor.execute("""DELETE FROM users WHERE user = ?""", (user,))
|
||||
self.connection.commit()
|
||||
return self.cursor.fetchone()
|
||||
|
||||
def remove_by_buffer(self, bufferType):
|
||||
""" Removes all users saved on a buffer. BufferType is 0 for no buffer, 1 for friends and 2 for followers"""
|
||||
self.cursor.execute("""DELETE FROM users WHERE from_a_buffer = ?""", (bufferType,))
|
||||
self.connection.commit()
|
||||
return self.cursor.fetchone()
|
||||
def remove_by_buffer(self, bufferType):
|
||||
""" Removes all users saved on a buffer. BufferType is 0 for no buffer, 1 for friends and 2 for followers"""
|
||||
self.cursor.execute("""DELETE FROM users WHERE from_a_buffer = ?""", (bufferType,))
|
||||
self.connection.commit()
|
||||
return self.cursor.fetchone()
|
||||
|
||||
def create_table(self):
|
||||
self.cursor.execute("""
|
||||
def create_table(self):
|
||||
self.cursor.execute("""
|
||||
create table users(
|
||||
user TEXT UNIQUE,
|
||||
name TEXT,
|
||||
from_a_buffer INTEGER
|
||||
)""")
|
||||
|
||||
def __del__(self):
|
||||
self.cursor.close()
|
||||
self.connection.close()
|
||||
def __del__(self):
|
||||
self.cursor.close()
|
||||
self.connection.close()
|
||||
|
@@ -1,44 +1,44 @@
|
||||
# -*- 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"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
label = wx.StaticText(panel, -1, _(u"Editing {0} users database").format(application.name,))
|
||||
self.users = widgets.list(panel, _(u"Username"), _(u"Name"), style=wx.LC_REPORT)
|
||||
sizer.Add(label, 0, wx.ALL, 5)
|
||||
sizer.Add(self.users.list, 0, wx.ALL, 5)
|
||||
self.add = wx.Button(panel, -1, _(u"Add user"))
|
||||
self.remove = wx.Button(panel, -1, _(u"Remove user"))
|
||||
optionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
optionsBox.Add(self.add, 0, wx.ALL, 5)
|
||||
optionsBox.Add(self.remove, 0, wx.ALL, 5)
|
||||
sizer.Add(optionsBox, 0, wx.ALL, 5)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
def __init__(self):
|
||||
super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocompletion database"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
label = wx.StaticText(panel, -1, _(u"Editing {0} users database").format(application.name,))
|
||||
self.users = widgets.list(panel, _(u"Username"), _(u"Name"), style=wx.LC_REPORT)
|
||||
sizer.Add(label, 0, wx.ALL, 5)
|
||||
sizer.Add(self.users.list, 0, wx.ALL, 5)
|
||||
self.add = wx.Button(panel, -1, _(u"Add user"))
|
||||
self.remove = wx.Button(panel, -1, _(u"Remove user"))
|
||||
optionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
optionsBox.Add(self.add, 0, wx.ALL, 5)
|
||||
optionsBox.Add(self.remove, 0, wx.ALL, 5)
|
||||
sizer.Add(optionsBox, 0, wx.ALL, 5)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def put_users(self, users):
|
||||
for i in users:
|
||||
j = [i[0], i[1]]
|
||||
self.users.insert_item(False, *j)
|
||||
def put_users(self, users):
|
||||
for i in users:
|
||||
j = [i[0], i[1]]
|
||||
self.users.insert_item(False, *j)
|
||||
|
||||
def get_user(self):
|
||||
usr = False
|
||||
userDlg = wx.TextEntryDialog(None, _(u"Twitter username"), _(u"Add user to database"))
|
||||
if userDlg.ShowModal() == wx.ID_OK:
|
||||
usr = userDlg.GetValue()
|
||||
return usr
|
||||
def get_user(self):
|
||||
usr = False
|
||||
userDlg = wx.TextEntryDialog(None, _(u"Twitter username"), _(u"Add user to database"))
|
||||
if userDlg.ShowModal() == wx.ID_OK:
|
||||
usr = userDlg.GetValue()
|
||||
return usr
|
||||
|
||||
def show_invalid_user_error(self):
|
||||
wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error!"), wx.ICON_ERROR).ShowModal()
|
||||
def show_invalid_user_error(self):
|
||||
wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error!"), wx.ICON_ERROR).ShowModal()
|
||||
|
@@ -1,26 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import wx
|
||||
|
||||
class menu(wx.Menu):
|
||||
def __init__(self, window, pattern, mode):
|
||||
super(menu, self).__init__()
|
||||
self.window = window
|
||||
self.pattern = pattern
|
||||
self.mode = mode
|
||||
def __init__(self, window, pattern, mode):
|
||||
super(menu, self).__init__()
|
||||
self.window = window
|
||||
self.pattern = pattern
|
||||
self.mode = mode
|
||||
|
||||
def append_options(self, options):
|
||||
for i in options:
|
||||
item = wx.MenuItem(self, wx.ID_ANY, "%s (@%s)" % (i[1], i[0]))
|
||||
self.AppendItem(item)
|
||||
self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item)
|
||||
def append_options(self, options):
|
||||
for i in options:
|
||||
item = wx.MenuItem(self, wx.ID_ANY, "%s (@%s)" % (i[1], i[0]))
|
||||
self.AppendItem(item)
|
||||
self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item)
|
||||
|
||||
def select_text(self, ev, text):
|
||||
if self.mode == "tweet":
|
||||
self.window.ChangeValue(self.window.GetValue().replace("@"+self.pattern, "@"+text+" "))
|
||||
elif self.mode == "dm":
|
||||
self.window.SetValue(self.window.GetValue().replace(self.pattern, text))
|
||||
self.window.SetInsertionPointEnd()
|
||||
def select_text(self, ev, text):
|
||||
if self.mode == "tweet":
|
||||
self.window.ChangeValue(self.window.GetValue().replace("@"+self.pattern, "@"+text+" "))
|
||||
elif self.mode == "dm":
|
||||
self.window.SetValue(self.window.GetValue().replace(self.pattern, text))
|
||||
self.window.SetInsertionPointEnd()
|
||||
|
||||
def destroy(self):
|
||||
self.Destroy()
|
||||
def destroy(self):
|
||||
self.Destroy()
|
||||
|
@@ -1,28 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import wx
|
||||
import widgetUtils
|
||||
import application
|
||||
|
||||
class autocompletionSettingsDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self):
|
||||
super(autocompletionSettingsDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.followers_buffer = wx.CheckBox(panel, -1, _(u"Add users from followers buffer"))
|
||||
self.friends_buffer = wx.CheckBox(panel, -1, _(u"Add users from friends buffer"))
|
||||
sizer.Add(self.followers_buffer, 0, wx.ALL, 5)
|
||||
sizer.Add(self.friends_buffer, 0, wx.ALL, 5)
|
||||
self.viewList = wx.Button(panel, -1, _(u"Manage database..."))
|
||||
sizer.Add(self.viewList, 0, wx.ALL, 5)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
def __init__(self):
|
||||
super(autocompletionSettingsDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.followers_buffer = wx.CheckBox(panel, -1, _(u"Add users from followers buffer"))
|
||||
self.friends_buffer = wx.CheckBox(panel, -1, _(u"Add users from friends buffer"))
|
||||
sizer.Add(self.followers_buffer, 0, wx.ALL, 5)
|
||||
sizer.Add(self.friends_buffer, 0, wx.ALL, 5)
|
||||
self.viewList = wx.Button(panel, -1, _(u"Manage database..."))
|
||||
sizer.Add(self.viewList, 0, wx.ALL, 5)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def show_success_dialog():
|
||||
wx.MessageDialog(None, _(u"{0}'s database of users has been updated.").format(application.name,), _(u"Done"), wx.OK).ShowModal()
|
||||
wx.MessageDialog(None, _(u"{0}'s database of users has been updated.").format(application.name,), _(u"Done"), wx.OK).ShowModal()
|
||||
|
@@ -9,37 +9,37 @@ short_langs = ["", "da", "du", "en", "fi", "fr", "de", "hu", "ko", "it", "ja", "
|
||||
OcrLangs = ["", "dan", "dut", "eng", "fin", "fre", "ger", "hun", "kor", "ita", "jpn", "pol", "por", "rus", "spa", "tur"]
|
||||
|
||||
class APIError(Exception):
|
||||
pass
|
||||
pass
|
||||
|
||||
class OCRSpaceAPI(object):
|
||||
|
||||
def __init__(self, key="4e72ae996f88957", url='https://api.ocr.space/parse/image'):
|
||||
self.key = key
|
||||
self.url = url
|
||||
def __init__(self, key="4e72ae996f88957", url='https://api.ocr.space/parse/image'):
|
||||
self.key = key
|
||||
self.url = url
|
||||
|
||||
def OCR_URL(self, url, overlay=False, lang=None):
|
||||
payload = {
|
||||
'url': url,
|
||||
'isOverlayRequired': overlay,
|
||||
'apikey': self.key,
|
||||
}
|
||||
if lang != None:
|
||||
payload.update(language=lang)
|
||||
r = requests.post(self.url, data=payload)
|
||||
result = r.json()['ParsedResults'][0]
|
||||
if result['ErrorMessage']:
|
||||
raise APIError(result['ErrorMessage'])
|
||||
return result
|
||||
def OCR_URL(self, url, overlay=False, lang=None):
|
||||
payload = {
|
||||
'url': url,
|
||||
'isOverlayRequired': overlay,
|
||||
'apikey': self.key,
|
||||
}
|
||||
if lang != None:
|
||||
payload.update(language=lang)
|
||||
r = requests.post(self.url, data=payload)
|
||||
result = r.json()['ParsedResults'][0]
|
||||
if result['ErrorMessage']:
|
||||
raise APIError(result['ErrorMessage'])
|
||||
return result
|
||||
|
||||
def OCR_file(self, fileobj, overlay=False):
|
||||
payload = {
|
||||
'isOverlayRequired': overlay,
|
||||
'apikey': self.key,
|
||||
'lang': 'es',
|
||||
}
|
||||
r = requests.post(self.url, data=payload, files={'file': fileobj})
|
||||
results = r.json()['ParsedResults']
|
||||
if results[0]['ErrorMessage']:
|
||||
raise APIError(results[0]['ErrorMessage'])
|
||||
return results
|
||||
def OCR_file(self, fileobj, overlay=False):
|
||||
payload = {
|
||||
'isOverlayRequired': overlay,
|
||||
'apikey': self.key,
|
||||
'lang': 'es',
|
||||
}
|
||||
r = requests.post(self.url, data=payload, files={'file': fileobj})
|
||||
results = r.json()['ParsedResults']
|
||||
if results[0]['ErrorMessage']:
|
||||
raise APIError(results[0]['ErrorMessage'])
|
||||
return results
|
||||
|
||||
|
@@ -2,4 +2,4 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import OCRSpace
|
||||
from . import OCRSpace
|
||||
|
@@ -4,5 +4,5 @@ from __future__ import unicode_literals
|
||||
from . import translator
|
||||
import platform
|
||||
if platform.system() == "Windows":
|
||||
from . import wx_ui as gui
|
||||
|
||||
from . import wx_ui as gui
|
||||
|
||||
|
@@ -1,115 +1,116 @@
|
||||
# -*- 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"),
|
||||
"am": _(u"Amharic"),
|
||||
"ar": _(u"Arabic"),
|
||||
"hy": _(u"Armenian"),
|
||||
"az": _(u"Azerbaijani"),
|
||||
"eu": _(u"Basque"),
|
||||
"be": _(u"Belarusian"),
|
||||
"bn": _(u"Bengali"),
|
||||
"bh": _(u"Bihari"),
|
||||
"bg": _(u"Bulgarian"),
|
||||
"my": _(u"Burmese"),
|
||||
"ca": _(u"Catalan"),
|
||||
"chr": _(u"Cherokee"),
|
||||
"zh": _(u"Chinese"),
|
||||
"zh-CN": _(u"Chinese_simplified"),
|
||||
"zh-TW": _(u"Chinese_traditional"),
|
||||
"hr": _(u"Croatian"),
|
||||
"cs": _(u"Czech"),
|
||||
"da": _(u"Danish"),
|
||||
"dv": _(u"Dhivehi"),
|
||||
"nl": _(u"Dutch"),
|
||||
"en": _(u"English"),
|
||||
"eo": _(u"Esperanto"),
|
||||
"et": _(u"Estonian"),
|
||||
"tl": _(u"Filipino"),
|
||||
"fi": _(u"Finnish"),
|
||||
"fr": _(u"French"),
|
||||
"gl": _(u"Galician"),
|
||||
"ka": _(u"Georgian"),
|
||||
"de": _(u"German"),
|
||||
"el": _(u"Greek"),
|
||||
"gn": _(u"Guarani"),
|
||||
"gu": _(u"Gujarati"),
|
||||
"iw": _(u"Hebrew"),
|
||||
"hi": _(u"Hindi"),
|
||||
"hu": _(u"Hungarian"),
|
||||
"is": _(u"Icelandic"),
|
||||
"id": _(u"Indonesian"),
|
||||
"iu": _(u"Inuktitut"),
|
||||
"ga": _(u"Irish"),
|
||||
"it": _(u"Italian"),
|
||||
"ja": _(u"Japanese"),
|
||||
"kn": _(u"Kannada"),
|
||||
"kk": _(u"Kazakh"),
|
||||
"km": _(u"Khmer"),
|
||||
"ko": _(u"Korean"),
|
||||
"ku": _(u"Kurdish"),
|
||||
"ky": _(u"Kyrgyz"),
|
||||
"lo": _(u"Laothian"),
|
||||
"lv": _(u"Latvian"),
|
||||
"lt": _(u"Lithuanian"),
|
||||
"mk": _(u"Macedonian"),
|
||||
"ms": _(u"Malay"),
|
||||
"ml": _(u"Malayalam"),
|
||||
"mt": _(u"Maltese"),
|
||||
"mr": _(u"Marathi"),
|
||||
"mn": _(u"Mongolian"),
|
||||
"ne": _(u"Nepali"),
|
||||
"no": _(u"Norwegian"),
|
||||
"or": _(u"Oriya"),
|
||||
"ps": _(u"Pashto"),
|
||||
"fa": _(u"Persian"),
|
||||
"pl": _(u"Polish"),
|
||||
"pt": _(u"Portuguese"),
|
||||
"pa": _(u"Punjabi"),
|
||||
"ro": _(u"Romanian"),
|
||||
"ru": _(u"Russian"),
|
||||
"sa": _(u"Sanskrit"),
|
||||
"sr": _(u"Serbian"),
|
||||
"sd": _(u"Sindhi"),
|
||||
"si": _(u"Sinhalese"),
|
||||
"sk": _(u"Slovak"),
|
||||
"sl": _(u"Slovenian"),
|
||||
"es": _(u"Spanish"),
|
||||
"sw": _(u"Swahili"),
|
||||
"sv": _(u"Swedish"),
|
||||
"tg": _(u"Tajik"),
|
||||
"ta": _(u"Tamil"),
|
||||
"tl": _(u"Tagalog"),
|
||||
"te": _(u"Telugu"),
|
||||
"th": _(u"Thai"),
|
||||
"bo": _(u"Tibetan"),
|
||||
"tr": _(u"Turkish"),
|
||||
"uk": _(u"Ukrainian"),
|
||||
"ur": _(u"Urdu"),
|
||||
"uz": _(u"Uzbek"),
|
||||
"ug": _(u"Uighur"),
|
||||
"vi": _(u"Vietnamese"),
|
||||
"cy": _(u"Welsh"),
|
||||
"yi": _(u"Yiddish")
|
||||
"af": _(u"Afrikaans"),
|
||||
"sq": _(u"Albanian"),
|
||||
"am": _(u"Amharic"),
|
||||
"ar": _(u"Arabic"),
|
||||
"hy": _(u"Armenian"),
|
||||
"az": _(u"Azerbaijani"),
|
||||
"eu": _(u"Basque"),
|
||||
"be": _(u"Belarusian"),
|
||||
"bn": _(u"Bengali"),
|
||||
"bh": _(u"Bihari"),
|
||||
"bg": _(u"Bulgarian"),
|
||||
"my": _(u"Burmese"),
|
||||
"ca": _(u"Catalan"),
|
||||
"chr": _(u"Cherokee"),
|
||||
"zh": _(u"Chinese"),
|
||||
"zh-CN": _(u"Chinese_simplified"),
|
||||
"zh-TW": _(u"Chinese_traditional"),
|
||||
"hr": _(u"Croatian"),
|
||||
"cs": _(u"Czech"),
|
||||
"da": _(u"Danish"),
|
||||
"dv": _(u"Dhivehi"),
|
||||
"nl": _(u"Dutch"),
|
||||
"en": _(u"English"),
|
||||
"eo": _(u"Esperanto"),
|
||||
"et": _(u"Estonian"),
|
||||
"tl": _(u"Filipino"),
|
||||
"fi": _(u"Finnish"),
|
||||
"fr": _(u"French"),
|
||||
"gl": _(u"Galician"),
|
||||
"ka": _(u"Georgian"),
|
||||
"de": _(u"German"),
|
||||
"el": _(u"Greek"),
|
||||
"gn": _(u"Guarani"),
|
||||
"gu": _(u"Gujarati"),
|
||||
"iw": _(u"Hebrew"),
|
||||
"hi": _(u"Hindi"),
|
||||
"hu": _(u"Hungarian"),
|
||||
"is": _(u"Icelandic"),
|
||||
"id": _(u"Indonesian"),
|
||||
"iu": _(u"Inuktitut"),
|
||||
"ga": _(u"Irish"),
|
||||
"it": _(u"Italian"),
|
||||
"ja": _(u"Japanese"),
|
||||
"kn": _(u"Kannada"),
|
||||
"kk": _(u"Kazakh"),
|
||||
"km": _(u"Khmer"),
|
||||
"ko": _(u"Korean"),
|
||||
"ku": _(u"Kurdish"),
|
||||
"ky": _(u"Kyrgyz"),
|
||||
"lo": _(u"Laothian"),
|
||||
"lv": _(u"Latvian"),
|
||||
"lt": _(u"Lithuanian"),
|
||||
"mk": _(u"Macedonian"),
|
||||
"ms": _(u"Malay"),
|
||||
"ml": _(u"Malayalam"),
|
||||
"mt": _(u"Maltese"),
|
||||
"mr": _(u"Marathi"),
|
||||
"mn": _(u"Mongolian"),
|
||||
"ne": _(u"Nepali"),
|
||||
"no": _(u"Norwegian"),
|
||||
"or": _(u"Oriya"),
|
||||
"ps": _(u"Pashto"),
|
||||
"fa": _(u"Persian"),
|
||||
"pl": _(u"Polish"),
|
||||
"pt": _(u"Portuguese"),
|
||||
"pa": _(u"Punjabi"),
|
||||
"ro": _(u"Romanian"),
|
||||
"ru": _(u"Russian"),
|
||||
"sa": _(u"Sanskrit"),
|
||||
"sr": _(u"Serbian"),
|
||||
"sd": _(u"Sindhi"),
|
||||
"si": _(u"Sinhalese"),
|
||||
"sk": _(u"Slovak"),
|
||||
"sl": _(u"Slovenian"),
|
||||
"es": _(u"Spanish"),
|
||||
"sw": _(u"Swahili"),
|
||||
"sv": _(u"Swedish"),
|
||||
"tg": _(u"Tajik"),
|
||||
"ta": _(u"Tamil"),
|
||||
"tl": _(u"Tagalog"),
|
||||
"te": _(u"Telugu"),
|
||||
"th": _(u"Thai"),
|
||||
"bo": _(u"Tibetan"),
|
||||
"tr": _(u"Turkish"),
|
||||
"uk": _(u"Ukrainian"),
|
||||
"ur": _(u"Urdu"),
|
||||
"uz": _(u"Uzbek"),
|
||||
"ug": _(u"Uighur"),
|
||||
"vi": _(u"Vietnamese"),
|
||||
"cy": _(u"Welsh"),
|
||||
"yi": _(u"Yiddish")
|
||||
}
|
||||
|
||||
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,46 +16,30 @@
|
||||
# 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):
|
||||
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.SetFocus()
|
||||
self.dest_lang.SetSelection(0)
|
||||
listSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
listSizer.Add(staticDest)
|
||||
listSizer.Add(self.dest_lang)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
self.SetEscapeId(wx.ID_CANCEL)
|
||||
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=languages, style = wx.CB_READONLY)
|
||||
self.dest_lang.SetFocus()
|
||||
self.dest_lang.SetSelection(0)
|
||||
listSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
listSizer.Add(staticDest)
|
||||
listSizer.Add(self.dest_lang)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
self.SetEscapeId(wx.ID_CANCEL)
|
||||
|
||||
def get(self, control):
|
||||
return getattr(self, control).GetSelection()
|
||||
def get(self, control):
|
||||
return getattr(self, control).GetSelection()
|
||||
|
@@ -9,11 +9,11 @@ from . import fix_urllib3_warnings # Avoiding some SSL warnings related to Twyth
|
||||
from . import fix_win32com
|
||||
#from . import fix_requests #fix cacert.pem location for TWBlue binary copies
|
||||
def setup():
|
||||
fix_arrow.fix()
|
||||
if hasattr(sys, "frozen"):
|
||||
fix_libloader.fix()
|
||||
fix_win32com.fix()
|
||||
fix_arrow.fix()
|
||||
if hasattr(sys, "frozen"):
|
||||
fix_libloader.fix()
|
||||
fix_win32com.fix()
|
||||
# fix_requests.fix()
|
||||
# else:
|
||||
# fix_requests.fix(False)
|
||||
fix_urllib3_warnings.fix()
|
||||
fix_urllib3_warnings.fix()
|
||||
|
@@ -1,95 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from arrow import locales
|
||||
from arrow.locales import Locale
|
||||
|
||||
def fix():
|
||||
# insert a modified function so if there is no language available in arrow, returns English locale.
|
||||
locales.get_locale = get_locale
|
||||
# insert a modified function so if there is no language available in arrow, returns English locale.
|
||||
locales.get_locale = get_locale
|
||||
|
||||
def get_locale(name):
|
||||
locale_cls = locales._locales.get(name.lower())
|
||||
if locale_cls is None:
|
||||
name = name[:2]
|
||||
locale_cls = locales._locales.get(name.lower())
|
||||
if locale_cls == None:
|
||||
return locales.EnglishLocale()
|
||||
return locale_cls()
|
||||
locale_cls = locales._locale_map.get(name.lower())
|
||||
if locale_cls is None:
|
||||
name = name[:2]
|
||||
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.
|
||||
class GalicianLocale(object):
|
||||
names = ['gl', 'gl_es', 'gl_gl']
|
||||
past = 'Hai {0}'
|
||||
future = 'En {0}'
|
||||
and_word = "e"
|
||||
|
||||
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',
|
||||
}
|
||||
timeframes = {
|
||||
'now': 'Agora',
|
||||
"second": "un segundo",
|
||||
'seconds': '{0} segundos',
|
||||
'minute': 'un minuto',
|
||||
'minutes': '{0} minutos',
|
||||
'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 = ['', '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']
|
||||
meridians = {"am": "am", "pm": "pm", "AM": "AM", "PM": "PM"}
|
||||
|
||||
class GalicianLocale(Locale):
|
||||
names = ['gl', 'gl_es', 'gl_gl']
|
||||
past = 'Fai {0}'
|
||||
future = 'En {0}'
|
||||
|
||||
timeframes = {
|
||||
'now': 'Agora mesmo',
|
||||
'seconds': 'segundos',
|
||||
'minute': 'un minuto',
|
||||
'minutes': '{0} minutos',
|
||||
'hour': 'una hora',
|
||||
'hours': '{0} horas',
|
||||
'day': 'un día',
|
||||
'days': '{0} días',
|
||||
'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']
|
||||
|
||||
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](?=[ºª]))[ºª])"
|
||||
|
||||
|
@@ -16,27 +16,27 @@ log = logging.getLogger("fixes.fix_libloader")
|
||||
fixed=False
|
||||
|
||||
def patched_getmodule(modname):
|
||||
mod=__import__(modname)
|
||||
return sys.modules[modname]
|
||||
mod=__import__(modname)
|
||||
return sys.modules[modname]
|
||||
|
||||
def load_com(*names):
|
||||
global fixed
|
||||
if fixed==False:
|
||||
gencache._GetModule=patched_getmodule
|
||||
com.prepare_gencache()
|
||||
fixed=True
|
||||
result = None
|
||||
for name in names:
|
||||
try:
|
||||
result = gencache.EnsureDispatch(name)
|
||||
break
|
||||
except com_error:
|
||||
continue
|
||||
if result is None:
|
||||
raise com_error("Unable to load any of the provided com objects.")
|
||||
return result
|
||||
global fixed
|
||||
if fixed==False:
|
||||
gencache._GetModule=patched_getmodule
|
||||
com.prepare_gencache()
|
||||
fixed=True
|
||||
result = None
|
||||
for name in names:
|
||||
try:
|
||||
result = gencache.EnsureDispatch(name)
|
||||
break
|
||||
except com_error:
|
||||
continue
|
||||
if result is None:
|
||||
raise com_error("Unable to load any of the provided com objects.")
|
||||
return result
|
||||
|
||||
def fix():
|
||||
log.debug("Applying fix for Libloader...")
|
||||
com.load_com = load_com
|
||||
log.debug("Load_com has been mapped correctly.")
|
||||
log.debug("Applying fix for Libloader...")
|
||||
com.load_com = load_com
|
||||
log.debug("Load_com has been mapped correctly.")
|
||||
|
@@ -7,6 +7,6 @@ import logging
|
||||
log = logging.getLogger("fixes.fix_requests")
|
||||
|
||||
def fix():
|
||||
log.debug("Applying fix for requests...")
|
||||
os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(paths.app_path(), "certifi", "cacert.pem")#.encode(paths.fsencoding)
|
||||
# log.debug("Changed CA path to %s" % (os.environ["REQUESTS_CA_BUNDLE"]))#.decode(paths.fsencoding)))
|
||||
log.debug("Applying fix for requests...")
|
||||
os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(paths.app_path(), "certifi", "cacert.pem")#.encode(paths.fsencoding)
|
||||
# log.debug("Changed CA path to %s" % (os.environ["REQUESTS_CA_BUNDLE"]))#.decode(paths.fsencoding)))
|
||||
|
@@ -8,8 +8,8 @@ import six
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
|
||||
def fix():
|
||||
urllib3.disable_warnings()
|
||||
fields.format_header_param=patched_format_header_param
|
||||
urllib3.disable_warnings()
|
||||
fields.format_header_param=patched_format_header_param
|
||||
|
||||
def patched_format_header_param(name, value):
|
||||
if not any(ch in value for ch in '"\\\r\n'):
|
||||
|
@@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
import win32com.client
|
||||
def fix():
|
||||
if win32com.client.gencache.is_readonly == True:
|
||||
win32com.client.gencache.is_readonly = False
|
||||
win32com.client.gencache.Rebuild()
|
||||
if win32com.client.gencache.is_readonly == True:
|
||||
win32com.client.gencache.is_readonly = False
|
||||
win32com.client.gencache.Rebuild()
|
||||
|
@@ -27,40 +27,40 @@ from suds.client import Client
|
||||
import constants
|
||||
|
||||
class reportBug(object):
|
||||
def __init__(self, user_name):
|
||||
self.user_name = user_name
|
||||
self.categories = [_(u"General")]
|
||||
self.reproducibilities = [_(u"always"), _(u"sometimes"), _(u"random"), _(u"have not tried"), _(u"unable to duplicate")]
|
||||
self.severities = [_(u"block"), _(u"crash"), _(u"major"), _(u"minor"), _(u"tweak"), _(u"text"), _(u"trivial"), _(u"feature")]
|
||||
self.dialog = wx_ui.reportBugDialog(self.categories, self.reproducibilities, self.severities)
|
||||
widgetUtils.connect_event(self.dialog.ok, widgetUtils.BUTTON_PRESSED, self.send)
|
||||
self.dialog.get_response()
|
||||
def __init__(self, user_name):
|
||||
self.user_name = user_name
|
||||
self.categories = [_(u"General")]
|
||||
self.reproducibilities = [_(u"always"), _(u"sometimes"), _(u"random"), _(u"have not tried"), _(u"unable to duplicate")]
|
||||
self.severities = [_(u"block"), _(u"crash"), _(u"major"), _(u"minor"), _(u"tweak"), _(u"text"), _(u"trivial"), _(u"feature")]
|
||||
self.dialog = wx_ui.reportBugDialog(self.categories, self.reproducibilities, self.severities)
|
||||
widgetUtils.connect_event(self.dialog.ok, widgetUtils.BUTTON_PRESSED, self.send)
|
||||
self.dialog.get_response()
|
||||
|
||||
def send(self, *args, **kwargs):
|
||||
if self.dialog.get("summary") == "" or self.dialog.get("description") == "":
|
||||
self.dialog.no_filled()
|
||||
return
|
||||
if self.dialog.get("agree") == False:
|
||||
self.dialog.no_checkbox()
|
||||
return
|
||||
try:
|
||||
client = Client(application.report_bugs_url)
|
||||
issue = client.factory.create('IssueData')
|
||||
issue.project.name = application.name
|
||||
issue.project.id = 0
|
||||
issue.summary = self.dialog.get("summary"),
|
||||
issue.description = "Reported by @%s on version %s (snapshot = %s)\n\n" % (self.user_name, application.version, application.snapshot) + self.dialog.get("description")
|
||||
# to do: Create getters for category, severity and reproducibility in wx_UI.
|
||||
issue.category = constants.categories[self.dialog.category.GetSelection()]
|
||||
issue.reproducibility.name = constants.reproducibilities[self.dialog.reproducibility.GetSelection()]
|
||||
issue.severity.name = constants.severities[self.dialog.severity.GetSelection()]
|
||||
issue.priority.name = "normal"
|
||||
issue.view_state.name = "public"
|
||||
issue.resolution.name = "open"
|
||||
issue.projection.name = "none"
|
||||
issue.eta.name = "eta"
|
||||
issue.status.name = "new"
|
||||
id = client.service.mc_issue_add(keys.keyring.get("bts_user"), keys.keyring.get("bts_password"), issue)
|
||||
self.dialog.success(id)
|
||||
except:
|
||||
self.dialog.error()
|
||||
def send(self, *args, **kwargs):
|
||||
if self.dialog.get("summary") == "" or self.dialog.get("description") == "":
|
||||
self.dialog.no_filled()
|
||||
return
|
||||
if self.dialog.get("agree") == False:
|
||||
self.dialog.no_checkbox()
|
||||
return
|
||||
try:
|
||||
client = Client(application.report_bugs_url)
|
||||
issue = client.factory.create('IssueData')
|
||||
issue.project.name = application.name
|
||||
issue.project.id = 0
|
||||
issue.summary = self.dialog.get("summary"),
|
||||
issue.description = "Reported by @%s on version %s (snapshot = %s)\n\n" % (self.user_name, application.version, application.snapshot) + self.dialog.get("description")
|
||||
# to do: Create getters for category, severity and reproducibility in wx_UI.
|
||||
issue.category = constants.categories[self.dialog.category.GetSelection()]
|
||||
issue.reproducibility.name = constants.reproducibilities[self.dialog.reproducibility.GetSelection()]
|
||||
issue.severity.name = constants.severities[self.dialog.severity.GetSelection()]
|
||||
issue.priority.name = "normal"
|
||||
issue.view_state.name = "public"
|
||||
issue.resolution.name = "open"
|
||||
issue.projection.name = "none"
|
||||
issue.eta.name = "eta"
|
||||
issue.status.name = "new"
|
||||
id = client.service.mc_issue_add(keys.keyring.get("bts_user"), keys.keyring.get("bts_password"), issue)
|
||||
self.dialog.success(id)
|
||||
except:
|
||||
self.dialog.error()
|
||||
|
@@ -21,75 +21,75 @@ import wx
|
||||
import widgetUtils
|
||||
import application
|
||||
class reportBugDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self, categories, reproducibilities, severities):
|
||||
super(reportBugDialog, self).__init__(parent=None, id=wx.NewId())
|
||||
self.SetTitle(_(u"Report an error"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
categoryLabel = wx.StaticText(panel, -1, _(u"Select a category"), size=wx.DefaultSize)
|
||||
self.category = wx.ComboBox(panel, -1, choices=categories, style=wx.CB_READONLY)
|
||||
self.category.SetSelection(0)
|
||||
categoryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
categoryB.Add(categoryLabel, 0, wx.ALL, 5)
|
||||
categoryB.Add(self.category, 0, wx.ALL, 5)
|
||||
self.category.SetFocus()
|
||||
sizer.Add(categoryB, 0, wx.ALL, 5)
|
||||
summaryLabel = wx.StaticText(panel, -1, _(u"Briefly describe what happened. You will be able to thoroughly explain it later"), size=wx.DefaultSize)
|
||||
self.summary = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.summary)
|
||||
dc.SetFont(self.summary.GetFont())
|
||||
self.summary.SetSize(dc.GetTextExtent("a"*80))
|
||||
summaryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
summaryB.Add(summaryLabel, 0, wx.ALL, 5)
|
||||
summaryB.Add(self.summary, 0, wx.ALL, 5)
|
||||
sizer.Add(summaryB, 0, wx.ALL, 5)
|
||||
descriptionLabel = wx.StaticText(panel, -1, _(u"Here, you can describe the bug in detail"), size=wx.DefaultSize)
|
||||
self.description = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE)
|
||||
dc = wx.WindowDC(self.description)
|
||||
dc.SetFont(self.description.GetFont())
|
||||
(x, y, z) = dc.GetMultiLineTextExtent("0"*2000)
|
||||
self.description.SetSize((x, y))
|
||||
descBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
descBox.Add(descriptionLabel, 0, wx.ALL, 5)
|
||||
descBox.Add(self.description, 0, wx.ALL, 5)
|
||||
sizer.Add(descBox, 0, wx.ALL, 5)
|
||||
reproducibilityLabel = wx.StaticText(panel, -1, _(u"how often does this bug happen?"), size=wx.DefaultSize)
|
||||
self.reproducibility = wx.ComboBox(panel, -1, choices=reproducibilities, style=wx.CB_READONLY)
|
||||
self.reproducibility.SetSelection(3)
|
||||
reprB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
reprB.Add(reproducibilityLabel, 0, wx.ALL, 5)
|
||||
reprB.Add(self.reproducibility, 0, wx.ALL, 5)
|
||||
sizer.Add(reprB, 0, wx.ALL, 5)
|
||||
severityLabel = wx.StaticText(panel, -1, _(u"Select the importance that you think this bug has"))
|
||||
self.severity = wx.ComboBox(panel, -1, choices=severities, style=wx.CB_READONLY)
|
||||
self.severity.SetSelection(3)
|
||||
severityB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
severityB.Add(severityLabel, 0, wx.ALL, 5)
|
||||
severityB.Add(self.severity, 0, wx.ALL, 5)
|
||||
sizer.Add(severityB, 0, wx.ALL, 5)
|
||||
self.agree = wx.CheckBox(panel, -1, _(u"I know that the {0} bug system will get my Twitter username to contact me and fix the bug quickly").format(application.name,))
|
||||
self.agree.SetValue(False)
|
||||
sizer.Add(self.agree, 0, wx.ALL, 5)
|
||||
self.ok = wx.Button(panel, wx.ID_OK, _(u"Send report"))
|
||||
self.ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.ok, 0, wx.ALL, 5)
|
||||
btnBox.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
def __init__(self, categories, reproducibilities, severities):
|
||||
super(reportBugDialog, self).__init__(parent=None, id=wx.NewId())
|
||||
self.SetTitle(_(u"Report an error"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
categoryLabel = wx.StaticText(panel, -1, _(u"Select a category"), size=wx.DefaultSize)
|
||||
self.category = wx.ComboBox(panel, -1, choices=categories, style=wx.CB_READONLY)
|
||||
self.category.SetSelection(0)
|
||||
categoryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
categoryB.Add(categoryLabel, 0, wx.ALL, 5)
|
||||
categoryB.Add(self.category, 0, wx.ALL, 5)
|
||||
self.category.SetFocus()
|
||||
sizer.Add(categoryB, 0, wx.ALL, 5)
|
||||
summaryLabel = wx.StaticText(panel, -1, _(u"Briefly describe what happened. You will be able to thoroughly explain it later"), size=wx.DefaultSize)
|
||||
self.summary = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.summary)
|
||||
dc.SetFont(self.summary.GetFont())
|
||||
self.summary.SetSize(dc.GetTextExtent("a"*80))
|
||||
summaryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
summaryB.Add(summaryLabel, 0, wx.ALL, 5)
|
||||
summaryB.Add(self.summary, 0, wx.ALL, 5)
|
||||
sizer.Add(summaryB, 0, wx.ALL, 5)
|
||||
descriptionLabel = wx.StaticText(panel, -1, _(u"Here, you can describe the bug in detail"), size=wx.DefaultSize)
|
||||
self.description = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE)
|
||||
dc = wx.WindowDC(self.description)
|
||||
dc.SetFont(self.description.GetFont())
|
||||
(x, y, z) = dc.GetMultiLineTextExtent("0"*2000)
|
||||
self.description.SetSize((x, y))
|
||||
descBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
descBox.Add(descriptionLabel, 0, wx.ALL, 5)
|
||||
descBox.Add(self.description, 0, wx.ALL, 5)
|
||||
sizer.Add(descBox, 0, wx.ALL, 5)
|
||||
reproducibilityLabel = wx.StaticText(panel, -1, _(u"how often does this bug happen?"), size=wx.DefaultSize)
|
||||
self.reproducibility = wx.ComboBox(panel, -1, choices=reproducibilities, style=wx.CB_READONLY)
|
||||
self.reproducibility.SetSelection(3)
|
||||
reprB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
reprB.Add(reproducibilityLabel, 0, wx.ALL, 5)
|
||||
reprB.Add(self.reproducibility, 0, wx.ALL, 5)
|
||||
sizer.Add(reprB, 0, wx.ALL, 5)
|
||||
severityLabel = wx.StaticText(panel, -1, _(u"Select the importance that you think this bug has"))
|
||||
self.severity = wx.ComboBox(panel, -1, choices=severities, style=wx.CB_READONLY)
|
||||
self.severity.SetSelection(3)
|
||||
severityB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
severityB.Add(severityLabel, 0, wx.ALL, 5)
|
||||
severityB.Add(self.severity, 0, wx.ALL, 5)
|
||||
sizer.Add(severityB, 0, wx.ALL, 5)
|
||||
self.agree = wx.CheckBox(panel, -1, _(u"I know that the {0} bug system will get my Twitter username to contact me and fix the bug quickly").format(application.name,))
|
||||
self.agree.SetValue(False)
|
||||
sizer.Add(self.agree, 0, wx.ALL, 5)
|
||||
self.ok = wx.Button(panel, wx.ID_OK, _(u"Send report"))
|
||||
self.ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.ok, 0, wx.ALL, 5)
|
||||
btnBox.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def no_filled(self):
|
||||
wx.MessageDialog(self, _(u"You must fill out both fields"), _(u"Error"), wx.OK|wx.ICON_ERROR).ShowModal()
|
||||
def no_filled(self):
|
||||
wx.MessageDialog(self, _(u"You must fill out both fields"), _(u"Error"), wx.OK|wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def no_checkbox(self):
|
||||
wx.MessageDialog(self, _(u"You need to mark the checkbox to provide us your twitter username to contact you if it is necessary."), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
def no_checkbox(self):
|
||||
wx.MessageDialog(self, _(u"You need to mark the checkbox to provide us your twitter username to contact you if it is necessary."), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def success(self, id):
|
||||
wx.MessageDialog(self, _(u"Thanks for reporting this bug! In future versions, you may be able to find it in the changes list. You've reported the bug number %i") % (id), _(u"reported"), wx.OK).ShowModal()
|
||||
self.EndModal(wx.ID_OK)
|
||||
def success(self, id):
|
||||
wx.MessageDialog(self, _(u"Thanks for reporting this bug! In future versions, you may be able to find it in the changes list. You've reported the bug number %i") % (id), _(u"reported"), wx.OK).ShowModal()
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
def error(self):
|
||||
wx.MessageDialog(self, _(u"Something unexpected occurred while trying to report the bug. Please, try again later"), _(u"Error while reporting"), wx.ICON_ERROR|wx.OK).ShowModal()
|
||||
self.EndModal(wx.ID_CANCEL)
|
||||
def error(self):
|
||||
wx.MessageDialog(self, _(u"Something unexpected occurred while trying to report the bug. Please, try again later"), _(u"Error while reporting"), wx.ICON_ERROR|wx.OK).ShowModal()
|
||||
self.EndModal(wx.ID_CANCEL)
|
||||
|
@@ -1,8 +1,8 @@
|
||||
from __future__ import absolute_import
|
||||
import platform
|
||||
if platform.system() == 'Linux':
|
||||
from .linux import LinuxKeyboardHandler as GlobalKeyboardHandler
|
||||
from .linux import LinuxKeyboardHandler as GlobalKeyboardHandler
|
||||
else:
|
||||
from .wx_handler import WXKeyboardHandler as GlobalKeyboardHandler
|
||||
from .wx_handler import WXKeyboardHandler as GlobalKeyboardHandler
|
||||
#elif platform.system() == 'Darwin':
|
||||
#from osx import OSXKeyboardHandler as GlobalKeyboardHandler
|
||||
#from osx import OSXKeyboardHandler as GlobalKeyboardHandler
|
||||
|
@@ -1,127 +1,127 @@
|
||||
keys = {
|
||||
'accept': 30,
|
||||
'add': 107,
|
||||
'apps': 93,
|
||||
'attn': 246,
|
||||
'back': 8,
|
||||
'browser_back': 166,
|
||||
'browser_forward': 167,
|
||||
'cancel': 3,
|
||||
'capital': 20,
|
||||
'clear': 12,
|
||||
'control': 17,
|
||||
'convert': 28,
|
||||
'crsel': 247,
|
||||
'decimal': 110,
|
||||
'delete': 46,
|
||||
'divide': 111,
|
||||
'down': 40,
|
||||
'end': 35,
|
||||
'ereof': 249,
|
||||
'escape': 27,
|
||||
'execute': 43,
|
||||
'exsel': 248,
|
||||
'f1': 112,
|
||||
'f10': 121,
|
||||
'f11': 122,
|
||||
'f12': 123,
|
||||
'f13': 124,
|
||||
'f14': 125,
|
||||
'f15': 126,
|
||||
'f16': 127,
|
||||
'f17': 128,
|
||||
'f18': 129,
|
||||
'f19': 130,
|
||||
'f2': 113,
|
||||
'f20': 131,
|
||||
'f21': 132,
|
||||
'f22': 133,
|
||||
'f23': 134,
|
||||
'f24': 135,
|
||||
'f3': 114,
|
||||
'f4': 115,
|
||||
'f5': 116,
|
||||
'f6': 117,
|
||||
'f7': 118,
|
||||
'f8': 119,
|
||||
'f9': 120,
|
||||
'final': 24,
|
||||
'hangeul': 21,
|
||||
'hangul': 21,
|
||||
'hanja': 25,
|
||||
'help': 47,
|
||||
'home': 36,
|
||||
'insert': 45,
|
||||
'junja': 23,
|
||||
'kana': 21,
|
||||
'kanji': 25,
|
||||
'lbutton': 1,
|
||||
'lcontrol': 162,
|
||||
'left': 37,
|
||||
'lmenu': 164,
|
||||
'lshift': 160,
|
||||
'lwin': 91,
|
||||
'mbutton': 4,
|
||||
'media_next_track': 176,
|
||||
'media_play_pause': 179,
|
||||
'media_prev_track': 177,
|
||||
'menu': 18,
|
||||
'modechange': 31,
|
||||
'multiply': 106,
|
||||
'next': 34,
|
||||
'noname': 252,
|
||||
'nonconvert': 29,
|
||||
'numlock': 144,
|
||||
'numpad0': 96,
|
||||
'numpad1': 97,
|
||||
'numpad2': 98,
|
||||
'numpad3': 99,
|
||||
'numpad4': 100,
|
||||
'numpad5': 101,
|
||||
'numpad6': 102,
|
||||
'numpad7': 103,
|
||||
'numpad8': 104,
|
||||
'numpad9': 105,
|
||||
'oem_clear': 254,
|
||||
'pa1': 253,
|
||||
'pagedown': 34,
|
||||
'pageup': 33,
|
||||
'pause': 19,
|
||||
'play': 250,
|
||||
'print': 42,
|
||||
'prior': 33,
|
||||
'processkey': 229,
|
||||
'rbutton': 2,
|
||||
'rcontrol': 163,
|
||||
'return': 13,
|
||||
'right': 39,
|
||||
'rmenu': 165,
|
||||
'rshift': 161,
|
||||
'rwin': 92,
|
||||
'scroll': 145,
|
||||
'select': 41,
|
||||
'separator': 108,
|
||||
'shift': 16,
|
||||
'snapshot': 44,
|
||||
'space': 32,
|
||||
'subtract': 109,
|
||||
'tab': 9,
|
||||
'up': 38,
|
||||
'volume_down': 174,
|
||||
'volume_mute': 173,
|
||||
'volume_up': 175,
|
||||
'xbutton1': 5,
|
||||
'xbutton2': 6,
|
||||
'zoom': 251,
|
||||
'/': 191,
|
||||
';': 218,
|
||||
'[': 219,
|
||||
'\\': 220,
|
||||
']': 221,
|
||||
'\'': 222,
|
||||
'=': 187,
|
||||
'-': 189,
|
||||
';': 186,
|
||||
'accept': 30,
|
||||
'add': 107,
|
||||
'apps': 93,
|
||||
'attn': 246,
|
||||
'back': 8,
|
||||
'browser_back': 166,
|
||||
'browser_forward': 167,
|
||||
'cancel': 3,
|
||||
'capital': 20,
|
||||
'clear': 12,
|
||||
'control': 17,
|
||||
'convert': 28,
|
||||
'crsel': 247,
|
||||
'decimal': 110,
|
||||
'delete': 46,
|
||||
'divide': 111,
|
||||
'down': 40,
|
||||
'end': 35,
|
||||
'ereof': 249,
|
||||
'escape': 27,
|
||||
'execute': 43,
|
||||
'exsel': 248,
|
||||
'f1': 112,
|
||||
'f10': 121,
|
||||
'f11': 122,
|
||||
'f12': 123,
|
||||
'f13': 124,
|
||||
'f14': 125,
|
||||
'f15': 126,
|
||||
'f16': 127,
|
||||
'f17': 128,
|
||||
'f18': 129,
|
||||
'f19': 130,
|
||||
'f2': 113,
|
||||
'f20': 131,
|
||||
'f21': 132,
|
||||
'f22': 133,
|
||||
'f23': 134,
|
||||
'f24': 135,
|
||||
'f3': 114,
|
||||
'f4': 115,
|
||||
'f5': 116,
|
||||
'f6': 117,
|
||||
'f7': 118,
|
||||
'f8': 119,
|
||||
'f9': 120,
|
||||
'final': 24,
|
||||
'hangeul': 21,
|
||||
'hangul': 21,
|
||||
'hanja': 25,
|
||||
'help': 47,
|
||||
'home': 36,
|
||||
'insert': 45,
|
||||
'junja': 23,
|
||||
'kana': 21,
|
||||
'kanji': 25,
|
||||
'lbutton': 1,
|
||||
'lcontrol': 162,
|
||||
'left': 37,
|
||||
'lmenu': 164,
|
||||
'lshift': 160,
|
||||
'lwin': 91,
|
||||
'mbutton': 4,
|
||||
'media_next_track': 176,
|
||||
'media_play_pause': 179,
|
||||
'media_prev_track': 177,
|
||||
'menu': 18,
|
||||
'modechange': 31,
|
||||
'multiply': 106,
|
||||
'next': 34,
|
||||
'noname': 252,
|
||||
'nonconvert': 29,
|
||||
'numlock': 144,
|
||||
'numpad0': 96,
|
||||
'numpad1': 97,
|
||||
'numpad2': 98,
|
||||
'numpad3': 99,
|
||||
'numpad4': 100,
|
||||
'numpad5': 101,
|
||||
'numpad6': 102,
|
||||
'numpad7': 103,
|
||||
'numpad8': 104,
|
||||
'numpad9': 105,
|
||||
'oem_clear': 254,
|
||||
'pa1': 253,
|
||||
'pagedown': 34,
|
||||
'pageup': 33,
|
||||
'pause': 19,
|
||||
'play': 250,
|
||||
'print': 42,
|
||||
'prior': 33,
|
||||
'processkey': 229,
|
||||
'rbutton': 2,
|
||||
'rcontrol': 163,
|
||||
'return': 13,
|
||||
'right': 39,
|
||||
'rmenu': 165,
|
||||
'rshift': 161,
|
||||
'rwin': 92,
|
||||
'scroll': 145,
|
||||
'select': 41,
|
||||
'separator': 108,
|
||||
'shift': 16,
|
||||
'snapshot': 44,
|
||||
'space': 32,
|
||||
'subtract': 109,
|
||||
'tab': 9,
|
||||
'up': 38,
|
||||
'volume_down': 174,
|
||||
'volume_mute': 173,
|
||||
'volume_up': 175,
|
||||
'xbutton1': 5,
|
||||
'xbutton2': 6,
|
||||
'zoom': 251,
|
||||
'/': 191,
|
||||
';': 218,
|
||||
'[': 219,
|
||||
'\\': 220,
|
||||
']': 221,
|
||||
'\'': 222,
|
||||
'=': 187,
|
||||
'-': 189,
|
||||
';': 186,
|
||||
}
|
||||
|
||||
modifiers = {'alt': 1, 'control': 2, 'shift': 4, 'win': 8}
|
||||
|
@@ -3,56 +3,56 @@ import threading
|
||||
import thread
|
||||
import pyatspi
|
||||
def parse(s):
|
||||
"""parse a string like control+f into (modifier, key).
|
||||
Unknown modifiers will return ValueError."""
|
||||
m = 0
|
||||
lst = s.split('+')
|
||||
if not len(lst): return (0, s)
|
||||
"""parse a string like control+f into (modifier, key).
|
||||
Unknown modifiers will return ValueError."""
|
||||
m = 0
|
||||
lst = s.split('+')
|
||||
if not len(lst): return (0, s)
|
||||
#Are these right?
|
||||
d = {
|
||||
"shift": 1<<pyatspi.MODIFIER_SHIFT,
|
||||
"control": 1<<pyatspi.MODIFIER_CONTROL,
|
||||
"alt": 1<<pyatspi.MODIFIER_ALT,
|
||||
"win":1<<pyatspi.MODIFIER_META3,
|
||||
}
|
||||
for item in lst:
|
||||
if item in d:
|
||||
m|=d[item]
|
||||
lst.remove(item)
|
||||
d = {
|
||||
"shift": 1<<pyatspi.MODIFIER_SHIFT,
|
||||
"control": 1<<pyatspi.MODIFIER_CONTROL,
|
||||
"alt": 1<<pyatspi.MODIFIER_ALT,
|
||||
"win":1<<pyatspi.MODIFIER_META3,
|
||||
}
|
||||
for item in lst:
|
||||
if item in d:
|
||||
m|=d[item]
|
||||
lst.remove(item)
|
||||
#end if
|
||||
if len(lst) > 1: #more than one key, parse error
|
||||
raise ValueError, 'unknown modifier %s' % lst[0]
|
||||
return (m, lst[0].lower())
|
||||
if len(lst) > 1: #more than one key, parse error
|
||||
raise ValueError, 'unknown modifier %s' % lst[0]
|
||||
return (m, lst[0].lower())
|
||||
class AtspiThread(threading.Thread):
|
||||
def run(self):
|
||||
pyatspi.Registry.registerKeystrokeListener(handler, kind=(pyatspi.KEY_PRESSED_EVENT,),
|
||||
mask=pyatspi.allModifiers())
|
||||
pyatspi.Registry.start()
|
||||
def run(self):
|
||||
pyatspi.Registry.registerKeystrokeListener(handler, kind=(pyatspi.KEY_PRESSED_EVENT,),
|
||||
mask=pyatspi.allModifiers())
|
||||
pyatspi.Registry.start()
|
||||
#the keys we registered
|
||||
keys = {}
|
||||
def handler(e):
|
||||
m,k = e.modifiers,e.event_string.lower()
|
||||
m,k = e.modifiers,e.event_string.lower()
|
||||
#not sure why we can't catch control+f. Try to fix it.
|
||||
if (not e.is_text) and e.id >= 97 <= 126:
|
||||
k = chr(e.id)
|
||||
if (m,k) not in keys: return False
|
||||
thread.start_new(keys[(m,k)], ())
|
||||
return True #don't pass it on
|
||||
if (not e.is_text) and e.id >= 97 <= 126:
|
||||
k = chr(e.id)
|
||||
if (m,k) not in keys: return False
|
||||
thread.start_new(keys[(m,k)], ())
|
||||
return True #don't pass it on
|
||||
class LinuxKeyboardHandler(KeyboardHandler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
KeyboardHandler.__init__(self, *args, **kwargs)
|
||||
t = AtspiThread()
|
||||
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."""
|
||||
def __init__(self, *args, **kwargs):
|
||||
KeyboardHandler.__init__(self, *args, **kwargs)
|
||||
t = AtspiThread()
|
||||
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."""
|
||||
#register key so we know if we have it on event receive.
|
||||
t = parse(key)
|
||||
keys[t] = function
|
||||
t = parse(key)
|
||||
keys[t] = function
|
||||
#if we got this far, the key is valid.
|
||||
KeyboardHandler.register_key(self, key, function)
|
||||
KeyboardHandler.register_key(self, key, function)
|
||||
|
||||
def unregister_key (self, key, function):
|
||||
KeyboardHandler.unregister_key(self, key, function)
|
||||
del keys[parse(key)]
|
||||
def unregister_key (self, key, function):
|
||||
KeyboardHandler.unregister_key(self, key, function)
|
||||
del keys[parse(key)]
|
||||
|
@@ -5,84 +5,84 @@ class KeyboardHandlerError (Exception): pass
|
||||
|
||||
class KeyboardHandler(object):
|
||||
|
||||
def __init__(self, repeat_rate=0.0, *args, **kwargs):
|
||||
self.repeat_rate = repeat_rate #How long between accepting the same keystroke?
|
||||
self._last_key = None
|
||||
self._last_keypress_time = 0
|
||||
super(KeyboardHandler, self).__init__(*args, **kwargs)
|
||||
self.active_keys = {}
|
||||
if not hasattr(self, 'replacement_mods'):
|
||||
self.replacement_mods = {}
|
||||
if not hasattr(self, 'replacement_keys'):
|
||||
self.replacement_keys = {}
|
||||
def __init__(self, repeat_rate=0.0, *args, **kwargs):
|
||||
self.repeat_rate = repeat_rate #How long between accepting the same keystroke?
|
||||
self._last_key = None
|
||||
self._last_keypress_time = 0
|
||||
super(KeyboardHandler, self).__init__(*args, **kwargs)
|
||||
self.active_keys = {}
|
||||
if not hasattr(self, 'replacement_mods'):
|
||||
self.replacement_mods = {}
|
||||
if not hasattr(self, 'replacement_keys'):
|
||||
self.replacement_keys = {}
|
||||
|
||||
def register_key (self, key, function):
|
||||
if key in self.active_keys:
|
||||
raise KeyboardHandlerError("Key %s is already registered to a function" % key)
|
||||
if not callable(function):
|
||||
raise TypeError("Must provide a callable to be invoked upon keypress")
|
||||
self.active_keys[key] = function
|
||||
def register_key (self, key, function):
|
||||
if key in self.active_keys:
|
||||
raise KeyboardHandlerError("Key %s is already registered to a function" % key)
|
||||
if not callable(function):
|
||||
raise TypeError("Must provide a callable to be invoked upon keypress")
|
||||
self.active_keys[key] = function
|
||||
|
||||
def unregister_key (self, key, function):
|
||||
try:
|
||||
if self.active_keys[key] != function:
|
||||
raise KeyboardHandlerError("key %s is not registered to that function" % key)
|
||||
except KeyError:
|
||||
raise KeyboardHandlerError("Key %s not currently registered" % key)
|
||||
del(self.active_keys[key])
|
||||
def unregister_key (self, key, function):
|
||||
try:
|
||||
if self.active_keys[key] != function:
|
||||
raise KeyboardHandlerError("key %s is not registered to that function" % key)
|
||||
except KeyError:
|
||||
raise KeyboardHandlerError("Key %s not currently registered" % key)
|
||||
del(self.active_keys[key])
|
||||
|
||||
def unregister_all_keys(self):
|
||||
for key in list(self.active_keys):
|
||||
self.unregister_key(key, self.active_keys[key])
|
||||
def unregister_all_keys(self):
|
||||
for key in list(self.active_keys):
|
||||
self.unregister_key(key, self.active_keys[key])
|
||||
|
||||
def handle_key (self, key):
|
||||
if self.repeat_rate and key == self._last_key and time.time() - self._last_keypress_time < self.repeat_rate:
|
||||
return
|
||||
try:
|
||||
function = self.active_keys[key]
|
||||
except KeyError:
|
||||
return
|
||||
self._last_key = key
|
||||
self._last_keypress_time = time.time()
|
||||
return function()
|
||||
def handle_key (self, key):
|
||||
if self.repeat_rate and key == self._last_key and time.time() - self._last_keypress_time < self.repeat_rate:
|
||||
return
|
||||
try:
|
||||
function = self.active_keys[key]
|
||||
except KeyError:
|
||||
return
|
||||
self._last_key = key
|
||||
self._last_keypress_time = time.time()
|
||||
return function()
|
||||
|
||||
def register_keys(self, keys):
|
||||
"""Given a mapping of keystrokes to functions, registers all keystrokes"""
|
||||
for k in keys:
|
||||
self.register_key(k, keys[k])
|
||||
def register_keys(self, keys):
|
||||
"""Given a mapping of keystrokes to functions, registers all keystrokes"""
|
||||
for k in keys:
|
||||
self.register_key(k, keys[k])
|
||||
|
||||
def unregister_keys(self, keys):
|
||||
"""Given a mapping of keys to their functions, unregisters all provided keys."""
|
||||
for k in keys:
|
||||
self.unregister_key(k, keys[k])
|
||||
def unregister_keys(self, keys):
|
||||
"""Given a mapping of keys to their functions, unregisters all provided keys."""
|
||||
for k in keys:
|
||||
self.unregister_key(k, keys[k])
|
||||
|
||||
def standardize_key(self, key):
|
||||
"""Takes a keystroke and places it in a standard case and order in a list."""
|
||||
working = key.split('+')
|
||||
working = [i.lower() for i in working]
|
||||
answer = []
|
||||
if "control" in working:
|
||||
answer.append("control")
|
||||
if "win" in working:
|
||||
answer.append("win")
|
||||
if "alt" in working:
|
||||
answer.append("alt")
|
||||
if "shift" in working:
|
||||
answer.append("shift")
|
||||
if working[-1] not in answer:
|
||||
answer.append(working[-1])
|
||||
return answer
|
||||
def standardize_key(self, key):
|
||||
"""Takes a keystroke and places it in a standard case and order in a list."""
|
||||
working = key.split('+')
|
||||
working = [i.lower() for i in working]
|
||||
answer = []
|
||||
if "control" in working:
|
||||
answer.append("control")
|
||||
if "win" in working:
|
||||
answer.append("win")
|
||||
if "alt" in working:
|
||||
answer.append("alt")
|
||||
if "shift" in working:
|
||||
answer.append("shift")
|
||||
if working[-1] not in answer:
|
||||
answer.append(working[-1])
|
||||
return answer
|
||||
|
||||
def standardize_keymap(self, keymap):
|
||||
"""Given a keymap, returns the keymap standardized."""
|
||||
full = {}
|
||||
for i in keymap:
|
||||
answer = ""
|
||||
new = self.standardize_key(keymap[i])
|
||||
for (c, j) in enumerate(new):
|
||||
if c < len(new)-1:
|
||||
answer = "%s%s+" % (answer, j)
|
||||
else:
|
||||
answer = "%s%s" % (answer, j)
|
||||
full[i] = answer
|
||||
return full
|
||||
def standardize_keymap(self, keymap):
|
||||
"""Given a keymap, returns the keymap standardized."""
|
||||
full = {}
|
||||
for i in keymap:
|
||||
answer = ""
|
||||
new = self.standardize_key(keymap[i])
|
||||
for (c, j) in enumerate(new):
|
||||
if c < len(new)-1:
|
||||
answer = "%s%s+" % (answer, j)
|
||||
else:
|
||||
answer = "%s%s" % (answer, j)
|
||||
full[i] = answer
|
||||
return full
|
||||
|
@@ -12,45 +12,45 @@ kEventHotKeyReleasedSubtype = 9
|
||||
|
||||
class OSXKeyboardHandler(KeyboardHandler):
|
||||
|
||||
def __init__(self):
|
||||
super(OSXKeyboardHandler, self).__init__()
|
||||
self.replacement_keys = dict()
|
||||
self.app = KeyboardCapturingNSApplication.alloc().init()
|
||||
self._event_thread = Thread(target=AppHelper.runEventLoop)
|
||||
self._event_thread.start()
|
||||
def __init__(self):
|
||||
super(OSXKeyboardHandler, self).__init__()
|
||||
self.replacement_keys = dict()
|
||||
self.app = KeyboardCapturingNSApplication.alloc().init()
|
||||
self._event_thread = Thread(target=AppHelper.runEventLoop)
|
||||
self._event_thread.start()
|
||||
|
||||
def register_key (self, key, function):
|
||||
super(OSXKeyboardHandler, self).register_key(key, function)
|
||||
k, m = self.parse_key(key)
|
||||
key_id = RegisterEventHotKey(k, m, (0, 0), GetApplicationEventTarget(), 0)
|
||||
self.key_ids[key] = key_id
|
||||
def register_key (self, key, function):
|
||||
super(OSXKeyboardHandler, self).register_key(key, function)
|
||||
k, m = self.parse_key(key)
|
||||
key_id = RegisterEventHotKey(k, m, (0, 0), GetApplicationEventTarget(), 0)
|
||||
self.key_ids[key] = key_id
|
||||
|
||||
def unregister_key (self, key, function):
|
||||
super(OSXKeyboardHandler, self).unregister_key(key, function)
|
||||
key_id = self.key_ids[key]
|
||||
raise NotImplementedError
|
||||
def unregister_key (self, key, function):
|
||||
super(OSXKeyboardHandler, self).unregister_key(key, function)
|
||||
key_id = self.key_ids[key]
|
||||
raise NotImplementedError
|
||||
|
||||
def parse_key (self, key):
|
||||
key=key.split("+")
|
||||
#replacements
|
||||
#Modifier keys:
|
||||
for index, item in enumerate(key[0:-1]):
|
||||
if self.replacement_mods.has_key(item):
|
||||
key[index] = self.replacement_mods[item]
|
||||
if self.replacement_keys.has_key(key[-1]):
|
||||
key[-1] = self.replacement_keys[key[-1]]
|
||||
elif len(key[-1])==1:
|
||||
key[-1] = ord(str(key[-1]))-36
|
||||
mods = 0
|
||||
for i in key[:-1]:
|
||||
mods = mods|i
|
||||
return [key[-1], mods]
|
||||
def parse_key (self, key):
|
||||
key=key.split("+")
|
||||
#replacements
|
||||
#Modifier keys:
|
||||
for index, item in enumerate(key[0:-1]):
|
||||
if self.replacement_mods.has_key(item):
|
||||
key[index] = self.replacement_mods[item]
|
||||
if self.replacement_keys.has_key(key[-1]):
|
||||
key[-1] = self.replacement_keys[key[-1]]
|
||||
elif len(key[-1])==1:
|
||||
key[-1] = ord(str(key[-1]))-36
|
||||
mods = 0
|
||||
for i in key[:-1]:
|
||||
mods = mods|i
|
||||
return [key[-1], mods]
|
||||
|
||||
class KeyboardCapturingNSApplication(NSApplication):
|
||||
|
||||
def sendEvent_(self, theEvent):
|
||||
if theEvent.type() == NSSystemDefined and theEvent.subtype() == kEventHotKeyPressedSubtype:
|
||||
self.activateIgnoringOtherApps_(True)
|
||||
NSRunAlertPanel(u'Hot Key Pressed', u'Hot Key Pressed', None, None, None)
|
||||
super(NSApplication, self).sendEvent_(theEvent)
|
||||
def sendEvent_(self, theEvent):
|
||||
if theEvent.type() == NSSystemDefined and theEvent.subtype() == kEventHotKeyPressedSubtype:
|
||||
self.activateIgnoringOtherApps_(True)
|
||||
NSRunAlertPanel(u'Hot Key Pressed', u'Hot Key Pressed', None, None, None)
|
||||
super(NSApplication, self).sendEvent_(theEvent)
|
||||
|
||||
|
@@ -5,36 +5,36 @@ from main import KeyboardHandler
|
||||
|
||||
class WindowsKeyboardHandler(KeyboardHandler):
|
||||
|
||||
def __init__ (self, *args, **kwargs):
|
||||
super(WindowsKeyboardHandler, self).__init__(*args, **kwargs)
|
||||
#Setup the replacement dictionaries.
|
||||
for i in dir(win32con):
|
||||
if i.startswith("VK_"):
|
||||
key = i[3:].lower()
|
||||
self.replacement_keys[key] = getattr(win32con, i)
|
||||
elif i.startswith("MOD_"):
|
||||
key = i[4:].lower()
|
||||
self.replacement_mods[key] = getattr(win32con, i)
|
||||
self.replacement_keys .update(dict(pageup=win32con.VK_PRIOR, pagedown=win32con.VK_NEXT))
|
||||
def __init__ (self, *args, **kwargs):
|
||||
super(WindowsKeyboardHandler, self).__init__(*args, **kwargs)
|
||||
#Setup the replacement dictionaries.
|
||||
for i in dir(win32con):
|
||||
if i.startswith("VK_"):
|
||||
key = i[3:].lower()
|
||||
self.replacement_keys[key] = getattr(win32con, i)
|
||||
elif i.startswith("MOD_"):
|
||||
key = i[4:].lower()
|
||||
self.replacement_mods[key] = getattr(win32con, i)
|
||||
self.replacement_keys .update(dict(pageup=win32con.VK_PRIOR, pagedown=win32con.VK_NEXT))
|
||||
|
||||
def parse_key (self, keystroke, separator="+"):
|
||||
keystroke = str(keystroke) #We don't want unicode
|
||||
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
|
||||
mods = 0
|
||||
for i in keystroke[:-1]:
|
||||
mods = mods | i #or everything together
|
||||
return (mods, keystroke[-1])
|
||||
def parse_key (self, keystroke, separator="+"):
|
||||
keystroke = str(keystroke) #We don't want unicode
|
||||
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
|
||||
mods = 0
|
||||
for i in keystroke[:-1]:
|
||||
mods = mods | i #or everything together
|
||||
return (mods, keystroke[-1])
|
||||
|
||||
def keycode_from_key(self, key):
|
||||
if key in self.replacement_mods:
|
||||
return self.replacement_mods[key]
|
||||
if key in self.replacement_keys:
|
||||
return self.replacement_keys[key]
|
||||
if len(key) == 1:
|
||||
return win32api.VkKeyScanEx(key, win32api.GetKeyboardLayout())
|
||||
def keycode_from_key(self, key):
|
||||
if key in self.replacement_mods:
|
||||
return self.replacement_mods[key]
|
||||
if key in self.replacement_keys:
|
||||
return self.replacement_keys[key]
|
||||
if len(key) == 1:
|
||||
return win32api.VkKeyScanEx(key, win32api.GetKeyboardLayout())
|
||||
|
||||
def is_key_pressed(self, key):
|
||||
"""Returns if the given key was pressed. Requires an active message loop or will simply give if the key was pressed recently."""
|
||||
key = self.keycode_from_key(key)
|
||||
return win32api.GetAsyncKeyState(key)
|
||||
def is_key_pressed(self, key):
|
||||
"""Returns if the given key was pressed. Requires an active message loop or will simply give if the key was pressed recently."""
|
||||
key = self.keycode_from_key(key)
|
||||
return win32api.GetAsyncKeyState(key)
|
||||
|
||||
|
@@ -10,121 +10,121 @@ from . import key_constants
|
||||
__all__ = ['WXKeyboardHandler', 'WXControlKeyboardHandler']
|
||||
|
||||
def call_after(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
wx.CallAfter(func, *args, **kwargs)
|
||||
functools.update_wrapper(wrapper, func)
|
||||
return wrapper
|
||||
def wrapper(*args, **kwargs):
|
||||
wx.CallAfter(func, *args, **kwargs)
|
||||
functools.update_wrapper(wrapper, func)
|
||||
return wrapper
|
||||
|
||||
|
||||
class BaseWXKeyboardHandler(KeyboardHandler):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseWXKeyboardHandler, self).__init__(*args, **kwargs)
|
||||
#Setup the replacement dictionaries.
|
||||
for i in dir(wx):
|
||||
if i.startswith('WXK_'):
|
||||
key = i[4:].lower()
|
||||
self.replacement_keys[key] = getattr(wx, i)
|
||||
elif i.startswith('MOD_'):
|
||||
key = i[4:].lower()
|
||||
self.replacement_mods[key] = getattr(wx, i)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseWXKeyboardHandler, self).__init__(*args, **kwargs)
|
||||
#Setup the replacement dictionaries.
|
||||
for i in dir(wx):
|
||||
if i.startswith('WXK_'):
|
||||
key = i[4:].lower()
|
||||
self.replacement_keys[key] = getattr(wx, i)
|
||||
elif i.startswith('MOD_'):
|
||||
key = i[4:].lower()
|
||||
self.replacement_mods[key] = getattr(wx, i)
|
||||
|
||||
def parse_key (self, keystroke, separator="+"):
|
||||
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
|
||||
mods = 0
|
||||
for i in keystroke[:-1]:
|
||||
mods = mods | i #or everything together
|
||||
return (mods, keystroke[-1])
|
||||
def parse_key (self, keystroke, separator="+"):
|
||||
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
|
||||
mods = 0
|
||||
for i in keystroke[:-1]:
|
||||
mods = mods | i #or everything together
|
||||
return (mods, keystroke[-1])
|
||||
|
||||
def keycode_from_key(self, key):
|
||||
result = None
|
||||
if key in self.replacement_mods:
|
||||
result = self.replacement_mods[key]
|
||||
elif key in self.replacement_keys:
|
||||
result = self.replacement_keys[key]
|
||||
if result >= 277:
|
||||
result -= 277
|
||||
elif len(key) == 1:
|
||||
result = ord(key.upper())
|
||||
if result is None:
|
||||
raise KeyboardHandlerError("Could not translate key %r into a valid keycode." % key)
|
||||
return result
|
||||
|
||||
def keycode_from_key(self, key):
|
||||
result = None
|
||||
if key in self.replacement_mods:
|
||||
result = self.replacement_mods[key]
|
||||
elif key in self.replacement_keys:
|
||||
result = self.replacement_keys[key]
|
||||
if result >= 277:
|
||||
result -= 277
|
||||
elif len(key) == 1:
|
||||
result = ord(key.upper())
|
||||
if result is None:
|
||||
raise KeyboardHandlerError("Could not translate key %r into a valid keycode." % key)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
class WXKeyboardHandler(BaseWXKeyboardHandler):
|
||||
|
||||
def __init__ (self, parent, *args, **kwargs):
|
||||
super(WXKeyboardHandler, self).__init__(*args, **kwargs)
|
||||
self.parent = parent
|
||||
self.key_ids = {}
|
||||
self.replacement_keys = key_constants.keys
|
||||
self.replacement_mods = key_constants.modifiers
|
||||
def __init__ (self, parent, *args, **kwargs):
|
||||
super(WXKeyboardHandler, self).__init__(*args, **kwargs)
|
||||
self.parent = parent
|
||||
self.key_ids = {}
|
||||
self.replacement_keys = key_constants.keys
|
||||
self.replacement_mods = key_constants.modifiers
|
||||
|
||||
@call_after
|
||||
def register_key(self, key, function):
|
||||
super(WXKeyboardHandler, self).register_key(key, function)
|
||||
key_id = wx.NewId()
|
||||
parsed = self.parse_key(key)
|
||||
res = self.parent.RegisterHotKey(key_id, *parsed)
|
||||
if not res:
|
||||
logger.warn("Failed to register hotkey: %s for function %r", key, function)
|
||||
self.parent.Bind(wx.EVT_HOTKEY, lambda evt: self.process_key(evt, key_id), id=key_id)
|
||||
self.key_ids[key] = key_id
|
||||
return res
|
||||
@call_after
|
||||
def register_key(self, key, function):
|
||||
super(WXKeyboardHandler, self).register_key(key, function)
|
||||
key_id = wx.NewId()
|
||||
parsed = self.parse_key(key)
|
||||
res = self.parent.RegisterHotKey(key_id, *parsed)
|
||||
if not res:
|
||||
logger.warn("Failed to register hotkey: %s for function %r", key, function)
|
||||
self.parent.Bind(wx.EVT_HOTKEY, lambda evt: self.process_key(evt, key_id), id=key_id)
|
||||
self.key_ids[key] = key_id
|
||||
return res
|
||||
|
||||
def parse_key (self, keystroke, separator="+"):
|
||||
keystroke = str(keystroke) #We don't want unicode
|
||||
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
|
||||
mods = 0
|
||||
for i in keystroke[:-1]:
|
||||
mods = mods | i #or everything together
|
||||
return (mods, keystroke[-1])
|
||||
def parse_key (self, keystroke, separator="+"):
|
||||
keystroke = str(keystroke) #We don't want unicode
|
||||
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
|
||||
mods = 0
|
||||
for i in keystroke[:-1]:
|
||||
mods = mods | i #or everything together
|
||||
return (mods, keystroke[-1])
|
||||
|
||||
@call_after
|
||||
def unregister_key (self, key, function):
|
||||
super(WXKeyboardHandler, self).unregister_key(key, function)
|
||||
if key not in self.key_ids:
|
||||
return #there's nothing we can do.
|
||||
key_id = self.key_ids[key]
|
||||
self.parent.UnregisterHotKey(key_id)
|
||||
self.parent.Unbind( wx.EVT_HOTKEY, id=key_id)
|
||||
self.key_ids.pop(key)
|
||||
@call_after
|
||||
def unregister_key (self, key, function):
|
||||
super(WXKeyboardHandler, self).unregister_key(key, function)
|
||||
if key not in self.key_ids:
|
||||
return #there's nothing we can do.
|
||||
key_id = self.key_ids[key]
|
||||
self.parent.UnregisterHotKey(key_id)
|
||||
self.parent.Unbind( wx.EVT_HOTKEY, id=key_id)
|
||||
self.key_ids.pop(key)
|
||||
|
||||
def process_key (self, evt, id):
|
||||
evt.Skip()
|
||||
key_ids = self.key_ids.keys()
|
||||
for i in key_ids:
|
||||
if self.key_ids.get(i) == id:
|
||||
self.handle_key(i)
|
||||
def process_key (self, evt, id):
|
||||
evt.Skip()
|
||||
key_ids = self.key_ids.keys()
|
||||
for i in key_ids:
|
||||
if self.key_ids.get(i) == id:
|
||||
self.handle_key(i)
|
||||
|
||||
class WXControlKeyboardHandler(wx.StaticText, KeyboardHandler):
|
||||
|
||||
def __init__(self, parent=None, *a, **k):
|
||||
wx.StaticText.__init__(self, parent=parent)
|
||||
KeyboardHandler.__init__(self, *a, **k)
|
||||
self.wx_replacements = {}
|
||||
for i in [d for d in dir(wx) if d.startswith('WXK_')]:
|
||||
self.wx_replacements[getattr(wx, i)] = i[4:].lower()
|
||||
self.Bind(wx.EVT_KEY_DOWN, self.process_key, self)
|
||||
self.SetFocus()
|
||||
def __init__(self, parent=None, *a, **k):
|
||||
wx.StaticText.__init__(self, parent=parent)
|
||||
KeyboardHandler.__init__(self, *a, **k)
|
||||
self.wx_replacements = {}
|
||||
for i in [d for d in dir(wx) if d.startswith('WXK_')]:
|
||||
self.wx_replacements[getattr(wx, i)] = i[4:].lower()
|
||||
self.Bind(wx.EVT_KEY_DOWN, self.process_key, self)
|
||||
self.SetFocus()
|
||||
|
||||
def process_key(self, evt):
|
||||
keycode = evt.GetKeyCode()
|
||||
keyname = self.wx_replacements.get(keycode, None)
|
||||
modifiers = ""
|
||||
replacements = ( (evt.ControlDown(), 'control+'),
|
||||
(evt.AltDown(), 'alt+'),
|
||||
(evt.ShiftDown(), 'shift+'),
|
||||
(evt.MetaDown(), 'win+')
|
||||
)
|
||||
for mod, ch in (replacements):
|
||||
if mod:
|
||||
modifiers += ch
|
||||
if keyname is None:
|
||||
if 27 < keycode < 256:
|
||||
keyname = chr(keycode).lower()
|
||||
else:
|
||||
keyname = "(%s)unknown" % keycode
|
||||
key = modifiers + keyname
|
||||
self.handle_key(key)
|
||||
def process_key(self, evt):
|
||||
keycode = evt.GetKeyCode()
|
||||
keyname = self.wx_replacements.get(keycode, None)
|
||||
modifiers = ""
|
||||
replacements = ( (evt.ControlDown(), 'control+'),
|
||||
(evt.AltDown(), 'alt+'),
|
||||
(evt.ShiftDown(), 'shift+'),
|
||||
(evt.MetaDown(), 'win+')
|
||||
)
|
||||
for mod, ch in (replacements):
|
||||
if mod:
|
||||
modifiers += ch
|
||||
if keyname is None:
|
||||
if 27 < keycode < 256:
|
||||
keyname = chr(keycode).lower()
|
||||
else:
|
||||
keyname = "(%s)unknown" % keycode
|
||||
key = modifiers + keyname
|
||||
self.handle_key(key)
|
||||
|
@@ -34,4 +34,5 @@ configuration = string(default="control+win+o")
|
||||
accountConfiguration = string(default="control+win+shift+o")
|
||||
update_buffer = string(default="control+win+shift+u")
|
||||
ocr_image = string(default="win+alt+o")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
add_alias=string(default="")
|
@@ -53,4 +53,5 @@ configuration = string(default="control+win+o")
|
||||
accountConfiguration = string(default="control+win+shift+o")
|
||||
update_buffer = string(default="control+win+shift+u")
|
||||
ocr_image = string(default="win+alt+o")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
add_alias=string(default="")
|
@@ -54,4 +54,5 @@ configuration = string(default="control+win+alt+o")
|
||||
accountConfiguration = string(default="control+win+shift+o")
|
||||
update_buffer = string(default="control+alt+shift+u")
|
||||
ocr_image = string(default="win+alt+o")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
add_alias=string(default="")
|
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="")
|
@@ -55,4 +55,5 @@ list_manager = string(default="control+win+shift+l")
|
||||
configuration = string(default="control+win+o")
|
||||
accountConfiguration = string(default="control+win+shift+o")
|
||||
update_buffer = string(default="control+win+shift+u")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
add_alias=string(default="")
|
@@ -56,4 +56,5 @@ configuration = string(default="control+win+o")
|
||||
accountConfiguration = string(default="control+win+shift+o")
|
||||
update_buffer = string(default="control+win+shift+u")
|
||||
ocr_image = string(default="win+alt+o")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
open_in_browser = string(default="alt+control+win+return")
|
||||
add_alias=string(default="")
|
@@ -14,9 +14,9 @@ import paths
|
||||
# lib = load_library("snapshot_api_keys64", x64_path=paths.app_path("keys/lib"))
|
||||
#else:
|
||||
if platform.architecture()[0][:2] == "32":
|
||||
lib = load_library("stable_api_keys32", x86_path=os.path.join(paths.app_path(), "keys", "lib"))
|
||||
lib = load_library("stable_api_keys32", x86_path=os.path.join(paths.app_path(), "keys", "lib"))
|
||||
else:
|
||||
lib = load_library("stable_api_keys64", x64_path=os.path.join(paths.app_path(), "keys", "lib"))
|
||||
lib = load_library("stable_api_keys64", x64_path=os.path.join(paths.app_path(), "keys", "lib"))
|
||||
|
||||
# import linuxKeys
|
||||
# lib = linuxKeys
|
||||
@@ -24,20 +24,20 @@ else:
|
||||
keyring = None
|
||||
|
||||
def setup():
|
||||
global keyring
|
||||
if keyring == None:
|
||||
keyring = Keyring()
|
||||
global keyring
|
||||
if keyring == None:
|
||||
keyring = Keyring()
|
||||
|
||||
class Keyring(object):
|
||||
def __init__(self):
|
||||
super(Keyring, self).__init__()
|
||||
def __init__(self):
|
||||
super(Keyring, self).__init__()
|
||||
|
||||
def _call_method(self, function):
|
||||
result = getattr(lib, function)
|
||||
result = c_char_p(result.__call__())
|
||||
return result.value
|
||||
def _call_method(self, function):
|
||||
result = getattr(lib, function)
|
||||
result = c_char_p(result.__call__())
|
||||
return result.value
|
||||
|
||||
def get(self, func):
|
||||
if hasattr(application,func+"_override"):
|
||||
return getattr(application,func+'_override')
|
||||
return getattr(self, "_call_method")("get_"+func)
|
||||
def get(self, func):
|
||||
if hasattr(application,func+"_override"):
|
||||
return getattr(application,func+'_override')
|
||||
return getattr(self, "_call_method")("get_"+func)
|
||||
|
@@ -1,9 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
def get_api_key():
|
||||
return "8pDLbyOW3saYnvSZ4uLFg\0"
|
||||
return "8pDLbyOW3saYnvSZ4uLFg\0"
|
||||
|
||||
def get_api_secret():
|
||||
return "YsgdrzY9B4yyYvYsyee78rKI3GshjHpenVS9LnFJXY\0";
|
||||
return "YsgdrzY9B4yyYvYsyee78rKI3GshjHpenVS9LnFJXY\0";
|
||||
|
||||
#char *get_dropbox_api_key(){
|
||||
#return "key\0";
|
||||
|
@@ -1,3 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from .keystrokeEditor import KeystrokeEditor
|
||||
from .keystrokeEditor import KeystrokeEditor
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user